summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/Parser.java79
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/node/StoreNode.java3
-rw-r--r--chapter18/src/test/java/com/seaofnodes/simple/Chapter17Test.java26
-rw-r--r--chapter18/src/test/java/com/seaofnodes/simple/Chapter18Test.java17
4 files changed, 84 insertions, 41 deletions
diff --git a/chapter18/src/main/java/com/seaofnodes/simple/Parser.java b/chapter18/src/main/java/com/seaofnodes/simple/Parser.java
index 03ae978a..c16e3267 100644
--- a/chapter18/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter18/src/main/java/com/seaofnodes/simple/Parser.java
@@ -2,7 +2,6 @@ package com.seaofnodes.simple;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.*;
-
import java.util.*;
/**
@@ -715,7 +714,10 @@ public class Parser {
tfp.setName(name);
} else {
- if( inferType && !_scope.inCon() )
+ // Need an expression to infer the type.
+ // Also, if not-null then need an initializing expression.
+ // Allowed in a class def, because type/init will happen in the constructor.
+ if( (inferType || (t instanceof TypeNil tn && !tn.nullable() )) && !_scope.inCon() )
throw errorSyntax("=expression");
// Initial value for uninitialized struct fields.
expr = switch( t ) {
@@ -1080,22 +1082,29 @@ public class Parser {
// Check for assign-update, x += e0;
char ch = _lexer.matchOperAssign();
if( ch==0 ) return rvalue;
+ Node op = opAssign(ch,rvalue,n._final,n.type(),n._name);
+ _scope.update(n,op);
+ return postfix(ch) ? rvalue.unkeep() : op;
+ }
- // the rvalue is an l-value now; it will be updated
- Node lhs = rvalue.keep();
- if( n._final )
- throw error("Cannot reassign final '"+n._name+"'");
- // RHS of the update
+ // Check for assign-update, "x += e0".
+ // Returns "x + e0"; the caller assigns value.
+ // if ch is postfix, then lhs is kept and caller will unkeep and return it.
+ private Node opAssign(char ch, Node lhs, boolean xfinal, Type t, String name ) {
+ if( xfinal )
+ throw error("Cannot reassign final '"+name+"'");
+ // RHS of the update.
+ lhs.keep(); // Alive across parseAsgn
Node rhs =
(byte)ch == 1 ? con( 1) : // var++
(byte)ch == -1 ? con(-1) : // var--
- parseAsgn(); // var op= rhs
- // Widen RHS int to a RHS float
- rhs = widenInt(rhs,n.type());
- // Complain a RHS float into a LHS int
- if( !(rhs._type instanceof TypeInteger) && !rhs._type.isa(n.type()) )
- throw error("Type " + rhs._type.str() + " is not of declared type " + n.type().str());
-
+ parseAsgn(); // var op= rhs
+ if( !postfix(ch) ) lhs.unkeep(); // Allow to die in next peep
+ // 4 cases:
+ // int + int ==>> narrow int
+ // int + flt ==>> error, caller must fail assigning flt into int
+ // flt + int ==>> use float op, wrap toFloat()
+ // flt + flt ==>> use float op
Node op = switch(ch) {
case 1, (char)-1,
'+' -> new AddNode(lhs,rhs);
@@ -1104,14 +1113,12 @@ public class Parser {
case '/' -> new DivNode(lhs,rhs);
default -> throw Utils.TODO();
};
-
- op = zsMask(peep(op.widen()),n.type());
- _scope.update(n,op);
- // Return pre-value (x+=1) or post-value (x++)
- lhs.unkeep();
- return (byte)ch== 1 || (byte)ch== -1 ? lhs : op;
+ // Convert to float ops, or narrow int types; error if not declared type.
+ // Also, if postfix LHS is still keep()
+ return liftExpr(peep(op.widen()),t,false);
}
+
// Expect an ID here. If not found, assume a forward reference function
Var requireLookupId() {
String id = _lexer.matchId();
@@ -1235,10 +1242,11 @@ public class Parser {
* lookup or a postfix operator like '#'
*
* <pre>
- * expr ('.' IDENTIFIER)* [ = expr ]
- * expr #
- * expr ('[' expr ']')* = [ = expr ]
- * expr '(' [args,]* ')'
+ * expr ('.' FIELD)* [ = expr ] // Field reference
+ * expr '#' // Postfix unary read operator
+ * expr ['++' | '--' ] // Postfix unary write operator
+ * expr ('[' expr ']')* = [ = expr ] // Array reference
+ * expr '(' [args,]* ')' // Function call
* </pre>
*/
private Node parsePostfix(Node expr) {
@@ -1297,22 +1305,17 @@ public class Parser {
if( base.isAry() ) load.setDef(0,ctrl());
load = peep(load);
- // ary[idx]++ or ptr.fld++
- if( matchx("++") || matchx("--") ) {
- if( f._final && !f._fname.equals("[]") )
- throw error("Cannot reassign final '"+f._fname+"'");
- load.keep();
- int delta = _lexer.peek(-1)=='+' ? 1 : -1; // Pre vs post
- Node val = f._type instanceof TypeFloat
- ? peep(new AddFNode(load,con(TypeFloat.constant(delta))))
- : zsMask(peep(new AddNode(load,con(delta))),f._type);
- Node st = new StoreNode(loc(), name, f._alias, tf, memAlias(f._alias), expr.unkeep(), off, val, false);
+ // Check for assign-update, "ptr.fld += expr" or "ary[idx]++"
+ char ch = _lexer.matchOperAssign();
+ if( ch!=0 ) {
+ Node op = opAssign(ch,load,f._final,tf,name);
+ Node st = new StoreNode(loc(), name, f._alias, tf, memAlias(f._alias), expr.unkeep(), off, op, false);
// Arrays include control, as a proxy for a safety range check.
// Structs don't need this; they only need a NPE check which is
// done via the type system.
if( base.isAry() ) st.setDef(0,ctrl());
memAlias(f._alias, peep(st));
- load.unkeep();
+ load = postfix(ch) ? load.unkeep() : op;
// And use the original loaded value as the result
} else expr.unkill();
off.unkill();
@@ -1441,6 +1444,11 @@ public class Parser {
private boolean peek(char ch) { return _lexer.peek(ch); }
private boolean peekIsId() { return _lexer.peekIsId(); }
+ // ch is +/- 1, means oper++ or oper-- means postfix
+ private static boolean postfix(char ch) {
+ return (byte)ch== 1 || (byte)ch== -1;
+ }
+
public int pos() { return _lexer._position; }
private int pos(int pos) {
int old = _lexer._position;
@@ -1704,7 +1712,6 @@ public class Parser {
if( ch0 == '-' && ch1 == '-' ) { _position += 2; return (char)-1; }
return 0;
}
-
}
ParseException errorSyntax(String syntax) { return _errorSyntax("expected "+syntax); }
diff --git a/chapter18/src/main/java/com/seaofnodes/simple/node/StoreNode.java b/chapter18/src/main/java/com/seaofnodes/simple/node/StoreNode.java
index 6032de48..0fa7d591 100644
--- a/chapter18/src/main/java/com/seaofnodes/simple/node/StoreNode.java
+++ b/chapter18/src/main/java/com/seaofnodes/simple/node/StoreNode.java
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node;
+import com.seaofnodes.simple.IterPeeps;
import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.type.*;
-
import java.util.BitSet;
/**
@@ -96,6 +96,7 @@ public class StoreNode extends MemOpNode {
// but is monotonic counting from this Store to the New.
nnn ._type = nnn .compute();
mem()._type = mem().compute();
+ IterPeeps.addAll(nnn._outputs);
return mem();
}
diff --git a/chapter18/src/test/java/com/seaofnodes/simple/Chapter17Test.java b/chapter18/src/test/java/com/seaofnodes/simple/Chapter17Test.java
index 98c30721..8ec95f33 100644
--- a/chapter18/src/test/java/com/seaofnodes/simple/Chapter17Test.java
+++ b/chapter18/src/test/java/com/seaofnodes/simple/Chapter17Test.java
@@ -8,11 +8,11 @@ public class Chapter17Test {
@Test
public void testJig() {
- CodeGen code = new CodeGen("""
+ CodeGen code = new CodeGen(
+"""
int i = 0;
i=i=1;
return i;
- //return 3.14;
""");
code.parse().opto();
assertEquals("return 1;", code.print());
@@ -157,14 +157,14 @@ return s.x;
}
@Test public void testVar4() {
- CodeGen code = new CodeGen("struct S{int x;}; S s; s=new S; s.x++; return s.x; // Ok, no initializer so x is mutable ");
+ CodeGen code = new CodeGen("struct S{int x;}; S? s; s=new S; s.x++; return s.x; // Ok, no initializer so x is mutable ");
code.parse().opto();
assertEquals("return 1;", code.print());
assertEquals("1", Eval2.eval(code, 0));
}
@Test public void testVar5() {
- CodeGen code = new CodeGen("struct S{int x;}; S s; s=new S{x=3;}; s.x++; return s.x; // Ok, no initializer so x is mutable ");
+ CodeGen code = new CodeGen("struct S{int x;}; S? s; s=new S{x=3;}; s.x++; return s.x; // Ok, no initializer so x is mutable ");
code.parse().opto();
assertEquals("return 4;", code.print());
assertEquals("4", Eval2.eval(code, 0));
@@ -293,6 +293,24 @@ return x;
catch( Exception e ) { assertEquals("Cannot reassign final 'x'",e.getMessage()); }
}
+ @Test
+ public void testVar17() {
+ CodeGen code = new CodeGen(
+"""
+struct Person {
+ bool focused;
+};
+val focus = { Person p ->
+ p.focused = true;
+};
+Person me; // Person !me = new Person;
+focus(me);
+return me;
+"""
+);
+ try { code.parse().opto().typeCheck(); fail(); }
+ catch( Exception e ) { assertEquals("Syntax error, expected =expression: ;",e.getMessage()); }
+ }
// ---------------------------------------------------------------
@Test
diff --git a/chapter18/src/test/java/com/seaofnodes/simple/Chapter18Test.java b/chapter18/src/test/java/com/seaofnodes/simple/Chapter18Test.java
index 1d7abde5..60f9b847 100644
--- a/chapter18/src/test/java/com/seaofnodes/simple/Chapter18Test.java
+++ b/chapter18/src/test/java/com/seaofnodes/simple/Chapter18Test.java
@@ -325,4 +325,21 @@ return f2f(o)(1);
assertEquals("Stop[ return Phi(Region,o( arg),o( 1)); return Parm_i(o,int,arg,1); ]", code._stop.toString());
assertEquals("1", Eval2.eval(code, 2));
}
+
+ @Test
+ public void testOperField() {
+ CodeGen code = new CodeGen(
+"""
+struct Person {
+ int coffee_count;
+};
+Person !p = new Person;
+p.coffee_count += 1;
+return p.coffee_count;
+""");
+ code.parse().opto().typeCheck().GCM().localSched();
+ assertEquals("return 1;", code._stop.toString());
+ assertEquals("1", Eval2.eval(code, 2));
+ }
+
}