diff options
| author | Cliff Click <cliffc@acm.org> | 2024-10-14 10:31:40 -0700 |
|---|---|---|
| committer | Cliff Click <cliffc@acm.org> | 2024-10-21 08:52:01 -0700 |
| commit | 94e557c9de68e7b3f5595d75e7330b63df76c323 (patch) | |
| tree | f16fca7e4b49f82f98f73c073b7495abedafd195 /chapter16/src/main/java/com | |
| parent | 475f66adc7c1c9f1cd479c4492999eae4c060c5b (diff) | |
| download | Simple-94e557c9de68e7b3f5595d75e7330b63df76c323.tar.gz Simple-94e557c9de68e7b3f5595d75e7330b63df76c323.zip | |
final fields and multi defs
`int x,y;` works
`int !x = 3;` works
Structs defs are statements, require a trailing `;`
Diffstat (limited to 'chapter16/src/main/java/com')
8 files changed, 145 insertions, 66 deletions
diff --git a/chapter16/src/main/java/com/seaofnodes/simple/Parser.java b/chapter16/src/main/java/com/seaofnodes/simple/Parser.java index 728aaca7..bdd14de6 100644 --- a/chapter16/src/main/java/com/seaofnodes/simple/Parser.java +++ b/chapter16/src/main/java/com/seaofnodes/simple/Parser.java @@ -147,8 +147,8 @@ public class Parser { _xScopes.push(_scope); // Enter a new scope for the initial control and arguments _scope.push(); - _scope.define(ScopeNode.CTRL, Type.CONTROL , new CProjNode(START, 0, ScopeNode.CTRL).peephole()); - _scope.define(ScopeNode.ARG0, TypeInteger.BOT, new ProjNode(START, 1, ScopeNode.ARG0).peephole()); + _scope.define(ScopeNode.CTRL, Type.CONTROL , new CProjNode(START, 0, ScopeNode.CTRL).peephole(), false); + _scope.define(ScopeNode.ARG0, TypeInteger.BOT, new ProjNode(START, 1, ScopeNode.ARG0).peephole(), false); // Parse whole program parseBlock(); @@ -212,12 +212,13 @@ public class Parser { * type IDENTIFIER ; * </pre> */ - private Field parseField() { - Type t = type(); - if( t==null ) - throw errorSyntax("Requires a field type, found '"+_lexer.getAnyNextToken()+"'"); - return require(Field.make(requireId().intern(),ALIAS++,t),";"); - } + //private Field parseField() { + // Type t = type(); + // if( t==null ) + // throw errorSyntax("Requires a field type, found '"+_lexer.getAnyNextToken()+"'"); + // boolean xfinal = match("!"); + // return require(Field.make(requireId().intern(),t,ALIAS++,xfinal),";"); + //} /** * Parse a struct declaration, and return the following statement. @@ -232,20 +233,35 @@ public class Parser { Type t = TYPES.get(typeName); if( t!=null && !(t instanceof TypeMemPtr tmp && tmp._obj._fields==null) ) throw errorSyntax("struct '" + typeName + "' cannot be redefined"); - // Parse a collection of fields - ArrayList<Field> fields = new ArrayList<>(); + // A Block scope parse, and inspect the scope afterwards for fields. + _scope.push(); require("{"); - while (!peek('}') && !_lexer.isEOF()) { - Field field = parseField(); - if (fields.contains(field)) throw errorSyntax("Field '" + field + "' already defined in struct '" + typeName + "'"); - fields.add(field); - } + while (!peek('}') && !_lexer.isEOF()) + parseStatement(); require("}"); + // Grab the declarations and build fields + HashMap<String,Type> decls = _scope._decls.lastElement(); + HashMap<String,Integer> idxs = _scope._idxs.lastElement(); + BitSet finals = _scope._finals; + + Field[] fs = new Field[decls.size()]; int i=0; + for( String fname : decls.keySet() ) { + int idx = idxs.get(fname); + boolean isFinal = finals.get(idx); + Type decl = decls.get(fname); + fs[i++] = Field.make(fname,decl,ALIAS++,isFinal); + if( isFinal ) throw Utils.TODO(); // Track write-once + if( _scope.in(idx)._type != decl.makeInit() ) throw Utils.TODO(); // Track non-zero init + } + // Done with scope + _scope.pop(); + // Build and install the TypeStruct - TypeStruct ts = TypeStruct.make(typeName, fields.toArray(new Field[fields.size()])); + TypeStruct ts = TypeStruct.make(typeName, fs); TYPES.put(typeName, TypeMemPtr.make(ts)); START.addMemProj(ts, _scope); // Insert memory edges - return parseStatement(); + require(";"); + return null; } /** @@ -429,43 +445,33 @@ public class Parser { return null; } - /** - * Parses an expression statement or a declaration statement where type is a struct - * - * <pre> - * name; // Error - * type name; // Define name with default initial value - * type name = expr; // Define name with given initial value - * name = expr; // Reassign existing - * expr // Something else - * </pre> - * @return an expression {@link Node}, never {@code null} + /** Parse: name [=expr] */ - private Node parseExpressionStatement() { + private Node parseAsgn(Type t, boolean xfinal) { + boolean isDecl = t!=null; // Having a type is a declaration, missing one is updating a prior name int old = _lexer._position; - Type t = type(); String name = requireId(); Node expr; - if( match(";") ) { // Assign a default value - // No type and no expr is an error + if( match(";") || peek(',') ) { + // Bare "name" is not allowed if( t==null ) throw errorSyntax("expression"); expr = new ConstantNode(t.makeInit()).peephole(); - } else if( match("=") ) { // Assign "= expr;" - expr = require(parseExpression(), ";"); - } else { // Neither, so just a normal expression parse + } else if( !match("=") ) { // Something else _lexer._position = old; - return require(parseExpression(),";"); - } + return null; + } else + expr = parseExpression(); // Defining a new variable vs updating an old one - if( t != null ) { - if( _scope.define(name,t,expr) == null ) - throw error("Redefining name '" + name + "'"); - } else { + if( !isDecl ) { // Assigning over an existing name + // Lookup if( _scope.lookup(name)==null ) throw error("Undefined name '" + name + "'"); + if( _scope.lookupFinal(name) ) + throw error("Cannot reassign final '"+name+"'"); t = _scope.lookupDeclaredType(name); } + // Auto-widen int to float if( expr._type instanceof TypeInteger && t instanceof TypeFloat ) expr = new ToFloatNode(expr).peephole(); @@ -478,7 +484,50 @@ public class Parser { // Type is sane if( !e.isa(t) ) throw error("Type " + e.str() + " is not of declared type " + t.str()); - return _scope.update(name,expr); + + + if( isDecl ) { + if( _scope.define(name,t,expr,xfinal) == null ) + throw error("Redefining name '" + name + "'"); + } else + _scope.update(name,expr); + return expr; + } + + /** Parse final: [!]asgn + */ + private Node parseFinal(Type t) { + boolean xfinal = t!=null && match("!"); + return parseAsgn(t,xfinal); + } + + /** + * Parses an expression statement or a declaration statement where type is a struct + * + * <pre> + * type decl [, decl]*; // Define many names with the same type + * asgn; // Assign or load a variable + * expr; // Something else + * name; // Bare name is an error + + * </pre> + * @return an expression {@link Node}, never {@code null} + */ + private Node parseExpressionStatement() { + Node n; + Type t = type(); + if( t != null ) { + // now parse final [, final]* + n = parseFinal(t); + while( match(",") ) + n = parseFinal(t); + + // Parse "asgn;" which is just "name = expr;" + } else if( ( n = parseAsgn(null,false)) == null ) { + // Something else + n = parseExpression(); + } + return require(n,";"); } // Parse and return a type or null. Valid types always are followed by an diff --git a/chapter16/src/main/java/com/seaofnodes/simple/node/SarNode.java b/chapter16/src/main/java/com/seaofnodes/simple/node/SarNode.java index ba99fde8..b46a7ba7 100644 --- a/chapter16/src/main/java/com/seaofnodes/simple/node/SarNode.java +++ b/chapter16/src/main/java/com/seaofnodes/simple/node/SarNode.java @@ -23,6 +23,8 @@ public class SarNode extends Node { public Type compute() { if (in(1)._type instanceof TypeInteger i0 && in(2)._type instanceof TypeInteger i1) { + if( i0 == TypeInteger.ZERO ) + return TypeInteger.ZERO; if( i0.isConstant() && i1.isConstant() ) return TypeInteger.constant(i0.value()>>i1.value()); if( i1.isConstant() ) { diff --git a/chapter16/src/main/java/com/seaofnodes/simple/node/ScopeNode.java b/chapter16/src/main/java/com/seaofnodes/simple/node/ScopeNode.java index 877ae4a4..0e7a5e9b 100644 --- a/chapter16/src/main/java/com/seaofnodes/simple/node/ScopeNode.java +++ b/chapter16/src/main/java/com/seaofnodes/simple/node/ScopeNode.java @@ -8,7 +8,7 @@ import java.util.*; /** * The Scope node is purely a parser helper - it tracks names to nodes with a - * stack of scopes. + * stack of hashmaps. */ public class ScopeNode extends Node { @@ -24,15 +24,21 @@ public class ScopeNode extends Node { */ public final Stack<HashMap<String, Integer>> _idxs; + /* + * Map input edge to Final bit + */ + public final BitSet _finals; + /** * Map variable names to declared Type */ - final Stack<HashMap<String, Type>> _decls; + public final Stack<HashMap<String, Type>> _decls; // A new ScopeNode public ScopeNode() { _idxs = new Stack<>(); _decls= new Stack<>(); + _finals = new BitSet(); _type = Type.BOTTOM; } @@ -89,16 +95,21 @@ public class ScopeNode extends Node { @Override public Node idealize() { return null; } public void push() { _idxs.push(new HashMap<>()); _decls.push(new HashMap<>()); } - public void pop() { popN(_idxs.pop().size()); _decls.pop(); } + public void pop() { + popN(_idxs.pop().size()); + _finals.clear(nIns()); + _decls.pop(); + } /** * Create a new variable name in the current scope */ - public Node define( String name, Type declaredType, Node n ) { + public Node define( String name, Type declaredType, Node n, boolean xfinal ) { HashMap<String,Integer> syms = _idxs.lastElement(); _decls.lastElement().put(name,declaredType); if( syms.put(name,nIns()) != null ) return null; // Double define + if( xfinal ) _finals.set(nIns()); // track finals return addDef(n); } @@ -156,6 +167,15 @@ public class ScopeNode extends Node { return null; } + // Return declared type for a variable + public boolean lookupFinal( String name ) { + for( int i=_idxs.size(); i>0; i-- ) { + Integer t = _idxs.get(i-1).get(name); + if( t != null ) return _finals.get(t); + } + throw Utils.TODO(); // Should not reach here + } + /** * Duplicate a ScopeNode; including all levels, up to Nodes. So this is * neither shallow (would dup the Scope but not the internal HashMap diff --git a/chapter16/src/main/java/com/seaofnodes/simple/node/ShlNode.java b/chapter16/src/main/java/com/seaofnodes/simple/node/ShlNode.java index 28e6dd14..6ad81b5c 100644 --- a/chapter16/src/main/java/com/seaofnodes/simple/node/ShlNode.java +++ b/chapter16/src/main/java/com/seaofnodes/simple/node/ShlNode.java @@ -22,7 +22,9 @@ public class ShlNode extends Node { @Override public Type compute() { if (in(1)._type instanceof TypeInteger i0 && - in(2)._type instanceof TypeInteger i1) { + in(2)._type instanceof TypeInteger i1 ) { + if( i0 == TypeInteger.ZERO ) + return TypeInteger.ZERO; if( i0.isConstant() && i1.isConstant() ) return TypeInteger.constant(i0.value()<<i1.value()); } diff --git a/chapter16/src/main/java/com/seaofnodes/simple/node/ShrNode.java b/chapter16/src/main/java/com/seaofnodes/simple/node/ShrNode.java index 16d78ebf..856c2114 100644 --- a/chapter16/src/main/java/com/seaofnodes/simple/node/ShrNode.java +++ b/chapter16/src/main/java/com/seaofnodes/simple/node/ShrNode.java @@ -22,7 +22,9 @@ public class ShrNode extends Node { @Override public Type compute() { if (in(1)._type instanceof TypeInteger i0 && - in(2)._type instanceof TypeInteger i1) { + in(2)._type instanceof TypeInteger i1 ) { + if( i0 == TypeInteger.ZERO ) + return TypeInteger.ZERO; if( i0.isConstant() && i1.isConstant() ) return TypeInteger.constant(i0.value()>>>i1.value()); } diff --git a/chapter16/src/main/java/com/seaofnodes/simple/node/StartNode.java b/chapter16/src/main/java/com/seaofnodes/simple/node/StartNode.java index 5964e19f..bbeb7fa1 100644 --- a/chapter16/src/main/java/com/seaofnodes/simple/node/StartNode.java +++ b/chapter16/src/main/java/com/seaofnodes/simple/node/StartNode.java @@ -43,7 +43,7 @@ public class StartNode extends CFGNode implements MultiNode { String name = Parser.memName(f._alias); Node n = new ProjNode(this, f._alias, name); // No 'compute' until ScopeNode gets typed n._type = args[f._alias]; - scope.define(name, tm_decl, n); + scope.define(name, tm_decl, n, false); } for (int i = 0; i < args.length; i++) if (args[i] == null) diff --git a/chapter16/src/main/java/com/seaofnodes/simple/type/Field.java b/chapter16/src/main/java/com/seaofnodes/simple/type/Field.java index 5e674132..eb49c3af 100644 --- a/chapter16/src/main/java/com/seaofnodes/simple/type/Field.java +++ b/chapter16/src/main/java/com/seaofnodes/simple/type/Field.java @@ -12,52 +12,56 @@ public class Field extends Type { // Field name public final String _fname; - // Unique memory alias - public final int _alias; // Type of the field public final Type _type; + // Unique memory alias, not sensibly part of a "type" but very convenient here. + public final int _alias; + // Field must be written to exactly once, no more, no less + public final boolean _final; - private Field(String fname, int alias, Type type ) { + private Field(String fname, Type type, int alias, boolean xfinal ) { super(TFLD); _fname = fname; - _alias = alias; _type = type; + _alias = alias; + _final = xfinal; } // Make with existing alias - public static Field make( String fname, int alias, Type type ) { - return new Field(fname,alias,type).intern(); + public static Field make( String fname, Type type, int alias, boolean xfinal ) { + return new Field(fname,type,alias,xfinal).intern(); } - public static final Field TEST = make("test",-2,TypeInteger.ZERO); + public static final Field TEST = make("test",TypeInteger.ZERO,-2,false); public static void gather(ArrayList<Type> ts) { ts.add(TEST); } @Override Field xmeet( Type that ) { Field fld = (Field)that; // Invariant assert _fname.equals(fld._fname); assert _alias==fld._alias; - return make(_fname,_alias,_type.meet(fld._type)); + assert _final==fld._final; + return make(_fname,_type.meet(fld._type),_alias,_final); } @Override - public Field dual() { return make(_fname,_alias,_type.dual()); } + public Field dual() { return make(_fname,_type.dual(),_alias,_final); } @Override public Field glb() { - return make(_fname,_alias,_type.glb()); + return make(_fname,_type.glb(),_alias,_final); } // Override in subclasses - int hash() { return _fname.hashCode() ^ _type.hashCode() ^ _alias; } + int hash() { return _fname.hashCode() ^ _type.hashCode() ^ _alias ^ (_final ? 1024 : 0); } boolean eq(Type t) { Field f = (Field)t; - return _fname.equals(f._fname) && _alias==f._alias && _type==f._type; + return _fname.equals(f._fname) && _type==f._type && _alias==f._alias && _final==f._final; } @Override public StringBuilder print( StringBuilder sb ) { - return _type.print(sb.append(_fname).append(":").append(_alias).append(" : ")); + return _type.print(sb.append(_final?"!":"").append(_fname).append(":").append(_alias).append(" : ")); } @Override public String str() { return _fname; } diff --git a/chapter16/src/main/java/com/seaofnodes/simple/type/TypeStruct.java b/chapter16/src/main/java/com/seaofnodes/simple/type/TypeStruct.java index abfb8a67..fbbb814f 100644 --- a/chapter16/src/main/java/com/seaofnodes/simple/type/TypeStruct.java +++ b/chapter16/src/main/java/com/seaofnodes/simple/type/TypeStruct.java @@ -42,15 +42,15 @@ public class TypeStruct extends Type { public static TypeStruct makeAry(TypeInteger len, int lenAlias, Type body, int bodyAlias) { assert !(body instanceof TypeMemPtr tmp && !tmp._nil); return make(body.str() + "[]", - Field.make("#" , lenAlias,len ), - Field.make("[]",bodyAlias,body)); + Field.make("#" ,len , lenAlias,true), + Field.make("[]",body,bodyAlias,true)); } // A pair of self-cyclic types private static final TypeStruct S1F = make("S1"); private static final TypeStruct S2F = make("S2"); - public static final TypeStruct S1 = make("S1", new Field[]{ Field.make("a", -1, TypeInteger.BOT), Field.make("s2",-2,TypeMemPtr.make(S2F,false)) }); - private static final TypeStruct S2 = make("S2", new Field[]{ Field.make("b", -3, TypeFloat .BOT), Field.make("s1",-4,TypeMemPtr.make(S1F,false)) }); + public static final TypeStruct S1 = make("S1", new Field[]{ Field.make("a", TypeInteger.BOT, -1, false), Field.make("s2",TypeMemPtr.make(S2F,false),-2, false) }); + private static final TypeStruct S2 = make("S2", new Field[]{ Field.make("b", TypeFloat .BOT, -3, false), Field.make("s1",TypeMemPtr.make(S1F,false),-4, false) }); private static final TypeStruct ARY = makeAry(TypeInteger.BOT,-1,TypeFloat.BOT,-2); |
