【问题标题】:How can my ANTLR parser (not lexer) trigger a lexical "include" (not AST splice)?我的 ANTLR 解析器(不是词法分析器)如何触发词法“包含”(不是 AST 拼接)?
【发布时间】:2012-08-19 17:14:20
【问题描述】:

ANTLR 网站描述了two approaches 以实现“包含”指令。第一种方法是识别词法分析器中的指令并在词法上包含文件(通过将 CharStream 推入堆栈并将其替换为读取新文件的堆栈);二是识别解析器中的指令,启动子解析器解析新文件,并拼接到子解析器生成的AST中。这些都不是我所需要的。

在我正在解析的语言中,由于以下几个原因,识别词法分析器中的指令是不切实际的:

  1. 没有始终表示“这是一个包含指令”的自包含字符模式。例如,顶层的Include "foo"; 是一个包含指令,但在Array bar --> Include "foo";Constant Include "foo"; 中,单词Include 是一个标识符。
  2. 要包含的文件的名称可以作为字符串或常量标识符给出,并且可以使用任意复杂的表达式来定义此类常量。

所以我想从解析器触发包含。但是要执行包含,我不能启动子解析器并将 AST 拼接在一起;我必须拼接令牌。在主文件中以{ 开头并在包含文件中以} 终止的块是合法的。包含在函数中的文件甚至可以关闭函数定义并开始新的定义。

似乎我需要类似于第一种方法的方法,但在 TokenStreams 级别而不是 CharStreams 级别。这是一个可行的方法吗?我需要在堆栈上保留多少状态,以及如何使解析器切换回原始令牌流而不是在遇到 EOF 时终止?或者有没有更好的方法来处理这个问题?

===========

这是该语言的一个示例,演示了在主文件中打开的块可以在包含的文件中关闭(反之亦然)。请注意,当指令在函数内部时,#Include 之前是必需的,但在外部是可选的。

main.inf:

[ 主要的; 打印“这是主要的!”; 如果(0){ #include "其他.h"; 打印“这是其他功能!”; ];

其他.h:

} !万一 ]; !结束主要 [其他功能;

【问题讨论】:

  • 这是为受虐狂设计的语言吗?
  • 嘿。这就是 Inform,一种用于编写文字冒险游戏的语言。类似英语的 Inform 7 (inform7.com) 是当您的设计不受传统上下文无关解析工具限制时可以做的优雅和令人敬畏的事情的一个例子。不幸的是,我正在解析 Inform 6,这是当您的设计不受传统上下文无关解析工具限制时您可以做的难以形容的可怕 事情的示例。
  • @BartKiers 幸运的是,不可以:文件名只能是带引号的字符串或之前使用 Constant 定义的单个标识符。给Constant 的定义必须是编译时常量,所以没有函数调用。该语言也没有文本运算符,因此没有连接,但它可以复制另一个常量:Constant FOO "file.h"; Constant BAR FOO; Include BAR;
  • 但是 Constant 指令通常可以具有任意复杂的表达式,因为它通常与数字一起使用:Constant FOO (BAR + 5 * BAZ); 等,因此在词法分析器中处理 Constant 是不切实际的。

标签: c# include antlr antlr3


【解决方案1】:

每个Include 语句都有可能让您的解析器创建您的词法分析器的新实例,并将词法分析器创建的这些新标记插入到解析器当前所在的索引处(请参阅解析器@ 中的insertTokens(...) 方法987654324@ 块。)。

这是一个快速演示:

Inform6.g

grammar Inform6;

options {
  output=AST;
}

tokens {
  STATS;
  F_DECL;
  F_CALL;
  EXPRS;
}

@parser::header {
  import java.util.Map;
  import java.util.HashMap;
}

@parser::members {
  private Map<String, String> memory = new HashMap<String, String>(); 

  private void putInMemory(String key, String str) {
    String value;
    if(str.startsWith("\"")) {
      value = str.substring(1, str.length() - 1);
    }
    else {
      value = memory.get(str);
    }
    memory.put(key, value);
  }

  private void insertTokens(String fileName) {
    // possibly strip quotes from `fileName` in case it's a Str-token
    try {
      CommonTokenStream thatStream = new CommonTokenStream(new Inform6Lexer(new ANTLRFileStream(fileName)));
      thatStream.fill();
      List extraTokens = thatStream.getTokens();
      extraTokens.remove(extraTokens.size() - 1); // remove EOF
      CommonTokenStream thisStream = (CommonTokenStream)this.getTokenStream();
      thisStream.getTokens().addAll(thisStream.index(), extraTokens);
    } catch(Exception e) {
      e.printStackTrace();
    }
  }
}

parse
 : stats EOF -> stats
 ;

stats
 : stat* -> ^(STATS stat*)
 ;

stat
 : function_decl
 | function_call
 | include
 | constant
 | if_stat
 ;

if_stat
 : If '(' expr ')' '{' stats '}' -> ^(If expr stats)
 ;

function_decl
 : '[' id ';' stats ']' ';' -> ^(F_DECL id stats)
 ;

function_call
 : Id exprs ';' -> ^(F_CALL Id exprs)
 ;

include
 : Include Str ';' {insertTokens($Str.text);}            -> /* omit statement from AST */
 | Include id ';'  {insertTokens(memory.get($id.text));} -> /* omit statement from AST */
 ;

constant
 : Constant id expr ';' {putInMemory($id.text, $expr.text);} -> ^(Constant id expr)
 ;

exprs
 : expr (',' expr)* -> ^(EXPRS expr+)
 ;

expr
 : add_expr
 ;

add_expr
 : mult_expr (('+' | '-')^ mult_expr)*
 ;

mult_expr
 : atom (('*' | '/')^ atom)*
 ;

atom
 : id
 | Num
 | Str
 | '(' expr ')' -> expr
 ;

id
 : Id
 | Include
 ;

Comment  : '!' ~('\r' | '\n')* {skip();};
Space    : (' ' | '\t' | '\r' | '\n')+ {skip();};
If       : 'if';
Include  : 'Include';
Constant : 'Constant';
Id       : ('a'..'z' | 'A'..'Z') ('a'..'z' | 'A'..'Z' | '0'..'9')+;
Str      : '"' ~'"'* '"';
Num      : '0'..'9'+ ('.' '0'..'9'+)?;

main.inf

Constant IMPORT "other.h";

[ Main;
  print "This is Main!";
  if (0) {    

  Include IMPORT;

  print "This is OtherFunction!";
];

其他.h

  } ! end if
];  ! end Main

[ OtherFunction;

Main.java

import org.antlr.runtime.*;
import org.antlr.runtime.tree.*;
import org.antlr.stringtemplate.*;

public class Main {
  public static void main(String[] args) throws Exception {
    // create lexer & parser
    Inform6Lexer lexer = new Inform6Lexer(new ANTLRFileStream("main.inf"));
    Inform6Parser parser = new Inform6Parser(new CommonTokenStream(lexer));

    // print the AST
    DOTTreeGenerator gen = new DOTTreeGenerator();
    StringTemplate st = gen.toDOT((CommonTree)parser.parse().getTree());
    System.out.println(st);
  }
}

要运行演示,请在命令行中执行以下操作:

java -cp antlr-3.3.jar org.antlr.Tool Inform6.g
javac -cp antlr-3.3.jar *.java
java -cp .:antlr-3.3.jar Main

您将看到的输出对应于以下 AST:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-01-30
    • 2021-11-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-04
    相关资源
    最近更新 更多