【问题标题】:antlr4: need to convert sequences of symbols to characters in lexerantlr4:需要在词法分析器中将符号序列转换为字符
【发布时间】:2015-07-18 23:20:29
【问题描述】:

我正在为 Wolfram 语言编写解析器。该语言具有“命名字符”的概念,由\[ 和] 分隔的名称指定。例如:\[Pi]。

假设我想为一个标识符指定一个正则表达式。标识符可以包括命名字符。我看到了两种方法:一种是使用预处理器将所有命名字符转换为它们的 unicode 表示,另一种是枚举所有可能的命名字符,作为正则表达式的一部分。

第二种方法似乎不可行,因为有很多命名字符。我希望在我的正则表达式中有一系列 unicode 字符。

所以我想预处理我的令牌流。换句话说,在我看来,词法分析器需要检查命名字符的语法是否正确,然后查找名称并将其转换为 unicode。

但如果语法不正确或名称不存在,我需要告诉用户。如何将此错误传播给用户,但让 antlr4 从错误中恢复并恢复?也许我可以使用“管道”词法分析器/解析器? (我是 antlr 的新手)。

编辑:

在 Wolfram 语言中,我可以将此字符串作为标识符:\[Pi]Squared。括号之间的部分称为“命名字符”。有一组有限的命名字符,每个字符对应一个 unicode 代码点。我正在尝试弄清楚如何标记这样的标识符。

我可以为我的令牌设置这样的规则(简化为命名字符和 ASCII 字符的组合):

NAME : ('\\[' [a-z]+ ']'|[a-zA-Z])+ ;

但我想检查命名字符是否确实存在(以及其他属性,例如它是否是字母,但后半部分超出了问题的范围),所以这个正则表达式不起作用。

我考虑过制作一个允许的命名字符列表,然后只制作一个枚举所有这些字符的长正则表达式,但这看起来很难看。

什么是解决这个问题的好方法?

编辑结束

【问题讨论】:

    标签: antlr antlr4


    【解决方案1】:

    一种常见的方法是编写词法分析器/解析器,以允许在语法上正确输入并将语义问题推迟到对生成的解析树的分析。在这种情况下,词法分析器可以天真地接受命名字符:

    NChar : NCBeg .? RBrack ;
    
    fragment NCBeg : '\\[' ;
    fragment LBrack: '['   ;
    fragment RBrack: ']'   ;
    

    更新

    在解析器中,允许 NChar 作为离散的终端节点存在于解析树中:

    idents : ident+ ;
    ident  : NChar   // named character string
           | ID      // simple character string?
           | Literal // something quoted?
           | ....
           ;
    

    这使得解析树的分析变得相当容易:每个ident 上下文将只包含一个非空值,用于离散可识别的alt;并将所有排序问题的分析隔离到idents 上下文中。

    更新2

    对于输入\[Pi]Squared,最容易分析的解析树形式将是具有两个有序子节点\[Pi] 和Squared 的idents。

    最佳做法是不要将两个孩子打包到同一个令牌中 - 只需稍后手动将令牌文本分成两部分,以检查它是否包含有效的命名字符以及特定的部分序列是否允许.

    没有正则表达式允许对命名字符进行最终验证。这将需要一个清单。然而,收紧 NChar 的词法分析器定义可以获得与正则表达式等效的结果:

    NChar : NCBeg [A-Z][A-Za-z]+ RBrack ;
    

    如果担心命名字符后可能有空格,请考虑使用语义警告而不是语法错误来更好地处理这种情况。与其在词法分析器中跳过空格,不如将空格放在隐藏通道上。然后,在每个idents上下文的验证分析中,检查隐藏通道是否有中间空白,并酌情发出警告。

    ----

    然后,解析树访问者可以检查、验证并酌情针对未知或拼写错误的命名字符发出警告。

    要在解析器中进行验证,如果更可取,请使用谓词规则来区分已知和未知的命名字符:

    @members {
        ArrayList<String> keyList = .... // list of named chars
    
        public boolean inList(String id) {
            return keyList.contains(id) ;
        }
    }
    
    nChar    : known
             | unknown
             ;
    
    known    : NChar { inList($NChar.getText()) }?             ;
    unknown  : NChar { error("Unknown " + $NChar.getText()); } ;
    

    inList 函数可以实现距离度量来检测拼写错误,但是直接在解析树中更正文本有点复杂。在访问者操作期间作为解析树装饰实现时更容易做到。

    最后,将named characters 刮擦成可用映射(unicode 和 ascii)可能值得处理表示以及转换和拼写错误。

    【讨论】:

    • 谢谢。你的意思是像ID=( [a-z] | NChar )+ 这样的标识符令牌并在构建解析树后检查命名字符的值?
    • 谢谢。我还没有看到它将如何帮助我制作与标识符的部分匹配的解析器产品。这可能只有在我不跳过空格时才有效,对吧?也许我完全糊涂了……
    • 您的评论在原始问题的上下文中似乎没有意义 - 似乎涉及新问题。如果上面提供的答案有助于解决您在原始帖子中发现的问题,请接受答案。然后发布一个新问题,详细说明您正在尝试做什么以及遇到的困难。
    • 例如:“标识符”可能的源输入是什么?您希望解析树表示哪些“部分”?
    • 我明白你的提议。非常感谢你。检查后是否可以重建解析树?假设idents 包含一个不属于名称的命名字符,而是一个运算符,那么我需要重新构建子表达式...如果我可以对令牌进行相同类型的操作(请参阅我的其他问题@ 987654322@) 那我想我可以轻松完成检查...
    猜你喜欢
    • 2019-05-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-27
    • 1970-01-01
    • 1970-01-01
    • 2015-09-04
    • 2011-02-11
    相关资源
    最近更新 更多