Parsica - PHP Parser Combinators - The easiest way to build robust parsers.
static
keyword on all closuresappendSuccess
in the Succeed
classguardEndOfStream()
in StringStream
These resulted in a small speedup of the JSON Parser example.
+-----------+----------------------+-----+------+-----+------------+-------------+---------+
| benchmark | subject | set | revs | its | mem_peak | mode | rstdev |
+-----------+----------------------+-----+------+-----+------------+-------------+---------+
| JSONBench | bench_json_encode | 0 | 5 | 3 | 1,710,104b | 7.617μs | ±46.68% |
| JSONBench | bench_Parsica_JSON | 0 | 5 | 3 | 2,168,552b | 6,539.741μs | ±5.28% |
| JSONBench | bench_basemax_jpophp | 0 | 5 | 3 | 1,878,760b | 633.373μs | ±24.11% |
+-----------+----------------------+-----+------+-----+------------+-------------+---------+
+-----------+----------------------+-----+------+-----+------------+-------------+----------+
| benchmark | subject | set | revs | its | mem_peak | mode | rstdev |
+-----------+----------------------+-----+------+-----+------------+-------------+----------+
| JSONBench | bench_json_encode | 0 | 5 | 3 | 1,710,200b | 5.978μs | ±29.48% |
| JSONBench | bench_Parsica_JSON | 0 | 5 | 3 | 2,160,184b | 6,256.940μs | ±8.37% |
| JSONBench | bench_basemax_jpophp | 0 | 5 | 3 | 1,878,856b | 633.060μs | ±3.00% |
+-----------+----------------------+-----+------+-----+------------+-------------+----------+
strlen()
instead of mb_strlen()
to find EOFmany()
400% faster by using while instead of recurseThese resulted in a x2 speedup of the JSON Parser example.
Subjects: 3, Assertions: 0, Failures: 0, Errors: 0
suite: 1346290d9eebcc1aade3cbbff1b5f7017d112890, date: 2021-03-20, stime: 10:39:04
+-----------+----------------------+-----+------+-----+------------+--------------+--------------+--------------+--------------+-----------+--------+-----------+
| benchmark | subject | set | revs | its | mem_peak | best | mean | mode | worst | stdev | rstdev | diff |
+-----------+----------------------+-----+------+-----+------------+--------------+--------------+--------------+--------------+-----------+--------+-----------+
| JSONBench | bench_json_encode | 0 | 5 | 3 | 1,709,704b | 5.200μs | 5.467μs | 5.576μs | 5.600μs | 0.189μs | 3.45% | 1.00x |
| JSONBench | bench_Parsica_JSON | 0 | 5 | 3 | 2,195,024b | 10,658.600μs | 11,040.933μs | 10,757.806μs | 11,737.200μs | 493.126μs | 4.47% | 2,019.68x |
| JSONBench | bench_basemax_jpophp | 0 | 5 | 3 | 1,878,400b | 520.600μs | 637.733μs | 571.178μs | 811.000μs | 125.023μs | 19.60% | 116.66x |
+-----------+----------------------+-----+------+-----+------------+--------------+--------------+--------------+--------------+-----------+--------+-----------+
Subjects: 3, Assertions: 0, Failures: 0, Errors: 0
suite: 1346290eaf4ce8a0eb08bdddb54b69d2020be280, date: 2021-03-20, stime: 10:38:11
+-----------+----------------------+-----+------+-----+------------+-------------+-------------+-------------+-------------+-----------+--------+-----------+
| benchmark | subject | set | revs | its | mem_peak | best | mean | mode | worst | stdev | rstdev | diff |
+-----------+----------------------+-----+------+-----+------------+-------------+-------------+-------------+-------------+-----------+--------+-----------+
| JSONBench | bench_json_encode | 0 | 5 | 3 | 1,710,104b | 5.200μs | 5.333μs | 5.388μs | 5.400μs | 0.094μs | 1.77% | 1.00x |
| JSONBench | bench_Parsica_JSON | 0 | 5 | 3 | 2,168,552b | 5,383.000μs | 5,505.267μs | 5,431.564μs | 5,693.200μs | 134.883μs | 2.45% | 1,032.24x |
| JSONBench | bench_basemax_jpophp | 0 | 5 | 3 | 1,878,760b | 541.800μs | 725.133μs | 794.251μs | 853.400μs | 133.036μs | 18.35% | 135.96x |
+-----------+----------------------+-----+------+-----+------------+-------------+-------------+-------------+-------------+-----------+--------+-----------+
As part of the move to the parsica-php organisation, we decided to move the namespace to Parsica. Sorry if this would break your integration with Parsica! We did a minor version update because we don't want to go to a v1.x yet, but want to let you know that there has been a backwards compatibility break.
Fix your composer.json file and make sure you use the correct namespace in your code!
FEATURE: Expression parser. Using a simple interface, you can define parsers for recursive expression languages with different associativity and operators precedence.
FEATURE: Lots of fine grained Psalm type annotations. You should now be able to use Psalm in your own parsers, and get useful warnings if you do something wrong.
FEATURE: Parser#thenEof()
. Make sure the parser reached the end of the input by appending ->thenEof()
IMPROVEMENT: Many error messages are now more clear
PERFORMANCE: 10x improvement by generating the error messages lazily. Still a long way before Parsica is performant enough for large scale production usage, but this gives us confidence there's still many low hanging fruits.
DOCS: New tutorial about writing expression parsers
DOCS: New tutorial about dealing with whitespace
DOCS: Added lots of missing API docs
DOCS: Improved the tutorial on types
DOCS: New examples in tests/Examples
for expressions and an quick demo of an Excel formula parser
BREAKING: The Fail
class no longer does double duty as an exception. Instead, use ParseResult#throw()
, which throws a ParserHasFailed
exception.
BREAKING: Parser#recurse()
doesn't return the parser anymore, and instead returns void.
BREAKING: Parser#construct(ClassName)
is removed in favour of map(fn($v) => new ClassName))
... and lots of minor improvements
There's always something you discover right after you tag a release :-D
FEATURE lookahead() combinator DOCS: A large chunk of API documentation was not being rendered FIX: PHPUnit max nesting level was insufficient IMPROVEMENT: reimplemented applicative functors without monads FIX: #10 fail when using applicative functors when the output of the first parser is not a callable
FEATURE: sepBy2 combinator
FEATURE: apply function
FEATURE: integer parser
FEATURE: float parser
FEATURE: Parser#and() is an alias for append()
FEATURE: Parser#then() is an alias for sequence()
DOCS: Misc improvements and additions
BREAKING: removed some helper functions from the JSON parser
BREAKING nicer API for ParserAssertions
FEATURE: Parser errors are now a lot more useful. They show the line and column position where the parser expected something else. Labels are also vastly improved.
BREAKING / FEATURE: Inputs must now be of type Stream. Right now it only comes with a StringStream implementation, but in the future this will allow us to handle different types of input, such as an actual stream.
FEATURE: The new emit() combinator can be used for side effects and for emitting parser events
FEATURE: We implemented a fully compliant JSON parser
FEATURE: zeroOrMore() combinator
FEATURE: map() is now also a function
BREAKING: Parser tracks label so that combinators can combine them into smarter error messages
BREAKING: Removed ParseResult::alternative
BREAKING: When appending with null, the null gets ignored
BREAKING: optional and success now output null instead of empty string
BREAKING: renamed success() and failure() to succeed() and fail()
BREAKING: skipWhile outputs null instead of empty string
BREAKING: skipWhile1 outputs null instead of empty string
...and lots of improvements and additions to the documentation, test coverage, code organisation, Psalm types, ...