LR-Style Parser Generator
A Tutorial with several examples is available.
Besides traditional LR and LALR parser generation, Rustlr supports the following options
E --> E + T
a dependency
between T
and E
can be declared so that only one AST type is generated for both.*
, +
and ?
, which simplify
the writing of grammars and allow better ASTs to be created.The following is a Rustlr grammar: ```
auto lifetime 'lt lexterminal LBRACE { lexterminal RBRACE } lexterminal LBRACK [ lexterminal RBRACK ] lexterminal LPAREN ( lexterminal RPAREN ) lexterminal COLON : lexterminal COMMA , lexterminal NULL null lexterminal MINUS - valueterminal TRUE~ bool~ Alphanum("true")~ true valueterminal FALSE~ bool~ Alphanum("false")~ false valueterminal STRING~ &'lt str~ Strlit(n)~ &n[1..n.len()-1] valueterminal NUM~ i64~ Num(n)~ n valueterminal FLOAT~ f64~ Float(n)~ n valueterminal BIGNUM~ &'lt str~ BigNumber(n)~ n nonterminal Integer i64 nonterminal Floatpt f64 nonterminal Boolean bool nonterminals Value KeyValuePair Number nonterminal List : Value nonterminal Object HashMap<&'lt str, LBox<@Value>>
startsymbol Value resync COMMA RBRACK RBRACE
Integer --> MINUS?:m NUM:n {if m.issome() {n*-1} else {n}}
Floatpt --> MINUS?:m FLOAT:n {if m.issome() {-1.0*n} else {n}}
Number:Bignum --> MINUS?:m BIGNUM
Number:Int --> Integer
Number:Float --> Floatpt
Boolean --> TRUE | FALSE
Value:Number --> Number
Value:Boolean --> Boolean
Value:Str --> STRING
Value:Objectmap --> Object
Value --> List
Value --> NULL
Value --> LPAREN Value RPAREN
KeyValuePair --> STRING COLON Value
List:List --> LBRACK Value
$use std::collections::HashMap;
!mod jsonast;
!fn main() {
! let srcfile = std::env::args().nth(1).unwrap();
! let source = LexSource::new(&srcfile).unwrap();
! let mut scanner1 = jsonlexer::fromsource(&source);
! let mut parser1 = makeparser();
! let parseresult = parsewith(&mut parser1, &mut scanner1);
! let ast = parseresult.unwraporelse(|x|{println!("Parsing errors encountered; results not guaranteed.."); x});
! println!("\nAST: {:?}\n",&ast);
!}//main
``
In addition to a parser, the grammar generates a lexical scanner from the
lexterminaland
valueterminal` declarations. It also created most of the
abstract syntax types and semantic actions required by the parser, alongside
some manual overrides. As this is a quick example, we've also injected a main
function, which expects the name of a json source as argument, directly into
the parser. To run rustlr on this example,
cargo install rustlr
rustlr = "0.4"
in its dependenciesjson.grammar
(must have .grammar
suffix).rustlr json.grammar -o src/main.rs
cargo run [some json file]
Note that <COMMA*>
specifies a comma-separated list and normally generates
semantic actions to create
a vector. However, for JSON "objects" we chose to create a hashmap
by manually writing the semantic action. Normally, Rustlr in auto
mode
generates an enum for each non-terminal symbol that's on the left-hand side
of multiple productions, and a struct for non-terminals with a single
production. However, declarations such as nonterminal List : Value
allow
a type to be absorbed into another: there is no separate type for
List
. There are other ways that a grammar can specify how ASTs are to be
created, distinguishing the abstract syntax tree from the parse tree.
Please consult the tutorial
for further documentation.