Pegjs: 能够忽略某些产品

创建于 2010-10-08  ·  29评论  ·  资料来源: pegjs/pegjs

能够告诉词法分析器/解析器忽略某些产生式(即空格和注释产生式)会很好,这样就没有必要用注释/空格允许乱扔所有其他产生式。 但这可能是不可能的,因为词法分析是内置的解析?

谢谢

feature

最有用的评论

@atesgoral - 我退出了。 我不需要“真正的解析器”——我只需要隔离目标文件中的某些命名元素。

所以我做了任何懦弱的人都会做的事情——使用正则表达式。 (然后我有两个问题:-)

但它成功了,所以我能够继续下一个挑战。 祝你的项目好运!

所有29条评论

同意。 目前有没有干净的方法来做到这一点?

@benekastah :目前还没有干净的方法。

如果不改变 PEG.js 的工作方式,这将很难做到。 可能的解决方案包括:

  1. 允许在生成的解析器之前添加词法分析器。
  2. 在语法中的某处嵌入有关 ingorined 规则的信息。 这可能也意味着区分语法的词汇和句法级别——这是我想避免的。

我现在不会处理这个问题,但这是功能中需要考虑的事情。

我需要这个功能。

也许你可以引入一个“跳过”-令牌。 因此,如果规则返回该令牌,它将被忽略并且不会在 AST 上获得任何节点(也就是数组中的条目)。

我也在寻找一种方法来做到这一点。

我有一个很大的语法文件(它解析 SNMP MIB 文件的 ASN.1 格式)。 我没有写,它,但我简单地将它从原始形式转换为在 PEG.js 中创建解析器。 (这很好。事实上,我花了不到 15 分钟的时间调整它以便 PEG.js 接受它,这是非常巧妙的。)

不幸的是,语法的编写具有在遇到空格和注释时简单地忽略它们的能力。 因此,无法处理真正的 MIB 文件,因为解析器在第一次出现空格时停止。

我并不急于弄清楚语法,以便我可以在所有规则中插入所有正确的空格(大约有 126 个产生式......)还有其他方法可以做到这一点吗?

注意:如果我必须手动修改语法,我会在 Google Groups 列表中的工单中请求帮助解决一些问题。 http://groups.google.com/group/pegjs/browse_thread/thread/568b629f093983b7

非常感谢!

感谢 Google 网上论坛的人们。 我想我得到了足够的信息来让我做我想做的事。

但我真的很期待 PEG.js 能够将空白/注释标记为完全忽略的东西,这样我就不必花几个小时来修改原本干净的语法......谢谢!

富有的

我同意 pegjs 需要能够跳过令牌的断言。 我可能会研究一下,因为如果你想写一个严肃的语法,你会在每个标记之间放置 ws 时变得疯狂。

由于生成的解析器是模块化的。 作为一种解决方法,创建一个简单的词法分析器并将其输出用作真实词法分析器的输入,例如:

elideWS.pegjs:

s = 输入:(whitespaceCharacter / textCharacter)*
{
var 结果 = "";

for(var i = 0;i < input.length;i++) 结果 += input[i];
返回结果;
}

whitespaceCharacter = [ nt] { 返回“”; }
textCharacter = c:[^ nt] { 返回 c; }

但是当空格是分隔符时会导致问题 - 就像标识符一样

经常碰到这个问题。
但是编写一个好的词法分析器并不容易(你最终可能会复制大量的初始语法以获得一个连贯的词法分析器)。

我在想的是能够定义跳过规则,只要没有匹配项,就可以将其用作替代方案。 不过,这引入了对不间断类的需求。 使用浮点数的arithmetics.pegjs示例

  = Term (("+" / "-") Term)*

Term
  = Factor (("*" / "/") Factor)*

Factor
  = "(" Expression ")"
  / Float

Float "float"
  = "-"? # [0-9]+ # ("." # [0-9]+) // # means that skip rules cannot match

// skip rule marked by "!="
// skip rules cannot match the empty string
_ "whitespace"
  != [ \t\n\r]+

还在消化这个。 任何反馈? 可能是一个非常愚蠢的想法。

所以区别在于你要区分整体引擎是什么时候
在词法分析器模式下操作(空格很重要),否则(空格很重要)
忽略)。

在词法分析器模式下,是否存在不想忽略空格的情况
作为一种选择? 或者相反,当不在正则表达式中时? 我觉得不行。

以下是等价的吗?

漂浮
"-?[0-9]+("." [0-9]+)"

或以其他方式扩展挂钩以直接和外部处理典型的正则表达式
带引号的字符串(包括正则表达式)空格被忽略。

