ANTLR 词典和解析器的标记识别错误。

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

我正在写一个ANTLR Lexer和Parser语法,它将解析与Java类相当相似的文本。最终,它将解析如下文本。

reference schema:"https://schema.org/";
reference dc:"https://www.dublincore.org/";

type dc:Author {
}

我正在慢慢建立Lexer和Parser. 我已经成功地解析了 reference但是在解析 type.

在添加支持之前。type 我可以在解析器中使用空格、冒号和分号的字符串字元,但在遇到了 cannot create implicit token for string literal 错误。我为每一个字符定义了一个词法规则,并用该规则替换了所有出现的文字。然而这破坏了对 references.

我已经将我的词法和解析器包含在内,它成功地解析了 reference下面的版本(包括一个输入示例和解析的抽象语法树)和进化的版本,这是不工作的。我没有得到任何编译错误,但有大量的 token recognition error的(截图附后)。

正确的处理解析方式是什么?

工作

Lexer

lexer grammar WorkingLexerGrammar;

WS: ('\t' | '\n' | '\r' )+ -> skip ;

fragment Colon : ':';
fragment SemiColon: ';';
fragment Underscores: '_'+ ;
fragment Digits: [0-9]+ ;
fragment LowercaseLetters: [a-z]+ ;
fragment UppercaseLetters: [A-Z]+ ;
fragment String: '"' .*? '"' ;
fragment Prefix: (Underscores | Digits | LowercaseLetters)+ ;

REFERENCE_KEYWORD: 'reference' ;
TYPE_KEYWORD: 'type' ;

PREFIXED_REFERENCE: ' ' -> pushMode(PrefixedReferenceMode) ;

mode PrefixedReferenceMode;
REFERENCE_PREFIX: Prefix;
REFERENCE_PREFIX_SEPARATOR: ':' -> pushMode(IriMode);
END_IRI: ';' -> popMode;

mode IriMode;
IRI: String  -> popMode;

解析器

parser grammar WorkingParserGrammar ;

options { tokenVocab=WorkingLexerGrammar; }

document: reference* EOF ;

prefixedReference: REFERENCE_PREFIX ':' IRI;
reference: REFERENCE_KEYWORD ' ' prefixedReference ';';

输入

reference schema:"https://schema.org/";
reference dc:"https://www.dublincore.org/";

产量

Abstract syntax tree output

已进化(无法使用)

Lexer

lexer grammar NotWorkingLexerGrammar;

WS: ('\t' | '\n' | '\r' )+ -> skip ;

fragment Colon : ':';
fragment SemiColon: ';';
fragment Underscores: '_'+ ;
fragment Digits: [0-9]+ ;
fragment LowercaseLetters: [a-z]+ ;
fragment UppercaseLetters: [A-Z]+ ;
fragment String: '"' .*? '"' ;
fragment Prefix: (Underscores | Digits | LowercaseLetters)+ ;

COLON: Colon;
SEMICOLON: SemiColon;
SPACE: ' ';

REFERENCE_KEYWORD: 'reference' ;
TYPE_KEYWORD: 'type' ;

PREFIXED_REFERENCE: SPACE -> pushMode(PrefixedReferenceMode) ;

mode PrefixedReferenceMode;
REFERENCE_PREFIX: Prefix;
REFERENCE_PREFIX_SEPARATOR: COLON -> pushMode(IriMode);
END_IRI: SEMICOLON -> popMode;

mode IriMode;
IRI: String  -> popMode;

PREFIXED_NAME: SPACE -> pushMode(PrefixedNameMode) ;

mode PrefixedNameMode;
NAME_PREFIX: Prefix;
NAME_PREFIX_SEPARATOR: COLON -> pushMode(LocalNameMode);
END_NAME: SEMICOLON -> popMode;

mode LocalNameMode;
LOCAL_NAME: (Underscores | Digits | LowercaseLetters | UppercaseLetters)+  -> popMode;

解析器

parser grammar NotWorkingParserGrammar ;

options { tokenVocab=NotWorkingLexerGrammar; }

document: reference* type* EOF ;

prefixedReference: REFERENCE_PREFIX COLON IRI;
reference: REFERENCE_KEYWORD SPACE prefixedReference SEMICOLON;

