PEG.jsの構文規則の書き方
PEG.jsの構文規則の書き方
PEG.js公式ドキュメントの翻訳と要約です.
PEG.jsとは
Parser Generatorと呼ばれるもので,コンパイラ生成器(コンパイラのコンパイラ)です.
構文規則ファイル(.pegjs)を入力として与えると,コンパイラが出てきます.
今回は使い方は紹介しません.構文規則ファイルの書き方のみ翻訳していきます.
構文例1
JavaScriptに似ています.以下の構文は2*(3+4)
のような計算式を計算するための構文規則です.
start
= additive
additive
= left:multiplicative "+" right:additive { return left + right; }
/ multiplicative
multiplicative
= left:primary "*" right:multiplicative { return left * right; }
/ primary
primary
= integer
/ "(" additive:additive ")" { return additive; }
integer "integer"
= digits:[0-9]+ { return parseInt(digits.join(""), 10); }
-
/.../
や/*...*/
はコメントとして使えます - rule:構文規則の固まりのことをそれぞれruleと呼びます(今回の例では5つのruleがあります)
-
name:ruleにはJavaScriptの変数名のような名前をつけ,重複してはいけません.(例:
integer
) -
parsing expression:構文式は入力されたテキストに対する実際の規則を書いている部分です(例:
digits:[0-9]+ { return parseInt(digits.join(""), 10); }
) -
human-readable name:エラーメッセージで使用される別名をつけることもできます.JavaScriptのStringのように書きます(例:
"integer"
) - 構文規則の入口のruleには必ずstartという名前をつけてください
- ruleは空白,空行によって分けてください.セミコロン(
;
)で分けることも可能です -
parser action:ruleの中にあらわれる
{}
で囲まれた部分です.マッチした結果をどのように変換するかを設定します.例だと,parseIntを使ってNumber型に変換しています(例:{ return parseInt(digits.join(""), 10); }
)
構文例2(initializer)
以下の例ようにinitializerを使用することもできます.最初の{
と}
で囲まれたJavaScriptがinitializerです.
{
function makeInteger(o) {
return parseInt(o.join(""), 10);
}
}
start
= additive
additive
= left:multiplicative "+" right:additive { return left + right; }
/ multiplicative
multiplicative
= left:primary "*" right:multiplicative { return left * right; }
/ primary
primary
= integer
/ "(" additive:additive ")" { return additive; }
integer "integer"
= digits:[0-9]+ { return makeInteger(digits); }
- initializerは構文解析器が作られる前に実行されてから,構文解析が実行されます.
- initializerで定義されたすべての変数,関数がruleの中で使用できます.
- initializerでは特別な変数optionsにもアクセスすることができます.
構文規則の書き方
-
"literal",'literal'
String型の定数です.literalの後にiをつけると大文字と小文字を区別しなくなります.
-
.(ドット)
任意の1文字です.
-
[characters]
文字列の集合charactersの中の任意の1文字です.例えば,
[a-z]なら任意の小文字1文字という意味になります. -
rule
ruleを再帰的に適用します.
-
( expression )
部分式を適用します.
-
expression *
expressionの0以上の繰り返しです.
-
expression +
expressionの1以上の繰り返しです.
-
expression ?
expressionが0または1個です.
-
& expression
expressionに適合した場合,undefinedを返します.(構文解析失敗)
-
! expression
expressionに適合しなかった場合,undefinedを返します.
-
& { predicate }
predicateはJavaScriptコードで,関数のように実行されます.
labeled expression
を変数として内部で使うことができます.内部では
return
を使って値を返します.返された値が
true
だった場合に,このexpressionの値はundefinedになります.predicateは中でinitializerで定義された変数や関数も使用できます.
また,location informationもlocation関数を使って参照できます.
location関数は以下のような値を返します.
{ start: { offset: 23, line: 5, column: 6 }, end: { offset: 23, line: 5, column: 6 } }
start
とend
は構文解析中の位置を表します.offset
は0始まりで,line
とcolumn
は1始まりです.また,options変数も参照できます.
-
! { predicate }
上とは逆で,返した値が
false
ならundefinedになります. -
\$ expression
expressionに適合したら,適合した文字列を返します.
-
label : expression
expressionにラベルをつけます.ラベルをつけたexpressionはpredicateなどで使用することができます.
-
expression1 expression2 ... expression n
配列をそれぞれexpressionで評価し,それぞれの返り値の配列を返します.
-
expression { action }
expressionに適合したらactionが実行されて,その返り値を返します.actionの書き方はpredicateと同じです.
-
expression1 / expression2 / ... / expression n
最初のexpression1から順番に評価していき,適合したらその値を返し,失敗したら次のexpressionを評価します.
おまけ(簡易Markdownの構文規則)
HeadingとParagraphだけのSimpleMarkdownのParserをPEG.jsで作ってみました.出力はhtmlです.これから改良していこうと思います.
start
= SimpleMarkdown
SimpleMarkdown
= b:Blocks {return "<html>\n<body>\n"+b+"</body>\n</html>\n"}
Blocks
= b:Block bs:Blocks {return b + bs}
/ $ ""
Block
= e:Element d:Devider {return e + d}
Element
= "###### " t:Heading6 {return t}
/ "##### " t:Heading5 {return t}
/ "#### " t:Heading4 {return t}
/ "### " t:Heading3 {return t}
/ "## " t:Heading2 {return t}
/ "# " t:Heading1 {return t}
/ Paragraph
Heading1
= t:InnerText {return "<h1>"+t+"</h1>"}
Heading2
= t:InnerText {return "<h2>"+t+"</h2>"}
Heading3
= t:InnerText {return "<h3>"+t+"</h3>"}
Heading4
= t:InnerText {return "<h4>"+t+"</h4>"}
Heading5
= t:InnerText {return "<h5>"+t+"</h5>"}
Heading6
= t:InnerText {return "<h6>"+t+"</h6>"}
Paragraph
= t:InnerText {return "<p>"+t+"</p>"}
InnerText
= t1:[^(' '\t\r\n)] t2:[^\n]* [\n] t3:InnerText {return t1+t2.join("")+t3}
/ t1:[^(' '\t\r\n)] t2:[^\n]* {return t1 + t2.join("")}
Devider
= ( EOF
/ [\n] _ EOF
/ [\n] _ [\n] [' '\t\r\n]* ) {return "\n"}
_ "whitespace"
= [' '\t\r]*
EOF "EOF"
= !.
# 転生したらカニだった件 ~おれのカニみそが超絶美味すぎて人生無双~
## あらすじ
トラックにひかれた田中は,目が覚めると異世界のカニになっていた.
めちゃくちゃ美味なカニみそを駆使し,異世界を牛耳ることを考える田中.
するとそこに,カニの天敵タコが現れ...?!
## 価格
50円です.
### 税込み価格
55円です.(軽減税率適用外)
## 感想
最初はふざけてる話かなと思ったけど,
最後はとっても感動するのかなと思ったけど,
やっぱりふざけていた!(40代女性)
読めば読むほど意味が分からない話.
古本屋でも買い取ってくれない.(30代男性)
<html>
<body>
<h1>転生したらカニだった件 ~おれのカニみそが超絶美味すぎて人生無双~</h1>
<h2>あらすじ</h2>
<p>トラックにひかれた田中は,目が覚めると異世界のカニになっていた.めちゃくちゃ美味なカニみそを駆使し,異世界を牛耳ることを考える田中.するとそこに,カニの天敵タコが現れ...?!</p>
<h2>価格</h2>
<p>50円です.</p>
<h3>税込み価格</h3>
<p>55円です.(軽減税率適用外)</p>
<h2>感想</h2>
<p>最初はふざけてる話かなと思ったけど,最後はとっても感動するのかなと思ったけど,やっぱりふざけていた!(40代女性)</p>
<p>読めば読むほど意味が分からない話.古本屋でも買い取ってくれない.(30代男性)</p>
</body>
</html>
Author And Source
この問題について(PEG.jsの構文規則の書き方), 我々は、より多くの情報をここで見つけました https://qiita.com/uwaai/items/b735038eb5a16c93ed7d著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .