2

作为 ANTLR 的新手,我试图弄清楚字符串模板是如何工作的。我想根据一个非常简单的输入文件生成一段 Java 代码。由于其灵活的概念,我想使用(字符串)模板。在 Java 中,通常必须生成成员声明,​​在其他地方初始化它们,甚至在其他地方使用它们。标识符名称应匹配并因此重复。这意味着这里和那里需要很少的模板实例化。当然可以做到,但我似乎无法找出方法,也许我错过了一些重要的“线索”?

我写了一个测试程序来研究这个概念。它需要一个简单的输入文件:

red = #FF0000
green = #00FF00
blue = #0000FF

并且应该产生类似于以下输出的内容:

class MyColors {
  // Class members
  public java.awt.Color red;
  public java.awt.Color green;
  public java.awt.Color blue;

  // Constructor
  /* Question: How to access the right initializer value here?!? The values are not accessible at this level of the grammar*/
  public MyColors() {
    red = java.awt.Color.getColor("#FF0000");
    green = java.awt.Color.getColor("#00FF00");
    blue = java.awt.Color.getColor("#0000FF");
  }
};

...根据输入填写构造函数中的变量和初始化程序的名称。

我定义的语法如下:

grammar Test;

options {
    output=template;
}

colors: (a+=def)+ -> colorClassDef(name={$a});

def: ident '=' name -> colorDef(id={$ident.text}, name={$name.text});

ident: ID;

name: ID;

ID: ('a'..'z'|'A'..'Z'|'#'|'0'..'9')+;
WS: (' '|'\t'|'\r'|'\n')+ { skip(); };

模板定义如下:

group Test;

colorClassDef(name, id) ::= <<
class MyColors {
// Class members
<name:{ v | public java.awt.Color <v>;
}>
// Constructor
/* Question: How to access the initializer value here?!? */
public MyColors() {
<name:{ v | <v> = java.awt.Color.getColor("<id>");
}>
}
};
>>

/* How to return both id and name here seperately, as ID should go into the declaration and name should to into the init? */
colorDef(id, name) ::= <<
<id>
>>

谁能建议我如何让 <id> 和 <name> 脱离规则“def”以便将它们包含在生成代码的正确部分中?

我发现了多个关于多个返回值的问题,例如Returning multiple values in ANTLR ruleantlr2 return multiple values,但没有一个包含字符串模板。我什至买了“这本书”并通过 java 字节码生成器按我的方式工作,但在那里没有找到我的答案。所有示例似乎都为一位输入生成一位输出。(不过没有遗憾,这本书非常适合睡前阅读;-)

谁能指出我缺少什么线索?解决此问题的最合适方法是什么?一些示例代码和指向文档的指针将不胜感激。

谢谢,

马丁

4

1 回答 1

1

谁能建议我如何获取和退出规则'def'以便将它们包含在生成代码的正确部分中?

这是一种直接的以 Java 为中心的方法来获得您想要的东西。它不像我想要的那样优雅(我认为还有改进的空间),但我认为它可以轻松解决问题。我重命名了一些东西,但我认为我保持了你方法的精神。

首先,模板。请注意,模板colorClassDef需要由语法确定的所有信息:每个 id、每个名称以及每个 id 与其对应名称之间的关联。这是从模板访问所有内容的方法之一:

group Colors;

colorClassDef(ids, colors) ::= <<
class MyColors {
// Class members
<ids:{ id | public java.awt.Color <id>;
}>

// Constructor
public MyColors() {
<ids:{ id | <id> = java.awt.Color.getColor("<colors.(id)>");
}>
}
};
>>

在这里,我使用参数ids来存储所有传入 id 的列表,并使用参数colors来存储将 id(键)与名称(值)相关联的映射。对于构造函数部分,ST 使用colors“间接属性查找”语法访问 id 的名称:<colors.(id)>. 由于colors是映射,id用作映射的键​​并将值写入模板。

模板colorClassDef处理一切,所以我删除了 template colorDef

第二,语法。它需要提供 ids 和 color map。这是这样做的一种方法:

grammar Colors;

options {
    output=template;
}

colors 
@init {
        java.util.LinkedList<String> ids = new java.util.LinkedList<String>(); 
        java.util.HashMap<String, String> colors = new java.util.HashMap<String, String>();
    }
    : (ident '=' name 
            {ids.add($ident.text); colors.put($ident.text, $name.text);}
      )+ EOF 
        -> colorClassDef(ids={ids}, colors={colors})
    ;

ident: ID;

name: ID;

ID: ('a'..'z'|'A'..'Z'|'#'|'0'..'9')+;
WS: (' '|'\t'|'\r'|'\n')+ { skip(); };

(为了保持语法相对简单,我将规则合并colorsdef. colors

每个都ident被添加到列表中ids,每个都name被添加到映射colors中作为对应ident键的值。然后他们去模板。

这是一个测试类来测试作品:

public class ColorsTest {

    public static void main(String[] args) throws Exception {

        final String code = "red = #FF0000\ngreen = #00FF00\nblue = #0000FF"; 

        process(code, "Colors.stg");

    }

    private static void process(final String code, String templateResourceName)
            throws IOException, RecognitionException, Exception {
        CharStream input = new ANTLRStringStream(code);
        ColorsLexer lexer = new ColorsLexer(input);
        CommonTokenStream tokens = new CommonTokenStream(lexer);

        ColorsParser parser = new ColorsParser(tokens);

        InputStream stream = ColorsTest.class.getResourceAsStream(templateResourceName);
        Reader reader = new InputStreamReader(stream);
        parser.setTemplateLib(new StringTemplateGroup(reader));
        reader.close();
        stream.close();

        ColorsParser.colors_return result = parser.colors();

        if (parser.getNumberOfSyntaxErrors() > 0){
            throw new Exception("Syntax Errors encountered!");
        }

        System.out.println(result.toString());
    }
}

这是一个基于您问题中的输入的测试用例。

输入

red = #FF0000
green = #00FF00
blue = #0000FF

输出

class MyColors {
// Class members
public java.awt.Color red;
public java.awt.Color green;
public java.awt.Color blue;


// Constructor
public MyColors() {
red = java.awt.Color.getColor("#FF0000");
green = java.awt.Color.getColor("#00FF00");
blue = java.awt.Color.getColor("#0000FF");

}
};
于 2012-12-27T22:00:52.117 回答