【问题标题】:Parse indentation level with PEG.js使用 PEG.js 解析缩进级别
【发布时间】:2012-07-24 10:29:52
【问题描述】:

我的问题与PEG for Python style indentation 基本相同,但我想获得更多关于this answer 的指导。

答案成功地生成了一个字符串数组,这些字符串是每行输入,行之间有“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 = '}'

使用缩进代替大括号来描绘块,并且仍然得到相同的输出?

(使用http://pegjs.majda.cz/online 通过以下输入测试该语法:a{bcd{zyx{}}}

【问题讨论】:

  • 请举例说明您希望输入的外观

标签: javascript parsing syntax peg


【解决方案1】:

2021 年更新

这是一个在online playgroundPeggy.js 中运行的工作示例。 Peggy.js 是积极开发中的 PEG.js 的一个分支。 PEG.js 已被 David Maida 停用。

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

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

(文件顶部)

  • {{...}}(全局初始化程序)- 在解析器生成时运行 ...
  • {...}(每解析初始化器)- 在解析器实例化时运行 ...

(在文件中)

  • X = Y {...}(动作)——当Y成功时执行...。初始化程序中的变量可用。如果... 返回某些内容,它将替换Y 返回的内容。
  • $X – 返回使用 X 解析的文本,而不是其结果。
  • ... @X ...(pluck 运算符)– 将 ... 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')
}

/* Because of this rule, results cache must be disabled */
DEDENT = &{
    indent = indentstack.pop()
    return true
} {
    found('dedent')
}

_ = [ \t]*
EOL = '\r\n' / '\n' / '\r'
NEWLINES = (_ EOL)+

/* Test with this input

H:
  a
  b
  c
  G:
    d
    e
    f

*/

旧答案

这是对@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() 函数,它返回一个包含偏移量和其他信息的对象。

// 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(""); })

DEDENT
  = &{ indent = indentStack.pop(); return true;}

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

【讨论】:

    【解决方案2】:

    解析器:

    // 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(); }
    

    输入:

    a
      b
      c
      d
        z
        y
        x
    

    输出:

    {
       "a": [
          "b",
          "c",
          {
             "d": [
                "z",
                "y",
                "x"
             ]
          }
       ]
    }
    

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

    【讨论】:

    • 我非常感谢这个答案。请问这个方法是怎么想出来的?
    • 如果我将它直接复制/粘贴到pegjs.majda.cz/online 中,它不会编译。经过一些调整后,尚不清楚如何“修复”它。
    • 刚刚测试过,sn-p 编译并产生预期的输出就好了。不知道你在那里遇到了什么错误。
    • 如何处理回溯情况?如果您有一组包含多个可能子表达式的优先规则,那么 indent.stack 不会被第一个子表达式中的一系列合法匹配搞砸,然后在解析第二个子表达式时出现混乱?
    • @joeriks:如果它仍然有用的话,我已经固定在 v0.8.0 上工作。
    猜你喜欢
    • 1970-01-01
    • 2021-10-16
    • 2011-04-06
    • 1970-01-01
    • 1970-01-01
    • 2021-12-02
    • 2022-01-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多