prefixedName: NAME_PREFIX SPACE LOCAL_NAME;
type: TYPE_KEYWORD SPACE prefixedName;

产量

Abstract syntax tree not parsing correctly


以下 巴特-基尔的帮助 我对lexer和parser语法进行了两次更新,效果不一。

第一次更新

这个变化正确地解析了类型定义,但前提是我必须删除lexer规则以供参考。我想原因是这两条规则是一样的(即 PREFIXED_REFERENCE: SPACE -> pushMode(PrefixedReferenceMode) ; for reference和 PREFIXED_NAME: SPACE -> pushMode(PrefixedNameMode) ; 为类型)--也就是说,它们都在空格上匹配。我的第二次更新试图解决这个问题,但完整的词典和解析器语法在下面。

词法器

lexer grammar NotWorkingLexerGrammar;

WS: ('\t' | '\n' | '\r' )+ -> skip ;

fragment Underscores: '_'+ ;
fragment Digits: [0-9]+ ;
fragment LowercaseLetters: [a-z]+ ;
fragment UppercaseLetters: [A-Z]+ ;
fragment String: '"' .*? '"' ;
fragment Prefix: (Underscores | Digits | LowercaseLetters)+ ;

fragment COLON: ':';
fragment SEMICOLON: ';';
fragment SPACE: ' ';

fragment REFERENCE_KEYWORD: 'reference' ;
fragment TYPE_KEYWORD: 'type' ;

PREFIXED_REFERENCE: SPACE -> pushMode(PrefixedReferenceMode) ;

mode PrefixedReferenceMode;
REFERENCE_PREFIX: Prefix;
REFERENCE_PREFIX_SEPARATOR: COLON -> pushMode(IriMode);
END_IRI: SEMICOLON -> popMode;

mode IriMode;
IRI: String  -> popMode;

PREFIXED_NAME: SPACE -> pushMode(PrefixedNameMode) ;

mode PrefixedNameMode;
NAME_PREFIX: Prefix;
NAME_PREFIX_SEPARATOR: COLON -> pushMode(LocalNameMode);
END_NAME: SEMICOLON -> popMode;

mode LocalNameMode;
LOCAL_NAME: (Underscores | Digits | LowercaseLetters | UppercaseLetters)+  -> popMode;

解析器

parser grammar NotWorkingParserGrammar ;

options { tokenVocab=NotWorkingLexerGrammar; }

document: reference* type* EOF ;

prefixedReference: REFERENCE_PREFIX REFERENCE_PREFIX_SEPARATOR IRI;
reference: REFERENCE_KEYWORD PREFIXED_REFERENCE prefixedReference END_IRI;

prefixedName: NAME_PREFIX NAME_PREFIX_SEPARATOR LOCAL_NAME;
type: TYPE_KEYWORD PREFIXED_NAME prefixedName END_NAME;

第二次更新

为了解决这个问题,我把 referencetype 关键词到相应部分的Lexer规则,但如果我删除所有的Lexer规则作为参考,这只是解析类型。然而引用是正确的。

Lexer

lexer grammar NotWorkingLexerGrammar;

WS: ('\t' | '\n' | '\r' )+ -> skip ;

fragment Underscores: '_'+ ;
fragment Digits: [0-9]+ ;
fragment LowercaseLetters: [a-z]+ ;
fragment UppercaseLetters: [A-Z]+ ;
fragment String: '"' .*? '"' ;
fragment Prefix: (Underscores | Digits | LowercaseLetters)+ ;

fragment COLON: ':';
fragment SEMICOLON: ';';
fragment SPACE: ' ';

fragment REFERENCE_KEYWORD: 'reference' ;
fragment TYPE_KEYWORD: 'type' ;

PREFIXED_REFERENCE: REFERENCE_KEYWORD SPACE -> pushMode(PrefixedReferenceMode) ;

mode PrefixedReferenceMode;
REFERENCE_PREFIX: Prefix;
REFERENCE_PREFIX_SEPARATOR: COLON -> pushMode(IriMode);
END_IRI: SEMICOLON -> popMode;

mode IriMode;
IRI: String  -> popMode;

