使用Scanner / Parser / Lexer进行脚本整理

时间:2009-12-29 18:52:53

标签: java javascript parsing lexer

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

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

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

我希望我能在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语句,如果命名空间未映射到有效的文件位置,则会失败,或以其他方式预先添加对象声明所在的脚本,在排序之前预处理元语法。

根据我提到的对象支持库表示的重写规则会导致可能的文件看起来像这样(我已经开发了许多表达对象的方法):< / p>

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);
 });

也许我正在构建我的要求,但我真正想要的是一个事件驱动的整理者 - 听众可以松散地与指令检测结合。

2 个答案:

答案 0 :(得分:4)

是的,使用解析器生成器(如ANTLR)是IMO的方法。如果你提供一个更具体的例子,说明你要解析的是什么,也许我(或其他人)可以帮助你。

Scott Stanchfield从一开始就为video tutorials创建了几个好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.gdownload ANTLR的文件中,然后让它生成词法分析器&amp;解析器是这样的:

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。

答案 1 :(得分:0)

您可能希望查看Mozilla Rhino项目 - 它是在JVM上运行JavaScript的完整解决方案,但解析JavaScript代码的代码已经很好地封装,可以在没有完整执行功能的情况下使用。