我目前使用ANTLR在Java中实现了一种简单易用的语言。我想做的是以纯文本形式嵌入,与PHP类似。
例如:
Lorem ipsum dolor sit amet
<% print('consectetur adipiscing elit'); %>
Phasellus volutpat dignissim sapien.
我预计生成的令牌流看起来像:
CDATA OPEN PRINT OPAREN APOS STRING APOS CPAREN SEMI CLOSE CDATA
我如何实现这一目标,还是有更好的方法?
对<%
块之外的内容没有限制。根据Michael Mrozek的回答,我假设<% print('%>'); %>
之类的东西是可能的,但在这种情况之外,<%
总是表示代码块的开始。
我根据Michael Mrozek的回答提出了一个解决方案,使用ANTLR的门控语义谓词来模拟Flex的启动条件:
lexer grammar Lexer;
@members {
boolean codeMode = false;
}
OPEN : {!codeMode}?=> '<%' { codeMode = true; } ;
CLOSE : {codeMode}?=> '%>' { codeMode = false;} ;
LPAREN : {codeMode}?=> '(';
//etc.
CHAR : {!codeMode}?=> ~('<%');
parser grammar Parser;
options {
tokenVocab = Lexer;
output = AST;
}
tokens {
VERBATIM;
}
program :
(code | verbatim)+
;
code :
OPEN statement+ CLOSE -> statement+
;
verbatim :
CHAR -> ^(VERBATIM CHAR)
;
答案 0 :(得分:2)
但在这种情况之外,&lt;%将始终指示代码块的开始。
在这种情况下,首先扫描文件以获取嵌入代码,然后一旦有了这些代码,使用专用解析器解析嵌入代码(在<%
之前和%>
标记之后没有噪声)。
ANTLR可以选择让词法分析器只解析输入文件的(小)部分而忽略其余部分。请注意,在这种情况下,您无法创建“组合语法”(解析器和词法分析器)。以下是如何创建这样的“部分词法分析器”:
// file EmbeddedCodeLexer.g
lexer grammar EmbeddedCodeLexer;
options{filter=true;} // <- enables the partial lexing!
EmbeddedCode
: '<%' // match an open tag
( String // ( match a string literal
| ~('%' | '\'') // OR match any char except `%` and `'`
| {input.LT(2) != '>'}?=> '%' // OR only match a `%` if `>` is not ahead of it
)* // ) <- zero or more times
'%>' // match a close tag
;
fragment
String
: '\'' ('\\' . | ~('\'' | '\\'))* '\''
;
如果您现在从中创建词法分析器:
java -cp antlr-3.2.jar org.antlr.Tool EmbeddedCodeLexer.g
并创建一个小测试工具:
import org.antlr.runtime.*;
public class Main {
public static void main(String[] args) throws Exception {
String source = "Lorem ipsum dolor sit amet \n"+
"<% \n"+
"a = 2 > 1 && 10 % 3; \n"+
"print('consectetur %> adipiscing elit'); \n"+
"%> \n"+
"Phasellus volutpat dignissim sapien. \n"+
"foo <% more code! %> bar \n";
ANTLRStringStream in = new ANTLRStringStream(source);
EmbeddedCodeLexer lexer = new EmbeddedCodeLexer(in);
CommonTokenStream tokens = new CommonTokenStream(lexer);
for(Object o : tokens.getTokens()) {
System.out.println("=======================================\n"+
"EmbeddedCode = "+((Token)o).getText());
}
}
}
全部编译:
javac -cp antlr-3.2.jar *.java
最后通过执行以下操作来运行Main类:
// *nix/MacOS
java -cp .:antlr-3.2.jar Main
// Windows
java -cp .;antlr-3.2.jar Main
它将产生以下输出:
=======================================
EmbeddedCode = <%
a = 2 > 1 && 10 % 3;
print('consectetur %> adipiscing elit');
%>
=======================================
EmbeddedCode = <% more code! %>
答案 1 :(得分:1)
实际概念看起来很好,尽管你不太可能有PRINT令牌;词法分析器可能会发出类似IDENTIFIER的东西,解析器将负责确定它是一个函数调用(例如通过查找IDENTIFIER OPAREN ... CPAREN
)并做适当的事情。
至于怎么做,我对ANTLR一无所知,但它可能有像flex start conditions这样的东西。如果是这样,您可以让INITIAL
启动条件不执行任何操作,只查找<%
,这将切换到CODE
状态,其中定义了所有实际令牌;那么'%&gt;'会转回去。在flex中它将是:
%s CODE
%%
<INITIAL>{
"<%" {BEGIN(CODE);}
. {}
}
/* All these are implicitly in CODE because it was declared %s,
but you could wrap it in <CODE>{} too
*/
"%>" {BEGIN(INITIAL);}
"(" {return OPAREN;}
"'" {return APOS;}
...
你需要注意在不是结束标记的上下文中匹配%>
之类的东西,比如在字符串中;如果你想允许<% print('%>'); %>
,你可以自己决定