在使用ANTLR4的简单词法分析器/解析器中匹配结束标记(通用文本)之前的任何内容



我想为一个简单的脚本语言做一个简单的解析器,它有文本块和脚本块,在这些脚本块中,我希望能够定义一个函数,以及执行任何类型的通用语句。

我真的不需要知道或关心什么被归类为"语句",但我确实需要解析函数声明。所以即使它看起来像一个while循环而我没有while循环的规则,我能匹配一个通用语句规则吗?如何获取内容?

使用一个通用规则,我可以做"通用文本"部分不错,但在脚本模式我不太成功,我试着拉出嵌套模式,我设置了一个' in FUNCTION'模式,但不断遇到障碍。

例如,当在functionDeclaration中的statement中,我如何匹配直到end function的所有内容。此外,我如何匹配一个"通用的";语句,这样我就不需要像emptyStatementassignmentStatement这样的语句类型。即使它只是变成了一个大的"脚本代码团"我没意见。

Where I have far:

我的语法:

parser grammar ExprParser;
options { tokenVocab=ExprLexer; }
file
: block* EOF
;

block
: textBlock+
| script
;

textBlock
: HtmlDtd
| GenericText
| ScriptEnd
;

script
: topStatement+
| statement
;

topStatement
: functionDeclaration
;
functionDeclaration
: FunctionStart Ident L_PAREN R_PAREN statement* FunctionEnd
;

statement
: assignmentStatement
| emptyStatement
;

assignmentStatement
: Ident ASSIGNTO Ident SEMICOLON
;

emptyStatement
: SEMICOLON
;

我的词法分析程序

lexer grammar ExprLexer;
channels { Comments, SkipChannel }

