1

Java で実装された JavaScript コレーター/コンポジターに取り組んでいます。それは機能しますが、それを実装するためのより良い方法が必要であり、レクサーが前進する方法かもしれないと思いますが、私は少しあいまいです.

JavaScript 言語のサブセットであるコンポジターのメタ構文を開発しました。典型的な JavaScript インタープリターに関する限り、コンポジターのメタ構文は有効ですが、機能しません (コンポジターが解釈するはずのコード ブロックが後に続くラベルとして、予約語の同義語を使用しています)。現在、スキャナーと正規表現を使用してソース ファイル内のメタ構文を検出し、法的な表現の検出に基づいて浅い字句変換を行っています。

書き直された JavaScript とスキャナー/パーサーの間には密接な関係がありますが、これは満足のいくものではありません。書き直された JavaScript は、この目的のために特別に作成されたオブジェクト サポート ライブラリの機能を使用しており、そのライブラリは変更される可能性があります。

Backaus-Naur または EBNF でメタ構文だけを宣言し、それをレクサー (ANTRL?) にフィードし、ソース ファイルで検出されたメタ構文式に基づいて、コンポジターに特定のアクションを指示できることを願っています。必要なスクリプトを別のスクリプトに追加したり、変数を宣言したり、適切にパラメータ化されたライブラリ関数呼び出し用のテキストを生成したり、スクリプトを圧縮したりします。

これはコンポジターを作成する適切な方法ですか? JavaScript を合成するためにスキャナー/パーサー/レクサーのアプローチを使用する必要がありますか? フィードバックをいただければ幸いです。どこから始めればよいかわかりません :)

更新: これは、より具体的な例です-メタ構文を使用したサンプルオブジェクト宣言:

namespace: ie.ondevice
{
    use: ie.ondevice.lang.Mixin;
    use: ie.ondevice.TraitsDeclaration;

    declare: Example < Mixin | TraitsDeclaration
    {
        include: "path/to/file.extension";
        // implementation here
    }
 }

これは、オブジェクト ie.ondevice.Example を記述します。これは、Mixin を継承し、TraitsDeclaration に似ています (つまり、「同じ関数と特性を実装します」)。コンポジターは use ステートメントを検出し、名前空間が有効なファイルの場所にマップされていない場合は失敗します。そうでない場合は、オブジェクト宣言が存在するスクリプトの先頭に追加し、照合前にメタ構文を前処理します。

私が言及したオブジェクト サポート ライブラリの観点から表現された書き換え規則は、次のようなファイルになります(私はオブジェクトを表現する方法をいくつか開発しました)。

module("ie.ondevice.Example", function (mScope)
{
   // mScope is a delegate
   mScope.use("ie.ondevice.lang.Mixin");
   mScope.use("ie.ondevice.TraitsDeclaration");

   // As a result of two use statements, the mScope.localVars string would
   // would look like this: "var Mixin= ie.ondevice.lang.Mixin, TraitsDeclaration= ie.ondevice.TraitsDeclaration
   // by evaling we introduce 'imported' objects with their 'local'names
   eval(mScope.localVars); 

   // Function.prototype has been extended with the functions
   // inherits, define, defineStatic, resembles and getName

   // Prototypal inheritance using an anonymous bridge constructor
   Example.inherits(Mixin);

   // named methods and properties are added to Example.prototype
   Example.define
   (
       // functions and other properties
   );
   // ensures that Example.prototype has all the same
   // property names and types as TraitsDeclaration.prototype
   // throwing an exception if not the case.
   // This is optionally turned off for production- these
   // operations are only performed when the object is declared
   // - instantiation incurs no additional overhead
   Example.resembles(TraitsDeclaration);

   // constructor
   function Example ()
   {
       Mixin.call(this);
   };

   // will generate the ie.ondevice object hierarchy if required
   // and avail the constructor to it
   mScope.exports(Example);
 });

おそらく私は自分の要件を設計しすぎていますが、私が本当に欲しいのは、イベント駆動型のコレーターです。リスナーは、ディレクティブ検出に疎結合できます。

4

2 に答える 2

4

はい、パーサージェネレーター(ANTLRなど)を使用することがIMOへの道です。解析しようとしているもののより具体的な例を提供する場合、おそらく私 (または他の誰か) がさらにあなたを助けることができます。

Scott Stanchfieldは、最初からANTLRの優れたビデオ チュートリアルをいくつか作成しました。

編集:

あなたの例を考えると:

namespace: ie.ondevice
{
    use: ie.ondevice.lang.Mixin;
    use: ie.ondevice.TraitsDeclaration;

    declare: Example < Mixin | TraitsDeclaration
    {
        include: "path/to/file.extension";
        // implementation here
    }
}

(ANTLRの)文法は次のようになります。

parse
    :   'namespace' ':' packageOrClass '{'
            useStatement*
            objDeclaration
        '}'
    ;

useStatement
    :    'use' ':' packageOrClass ';'
    ;

includeStatement
    :    'include' ':' StringLiteral ';'
    ;

