Powered by SmartDoc

KMyaccユーザーズガイド

kmyaccはyaccやbisonと同じLALRパーサージェネレータです。yaccと互換性があり、生成される表が小さく、ホスト言語としてC以外にJava, JavaScript,Perlも使うことができます。

なおこの文書の内容は、予告なく変更される可能性があることをあらかじめ御了承ください。

プログラムの入手とインストール

最新のソースコード(ベータ版)はkmyacc-4.1.4.tar.gzです。ソースのみでバイナリは含まれていません。

コンパイルするには、ANSI Cコンパイラ(gccで十分)が必要です。

以下のコマンド

 gzip -d kmyacc-4.1.4.tar.gz | tar xvf -

でアーカイブを展開するとkmyacc-4.1.4というディレクトリができるので、この中に移動します。

この中のMakefile中の

BIN = /usr/local/bin
PARLIB = /usr/local/lib

を適切なインストール先に変更してください。BINはkmyaccバイナリが置かれる場所、PARLIBはパーサーのプロトタイプファイルが置かれる場所です。

そして、

make
make install

でコンパイル・インストールが完了します。

使用方法

kmyaccの起動の書式は以下の通りです。

  kmyacc [ -dvltani ] [ -b X ] [ -p XX ] [ -m MODEL ] [ -L LANG ] grammarfile

grammerfileにはyaccソースを指定します。kmyaccはこのファイルの拡張子から、以下のルールに従ってホスト言語を推定します。

拡張子と言語の対応
yacc拡張子 ホスト言語 出力ファイル拡張子
.y C y.tab.c
.jy Java .java
.jsy JavaScript .js
.ply Perl .pl

出力されるパーサープログラムのファイル名は、C言語の場合はy.tab.cに固定されていますが、それ以外の言語では元のgrammarfile名から作られます。

kmyacc foo.y  →  y.tab.c
kmyacc Foo.jy →  Foo.java
kmyacc Foo.jsy → Foo.js
kmyacc foo.ply → foo.pl

オプション

オプションとして以下のものが指定できます。

-d
(C言語のみ)トークンの値とスタックの型YYSTYPEの定義を含んだファイルy.tab.hを生成する
-v
LR(0)アイテムと構文解析の状態遷移表を人間に分かるように記したファイルy.outputを生成する。
-l
(C言語のみ)#lineを出力ファイルに挿入しない。
-t
デバッグのためのトレース表示を行うコードを挿入する。C言語の場合は、このオプションの指定なしでもコンパイル時に-DYYDEBUGを指定すれば同じ効果が得られる。
-a
各意味アクションを独立した関数(orメソッド)にしてyyparseから外に出す。コンパイラが大きな関数やメソッドを処理できない場合に指定する。
-b X
(C言語のみ)出力ファイルの名前をy.tab.c, y.outputでなくX.tab.c, X.outputにする。
-p XX
(C言語のみ)パーサーコード中の外部シンボルの名前をyyでなくXXで始まる名前に変える。複数のパーサーが一つのプログラムにリンクされる場合に、シンボルの衝突を避けるために指定する。
-m MODEL
パーサーの原型となるファイルを指定する。省略時は、/usr/local/lib/kmyacc.LANG.parserというファイルが使われる(LANGはホスト言語の拡張子)。
-L LANG
ホスト言語を明示的に指定する。現在はc, java, javascript, perlが指定できる。
-n
アクション中のsemantic valueの参照を名前で行うことを許す。C以外のホスト言語では自動的に指定される。
-i
アクション中の$$,$1,$2...の形式でのsemantic valueの参照を禁止し、名前による参照のみにする。Perlでは自動的に指定される。

文法定義

基本的にはyaccと同じですが、以下に違いを示します。

