数据样例

x = 40 + 2;
print x;

y = x - (5/2);
print y;

z = 1 + y * x;
print z;

print 14 - 16/3 + x;

目录结构如下:

.
├── Lang
│   ├── Actions.pm6
│   └── Grammar.pm6
├── data
│   ├── calc.lang

Grammar

Grammar.pm6 的内容如下:

unit grammar Lang::Grammar;

rule TOP {
    ^ <statements> $
}

rule statements {
    <statement>+ %% ';'
}

rule statement {
    | <assignment>
    | <printout>
}

rule assignment {
    <identifier> '=' <expression>
}

rule printout {
    'print' <expression>
}

rule expression {
    | <term>+ %% $<op>=(['+'|'-'])
    | <group>
}

rule term {
    <factor>+  %% $<op>=(['*'|'/'])
}

rule factor {
    | <identifier>
    | <value>
    | <group>
}

rule group {
    '(' <expression> ')'
}

token identifier {
    (<:alpha>+)
}

token value {
    (
    | \d+['.' \d+]?
    | '.' \d+
    )
}

Action

Actions.pm6 的内容如下:

unit class Lang::Actions;

has %.var;

method assignment($/) {
    %!var{$<identifier>} = $<expression>.ast;
}

method printout($/) {
    say $<expression>.ast;
}

method expression($/) {
    if $<group> {
        $/.make: $<group>.ast
    }
    else {
        my $result = $<term>[0].ast;

        if $<op> {
            my @ops = $<op>.map(~*);
            my @vals = $<term>[1..*].map(*.ast);

            for 0..@ops.elems - 1 -> $c {
                if @ops[$c] eq '+' {
                    $result += @vals[$c];
                }
                else {
                    $result -= @vals[$c];
                }
            }
        }

        $/.make: $result;
    }
}

method term($/) {
    my $result = $<factor>[0].ast;

    if $<op> {
        my @ops = $<op>.map(~*);
        my @vals = $<factor>[1..*].map(*.ast);

        for 0..@ops.elems - 1 -> $c {
            if @ops[$c] eq '*' {
                $result *= @vals[$c];
            }
            else {
                $result /= @vals[$c];
            }
        }
    }

    $/.make: $result;
}

method factor($/) {
    if $<identifier> {
        $/.make: %!var{~$<identifier>} // 0
    }
    elsif $<value> {
        $/.make: $<value>.ast
    }
    elsif $<group> {
        $/.make: $<group>.ast
    }
}

method group($/) {
    $/.make: $<expression>.ast
}

method identifier($/) {
    $/.make: ~$0
}

method value($/) {
    $/.make: +$0
}

解析

use lib '.';
use Lang::Grammar;
use Lang::Actions;

my $parsed = Lang::Grammar.parsefile(@*ARGS[0] // 'data/calc.lang', :actions(Lang::Actions.new()));
say $parsed;