我想为一个简单的脚本语言做一个简单的解析器,它有文本块和脚本块,在这些脚本块中,我希望能够定义一个函数,以及执行任何类型的通用语句。
我真的不需要知道或关心什么被归类为"语句",但我确实需要解析函数声明。所以即使它看起来像一个while循环而我没有while循环的规则,我能匹配一个通用语句规则吗?如何获取内容?
使用一个通用规则,我可以做"通用文本"部分不错,但在脚本模式我不太成功,我试着拉出嵌套模式,我设置了一个' in FUNCTION'模式,但不断遇到障碍。
例如,当在functionDeclaration
中的statement
中,我如何匹配直到end function
的所有内容。此外,我如何匹配一个"通用的";语句,这样我就不需要像emptyStatement
或assignmentStatement
这样的语句类型。即使它只是变成了一个大的"脚本代码团"我没意见。
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用最贪婪的规则覆盖这些规则),所以我不能有一个参数规则,这是确认它们适合一个标识符,同时有一个'。+'规则收集函数体。
一种折衷方法是将函数作为一个整体收集(function
和end 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>)