Antlr setText 没有按我预期的方式工作

发布于 2024-12-08 06:57:43 字数 2569 浏览 2 评论 0原文

我需要将标识符转换为 beanutil 字符串,以便从对象中检索项目。标识符到字符串的转换如下所示:

name                                        ==> name
attribute.name                              ==> attributes(name)[0].value
attribute.name[2]                           ==> attributes(name)[2].value
address.attribute.postalcode                ==> contactDetails.addresses[0].attributes(postalcode)[0].value
address[2].attribute.postalcode             ==> contactDetails.addresses[2].attributes(postalcode)[0].value
address[2].attribute.postalcode[3]          ==> contactDetails.addresses[2].attributes(postalcode)[3].value

现在我决定使用 antlr 来执行此操作,因为我觉得它可能与使用一组“if”语句一样快。请随意告诉我我错了。

现在,我已经使用 antlr 完成了这部分工作,但是一旦我开始执行“地址”部分,setText 部分似乎就停止为属性工作。

我是否以正确的方式执行此操作,或者是否有更好的方法使用 antlr 来获得我想要的结果?

grammar AttributeParser;

parse returns [ String result ]
:  Address EOF { $result = $Address.text; }
|  Attribute EOF { $result = $Attribute.text; }
|  Varname EOF { $result = $Varname.text; }
;

Address
: 'address' (Arraypos)* '.' Attribute { setText("contactDetails.addresses" + ($Arraypos == null ? "[0]" : $Arraypos.text ) + "." + $Attribute.text); }
; 

Attribute
:  'attribute.' Varname (Arraypos)* { setText("attributes(" + $Varname.text + ")" + ($Arraypos == null ? "[0]" : $Arraypos.text ) + ".value"); }
;

Arraypos
: '[' Number+ ']'
;


Varname  
:  ('a'..'z'|'A'..'Z')+
;

Number
: '0'..'9'+
;

Spaces
:  (' ' | '\t' | '\r' | '\n')+ { setText(" "); }
;

下面是两个单元测试,第一个返回我期望的结果,第二个则没有。

@Test
public void testSimpleAttributeWithArrayRef() throws Exception {

    String source = "attribute.name[2]";
    ANTLRStringStream in = new ANTLRStringStream(source);
    AttributeParserLexer lexer = new AttributeParserLexer(in);
    CommonTokenStream tokens = new CommonTokenStream(lexer);
    AttributeParserParser parser = new AttributeParserParser(tokens);
    String result = parser.parse();
    assertEquals("attributes(name)[2].value", result);       
}

@Test
public void testAddress() throws Exception {

    String source = "address.attribute.postalcode";
    ANTLRStringStream in = new ANTLRStringStream(source);
    AttributeParserLexer lexer = new AttributeParserLexer(in);
    CommonTokenStream tokens = new CommonTokenStream(lexer);
    AttributeParserParser parser = new AttributeParserParser(tokens);
    String result = parser.parse();
    System.out.println("Result: " + result);
    assertEquals("contactDetails.addresses[0].attributes(postalcode)[0].value", result);       
}

I have a requirement to convert an identifier into a beanutil string for retrieving an item from an object. The the identifiers to string conversions look like:

name                                        ==> name
attribute.name                              ==> attributes(name)[0].value
attribute.name[2]                           ==> attributes(name)[2].value
address.attribute.postalcode                ==> contactDetails.addresses[0].attributes(postalcode)[0].value
address[2].attribute.postalcode             ==> contactDetails.addresses[2].attributes(postalcode)[0].value
address[2].attribute.postalcode[3]          ==> contactDetails.addresses[2].attributes(postalcode)[3].value

Now I have decided to do this using antlr as I feel its probably going to be just as quick as using a set of 'if' statements. Feel free to tell me I'm wrong.

Right now, I've got this partial working using antlr, however once I start doing the 'address' ones, the setText part seems to stop working for Attribute.

Am I doing this the correct way or is there a better way of using antlr to get the result I want?

grammar AttributeParser;

parse returns [ String result ]
:  Address EOF { $result = $Address.text; }
|  Attribute EOF { $result = $Attribute.text; }
|  Varname EOF { $result = $Varname.text; }
;

