10

对于 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 = '}'

使用缩进而不是大括号来描绘块,并且仍然获得相同的输出?

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

4

2 回答 2

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

输入:

a
  b
  c
  d
    z
    y
    x

输出:

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

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

于 2012-07-28T11:16:18.213 回答
2

2021 年更新

这是一个在Peggy.js的在线游乐场中运行的工作示例。Peggy.js 是积极开发中的 PEG.js 的一个分支。PEG.js 已被 David Maida 终止。

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

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

(文件顶部)

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

(在文件中)

  • X = Y {...}(action) –成功...时执行Y。初始化程序中的变量可用。如果...返回一些东西,它将取代返回的东西Y
  • $X– 返回用 解析的文本X,而不是其结果。
  • ... @X ...(pluck 运算符) – 将 的结果替换为... X ... 的结果X
  • &{...}(谓词)——“而且...也必须是真的”。
  • X = &(...)– 如果...成功,则X成功。...不消耗任何输入。

有关更多信息,请参阅文档

{{
    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

*/

旧答案

这是在 PEG.js v 0.10.0 中工作的@Jakub Kulhan 语法的修复。最后一行需要更改为,= &{ 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 运算符@这将允许更简单地编写此语法,但由于它目前不在在线解析器中,我将避免将其添加到此示例中。

于 2019-07-07T20:54:50.147 回答