support x:T notation in rule arguments, update documentation, improve [...] scope parsing testing
This commit is contained in:
parent
03fa75efba
commit
5aae2edb24
|
@ -380,8 +380,17 @@ The attributes defined within those [...] can be used like any other variable. H
|
||||||
add[int x] returns [int result] : '+=' INT {$result = $x + $INT.int;} ;
|
add[int x] returns [int result] : '+=' INT {$result = $x + $INT.int;} ;
|
||||||
```
|
```
|
||||||
|
|
||||||
As with the grammar level, you can specify rule-level named actions. For rules, the valid names are init and after. As the names imply, parsers execute init actions immediately before trying to match the associated rule and execute after actions immediately after matching the rule. ANTLR after actions do not execute as part of the finally code block of the generated rule function. Use the ANTLR finally action to place code in the generated rule function finally code block.
|
The args, locals, and return `[...]` are generally in the target language but with some constraints. The `[...]` string is a comma-separated list of declarations either with prefix or postfix type notation or no-type notation. The elements can have initializer such as `[int x = 32, float y]` but don't go too crazy as we are parsing this generic text manually in [ScopeParser](https://github.com/antlr/antlr4/blob/master/tool/src/org/antlr/v4/parse/ScopeParser.java).
|
||||||
The actions come after any argument, return value, or local attribute definition actions. The row rule preamble from Section 10.2, Accessing Token and Rule Attributes illustrates the syntax nicely:
|
|
||||||
|
* Java, CSharp, C++ use `int x` notation but C++ must use a slightly altered notation for array references, `int[] x`, to fit in the *type* *id* syntax.
|
||||||
|
* Go and Swift give the type after the variable name, but Swift requires a `:` in between. Go `i int`, Swift `i:int`. For Go target, you must either use `int i` or `i:int`.
|
||||||
|
* Python and JavaScript don't specify static types so actions are just identifier lists such as `[i,j]`.
|
||||||
|
|
||||||
|
Technically any target could use either notation. For examples, see [TestScopeParsing](https://github.com/antlr/antlr4/blob/master/tool-testsuite/test/org/antlr/v4/test/tool/TestScopeParsing.java).
|
||||||
|
|
||||||
|
As with the grammar level, you can specify rule-level named actions. For rules, the valid names are `init` and `after`. As the names imply, parsers execute init actions immediately before trying to match the associated rule and execute after actions immediately after matching the rule. ANTLR after actions do not execute as part of the finally code block of the generated rule function. Use the ANTLR finally action to place code in the generated rule function finally code block.
|
||||||
|
|
||||||
|
The actions come after any argument, return value, or local attribute definition actions. The `row` rule preamble from Section 10.2, Accessing Token and Rule Attributes illustrates the syntax nicely:
|
||||||
actions/CSV.g4
|
actions/CSV.g4
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
|
@ -30,46 +30,95 @@
|
||||||
|
|
||||||
package org.antlr.v4.test.tool;
|
package org.antlr.v4.test.tool;
|
||||||
|
|
||||||
|
import org.antlr.v4.misc.Utils;
|
||||||
import org.antlr.v4.parse.ScopeParser;
|
import org.antlr.v4.parse.ScopeParser;
|
||||||
import org.antlr.v4.test.runtime.java.BaseJavaTest;
|
import org.antlr.v4.test.runtime.java.BaseJavaTest;
|
||||||
|
import org.antlr.v4.tool.Attribute;
|
||||||
import org.antlr.v4.tool.Grammar;
|
import org.antlr.v4.tool.Grammar;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.junit.runners.Parameterized;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
@RunWith(Parameterized.class)
|
||||||
public class TestScopeParsing extends BaseJavaTest {
|
public class TestScopeParsing extends BaseJavaTest {
|
||||||
String[] argPairs = {
|
static String[] argPairs = {
|
||||||
"", "{}",
|
"", "",
|
||||||
" ", "{}",
|
" ", "",
|
||||||
"int i", "{i=int i}",
|
"int i", "i:int",
|
||||||
"int[] i, int j[]", "{i=int[] i, j=int [] j}",
|
"int[] i, int j[]", "i:int[], j:int []",
|
||||||
"Map<A,B>[] i, int j[]", "{i=Map<A,B>[] i, j=int [] j}",
|
"Map<A,B>[] i, int j[]", "i:Map<A,B>[], j:int []",
|
||||||
"Map<A,List<B>>[] i", "{i=Map<A,List<B>>[] i}",
|
"Map<A,List<B>>[] i", "i:Map<A,List<B>>[]",
|
||||||
"int i = 34+a[3], int j[] = new int[34]",
|
"int i = 34+a[3], int j[] = new int[34]",
|
||||||
"{i=int i= 34+a[3], j=int [] j= new int[34]}",
|
"i:int=34+a[3], j:int []=new int[34]",
|
||||||
"char *foo32[3] = {1,2,3}", "{3=char *foo32[] 3= {1,2,3}}",
|
"char *[3] foo = {1,2,3}", "foo:char *[3]={1,2,3}", // not valid C really, C is "type name" however so this is cool (this was broken in 4.5 anyway)
|
||||||
"String[] headers", "{headers=String[] headers}",
|
"String[] headers", "headers:String[]",
|
||||||
|
|
||||||
// python/ruby style
|
// python/ruby style
|
||||||
"i", "{i=null i}",
|
"i", "i",
|
||||||
"i,j", "{i=null i, j=null j}",
|
"i,j", "i, j",
|
||||||
"i,j, k", "{i=null i, j=null j, k=null k}",
|
"i\t,j, k", "i, j, k",
|
||||||
|
|
||||||
|
// swift style
|
||||||
|
"x: int", "x:int",
|
||||||
|
"x :int", "x:int",
|
||||||
|
"x:int", "x:int",
|
||||||
|
"x:int=3", "x:int=3",
|
||||||
|
"r:Rectangle=Rectangle(fromLength: 6, fromBreadth: 12)", "r:Rectangle=Rectangle(fromLength: 6, fromBreadth: 12)",
|
||||||
|
"p:pointer to int", "p:pointer to int",
|
||||||
|
"a: array[3] of int", "a:array[3] of int",
|
||||||
|
"a \t:\tfunc(array[3] of int)", "a:func(array[3] of int)",
|
||||||
|
"x:int, y:float", "x:int, y:float",
|
||||||
|
"x:T?, f:func(array[3] of int), y:int", "x:T?, f:func(array[3] of int), y:int",
|
||||||
|
|
||||||
|
// go is postfix type notation like "x int" but must use either "int x" or "x:int" in [...] actions
|
||||||
|
"float64 x = 3", "x:float64=3",
|
||||||
|
"map[string]int x", "x:map[string]int",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
String input;
|
||||||
|
String output;
|
||||||
|
|
||||||
|
public TestScopeParsing(String input, String output) {
|
||||||
|
this.input = input;
|
||||||
|
this.output = output;
|
||||||
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
@Override
|
@Override
|
||||||
public void testSetUp() throws Exception {
|
public void testSetUp() throws Exception {
|
||||||
super.testSetUp();
|
super.testSetUp();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test public void testArgs() throws Exception {
|
@Test
|
||||||
for (int i = 0; i < argPairs.length; i+=2) {
|
public void testArgs() throws Exception {
|
||||||
String input = argPairs[i];
|
|
||||||
String expected = argPairs[i+1];
|
|
||||||
Grammar dummy = new Grammar("grammar T; a:'a';");
|
Grammar dummy = new Grammar("grammar T; a:'a';");
|
||||||
String actual = ScopeParser.parseTypedArgList(null, input, dummy).attributes.toString();
|
|
||||||
assertEquals(expected, actual);
|
LinkedHashMap<String, Attribute> attributes = ScopeParser.parseTypedArgList(null, input, dummy).attributes;
|
||||||
}
|
List<String> out = new ArrayList<>();
|
||||||
|
for (String arg : attributes.keySet()) {
|
||||||
|
Attribute attr = attributes.get(arg);
|
||||||
|
out.add(attr.toString());
|
||||||
|
}
|
||||||
|
String actual = Utils.join(out.toArray(), ", ");
|
||||||
|
assertEquals(output, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Parameterized.Parameters(name="{0}")
|
||||||
|
public static Collection<Object[]> getAllTestDescriptors() {
|
||||||
|
List<Object[]> tests = new ArrayList<>();
|
||||||
|
for (int i = 0; i < argPairs.length; i+=2) {
|
||||||
|
String arg = argPairs[i];
|
||||||
|
String output = argPairs[i+1];
|
||||||
|
tests.add(new Object[]{arg,output});
|
||||||
|
}
|
||||||
|
return tests;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,6 @@ package org.antlr.v4.parse;
|
||||||
|
|
||||||
import org.antlr.runtime.BaseRecognizer;
|
import org.antlr.runtime.BaseRecognizer;
|
||||||
import org.antlr.runtime.CommonToken;
|
import org.antlr.runtime.CommonToken;
|
||||||
import org.antlr.v4.runtime.misc.IntegerList;
|
|
||||||
import org.antlr.v4.runtime.misc.Pair;
|
import org.antlr.v4.runtime.misc.Pair;
|
||||||
import org.antlr.v4.tool.Attribute;
|
import org.antlr.v4.tool.Attribute;
|
||||||
import org.antlr.v4.tool.AttributeDict;
|
import org.antlr.v4.tool.AttributeDict;
|
||||||
|
@ -49,7 +48,8 @@ import java.util.List;
|
||||||
* rule[arg1, arg2, ..., argN] returns [ret1, ..., retN]
|
* rule[arg1, arg2, ..., argN] returns [ret1, ..., retN]
|
||||||
* <p>
|
* <p>
|
||||||
* text is target language dependent. Java/C#/C/C++ would
|
* text is target language dependent. Java/C#/C/C++ would
|
||||||
* use "int i" but ruby/python would use "i".
|
* use "int i" but ruby/python would use "i". Languages with
|
||||||
|
* postfix types like Go, Swift use "x : T" notation or "T x".
|
||||||
*/
|
*/
|
||||||
public class ScopeParser {
|
public class ScopeParser {
|
||||||
/**
|
/**
|
||||||
|
@ -97,7 +97,7 @@ public class ScopeParser {
|
||||||
int equalsIndex = decl.a.indexOf('=');
|
int equalsIndex = decl.a.indexOf('=');
|
||||||
if (equalsIndex > 0) {
|
if (equalsIndex > 0) {
|
||||||
// everything after the '=' is the init value
|
// everything after the '=' is the init value
|
||||||
attr.initValue = decl.a.substring(equalsIndex + 1, decl.a.length());
|
attr.initValue = decl.a.substring(equalsIndex + 1, decl.a.length()).trim();
|
||||||
rightEdgeOfDeclarator = equalsIndex - 1;
|
rightEdgeOfDeclarator = equalsIndex - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,11 +32,11 @@ package org.antlr.v4.tool;
|
||||||
|
|
||||||
import org.antlr.runtime.Token;
|
import org.antlr.runtime.Token;
|
||||||
|
|
||||||
/** Track the names of attributes define in arg lists, return values,
|
/** Track the names of attributes defined in arg lists, return values,
|
||||||
* scope blocks etc...
|
* scope blocks etc...
|
||||||
*/
|
*/
|
||||||
public class Attribute {
|
public class Attribute {
|
||||||
/** The entire declaration such as "String foo;" */
|
/** The entire declaration such as "String foo" or "x:int" */
|
||||||
public String decl;
|
public String decl;
|
||||||
|
|
||||||
/** The type; might be empty such as for Python which has no static typing */
|
/** The type; might be empty such as for Python which has no static typing */
|
||||||
|
@ -66,8 +66,11 @@ public class Attribute {
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
if ( initValue!=null ) {
|
if ( initValue!=null ) {
|
||||||
return type+" "+name+"="+initValue;
|
return name+":"+type+"="+initValue;
|
||||||
}
|
}
|
||||||
return type+" "+name;
|
if ( type!=null ) {
|
||||||
|
return name+":"+type;
|
||||||
|
}
|
||||||
|
return name;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -65,7 +65,7 @@ public class AttributeDict {
|
||||||
predefinedTokenDict.add(new Attribute("int"));
|
predefinedTokenDict.add(new Attribute("int"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static enum DictType {
|
public enum DictType {
|
||||||
ARG, RET, LOCAL, TOKEN,
|
ARG, RET, LOCAL, TOKEN,
|
||||||
PREDEFINED_RULE, PREDEFINED_LEXER_RULE,
|
PREDEFINED_RULE, PREDEFINED_LEXER_RULE,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue