From 2f2d18567f31f5d2de2668bf26e9b81809ba2a1d Mon Sep 17 00:00:00 2001 From: Cliff Click Date: Sun, 27 Apr 2025 14:51:23 -0700 Subject: Mid hack --- .../main/java/com/seaofnodes/simple/Parser.java | 205 ++++++++++++--------- .../src/main/java/com/seaofnodes/simple/Var.java | 21 ++- .../com/seaofnodes/simple/node/ExternNode.java | 26 +++ .../main/java/com/seaofnodes/simple/node/Node.java | 8 +- .../java/com/seaofnodes/simple/node/ScopeNode.java | 89 ++++++--- .../main/java/com/seaofnodes/simple/sys/sys.java | 25 +++ .../main/java/com/seaofnodes/simple/sys/sys.smp | 10 + .../java/com/seaofnodes/simple/Chapter22Test.java | 1 - .../src/test/java/com/seaofnodes/simple/Eval2.java | 3 +- .../seaofnodes/simple/fuzzer/ScriptGenerator.java | 2 +- .../com/seaofnodes/simple/progs/helloWorld.smp | 2 + 11 files changed, 272 insertions(+), 120 deletions(-) create mode 100644 chapter22/src/main/java/com/seaofnodes/simple/node/ExternNode.java create mode 100644 chapter22/src/main/java/com/seaofnodes/simple/sys/sys.java create mode 100644 chapter22/src/main/java/com/seaofnodes/simple/sys/sys.smp create mode 100644 chapter22/src/test/java/com/seaofnodes/simple/progs/helloWorld.smp 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 TYPES; - - // Mapping from a type name to the constructor for a Type. - public final HashMap 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 defaultTypes() { - return new HashMap<>() {{ + public static final HashMap 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 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 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 fs = new Ary<>(Field.class); + StructNode inst = new StructNode(); + StructNode clz = new StructNode(); + Ary fs = new Ary<>(Field.class); // Struct fields + Ary cs = new Ary<>(Field.class); // Class fields for( int i=lexlen; i