diff --git a/src/codegen.nim b/src/codegen.nim
index 8391b8f..92f2451 100644
--- a/src/codegen.nim
+++ b/src/codegen.nim
@@ -516,8 +516,8 @@ proc genContainerIndex(ctx: CodeGenState, sym: SymbolInfo = nil,
else:
ctx.genTCall(FIndex)
-proc genPushStaticString(ctx: CodeGenState, name: string) =
- ctx.emitInstruction(ZPushStaticPtr, ctx.addStaticObject(name), tid = TString)
+proc genPushStaticString(ctx: CodeGenState, s: string) =
+ ctx.emitInstruction(ZPushStaticPtr, ctx.addStaticObject(s), tid = TString)
proc genLoadAttr(ctx: CodeGenState, tid: TypeId, mode = 0) =
ctx.emitInstruction(ZLoadFromAttr, tid = tid, arg = mode)
@@ -1234,73 +1234,115 @@ proc genFunction(ctx: CodeGenState, fn: FuncInfo) =
ctx.mcur.objInfo.codesyms[zinfo.offset] = fullname
+# Returns true if native.
+proc backpatch_callback(ctx: CodeGenState, patchloc: int,
+ node: IrNode): (int32, bool) =
+ ## m.instructions[offset] is ZPushVmPtr, which needs to be patched.
+ ## next one is ZRunCallback
+ var
+ m = ctx.mcur
+ offset = patchloc
+ cb = cast[ptr Callback](node.value)
+ fnid: int32
+ native: bool
+
+ 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.
+ 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:
+ let to = cb.tid.idToTypeRef()
+ fnid = int32(i)
+ m.objinfo.instructions[offset].arg = fnid
+ m.objinfo.instructions[offset].typeInfo = to.items[^1]
+ break
+ else:
+ fnid = int32(cb.impl.internalId)
+ native = true
+ m.objinfo.instructions[offset].arg = fnid
+
+ offset += 1
+ m.objinfo.instructions[offset].typeInfo = cb.tid
+
+ return (fnid, native)
+
proc stashParam(ctx: CodeGenState, m: Module, info: ParamInfo) =
var zparam = ZParamInfo(shortdoc: info.shortdoc,
- longdoc: info.longdoc)
-
+ longdoc: info.longdoc,
+ private: info.private)
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
+ # TODO: I think may need to ensure const?
zparam.haveDefault = true
- zparam.default = info.defaultIr.get().value
+ zparam.default = info.defaultIr.get().value
zparam.tid = info.sym.tid
- if info.validatorIr.isSome():
+ if info.initializeIr.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))
+ offset = info.iPatchLoc
+ litnode = info.initializeIr.get()
- 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
+ (zparam.iFnIx, zparam.iNative) = ctx.backpatch_callback(offset, litnode)
+ else:
+ zparam.iFnIx = -1
- offset += 1
- m.objinfo.instructions[offset].typeInfo = cb.tid
+ if info.validatorIr.isSome():
+ var
+ offset = info.vPatchLoc
+ litnode = info.validatorIr.get()
+ (zparam.vFnIx, zparam.iNative) = ctx.backpatch_callback(offset, litnode)
else:
- zparam.funcIx = -1
-
+ zparam.vFnIx = -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 emitBail(ctx: CodeGenState, msg: string) =
+ ctx.genPushStaticString(msg)
+ ctx.emitInstruction(ZBail)
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)
+ ctx.genPushTypeOf(param.sym)
+ let patchloc = m.objInfo.instructions.len()
+ ctx.startJnz()
+ ctx.emitBail("Parameter " & param.sym.name & " was not set when " &
+ "entering module " & m.modname)
+ ctx.backpatch(patchloc)
+
+ if param.validatorIr.isSome():
+ ctx.genLoadSymbol(param.sym)
+ m.params[i].vPatchLoc = m.objInfo.instructions.len()
+ ctx.emitInstruction(ZPushVmPtr) # Will overwrite this later.
+ ctx.emitInstruction(ZRunCallback, arg = 1)
+ ctx.emitInstruction(ZMoveSp, -1)
+ ctx.emitInstruction(ZPushRes)
+ ctx.emitInstruction(ZParamCheck, arg = i)
+
+proc genInitializer(ctx: CodeGenState, p: ParamInfo) =
+ ctx.genPushTypeOf(p.sym)
+ let patchloc = ctx.mcur.objInfo.instructions.len()
+ ctx.startJnz()
+ # Not sure what kind of callback we're pushing yet.
+ # We'll come back to the next instruction we emit later.
+ p.iPatchloc = ctx.mcur.objInfo.instructions.len()
+ ctx.emitInstruction(ZPushVmPtr)
+ ctx.emitInstruction(ZRunCallback, arg = -1)
+ ctx.emitInstruction(ZPushRes)
+ ctx.genStore(p.sym)
+ ctx.backPatch(patchloc)
proc genModule(ctx: CodeGenState, m: Module) =
let curMod = ctx.minfo[m.key]
@@ -1315,6 +1357,10 @@ proc genModule(ctx: CodeGenState, m: Module) =
curMod.longdoc = m.longdoc
ctx.genLabel("Module '" & m.modname & "' :")
+ for param in m.params:
+ if param.initializeIr.isSome():
+ ctx.genInitializer(param)
+
ctx.emitInstruction(ZModuleEnter, arg = m.params.len())
if m.params.len() != 0:
diff --git a/src/commands/objdump.nim b/src/commands/objdump.nim
index c4cfdd6..eb4cf05 100644
--- a/src/commands/objdump.nim
+++ b/src/commands/objdump.nim
@@ -22,8 +22,8 @@ proc get_basic_object_info*(obj: ZObjectFile): Rope =
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"]]
+ "Native Validator?",
+ "Validator index", "Short Doc", "Long Doc"]]
for item in params:
var row: seq[string] = @[]
if item.attr != "":
@@ -33,11 +33,11 @@ proc format_module_params*(obj: ZObjectFile, params: seq[ZParamInfo]): Rope =
echo "offset = ", item.offset
row.add($item.offset)
row.add(item.tid.toString())
- if item.native:
+ if item.vnative:
row.add("✓")
else:
row.add("✗")
- row.add($item.funcIx)
+ row.add($item.vfnIx)
row.add(item.shortdoc & " ")
row.add(item.longdoc & " ")
cells.add(row)
diff --git a/src/common.nim b/src/common.nim
index dc24a64..4dbc5e7 100644
--- a/src/common.nim
+++ b/src/common.nim
@@ -402,23 +402,29 @@ type
shortdoc*: string
longdoc*: string
validatorIr*: Option[IrNode]
+ initializeIr*: Option[IrNode]
defaultParse*: Option[ParseNode]
defaultIr*: Option[IrNode]
- backpatchLoc*: int
+ private*: bool
+ iPatchLoc*: int
+ vPatchLoc*: 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
+ attr*: string # Either it's an attribute...
+ offset*: int # or in theerr current module.
+ default*: pointer
+ tid*: TypeId
+ haveDefault*: bool
+ private*: bool
+ vFnIx*: int32
+ vNative*: bool # Whether the validation fn is native.
+ iFnIx*: int32
+ iNative*: bool # Whether the validation fn is native.
+ userparam*: pointer
+ userType*: TypeId
+ shortdoc*: string
+ longdoc*: string
ZParamExport* = ref object
## What we pass when asked what parameters need to be provided.
@@ -433,6 +439,7 @@ type
modid*: int32
paramid*: int32
tid*: TypeId
+ private*: bool
havedefault*: bool
default*: pointer
@@ -897,6 +904,9 @@ type
# through a ZAssignAttr.
ZLockOnWrite = 0xb0,
+ # Print an error message and abort.
+ ZBail = 0xee,
+
# A no-op. These double as labels for disassembly too.
ZNop = 0xff
@@ -1218,8 +1228,6 @@ template debug*(s: string, s2: string, moreargs: varargs[string]) =
debug(cells.quickTable(verticalHeaders = true))
-
-
# The current runtime, so that builtin functions can access the state.
# Was having a weird link error so moved this here.
var
diff --git a/src/err.nim b/src/err.nim
index 3c18719..58787ca 100644
--- a/src/err.nim
+++ b/src/err.nim
@@ -1,907 +1,2 @@
-import std/[posix, terminal]
-import "."/common
-
-## If we have to bail on control flow, we'll do so here.
-proc newCon4mException*(errs: seq[Con4mError] = @[]): Con4mException =
- result = new(Con4mException)
- result.errors = errs
-
-proc newCon4mLongJmp*(msg = ""): Con4mLongJmp =
- result = new(Con4mLongJmp)
- result.msg = msg
-
-template con4mLongJmp*(msg = "") =
- raise newCon4mLongJmp(msg)
-
-const errorMsgs = [
- ("FileNotFound", "Module could not be located."),
- ("ModuleNotFound", "Module $1 could not be located."),
- ("StrayCr", "Carriage return (\r) without newline."),
- ("BadEscape", "Unterminated escape sequence in literal."),
- ("UnicodeEscape", "Invalid unicode escape character found in literal."),
- ("LitModExpected", "Expected a valid literal modifier"),
- ("CommentTerm", "Unterminated C-style long comment."),
- ("BadFloat", "Invalid character in float literal"),
- ("MissingDigits", "Must have digits after the dot in a valid float literal"),
- ("BadExponent", "Float exponent specified, with no exponent " &
- "value provided"),
- ("BadChar", "Invalid character literal"),
- ("StringNl", "Unterminated single-quoted string."),
- ("StringTerm", "Unterminated string literal"),
- ("CharTerm", "Unterminated character literal"),
- ("OtherTerm", "Unterminated literal"),
- ("BadChar", "Invalid character found in token"),
- ("StmtEnd", "Expected end of a statement after $1."),
- ("NotALit", "Not a literal; literal modifier not allowed here."),
- ("BadLitMod", "Unknown literal modifier $1 for $2" &
- " syntax."),
- ("LitModTypeErr", "Literal modifier $1 can't be used with " &
- "$2 literals"),
- ("MissingTok", "Expected $1 here."),
- ("MemberSpec", "'.' operator must have an identifier on " &
- "both sides."),
- ("ItemExpected", "Expected either another item or '$1'"),
- ("BadTuple", "Tuple types must contain two or more items."),
- ("AccessExpr", "Expected either an identifier or paren here."),
- ("ExprStart", "Expected the start of an expression here, but got $1."),
- ("BinaryNot", "'not' operator takes only one operand."),
- ("LitExpected", "Expected a literal value here."),
- ("ForFromIx", "for ... from loops must have a single index " &
- "variable."),
- ("NameInTypeSpec", "'$1' is not a builtin type. " &
- "Use struct[typename] for user types."),
- ("BadOneOf", "OneOf types must have multiple type options that " &
- "are more constrained than a generic type variable."),
- ("BadObjType", "Object types must provide either a name or a type " &
- "variable if specifying an object where no fields " &
- "will be referenced."),
- ("BadTypeDecl", "Invalid syntax inside a type declaration."),
- ("BadFormalEnd", "Expected either a closing parenthesis (')'" &
- " or an additional parameter."),
- ("BadLock", "Attribute Lock operator (~) must be " &
- "followed either by an assignment statement or " &
- "an attribute to lock."),
- ("BadUseSyntax", "'use' statement requires a string literal " &
- " after 'from'"),
- ("BadParamName", "'parameter' keyword must be followed by " &
- "an attribute (which can be dotted), or the " &
- "'var' keyword followed by a local variable " &
- "name. The variable can be optionally typed."),
- ("BadExternField", "Bad field for 'extern' block."),
- ("NeedSig", "Field requires a function type signature."),
- ("BadRCParam", "Reference counting specs must either be identifiers " &
- "that match the names given in the function signature, " &
- "or the special value return (for incref only)"),
- ("BadCType", "Invalid C type for external parameter: $1"),
- ("PureBool", "The 'pure' property for external functions " &
- "must be a boolean value (true or " &
- "false)"),
- ("DupeExtField", "Duplicate field $1 provided for an " &
- "extern block."),
- ("DupeDllName", "Duplicate DLL name $1 provided for an " &
- "external function (ignored)."),
- ("DupeVal", "Duplicate parameter value $1 in the " &
- "$2 property of the extern spec."),
- ("ExtNotSpecd", "None of the external function parameters were given" &
- " the name $1"),
- ("ExtAllocNHold", "Extern function parameter $1 cannot be " &
- "spec'd to have the external function hold memory " &
- "we pass, and to allocate that memory."),
- ("NoMemMan", "Since $1 is not a pointer or array type, " &
- "memory management will not be performend, and this " &
- "annotation will be ignored."),
- ("WontLink", "Was not able to locate the external symbol $1" &
- ", which may be required for running."),
- ("MissingSym", "Was not able to locate the external symbol $1" &
- "; program will crash if it is accessed, unless " &
- "it is dynamically loaded first."),
- ("PurePlz", "Please provide a value for the pure property " &
- "for extern functions. Pure functions always " &
- "return the same output for the same input, and do " &
- "not do any I/O, allowing us to pre-execute."),
- ("EofInBlock", "Block was not closed when end-of-file was found."),
- ("TopLevelOnly", "'$1' is only allowed at the top-level of a " &
- "module."),
- ("TopLevelPlural", "$1 are only allowed at the top-level of a module."),
- ("InLoopsOnly", "'$1' is only allowed inside loops."),
- ("RetOutOfFunc", "'return' is only allowed inside functions."),
- ("UToSSmaller", "Conversion of unsigned value to a smaller signed type" &
- "can lead to both sign changes and truncated values."),
- ("UToSSameSz", "Conversion of same-typed unsized values" &
- "will turn large numbers negative. Explicitly cast " &
- "to a bigger type if possible to avoid this scenario."),
- ("CanTruncate", "Conversion to a smaller type can result in values " &
- "getting truncated."),
- ("StoU", "Conversion of signed integer to unsigned type turns " &
- "negative values into larger integers."),
- ("SToSmallerU", "Conversion of signed integer to unsigned type turns " &
- "negative values into larger integers, and the " &
- "smaller type may truncate."),
- ("HexTooLarge", "Hex number is too large for type $1"),
- ("IntTooLarge", "Integer literal is too large for type $1"),
- ("FloatTooLarge", "Integer portion of float is too large; " &
- "use 'e' notation."),
- ("ExpTooLarge", "Exponent portion of float is too large."),
- ("BadHex", "Invalid hex literal"),
- ("BadInt", "Invalid int literal"),
- ("BadBool", "Invalid bool literal."),
- ("BadByte", "Invalid value for a byte (cannot be above 0xff)"),
- ("BadCodepoint", "Invalid character; unicode values may not be " &
- "above U+10FFFF."),
- ("BadCP2", "Invalid character; the unicode standard does not " &
- "allow values from U+D800 to U+DFFF"),
- ("LoseFormat", "Conversion discards string formatting information."),
- ("TypeNotSigned", "Literal is negative, but the specified type " &
- "is unsigned."),
- ("TypeMismatch", "$1 and $2 are incompatible types."),
- ("LoopVarAssign", "Cannot assign to (or re-declare) loop iteration " &
- "variables."),
- ("AlreadyAFunc", "Variable names cannot have names that are identical to " &
- "functions named in the same module's top-level."),
- ("AlreadyAVar", "Already found a top-level variable with the same " &
- "name as this function; this is not allowed within a " &
- "module."),
- ("VarRedef", "Variable $1 declared multiple times in " &
- "the same scope."),
- ("LabelDupe", "Nested loops have the same label ($1)."),
- ("LabelLoc", "label statement must come immediately before " &
- "either a for loop or a while loop."),
- ("DeadCode", "Dead code after $1."),
- ("ElifLoc", "elif statement must follow either an " &
- "if block, or another elif block."),
- ("ElseLoc", "else statement must follow either an " &
- "if block or another elif block."),
- ("BadLoopExit", "$1 to label $2 is invalid, because " &
- "it is not contained inside a loop that have that label."),
- ("DupeParamProp", "Duplicate parameter property for $1"),
- ("ParamType", "Parameter property $1 must be a $2."),
- ("BadParamProp", "Invalid property for a parameter: $1"),
- ("EnumDeclConst", "Enum value assignments must be constants"),
- ("EnumInt", "Currently, enum values may only be int types"),
- ("EnumReuse", "Value $1 has already been used in this enum."),
- ("BinaryOpCompat", "LHS and RHS do not have compatable types " &
- "(lhs: $1; rhs: $2)."),
- ("NoBoolCast", "The condition cannot be automatically converted to " &
- "a true / false value; provide a specific check. " &
- "Type of condition is: $1"),
- ("CannotCast", "Cannot convert from type $1 to " &
- "type $2"),
- ("BoolAutoCast", "This condition (of type $1) is not a " &
- "boolean value, but is being auto-cast."),
- ("TyDiffListItem", "List item type is not consistent with other items (" &
- "Previous items were $1; this is a $2)"),
- ("TyDiffKey", "Key type is not consistent with other keys (" &
- "Previous keys were $1; this is a $2)"),
- ("TyDiffValue", "Value type is not consistent with other values (" &
- "Previous values were $1; this is a " &
- "$2)"),
- ("VarInSecDef", "In explicit section declarations, the `=` is " &
- "expecting valid attributes only on the LHS, but " &
- "$1 is not allowed as a top-level attribute." &
- "Assuming this is a variable, not an attribute. " &
- "If it should be an attribute, fix the specification. " &
- "Otherwise, to get rid of this warning, either move " &
- "the assignment outside the section block, or " &
- "Use the = assignment operator, which " &
- "forces variable assignment."),
- ("TryVarAssign", "The attribute specifcation doesn't allow this field. " &
- "If you'd like a variable with this name, you can " &
- "use the = operator, which creates a" &
- "variable, even when there's an attribute of the " &
- "same name."),
- ("AssignToSec", "Cannot assign directly to $1; it is a " &
- "section that supports multiple instances, not a " &
- "field within a section."),
- ("AsgnSingleton", "Cannot assign directly to $1; it is a single" &
- "section that contains fields, not a field within " &
- "a section."),
- ("AsgnInstance", "Cannot assign directly to $1; the parent " &
- "section supports multiple instances, so this name " &
- "would be an instance, to which you can then add fields."),
- ("SecUnderField", "Cannot assign; $1 is a field, so may not " &
- "contain sub-fields."),
- ("SectionNoSpec", "While $1 is a valid section, there is no " &
- "known section type named $2."),
- ("RootNoSec", "There is no allowed section or attribute named: " &
- "$1"),
- ("SectionDenied", "While $2 is a known section type, it is not " &
- "permitted within $1."),
- ("RootSecDenied", "The root attribute scope doesn't allow $1 " &
- "sections."),
- ("AttrNotSpecd", "$1 is not an allowed attribute name."),
- ("BadSectionType", "There isn't an allowed section type named $1."),
- ("SecNotAllowed", "A $1 section is not allowed from within $2."),
- ("NotASingleton", "The $1 section expects an instance name."),
- ("IsASingleton", "A $1 section does not allow named instances;" &
- " there is only one unnamed section."),
- ("TypeVsSpec", "The type of this use ($1) is not compatible " &
- "with the specified type ($2)"),
- ("UnsignedUMinus", "Unary minus would turn an unsigned value to a signed " &
- "value; cast either to the same size (which you " &
- "shouldn't do if the int value might be larger than the " &
- "highest signed value), or cast to the next size up if " &
- "possible."),
- ("128BitLimit", "$1 is not currently supported for 128-bit integers."),
- ("U64Div", "Division producing a float isn't currently defined for " &
- "unsigned 64-bit integers."),
- ("TupleLhs", "When unpacking a tuple, all items on the left hand " &
- "side of the assignment must be valid variables, and " &
- "cannot currently be attributes."),
- ("MemberTop", "Attribute member access (.) can only be applied " &
- "to attributes, not to values."),
- ("TupleConstIx", "When indexing a tuple, the index must evaluate to " &
- "a constant integer."),
- ("TupleIxBound", "Constant index is out of bounds for the tuple being " &
- "indexed."),
- ("ContainerType", "Cannot distinguish what kind of container type this " &
- "is (could be a dict, list, tuple, etc.) " &
- " Please explicitly declare this type."),
- ("BadUrl", "Invalid URL for loading con4m source code."),
- ("InsecureUrl", "Warning: loading file from an insecure URL. " &
- "The contents could be injected by an attacker."),
- ("NoImpl", "Could not find any function implementations in scope " &
- "named $1. Full signature: $1$2"),
- ("NotAFunc", "There is a variable named $1, but there was " &
- "Not a function in scope with the signature: " &
- "$1$2"),
- ("BadSig", "No implementation of $1 matched this $3. " &
- "The $3 had the type: $1$2"),
- ("CallAmbig", "Found multiple functions matching $1$2" &
- " Please disambiguate."),
- ("NotIndexible", "Type $1 is not indexible."),
- ("CantLiftFunc", "Function $1 has the same name as a " &
- "global variable. Currently, other modules will have to " &
- "explicitly qualify the module to call this function."),
- ("DoesntExit", "Control does not reach the end of this $1."),
- ("ExprResult", "Result of expression is unused. Expression result is " &
- "of type $1. Please assign to _ to discard."),
- ("InfLoop", "While loop does not exit."),
- ("UseBeforeDef", "Likely use of $1 before assignment;" &
- " If you think this is wrong, set a default value " &
- "at the top of this scope."),
- ("ConstReassign", "This variable has been declared constant, and was " &
- "previously assigned."),
- ("Immutable", "Cannot modify immutable values."),
- ("DefWoUse", "Variable $1 is defined, but never used."),
- ("UseWoDef", "Definite use of $1 without assignment."),
- ("ConstNotSet", "Constant $1 was declared, but no value was " &
- "set."),
- ("$assign", "Variables starting with $ are set by the " &
- "system, and cannot be otherwise assigned."),
- ("SigOverlap", "In this module, for the function name $1, " &
- "implementations have overlapping signatures:
" &
- "2. $3
" &
- "1. $2 (line $4)."),
- ("NextCase", "Statement seemed to end, and was expecting " &
- "another case branch, an else, " &
- " or } to end the cases."),
- ("CaseBodyStart", "Case bodies may either be regular blocks (i.e., " &
- "{ ... }) or can be a colon followed by a list " &
- "of statements."),
- ("DeadTypeCase", "Variable can never be of type $1; case cannot " &
- "be taken."),
- ("BadIPv4", "Invalid IPv4 address."),
- ("BadPort", "Invalid port number."),
- ("BadDuration", "Invalid duration literal."),
- ("BadSize", "Invalid size literal."),
- ("BadUrl", "Invalid URL literal."),
- ("BadDateTime", "Invalid literal value for datetime type."),
- ("BadDate", "Invalid literal value for date type."),
- ("BadTime", "Invalid literal value for time type."),
- ("InvalidOther", "Invalid literal, did not match any known literal type."),
- ("OtherLit", "Inferred type of literal is $1. If incorrect, " &
- "place in quotes and provide an explicit literal " &
- "modifier after (e.g., \"2 gb\"'size for " &
- "a size literal.)"),
- ("OtherQuotes", "The := operator is used for values of special " &
- "types, where the system takes all input till the end " &
- " of the line, then tries to treat it as a primitive " &
- "type. The quotes here are treated like they're inside " &
- "the data you're providing, which may not be what you " &
- "want."),
- ("OtherBrak", "The := operator does not process brackets " &
- "or parentheses of any kind; this will be treated as " &
- "a single string."),
- ("OtherNum", "The := operator does not result in numeric " &
- "types; it asks the system to guess from a number of " &
- "specialized types. Use = or add a modifier " &
- "that's appropriate for the type of value you're trying " &
- "to create."),
- ("AttrUse", "Attempted to use an attribute $1 that has not " &
- "been set."),
- ("RT_BadTOp", "Unknown function id for internal type API call " &
- "($1)"),
- ("NeedExName", "Local function specs must include names for every " &
- "parameter."),
- ("DupeExParam", "Duplicate parameter name for local function: " &
- "$1"),
- ("ExternZArgCt", "Local function spec has more arguments than external " &
- "function for $1, which is currently not " &
- "allowed. C function has $3 args, local has $2 ($4)."),
- ("ExternCArgCt", "External function spec has more arguments than local " &
- "function for $1, which is currently not " &
- "allowed. C function has $3 args, local has $2."),
- ("ArrayIxErr", "Array index $1 is not in bounds."),
- ("DictKeyErr", "Could not find dictionary key: $1."),
- ("LockedAttr", "Attempted to set attribute $1, which is " &
- "locked. Current value: $2 Attempted value: " &
- "$3"),
- ("AlreadyLocked", "Tried to set lock-on-write for attribute $1, " &
- "but it is already locked."),
- ("DupeSection", "Section $1 cannot be specified twice"),
- ("SpecFieldType", "Specification field $1 was expected to be " &
- "of type $3, but was of type $2"),
- ("RequiredProp", "Specified field $1 does not have the " &
- "required property $2."),
- ("SpecLock", "Cannot make changes to the attribute specification; " &
- "it is locked."),
- ("NoSecSpec", "When processing $1, there is no section " &
- "specification for $2"),
- ("MissingField", "Attribute $1 must be set, but was not " &
- "provided."),
- ("NoSecType", "In the top level of a confspec block, got " &
- "$1 but only allowed contents are: " &
- "root, singleton or named" &
- " blocks."),
- ("InstSecStart", "Was expecting this to start an instantiation " &
- "of a $1 section named $2, but " &
- "would need a { here."),
- ("RootOverwrite", "Overwriting an existing root $1."),
- ("MissingSec", "Spec tries to $2 a section of type $1" &
- ", but no section named $1 has been spec'd yet."),
-
- ("BadRange", "When validating $1, an invalid value, " &
- "$2, was chosen. But the value must be no less " &
- "than $3 and no greater than $4."),
- ("BadChoice", "When validating $1, an invalid value, " &
- "$2, was chosen. Valid choices are: $3"),
- ("MissingSection", "When validating $1, expected a required " &
- "section named $2, which was not provided."),
- ("MissingField", "When validating $1, expected a required " &
- "field named $2, but it was not found."),
- ("BadSection", "When validating $1, the section $2 " &
- "is not allowed."),
- ("NotTSpec", "When attempting to determine the type of the " &
- "value $1$2, the field $3 was " &
- "expected to specify the value's type, but " &
- "that field did not contain a type specification."),
- ("BadField", "When validating $1, found the field " &
- "$2, which is not a valid field name for a " &
- "$3 section."),
- ("FieldMutex", "When validating $1, found the field " &
- "$2, which is specified to not be able " &
- "to appear together in a section with the field " &
- "$3 (which was also present)."),
- ("ExternVarargs", "External variable argument functions are not yet " &
- " supported (external function $1)"),
- ("StackOverflow", "Exceeded the maximum stack depth."),
- ("NoSpecForSec", "Used a section $1, which is not defined " &
- "by the attribute specification."),
- ("InvalidStart", "Found the start of an attribute assignment that " &
- "is invalid according to the spec, at: $1"),
- ("NoInstance", "This section must have an instance, meaning " &
- "you must specify named sections underneath it."),
- ("DupeProp", "Property $1 cannot appear twice in one " &
- "item spec"),
- ("NotBool", "Property $1 is reuquired to be bool, " &
- "but here is $2)"),
- ("RangeAndChoice", "Cannot have both range and choice " &
- "constraints for the same field."),
- ("TyDiffListItem", "Inconsistent item type for choice. Previously " &
- "it type was $1, but here it was $2"),
- ("BadRangeSpec", "Start value for a range must be less than the end " &
- "value."),
- ("SpecWhenLocked", "Cannot add confspec fields; the " &
- "specification has already been locked."),
- ("DupeSpecField", "Duplicate specification for field $1."),
- ("TCaseOverlap", "Type cases have overlapping types."),
- ("DupeExclusion", "Exclusion duplicated for $1."),
- ("DupeAllow", "Section $1 appears multiple times in " &
- "allow list"),
- ("AllowInReq", "Section $1 appears in both require" &
- "and allow; suggest removing from allow"),
- ("DupeRequire", "Section $1 appears multiple times in " &
- "require list"),
- ("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.
-
-proc baseError*(list: var seq[Con4mError], code: string, cursor: StringCursor,
- modname: string, line: int, lineOffset: int,
- phase: Con4mErrPhase, severity = LlFatal,
- extraContents: seq[string] = @[], detail: Rope = nil,
- trace: string = "", ii: Option[InstantiationInfo] =
- none(InstantiationInfo)) =
- if severity < config_log_level:
- return
- var err = Con4mError(phase: phase, severity: severity, code: code,
- cursor: cursor, modname: modname, line: line,
- offset: lineOffset, extra: extraContents, detail: detail)
-
- when not defined(release):
- err.trace = trace
- if ii.isSome():
- err.ii = ii.get()
-
- var foundCode = false
- for (k, v) in errorMsgs:
- if code == k:
- foundCode = true
- break
- if not foundCode:
- print fgColor("error: ", "red") + text("Error code '") + em(code) +
- text("' was not found.")
-
- list.add(err)
-
-proc baseError*(list: var seq[Con4mError], code: string, tok: Con4mToken,
- modname: string, phase: Con4mErrPhase, severity = LlFatal,
- extra: seq[string] = @[], detail: Rope = nil, trace = "",
- ii = none(InstantiationInfo)) =
- list.baseError(code, tok.cursor, modname, tok.lineNo, tok.lineOffset,
- phase, severity, extra, detail, trace, ii)
-
-proc lexBaseError*(ctx: Module, basemsg: string, t: Con4mToken = nil,
- subs: seq[string] = @[]) =
- var t = t
-
- if t == nil:
- t = ctx.tokens[^1]
-
- ctx.errors.baseError(basemsg, t.cursor, ctx.modname & ctx.ext, t.lineNo,
- t.lineOffset, ErrLex, LlFatal, subs)
-
-proc lexFatal*(ctx: Module, basemsg: string, t: Con4mToken = nil) =
- ctx.lexBaseError(basemsg, t)
- raise newCon4mException()
-
-template lexError*(msg: string, t: Con4mToken = nil) =
- ctx.lexFatal(msg, t)
-
-proc baseError*(list: var seq[Con4mError], code: string, node: ParseNode,
- modname: string, phase: Con4mErrPhase, severity = LlFatal,
- extra = seq[string](@[]), detail: Rope = nil, trace = "",
- ii = none(InstantiationInfo)) =
- if node == nil:
- list.baseError(code, nil, modname, 0, 0, phase, severity, extra,
- detail, trace, ii)
- return
-
- if node.err and severity != LlInfo:
- return
- if severity in [LlErr, LlFatal]:
- node.err = true
- list.baseError(code, node.token, modname, phase, severity, extra,
- detail, trace, ii)
-
-template irError*(ctx: Module, msg: string, extra: seq[string] = @[],
- w = ParseNode(nil), detail = Rope(nil)) =
- var where = if w == nil: ctx.pt else: w
- ctx.errors.baseError(msg, where, ctx.modname & ctx.ext, ErrIrGen, LlFatal,
- extra, detail)
-
-template irError*(ctx: Module, msg: string, w: IrNode,
- extra: seq[string] = @[], detail: Rope = nil) =
- var where = if w == nil: ctx.pt else: w.parseNode
- ctx.errors.baseError(msg, where, ctx.modname & ctx.ext, ErrIrGen, LlFatal,
- extra, detail)
-
-template irNonFatal*(ctx: Module, msg: string, extra: seq[string] = @[],
- w = ParseNode(nil)) =
- # Things we consider errors, but we may end up allowing. Currently, this
- # is just for use-before-def errors.
- var where = if w == nil: ctx.pt else: w
- ctx.errors.baseError(msg, where, ctx.modname & ctx.ext, ErrIrGen, LlErr,
- extra)
-
-template irNonFatal*(ctx: Module, msg: string, w: IrNode,
- extra: seq[string] = @[]) =
- # Things we consider errors, but we may end up allowing. Currently, this
- # is just for use-before-def errors.
- var where = if w == nil: ctx.pt else: w.parseNode
- ctx.errors.baseError(msg, where, ctx.modname & ctx.ext, ErrIrGen, LlErr,
- extra)
-
-template irWarn*(ctx: Module, msg: string, extra: seq[string] = @[],
- w = ParseNode(nil)) =
- var where = if w == nil: ctx.pt else: w
- ctx.errors.baseError(msg, where, ctx.modname & ctx.ext, ErrIrGen, LlWarn,
- extra)
-
-template irWarn*(ctx: Module, msg: string, w: IrNode,
- extra: seq[string] = @[]) =
- var where = if w == nil: ctx.pt else: w.parseNode
- ctx.errors.baseError(msg, where, ctx.modname & ctx.ext, ErrIrGen, LlWarn,
- extra)
-
-template irInfo*(ctx: Module, msg: string, extra: seq[string] = @[],
- w = ParseNode(nil)) =
- var where = if w == nil: ctx.pt else: w
- ctx.errors.baseError(msg, where, ctx.modname & ctx.ext, ErrIrGen, LlInfo,
- extra)
-
-template irInfo*(ctx: Module, msg: string, w: IrNode,
- extra: seq[string] = @[]) =
- var where = if w == nil: ctx.pt else: w.parseNode
- ctx.errors.baseError(msg, where, ctx.modname & ctx.ext, ErrIrGen, LlInfo,
- extra)
-
-template loadError*(ctx: CompileCtx, msg: string, modname: string,
- extra: seq[string] = @[]) =
- # TODO: don't hardcode the extension.
- ctx.errors.baseError(msg, ParseNode(nil), modname & ".c4m", ErrLoad,
- LlFatal, extra)
-
-template loadWarn*(ctx: CompileCtx, msg: string, modname: string,
- extra: seq[string] = @[]) =
- ctx.errors.baseError(msg, ParseNode(nil), modname & ".c4m", ErrLoad, LlWarn,
- extra)
-
-proc canProceed*(errs: seq[Con4mError]): bool =
- for err in errs:
- if err.severity in [LlErr, LlFatal]:
-
- return false
- return true
-
-template canProceed*(ctx: CompileCtx): bool =
- ctx.errors.canProceed()
-
-proc lookupMsg(code: string): string =
- for (k, v) in errorMsgs:
- if k == code:
- return v
-
- return "Unknown error code: " & code
-
-proc performSubs(extra: seq[string], s: var string) =
- for i, item in extra:
- s = s.replace("$" & `$`(i + 1), item)
-
-proc oneErrToRopeList(err: Con4mError, s: string): seq[Rope] =
- case err.severity
- of LlErr, LlFatal:
- result.add(fgColor("error:", "red").td().overflow(OTruncate))
- of LlWarn:
- result.add(fgColor("warn:", "yellow").td().overflow(OTruncate))
- of LLInfo:
- result.add(fgColor("info:", "atomiclime").td().overflow(OTruncate))
- of LlNone:
- unreachable
-
- if err.modname.len() != 0:
- let modname = fgColor(err.modname, "jazzberry") + text(":")
- result.add(modname.overflow(OTruncate))
- else:
- result.add(text(""))
- if err.line >= 1:
- let offset = td(text(`$`(err.line) & ":" & `$`(err.offset + 1) & ":"))
- result.add(offset.overflow(OTruncate))
- else:
- result.add(text(""))
-
- result.add(s.htmlStringToRope(markdown = false, add_div = false))
-
-proc getVerboseInfo(err: Con4mError): Rope =
- var
- noLoc = false
-
- if err.cursor == nil:
- return nil
-
- elif err.offset < 0:
- noLoc = true
-
- let
- src = $(err.cursor.runes)
- lines = src.split("\n")
-
- if lines.len() == 0 or err.line <= 0:
- return nil
-
- result = text(lines[err.line - 1]) + newBreak()
-
- if not noLoc:
- result += em(repeat((' '), err.offset) & "^") + newBreak()
-
- if err.detail != nil:
- result = result + err.detail
-
-proc getLocWidth(errs: seq[Con4mError]): int =
- for err in errs:
- let r = 2 + `$`(err.line).len() + `$`(err.offset + 1).len()
-
- if r > result:
- result = r
-
-proc getModuleWidth(errs: seq[Con4mError]): int =
- for err in errs:
- let l = err.modname.len()
- if l != 0 and (l + 1) > result:
- result = l + 1
-
-proc dupeLocationCheck(err: Con4mError, locs: var seq[(int, int)]): bool =
- for (l, c) in locs:
- if err.line == l and err.offset == c:
- return true
-
- locs.add((err.line, err.offset))
-
-proc formatErrors*(errs: seq[Con4mError], verbose = true): Rope =
- var
- errList: seq[seq[Rope]]
- locs: seq[(int, int)]
-
- let
- mw = errs.getModuleWidth() + 1
- lw = errs.getLocWidth() + 1
-
- for i, error in errs:
- if error.dupeLocationCheck(locs):
- continue
- if i == 30: # TODO; make this a configurable limit.
- break
- var msg = error.code.lookupMsg() & " (" & error.code & ")"
- error.extra.performSubs(msg)
- errList.add(error.oneErrToRopeList(msg))
-
- if not verbose:
- let table = quickTable[Rope](errList, noHeaders = true,
- borders = BorderNone)
- var one: Rope
- result = table.colWidths([(7, true), (mw, true), (lw, true), (0, false)])
- result = result.lpad(0, true).rpad(0, true)
- result = result.bpad(0, true).tpad(0, true)
- else:
- for i, item in errlist:
- var table = quickTable(@[item], noHeaders = true, borders = BorderNone)
- table = table.colWidths([(7, true), (mw, true), (lw, true), (0, false)])
- table = table.lpad(0, true).rpad(0, true).bpad(0, true).tpad(0, true)
- var one = table + container(errs[i].getVerboseInfo())
- result += one
-
-proc get_con4m_stack_trace(ctx: RuntimeState): Rope {.importc, cdecl.}
-proc find_string_at(mem: string, offset: int): string {.importc, cdecl.}
-proc toString(x: TypeId): string {.importc, cdecl.}
-
-proc location_from_instruction*(ctx: RuntimeState,
- ins: ptr ZInstruction): (string, int) =
- return (ctx.obj.moduleContents[ins.moduleId - 1].modname,
- int(ins.lineno))
-
-proc print_con4m_trace*(ctx: RuntimeState) {.exportc, cdecl.} =
- print(ctx.get_con4m_stack_trace(), file = stderr)
-
-proc formatLateError(err: string, severity: Con4mSeverity, location: string,
- args: seq[string], verbose = true): Rope =
- # `location` should be an indication of the instruction if we are executing,
- # attribute information if we're validating, and whatever is appropriate
- # if it's some other error.
- var
- msg = lookupMsg(err)
- row: seq[Rope]
-
- performSubs(args, msg)
-
- case severity
- of LlErr, LlFatal:
- row.add(fgColor("error: ", "red").td().lpad(0))
- of LlWarn:
- row.add(fgColor("warn: ", "yellow").td().overflow(OTruncate))
- of LLInfo:
- row.add(fgColor("info: ", "atomiclime").td().overflow(OTruncate))
- of LlNone:
- unreachable
-
- row.add(italic(location & ": "))
- row.add(markdown(msg))
- row.add(italic("(" & err & ")"))
-
- var
- width_used = 11 + location.len() + err.len()
- remains = terminalWidth() - width_used
- msg_width = row[2].runeLength() + 1
- msg_col: int
-
- if msg_width < remains:
- msg_col = msg_width
- else:
- msg_col = remains
-
- result = @[row].quickTable(noheaders = true, borders = BorderNone)
- result.colWidths([(7, true), (location.len() + 2, true),
- (msg_col, true), (err.len() + 2, true)])
- result.lpad(0, true).rpad(0, true).bpad(0, true).tpad(0, true)
-
-proc assemble_validation_msg(ctx: RuntimeState, path: string, msg: Rope,
- code: string, other: Rope = nil): Rope =
- var
- nim_path = path
- last_touch: Rope
-
- if nim_path.len() != 0:
- if nim_path[0] == '.':
- nim_path = nim_path[1 .. ^1]
-
- if nim_path[^1] == '.':
- nim_path = nim_path[0 ..< ^1]
-
- let attrOpt = ctx.attrs.lookup(nim_path)
- if attrOpt.isSome():
- let record = attrOpt.get()
- if record.lastset == nil:
- last_touch = text("Attribute has not been set this execution.")
- else:
- let (module, line) = ctx.location_from_instruction(record.lastset)
-
- last_touch = text("Attribute last set at: ") +
- em(module & ":" & $line)
-
- if nim_path.len() == 0:
- nim_path = "root attribute section"
-
- result = text("Validation for ") + em(nim_path) + text(" failed: ")
- result += italic(msg)
- result += last_touch
-
- if other != nil:
- result += text(" ") + other
-
- result += italic(" (" & code & ")")
- GC_ref(result)
-
-proc formatValidationError*(ctx: RuntimeState, attr: string, err: string,
- args: seq[string]): Rope =
- var msg = err.lookupMsg()
- performSubs(args, msg)
-
- let asRope = htmlStringToRope(msg, markdown = false, add_div = false)
-
- return ctx.assemble_validation_msg(attr, asRope, err)
-
-proc formatValidationError*(ctx: RuntimeState, attr: C4Str, err: string,
- args: seq[string]): Rope =
- return ctx.formatValidationError(attr.toNimStr(), err, args)
-
-proc customValidationError*(ctx: RuntimeState, path: C4Str, usrmsg: C4Str,
- cb: ptr ZCallback): Rope =
- let
- asRope = htmlStringToRope(usrmsg.toNimStr(), markdown = false,
- add_div = false)
- cbName = find_string_at(ctx.obj.staticData, cb.nameOffset)
- validator_info = text("Validation function: ") +
- em(cbname & cb.tid.toString())
-
- return ctx.assemble_validation_msg(path.toNimStr(), asRope,
- "CustomValidator", validator_info)
-
-proc runtimeIssue(ctx: RuntimeState, err: string, args: seq[string],
- severity = LLFatal) =
- if severity < config_log_level:
- return
-
- let
- instr = addr ctx.curModule.instructions[ctx.ip]
- (m, l) = ctx.location_from_instruction(instr)
- extra = if l == -1: "" else: ":" & $(l)
- loc = "When executing " & m & extra
-
- print(err.formatLateError(severity, loc, args), file = stderr)
-
-proc runtimeWarn*(ctx: RuntimeState, err: string, args: seq[string] = @[]) =
- if config_debug:
- ctx.print_con4m_trace()
- ctx.runtimeIssue(err, args, LlWarn)
-
-proc runtimeError*(ctx: RuntimeState, err: string, args: seq[string] = @[]) =
- ctx.print_con4m_trace()
- ctx.runtimeIssue(err, args)
- quit(-1)
-
-proc runtimeError*(ctx: RuntimeState, err: string, file: ZModuleInfo, line: int,
- args: seq[string] = @[]) =
- 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)
- quit(-1)
-
-proc codeGenError*(err: string, args: seq[string] = @[]) =
- # TODO: the module / function info needs to show up here.
- print(err.formatLateError(LlErr, "When generating code", args),
- file = stderr)
- quit(-1)
-
-proc objLoadWarn*(ctx: RuntimeState, err: string, args: seq[string] = @[]) =
- if config_log_level > LlWarn:
- return
-
- # TODO: the module / function info needs to show up here.
- print(err.formatLateError(LlWarn, "When loading object file", args),
- file = stderr)
- quit(-1)
-
-let sigNameMap = { 1: "SIGHUP", 2: "SIGINT", 3: "SIGQUIT", 4: "SIGILL",
- 6: "SIGABRT",7: "SIGBUS", 9: "SIGKILL", 11: "SIGSEGV",
- 15: "SIGTERM" }.toDict()
-var
- LC_ALL {.importc, header: "".}: cint
- savedTermState: Termcap
-
-proc restoreTerminal() {.noconv.} =
- tcSetAttr(cint(1), TcsaConst.TCSAFLUSH, savedTermState)
-
-proc regularTerminationSignal(signal: cint) {.noconv.} =
- let pid = getpid()
-
- print(h5("pid: " & $(pid) & " - Aborting due to signal: " &
- sigNameMap[signal] & "(" & $(signal) & ")"), file = stderr)
-
- let rt = getCon4mRuntime()
-
- if rt != nil and rt.running:
- print(getCon4mRuntime().get_con4m_stack_trace(), file = stderr)
- else:
- print(h2(text("Program was ") + em("NOT") +
- text(" executing when we crashed.")))
-
- when defined(debug):
- print(h4("Nim stack trace:"))
- echo getStackTrace()
- else:
- print(h4(text("Nim stack trace is unavailable " &
- "(must compile w/ ") + strong("-d:debug") + text(" for traces)")))
- var sigset: SigSet
-
- discard sigemptyset(sigset)
-
- for signal in [SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, SIGBUS, SIGKILL,
- SIGSEGV, SIGTERM]:
- discard sigaddset(sigset, signal)
- discard sigprocmask(SIG_SETMASK, sigset, sigset)
-
-
- exitnow(signal + 128)
-
-proc setlocale(category: cint, locale: cstring): cstring {. importc, cdecl,
- nodecl, header: "", discardable .}
-
-proc setupTerminal*() =
- setlocale(LC_ALL, cstring(""))
- tcGetAttr(cint(1), savedTermState)
- addQuitProc(restoreTerminal)
-
-proc setupSignalHandlers*() =
- var handler: SigAction
-
- handler.sa_handler = regularTerminationSignal
- handler.sa_flags = 0
-
- for signal in [SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, SIGBUS, SIGKILL,
- SIGSEGV, SIGTERM]:
- discard sigaction(signal, handler, nil)
+import "err"/[errbase, output, backtrace, signal]
+export errbase, output, backtrace, signal
diff --git a/src/err/backtrace.nim b/src/err/backtrace.nim
new file mode 100644
index 0000000..f14afb9
--- /dev/null
+++ b/src/err/backtrace.nim
@@ -0,0 +1,59 @@
+import ".."/common
+
+template getModName*(ctx: RuntimeState): string =
+ ctx.curModule.modName & ".c4m"
+
+template getLineNo*(ctx: RuntimeState): int =
+ ctx.curModule.instructions[ctx.ip].lineNo
+
+proc storageAddr*(ctx: RuntimeState, x: ZInstruction,
+ p: int64): ptr pointer =
+ if x.moduleId == -1:
+ result = addr ctx.stack[ctx.fp - (p * 2)]
+ elif x.moduleId == -2:
+ # Static data, no type info assumed.
+ result = cast[ptr pointer](addr ctx.obj.staticData[p])
+ else:
+ result = addr ctx.moduleAllocations[x.moduleId][p * 2]
+
+proc getSourceLoc*(ctx: RuntimeState): string =
+ ## Decode the source location of the current runtime state from
+ ## the current instruction.
+ let line = ctx.getLineNo()
+ if line != -1:
+ return ctx.getModName() & " (line #" & $(ctx.getLineNo()) & ")"
+ else:
+ return ""
+
+proc get_stack_trace*(ctx: RuntimeState): Rope {.exportc, cdecl.} =
+ var cells: seq[seq[string]] = @[@["Caller module", "Line #",
+ "Call target"]]
+
+ for i in 1 ..< ctx.numFrames:
+ var
+ frame = ctx.frameInfo[i]
+ row: seq[string]
+
+ row.add(frame.callModule.modname)
+ if i == ctx.numFrames - 1:
+ row.add($(ctx.getLineNo()))
+ else:
+ row.add($(frame.calllineno))
+
+ if frame.targetfunc == nil:
+ row.add(frame.targetmodule.modname & ".__mod_run__")
+ else:
+ row.add(frame.targetmodule.modname & "." & frame.targetfunc.funcname)
+
+ cells.add(row)
+
+ let loc = ctx.getSourceLoc()
+ if loc != "":
+ result = cells.quicktable(title = "Stack trace",
+ caption = "Source location: " &
+ ctx.getSourceLoc())
+ else:
+ result = cells.quicktable(title = "Stack trace")
+
+ result.colWidths([(17, true), (15, true), (20, true)])
+ result.tpad(0, true).bpad(0, true)
diff --git a/src/err/errbase.nim b/src/err/errbase.nim
new file mode 100644
index 0000000..1695722
--- /dev/null
+++ b/src/err/errbase.nim
@@ -0,0 +1,155 @@
+import ".."/common
+import "."/messages
+
+## If we have to bail on control flow, we'll do so here.
+proc newCon4mException*(errs: seq[Con4mError] = @[]): Con4mException =
+ result = new(Con4mException)
+ result.errors = errs
+
+proc newCon4mLongJmp*(msg = ""): Con4mLongJmp =
+ result = new(Con4mLongJmp)
+ result.msg = msg
+
+template con4mLongJmp*(msg = "") =
+ raise newCon4mLongJmp(msg)
+
+proc baseError*(list: var seq[Con4mError], code: string, cursor: StringCursor,
+ modname: string, line: int, lineOffset: int,
+ phase: Con4mErrPhase, severity = LlFatal,
+ extraContents: seq[string] = @[], detail: Rope = nil,
+ trace: string = "", ii: Option[InstantiationInfo] =
+ none(InstantiationInfo)) =
+ if severity < config_log_level:
+ return
+ var err = Con4mError(phase: phase, severity: severity, code: code,
+ cursor: cursor, modname: modname, line: line,
+ offset: lineOffset, extra: extraContents, detail: detail)
+
+ when not defined(release):
+ err.trace = trace
+ if ii.isSome():
+ err.ii = ii.get()
+
+ var foundCode = false
+ for (k, v) in errorMsgs:
+ if code == k:
+ foundCode = true
+ break
+ if not foundCode:
+ print fgColor("error: ", "red") + text("Error code '") + em(code) +
+ text("' was not found.")
+
+ list.add(err)
+
+proc baseError*(list: var seq[Con4mError], code: string, tok: Con4mToken,
+ modname: string, phase: Con4mErrPhase, severity = LlFatal,
+ extra: seq[string] = @[], detail: Rope = nil, trace = "",
+ ii = none(InstantiationInfo)) =
+ list.baseError(code, tok.cursor, modname, tok.lineNo, tok.lineOffset,
+ phase, severity, extra, detail, trace, ii)
+
+proc lexBaseError*(ctx: Module, basemsg: string, t: Con4mToken = nil,
+ subs: seq[string] = @[]) =
+ var t = t
+
+ if t == nil:
+ t = ctx.tokens[^1]
+
+ ctx.errors.baseError(basemsg, t.cursor, ctx.modname & ctx.ext, t.lineNo,
+ t.lineOffset, ErrLex, LlFatal, subs)
+
+proc lexFatal*(ctx: Module, basemsg: string, t: Con4mToken = nil) =
+ ctx.lexBaseError(basemsg, t)
+ raise newCon4mException()
+
+template lexError*(msg: string, t: Con4mToken = nil) =
+ ctx.lexFatal(msg, t)
+
+proc baseError*(list: var seq[Con4mError], code: string, node: ParseNode,
+ modname: string, phase: Con4mErrPhase, severity = LlFatal,
+ extra = seq[string](@[]), detail: Rope = nil, trace = "",
+ ii = none(InstantiationInfo)) =
+ if node == nil:
+ list.baseError(code, nil, modname, 0, 0, phase, severity, extra,
+ detail, trace, ii)
+ return
+
+ if node.err and severity != LlInfo:
+ return
+ if severity in [LlErr, LlFatal]:
+ node.err = true
+ list.baseError(code, node.token, modname, phase, severity, extra,
+ detail, trace, ii)
+
+template irError*(ctx: Module, msg: string, extra: seq[string] = @[],
+ w = ParseNode(nil), detail = Rope(nil)) =
+ var where = if w == nil: ctx.pt else: w
+ ctx.errors.baseError(msg, where, ctx.modname & ctx.ext, ErrIrGen, LlFatal,
+ extra, detail)
+
+template irError*(ctx: Module, msg: string, w: IrNode,
+ extra: seq[string] = @[], detail: Rope = nil) =
+ var where = if w == nil: ctx.pt else: w.parseNode
+ ctx.errors.baseError(msg, where, ctx.modname & ctx.ext, ErrIrGen, LlFatal,
+ extra, detail)
+
+template irNonFatal*(ctx: Module, msg: string, extra: seq[string] = @[],
+ w = ParseNode(nil)) =
+ # Things we consider errors, but we may end up allowing. Currently, this
+ # is just for use-before-def errors.
+ var where = if w == nil: ctx.pt else: w
+ ctx.errors.baseError(msg, where, ctx.modname & ctx.ext, ErrIrGen, LlErr,
+ extra)
+
+template irNonFatal*(ctx: Module, msg: string, w: IrNode,
+ extra: seq[string] = @[]) =
+ # Things we consider errors, but we may end up allowing. Currently, this
+ # is just for use-before-def errors.
+ var where = if w == nil: ctx.pt else: w.parseNode
+ ctx.errors.baseError(msg, where, ctx.modname & ctx.ext, ErrIrGen, LlErr,
+ extra)
+
+template irWarn*(ctx: Module, msg: string, extra: seq[string] = @[],
+ w = ParseNode(nil)) =
+ var where = if w == nil: ctx.pt else: w
+ ctx.errors.baseError(msg, where, ctx.modname & ctx.ext, ErrIrGen, LlWarn,
+ extra)
+
+template irWarn*(ctx: Module, msg: string, w: IrNode,
+ extra: seq[string] = @[]) =
+ var where = if w == nil: ctx.pt else: w.parseNode
+ ctx.errors.baseError(msg, where, ctx.modname & ctx.ext, ErrIrGen, LlWarn,
+ extra)
+
+template irInfo*(ctx: Module, msg: string, extra: seq[string] = @[],
+ w = ParseNode(nil)) =
+ var where = if w == nil: ctx.pt else: w
+ ctx.errors.baseError(msg, where, ctx.modname & ctx.ext, ErrIrGen, LlInfo,
+ extra)
+
+template irInfo*(ctx: Module, msg: string, w: IrNode,
+ extra: seq[string] = @[]) =
+ var where = if w == nil: ctx.pt else: w.parseNode
+ ctx.errors.baseError(msg, where, ctx.modname & ctx.ext, ErrIrGen, LlInfo,
+ extra)
+
+template loadError*(ctx: CompileCtx, msg: string, modname: string,
+ extra: seq[string] = @[]) =
+ # TODO: don't hardcode the extension.
+ ctx.errors.baseError(msg, ParseNode(nil), modname & ".c4m", ErrLoad,
+ LlFatal, extra)
+
+template loadWarn*(ctx: CompileCtx, msg: string, modname: string,
+ extra: seq[string] = @[]) =
+ ctx.errors.baseError(msg, ParseNode(nil), modname & ".c4m", ErrLoad, LlWarn,
+ extra)
+
+proc canProceed*(errs: seq[Con4mError]): bool =
+ for err in errs:
+ if err.severity in [LlErr, LlFatal]:
+
+ return false
+ return true
+
+template canProceed*(ctx: CompileCtx): bool =
+ ctx.errors.canProceed()
diff --git a/src/err/messages.nim b/src/err/messages.nim
new file mode 100644
index 0000000..a42de95
--- /dev/null
+++ b/src/err/messages.nim
@@ -0,0 +1,425 @@
+const errorMsgs* = [
+ ("FileNotFound", "Module could not be located."),
+ ("ModuleNotFound", "Module $1 could not be located."),
+ ("StrayCr", "Carriage return (\r) without newline."),
+ ("BadEscape", "Unterminated escape sequence in literal."),
+ ("UnicodeEscape", "Invalid unicode escape character found in literal."),
+ ("LitModExpected", "Expected a valid literal modifier"),
+ ("CommentTerm", "Unterminated C-style long comment."),
+ ("BadFloat", "Invalid character in float literal"),
+ ("MissingDigits", "Must have digits after the dot in a valid float literal"),
+ ("BadExponent", "Float exponent specified, with no exponent " &
+ "value provided"),
+ ("BadChar", "Invalid character literal"),
+ ("StringNl", "Unterminated single-quoted string."),
+ ("StringTerm", "Unterminated string literal"),
+ ("CharTerm", "Unterminated character literal"),
+ ("OtherTerm", "Unterminated literal"),
+ ("BadChar", "Invalid character found in token"),
+ ("StmtEnd", "Expected end of a statement after $1."),
+ ("NotALit", "Not a literal; literal modifier not allowed here."),
+ ("BadLitMod", "Unknown literal modifier $1 for $2" &
+ " syntax."),
+ ("LitModTypeErr", "Literal modifier $1 can't be used with " &
+ "$2 literals"),
+ ("MissingTok", "Expected $1 here."),
+ ("MemberSpec", "'.' operator must have an identifier on " &
+ "both sides."),
+ ("ItemExpected", "Expected either another item or '$1'"),
+ ("BadTuple", "Tuple types must contain two or more items."),
+ ("AccessExpr", "Expected either an identifier or paren here."),
+ ("ExprStart", "Expected the start of an expression here, but got $1."),
+ ("BinaryNot", "'not' operator takes only one operand."),
+ ("LitExpected", "Expected a literal value here."),
+ ("ForFromIx", "for ... from loops must have a single index " &
+ "variable."),
+ ("NameInTypeSpec", "'$1' is not a builtin type. " &
+ "Use struct[typename] for user types."),
+ ("BadOneOf", "OneOf types must have multiple type options that " &
+ "are more constrained than a generic type variable."),
+ ("BadObjType", "Object types must provide either a name or a type " &
+ "variable if specifying an object where no fields " &
+ "will be referenced."),
+ ("BadTypeDecl", "Invalid syntax inside a type declaration."),
+ ("BadFormalEnd", "Expected either a closing parenthesis (')'" &
+ " or an additional parameter."),
+ ("BadLock", "Attribute Lock operator (~) must be " &
+ "followed either by an assignment statement or " &
+ "an attribute to lock."),
+ ("BadUseSyntax", "'use' statement requires a string literal " &
+ " after 'from'"),
+ ("BadParamName", "'parameter' keyword must be followed by " &
+ "an attribute (which can be dotted), or the " &
+ "'var' keyword followed by a local variable " &
+ "name. The variable can be optionally typed."),
+ ("BadExternField", "Bad field for 'extern' block."),
+ ("NeedSig", "Field requires a function type signature."),
+ ("BadRCParam", "Reference counting specs must either be identifiers " &
+ "that match the names given in the function signature, " &
+ "or the special value return (for incref only)"),
+ ("BadCType", "Invalid C type for external parameter: $1"),
+ ("PureBool", "The 'pure' property for external functions " &
+ "must be a boolean value (true or " &
+ "false)"),
+ ("DupeExtField", "Duplicate field $1 provided for an " &
+ "extern block."),
+ ("DupeDllName", "Duplicate DLL name $1 provided for an " &
+ "external function (ignored)."),
+ ("DupeVal", "Duplicate parameter value $1 in the " &
+ "$2 property of the extern spec."),
+ ("ExtNotSpecd", "None of the external function parameters were given" &
+ " the name $1"),
+ ("ExtAllocNHold", "Extern function parameter $1 cannot be " &
+ "spec'd to have the external function hold memory " &
+ "we pass, and to allocate that memory."),
+ ("NoMemMan", "Since $1 is not a pointer or array type, " &
+ "memory management will not be performend, and this " &
+ "annotation will be ignored."),
+ ("WontLink", "Was not able to locate the external symbol $1" &
+ ", which may be required for running."),
+ ("MissingSym", "Was not able to locate the external symbol $1" &
+ "; program will crash if it is accessed, unless " &
+ "it is dynamically loaded first."),
+ ("PurePlz", "Please provide a value for the pure property " &
+ "for extern functions. Pure functions always " &
+ "return the same output for the same input, and do " &
+ "not do any I/O, allowing us to pre-execute."),
+ ("EofInBlock", "Block was not closed when end-of-file was found."),
+ ("TopLevelOnly", "'$1' is only allowed at the top-level of a " &
+ "module."),
+ ("TopLevelPlural", "$1 are only allowed at the top-level of a module."),
+ ("InLoopsOnly", "'$1' is only allowed inside loops."),
+ ("RetOutOfFunc", "'return' is only allowed inside functions."),
+ ("UToSSmaller", "Conversion of unsigned value to a smaller signed type" &
+ "can lead to both sign changes and truncated values."),
+ ("UToSSameSz", "Conversion of same-typed unsized values" &
+ "will turn large numbers negative. Explicitly cast " &
+ "to a bigger type if possible to avoid this scenario."),
+ ("CanTruncate", "Conversion to a smaller type can result in values " &
+ "getting truncated."),
+ ("StoU", "Conversion of signed integer to unsigned type turns " &
+ "negative values into larger integers."),
+ ("SToSmallerU", "Conversion of signed integer to unsigned type turns " &
+ "negative values into larger integers, and the " &
+ "smaller type may truncate."),
+ ("HexTooLarge", "Hex number is too large for type $1"),
+ ("IntTooLarge", "Integer literal is too large for type $1"),
+ ("FloatTooLarge", "Integer portion of float is too large; " &
+ "use 'e' notation."),
+ ("ExpTooLarge", "Exponent portion of float is too large."),
+ ("BadHex", "Invalid hex literal"),
+ ("BadInt", "Invalid int literal"),
+ ("BadBool", "Invalid bool literal."),
+ ("BadByte", "Invalid value for a byte (cannot be above 0xff)"),
+ ("BadCodepoint", "Invalid character; unicode values may not be " &
+ "above U+10FFFF."),
+ ("BadCP2", "Invalid character; the unicode standard does not " &
+ "allow values from U+D800 to U+DFFF"),
+ ("LoseFormat", "Conversion discards string formatting information."),
+ ("TypeNotSigned", "Literal is negative, but the specified type " &
+ "is unsigned."),
+ ("TypeMismatch", "$1 and $2 are incompatible types."),
+ ("LoopVarAssign", "Cannot assign to (or re-declare) loop iteration " &
+ "variables."),
+ ("AlreadyAFunc", "Variable names cannot have names that are identical to " &
+ "functions named in the same module's top-level."),
+ ("AlreadyAVar", "Already found a top-level variable with the same " &
+ "name as this function; this is not allowed within a " &
+ "module."),
+ ("VarRedef", "Variable $1 declared multiple times in " &
+ "the same scope."),
+ ("LabelDupe", "Nested loops have the same label ($1)."),
+ ("LabelLoc", "label statement must come immediately before " &
+ "either a for loop or a while loop."),
+ ("DeadCode", "Dead code after $1."),
+ ("ElifLoc", "elif statement must follow either an " &
+ "if block, or another elif block."),
+ ("ElseLoc", "else statement must follow either an " &
+ "if block or another elif block."),
+ ("BadLoopExit", "$1 to label $2 is invalid, because " &
+ "it is not contained inside a loop that have that label."),
+ ("DupeParamProp", "Duplicate parameter property for $1"),
+ ("ParamType", "Parameter property $1 must be a $2."),
+ ("BadParamProp", "Invalid property for a parameter: $1"),
+ ("EnumDeclConst", "Enum value assignments must be constants"),
+ ("EnumInt", "Currently, enum values may only be int types"),
+ ("EnumReuse", "Value $1 has already been used in this enum."),
+ ("BinaryOpCompat", "LHS and RHS do not have compatable types " &
+ "(lhs: $1; rhs: $2)."),
+ ("NoBoolCast", "The condition cannot be automatically converted to " &
+ "a true / false value; provide a specific check. " &
+ "Type of condition is: $1"),
+ ("CannotCast", "Cannot convert from type $1 to " &
+ "type $2"),
+ ("BoolAutoCast", "This condition (of type $1) is not a " &
+ "boolean value, but is being auto-cast."),
+ ("TyDiffListItem", "List item type is not consistent with other items (" &
+ "Previous items were $1; this is a $2)"),
+ ("TyDiffKey", "Key type is not consistent with other keys (" &
+ "Previous keys were $1; this is a $2)"),
+ ("TyDiffValue", "Value type is not consistent with other values (" &
+ "Previous values were $1; this is a " &
+ "$2)"),
+ ("VarInSecDef", "In explicit section declarations, the `=` is " &
+ "expecting valid attributes only on the LHS, but " &
+ "$1 is not allowed as a top-level attribute." &
+ "Assuming this is a variable, not an attribute. " &
+ "If it should be an attribute, fix the specification. " &
+ "Otherwise, to get rid of this warning, either move " &
+ "the assignment outside the section block, or " &
+ "Use the = assignment operator, which " &
+ "forces variable assignment."),
+ ("TryVarAssign", "The attribute specifcation doesn't allow this field. " &
+ "If you'd like a variable with this name, you can " &
+ "use the = operator, which creates a" &
+ "variable, even when there's an attribute of the " &
+ "same name."),
+ ("AssignToSec", "Cannot assign directly to $1; it is a " &
+ "section that supports multiple instances, not a " &
+ "field within a section."),
+ ("AsgnSingleton", "Cannot assign directly to $1; it is a single" &
+ "section that contains fields, not a field within " &
+ "a section."),
+ ("AsgnInstance", "Cannot assign directly to $1; the parent " &
+ "section supports multiple instances, so this name " &
+ "would be an instance, to which you can then add fields."),
+ ("SecUnderField", "Cannot assign; $1 is a field, so may not " &
+ "contain sub-fields."),
+ ("SectionNoSpec", "While $1 is a valid section, there is no " &
+ "known section type named $2."),
+ ("RootNoSec", "There is no allowed section or attribute named: " &
+ "$1"),
+ ("SectionDenied", "While $2 is a known section type, it is not " &
+ "permitted within $1."),
+ ("RootSecDenied", "The root attribute scope doesn't allow $1 " &
+ "sections."),
+ ("AttrNotSpecd", "$1 is not an allowed attribute name."),
+ ("BadSectionType", "There isn't an allowed section type named $1."),
+ ("SecNotAllowed", "A $1 section is not allowed from within $2."),
+ ("NotASingleton", "The $1 section expects an instance name."),
+ ("IsASingleton", "A $1 section does not allow named instances;" &
+ " there is only one unnamed section."),
+ ("TypeVsSpec", "The type of this use ($1) is not compatible " &
+ "with the specified type ($2)"),
+ ("UnsignedUMinus", "Unary minus would turn an unsigned value to a signed " &
+ "value; cast either to the same size (which you " &
+ "shouldn't do if the int value might be larger than the " &
+ "highest signed value), or cast to the next size up if " &
+ "possible."),
+ ("128BitLimit", "$1 is not currently supported for 128-bit integers."),
+ ("U64Div", "Division producing a float isn't currently defined for " &
+ "unsigned 64-bit integers."),
+ ("TupleLhs", "When unpacking a tuple, all items on the left hand " &
+ "side of the assignment must be valid variables, and " &
+ "cannot currently be attributes."),
+ ("MemberTop", "Attribute member access (.) can only be applied " &
+ "to attributes, not to values."),
+ ("TupleConstIx", "When indexing a tuple, the index must evaluate to " &
+ "a constant integer."),
+ ("TupleIxBound", "Constant index is out of bounds for the tuple being " &
+ "indexed."),
+ ("ContainerType", "Cannot distinguish what kind of container type this " &
+ "is (could be a dict, list, tuple, etc.) " &
+ " Please explicitly declare this type."),
+ ("BadUrl", "Invalid URL for loading con4m source code."),
+ ("InsecureUrl", "Warning: loading file from an insecure URL. " &
+ "The contents could be injected by an attacker."),
+ ("NoImpl", "Could not find any function implementations in scope " &
+ "named $1. Full signature: $1$2"),
+ ("NotAFunc", "There is a variable named $1, but there was " &
+ "Not a function in scope with the signature: " &
+ "$1$2"),
+ ("BadSig", "No implementation of $1 matched this $3. " &
+ "The $3 had the type: $1$2"),
+ ("CallAmbig", "Found multiple functions matching $1$2" &
+ " Please disambiguate."),
+ ("NotIndexible", "Type $1 is not indexible."),
+ ("CantLiftFunc", "Function $1 has the same name as a " &
+ "global variable. Currently, other modules will have to " &
+ "explicitly qualify the module to call this function."),
+ ("DoesntExit", "Control does not reach the end of this $1."),
+ ("ExprResult", "Result of expression is unused. Expression result is " &
+ "of type $1. Please assign to _ to discard."),
+ ("InfLoop", "While loop does not exit."),
+ ("UseBeforeDef", "Likely use of $1 before assignment;" &
+ " If you think this is wrong, set a default value " &
+ "at the top of this scope."),
+ ("ConstReassign", "This variable has been declared constant, and was " &
+ "previously assigned."),
+ ("Immutable", "Cannot modify immutable values."),
+ ("DefWoUse", "Variable $1 is defined, but never used."),
+ ("UseWoDef", "Definite use of $1 without assignment."),
+ ("ConstNotSet", "Constant $1 was declared, but no value was " &
+ "set."),
+ ("$assign", "Variables starting with $ are set by the " &
+ "system, and cannot be otherwise assigned."),
+ ("SigOverlap", "In this module, for the function name $1, " &
+ "implementations have overlapping signatures:
" &
+ "2. $3
" &
+ "1. $2 (line $4)."),
+ ("NextCase", "Statement seemed to end, and was expecting " &
+ "another case branch, an else, " &
+ " or } to end the cases."),
+ ("CaseBodyStart", "Case bodies may either be regular blocks (i.e., " &
+ "{ ... }) or can be a colon followed by a list " &
+ "of statements."),
+ ("DeadTypeCase", "Variable can never be of type $1; case cannot " &
+ "be taken."),
+ ("BadIPv4", "Invalid IPv4 address."),
+ ("BadPort", "Invalid port number."),
+ ("BadDuration", "Invalid duration literal."),
+ ("BadSize", "Invalid size literal."),
+ ("BadUrl", "Invalid URL literal."),
+ ("BadDateTime", "Invalid literal value for datetime type."),
+ ("BadDate", "Invalid literal value for date type."),
+ ("BadTime", "Invalid literal value for time type."),
+ ("InvalidOther", "Invalid literal, did not match any known literal type."),
+ ("OtherLit", "Inferred type of literal is $1. If incorrect, " &
+ "place in quotes and provide an explicit literal " &
+ "modifier after (e.g., \"2 gb\"'size for " &
+ "a size literal.)"),
+ ("OtherQuotes", "The := operator is used for values of special " &
+ "types, where the system takes all input till the end " &
+ " of the line, then tries to treat it as a primitive " &
+ "type. The quotes here are treated like they're inside " &
+ "the data you're providing, which may not be what you " &
+ "want."),
+ ("OtherBrak", "The := operator does not process brackets " &
+ "or parentheses of any kind; this will be treated as " &
+ "a single string."),
+ ("OtherNum", "The := operator does not result in numeric " &
+ "types; it asks the system to guess from a number of " &
+ "specialized types. Use = or add a modifier " &
+ "that's appropriate for the type of value you're trying " &
+ "to create."),
+ ("AttrUse", "Attempted to use an attribute $1 that has not " &
+ "been set."),
+ ("RT_BadTOp", "Unknown function id for internal type API call " &
+ "($1)"),
+ ("NeedExName", "Local function specs must include names for every " &
+ "parameter."),
+ ("DupeExParam", "Duplicate parameter name for local function: " &
+ "$1"),
+ ("ExternZArgCt", "Local function spec has more arguments than external " &
+ "function for $1, which is currently not " &
+ "allowed. C function has $3 args, local has $2 ($4)."),
+ ("ExternCArgCt", "External function spec has more arguments than local " &
+ "function for $1, which is currently not " &
+ "allowed. C function has $3 args, local has $2."),
+ ("ArrayIxErr", "Array index $1 is not in bounds."),
+ ("DictKeyErr", "Could not find dictionary key: $1."),
+ ("LockedAttr", "Attempted to set attribute $1, which is " &
+ "locked. Current value: $2 Attempted value: " &
+ "$3"),
+ ("AlreadyLocked", "Tried to set lock-on-write for attribute $1, " &
+ "but it is already locked."),
+ ("DupeSection", "Section $1 cannot be specified twice"),
+ ("SpecFieldType", "Specification field $1 was expected to be " &
+ "of type $3, but was of type $2"),
+ ("RequiredProp", "Specified field $1 does not have the " &
+ "required property $2."),
+ ("SpecLock", "Cannot make changes to the attribute specification; " &
+ "it is locked."),
+ ("NoSecSpec", "When processing $1, there is no section " &
+ "specification for $2"),
+ ("MissingField", "Attribute $1 must be set, but was not " &
+ "provided."),
+ ("NoSecType", "In the top level of a confspec block, got " &
+ "$1 but only allowed contents are: " &
+ "root, singleton or named" &
+ " blocks."),
+ ("InstSecStart", "Was expecting this to start an instantiation " &
+ "of a $1 section named $2, but " &
+ "would need a { here."),
+ ("RootOverwrite", "Overwriting an existing root $1."),
+ ("MissingSec", "Spec tries to $2 a section of type $1" &
+ ", but no section named $1 has been spec'd yet."),
+
+ ("BadRange", "When validating $1, an invalid value, " &
+ "$2, was chosen. But the value must be no less " &
+ "than $3 and no greater than $4."),
+ ("BadChoice", "When validating $1, an invalid value, " &
+ "$2, was chosen. Valid choices are: $3"),
+ ("MissingSection", "When validating $1, expected a required " &
+ "section named $2, which was not provided."),
+ ("MissingField", "When validating $1, expected a required " &
+ "field named $2, but it was not found."),
+ ("BadSection", "When validating $1, the section $2 " &
+ "is not allowed."),
+ ("NotTSpec", "When attempting to determine the type of the " &
+ "value $1$2, the field $3 was " &
+ "expected to specify the value's type, but " &
+ "that field did not contain a type specification."),
+ ("BadField", "When validating $1, found the field " &
+ "$2, which is not a valid field name for a " &
+ "$3 section."),
+ ("FieldMutex", "When validating $1, found the field " &
+ "$2, which is specified to not be able " &
+ "to appear together in a section with the field " &
+ "$3 (which was also present)."),
+ ("ExternVarargs", "External variable argument functions are not yet " &
+ " supported (external function $1)"),
+ ("StackOverflow", "Exceeded the maximum stack depth."),
+ ("NoSpecForSec", "Used a section $1, which is not defined " &
+ "by the attribute specification."),
+ ("InvalidStart", "Found the start of an attribute assignment that " &
+ "is invalid according to the spec, at: $1"),
+ ("NoInstance", "This section must have an instance, meaning " &
+ "you must specify named sections underneath it."),
+ ("DupeProp", "Property $1 cannot appear twice in one " &
+ "item spec"),
+ ("NotBool", "Property $1 is reuquired to be bool, " &
+ "but here is $2)"),
+ ("RangeAndChoice", "Cannot have both range and choice " &
+ "constraints for the same field."),
+ ("TyDiffListItem", "Inconsistent item type for choice. Previously " &
+ "it type was $1, but here it was $2"),
+ ("BadRangeSpec", "Start value for a range must be less than the end " &
+ "value."),
+ ("SpecWhenLocked", "Cannot add confspec fields; the " &
+ "specification has already been locked."),
+ ("DupeSpecField", "Duplicate specification for field $1."),
+ ("TCaseOverlap", "Type cases have overlapping types."),
+ ("DupeExclusion", "Exclusion duplicated for $1."),
+ ("DupeAllow", "Section $1 appears multiple times in " &
+ "allow list"),
+ ("AllowInReq", "Section $1 appears in both require" &
+ "and allow; suggest removing from allow"),
+ ("DupeRequire", "Section $1 appears multiple times in " &
+ "require list"),
+ ("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"),
+ ("DefaultMutex", "Module parameters cannot provide both a " &
+ "default value and an initialize. " &
+ "The initializer is intended for computing a default " &
+ "value when needed."),
+ ("InitArg", "Parameter initializer callbacks do not take take " &
+ "any arguments; they only return a value used to " &
+ "initialize the parameter."),
+ ("LitRequired", "Currently, $1 must be a $2 literal value, " &
+ "and cannot be computed or taken from a variable."),
+ ("CantInitialize", "Parameter cannot be initialized if it is private, " &
+ "because no default value has been set, and no " &
+ "initialize callback has been set. If this " &
+ "value is going to be initialized every time in " &
+ "the module, then it shouldn't be a parameter."),
+ ]
diff --git a/src/err/output.nim b/src/err/output.nim
new file mode 100644
index 0000000..1956448
--- /dev/null
+++ b/src/err/output.nim
@@ -0,0 +1,280 @@
+import "std"/terminal
+import ".."/common
+import "."/[messages, backtrace]
+
+proc lookupMsg(code: string): string =
+ for (k, v) in errorMsgs:
+ if k == code:
+ return v
+
+ return "Unknown error code: " & code
+
+proc performSubs(extra: seq[string], s: var string) =
+ for i, item in extra:
+ s = s.replace("$" & `$`(i + 1), item)
+
+proc oneErrToRopeList(err: Con4mError, s: string): seq[Rope] =
+ case err.severity
+ of LlErr, LlFatal:
+ result.add(fgColor("error:", "red").td().overflow(OTruncate))
+ of LlWarn:
+ result.add(fgColor("warn:", "yellow").td().overflow(OTruncate))
+ of LLInfo:
+ result.add(fgColor("info:", "atomiclime").td().overflow(OTruncate))
+ of LlNone:
+ unreachable
+
+ if err.modname.len() != 0:
+ let modname = fgColor(err.modname, "jazzberry") + text(":")
+ result.add(modname.overflow(OTruncate))
+ else:
+ result.add(text(""))
+ if err.line >= 1:
+ let offset = td(text(`$`(err.line) & ":" & `$`(err.offset + 1) & ":"))
+ result.add(offset.overflow(OTruncate))
+ else:
+ result.add(text(""))
+
+ result.add(s.htmlStringToRope(markdown = false, add_div = false))
+
+proc getVerboseInfo(err: Con4mError): Rope =
+ var
+ noLoc = false
+
+ if err.cursor == nil:
+ return nil
+
+ elif err.offset < 0:
+ noLoc = true
+
+ let
+ src = $(err.cursor.runes)
+ lines = src.split("\n")
+
+ if lines.len() == 0 or err.line <= 0:
+ return nil
+
+ result = text(lines[err.line - 1]) + newBreak()
+
+ if not noLoc:
+ result += em(repeat((' '), err.offset) & "^") + newBreak()
+
+ if err.detail != nil:
+ result = result + err.detail
+
+proc getLocWidth(errs: seq[Con4mError]): int =
+ for err in errs:
+ let r = 2 + `$`(err.line).len() + `$`(err.offset + 1).len()
+
+ if r > result:
+ result = r
+
+proc getModuleWidth(errs: seq[Con4mError]): int =
+ for err in errs:
+ let l = err.modname.len()
+ if l != 0 and (l + 1) > result:
+ result = l + 1
+
+proc dupeLocationCheck(err: Con4mError, locs: var seq[(int, int)]): bool =
+ for (l, c) in locs:
+ if err.line == l and err.offset == c:
+ return true
+
+ locs.add((err.line, err.offset))
+
+proc formatErrors*(errs: seq[Con4mError], verbose = true): Rope =
+ var
+ errList: seq[seq[Rope]]
+ locs: seq[(int, int)]
+
+ let
+ mw = errs.getModuleWidth() + 1
+ lw = errs.getLocWidth() + 1
+
+ for i, error in errs:
+ if error.dupeLocationCheck(locs):
+ continue
+ if i == 30: # TODO; make this a configurable limit.
+ break
+ var msg = error.code.lookupMsg() & " (" & error.code & ")"
+ error.extra.performSubs(msg)
+ errList.add(error.oneErrToRopeList(msg))
+
+ if not verbose:
+ let table = quickTable[Rope](errList, noHeaders = true,
+ borders = BorderNone)
+ var one: Rope
+ result = table.colWidths([(7, true), (mw, true), (lw, true), (0, false)])
+ result = result.lpad(0, true).rpad(0, true)
+ result = result.bpad(0, true).tpad(0, true)
+ else:
+ for i, item in errlist:
+ var table = quickTable(@[item], noHeaders = true, borders = BorderNone)
+ table = table.colWidths([(7, true), (mw, true), (lw, true), (0, false)])
+ table = table.lpad(0, true).rpad(0, true).bpad(0, true).tpad(0, true)
+ var one = table + container(errs[i].getVerboseInfo())
+ result += one
+
+proc find_string_at(mem: string, offset: int): string {.importc, cdecl.}
+proc toString(x: TypeId): string {.importc, cdecl.}
+
+proc location_from_instruction*(ctx: RuntimeState,
+ ins: ptr ZInstruction): (string, int) =
+ return (ctx.obj.moduleContents[ins.moduleId - 1].modname,
+ int(ins.lineno))
+
+proc print_con4m_trace*(ctx: RuntimeState) {.exportc, cdecl.} =
+ print(ctx.get_stack_trace(), file = stderr)
+
+proc formatLateError(err: string, severity: Con4mSeverity, location: string,
+ args: seq[string], verbose = true): Rope =
+ # `location` should be an indication of the instruction if we are executing,
+ # attribute information if we're validating, and whatever is appropriate
+ # if it's some other error.
+ var
+ msg = lookupMsg(err)
+ row: seq[Rope]
+
+ performSubs(args, msg)
+
+ case severity
+ of LlErr, LlFatal:
+ row.add(fgColor("error: ", "red").td().lpad(0))
+ of LlWarn:
+ row.add(fgColor("warn: ", "yellow").td().overflow(OTruncate))
+ of LLInfo:
+ row.add(fgColor("info: ", "atomiclime").td().overflow(OTruncate))
+ of LlNone:
+ unreachable
+
+ row.add(italic(location & ": "))
+ row.add(markdown(msg))
+ row.add(italic("(" & err & ")"))
+
+ var
+ width_used = 11 + location.len() + err.len()
+ remains = terminalWidth() - width_used
+ msg_width = row[2].runeLength() + 1
+ msg_col: int
+
+ if msg_width < remains:
+ msg_col = msg_width
+ else:
+ msg_col = remains
+
+ result = @[row].quickTable(noheaders = true, borders = BorderNone)
+ result.colWidths([(7, true), (location.len() + 2, true),
+ (msg_col, true), (err.len() + 2, true)])
+ result.lpad(0, true).rpad(0, true).bpad(0, true).tpad(0, true)
+
+proc assemble_validation_msg(ctx: RuntimeState, path: string, msg: Rope,
+ code: string, other: Rope = nil): Rope =
+ var
+ nim_path = path
+ last_touch: Rope
+
+ if nim_path.len() != 0:
+ if nim_path[0] == '.':
+ nim_path = nim_path[1 .. ^1]
+
+ if nim_path[^1] == '.':
+ nim_path = nim_path[0 ..< ^1]
+
+ let attrOpt = ctx.attrs.lookup(nim_path)
+ if attrOpt.isSome():
+ let record = attrOpt.get()
+ if record.lastset == nil:
+ last_touch = text("Attribute has not been set this execution.")
+ else:
+ let (module, line) = ctx.location_from_instruction(record.lastset)
+
+ last_touch = text("Attribute last set at: ") +
+ em(module & ":" & $line)
+
+ if nim_path.len() == 0:
+ nim_path = "root attribute section"
+
+ result = text("Validation for ") + em(nim_path) + text(" failed: ")
+ result += italic(msg)
+ result += last_touch
+
+ if other != nil:
+ result += text(" ") + other
+
+ result += italic(" (" & code & ")")
+ GC_ref(result)
+
+proc formatValidationError*(ctx: RuntimeState, attr: string, err: string,
+ args: seq[string]): Rope =
+ var msg = err.lookupMsg()
+ performSubs(args, msg)
+
+ let asRope = htmlStringToRope(msg, markdown = false, add_div = false)
+
+ return ctx.assemble_validation_msg(attr, asRope, err)
+
+proc formatValidationError*(ctx: RuntimeState, attr: C4Str, err: string,
+ args: seq[string]): Rope =
+ return ctx.formatValidationError(attr.toNimStr(), err, args)
+
+proc customValidationError*(ctx: RuntimeState, path: C4Str, usrmsg: C4Str,
+ cb: ptr ZCallback): Rope =
+ let
+ asRope = htmlStringToRope(usrmsg.toNimStr(), markdown = false,
+ add_div = false)
+ cbName = find_string_at(ctx.obj.staticData, cb.nameOffset)
+ validator_info = text("Validation function: ") +
+ em(cbname & cb.tid.toString())
+
+ return ctx.assemble_validation_msg(path.toNimStr(), asRope,
+ "CustomValidator", validator_info)
+
+proc runtimeIssue(ctx: RuntimeState, err: string, args: seq[string],
+ severity = LLFatal) =
+ if severity < config_log_level:
+ return
+
+ let
+ instr = addr ctx.curModule.instructions[ctx.ip]
+ (m, l) = ctx.location_from_instruction(instr)
+ extra = if l == -1: "" else: ":" & $(l)
+ loc = "When executing " & m & extra
+
+ print(err.formatLateError(severity, loc, args), file = stderr)
+
+proc runtimeWarn*(ctx: RuntimeState, err: string, args: seq[string] = @[]) =
+ if config_debug:
+ ctx.print_con4m_trace()
+ ctx.runtimeIssue(err, args, LlWarn)
+
+proc runtimeError*(ctx: RuntimeState, err: string, args: seq[string] = @[]) =
+ ctx.print_con4m_trace()
+ ctx.runtimeIssue(err, args)
+ quit(-1)
+
+proc runtimeError*(ctx: RuntimeState, err: string, file: ZModuleInfo, line: int,
+ args: seq[string] = @[]) =
+ 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)
+ quit(-1)
+
+proc codeGenError*(err: string, args: seq[string] = @[]) =
+ # TODO: the module / function info needs to show up here.
+ print(err.formatLateError(LlErr, "When generating code", args),
+ file = stderr)
+ quit(-1)
+
+proc objLoadWarn*(ctx: RuntimeState, err: string, args: seq[string] = @[]) =
+ if config_log_level > LlWarn:
+ return
+
+ # TODO: the module / function info needs to show up here.
+ print(err.formatLateError(LlWarn, "When loading object file", args),
+ file = stderr)
+ quit(-1)
diff --git a/src/err/signal.nim b/src/err/signal.nim
new file mode 100644
index 0000000..4a448b3
--- /dev/null
+++ b/src/err/signal.nim
@@ -0,0 +1,63 @@
+import std/posix
+import ".."/common
+import "."/backtrace
+
+let sigNameMap = { 1: "SIGHUP", 2: "SIGINT", 3: "SIGQUIT", 4: "SIGILL",
+ 6: "SIGABRT",7: "SIGBUS", 9: "SIGKILL", 11: "SIGSEGV",
+ 15: "SIGTERM" }.toDict()
+var
+ LC_ALL {.importc, header: "".}: cint
+ savedTermState: Termcap
+
+proc restoreTerminal() {.noconv.} =
+ tcSetAttr(cint(1), TcsaConst.TCSAFLUSH, savedTermState)
+
+proc regularTerminationSignal(signal: cint) {.noconv.} =
+ let pid = getpid()
+
+ print(h5("pid: " & $(pid) & " - Aborting due to signal: " &
+ sigNameMap[signal] & "(" & $(signal) & ")"), file = stderr)
+
+ let rt = getCon4mRuntime()
+
+ if rt != nil and rt.running:
+ print(getCon4mRuntime().get_stack_trace(), file = stderr)
+ else:
+ print(h2(text("Program was ") + em("NOT") +
+ text(" executing when we crashed.")))
+
+ when defined(debug):
+ print(h4("Nim stack trace:"))
+ echo getStackTrace()
+ else:
+ print(h4(text("Nim stack trace is unavailable " &
+ "(must compile w/ ") + strong("-d:debug") + text(" for traces)")))
+ var sigset: SigSet
+
+ discard sigemptyset(sigset)
+
+ for signal in [SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, SIGBUS, SIGKILL,
+ SIGSEGV, SIGTERM]:
+ discard sigaddset(sigset, signal)
+ discard sigprocmask(SIG_SETMASK, sigset, sigset)
+
+
+ exitnow(signal + 128)
+
+proc setlocale(category: cint, locale: cstring): cstring {.importc, cdecl,
+ nodecl, header: "", discardable .}
+
+proc setupTerminal*() =
+ setlocale(LC_ALL, cstring(""))
+ tcGetAttr(cint(1), savedTermState)
+ addQuitProc(restoreTerminal)
+
+proc setupSignalHandlers*() =
+ var handler: SigAction
+
+ handler.sa_handler = regularTerminationSignal
+ handler.sa_flags = 0
+
+ for signal in [SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, SIGBUS, SIGKILL,
+ SIGSEGV, SIGTERM]:
+ discard sigaction(signal, handler, nil)
diff --git a/src/irgen.nim b/src/irgen.nim
index d49eb55..65196d6 100644
--- a/src/irgen.nim
+++ b/src/irgen.nim
@@ -661,7 +661,7 @@ proc convertConstStmt(ctx: Module) =
proc convertParamBody(ctx: Module, sym: var SymbolInfo) =
var
- gotValid, gotDefault: bool
+ gotValid, gotDefault, gotInitialize, gotPrivate, isPrivate: bool
paramInfo = ParamInfo(shortdoc: ctx.pt.extractShortDocPlain(),
longdoc: ctx.pt.extractLongDocPlain())
@@ -672,6 +672,28 @@ proc convertParamBody(ctx: Module, sym: var SymbolInfo) =
continue
let propname = ctx.getText(i, 0)
case propname
+ of "initialize":
+ if gotDefault:
+ ctx.irError("DefaultMutex", @[], ctx.pt.children[i])
+ if gotInitialize:
+ ctx.irError("DupeParamProp", @["initialize"], ctx.pt.children[i])
+ let
+ irNode = ctx.downNode(i, 1)
+ to = irNode.tid.idToTypeRef()
+
+ if to.kind != C4Func:
+ ctx.irError("ParamType", @["initializor", "callback"],
+ ctx.pt.children[i])
+ elif to.items.len() != 0:
+ if to.items.len() != 1:
+ ctx.irError("InitArg", @[], ctx.pt.children[i])
+ else:
+ if unify(tFunc(@[sym.tid]), irNode.tid) == TBottom:
+ ctx.irError("InitArg", @[], ctx.pt.children[i])
+
+ paramInfo.initializeIr = some(irNode)
+ gotInitialize = true
+
of "validator":
let
irNode = ctx.downNode(i, 1)
@@ -682,7 +704,7 @@ proc convertParamBody(ctx: Module, sym: var SymbolInfo) =
ctx.pt.children[i])
elif to.items.len() != 0:
if to.items.len() != 2:
- ctx.irError("ParamValNArgs")
+ ctx.irError("ParamValNArgs", @[], ctx.pt.children[i])
else:
if unify(sym.tid, to.items[0]) == TBottom:
ctx.irError("ParamValParTy", @[sym.tid.toString(),
@@ -694,7 +716,22 @@ proc convertParamBody(ctx: Module, sym: var SymbolInfo) =
paramInfo.validatorIr = some(irNode)
gotValid = true
+
+ of "private":
+ if gotPrivate:
+ ctx.irError("DupeParamProp", @["private"], ctx.pt.children[i])
+ else:
+ gotPrivate = true
+ let irNode = ctx.downNode(i, 1)
+ ctx.typeCheck(irNode.tid, TBool, where = irNode.parseNode)
+ if irNode.contents.kind != IrLit:
+ ctx.irError("LitRequired",
+ @["private field for parameters", "bool"],
+ ctx.pt.children[i])
+ isPrivate = cast[bool](irNode.value)
of "default":
+ if gotInitialize:
+ ctx.irError("DefaultMutex", @[], ctx.pt.children[i])
if gotDefault:
ctx.irError("DupeParamProp", @["default"], ctx.pt.children[i])
continue
@@ -706,8 +743,12 @@ proc convertParamBody(ctx: Module, sym: var SymbolInfo) =
ctx.irError("BadParamProp", @[propname], ctx.pt.children[i])
continue
- paramInfo.sym = sym
- sym.pinfo = paramInfo
+ paramInfo.private = isPrivate
+ paramInfo.sym = sym
+ sym.pinfo = paramInfo
+
+ if isPrivate and not gotDefault and not gotInitialize:
+ ctx.irError("CantInitialize")
ctx.params.add(paramInfo)
diff --git a/src/modparams.nim b/src/modparams.nim
index 99ef782..64b7393 100644
--- a/src/modparams.nim
+++ b/src/modparams.nim
@@ -9,10 +9,10 @@ proc get_parameter_info*(ctx: RuntimeState): seq[ZParamExport] =
data = ZParamExport(modid: int32(module.moduleId),
modname: module.modname,
paramid: int32(i),
+ private: param.private,
shortdoc: param.shortdoc,
longdoc: param.longdoc,
tid: param.tid)
- default: pointer
if param.attr != "":
data.name = param.attr
@@ -43,6 +43,15 @@ proc get_parameter_info*(ctx: RuntimeState): seq[ZParamExport] =
data.havedefault = true
data.default = param.default
+ elif param.iFnIx != -1:
+ if param.iNative:
+ ctx.z_ffi_call(cast[int](param.iFnIx))
+ data.default = ctx.returnRegister
+ data.tid = cast[TypeId](ctx.rrType)
+ else:
+ data.default = ctx.foreign_z_call(cast[int](param.iFnIx))
+ data.tid = cast[TypeId](ctx.rrType)
+
proc get_current_instruction(ctx: RuntimeState):
ptr ZInstruction {.importc, cdecl.}
@@ -52,10 +61,10 @@ proc run_param_validator*(ctx: RuntimeState, p: ZParamInfo,
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 p.vFnIx != -1:
+ var cb = ZCallback(impl: cast[pointer](int64(p.vFnIx)))
- if not p.native:
+ if not p.vNative:
cb.ffi = true
let s = ctx.run_callback_internal(addr cb, [(val, p.tid)])
diff --git a/src/parse.nim b/src/parse.nim
index fa375c3..fa10d95 100644
--- a/src/parse.nim
+++ b/src/parse.nim
@@ -2301,7 +2301,10 @@ proc buildType*(n: ParseNode, tvars: Dict[string, TypeId]): TypeId =
va = true
items.add(kid.children[0].buildType(tvars))
elif kid.kind == NodeReturnType:
- items.add(kid.children[0].buildType(tvars))
+ if kid.children.len() == 0:
+ items.add(tVar())
+ else:
+ items.add(kid.children[0].buildType(tvars))
else:
items.add(kid.buildType(tvars))
return newFuncType(items, va).typeId
diff --git a/src/rtmarshal.nim b/src/rtmarshal.nim
index abb5000..3f0bb10 100644
--- a/src/rtmarshal.nim
+++ b/src/rtmarshal.nim
@@ -135,9 +135,12 @@ proc marshal_mod_params(rt: RuntimeState, params: seq[ZParamInfo]): C4Str =
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(item.private.marshal_bool())
toAdd.add(cast[pointer](item.tid.followForwards()).marshal_64_bit_value())
- toAdd.add(item.funcIx.marshal_32_bit_value())
+ toAdd.add(item.vFnIx.marshal_32_bit_value())
+ toAdd.add(item.iFnIx.marshal_32_bit_value())
+ toAdd.add(item.vNative.marshal_bool())
+ toAdd.add(item.iNative.marshal_bool())
toAdd.add(item.haveDefault.marshal_bool())
if item.haveDefault:
toAdd.add(marshal(item.default, item.tid, rt.memos))
@@ -150,9 +153,12 @@ proc unmarshal_mod_params(rt: RuntimeState, p: var cstring): seq[ZParamInfo] =
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.private = p.unmarshal_bool()
param.tid = cast[TypeId](p.unmarshal_64_bit_value())
- param.funcIx = p.unmarshal_32_bit_value()
+ param.vFnIx = p.unmarshal_32_bit_value()
+ param.iFnIx = p.unmarshal_32_bit_value()
+ param.vNative = p.unmarshal_bool()
+ param.iNative = p.unmarshal_bool()
param.haveDefault = p.unmarshal_bool()
if param.haveDefault:
param.default = p.unmarshal(param.tid, rt.memos)
diff --git a/src/vm.nim b/src/vm.nim
index 01d4cd9..0949a44 100644
--- a/src/vm.nim
+++ b/src/vm.nim
@@ -8,6 +8,22 @@ proc using_spec(ctx: RuntimeState): bool {.importc, cdecl.}
const sz = sizeof(ZInstruction)
+
+proc bailHere*(ctx: RuntimeState, errCode: string, extra: seq[string] = @[]) =
+ var
+ errList: seq[Con4mError]
+ runes = ctx.curModule.source.toRunes()
+
+ errList.baseError(code = errCode,
+ cursor = StringCursor(runes: runes),
+ modname = ctx.getModName(),
+ line = ctx.getLineNo(), lineOffset = -1,
+ phase = ErrRuntime, severity = LlFatal,
+ extraContents = extra)
+ print errlist.formatErrors()
+ ctx.print_con4m_trace()
+ quit(-1)
+
when USE_TRACE:
import codegen # For instr.toString()
var trace_on = true
@@ -99,85 +115,10 @@ else:
template traceExecution(ctx: RuntimeState, instr: ZInstruction) =
discard
-template getModName(ctx: RuntimeState): string =
- ctx.curModule.modName & ".c4m"
-
-template getLineNo(ctx: RuntimeState): int =
- ctx.curModule.instructions[ctx.ip].lineNo
-
-proc getSourceLoc*(ctx: RuntimeState): string =
- ## Decode the source location of the current runtime state from
- ## the current instruction.
- let line = ctx.getLineNo()
- if line != -1:
- return ctx.getModName() & " (line #" & $(ctx.getLineNo()) & ")"
- else:
- return ""
-
proc get_current_instruction*(ctx: RuntimeState):
ptr ZInstruction {.exportc, cdecl.} =
return addr ctx.curModule.instructions[ctx.ip]
-proc get_con4m_stack_trace*(ctx: RuntimeState): Rope {.exportc, cdecl.} =
- var cells: seq[seq[string]] = @[@["Caller module", "Line #",
- "Call target"]]
-
- for i in 1 ..< ctx.numFrames:
- var
- frame = ctx.frameInfo[i]
- row: seq[string]
-
- row.add(frame.callModule.modname)
- if i == ctx.numFrames - 1:
- row.add($(ctx.getLineNo()))
- else:
- row.add($(frame.calllineno))
-
- if frame.targetfunc == nil:
- row.add(frame.targetmodule.modname & ".__mod_run__")
- else:
- row.add(frame.targetmodule.modname & "." & frame.targetfunc.funcname)
-
- cells.add(row)
-
- let loc = ctx.getSourceLoc()
- if loc != "":
- result = cells.quicktable(title = "Stack trace",
- caption = "Source location: " &
- ctx.getSourceLoc())
- else:
- result = cells.quicktable(title = "Stack trace")
-
- result.colWidths([(17, true), (15, true), (20, true)])
- result.tpad(0, true).bpad(0, true)
-
-proc printStackTrace*(ctx: RuntimeState) =
- print ctx.get_con4m_stack_trace()
-
-proc bailHere(ctx: RuntimeState, errCode: string, extra: seq[string] = @[]) =
- var
- errList: seq[Con4mError]
- runes = ctx.curModule.source.toRunes()
-
- errList.baseError(code = errCode,
- cursor = StringCursor(runes: runes),
- modname = ctx.getModName(),
- line = ctx.getLineNo(), lineOffset = -1,
- phase = ErrRuntime, severity = LlFatal,
- extraContents = extra)
- print errlist.formatErrors()
- ctx.printStackTrace()
- quit(-1)
-
-proc storageAddr(ctx: RuntimeState, x: ZInstruction,
- p: int64): ptr pointer =
- if x.moduleId == -1:
- result = addr ctx.stack[ctx.fp - (p * 2)]
- elif x.moduleId == -2:
- # Static data, no type info assumed.
- result = cast[ptr pointer](addr ctx.obj.staticData[p])
- else:
- result = addr ctx.moduleAllocations[x.moduleId][p * 2]
template storageAddr(ctx: RuntimeState, x: ZInstruction): ptr pointer =
ctx.storageAddr(x, int64(x.arg))
@@ -238,7 +179,7 @@ proc runMainExecutionLoop(ctx: RuntimeState): int =
ctx.stack[ctx.sp] = cast[pointer](instr.immediate)
of ZPushRes:
ctx.sp -= 1
- ctx.stack[ctx.sp] = cast[pointer](instr.typeInfo)
+ ctx.stack[ctx.sp] = cast[pointer](ctx.rrType)
ctx.sp -= 1
ctx.stack[ctx.sp] = ctx.returnRegister
of ZSetRes:
@@ -769,7 +710,7 @@ proc runMainExecutionLoop(ctx: RuntimeState): int =
of ZModuleRet:
if ctx.numFrames <= 2:
- return
+ return 0
discard ctx.module_lock_stack.pop()
leaveFrame()
of ZFFICall:
@@ -829,6 +770,7 @@ proc runMainExecutionLoop(ctx: RuntimeState): int =
ctx.tupleStash = cast[Con4mTuple](ctx.stack[ctx.sp])
ctx.stashType = ctx.stack[ctx.sp + 1]
ctx.sp += 2
+
of ZUnpack:
let
n = instr.arg
@@ -852,6 +794,11 @@ proc runMainExecutionLoop(ctx: RuntimeState): int =
address[] = valaddr
typeaddr[] = cast[pointer](itemType)
+ of ZBail:
+ let s = cast[cstring](ctx.stack[ctx.sp])
+ print(fgColor("error: ", "red") + text($s))
+ return -1
+
ctx.ip += 1
proc setupArena(ctx: RuntimeState,
diff --git a/tests/modparam.c4m b/tests/modparam.c4m
index ec2cd4c..4dc1adb 100644
--- a/tests/modparam.c4m
+++ b/tests/modparam.c4m
@@ -1,3 +1,7 @@
+func value_provider() {
+ return 101
+}
+
func example_checker(x) {
result = ""
@@ -16,7 +20,7 @@ parameter var example1 {
parameter var example2 {
"This should be some documentation."
"Also this."
- default: 101
+ initialize: func value_provider()
validator: func example_checker(int) -> string
}