当前位置: 首页 > 知识库问答 >
问题:

分析字符串antlr

墨宜人
2023-03-14

我将字符串作为解析器规则而不是词法分析器,因为字符串可能包含带有表达式的转义,例如“变量是\(变量)”

string
 : '"' character* '"'
 ;

character
 : escapeSequence
 | .
 ;

escapeSequence
 : '\(' expression ')'
 ;

IDENTIFIER
 : [a-zA-Z][a-zA-Z0-9]*
 ;

WHITESPACE
 : [ \r\t,] -> skip
 ;

这不起作用,因为 匹配任何标记,而不是任何字符,因此许多标识符将被匹配,空白将被完全忽略。

如何解析可以包含表达式的字符串?

查看Swift和Javascript的解析器,这两种语言都支持这样的东西,我不知道它们是如何工作的。据我所知,它们只是输出一个字符串,例如“我的字符串中包含(变量)”,而实际上无法将变量解析为它自己的东西。


共有1个答案

颛孙昆
2023-03-14
匿名用户

这个问题可以使用词法模式来解决,方法是字符串内部有一个模式,外部有一个(或多个)模式。看到外面的"会切换到里面的模式,看到外面的\(or"会切换回外面。唯一复杂的部分是在外面看到一个:有时它应该切换回内部(因为它对应于\(),有时它不应该(当它对应于纯)。

实现这一目标的最基本方法如下:

Lexer:

lexer grammar StringLexer;

IDENTIFIER: [a-zA-Z_][a-zA-Z0-9_]* ;
DQUOTE: '"' -> pushMode(IN_STRING);
LPAR: '(' -> pushMode(DEFAULT_MODE);
RPAR: ')' -> popMode;

mode IN_STRING;

TEXT: ~[\\"]+ ;

BACKSLASH_PAREN: '\\(' -> pushMode(DEFAULT_MODE);

ESCAPE_SEQUENCE: '\\' . ;

DQUOTE_IN_STRING: '"' -> type(DQUOTE), popMode;

分析器:

parser grammar StringParser;

options {
    tokenVocab = 'StringLexer';
}

start: exp EOF ;

exp : '(' exp ')'
    | IDENTIFIER
    | DQUOTE stringContents* DQUOTE
    ;

stringContents : TEXT
               | ESCAPE_SEQUENCE
               | '\\(' exp ')'
               ;

在这里,我们每次看到一个\()时都会推默认模式,每次看到一个)时都会弹出该模式。这样,只有当堆栈顶部的模式是字符串模式时,它才会返回字符串内部,只有在上一次之后没有任何未关闭的)()。

这种方法有效,但缺点是不匹配的将导致空堆栈异常而不是正常的语法错误,因为我们在空堆栈上调用popMode

为了避免这种情况,我们可以添加一个成员来跟踪括号内的嵌套深度,并且在嵌套级别为0时(即,如果堆栈为空)不会弹出堆栈:

@members {
    int nesting = 0;
}

LPAR: '(' {
    nesting++;
    pushMode(DEFAULT_MODE);
};
RPAR: ')' {
    if (nesting > 0) {
        nesting--;
        popMode();
    }
};

mode IN_STRING;

BACKSLASH_PAREN: '\\(' {
    nesting++;
    pushMode(DEFAULT_MODE);
};

(我遗漏的部分与前一版本中的相同)。

这会对不匹配的)产生正常语法错误。但是,它包含动作,因此不再是语言不可知的,这只是计划使用多种语言的语法时的问题(根据语言的不同,您甚至可能很幸运,代码可能在所有目标语言中都有效)。

如果要避免操作,最后一种选择是使用三种模式:一种用于任何字符串外部的代码,一种用于字符串内部,另一种用于<代码>\() 内部。第三个模式与外部模式几乎相同,只是当看到括号时,它会推动并弹出模式,而外部模式则不会。为了使两种模式产生相同类型的令牌,第三种模式中的规则都将调用type()。如下所示:

lexer grammar StringLexer;

IDENTIFIER: [a-zA-Z_][a-zA-Z0-9_]* ;
DQUOTE: '"' -> pushMode(IN_STRING);
LPAR: '(';
RPAR: ')';

mode IN_STRING;

TEXT: ~[\\"]+ ;

BACKSLASH_PAREN: '\\(' -> pushMode(EMBEDDED);

ESCAPE_SEQUENCE: '\\' . ;

DQUOTE_IN_STRING: '"' -> type(DQUOTE), popMode;

mode EMBEDDED;

E_IDENTIFIER: [a-zA-Z_][a-zA-Z0-9_]* -> type(IDENTIFIER);
E_DQUOTE: '"' -> pushMode(IN_STRING), type(DQUOTE);
E_LPAR: '(' -> type(LPAR), pushMode(EMBEDDED);
E_RPAR: ')' -> type(RPAR), popMode;

注意,我们现在不能再在解析器语法中使用字符串文字,因为当使用相同的字符串文字定义多个lexer规则时,不能使用字符串文字。因此,现在我们必须使用LPAR而不是解析器中的LPAR(等等)(出于同样的原因,我们已经为DQUOTE做了这件事)。

由于此版本涉及大量重复(尤其是当标记数量增加时),并且防止在解析器语法中使用字符串文字,因此我通常更喜欢带有操作的版本。

这三种备选方案的完整代码也可以在GitHub上找到。

 类似资料:
  • 我需要解析“txf”格式的数据文件。这些文件可能包含 1000 多个条目。由于格式像JSON一样定义得很好,我想做一个像JSON这样的通用解析器,它可以序列化和解串化txf文件。 与JSON相反,标记没有办法识别对象或数组。如果一个带有相同标签的条目出现,我们需要把它看作一个数组。 标记对象的开始。 标记对象的成员 标记对象的结尾 下面是一个示例“txf”文件 我能够使用NSScanner创建通用

  • 本文向大家介绍Python字符串替换实例分析,包括了Python字符串替换实例分析的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了Python字符串替换的方法。分享给大家供大家参考。具体如下: 单个字符替换 输出结果为:abcd 字符串替换,改善版 输出结果为:hello, i'm mr, hoho~~,hoho~~mrmr 希望本文所述对大家的Python程序设计有所帮助。

  • 本文向大家介绍python字符串连接方法分析,包括了python字符串连接方法分析的使用技巧和注意事项,需要的朋友参考一下 本文实例分析了python字符串连接方法。分享给大家供大家参考,具体如下: python字符串连接有几种方法,把大家可能用到的列出来,第一个方法效率是最低的,另外给大家介绍后面的 2种效率高的方法,希望对大家有帮助。 先介绍下效率比较低的,有些新手朋友就会犯这个错误: 说下为

  • 我正在尝试对字符串我使用但是拆分[1]只返回第一个字符而不是整个文件名。我将如何编辑我的正则表达式,以便它返回最后一个正斜杠之后的所有内容?

  • 我有一个逗号分层的字符串,当调用时,它返回大约60的数组大小。在特定的用例中,我只需要从数组中返回第二个值的值。例如,