diff --git a/src/attrstore.nim b/src/attrstore.nim index 89a02f3..01ebc72 100644 --- a/src/attrstore.nim +++ b/src/attrstore.nim @@ -160,11 +160,14 @@ proc set*(ctx: RuntimeState, key: string, value: pointer, tid: TypeId, if curOpt.isSome(): curInfo = curOpt.get() + newInfo.moduleLock = curInfo.moduleLock if newInfo.override and not override: return true # Pretend it was successful. - if curInfo.locked: + if curInfo.locked or + (curInfo.moduleLock != 0 and + curInfo.moduleLock != ctx.curModule.moduleId): if not override: if not call_eq(value, curInfo.contents, curInfo.tid): return false @@ -179,6 +182,8 @@ proc set*(ctx: RuntimeState, key: string, value: pointer, tid: TypeId, if ctx.running: newInfo.lastset = addr ctx.curModule.instructions[ctx.ip] + if ctx.module_lock_stack.len() != 0: + newInfo.moduleLock = ctx.module_lock_stack[^1] # Don't trigger write lock if we're setting a default (i.e., internal is set). if (lock or wlock) and not internal: diff --git a/src/codegen.nim b/src/codegen.nim index a6828d6..97750b6 100644 --- a/src/codegen.nim +++ b/src/codegen.nim @@ -18,7 +18,7 @@ # defer typing things at the top-level; this will be a subtlety we # deal with when we get to doing the REPL. -import "."/irgen +import "."/[irgen, compile] import ztypes/api proc findAndLoadModule(ctx: CompileCtx, location, fname, ext: string): @@ -174,6 +174,8 @@ proc rawReprInstructions*(module: ZModuleInfo, ctx: ZObjectFile, fn = 0): Rope = of ZTCall: arg1 = text(hex(item.arg)) lbl = em(tCallReverseMap[item.arg]) + of ZCallModule: + arg1 = text($(item.arg)) of Z0Call: # Look up the symbol name by moduleId. It's always going to be a module, # so it's really about indexing into the right module in the @@ -308,10 +310,11 @@ proc rawReprInstructions*(module: ZModuleInfo, ctx: ZObjectFile, fn = 0): Rope = proc disassembly*(ctx: ZObjectFile, skip_nops = true): Rope = for item in ctx.moduleContents: - if item.instructions.len() == 3 and skip_nops: + if item.instructions.len() == 4 and skip_nops: if item.instructions[0].op == ZNop and - item.instructions[1].op == ZModuleRet and - item.instructions[2].op == ZNop: + item.instructions[1].op == ZModuleEnter and + item.instructions[2].op == ZModuleRet and + item.instructions[3].op == ZNop: continue result = result + rawReprInstructions(item, ctx) @@ -886,6 +889,7 @@ proc genExternCall(ctx: CodeGenState, cur: IrContents) = if tid != TVoid: ctx.emitInstruction(ZPushRes, tid = tid) + proc genRunCallback(ctx: CodeGenState, cur: IrContents) = var i = cur.actuals.len() @@ -1197,9 +1201,6 @@ proc oneIrNode(ctx: CodeGenState, n: IrNode) = ctx.curNode = saved -proc addParameter(ctx: CodeGenState, symbol: SymbolInfo) = - discard - proc stashSymbolInfo(scope: Scope, d: var Dict[int, string], t: var seq[(int, TypeId)]) = for (name, sym) in scope.table.items(sort=true): @@ -1233,6 +1234,74 @@ proc genFunction(ctx: CodeGenState, fn: FuncInfo) = ctx.mcur.objInfo.codesyms[zinfo.offset] = fullname +proc stashParam(ctx: CodeGenState, m: Module, info: ParamInfo) = + + var zparam = ZParamInfo(shortdoc: info.shortdoc, + longdoc: info.longdoc) + + if info.sym.isAttr: + zparam.attr = info.sym.name + else: + zparam.offset = info.sym.offset + + if info.defaultIr.isSome(): + # TODO: do I need to check if this is constant? Don't remember + zparam.haveDefault = true + zparam.default = info.defaultIr.get().value + + zparam.tid = info.sym.tid + + if info.validatorIr.isSome(): + var + offset = info.backpatchLoc + ### m.instructions[offset] is ZPushVmPtr + ### next one is ZRunCallback + litnode = info.validatorIr.get() + cb = cast[ptr Callback](litnode.value) + + if cb.impl.externInfo != nil: + # TODO Shouldn't the offset already be in cb.impl? + # Not sure we need to loop; just copying from above. + zparam.native = false + for i, item in ctx.zobj.ffiInfo: + let + p = cast[pointer](addr ctx.zobj.staticData[item.nameOffset]) + s = $(cast[cstring](p)) + + if s == cb.impl.externName: + zparam.funcIx = int32(i) + m.objinfo.instructions[offset].arg = zparam.funcIx + m.objinfo.instructions[offset].typeInfo = zparam.tid + break + else: + zparam.native = true + zparam.funcIx = int32(cb.impl.internalId) + m.objinfo.instructions[offset].arg = zparam.funcIx + + offset += 1 + m.objinfo.instructions[offset].typeInfo = cb.tid + + else: + zparam.funcIx = -1 + + + m.objInfo.parameters.add(zparam) + +proc placeholdParamValidation(ctx: CodeGenState, m: Module, i: int) = + m.params[i].backpatchLoc = m.objInfo.instructions.len() + ctx.emitInstruction(ZPushVmPtr) + ctx.emitInstruction(ZRunCallback, arg = 1) + ctx.emitInstruction(ZMoveSp, -1) + ctx.emitInstruction(ZPushRes) + +proc genParamChecks(ctx: CodeGenState, m: Module) = + for i, param in m.params: + if param.validatorIr.isNone(): + continue + ctx.genLoadSymbol(param.sym) + ctx.placeholdParamValidation(m, i) + ctx.emitInstruction(ZParamCheck, arg = i) + proc genModule(ctx: CodeGenState, m: Module) = let curMod = ctx.minfo[m.key] if curMod.processed: @@ -1241,10 +1310,10 @@ proc genModule(ctx: CodeGenState, m: Module) = ctx.mcur = curMod ctx.genLabel("Module '" & m.modname & "' :") + ctx.emitInstruction(ZModuleEnter, arg = m.params.len()) - for (_, sym) in m.moduleScope.table.items(sort=true): - if sym.pInfo != nil: - ctx.addParameter(sym) + if m.params.len() != 0: + ctx.genParamChecks(m) ctx.oneIrNode(m.ir) ctx.emitInstruction(ZModuleRet) @@ -1258,6 +1327,9 @@ proc genModule(ctx: CodeGenState, m: Module) = continue ctx.genFunction(fn) + for param in m.params: + ctx.stashParam(m, param) + curMod.processed = true let deps = ctx.minfo.items(sort = true) @@ -1289,7 +1361,7 @@ proc applyArenaId(ctx: CodeGenState, scope: Scope, moduleId: int, proc applyArenasToGloballyVisibleFuncs(ctx: CodeGenState, scope: Scope, curFnId: var int) = - for (name, sym) in scope.table.items(sort=true): + for (name, sym) in scope.table.items(sort = true): for fn in sym.fimpls: if fn.externInfo != nil: continue @@ -1299,7 +1371,7 @@ proc applyArenasToGloballyVisibleFuncs(ctx: CodeGenState, scope: Scope, mid = fn.defModule.objInfo.moduleId fi = ZFnInfo(funcname: name, mid: int32(mid), tid: fn.tid, offset: int32(fn.codeOffset)) - ctx.zobj.funcInfo.adD(fi) + ctx.zobj.funcInfo.add(fi) fi.syms.initDict() fn.fnScope.stashSymbolInfo(fi.syms, fi.symTypes) curFnId += 1 @@ -1401,6 +1473,27 @@ proc extractSectionDocs(ctx: CodeGenState, let docObj = AttrDocs(shortdoc: node.shortdoc, longdoc: node.longdoc) d[sec] = docObj +template setupOneModule() = + var mi = ZModuleInfo(moduleId: curArena, modname: module.modname, + key: module.key, ext: module.ext, url: module.url, + moduleVarSize: module.moduleScope.scopeSize) + module.objInfo = mi + ctx.applyArenaId(module.moduleScope, curArena, curFnId) + curArena = curArena + 1 + ctx.minfo[module.key] = module + ctx.zobj.moduleContents.add(mi) + mi.codesyms.initDict() + mi.datasyms.initDict() + + # TODO: I have disabled eliding source code until incremental + # compilation is properly done. + + #if not module.system: + mi.source = $(module.s.runes) + + module.moduleScope.stashSymbolInfo(mi.datasyms, mi.symTypes) + mi.codesyms[0] = module.modname & ".__mod_run__()" + proc setupModules(ctx: CodeGenState, d: Dict[string, AttrDocs]) = # This call applies a unique number to each module # and then applies a unique number to each function. @@ -1417,32 +1510,29 @@ proc setupModules(ctx: CodeGenState, d: Dict[string, AttrDocs]) = ctx.cc.globalScope.stashSymbolInfo(ctx.zobj.globals, ctx.zobj.symTypes) ctx.zobj.globalScopeSz = ctx.cc.globalScope.scopeSize - for (_, module) in ctx.cc.modules.items(sort=true): + for (_, module) in ctx.cc.modules.items(sort = true): ctx.addFfiInfo(module) ctx.extractSectionDocs(module, d) - for (_, module) in ctx.cc.modules.items(sort=true): - var mi = ZModuleInfo(moduleId: curArena, modname: module.modname, - moduleVarSize: module.moduleScope.scopeSize) - module.objInfo = mi - ctx.applyArenaId(module.moduleScope, curArena, curFnId) - curArena = curArena + 1 - ctx.minfo[module.key] = module - ctx.zobj.moduleContents.add(mi) - mi.codesyms.initDict() - mi.datasyms.initDict() - - if not module.system: - mi.source = $(module.s.runes) - - module.moduleScope.stashSymbolInfo(mi.datasyms, mi.symTypes) - mi.codesyms[0] = module.modname & ".__mod_run__()" - + for (_, module) in ctx.cc.modules.items(sort = true): + setupOneModule() # Finally, apply module IDs to the functions that are globally visible # so we can look up their addresses. ctx.applyArenasToGloballyVisibleFuncs(ctx.cc.globalScope, curFnId) +proc setupNewModules(ctx: CodeGenState, d: Dict[string, AttrDocs]) = + # The incremental version. + var + curArena = ctx.zobj.moduleContents[^1].moduleId + 1 + curFnId = ctx.zobj.funcInfo.len() + 1 + + for (_, module) in ctx.cc.modules.items(sort = true): + if module.objInfo != nil: + continue + + setupOneModule() + proc fillCallBackpatches(ctx: CodeGenState) = for (fn, dstmodule, patchloc) in ctx.callBackpatches: var cur = dstmodule.objInfo.instructions[patchloc] @@ -1455,8 +1545,7 @@ proc cacheAllTypeInfo(ctx: CodeGenState) = # output, we go ahead and keep any type that does not forward. This # is far less error prone than trying to make sure we always # remember to save off type info, but it might end up causing too - # much bloat. I doubt it, though! However, we definitely should - # collect metrics on this. + # much bloat. I doubt it, though! for (id, tObj) in typeStore.items(sort=true): if tObj.typeId != id: @@ -1468,12 +1557,7 @@ proc cacheAllTypeInfo(ctx: CodeGenState) = discard ctx.zobj.tInfo.add(id, loc) - var typeCache: seq[string] - for (k, v) in ctx.zobj.tInfo.items(sort=true): - typeCache.add($(cast[int](k))) - typeCache.add($(cast[cstring](addr ctx.zobj.staticdata[v]))) - -proc generateCode*(cc: CompileCtx, nextId = -1): RuntimeState = +proc generateInitialCodeObject*(cc: CompileCtx, nextId = -1): RuntimeState = var ctx = CodeGenState() result = RuntimeState() @@ -1497,5 +1581,99 @@ proc generateCode*(cc: CompileCtx, nextId = -1): RuntimeState = result.obj.nextEntrypoint = int32(nextId) result.obj.spec = ctx.cc.attrSpec +when false: + proc setup_incremental_compile*(ctx: RuntimeState): CompileCtx = + result = ctx.newCompileContext(ctx.obj.spec) + result.incremental = true + + for (attr, contents) in ctx.attrs: + var sym = SymbolInfo(name: attr, tid: contents.tid, + isAttr: true, inObj: true) + result.usedAttrs.table[attr] = sym + + for (offset, name) in ctx.obj.globals.items(): + var sym = SymbolInfo(name: name) + + sym.tid = x + sym.constValue = x + sym.declaredType = x + sym.immutable = x + sym.global = true + sym.inObj = true + sym.offset = offset + sym.moduleId = 0 + + result.globalScope.table[name] = sym + + for zmod in ctx.obj.moduleContents: + var module = Module(loaded: true, system: true, modname: zmod.modname, + where: zmod.location, ext: zmod.ext, key: zmod.key, + exitNode: CfgNode(), processed: true, + objinfo: zmod) + result.modules[module.key] = module + + # global functions + # ffi functions + # module stubs +else: + proc setup_incremental_compile*(ctx: RuntimeState): CompileCtx = + # Assume this all is going to just work... + result = CompileCtx() + + result.globalScope = initScope() + result.usedAttrs = initScope() + result.modules.initDict() + + let entrypointix = ctx.obj.entrypoint + var entry: Module + + var modlist: seq[Module] + + for zmod in ctx.obj.moduleContents: + let m = newModuleObj(result, zmod.source, zmod.modname, zmod.location, + zmod.ext, zmod.url, zmod.key) + if zmod.moduleId == entrypointix: + entry = m + + m.objInfo = zmod + result.modules[zmod.key] = m + + modlist.add(m) + + for m in modlist: + m.system = true + result.loadModule(m) + + discard result.build_program(entry) + + for m in modlist: + m.processed = true + +proc generate_incremental_code*(rt: RuntimeState, cc: CompileCtx, + newEntry: Module) = + # TODO -- Rebuild the string cache? + let start = rt.obj.moduleContents.len() + var ctx = CodeGenState() + ctx.cc = cc + ctx.zobj = rt.obj + ctx.memos = Memos() + rt.obj.spec = cc.attrSpec + ctx.minfo = cc.modules + + ctx.strCache.initDict() + ctx.memos.map.initDict() + ctx.cacheAllTypeInfo() + ctx.setupNewModules(rt.sectionDocs) + ctx.genModule(newEntry) + ctx.fillCallBackpatches() + + rt.obj.entrypoint = int32(newEntry.objInfo.moduleId) + + for i, item in rt.obj.moduleContents: + if i < start: + continue + var moduleAllocation = newSeq[pointer](item.moduleVarSize * 2) + rt.moduleAllocations.add(moduleAllocation) + # TODO -- pushTypeOf needs to handle attributes. # TODO -- varargs for func entry. diff --git a/src/commands/objdump.nim b/src/commands/objdump.nim index 7987b2a..125c8f0 100644 --- a/src/commands/objdump.nim +++ b/src/commands/objdump.nim @@ -19,6 +19,33 @@ proc get_basic_object_info*(obj: ZObjectFile): Rope = result = cells.quickTable(title = "Basic object info", verticalHeaders = true, caption = config_args[0].resolvePath()) +proc format_module_params*(obj: ZObjectFile, params: seq[ZParamInfo]): Rope = + var cells: seq[seq[string]] = @[@["Attr or offset", + "Type", + "Native func?", + "Function index", "Short Doc", "Long Doc"]] + for item in params: + var row: seq[string] = @[] + if item.attr != "": + echo "Attr = ", item.attr + row.add(item.attr) + else: + echo "offset = ", item.offset + row.add($item.offset) + row.add(item.tid.toString()) + if item.native: + row.add("✓") + else: + row.add("✗") + row.add($item.funcIx) + row.add(item.shortdoc & " ") + row.add(item.longdoc & " ") + cells.add(row) + + return cells.quickTable(title = "Module param info", + caption = "Sorry, haven't had the dump look up " & + "local names or functions yet") + proc get_per_module_info*(obj: ZObjectFile): Rope = var cells: seq[seq[string]] @@ -76,6 +103,11 @@ proc get_per_module_info*(obj: ZObjectFile): Rope = result += h2(text("Source code for ") + em(item.modname)) result += item.source.pretty() + if item.parameters.len() == 0: + result += h2("No stored parameters.") + else: + result += obj.format_module_params(item.parameters) + result += h2(text("Disassembly for ") + em(item.modname)) result += item.rawReprInstructions(obj) diff --git a/src/commands/run.nim b/src/commands/run.nim index 742b285..c630f2b 100644 --- a/src/commands/run.nim +++ b/src/commands/run.nim @@ -110,9 +110,8 @@ proc cmd_resume*() = rt.run_object(fname, resumed = true) -proc base_compile*(ctx: CompileCtx): RuntimeState = +proc base_compile(ctx: CompileCtx, entryname: string): bool = let - entryname = config_args[0] can_proceed = ctx.buildFromEntryPoint(entryname) entry = ctx.entrypoint @@ -130,19 +129,19 @@ proc base_compile*(ctx: CompileCtx): RuntimeState = if config_debug: if entry.tokens.len() == 0: - return nil + return false print(h1("Tokens for module '" & entry.modname & "'")) entry.printTokens() if entry.root == nil: - return nil + return false print(h1("Parse tree for module '" & entry.modname & "'")) entry.printParseTree() if entry.ir == nil: - return nil + return false print(h1("IR for module '" & entry.modname & "'")) entry.printIr() @@ -174,13 +173,47 @@ proc base_compile*(ctx: CompileCtx): RuntimeState = print(h1("Global CFG")) ctx.printProgramCfg() - if not ctx.printErrors(): + return can_proceed + +proc base_initial_compile*(ctx: CompileCtx): RuntimeState = + if not ctx.base_compile(config_args[0]) or not ctx.printErrors(): return nil - return ctx.generateCode() + return ctx.generateInitialCodeObject() + +proc cmd_add*() = + if config_args[1].endswith(obj_file_extension): + print(fgcolor("error: ", "red") + + text("Cannot compile object file ") + em(config_args[1]) + + text(". Please specify a module entry point.")) + quit(-4) + + config_save_object = true + + var + obj = config_args[0] + module = config_args[1] + rt = obj.load_object_file() + cc = rt.setup_incremental_compile() + + if cc.base_compile(module) and cc.printErrors(): + rt.generate_incremental_code(cc, cc.entryPoint) + + if config_debug: + print rt.obj.disassembly() + + rt.save_object_to_disk(cc.entrypoint.modname) proc cmd_compile*(ctx: CompileCtx) = - var rt = ctx.base_compile() + if config_args[0].endswith(obj_file_extension): + print(fgcolor("error: ", "red") + + text("Cannot compile object file ") + em(config_args[0]) + + text(". Please specify a module entry point.")) + quit(-4) + + config_save_object = true + + var rt = ctx.base_initial_compile() if rt == nil or rt.obj == nil: quit(-1) @@ -197,7 +230,7 @@ proc cmd_compile*(ctx: CompileCtx) = proc cmd_run*(ctx: CompileCtx) = var - rt = ctx.base_compile() + rt = ctx.base_initial_compile() if rt == nil or rt.obj == nil: quit(-1) diff --git a/src/commands/spec.c4m b/src/commands/spec.c4m index 31a348a..20ab39c 100644 --- a/src/commands/spec.c4m +++ b/src/commands/spec.c4m @@ -115,6 +115,29 @@ Future runs can save a new entry point via the `--entry` flag. args: (1, 1) } + command add { + " Add a new module into an object." +""" +## Incrementally compiles a new module, updating the object file. + +This command will compile the new module and any dependencies +necessary that aren't already in the object file, updating the object +file, and setting the new module as the entry point to run. + +#### Parameters + +1. The object file +2. The new entry point module to add. + +#### Output + +The new object does *not* overwrite the original object file. Instead, +a new object file (.0c00l extension) will get added, using the name +of your new entrypoint. +""" + args: (2, 2) + } + command check { "Parse and validate con4m code" diff --git a/src/common.nim b/src/common.nim index 867a2c2..f0da563 100644 --- a/src/common.nim +++ b/src/common.nim @@ -398,11 +398,43 @@ type ParamInfo* = ref object ## Module parameters. - shortdoc*: Option[string] - doc*: Option[string] - validator*: Option[Callback] + sym*: SymbolInfo + shortdoc*: string + longdoc*: string + validatorIr*: Option[IrNode] defaultParse*: Option[ParseNode] defaultIr*: Option[IrNode] + backpatchLoc*: int + + ZParamInfo* = ref object + ## What we stick in the obj file. + attr*: string # Either it's an attribute... + offset*: int # or in theerr current module. + default*: pointer + tid*: TypeId + haveDefault*: bool + native*: bool + funcIx*: int32 + userparam*: pointer + userType*: TypeId + shortdoc*: string + longdoc*: string + + ZParamExport* = ref object + ## What we pass when asked what parameters need to be provided. + ## Currently, con4m is NOT storing these parameters itself between + ## runs, but if the modules holding the parameter gets called, + ## there will be a value set, and we'll report that it's got + ## a default value. + name*: string + modname*: string + shortdoc*: string + longdoc*: string + modid*: int32 + paramid*: int32 + tid*: TypeId + havedefault*: bool + default*: pointer SymbolInfo* = ref object name*: string @@ -411,7 +443,6 @@ type uses*: seq[IrNode] defs*: seq[IrNode] fimpls*: seq[FuncInfo] - pInfo*: ParamInfo defaultVal*: pointer constValue*: pointer isFunc*: bool @@ -425,6 +456,9 @@ type err*: bool formal*: bool declNode*: ParseNode + pinfo*: ParamInfo + inObj*: bool + # heapalloc is only true for global variables and # indicates that the offset generated won't be relative to the # stack, but from some start offset associated with the module @@ -591,9 +625,9 @@ type # available (stashed in attrSpec). # First two fields are refs to the one in the compile context. - globalScope*: Scope - attrSpec*: ValidationSpec - declaredSpec*: ValidationSpec + globalScope*: Scope + attrSpec*: ValidationSpec + declaredSpec*: ValidationSpec moduleScope*: Scope funcScope*: Scope usedAttrs*: Scope @@ -628,6 +662,7 @@ type attrContext*: bool # True when we are def processing an attr. ambigAssign*: bool secDefContext*: bool + inParamCtx*: bool curSym*: SymbolInfo usedModules*: seq[(string, string)] funcsToResolve*: seq[(IrNode, TypeId, string)] @@ -636,20 +671,11 @@ type curSecSpec*: SectionSpec didFoldingPass*: bool # Might not be using this anymore. maxOffset*: int + params*: seq[ParamInfo] # These help us to fold. branchOrLoopDepth*: int - # We currently lift out any attrs set from a module if they are - # not nested in any control flow or function, if the values are - # constant, and there are no uses earlier in the file, or in a - # function. - # - # This is particularly useful for being able to statically apply - # specs. - - staticAttrs*: Dict[string, AttrContents] - # Used in code generation. processed*: bool loopLocs*: seq[(IrNode, int)] @@ -658,17 +684,19 @@ type secDocNodes*: seq[IRNode] CompileCtx* = ref object - modules*: Dict[string, Module] - defaultExt*: string = ".c4m" - attrSpec*: ValidationSpec - globalScope*: Scope - usedAttrs*: Scope - entrypoint*: Module - fatal*: bool - topExitNode*: CfgNode # Used when building CFG - modulePath*: seq[string] = @[".", "https://chalkdust.io/"] - sysdir*: string - errors*: seq[Con4mError] + incremental*: bool + modules*: Dict[string, Module] + defaultExt*: string = ".c4m" + attrSpec*: ValidationSpec + globalScope*: Scope + usedAttrs*: Scope + entrypoint*: Module + fatal*: bool + topExitNode*: CfgNode # Used when building CFG + modulePath*: seq[string] = @[".", "https://chalkdust.io/"] + sysdir*: string + errors*: seq[Con4mError] + # The remaining data structures are used in code generation and / or # runtime. Any type here with a Z in front is intended to get @@ -836,6 +864,15 @@ type ZModuleRet = 0x81, ZHalt = 0x82, + # Generated on module entry. If the module takes parameters, + # the argument is non-zero, and + # are checked and validated on entry to the module. + ZModuleEnter = 0x83, + + # Checks a single parameter once its validator has run, popping + # the string result off the stack, and erroring if it's not null. + ZParamCheck = 0x84, + # SetRet should be called by any function before exiting, where # that function is non-void. It moves the value at fp - 2 # into the return register. @@ -971,6 +1008,9 @@ type ZModuleInfo* = ref object modname*: string location*: string + key*: string + ext*: string + url*: string version*: string symTypes*: seq[(int, TypeId)] codesyms*: Dict[int, string] @@ -979,6 +1019,7 @@ type moduleId*: int moduleVarSize*: int initSize*: int # size of init code before functions begin. + parameters*: seq[ZParamInfo] instructions*: seq[ZInstruction] # Used in vm.nim @@ -994,6 +1035,7 @@ type isSet*: bool locked*: bool lockOnWrite*: bool + moduleLock*: int32 override*: bool contents*: pointer lastset*: ptr ZInstruction # (not marhshaled) @@ -1031,7 +1073,7 @@ type running*: bool memos*: Memos cmdline_info*: ArgResult - + module_lock_stack*: seq[int32] MixedObj* = object t*: TypeId diff --git a/src/compile.nim b/src/compile.nim index d549346..d2878ba 100644 --- a/src/compile.nim +++ b/src/compile.nim @@ -7,7 +7,7 @@ proc getRootSection(spec: ValidationSpec): SectionSpec {.importc, cdecl.} proc mergeStaticSpec(m: Module) {.cdecl, importc.} proc findAndLoadFromUrl(ctx: CompileCtx, url: string): Option[Module] {.exportc, cdecl.} -proc loadModule(ctx: CompileCtx, module: Module) +proc loadModule*(ctx: CompileCtx, module: Module) @@ -125,7 +125,7 @@ proc findAndLoadFromUrl(ctx: CompileCtx, url: string): Option[Module] = let (loc, name, ext) = url.splitFile() return ctx.findAndLoadModule(loc, name, ext) -proc loadModule(ctx: CompileCtx, module: Module) = +proc loadModule*(ctx: CompileCtx, module: Module) = # This is the thing that actually orchestrates the load once found. # # The module's global scope is for its own view on what symbols diff --git a/src/con4m.nim b/src/con4m.nim index 3e48085..54e5728 100644 --- a/src/con4m.nim +++ b/src/con4m.nim @@ -58,18 +58,12 @@ when isMainModule: session.cmd_pretty() elif config_cmd == "resume": cmd_resume() - elif config_cmd == "help": - rt.cmd_help() + elif config_cmd == "add": + cmd_add() elif config_cmd == "compile": - if config_args[0].endswith(obj_file_extension): - print(fgcolor("error: ", "red") + - text("Cannot compile object file ") + em(config_args[0]) + - text(". Please specify a module entry point.")) - quit(-4) - - config_save_object = true - session.cmd_compile() + elif config_cmd == "help": + rt.cmd_help() elif config_cmd == "run": if config_args[0].endswith(obj_file_extension): cmd_resume() diff --git a/src/err.nim b/src/err.nim index a5d8bc0..9079bb4 100644 --- a/src/err.nim +++ b/src/err.nim @@ -405,6 +405,24 @@ const errorMsgs = [ ("DupeRootSpec", "Should not have duplicate root section" & "in one module"), ("NotConst", "Value must be a constant at compile time."), + ("ParamValParTy", "The validation function for this parameter takes an " & + "argument that is inconsistent with the type we have " & + "for the parameter. Previously, we had $1, " & + "But the function takes a $2."), + ("ParamValNArgs", "Validation functions for parameters must take a " & + "single argument, where a value to validate will be " & + "passed. It must return a string; the empty string " & + "indicates no validation error. Otherwise, the return " & + "value should be an error message."), + ("ParamValRetTy", "Validation functions for parameters must return " & + "a string, which represents any error message to " & + "give as feedback."), + ("NoCbMatch", "Could not find a function to match to the callback " & + "$1$2"), + ("ParamNotSet", "Module parameter $1 was not set when entering " & + "module $2."), + ("ParamNotValid", "Module parameter $1 was set when entering " & + "module $2, but was not valid: $3"), ] # "CustomValidator" is possible, but not looked up in this array. @@ -786,7 +804,8 @@ proc runtimeIssue(ctx: RuntimeState, err: string, args: seq[string], let instr = addr ctx.curModule.instructions[ctx.ip] (m, l) = ctx.location_from_instruction(instr) - loc = "When executing " & m & ":" & $(l) + extra = if l == -1: "" else: ":" & $(l) + loc = "When executing " & m & extra print(err.formatLateError(severity, loc, args), file = stderr) @@ -802,7 +821,11 @@ proc runtimeError*(ctx: RuntimeState, err: string, args: seq[string] = @[]) = proc runtimeError*(ctx: RuntimeState, err: string, file: ZModuleInfo, line: int, args: seq[string] = @[]) = - let loc = "When executing " & file.modname & ":" & $(line) + var extra: string = "" + if line != -1: + extra = ":" & $(line) + + let loc = "When executing " & file.modname & extra ctx.print_con4m_trace() print(err.formatLateError(LlErr, loc, args), file = stderr) diff --git a/src/getopts.nim b/src/getopts.nim index cd7d297..7a06170 100644 --- a/src/getopts.nim +++ b/src/getopts.nim @@ -1417,7 +1417,7 @@ proc parse_command_line*(code: string, refname = "c4m_getopt"): RuntimeState = quit(-4) var - rt = ctx.generateCode() + rt = ctx.generateInitialCodeObject() exit = rt.executeObject() params = commandLineParams() diff --git a/src/irgen.nim b/src/irgen.nim index f121837..ea0ace3 100644 --- a/src/irgen.nim +++ b/src/irgen.nim @@ -199,12 +199,25 @@ proc extractShortDoc(n: ParseNode): Rope = else: return doc_extract(n.docNodes.children[0].getText()) +proc extractShortDocPlain(n: ParseNode): string = + if n == nil or n.docNodes == nil: + return "" + else: + return n.docNodes.children[0].getText() + proc extractLongDoc(n: ParseNode): Rope = if n == nil or n.docNodes == nil or len(n.docNodes.children) < 2: return nil else: return doc_extract(n.docNodes.children[1].getText()) +proc extractLongDocPlain(n: ParseNode): string = + if n == nil or n.docNodes == nil or len(n.docNodes.children) < 2: + return "" + else: + return n.docNodes.children[1].getText() + + proc fmt(s: string, x = "", y = "", t = TBottom): Rope = result = atom(s).fgColor("atomiclime") if x != "": @@ -601,7 +614,7 @@ proc extractSymInfo(ctx: Module, scope: Scope, isConst = false, for i, (name, tid, kid) in toAdd: let symOpt = ctx.scopeDeclare(scope, name, false, tid.getTid(), isConst, - declnode = kid) + declnode = kid, inparam = ctx.inParamCtx) if i == toAdd.len() - 1 and symOpt.isSome(): result = symOpt.get() @@ -648,8 +661,9 @@ proc convertConstStmt(ctx: Module) = proc convertParamBody(ctx: Module, sym: var SymbolInfo) = var - gotShort, gotLong, gotValid, gotDefault: bool - paramInfo = ParamInfo() + gotValid, gotDefault: bool + paramInfo = ParamInfo(shortdoc: ctx.pt.extractShortDocPlain(), + longdoc: ctx.pt.extractLongDocPlain()) independentSubtree: for i in 0 ..< ctx.numKids(): @@ -658,67 +672,80 @@ proc convertParamBody(ctx: Module, sym: var SymbolInfo) = continue let propname = ctx.getText(i, 0) case propname - of "shortdoc": - if gotShort: - ctx.irError("DupeParamProp", @["shortdoc"], ctx.pt.children[i]) - continue - if ctx.kidKind(i, 1) != NodeStringLit: - ctx.irError("BadParamProp", @["shortdoc", "string literal"], - ctx.pt.children[i]) - continue - paramInfo.shortdoc = some(ctx.getText(i, 1)) - gotShort = true - of "doc": - if gotLong: - ctx.irError("DupeParamProp", @["doc"], ctx.pt.children[i]) - continue - if ctx.kidKind(i, 1) != NodeStringLit: - ctx.irError("BadParamProp", @["doc", "string literal"], - ctx.pt.children[i]) - continue - paramInfo.doc = some(ctx.getText(i, 1)) - gotLong = true of "validator": - if ctx.kidKind(i, 1) != NodeCallbackLit: + let + irNode = ctx.downNode(i, 1) + to = irNode.tid.idToTypeRef() + + if to.kind != C4Func: ctx.irError("ParamType", @["validator", "callback"], ctx.pt.children[i]) - let irNode = ctx.downNode(i, 1) - ctx.typeCheck(irNode.tid, tFunc(@[sym.tid, TRich])) - let cb = extractRef[Callback](irNode.value) - paramInfo.validator = some(cb) - gotValid = true + elif to.items.len() != 0: + if to.items.len() != 2: + ctx.irError("ParamValNArgs") + else: + if unify(sym.tid, to.items[0]) == TBottom: + ctx.irError("ParamValParTy", @[sym.tid.toString(), + irnode.tid.toString()], + w = irNode.parseNode) + + elif unify(irNode.tid, tFunc(@[sym.tid, TString])) == TBottom: + ctx.irError("ParamValRetTy") + + paramInfo.validatorIr = some(irNode) + gotValid = true of "default": if gotDefault: ctx.irError("DupeParamProp", @["default"], ctx.pt.children[i]) continue + let irNode = ctx.downNode(i, 1) paramInfo.defaultParse = some(ctx.pt.children[i].children[1]) gotDefault = true + ctx.typeCheck(irNode.tid, sym.tid, where = irNode.parseNode) else: ctx.irError("BadParamProp", @[propname], ctx.pt.children[i]) continue - sym.pinfo = paramInfo + paramInfo.sym = sym + sym.pinfo = paramInfo + + ctx.params.add(paramInfo) proc convertParamBlock(ctx: Module) = var sym: SymbolInfo + var ir: IrNode + + ctx.inParamCtx = true + if ctx.pt.children[0].kind == NodeMember: independentSubtree: - let memberIr = ctx.downNode(0) - sym = ctx.scopeDeclare(ctx.usedAttrs, memberIr.contents.name, false, + ir = ctx.downNode(0) + sym = ctx.scopeDeclare(ctx.usedAttrs, ir.contents.name, false, tVar()).getOrElse(nil) - else: # will be something we can call extractSymInfo on. - var savedPt = ctx.pt - ctx.pt = ctx.pt.children[0] - sym = ctx.extractSymInfo(ctx.moduleScope, checkdollar = true) - ctx.pt = savedPt + else: + let + ir = IrNode() + n = ctx.pt.children[0].children[0] + name = n.getText() + opt = ctx.scopeDeclare(ctx.moduleScope, name, false, tid = tVar(), + declNode = n, inparam = true) + + if opt.isSome(): + sym = opt.get() + else: + unreachable # TODO, make sure this is true + + sym.defs.add(ir) var savedPt = ctx.pt ctx.pt = ctx.pt.children[1] ctx.convertParamBody(sym) ctx.pt = savedPt + ctx.inParamCtx = false + proc extractCTypes(ctx: Module, n: ParseNode, fn: var FuncInfo) = var info = ExternFnInfo() @@ -1371,7 +1398,7 @@ proc findDeclarations(ctx: Module, n: ParseNode) = for kid in n.children: ctx.pt = kid case kid.kind - of NodeFuncDef: + of NodeFuncDef, NodeParamBlock: continue of NodeVarStmt: ctx.convertVarStmt() @@ -1379,8 +1406,6 @@ proc findDeclarations(ctx: Module, n: ParseNode) = ctx.convertGlobalStmt() of NodeConstStmt: ctx.convertConstStmt() - of NodeParamBlock: - ctx.convertParamBlock() of NodeExternBlock: ctx.convertExternBlock() of NodeConfSpec: @@ -1411,6 +1436,10 @@ proc findDeclarations*(ctx: Module) = ctx.pt = item ctx.convertFuncDefinition() continue + of NodeParamBlock: + ctx.pt = item + ctx.convertParamBlock() + continue else: ctx.findDeclarations(item) @@ -2407,15 +2436,22 @@ proc resolveDeferredSymbols*(ctx: CompileCtx, m: Module) = n.contents.toCall = matches[0] continue elif matches.len() == 0: - - let info = showCallMistakes(n.contents.fname, fails, t) - m.irError("BadSig", n, @[n.contents.fname, t.toString(), "call"], - detail = info) + if n.contents.kind == IrLit: + m.irError("NoCbMatch", w = n, @[name, t.toString]) + else: + let info = showCallMistakes(n.contents.fname, fails, t) + m.irError("BadSig", n, @[n.contents.fname, t.toString(), "call"], + detail = info) else: - let info = fmtImplementationList(n.contents.fname, - matches, t).quicktable() - m.irError("CallAmbig", n, @[n.contents.fname, t.toString(), "call"], - detail = info) + if n.contents.kind == IrLit: + let info = fmtImplementationList(name, matches, t).quicktable() + m.irError("CallAmbig", w = n, @[name, t.toString(), "callback"], + detail = info) + else: + let info = fmtImplementationList(n.contents.fname, + matches, t).quicktable() + m.irError("CallAmbig", n, @[n.contents.fname, t.toString(), "call"], + detail = info) m.funcsToResolve = @[] diff --git a/src/modparams.nim b/src/modparams.nim new file mode 100644 index 0000000..72c7f92 --- /dev/null +++ b/src/modparams.nim @@ -0,0 +1,95 @@ +import "."/[stchecks, attrstore] + +proc get_parameter_info*(ctx: RuntimeState): seq[ZParamExport] = + ## Pass back the big list of all the parameters we know about, with + ## status information. + for module in ctx.obj.moduleContents: + for i, param in module.parameters: + var + data = ZParamExport(modid: int32(module.moduleId), + modname: module.modname, + paramid: int32(i), + shortdoc: param.shortdoc, + longdoc: param.longdoc, + tid: param.tid) + default: pointer + + if param.attr != "": + data.name = param.attr + var + err: bool + tid: TypeId + + let val = ctx.get(data.name, err, addr tid) + if not err: + data.havedefault = true + data.default = val + data.tid = tid + continue + else: + data.name = ctx.curModule.datasyms[param.offset] + let + p = param.offset + dataaddr = addr ctx.moduleAllocations[module.moduleId][p] + typeaddr = cast[ptr TypeId](cast[uint](dataaddr) + + uint(sizeof(pointer))) + if typeaddr[] != TBottom: + data.havedefault = true + data.default = dataaddr[] + data.tid = typeaddr[] + continue + + if param.havedefault: + data.havedefault = true + data.default = param.default + +proc get_current_instruction(ctx: RuntimeState): + ptr ZInstruction {.importc, cdecl.} + +proc run_param_validator*(ctx: RuntimeState, p: ZParamInfo, + val: pointer, t: TypeId): string = + + if p.tid.tCopy().unify(t) == TBottom: + return "Specified type for parameter was not compatable with the " & + "stored type (" & t.toString() & ")" + if p.funcIx != -1: + var cb = ZCallback(impl: cast[pointer](int64(p.funcIx))) + + if not p.native: + cb.ffi = true + + let s = ctx.run_callback_internal(addr cb, [(val, p.tid)]) + + if s != nil and s.len() != 0: + return s.toNimStr() + +proc set_user_param*(ctx: RuntimeState, mid: int, paramix: int, + value: pointer, t: TypeId): string {.exportc, cdecl.} = + ## Sets the user parameter, and runs the validator. If it returns + ## an error message, then it's feedback for the user, and try + ## again. Otherwise, it succeeded. + + let + module = ctx.obj.moduleContents[mid - 1] + param = module.parameters[paramix] + + result = ctx.run_param_validator(param, value, t) + + param.userType = t + param.userparam = value + +proc get_param_value*(ctx: RuntimeState, param: ZParamInfo): (pointer, TypeId) = + if param.userType != TBottom: + return (param.userparam, param.userType) + + if param.haveDefault: + return (param.default, param.tid) + + var name: string + + if param.attr != "": + name = param.attr + else: + name = ctx.curModule.datasyms[param.offset] + + ctx.runtimeError("ParamNotSet", @[name, ctx.curModule.modName]) diff --git a/src/rtmarshal.nim b/src/rtmarshal.nim index d0f1847..f88a9c9 100644 --- a/src/rtmarshal.nim +++ b/src/rtmarshal.nim @@ -7,7 +7,7 @@ proc marshal_int_list[T](l: seq[T]): C4Str = proc unmarshal_int_list(p: var cstring): seq[int] = list_unmarshal_helper(p): - let i = cast[int](p.unmarshal_64_bit_value()) + let i = cast[int](p.unmarshal_64_bit_value()) result.add(i) proc marshal_ffi_arg_info(args: seq[ZFFiArgInfo]): C4Str = @@ -117,10 +117,43 @@ proc unmarshal_byte_code(p: var cstring): seq[ZInstruction] = instr.typeInfo = cast[TypeId](p.unmarshal_64_bit_value()) result.add(instr) +proc marshal_mod_params(rt: RuntimeState, params: seq[ZParamInfo]): C4Str = + list_marshal_helper(params): + toAdd.add(item.attr.marshal_nim_string()) + toAdd.add(item.shortdoc.marshal_nim_string()) + toAdd.add(item.longdoc.marshal_nim_string()) + toAdd.add(item.offset.int32().marshal_32_bit_value()) + toAdd.add(item.native.marshal_bool()) + toAdd.add(cast[pointer](item.tid.followForwards()).marshal_64_bit_value()) + toAdd.add(item.funcIx.marshal_32_bit_value()) + toAdd.add(item.haveDefault.marshal_bool()) + if item.haveDefault: + toAdd.add(marshal(item.default, item.tid, rt.memos)) + +proc unmarshal_mod_params(rt: RuntimeState, p: var cstring): seq[ZParamInfo] = + list_unmarshal_helper(p): + var param = ZParamInfo() + + param.attr = p.unmarshal_nim_string() + param.shortdoc = p.unmarshal_nim_string() + param.longdoc = p.unmarshal_nim_string() + param.offset = int(p.unmarshal_32_bit_value()) + param.native = p.unmarshal_bool() + param.tid = cast[TypeId](p.unmarshal_64_bit_value()) + param.funcIx = p.unmarshal_32_bit_value() + param.haveDefault = p.unmarshal_bool() + if param.haveDefault: + param.default = p.unmarshal(param.tid, rt.memos) + + result.add(param) + proc marshal_one_module(rt: RuntimeState, m: ZModuleInfo): C4Str = basic_marshal_helper: toAdd.add(m.modname.marshal_nim_string()) toAdd.add(m.location.marshal_nim_string()) + toAdd.add(m.key.marshal_nim_string()) + toAdd.add(m.ext.marshal_nim_string()) + toAdd.add(m.url.marshal_nim_string()) toAdd.add(m.version.marshal_nim_string()) toAdd.add(m.symTypes.marshal_sym_types()) toAdd.add(m.codesyms.marshal_sym_names()) @@ -130,6 +163,7 @@ proc marshal_one_module(rt: RuntimeState, m: ZModuleInfo): C4Str = toAdd.add(cast[pointer](m.moduleId).marshal_64_bit_value()) toAdd.add(cast[pointer](m.moduleVarSize).marshal_64_bit_value()) toAdd.add(cast[pointer](m.initSize).marshal_64_bit_value()) + toAdd.add(rt.marshal_mod_params(m.parameters)) toAdd.add(m.instructions.marshal_byte_code()) proc unmarshal_one_module(rt: RuntimeState, p: var cstring): ZModuleInfo = @@ -137,6 +171,9 @@ proc unmarshal_one_module(rt: RuntimeState, p: var cstring): ZModuleInfo = result.modname = p.unmarshal_nim_string() result.location = p.unmarshal_nim_string() + result.key = p.unmarshal_nim_string() + result.ext = p.unmarshal_nim_string() + result.url = p.unmarshal_nim_string() result.version = p.unmarshal_nim_string() result.symTypes = p.unmarshal_sym_types() result.codeSyms = p.unmarshal_sym_names() @@ -145,6 +182,7 @@ proc unmarshal_one_module(rt: RuntimeState, p: var cstring): ZModuleInfo = result.moduleId = int(p.unmarshal_64_bit_value()) result.moduleVarSize = int(p.unmarshal_64_bit_value()) result.initSize = int(p.unmarshal_64_bit_value()) + result.parameters = rt.unmarshal_mod_params(p) result.instructions = p.unmarshal_byte_code() proc marshal_validators(rt: RuntimeState, vlist: seq[Validator]): C4Str = @@ -311,6 +349,11 @@ proc unmarshal_types(p: var cstring): Dict[TypeId, int] = result[k] = v proc marshal_object(rt: RuntimeState, nextmid: int32): C4Str = + var nextmid = nextmid + + if nextmid == 0 and rt.obj.nextEntrypoint != 0: + nextmid = rt.obj.nextEntrypoint + basic_marshal_helper: toAdd.add(cast[pointer](rt.obj.zeroMagic).marshal_64_bit_value()) toAdd.add(rt.obj.zcObjectVers.marshal_16_bit_value()) diff --git a/src/scope.nim b/src/scope.nim index c1c1d1a..87bb8f9 100644 --- a/src/scope.nim +++ b/src/scope.nim @@ -65,7 +65,7 @@ proc lookupOrAdd(ctx: Module, scope: Scope, n: string, proc scopeDeclare*(ctx: Module, scope: Scope, n: string, isfunc: bool, tid = TBottom, immutable = false, - declnode = ctx.pt): + declnode = ctx.pt, inparam = false): Option[SymbolInfo] = # This is meant for things that should be declared in a specific scope, # like enums, functions and explicitly declared variables. @@ -79,8 +79,10 @@ proc scopeDeclare*(ctx: Module, scope: Scope, n: string, var sym = result.get() # Funcs can have multiple declarations as long as signatures are disjoint. - if sym.declaredType and not isFunc: - ctx.irWarn("VarRedef") + # Similarly, we ignore this in param blocks. + if sym.declaredType and not isFunc and not inparam: + echo getStackTrace() + ctx.irWarn("VarRedef", @[sym.name]) else: sym.declNode = declnode diff --git a/src/vm.nim b/src/vm.nim index efd38ac..aacad78 100644 --- a/src/vm.nim +++ b/src/vm.nim @@ -1,6 +1,6 @@ # This is just a basic runtime with no thought to performance; we'll # do one more focused on performance later, probably directly in C. -import "."/[stchecks, attrstore] +import "."/[stchecks, attrstore, modparams] export attrstore proc load_spec() {.cdecl, importc.} @@ -114,6 +114,10 @@ proc getSourceLoc*(ctx: RuntimeState): string = else: return "" +proc get_current_instruction*(ctx: RuntimeState): + ptr ZInstruction {.exportc, cdecl.} = + return addr ctx.curModule.instructions[ctx.ip] + proc getStackTrace*(ctx: RuntimeState): Rope {.exportc, cdecl.} = var cells: seq[seq[string]] = @[@["Caller module", "Line #", "Call target"]] @@ -707,9 +711,66 @@ proc runMainExecutionLoop(ctx: RuntimeState): int = ctx.run_callback_internal(cbObj) of ZRet: leaveFrame() + of ZModuleEnter: + if instr.arg != 0: + for i, param in ctx.curModule.parameters: + var + val: pointer + t: TypeId + + ## Fill in all parameter values now. If there's a validator, + ## it will get called after this loop, along w/ a call to + ## ZParamCheck. + + if param.attr != "": + let attropt = ctx.attrs.lookup(param.attr) + if attropt.isNone(): + (val, t) = ctx.get_param_value(param) + discard ctx.set(param.attr, val, param.tid, lock = true, + internal = true) + else: + let + p = param.offset + id = ctx.curModule.moduleId + a = addr ctx.moduleAllocations[id][p * 2] + address = cast[ptr pointer](a) + typeaddr = cast[ptr pointer](cast[uint](address) + + uint(sizeof(pointer))) + if typeaddr[] == nil: # TBottom + (val, t) = ctx.get_param_value(param) + address[] = val + typeaddr[] = cast[pointer](t) + + ctx.module_lock_stack.add(int32(ctx.curModule.moduleId)) + else: + if ctx.module_lock_stack.len() == 0: + ctx.module_lock_stack.add(0) + else: + ctx.module_lock_stack.add(ctx.module_lock_stack[^1]) + + of ZParamCheck: + let s = cast[C4Str](ctx.stack[ctx.sp]) + + ctx.sp += 2 + + if s != nil and s.len() != 0: + var + param = ctx.curModule.parameters[instr.arg] + modname = ctx.curModule.modname + err = s.toNimStr() + name: string + + if param.attr != "": + name = param.attr + else: + name = ctx.curModule.datasyms[param.offset] + + ctx.runtimeError("ParamNotValid", @[name, modname, err]) + of ZModuleRet: if ctx.numFrames <= 2: return + discard ctx.module_lock_stack.pop() leaveFrame() of ZFFICall: ctx.z_ffi_call(instr.arg) @@ -799,8 +860,8 @@ proc setupArena(ctx: RuntimeState, # 128 bits per item. var moduleAllocation = newSeq[pointer](moduleIdSz * 2) - for (offset, tid) in typeInfo: - moduleAllocation[offset*2 + 1] = cast[pointer](tid) + #for (offset, tid) in typeInfo: + # moduleAllocation[offset*2 + 1] = cast[pointer](tid) ctx.moduleAllocations.add(moduleAllocation) @@ -877,6 +938,7 @@ proc setup_first_execution*(ctx: var RuntimeState) {.exportc, cdecl.} = for i, item in ctx.obj.moduleContents: ctx.setupArena(item.symTypes, item.moduleVarSize) + proc execute_object*(ctx: var RuntimeState): int {.exportc, cdecl.} = ## This call is intended for first execution, not for save / ## resumption. diff --git a/src/ztypes/function.nim b/src/ztypes/function.nim index 3832b37..768ab37 100644 --- a/src/ztypes/function.nim +++ b/src/ztypes/function.nim @@ -92,8 +92,8 @@ template initForeignModule(): ZModuleInfo = proc run_validator(ctx: RuntimeState, startswith: string): FlexArray[pointer] {.cdecl, importc.} -proc foreign_z_call(ctx: RuntimeState, funcid: int): - pointer {.exportc, cdecl.} = +proc foreign_z_call*(ctx: RuntimeState, funcid: int): + pointer {.exportc, cdecl.} = # We use a dummy module with two instructions, a call and a halt, # then we re-start the VM using that as an entry point. # Any result is left in the return register. @@ -209,10 +209,10 @@ proc find_string_at(mem: string, offset: int): string {.importc, cdecl.} proc baseunify(id1, id2: TypeId): TypeId {.importc, cdecl.} proc run_callback*(ctx: RuntimeState, - name: string, - t: TypeId, - args: openarray[(pointer, TypeId)] = [], - ffi = false): pointer {.discardable, exportc, cdecl.} = + name: string, + t: TypeId, + args: openarray[(pointer, TypeId)] = [], + ffi = false): pointer {.discardable, exportc, cdecl.} = ## This version of run_callback finds the function to call, and ## creates the callback pointer for you, based on the name and ## signature. diff --git a/src/ztypes/marshal.nim b/src/ztypes/marshal.nim index 1d096c5..01ab59c 100644 --- a/src/ztypes/marshal.nim +++ b/src/ztypes/marshal.nim @@ -111,9 +111,9 @@ proc unmarshal_nim_string*(s: var cstring): string {.exportc, cdecl.} = l = s.unmarshal_32_bit_value() if l == 0: - result = newString(1) + result = "" else: - result = newString(l + 1) + result = newString(l) for i in 0 ..< l: result[i] = s[i] diff --git a/tests/modparam.c4m b/tests/modparam.c4m new file mode 100644 index 0000000..0d52a3c --- /dev/null +++ b/tests/modparam.c4m @@ -0,0 +1,26 @@ +func example_checker(x) { + result = "" + + if (x % 2) != 0 { + result = "Parameter value must be even." + } +} + +parameter var example1 { + "This should be some documentation." + "Also this." + default: 100 + validator: func example_checker(int) -> string +} + +parameter var example2 { + "This should be some documentation." + "Also this." + default: 101 + validator: func example_checker(int) -> string +} + +# Neither of these should happen. +print(example1) +print(example2) +assert false \ No newline at end of file diff --git a/tests/modparam.kat b/tests/modparam.kat new file mode 100644 index 0000000..548691e --- /dev/null +++ b/tests/modparam.kat @@ -0,0 +1,2 @@ +fail +ParamNotValid