diff --git a/doc/parser-rules.md b/doc/parser-rules.md index 2fa7d6b5c..73c363b99 100644 --- a/doc/parser-rules.md +++ b/doc/parser-rules.md @@ -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 ``` diff --git a/tool-testsuite/test/org/antlr/v4/test/tool/TestScopeParsing.java b/tool-testsuite/test/org/antlr/v4/test/tool/TestScopeParsing.java index 1dc39467c..71dedfee5 100644 --- a/tool-testsuite/test/org/antlr/v4/test/tool/TestScopeParsing.java +++ b/tool-testsuite/test/org/antlr/v4/test/tool/TestScopeParsing.java @@ -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[] i, int j[]", "{i=Map[] i, j=int [] j}", - "Map>[] i", "{i=Map>[] i}", + static String[] argPairs = { + "", "", + " ", "", + "int i", "i:int", + "int[] i, int j[]", "i:int[], j:int []", + "Map[] i, int j[]", "i:Map[], j:int []", + "Map>[] i", "i:Map>[]", "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 attributes = ScopeParser.parseTypedArgList(null, input, dummy).attributes; + List 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 getAllTestDescriptors() { + List 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; + } } diff --git a/tool/src/org/antlr/v4/parse/ScopeParser.java b/tool/src/org/antlr/v4/parse/ScopeParser.java index 7599c9ffc..53094c668 100644 --- a/tool/src/org/antlr/v4/parse/ScopeParser.java +++ b/tool/src/org/antlr/v4/parse/ScopeParser.java @@ -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] *

* 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; } diff --git a/tool/src/org/antlr/v4/tool/Attribute.java b/tool/src/org/antlr/v4/tool/Attribute.java index 3dc025839..fea2daa73 100644 --- a/tool/src/org/antlr/v4/tool/Attribute.java +++ b/tool/src/org/antlr/v4/tool/Attribute.java @@ -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; } -} \ No newline at end of file +} diff --git a/tool/src/org/antlr/v4/tool/AttributeDict.java b/tool/src/org/antlr/v4/tool/AttributeDict.java index e8d487f0d..906d2acf2 100644 --- a/tool/src/org/antlr/v4/tool/AttributeDict.java +++ b/tool/src/org/antlr/v4/tool/AttributeDict.java @@ -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, }