ANTLR3:完全匹配令牌中的一个字符

问题描述 投票:1回答:1

我是ANTLR的新手,正在使用ANTLR3进行解析器工作,但是在以下情况下遇到了麻烦。在我们分析的文本中,可能存在^-字符出现的多种情况。但是,在一种特殊情况下,“ ^”后面紧跟一个字符。这发生在字符串中:

  1. ''MyText'^ M
  2. ^ MyValue

在第一种情况下'^ M'是字符串的一部分,其中^ M表示13十六进制,但在第二种情况下不是;那里是一个指针指示器。第二种情况是在语法规则中捕获的(在多个规则中使用^-字符)。

如果我使用以下标记解决它,它将失败,因为'^ MyValue'在'^ M'和'yValue'中被标记。但是,我希望仅在^之后有一个字符时才使用令牌ControlChar。否则,应将其忽略而不是标记化,以便可以在语法中使用它。

Pointer                 : '^'
                        ;
QuotedString            : '\'' ('\'\'' | ~('\''))* '\''
                        ;
TkIdentifier            : (Alpha | '_') (Alpha | Digit | '_')*
                        ;
ControlString           : Controlchar (Controlchar)*
                        ;
fragment
Controlchar             : '#' Digitseq
                        | '#' '$' Hexdigitseq
                        | '^' Alpha
                        ;
fragment
Alpha                   : 'a'..'z'
                        | 'A'..'Z'
                        ;
fragment
Digit                   : '0'..'9'
                        ;

所以,我的问题是。如何指示ANTLR '^' Alpha仅在此字符后紧跟一个Alpha时才匹配,否则在文本中保留'^'并将其标记为TkIdentifier令牌吗?

例如,词法分析器应创建以下标记:

^Foo -> Pointer TkIdentifier
^F oo -> ControlChar TkIdentifier
^ F oo -> Pointer TkIdentifier TkIdentifier
Foo^M -> TkIdentifier ControlChar
Foo ^ M -> TkIdentifier Pointer TkIdentifier
Foo ^M -> TkIdentifier ControlChar
Foo^ M -> TkIdentifier Pointer TkIdentifier

'Text'^M -> QuotedString ControlChar
'Text' ^M -> QuotedString ControlChar
'Text' ^ M -> QuotedString Pointer TkIdentifier
^M'Text' -> ControlChar QuotedString
^M 'Text' -> ControlChar QuotedString
^ M'Text' -> Pointer TkIdentifier QuotedString
antlr antlr3
1个回答
0
投票

在这种情况下,您必须在语法的a predicate中使用目标特定的代码。

您需要做的是:每当词法分析器偶然发现^时,它都必须在流中向前看2个字符。如果这两个字符是一个单词和一个非单词,它将创建一个Controlchar令牌,其中包括Alpha之后的^。如果不是,请从Pointer创建一个^

对于以Java为目标语言的ANTLR3,可能看起来像这样:

// Cannot be a fragment now, because we're changing the `type` in certain cases. And because it is
// no fragment any more, it has to come before the `ControlString` rule.
Controlchar
 : '^' ( // Execute the predicate, which looks ahead 2 chars and passes if 
         // these 2 chars are a word and a non-word
         {((char)input.LA(1) + "" + (char)input.LA(2)).matches("\\w\\W")}?=> 
         // If the predicate is true, match a single `Alpha`
         Alpha
       |  // If the predicate failed, change the type of this token to a `Pointer`
         {$type=Pointer;}
       )
 | '#' Digitseq
 | '#' '$' Hexdigitseq
 ;

ControlString
 : Controlchar+
 ;

我进行了快速测试:

import org.antlr.runtime.*;

public class Main {

    public static void main(String[] args) throws Exception {

        String[] tests = {
                "^Foo",
                "^F oo",
                "^ F oo",
                "Foo^M",
                "Foo ^ M",
                "Foo ^M",
                "Foo^ M",
                "'Text'^M",
                "'Text' ^M",
                "'Text' ^ M",
                "^M'Text'",
                "^M 'Text'",
                "^ M'Text'",
                "^Q^E^D"
        };

        for (String test : tests) {

            TLexer lexer = new TLexer(new ANTLRStringStream(test));
            CommonTokenStream tokenStream = new CommonTokenStream(lexer);
            tokenStream.fill();

            System.out.printf("\ntest: %-15s tokens: ", test);

            for (Token t : tokenStream.getTokens()) {
                if (t.getType() != -1) {
                    System.out.printf(" %s", TParser.tokenNames[t.getType()]);
                }
            }
        }
    }
}

其中印刷:

test: ^Foo            tokens:  Pointer TkIdentifier
test: ^F oo           tokens:  Controlchar TkIdentifier
test: ^ F oo          tokens:  Pointer TkIdentifier TkIdentifier
test: Foo^M           tokens:  TkIdentifier Controlchar
test: Foo ^ M         tokens:  TkIdentifier Pointer TkIdentifier
test: Foo ^M          tokens:  TkIdentifier Controlchar
test: Foo^ M          tokens:  TkIdentifier Pointer TkIdentifier
test: 'Text'^M        tokens:  QuotedString Controlchar
test: 'Text' ^M       tokens:  QuotedString Controlchar
test: 'Text' ^ M      tokens:  QuotedString Pointer TkIdentifier
test: ^M'Text'        tokens:  Controlchar QuotedString
test: ^M 'Text'       tokens:  Controlchar QuotedString
test: ^ M'Text'       tokens:  Pointer TkIdentifier QuotedString
test: ^Q^E^D          tokens:  ControlString

请注意,您还可以通过移动lexer::members部分中的嵌入式代码来使语法保持一点(清晰):

// Place this in the top of your grammar definition
@lexer::members {
  private boolean isControlchar() {
    // TODO 
    //  - check if there are actually 2 chars ahead and not an EOF
    //  - perhaps something else than a regex match here
    return ((char)input.LA(1) + "" + (char)input.LA(2)).matches("\\w\\W");
  }
}

...

Controlchar
 : '^' ( {isControlchar()}?=> Alpha
       | {$type=Pointer;}
       )
 | '#' Digitseq
 | '#' '$' Hexdigitseq
 ;
© www.soinside.com 2019 - 2024. All rights reserved.