【问题标题】:Antlr3 - Non Greedy Double Quoted String with Escaped Double QuoteAntlr3 - 带有转义双引号的非贪婪双引号字符串
【发布时间】:2014-03-05 17:35:19
【问题描述】:

以下 Antlr3 语法文件不支持作为 STRING 词法分析器规则的一部分的转义双引号。任何想法为什么?

表达式工作:

  • \"你好\"
  • ref(\"你好\",\"你好\")

表达式不起作用:

  • \"h\"e\"l\"l\"o\"
  • ref(\"hello\", \"hel\"lo\")

可在 AntlrWorks 中运行的 Antlr3 语法文件:

grammar Grammar;

options
{
    output=AST;
    ASTLabelType=CommonTree;
    language=CSharp3;
}

public oaExpression
   : exponentiationExpression EOF!
   ;

exponentiationExpression
    :       equalityExpression ( '^' equalityExpression )*
    ;

equalityExpression
    :       relationalExpression ( ( ('==' | '=' ) | ('!=' | '<>' ) ) relationalExpression )*
    ;

relationalExpression
    :       additiveExpression ( ( '>' | '>=' | '<' | '<=' ) additiveExpression )*
    ;

additiveExpression
    :       multiplicativeExpression ( ( '+' | '-' ) multiplicativeExpression )*
    ;

multiplicativeExpression
    :       primaryExpression ( ( '*' | '/' ) primaryExpression )*
    ;

primaryExpression
    :       '(' exponentiationExpression ')' | value | identifier (arguments )?
    ;

value
    :       STRING
    ;

identifier
    :       ID
    ;

expressionList
    :       exponentiationExpression ( ',' exponentiationExpression )*
    ;

arguments
    :       '(' ( expressionList )? ')'
    ;                      

/*
 * Lexer rules
 */

ID
    :       LETTER (LETTER | DIGIT)*
    ;

STRING
    :       '"' ( options { greedy=false; } : ~'"' )* '"'
    ;

WS
    :       (' '|'\r'|'\t'|'\u000C'|'\n') {$channel=Hidden;}
    ;

/*
 * Fragment Lexer rules
 */

fragment
LETTER
    :       'a'..'z'
    |       'A'..'Z'
    |       '_'
    ;

fragment
EXPONENT
    :       ('e'|'E') ('+'|'-')? ( DIGIT )+
    ;

fragment
HEX_DIGIT
    :       ( DIGIT |'a'..'f'|'A'..'F')
    ;

fragment
DIGIT
    :       '0'..'9'
    ;

【问题讨论】:

  • 为什么?我的意思是该规则仅在没有结束引号时匹配整个输入,在这种情况下输入无论如何都是无效的,对吗?你能澄清一下吗?
  • 嗨@BartKiers,我已经编辑了问题以提供完整的语法。我已经尝试了您的建议,但它们似乎不起作用。

标签: parsing antlr antlr3 lexer


【解决方案1】:

试试这个:

STRING
 : '"'                          // a opening quote
   (                            // start group
     '\\' ~('\r' | '\n')        // an escaped char other than a line break char
     |                          // OR
     ~('\\' | '"'| '\r' | '\n') // any char other than '"', '\' and line breaks
   )*                           // end group and repeat zero or more times
   '"'                          // the closing quote
 ;

当我从您的评论中测试 4 个不同的测试用例时:

"\"hello\""
"ref(\"hello\",\"hello\")"
"\"h\"e\"l\"l\"o\""
"ref(\"hello\", \"hel\"lo\")"

使用我建议的词法分析器规则:

grammar T;

parse
 : string+ EOF
 ;

string
 : STRING
 ;

STRING
 : '"' ('\\' ~('\r' | '\n') | ~('\\' | '"'| '\r' | '\n'))* '"'
 ;

SPACE
 : (' ' | '\t' | '\r' | '\n')+ {skip();}    
 ;

ANTLRWorks 的调试器生成以下解析树:

换句话说:它工作得很好(在我的机器上:))。

编辑二

我还使用了您的语法(进行了一些小的更改以使其与 Java 兼容),我将不正确的 STRING 规则替换为我建议的规则:

oaExpression
   :        STRING+ EOF!
   //: exponentiationExpression EOF!
   ;

exponentiationExpression
    :       equalityExpression ( '^' equalityExpression )*
    ;

equalityExpression
    :       relationalExpression ( ( ('==' | '=' ) | ('!=' | '<>' ) ) relationalExpression )*
    ;

relationalExpression
    :       additiveExpression ( ( '>' | '>=' | '<' | '<=' ) additiveExpression )*
    ;

additiveExpression
    :       multiplicativeExpression ( ( '+' | '-' ) multiplicativeExpression )*
    ;

multiplicativeExpression
    :       primaryExpression ( ( '*' | '/' ) primaryExpression )*
    ;

primaryExpression
    :       '(' exponentiationExpression ')' | value | identifier (arguments )?
    ;

value
    :       STRING
    ;

identifier
    :       ID
    ;

expressionList
    :       exponentiationExpression ( ',' exponentiationExpression )*
    ;

arguments
    :       '(' ( expressionList )? ')'
    ;                      

/*
 * Lexer rules
 */

ID
    :       LETTER (LETTER | DIGIT)*
    ;

//STRING
//    :       '"' ( options { greedy=false; } : ~'"' )* '"'
//    ;
STRING
    :       '"' ('\\' ~('\r' | '\n') | ~('\\' | '"'| '\r' | '\n'))* '"'
    ;

WS
    :       (' '|'\r'|'\t'|'\u000C'|'\n') {$channel=HIDDEN;} /*{$channel=Hidden;}*/
    ;

/*
 * Fragment Lexer rules
 */

fragment
LETTER
    :       'a'..'z'
    |       'A'..'Z'
    |       '_'
    ;

fragment
EXPONENT
    :       ('e'|'E') ('+'|'-')? ( DIGIT )+
    ;

fragment
HEX_DIGIT
    :       ( DIGIT |'a'..'f'|'A'..'F')
    ;

fragment
DIGIT
    :       '0'..'9'
    ;

它在相同的解析树中解析我之前示例中的输入。

【讨论】:

  • 嗨@BartKiers,上面的 Lexer 规则适用于以下场景:"\"hello\"", "ref(\"hello\",\ "hello\")" 但不适用于转义双引号场景:"\"h\"e\"l\"l\"o\"", "ref(\"hello\", \"hel\"lo\")"。谢谢你的帮助。
  • @bjrave,我不知道你是如何测试它的,但它工作得很好。不过祝你好运。
  • 我同意您的精简语法在 ANTLRWorks 中运行良好。使用 ANTLRWorks 和通过 C# 代码编译的解析器,STRING Lexer 规则在与我的语法文件的其余部分集成后似乎不起作用。如果你想看一下,我已经添加了一个指向我的语法文件的链接。
  • @bjrave,不,我不会看:你的语法里面充满了嵌入式代码,这意味着我需要先过滤掉所有这些,然后才能给它一个旋转。也许如果您在原始问题中发布没有代码的语法,我或其他人可能会看看。外部链接通常不会被查看。
  • 我明白你的意思。我已经从语法文件中剥离了嵌入的代码。
【解决方案2】:

这就是我如何处理可以包含转义序列的字符串(不仅仅是 \" 而是任何):

DOUBLE_QUOTED_TEXT
@init { int escape_count = 0; }:
    DOUBLE_QUOTE
    (
        DOUBLE_QUOTE DOUBLE_QUOTE { escape_count++; }
        | ESCAPE_OPERATOR .  { escape_count++; }
        | ~(DOUBLE_QUOTE | ESCAPE_OPERATOR)
    )*
    DOUBLE_QUOTE
    { EMIT(); LTOKEN->user1 = escape_count; }
;

该规则另外计算转义并将它们存储在令牌中。这允许接收器快速查看它是否需要对字符串执行任何操作(如果 user1 > 0)。如果您不需要删除 @init 部分和操作。

【讨论】:

  • 我已经尝试过您的建议,但似乎没有任何区别。我更新了我的问题以包含完整的语法。
猜你喜欢
  • 1970-01-01
  • 2015-10-27
  • 2015-12-02
  • 1970-01-01
  • 2020-11-29
  • 2013-01-06
  • 1970-01-01
相关资源
最近更新 更多