From 664761624f4b0e3d6d1593b99e576854beec5949 Mon Sep 17 00:00:00 2001 From: Cliff Click Date: Tue, 6 May 2025 13:44:10 -0700 Subject: Chapter 23: Methods and Revisiting Types --- src/main/java/com/seaofnodes/simple/Parser.java | 420 ++++++++++++++++-------- 1 file changed, 279 insertions(+), 141 deletions(-) (limited to 'src/main/java/com/seaofnodes/simple/Parser.java') diff --git a/src/main/java/com/seaofnodes/simple/Parser.java b/src/main/java/com/seaofnodes/simple/Parser.java index 23e9ddee..1db873e0 100644 --- a/src/main/java/com/seaofnodes/simple/Parser.java +++ b/src/main/java/com/seaofnodes/simple/Parser.java @@ -4,7 +4,9 @@ import com.seaofnodes.simple.codegen.CodeGen; import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.print.GraphVisualizer; import com.seaofnodes.simple.type.*; - +import com.seaofnodes.simple.util.Ary; +import com.seaofnodes.simple.util.Utils; +import com.seaofnodes.simple.node.ScopeNode.Kind; import java.util.*; /** @@ -82,8 +84,7 @@ public class Parser { FunNode _fun; // Current function being parsed // Mapping from a type 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. + // `type.str()` call. public static HashMap TYPES = defaultTypes(); // Mapping from a type name to the constructor for a Type. @@ -152,7 +153,7 @@ public class Parser { _xScopes.push(_scope); // Parse whole program, as-if function header "{ int arg -> body }" - parseFunctionBody(_code._main,loc(),"arg"); + parseFunctionBody( _code._main,loc(),"arg"); // Kill an empty default main. Keep only if it was explicitly defined // (programmer asked for a "main") or it has stuff (i.e. beginner @@ -188,10 +189,16 @@ public class Parser { /** * Parses a function body, assuming the header is parsed. */ - private ReturnNode parseFunctionBody(TypeFunPtr sig, Lexer loc, String... ids) { + private ReturnNode parseFunctionBody( TypeFunPtr sig, Lexer loc, String... ids) { + // If this is a method, record & restore the existing struct fields, + // which are also the struct initializers. The method might update + // mutable fields, so when the function parse is done reset back to + // pre-function-parse values. + Node[] preParse = new Node[_scope.nIns()]; + for( int i=0; i<_scope.nIns(); i++ ) + preParse[i] = _scope.in(i)==null ? null : _scope.in(i).keep(); + // Stack parser state on the local Java stack, and unstack it later - Node oldctrl = ctrl().keep(); - Node oldmem = _scope.mem().keep(); FunNode oldfun = _fun; ScopeNode breakScope = _breakScope; _breakScope = null; ScopeNode continueScope = _continueScope; _continueScope = null; @@ -206,7 +213,7 @@ public class Parser { Node rpc = new ParmNode("$rpc",0,TypeRPC.BOT,fun,con(TypeRPC.BOT)).peephole(); // Build a multi-exit return point for all function returns - RegionNode r = new RegionNode((Lexer)null,null,null).init(); + RegionNode r = new RegionNode( null,null,null).init(); assert r.inProgress(); PhiNode rmem = new PhiNode(ScopeNode.MEM0,TypeMem.BOT,r,null).init(); PhiNode rrez = new PhiNode(ScopeNode.ARG0,Type.BOTTOM,r,null).init(); @@ -215,10 +222,11 @@ public class Parser { assert ret.inProgress(); _code._stop.addDef(ret); + // 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(new Kind.Func()); ctrl(fun); // Scope control from function // Private mem alias tracking per function MemMergeNode mem = new MemMergeNode(true); @@ -259,9 +267,10 @@ public class Parser { _fun = oldfun; _breakScope = breakScope; _continueScope = continueScope; - // Reset control and memory to pre-function parsing days - ctrl(oldctrl.unkeep()); - _scope.mem(oldmem.unkeep()); + + // Reset all fields to pre-parse days + for( int i=0; i than the loop head // We use _breakScope as a proxy for the loop head scope to obtain the depth - while( cur._lexSize.size() > _breakScope._lexSize.size() ) + while( cur.depth() > _breakScope.depth() ) cur.pop(); // If this is a continue then first time the target is null // So we just use the pruned current scope as the base for the @@ -470,7 +479,7 @@ public class Parser { if( toScope == null ) return cur; // toScope is either the break scope, or a scope that was created here - assert toScope._lexSize.size() <= _breakScope._lexSize.size(); + assert toScope.depth() <= _breakScope.depth(); toScope.ctrl(toScope.mergeScopes(cur,loc()).peephole()); return toScope; } @@ -581,7 +590,7 @@ public class Parser { // Merge results RegionNode r = ctrl(tScope.mergeScopes(fScope,loc())); - Node ret = peep(new PhiNode("",lhs._type.meet(rhs._type),r,lhs.unkeep(),rhs.unkeep())); + Node ret = peep(new PhiNode("",lhs._type.meet(rhs._type).glb(false),r,lhs.unkeep(),rhs.unkeep())); // Immediately fail e.g. `arg ? 7 : ptr` ParseException err; if( !stmt && (err=ret.err()) !=null ) throw err; @@ -621,27 +630,37 @@ public class Parser { private Node parseAsgn() { int old = pos(); String name = _lexer.matchId(); + int old_pre_equals = pos(); // Just a plain expression, no assignment. // Distinguish `var==expr` from `var=expr` if( name==null || KEYWORDS.contains(name) || !matchOpx('=','=') ) { pos(old); return parseExpression(); } - // Parse assignment expression - Node expr = parseAsgn(); - - // Final variable to update + // Find variable to update Var def = _scope.lookup(name); if( def==null ) throw error("Undefined name '" + name + "'"); + // If a method var, allow nested blocks and methods, but not fcns or other structs. + // Else safety check for accessing fields out of function scope. + if( instance(def) ) { + pos(old_pre_equals); // setup for `self.name = expr` + Node self = _scope.in(_scope.lookup("self")); // Insert self reference + return parsePostfixName(self,name); + } + // TOP fields are for late-initialized fields; these have never // been written to, and this must be the final write. Other writes // 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.inConstructor() && def._idx >= _scope._lexSize.last()) ) + // Inside an allocation, final assign is OK, outside nope. + // The alloc() call added the allocation scope + !(_scope.inAllocation() && def._idx >= _scope._kinds.last(-1)._lexSize) ) throw error("Cannot reassign final '"+name+"'"); + // Parse assignment expression + Node expr = parseAsgn(); + // Lift expression, based on type Node lift = liftExpr(expr.keep(), def.type(), def._final, true); // Update @@ -653,7 +672,7 @@ public class Parser { // Make finals deep; widen ints to floats; narrow wide int types. // Early error if types do not match variable. private Node liftExpr( Node expr, Type t, boolean xfinal, boolean isLoad ) { - if( expr._type instanceof TypeMemPtr tmp && tmp.isFRef() ) + if( expr._type instanceof TypeMemPtr tmp && TYPES.get(tmp._obj._name).isFRef() ) throw error("Must define forward ref "+tmp._obj._name); // Final is deep on ptrs if( xfinal && t instanceof TypeMemPtr tmp ) { @@ -662,7 +681,7 @@ public class Parser { } // Auto-widen array to i64 (cast ptr to raw int bits) if( t == TypeInteger.BOT && expr._type instanceof TypeMemPtr tmp && tmp._obj.isAry() ) - expr = peep(new AddNode(peep(new CastNode(t,ctrl(),expr)),con(tmp._obj.aryBase()))); + expr = peep(new AddNode(peep(new CastNode(t,ctrl(),expr)),off(tmp._obj,"[]"))); // Auto-widen int to float expr = widenInt( expr, t ); // Auto-narrow wide ints to narrow ints. For loads, emit code to force @@ -674,7 +693,7 @@ public class Parser { // Type is sane if( et!=Type.BOTTOM && !et.shallowISA(t) ) - throw error("Type " + et.str() + " is not of declared type " + t.str()); + expr = peep(new CastNode(t,null,expr)); return expr; } @@ -715,6 +734,7 @@ public class Parser { String name = requireId(); // Optional initializing expression follows boolean xfinal = false; + boolean fld_final = false; // Field is final, but not deeply final Node expr; if( match("=") ) { expr = isExternDecl() @@ -722,14 +742,18 @@ public class Parser { : parseAsgn(); // TOP means val and val is always final xfinal = (t==Type.TOP) || + expr instanceof ExternNode || // BOTTOM is var and var is always not-final (t!=Type.BOTTOM && // no Bang AND !hasBang && + // Locals are not-final by default + !_scope.inFunction() && // not-null (expecting null to be set to not-null) expr._type != Type.NIL && // Pointers are final by default; int/flt are not-final by default. (t instanceof TypeNil)); + // var/val, then type comes from expression if( inferType ) { if( expr._type==Type.NIL ) @@ -760,11 +784,15 @@ public class Parser { // the constructor and that's when we'll discover the type. case Type tt -> { assert tt==Type.BOTTOM; yield con(tt); } }; + // Nullable fields are set in the constructor, but remain shallow final. + // e.g. final pointer to a r/w array + if( t instanceof TypeNil tn && !tn.nullable() && !hasBang ) + fld_final = true; } // Lift expression, based on type Node lift = liftExpr(expr, t, xfinal, true); - + // Rebuild the deep RO 't', not returned from liftExpr if( xfinal && t instanceof TypeMemPtr tmp ) t = tmp.makeRO(); @@ -773,7 +801,7 @@ public class Parser { if( !lift._type.isa(t) ) lift = peep(new CastNode(t,null,lift)); // Define a new name - if( !_scope.define(name,t,xfinal,lift, loc) ) + if( !_scope.define(name,t,xfinal || fld_final,lift, loc) ) throw error("Redefining name '" + name + "'", loc); return lift; } @@ -793,30 +821,46 @@ public class Parser { String typeName = requireId(); if( old!=null ) typeName = old+"."+typeName; _nestedType = typeName; + // New declared or forward-ref type + TypeStruct ts = TYPES.get(typeName) instanceof TypeMemPtr tmp ? tmp._obj : TypeStruct.open(typeName); + TYPES.put(typeName,TypeMemPtr.make(ts)); // A Block scope parse, and inspect the scope afterward for fields. - _scope.push(ScopeNode.Kind.Constructor); + _scope.push(new Kind.Define(TypeMemPtr.make(ts))); require("{"); - while (!peek('}') && !_lexer.isEOF()) + int lexlen = _scope._kinds.last()._lexSize, nvar=lexlen; + while (!peek('}') && !_lexer.isEOF()) { parseStatement().isKill(); + while( nvar < _scope._vars._len ) { + Var v = _scope.var(nvar++); + if( !v.isFRef() ) { + ts = ts.add( Field.make( v._name, v.type(), _code.getALIAS(), v._final, v._final && _scope.in( v._idx )._type != Type.TOP ) ); + TYPES.put( typeName, TypeMemPtr.make( ts ) ); + } + } + } - // Grab the declarations and build fields and a Struct - int lexlen = _scope._lexSize.last(); + // Count struct declarations int varlen = _scope._vars._len; - StructNode s = new StructNode(); - Ary fs = new Ary<>(Field.class); - for( int i=lexlen; i type }` or `{ type }`. @@ -916,7 +960,7 @@ public class Parser { Type t0 = type(); // Either return or first arg if( t0==null ) return posT(old); // Not a function if( match("}") ) // No-arg function { -> type } - return TypeFunPtr.make(match("?"),TypeTuple.BOT,t0); + return TypeFunPtr.make(match("?"),false,TypeFunPtr.TEMPTY,t0); Ary ts = new Ary<>(Type.class); ts.push(t0); // First argument while( true ) { @@ -924,7 +968,7 @@ public class Parser { Type ret = type(); if( ret==null || !match("}") ) return posT(old); // Not a function - return TypeFunPtr.make(match("?"),TypeTuple.make(ts.asAry()),ret); + return TypeFunPtr.make(match("?"),false,ts.asAry(),ret); } Type t1 = type(); if( t1==null ) return posT(old); // Not a function @@ -1103,14 +1147,17 @@ public class Parser { } if (match("-")) return peep(new MinusNode(parseUnary()).widen()); if (match("!")) return peep(new NotNode(parseUnary())); - return parsePostfix(parsePrimary()); + // Else be a primary expression + return parsePrimary(); } /** * Parse a primary expression: * *
-     *     primaryExpr : integerLiteral | true | false | null | new Type | '(' expression ')' | Id['++','--']
+     *     primaryExpr : integerLiteral | "string" | 'char' | true | false | null |
+     *                   new Type | '(' expression ')' | Id['++','--'] |
+     *                   [static.]* Id
      * 
* @return a primary {@link Node}, never {@code null} */ @@ -1121,43 +1168,96 @@ public class Parser { if( matchx("false") ) return ZERO; if( matchx("null" ) ) return NIL; if( match ("'" ) ) return parseChar(); - if( match ("(" ) ) return require(parseAsgn(), ")"); - if( matchx("new" ) ) return alloc(); - if( match ("{" ) ) return require(func(),"}"); + if( match ("(" ) ) return parsePostfix(require(parseAsgn(), ")")); + if( matchx("new" ) ) return parsePostfix(alloc()); + if( match ("{" ) ) return parsePostfix(require(func(),"}")); // Parse static variable lookup: `type.fld` in the type namespace + // CNC: not a full type parse, just a typename parse + int pos = pos(); Type t = type(); - if( t!=null && match(".") ) { - String fld = requireId(); - TypeMemPtr tmp = ((TypeMemPtr)t); - String tname = tmp._obj._name; - StructNode init = INITS.get(tname); - int idx = tmp._obj.find(fld); - if( idx == -1 ) - throw error("Accessing unknown STATIC field"); - return init.in(idx); + if( t!=null ) { + if( peek('.') ) + return parsePostfix(con(t)); + //pos(pos); // TODO: not a `type.fld` + //return null; + throw Utils.TODO(); } // Expect an identifier now - Var n = requireLookupId(); - Node rvalue = _scope.in(n); + + // - if null, make fref, normal var handling + // - if final constant, normal var handling + // - if method var, scan skipping blocks & methods, must hit correct struct no fcns + // - if normal var, scan skipping blocks, allowing last fcn + // - NO fcns nested in methods, NO methods nested in fcns + + String id = _lexer.matchId(); + if( id == null || KEYWORDS.contains(id) ) + throw errorSyntax("an identifier or expression"); + Var var = _scope.lookup(id); + + if( var==null ) { + // If missing, assume a forward reference + _scope.define(id, FRefNode.FREF_TYPE, true, XCTRL, loc()); + var = _scope.lookup(id); + } + + // If a method var, allow nested blocks and methods, but not fcns or other structs. + // Else safety check for accessing fields out of function scope. + if( instance(var) ) { + Node self = _scope.in(_scope.lookup("self")); // Insert self reference + return parsePostfixName(self,id); + } + + // Load local value + Node rvalue = _scope.in(var); if( rvalue._type == Type.BOTTOM ) - if( rvalue instanceof FRefNode ) return rvalue; - else throw error("Cannot read uninitialized field '"+n._name+"'"); + if( rvalue instanceof FRefNode ) + return parsePostfix(rvalue); + else throw error("Cannot read uninitialized field '"+id+"'"); + // 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); + if( ch==0 ) // Normal primary, check for postfix updates + return parsePostfix(rvalue); + // Assign-update direct into Scope + Node op = opAssign(ch,rvalue, var.type() ); + _scope.update(var,op); return postfix(ch) ? rvalue.unkeep() : op; } + // true if instance variable lookup (var is in any number of nested blocks, + // then an instance function then inside a struct declaration). + private boolean instance(Var var ) { + // If a method var, allow nested blocks and methods, but not fcns or other structs + Kind kind = _scope.kind(var); + if( kind instanceof Kind.Define ) { + for( int i = _scope.depth()-1; i>=0 && var._idx < _scope._kinds.at(i)._lexSize; i-- ) { + Kind xkind = _scope._kinds.at(i); + if( xkind instanceof Kind.Block ) continue; + if( xkind instanceof Kind.Func ) { + if( _scope._kinds.at(i-1)==kind ) + return true; // Instance variable: lookup in a method in a decl + } + throw Utils.TODO(); + } + } else { + // If a normal var, allow any nested blocks, and a last fcn - + // but not functions further out (which involves capture). + Node def = _scope.in(var._idx); + for( int i = _scope.depth()-1; i>=0 && var._idx < _scope._kinds.at(i)._lexSize; i-- ) + if( _scope._kinds.at(i) instanceof Kind.Func && + !(def instanceof FRefNode) && !(var._final && def._type.isConstant()) ) + throw error("Variable '"+var._name+"' is out of function scope and must be a final constant"); + } + return false; + } + // 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+"'"); + private Node opAssign( char ch, Node lhs, Type t ) { // RHS of the update. lhs.keep(); // Alive across parseAsgn Node rhs = @@ -1184,30 +1284,6 @@ public class Parser { } - // Expect an ID here. If not found, assume a forward reference function - Var requireLookupId() { - // Try in the normal variable namespace - String id = _lexer.matchId(); - if( id == null || KEYWORDS.contains(id) ) - throw errorSyntax("an identifier or expression"); - Var n = _scope.lookup(id); - // If missing, assume a forward reference - if( n==null ) { - _scope.define(id, FRefNode.FREF_TYPE, true, XCTRL, loc()); - n = _scope.lookup(id); - } else { - // Lookup worked on an out-of-function value. - if( _scope.outOfFunction(n) ) { - // No closures, so this has to be a final constant (which - // includes forward refs) - Node def = _scope.in(n._idx); - if( !(def instanceof FRefNode) && !(n._final && def._type.isConstant()) ) - throw error("Variable '"+n._name+"' is out of function scope and must be a final constant"); - } - } - return n; - } - /** Parse an allocation */ @@ -1222,7 +1298,7 @@ public class Parser { if( !(len._type instanceof TypeInteger) ) throw error("Cannot allocate an array with length "+len._type); require("]"); - TypeMemPtr tmp = typeAry(t); + TypeMemPtr tmp = typeAry(t,false); return newArray(tmp._obj,len); } @@ -1241,19 +1317,23 @@ public class Parser { if( hasConstructor ) { idx = _scope.nIns(); // Push a scope, and pre-assign all struct fields. - _scope.push(ScopeNode.Kind.Block); + _scope.push(new Kind.Block()); Lexer loc = loc(); for( int i=0; i ALTMP = new Ary<>(Node.class); private Node newArray(TypeStruct ary, Node len) { - int base = ary.aryBase (); + ConFldOffNode base = off(ary, "[]"); int scale= ary.aryScale(); - Node size = peep(new AddNode(con(base),peep(new ShlNode(null,len.keep(),con(scale))))); - ALTMP.clear(); ALTMP.add(len.unkeep()); ALTMP.add(con(ary._fields[1]._type.makeZero())); + Node size = peep(new AddNode(base,peep(new ShlNode(null,len.keep(),con(scale))))); + ALTMP.clear(); ALTMP.add(len.unkeep()); ALTMP.add(con(ary._fields[1]._t.makeZero())); return newStruct(ary,size,0,ALTMP); } private Node newString(String s) { - TypeMemPtr tmp = typeAry(TypeInteger.U8); + TypeMemPtr tmp = typeAry(TypeInteger.U8,true); int lenAlias = tmp._obj.field("#" )._alias; int elemAlias = tmp._obj.field("[]")._alias; TypeConAryB con = TypeConAryB.make(s); @@ -1312,7 +1394,8 @@ public class Parser { // Make a TMP, not-null (byte)2, singleton (true) (requires text // strings are hash-interned), with a TypeStruct having a constant // array body. - TypeMemPtr str = TypeMemPtr.make((byte)2,TypeStruct.makeAry("[u8]", TypeInteger.U32, lenAlias, elem, elemAlias, true, con),true); + TypeMemPtr str = TypeMemPtr.make((byte)2,TypeStruct.makeAry("[]u8", TypeInteger.constant(s.length()), lenAlias, con, elemAlias, true),true); + assert str.isConstant(); return con(str); } @@ -1341,9 +1424,24 @@ public class Parser { if( match(".") ) name = requireId(); else if( match("#") ) name = "#"; else if( match("[") ) name = "[]"; - else if( match("(") ) return parsePostfix(require(functionCall(expr),")")); + else if( match("(") ) return parsePostfix(require(functionCall(expr,null),")")); else return expr; // No postfix + return parsePostfixName(expr,name); + } + + /** + * Parse postfix expression; this can be a field expression, an array + * lookup or a postfix operator like '#' + * + *
+     *     expr ['++' | '--' ]                 // Postfix unary write operator
+     *     expr [ [op]= expr ]                 // Field reference
+     *     expr ('[' expr ']')* [ [op]= expr ] // Array reference
+     * 
+ */ + private Node parsePostfixName(Node expr, String name) { + if( expr._type==Type.NIL ) throw error("Accessing unknown field '" + name + "' from 'null'"); @@ -1352,23 +1450,25 @@ public class Parser { throw error( "Expected "+(name=="#" || name=="[]" ? "array" : "reference")+" but found " + expr._type.str() ); // Find the field from the Type. Lookup in the base object field names. + TypeMemPtr ptr2 = (TypeMemPtr)TYPES.get(ptr._obj._name); + ptr = (TypeMemPtr)ptr.join(ptr2);// Upgrade to latest TYPES TypeStruct base = ptr._obj; - assert !base.isFRef(); int fidx = base.find(name); - if( fidx == -1 ) throw error("Accessing unknown field '" + name + "' from '" + ptr.str() + "'"); + if( fidx == -1 ) + throw error("Accessing unknown field '" + name + "' from '" + ptr.str() + "'"); // 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; + Type tf = f._t; if( tf instanceof TypeMemPtr ftmp && ftmp.isFRef() ) tf = ftmp.makeFrom(((TypeMemPtr)(TYPES.get(ftmp._obj._name)))._obj); // Field offset; fixed for structs, computed for arrays Node off = (name.equals("[]") // If field is an array body // Array index math - ? peep(new AddNode(con(base.aryBase()),peep(new ShlNode(null,require(parseAsgn(),"]"),con(base.aryScale()))))) + ? peep(new AddNode(off(base,"[]"),peep(new ShlNode(null,require(parseAsgn(),"]"),con(base.aryScale()))))) // Struct field offsets are hardwired - : con(base.offset(fidx))).keep(); + : off(base,name)).keep(); // Disambiguate "obj.fld==x" boolean test from "obj.fld=x" field assignment if( matchOpx('=','=') ) { @@ -1384,7 +1484,12 @@ public class Parser { return val.unkeep(); // "obj.a = expr" returns the expression while updating memory } - Node load = new LoadNode(loc(),name, f._alias, tf, memAlias(f._alias), expr.keep(), off); + // Keep expr for possible store update + expr.keep(); + // Load field + Node load = f._one && INITS.containsKey( base._name ) + ? INITS.get(base._name).in(base.find(name)) + : new LoadNode(loc(),name, f._alias, tf, memAlias(f._alias), expr, off); // 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. @@ -1394,8 +1499,8 @@ public class Parser { // 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); + Node op = opAssign(ch,load, tf ); + Node st = new StoreNode(loc(), name, f._alias, tf, memAlias(f._alias), expr.unkeep(), off.unkeep(), 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. @@ -1403,8 +1508,25 @@ public class Parser { memAlias(f._alias, peep(st)); load = postfix(ch) ? load.unkeep() : op; // And use the original loaded value as the result - } else expr.unkill(); - off.unkill(); + return load; + //throw Utils.TODO(); // NO POSTFIX AFTER STORE, JUST RETURN + } else { + off.unkill(); + // Method call: Loaded a function from a named field, AND the base + // field type is final. + if( load._type instanceof TypeFunPtr && name!="[]" && + !(load instanceof ExternNode) && + ((TypeMemPtr)TYPES.get(base._name))._obj.field(name)._final && + // And calling a function + match("(") ) { + return parsePostfix(require(functionCall(load,expr),")")); + } + + // Drop expr, keeping load alive + load.keep(); + expr.unkill(); + load.unkeep(); + } return parsePostfix(load); } @@ -1440,6 +1562,17 @@ public class Parser { private Node func() { Ary ts = new Ary<>(Type.class); Ary ids = new Ary<>(String.class); + + // Defined in constructor? Add `self` argument. "static" calls still + // add a `self` they just ignore it. + if( _scope._kinds.last() instanceof Kind.Define define ) { + // Upgrade defined struct to latest fields + TypeMemPtr tmp = (TypeMemPtr)TYPES.get(define._tmp._obj._name); + ts .push(tmp); + ids.push("self"); + } + + // Parse other arguments _lexer.skipWhiteSpace(); Lexer loc = loc(); // First argument location while( true ) { @@ -1452,7 +1585,7 @@ public class Parser { } require("->"); // Make a concrete function type, with a fidx - TypeFunPtr tfp = _code.makeFun(TypeTuple.make(ts.asAry()),Type.BOTTOM); + TypeFunPtr tfp = _code.makeFun(TypeFunPtr.make(false,false,ts.asAry(),Type.BOTTOM)); ReturnNode ret = parseFunctionBody(tfp,loc,ids.asAry()); return con(ret._fun.sig()); } @@ -1463,16 +1596,18 @@ public class Parser { * ( arg* ) * */ - private Node functionCall(Node expr) { - if( expr._type == Type.NIL ) + private Node functionCall(Node fcn, Node self) { + if( fcn._type == Type.NIL ) throw error("Calling a null function pointer"); - if( !(expr instanceof FRefNode) && !expr._type.isa(TypeFunPtr.BOT) ) - throw error("Expected a function but got "+expr._type.glb(false).str()); - expr.keep(); // Keep while parsing args + if( !(fcn instanceof FRefNode) && !fcn._type.isa(TypeFunPtr.BOT) ) + throw error("Expected a function but got "+fcn._type.glb(false).str()); + fcn.keep(); // Keep while parsing args Ary args = new Ary<>( Node.class ); args.push(null); // Space for ctrl,mem args.push(null); + if( self != null ) // Method call has a self + args.push(self); while( !peek(')') ) { Node arg = parseAsgn(); if( arg==null ) break; @@ -1482,7 +1617,7 @@ public class Parser { // Control & memory after parsing args args.set(0,ctrl().keep()); args.set(1,_scope.mem().merge().keep()); - args.push(expr); // Function pointer + args.push(fcn); // Function pointer // Unkeep them all for( Node arg : args ) arg.unkeep(); @@ -1521,8 +1656,8 @@ public class Parser { } // External linked constant private Node externDecl( String ex, Type t ) { - if( t instanceof TypeFunPtr tfp ) // Generic TFP from type parse - t = _code.makeFun(tfp._sig,tfp._ret); // Get a FIDX, becomes a constant + if( t instanceof TypeFunPtr tfp ) // Generic TFP from type parse + t = _code.makeFun(tfp); // Get a FIDX, becomes a constant return new ExternNode(t,ex).peephole(); } @@ -1537,6 +1672,7 @@ public class Parser { private ConstantNode parseLiteral() { return con(_lexer.parseNumber()); } public static Node con( long con ) { return con==0 ? ZERO : con(TypeInteger.constant(con)); } public static ConstantNode con( Type t ) { return (ConstantNode)new ConstantNode(t).peephole(); } + public static ConFldOffNode off( TypeStruct base, String fname ) { return (ConFldOffNode)(new ConFldOffNode(base._name,fname).peephole()); } public Node peep( Node n ) { // Peephole, then improve with lexically scoped guards return _scope.upcastGuard(n.peephole()); @@ -1740,6 +1876,7 @@ public class Parser { _position -= syntax.length(); return false; } + // Match these two characters in a row boolean matchOpx(char c0, char c1) { skipWhiteSpace(); if( _position+1 >= _input.length || _input[_position]!=c0 || _input[_position+1]==c1 ) @@ -1861,4 +1998,5 @@ public class Parser { //String msg = "src:"+_line_number+":"+(_position-_line_start)+" "+errorMessage; ParseException( String msg, Lexer loc ) { super(msg); _loc = loc; } } + } -- cgit v1.2.3