2021更新
这是一个在在线 playground上运行的可工作示例,使用了Peggy.js。Peggy.js是活跃开发中的PEG.js的分支。PEG.js已被David Maida停止维护。
该示例展示了如何解析INDENT
、SAMEDENT
和DEDENT
规则以及如何使用解析位置。请查看控制台日志。
它使用了其他解析器生成器可能不知道的这些语法:
(文件顶部)
{{...}}
(全局初始化器)- 在解析器生成时运行...
。
{...}
(每次解析初始化器)- 在解析器实例化时运行...
。
(文件内)
X {...}
(操作)- 当 X
成功时执行 ...
。可以使用初始值设定项中的变量。如果 ...
返回了某些内容,则会替换 X
的返回值。
$X
- 返回使用 X
解析的原始文本,而不是 X 的结果。
... @X ...
(pluck 运算符)- 用 X
的结果替换 ... X ...
的结果。
X &{...}
(谓词)- "X 的成功还需要满足 ...
的条件"。
X = &(...)
- 如果 ...
成功,则 X
成功。 ...
不消耗任何输入。
请参阅 docs 以获取更多信息。
{{
console.clear()
console.log('Parser generated')
}}
{
let indentstack = []
let indent = ''
function found (what) {
let loc = location()
console.log(`[${loc.start.line}:${loc.start.column} - ${loc.end.line}:${loc.end.column}] found ${what}`)
}
console.log('Parser instantiated')
}
DOCUMENT = NEWLINES? @THINGS NEWLINES? _
THINGS = ( SAMEDENT @( OBJECT / LINE ) )*
OBJECT = key:KEY childs:(BLOCK / INLINE) {
found(`object "${key}"`)
let o = {}
o[key] = childs
return o
}
KEY = @$( [^ \t\r\n:]+ ) _ ':' _
BLOCK = NEWLINES INDENT @THINGS DEDENT
INLINE = line:LINE { return [line] }
LINE = text:$( (!EOL .)+ ) NEWLINES? {
found(`line "${text}"`)
return text
}
INDENT = &(
spaces:$( [ \t]+ ) &{
return spaces.length > indent.length
} {
indentstack.push(indent)
indent = spaces
}
) {
found('indent')
}
SAMEDENT = spaces:$( [ \t]* ) &{
return spaces === indent
} {
found('samedent')
}
DEDENT = &{
indent = indentstack.pop()
return true
} {
found('dedent')
}
_ = [ \t]*
EOL = '\r\n' / '\n' / '\r'
NEWLINES = (_ EOL)+
旧答案
这是对@Jakub Kulhan语法的修复,适用于PEG.js v 0.10.0。最后一行需要更改为= &{ indent = indentStack.pop(); return true;}
,因为PEG.js现在不再允许在语法中使用独立的动作({...}
)。这一行现在是一个谓词(&{...}
),它总是成功的(return true;
)。
我还删除了pos = offset;
,因为它会产生一个错误offset is not defined
。可能Jakub是指旧版本的PEG.js中可用的一些全局变量。PEG.js现在提供了location()
函数,该函数返回一个包含偏移量和其他信息的对象。
{ var indentStack = [], indent = ""; }
start
= INDENT? l:line
{ return l; }
line
= SAMEDENT line:(!EOL c:. { return c; })+ EOL?
children:( INDENT c:line* DEDENT { return c; })?
{ var o = {}; o[line] = children; return children ? o : line.join(""); }
EOL
= "\r\n" / "\n" / "\r"
SAMEDENT
= i:[ \t]* &{ return i.join("") === indent; }
INDENT
= &(i:[ \t]+ &{ return i.length > indent.length; }
{ indentStack.push(indent); indent = i.join(""); })
DEDENT
= &{ indent = indentStack.pop(); return true;}
从v 0.11.0开始,PEG.js还支持值提取运算符@
,这将使编写该语法更加简单,但由于它目前不在在线解析器中,我将不会将其添加到此示例中。