Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Add a basic TextFileStream and tests #23

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@ trim_trailing_whitespace = true

[*.md]
trim_trailing_whitespace = false

[*.txt]
insert_final_newline = false
10 changes: 10 additions & 0 deletions benchmarks/JSONBench.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@

use Parsica\Parsica\JSON\JSON as ParsicaJSON;
use Json as BaseMaxJson;
use Parsica\Parsica\TextFileStream;

class JSONBench
{
private string $data;

function __construct()
{
$this->fileData = new TextFileStream(__DIR__ . '/JSONbench.json');
$this->data = <<<JSON
{
"name": "mathiasverraes/parsica",
Expand Down Expand Up @@ -72,6 +74,14 @@ public function bench_Parsica_JSON()
$result = ParsicaJSON::json()->tryString($this->data);
}

/**
* @Revs(5)
* @Iterations(3)
*/
public function bench_Parsica_TextFileStream_JSON()
{
$result = ParsicaJSON::json()->try($this->fileData);
}

/**
* @Revs(5)
Expand Down
31 changes: 31 additions & 0 deletions benchmarks/JSONbench.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"name": "mathiasverraes/parsica",
"type": "library",
"alotoftext": [
"Lorem Ipsum dolor sit amet",
"Lorem Ipsum dolor sit amet",
"Lorem Ipsum dolor sit amet",
"Lorem Ipsum dolor sit amet",
"Lorem Ipsum dolor sit amet",
"Lorem Ipsum dolor sit amet",
"Lorem Ipsum dolor sit amet",
"Lorem Ipsum dolor sit amet",
"Lorem Ipsum dolor sit amet",
"Lorem Ipsum dolor sit amet",
"Lorem Ipsum dolor sit amet",
"Lorem Ipsum dolor sit amet",
"Lorem Ipsum dolor sit amet",
"Lorem Ipsum dolor sit amet",
"Lorem Ipsum dolor sit amet",
"Lorem Ipsum dolor sit amet",
"Lorem Ipsum dolor sit amet"
],
"alotmoretext": "Fuga iusto dolores ipsam. Qui excepturi veniam iste autem ducimus porro et voluptas. Veniam veniam ducimus cumque facere repudiandae corrupti sint quas. Cupiditate asperiores iure omnis dolores nihil asperiores qui quo. Assumenda quia iure deserunt deserunt. Perspiciatis velit quia et.\n\nExplicabo non dolores aut facere. Perferendis in est voluptate. Et laboriosam et autem voluptatum rem nam et aut. Voluptatem praesentium et earum fugit accusamus tempore consectetur natus. Beatae sunt nisi rerum blanditiis consequatur rerum ut.\n\nIure ipsa sit assumenda. Vitae nisi qui vero. Eveniet cum aliquam molestiae molestias. Nisi aut ea alias quo ea voluptatem. Minus ea mollitia quis.",
"description": "The easiest way to build robust parsers in PHP.",
"keywords": [
"parser",
"parser-combinator",
"parser combinator",
"parsing"
]
}
1 change: 1 addition & 0 deletions benchmarks/ManyBench.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
97 changes: 97 additions & 0 deletions benchmarks/ManyBenchTextFileStream.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<?php declare(strict_types=1);
/**
* This file is part of the Parsica library.
*
* Copyright (c) 2020 Mathias Verraes <mathias@verraes.net>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

use Parsica\Parsica\Parser;
use Parsica\Parsica\TextFileStream;
use function Parsica\Parsica\any;
use function Parsica\Parsica\char;
use function Parsica\Parsica\collect;
use function Parsica\Parsica\many;
use function Parsica\Parsica\map;
use function Parsica\Parsica\pure;
use function Parsica\Parsica\recursive;
use function Parsica\Parsica\satisfy;
use function Parsica\Parsica\takeWhile;

class ManyBenchTextFileStream
{
private string $data;

function __construct()
{
$this->fileData = new TextFileStream(__DIR__ . '/ManyBench.txt');

$this->takeWhile = takeWhile(fn(string $c): bool => $c === 'a');
$this->manySatisfy = many(satisfy(fn(string $c): bool => $c === 'a'));
$this->manyChar = many(char('a'));
$this->oldManySatisfy = static::oldMany(satisfy(fn(string $c): bool => $c === 'a'));
$this->oldManyChar = static::oldMany(char('a'));
}

/**
* @Revs(10)
* @Iterations(10)
*/
public function bench_takeWhile()
{
$result = $this->takeWhile->try($this->fileData);
}

/**
* @Revs(10)
* @Iterations(10)
*/
public function bench_manySatisfy()
{
$result = $this->manySatisfy->try($this->fileData);
}

/**
* @Revs(10)
* @Iterations(10)
*/
public function bench_manyChar()
{
$result = $this->manyChar->try($this->fileData);
}

/**
* @Revs(10)
* @Iterations(10)
*/
public function bench_oldManySatisfy()
{
$result = $this->oldManySatisfy->try($this->fileData);
}

/**
* @Revs(10)
* @Iterations(10)
*/
public function bench_oldManyChar()
{
$result = $this->oldManyChar->try($this->fileData);
}

public static function oldMany(Parser $parser)
{
$rec = recursive();
$rec->recurse(
any(
map(
collect($parser, $rec),
fn(array $o): array => array_merge([$o[0]], $o[1])
),
pure([]),
)
);
return $rec;
}
}
5 changes: 3 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
"require": {
"php": "^7.4 || ^8.0",
"ext-mbstring": "*",
"cypresslab/php-curry": "^0.5.0"
"cypresslab/php-curry": "^0.5.0",
"mallardduck/immutable-read-file": "^1.0.0"
},
"require-dev": {
"ext-json": "*",
Expand Down Expand Up @@ -55,7 +56,7 @@
},
"autoload-dev": {
"psr-4": {
"Tests\\Verraes\\Parsica\\": "tests/"
"Tests\\Parsica\\Parsica\\": "tests/"
}
},
"scripts": {
Expand Down
13 changes: 13 additions & 0 deletions src/Internal/BasePosition.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace Parsica\Parsica\Internal;

interface BasePosition
{
public static function initial(string $filename): BasePosition;
public function pretty(): string;
public function advance(string $parsed): BasePosition;
public function filename(): string;
public function line(): int;
public function column(): int;
}
2 changes: 1 addition & 1 deletion src/Internal/Position.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
* @psalm-immutable
* @psalm-external-mutation-free
*/
final class Position
final class Position implements BasePosition
{
/** @psalm-readonly */
private string $filename;
Expand Down
99 changes: 99 additions & 0 deletions src/Internal/PositionWithBytes.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<?php declare(strict_types=1);
/**
* This file is part of the Parsica library.
*
* Copyright (c) 2020 Mathias Verraes <mathias@verraes.net>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Parsica\Parsica\Internal;

/**
* File, line, and column position of the parser.
*
* @psalm-immutable
* @psalm-external-mutation-free
*/
final class PositionWithBytes implements BasePosition
{
/** @psalm-readonly */
private string $filename;
/** @psalm-readonly */
private int $line;
/** @psalm-readonly */
private int $column;
/** @psalm-readonly */
private int $bytePosition;

function __construct(string $filename, int $line, int $column, int $bytePosition)
{
$this->filename = $filename;
$this->line = $line;
$this->column = $column;
$this->bytePosition = $bytePosition;
}

/**
* Initial position (line 1, column 1). The optional filename is the source of the input, and is really just a label
* to make more useful error messages.
*/
public static function initial(string $filename = "<input>"): PositionWithBytes
{
return new PositionWithBytes($filename, 1, 1, 0);
}

/**
* Pretty print as "filename:line:column"
*/
public function pretty(): string
{
return $this->filename . ":" . $this->line . ":" . $this->column;
}

public function advance(string $parsed): PositionWithBytes
{
$column = $this->column;
$line = $this->line;
$bytePosition = $this->bytePosition;
/** @psalm-var string $char */
foreach (mb_str_split($parsed, 1) as $char) {
switch ($char) {
case "\n":
case "\r":
$line++;
$column = 1;
break;
case "\t":
$column = $column + 4 - (($column - 1) % 4);
break;
default:
$column++;
}
$bytePosition += strlen($char);
}

return new PositionWithBytes($this->filename, $line, $column, $bytePosition);
}

public function filename(): string
{
return $this->filename;
}

public function line(): int
{
return $this->line;
}

public function column(): int
{
return $this->column;
}

public function bytePosition(): int
{
return $this->bytePosition;
}
}
24 changes: 24 additions & 0 deletions src/PHPUnit/ParserAssertions.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

use Exception;
use Parsica\Parsica\Parser;
use Parsica\Parsica\Stream;
use Parsica\Parsica\StringStream;

/**
Expand Down Expand Up @@ -46,6 +47,29 @@ protected function assertParses(string $input, Parser $parser, $expectedOutput,
}
}

/**
* @psalm-param mixed $expectedOutput
*
* @api
*/
protected function assertParsesStream(Stream $input, Parser $parser, $expectedOutput, string $message = ""): void
{
$actualResult = $parser->run($input);
if ($actualResult->isSuccess()) {
$this->assertStrictlyEquals(
$expectedOutput,
$actualResult->output(),
$message . "\n" . "The parser succeeded but the output doesn't match your expected output."
);
} else {
$this->fail(
$message . "\n"
."The parser failed with the following error message:\n"
.$actualResult->errorMessage()."\n"
);
}
}

/**
* Behaves like assertSame for primitives, behaves like assertEquals for objects of the same type, and fails
* for everything else.
Expand Down
6 changes: 3 additions & 3 deletions src/Stream.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@

namespace Parsica\Parsica;

use Parsica\Parsica\Internal\Position;
use Parsica\Parsica\Internal\BasePosition;
use Parsica\Parsica\Internal\TakeResult;

/**
* Represents an input stream. This allows us to have different types of input, each with their own optimizations.
*
* @psalm-immutable
* @psalm-external-mutation-free
*/
interface Stream
{
Expand Down Expand Up @@ -67,5 +67,5 @@ public function isEOF(): bool;
*
* @internal
*/
public function position() : Position;
public function position() : BasePosition;
}
2 changes: 1 addition & 1 deletion src/StringStream.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
use Parsica\Parsica\Internal\TakeResult;

/**
* @psalm-immutable
* @psalm-external-mutation-free
*/
final class StringStream implements Stream
{
Expand Down
Loading