diff options
| author | Cliff Click <cliffc@acm.org> | 2025-04-27 14:51:23 -0700 |
|---|---|---|
| committer | Cliff Click <cliffc@acm.org> | 2025-04-27 14:51:23 -0700 |
| commit | 2f2d18567f31f5d2de2668bf26e9b81809ba2a1d (patch) | |
| tree | c9f68dc834c234add5b5dd3dca884395a61c203f /chapter22/src | |
| parent | 71f9707231aca6edbfd7db3363d39929f265792d (diff) | |
| download | Simple-2f2d18567f31f5d2de2668bf26e9b81809ba2a1d.tar.gz Simple-2f2d18567f31f5d2de2668bf26e9b81809ba2a1d.zip | |
Mid hack
Diffstat (limited to 'chapter22/src')
11 files changed, 272 insertions, 120 deletions
diff --git a/chapter22/src/main/java/com/seaofnodes/simple/Parser.java b/chapter22/src/main/java/com/seaofnodes/simple/Parser.java index d7f5087d..d44ea46e 100644 --- a/chapter22/src/main/java/com/seaofnodes/simple/Parser.java +++ b/chapter22/src/main/java/com/seaofnodes/simple/Parser.java @@ -4,7 +4,7 @@ import com.seaofnodes.simple.codegen.CodeGen; import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.print.GraphVisualizer; import com.seaofnodes.simple.type.*; -import java.text.ParseException; + import java.util.*; /** @@ -81,31 +81,11 @@ public class Parser { ScopeNode _breakScope; // Merge all the while-breaks here FunNode _fun; // Current function being parsed - // Mapping from a type name to a Type. The string name matches + // Mapping from a primitive 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; - - // Mapping from a type name to the constructor for a Type. - public final HashMap<String, StructNode> INITS; - - - public Parser(CodeGen code, TypeInteger arg) { - _code = code; - _scope = new ScopeNode(); - _continueScope = _breakScope = null; - ZERO = con(TypeInteger.ZERO).keep(); - NIL = con(Type.NIL).keep(); - XCTRL= new XCtrlNode().peephole().keep(); - TYPES = defaultTypes(); - INITS = new HashMap<>(); - } - - @Override - public String toString() { return _lexer.toString(); } - - public static HashMap<String, Type> defaultTypes() { - return new HashMap<>() {{ + public static final HashMap<String, Type> PRIMS = + new HashMap<>() {{ put("bool",TypeInteger.U1 ); put("byte",TypeInteger.U8 ); put("f32" ,TypeFloat .F32); @@ -123,8 +103,25 @@ public class Parser { put("val" ,Type.TOP); // Marker type, indicates type inference put("var" ,Type.BOTTOM); // Marker type, indicates type inference }}; + + + // Mapping from a type name to the constructor for a Type. + public final HashMap<String, StructNode> INITS; + + + public Parser(CodeGen code, TypeInteger arg) { + _code = code; + _scope = new ScopeNode(); + _continueScope = _breakScope = null; + ZERO = con(TypeInteger.ZERO).keep(); + NIL = con(Type.NIL).keep(); + XCTRL= new XCtrlNode().peephole().keep(); + INITS = new HashMap<>(); } + @Override + public String toString() { return _lexer.toString(); } + // Debugging utility to find a Node by index public Node f(int nid) { return _code.f(nid); } @@ -132,19 +129,32 @@ public class Parser { private <N extends Node> N ctrl(N n) { return _scope.ctrl(n); } public void parse() { + _scope.define(ScopeNode.CTRL, Type.CONTROL , false, null, null); + _scope.define(ScopeNode.MEM0, TypeMem.BOT , false, null, null); + _scope.define(ScopeNode.ARG0, TypeInteger.BOT, false, null, null); + + ctrl(XCTRL); + _scope.mem(new MemMergeNode(false)); + + // Parse the "sys" import //_parse(com.seaofnodes.simple.sys.sys.SYS); + + _code._stop.popUntil(0); // Delete return from parsing sys import + + // Parse program text _parse(_code._src); + + // Clean up and reset + _scope.kill(); + for( StructNode init : INITS.values() ) + init.unkeep().kill(); + INITS.clear(); + _code._stop.peephole(); } private void _parse(String src) { _lexer = new Lexer(src); _xScopes.push(_scope); - _scope.define(ScopeNode.CTRL, Type.CONTROL , false, null, _lexer); - _scope.define(ScopeNode.MEM0, TypeMem.BOT , false, null, _lexer); - _scope.define(ScopeNode.ARG0, TypeInteger.BOT, false, null, _lexer); - - ctrl(XCTRL); - _scope.mem(new MemMergeNode(false)); // Parse whole program, as-if function header "{ int arg -> body }" parseFunctionBody(_code._main,loc(),"arg"); @@ -172,11 +182,6 @@ public class Parser { // Clean up and reset _xScopes.pop(); - _scope.kill(); - for( StructNode init : INITS.values() ) - init.unkeep().kill(); - INITS.clear(); - stop.peephole(); } /** @@ -212,7 +217,7 @@ public class Parser { // Pre-call the function from Start, with worse-case arguments. This // represents all the future, yet-to-be-parsed functions calls and // external calls. - _scope.push(ScopeNode.Kind.Function); + _scope.push(ScopeNode.Kind.Function,null); ctrl(fun); // Scope control from function // Private mem alias tracking per function MemMergeNode mem = new MemMergeNode(true); @@ -271,7 +276,7 @@ public class Parser { */ private Node parseBlock(ScopeNode.Kind kind) { // Enter a new scope - _scope.push(kind); + _scope.push(kind,null); Node last = ZERO; while (!peek('}') && !_lexer.isEOF()) last = parseStatement(); @@ -290,8 +295,7 @@ public class Parser { * @return a {@link Node} or {@code null} */ private Node parseStatement() { - if( false ) return null; - else if (matchx("return") ) return parseReturn(); + if( matchx("return") ) return parseReturn(); else if (matchx("if") ) return parseIf(); else if (matchx("while") ) return parseWhile(); else if (matchx("for") ) return parseFor(); @@ -339,7 +343,7 @@ public class Parser { // } // } require("("); - _scope.push(ScopeNode.Kind.Block); // Scope for the index variables + _scope.push(ScopeNode.Kind.Block,null); // Scope for the index variables if( !match(";") ) // Can be empty init "for(;test;next) body" parseDeclarationStatement(); // Non-empty init Node rez = parseLooping(true); @@ -406,7 +410,7 @@ public class Parser { // Our current scope is the body Scope ctrl(ifT.unkeep()); // set ctrl token to ifTrue projection _scope.addGuards(ifT,pred.unkeep(),false); // Up-cast predicate - parseStatement(); // Parse loop body + parseStatement().isKill(); // Parse loop body _scope.removeGuards(ifT); // Merge the loop bottom into other continue statements @@ -631,7 +635,7 @@ public class Parser { // outside the constructor need to check the final bit. if( _scope.in(def._idx)._type!=Type.TOP && def._final && // Inside a constructor, final assign is OK, outside nope - !(_scope.inCon() && def._idx >= _scope._lexSize.last()) ) + !(_scope.inConstructor() && def._idx >= _scope._lexSize.last()) ) throw error("Cannot reassign final '"+name+"'"); // Lift expression, based on type @@ -673,9 +677,17 @@ public class Parser { * exprAsgn = var '=' exprAsgn | expr */ private Node parseDeclarationStatement() { + int old = pos(); Type t = type(); if( t == null ) return require(parseAsgn(),";"); + // Ambiguous leading type then doing a type/class static field lookup. + // "T var" - declare a variable of type T + // "T.fld" - lookup field "fld" in type T + if( peek('.') ) { + pos(old); + return require(parseAsgn(),";"); + } // now parse var['=' asgnexpr] in a loop Node n = parseDeclaration(t); @@ -717,14 +729,17 @@ public class Parser { t = expr._type; if( !xfinal ) t = t.glb(); // Widen if not final } - if( t instanceof TypeFunPtr && expr._type instanceof TypeFunPtr tfp && tfp.isConstant() ) - _code.link(tfp)._name = name; + // expr is a constant function + if( t instanceof TypeFunPtr && expr._type instanceof TypeFunPtr tfp && tfp.isConstant() ) { + if( expr instanceof ExternNode ) t = expr._type; // Upgrade declared type to exact function + else _code.link(tfp)._name = name; // Assign debug name to Simple function + } } else { // 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() ) + if( (inferType || (t instanceof TypeNil tn && !tn.nullable() )) && !_scope.inDeclarationQ() ) throw errorSyntax("=expression"); // Initial value for uninitialized struct fields. expr = switch( t ) { @@ -759,44 +774,61 @@ public class Parser { /** * Parse a struct declaration, and return the following statement. - * Only allowed in top level scope. - * Structs cannot be redefined. + * Structs cannot be redefined, but can be nested. * - * @return The statement following the struct + * @return zero */ private Node parseStruct() { if (_xScopes.size() > 1) throw error("struct declarations can only appear in top level scope"); + // "struct" already parsed, so expect the struct name next. String typeName = requireId(); - Type t = TYPES.get(typeName); - if( t!=null && !(t instanceof TypeMemPtr tmp && tmp.isFRef() ) ) - throw error("struct '" + typeName + "' cannot be redefined"); // A Block scope parse, and inspect the scope afterward for fields. - _scope.push(ScopeNode.Kind.Constructor); + _scope.push(ScopeNode.Kind.DeclarationQ,typeName); require("{"); while (!peek('}') && !_lexer.isEOF()) - parseStatement(); + parseStatement().isKill(); // Grab the declarations and build fields and a Struct int lexlen = _scope._lexSize.last(); int varlen = _scope._vars._len; - StructNode s = new StructNode(); - Ary<Field> fs = new Ary<>(Field.class); + StructNode inst = new StructNode(); + StructNode clz = new StructNode(); + Ary<Field> fs = new Ary<>(Field.class); // Struct fields + Ary<Field> cs = new Ary<>(Field.class); // Class fields for( int i=lexlen; i<varlen; i++ ) { - Var v = _scope._vars.at(i); - if( !v.isFRef() ) { // Promote to outer scope, not defined here - s.addDef(_scope.in(i)); - fs.push( Field.make( v._name, v.type(), _code.getALIAS(), v._final ) ); - } + Var v = _scope.var(i); + if( v.isFRef() ) continue; // Promote to outer scope, not defined here + var flds = v._final && v.type().isConstant() ? cs : fs; + var stru = v._final && v.type().isConstant() ? clz : inst; + // Declare field, gather field init + flds.push(Field.make(v._name,v.type(),_code.getALIAS(),v._final)); + stru.addDef(_scope.in(i)); } TypeStruct ts = s._ts = TypeStruct.make(typeName, fs.asAry()); - TYPES.put(typeName, TypeMemPtr.make(ts)); + //PRIMS.put(typeName, TypeMemPtr.make(ts)); INITS.put(typeName,s.peephole().keep()); + + //String clzName = typeName + ".clz"; // Class name + //PRIMS.put(clzName, TypeMemPtr.make(TypeStruct.make(clzName, cs.asAry()))); // Done with struct/block scope require("}"); require(";"); _scope.pop(); - return ZERO; + // Make a pointer to the class fields + + // ISSUE: LOOKUP EXPECTS TO FIND INSTANCE FIELDS FOR 'NEW' OR TYPE DECLS: + // Vector2D v = ...; + // Type for 'v' includes fields 'x,y'. + // Whereas: Vector2D.PI lookups up via the class struct. + // So e.g. add a hidden " decl" field that gets the instance fields. + cs.push(Field.make(" decl",TypeMemPtr.make(ts),-1,true)); + + TypeMemPtr tclz = TypeMemPtr.make(TypeStruct.make(typeName, cs.asAry())); + ConstantNode nclz = con(tclz); + // Define the constant clazz fields + _scope.define(typeName,tclz, true, nclz, true, loc()); + return nclz; } @@ -815,13 +847,15 @@ public class Parser { String tname = _lexer.matchId(); if( tname==null ) return null; - // Convert the type name to a type. - Type t0 = TYPES.get(tname); + // Convert the type name to a type, either user instance type or a primitive. + Type t0 = _scope.lookupInstance(tname); + if( t0 == null ) t0 = PRIMS.get(tname); // No new types as keywords if( t0 == null && KEYWORDS.contains(tname) ) return posT(old1); if( t0 == Type.BOTTOM || t0 == Type.TOP ) return t0; // var/val type inference - Type t1 = t0 == null ? TypeMemPtr.make(TypeStruct.makeFRef(tname)) :t0; // Null: assume a forward ref type + // Still no type found? Assume forward reference + Type t1 = t0 == null ? TypeMemPtr.make(TypeStruct.makeFRef(tname)) : t0; // Null: assume a forward ref type // Nest arrays and '?' as needed Type t2 = t1; while( true ) { @@ -843,13 +877,12 @@ public class Parser { int old2 = pos(); match("!"); String id = _lexer.matchId(); - if( !(peek(',') || peek(';') || match("->")) ) - return posT(old1); - pos(old2); // Reset lexer to reparse - if( id==null ) + if( !(peek(',') || peek(';') || match("->")) || id==null ) return posT(old1); // Reset lexer to reparse + pos(old2); // Reset lexer to reparse // Yes a forward ref, so declare it - TYPES.put(tname,t1); + _scope.define(tname, t1, true, XCTRL, true, null); + // Return the (array, final) type return t2; } @@ -858,14 +891,15 @@ public class Parser { if( t instanceof TypeMemPtr tmp && tmp.notNull() ) throw error("Arrays of reference types must always be nullable"); String tname = "["+t.str()+"]"; - Type ta = TYPES.get(tname); + Type ta = PRIMS.get(tname); if( ta != null ) return (TypeMemPtr)ta; // Need make an array type. TypeStruct ts = TypeStruct.makeAry(TypeInteger.U32,_code.getALIAS(),t,_code.getALIAS()); assert ts.str().equals(tname); TypeMemPtr tary = TypeMemPtr.make(ts); - TYPES.put(tname,tary); - return tary; + //PRIMS.put(tname,tary); + //return tary; + throw Utils.TODO(); } // A function type is `{ type... -> type }` or `{ type }`. @@ -1183,7 +1217,7 @@ public class Parser { if( hasConstructor ) { idx = _scope.nIns(); // Push a scope, and pre-assign all struct fields. - _scope.push(ScopeNode.Kind.Block); + _scope.push(ScopeNode.Kind.Block,null); Lexer loc = loc(); for( int i=0; i<fs.length; i++ ) _scope.define(fs[i]._fname, fs[i]._type, fs[i]._final, s.in(i)._type==Type.TOP ? con(Type.BOTTOM) : s.in(i), loc); @@ -1282,18 +1316,24 @@ public class Parser { throw error( "Expected "+(name=="#" || name=="[]" ? "array" : "reference")+" but found " + expr._type.str() ); } - // Sanity check field name for existing - TypeMemPtr tmp = (TypeMemPtr)TYPES.get(ptr._obj._name); - if( tmp == null ) throw error("Accessing unknown field '" + name + "' from '" + ptr + "'"); - TypeStruct base = tmp._obj; + // Find the field from the Type. Lookup in the base object field names. + TypeStruct base = ptr._obj; + if( base.isFRef() ) { + //ptr = _scope.lookupInstance(base._name); + //base = ptr._obj; + throw Utils.TODO(); + } int fidx = base.find(name); - if( fidx == -1 ) throw error("Accessing unknown field '" + name + "' from '" + ptr.str() + "'"); + if( fidx == -1 ) { // No such field! + throw error("Accessing unknown field '" + name + "' from '" + ptr.str() + "'"); + // TODO: ch22 allow class field lookups + } // Get field type and layout offset from base type and field index fidx Field f = base._fields[fidx]; // Field from field index Type tf = f._type; if( tf instanceof TypeMemPtr ftmp && ftmp.isFRef() ) - tf = ftmp.makeFrom(((TypeMemPtr)(TYPES.get(ftmp._obj._name)))._obj); + tf = ftmp.makeFrom(((TypeMemPtr)(PRIMS.get(ftmp._obj._name)))._obj); // Field offset; fixed for structs, computed for arrays Node off = (name.equals("[]") // If field is an array body @@ -1453,8 +1493,9 @@ public class Parser { } // External linked constant private Node externDecl( String ex, Type t ) { - //return new ExternNode(t,ex).peephole(); - throw Utils.TODO(); + if( t instanceof TypeFunPtr tfp ) // Generic TFP from type parse + t = _code.makeFun(tfp._sig,tfp._ret); // Get a FIDX, becomes a constant + return new ExternNode(t,ex).peephole(); } /** diff --git a/chapter22/src/main/java/com/seaofnodes/simple/Var.java b/chapter22/src/main/java/com/seaofnodes/simple/Var.java index e71d7ef2..154b99f4 100644 --- a/chapter22/src/main/java/com/seaofnodes/simple/Var.java +++ b/chapter22/src/main/java/com/seaofnodes/simple/Var.java @@ -9,28 +9,33 @@ import com.seaofnodes.simple.type.*; public class Var { public final String _name; // Declared name + // These fields are not final for forward reference late updates or + // promotions. public int _idx; // index in containing scope private Type _type; // Declared type public boolean _final; // Final field - public Parser.Lexer _loc; // Source location public boolean _fref; // Forward ref + public final boolean _clz; // Is a Simple clazz + public Parser.Lexer _loc; // Source location public Var(int idx, String name, Type type, boolean xfinal, Parser.Lexer loc ) { - this(idx,name,type,xfinal,loc,false); + this(idx,name,type,xfinal,loc,false,false); } - public Var(int idx, String name, Type type, boolean xfinal, Parser.Lexer loc, boolean fref) { + public Var(int idx, String name, Type type, boolean xfinal, Parser.Lexer loc, boolean clz, boolean fref) { _idx = idx; _name = name; _type = type; _final = xfinal; - _loc = loc; _fref = fref; + _clz = clz; + _loc = loc; } public Type type() { if( !_type.isFRef() ) return _type; // Update self to no longer use the forward ref type - Type def = Parser.TYPES.get(((TypeMemPtr)_type)._obj._name); - return (_type=_type.meet(def)); + //Type def = Parser.TYPES.get(((TypeMemPtr)_type)._obj._name); + //return (_type=_type.meet(def)); + throw Utils.TODO(); } public Type lazyGLB() { Type t = type(); @@ -45,11 +50,11 @@ public class Var { assert isFRef() && xfinal; _type = type; _final = true; - _loc = loc; _fref = false; + _loc = loc; } @Override public String toString() { - return _type.toString()+(_final ? " ": " !")+_name; + return _type.toString()+(_final ? " ": " !")+(_clz?"@":"")+_name; } } diff --git a/chapter22/src/main/java/com/seaofnodes/simple/node/ExternNode.java b/chapter22/src/main/java/com/seaofnodes/simple/node/ExternNode.java new file mode 100644 index 00000000..3c3701c7 --- /dev/null +++ b/chapter22/src/main/java/com/seaofnodes/simple/node/ExternNode.java @@ -0,0 +1,26 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeFunPtr; +import java.util.BitSet; + +/** + A constant with external linkage. + */ + +public class ExternNode extends ConstantNode { + public final String _extern; + public ExternNode(Type t, String ex) { super(t); _extern = ex; } + + @Override public String label() { return "#"+_con+":"+_extern; } + @Override public String glabel() { return _con.gprint(new SB().p("#")).p(":").p(_extern).toString(); } + @Override public String uniqueName() { return "Extern_" + _nid; } + + @Override public boolean eq(Node n) { return this==n; } + + @Override int hash() { return _con.hashCode(); } + +} diff --git a/chapter22/src/main/java/com/seaofnodes/simple/node/Node.java b/chapter22/src/main/java/com/seaofnodes/simple/node/Node.java index c0c2e606..18a8bc14 100644 --- a/chapter22/src/main/java/com/seaofnodes/simple/node/Node.java +++ b/chapter22/src/main/java/com/seaofnodes/simple/node/Node.java @@ -239,7 +239,7 @@ public abstract class Node implements Cloneable { // Shortcut for "popping" until n nodes. A "pop" is basically a // setDef(last,null) followed by lowering the nIns() count. - void popUntil(int n) { + public void popUntil(int n) { unlock(); while( nIns() > n ) { Node old_def = _inputs.pop(); @@ -255,9 +255,9 @@ public abstract class Node implements Cloneable { * code elimination. */ public void kill( ) { + assert isUnused(); // Has no uses, so it is dead unlock(); moveDepsToWorklist(); - assert isUnused(); // Has no uses, so it is dead _type=null; // Flag as dead while( nIns()>0 ) { // Set all inputs to null, recursively killing unused Nodes Node old_def = _inputs.removeLast(); @@ -295,6 +295,10 @@ public abstract class Node implements Cloneable { if( unkeep().isUnused() ) kill(); } + public void isKill() { + if( isUnused() ) + kill(); + } // Replace self with nnn in the graph, making 'this' go dead diff --git a/chapter22/src/main/java/com/seaofnodes/simple/node/ScopeNode.java b/chapter22/src/main/java/com/seaofnodes/simple/node/ScopeNode.java index 38a5d968..eda29915 100644 --- a/chapter22/src/main/java/com/seaofnodes/simple/node/ScopeNode.java +++ b/chapter22/src/main/java/com/seaofnodes/simple/node/ScopeNode.java @@ -25,9 +25,15 @@ public class ScopeNode extends MemMergeNode { // Size of each nested lexical scope public final Ary<Integer> _lexSize; - // Lexical scope is one of normal Block, constructor or function - public enum Kind { Block, Constructor, Function }; + // Lexical scope is typed: + // - Block scope - basic block scoping + // - Constructor - struct type is defined, and here we give values to all fields + // - Declaration - struct is declared, final constant fields defined + // - Function - out-of-scope lookups restricted to final constants + public enum Kind { Block, Constructor, DeclarationQ, Function }; public final Ary<Kind> _kinds; + // Each Constructor scope is named, allowing for nested Type names + public final Ary<String> _tnamesQ; // Extra guards; tested predicates and casted results private final Ary<Node> _guards; @@ -38,6 +44,7 @@ public class ScopeNode extends MemMergeNode { _vars = new Ary<>(Var .class); _lexSize= new Ary<>(Integer.class); _kinds = new Ary<>(Kind .class); + _tnamesQ = new Ary<>(String .class); _guards = new Ary<>(Node .class); } @@ -49,7 +56,7 @@ public class ScopeNode extends MemMergeNode { int j=1; for( int i=0; i<nIns(); i++ ) { if( j < _lexSize._len && i == _lexSize.at(j) ) { sb.append("| "); j++; } - Var v = _vars.get(i); + Var v = _vars.at(i); sb.append(v.type().print(new SB())); sb.append(" "); if( v._final ) sb.append("!"); @@ -71,6 +78,7 @@ public class ScopeNode extends MemMergeNode { public Node ctrl() { return in(0); } public MemMergeNode mem() { return (MemMergeNode)in(1); } + public Var var(int i) { return _vars.at(i); } /** * The ctrl of a ScopeNode is always bound to the currently active @@ -85,20 +93,23 @@ public class ScopeNode extends MemMergeNode { public <N extends Node> N ctrl(N n) { return setDef(0,n); } public Node mem(Node n) { return setDef(1,n); } - public void push(Kind kind) { - assert _lexSize._len==_kinds._len; + public void push(Kind kind, String tnameQ) { + assert _lexSize._len==_kinds._len && _lexSize._len==_tnamesQ._len; + assert !(kind==Kind.DeclarationQ && tnameQ==null); _lexSize.push(_vars.size()); _kinds .push(kind); + _tnamesQ .push(tnameQ); } // Pop a lexical scope public void pop() { - assert _lexSize._len==_kinds._len; - promote(); + assert _lexSize._len==_kinds._len && _lexSize._len==_tnamesQ._len; + promote(); // Promote forward references to the next outer scope int n = _lexSize.pop(); _kinds.pop(); - popUntil(n); - _vars.setLen(n); + _tnamesQ.pop(); + popUntil(n); // Pop off inputs going out of scope + _vars.setLen(n); // Pop off variables going out of scope } @@ -107,7 +118,7 @@ public class ScopeNode extends MemMergeNode { public void promote() { int n = _lexSize.last(); for( int i=n; i<nIns(); i++ ) { - Var v = _vars.at(i); + Var v = var(i); if( !v.isFRef() ) continue; if( _lexSize._len==1 ) throw Parser.error("Undefined name '" + v._name + "'",v._loc); @@ -120,7 +131,15 @@ public class ScopeNode extends MemMergeNode { } - public boolean inCon() { return _kinds.last() == Kind.Constructor; } + public boolean inConstructor() { return _kinds.last() == Kind.Constructor; } + public boolean inDeclarationQ() { return _kinds.last() == Kind.DeclarationQ; } + // Nearest enclosing declaration scope name + public String declNameQ() { + for( int i=_lexSize._len-1; i>=0; i-- ) + if( _kinds.at(i)==Kind.DeclarationQ ) + return _tnamesQ.at(i); + return null; + } // Is v outside any current function scope? public boolean outOfFunction( Var v ) { @@ -134,9 +153,9 @@ public class ScopeNode extends MemMergeNode { // Find name in reverse, return an index into _vars or -1. Linear scan // instead of hashtable, but probably doesn't matter until the scan // typically hits many dozens of variables. - int find( String name ) { + public int find( String name, boolean clz ) { for( int i=_vars.size()-1; i>=0; i-- ) - if( _vars.at(i)._name.equals(name) ) + if( var(i)._name.equals(name) && var(i)._clz==clz ) return i; return -1; } @@ -145,10 +164,13 @@ public class ScopeNode extends MemMergeNode { * Create a new variable name in the current scope */ public boolean define( String name, Type declaredType, boolean xfinal, Node init, Parser.Lexer loc ) { + return define(name,declaredType,xfinal,init,false,loc); + } + public boolean define( String name, Type declaredType, boolean xfinal, Node init, boolean clz, Parser.Lexer loc ) { assert _lexSize.isEmpty() || name.charAt(0)!='$' ; // Later scopes do not define memory if( _lexSize._len > 0 ) for( int i=_vars.size()-1; i>=_lexSize.last(); i-- ) { - Var n = _vars.at(i); + Var n = var(i); if( n._name.equals(name) ) { if( !n.isFRef() ) return false; // Double define FRefNode fref = (FRefNode)in(n._idx); // Get forward ref @@ -157,7 +179,7 @@ public class ScopeNode extends MemMergeNode { setDef(n._idx,fref.addDef(init)); // Set FRef to defined; tell parser also } } - Var v = new Var(nIns(),name,declaredType,xfinal,loc,init==Parser.XCTRL); + Var v = new Var(nIns(),name,declaredType,xfinal,loc,clz,init==Parser.XCTRL); _vars.add(v); // Creating a forward reference if( init==Parser.XCTRL ) @@ -179,21 +201,37 @@ public class ScopeNode extends MemMergeNode { * @return null if not found, or the implementing Node */ public Var lookup( String name ) { - int idx = find(name); + int idx = find(name,false); + // -1 is missed in all scopes, not found + return idx == -1 ? null : update(var(idx),null); + } + + /** + * Lookup a type name and return the instance TypeMemPtr + * + * @param name of Type + * @return null if not found, or *instance* TypeMemPtr + */ + public TypeMemPtr lookupInstance( String name ) { + int idx = find(name,true); // -1 is missed in all scopes, not found - return idx == -1 ? null : update(_vars.at(idx),null); + if( idx == -1 ) return null; + // Class types are always TMPs + TypeMemPtr clz = (TypeMemPtr)var(idx).type(); + // Instance types are always TMPs + return (TypeMemPtr)(clz._obj.field(" decl")._type); } /** - * If the name is present in any scope, then redefine else null + * Redefine an existing name * * @param name Name being redefined * @param n The node to bind to the name */ public void update( String name, Node n ) { - int idx = find(name); + int idx = find(name,false); assert idx>=0; - update(_vars.at(idx),n); + update(var(idx),n); } public Var update( Var v, Node st ) { @@ -238,6 +276,7 @@ public class ScopeNode extends MemMergeNode { dup._vars .addAll(_vars ); dup._lexSize.addAll(_lexSize); dup._kinds .addAll(_kinds ); + dup._tnamesQ .addAll(_tnamesQ ); dup._guards .addAll(_guards ); // The dup'd guards all need dup'd keepers, to keep proper accounting // when later removing all guards @@ -260,7 +299,7 @@ public class ScopeNode extends MemMergeNode { for( int i=2; i<nIns(); i++ ) // For lazy phis on loops we use a sentinel // that will trigger phi creation on update - dup.addDef(loop && !_vars.at(i)._final ? this : in(i)); + dup.addDef(loop && !var(i)._final ? this : in(i)); return dup; } @@ -286,7 +325,7 @@ public class ScopeNode extends MemMergeNode { if( in(i) != that.in(i) ) { // No need for redundant Phis // If we are in lazy phi mode we need to a lookup // by name as it will trigger a phi creation - Var v = _vars.at(i); + Var v = var(i); Node lhs = this.in(this.update(v,null)); Node rhs = that.in(that.update(v,null)); setDef(i, new PhiNode(v._name, v.type(), r, lhs, rhs).peephole()); @@ -297,7 +336,7 @@ public class ScopeNode extends MemMergeNode { // Forward refs are copied to the other side, "as if" they were there all along. public void balanceIf( ScopeNode scope ) { for( int i = nIns(); i < scope.nIns(); i++ ) { - Var n = scope._vars.at(i); + Var n = scope.var(i); if( n.isFRef() ) { // RHS has forward refs _vars.add(n); // Copy to LHS addDef(scope.in(i)); @@ -329,8 +368,8 @@ public class ScopeNode extends MemMergeNode { // Fill in the backedge of any inserted Phis void _endLoop( ScopeNode scope, Node back, Node exit ) { for( int i=2; i<nIns(); i++ ) { - if( _vars.at(i)._final ) continue; // Final vars did not get modified in the loop - if( _vars.at(i).type().isHighOrConst() ) continue; // Cannot lift higher than a constant, so no Phi + if( var(i)._final ) continue; // Final vars did not get modified in the loop + if( var(i).type().isHighOrConst() ) continue; // Cannot lift higher than a constant, so no Phi if( back.in(i) != scope ) { PhiNode phi = (PhiNode)in(i); assert phi.region()==scope.ctrl() && phi.in(2)==null; diff --git a/chapter22/src/main/java/com/seaofnodes/simple/sys/sys.java b/chapter22/src/main/java/com/seaofnodes/simple/sys/sys.java new file mode 100644 index 00000000..399e7cb7 --- /dev/null +++ b/chapter22/src/main/java/com/seaofnodes/simple/sys/sys.java @@ -0,0 +1,25 @@ +package com.seaofnodes.simple.sys; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + + +/** Default included namespaces for Simple. + + You can think of this as {@code #include <stdio.h>} for C or {@code import java.lang.*} for Java. + */ +public abstract class sys { + + public static final String SYS; + + static { + try { + SYS = Files.readString(Path.of("src/main/java/com/seaofnodes/simple/sys/sys.smp")); + } + catch( Exception e ) { + throw new RuntimeException(e); + } + } + +} diff --git a/chapter22/src/main/java/com/seaofnodes/simple/sys/sys.smp b/chapter22/src/main/java/com/seaofnodes/simple/sys/sys.smp new file mode 100644 index 00000000..8c470716 --- /dev/null +++ b/chapter22/src/main/java/com/seaofnodes/simple/sys/sys.smp @@ -0,0 +1,10 @@ +// -*- mode: java; -*- + +// top-level default import +struct sys { + // libc bindings + struct libc { + /** https://www.man7.org/linux/man-pages/man2/write.2.html */ + { i32 u8[] i32 -> i32 } write = "C"; + }; +}; diff --git a/chapter22/src/test/java/com/seaofnodes/simple/Chapter22Test.java b/chapter22/src/test/java/com/seaofnodes/simple/Chapter22Test.java index 106932f5..dccf00bc 100644 --- a/chapter22/src/test/java/com/seaofnodes/simple/Chapter22Test.java +++ b/chapter22/src/test/java/com/seaofnodes/simple/Chapter22Test.java @@ -28,7 +28,6 @@ public class Chapter22Test { assertEquals(stop, code._stop.toString()); } - @Test public void testCoRecur() { String src = """ diff --git a/chapter22/src/test/java/com/seaofnodes/simple/Eval2.java b/chapter22/src/test/java/com/seaofnodes/simple/Eval2.java index ecea97dd..38da6898 100644 --- a/chapter22/src/test/java/com/seaofnodes/simple/Eval2.java +++ b/chapter22/src/test/java/com/seaofnodes/simple/Eval2.java @@ -407,7 +407,8 @@ public abstract class Eval2 { // Since never can close the type cycle, without cyclic types, have // to handle FRefs even here if( tmp.isFRef() ) - tmp = tmp.makeFrom(((TypeMemPtr)Parser.TYPES.get(tmp._obj._name))._obj); + //tmp = tmp.makeFrom(((TypeMemPtr)Parser.TYPES.get(tmp._obj._name))._obj); + throw Utils.TODO(); Object[] xs = (Object[])x; // Array of fields if( tmp._obj.isAry() ) { diff --git a/chapter22/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java b/chapter22/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java index f119fa1a..c9f7d801 100644 --- a/chapter22/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java +++ b/chapter22/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java @@ -97,7 +97,7 @@ public class ScriptGenerator { private static final List<TypeInt> INTTYPES = new ArrayList<>(); static { - for( var e : Parser.defaultTypes().entrySet() ) { + for( var e : Parser.PRIMS.entrySet() ) { if( e.getValue() instanceof TypeInteger ) { INTTYPES.add(new TypeInt(e.getKey())); } diff --git a/chapter22/src/test/java/com/seaofnodes/simple/progs/helloWorld.smp b/chapter22/src/test/java/com/seaofnodes/simple/progs/helloWorld.smp new file mode 100644 index 00000000..0234aa73 --- /dev/null +++ b/chapter22/src/test/java/com/seaofnodes/simple/progs/helloWorld.smp @@ -0,0 +1,2 @@ +// -*- mode: java; -*- +sys.libc.write(1,"Hello, World!\n",14); |
