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

使用ANTLR4的lexer模式解析内联语句和多行语句

仰城
2023-03-14

我目前正在开发一个孤岛语法解析器,用于解析同一个文件中的两种编程语言。第二种编程语言的语句总是以一个特殊的字符(*)开始,但它们可以有两种形式:内联语句或多行语句。

如果是内联语句,行以*开始,以换行符(\r?\n)结束。

我很难使用ANTLR4的lexer模式来完成这一点。有人能给我指个正确的方向吗?

我在下面给出了我的语法。下面的示例中,解析器显示了两个错误

line 5:21 extraneous input '\n' expecting {<EOF>, ID, SWITCH_CHAR}
line 8:31 extraneous input '\n' expecting {<EOF>, ID, SWITCH_CHAR}

示例:

first programming language 
*example one second programming language inline statement ending with semicolon;
*example two another valid second programming language inline statement ending with newline
*example three second programming language may expand to the next line
until semicolon char;
*example four second programming language example may expand 
to a number of lines
too ending with semicolon char;
first programming language again

lexer:

lexer grammar ComplexLanguageLexer;
/*** SEA ****/
ID: [a-z]+;
WS: [ \t\f]+ -> skip;
SWITCH_CHAR: '*' -> pushMode(inline_mode), pushMode(multiline_mode);
NEWLINE:  '\r'? '\n' -> skip;

/***ISLANDS****/
mode multiline_mode;
MULTILINE_SWITCH_CHAR: ';' -> popMode;  //seek until ';'
MULTILINE_ID: [a-z]+;
MULTILINE_WS: [ \t\f]+ -> skip;
MULTILINE_NEWLINE:  '\r'? '\n' -> skip; //just skip newlines in the multiline mode

mode inline_mode;
INLINE_NEWLINE:  '\r'? '\n' -> type(NEWLINE), popMode;
INLINE_SEMICOLONCHAR: ';' ; //just match semicolonchar
INLINE_ID: [a-z]+;
INLINE_WS: [ \t\f]+ -> skip;

语法:

parser grammar ComplexLanguageParser;
options { tokenVocab = ComplexLanguageLexer ; }

startRule:   programStatement+;

programStatement:
    word | inlineStatement| multilineStatement
;

word: ID;

inlineStatement:
    SWITCH_CHAR INLINE_ID+ INLINE_SEMICOLONCHAR? NEWLINE
;

multilineStatement:
    SWITCH_CHAR MULTILINE_ID+ MULTILINE_SWITCH_CHAR
;
lexer grammar ComplexLanguageLexer;

SWITCH_CHAR: STAR -> pushMode(second_mode) ;
ID1         : ID ;
WS1         : WS -> skip ;
NL1         : NL -> skip ;

fragment STAR : '*' ;
fragment ID   : [a-z]+ ;
fragment WS   : [ \t\f]+ ;
fragment NL   : '\r'? '\n' ;

mode second_mode ;
    TERM1 : ( WS | NL )* SEMI -> popMode ;
    TERM2 : WS* NL -> popMode ;
    ID2   : ID ;
    WS2   : WS+ -> skip ;
    NL2   : NL;
    SEMI : ';';
parser grammar ComplexLanguageParser;
options { tokenVocab = ComplexLanguageLexer ; }

startRule:  programStatement+;
programStatement:   firstLanguageStatement | secondLanguageStatment ;
firstLanguageStatement:    word ;
secondLanguageStatment:    SWITCH_CHAR (inlineStatement| multilineStatement)     ;
word: ID1;
multilineStatement:    (ID2|NL2)+ TERM1;
inlineStatement:   ID2+ TERM2;

它对内联语句起到了预期的作用,但对多行语句仍然不起作用。不知道我在这里做错了什么?

例如。

first language            -> ok
*second language inline   -> ok 
*multi line;              -> ok
*multi line expands to 
 next line;                ->  token recognition error at ';'
*multi line
;                          -> ok
first language again       -> ok

共有1个答案

陶寒
2023-03-14

pushmodepopmode命令使用单个堆栈实现。所以,规则

SWITCH_CHAR: '*' -> pushMode(inline_mode), pushMode(multiline_mode);

应该导致lexer计算multiline_mode规则。在pop上,lexer将计算inline_mode规则。不太可能是想要的。

最好实现一个能够正确处理所有第二语言语句的单个lexer模式。基本思想是:

SWITCH_CHAR : STAR -> pushMode(second_mode) ;

mode second_mode ;
    STMT1 : ( ID | WS | NL )+ SEMI -> popMode() ;
    STMT2 : ( ID | WS )+ NL -> popMode() ;

未经测试,但应该可以工作,只要ID不包括starsemi

更新

要向解析器公开ID,只需将其从语句规则中拆分出来:

SWITCH_CHAR: STAR -> pushMode(second_mode) ;
ID1         : ID ;
WS1         : WS -> skip ;
NL1         : NL -> skip ;

fragment STAR : '*' ;
fragment ID   : [a-z]+ ;
fragment WS   : [ \t\f]+ ;
fragment NL   : '\r'? '\n' ;

mode second_mode ;
    TERM1 : ( WS | NL )* SEMI -> popMode() ;
    TERM2 : WS+ NL -> popMode() ;
    ID2   : ID ;
    WS2   : WS+ -> skip ;
 *example two inline statement ending with newline
 first programming language again (including a semicolon)

在此之前,一个可能更好的设计选择是将第一语言和第二语言之间的任何区别推迟到解析器,或者更好的是对生成的解析树进行分析

 类似资料:
  • 我试图将一个lexer语法导入到另一个lexer语法中。导入的语法使用不同的模式(在XMLLexer示例中,mode INSIDE和PROC_INSTR)。 如果我将导入的部分内联到主lexer定义中,解析器就可以工作,但我希望使用导入功能来进行干净的分离。(不是针对XML/HTML示例,而是在另一种情况下。) 是否有可能让它工作使用进口或这是一个ANTLR4的限制?

  • 如何实现这些模式?

  • 我正在尝试创建一个语法来解析Solr查询(只需要稍微相关,您不需要了解任何关于Solr的信息来回答这个问题--只需要比我了解更多关于ANTLR4.7的信息就可以了)。我将它建立在Solr6中的QueryParser.jj文件的基础上。我找了一个现存的,但似乎没有一个不是旧的和过时的。 我被困住了,因为当我尝试运行解析器时,我得到了“token recognition error”。 我创建的lex

  • 我正在使用ANTLR4生成一个解析器。我是语法分析器的新手。我读过非常有帮助的ANTLR Mega教程,但我仍然停留在如何正确排序(和/或编写)我的lexer和解析器规则上。 我希望解析器能够处理以下内容: 你好< >,你好吗? 下面是我的语法: 旁注:我加了“punct?”在“item”规则的末尾,因为有可能在“func”后面出现一个逗号,例如在我上面给出的例句中。但由于“word”后面也可以有

  • 在ANTLR4中,我有一个lexer规则,说我可以使用任何字符得到任何单词,但空格和换行符除外。其定义如下: 我还有一个lexer规则(定义在than WORD之前),用于进入EVAL模式: 我考虑的另一个选择是将“word”定义为${and}包围的文本以外的任何东西。但我不知道如何创建这样的lexer规则。 我该怎么解决?要区分评价和词?

  • 可能在内部使用的代码将在规则之后被取消,如下所示: ANTLR4就是这样做事的吗?