在2014年4月19日,在下午3:22,安德烈Neculau [email protected]写道:

经常碰到这个问题。
但是编写一个好的词法分析器并不容易(你最终可能会复制大量的初始语法以获得一个连贯的词法分析器)。

我在想的是能够定义跳过规则,只要没有匹配项,就可以将其用作替代方案。 不过,这引入了对不间断类的需求。 使用 Floats 的 algorithms.pegjs 示例

表达
= 期限 (("+" / "-") 期限)*

学期
= 因子 (("_" / "/") 因子)_

因素
= "(" 表达式 ")"
/ 漂浮

浮动“浮动”
=“-”? # [0-9]+ # ("." # [0-9]+) // # 表示跳过规则无法匹配

// 跳过标记为“!=”的规则
// 跳过规则不能匹配空字符串
_“空白”
!= [ tnr]+
还在消化这个。 任何反馈? 可能是一个非常愚蠢的想法。


直接回复此邮件或在 GitHub 上查看。

@waTeim实际上没有。

传统上,解析过程分为词法分析和解析。 在词法分析期间,每个字符都很重要,包括空格。 但是这些随后被词法化为“丢弃”令牌。 解析器在前进到下一个标记时,将丢弃任何丢弃的标记。 重要的是你可以丢弃任何东西,而不仅仅是空格。 这种行为正是@andreineculau所描述的。

如何实现这一点的基本思想是在从一个状态转换到下一个状态时需要额外检查所有丢弃规则。

2014 年 4 月 23 日下午 2:54,Sean Farrell [email protected]写道:

@waTeim实际上没有。

所以我们同意。 传统的方法就足够了。 没有必要
严格解析器部分识别丢弃标记的存在,并且有
没有理由让词法分析器部分有条件地表现(以上下文敏感的方式)
wrt 识别令牌。

因此不需要在语言中使用粘合元素(例如“#”)
因为这就足够了

1) 令牌可以仅从正则表达式创建,并且与上下文无关。
2) 令牌可以被标记为无一例外地被丢弃。

传统上,解析过程分为词法分析和解析。 在词法分析期间,每个字符都很重要,包括空格。 但是这些随后被词法化为“丢弃”令牌。 解析器在前进到下一个标记时,将丢弃任何丢弃的标记。 重要的是你可以丢弃任何东西,而不仅仅是空格。 这种行为正是@andreineculau所描述的。

如何实现这一点的基本思想是在从一个状态转换到下一个状态时需要额外检查所有丢弃规则。


直接回复此邮件或在 GitHub 上查看。

好吧,那我误会你了。 词法分析器状态可能存在一些情况,但这是完全不同的要求,恕我直言,超出了 peg.js 的范围。

@waTeim @rioki忘记我的建议吧。

动手,遵守这个规则。 如果你想通过收走了,简化了规则的语法*WS ,那么你会怎么请示PEGjs不允许*WS之间field_name:

@andreineculau因为您的语法对空格敏感,所以这不适用。 丢弃标记将是语法的一部分,准确地说是词法部分。 我不知道这里有什么大问题,这在 70 年代已经充分解决了。 每种语言都有自己的可跳过标记以及它们适用的地方。 空格和注释是语言定义的一部分,因此也是语法的一部分。 事实证明,对于大多数语言,可跳过的标记可能位于每个其他标记之间,并且使用丢弃规则比为每个规则编写expr = WS lit WS op WS expr WS ";"更简单。 想象一下像 C 的语法一样的具有空白处理的语法吗?

我知道将丢弃规则重新引入 pegjs 并不容易,但这并不意味着它不是一个值得称赞的目标。

哦,伙计,免费回复部分! 我有很多话要说,抱歉篇幅过长。

1) 对于 TL; DR 的人,如果我可以添加任何我想要的挂钩元素,我会这样写

标题字段
= field_name ":" field_value

空格(忽略)
= [t ]+

我要添加的是一个选项部分,可以包含在任何产品中

http-bis 语言将不受此重写的限制(参见附录 a)。

2)我对建议的问题#

感觉就像你在交换要求用户用一堆填充解析器定义
丢弃非终端(通常是空格/分隔符)并要求用户填写
带有一堆“这里的字符不被丢弃”元字符的解析器定义
不必要地。 诚然,这种情况的发生会更少。 这是罕见的情况,当
人们确实使用分隔符并用它们做一些事情,就像我在评论

附录 a,HTTP-bis 不是这些事件之一,只是记录不当。

3) 用户定义的解析器状态

