3

私は現在、ANTLRを使用してJavaで実装された実用的でシンプルな言語を持っています。私がやりたいのは、PHPと同じように、プレーンテキストに埋め込むことです。

例えば:

Lorem ipsum dolor sit amet
<% print('consectetur adipiscing elit'); %>
Phasellus volutpat dignissim sapien.

結果のトークンストリームは次のようになると思います。

CDATA OPEN PRINT OPAREN APOS STRING APOS CPAREN SEMI CLOSE CDATA

どうすればこれを達成できますか、それとももっと良い方法がありますか?

<%ブロックの外側にある可能性のあるものに制限はありません。Michael Mrozekの答えによると、のようなもの<% print('%>'); %>が可能であると思いましたが、そのような状況以外では、<%常にコードブロックの開始を示します。


サンプル実装

私はMichaelMrozekの回答で与えられたアイデアに基づいてソリューションを開発し、ANTLRのゲート付きセマンティック述語を使用してFlexの開始条件をシミュレートしました。

lexer grammar Lexer;

@members {
    boolean codeMode = false;
}

OPEN    : {!codeMode}?=> '<%' { codeMode = true; } ;
CLOSE   : {codeMode}?=> '%>' { codeMode = false;} ;
LPAREN  : {codeMode}?=> '(';
//etc.

CHAR    : {!codeMode}?=> ~('<%');


parser grammar Parser;

options {
    tokenVocab = Lexer;
    output = AST;
}

tokens {
    VERBATIM;
}

program :
    (code | verbatim)+
    ;   

code :
    OPEN statement+ CLOSE -> statement+
    ;

verbatim :
    CHAR -> ^(VERBATIM CHAR)
    ;
4

2 に答える 2

2

しかし、そのような状況以外では、<%は常にコードブロックの開始を示します。

その場合は、最初にファイルをスキャンして埋め込みコードを探し、それができたら、専用のパーサーを使用して埋め込みコードを解析します(タグの前後のノイズはありません)<%%>

ANTLRには、レクサーが入力ファイルの(小さな)部分だけを解析し、残りを無視するようにするオプションがあります。その場合、「結合された文法」(パーサーとレクサーが1つになっている)を作成できないことに注意してください。このような「部分レクサー」を作成する方法は次のとおりです。

// file EmbeddedCodeLexer.g
lexer grammar EmbeddedCodeLexer;

options{filter=true;} // <- enables the partial lexing!

EmbeddedCode
  :  '<%'                            // match an open tag
     (  String                       // ( match a string literal
     |  ~('%' | '\'')                //   OR match any char except `%` and `'`
     |  {input.LT(2) != '>'}?=> '%'  //   OR only match a `%` if `>` is not ahead of it
     )*                              // ) <- zero or more times
     '%>'                            // match a close tag
  ;

fragment
String
  :  '\'' ('\\' . | ~('\'' | '\\'))* '\''
  ;

ここからレクサーを作成する場合:

java -cp antlr-3.2.jar org.antlr.Tool EmbeddedCodeLexer.g 

小さなテストハーネスを作成します。

import org.antlr.runtime.*;

public class Main {
    public static void main(String[] args) throws Exception {
        String source = "Lorem ipsum dolor sit amet       \n"+
                "<%                                       \n"+
                "a = 2 > 1 && 10 % 3;                     \n"+
                "print('consectetur %> adipiscing elit'); \n"+
                "%>                                       \n"+
                "Phasellus volutpat dignissim sapien.     \n"+
                "foo <% more code! %> bar                 \n";
        ANTLRStringStream in = new ANTLRStringStream(source);
        EmbeddedCodeLexer lexer = new EmbeddedCodeLexer(in);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        for(Object o : tokens.getTokens()) {
            System.out.println("=======================================\n"+
                    "EmbeddedCode = "+((Token)o).getText());
        }
    }
}

すべてをコンパイルします。

javac -cp antlr-3.2.jar *.java

最後に、次のようにしてMainクラスを実行します。

// *nix/MacOS
java -cp .:antlr-3.2.jar Main

// Windows
java -cp .;antlr-3.2.jar Main 

次の出力が生成されます。

=======================================
EmbeddedCode = <%                                       
a = 2 > 1 && 10 % 3;                     
print('consectetur %> adipiscing elit'); 
%>
=======================================
EmbeddedCode = <% more code! %>
于 2010-05-13T15:33:25.343 に答える
1

PRINTトークンを持っている可能性は低いですが、実際の概念は問題ないように見えます。レクサーはおそらくIDENTIFIERのようなものを出力し、パーサーはそれが関数呼び出しであることを理解し(たとえば、を探すことによってIDENTIFIER OPAREN ... CPAREN)、適切なことを実行する責任があります。

方法については、ANTLRについては何も知りませんが、フレックスのスタートコンディションのようなものがあると思います。もしそうなら、あなたはINITIAL開始条件に何もしないようにさせることができます、それはすべての実際のトークンが定義されている状態に<%切り替わります。CODEその後、「%>」は元に戻ります。フレックスでは次のようになります。

%s CODE

%%

<INITIAL>{
    "<%"      {BEGIN(CODE);}
    .         {}
}

 /* All these are implicitly in CODE because it was declared %s,
    but you could wrap it in <CODE>{} too
  */
"%>"          {BEGIN(INITIAL);}
"("           {return OPAREN;}
"'"           {return APOS;}
...

%>文字列内のように、終了マーカーではないコンテキストでのマッチングなどに注意する必要があります。許可するかどうかはあなた次第です<% print('%>'); %>が、ほとんどの場合、許可します

于 2010-05-09T18:03:28.317 に答える