Address
: 'address' (Arraypos)* '.' Attribute { setText("contactDetails.addresses" + ($Arraypos == null ? "[0]" : $Arraypos.text ) + "." + $Attribute.text); }
; 

Attribute
:  'attribute.' Varname (Arraypos)* { setText("attributes(" + $Varname.text + ")" + ($Arraypos == null ? "[0]" : $Arraypos.text ) + ".value"); }
;

Arraypos
: '[' Number+ ']'
;


Varname  
:  ('a'..'z'|'A'..'Z')+
;

Number
: '0'..'9'+
;

Spaces
:  (' ' | '\t' | '\r' | '\n')+ { setText(" "); }
;

Below are two unit tests, the first returns what I expect, the second doesn't.

@Test
public void testSimpleAttributeWithArrayRef() throws Exception {

    String source = "attribute.name[2]";
    ANTLRStringStream in = new ANTLRStringStream(source);
    AttributeParserLexer lexer = new AttributeParserLexer(in);
    CommonTokenStream tokens = new CommonTokenStream(lexer);
    AttributeParserParser parser = new AttributeParserParser(tokens);
    String result = parser.parse();
    assertEquals("attributes(name)[2].value", result);       
}

@Test
public void testAddress() throws Exception {

    String source = "address.attribute.postalcode";
    ANTLRStringStream in = new ANTLRStringStream(source);
    AttributeParserLexer lexer = new AttributeParserLexer(in);
    CommonTokenStream tokens = new CommonTokenStream(lexer);
    AttributeParserParser parser = new AttributeParserParser(tokens);
    String result = parser.parse();
    System.out.println("Result: " + result);
    assertEquals("contactDetails.addresses[0].attributes(postalcode)[0].value", result);       
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

美人如玉 2024-12-15 06:57:43

不,您不能执行 (Arraypos)*,然后将内容引用为:$Arraypos.text

我不会更改标记的内部文本,而是创建几个解析器规则并让它们返回适当的文本。

一个小演示:

grammar AttributeParser;

parse returns [String s]
  :  input EOF {$s = $input.s;}
  ;

input returns [String s]
  :  address   {$s = $address.s;}
  |  attribute {$s = $attribute.s;}
  |  Varname   {$s = $Varname.text;}
  ;

address returns [String s]
  :  Address arrayPos '.' attribute
     {$s = "contactDetails.addresses" + $arrayPos.s + "." + $attribute.s;}
  ;

attribute returns [String s]
  :  Attribute '.' Varname arrayPos 
     {$s = "attributes(" + $Varname.text + ")" + $arrayPos.s + ".value" ;}
  ;

arrayPos returns [String s]
  :  Arraypos      {$s = $Arraypos.text;}
  |  /* nothing */ {$s = "[0]";}
  ;

Attribute : 'attribute';
Address   : 'address';
Arraypos  : '[' '0'..'9'+ ']';
Varname   : ('a'..'z' | 'A'..'Z')+;

可以使用以下命令进行测试:

import org.antlr.runtime.*;

public class Main {
  public static void main(String[] args) throws Exception {
    String[][] tests = {
      {"name",                                "name"},
      {"attribute.name",                      "attributes(name)[0].value"},
      {"attribute.name[2]",                   "attributes(name)[2].value"},
      {"address.attribute.postalcode",        "contactDetails.addresses[0].attributes(postalcode)[0].value"},
      {"address[2].attribute.postalcode",     "contactDetails.addresses[2].attributes(postalcode)[0].value"},
      {"address[2].attribute.postalcode[3]",  "contactDetails.addresses[2].attributes(postalcode)[3].value"}
    };
    for(String[] test : tests) {
      String input = test[0];
      String expected = test[1];
      AttributeParserLexer lexer = new AttributeParserLexer(new ANTLRStringStream(input));
      AttributeParserParser parser = new AttributeParserParser(new CommonTokenStream(lexer));
      String output = parser.parse();
      if(!output.equals(expected)) {
        throw new RuntimeException(output + " != " + expected);
      }
      System.out.printf("in  = %s\nout = %s\n\n", input, output, expected);
    }
  }
}

要运行演示,请执行以下操作:

java -cp antlr-3.3.jar org.antlr.Tool AttributeParser.g
javac -cp antlr-3.3.jar *.java
java -cp .:antlr-3.3.jar Main

将以下内容打印到控制台:

in  = name
out = name

in  = attribute.name
out = attributes(name)[0].value

in  = attribute.name[2]
out = attributes(name)[2].value

in  = address.attribute.postalcode
out = contactDetails.addresses[0].attributes(postalcode)[0].value

in  = address[2].attribute.postalcode
out = contactDetails.addresses[2].attributes(postalcode)[0].value

in  = address[2].attribute.postalcode[3]
out = contactDetails.addresses[2].attributes(postalcode)[3].value

编辑

请注意,您还可以让解析器规则返回多个对象,如下所示:

bar
  :  foo {System.out.println($foo.text + ", " + $foo.number);}
  ;

foo returns [String text, int number]
  :  'FOO' {$text = "a"; $number = 1;}
  |  'foo' {$text = "b"; $number = 2;}
  ;

No, you can't do (Arraypos)* and then refer to the contents as this: $Arraypos.text.

I wouldn't go changing the inner text of the tokens, but create a couple of parser rules and let them return the appropriate text.

A little demo:

grammar AttributeParser;

parse returns [String s]
  :  input EOF {$s = $input.s;}
  ;

input returns [String s]
  :  address   {$s = $address.s;}
  |  attribute {$s = $attribute.s;}
  |  Varname   {$s = $Varname.text;}
  ;

address returns [String s]
  :  Address arrayPos '.' attribute
     {$s = "contactDetails.addresses" + $arrayPos.s + "." + $attribute.s;}
  ;

attribute returns [String s]
  :  Attribute '.' Varname arrayPos 
     {$s = "attributes(" + $Varname.text + ")" + $arrayPos.s + ".value" ;}
  ;

arrayPos returns [String s]
  :  Arraypos      {$s = $Arraypos.text;}
  |  /* nothing */ {$s = "[0]";}
  ;

Attribute : 'attribute';
Address   : 'address';
Arraypos  : '[' '0'..'9'+ ']';
Varname   : ('a'..'z' | 'A'..'Z')+;

which can be tested with:

import org.antlr.runtime.*;

public class Main {
  public static void main(String[] args) throws Exception {
    String[][] tests = {
      {"name",                                "name"},
      {"attribute.name",                      "attributes(name)[0].value"},
      {"attribute.name[2]",                   "attributes(name)[2].value"},
      {"address.attribute.postalcode",        "contactDetails.addresses[0].attributes(postalcode)[0].value"},
      {"address[2].attribute.postalcode",     "contactDetails.addresses[2].attributes(postalcode)[0].value"},
      {"address[2].attribute.postalcode[3]",  "contactDetails.addresses[2].attributes(postalcode)[3].value"}
    };
    for(String[] test : tests) {
      String input = test[0];
      String expected = test[1];
      AttributeParserLexer lexer = new AttributeParserLexer(new ANTLRStringStream(input));
      AttributeParserParser parser = new AttributeParserParser(new CommonTokenStream(lexer));
      String output = parser.parse();
      if(!output.equals(expected)) {
        throw new RuntimeException(output + " != " + expected);
      }
      System.out.printf("in  = %s\nout = %s\n\n", input, output, expected);
    }
  }
}

And to run the demo do:

java -cp antlr-3.3.jar org.antlr.Tool AttributeParser.g
javac -cp antlr-3.3.jar *.java
java -cp .:antlr-3.3.jar Main

which will print the following to the console:

in  = name
out = name

in  = attribute.name
out = attributes(name)[0].value

in  = attribute.name[2]
out = attributes(name)[2].value

in  = address.attribute.postalcode
out = contactDetails.addresses[0].attributes(postalcode)[0].value

in  = address[2].attribute.postalcode
out = contactDetails.addresses[2].attributes(postalcode)[0].value

in  = address[2].attribute.postalcode[3]
out = contactDetails.addresses[2].attributes(postalcode)[3].value

EDIT

Note that you can also let parser rules return more than just one object like this:

bar
  :  foo {System.out.println($foo.text + ", " + $foo.number);}
  ;

foo returns [String text, int number]
  :  'FOO' {$text = "a"; $number = 1;}
  |  'foo' {$text = "b"; $number = 2;}
  ;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文