用PEG.js解析缩进级别

时间:2012-07-25 21:43:37

标签: javascript parsing syntax peg

我的问题基本上与PEG for Python style indentation相同,但我想就this answer提出更多指示。

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

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

举个例子,我该如何改变这个语法:

start = obj
obj = id:id children:(indent obj* outdent)?
      {
          var o = {};
          o[id] = children[1];
          return (children[1] ? o : id);
      }
id = [a-z]
indent = '{'
outdent = '}'

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

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

3 个答案:

答案 0 :(得分: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"
         ]
      }
   ]
}

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

答案 1 :(得分:0)

这里是@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 :(得分:0)

此示例使用冒号(:)来分隔对象和简单字母。这样,它也可以以对象结尾,但是需要冒号。像问题中的示例一样,它不会处理可忽略的空格(例如在冒号之前)。它基于Jakubs Kulhans的示例:

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

{ var indentStack = [], indent = ""; }

Start = Object

Object = Block / Letterline

Block = Samedent id:Letter ':' childs:(
    Newline Indent childs:Object* Dedent {return childs;}
)* {
    if (childs) {
        var o = {}; o[id] = childs.flat().flat();
        return o;
    } else {
        return id;
    }
}

Letterline = Samedent letters:Letter+ Newline? {return letters;}

Letter = [a-z]

Newline = "\r\n" / "\n" / "\r"

Indent = &(
    i:[ ]+ &{
        return i.length > indent.length;
    } {
        indentStack.push(indent);
        indent = i.join("");
    }
)

Samedent = i:[ ]* &{ return i.join("") === indent; }

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

语法将为以下输入产生所需的输出:

a:
  bc
  d:
    zy
    x: