support x:T notation in rule arguments, update documentation, improve [...] scope parsing testing

This commit is contained in:
parrt 2016-11-16 11:54:23 -08:00
parent 03fa75efba
commit 5aae2edb24
5 changed files with 93 additions and 32 deletions

View File

@ -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;} ;
```
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:
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).
* 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
```

View File

@ -30,46 +30,95 @@
package org.antlr.v4.test.tool;
import org.antlr.v4.misc.Utils;
import org.antlr.v4.parse.ScopeParser;
import org.antlr.v4.test.runtime.java.BaseJavaTest;
import org.antlr.v4.tool.Attribute;
import org.antlr.v4.tool.Grammar;
import org.junit.Before;
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;
@RunWith(Parameterized.class)
public class TestScopeParsing extends BaseJavaTest {
String[] argPairs = {
"", "{}",
" ", "{}",
"int i", "{i=int i}",
"int[] i, int j[]", "{i=int[] i, j=int [] j}",
"Map<A,B>[] i, int j[]", "{i=Map<A,B>[] i, j=int [] j}",
"Map<A,List<B>>[] i", "{i=Map<A,List<B>>[] i}",
static String[] argPairs = {
"", "",
" ", "",
"int i", "i:int",
"int[] i, int j[]", "i:int[], j:int []",
"Map<A,B>[] i, int j[]", "i:Map<A,B>[], j:int []",
"Map<A,List<B>>[] i", "i:Map<A,List<B>>[]",
"int i = 34+a[3], int j[] = new int[34]",
"{i=int i= 34+a[3], j=int [] j= new int[34]}",
"char *foo32[3] = {1,2,3}", "{3=char *foo32[] 3= {1,2,3}}",
"String[] headers", "{headers=String[] headers}",
"i:int=34+a[3], j:int []=new int[34]",
"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[]",
// python/ruby style
"i", "{i=null i}",
"i,j", "{i=null i, j=null j}",
"i,j, k", "{i=null i, j=null j, k=null k}",
"i", "i",
"i,j", "i, j",
"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
@Override
public void testSetUp() throws Exception {
super.testSetUp();
}
@Test public void testArgs() throws Exception {
for (int i = 0; i < argPairs.length; i+=2) {
String input = argPairs[i];
String expected = argPairs[i+1];
Grammar dummy = new Grammar("grammar T; a:'a';");
String actual = ScopeParser.parseTypedArgList(null, input, dummy).attributes.toString();
assertEquals(expected, actual);
}
@Test
public void testArgs() throws Exception {
Grammar dummy = new Grammar("grammar T; a:'a';");
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;
}
}

View File

@ -32,7 +32,6 @@ package org.antlr.v4.parse;
import org.antlr.runtime.BaseRecognizer;
import org.antlr.runtime.CommonToken;
import org.antlr.v4.runtime.misc.IntegerList;
import org.antlr.v4.runtime.misc.Pair;
import org.antlr.v4.tool.Attribute;
import org.antlr.v4.tool.AttributeDict;
@ -49,7 +48,8 @@ import java.util.List;
* rule[arg1, arg2, ..., argN] returns [ret1, ..., retN]
* <p>
* 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 {
/**
@ -97,7 +97,7 @@ public class ScopeParser {
int equalsIndex = decl.a.indexOf('=');
if (equalsIndex > 0) {
// 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;
}

View File

@ -32,11 +32,11 @@ package org.antlr.v4.tool;
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...
*/
public class Attribute {
/** The entire declaration such as "String foo;" */
/** The entire declaration such as "String foo" or "x:int" */
public String decl;
/** The type; might be empty such as for Python which has no static typing */
@ -66,8 +66,11 @@ public class Attribute {
@Override
public String toString() {
if ( initValue!=null ) {
return type+" "+name+"="+initValue;
return name+":"+type+"="+initValue;
}
return type+" "+name;
if ( type!=null ) {
return name+":"+type;
}
return name;
}
}
}

View File

@ -65,7 +65,7 @@ public class AttributeDict {
predefinedTokenDict.add(new Attribute("int"));
}
public static enum DictType {
public enum DictType {
ARG, RET, LOCAL, TOKEN,
PREDEFINED_RULE, PREDEFINED_LEXER_RULE,
}