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