Hello Wor.log

IT系大学生4人による備忘録のようなもの

javaでbasicっぽいインタプリタ作る(字句解析・構文解析)

CPPXのXです。
javaでbasicっぽい何かのインタプリタ作ってる最中なので忘却しないために書いておきます。
雑な実装なんで、処理速度とか度外視な上、正しく動くか怪しいです。

環境

  • java8

現在

字句解析終わって、構文解析を終えたところです。
現状、構文木が生まれます。

コード説明とコード

githubにぽいっと全てを。

src下にソースコードが入っており、lex.pdfが字句解析の説明、tree.pdfが構文解析の説明です。

課題として投げつけるために書いたので、分かりやすくはないです・・・。

ただ、このまま終わると記事にした意味ないので、少しだけ仕様の説明を書き留めておきたいと思います。

仕様

字句定義

LexicalTypeを追加して、AnalyzerImplにてLexicalTypeに対応する字句パターンを書きなぐっていきます。
読み飛ばしたい字句はignoreへ。

字句定義おわり
後はget呼んでれば、いい感じに字句解析してくれます。

構文定義

新しくNodeの子クラスを定義して、SimpleParseとDefineをくっつけておきます。
Defineには、NodeTypeとファースト集合フィールド、構文定義フィールドを指定します。
NodeType使われてないし、消し飛ばそうかな・・・。

現状だと、ファースト集合もあってないようなものなので、そのうち消し飛ばそう。

構文定義には、LexicalTypeで終端記号を、Nodeの子クラスで非終端記号の定義をします。

@NodeUtil.SimpleParse
@NodeUtil.Define(type = NodeType.EXPR)
public class Expr extends Node {
    public final static NodeUtil.FirstSet firstSet = new NodeUtil.FirstSet(
            LexicalType.SUB,
            LexicalType.LP,
            LexicalType.NAME,
            LexicalType.INTVAL,
            LexicalType.DOUBLEVAL,
            LexicalType.LITERAL)
            .merge(CallFunc.firstSet);
    public final static NodeUtil.Children children = new NodeUtil.Children<Expr>()
            .or(Expr.class, LexicalType.ADD, Expr.class)
            .or(Expr.class, LexicalType.SUB, Expr.class)
            .or(Expr.class, LexicalType.MUL, Expr.class)
            .or(Expr.class, LexicalType.DIV, Expr.class)
            .or(LexicalType.SUB, Expr.class)
            .or(LexicalType.LP, Expr.class, LexicalType.RP)
            .or(LexicalType.NAME)
            .or(LexicalType.INTVAL)
            .or(LexicalType.DOUBLEVAL)
            .or(LexicalType.LITERAL)
            .or(CallFunc.class);
}

こんな感じ。

元定義こんな感じ。

<expr> ::= 
    <expr> <ADD> <expr>
    | <expr> <SUB> <expr>
    | <expr> <MUL> <expr>
    | <expr> <DIV> <expr>
    | <SUB> <expr>
    | <LP> <expr> <RP>
    | <NAME>
    | <INTVAL>
    | <DOUBLEVAL>
    | <LITERAL>
    | <call_func>

まあ悪くなさそう。
ファースト集合、自動で求めるなり、消すなりしてもうちょっとスッキリさせたい。

構文定義おわり
Programのparse呼んであげれば、構文木が生えてきます。

今後

後、木から結果を評価すれば終わりです。

...
.or(Expr.class, LexicalType.ADD, Expr.class).f(e -> {
    return e[0].getValue() + e[2].getValue();
})
.or(...

こんな感じで評価処理を定義できるようにしていく予定です。
というよりも、そうなりました。

演算子の順序が面倒臭そうですが、方針が立ってるので何とかなるでしょう。

おわりです。
githubへのリンク貼っておきます。

github.com