lark parsing library

repository·master·Indexed 27 days ago

https://github.com/lark-parser/lark

A versatile, pure-python parsing toolkit for Python designed for ergonomics and performance. Lark can parse almost any context-free language using Earley, LALR(1), and CYK parsing algorithms. It features an EBNF grammar system, support for Shared Packed Parse Forests (SPPF), and utilities for building Abstract Syntax Trees (AST) and handling indentation-sensitive languages.

Tokens
17K
Snippets
35
Records
126
Agent score
90%

What's inside lark

  1. Overview of Lark features

    master

    Lark is a modern parsing library for Python capable of parsing any context-free grammar. Key features include:

    • Advanced grammar language: Based on EBNF.
    • Multiple parsing algorithms: Choose from Earley, LALR(1), and CYK.
    • Automatic tree construction: Trees are automatically inferred from your grammar.
    • Fast unicode lexer: Includes regexp support and automatic line-counting.
  2. Overview of Lark parsing algorithms

    master

    Lark provides two main parsing algorithms that allow you to trade off power and speed:

    • Earley: A powerful parser that can handle all context-free grammars, including highly ambiguous ones.
    • LALR(1): A fast and lightweight parser suitable for deterministic grammars. It can also be used to generate a stand-alone parser.
  3. Choose a Lark parser type

    master

    Lark provides different parser implementations depending on your grammar complexity and performance requirements:

    • Earley parser: Capable of parsing any context-free grammar. It implements SPPF (Shared Packed Parse Forest) for efficient parsing and storing of ambiguous grammars.
    • LALR(1) parser: Highly efficient in space and performance (O(n)). It uses a parse-aware lexer that offers more expressive power than traditional LALR implementations like ply.
    • CYK parser: An additional parser implementation available in Lark.
  4. Import grammars from Nearley.js

    master

    Lark provides a tool to convert grammars from Nearley (a Javascript Earley library) into Python modules. This process uses Js2Py to convert and execute Javascript postprocessing code segments.

    Requirements

    1. Install Lark with the nearley component:
    pip install lark[nearley]
    1. Acquire a copy of the Nearley codebase via git.

    Usage

    Run the converter using the following command structure: python -m lark.tools.nearley <grammar.ne> <start_rule> <path_to_nearley_repo>

    Limitations

    • Lark cannot import templates from Nearley.
    • Lark cannot export grammars to Nearley.
    git clone https://github.com/Hardmath123/nearley
    python -m lark.tools.nearley nearley/examples/calculator/arithmetic.ne main ./nearley > ncalc.py
  5. Obtain a Shared Packed Parse Forest (SPPF) using Earley

    master

    When using the Earley parser, you can obtain the Shared Packed Parse Forest (SPPF) instead of a standard tree by passing the ambiguity='forest' option to the parser. This is useful for efficiently storing highly ambiguous parses and handling infinite ambiguities, though it is more complex than working with a standard tree.

    Note on Grammar Features in SPPF:

    • Rules starting with _ are not inlined.
    • Rules starting with ? are never inlined.
    • All tokens will appear in the SPPF.
  6. Prevent terminal filtering with `!`

    master

    By default, Lark filters out certain terminals (like unnamed literals or terminals starting with _) to reduce tree noise. To force a rule to retain all its terminals (including literals) as nodes in the tree, prefix the rule name with an exclamation mark !.

    !expr: "(" expr ")"
         | NAME+
    NAME: /\w+/
    %ignore " "
  7. Write a grammar in EBNF

    master

    Lark uses Extended Backus-Naur form (EBNF) to define grammars.

    Key syntax rules:

    • rule_name : items defines a rule.
    • TERMINAL: "text" defines a terminal (string or regex).
    • rule* matches zero or more instances.
    • rule+ matches one or more instances.
    • [rule] matches an optional rule (zero or one).
    • rule? is an alternative way to denote an optional rule.
    • (rule1 | rule2) groups rules together.
    • -> alias creates an alias for a specific part of a rule, which is useful for naming branches in the resulting parse tree.
    • %import common.NAME imports predefined terminals from Lark's common library.
    • %ignore WS tells the parser to ignore whitespace.

    Note: Terminals are typically written in UPPER-CASE, while rules are written in lower-case.

        value: dict
             | list
             | STRING
             | NUMBER
             | "true" | "false" | "null"
    
        list : "[" [value ("," value)*] "]"
    
        dict : "{" [pair ("," pair)*] "}"
        pair : STRING ":" value
    
        %import common.ESCAPED_STRING   -> STRING
        %import common.SIGNED_NUMBER    -> NUMBER
        %import common.WS
        %ignore WS
  8. Use tree-less LALR(1) for improved speed and memory efficiency

    master

    When using the LALR(1) parser, you can avoid the overhead of building a full parse tree by passing a Transformer directly to the Lark constructor via the transformer argument.

    When a transformer is provided this way, the .parse() method returns the transformed data directly instead of a tree structure. This is more memory-efficient and faster for large datasets. It is recommended to use this approach only once your transformer logic is already verified and working.

  9. Recommended work process for Lark

    master

    When building a parser with Lark, follow this iterative workflow:

    1. Collect Samples: Gather input samples that demonstrate the key features and behaviors of your target language.
    2. Write a Grammar: Create a grammar that is intuitive and imitates how you would explain the language naturally.
    3. Test against Samples: Run your grammar against your input samples and verify that the resulting parse-trees are correct.
    4. Shape the Tree: Use Lark's grammar features (like inlining rules or using aliases) to refine the tree structure and remove superfluous nodes.
    5. Create a Transformer: Implement a transformer to evaluate the parse-tree into a usable data structure, such as an Abstract Syntax Tree (AST) or a custom set of Python classes.
  10. Run Lark unit tests

    master

    You can run the full suite of unit tests from the project root using several different methods depending on your environment and preference.

    # Using the standard python module
    python -m tests
    
    # Using pypy
    pypy -m tests
    
    # Using pytest
    pytest tests
    
    # Using setup.py
    python setup.py test
  11. Build parse-trees for easier processing

    master

    By default, Lark builds a parse-tree. Trees are preferred over state-machines because they allow you to visualize the state, maintain awareness of previous/future states, and process the parse in incremental steps.

    If you are using the LALR(1) algorithm and need to improve performance, you can skip tree construction by providing a transformer instead.