gpt4 book ai didi

java - 交互式 Ant

转载 作者:搜寻专家 更新时间:2023-11-01 01:57:32 29 4
gpt4 key购买 nike

我正在尝试使用 antlr 编写一种简单的交互式(使用 System.in 作为源)语言,但我遇到了一些问题。我在网上找到的例子都是使用每行循环,例如:

while(readline)
result = parse(line)
doStuff(result)

但是如果我正在编写类似 pascal/smtp/etc 的东西,“第一行”看起来像 X 要求怎么办?我知道它可以在 doStuff 中检查,但我认为逻辑上它是语法的一部分。

或者如果一个命令被分成多行怎么办?我可以试试

while(readline)
lines.add(line)
try
result = parse(lines)
lines = []
doStuff(result)
catch
nop

但是我也隐藏了真正的错误。

或者我可以每次都重新解析所有行,但是:

  1. 会很慢
  2. 有些指令我不想运行两次

这可以用 ANTLR 完成吗?如果不能,可以用其他东西吗?

最佳答案

Dutow wrote:

Or I could reparse all lines everytime, but:

it will be slow there are instructions I don't want to run twice Can this be done with ANTLR, or if not, with something else?

是的,ANTLR 可以做到这一点。也许不是开箱即用,但使用一些自定义代码,这肯定是可能的。您也不需要为它重新解析整个 token 流。

假设你想逐行解析一个非常简单的语言,其中每一行要么是一个program声明,要么是一个uses声明,要么是一个语句

它应该始终以程序 声明开头,后跟零个或多个uses 声明,然后是零个或多个语句uses 声明不能跟在 statement 之后,并且不能有多个 program 声明。

为简单起见,语句只是一个简单的赋值:a = 4b = 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();};

但是,我们当然需要添加一些检查。此外,默认情况下,解析器在其构造函数中采用 token 流,但由于我们计划在解析器中逐行添加 token ,因此我们需要在解析器中创建一个新的构造函数。您可以通过将自定义成员放在 @parser::members { ... } @lexer::members { ... } 中来在词法分析器或解析器类中添加自定义成员> 部分分别。我们还将添加几个 boolean 标志来跟踪 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
}
}

现在在我们的语法中,我们将检查几个 gated semantic predicates 如果我们以正确的顺序解析声明。在解析了某个声明或语句之后,我们将希望翻转某些 boolean 标志以允许或禁止声明。这些 boolean 标志的翻转是通过每个规则的 @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();};

可以通过以下类进行测试:

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!");
}
}

要运行这个测试类,请执行以下操作:

# 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

如您所见,您只能声明一个程序一次:

> program A
program <- A

> program B
line 1:0 rule programDeclaration failed predicate: {!programDeclDone}?

uses 不能在 statement 之后:

> 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}?

并且您必须以程序声明开始:

> uses foo
line 1:0 rule parse failed predicate: {programDeclDone}?

关于java - 交互式 Ant ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5110507/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com