From 3b2e5240eae6a0b7f29a1eb491591dcbd0e9c2d5 Mon Sep 17 00:00:00 2001 From: Cliff Click Date: Mon, 14 Oct 2024 16:47:15 -0700 Subject: Constructors Missing arrays yet. Allow constructor syntax in 2 parts; the type description and at the New. must-init fields (final and not-null) have to be initialized in at least one of the two places; finals must be initialized exactly once. Any code is allowed in the constructors. --- .../main/java/com/seaofnodes/simple/Parser.java | 209 ++++++++++++--------- .../java/com/seaofnodes/simple/node/LoadNode.java | 7 +- .../java/com/seaofnodes/simple/node/NewNode.java | 33 +++- .../java/com/seaofnodes/simple/node/ScopeNode.java | 10 +- .../com/seaofnodes/simple/node/StructNode.java | 53 ++++++ .../java/com/seaofnodes/simple/type/Field.java | 3 + 6 files changed, 215 insertions(+), 100 deletions(-) create mode 100644 chapter16/src/main/java/com/seaofnodes/simple/node/StructNode.java (limited to 'chapter16/src/main/java') diff --git a/chapter16/src/main/java/com/seaofnodes/simple/Parser.java b/chapter16/src/main/java/com/seaofnodes/simple/Parser.java index bdd14de6..572c170a 100644 --- a/chapter16/src/main/java/com/seaofnodes/simple/Parser.java +++ b/chapter16/src/main/java/com/seaofnodes/simple/Parser.java @@ -7,7 +7,7 @@ import java.util.*; /** * The Parser converts a Simple source program to the Sea of Nodes intermediate - * representation directly in one pass. There is no intermediate Abstract + * representation directly in one pass. There is no intermediate Abstract * Syntax Tree structure. *

* This is a simple recursive descent parser. All lexical analysis is done here as well. @@ -97,6 +97,10 @@ public class Parser { // have C-style '*ptr' references. public static HashMap TYPES = new HashMap<>(); + // Mapping from a type name to the constructor for a Type. + public static HashMap INITS = new HashMap<>(); + + public Parser(String source, TypeInteger arg) { Node.reset(); IterPeeps.reset(); @@ -115,6 +119,7 @@ public class Parser { TYPES.put("u16" ,TypeInteger.U16); TYPES.put("u32" ,TypeInteger.U32); TYPES.put("u8" ,TypeInteger.U8 ); + INITS.clear(); SCHEDULED = false; _lexer = new Lexer(source); _scope = new ScopeNode(); @@ -205,65 +210,6 @@ public class Parser { // to ambiguity else return parseExpressionStatement(); } - - /** - * Parse a struct field. - *

-     *     type IDENTIFIER ;
-     * 
- */ - //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. - * Only allowed in top level scope. - * Structs cannot be redefined. - * - * @return The statement following the struct - */ - private Node parseStruct() { - if (_xScopes.size() > 1) throw errorSyntax("struct declarations can only appear in top level scope"); - String typeName = requireId(); - Type t = TYPES.get(typeName); - if( t!=null && !(t instanceof TypeMemPtr tmp && tmp._obj._fields==null) ) - throw errorSyntax("struct '" + typeName + "' cannot be redefined"); - // A Block scope parse, and inspect the scope afterwards for fields. - _scope.push(); - require("{"); - while (!peek('}') && !_lexer.isEOF()) - parseStatement(); - require("}"); - // Grab the declarations and build fields - HashMap decls = _scope._decls.lastElement(); - HashMap 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, fs); - TYPES.put(typeName, TypeMemPtr.make(ts)); - START.addMemProj(ts, _scope); // Insert memory edges - require(";"); - return null; - } - /** * Parses a while statement * @@ -452,10 +398,17 @@ public class Parser { int old = _lexer._position; String name = requireId(); Node expr; - if( match(";") || peek(',') ) { + if( peek(';') || peek(',') ) { // Bare "name" is not allowed if( t==null ) throw errorSyntax("expression"); - expr = new ConstantNode(t.makeInit()).peephole(); + // Does this declaration need an initializer and not getting one? + Type init = xfinal || t instanceof TypeMemPtr tmp && !tmp._nil + // Must be initialized in the "new" constructor + ? Type.TOP + // Else takes the default + : t.makeInit(); + expr = new ConstantNode(init).peephole(); + } else if( !match("=") ) { // Something else _lexer._position = old; return null; @@ -465,9 +418,13 @@ public class Parser { // Defining a new variable vs updating an old one if( !isDecl ) { // Assigning over an existing name // Lookup - if( _scope.lookup(name)==null ) + Node def = _scope.lookup(name); + if( def==null ) throw error("Undefined name '" + name + "'"); - if( _scope.lookupFinal(name) ) + // TOP fields are for late-initialized fields; these have never + // been written to, and this must be the final write. Other writes + // need to check the final bit. + if( def._type!=Type.TOP && _scope.lookupFinal(name) ) throw error("Cannot reassign final '"+name+"'"); t = _scope.lookupDeclaredType(name); } @@ -485,9 +442,8 @@ public class Parser { if( !e.isa(t) ) throw error("Type " + e.str() + " is not of declared type " + t.str()); - if( isDecl ) { - if( _scope.define(name,t,expr,xfinal) == null ) + if( !_scope.define(name,t,expr,xfinal) ) throw error("Redefining name '" + name + "'"); } else _scope.update(name,expr); @@ -530,6 +486,60 @@ public class Parser { return require(n,";"); } + + /** + * Parse a struct declaration, and return the following statement. + * Only allowed in top level scope. + * Structs cannot be redefined. + * + * @return The statement following the struct + */ + private Node parseStruct() { + if (_xScopes.size() > 1) throw errorSyntax("struct declarations can only appear in top level scope"); + String typeName = requireId(); + Type t = TYPES.get(typeName); + if( t!=null && !(t instanceof TypeMemPtr tmp && tmp._obj._fields==null) ) + throw errorSyntax("struct '" + typeName + "' cannot be redefined"); + + // A Block scope parse, and inspect the scope afterwards for fields. + _scope.push(); + require("{"); + while (!peek('}') && !_lexer.isEOF()) + parseStatement(); + + // Grab the declarations and build fields and a Struct + HashMap decls = _scope._decls.lastElement(); + HashMap idxs = _scope._idxs.lastElement(); + BitSet finals = _scope._finals; + + StructNode s = new StructNode().keep(); + 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); + s.addDef(_scope.in(idx)); + } + // Done with struct/block scope + require("}"); + _scope.pop(); + + // Build and install the TypeStruct + TypeStruct ts = TypeStruct.make(typeName, fs); + TYPES.put(typeName, TypeMemPtr.make(ts)); + require(";"); + // Save initial constructor + s._ts = ts; + INITS.put(typeName,(StructNode)s.peephole()); + + // Insert memory edges + START.addMemProj(ts, _scope); + + return null; + } + + // Parse and return a type or null. Valid types always are followed by an // 'id' which the caller must parse. This lets us distinguish forward ref // types (which ARE valid here) from local vars in an (optional) forward @@ -756,12 +766,32 @@ public class Parser { return newArray(tmp._obj,len); } if( t instanceof TypeMemPtr tmp ) { - TypeStruct obj = tmp._obj; - if( obj._fields==null ) - throw error("Unknown struct type '" + obj._name + "'"); - if( obj.isAry() ) - throw Utils.TODO(); // Missing array length - return newStruct(obj,con(obj.offset(obj._fields.length))); + StructNode s = INITS.get(tmp._obj._name); + if( s==null ) throw error("Unknown struct type '" + tmp._obj._name + "'"); + Field[] fs = s._ts._fields; + // if the object is fully initialized, we can skip a block here. + // Check for constructor block: + boolean hasConstructor = match("{"); + Node init=s; int idx=0; + if( hasConstructor ) { + init = _scope; + idx = _scope.nIns(); + // Push a scope, and pre-assign all struct fields. + _scope.push(); + for( int i=0; i syms = _idxs.lastElement(); _decls.lastElement().put(name,declaredType); if( syms.put(name,nIns()) != null ) - return null; // Double define + return false; // Double define if( xfinal ) _finals.set(nIns()); // track finals - return addDef(n); + addDef(n); + return true; } /** diff --git a/chapter16/src/main/java/com/seaofnodes/simple/node/StructNode.java b/chapter16/src/main/java/com/seaofnodes/simple/node/StructNode.java new file mode 100644 index 00000000..69e9f444 --- /dev/null +++ b/chapter16/src/main/java/com/seaofnodes/simple/node/StructNode.java @@ -0,0 +1,53 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.type.*; +import java.util.BitSet; + +/** + * Build a compound object + */ +public class StructNode extends Node { + + public TypeStruct _ts; + + @Override public String label() { return _ts==null ? "STRUCT?" : _ts.str(); } + + @Override + StringBuilder _print1(StringBuilder sb, BitSet visited) { + if( _ts==null ) return sb.append("STRUCT?"); + sb.append(_ts._name).append(" {"); + for( int i=0; i ts) { ts.add(TEST); } -- cgit v1.2.3