仕様
項目 意味 yaccとの違い
%token 終端記号の定義 -
%left 左結合終端記号の定義 -
%right 右結合終端記号の定義 -
%nonassoc 非結合終端記号の定義 -
%{ ... %} コードの挿入 Javaではimportを書く
%union 意味スタックの型定義 C言語のみ使用可能
%type 終端記号、非終端記号の型定義 C言語では%unionで宣言したフィールド名、

Javaではクラス名
%start 開始記号定義 -
%expect N shift/reduce conflictの数を指定 拡張
%pure_parser reentrantなパーサーを生成 拡張
$$, $1, $2... semantic valueの参照 名前による参照も可能

最後の項目「semantic valueの参照」について補足します。

kmyaccでは、アクション中のsemantic valueを、$$, $1, $2...の代わりに文法記号の名前で参照することができます(Cでは-nオプションの指定が必要)。

以下に例を示します。

foo: bar '+' baz { foo = bar + baz; }

上のアクションは、{ $$ = $1 + $3; }と同じです。

同じ文法記号が規則中に複数回現われる場合は、どの要素を指しているのかが曖昧なため、そのまま文法記号で参照するとエラーになってしまいます。

こういう場合には文法記号の前に「名前@」を前置することにより、その名前でsemantic valueを参照することができます。

result@expr: addendum@expr '+' addenda@expr
   { result = addendum + addenda; }

以下、文法定義ファイルの書き方を各言語別に説明します(Cは省略)。

Java

Java版のパーサーは、単一のクラスとして生成されます。

クラス名は、ソースファイルの名前から拡張子を除いて作られます。例えば、ソースがFoo.jyならクラス名はFooとなります。

文法定義の次の%%以降の部分は、このクラス内にそのままコピーされますが、%{ ... %}の中に記述したコードは、クラス定義の外側に展開されます。従って、package宣言やimportはこの中に書く必要があります。

アクション記述

Java版では、意味スタックはObjectとして宣言されており、ある記号に対し%typeによって型が指定されている場合、その型へのキャストが自動的に生成されます。

例えば以下の文法定義を考えてみます。

%type <Integer> expr

%%
expr : expr '+' expr  { $$ = new Integer($1.intValue() + $3.intValue()); }

この場合、{}内のアクションは以下のようなJavaプログラムに展開されます。

{ yyval = new Integer(((Integer)yyastk[yysp-2]).intValue()
        + ((Integer)yyastk[yysp-0]).intValue()); }

構文エラーの扱い

以下に、アクション中でエラーの処理を行うための機能を説明します。C版ではマクロで提供されている機能ですが、Javaではマクロがないため、直接制御変数を書き換えたり参照したりする必要があります。参考までに括弧内にC版でのマクロ名を示しておきます。

強制的に構文エラーを起こす(YYERROR)
変数yyparseerrorにtrueを代入します。
{ yyparseerror = true; }
構文エラーの区切(yyerrok)
yaccではエラーメッセージの雪崩を防ぐため、一度構文エラーが起こると3トークン正常にshiftされるまでは次のエラーメッセージを表示しないようになっています。これを止めて次のエラー検出を再開させるには、yyerrflagに0を代入します。
{ yyerrflag = 0; }
先読みのクリア(yyclearin)
エラー処理に先立ち、先読みトークンを捨てるにはyycharに-1を代入します。
{ yychar = -1; }
エラーリカバリー中か否かの判定(YYRECOVERING)
現在エラーリカバリー中であるかどうかを判定するには、変数yyerrflagが0かどうかを調べます。
{ if (yyerrflag) hoge(); }

インターフェース

生成されるパーサーのクラスメソッドは以下の通りです。

public int yyparse()
パーサーのエントリポイントです。正常に終了した、あるいはエラーが発生したが回復できた場合は0を、エラーから回復できなかった場合は1を返します。

また、yyparse中からは以下のメソッドを呼出します。ユーザーはこれらのメソッドを%%以下のコード中で定義する必要があります。

int yylex()

字句解析のインターフェースです。トークンを読み出してその値を返します。返す値は、リテラルトークン(例:'+')かまたは%tokenで宣言されたトークンの値です。また、semantic valueは変数yylvalにセットします。

