Dut*_*tow 4 java parsing antlr
我正在尝试使用antlr编写一种简单的交互式(使用System.in作为源代码)语言,但我遇到了一些问题。我在网上找到的示例都是按行周期使用的,例如:
while(readline)
result = parse(line)
doStuff(result)
Run Code Online (Sandbox Code Playgroud)
但是,如果我写的是pascal / smtp / etc之类的东西,并且带有“第一行”,看起来像X需求,该怎么办?我知道可以在doStuff中检查它,但是从逻辑上讲,它是语法的一部分。
还是将命令分成多行怎么办?我可以试试
while(readline)
lines.add(line)
try
result = parse(lines)
lines = []
doStuff(result)
catch
nop
Run Code Online (Sandbox Code Playgroud)
但是与此同时,我也隐藏了真正的错误。
或者我可以每次重新分析所有行,但是:
可以使用ANTLR完成此操作吗?
杜托写道:
或者我可以每次重新分析所有行,但是:
会有一些指令,我不想运行两次,这会很慢。可以使用ANTLR进行此操作吗?如果没有,可以使用其他方法完成此操作吗?
是的,ANTLR可以做到这一点。也许不是开箱即用,但是有了一些自定义代码,这肯定是可能的。您也不需要为此重新解析整个令牌流。
假设您要逐行解析一种非常简单的语言,其中每行要么是一个program
声明,要么是一个声明,要么是uses
一个statement
。
它应始终以program
声明开头,然后是零个或多个uses
声明,然后是零个或多个statement
s。uses
声明不能在statement
s 之后,并且声明不能超过一个program
。
为简单起见,a statement
只是一个简单的赋值:a = 4
或b = a
。
这种语言的ANTLR语法如下所示:
grammar REPL;
parse
: programDeclaration EOF
| usesDeclaration EOF
| statement EOF
;
programDeclaration
: PROGRAM ID
;
usesDeclaration
: USES idList
;
statement
: ID '=' (INT | ID)
;
idList
: ID (',' ID)*
;
PROGRAM : 'program';
USES : 'uses';
ID : ('a'..'z' | 'A'..'Z' | '_') ('a'..'z' | 'A'..'Z' | '_' | '0'..'9')*;
INT : '0'..'9'+;
SPACE : (' ' | '\t' | '\r' | '\n') {skip();};
Run Code Online (Sandbox Code Playgroud)
但是,我们当然需要添加一些检查。另外,默认情况下,解析器会在其构造函数中使用令牌流,但是由于我们计划逐行在解析器中滴入令牌,因此我们需要在解析器中创建一个新的构造函数。您可以通过将自定义成员分别放在@parser::members { ... }
或@lexer::members { ... }
部分中来在lexer或parser类中添加自定义成员。我们还将添加几个布尔标志来跟踪program
声明是否已经发生以及是否uses
允许声明。最后,我们将添加一个process(String source)
方法,该方法为每行新代码创建一个词法分析器,该词法分析器将被馈送到解析器。
所有这些看起来像:
@parser::members {
boolean programDeclDone;
boolean usesDeclAllowed;
public REPLParser() {
super(null);
programDeclDone = false;
usesDeclAllowed = true;
}
public void process(String source) throws Exception {
ANTLRStringStream in = new ANTLRStringStream(source);
REPLLexer lexer = new REPLLexer(in);
CommonTokenStream tokens = new CommonTokenStream(lexer);
super.setTokenStream(tokens);
this.parse(); // the entry point of our parser
}
}
Run Code Online (Sandbox Code Playgroud)
现在在语法中,我们将检查几个门控语义谓词是否以正确的顺序解析声明。并且在解析了某个声明或语句之后,我们希望从那时开始翻转某些布尔标志以允许或禁止声明。这些布尔标志的翻转是通过匹配解析器规则的标记后@after { ... }
执行的每个规则的部分完成的(毫不奇怪)。
现在,您的最终语法文件如下所示(包括一些System.out.println
用于调试的文件):
grammar REPL;
@parser::members {
boolean programDeclDone;
boolean usesDeclAllowed;
public REPLParser() {
super(null);
programDeclDone = false;
usesDeclAllowed = true;
}
public void process(String source) throws Exception {
ANTLRStringStream in = new ANTLRStringStream(source);
REPLLexer lexer = new REPLLexer(in);
CommonTokenStream tokens = new CommonTokenStream(lexer);
super.setTokenStream(tokens);
this.parse();
}
}
parse
: programDeclaration EOF
| {programDeclDone}? (usesDeclaration | statement) EOF
;
programDeclaration
@after{
programDeclDone = true;
}
: {!programDeclDone}? PROGRAM ID {System.out.println("\t\t\t program <- " + $ID.text);}
;
usesDeclaration
: {usesDeclAllowed}? USES idList {System.out.println("\t\t\t uses <- " + $idList.text);}
;
statement
@after{
usesDeclAllowed = false;
}
: left=ID '=' right=(INT | ID) {System.out.println("\t\t\t " + $left.text + " <- " + $right.text);}
;
idList
: ID (',' ID)*
;
PROGRAM : 'program';
USES : 'uses';
ID : ('a'..'z' | 'A'..'Z' | '_') ('a'..'z' | 'A'..'Z' | '_' | '0'..'9')*;
INT : '0'..'9'+;
SPACE : (' ' | '\t' | '\r' | '\n') {skip();};
Run Code Online (Sandbox Code Playgroud)
可以通过以下课程进行测试:
import org.antlr.runtime.*;
import java.util.Scanner;
public class Main {
public static void main(String[] args) throws Exception {
Scanner keyboard = new Scanner(System.in);
REPLParser parser = new REPLParser();
while(true) {
System.out.print("\n> ");
String input = keyboard.nextLine();
if(input.equals("quit")) {
break;
}
parser.process(input);
}
System.out.println("\nBye!");
}
}
Run Code Online (Sandbox Code Playgroud)
要运行此测试类,请执行以下操作:
# generate a lexer and parser:
java -cp antlr-3.2.jar org.antlr.Tool REPL.g
# compile all .java source files:
javac -cp antlr-3.2.jar *.java
# run the main class on Windows:
java -cp .;antlr-3.2.jar Main
# or on Linux/Mac:
java -cp .:antlr-3.2.jar Main
Run Code Online (Sandbox Code Playgroud)
如您所见,您只能声明program
一次:
> program A
program <- A
> program B
line 1:0 rule programDeclaration failed predicate: {!programDeclDone}?
Run Code Online (Sandbox Code Playgroud)
uses
不能在statement
s 之后出现:
> program X
program <- X
> uses a,b,c
uses <- a,b,c
> a = 666
a <- 666
> uses d,e
line 1:0 rule usesDeclaration failed predicate: {usesDeclAllowed}?
Run Code Online (Sandbox Code Playgroud)
并且必须从program
声明开始:
> uses foo
line 1:0 rule parse failed predicate: {programDeclDone}?
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
1874 次 |
最近记录: |