考え方
- インデントする度に、現在のインデントコンテキストを保存する
- peg.jsの
& { pred... }
で、次のブロックがindentしたブロックかどうか判定する - dedentするときには indentStackを巻き戻す
{
global.ctx = {}
ctx.indentStack = []
ctx.indent = ""
}
start = program
program = lines:line* { type: 'program', body: lines}
line = expr
expr = SAMEDENT s:symbol + EOL? children: ( INDENT c:line* DEDENT { c })?
{
{type: 'expr', value: s, children}
}
symbol = $([a-zA-Z\d]+)
EOL = "\r\n" / "\n" / "\r"
SAMEDENT
= i:[ \t]* &{
i.join('') is ctx.indent
}
INDENT
= &(
i:[ \t]+ &{
i.length > ctx.indent.length
}
{
ctx.indentStack.push(ctx.indent)
ctx.indent = i.join("")
}
)
DEDENT
= {
ctx.indent = ctx.indentStack.pop()
}
パース用テキスト
a
f
k
b
t
c
d
実行
{ type: 'program',
body:
[ { type: 'expr',
value: [ 'a' ],
children:
[ { type: 'expr',
value: [ 'f' ],
children: [ { type: 'expr', value: [ 'k' ], children: null } ] },
{ type: 'expr', value: [ 'b' ], children: null } ] },
{ type: 'expr',
value: [ 't' ],
children:
[ { type: 'expr', value: [ 'c' ], children: null },
{ type: 'expr', value: [ 'd' ], children: null } ] } ] }