objDeclaration
    :    'declare' ':' Identifier ( '<' packageOrClass )? ( '|' packageOrClass )* '{' 
             includeStatement* 
         '}'
    ;

packageOrClass
    :    ( Identifier ( '.' Identifier )* )
    ;

StringLiteral
    :    '"' ( '\\\\' | '\\"' | ~( '"' | '\\' ) )* '"'
    ;

Identifier
    :    ( 'a'..'z' | 'A'..'Z' | '_' ) ( 'a'..'z' | 'A'..'Z' | '_' | '0'..'9' )*    
    ;

LineComment
    :    '//' ~( '\r' | '\n' )* ( '\r'? '\n' | EOF )     
    ;

Spaces
    :    ( ' ' | '\t' | '\r' | '\n' )     
    ;

上記は混合文法と呼ばれます (ANTLR はレクサーとパーサーの両方を生成します)。大文字で始まる「ルール」はレクサールールであり、小文字で始まるルールはパーサールールです。

これで、生成されたパーサーにFJSObject (Fuzzy JavaScript Object)を作成させることができます。

class FJSObject {

    String name;
    String namespace;
    String inherit;
    List<String> use;
    List<String> include;
    List<String> resemble;

    FJSObject() {
        use = new ArrayList<String>();
        include = new ArrayList<String>();
        resemble = new ArrayList<String>();
    }

    @Override
    public String toString() {
        StringBuilder b = new StringBuilder();
        b.append("name      : ").append(name).append('\n');
        b.append("namespace : ").append(namespace).append('\n');
        b.append("inherit   : ").append(inherit).append('\n');
        b.append("resemble  : ").append(resemble).append('\n');
        b.append("use       : ").append(use).append('\n');
        b.append("include   : ").append(include);
        return b.toString();
    }
}

パーサーがトークンストリームを通過している間、パーサーは単にFJSObjectの変数を「埋める」だけです。プレーンな Java コードをラップして文法に埋め込むことができ{ます}。次に例を示します。

grammar FJS;

@parser::members {FJSObject obj = new FJSObject();}

parse
    :   'namespace' ':' p=packageOrClass {obj.namespace = $p.text;}
        '{'
            useStatement*
            objDeclaration
        '}'
    ;

useStatement
    :   'use' ':' p=packageOrClass {obj.use.add($p.text);} ';'
    ;

includeStatement
    :   'include' ':' s=StringLiteral {obj.include.add($s.text);} ';'
    ;

objDeclaration
    :   'declare' ':' i=Identifier {obj.name = $i.text;} 
        ( '<' p=packageOrClass {obj.inherit = $p.text;} )? 
        ( '|' p=packageOrClass {obj.resemble.add($p.text);} )* 
        '{' 
            includeStatement* 
            // ...
        '}'
    ;

packageOrClass
    :   ( Identifier ( '.' Identifier )* )
    ;

StringLiteral
    :   '"' ( '\\\\' | '\\"' | ~( '"' | '\\' ) )* '"'
    ;

Identifier
    :   ( 'a'..'z' | 'A'..'Z' | '_' ) ( 'a'..'z' | 'A'..'Z' | '_' | '0'..'9' )* 
    ;

LineComment
    :   '//' ~( '\r' | '\n' )* ( '\r'? '\n' | EOF ) {skip();} // ignoring these tokens
    ;

Spaces
    :   ( ' ' | '\t' | '\r' | '\n' ) {skip();} // ignoring these tokens
    ;

上記を というファイルに保存しFJS.g、 ANTLR をダウンロードして、次のようにレクサーとパーサーを生成します。

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

そして、それをテストするには、これを実行します:

public class ANTLRDemo {
    public static void main(String[] args) throws Exception {
        String source =
                "namespace: ie.ondevice                             \n"+
                "{                                                  \n"+
                "    use: ie.ondevice.lang.Mixin;                   \n"+
                "    use: ie.ondevice.TraitsDeclaration;            \n"+
                "                                                   \n"+
                "    declare: Example < Mixin | TraitsDeclaration   \n"+
                "    {                                              \n"+
                "        include: \"path/to/file.extension\";       \n"+
                "        // implementation here                     \n"+
                "    }                                              \n"+
                "}                                                    ";
        ANTLRStringStream in = new ANTLRStringStream(source);
        CommonTokenStream tokens = new CommonTokenStream(new FJSLexer(in));
        FJSParser parser = new FJSParser(tokens);
        parser.parse();
        System.out.println(parser.obj);
    }
} 

これにより、以下が生成されます。

name      : Example
namespace : ie.ondevice
inherit   : Mixin
resemble  : [TraitsDeclaration]
use       : [ie.ondevice.lang.Mixin, ie.ondevice.TraitsDeclaration]
include   : ["path/to/file.extension"]

FJSObjectこれで、クラスにメタ/ソース ファイルを生成/書き換えさせることができます。そのクラスから、インクルードされたファイルが実際に存在するかどうかを確認することもできます。

HTH。

于 2009-12-29T19:26:18.757 に答える