Enhanced LALR(1) Parser Generator for D

Peter Williams pwil3058 at bigpond.net.au
Tue Apr 9 19:28:34 PDT 2013


As an exercise to help me learn D, I've implemented an enhanced version 
of the LALR(1) parser generator described in Aho, Sethi and Ullman's 
dragon book.  The enhancements include:

1. a built in lexical analyser (where a flex like pattern is required as 
part of the definition of a token),

2. the ability to resolve reduce/reduce conflicts using predicates 
attached to grammar rules (inspired by the work of Ganapathi and Fischer 
in the 80s), and

3. for "literal" tokens the option of using the literal pattern in 
grammar rules instead of the token name e.g. if you had a token named 
LESSOREQUAL with the pattern "<=" then you can "<=" in grammar rules 
instead of LESSOREQUAL.

The code for this tool resides in the dunnart repository at GitHub.

Unfortunately, at this stage, the documentation is not very good but a 
small example that illustrates all features is attached.

As I said at the start, this was an exercise in learning D so there may 
be room for improvement in some of the code.  Suggestions are welcome.

Cheers
Peter
-------------- next part --------------
%{
import std.stdio;
double[string] variables;
enum Errors { undefinedVariables = 1, divideByZero = 2, syntaxError = 3 };
uint errors;

void report_errors()
{
    auto report = "Errors:";
    if (errors & Errors.undefinedVariables) {
        report ~= " \"Undefined Variables\"";
    }
    if (errors & Errors.divideByZero) {
        report ~= " \"Divide by Zero\"";
    }
    if (errors & Errors.syntaxError) {
        report ~= " \"Syntax Errors\"";
    }
    stderr.writeln(report);
}
%}

%field  double value
%field  string id

%token          EOL     (\n)
%token          PLUS    "+"
%token          MINUS   "-"
%token          TIMES   "*"
%token          DIVIDE  "/"
%token          ASSIGN  "="
%token  <value> NUMBER  ([0-9]+(\.[0-9]+){0,1})
%token  <id>    ID      ([a-zA-Z]+)
%token          LPR     "("
%token          RPR     ")"

%skip   ([\t\r ]+)

%right  UMINUS
%left   TIMES DIVIDE
%left   PLUS MINUS
%left   EOL

%%
line: setup expr ?(errors > 0?) !{report_errors();!}
    | setup expr !{writeln($2.value);!}
    | setup ID "=" expr ?(errors == 0?) !{variables[$2.id] = $4.value;!}
    | setup ID "=" expr !{report_errors();!}
    | line EOL line
    | line EOL
    .

setup: !{errors = 0;!}.

expr: expr "+" expr ?($1.value == 0?) !{$$.value = $3.value;!}
    | expr "+" expr ?($3.value == 0?) !{$$.value = $1.value;!}
    | expr "+" expr !{$$.value = $1.value + $3.value;!}
    | expr "-" expr ?($1.value == 0?) !{$$.value = -$3.value;!}
    | expr "-" expr ?($3.value == 0?) !{$$.value = $1.value;!}
    | expr "-" expr !{$$.value = $1.value - $3.value;!}
    | expr "*" expr ?($1.value == 0 || $3.value == 0?) !{$$.value = -$3.value;!}
    | expr "*" expr ?($1.value == 1?) !{$$.value = $3.value;!}
    | expr "*" expr ?($3.value == 1?) !{$$.value = $1.value;!}
    | expr "*" expr !{$$.value = $1.value * $3.value;!}
    | expr "/" expr ?($3.value == 1?) !{$$.value = $1.value;!}
    | expr "/" expr ?($3.value == 0?) !{errors |= Errors.divideByZero;!}
    | expr "/" expr ?($1.value == 0?) !{$$.value = 0;!}
    | expr "/" expr !{$$.value = $1.value / $3.value;!}
    | "(" expr ")" !{$$.value = $2.value;!}
    | "-" expr %prec UMINUS !{$$.value = $2.value;!}
    | NUMBER !{$$.value = $1.value;!}
    | ID ?($1.id in variables?) !{$$.value = variables[$1.id];!}
    | ID !{errors |= Errors.undefinedVariables; $$.value = 0;!}
    | %error !{errors |= Errors.syntaxError;!}
    .


More information about the Digitalmars-d mailing list