Javascript 使用PEG.js解析缩进级别

Javascript 使用PEG.js解析缩进级别,javascript,parsing,syntax,peg,Javascript,Parsing,Syntax,Peg,我有一个基本上与相同的问题,但我想了解更多的方向 答案成功地生成了一个字符串数组,这些字符串是每行输入之间带有“INDENT”和“DEDENT”的字符串。看起来他几乎习惯于使用PEG.js来标记,但是没有真正的解析发生 那么我如何扩展他的示例来进行一些实际的解析呢 例如,如何更改此语法: start = obj obj = id:id children:(indent obj* outdent)? { if (children) { let o =

我有一个基本上与相同的问题,但我想了解更多的方向

答案成功地生成了一个字符串数组,这些字符串是每行输入之间带有“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 = '}'
使用缩进而不是大括号来描绘块,并且仍然得到相同的输出

(用于使用以下输入测试该语法:
a{bcd{zyx{}}

解析器:

// 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
规则,当缩进级别没有改变时,它会成功<代码>缩进和
DEDENT
更改当前缩进级别而不更改文本中的位置
pos=offset

这里是@Jakub Kulhan语法的一个补丁,它在PEG.js v 0.10.0中工作。最后一行需要更改为
=&{indent=indentStack.pop();return true;}
,因为PEG.js现在不再允许语法中的独立操作(
{…}
)。这一行现在是一个谓词(
&{…}
),它总是成功(
返回true;

我还删除了
pos=offset因为它给出了错误
未定义偏移量
。可能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;}

从V0.11.0peg.js开始,还支持,
@
,这将使编写此语法更加简单,但由于它目前不在联机解析器中,我将避免将其添加到此示例中。

此示例使用冒号(
)来分隔对象和简单字母。这样,它也可以以对象结束,但冒号是必需的。与问题中的示例一样,它不考虑可忽略的空格(例如冒号之前)。它基于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:

我真的很感激你。你是怎么想出这个方法的?如果我直接复制/粘贴到它里面,它不会编译。经过一些调整后,还不清楚如何“修复”它。刚刚测试过,代码段编译并生成了预期的输出。不确定您在那里遇到了什么错误。这是如何处理回溯案例的?如果您有一组包含多个子表达式的先行规则,indent.stack会不会因为第一个子表达式中的一系列合法匹配而出错,然后在解析第二个子表达式时会出现混乱?@joeriks:如果仍然有用,我已经决定使用v0.8.0。请给出一个示例,说明您希望输入的外观