【问题标题】:How to force no whitespace in dot notation如何在点符号中强制没有空格
【发布时间】:2022-08-15 01:52:44
【问题描述】:

我正在尝试使用 Ply 实现现有的脚本语言。一切都很好,直到我遇到了在对象上使用点符号的部分。对于大多数操作,空格无关紧要,所以我把它放在了忽略列表中。 \"3+5\" 的工作方式与 \"3 + 5\" 等相同。但是,在使用这种脚本语言的现有程序中(我希望尽可能准确),有无法插入空格的情况,例如 \"this.field.array[5]\" 在标识符和点或括号之间不能有任何空格。有没有办法在解析器规则中指出这一点,而不必处理在其他地方不重要的空格?还是我最好在词法分析器中构建这些项目?

    标签: parsing yacc ply


    【解决方案1】:

    除非您在词法扫描器中执行某些操作以将空格传递给解析器,否则解析器无能为力。

    了解为什么this.field.array[5] 必须写成不带空格会很有用。 (或者,也许,大部分没有空格:也许this.field.array[ 5 ] 是可以接受的。)如果有空格,还有其他解释吗?还是只是脚本语言设计者的一些错误的审美判断?

    第二种情况要简单得多。如果唯一的可能性是没有空格或语法错误的正确解析,则只需要在解析器识别后验证表达式。一个简单的验证函数将简单地检查每个标记的起始位置(可用 p.lexpos(i) 提供,其中 p 是操作函数的参数,i 是生产的 RHS 的标记的索引)正是前一个令牌加上前一个令牌的长度。

    要求索引字段的名称紧跟. 的一个可能原因是简化词法扫描器,以防希望将其他保留字用作成员名称。理论上,任何标识符(包括语言关键字)都没有理由不能用作object.field 这样的表达式中的成员选择器。 . 是一个明确的信号,表明以下标记是成员名称,而不是不同的句法实体。例如,JavaScript 允许任意标识符作为成员名称;虽然它可能会让读者感到困惑,但没有什么能阻止你写obj.if = true

    不过,这对词法扫描器来说是一个很大的挑战。为了正确分析输入流,需要了解每个标识符的上下文;如果标识符紧跟在用作成员选择器的. 之后,则必须禁止关键字识别规则。这可以使用大多数词法分析器生成器中可用的词法状态来完成,但这绝对是一个复杂的问题。或者,可以采用成员选择器是单个标记的规则,包括.。在这种情况下,obj.if 由两个令牌(objIDENTIFIER.ifSELECTOR)组成。最简单的实现是使用\.[a-zA-Z_][a-zA-Z0-9_]* 之类的模式识别SELECTOR。 (这不是 JavaScript 所做的。在 JavaScript 中,不仅可以在 . 和选择器之间插入任意空格,甚至可以在 cmets 之间插入任意空格。)

    根据 OP 的评论,这似乎是设计原始脚本语言的部分原因,尽管它没有解释在 .[ 运算符之前禁止空格。

    有些语言可以根据周围空格的存在与否来解决语法歧义,例如在可以是一元或二元 (Swift) 的消歧运算符中;或将| 用作布尔运算符与将其用作绝对值表达式进行区分(不常见,但请参见https://cs.stackexchange.com/questions/28408/lexing-and-parsing-a-language-with-juxtaposition-as-an-operator);甚至将 (...) 在分组表达式中的使用与它们在函数调用中的使用区分开来。 (例如,Awk)。因此,当然可以想象一种语言,其中. 和/或[ 标记根据周围空白的存在或不存在而具有不同的解释。

    如果您需要区分带有和不带有周围空格的标记的情况,以便语法可以以不同的方式识别它们,那么您需要将空格作为标记传递,这会污染整个语法,或者提供两个(或更多)不同版本的标记,其语法因空格而异。您可以使用正则表达式来做到这一点,但在词法操作本身中这样做可能更容易,再次使用词法分析器状态。请注意,词法分析器状态包括lexdata(输入字符串本身)和lexpos(下一个输入字符的索引);当前标记中第一个字符的索引在标记的lexpos 属性中。因此,例如,如果t.lexpos == 0 or t.lexer.lexdata[t.lexpos-1].isspace(),则令牌前面有空格,如果t.lexer.lexpos == len(t.lexer.lexdata) or t.lexer.lexdata[t.lexer.lexpos].isspace(),则后面有空格。

    一旦你将令牌划分为两种或更多令牌类型,你会发现在大多数制作中你真的不需要划分。因此,您通常会发现为每个表示该标记的所有空白上下文变体的标记类型定义一个新的非终结符很有用;然后,您只需要在重要的产品中使用特定的变体。

    【讨论】:

    • 你是对的,括号内的空格很好。您可以说“this.field.array[5]”,但不能说“this.field.array[5]”或“this.field.array [5]”。尝试这样做会导致“使用的字段语法无效”或“脚本中保留字“字段”的使用无效”的变化。它实际上非常一致地实现,这就是为什么我希望将它编码为一个简单的规则。
    • 不幸的是,除了我可以通过将代码放入程序并查看我得到的错误消息来猜测之外,我不知道如何将代码分解为令牌。我不确定它是否将项目标记为它喜欢的东西然后在解析器上失败,或者它是否更早被捕获。
    • 由于这对我来说是一个玩具项目,我现在可能只是以“不正确”的方式实现它,并记下我可能想稍后检查是否有更好的正确性。谢谢你。
    • @sean:根据您上面提到的错误消息(“无效使用保留字”),我在答案中添加了几段。
    猜你喜欢
    • 2021-12-20
    • 1970-01-01
    • 1970-01-01
    • 2017-05-23
    • 2011-01-17
    • 1970-01-01
    • 1970-01-01
    • 2020-12-09
    • 2021-11-17
    相关资源
    最近更新 更多