2

ANTLR Webサイトでは、「include」ディレクティブを実装するための2つのアプローチについて説明しています。最初のアプローチは、レクサー内のディレクティブを認識し、ファイルを字句的に含めることです(CharStreamをスタックにプッシュし、新しいファイルを読み取るものに置き換えることによって)。2つ目は、パーサーでディレクティブを認識し、サブパーサーを起動して新しいファイルを解析し、サブパーサーによって生成されたASTでスプライスします。これらのどちらも私が必要としているものではありません。

私が解析している言語では、レクサーのディレクティブを認識することは、いくつかの理由で実用的ではありません。

  1. 常に「これはincludeディレクティブです」を意味する自己完結型の文字パターンはありません。たとえば、Include "foo";最上位にはincludeディレクティブがありますが、inArray bar --> Include "foo";またはConstant Include "foo";wordIncludeは識別子です。
  2. 含めるファイルの名前は、文字列または定数識別子として指定でき、そのような定数は任意の複雑な式で定義できます。

したがって、パーサーからインクルードをトリガーしたいと思います。しかし、包含を実行するために、サブパーサーを起動してASTをつなぎ合わせることができません。トークンをつなぎ合わせる必要があります。{ブロックがメインファイルで始まり、インクルードさ}れたファイルで終了することは合法です。関数内に含まれるファイルは、関数定義を閉じて新しい定義を開始することもできます。

最初のアプローチのようなものが必要なようですが、CharStreamsではなくTokenStreamsのレベルです。それは実行可能なアプローチですか?スタックに保持する必要のある状態の量と、パーサーがEOFに達したときに終了するのではなく、元のトークンストリームに戻すにはどうすればよいですか?または、これを処理するためのより良い方法はありますか?

==========

これは言語の例であり、メインファイルで開かれたブロックをインクルードファイルで閉じることができることを示しています(またはその逆)。#ディレクティブが関数の内部にある場合はbeforeIncludeが必要ですが、外部ではオプションであることに注意してください。

main.inf:

[ 主要;
  印刷"これはメインです!";
  if(0){
  #include "other.h";
  print "これはOtherFunctionです!";
];

other.h:

  }!終了する場合
]; !メインを終了

[OtherFunction;
4

1 に答える 1

2

ステートメントごとIncludeに、パーサーがレクサーの新しいインスタンスを作成し、パーサーが現在いるインデックスにレクサーが作成するこれらの新しいトークンを挿入する可能性があります (insertTokens(...)パーサーの@membersブロックのメソッドを参照してください)。

簡単なデモを次に示します。

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 メイン

表示される出力は、次の AST に対応しています。

ここに画像の説明を入力

于 2012-08-24T20:20:37.733 に答える