Lark is a general-purpose parsing library. It’s written in Python, and supports two parsing algorithms: Earley (default) and LALR(1).
Lark also supports scanless parsing (with Earley), contextual lexing (with LALR), and regular lexing for both parsers.
Lark is a re-write of my previous parsing library, PlyPlus.
Lark accepts its grammars in EBNF form.
The grammar is a list of rules and terminals, each in their own line.
Rules and terminals can be defined on multiple lines when using the OR operator ( | ).
Comments start with // and last to the end of the line (C++ style)
Lark begins the parse with the rule ‘start’, unless specified otherwise in the options.
It might help to think of Rules and Terminals as existing in two separate layers, so that all the terminals are recognized first, and all the rules are recognized afterwards. This is not always how things happen (depending on your choice of parser & lexer), but the concept is relevant in all cases.
Each rule is defined in terms of:
name : list of items to match
| another list of items -> optional_alias
| etc.
An alias is a name for the specific rule alternative. It affects tree construction.
An item is a:
Example:
float: "-"? DIGIT* "." DIGIT+ exp
| "-"? DIGIT+ exp
exp: "-"? ("e" | "E") DIGIT+
DIGIT: /[0-9]/
Terminals are defined just like rules, but cannot contain rules:
NAME : list of items to match
Example:
IF: "if"
INTEGER : /[0-9]+/
DECIMAL: INTEGER "." INTEGER
WHITESPACE: (" " | /\t/ )+
Lark builds a tree automatically based on the structure of the grammar. Is also accepts some hints.
In general, Lark will place each rule as a branch, and its matches as the children of the branch.
Terminals are always values in the tree, never branches.
In grammar rules, using item+ or item* will result in a list of items.
Example:
expr: "(" expr ")"
| NAME+
NAME: /\w+/
%ignore " "
Lark will parse “(((hello world)))” as:
expr
expr
expr
"hello"
"world"
The brackets do not appear in the tree by design.
Terminals that won’t appear in the tree are:
Terminals that will appear in the tree are:
a. Rules whose name begins with an underscore will be inlined into their containing rule.
Example:
start: "(" _greet ")"
_greet: /\w+/ /\w+/
Lark will parse “(hello world)” as:
start
"hello"
"world"
b. Rules that receive a question mark (?) at the beginning of their definition, will be inlined if they have a single child.
Example:
start: greet greet
?greet: "(" /\w+/ ")"
| /\w+ /\w+/
Lark will parse “hello world (planet)” as:
start
greet
"hello"
"world"
"planet"
c. Rules that begin with an exclamation mark will keep all their terminals (they won’t get filtered).
d. Aliases - options in a rule can receive an alias. It will be then used as the branch name for the option.
Example:
start: greet greet
greet: "hello" -> hello
| "world"
Lark will parse “hello world” as:
start
hello
greet
When initializing the Lark object, you can provide it with keyword options:
start - The start symbol (Default: “start”)
parser - Decides which parser engine to use, “earley” or “lalr”. (Default: “earley”) Note: “lalr” requires a lexer
lexer - Decides whether or not to use a lexer stage
transformer - Applies the transformer to every parse tree (only allowed with parser="lalr”)
postlex - Lexer post-processing (Default: None)
To be supported: