DSL
構文上での改行 †
一言要約 †
文のセパレータを使うことで改行に構文上の意味を与える。
要約 †
- 文の終わりを示すために改行を使うことは、プログラミング言語の共通の特長である。
- このことは、改行が入力を分割するために主要な区切り文字として使用されてから、
区切り文字直接変換(Delimiter Directed Translation)に、非常によく合う。
- しかし、構文直接変換(Syntax Directed Translation)で、構文上での改行(Syntactic Newlines)は、むしろ扱いにくい。このセクションでは、いくつか巧妙なトラップを紹介、注目し、つまずくことを避けたい。
仕組み †
- 構文上での改行(Syntactic Newlines)と構文直接変換(Syntax Directed Translation)が相性がとても良くない理由は、あなたが構文上での改行(Syntactic Newlines)を使うとき、改行が2つの役割を果たすからである。
catalog : statement*;
statement : 'item' ID EOL;
EOL : '\r'? '\n';
ID : ('a'..'z' | 'A'..'Z' | '0'..'9' | '_' )+;
WS : (' ' |'\t' )+ {$channel = HIDDEN;} ;
- 上記の文法は、各行が、項目名の識別子に続いて、キーワード'item' である項目の単純なリストに捉える。単純な構文解析の、”Hello world”のような例である文法として使うようになった。
- 上記の文法は、キーワード、識別子、改行 と続くため、簡単であるが、多くの共通のつまずくであろう以下のケースがある。
- 文の間にある空白行
- 最初の文の前の空白行
- 最後の文の後の空白行
- end-of-lineがない最終行での最後の文
- 上記の最初の3つは、全て空白行であるが、文法上でこれらの空白行の扱いを異なる方法で必要とするかもしれない。そのためにも、すべてのテストをするべきである。
- 上記のケースのテストをすることは、おそらく最も重要なやるべきことである。
- 以下に、これらの問題のためのいくつかの解決策を述べる。
- 空白行を効率的に扱う一つの方法は、複数の行末に一致する EOLルールを使うことである。
- 標準のルールであることから、このルールを加える論理的な場所は、字句解析である。(ここでの、"標準"とは、言語理論の単語の意味で使っている。そのため、一致させるために正規表現を利用することができる。)
- ファイルの最終行が、EOLがない文である場所の最後のテストは、いくらか複雑になる。
- 字句解析でEOFの文字を一致させる必要があるケースを扱うことは、パーサジェネレーターに依存して、可能ではないかもしれない。
- Antlrでやるためには、構文解析の文法に、文の終わりのルールを必要とする。
catalog : verticalSpace statement*;
statement : 'item' ID eos;
verticalSpace : EOL*;
eos : EOL+ | EOF;
- 最終行でEOFを見逃すことは、たいてい扱いにくいケースである。
- どれほど扱いにくくなるかは、どのようなパーサジェネレータシステムで EOFを扱うかによる。
- Antlrは、トークンとして構文解析を利用できる。構文解析ルールにおいて一致する(字句解析ルールではない)。
- いくつかは、EOFとマッチングさせることが大変難しく、不可能である。
- そのために考慮するオプションとしては、どちらか一方の終わり、字句解析か、もしかしたら事前の字句解析で、EOFを強制させることである。
- 文の終端を扱うためのもう一方のアプローチは、最後の終端を逃してしまう一般的な問題を避けることである。
- 終端の代わりにセパレータとして考えるべきで次の形式のルールにつながる。
catalog : verticalSpace statement (separator statement)* verticalSpace;
statement : 'item' ID;
separator : EOL+;
verticalSpace : EOL*;
- 構文上での改行(Syntactic Newlines)で多くのトラブルをもたらす、分割の要素は、コメントにする。その中でもEOLによく一致するコメントは、大変役に立つ。
- 改行を無視しているとき、上記の改行を読み込ませる方法で、簡単にコメントを一致させることができる。
- もしコメントの一致が、改行を読み込ませるのであれば、文の終端も無くすであろう。
item laser # explain something
- 通常、以下のような表現を使用してこの問題を回避することは簡単である。
COMMENT : '#' ~'\n'* {skip();};
- 古典的な正規表現の単語が、次の通り(JFlex)である。
Comment = #[^\n]*
- 最後の問題で、構文上での改行(Syntactic Newlines)を考慮して、
長すぎる行のためにcontinuationの文字を用意する。これは、簡単に次のような字句解析ルールでで処理される。
CONTINUATION : '&' WS* EOL {skip();};
いつ使うのか †
- 構文上での改行(Syntactic Newlines)を使うために決め手となることとして、2つの決定が事項がある。
- 文のセパレータを使うための決定
- セパレータの文字として改行を使うための決定
- DSLの限られた構造では、文のセパレータなしで持ちこたえることができない。
- パーサーは通常使用している様々なキーワードから、構文解析の文脈を理解することができるから。
- 文のセパレータが使えない問題は、エラーの特定を難しくする。
- 文のセパレータなしで、いくつかの行の後までに発生するスクリプトの行のエラーは、構文解析では、困惑するエラーメッセージを導き、すぐに分からないかも知れない。
- 文のセパレータは、パーサーを強制的に各セパレータ自身のチェックポイントを指定して、避けることができ、エラーが特定できる。
- 文のセパレータを使う決定をしたら、視覚的な文字か、セミコロンか、改行の中から選ぶ必要がある。
- 改行を使うことについて良いことが多く、とにかく1行あたりに1つの文が存在している
。そのため、改行のセパレータを使うことは、DSLにいくつかの構文的なノイズを追加しないこととなり、特に価値のあることである。
- 構文上での改行(Syntactic Newlines)で不都合な点は、構文上での直接変換(Syntax Directed Translation)はより細心の注意が必要となり、ここで記載したテクニックを使わなければならないことである。
- 共通の問題のケースをカバーするためのテストを確認する必要がある。
- ファウラーは、全体的に見て、視覚的な文のセパレータより構文上での改行(Syntactic Newlines)を使う方が好み。
ファウラーへのフィードバック †
担当者のつぶやき †
- 担当分の完成が遅れてしまいましてすいませんでした・・。
- Syntactic Newlinesでは、改行をセパレータに使うのがおすすめです。
- Antlrは触ったことがないので、機会があれば触ってみたいです。
みんなの突っ込み †
- 「構文上での改行」の方が自然かなー > タイトル -- kakuda?