セマンティクスを数式で書くパーサジェネレータ

Eclipseのようなリファクタリングやら,コード補完やらの機能を,自分の作った「俺言語」でも使えるように自動生成したい,という目標のもと,パーサジェネレータを製作中.

http://www2.eplang.jp/tonyu/adaptive/AdaptiveParser071023.jar
(jar解凍後,test.batを起動すれば一応動きます)

こんなコードを一応解析できるレベル.

ファイル名: work/test.fproc

type String {}
type int {}
type Coord {
   var x as int
   var y as int
}
type Equa {
  var x as int
  var expr as String
}
var c as Coord
var e as Equa
c.x=3
c.y="cde"
e.x=c.y
e.expr="abc"

解析といっても,

  • c.xはint型で,右辺は数値だから代入OK
  • c.yはint型なのに,右辺で文字列を代入しようとしているからエラー

とかいう判定ができるくらい.

上の言語の定義ファイルはこんな感じ:

ファイル名: work/fproc.grm

S:= structs vardecls stmts '$ ;
structs := e | struct structs ;
stmts   := e | stmt   stmts ;
struct := 'type '[a-zA-Z]+ '\{ vardecls '\} {
   (and
      (isa $result Type)
      (eq (nameOf $result) $rl)
      (eq (varsOf $result) $rrrl)
   )
};
vardecls := e | vardecl vardecls;
vardecl  := 'var '[a-zA-Z]+ 'as type {
   (and
      (isa $result Var)
      (eq (typeOf $result) $rrr)
      (eq (nameOf $result) $rl)
      (eq (ownerOf $result) (outer Type enclose $result default MAIN))
   )
};

stmt := varRef '= val {
        (and
           (eq $result (set (addrOf $l) $rr))
           (assertEq (typeOf $l) (typeOf $rr))  
        )
     };
varRef :=
  '[a-zA-Z]+ {
    (varRef MAIN $)
  } |
  val '. '[a-zA-Z]+ {
    (varRef $l $rr)
  };
val := varRef { (valueOf $) } | num | str;
str := '"[a-zA-Z\s]+" {
  (and
    (eq (typeOf (string $)) 
        (select _s from Type where (assertEq (nameOf _s) String)) 
    )
    (eq $result (string $))
  )
} ;
num := '[0-9]+ {
  (and
    (eq (typeOf (num $)) 
        (select _i from Type where (assertEq (nameOf _i) int)) 
    )
    (eq $result (num $))
  )
};
type := '[a-zA-Z]+ {
  (select _t from Type where (assertEq (nameOf _t) $))
}

(
  (eq (typeOf (valueOf _)) (typeOf _))
  (eq (typeOf MAIN) MAIN)
  (eq
      (typeOf (varRef _target _name))
      (typeOf (select var from Var where
        (and
           (assertEq (nameOf var) _name)
           (assertEq (ownerOf var) (typeOf _target))
        )
      ))
  )
)

セマンティクスの書き方

struct := 'type '[a-zA-Z]+ '\{ vardecls '\} {
   (and
      (isa $result Type)
      (eq (nameOf $result) $rl)
      (eq (varsOf $result) $rrrl)
   )
};

は,構造体を定義している部分の文法.

構造体の文法は

  • 予約語 type
  • 任意の英字列
  • 記号 {
  • 変数宣言
  • 記号 }

であり,最後の { .... } の中に定義されたセマンティクスをもつ

セマンティクスはS式で書く(文法定義が面倒だからとりあえずS式)

  • 構文 struct のセマンティクス($result)は Type という型の値である
  • $result の 名前は, 2番目の要素 ( '[a-zA-Z]+ ) である
  • $result がもつ変数は 4番目の要素 ( vardecls ) である

※$rl とか $rrrl の意味:構文木が二分木になっているため,

  • 文法木の2番目の要素は「右側の左側(cdr の car )」
  • 文法木の4番目の要素は「右側の右側の右側の左側(cdrのcdrのcdrの car )」

みたいな方式で取得する.

outer context

vardecl のところに出現する

 (outer Type enclose $result default MAIN)

は,「この値 (Var) の親で, Type を型に持つもの 」 という意味.「親」とか「子」とかは,構文木の親子関係にもとづいて決定する.

ToDo

リファクタリングとコード補完の自動生成(一番肝心なところがまだだったりする