SeaWhitespace:  [ trnf]+ -> channel(HIDDEN);
HtmlDtd:        '<!' .*? '>';
ScriptStart:       SCRIPT_START_FRAGMENT -> channel(SkipChannel), pushMode(SCRIPT);
// Catch all text
GenericText : . ; 
mode SCRIPT;
ScriptEnd :'%' '>' -> channel(SkipChannel), popMode;
ScriptWhitespace : [ trnf]+ -> channel(SkipChannel);
// Comments begin with single quote
ScriptSingleLineComment:  ''' -> channel(SkipChannel), pushMode(SingleLineCommentMode);

FunctionStart :  FUNCTION_START_FRAGMENT;
FunctionEnd : FUNCTION_END_FRAGMENT;
Ident : ID;
COMMA     : ',';
SEMICOLON : ';';
L_PAREN   : '(';
R_PAREN   : ')';
ASSIGNTO  : '=';
mode SingleLineCommentMode;
Comment:                 ~[rn?]+ -> channel(Comments);
CommentEnd:              [rn] -> channel(SkipChannel), popMode; // exit from comment.

// Fragments
fragment ID: [a-zA-Z0-9_u0080-ufffe]+;
fragment NameString: [a-zA-Z_u0080-ufffe][a-zA-Z0-9_u0080-ufffe]*;
fragment SCRIPT_START_FRAGMENT : '<%';
fragment SCRIPT_END_FRAGMENT : '%>';
fragment FUNCTION_START_FRAGMENT : 'function';
fragment FUNCTION_END_FRAGMENT : 'end function'; // Space is required here

一些测试字符串

<! tagsIknow >
<tagsIdontknowbutwant>
<%
function xxx() 'this is a comment 
x = y;
a = 1;
;
;
end function
a = 1;
b = 2;
%>
randomtext
<%
'another script
x = 3; 'inline comment again
%>

我想使用的脚本类型

blah
<%
function xxx() 
while (true) ' notice I have no rule for a while loop
get me everything in here verbatim except for comments ' this ideally is trimmed
endwhile 
end function ' I want everything until the 'end function' keyword, basically
%>
more generic text

编辑:

我的目标是像这样的输入

text1
<%
arbitrary script lines1
arbitrary script lines2

function x(a,b) 
arbitrary script body containing anything
end function
arbitrary script lines3 again
%>
plain text
<%
arbitrary script lines4 again
function y() 
different function body
end function
%>

所以我得到这个:

PLAIN_TEXT_BLOB (matching TEXT1)
SCRIPT_BLOB (matching script lines 1 & 2 together)
FUNCTION
name: x
params: [a, b]
body: SCRIPT_BLOB (containing the body)
SCRIPT_BLOB (matching line 3)
PLAIN_TEXT_BLOB (matching 'plain text')
SCRIPT_BLOB (matching line 4)
FUNCTION
name: y
params: []
body: SCRIPT_BLOB (containing the body)
EOF

所以理论上只有三种"类型",纯文本,脚本对象(多行)和函数(它们本身包含一些参数和单个脚本对象)

这样,在给定上述对象的情况下,我可以维持我遇到的顺序,并适当地处理,推送"PLAIN TEXT"输出原始的、正在运行的"非功能脚本";

问题是我似乎无法捕捉到函数名或参数等东西,而我有一个贪婪的规则(这是由于ANTLR用最贪婪的规则覆盖这些规则),所以我不能有一个参数规则,这是确认它们适合一个标识符,同时有一个'。+'规则收集函数体。

一种折衷方法是将函数作为一个整体收集(functionend function中的所有内容),并在该块上执行第二次解析以解析函数头(name + params),试图避免。

另一个想法是有一个额外的模式,进入"FUNCTION_BODY_MODE"一旦遇到R_PAREN,并在找到end function时弹出(两次)。这样,在R_PAREN和end function之间的任何东西都是函数体,在更高级别的模式中,我可以有一个贪婪规则。

之类的
FunctionStart:       FUNCTION_START_FRAGMENT-> channel(SkipChannel), pushMode(IN_FUNCTION);
mode IN_FUNCTION;
FunctionBodyStart:       R_PAREN_FRAGMENT -> channel(SkipChannel), pushMode(IN_FUNCTION_BODY);
mode IN_FUNCTION_BODY;
FunctionBodyAndFunctionEnd : FUNCTION_END_FRAGMENT -> channel(SkipChannel), popMode, popMode; // double pop
ALL_TEXT : . ; // will consume everything

我对上面的问题是它听起来非常违反直觉,我对ANTLR解析器非常陌生,所以只是试图得到最好的建议来做适合我的目的。

而不是推模式,我只是使用mode(...)切换到另一种模式。这意味着你不需要弹出模式,使它更容易理解发生了什么。

我会这样写:

ExprLexer.g4

lexer grammar ExprLexer;
ScriptStart : '<%' -> mode(Script);
GenericText : . ;
fragment Spaces : [ rnt]+;
fragment Id     : [a-zA-Z0-9_u0080-ufffe]+;
mode Script;
ScriptEnd  : '%>' -> mode(DEFAULT_MODE);
Comment    : ''' ~[rn]* -> skip;
Function   : 'function' -> mode(FunctionDeclaration);
ScriptText : . ;
mode FunctionDeclaration;
FunctionName      : Id;
DeclarationSpaces : Spaces+ -> skip;
OPar              : '(' -> mode(FunctionParameter);
mode FunctionParameter;
ParameterName   : Id;
ParameterSpaces : Spaces+ -> skip;
Comma           : ',';
CPar            : ')' -> mode(InFunction);
mode InFunction;
EndFunction    : 'end' Spaces 'function' -> mode(Script);
FunctionSpaces : Spaces+ -> skip;
FunctionText   : . ;

ExprParser.g4

parser grammar ExprParser;
options { tokenVocab=ExprLexer; }
file
: block* EOF
;
block
: plainText
| ScriptStart script* ScriptEnd
;
plainText
: GenericText+
;
script
: ScriptText+
| function
;
function
: Function FunctionName OPar parameters? CPar functionBody EndFunction
;
functionBody
: FunctionText*
;
parameters
: ParameterName ( Comma ParameterName )*
;

将解析您的输入:

text1
<%
arbitrary script lines1
arbitrary script lines2
function x(a,b)
arbitrary script body containing anything
end function
arbitrary script lines3 again
%>
plain text
<%
arbitrary script lines4 again
function y()
different function body
end function
%>
MU

:

(file 
(block 
(plainText t e x t 1 n)) 
(block <% 
(script n a r b i t r a r y   s c r i p t   l i n e s 1 n a r b i t r a r y   s c r i p t   l i n e s 2 n n) 
(script 
(function function x ( (parameters a , b) ) 
(functionBody a r b i t r a r y s c r i p t b o d y c o n t a i n i n g a n y t h i n g) end function)) 
(script n n a r b i t r a r y   s c r i p t   l i n e s 3   a g a i n n) %>) 
(block 
(plainText n p l a i n   t e x t n)) 
(block <% 
(script n a r b i t r a r y   s c r i p t   l i n e s 4   a g a i n n n) 
(script 
(function function y ( ) 
(functionBody d i f f e r e n t f u n c t i o n b o d y) end function)) 
(script n) %>) 
(block 
(plainText n M U)) <EOF>)

相关内容

  • 没有找到相关文章

最新更新