summaryrefslogtreecommitdiff
path: root/src/test
diff options
context:
space:
mode:
authorCliff Click <cliffc@acm.org>2024-07-17 18:11:56 +0100
committerCliff Click <cliffc@acm.org>2026-10-08 16:52:27 -0700
commitc54580d2b2685829dab0976ed8c2bcae59322f69 (patch)
tree2864b20807bcc8b0d8e7a9058ec75ef3742aa577 /src/test
parent1659b2591654acd3679c2779fd62281e496fdaf6 (diff)
downloadSimple-c54580d2b2685829dab0976ed8c2bcae59322f69.tar.gz
Simple-c54580d2b2685829dab0976ed8c2bcae59322f69.zip
Chapter 12: References
Co-authored-by: Hsiang-Ying Fu <66062782+fuhsnn@users.noreply.github.com> Co-authored-by: Dibyendu Majumdar <mobile@majumdar.org.uk> Co-authored-by: Thalia Archibald <thalia@archibald.dev> Co-authored-by: tannal <tannal2409@gmail.com> Co-authored-by: hadha <helsgithub15@gmail.com> Co-authored-by: fuhsnn <66062782+fuhsnn@users.noreply.github.com> Co-authored-by: XmiliaH <45106915+XmiliaH@users.noreply.github.com> Co-authored-by: Tamás Hadházy <85063808+Hels15@users.noreply.github.com> Co-authored-by: Symious <14933944+symious@users.noreply.github.com> Co-authored-by: RobertObkircher <robert.obkircher@gmail.com> Co-authored-by: Robert Obkircher <robert.obkircher@gmail.com> Co-authored-by: Jun Kawasaki <jun784@gmail.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'src/test')
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter05Test.java6
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter06Test.java6
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter08Test.java6
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter09Test.java10
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter10Test.java17
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter12Test.java200
-rw-r--r--src/test/java/com/seaofnodes/simple/TypeTest.java4
-rw-r--r--src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java149
8 files changed, 325 insertions, 73 deletions
diff --git a/src/test/java/com/seaofnodes/simple/Chapter05Test.java b/src/test/java/com/seaofnodes/simple/Chapter05Test.java
index cf7bd6cf..97cd9940 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter05Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter05Test.java
@@ -37,7 +37,7 @@ if (arg == 1) {
}
return c;""", TypeInteger.BOT);
StopNode ret = parser.parse().iterate();
- assertEquals("return Phi(Region17,4,3);", ret.toString());
+ assertEquals("return Phi(Region18,4,3);", ret.toString());
}
@Test
@@ -116,7 +116,7 @@ if( arg==0 )
return arg+a+b;
""", TypeInteger.BOT);
StopNode stop = parser.parse();
- assertEquals("return ((arg+Phi(Region14,1,0))+Phi(Region29,2,0));", stop.toString());
+ assertEquals("return ((arg+Phi(Region16,1,0))+Phi(Region32,2,0));", stop.toString());
}
@Test
@@ -130,7 +130,7 @@ if( arg==1 )
}
return a;""");
StopNode ret = parser.parse().iterate();
- assertEquals("return (arg==Phi(Region17,3,2));", ret.toString());
+ assertEquals("return (arg==Phi(Region18,3,2));", ret.toString());
}
@Test
diff --git a/src/test/java/com/seaofnodes/simple/Chapter06Test.java b/src/test/java/com/seaofnodes/simple/Chapter06Test.java
index 920e26e3..9af11b68 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter06Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter06Test.java
@@ -30,7 +30,7 @@ if (arg)
return (arg < a) < 3;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return ((arg<Phi(Region13,2,1))<3);", stop.toString());
+ assertEquals("return ((arg<Phi(Region15,2,1))<3);", stop.toString());
}
@Test
@@ -200,7 +200,7 @@ if( arg ) {
return a+b;
""");
StopNode ret = parser.parse().iterate();
- assertEquals("return Phi(Region25,4,1);", ret.toString());
+ assertEquals("return Phi(Region27,4,1);", ret.toString());
}
@@ -251,7 +251,7 @@ if( arg ) {
return a+b+c;
""");
StopNode ret = parser.parse().iterate();
- assertEquals("return (Phi(Region37,Phi(Region24,2,3),0)+Phi(Region,3,1));", ret.toString());
+ assertEquals("return (Phi(Region39,Phi(Region24,2,3),0)+Phi(Region,3,1));", ret.toString());
}
diff --git a/src/test/java/com/seaofnodes/simple/Chapter08Test.java b/src/test/java/com/seaofnodes/simple/Chapter08Test.java
index 4fa08bd9..02c3a301 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter08Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter08Test.java
@@ -24,7 +24,7 @@ while(arg < 10) {
return arg;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Region37,Phi(Region26,Phi(Loop7,arg,(Phi_arg+1)),Add),Add);", stop.toString());
+ assertEquals("return Phi(Region38,Phi(Region26,Phi(Loop7,arg,(Phi_arg+1)),Add),Add);", stop.toString());
assertTrue(stop.ret().ctrl() instanceof RegionNode);
Assert.assertEquals(5L, Evaluator.evaluate(stop, 1));
Assert.assertEquals(10L, Evaluator.evaluate(stop, 6));
@@ -47,7 +47,7 @@ while(arg < 10) {
return a;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Loop8,1,Phi(Region43,Phi_a,(Phi_a+1)));", stop.toString());
+ assertEquals("return Phi(Loop8,1,Phi(Region45,Phi_a,(Phi_a+1)));", stop.toString());
assertTrue(stop.ret().ctrl() instanceof ProjNode);
}
@@ -66,7 +66,7 @@ while(arg < 10) {
return arg;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Region35,Phi(Loop7,arg,(Phi_arg+1)),Add);", stop.toString());
+ assertEquals("return Phi(Region36,Phi(Loop7,arg,(Phi_arg+1)),Add);", stop.toString());
assertTrue(stop.ret().ctrl() instanceof RegionNode);
}
diff --git a/src/test/java/com/seaofnodes/simple/Chapter09Test.java b/src/test/java/com/seaofnodes/simple/Chapter09Test.java
index 1b1920e8..b1fdc459 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter09Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter09Test.java
@@ -134,14 +134,14 @@ while(v1+arg) {
}
""");
StopNode stop = parser.parse().iterate();
- assertEquals("Stop[ ]", stop.toString());
+ assertEquals("return 0;", stop.toString());
}
@Test
public void testWhile0() {
Parser parser = new Parser("while(0) continue; if(0) arg=0;");
StopNode stop = parser.parse().iterate();
- assertEquals("Stop[ ]", stop.toString());
+ assertEquals("return 0;", stop.toString());
}
@Test
@@ -153,7 +153,7 @@ if(0) while(0) {
}
""");
StopNode stop = parser.parse().iterate();
- assertEquals("Stop[ ]", stop.toString());
+ assertEquals("return 0;", stop.toString());
}
@@ -215,7 +215,7 @@ return -0+0+0;
public void testFuzz3() {
Parser parser = new Parser("int v0=0; while(0==69) while(v0) return 0;");
StopNode stop = parser.parse().iterate();
- assertEquals("Stop[ ]", stop.toString());
+ assertEquals("return 0;", stop.toString());
}
@Test
@@ -260,7 +260,7 @@ while(0==1) while(v0)
v0=1+v0;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("Stop[ ]", stop.toString());
+ assertEquals("return 0;", stop.toString());
}
@Test
diff --git a/src/test/java/com/seaofnodes/simple/Chapter10Test.java b/src/test/java/com/seaofnodes/simple/Chapter10Test.java
index 9a5e4677..e5d9b882 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter10Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter10Test.java
@@ -221,7 +221,7 @@ if( bar ) bar.a = 1;
return bar;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Region16,null,new Bar);", stop.toString());
+ assertEquals("return Phi(Region31,(*void)Phi(Region18,null,new Bar),null);", stop.toString());
}
@Test
@@ -237,7 +237,7 @@ else bar.a = 1;
return rez;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Region34,4,3);", stop.toString());
+ assertEquals("return Phi(Region37,4,3);", stop.toString());
}
@Test
@@ -290,8 +290,7 @@ while( i.x < i.len ) {
return sum;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Loop16,0,(Phi(Loop,0,(Phi_$2+1))+Phi_sum));", stop.toString());
- assertEquals(10L,Evaluator.evaluate(stop,5));
+ assertEquals("return Phi(Loop17,0,(Phi(Loop,0,(Phi_x+1))+Phi_sum));", stop.toString());
}
@@ -311,7 +310,7 @@ while(arg) {
return ret;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Loop11,new s0,Phi(Region34,new s0,Phi_ret));", stop.toString());
+ assertEquals("return Phi(Loop11,new s0,Phi(Region35,new s0,Phi_ret));", stop.toString());
}
@Test
@@ -328,7 +327,7 @@ while(arg) {
return ret;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Loop15,new s0,Phi(Region35,new s0,Phi_ret));", stop.toString());
+ assertEquals("return Phi(Loop15,new s0,Phi(Region36,new s0,Phi_ret));", stop.toString());
}
@@ -345,7 +344,7 @@ while(arg < 10) {
return ret;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Loop11,new s0,Phi(Region32,new s0,Phi_ret));", stop.toString());
+ assertEquals("return Phi(Loop11,new s0,Phi(Region33,new s0,Phi_ret));", stop.toString());
}
@Test
@@ -375,7 +374,7 @@ if(0) {
}
""");
StopNode stop = parser.parse().iterate();
- assertEquals("Stop[ ]", stop.toString());
+ assertEquals("return 0;", stop.toString());
}
@Test
@@ -449,7 +448,7 @@ while(0) {}
}
""");
StopNode stop = parser.parse().iterate();
- assertEquals("Stop[ ]", stop.toString());
+ assertEquals("return 0;", stop.toString());
}
@Test
diff --git a/src/test/java/com/seaofnodes/simple/Chapter12Test.java b/src/test/java/com/seaofnodes/simple/Chapter12Test.java
new file mode 100644
index 00000000..0d4f3c97
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/Chapter12Test.java
@@ -0,0 +1,200 @@
+package com.seaofnodes.simple;
+
+import com.seaofnodes.simple.evaluator.Evaluator;
+import com.seaofnodes.simple.node.StopNode;
+import com.seaofnodes.simple.type.TypeStruct;
+import com.seaofnodes.simple.type.TypeMemPtr;
+import org.junit.Ignore;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+
+public class Chapter12Test {
+
+ @Test
+ public void testLinkedList0() {
+ Parser parser = new Parser(
+"""
+struct LLI { LLI? next; int i; }
+LLI? head = null;
+while( arg ) {
+ LLI x = new LLI;
+ x.next = head;
+ x.i = arg;
+ head = x;
+ arg = arg-1;
+}
+return head.next.i;
+""");
+ try { parser.parse().iterate(); fail(); }
+ catch( Exception e ) { assertEquals("Might be null accessing 'next'",e.getMessage()); }
+ }
+
+ @Test
+ public void testLinkedList1() {
+ Parser parser = new Parser(
+"""
+struct LLI { LLI? next; int i; }
+LLI? head = null;
+while( arg ) {
+ LLI x = new LLI;
+ x.next = head;
+ x.i = arg;
+ head = x;
+ arg = arg-1;
+}
+if( !head ) return 0;
+LLI? next = head.next;
+if( next==null ) return 1;
+return next.i;
+""");
+ StopNode stop = parser.parse().iterate();
+ assertEquals("Stop[ return 0; return 1; return .i; ]", stop.toString());
+ assertEquals(2L, Evaluator.evaluate(stop, 3));
+ }
+
+ @Test
+ public void testCoRecur() {
+ Parser parser = new Parser(
+"""
+struct Left { int i; Right? f; }
+struct Right { int f; Left? i; }
+Left left = new Left;
+left.i = 17;
+Right right = new Right;
+right.f = 314;
+left.f = right;
+right.i = left;
+return right.i.f.i.i;
+""");
+ StopNode stop = parser.parse().iterate();
+ assertEquals("return 17;", stop.toString());
+ }
+
+ @Test
+ public void testNullReright() {
+ Parser parser = new Parser(
+"""
+struct N { N next; int i; }
+N n = new N;
+return n.next;
+""");
+ StopNode stop = parser.parse().iterate();
+ assertEquals("return null;", stop.toString());
+ }
+
+ @Test
+ public void testNullRef1() {
+ Parser parser = new Parser(
+"""
+struct N { N next; int i; }
+N n = new N;
+n.next = new N;
+return n.next;
+""");
+ StopNode stop = parser.parse().iterate();
+ assertEquals("return new N;", stop.toString());
+ }
+
+ @Test
+ public void testNullRef2() {
+ Parser parser = new Parser(
+"""
+struct N { N next; int i; }
+N n = new N;
+n.next = null;
+return n.next;
+""");
+ try { parser.parse().iterate(); fail(); }
+ catch( Exception e ) { assertEquals("Cannot store null into field *N next",e.getMessage()); }
+ }
+
+
+ @Test
+ public void testNullRef4() {
+ Parser parser = new Parser("-null-5/null-5");
+ try { parser.parse().iterate(); fail(); }
+ catch( Exception e ) { assertEquals("Expected an identifier, found 'null'",e.getMessage()); }
+ }
+
+ @Test public void testNullRef5() {
+ Parser parser = new Parser("return null+42;");
+ try { parser.parse().iterate(); fail(); }
+ catch( Exception e ) { assertEquals("Cannot 'Add' null",e.getMessage()); }
+ }
+
+ @Test
+ public void testEmpty() {
+ Parser parser = new Parser(
+"""
+struct S{};
+""");
+ StopNode stop = parser.parse().iterate();
+ assertEquals("return 0;", stop.toString());
+ assertEquals(0L, Evaluator.evaluate(stop, 0));
+ }
+
+ @Test
+ public void testForwardReright() {
+ Parser parser = new Parser(
+"""
+struct S1 { S2 s; }
+return new S2;
+""");
+ try { parser.parse().iterate(); fail(); }
+ catch( Exception e ) { assertEquals("Unknown struct type 'S2'",e.getMessage()); }
+ }
+
+ @Test
+ public void testForwardRef1() {
+ Parser parser = new Parser(
+"""
+struct S1 { S2? s; }
+struct S2 { int x; }
+return new S1.s=new S2;
+""");
+ StopNode stop = parser.parse().iterate();
+ assertEquals("return new S1;", stop.toString());
+ }
+
+ @Test
+ public void testcheckNull() {
+ Parser parser = new Parser(
+"""
+struct I {int i;}
+struct P {I pi;}
+P p1 = new P;
+P p2 = new P;
+p2.pi = new I;
+p2.pi.i = 2;
+if (arg) p1 = new P;
+return p1.pi.i + 1;
+""");
+ try { parser.parse().iterate(); fail(); }
+ catch( Exception e ) { assertEquals("Might be null accessing 'i'",e.getMessage()); }
+ }
+
+
+ @Test
+ public void testShallowRecursiveType() {
+ new Parser("struct LLI { LLI? next; int i; } return 0;").parse().iterate();
+ TypeStruct l1 = (TypeStruct)Parser.TYPES.get("LLI");
+ TypeStruct l0 = ((TypeMemPtr)l1._fields[0]._type)._obj;
+ assertEquals("LLI", l0._name);
+ assertNull(l0._fields);
+ assertEquals(2, l1._fields.length);
+ assertSame(l0, l1.meet(l0));
+ assertSame(l0, l0.dual());
+ assertSame(l0, l0.glb());
+ }
+
+ @Test
+ public void testUnusedForwardReference() {
+ StopNode stop = new Parser("struct Holder { Missing? ref; } Holder h = new Holder; return h.ref;")
+ .parse().iterate();
+ assertEquals("return null;", stop.toString());
+ assertNull(((TypeStruct)Parser.TYPES.get("Missing"))._fields);
+ }
+}
diff --git a/src/test/java/com/seaofnodes/simple/TypeTest.java b/src/test/java/com/seaofnodes/simple/TypeTest.java
index f9c580cc..298fc6f8 100644
--- a/src/test/java/com/seaofnodes/simple/TypeTest.java
+++ b/src/test/java/com/seaofnodes/simple/TypeTest.java
@@ -36,9 +36,9 @@ public class TypeTest {
Assert.assertEquals(TypeMem .BOT, m2.meet(m3));
Assert.assertEquals(TypeMem .BOT, m3.meet(m4));
- Assert.assertEquals(TypeMem.BOT, m1.glb());
+ Assert.assertEquals(m1, m1.glb());
Assert.assertEquals(m1, m1.dual());
- Assert.assertEquals(TypeMem.TOP, m1.glb().dual());
+ Assert.assertEquals(m1, m1.glb().dual());
TypeMemPtr ptr1 = TypeMemPtr.make(s1);
Assert.assertEquals(s1, ptr1._obj);
diff --git a/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java b/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java
index 155063ab..14baadba 100644
--- a/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java
+++ b/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java
@@ -3,9 +3,7 @@ package com.seaofnodes.simple.fuzzer;
import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.node.ScopeNode;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Random;
+import java.util.*;
import java.util.function.Predicate;
/**
@@ -22,6 +20,7 @@ public class ScriptGenerator {
private static final int MAX_NAME_LENGTH = 10;
private static final int MAX_BLOCK_DEPTH = 4;
private static final int MAX_EXPRESSION_DEPTH = 10;
+ private static final int MAX_SUFFIX_RECURSIVE_DEPTH = 10;
/**
* Number of spaces per indentation
@@ -68,12 +67,11 @@ public class ScriptGenerator {
}
private static class TypeStruct extends Type {
- record Field (String name, Type type) {}
- final Field[] fields;
+ record Field (String name, Type type, TypeStruct struct) {}
+ Field[] fields;
final TypeNullable nullable = new TypeNullable(this);
- TypeStruct(String name, Field[] fields) {
+ TypeStruct(String name) {
super(name);
- this.fields = fields;
}
boolean isa(Type other) { return this == other || other == nullable; }
}
@@ -144,6 +142,10 @@ public class ScriptGenerator {
*/
private final ArrayList<TypeStruct> structs = new ArrayList<>();
/**
+ * All forward declared structs
+ */
+ private final ArrayList<TypeStruct> forwardStructs = new ArrayList<>();
+ /**
* Allow to declare structs.
*/
private boolean allowStructs = true;
@@ -286,6 +288,7 @@ public class ScriptGenerator {
for (var s:structs) {
if (s.name.equals(name)) return s;
}
+ for (var s:forwardStructs) if (s.name.equals(name)) return s;
return null;
}
@@ -318,12 +321,18 @@ public class ScriptGenerator {
return v;
}
+ private <T> T randFromList(List<T> list) {
+ return list.get(random.nextInt(list.size()));
+ }
+
/**
* Get a random structure name
* @return A random structure name
*/
private String getStructName() {
- if (!structs.isEmpty() && generateInvalid()) return structs.get(random.nextInt(structs.size())).name;
+ if (!structs.isEmpty() && generateInvalid()) return randFromList(structs).name;
+ if (!forwardStructs.isEmpty() && generateInvalid()) return randFromList(forwardStructs).name;
+
// Generate a new random struct name
StringBuilder sb = getRandomName();
var v = sb.toString();
@@ -342,14 +351,26 @@ public class ScriptGenerator {
* Get a random type
* @return A random type
*/
- private Type getType() {
- if (structs.isEmpty() || random.nextBoolean()) return TYPE_INT;
- var idx = random.nextInt(structs.size());
- TypeStruct struct = structs.get(idx);
+ private Type getType(boolean allowForward) {
+ int t = random.nextInt(10);
+ if( t<5 || structs.isEmpty() ) return TYPE_INT;
+ if (allowForward && random.nextInt(10)==0) {
+ int i=random.nextInt(forwardStructs.size()+1);
+ if (i>0) return forwardStructs.get(i-1).nullable;
+ var name = getStructName();
+ var struct = new TypeStruct(name);
+ forwardStructs.add(struct);
+ return struct.nullable;
+ }
+ TypeStruct struct = randFromList(structs);
if (random.nextBoolean()) return struct.nullable;
return struct;
}
+ private Type getType() {
+ return getType(false);
+ }
+
/**
* Generates a program and writes it into the string builder supplied in the constructor.
* @return If the program is allowed to be invalid returns if something potentially invalid was generated.
@@ -409,19 +430,29 @@ public class ScriptGenerator {
* @return 0
*/
public int genStruct() {
- var name = getStructName();
+ int idx = random.nextInt(forwardStructs.size()+10);
+ TypeStruct struct;
+ if (idx < 10) {
+ var name = getStructName();
+ struct = new TypeStruct(name);
+ forwardStructs.add(struct);
+ } else {
+ struct = forwardStructs.get(idx-10);
+ }
var fields = new TypeStruct.Field[generateInvalid() ? 0 : random.nextInt(10)];
- sb.append("struct ").append(name).append(" {\n");
+ sb.append("struct ").append(struct.name).append(" {\n");
indentation += INDENTATION;
for (int i=0; i<fields.length; i++) {
var fieldName = getRandomName();
- var type = generateInvalid() ? getType() : TYPE_INT;
+ var type = getType(true);
printIndentation().append(type.name).append(" ").append(fieldName).append(";\n");
- fields[i] = new TypeStruct.Field(fieldName.toString(), type);
+ fields[i] = new TypeStruct.Field(fieldName.toString(), type, struct);
}
indentation -= INDENTATION;
printIndentation().append("}");
- structs.add(new TypeStruct(name, fields));
+ struct.fields = fields;
+ forwardStructs.remove(struct);
+ structs.add(struct);
return 0;
}
@@ -515,7 +546,7 @@ public class ScriptGenerator {
*/
public int genIf() {
sb.append("if(");
- genExpression(TYPE_INT);
+ genExpression(TYPE_INT, true);
sb.append(") ");
var stop = genStatementBlock();
if ((stop & FLAG_IF_WITHOUT_ELSE) == 0 && random.nextInt(10) > 3) {
@@ -561,7 +592,7 @@ public class ScriptGenerator {
*/
public int genWhile() {
sb.append("while(");
- genExpression(TYPE_INT);
+ genExpression(TYPE_INT, true);
sb.append(") ");
loopDepth++;
genStatementBlock();
@@ -587,17 +618,17 @@ public class ScriptGenerator {
indentation += INDENTATION;
String name = getVarName();
printIndentation().append("int ").append(name).append("=");
- genExpression(TYPE_INT);
+ genExpression(TYPE_INT, true);
sb.append(";\n");
addVariable(name, TYPE_INT);
printIndentation().append("while(").append(name).append("<");
- genExpression(TYPE_INT);
+ genExpression(TYPE_INT, true);
sb.append(") {\n");
indentation += INDENTATION;
depth--;
loopDepth++;
printIndentation().append(name).append("=").append(name).append("+");
- genExpression(TYPE_INT);
+ genExpression(TYPE_INT, true);
sb.append(";\n");
genStatements();
loopDepth--;
@@ -621,7 +652,7 @@ public class ScriptGenerator {
sb.append(generateInvalid() ? getRandomName() : type.name).append(" ").append(name);
if (!(type instanceof TypeNullable) || random.nextBoolean()) {
sb.append("=");
- genExpression(type);
+ genExpression(type, true);
}
sb.append(";");
addVariable(name, type);
@@ -656,7 +687,7 @@ public class ScriptGenerator {
declared = field.type;
}
sb.append("=");
- genExpression(declared);
+ genExpression(declared, true);
sb.append(";");
return 0;
}
@@ -668,7 +699,7 @@ public class ScriptGenerator {
public int genReturn() {
var type = getType();
sb.append("return ");
- genExpression(type);
+ genExpression(type, true);
sb.append(";");
return FLAG_STOP;
}
@@ -677,70 +708,91 @@ public class ScriptGenerator {
* Generate a binary expression.
* This method does not care about operator precedence.
*/
- public void genExpression(Type type) {
- if (generateInvalid()) type = getType();
+ public void genExpression(Type type, boolean change) {
+ if (change && generateInvalid()) type = getType();
if (type != TYPE_INT) {
- genUnary(type);
+ genUnary(type, false);
return;
}
var num = randLog(MAX_BINARY_EXPRESSIONS_PER_EXPRESSION);
while(num-->0) {
- genUnary(TYPE_INT);
+ genUnary(type, false);
sb.append(BINARY_OP[random.nextInt(BINARY_OP.length)]);
}
- genUnary(TYPE_INT);
+ genUnary(type, false);
}
/**
* Generate a unary expression.
*/
- public void genUnary(Type type) {
- if (generateInvalid()) type = getType();
+ public void genUnary(Type type, boolean change) {
+ if (change && generateInvalid()) type = getType();
if (type != TYPE_INT) {
- genSuffix(type);
+ genSuffix(type, false);
return;
}
var num = randLog(MAX_UNARY_EXPRESSIONS_PER_EXPRESSION);
while(num-->0) {
sb.append(UNARY_OP[random.nextInt(UNARY_OP.length)]);
}
- genSuffix(TYPE_INT);
+ genSuffix(type, false);
+ }
+
+ public TypeStruct.Field getField(Type type) {
+ int n=0;
+ for (var s: structs) {
+ for (var f: s.fields) {
+ if (f.type.isa(type)) n++;
+ }
+ }
+ if (n==0) return null;
+ n = random.nextInt(n);
+ for (var s: structs) {
+ for (var f: s.fields) {
+ if (f.type.isa(type)) if (n--==0) return f;
+ }
+ }
+ throw new AssertionError();
}
/**
* Generate
*/
- public void genSuffix(Type type) {
- if (generateInvalid()) type = getType();
- if (type == TYPE_INT && random.nextBoolean()) {
- var t = getType();
+ public void genSuffix(Type type, boolean change) {
+ genSuffixRecursive(type, change, MAX_SUFFIX_RECURSIVE_DEPTH);
+ }
+
+ public void genSuffixRecursive(Type type, boolean change, int depth) {
+ if (change && generateInvalid()) type = getType();
+ if (depth > 0 && random.nextBoolean()) {
if (generateInvalid()) {
var field = getRandomName();
- genPrimary(t);
+ var t = getType();
+ genSuffixRecursive(t, true, depth-1);
sb.append(".").append(field);
return;
- } else if (t instanceof TypeStruct s && s.fields.length > 0) {
- var field = s.fields[random.nextInt(s.fields.length)];
- if (field.type == TYPE_INT) {
- genPrimary(t);
+ } else {
+ var field = getField(type);
+ if (field != null) {
+ genSuffixRecursive(field.struct, true, depth-1);
sb.append(".").append(field.name);
return;
}
}
}
- genPrimary(type);
+ genPrimary(type, false);
}
/**
* Generate a primary expression.
*/
- public void genPrimary(Type type) {
- if (generateInvalid()) type = getType();
+ public void genPrimary(Type type, boolean change) {
+ if (change && generateInvalid()) type = getType();
var rand = random.nextInt(10);
if (rand == 0 && exprDepth != 0) {
sb.append("(");
exprDepth--;
- genExpression(type);
+ genExpression(type, false);
exprDepth++;
sb.append(")");
} else if (rand < 6) {
@@ -762,8 +814,9 @@ public class ScriptGenerator {
case 1 -> sb.append("false");
default -> sb.append(random.nextInt(1<<(rand-2)));
}
+
} else if (type instanceof TypeNullable n) {
- if (random.nextBoolean()) {
+ if ((n.base instanceof TypeStruct s && s.fields == null) || random.nextBoolean()) {
sb.append("null");
} else {
sb.append("new ").append(generateInvalid() ? getRandomName() : n.base.name);