Darser: A LL(1) to Recursive Decent Parser/AST/Visitor Generator
Robert Schadek
rschadek at symmetryinvestments.com
Wed Mar 20 17:20:48 UTC 2019
To get graphqld up and running I needed a parser/ast/visitor.
Being lazy, I created parser/ast/visitor generated for that.
Darser is the result.
Given a language BNF, as e.yaml, darser will generate a recursive
decent parser, a set of classes making up the AST, a visitor
class and a AST printer class. The parser, AST, and visitor can
be extended by hand written extensions.
Given a yaml file like this:
```
PrimaryExpression:
Identifier: [ identifier#value ]
Float: [ float64#value ]
Integer: [ integer#value ]
Parenthesis: [lparen, Expression#expr, rparen]
```
darser will create a parser function as such:
```D
PrimaryExpression parsePrimaryExpressionImpl() {
string[] subRules;
subRules = ["Identifier"];
if(this.lex.front.type == TokenType.identifier) {
Token value = this.lex.front;
this.lex.popFront();
return new PrimaryExpression(PrimaryExpressionEnum.Identifier
, value
);
} else if(this.lex.front.type == TokenType.float64) {
Token value = this.lex.front;
this.lex.popFront();
return new PrimaryExpression(PrimaryExpressionEnum.Float
, value
);
} else if(this.lex.front.type == TokenType.integer) {
Token value = this.lex.front;
this.lex.popFront();
return new PrimaryExpression(PrimaryExpressionEnum.Integer
, value
);
} else if(this.lex.front.type == TokenType.lparen) {
this.lex.popFront();
subRules = ["Parenthesis"];
if(this.firstExpression()) {
Expression expr = this.parseExpression();
subRules = ["Parenthesis"];
if(this.lex.front.type == TokenType.rparen) {
this.lex.popFront();
return new PrimaryExpression(PrimaryExpressionEnum.Parenthesis
, expr
);
}
auto app = appender!string();
formattedWrite(app,
"Found a '%s' while looking for",
this.lex.front
);
throw new ParseException(app.data,
__FILE__, __LINE__,
subRules,
["rparen"]
);
}
auto app = appender!string();
formattedWrite(app,
"Found a '%s' while looking for",
this.lex.front
);
throw new ParseException(app.data,
__FILE__, __LINE__,
subRules,
["float64 -> PostfixExpression",
"identifier -> PostfixExpression",
"integer -> PostfixExpression",
"lparen -> PostfixExpression"]
);
}
auto app = appender!string();
formattedWrite(app,
"Found a '%s' while looking for",
this.lex.front
);
throw new ParseException(app.data,
__FILE__, __LINE__,
subRules,
["identifier","float64","integer","lparen"]
);
}
```
and an AST class like that:
```D
enum PrimaryExpressionEnum {
Identifier,
Float,
Integer,
Parenthesis,
}
class PrimaryExpression {
PrimaryExpressionEnum ruleSelection;
Token value;
Expression expr;
this(PrimaryExpressionEnum ruleSelection, Token value) {
this.ruleSelection = ruleSelection;
this.value = value;
}
this(PrimaryExpressionEnum ruleSelection, Expression expr) {
this.ruleSelection = ruleSelection;
this.expr = expr;
}
void visit(Visitor vis) {
vis.accept(this);
}
void visit(Visitor vis) const {
vis.accept(this);
}
}
```
The lexer has to be hand written.
More information about the Digitalmars-d-announce
mailing list