1

我正在研究用 Java 实现的 JavaScript 整理器/合成器。它有效,但必须有更好的方法来实现它,我认为 Lexer 可能是前进的方向,但我有点模糊。

我为合成器开发了一种元语法,它是 JavaScript 语言的一个子集。就典型的 JavaScript 解释器而言,合成器元语法是合法的,只是不起作用(我使用保留字的同义词作为标签,后跟合成器应该解释的代码块)。现在,我正在使用扫描仪和正则表达式来查找源文件中的元语法,然后根据合法表达式的检测进行浅层词法转换。

重写的 javascript 和我不满意的扫描器/解析器之间存在紧密耦合,因为重写的 javascript 使用了专门为此目的编写的对象支持库的功能,并且该库可能会发生变化。

我希望我可以只在 Backaus-Naur 或 EBNF 中声明元语法,将其提供给词法分析器(ANTRL?),并根据在源文件中检测到的元语法表达式,将合成器定向到某些操作,例如将所需的脚本附加到另一个脚本,声明变量,为适当参数化的库函数调用生成文本,甚至压缩脚本。

这是制作合成器的合适方法吗?我是否应该使用 Scanner/Parser/Lexer 方法来合成 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(模糊 JavaScript 对象):

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 回答
0

您可能想查看Mozilla Rhino项目——它是在 JVM 上运行 JavaScript 的完整解决方案,但解析 JavaScript 代码的代码封装得相当好,可以在没有完整执行功能的情况下使用。

于 2009-12-29T21:36:21.893 回答