当前位置: 首页 > 面试题库 >

互动式蚂蚁

海翔宇
2023-03-14
问题内容

我正在尝试使用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写道:

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

这很慢,有些指令我不想运行两次,这可以用ANTLR来完成吗?

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

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

它应始终以program声明开头,然后是零个或多个uses声明,然后是零个或多个statements。uses声明不能在statements
之后,并且不能超过一个program声明。

为简单起见,a statement只是一个简单的赋值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();};

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

现在在语法中,我们将检查几个 门控语义谓词
是否以正确的顺序解析声明。并且在解析了某个声明或语句之后,我们希望从那时开始翻转某些布尔标志以允许或禁止声明。这些布尔标志的翻转是通过匹配解析器规则的标记
@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一次:

> program A
                         program <- A

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

uses不能在statements 之后出现:

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

并且必须从program声明开始:

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


 类似资料:
  • 响应式交互能让用户信任,并且吸引他们。 当用户操作一个美观且符合常理的应用时,他们会感到满意甚至很高兴。那是一种经过深思熟虑、有目的、非随机的而且可以带有轻微异想天开但不会让人分心的交互。 在 material design 中,应用是响应式的并且渴望用户操作的: 触摸,语音,键盘及鼠标作为首要考虑的输入方式。 虽然 UI 元素是有形的,但是他们被限制在屏幕里面(电脑或者移动设备的屏幕),视觉元素

  • 我正在尝试自动化项目的构建,现在如果我使用Ant构建它会失败。 如果我用netbeans构建,它是成功的。(尽管有时如果我做了任何更改,比如svnrevert或其他我尝试过的东西,我必须重新启动netbeans) 如果我在netbean构建后使用ant构建,它就成功了。 有关于如何解决这个问题的建议吗? 我已经搜索了高和低如何处理这一点,但不知道。据我所知,build.xml和其他构建相关文件是从

  • 问题内容: 就我而言,每次只想拖一个点。但是,由于两个点严重重叠,因此拖动一个点将导致另一个点被拖动。我如何只拖动上面的点?谢谢! 问题答案: Joe的方法工作正常,但是它使一组可拖动点作为一个类,而不是单个可拖动点类。我只是遇到了一种使用动画blit技术解决上述问题的替代方法。它不仅使拖动更快,更流畅,而且只能拖动一个点。请参阅以下代码。

  • 原文:Interactive navigation 所有图形窗口都带有导航工具栏,可用于浏览数据集。 以下是工具栏底部的每个按钮的说明: Home(首页)、Forward(前进)和Back(后退)按钮: 这些类似于 Web 浏览器的前进和后退按钮。 它们用于在之前定义的视图之间来回浏览。 它们没有意义,除非你已经使用平移和缩放按钮访问了其他地方。 这类似于尝试在访问新页面之前单击 Web 浏览器上

  • 我曾尝试在VisualStudio2008中设置Interactive Broker的C API,但我知道的C非常有限,并且不断出现错误: 1)是否有任何方法可以使用某种轻量级的脚本语言来连接到Interactive Brokers并进行交易。 像Python这样轻量级的东西就可以了,是的,我已经研究过IBMY,但我不明白java2python系统是如何工作的。 2) 您是如何设置您的自动系统的,

  • 问题内容: 在定义顺序构建步骤时,我使用元素的属性。我最近看到了一个ant文件,其中的构建顺序是由目标内部的元素定义的。为了显示 : 与 两种方法之间是否有真正的区别?是其中之一更可取吗? 问题答案: 与这两种方法的主要区别在于目标是 始终 执行,而在目标只有在包含目标是执行。 一个澄清的例子: 在这里,将始终执行,而只有在定义时才会执行。 在此,仅当is即定义时才执行。