但是我可以看到在解析器定义器上简单地剪切和粘贴
定义中的语言规范,所以如果你必须有这样的东西,那么
这可以通过肖恩早先提到的词汇状态来完成。 我想我会这样做
通过以下方式。

生产1(状态== 1)
= 东西

生产2(状态== 2)
= 东西

生产3
= 东西 {状态 = 1}

生产4
= 东西 {状态 = 2}

换句话说,就像 lex/yacc 一样,可以让产品只可用

如果系统处于特定状态,并允许用户设置该状态值。

4) 更多选择

或者你可以让用户更容易,让读者更清楚地看到另一个
选项

生产(不要忽略)
= 东西

这将允许解析器覆盖丢弃标记的令牌的默认操作
作为丢弃,但仅用于该生产。 这真的和3一样,只是更简单
读。 这不如 # 提案灵活,因为要么生产都被忽略

或者不忽略,但我认为不需要额外的灵活性。

5) 向 getNextToken() 添加参数允许上下文敏感

我认为这一切归结为(我在这里做一些假设)目前,
解析器部分调用 getNextToken(input),而需要发生的是添加一个

它的参数 getNextToken(input,options)。

附录 a) HTTP-bis 规范

好的,我读了一些,但还没有读完所有这些

超文本传输​​协议 (HTTP/1.1):消息语法和路由
Draft-ietf-httpbis-p1-messaging-26

我不喜欢他们定义语法的方式。 我不建议改变输入它
接受,但我不会像他们那样定义它。 特别是,我不喜欢为什么他们有
定义了 OWS、RWS 和 BWS,它们都等同于完全相同的字符串
但在不同的背景下。 他们定义了

OWS ::== (SP | HTAB)*
RWS ::== (SP | HTAB)+
BWS ::== OWS

这只是制表符和空格的重复

没有充分的理由。 他们使语言更难解析——需要词法分析器
跟踪它的上下文——他们不需要这样做。

他们将 OWS 定义为“可选空白” BWS 为“坏空白”或其他可选
空白,但在“坏”上下文中 - 没有必要 - 而 RWS 需要空白
分隔令牌所必需的。 除了可能有一个解析器之外,没有任何地方使用这个空格
警告,如果它匹配 BWS(“检测到不必要的尾随空格”或类似的),这就是全部
无论如何,分隔符都可以。

在他们的规范中,唯一使用 RWS 的地方是这里

Via = 1#(收到-协议RWS收到-由[RWS评论])

 received-protocol = [ protocol-name "/" ] protocol-version
                     ; see Section 6.7
 received-by       = ( uri-host [ ":" port ] ) / pseudonym
 pseudonym         = token

但是'protocol-version' 是数字和字母,而'received-by' 是数字和字母。 换句话说,
词法分析器不会正确识别这两个部分,除非它们被空格分隔
如果没有至少 1 个,无论是否明确识别 RWS,都会出现语法错误
空白字符。 所以只需从制作中完全删除 RWS 并处理空白
无处不在作为分隔符,它不会改变语言,只是它的记录方式。

在2014年4月24日,在下午1:23,安德烈Neculau [email protected]写道:

@waTeim @rioki忘记我的建议吧。

动手,遵守这个规则。 如果您想通过去掉 OWS 来简化规则的语法,那么您将如何指示 PEGjs 在 field_name 和 : 之间不允许 OWS?


直接回复此邮件或在 GitHub 上查看。

@waTeim我认为您对此

我从未见过任何正确使用源自解析器的词法分析器状态。 这里的基本问题是,先看一下,当解析器看到要切换状态的标记时,词法分析器已经错误地对下一个标记进行了词法分析。 如果没有回溯,您提出的建议几乎不可能实现,这在解析器中从来都不是一个好功能。

