用PEG.js解析缩进级别

Tre*_*xon 9 javascript syntax parsing peg

我有与Python样式缩进的PEG基本相同的问题,但我想对这个答案有更多的指导.

答案成功生成了一个字符串数组,每行输入行之间有'INDENT'和'DEDENT'.看起来他几乎用PEG.js来标记,但是没有真正的解析.

那么我怎样才能扩展他的例子来做一些实际的解析呢?

作为一个例子,我该如何改变这个语法:

start = obj
obj = id:id children:(indent obj* outdent)?
    {
        if (children) {
            let o = {}; o[id] = children[1];
            return o;
        } else {
            return id;
        }
    }
id = [a-z]
indent = '{'
outdent = '}'
Run Code Online (Sandbox Code Playgroud)

使用缩进而不是大括号来描述块,仍然得到相同的输出?

(使用http://pegjs.majda.cz/online来测试语法具有以下输入:a{bcd{zyx{}}})

Jak*_*han 18

分析器:

// do not use result cache, nor line and column tracking

{ 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(""); pos = offset; })

DEDENT
  = { indent = indentStack.pop(); }
Run Code Online (Sandbox Code Playgroud)

输入:

a
  b
  c
  d
    z
    y
    x
Run Code Online (Sandbox Code Playgroud)

输出:

{
   "a": [
      "b",
      "c",
      {
         "d": [
            "z",
            "y",
            "x"
         ]
      }
   ]
}
Run Code Online (Sandbox Code Playgroud)

它无法解析空对象(最后一个x),但它应该很容易解决.这里的伎俩是SAMEDENT规则,当缩进级别没有改变时它会成功.INDENTDEDENT更改当前缩进级别而不更改文本中的位置pos = offset.


Nil*_*ils 5

2021 年更新

\n

这是一个在Peggy.js在线 Playground中运行的工作示例。Peggy.js 是 PEG.js 的一个分支,正在积极开发中。David Maida 终止了 PEG.js。

\n

该示例显示了如何解析INDENTSAMEDENTDEDENT规则,以及如何使用解析位置。检查控制台日志。

\n

它使用这些语法,其他解析器生成器可能不知道这些语法:

\n

(文件顶部)

\n
    \n
  • {{...}}(全局初始值设定项) \xe2\x80\x93...在解析器生成上运行。
  • \n
  • {...}(每个解析初始值设定项) \xe2\x80\x93...在解析器实例化上运行。
  • \n
\n

(文件内)

\n
    \n
  • X {...}(action) \xe2\x80\x93成功...时执行X。初始化器中的变量可用。如果...返回某些内容,它将替换X返回的内容。
  • \n
  • $X\xe2\x80\x93 返回用 解析的原始文本X,而不是 X 的结果。
  • \n
  • ... @X ...(pluck 运算符) \xe2\x80\x93 将 的结果替换... X ... 为 的结果X
  • \n
  • X &{...}(谓词)\xe2\x80\x93“并且...X 必须为真才能成功”。
  • \n
  • X = &(...)\xe2\x80\x93 如果...成功则X成功。...不消耗任何输入。
  • \n
\n

请参阅文档以获取更多信息。

\n
{{\n    console.clear()\n    console.log(\'Parser generated\')\n}}\n\n{\n    let indentstack = []\n    let indent = \'\'\n    function found (what) {\n        let loc = location()\n        console.log(`[${loc.start.line}:${loc.start.column} - ${loc.end.line}:${loc.end.column}] found ${what}`)\n    }\n    console.log(\'Parser instantiated\')\n}\n\nDOCUMENT = NEWLINES? @THINGS NEWLINES? _\n\nTHINGS = ( SAMEDENT @( OBJECT / LINE ) )*\n\nOBJECT = key:KEY childs:(BLOCK / INLINE) {\n    found(`object "${key}"`)\n    let o = {}\n    o[key] = childs\n    return o\n}\n\nKEY = @$( [^ \\t\\r\\n:]+ ) _ \':\' _\n\nBLOCK = NEWLINES INDENT @THINGS DEDENT\n\nINLINE = line:LINE { return [line] }\n\nLINE = text:$( (!EOL .)+ ) NEWLINES? {\n    found(`line "${text}"`)\n    return text\n}\n\nINDENT = &(\n    spaces:$( [ \\t]+ ) &{\n        return spaces.length > indent.length\n    } {\n        indentstack.push(indent)\n        indent = spaces\n    }\n) {\n    found(\'indent\')\n}\n\nSAMEDENT = spaces:$( [ \\t]* ) &{\n    return spaces === indent\n} {\n    found(\'samedent\')\n}\n\n/* Because of this rule, results cache must be disabled */\nDEDENT = &{\n    indent = indentstack.pop()\n    return true\n} {\n    found(\'dedent\')\n}\n\n_ = [ \\t]*\nEOL = \'\\r\\n\' / \'\\n\' / \'\\r\'\nNEWLINES = (_ EOL)+\n\n/* Test with this input\n\nH:\n  a\n  b\n  c\n  G:\n    d\n    e\n    f\n\n*/\n
Run Code Online (Sandbox Code Playgroud)\n
\n

旧答案

\n

这是 @Jakub Kulhan\xc2\xb4s 语法的修复,适用于 PEG.js v 0.10.0。最后一行需要更改为,= &{ indent = indentStack.pop(); return true;}因为 PEG.js 现在不再允许{...}语法中的独立操作 ( )。该行现在是一个谓词 ( &{...}),它总是成功 ( return true;)。

\n

我还删除了pos = offset;因为它给出了错误offset is not defined。Jakub 可能指的是旧版本 PEG.js 中可用的某些全局变量。PEG.js 现在提供了location()返回包含偏移量和其他信息的对象的函数。

\n
// do not use result cache, nor line and column tracking\n\n{ var indentStack = [], indent = ""; }\n\nstart\n  = INDENT? l:line\n    { return l; }\n\nline\n  = SAMEDENT line:(!EOL c:. { return c; })+ EOL?\n    children:( INDENT c:line* DEDENT { return c; })?\n    { var o = {}; o[line] = children; return children ? o : line.join(""); }\n\nEOL\n  = "\\r\\n" / "\\n" / "\\r"\n\nSAMEDENT\n  = i:[ \\t]* &{ return i.join("") === indent; }\n\nINDENT\n  = &(i:[ \\t]+ &{ return i.length > indent.length; }\n      { indentStack.push(indent); indent = i.join(""); })\n\nDEDENT\n  = &{ indent = indentStack.pop(); return true;}\n
Run Code Online (Sandbox Code Playgroud)\n

从 v 0.11.0 开始,PEG.js 还支持Value Plucking 运算符@这将使编写此语法变得更加简单,但由于它当前不在在线解析器中,我将避免将其添加到此示例中。

\n