summaryrefslogtreecommitdiff
path: root/chapter15
diff options
context:
space:
mode:
authorCliff Click <cliffc@acm.org>2024-10-01 10:36:59 -0700
committerCliff Click <cliffc@acm.org>2024-10-06 08:56:22 -0700
commitbd4c0c8d9fd33b35a0c4d98ca2dd641e3fafbfd3 (patch)
tree9f3a191348482bc6942cb1f256d112059a122776 /chapter15
parente315432bbd1ffa4e2dd562c4f4fc454ea1ba2464 (diff)
downloadSimple-bd4c0c8d9fd33b35a0c4d98ca2dd641e3fafbfd3.tar.gz
Simple-bd4c0c8d9fd33b35a0c4d98ca2dd641e3fafbfd3.zip
Fix parsing forward ref arrays
Diffstat (limited to 'chapter15')
-rw-r--r--chapter15/README.md16
-rw-r--r--chapter15/src/main/java/com/seaofnodes/simple/Parser.java65
-rw-r--r--chapter15/src/main/java/com/seaofnodes/simple/type/TypeStruct.java5
-rw-r--r--chapter15/src/test/java/com/seaofnodes/simple/Chapter15Test.java38
-rw-r--r--chapter15/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java4
5 files changed, 85 insertions, 43 deletions
diff --git a/chapter15/README.md b/chapter15/README.md
index 50a0493b..354b6c66 100644
--- a/chapter15/README.md
+++ b/chapter15/README.md
@@ -14,12 +14,12 @@ Like Java, arrays are always safety checked, and failing a runtime check
will panic the Evaluator.
- Indexing out of bounds:
-- - `ary[-1]`
-- - `ary[ary#]`
+- `ary[-1]`
+- `ary[ary#]`
- Creating an array with a bad length:
-- - `new int[-1]; // Negative length`
-- - `new int[1<<63]; // Too large`
-- - `new int[3.14]; // Not an integer`
+- `new int[-1]; // Negative length`
+- `new int[1<<63]; // Too large`
+- `new int[3.14]; // Not an integer`
## Parser
@@ -33,8 +33,10 @@ Arrays are always created with zero/null bodies, so implicitly always allow
`null` values.
We make a new array with much the same syntax as making a new object: `new
-int[99][99]`. The length is required and is any integer expression, so
-e.g. `int[arg]` is an integer array of length `arg`.
+int[99]`. The length is required and is any integer expression, so
+e.g. `int[arg]` is an integer array of length `arg`. Multi-dimensional arrays
+only make the outer dimension; the elements themselves are all null and typed
+as the next lower dimension array.
Arrays implicitly have two fields: the length and the body. The length is a
read-only field set when the array is allocated; it is referenced with the
diff --git a/chapter15/src/main/java/com/seaofnodes/simple/Parser.java b/chapter15/src/main/java/com/seaofnodes/simple/Parser.java
index b503bf15..e0077102 100644
--- a/chapter15/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter15/src/main/java/com/seaofnodes/simple/Parser.java
@@ -92,7 +92,9 @@ public class Parser {
ScopeNode _continueScope;
ScopeNode _breakScope;
- // Mapping from a type name to a Type.
+ // Mapping from a type name to a Type. The string name matches
+ // `type.str()` call. No TypeMemPtrs are in here, because Simple does not
+ // have C-style '*ptr' references.
public static HashMap<String, Type> TYPES = new HashMap<>();
public Parser(String source, TypeInteger arg) {
@@ -488,17 +490,10 @@ public class Parser {
if( tname==null ) return null;
// Convert the type name to a type.
Type t0 = TYPES.get(tname);
- Type t1 = t0==null ? TypeStruct.BOT : t0; // Null: assume a forward ref type
+ Type t1 = t0 == null ? TypeStruct.make(tname) : t0; // Null: assume a forward ref type
// Nest arrays as needed
- while( match("[]") ) {
- Type t2 = TYPES.get("[]"+tname);
- if( t2==null ) {
- TYPES.put("[]"+tname,t1 = TypeStruct.makeAry(TypeInteger.BOT,ALIAS++,t1,ALIAS++));
- START.addMemProj((TypeStruct)t1, _scope); // Insert memory edges
- } else {
- t1 = t2;
- }
- }
+ while( match("[]") )
+ t1 = typeAry(t1);
// Handle trailing '?' for nullable
Type t2 = t1 instanceof TypeStruct obj ? TypeMemPtr.make(obj,match("?")) : t1;
@@ -514,8 +509,23 @@ public class Parser {
return null; // Not a type
}
// Yes a forward ref, so declare it
- TYPES.put(tname, t0 = TypeStruct.make(tname));
- return ((TypeMemPtr)t2).makeFrom((TypeStruct)t0);
+ TYPES.put(tname,t1);
+ return t2;
+ }
+
+ // Make an array type of t
+ private TypeStruct typeAry( Type t ) {
+ assert !(t instanceof TypeMemPtr); // Arrays of references, not inlined structs
+ String tname = t.str()+"[]";
+ Type ta = TYPES.get(tname);
+ if( ta != null ) return (TypeStruct)ta;
+ // Need make an array type. If the base type is a struct, wrap it.
+ if( t instanceof TypeStruct obj )
+ t = TypeMemPtr.make(obj,true);
+ TypeStruct ts = TypeStruct.makeAry(TypeInteger.BOT,ALIAS++,t,ALIAS++);
+ TYPES.put(tname,ts);
+ START.addMemProj(ts, _scope); // Insert memory alias edges
+ return ts;
}
@@ -677,14 +687,21 @@ public class Parser {
String typeName = _lexer.matchId();
if( typeName==null ) throw error("Expected a type");
Type t = TYPES.get(typeName);
- if( match("[") )
- return require(newArray(t),"]");
- if( t instanceof TypeStruct obj ) {
- if( obj._fields==null )
- throw error("Unknown struct type '" + typeName + "'");
+ if( t instanceof TypeStruct obj && obj._fields==null )
+ throw error("Unknown struct type '" + typeName + "'");
+ if( t!=null && match("[") ) {
+ Node len = parseExpression().keep();
+ if( !(len._type instanceof TypeInteger) )
+ throw error("Cannot allocate an array with length "+len._type);
+ require("]");
+ TypeStruct ary = typeAry(t);
+ while( match("[]") )
+ ary = typeAry(ary);
+ return newArray(ary,len);
+ } else if( t instanceof TypeStruct obj ) {
return newStruct(obj,con(obj.offset(obj._fields.length)));
}
- throw error("Cannot allocate a "+t);
+ throw error("Cannot allocate a "+typeName);
}
String name = _lexer.matchId();
if( name == null) throw errorSyntax("an identifier or expression");
@@ -709,15 +726,7 @@ public class Parser {
return new ProjNode(nnn,1,obj._name).peephole();
}
- private Node newArray(Type t) {
- Node len = parseExpression().keep();
- if( !(len._type instanceof TypeInteger) )
- throw error("Cannot allocate an array with length "+len._type);
- TypeStruct ary = (TypeStruct)TYPES.get("[]"+t.str());
- if( ary == null ) {
- TYPES.put("[]"+t.str(), ary = TypeStruct.makeAry(TypeInteger.BOT,ALIAS++,t,ALIAS++));
- START.addMemProj(ary, _scope); // Insert memory edges
- }
+ private Node newArray(TypeStruct ary, Node len) {
int base = ary.aryBase ();
int scale= ary.aryScale();
Node size = new AddNode(con(base),new ShlNode(len,con(scale)).peephole()).peephole();
diff --git a/chapter15/src/main/java/com/seaofnodes/simple/type/TypeStruct.java b/chapter15/src/main/java/com/seaofnodes/simple/type/TypeStruct.java
index 9e38e025..b3a8f867 100644
--- a/chapter15/src/main/java/com/seaofnodes/simple/type/TypeStruct.java
+++ b/chapter15/src/main/java/com/seaofnodes/simple/type/TypeStruct.java
@@ -40,8 +40,9 @@ public class TypeStruct extends Type {
// Array
public static TypeStruct makeAry(TypeInteger len, int lenAlias, Type body, int bodyAlias) {
- String name = "[]" + body.str();
- return make("[]" + body.str(),
+ Type t = body;
+ if( t instanceof TypeMemPtr tmp ) { assert tmp._nil; t = tmp._obj; }
+ return make(t.str() + "[]",
Field.make("#" , lenAlias,len ),
Field.make("[]",bodyAlias,body));
}
diff --git a/chapter15/src/test/java/com/seaofnodes/simple/Chapter15Test.java b/chapter15/src/test/java/com/seaofnodes/simple/Chapter15Test.java
index 6ba6eed4..62e865aa 100644
--- a/chapter15/src/test/java/com/seaofnodes/simple/Chapter15Test.java
+++ b/chapter15/src/test/java/com/seaofnodes/simple/Chapter15Test.java
@@ -61,6 +61,36 @@ return a[0];
}
@Test
+ public void testBasic4() {
+ Parser parser = new Parser(
+"""
+struct A { int i; }
+A[] a = new A[2];
+return a;
+""");
+ StopNode stop = parser.parse(false).iterate(false);
+ assertEquals("return A[];", stop.toString());
+ assertEquals("Obj<A[]> {\n #=2\n []=null\n}", Evaluator.evaluate(stop, 0).toString());
+ }
+
+ @Test
+ public void testTree() {
+ Parser parser = new Parser(
+"""
+// Can we define a forward-reference array?
+struct Tree { Tree[] _kids; }
+Tree root = new Tree;
+root._kids = new Tree[2];
+root._kids[0] = new Tree;
+return root;
+""");
+ StopNode stop = parser.parse(false).iterate(false);
+ assertEquals("return Tree;", stop.toString());
+ assertEquals("Obj<Tree> {\n _kids=Obj<Tree[]> {\n #=2\n []=Obj<Tree> {\n _kids=null\n}\n}\n}", Evaluator.evaluate(stop, 0).toString());
+ }
+
+
+ @Test
public void testRollingSum() {
Parser parser = new Parser(
"""
@@ -123,9 +153,9 @@ while( j < nprimes ) {
return rez;
""");
StopNode stop = parser.parse(false).iterate(true);
- assertEquals("return []int;", stop.toString());
+ assertEquals("return int[];", stop.toString());
Evaluator.Obj obj = (Evaluator.Obj)Evaluator.evaluate(stop, 20);
- assertEquals("[]int {\n # :int;\n [] :int;\n}",obj.struct().toString());
+ assertEquals("int[] {\n # :int;\n [] :int;\n}",obj.struct().toString());
long nprimes = (Long)obj.fields()[0];
long[] primes = new long[]{2,3,5,7,11,13,17,19};
for( int i=0; i<nprimes; i++ )
@@ -140,7 +170,7 @@ return rez;
return new flt;
""");
try { parser.parse(false).iterate(false); fail(); }
- catch( Exception e ) { assertEquals("Cannot allocate a FltBot",e.getMessage()); }
+ catch( Exception e ) { assertEquals("Cannot allocate a flt",e.getMessage()); }
}
@Test
@@ -150,7 +180,7 @@ return new flt;
int is = new int[2];
""");
try { parser.parse(false).iterate(false); fail(); }
- catch( Exception e ) { assertEquals("Type *[]int is not of declared type int",e.getMessage()); }
+ catch( Exception e ) { assertEquals("Type *int[] is not of declared type int",e.getMessage()); }
}
@Test
diff --git a/chapter15/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java b/chapter15/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java
index 324dd42d..7cd854bf 100644
--- a/chapter15/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java
+++ b/chapter15/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java
@@ -109,8 +109,8 @@ public class ScriptGenerator {
static {
new Parser("", TypeInteger.BOT);
- for(var e : Parser.TYPES.entrySet() ) {
- if (e.getValue() instanceof TypeInteger) {
+ for( var e : Parser.TYPES.entrySet() ) {
+ if( e.getValue() instanceof TypeInteger ) {
INTTYPES.add(new TypeInt(e.getKey()));
}
}