在编写语法时,您基本上定义了哪些产生式被认为是解析的,哪些可以被啜饮。 在@andreineculau的示例中,有两个选项,要么处理解析器中的空格,要么定义标记的尾随“:”部分。 ( [a-zA-Z0-9!#$%&'+-.^_|~]+ ":" )。

我可能建议将问题转化为指定白名单——我想要捕获和转换哪些部分——而不是黑名单。 尽管空白是当前捕获系统的一个问题,但规则的嵌套是另一个问题。 正如在问题 #66 中

有关捕获方面的 LPeg 与 PEG.js 的简单示例,请参阅我在问题 #66 中的评论。 尽管名称有点含糊,但请参阅 LPeg 文档的“捕获”部分,了解捕获或转换给定产品(或其一部分)的各种方法。

您好,我创建了一个片段来忽略一些一般情况: nullundefined和只有空格符号的字符串。
它可以在语法文件的头部需要,例如:

{
  var strip = require('./strip-ast');
}

两种改进方法:

  • 可定制的术语过滤器——忽略需要特定语法的特定术语。
  • 跳过嵌套的空数组——这可以在strip之后的第二阶段完成,它将删除嵌套空数组的“金字塔”。
    如果有人感兴趣,我们可以将其升级为软件包。

@richb-hanover 您的 ASN.1 定义解析器工作在哪里?

@atesgoral - 我退出了。 我不需要“真正的解析器”——我只需要隔离目标文件中的某些命名元素。

所以我做了任何懦弱的人都会做的事情——使用正则表达式。 (然后我有两个问题:-)

但它成功了,所以我能够继续下一个挑战。 祝你的项目好运!

看过chevrotain 及其跳过选项后,非常需要这样的东西。

我们经常发现自己在写这样的东西:

Pattern = head:PatternPart tail:( WS "," WS PatternPart )*
{
  return {
    type: 'pattern',
    elements: buildList( head, tail, 3 )
  };
}

如果我们可以这样写会很酷:

WS "whitespace" = [ \t\n\r] { return '@<strong i="11">@skipped</strong>' }

IgnoredComma = "," { return '@<strong i="12">@skipped</strong>' }

Pattern = head:PatternPart tail:( WS IgnoredComma WS PatternPart )*
{
  return {
    type: 'pattern',
    elements: [head].concat(tail)
  };
}

@ richb -汉诺威,和其他人谁在寻找同样需要来到这里,最后我写我自己的解析器,太: https://www.npmjs.com/package/asn1expHTTPS://www.npmjs。 com/package/asn1-tree

使用es6 symbol可以相对容易地实现跳过,或者通过在解析时向解析器传递一个谓词可能更持久(我更喜欢后一个选项)

也偶然发现了这一点。
对 PEG.js 的内部一无所知,让我在那里扔骨头......

当我们编写规则时,在它的末尾我们可以添加一个返回块。
在那个块中,我们可以调用text()location() 。 这些是内部函数。

在代码的某个地方,该块的返回值进入输出流。

那么,如果我想跳过规则返回的值,如果该值是调用skip本地函数的返回值,那么在 PEG.js 中需要更改什么?

例如comment = "//" space ([^\n])* newline { return skip() }

如上所述,skip() 可以返回一个符号,然后在某处由代码检查并删除。
类似于 lzhaki 所说的,但在图书馆内部

我不明白你的问题。 您是否正在寻找一种在某些情况下使规则失效的方法? 使用&{...}!{...} 。 否则就不要使用comment规则的返回值:

seq = comment r:another_rule { return r; };
choice = (comment / another_rule) { <you need to decide what to return instead of "comment" result> };

如果它对任何人有帮助,我会通过让我的顶级规则过滤结果数组来忽略空格。

例子:

    = prog:expression+ {return prog.filter(a => a)}

expression
    = float
    / number
    / whitespace

float
    = digits:(number"."number) {return parseFloat(digits.join(""),10)}

number 
    = digits:digit+ {return parseInt(digits.join(""),10)}

digit 
    = [0-9]

whitespace
    = [ \t\r\n] {return undefined}

这将愉快地解析输入,同时将空格排除在结果数组之外。
这也适用于评论之类的事情,只需让规则返回未定义,顶级规则就会将其过滤掉

这只适用于顶级制作。 您必须手动过滤可能包含可过滤子项的每个父项。

@StoneCypher True,它确实需要一些顶级工作,但它对我有用,而且我认为只要 gammar 不是太复杂,就应该能够使用顶级过滤器。

除此之外,我能想到的就是有一个顶级函数来过滤输入中的空白并通过它传递每个匹配项。 肯定会更慢,并且需要更多的调用,但是如果您(像我一样)将所有内容都传递到令牌生成器中,则很容易。 您可以从生成令牌的地方调用过滤器函数,只需要担心生成令牌和空格或多或少会自动过滤

我喜欢 pegjs 当前 HEAD 的一件事是它(未记录的)支持选择字段,而无需创建标签和返回语句。 它看起来像这样:

foo = <strong i="6">@bar</strong> _ <strong i="7">@baz</strong>
bar = $"bar"i
baz = $"baz"i
_ = " "*
parse('barbaz') // returns [ 'bar', 'baz' ]

我觉得这为这个用例以及其他一些用例提供了漂亮、干净、明确的语法。

此页面是否有帮助?
0 / 5 - 0 等级