TYPE_DEFINITION: TYPE_KEYWORD SPACE -> pushMode(PrefixedNameMode) ;

mode PrefixedNameMode;
NAME_PREFIX: Prefix;
NAME_PREFIX_SEPARATOR: COLON -> pushMode(LocalNameMode);
END_NAME: SEMICOLON -> popMode;

mode LocalNameMode;
LOCAL_NAME: (Underscores | Digits | LowercaseLetters | UppercaseLetters)+  -> popMode;

解析器

parser grammar NotWorkingParserGrammar ;

options { tokenVocab=NotWorkingLexerGrammar; }

document: reference* type* EOF ;

prefixedReference: REFERENCE_PREFIX REFERENCE_PREFIX_SEPARATOR IRI;
reference: PREFIXED_REFERENCE prefixedReference END_IRI;

prefixedName: NAME_PREFIX NAME_PREFIX_SEPARATOR LOCAL_NAME;
type: TYPE_DEFINITION prefixedName END_NAME;

产量

对于以下输入。

reference schema:"https://schema.org/";
reference dc:"https://www.dublincore.org/";

type dc:Author;

这就是输出。

line 4:0 token recognition error at: 't'
line 4:1 token recognition error at: 'y'
line 4:2 token recognition error at: 'p'
line 4:3 token recognition error at: 'e'
line 4:4 token recognition error at: ' '
line 4:5 token recognition error at: 'd'
line 4:6 token recognition error at: 'c'
line 4:7 token recognition error at: ':'
line 4:8 token recognition error at: 'A'
line 4:9 token recognition error at: 'u'
line 4:10 token recognition error at: 't'
line 4:11 token recognition error at: 'h'
line 4:12 token recognition error at: 'o'
line 4:13 token recognition error at: 'r;'

我使用模式的原因是为了限制规则的范围. 这是我控制的一种语言,但不希望对它进行大幅度的改变。这门语言的内容比我在这里展示的要多得多,我们已经有了一个语法(目前是一个组合语法),但它很脆弱。我试图做一个改变,以防止在前缀中使用大写字符,但允许在本地名称中使用它们,但这雪上加霜,其他规则开始应用。研究表明,模式是处理这种情况的一种方法,但我对ANTLR不是很熟悉,所以我可能误解了它。

Abstract syntax tree for second update

parsing antlr antlr4
1个回答
1
投票

当遇到类似这些错误警告时。

line 4:0 token recognition error at: 't'
line 4:1 token recognition error at: 'y'
line 4:2 token recognition error at: 'p'
line 4:3 token recognition error at: 'e'
...

意味着词典无法为输入构建一个标记(type ... 在这种情况下)。) 在你的情况下,这意味着词典无法在当时所处的模式下从输入中创建一个标记。

我曾试着做了一个改变,以防止在前缀中使用大写字母,但允许在本地名称中使用大写字母。

解决这种事情有两种选择。

  1. 就像解析普通标识符一样解析前缀(大写或小写),解析后,走一遍生成的解析树,用ANTLR访问者或监听器验证前缀标识符是否真的是小写的(见。https:/github.comantlrantlr4blobmasterdoclisteners.md)。)
  2. 在你的词典中区分小写和大写的标识符,并在你的解析器规则中相应地使用它们,类似这样的东西可以用。
document
 : reference* type* EOF
 ;

reference
 :  K_REFERENCE LOWER_ID COL STRING SCOL
 ;

type
 : K_TYPE LOWER_ID COL id OPAR CPAR
 ;

id
 : LOWER_ID
 | ID
 ;

K_REFERENCE : 'reference';
K_TYPE      : 'type';
LOWER_ID    : [a-z_] [a-z_0-9]*;
ID          : [a-zA-Z_] [a-zA-Z_0-9]*;
STRING      : '"' ~["]* '"';
SCOL        : ';';
COL         : ':';
OPAR        : '{';
CPAR        : '}';
SPACES      : [ \t\r\n] -> skip;

模式是用来处理真正由两种(或更多)语言相互嵌入的输入。例如解析HTML文件:有内容(文本)和带有属性的标签。就我看来,你并没有按照它的使用目的来使用它,我认为。

© www.soinside.com 2019 - 2024. All rights reserved.