字句解析クラスを独立したクラスとして定義する場合は、このメソッドでインターフェースの調整を行ってください。

void yyerror(String msg)
構文エラーが発生したときに呼出されるメソッドです。

なお、トークンの値は、public static final intとして定義されます。従って、これらの値をクラスの外部からFoo.IDENTIFIERのように参照することができます。

JavaScript

生成されるパーサはJavaScriptのプログラムの形となっており、<script>タグは付加されていません。HTMLファイルとして使用可能な形にするためには、以下のような適切なヘッダ

%{
<html>
<head>
<title>Parser</title>
<script type="text/javascript">
<!--
%}

を、末尾にそれを閉じる以下のようなタグ

// --></script></head>
<body>...</body>
</html>

を置く必要があります。

JavaScriptは静的な型のない言語ですので、%typeは意味をもちません。

生成されたパーサーはCと同様、function yyparse()が入口となります。

ユーザは、function yylex()とyyerror(msg)を用意する必要があります。

yylex()は入力トークンのsemantic valueを変数yylvalにセットし、その値を整数値で返します。

yyerror(msg)は、エラーメッセージの表示を行う関数です。渡されたmsgの内容を表示します。

Perl

Perlは静的な型のない言語ですので、%typeは意味をもちません。

アクション中のsemantic valueは、$$, $1, $2...の代わりにgrammar symbolの名前で参照します。従って、$で始まるPerlの変数は普通に使うことができます(前出の例を参照)。

生成されたパーサは、Cと同様サブルーチンyyparse()がパーサの入口となります。

ユーザはサブルーチンyylex()とyyerror(msg)を用意する必要があります。

yylex()はCと同様にsemantic valueを$yylvalにセットし、トークンの値を整数値で返します。

yyerror(msg)は、エラーメッセージの表示を行う関数です。渡されたmsgの内容を表示します。

プロトタイプファイルの作り方

kmyaccでは、-mオプションで任意のプロトタイプファイルを指定することができます。

プロトタイプファイルは、パーサーの原型となるソースプログラムで、ホスト言語ごとに別々のものが用意されており、言語の違いはほとんどこのファイルで吸収しています(一部はkmyacc本体で対応)。

プロトタイプファイルの中では、表や定数を生成したり、条件によって別のコードを生成したりするためのマクロ機能が使われています。これはC言語のプリプロセッサとは別のもので、kmyaccによって処理されます。

以下、このマクロ機能について説明します。

マクロ呼出し

プロトタイプ中のマクロ機能には、行単位のものと、文字単位のものがあります。

行単位のものは、1行すべてがマクロ呼出しであり、他のコードは含むことができません。一方、文字単位のものはテキスト中の任意の場所に書くことができます。

また、実装上の都合で、$includeと$semvalは書ける場所に制限があります。

値参照

事前に定義されたスカラー変数の値を参照する機能です。文字単位でソースのどの位置にも現われることができ、以下の形式をしています。

$(変数名)

変数名としては、以下のものがあります。

YYSTATES, YYNLSTATES, YYINTERRTOK, YYUNEXPECTED,
YYDEFAULT, YYTERMS, YYNONTERMS, YYBADCH, YYMAXLEX, 
YYLAST, YYGLAST, YY2TBLSTATE, CLASSNAME, -p

$(-p)は、-pオプションに記述されたプリフィックス値に展開されます。

$listvar

$listvarは、配列変数の値を展開する機能です。

$listvar 配列名

行単位で、','で区切られた変数の値のリストに展開されます。

配列名には、以下のものが指定できます。

yytranslate, yyaction, yycheck, yybase, yydefault,
yygoto, yygcheck, yygbase, yygdefault, yylhs, yylen,
terminals, nonterminals

$TYPEOF

$TYPEOFは、配列変数の型(char, short)に展開される機能です。

$TYPEOF(配列名)

