{"id":13395741,"url":"https://github.com/petitparser/dart-petitparser","last_synced_at":"2025-05-14T03:07:19.028Z","repository":{"id":3410489,"uuid":"4460928","full_name":"petitparser/dart-petitparser","owner":"petitparser","description":"Dynamic parser combinators in Dart.","archived":false,"fork":false,"pushed_at":"2025-01-19T17:25:48.000Z","size":2456,"stargazers_count":468,"open_issues_count":8,"forks_count":49,"subscribers_count":10,"default_branch":"main","last_synced_at":"2025-03-31T14:14:12.386Z","etag":null,"topics":["dart","flutter","grammar","parser","parser-combinator","parser-framework","parser-library","parsing-expression-grammar","petitparser"],"latest_commit_sha":null,"homepage":"https://pub.dartlang.org/packages/petitparser","language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/petitparser.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":"AUTHORS","dei":null,"publiccode":null,"codemeta":null}},"created_at":"2012-05-27T09:28:10.000Z","updated_at":"2025-03-22T03:43:04.000Z","dependencies_parsed_at":"2023-02-18T22:31:25.309Z","dependency_job_id":"492be3dd-a72d-4dc1-a292-fe7725768f9b","html_url":"https://github.com/petitparser/dart-petitparser","commit_stats":{"total_commits":1234,"total_committers":19,"mean_commits":64.94736842105263,"dds":"0.034846029173419724","last_synced_commit":"c8670784700c5be139a175006724115748b35d46"},"previous_names":[],"tags_count":176,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/petitparser%2Fdart-petitparser","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/petitparser%2Fdart-petitparser/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/petitparser%2Fdart-petitparser/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/petitparser%2Fdart-petitparser/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/petitparser","download_url":"https://codeload.github.com/petitparser/dart-petitparser/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248261928,"owners_count":21074225,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dart","flutter","grammar","parser","parser-combinator","parser-framework","parser-library","parsing-expression-grammar","petitparser"],"created_at":"2024-07-30T18:00:30.263Z","updated_at":"2025-04-10T17:17:50.722Z","avatar_url":"https://github.com/petitparser.png","language":"Dart","readme":"PetitParser for Dart\n====================\n\n[![Pub Package](https://img.shields.io/pub/v/petitparser.svg)](https://pub.dev/packages/petitparser)\n[![Build Status](https://github.com/petitparser/dart-petitparser/actions/workflows/dart.yml/badge.svg?branch=main)](https://github.com/petitparser/dart-petitparser/actions)\n[![Code Coverage](https://codecov.io/gh/petitparser/dart-petitparser/branch/main/graph/badge.svg?token=2yW74MVgun)](https://codecov.io/gh/petitparser/dart-petitparser)\n[![GitHub Issues](https://img.shields.io/github/issues/petitparser/dart-petitparser.svg)](https://github.com/petitparser/dart-petitparser/issues)\n[![GitHub Forks](https://img.shields.io/github/forks/petitparser/dart-petitparser.svg)](https://github.com/petitparser/dart-petitparser/network)\n[![GitHub Stars](https://img.shields.io/github/stars/petitparser/dart-petitparser.svg)](https://github.com/petitparser/dart-petitparser/stargazers)\n[![GitHub License](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/petitparser/dart-petitparser/main/LICENSE)\n\nGrammars for programming languages are traditionally specified statically. They are hard to compose and reuse due to ambiguities that inevitably arise. PetitParser combines ideas from [scannerless parsing](https://en.wikipedia.org/wiki/Scannerless_parsing), [parser combinators](https://en.wikipedia.org/wiki/Parser_combinator), [parsing expression grammars](https://en.wikipedia.org/wiki/Parsing_expression_grammar) (PEG) and packrat parsers to model grammars and parsers as objects that can be reconfigured dynamically.\n\nThis library is open source, stable and well tested. Development happens on [GitHub](https://github.com/petitparser/dart-petitparser). Feel free to report issues or create a pull-request there. General questions are best asked on [StackOverflow](https://stackoverflow.com/questions/tagged/petitparser+dart).\n\nThe package is hosted on [dart packages](https://pub.dev/packages/petitparser). Up-to-date [API documentation](https://pub.dev/documentation/petitparser/latest/) is created with every release.\n\n\nTutorial\n--------\n\nBelow are step-by-step instructions of how to write your first parser. More elaborate examples (JSON parser, LISP parser and evaluator, Prolog parser and evaluator, etc.) are included in the [example repository](https://github.com/petitparser/dart-petitparser-examples). Try out the running demos at [petitparser.github.io](https://petitparser.github.io/).\n\n\n### Installation\n\nFollow the installation instructions on [dart packages](https://pub.dev/packages/petitparser/install).\n\nImport the package into your Dart code using:\n\n```dart\nimport 'package:petitparser/petitparser.dart';\n```\n\nIt is also possible to more selectively import only certain parts of this library, i.e. `package:petitparser/core.dart` and `package:petitparser/parser.dart` for core infrastructure and the basic parsers.\n\n\u003e [!IMPORTANT]\n\u003e This library makes extensive use of [static extension methods](https://dart.dev/guides/language/extension-methods). If you [import the library](https://dart.dev/guides/language/language-tour#using-libraries) using a _library prefix_ or only _selectively show classes_ you might miss some of the functionality.\n\n### Writing a Simple Grammar\n\nWriting grammars with PetitParser is as simple as writing Dart code. For example, the following code creates a parser that can read identifiers (a letter followed by zero or more letter or digits):\n\n```dart\nfinal id = letter() \u0026 (letter() | digit()).star();  // (0): Parser\u003cList\u003cdynamic\u003e\u003e\n```\n\nIf you inspect the object `id` in the debugger, you'll notice that the code above builds a tree of parser objects:\n\n- SequenceParser: This parser accepts the sequence of its child parsers.\n  - SingleCharacterParser: This parser accepts a single letter.\n  - PossessiveRepeatingParser: This parser accepts zero or more times its child parsers.\n    - ChoiceParser: This parser accepts the first of its succeeding child parsers, or otherwise fails.\n      - SingleCharacterParser: This parser accepts a single letter.\n      - SingleCharacterParser: This parser accepts a single digit.\n\nThe operators `\u0026` and `|` are overloaded and create a sequence and a choice parser respectively. In some contexts it might be more convenient to use chained function calls, or the extension methods on lists. All of the following parsers accept the same inputs as the parser above:\n\n```dart\nfinal id1 = letter().seq(letter().or(digit()).star());  // (1): Parser\u003cList\u003cdynamic\u003e\u003e\nfinal id2 = [letter(), [letter(), digit()].toChoiceParser().star()].toSequenceParser();  // (2): Parser\u003cList\u003cObject\u003e\u003e\nfinal id3 = seq2(letter(), [letter(), digit()].toChoiceParser().star());  // (3): Parser\u003c(String, List\u003cString\u003e)\u003e\n```\n\n\u003e [!NOTE]  \n\u003e The inferred type of the 3 parsers is not equivalent: Due to [github.com/dart-lang/language/issues/1557](https://github.com/dart-lang/language/issues/1557) the inferred type of sequence and choice parsers created with operators (0) or chained function calls (1) is `Parser\u003cdynamic\u003e`. The parser built from lists (2) provides the most generic type, `List\u003cObject\u003e` in this example. The last variation (3) is the only one that doesn't lose type information and produces a [record](https://dart.dev/language/records) (tuple) with two typed elements `String` and `List\u003cString\u003e`.\n\n### Parsing Some Input\n\nTo actually consume an input string we use the method `Parser.parse`:\n\n```dart\nfinal result1 = id.parse('yeah');\nfinal result2 = id.parse('f12');\n```\n\nThe method `Parser.parse` returns a [`Result`](https://pub.dev/documentation/petitparser/latest/petitparser/Result-class.html), which is either an instance of [`Success`](https://pub.dev/documentation/petitparser/latest/petitparser/Success-class.html) or [`Failure`](https://pub.dev/documentation/petitparser/latest/petitparser/Failure-class.html). In both examples we are successful and can retrieve the resulting value using `Success.value`:\n\n```dart\nprint(result1.value);                   // ['y', ['e', 'a', 'h']]\nprint(result2.value);                   // ['f', ['1', '2']]\n```\n\nWhile it seems odd to get these nested arrays with characters as a return value, this is the default decomposition of the input into a parse-tree. We'll see in a while how that can be customized.\n\nIf we try to parse something invalid we get an instance of `Failure` and we can retrieve a descriptive error message using `Failure.message`:\n\n```dart\nfinal result3 = id.parse('123');\nprint(result3.message);                 // 'letter expected'\nprint(result3.position);                // 0\n```\n\nTrying to retrieve result by calling `Failure.value` would throw the exception [`ParserError`](https://pub.dev/documentation/petitparser/latest/petitparser/ParserException-class.html). [Pattern matching](https://dart.dev/language/patterns) can be used to decide if the parse result was a success or a failure:\n\n```dart\nswitch (id.parse(input)) {\n  case Success(value: final value):\n    print('Success: $value');\n  case Failure(message: final message, position: final position):\n    print('Failure at $position: $message');\n}\n```\n\nIf you are only interested if a given string is valid you can use the helper method `Parser.accept`:\n\n```dart\nprint(id.accept('foo'));                // true\nprint(id.accept('123'));                // false\n```\n\n### Different Kinds of Parsers\n\nPetitParser provides a large set of ready-made parser that you can compose to consume and transform arbitrarily complex languages. \n\n#### Terminal Parsers\n\nTerminal parsers are the simplest. We've already seen a few of those:\n\n- [`any()`](https://pub.dev/documentation/petitparser/latest/petitparser/any.html) parses any character.\n- [`anyOf('abc')`](https://pub.dev/documentation/petitparser/latest/petitparser/anyOf.html) parses any of the characters, *a*, *b* or *c*.\n- [`char('a')`](https://pub.dev/documentation/petitparser/latest/petitparser/char.html) (or [`'a'.toParser()`](https://pub.dev/documentation/petitparser/latest/petitparser/PredicateStringExtension/toParser.html)) parses the character *a*.\n- [`digit()`](https://pub.dev/documentation/petitparser/latest/petitparser/digit.html) parses a single digit from *0* to *9*.\n- [`letter()`](https://pub.dev/documentation/petitparser/latest/petitparser/letter.html) parses a single letter from *a* to *z* and *A* to *Z*.\n- [`noneOf('abc')`](https://pub.dev/documentation/petitparser/latest/petitparser/noneOf.html) parses none of the characters, i.e. *d*, *1*, or *A*.\n- [`newline()`](https://pub.dev/documentation/petitparser/latest/petitparser/newline.html) parses a newline character sequence, i.e. *LF* (Unix) or *CR+LF* (Windows).\n- [`pattern('a-f')`](https://pub.dev/documentation/petitparser/latest/petitparser/pattern.html) (or [`'a-f'.toParser(isPattern: true)`](https://pub.dev/documentation/petitparser/latest/petitparser/PredicateStringExtension/toParser.html)) parses a single character between *a* and *f*.\n- [`string('abc')`](https://pub.dev/documentation/petitparser/latest/petitparser/string.html) (or [`'abc'.toParser()`](https://pub.dev/documentation/petitparser/latest/petitparser/PredicateStringExtension/toParser.html)) parses the string *abc*.\n- [`whitespace()`](https://pub.dev/documentation/petitparser/latest/petitparser/whitespace.html) parses a whitespace character, i.e. *␣* or *↦*.\n- [`word()`](https://pub.dev/documentation/petitparser/latest/petitparser/word.html) parses a single letter, digit, or the underscore character.\n\nBy default all parsers use an automatically generated descriptive error message, match case-sensitive, and work on 16-bit UTF-16 code units. To change this default behavior use the named arguments (where appropriate):\n\n- `message: 'expected a special character'` to use a custom error message,\n- `ignoreCase: true` to accept both lower- and uppercase variations, and\n- `unicode: true` to decode surrogate pairs and read Unicode code-points.\n\n#### Combinator Parsers\n\nThe next set of parsers are used to combine other parsers together:\n\n- [`p1 \u0026 p2`](https://pub.dev/documentation/petitparser/latest/petitparser/SequenceParserExtension/operator_bitwise_and.html), [`p1.seq(p2)`](https://pub.dev/documentation/petitparser/latest/petitparser/SequenceParserExtension/seq.html), [`[p1, p2].toSequenceParser()`](https://pub.dev/documentation/petitparser/latest/petitparser/SequenceIterableExtension/toSequenceParser.html), [`seq2(p1, p2)`](https://pub.dev/documentation/petitparser/latest/petitparser/seq2.html) or [`(p1, p2).toSequenceParser()`](https://pub.dev/documentation/petitparser/latest/petitparser/RecordOfParsersExtension2/toSequenceParser.html) parse *p1* followed by *p2* (sequence). The first two produce a result of type `List\u003cdynamic\u003e`, the third one a `List\u003cP1 \u0026 P2\u003e`, and the last two a strictly typed record type `(P1, P2)`.\n- [`p1 | p2`](https://pub.dev/documentation/petitparser/latest/petitparser/ChoiceParserExtension/operator_bitwise_or.html), [`p1.or(p2)`](https://pub.dev/documentation/petitparser/latest/petitparser/ChoiceParserExtension/or.html), or [`[p1, p2].toChoiceParser()`](https://pub.dev/documentation/petitparser/latest/petitparser/ChoiceIterableExtension/toChoiceParser.html) parse *p1*, if that doesn't work parse *p2* (ordered choice). The first two produce a result of type `dynamic`, the last one a result of type `P1 \u0026 P2`.\n\nThe following parsers [repeat](https://pub.dev/documentation/petitparser/latest/petitparser/PossessiveRepeatingParserExtension.html) another parser a configured amount of times, and produce a list of parsed results. Check the documentation for other repeaters that are [lazy](https://pub.dev/documentation/petitparser/latest/petitparser/LazyRepeatingParserExtension.html) or [greedy](https://pub.dev/documentation/petitparser/latest/petitparser/GreedyRepeatingParserExtension.html), or that can handle [separators](https://pub.dev/documentation/petitparser/latest/petitparser/SeparatedRepeatingParserExtension.html).\n\n- [`p.star()`](https://pub.dev/documentation/petitparser/latest/petitparser/PossessiveRepeatingParserExtension/star.html) parses *p* zero or more times.\n- [`p.plus()`](https://pub.dev/documentation/petitparser/latest/petitparser/PossessiveRepeatingParserExtension/plus.html) parses *p* one or more times.\n- [`p.times(n)`](https://pub.dev/documentation/petitparser/latest/petitparser/PossessiveRepeatingParserExtension/times.html) parsers *p* exactly _n_ times.\n- [`p.repeat(n, m)`](https://pub.dev/documentation/petitparser/latest/petitparser/PossessiveRepeatingParserExtension/repeat.html) parses *p* between _n_ and _m_ times.\n\nA variation of the parsers above is the optional operator, it produces the value of *p* or *null*.\n\n- [`p.optional()`](https://pub.dev/documentation/petitparser/latest/petitparser/OptionalParserExtension/optional.html) parses *p* and returns its result, otherwise returns `null`.\n- [`p.optionalWith(v)`](https://pub.dev/documentation/petitparser/latest/petitparser/OptionalParserExtension/optionalWith.html) parses *p* and returns its result, otherwise returns the argument _v_.\n\nMore complicated combinators that can come in handy at times are:\n\n- [`p.and()`](https://pub.dev/documentation/petitparser/latest/petitparser/AndParserExtension/and.html) parses *p*, but does not consume its input.\n- [`p.not()`](https://pub.dev/documentation/petitparser/latest/petitparser/NotParserExtension/not.html) parses *p* and succeeds when p fails, but does not consume its input.\n- [`p.end()`](https://pub.dev/documentation/petitparser/latest/petitparser/EndOfInputParserExtension/end.html) parses *p* and succeeds at the end of the input.\n\n#### Transforming Parsers\n\nThe last type of parsers are actions or transformations we can use as follows:\n\n- [`p.map((value) =\u003e ...)`](https://pub.dev/documentation/petitparser/latest/petitparser/MapParserExtension/map.html) performs a transformation using the provided callback on the result of *p*.\n- [`p.where((value) =\u003e ...)`](https://pub.dev/documentation/petitparser/latest/petitparser/WhereParserExtension/where.html) fails the parser *p* if its result does not satisfy the predicate.\n- [`p.pick(n)`](https://pub.dev/documentation/petitparser/latest/petitparser/PickParserExtension/pick.html) returns the *n*-th element of the list *p* returns.\n- [`p.cast\u003cT\u003e()`](https://pub.dev/documentation/petitparser/latest/petitparser/CastParserExtension/cast.html) casts the result of *p* to the type `T`.\n- [`p.flatten()`](https://pub.dev/documentation/petitparser/latest/petitparser/FlattenParserExtension/flatten.html) creates a string from the consumed input of *p*.\n- [`p.token()`](https://pub.dev/documentation/petitparser/latest/petitparser/TokenParserExtension/token.html) creates a token that encapsulates the begin and end position, and result of *p*.\n- [`p.trim()`](https://pub.dev/documentation/petitparser/latest/petitparser/TrimmingParserExtension/trim.html) trims whitespaces before and after *p*.\n- [`p.skip(before: p1, after: p2)`](https://pub.dev/documentation/petitparser/latest/petitparser/SkipParserExtension/skip.html) consumes *p1*, *p*, and *p2* in sequence, but only returns the result of *p*.\n\n\u003e [!TIP]\n\u003e Various other parsers for more specific use-cases are available, browse the subclasses and extensions of the [Parser](https://pub.dev/documentation/petitparser/latest/core/Parser-class.html) class.\n\nTo return a string of the parsed identifier, we can modify our parser like this:\n\n```dart\nfinal id = (letter() \u0026 pattern('a-zA-Z0-9').star()).flatten();\n```\n\nTo conveniently find all matches in a given input string you can use `Parser.allMatches`:\n\n```dart\nfinal matches = id.allMatches('foo 123 bar4');\nprint(matches);                         // ['foo', 'bar4']\n```\n\n\n### Writing a More Complicated Grammar\n\nNow we are able to write a more complicated grammar for evaluating simple arithmetic expressions. Within a file we start with the grammar for an integer:\n\n```dart\nfinal number = digit().plus().flatten().trim().map(int.parse);\n```\n\nThen we define the productions for addition and multiplication in order of precedence. Note that we instantiate the productions with undefined parsers upfront, because they recursively refer to each other. Later on we can resolve this recursion by setting their reference:\n\n```dart\nfinal term = undefined();\nfinal prod = undefined();\nfinal prim = undefined();\n\nfinal add = (prod \u0026 char('+').trim() \u0026 term)\n    .map((values) =\u003e values[0] + values[2]);\nterm.set(add | prod);\n\nfinal mul = (prim \u0026 char('*').trim() \u0026 prod)\n    .map((values) =\u003e values[0] * values[2]);\nprod.set(mul | prim);\n\nfinal parens = (char('(').trim() \u0026 term \u0026 char(')').trim())\n    .map((values) =\u003e values[1]);\nfinal number = digit().plus().flatten().trim().map(int.parse);\nprim.set(parens | number);\n```\n\nTo make sure our parser consumes all input we wrap it with the [`end()`](https://pub.dev/documentation/petitparser/latest/petitparser/EndOfInputParserExtension/end.html) parser in the start production:\n\n```dart\nfinal parser = term.end();\n```\n\nThat's it, now we can test our parser and evaluator:\n\n```dart\nparser.parse('1 + 2 * 3');              // 7\nparser.parse('(1 + 2) * 3');            // 9\n```\n\n\n### Using Parser References\n\nDefining and reusing complex grammars can be cumbersome, particularly if the grammar is large and recursive (such as the example above). PetitParser provides building blocks to conveniently define and build complex grammars with possibly hundreds of productions.\n\nTo create a new grammar definition subclass [`GrammarDefinition`](https://pub.dev/documentation/petitparser/latest/petitparser/GrammarDefinition-class.html). In our case we call the class `ExpressionDefinition`. For every production create a new method returning the primitive parser defining it. The method called `start` is supposed to return the start production of the grammar. To refer to a production defined in the same definition use [`ref(Function)`](https://pub.dev/documentation/petitparser/latest/petitparser/ref.html) with the function reference as the argument.\n\n```dart\nclass ExpressionDefinition extends GrammarDefinition {\n  Parser start() =\u003e ref(term).end();\n\n  Parser term() =\u003e ref(add) | ref(prod);\n  Parser add() =\u003e ref(prod) \u0026 char('+').trim() \u0026 ref(term);\n\n  Parser prod() =\u003e ref(mul) | ref(prim);\n  Parser mul() =\u003e ref(prim) \u0026 char('*').trim() \u0026 ref(prod);\n\n  Parser prim() =\u003e ref(parens) | ref(number);\n  Parser parens() =\u003e char('(').trim() \u0026 ref(term) \u0026 char(')').trim();\n\n  Parser number() =\u003e digit().plus().flatten().trim();\n}\n```\n\nTo create a parser with all the references correctly resolved call [`build()`](https://pub.dev/documentation/petitparser/latest/petitparser/GrammarDefinition/build.html).\n\n```dart\nfinal definition = ExpressionDefinition();\nfinal parser = definition.build();\nparser.parse('1 + 2 * 3');              // ['1', '+', ['2', '+', '3']]\n```\n\nAgain, since this is plain Dart, common code refactorings such as renaming a production updates all references correctly. Also code navigation and code completion works as expected.\n\n\u003e [!TIP] \n\u003e [`ref(Function)`](https://pub.dev/documentation/petitparser/latest/petitparser/ref.html) is not limited to subclasses of [`GrammarDefinition`](https://pub.dev/documentation/petitparser/latest/petitparser/GrammarDefinition-class.html), it can be used from anywhere in Dart. To build the resulting parser use [`resolve(Parser)`](https://pub.dev/documentation/petitparser/latest/petitparser/resolve.html) on the root node of the grammar.\n\n\u003e [!TIP]\n\u003e [`ref`](https://pub.dev/documentation/petitparser/latest/petitparser/ref1.html) takes positional arguments to parametrize the created parser, if the referenced function takes arguments. While `ref` supports an arbitrary amount of arguments, it can neither infer nor check return or argument types at compile time. The variations [`ref0`](https://pub.dev/documentation/petitparser/latest/petitparser/ref0.html), [`ref1`](https://pub.dev/documentation/petitparser/latest/petitparser/ref1.html), [`ref2`](https://pub.dev/documentation/petitparser/latest/petitparser/ref2.html), ... solve this problem, but require you to specify the number of arguments.\n\nTo attach custom production actions you might want to further subclass your grammar definition and override overriding the necessary productions defined in the superclass:\n\n```dart\nclass EvaluatorDefinition extends ExpressionDefinition {\n  Parser add() =\u003e super.add().map((values) =\u003e values[0] + values[2]);\n  Parser mul() =\u003e super.mul().map((values) =\u003e values[0] * values[2]);\n  Parser parens() =\u003e super.parens().castList\u003cnum\u003e().pick(1);\n  Parser number() =\u003e super.number().map((value) =\u003e int.parse(value));\n}\n```\n\nSimilarly, build the evaluator parser like so:\n\n```dart\nfinal definition = EvaluatorDefinition();\nfinal parser = definition.build();\nparser.parse('1 + 2 * 3');              // 7\n```\n\n\u003e [!TIP]\n\u003e Subclassing of definitions only works well, if you keep your parsers dynamic like in the example above (`Parser` or `Parser\u003cdynamic\u003e`). While this might increase reusability of your parser definitions, it might also increase your code size and come with extra run-time cost.\n\nTo use just a part of the parser you can specify the start production when building. For example, to reuse the number parser one would write:\n\n```dart\nfinal definition = EvaluatorDefinition();\nfinal parser = definition.build(start: definition.number);\nparser.parse('42');                     // 42\n```\n\nCheck out [the documentation](https://pub.dev/documentation/petitparser/latest/petitparser/GrammarDefinition-class.html) for more examples.\n\n\n### Using the Expression Builder\n\nWriting such expression parsers is pretty common and can be tricky to get right. To simplify things, PetitParser comes with a builder that can help you to define such grammars easily. It supports the definition of operator precedence; and prefix, postfix, left- and right-associative operators.\n\nThe following code creates the empty [`ExpressionBuilder`](https://pub.dev/documentation/petitparser/latest/petitparser/ExpressionBuilder-class.html) producing values of type `num`:\n\n```dart\nfinal builder = ExpressionBuilder\u003cnum\u003e();\n```\n\nEvery `ExpressionBuilder` needs to define at least one primitive type to parse. In this example these are the literal numbers. This time we accept floating-point numbers, not just integers. The mapping function converts the string input into an actual number.\n\n```dart\nbuilder.primitive(digit()\n    .plus()\n    .seq(char('.').seq(digit().plus()).optional())\n    .flatten()\n    .trim()\n    .map(num.parse));\n```\n\nThen we define the operator-groups in descending precedence. The highest precedence have parentheses. The mapping function receives both the opening parenthesis, the value, and the closing parenthesis as arguments:\n\n```dart\nbuilder.group().wrapper(\n    char('(').trim(), char(')').trim(), (left, value, right) =\u003e value);\n```\n\nThen come the normal arithmetic operators. We are using [cascade notation](https://dart.dev/guides/language/language-tour#cascade-notation) to define multiple operators on the same precedence-group. The mapping functions receive both, the terms and the parsed operator in the order they appear in the parsed input:\n\n```dart\n// Negation is a prefix operator.\nbuilder.group().prefix(char('-').trim(), (operator, value) =\u003e -value);\n\n// Power is right-associative.\nbuilder.group().right(\n    char('^').trim(), (left, operator, right) =\u003e math.pow(left, right));\n\n// Multiplication and addition are left-associative, multiplication has\n// higher priority than addition.\nbuilder.group()\n  ..left(char('*').trim(), (left, operator, right) =\u003e left * right)\n  ..left(char('/').trim(), (left, operator, right) =\u003e left / right);\nbuilder.group()\n  ..left(char('+').trim(), (left, operator, right) =\u003e left + right)\n  ..left(char('-').trim(), (left, operator, right) =\u003e left - right);\n```\n\nFinally, we can build the parser:\n\n```dart\nfinal parser = builder.build().end();\n```\n\nAfter executing the above code we get an efficient parser that correctly evaluates expressions like:\n\n```dart\nparser.parse('-8');                     // -8\nparser.parse('1+2*3');                  // 7\nparser.parse('1*2+3');                  // 5\nparser.parse('8/4/2');                  // 1\nparser.parse('2^2^3');                  // 256\n```\n\nCheck out [the documentation](https://pub.dev/documentation/petitparser/latest/petitparser/ExpressionBuilder-class.html) for more examples.\n\n\n### Testing your Grammars\n\nReal world grammars are typically large and complicated. PetitParser's architecture allows one to break down a grammar into manageable pieces, and develop and test each part individually before assembling the complete system.\n\nStart the development and testing of a new grammar at the leaves (or tokens): write the parsers that read numbers, strings, and variables first; then continue with the expressions that can be built from these literals; and finally conclude with control structures, classes and other overarching constructs. At each step add tests and assert that the individual parsers behave as desired, so that you can be sure they also work when composing them to a larger grammar later.\n\nAccessing and testing individual productions is simple: If you organize your grammar in your own code, make sure to expose parts of the grammar individually. If you use a [`GrammarDefinition`](https://pub.dev/documentation/petitparser/latest/petitparser/GrammarDefinition-class.html), you can build individual productions using the [`buildFrom`](https://pub.dev/documentation/petitparser/latest/petitparser/GrammarDefinition/buildFrom.html) method. For example, to test the number production of the `EvaluatorDefinition` from above you would write:\n\n```dart\ntest('number parsing', () {\n  final definition = EvaluatorDefinition();\n  final parser = definition.buildFrom(definition.number);\n  expect(parser.parse('42').value, 42);\n});\n```\n\nAdditionally, PetitParser provides a Linter that comes with a collection of predefined rules that can help you find common bugs or inefficient constructs in your code. Among other things, the analyzer detects infinite loops, unreachable parsers, repeated parsers, and unresolved parsers. For an up-to-date list of all available rules check the implementation at [linter_rules.dart](https://github.com/petitparser/dart-petitparser/blob/main/lib/src/reflection/internal/linter_rules.dart).\n\nTo run the linter as part of your tests include the package `petitparser/reflection.dart`, call the `linter` function with the starting parser of your grammar, and assert that there are no findings. With the `EvaluatorDefinition` from above one would write:\n\n```dart\ntest('detect common problems', () {\n  final definition = EvaluatorDefinition();\n  final parser = definition.build();\n  expect(linter(parser), isEmpty);\n});\n```\n\nTo exclude certain rules from being reported you can exclude certain rules, i.e. `linter(parser, excludedRules: {'Nested choice'})`.\n\nCheck out the extensive test suites of [PetitParser](https://github.com/petitparser/dart-petitparser/blob/main/test) and [PetitParser Examples](https://github.com/petitparser/dart-petitparser-examples/blob/main/test) for examples on testing.\n\n\n### Debugging your Grammars\n\nSometimes parsers might not behave the way you expect them to. The first step should always be to come up with a small reproducible example. If this doesn't already solve the problem, PetitParser comes with a set of built-in tools that can help you understand what is going on.\n\nThe function [`trace(Parser)`](https://pub.dev/documentation/petitparser/latest/debug/trace.html) transforms your grammar so that each parser prints its activation and results:\n\n```dart\nfinal parser = letter() \u0026 word().star();\ntrace(parser).parse('f1');\n```\n\nThe above snippet produces the following output: \n\n```\nSequenceParser\u003cdynamic\u003e\n  SingleCharacterParser[letter expected]\n  Success\u003cString\u003e[1:2]: f\n  PossessiveRepeatingParser\u003cString\u003e[0..*]\n    SingleCharacterParser[letter or digit expected]\n    Success\u003cString\u003e[1:3]: 1\n    SingleCharacterParser[letter or digit expected]\n    Failure[1:3]: letter or digit expected\n  Success\u003cList\u003cString\u003e\u003e[1:3]: [1]\nSuccess\u003cList\u003cdynamic\u003e\u003e[1:3]: [f, [1]]\n```\n\nIndentation signifies the activation of a parser object. Reverse indentation signifies the returning of a parse result either with a success or failure context.\n\nSimilarly, the function [`profile(Parser)`](https://pub.dev/documentation/petitparser/latest/debug/profile.html) produces a table of activation counts and run-time tallies of each parser. And, [`progress(Parser)`](https://pub.dev/documentation/petitparser/latest/debug/progress.html) visualizes how the parsers process (and possibly backtrack) through your input. Both tools can help to understand and optimize the performance characteristics of your parsers.\n\n\nMisc\n----\n\n[petitparser.github.io](https://petitparser.github.io/) contains up-to-date information about PetitParser and ports to other languages.\n\n\n### Examples\n\nThe package comes with a large collection of example grammars and language experiments ready to explore:\n\n- [CSV](https://github.com/petitparser/dart-petitparser-examples/blob/main/lib/csv.dart) contains a simple Comma-separated values (CSV) parser.\n- [Dart](https://github.com/petitparser/dart-petitparser-examples/tree/main/lib/src/dart) contains an experimental Dart grammar.\n- [JSON](https://github.com/petitparser/dart-petitparser-examples/tree/main/lib/src/json) contains a complete JSON grammar and parser.\n- [Lisp](https://github.com/petitparser/dart-petitparser-examples/tree/main/lib/src/lisp) contains a complete LISP grammar, parser and evaluator.\n- [Math](https://github.com/petitparser/dart-petitparser-examples/tree/main/lib/src/math) contains an mathematical expression parser and evaluator.\n- [Pascal](https://github.com/petitparser/dart-petitparser-examples/tree/main/lib/src/pascal) contains an experimental Pascal grammar.\n- [Prolog](https://github.com/petitparser/dart-petitparser-examples/tree/main/lib/src/prolog) contains a basic Prolog grammar, parser and evaluator.\n- [Smalltalk](https://github.com/petitparser/dart-petitparser-examples/tree/main/lib/src/smalltalk) contains a complete Smalltalk grammar.\n- [Uri](https://github.com/petitparser/dart-petitparser-examples/blob/main/lib/uri.dart) contains a simple URI parser.\n\nFurthermore, there are [numerous open source projects](https://pub.dev/packages?q=dependency:petitparser) using PetitParser:\n\n- [apollovm](https://pub.dev/packages/apollovm), a simple VM that can parse, run and generate basic Dart and Java8 code.\n- [equations](https://pub.dev/packages/equations) is an equation solving library.\n- [expression_language](https://pub.dev/packages/expression_language) is a library for parsing and evaluating expressions.\n- [expressions](https://pub.dev/packages/expressions) is a library to parse and evaluate simple expressions.\n- [json_path](https://pub.dev/packages/json_path) is an implementation of JSONPath expressions.\n- [pem](https://pub.dev/packages/pem) encodes and decodes textual cryptographic keys.\n- [puppeteer](https://pub.dev/packages/puppeteer) is a library to automate the Chrome browser.\n- [query](https://pub.dev/packages/query) implements search queries with support for boolean groups, field scopes, ranges, etc.\n- [xml](https://pub.dev/packages/xml) is a lightweight library for parsing, traversing, and querying XML documents.\n\n\n### History\n\nPetitParser was originally implemented in [Smalltalk](https://www.lukas-renggli.ch/smalltalk/helvetia/petitparser). Later on, as a means to learn these languages, I reimplemented PetitParser in [Java](https://github.com/petitparser/java-petitparser) and [Dart](https://github.com/petitparser/dart-petitparser). The implementations are very similar in their API and the supported features. If possible, the implementations adopt best practices of the target language.\n\n\n### License\n\nThe MIT License, see [LICENSE](https://raw.githubusercontent.com/petitparser/dart-petitparser/main/LICENSE).\n","funding_links":[],"categories":["Parsers","Dart","解析器","Libraries"],"sub_categories":["Parsers"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpetitparser%2Fdart-petitparser","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpetitparser%2Fdart-petitparser","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpetitparser%2Fdart-petitparser/lists"}