From bfd9af8f9518a6fe87cbb706813cd87c8dc5ea90 Mon Sep 17 00:00:00 2001 From: Cliff Click Date: Sun, 1 Nov 2026 12:32:14 -0800 Subject: First cut parser split --- .../com/seaofnodes/simple/codegen/CodeGen.java | 14 +- .../com/seaofnodes/simple/codegen/CompUnit.java | 3 +- .../com/seaofnodes/simple/codegen/Encoding.java | 5 +- .../com/seaofnodes/simple/codegen/ParseAll.java | 16 +-- .../com/seaofnodes/simple/codegen/Serialize.java | 8 +- .../com/seaofnodes/simple/codegen/StaticData.java | 6 +- .../com/seaofnodes/simple/node/BulkMemPhiNode.java | 2 +- .../com/seaofnodes/simple/node/CLInitNode.java | 9 +- .../com/seaofnodes/simple/node/ConFldOffNode.java | 6 + .../com/seaofnodes/simple/node/ConvertNode.java | 2 +- .../java/com/seaofnodes/simple/node/ScopeNode.java | 4 + .../java/com/seaofnodes/simple/node/StoreNode.java | 2 +- .../com/seaofnodes/simple/parser/Allocations.java | 12 +- .../com/seaofnodes/simple/parser/Conversions.java | 2 +- .../com/seaofnodes/simple/parser/Expressions.java | 2 +- .../java/com/seaofnodes/simple/parser/Lexer.java | 5 + .../java/com/seaofnodes/simple/parser/Parser.java | 148 +++++++++------------ .../java/com/seaofnodes/simple/parser/README.md | 5 + .../com/seaofnodes/simple/parser/References.java | 22 +-- .../java/com/seaofnodes/simple/parser/Structs.java | 106 +++++++-------- .../java/com/seaofnodes/simple/parser/Types.java | 2 +- .../com/seaofnodes/simple/type/TypeStruct.java | 19 ++- 22 files changed, 201 insertions(+), 199 deletions(-) (limited to 'chapter29/src/main/java') diff --git a/chapter29/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java b/chapter29/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java index bdb510b4..6e1c0af5 100644 --- a/chapter29/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java +++ b/chapter29/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java @@ -114,13 +114,13 @@ public class CodeGen { } public static boolean hasUserConstructor(TypeStruct self) { - Type t = Parser.TYPES.get(Parser.addClzPrefix(self._name)); + Type t = Parser.TYPES.get(TypeStruct.addClzPrefix(self._name)); return t instanceof TypeStruct clz && clz.field("") != null; } // A forward reference has a placeholder class, not a definition yet. private static boolean resolvedClass(TypeStruct clz) { - Type t = Parser.TYPES.get(clz._name.substring(Parser.CLZ.length())); + Type t = Parser.TYPES.get(clz._name.substring(TypeStruct.CLZ.length())); return !(t instanceof TypeStruct ts) || !ts._fref; } @@ -139,13 +139,13 @@ public class CodeGen { _publicFIDXs = _publicAliases = XInt.EMPTY; for( Type t : Parser.TYPES.values() ) if( t instanceof TypeStruct clz && - Parser.startsClzPrefix(clz._name) && - Parser.isPublicClassName(clz._name) && + TypeStruct.startsClzPrefix(clz._name) && + TypeStruct.isPublicClassName(clz._name) && resolvedClass(clz) ) { // Gather function pointers from public fields of both class // and instance objects. gatherPublicFields(clz,true); - Type self = Parser.TYPES.get(clz._name.substring(Parser.CLZ.length())); + Type self = Parser.TYPES.get(clz._name.substring(TypeStruct.CLZ.length())); if( self instanceof TypeStruct ts ) gatherPublicFields(ts,false); } // Limit broad function types to known FIDXs, excluding reserved bits. @@ -309,7 +309,7 @@ public class CodeGen { } public String entryClinitName() { - return Parser.addClzPrefix(_srcName==null ? "Test" : _srcName.replace('/','.'))+"."; + return TypeStruct.addClzPrefix(_srcName==null ? "Test" : _srcName.replace('/','.'))+"."; } // Verbose printing during compilation @@ -790,7 +790,7 @@ public class CodeGen { // Publish definitions only; referenced classes belong to other objects. for( String fname : elf._units ) - _externSymbols.putIfAbsent(Parser.addClzPrefix(fname.replace('/','.')),elf); + _externSymbols.putIfAbsent(TypeStruct.addClzPrefix(fname.replace('/','.')),elf); // A -L object can name dependencies alongside itself. They // need metadata discovery even when no directory was supplied. for( String dep : elf._deps ) { diff --git a/chapter29/src/main/java/com/seaofnodes/simple/codegen/CompUnit.java b/chapter29/src/main/java/com/seaofnodes/simple/codegen/CompUnit.java index 2b091b3c..995301c3 100644 --- a/chapter29/src/main/java/com/seaofnodes/simple/codegen/CompUnit.java +++ b/chapter29/src/main/java/com/seaofnodes/simple/codegen/CompUnit.java @@ -1,6 +1,5 @@ package com.seaofnodes.simple.codegen; -import com.seaofnodes.simple.parser.Parser; import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.type.Type; import com.seaofnodes.simple.type.TypeStruct; @@ -135,7 +134,7 @@ public class CompUnit { static File dependencyObj(String fname, boolean local) { File obj = new File(CODE._buildDir+"/"+fname+".o"); if( local && (obj.isFile() || new File(CODE._modDir+"/"+fname+".smp").isFile()) ) return obj; - ElfReader elf = CODE.findExternalSimple(Parser.addClzPrefix(fname.replace('/','.'))); + ElfReader elf = CODE.findExternalSimple(TypeStruct.addClzPrefix(fname.replace('/','.'))); return elf==null ? null : elf._file; } diff --git a/chapter29/src/main/java/com/seaofnodes/simple/codegen/Encoding.java b/chapter29/src/main/java/com/seaofnodes/simple/codegen/Encoding.java index 8b868218..b07a8fec 100644 --- a/chapter29/src/main/java/com/seaofnodes/simple/codegen/Encoding.java +++ b/chapter29/src/main/java/com/seaofnodes/simple/codegen/Encoding.java @@ -1,6 +1,5 @@ package com.seaofnodes.simple.codegen; -import com.seaofnodes.simple.parser.Parser; import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.print.IRPrinter; import com.seaofnodes.simple.type.*; @@ -66,7 +65,7 @@ public class Encoding implements com.seaofnodes.isa.CodeSink { // Class initializers still write their fields, even when every // final value is constant. Their storage must remain writable. return !(t instanceof TypeStruct ts) || - (!Parser.startsClzPrefix(ts._name) && ts.isConstant()); + (!TypeStruct.startsClzPrefix(ts._name) && ts.isConstant()); } } @@ -201,7 +200,7 @@ public class Encoding implements com.seaofnodes.isa.CodeSink { // addressing to load it. Type is stored in either the .rodata or .data. public void largeConstant( Node relo, Type t, int off, int elf ) { // TODO: Any-old struct in the cpool - assert t.isConstant() || (t instanceof TypeStruct ts && Parser.startsClzPrefix(ts._name)); + assert t.isConstant() || (t instanceof TypeStruct ts && TypeStruct.startsClzPrefix(ts._name)); assert (byte)off == off; assert (byte)elf == elf; _bigCons.put(relo,new Relo(relo,t,(byte)off,(byte)elf)); diff --git a/chapter29/src/main/java/com/seaofnodes/simple/codegen/ParseAll.java b/chapter29/src/main/java/com/seaofnodes/simple/codegen/ParseAll.java index 1218c956..7fa4c662 100644 --- a/chapter29/src/main/java/com/seaofnodes/simple/codegen/ParseAll.java +++ b/chapter29/src/main/java/com/seaofnodes/simple/codegen/ParseAll.java @@ -117,7 +117,7 @@ public abstract class ParseAll { Parser.TYPES.put(ts._name,ts); for( CompUnit cu : code._compunits.values() ) if( cu._ext==null ) - cu._clz = (TypeStruct)Parser.TYPES.get(Parser.addClzPrefix(cu._cname)); + cu._clz = (TypeStruct)Parser.TYPES.get(TypeStruct.addClzPrefix(cu._cname)); // Walk over all Nodes, and upgrade the internal constants to the // closed-over types. @@ -135,8 +135,8 @@ public abstract class ParseAll { if( alloc ) { Field ctor = clz.field(""); if( ctor == null || !(ctor._t instanceof TypeFunPtr tfp) ) { - String name = clzName.startsWith(Parser.CLZ) - ? clzName.substring(Parser.CLZ.length()) : clzName; + String name = clzName.startsWith(TypeStruct.CLZ) + ? clzName.substring(TypeStruct.CLZ.length()) : clzName; int dot = name.lastIndexOf('.'); throw Parser.error("Unknown struct type '"+(dot==-1 ? name : name.substring(dot+1))+"'",fref._loc); } @@ -184,7 +184,7 @@ public abstract class ParseAll { xcunit = findCUnitExternalSimple(code,fref._name); if( xcunit != null ) { // Replace the FRef with the discovered class name. - fref._name = Parser.addClzPrefix(xcunit._cname); + fref._name = TypeStruct.addClzPrefix(xcunit._cname); cunit.addDep(xcunit); } else { throw new RuntimeException("Undefined name '" + fref._name +"'"); @@ -261,7 +261,7 @@ public abstract class ParseAll { private static File externalObj(CodeGen code, CompUnit par, String name) { String cname = par==null ? name : par._cname + "." + name; - ElfReader elf = code.findExternalSimple(Parser.addClzPrefix(cname)); + ElfReader elf = code.findExternalSimple(TypeStruct.addClzPrefix(cname)); return elf == null ? null : elf._file; } @@ -286,7 +286,7 @@ public abstract class ParseAll { ProjNode oldMem = oldStart==null ? null : oldStart.proj(1); if( oldMem!=null ) code.releaseReplacedNode(oldMem); elf.loadSimple(code); - cunit._clz = (TypeStruct)Parser.TYPES.get(Parser.addClzPrefix(cunit._cname)); + cunit._clz = (TypeStruct)Parser.TYPES.get(TypeStruct.addClzPrefix(cunit._cname)); StartNode lstart = (StartNode)elf._nodes.at(0); StopNode lstop = ( StopNode)elf._nodes.last(); @@ -419,8 +419,8 @@ public abstract class ParseAll { // Search external Simple objects only. C linkage uses explicit "C" // declarations, which produce ExternNodes and are resolved by the linker. public static CompUnit findCUnitExternalSimple( CodeGen code, String symbol ) { - String clzName = symbol.startsWith(Parser.CLZ) ? symbol : Parser.addClzPrefix(symbol); - String cname = clzName.substring(Parser.CLZ.length()); + String clzName = symbol.startsWith(TypeStruct.CLZ) ? symbol : TypeStruct.addClzPrefix(symbol); + String cname = clzName.substring(TypeStruct.CLZ.length()); String fname = cname.replace('.','/'); CompUnit cunit = code._compunits.get(fname); diff --git a/chapter29/src/main/java/com/seaofnodes/simple/codegen/Serialize.java b/chapter29/src/main/java/com/seaofnodes/simple/codegen/Serialize.java index 76829151..4bb94183 100644 --- a/chapter29/src/main/java/com/seaofnodes/simple/codegen/Serialize.java +++ b/chapter29/src/main/java/com/seaofnodes/simple/codegen/Serialize.java @@ -313,8 +313,8 @@ public class Serialize { // A nested declaration belongs to its nearest enclosing file. private static CompUnit classOwner(CodeGen code, TypeStruct ts) { - if( !Parser.startsClzPrefix(ts._name) ) return null; - String name = ts._name.substring(Parser.CLZ.length()).replace('.','/'); + if( !TypeStruct.startsClzPrefix(ts._name) ) return null; + String name = ts._name.substring(TypeStruct.CLZ.length()).replace('.','/'); while( true ) { CompUnit cu = code._compunits.get(name); if( cu!=null ) return cu; @@ -418,9 +418,9 @@ public class Serialize { if( type instanceof TypeStruct ts ) Parser.TYPES.put(ts._name,ts); for( Type type : types ) { - if( !(type instanceof TypeStruct ts) || !Parser.startsClzPrefix(ts._name) ) + if( !(type instanceof TypeStruct ts) || !TypeStruct.startsClzPrefix(ts._name) ) continue; - String cname = ts._name.substring(Parser.CLZ.length()); + String cname = ts._name.substring(TypeStruct.CLZ.length()); int dot = cname.lastIndexOf('.'); String name = dot == -1 ? cname : cname.substring(dot+1); Field inst = ts.field(name.intern()); diff --git a/chapter29/src/main/java/com/seaofnodes/simple/codegen/StaticData.java b/chapter29/src/main/java/com/seaofnodes/simple/codegen/StaticData.java index f813fab5..5d1a519b 100644 --- a/chapter29/src/main/java/com/seaofnodes/simple/codegen/StaticData.java +++ b/chapter29/src/main/java/com/seaofnodes/simple/codegen/StaticData.java @@ -41,11 +41,11 @@ public final class StaticData { } static boolean isClass(Type t) { - return t instanceof TypeStruct ts && Parser.startsClzPrefix(ts._name); + return t instanceof TypeStruct ts && TypeStruct.startsClzPrefix(ts._name); } static CompUnit owner(CodeGen code,TypeStruct ts) { - return owner(code,ts._name.substring(Parser.CLZ.length())); + return owner(code,ts._name.substring(TypeStruct.CLZ.length())); } static CompUnit owner(CodeGen code,String name) { if(name==null) return null; @@ -65,7 +65,7 @@ public final class StaticData { if( clz && Parser.TYPES.get(((TypeStruct)t)._name) instanceof TypeStruct canonical ) t=canonical; CompUnit owner=clz ? owner(enc._code,(TypeStruct)t) : t instanceof TypeFunPtr fp ? owner(enc._code,enc._code._fidxs.owner(fp.fidx())) : null; - String symbol=clz ? ((TypeStruct)t)._name.substring(Parser.CLZ.length())+".$class" + String symbol=clz ? ((TypeStruct)t)._name.substring(TypeStruct.CLZ.length())+".$class" : t instanceof TypeFunPtr fp ? functionSymbol(fp) : prefix+objects.size(); ObjectData obj=new ObjectData(t,symbol,!clz && (!(t instanceof TypeStruct ts) || ts.isConstant()), owner!=null && !enc._code.emits(owner)); diff --git a/chapter29/src/main/java/com/seaofnodes/simple/node/BulkMemPhiNode.java b/chapter29/src/main/java/com/seaofnodes/simple/node/BulkMemPhiNode.java index 6a902f3c..130b631c 100644 --- a/chapter29/src/main/java/com/seaofnodes/simple/node/BulkMemPhiNode.java +++ b/chapter29/src/main/java/com/seaofnodes/simple/node/BulkMemPhiNode.java @@ -258,7 +258,7 @@ public class BulkMemPhiNode extends PhiNode { MemPhiNode mphi = _findPhi(alias); if( mphi!=null ) return mphi; - mphi = new MemPhiNode("$"+alias,alias); + mphi = new MemPhiNode(ScopeNode.memName(alias),alias); mphi.addDef(region()); // Due to cycles, must set before calling peephole // Splitting preserves the bulk Phi's established escape bound. Starting diff --git a/chapter29/src/main/java/com/seaofnodes/simple/node/CLInitNode.java b/chapter29/src/main/java/com/seaofnodes/simple/node/CLInitNode.java index f381e57a..f510be39 100644 --- a/chapter29/src/main/java/com/seaofnodes/simple/node/CLInitNode.java +++ b/chapter29/src/main/java/com/seaofnodes/simple/node/CLInitNode.java @@ -2,7 +2,6 @@ package com.seaofnodes.simple.node; import com.seaofnodes.simple.parser.Lexer; import com.seaofnodes.print.ExprPrinter; -import com.seaofnodes.simple.parser.Parser; import com.seaofnodes.simple.codegen.CodeGen; import com.seaofnodes.simple.codegen.CompUnit; import com.seaofnodes.simple.codegen.Iter; @@ -43,14 +42,14 @@ public class CLInitNode extends CFGNode implements MultiNode { // The field/alias can be unresolved until forward references are loaded. public Field flag() { - return ptr()._type instanceof TypeMemPtr tmp && Parser.startsClzPrefix(tmp._obj._name) + return ptr()._type instanceof TypeMemPtr tmp && TypeStruct.startsClzPrefix(tmp._obj._name) ? tmp._obj.field("$init") : null; } @Override public String label() { return "CLInit"; } // Best effort dst compunit, based on ptr - or null public CompUnit cu() { - if( !(ptr()._type instanceof TypeMemPtr ptr && Parser.startsClzPrefix(ptr._obj._name)) ) + if( !(ptr()._type instanceof TypeMemPtr ptr && TypeStruct.startsClzPrefix(ptr._obj._name)) ) return null; // Convert "class:sys.ary" into "sys/ary" and lookup CompUnit String fname = ptr._obj._name.substring(6).replace(".","/"); @@ -70,7 +69,7 @@ public class CLInitNode extends CFGNode implements MultiNode { return ptr()._type==Type.NIL || (ptr()._type instanceof TypeMemPtr tmp && tmp._obj!=TypeStruct.BOT && !tmp._obj.isHigh() && !tmp._obj._open && !tmp._obj._fref && - !Parser.startsClzPrefix(tmp._obj._name)); + !TypeStruct.startsClzPrefix(tmp._obj._name)); } public boolean bypass() { return instance() || ready()._type==TypeInteger.TRUE; } @@ -205,6 +204,6 @@ public class CLInitNode extends CFGNode implements MultiNode { dep.addDep(init.ptr()); Field flag = init.flag(); if( flag==null || flag._alias!=alias ) return mem; - return new ProjNode(init,2,Parser.memName(alias)).peephole(); + return new ProjNode(init,2,ScopeNode.memName(alias)).peephole(); } } diff --git a/chapter29/src/main/java/com/seaofnodes/simple/node/ConFldOffNode.java b/chapter29/src/main/java/com/seaofnodes/simple/node/ConFldOffNode.java index cb99b4dd..4024c5a9 100644 --- a/chapter29/src/main/java/com/seaofnodes/simple/node/ConFldOffNode.java +++ b/chapter29/src/main/java/com/seaofnodes/simple/node/ConFldOffNode.java @@ -21,6 +21,12 @@ public class ConFldOffNode extends ConstantNode { public TypeStruct _ts; // Struct holding field public final String _fname; // Field name public ConFldOffNode( TypeStruct ts, String fname ) { super(TypeInteger.BOT); _ts = ts; _fname = fname; } + + // Field offset for a known type (but unknown offset). + public static ConFldOffNode make( TypeStruct ts, String fname ) { + return (ConFldOffNode)new ConFldOffNode(ts,fname).peephole(); + } + @Override public Tag serialTag() { throw Utils.TODO("should not reach here"); } @Override public String label() { diff --git a/chapter29/src/main/java/com/seaofnodes/simple/node/ConvertNode.java b/chapter29/src/main/java/com/seaofnodes/simple/node/ConvertNode.java index a969fb4e..81f46097 100644 --- a/chapter29/src/main/java/com/seaofnodes/simple/node/ConvertNode.java +++ b/chapter29/src/main/java/com/seaofnodes/simple/node/ConvertNode.java @@ -52,7 +52,7 @@ public class ConvertNode extends Node { // the address of the first element. The representation cast itself is // zero-code; the Add skips the array header. if( _dst==TypeInteger.BOT && src instanceof TypeMemPtr tmp && tmp._obj.isAry() ) - return new AddNode(new PtrToIntNode(val()).peephole(),Parser.off(tmp._obj,"[]")); + return new AddNode(new PtrToIntNode(val()).peephole(),ConFldOffNode.make(tmp._obj,"[]")); // Integer (and nil-as-zero) to floating point. if( (src instanceof TypeInteger || src==Type.NIL) && _dst instanceof TypeFloat ) diff --git a/chapter29/src/main/java/com/seaofnodes/simple/node/ScopeNode.java b/chapter29/src/main/java/com/seaofnodes/simple/node/ScopeNode.java index ff3d8b4d..5fc2b8cd 100644 --- a/chapter29/src/main/java/com/seaofnodes/simple/node/ScopeNode.java +++ b/chapter29/src/main/java/com/seaofnodes/simple/node/ScopeNode.java @@ -26,6 +26,10 @@ public class ScopeNode extends MemMergeNode { public static final String ARG0 = "arg"; public static final String MEM0 = "$mem"; + // Structural label for a precise memory alias. The '$' prefix cannot be + // referenced in Simple source. + public static String memName(int alias) { return ("$"+alias).intern(); } + // All active/live variables in all nested scopes, all run together. // Maps 1-to-1 to the _inputs array. public final Ary _vars; diff --git a/chapter29/src/main/java/com/seaofnodes/simple/node/StoreNode.java b/chapter29/src/main/java/com/seaofnodes/simple/node/StoreNode.java index 96e15a58..fe7d428c 100644 --- a/chapter29/src/main/java/com/seaofnodes/simple/node/StoreNode.java +++ b/chapter29/src/main/java/com/seaofnodes/simple/node/StoreNode.java @@ -97,7 +97,7 @@ public class StoreNode extends MemOpNode { // A constant runtime value alone is not enough: mutable fields (notably // $init) still need their writes, and instance fields need allocation. Field fld = ptr._obj.field(_name); - if( _alias!=1 && ptr._one && Parser.startsClzPrefix(ptr._obj._name) && + if( _alias!=1 && ptr._one && TypeStruct.startsClzPrefix(ptr._obj._name) && fld!=null && fld._final && decl.isConstant() ) return TypeMem.make(_alias,val,true,true,true,mem._escFs,mem._escAs).escapesFrom(val); diff --git a/chapter29/src/main/java/com/seaofnodes/simple/parser/Allocations.java b/chapter29/src/main/java/com/seaofnodes/simple/parser/Allocations.java index c5b8566b..8405278c 100644 --- a/chapter29/src/main/java/com/seaofnodes/simple/parser/Allocations.java +++ b/chapter29/src/main/java/com/seaofnodes/simple/parser/Allocations.java @@ -43,7 +43,7 @@ final class Allocations { String typeName = ts._name.indexOf('.') == -1 ? P.types.fullTypeName(ts._name) : ts._name; Type resolved = Parser.TYPES.get(typeName); if( resolved instanceof TypeStruct rts ) ts = rts; - TypeStruct clz = (TypeStruct)Parser.TYPES.get(Parser.addClzPrefix(typeName)); + TypeStruct clz = (TypeStruct)Parser.TYPES.get(TypeStruct.addClzPrefix(typeName)); if( clz==null ) throw P.error("Unknown struct type '" + ts._name + "'"); Field afld = clz.field(""); @@ -52,7 +52,7 @@ final class Allocations { TypeFunPtr tfp = (TypeFunPtr)afld._t; FunNode fun = P._code.link(tfp); alloc = fun==null - ? new FRefNode((Parser.addClzPrefix(typeName)+".").intern(),P.loc()) + ? new FRefNode((TypeStruct.addClzPrefix(typeName)+".").intern(),P.loc()) : new FunPtrNode(tfp,P._code._start,fun.ret()).peephole(); } else { if( !ts._fref ) @@ -60,7 +60,7 @@ final class Allocations { Type[] sig = new Type[ctorArgs._len]; for( int i = 0; i").intern(),P.loc(), + alloc = new FRefNode((TypeStruct.addClzPrefix(typeName)+".").intern(),P.loc(), TypeFunPtr.make(false,sig,TypeMemPtr.make(ts))); } Ary args = new Ary<>(Node.class); @@ -83,7 +83,7 @@ final class Allocations { Field lenFld = ts.field("#" ); Field bodyFld = ts.field("[]"); - ConFldOffNode base = Parser.off(ts, "[]"); + ConFldOffNode base = ConFldOffNode.make(ts, "[]"); int scale = ts.aryScale(); Node size = P.peep(new AddNode(base,P.peep(new ShlNode(null,len,P.con(scale))))); @@ -95,7 +95,7 @@ final class Allocations { // Store length. Rest of array is zero'd via CALLOC during CodeGen. // Length is casted to sanity. // TODO: Needs runtime check. - Node st = P.peep(new StoreNode(null, "#", 1, TypeInteger.U32, null, smem, self, Parser.off(ts,"#"), len.unkeep(), true)); + Node st = P.peep(new StoreNode(null, "#", 1, TypeInteger.U32, null, smem, self, ConFldOffNode.make(ts,"#"), len.unkeep(), true)); // TODO: Allow add-on "constructor" to init the array elements @@ -145,7 +145,7 @@ final class Allocations { for( int i = 0; i> narrow int // int + flt ==>> error, caller must fail assigning flt into int diff --git a/chapter29/src/main/java/com/seaofnodes/simple/parser/Lexer.java b/chapter29/src/main/java/com/seaofnodes/simple/parser/Lexer.java index f2857c9f..ec88738b 100644 --- a/chapter29/src/main/java/com/seaofnodes/simple/parser/Lexer.java +++ b/chapter29/src/main/java/com/seaofnodes/simple/parser/Lexer.java @@ -298,6 +298,11 @@ public class Lexer { // Distinct tags for the multi-character compound operators. static final char SHL = 2, SAR = 3, SHR = 4; + // ch is +/- 1, means oper++ or oper-- means postfix. + static boolean postfix(char ch) { + return (byte)ch== 1 || (byte)ch== -1; + } + public char matchOperAssign() { skipWhiteSpace(); if( match("<<=" ) ) return SHL; diff --git a/chapter29/src/main/java/com/seaofnodes/simple/parser/Parser.java b/chapter29/src/main/java/com/seaofnodes/simple/parser/Parser.java index 913fd2b2..ccc9ebdc 100644 --- a/chapter29/src/main/java/com/seaofnodes/simple/parser/Parser.java +++ b/chapter29/src/main/java/com/seaofnodes/simple/parser/Parser.java @@ -30,9 +30,6 @@ public class Parser { // The Lexer. Thin wrapper over a byte[] buffer with a cursor. Lexer _lexer; - // Class-object name, distinct from its instance type. - public static final String CLZ = "class:"; - // Source file for compilation public CompUnit _ref; @@ -142,7 +139,7 @@ public class Parser { // File-level struct declaration _ref = ref; _nestedType = ref._cname; - String typeName = addClzPrefix(ref._cname); + String typeName = TypeStruct.addClzPrefix(ref._cname); // Parse whole file, which is a struct-level class initializer structs.parseInitializer( true, typeName ); @@ -163,32 +160,58 @@ public class Parser { return frefs; } - @Override - public String toString() { return _lexer==null ? "" : _lexer.toString(); } + // Cursor and token helpers - // Debugging utility to find a Node by index - public Node f(int nid) { return _code.f(nid); } - public static String addClzPrefix( String x ) { - assert !x.startsWith(CLZ); - return (CLZ+x).intern(); + // Return true and skip if "syntax" is next in the stream. + boolean match (String syntax) { return _lexer.match (syntax); } + // Match must be "exact", not be followed by more id letters + boolean matchx(String syntax) { return _lexer.matchx(syntax); } + boolean matchOp(char c0 ) { return _lexer.matchOp(c0); } + boolean matchOpx(char c0, char c1) { return _lexer.matchOpx(c0,c1); } + // Skip whitespace, but leave the next token unread. + boolean peek(char ch) { return _lexer.peek(ch); } + + public int pos() { return _lexer._position; } + int pos(int pos) { + int old = _lexer._position; + _lexer._position = pos; + return old; + } + Type posT(int pos) { _lexer._position = pos; return null; } + // Source code location + Lexer loc() { return new Lexer(_lexer); } + + // Require and return an identifier + String requireId() { + String id = _lexer.matchId(); + if (id != null && !KEYWORDS.contains(id) ) return id.intern(); + throw error("Expected an identifier, found '"+id+"'"); } - public static boolean startsClzPrefix( String s ) { - return s.startsWith(CLZ); + + String matchId() { + int old = pos(); + String id = _lexer.matchId(); + if( id==null ) return null; + if( !KEYWORDS.contains(id) ) return id; + pos(old); + return null; } - public static boolean isPublicClassName( String name ) { - if( startsClzPrefix(name) ) name = name.substring(CLZ.length()); - return !name.isEmpty() && name.charAt(0)!='_' && name.indexOf("._")==-1; + + // Require an exact match + Parser require(String syntax) { require(null, syntax); return this; } + N require(N n, String syntax) { + if (match(syntax)) return n; + throw errorSyntax(syntax); } + // Scope, control, and memory + // Read and set CTRL Node ctrl() { return _scope.ctrl(); } N ctrl(N n) { return _scope.ctrl(n); } // Read and set Memory Node mem() { return _scope.mem(1); } void mem(Node mem) { _scope.mem(mem); } - // Structural label for precise memory Phis. The '$' prefix cannot be - // referenced in Simple source. - public static String memName(int alias) { return ("$"+alias).intern(); } // Ordinary parsing carries one whole-memory value. Alias partitioning is // graph optimization, not parser state. @@ -196,16 +219,6 @@ public class Parser { mem(alias==1 ? st : mergeAlias(prior,alias,st)); } - // A semantic graph partition representing the complete memory after a - // precise alias update. ScopeNode treats this as one opaque value. - Node mergeAlias(Node bulk, int alias, Node precise) { - assert bulk != null : "Missing bulk memory for alias #"+alias; - MemMergeNode merge = new MemMergeNode(null,bulk); - merge.alias(alias,precise); - return peep(merge); - } - ConstantNode con( long con ) { return _code.con(con); } - ConstantNode con( Type t ) { return _code.con(t ); } void classInit(Lexer site, Node ptr) { // Ordinary load machinery resolves the flag alias and offset when // the receiver is still a forward reference. @@ -217,14 +230,23 @@ public class Parser { Node bulk = new ProjNode(init,1,ScopeNode.MEM0).peephole().keep(); flag = init.flag(); mem(flag==null ? bulk : mergeAlias(bulk,flag._alias, - new ProjNode(init,2,memName(flag._alias)).peephole())); + new ProjNode(init,2,ScopeNode.memName(flag._alias)).peephole())); bulk.unkeep(); init.unkeep(); } - // Field offset for a known type (but unknown offset) - public static ConFldOffNode off( TypeStruct ts, String fname ) { - return (ConFldOffNode)(new ConFldOffNode(ts,fname).peephole()); + // IR construction using the current parser context + + ConstantNode con( long con ) { return _code.con(con); } + ConstantNode con( Type t ) { return _code.con(t ); } + + // A semantic graph partition representing the complete memory after a + // precise alias update. ScopeNode treats this as one opaque value. + Node mergeAlias(Node bulk, int alias, Node precise) { + assert bulk != null : "Missing bulk memory for alias #"+alias; + MemMergeNode merge = new MemMergeNode(null,bulk); + merge.alias(alias,precise); + return peep(merge); } // Field offset for an unknown type public Node fldoff( Node n, String name ) { @@ -237,57 +259,7 @@ public class Parser { return _scope.upcastGuard(n.peephole()); } - // Parse a string or null - - ////////////////////////////////// - // Utilities for lexical analysis - - // Return true and skip if "syntax" is next in the stream. - boolean match (String syntax) { return _lexer.match (syntax); } - // Match must be "exact", not be followed by more id letters - boolean matchx(String syntax) { return _lexer.matchx(syntax); } - boolean matchOp(char c0 ) { return _lexer.matchOp(c0); } - boolean matchOpx(char c0, char c1) { return _lexer.matchOpx(c0,c1); } - // Return true and do NOT skip if 'ch' is next - boolean peek(char ch) { return _lexer.peek(ch); } - - // ch is +/- 1, means oper++ or oper-- means postfix - static boolean postfix(char ch) { - return (byte)ch== 1 || (byte)ch== -1; - } - - public int pos() { return _lexer._position; } - int pos(int pos) { - int old = _lexer._position; - _lexer._position = pos; - return old; - } - Type posT(int pos) { _lexer._position = pos; return null; } - // Source code location - Lexer loc() { return new Lexer(_lexer); } - - // Require and return an identifier - String requireId() { - String id = _lexer.matchId(); - if (id != null && !KEYWORDS.contains(id) ) return id.intern(); - throw error("Expected an identifier, found '"+id+"'"); - } - - String matchId() { - int old = pos(); - String id = _lexer.matchId(); - if( id==null ) return null; - if( !KEYWORDS.contains(id) ) return id; - pos(old); - return null; - } - - // Require an exact match - Parser require(String syntax) { require(null, syntax); return this; } - N require(N n, String syntax) { - if (match(syntax)) return n; - throw errorSyntax(syntax); - } + // Diagnostics ParseException errorSyntax(String syntax) { return _errorSyntax("expected `"+syntax+"`"); } private ParseException _errorSyntax(String msg) { @@ -295,4 +267,12 @@ public class Parser { } ParseException error(String msg) { return error(msg,_lexer); } public static ParseException error(String msg, Lexer loc) { return new ParseException(msg,loc); } + + // Debugging + + @Override + public String toString() { return _lexer==null ? "" : _lexer.toString(); } + + // Debugging utility to find a Node by index + public Node f(int nid) { return _code.f(nid); } } diff --git a/chapter29/src/main/java/com/seaofnodes/simple/parser/README.md b/chapter29/src/main/java/com/seaofnodes/simple/parser/README.md index 02a1a2d0..e3e94b97 100644 --- a/chapter29/src/main/java/com/seaofnodes/simple/parser/README.md +++ b/chapter29/src/main/java/com/seaofnodes/simple/parser/README.md @@ -24,3 +24,8 @@ productions to their entry points. Start with `Statements.parseStatement` or `Expressions.parseAssignment` to follow the grammar. Loop updates are parsed after their bodies, and declared constructor bodies after field discovery; `Lexer.skipTo` scans these deferred fragments. + +`Parser` keeps the short cursor, scope/memory, IR-building, and diagnostic helpers +used by these components. Independent helpers live with their data: class-object +names in `TypeStruct`, postfix operator encoding in `Lexer`, known field-offset +creation in `ConFldOffNode.make`, and memory-alias labels in `ScopeNode.memName`. diff --git a/chapter29/src/main/java/com/seaofnodes/simple/parser/References.java b/chapter29/src/main/java/com/seaofnodes/simple/parser/References.java index 4b474cb8..a22383cf 100644 --- a/chapter29/src/main/java/com/seaofnodes/simple/parser/References.java +++ b/chapter29/src/main/java/com/seaofnodes/simple/parser/References.java @@ -46,7 +46,7 @@ final class References { CompUnit cu = ParseAll.findCompUnit(P._code,P._ref,id); if( cu!=null ) { // Must be a final class-type - String clzName = Parser.addClzPrefix(cu._cname); + String clzName = TypeStruct.addClzPrefix(cu._cname); TypeStruct clz = (TypeStruct)Parser.TYPES.get(clzName); if( clz == null ) Parser.TYPES.put(clzName,clz = TypeStruct.make(clzName,true)); @@ -113,8 +113,8 @@ final class References { checkSharedFunction(var); Node op = P.expressions.opAssign(ch,rvalue, var.type() ); P._scope.update(var,op); - if( Parser.postfix(ch) ) rvalue.unkeep(); - return pre==0 && Parser.postfix(ch) ? rvalue : op; + if( Lexer.postfix(ch) ) rvalue.unkeep(); + return pre==0 && Lexer.postfix(ch) ? rvalue : op; } void checkAssignment(Var def) { @@ -210,8 +210,8 @@ final class References { } Field fld = ts==null ? null : ts.field(name); - if( fld==null && ts!=null && !Parser.startsClzPrefix(ts._name) ) { - Type clz0 = Parser.TYPES.get(Parser.addClzPrefix(ts._name)); + if( fld==null && ts!=null && !TypeStruct.startsClzPrefix(ts._name) ) { + Type clz0 = Parser.TYPES.get(TypeStruct.addClzPrefix(ts._name)); if( clz0 instanceof TypeStruct clz && (fld = P.types.latestStruct(clz).field(name)) != null ) { expr.unkeep(); ts = P.types.latestStruct(clz); @@ -227,7 +227,7 @@ final class References { // need to force class initialization. Basically, we insert an CLInit, // and if it does not fold away it'll turn into a runtime check. Kind.Func func = (Kind.Func)P._scope._kinds.at(P._scope.enclosingFuncOrDecl()); - if( (ts==null || ts==TypeStruct.BOT || ts.isHigh() || Parser.startsClzPrefix(ts._name)) && + if( (ts==null || ts==TypeStruct.BOT || ts.isHigh() || TypeStruct.startsClzPrefix(ts._name)) && !(ts!=null && (ts._name+".").equals(func._name)) ) P.classInit(site,expr); @@ -287,8 +287,8 @@ final class References { mem.unkeep(); op.unkeep(); // A narrow Store may have folded away the expression's conversion. - if( Parser.postfix(ch) ) load.unkeep(); - return pre==0 && Parser.postfix(ch) ? load : op; + if( Lexer.postfix(ch) ) load.unkeep(); + return pre==0 && Lexer.postfix(ch) ? load : op; } off.unkill(); // Might be a method call, so pass expr as 'self' @@ -327,8 +327,8 @@ final class References { checkSharedFunction(v); Node op = P.expressions.opAssign(ch,value,v.type()); P._scope.update(v,op); - if( pre!=0 && Parser.postfix(ch) ) value.unkeep(); - return pre==0 && Parser.postfix(ch) ? value.unkeep() : op; + if( pre!=0 && Lexer.postfix(ch) ) value.unkeep(); + return pre==0 && Lexer.postfix(ch) ? value.unkeep() : op; } return P.expressions.parsePostfixMethod(value,defaultSelf()); } @@ -360,7 +360,7 @@ final class References { for( FRefNode fref : _frefs ) { if( fref.nIns() != 1 && fref._con == FRefNode.FREF_TYPE ) continue; CompUnit cu = ParseAll.findCompUnitOrThrow(P._code,P._ref,fref._name); - fref._con = TypeMemPtr.make((byte)2,TypeStruct.make(Parser.addClzPrefix(cu._cname),true),true); + fref._con = TypeMemPtr.make((byte)2,TypeStruct.make(TypeStruct.addClzPrefix(cu._cname),true),true); frefs.push(fref); } diff --git a/chapter29/src/main/java/com/seaofnodes/simple/parser/Structs.java b/chapter29/src/main/java/com/seaofnodes/simple/parser/Structs.java index db17cea3..57b43715 100644 --- a/chapter29/src/main/java/com/seaofnodes/simple/parser/Structs.java +++ b/chapter29/src/main/java/com/seaofnodes/simple/parser/Structs.java @@ -3,17 +3,11 @@ package com.seaofnodes.simple.parser; import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.node.ScopeNode.Kind; import com.seaofnodes.simple.type.*; -import com.seaofnodes.simple.util.Ary; -import java.util.IdentityHashMap; /** Struct and constructor declarations, field discovery, and initialization checks. */ final class Structs { private final Parser P; - // Constructor exit types survive scope teardown until all fields are - // known. A null entry denotes a constructor with no returning path. - Ary> _ctorExits; - // Constructor declaration: new TypeName = { args -> body }; record Constructor(String name, Lexer body) {} Constructor _pendingCtor; @@ -39,12 +33,12 @@ final class Structs { ReturnNode ret = parseInitializer( false, fullName ); - P._nestedType = old; + P._nestedType = old; // Pop nested stack // Insert a field into class:NESTED.typeName of "val typeName = fcn-ptr-to-init" // Function does malloc internally. // Future self: means subtyping has to deal with "who does the malloc" - String clzName = Parser.addClzPrefix(fullName); + String clzName = TypeStruct.addClzPrefix(fullName); TypeFunPtr sig = ret.fun().sig(); TypeStruct ts = (TypeStruct)Parser.TYPES.get(clzName); String initName = initFieldName(fullName); @@ -109,7 +103,7 @@ final class Structs { TypeStruct self = P._scope.constructorSelf(); Node ctor = P.functions.parseConstructor(name); TypeFunPtr sig = (TypeFunPtr)ctor._type; - String clzName = Parser.addClzPrefix(self._name); + String clzName = TypeStruct.addClzPrefix(self._name); TypeStruct clz = (TypeStruct)Parser.TYPES.get(clzName); Field old = clz==null ? null : clz.field(""); if( old != null ) @@ -122,8 +116,6 @@ final class Structs { // Parse a struct declaration (not an allocation); file-level is a // class-init, and scope structs are normal ReturnNode parseInitializer( boolean isClz, String typeName ) { - var oldExits = _ctorExits; - _ctorExits = new Ary(IdentityHashMap.class); // Record & restore global state set during parsing the code Node oldCtrl= P._scope.ctrl().keep(); Node oldMem = P._scope.mem ().keep(); @@ -147,7 +139,7 @@ final class Structs { // Struct decls look like function bodies. Parse function body normally. ReturnNode ret = P.functions.parseBody(fname,sig,P.loc(),ids); - _ctorExits = oldExits; + if( !isClz ) checkInitFields(ret); // Unwind global state. P.ctrl(oldCtrl.unkeep()); P.mem (oldMem .unkeep()); @@ -168,8 +160,8 @@ final class Structs { String clzBare = tself._name; int order = 3; // 3 is always the order, 2nd in the file just behind the if( isClz ) { - assert Parser.startsClzPrefix( clzBare ); - clzBare = clzBare.substring(Parser.CLZ.length()).intern(); + assert TypeStruct.startsClzPrefix( clzBare ); + clzBare = clzBare.substring(TypeStruct.CLZ.length()).intern(); order = 2; // 2 is always the order, first in file and skipping reserved #1 } return TypeFunPtr.make1((byte)2, true, targs, tret, P._code.fidx(clzBare,order)); @@ -232,20 +224,6 @@ final class Structs { smem._declaredType = smem._type = TypeMem.makePrivate(tself); } - if( !upgrade ) { - IdentityHashMap exit = null; - if( P.functions._returnScope!=null && !P.functions._returnScope.ctrl()._type.isHigh() ) { - exit = new IdentityHashMap<>(); - int first = P._scope._kinds.at(P._scope.depth()-2)._lexSize+2; - for( int i = first; i()); - for( IdentityHashMap exit : _ctorExits ) { - if( exit==null ) continue; - for( Field fld : ts._fields ) { - int idx = fieldVarIdx(base,fld._fname); - if( idx == -1 ) continue; - Var v = P._scope.var(idx); - if( !v._uninit || v._fref ) continue; - Type t = exit.getOrDefault(v,P.functions._returnScope.in(idx)._type); - if( !initialized(t) ) - throw Parser.error("'"+ts._name+"' is not fully initialized, field '"+fld._fname+ - (implicit ? "' needs to be set in a constructor" : - "' is only partially set in the constructor"),v._loc); - } + // addReturn already merged every exit. Inspect the completed helpers, + // after parsing has discovered all fields and torn down their scopes. + private void checkInitFields(ReturnNode init) { + if( init.ctrl()._type.isHigh() || init.expr()._type.isHigh() ) return; + TypeStruct ts = ((TypeMemPtr)init.fun().sig().arg(0))._obj; + TypeStruct clz = (TypeStruct)Parser.TYPES.get(TypeStruct.addClzPrefix(ts._name)); + Field ctor = clz==null ? null : clz.field(""); + // The older same-named function binding is parsed immediately, so + // fields declared after it are supplied by the declaration initializer. + Field legacy = ts.field(initFieldName(ts._name)); + if( ctor!=null ) checkInitFields(ts,init,P._code.link((TypeFunPtr)ctor._t).ret()); + if( legacy!=null && legacy._t instanceof TypeFunPtr sig ) + checkInitFields(ts,init,P._code.link(sig).ret()); + else if( ctor==null ) checkInitFields(ts,init,init); + } + + private void checkInitFields(TypeStruct ts, ReturnNode init, ReturnNode ret) { + if( ret.ctrl()._type.isHigh() || ret.expr()._type.isHigh() ) return; + TypeStruct fields = (TypeStruct)((TypeMem)ret.expr()._type)._t; + TypeStruct defaults = (TypeStruct)((TypeMem)init.expr()._type)._t; + for( Field fld : ts._fields ) { + if( fld._extern ) continue; + Field actual = fields.field(fld._fname); + if( actual==null ) actual = defaults.field(fld._fname); + if( actual._t==Type.TOP ) + throw Parser.error("'"+ts._name+"' is not fully initialized, field '"+fld._fname+ + (ret==init ? "' needs to be set in a constructor" : + "' is only partially set in the constructor"),ret.fun()._loc); } } @@ -326,7 +317,7 @@ final class Structs { Var v = P._scope.var(idx); if( v._fref || v._extern!=0 || v._uninit || P._scope.in(v)._type.isConstant() ) continue; Node value = P.conversions.convertExpr( - new LoadNode(loc,fld._fname,fld._alias,P.ctrl(),fun.parm(3),fun.parm(2),Parser.off(shape,fld._fname)).peephole(),v.type()); + new LoadNode(loc,fld._fname,fld._alias,P.ctrl(),fun.parm(3),fun.parm(2),ConFldOffNode.make(shape,fld._fname)).peephole(),v.type()); P._scope.update(v,value); } } @@ -336,7 +327,7 @@ final class Structs { // run-once flag. Keep this in the body so every execution strategy // shares the same ordering, including direct entry into a child. if( P._ref._par!=null ) { - String pname = Parser.addClzPrefix(P._ref._par._cname); + String pname = TypeStruct.addClzPrefix(P._ref._par._cname); TypeStruct parent = (TypeStruct)Parser.TYPES.get(pname); if( parent==null ) parent = TypeStruct.make(pname,true); Node ptr = P.con(TypeMemPtr.make((byte)2,parent,true)).keep(); @@ -352,8 +343,7 @@ final class Structs { prior.unkeep(); } - // Validate each user constructor, or an implicit empty one, before the - // declaration scope goes away. Later fields use their declaration defaults. + // Required-field values must be initialized before conversion for storage. static boolean initialized(Type t) { // Can't be BOTTOM, that means explicit not initialized. // Can't be high (initialized to an error) @@ -403,8 +393,8 @@ final class Structs { } private static String initFieldName( String typeName ) { - if( Parser.startsClzPrefix(typeName) ) - typeName = typeName.substring(Parser.CLZ.length()); + if( TypeStruct.startsClzPrefix(typeName) ) + typeName = typeName.substring(TypeStruct.CLZ.length()); int idx = typeName.lastIndexOf('.'); return (idx == -1 ? typeName : typeName.substring(idx+1)).intern(); } diff --git a/chapter29/src/main/java/com/seaofnodes/simple/parser/Types.java b/chapter29/src/main/java/com/seaofnodes/simple/parser/Types.java index 79ac9d12..970b5a50 100644 --- a/chapter29/src/main/java/com/seaofnodes/simple/parser/Types.java +++ b/chapter29/src/main/java/com/seaofnodes/simple/parser/Types.java @@ -86,7 +86,7 @@ final class Types { TypeMemPtr forwardRefType( String tname, TypeStruct tself ) { Parser.TYPES.put(tname,tself); - String clzName = Parser.addClzPrefix( tname ); + String clzName = TypeStruct.addClzPrefix( tname ); // Only a struct declaration publishes an instance initializer. A // forward type may name a file with just , so do not invent // a constructor field (or overwrite an already parsed class). diff --git a/chapter29/src/main/java/com/seaofnodes/simple/type/TypeStruct.java b/chapter29/src/main/java/com/seaofnodes/simple/type/TypeStruct.java index 4e73e148..9c0c9693 100644 --- a/chapter29/src/main/java/com/seaofnodes/simple/type/TypeStruct.java +++ b/chapter29/src/main/java/com/seaofnodes/simple/type/TypeStruct.java @@ -126,9 +126,9 @@ public class TypeStruct extends Type { // With a user constructor, the same-named function field is the hidden // default allocation helper, not part of the public class surface. public boolean hiddenInit(Field fld) { - if( field("") == null || !_name.startsWith("class:") ) return false; + if( field("") == null || !startsClzPrefix(_name) ) return false; int idx = _name.lastIndexOf('.'); - String name = _name.substring(idx<0 ? "class:".length() : idx+1); + String name = _name.substring(idx<0 ? CLZ.length() : idx+1); return fld._fname.equals(name); } @@ -653,4 +653,19 @@ public class TypeStruct extends Type { return is; } + + // Class-object name, distinct from its instance type. + public static final String CLZ = "class:"; + + public static String addClzPrefix( String x ) { + assert !x.startsWith(CLZ); + return (CLZ+x).intern(); + } + public static boolean startsClzPrefix( String s ) { + return s.startsWith(CLZ); + } + public static boolean isPublicClassName( String name ) { + if( startsClzPrefix(name) ) name = name.substring(CLZ.length()); + return !name.isEmpty() && name.charAt(0)!='_' && name.indexOf("._")==-1; + } } -- cgit v1.2.3