行の中の任意の位置に書くことができ、Cならばcharもしくはshortに、Javaならばbyteもしくはshortに展開されます。

配列名には、以下のものが指定できます。

yytranslate, yycheck, yygcheck, yylhs, yylen

$if, $ifnot

$ifは、条件が成り立っているかどうかを調べ、成り立っているときのみ$endifまでのコードを生成します。$ifnotはその逆です。

行単位であり、ネストは許されません。

$if 条件
...
$endif

現在のところ記述できる条件は、オプション-t, -a, -pだけです。

$reduce

$reduceは、yaccのアクションの生成パターンを記述する行単位マクロです。

$reduce
...
[$noact
...
]
$endreduce

$reduceと$endreduceの間(または$noactの間)のコードが、アクションの数だけ生成されます。また、%nと書くとアクション番号に、%bと書くとアクションコード本体に展開されます。

また、$noactと$endreduceの間のコードは、ユーザ定義のアクションがなかった場合に生成されるコードです。

例:

          switch (yyn) {
$reduce
          case %n:
            {%b} break;
$endreduce
          }

$meta

$metaは、プロトタイプファイル中のマクロ機能呼出しを示すメタキャラクタ'$'を他の文字に変えます。

$meta new-metacharacter

$metaの次の最初の空白でない文字が、新しいメタキャラクタとして$の代わりに使われます。

以下の例では、!が新しくメタキャラクタとして使われます。

$meta !
!include

なお、このメタキャラクタは、アクション中のsemantic valueの参照を示す$とは関係ありません。$meta指定があっても、アクション中でのsemantic valueの参照には$を使う必要があります。

$semval

$semvalは、アクション中のsemantic value($$, $1, $2...)をどのように展開するかを定義する機能です。これは必須であり、これを書かないとアクションは正常に展開されません。

$semval($) body-for-$$-without-type
$semval($,%t) body-for-$$-with-type
$semval(%n) body-for-$n-without-type
$semval(%n,%t) body-for-$n-with-type

semantic valueには$$と$数字の二種類があり、さらにそれぞれが%typeによる型指定の有無によって二つに分けられ、全体で4種類のバリエーションがあります。この4種類すべてを定義しなくてはなりません。

$semval($)は、型なしの$$の展開形式を定義します。

$semval($,%t)は、型つきの$$の展開形式を定義します。

$semval(%n)は、型なしの$数字に対する展開形式を定義します。

$semval(%n,%t)は、型つきの$数字に対する展開形式を定義します。

どの定義でも、定義中に%tと書くと、それは型名(フィールド名)に置き換えられ、%nと書くと、それは$の後の数字に置き換えられます。

$semvalは、$includeよりも先に現れなくてはいけません。

C言語での例:

$semval($) yyval
$semval($,%t) yyval.%t
$semval(%n) YYASP(%n-%l)
$semval(%n,%t) YYASP(%n-%l).%t

$include

$includeは%{ ... %}の中身に展開される行単位マクロです。

$include

$includeは$semvalマクロを除くすべてのマクロ呼出しに先行して書かなくてはいけません。また、$includeがないとパーサーコード全体が正常に作成されません。

$tailcode

$tailcodeは、文法定義ファイル中の最後の%%以降の部分に展開される行単位マクロです。

$tailcode

$union

$unionは、%union { ... }の中身に展開される行単位マクロです。

$union

$tokenval

$tokenvalは、終端記号の値定義を生成するための行単位マクロです。

$tokenval
...
$endtokenval

$tokenvalと$endtokenvalの間が、文字定数以外の終端記号の個数分だけ繰り返し生成されます。そのさい、%nは終端記号の値の十進表記に、%sは終端記号の名前に展開されます。

例えば

$tokenval
  public static final int %s = %n;
$endtokenval

は、終端記号IDENTIFIERとNUMBERが定義されているとき、以下のように展開されます。

  public static final int IDENTIFIER = 256;
  public static final int NUMBER = 257;