这个问题的后续问题Bart已经完美地回答了
我的目标可能是为"通用脚本行"one_answers";或"函数体内的行",理想情况下丢弃空白,但仍然可以批量获取<%
和%>
标记之外的任何行。我想到了一个解决方案,但看着这棵树,它似乎很乱。
这是我的lexer:
lexer grammar CmScriptLexer;
//Whitespace: Spaces -> channel(HIDDEN);
ScriptStart : '<%' (Spaces)* -> mode(Script);
SpacesPlain : [rn]+ -> skip;
GenericText : . ;
mode Script;
ScriptEnd : '%>' -> mode(DEFAULT_MODE);
Comment : ''' ~[rn]* -> skip;
Function : 'function' -> mode(FunctionDeclaration);
NL : [rn]+;
ScriptText : . ;
mode FunctionDeclaration;
FunctionComment : ''' ~[rn]* -> skip;
FunctionName : Id;
DeclarationSpaces : Spaces+ -> skip;
OPar : '(' -> mode(FunctionParameter);
mode FunctionParameter;
FunctionParameterComment : ''' ~[rn]* -> skip;
ParameterName : Id;
ParameterSpaces : Spaces+ -> skip;
Comma : ',';
CPar : ')' -> mode(InFunction);
mode InFunction;
FunctionBodyComment : ''' ~[rn]* -> skip;
EndFunction : 'end' Spaces 'function' -> mode(Script);
FunctionLine : ~[ rn]+;
FunctionSpaces : Spaces+;
//FunctionText : . ;
fragment Spaces : [ rnt]+;
fragment Id : [a-zA-Z0-9_u0080-ufffe]+;
和解析器:
parser grammar CmScriptParser;
options { tokenVocab=CmScriptLexer; }
file
: block* EOF
;
block
: plainText
| ScriptStart script* ScriptEnd
;
plainText
: GenericText+ NL*
;
script
: simpleScript NL*
| function NL*
;
simpleScript
: ScriptText+
;
function
: Function FunctionName OPar parameters? CPar functionBody EndFunction
;
functionBody
: functionLines+
;
functionLines
: FunctionSpaces* functionLine FunctionSpaces*
;
functionLine
: FunctionLine+
;
parameters
: ParameterName ( Comma ParameterName )*
;
最后是我用作测试用例的:
foo
bar
<%
line 1
line 2
function x(y)
spanning
multiple
lines
end function
function a(b) no newlines end function
%>
baz
我的问题是它看起来真的很冗长,我害怕我的"解决方案"。而测试用例只是布局不佳,我可能过度思考规则。
有什么改进的建议吗?我想要的只是修剪线条元素,因此匹配n nntscript line nntn
之类的东西导致script line
的行是理想的。
编辑:添加我认为是我所追求的一个例子,再一次,可能不是最好的表达方式:
simpleScript:
scriptLine: line1
scriptLine: line2
function:
name: x
parameters:
paramter: y
body:
functionLine: spanning
functionLine: multiple
functionLine: lines
function:
name: a
parameters:
paramter: b
body:
functionLine: no newlines
最后的目标是当遍历树时,我可以创建一个新的"函数调用对象",并调用像
这样的东西script = new Script() // on script "enter"
script.addLine("line 1")
script.addLine("line 2")
program.addNode(script) // on script "exit"
...
function = new Function() // on function "enter"
function.setName("y") // on "function"?
...
function.addParameter("a") // on "parameter"
...
function.addBodyLine("spanning") // on "line" ??
function.addBodyLine("multiple")
function.addBodyLine("lines")
...
program.addFunctionDeclaration(function) // on function "exit" once complete
问题是,在脚本中,您不能简单地告诉语法匹配除换行符以外的所有非空格。当然,这将匹配line 1
,但这也将匹配function x(y)
,因为词法分析器会贪婪地匹配(它试图消耗尽可能多的字符)。因此,您必须在空格上分割标记。
您可以使用~[ trn]+
合并一些单个字符标记,但是您不能创建导致多个单词之间有空格作为单个标记匹配的标记。
像这样:
lexer grammar CmScriptLexer;
ScriptStart : '<%' Spaces* -> mode(Script);
GenericText : ~[ trn]+;
TextSpaces : Spaces -> skip;
mode Script;
ScriptEnd : '%>' -> mode(DEFAULT_MODE);
Comment : ''' ~[rn]* -> skip;
Function : 'function' -> mode(FunctionDeclaration);
NL : [rn]+;
ScriptText : ~[ trn]+;
SciptSpaces : Spaces -> skip;
mode FunctionDeclaration;
FunctionComment : ''' ~[rn]* -> skip;
FunctionName : Id;
DeclarationSpaces : Spaces+ -> skip;
OPar : '(' -> mode(FunctionParameter);
mode FunctionParameter;
FunctionParameterComment : ''' ~[rn]* -> skip;
ParameterName : Id;
ParameterSpaces : Spaces+ -> skip;
Comma : ',';
CPar : ')' -> mode(InFunction);
mode InFunction;
FunctionBodyComment : ''' ~[rn]* -> skip;
EndFunction : 'end' Spaces 'function' -> mode(Script);
FunctionLine : ~[ trn]+;
FunctionSpaces : Spaces+ -> skip;
fragment Spaces : [ rnt]+;
fragment Id : [a-zA-Z0-9_u0080-ufffe]+;