# # # The Nimrod Compiler # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # # This file implements the evaluator for Nimrod code. # The evaluator is very slow, but simple. Since this # is used mainly for evaluating macros and some other # stuff at compile time, performance is not that # important. import strutils, magicsys, lists, options, ast, astalgo, trees, treetab, nimsets, msgs, os, condsyms, idents, renderer, types, passes, semfold, transf, parser, ropes, rodread, idgen, osproc, streams, evaltempl type PStackFrame* = ref TStackFrame TStackFrame*{.final.} = object mapping*: TIdNodeTable # mapping from symbols to nodes prc*: PSym # current prc; proc that is evaluated call*: PNode next*: PStackFrame # for stacking params*: TNodeSeq # parameters passed to the proc TEvalMode* = enum ## reason for evaluation emRepl, ## evaluate because in REPL mode emConst, ## evaluate for 'const' according to spec emOptimize, ## evaluate for optimization purposes (same as ## emConst?) emStatic ## evaluate for enforced compile time eval ## ('static' context) TEvalContext* = object of passes.TPassContext module*: PSym tos*: PStackFrame # top of stack lastException*: PNode callsite: PNode # for 'callsite' magic mode*: TEvalMode globals*: TIdNodeTable # state of global vars getType*: proc(n: PNode): PNode {.closure.} PEvalContext* = ref TEvalContext TEvalFlag = enum efNone, efLValue TEvalFlags = set[TEvalFlag] const evalMaxIterations = 500_000 # max iterations of all loops evalMaxRecDepth = 10_000 # max recursion depth for evaluation # other idea: use a timeout! -> Wether code compiles depends on the machine # the compiler runs on then! Bad idea! proc newStackFrame*(): PStackFrame = new(result) initIdNodeTable(result.mapping) result.params = @[] proc newEvalContext*(module: PSym, filename: string, mode: TEvalMode): PEvalContext = new(result) result.module = module result.mode = mode initIdNodeTable(result.globals) proc pushStackFrame*(c: PEvalContext, t: PStackFrame) {.inline.} = t.next = c.tos c.tos = t proc popStackFrame*(c: PEvalContext) {.inline.} = if c.tos != nil: c.tos = c.tos.next else: InternalError("popStackFrame") proc evalMacroCall*(c: PEvalContext, n, nOrig: PNode, sym: PSym): PNode proc evalAux(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode proc raiseCannotEval(c: PEvalContext, info: TLineInfo): PNode = result = newNodeI(nkExceptBranch, info) # creating a nkExceptBranch without sons # means that it could not be evaluated proc stackTraceAux(x: PStackFrame) = if x != nil: stackTraceAux(x.next) var info = if x.call != nil: x.call.info else: UnknownLineInfo() # we now use the same format as in system/except.nim var s = toFilename(info) var line = toLineNumber(info) if line > 0: add(s, '(') add(s, $line) add(s, ')') if x.prc != nil: for k in 1..max(1, 25-s.len): add(s, ' ') add(s, x.prc.name.s) MsgWriteln(s) proc stackTrace(c: PEvalContext, n: PNode, msg: TMsgKind, arg = "") = MsgWriteln("stack trace: (most recent call last)") stackTraceAux(c.tos) LocalError(n.info, msg, arg) proc isSpecial(n: PNode): bool {.inline.} = result = n.kind == nkExceptBranch proc myreset(n: PNode) = when defined(system.reset): var oldInfo = n.info reset(n[]) n.info = oldInfo proc evalIf(c: PEvalContext, n: PNode): PNode = var i = 0 var length = sonsLen(n) while (i < length) and (sonsLen(n.sons[i]) >= 2): result = evalAux(c, n.sons[i].sons[0], {}) if isSpecial(result): return if (result.kind == nkIntLit) and (result.intVal != 0): return evalAux(c, n.sons[i].sons[1], {}) inc(i) if (i < length) and (sonsLen(n.sons[i]) < 2): result = evalAux(c, n.sons[i].sons[0], {}) else: result = emptyNode proc evalCase(c: PEvalContext, n: PNode): PNode = result = evalAux(c, n.sons[0], {}) if isSpecial(result): return var res = result result = emptyNode for i in countup(1, sonsLen(n) - 1): if n.sons[i].kind == nkOfBranch: for j in countup(0, sonsLen(n.sons[i]) - 2): if overlap(res, n.sons[i].sons[j]): return evalAux(c, lastSon(n.sons[i]), {}) else: result = evalAux(c, lastSon(n.sons[i]), {}) var gWhileCounter: int # Use a counter to prevent endless loops! # We make this counter global, because otherwise # nested loops could make the compiler extremely slow. gNestedEvals: int # count the recursive calls to ``evalAux`` to prevent # endless recursion proc evalWhile(c: PEvalContext, n: PNode): PNode = while true: result = evalAux(c, n.sons[0], {}) if isSpecial(result): return if getOrdValue(result) == 0: break result = evalAux(c, n.sons[1], {}) case result.kind of nkBreakStmt: if result.sons[0].kind == nkEmpty: result = emptyNode # consume ``break`` token # Bugfix (see tmacro2): but break in any case! break of nkExceptBranch, nkReturnToken: break else: nil dec(gWhileCounter) if gWhileCounter <= 0: stackTrace(c, n, errTooManyIterations) break proc evalBlock(c: PEvalContext, n: PNode): PNode = result = evalAux(c, n.sons[1], {}) if result.kind == nkBreakStmt: if result.sons[0] != nil: assert(result.sons[0].kind == nkSym) if n.sons[0].kind != nkEmpty: assert(n.sons[0].kind == nkSym) if result.sons[0].sym.id == n.sons[0].sym.id: result = emptyNode # blocks can only be left with an explicit label now! #else: # result = emptyNode # consume ``break`` token proc evalFinally(c: PEvalContext, n, exc: PNode): PNode = var finallyNode = lastSon(n) if finallyNode.kind == nkFinally: result = evalAux(c, finallyNode, {}) if result.kind != nkExceptBranch: result = exc else: result = exc proc evalTry(c: PEvalContext, n: PNode): PNode = result = evalAux(c, n.sons[0], {}) case result.kind of nkBreakStmt, nkReturnToken: nil of nkExceptBranch: if sonsLen(result) >= 1: # creating a nkExceptBranch without sons means that it could not be # evaluated var exc = result var i = 1 var length = sonsLen(n) while (i < length) and (n.sons[i].kind == nkExceptBranch): var blen = sonsLen(n.sons[i]) if blen == 1: # general except section: result = evalAux(c, n.sons[i].sons[0], {}) exc = result break else: for j in countup(0, blen - 2): assert(n.sons[i].sons[j].kind == nkType) if exc.typ.id == n.sons[i].sons[j].typ.id: result = evalAux(c, n.sons[i].sons[blen - 1], {}) exc = result break inc(i) result = evalFinally(c, n, exc) else: result = evalFinally(c, n, emptyNode) proc getNullValue(typ: PType, info: TLineInfo): PNode proc getNullValueAux(obj: PNode, result: PNode) = case obj.kind of nkRecList: for i in countup(0, sonsLen(obj) - 1): getNullValueAux(obj.sons[i], result) of nkRecCase: getNullValueAux(obj.sons[0], result) for i in countup(1, sonsLen(obj) - 1): getNullValueAux(lastSon(obj.sons[i]), result) of nkSym: var s = obj.sym var p = newNodeIT(nkExprColonExpr, result.info, s.typ) addSon(p, newSymNode(s, result.info)) addSon(p, getNullValue(s.typ, result.info)) addSon(result, p) else: InternalError(result.info, "getNullValueAux") proc getNullValue(typ: PType, info: TLineInfo): PNode = var t = skipTypes(typ, abstractRange) result = emptyNode case t.kind of tyBool, tyEnum, tyChar, tyInt..tyInt64: result = newNodeIT(nkIntLit, info, t) of tyUInt..tyUInt64: result = newNodeIT(nkUIntLit, info, t) of tyFloat..tyFloat128: result = newNodeIt(nkFloatLit, info, t) of tyVar, tyPointer, tyPtr, tyRef, tyCString, tySequence, tyString, tyExpr, tyStmt, tyTypeDesc, tyProc: result = newNodeIT(nkNilLit, info, t) of tyObject: result = newNodeIT(nkPar, info, t) getNullValueAux(t.n, result) # initialize inherited fields: var base = t.sons[0] while base != nil: getNullValueAux(skipTypes(base, skipPtrs).n, result) base = base.sons[0] of tyArray, tyArrayConstr: result = newNodeIT(nkBracket, info, t) for i in countup(0, int(lengthOrd(t)) - 1): addSon(result, getNullValue(elemType(t), info)) of tyTuple: result = newNodeIT(nkPar, info, t) for i in countup(0, sonsLen(t) - 1): var p = newNodeIT(nkExprColonExpr, info, t.sons[i]) var field = if t.n != nil: t.n.sons[i].sym else: newSym( skField, getIdent(":tmp" & $i), t.owner, info) addSon(p, newSymNode(field, info)) addSon(p, getNullValue(t.sons[i], info)) addSon(result, p) of tySet: result = newNodeIT(nkCurly, info, t) else: InternalError("getNullValue: " & $t.kind) proc evalVar(c: PEvalContext, n: PNode): PNode = for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] if a.kind == nkCommentStmt: continue if a.kind != nkIdentDefs: return raiseCannotEval(c, n.info) # XXX var (x, y) = z support? #assert(a.sons[0].kind == nkSym) can happen for transformed vars if a.sons[2].kind != nkEmpty: result = evalAux(c, a.sons[2], {}) if isSpecial(result): return else: result = getNullValue(a.sons[0].typ, a.sons[0].info) if a.sons[0].kind == nkSym: var v = a.sons[0].sym IdNodeTablePut(c.tos.mapping, v, result) else: # assign to a.sons[0]: var x = result result = evalAux(c, a.sons[0], {}) if isSpecial(result): return myreset(x) x.kind = result.kind x.typ = result.typ case x.kind of nkCharLit..nkInt64Lit: x.intVal = result.intVal of nkFloatLit..nkFloat64Lit: x.floatVal = result.floatVal of nkStrLit..nkTripleStrLit: x.strVal = result.strVal of nkIdent: x.ident = result.ident of nkSym: x.sym = result.sym else: if x.kind notin {nkEmpty..nkNilLit}: discardSons(x) for i in countup(0, sonsLen(result) - 1): addSon(x, result.sons[i]) result = emptyNode proc evalCall(c: PEvalContext, n: PNode): PNode = var d = newStackFrame() d.call = n var prc = n.sons[0] let isClosure = prc.kind == nkClosure setlen(d.params, sonsLen(n) + ord(isClosure)) if isClosure: #debug prc result = evalAux(c, prc.sons[1], {efLValue}) if isSpecial(result): return d.params[sonsLen(n)] = result result = evalAux(c, prc.sons[0], {}) else: result = evalAux(c, prc, {}) if isSpecial(result): return prc = result # bind the actual params to the local parameter of a new binding if prc.kind != nkSym: InternalError(n.info, "evalCall " & n.renderTree) return d.prc = prc.sym if prc.sym.kind notin {skProc, skConverter, skMacro}: InternalError(n.info, "evalCall") return for i in countup(1, sonsLen(n) - 1): result = evalAux(c, n.sons[i], {}) if isSpecial(result): return d.params[i] = result if n.typ != nil: d.params[0] = getNullValue(n.typ, n.info) pushStackFrame(c, d) result = evalAux(c, prc.sym.getBody, {}) if result.kind == nkExceptBranch: return if n.typ != nil: result = d.params[0] popStackFrame(c) proc aliasNeeded(n: PNode, flags: TEvalFlags): bool = result = efLValue in flags or n.typ == nil or n.typ.kind in {tyExpr, tyStmt, tyTypeDesc} proc evalVariable(c: PStackFrame, sym: PSym, flags: TEvalFlags): PNode = # We need to return a node to the actual value, # which can be modified. var x = c while x != nil: if sym.kind == skResult and x.params.len > 0: result = x.params[0] if result == nil: result = emptyNode return result = IdNodeTableGet(x.mapping, sym) if result != nil and not aliasNeeded(result, flags): result = copyTree(result) if result != nil: return x = x.next #internalError(sym.info, "cannot eval " & sym.name.s) result = raiseCannotEval(nil, sym.info) #result = emptyNode proc evalGlobalVar(c: PEvalContext, s: PSym, flags: TEvalFlags): PNode = if sfCompileTime in s.flags or c.mode == emRepl: result = IdNodeTableGet(c.globals, s) if result != nil: if not aliasNeeded(result, flags): result = copyTree(result) else: result = s.ast if result == nil or result.kind == nkEmpty: result = getNullValue(s.typ, s.info) else: result = evalAux(c, result, {}) if isSpecial(result): return IdNodeTablePut(c.globals, s, result) else: result = raiseCannotEval(nil, s.info) proc evalArrayAccess(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode = result = evalAux(c, n.sons[0], flags) if isSpecial(result): return var x = result result = evalAux(c, n.sons[1], {}) if isSpecial(result): return var idx = getOrdValue(result) result = emptyNode case x.kind of nkPar: if (idx >= 0) and (idx < sonsLen(x)): result = x.sons[int(idx)] if result.kind == nkExprColonExpr: result = result.sons[1] if not aliasNeeded(result, flags): result = copyTree(result) else: stackTrace(c, n, errIndexOutOfBounds) of nkBracket, nkMetaNode: if (idx >= 0) and (idx < sonsLen(x)): result = x.sons[int(idx)] if not aliasNeeded(result, flags): result = copyTree(result) else: stackTrace(c, n, errIndexOutOfBounds) of nkStrLit..nkTripleStrLit: if efLValue in flags: return raiseCannotEval(c, n.info) result = newNodeIT(nkCharLit, x.info, getSysType(tyChar)) if (idx >= 0) and (idx < len(x.strVal)): result.intVal = ord(x.strVal[int(idx) + 0]) elif idx == len(x.strVal): nil else: stackTrace(c, n, errIndexOutOfBounds) else: stackTrace(c, n, errNilAccess) proc evalFieldAccess(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode = # a real field access; proc calls have already been transformed # XXX: field checks! result = evalAux(c, n.sons[0], flags) if isSpecial(result): return var x = result if x.kind != nkPar: return raiseCannotEval(c, n.info) var field = n.sons[1].sym for i in countup(0, sonsLen(x) - 1): var it = x.sons[i] if it.kind != nkExprColonExpr: # lookup per index: result = x.sons[field.position] if result.kind == nkExprColonExpr: result = result.sons[1] if not aliasNeeded(result, flags): result = copyTree(result) return #InternalError(it.info, "evalFieldAccess") if it.sons[0].sym.name.id == field.name.id: result = x.sons[i].sons[1] if not aliasNeeded(result, flags): result = copyTree(result) return stackTrace(c, n, errFieldXNotFound, field.name.s) result = emptyNode proc evalAsgn(c: PEvalContext, n: PNode): PNode = var a = n.sons[0] if a.kind == nkBracketExpr and a.sons[0].typ.kind in {tyString, tyCString}: result = evalAux(c, a.sons[0], {efLValue}) if isSpecial(result): return var x = result result = evalAux(c, a.sons[1], {}) if isSpecial(result): return var idx = getOrdValue(result) result = evalAux(c, n.sons[1], {}) if isSpecial(result): return if result.kind != nkCharLit: return raiseCannotEval(c, n.info) if (idx >= 0) and (idx < len(x.strVal)): x.strVal[int(idx)] = chr(int(result.intVal)) else: stackTrace(c, n, errIndexOutOfBounds) else: result = evalAux(c, n.sons[0], {efLValue}) if isSpecial(result): return var x = result result = evalAux(c, n.sons[1], {}) if isSpecial(result): return myreset(x) x.kind = result.kind x.typ = result.typ case x.kind of nkCharLit..nkInt64Lit: x.intVal = result.intVal of nkFloatLit..nkFloat64Lit: x.floatVal = result.floatVal of nkStrLit..nkTripleStrLit: x.strVal = result.strVal of nkIdent: x.ident = result.ident of nkSym: x.sym = result.sym else: if x.kind notin {nkEmpty..nkNilLit}: discardSons(x) for i in countup(0, sonsLen(result) - 1): addSon(x, result.sons[i]) result = emptyNode assert result.kind == nkEmpty proc evalSwap(c: PEvalContext, n: PNode): PNode = result = evalAux(c, n.sons[0], {efLValue}) if isSpecial(result): return var x = result result = evalAux(c, n.sons[1], {efLValue}) if isSpecial(result): return if x.kind != result.kind: stackTrace(c, n, errCannotInterpretNodeX, $n.kind) else: case x.kind of nkCharLit..nkInt64Lit: swap(x.intVal, result.intVal) of nkFloatLit..nkFloat64Lit: swap(x.floatVal, result.floatVal) of nkStrLit..nkTripleStrLit: swap(x.strVal, result.strVal) of nkIdent: swap(x.ident, result.ident) of nkSym: swap(x.sym, result.sym) else: var tmpn = copyTree(x) discardSons(x) for i in countup(0, sonsLen(result) - 1): addSon(x, result.sons[i]) discardSons(result) for i in countup(0, sonsLen(tmpn) - 1): addSon(result, tmpn.sons[i]) result = emptyNode proc evalSym(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode = var s = n.sym case s.kind of skProc, skConverter, skMacro, skType: result = n #result = s.getBody of skVar, skLet, skForVar, skTemp, skResult: if sfGlobal notin s.flags: result = evalVariable(c.tos, s, flags) else: result = evalGlobalVar(c, s, flags) of skParam: # XXX what about LValue? if s.position + 1 <% c.tos.params.len: result = c.tos.params[s.position + 1] of skConst: result = s.ast of skEnumField: result = newIntNodeT(s.position, n) else: result = nil if result == nil or {sfImportc, sfForward} * s.flags != {}: result = raiseCannotEval(c, n.info) proc evalIncDec(c: PEvalContext, n: PNode, sign: biggestInt): PNode = result = evalAux(c, n.sons[1], {efLValue}) if isSpecial(result): return var a = result result = evalAux(c, n.sons[2], {}) if isSpecial(result): return var b = result case a.kind of nkCharLit..nkInt64Lit: a.intval = a.intVal + sign * getOrdValue(b) else: return raiseCannotEval(c, n.info) result = emptyNode proc getStrValue(n: PNode): string = case n.kind of nkStrLit..nkTripleStrLit: result = n.strVal else: InternalError(n.info, "getStrValue") result = "" proc evalEcho(c: PEvalContext, n: PNode): PNode = for i in countup(1, sonsLen(n) - 1): result = evalAux(c, n.sons[i], {}) if isSpecial(result): return Write(stdout, getStrValue(result)) writeln(stdout, "") result = emptyNode proc evalExit(c: PEvalContext, n: PNode): PNode = if c.mode in {emRepl, emStatic}: result = evalAux(c, n.sons[1], {}) if isSpecial(result): return Message(n.info, hintQuitCalled) quit(int(getOrdValue(result))) else: result = raiseCannotEval(c, n.info) proc evalOr(c: PEvalContext, n: PNode): PNode = result = evalAux(c, n.sons[1], {}) if isSpecial(result): return if result.kind != nkIntLit: InternalError(n.info, "evalOr") elif result.intVal == 0: result = evalAux(c, n.sons[2], {}) proc evalAnd(c: PEvalContext, n: PNode): PNode = result = evalAux(c, n.sons[1], {}) if isSpecial(result): return if result.kind != nkIntLit: InternalError(n.info, "evalAnd") elif result.intVal != 0: result = evalAux(c, n.sons[2], {}) proc evalNew(c: PEvalContext, n: PNode): PNode = #if c.mode == emOptimize: return raiseCannotEval(c, n.info) # we ignore the finalizer for now and most likely forever :-) result = evalAux(c, n.sons[1], {efLValue}) if isSpecial(result): return var a = result var t = skipTypes(n.sons[1].typ, abstractVar) if a.kind == nkEmpty: InternalError(n.info, "first parameter is empty") myreset(a) a.kind = nkRefTy a.info = n.info a.typ = t a.sons = nil addSon(a, getNullValue(t.sons[0], n.info)) result = emptyNode proc evalDeref(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode = result = evalAux(c, n.sons[0], {efLValue}) if isSpecial(result): return case result.kind of nkNilLit: stackTrace(c, n, errNilAccess) of nkRefTy: # XXX efLValue? result = result.sons[0] else: result = raiseCannotEval(c, n.info) proc evalAddr(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode = result = evalAux(c, n.sons[0], {efLValue}) if isSpecial(result): return var a = result var t = newType(tyPtr, c.module) addSonSkipIntLit(t, a.typ) result = newNodeIT(nkRefTy, n.info, t) addSon(result, a) proc evalConv(c: PEvalContext, n: PNode): PNode = result = evalAux(c, n.sons[1], {efLValue}) if isSpecial(result): return if result.typ != nil: var a = result result = foldConv(n, a) if result == nil: # foldConv() cannot deal with everything that we want to do here: result = a proc evalCheckedFieldAccess(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode = result = evalAux(c, n.sons[0], flags) proc evalUpConv(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode = result = evalAux(c, n.sons[0], flags) if isSpecial(result): return var dest = skipTypes(n.typ, abstractPtrs) var src = skipTypes(result.typ, abstractPtrs) if inheritanceDiff(src, dest) > 0: stackTrace(c, n, errInvalidConversionFromTypeX, typeToString(src)) proc evalRangeChck(c: PEvalContext, n: PNode): PNode = result = evalAux(c, n.sons[0], {}) if isSpecial(result): return var x = result result = evalAux(c, n.sons[1], {}) if isSpecial(result): return var a = result result = evalAux(c, n.sons[2], {}) if isSpecial(result): return var b = result if leValueConv(a, x) and leValueConv(x, b): result = x # a <= x and x <= b result.typ = n.typ else: stackTrace(c, n, errGenerated, msgKindToString(errIllegalConvFromXtoY) % [ typeToString(n.sons[0].typ), typeToString(n.typ)]) proc evalConvStrToCStr(c: PEvalContext, n: PNode): PNode = result = evalAux(c, n.sons[0], {}) if isSpecial(result): return result.typ = n.typ proc evalConvCStrToStr(c: PEvalContext, n: PNode): PNode = result = evalAux(c, n.sons[0], {}) if isSpecial(result): return result.typ = n.typ proc evalRaise(c: PEvalContext, n: PNode): PNode = if c.mode in {emRepl, emStatic}: if n.sons[0].kind != nkEmpty: result = evalAux(c, n.sons[0], {}) if isSpecial(result): return var a = result result = newNodeIT(nkExceptBranch, n.info, a.typ) addSon(result, a) c.lastException = result elif c.lastException != nil: result = c.lastException else: stackTrace(c, n, errExceptionAlreadyHandled) result = newNodeIT(nkExceptBranch, n.info, nil) addSon(result, ast.emptyNode) else: result = raiseCannotEval(c, n.info) proc evalReturn(c: PEvalContext, n: PNode): PNode = if n.sons[0].kind != nkEmpty: result = evalAsgn(c, n.sons[0]) if isSpecial(result): return result = newNodeIT(nkReturnToken, n.info, nil) proc evalProc(c: PEvalContext, n: PNode): PNode = if n.sons[genericParamsPos].kind == nkEmpty: var s = n.sons[namePos].sym if (resultPos < sonsLen(n)) and (n.sons[resultPos].kind != nkEmpty): var v = n.sons[resultPos].sym result = getNullValue(v.typ, n.info) IdNodeTablePut(c.tos.mapping, v, result) result = evalAux(c, s.getBody, {}) if result.kind == nkReturnToken: result = IdNodeTableGet(c.tos.mapping, v) else: result = evalAux(c, s.getBody, {}) if result.kind == nkReturnToken: result = emptyNode else: result = emptyNode proc evalHigh(c: PEvalContext, n: PNode): PNode = result = evalAux(c, n.sons[1], {}) if isSpecial(result): return case skipTypes(n.sons[1].typ, abstractVar).kind of tyOpenArray, tySequence, tyVarargs: result = newIntNodeT(sonsLen(result)-1, n) of tyString: result = newIntNodeT(len(result.strVal) - 1, n) else: InternalError(n.info, "evalHigh") proc evalOf(c: PEvalContext, n: PNode): PNode = result = evalAux(c, n.sons[1], {}) if isSpecial(result): return result = newIntNodeT(ord(inheritanceDiff(result.typ, n.sons[2].typ) >= 0), n) proc evalSetLengthStr(c: PEvalContext, n: PNode): PNode = result = evalAux(c, n.sons[1], {efLValue}) if isSpecial(result): return var a = result result = evalAux(c, n.sons[2], {}) if isSpecial(result): return var b = result case a.kind of nkStrLit..nkTripleStrLit: var newLen = int(getOrdValue(b)) setlen(a.strVal, newLen) else: InternalError(n.info, "evalSetLengthStr") result = emptyNode proc evalSetLengthSeq(c: PEvalContext, n: PNode): PNode = result = evalAux(c, n.sons[1], {efLValue}) if isSpecial(result): return var a = result result = evalAux(c, n.sons[2], {}) if isSpecial(result): return var b = result if a.kind != nkBracket: InternalError(n.info, "evalSetLengthSeq") return var newLen = int(getOrdValue(b)) var oldLen = sonsLen(a) setlen(a.sons, newLen) for i in countup(oldLen, newLen - 1): a.sons[i] = getNullValue(skipTypes(n.sons[1].typ, abstractVar), n.info) result = emptyNode proc evalNewSeq(c: PEvalContext, n: PNode): PNode = result = evalAux(c, n.sons[1], {efLValue}) if isSpecial(result): return var a = result result = evalAux(c, n.sons[2], {}) if isSpecial(result): return var b = result var t = skipTypes(n.sons[1].typ, abstractVar) if a.kind == nkEmpty: InternalError(n.info, "first parameter is empty") myreset(a) a.kind = nkBracket a.info = n.info a.typ = t a.sons = nil var L = int(getOrdValue(b)) newSeq(a.sons, L) for i in countup(0, L-1): a.sons[i] = getNullValue(t.sons[0], n.info) result = emptyNode proc evalIncl(c: PEvalContext, n: PNode): PNode = result = evalAux(c, n.sons[1], {efLValue}) if isSpecial(result): return var a = result result = evalAux(c, n.sons[2], {}) if isSpecial(result): return var b = result if not inSet(a, b): addSon(a, copyTree(b)) result = emptyNode proc evalExcl(c: PEvalContext, n: PNode): PNode = result = evalAux(c, n.sons[1], {efLValue}) if isSpecial(result): return var a = result result = evalAux(c, n.sons[2], {}) if isSpecial(result): return var b = newNodeIT(nkCurly, n.info, n.sons[1].typ) addSon(b, result) var r = diffSets(a, b) discardSons(a) for i in countup(0, sonsLen(r) - 1): addSon(a, r.sons[i]) result = emptyNode proc evalAppendStrCh(c: PEvalContext, n: PNode): PNode = result = evalAux(c, n.sons[1], {efLValue}) if isSpecial(result): return var a = result result = evalAux(c, n.sons[2], {}) if isSpecial(result): return var b = result case a.kind of nkStrLit..nkTripleStrLit: add(a.strVal, chr(int(getOrdValue(b)))) else: return raiseCannotEval(c, n.info) result = emptyNode proc evalConStrStr(c: PEvalContext, n: PNode): PNode = # we cannot use ``evalOp`` for this as we can here have more than 2 arguments var a = newNodeIT(nkStrLit, n.info, n.typ) a.strVal = "" for i in countup(1, sonsLen(n) - 1): result = evalAux(c, n.sons[i], {}) if isSpecial(result): return a.strVal.add(getStrOrChar(result)) result = a proc evalAppendStrStr(c: PEvalContext, n: PNode): PNode = result = evalAux(c, n.sons[1], {efLValue}) if isSpecial(result): return var a = result result = evalAux(c, n.sons[2], {}) if isSpecial(result): return var b = result case a.kind of nkStrLit..nkTripleStrLit: a.strVal = a.strVal & getStrOrChar(b) else: return raiseCannotEval(c, n.info) result = emptyNode proc evalAppendSeqElem(c: PEvalContext, n: PNode): PNode = result = evalAux(c, n.sons[1], {efLValue}) if isSpecial(result): return var a = result result = evalAux(c, n.sons[2], {}) if isSpecial(result): return var b = result if a.kind == nkBracket: addSon(a, copyTree(b)) else: return raiseCannotEval(c, n.info) result = emptyNode proc evalRepr(c: PEvalContext, n: PNode): PNode = result = evalAux(c, n.sons[1], {}) if isSpecial(result): return result = newStrNodeT(renderTree(result, {renderNoComments}), n) proc isEmpty(n: PNode): bool = result = n != nil and n.kind == nkEmpty # The lexer marks multi-line strings as residing at the line where they # are closed. This function returns the line where the string begins # Maybe the lexer should mark both the beginning and the end of expressions, # then this function could be removed. proc stringStartingLine(s: PNode): int = result = s.info.line.int - countLines(s.strVal) proc evalParseExpr(c: PEvalContext, n: PNode): PNode = var code = evalAux(c, n.sons[1], {}) var ast = parseString(code.getStrValue, code.info.toFilename, code.stringStartingLine) if sonsLen(ast) != 1: GlobalError(code.info, errExprExpected, "multiple statements") result = ast.sons[0] result.typ = newType(tyExpr, c.module) proc evalParseStmt(c: PEvalContext, n: PNode): PNode = var code = evalAux(c, n.sons[1], {}) result = parseString(code.getStrValue, code.info.toFilename, code.stringStartingLine) result.typ = newType(tyStmt, c.module) proc evalTypeTrait*(n: PNode, context: PSym): PNode = ## XXX: This should be pretty much guaranteed to be true # by the type traits procs' signatures, but until the # code is more mature it doesn't hurt to be extra safe internalAssert n.sons.len >= 2 and n.sons[1].kind == nkSym let typ = n.sons[1].sym.typ.skipTypes({tyTypeDesc}) case n.sons[0].sym.name.s.normalize of "name": result = newStrNode(nkStrLit, typ.typeToString(preferName)) result.typ = newType(tyString, context) result.info = n.info else: internalAssert false proc evalIsOp*(n: PNode): PNode = InternalAssert n.sonsLen == 3 and n[1].kind == nkSym and n[1].sym.kind == skType and n[2].kind in {nkStrLit..nkTripleStrLit, nkType} let t1 = n[1].sym.typ if n[2].kind in {nkStrLit..nkTripleStrLit}: case n[2].strVal.normalize of "closure": let t = skipTypes(t1, abstractRange) result = newIntNode(nkIntLit, ord(t.kind == tyProc and t.callConv == ccClosure)) else: let t2 = n[2].typ var match = if t2.kind == tyTypeClass: matchTypeClass(t2, t1) else: sameType(t1, t2) result = newIntNode(nkIntLit, ord(match)) result.typ = n.typ proc expectString(n: PNode) = if n.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit}: GlobalError(n.info, errStringLiteralExpected) proc evalSlurp*(e: PNode, module: PSym): PNode = expectString(e) result = newNodeIT(nkStrLit, e.info, getSysType(tyString)) try: var filename = e.strVal.FindFile result.strVal = readFile(filename) # we produce a fake include statement for every slurped filename, so that # the module dependencies are accurate: appendToModule(module, newNode(nkIncludeStmt, e.info, @[ newStrNode(nkStrLit, filename)])) except EIO: result.strVal = "" LocalError(e.info, errCannotOpenFile, e.strVal) proc readOutput(p: PProcess): string = result = "" var output = p.outputStream discard p.waitForExit while not output.atEnd: result.add(output.readLine) proc evalStaticExec*(cmd, input: PNode): PNode = expectString(cmd) var p = startCmd(cmd.strVal) if input != nil: expectString(input) p.inputStream.write(input.strVal) p.inputStream.close() result = newStrNode(nkStrLit, p.readOutput) result.typ = getSysType(tyString) result.info = cmd.info proc evalExpandToAst(c: PEvalContext, original: PNode): PNode = var n = original.copyTree macroCall = n.sons[1] expandedSym = macroCall.sons[0].sym for i in countup(1, macroCall.sonsLen - 1): macroCall.sons[i] = evalAux(c, macroCall.sons[i], {}) case expandedSym.kind of skTemplate: let genSymOwner = if c.tos != nil and c.tos.prc != nil: c.tos.prc else: c.module result = evalTemplate(macroCall, expandedSym, genSymOwner) of skMacro: # At this point macroCall.sons[0] is nkSym node. # To be completely compatible with normal macro invocation, # we want to replace it with nkIdent node featuring # the original unmangled macro name. macroCall.sons[0] = newIdentNode(expandedSym.name, expandedSym.info) result = evalMacroCall(c, macroCall, original, expandedSym) else: InternalError(macroCall.info, "ExpandToAst: expanded symbol is no macro or template") result = emptyNode proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode = var m = getMagic(n) case m of mNone: result = evalCall(c, n) of mOf: result = evalOf(c, n) of mSizeOf: result = raiseCannotEval(c, n.info) of mHigh: result = evalHigh(c, n) of mExit: result = evalExit(c, n) of mNew, mNewFinalize: result = evalNew(c, n) of mNewSeq: result = evalNewSeq(c, n) of mSwap: result = evalSwap(c, n) of mInc: result = evalIncDec(c, n, 1) of ast.mDec: result = evalIncDec(c, n, - 1) of mEcho: result = evalEcho(c, n) of mSetLengthStr: result = evalSetLengthStr(c, n) of mSetLengthSeq: result = evalSetLengthSeq(c, n) of mIncl: result = evalIncl(c, n) of mExcl: result = evalExcl(c, n) of mAnd: result = evalAnd(c, n) of mOr: result = evalOr(c, n) of mAppendStrCh: result = evalAppendStrCh(c, n) of mAppendStrStr: result = evalAppendStrStr(c, n) of mAppendSeqElem: result = evalAppendSeqElem(c, n) of mParseExprToAst: result = evalParseExpr(c, n) of mParseStmtToAst: result = evalParseStmt(c, n) of mExpandToAst: result = evalExpandToAst(c, n) of mTypeTrait: n.sons[1] = evalAux(c, n.sons[1], {}) result = evalTypeTrait(n, c.module) of mIs: n.sons[1] = evalAux(c, n.sons[1], {}) result = evalIsOp(n) of mSlurp: result = evalSlurp(evalAux(c, n.sons[1], {}), c.module) of mStaticExec: let cmd = evalAux(c, n.sons[1], {}) let input = if n.sonsLen == 3: evalAux(c, n.sons[2], {}) else: nil result = evalStaticExec(cmd, input) of mNLen: result = evalAux(c, n.sons[1], {efLValue}) if isSpecial(result): return var a = result result = newNodeIT(nkIntLit, n.info, n.typ) case a.kind of nkEmpty..nkNilLit: nil else: result.intVal = sonsLen(a) of mNChild: result = evalAux(c, n.sons[1], {efLValue}) if isSpecial(result): return var a = result result = evalAux(c, n.sons[2], {efLValue}) if isSpecial(result): return var k = getOrdValue(result) if not (a.kind in {nkEmpty..nkNilLit}) and (k >= 0) and (k < sonsLen(a)): result = a.sons[int(k)] if result == nil: result = newNode(nkEmpty) else: stackTrace(c, n, errIndexOutOfBounds) result = emptyNode of mNSetChild: result = evalAux(c, n.sons[1], {efLValue}) if isSpecial(result): return var a = result result = evalAux(c, n.sons[2], {efLValue}) if isSpecial(result): return var b = result result = evalAux(c, n.sons[3], {efLValue}) if isSpecial(result): return var k = getOrdValue(b) if (k >= 0) and (k < sonsLen(a)) and not (a.kind in {nkEmpty..nkNilLit}): a.sons[int(k)] = result else: stackTrace(c, n, errIndexOutOfBounds) result = emptyNode of mNAdd: result = evalAux(c, n.sons[1], {efLValue}) if isSpecial(result): return var a = result result = evalAux(c, n.sons[2], {efLValue}) if isSpecial(result): return addSon(a, result) result = emptyNode of mNAddMultiple: result = evalAux(c, n.sons[1], {efLValue}) if isSpecial(result): return var a = result result = evalAux(c, n.sons[2], {efLValue}) if isSpecial(result): return for i in countup(0, sonsLen(result) - 1): addSon(a, result.sons[i]) result = emptyNode of mNDel: result = evalAux(c, n.sons[1], {efLValue}) if isSpecial(result): return var a = result result = evalAux(c, n.sons[2], {efLValue}) if isSpecial(result): return var b = result result = evalAux(c, n.sons[3], {efLValue}) if isSpecial(result): return for i in countup(0, int(getOrdValue(result)) - 1): delSon(a, int(getOrdValue(b))) result = emptyNode of mNKind: result = evalAux(c, n.sons[1], {}) if isSpecial(result): return var a = result result = newNodeIT(nkIntLit, n.info, n.typ) result.intVal = ord(a.kind) of mNIntVal: result = evalAux(c, n.sons[1], {}) if isSpecial(result): return var a = result result = newNodeIT(nkIntLit, n.info, n.typ) case a.kind of nkCharLit..nkInt64Lit: result.intVal = a.intVal else: stackTrace(c, n, errFieldXNotFound, "intVal") of mNFloatVal: result = evalAux(c, n.sons[1], {}) if isSpecial(result): return var a = result result = newNodeIT(nkFloatLit, n.info, n.typ) case a.kind of nkFloatLit..nkFloat64Lit: result.floatVal = a.floatVal else: stackTrace(c, n, errFieldXNotFound, "floatVal") of mNSymbol: result = evalAux(c, n.sons[1], {efLValue}) if isSpecial(result): return if result.kind != nkSym: stackTrace(c, n, errFieldXNotFound, "symbol") of mNIdent: result = evalAux(c, n.sons[1], {}) if isSpecial(result): return if result.kind != nkIdent: stackTrace(c, n, errFieldXNotFound, "ident") of mNGetType: var ast = evalAux(c, n.sons[1], {}) InternalAssert c.getType != nil result = c.getType(ast) of mNStrVal: result = evalAux(c, n.sons[1], {}) if isSpecial(result): return var a = result result = newNodeIT(nkStrLit, n.info, n.typ) case a.kind of nkStrLit..nkTripleStrLit: result.strVal = a.strVal else: stackTrace(c, n, errFieldXNotFound, "strVal") of mNSetIntVal: result = evalAux(c, n.sons[1], {efLValue}) if isSpecial(result): return var a = result result = evalAux(c, n.sons[2], {}) if isSpecial(result): return if a.kind in {nkCharLit..nkInt64Lit} and result.kind in {nkCharLit..nkInt64Lit}: a.intVal = result.intVal else: stackTrace(c, n, errFieldXNotFound, "intVal") result = emptyNode of mNSetFloatVal: result = evalAux(c, n.sons[1], {efLValue}) if isSpecial(result): return var a = result result = evalAux(c, n.sons[2], {}) if isSpecial(result): return if a.kind in {nkFloatLit..nkFloat64Lit} and result.kind in {nkFloatLit..nkFloat64Lit}: a.floatVal = result.floatVal else: stackTrace(c, n, errFieldXNotFound, "floatVal") result = emptyNode of mNSetSymbol: result = evalAux(c, n.sons[1], {efLValue}) if isSpecial(result): return var a = result result = evalAux(c, n.sons[2], {efLValue}) if isSpecial(result): return if a.kind == nkSym and result.kind == nkSym: a.sym = result.sym else: stackTrace(c, n, errFieldXNotFound, "symbol") result = emptyNode of mNSetIdent: result = evalAux(c, n.sons[1], {efLValue}) if isSpecial(result): return var a = result result = evalAux(c, n.sons[2], {efLValue}) if isSpecial(result): return if a.kind == nkIdent and result.kind == nkIdent: a.ident = result.ident else: stackTrace(c, n, errFieldXNotFound, "ident") result = emptyNode of mNSetType: result = evalAux(c, n.sons[1], {efLValue}) if isSpecial(result): return var a = result result = evalAux(c, n.sons[2], {efLValue}) if isSpecial(result): return InternalAssert result.kind == nkSym and result.sym.kind == skType a.typ = result.sym.typ result = emptyNode of mNSetStrVal: result = evalAux(c, n.sons[1], {efLValue}) if isSpecial(result): return var a = result result = evalAux(c, n.sons[2], {}) if isSpecial(result): return if a.kind in {nkStrLit..nkTripleStrLit} and result.kind in {nkStrLit..nkTripleStrLit}: a.strVal = result.strVal else: stackTrace(c, n, errFieldXNotFound, "strVal") result = emptyNode of mNNewNimNode: result = evalAux(c, n.sons[1], {}) if isSpecial(result): return var k = getOrdValue(result) result = evalAux(c, n.sons[2], {efLValue}) if result.kind == nkExceptBranch: return var a = result if k < 0 or k > ord(high(TNodeKind)): internalError(n.info, "request to create a NimNode with invalid kind") result = newNodeI(TNodeKind(int(k)), if a.kind == nkNilLit: n.info else: a.info) of mNCopyNimNode: result = evalAux(c, n.sons[1], {efLValue}) if isSpecial(result): return result = copyNode(result) of mNCopyNimTree: result = evalAux(c, n.sons[1], {efLValue}) if isSpecial(result): return result = copyTree(result) of mNBindSym: # trivial implementation: result = n.sons[1] of mStrToIdent: result = evalAux(c, n.sons[1], {}) if isSpecial(result): return if not (result.kind in {nkStrLit..nkTripleStrLit}): stackTrace(c, n, errFieldXNotFound, "strVal") return var a = result result = newNodeIT(nkIdent, n.info, n.typ) result.ident = getIdent(a.strVal) of mIdentToStr: result = evalAux(c, n.sons[1], {}) if isSpecial(result): return var a = result result = newNodeIT(nkStrLit, n.info, n.typ) if a.kind == nkSym: result.strVal = a.sym.name.s else: if a.kind != nkIdent: InternalError(n.info, "no ident node") result.strVal = a.ident.s of mEqIdent: result = evalAux(c, n.sons[1], {}) if isSpecial(result): return var a = result result = evalAux(c, n.sons[2], {}) if isSpecial(result): return var b = result result = newNodeIT(nkIntLit, n.info, n.typ) if (a.kind == nkIdent) and (b.kind == nkIdent): if a.ident.id == b.ident.id: result.intVal = 1 of mEqNimrodNode: result = evalAux(c, n.sons[1], {efLValue}) if isSpecial(result): return var a = result result = evalAux(c, n.sons[2], {efLValue}) if isSpecial(result): return var b = result result = newNodeIT(nkIntLit, n.info, n.typ) if (a == b) or (b.kind in {nkNilLit, nkEmpty}) and (a.kind in {nkNilLit, nkEmpty}): result.intVal = 1 of mNLineInfo: result = evalAux(c, n.sons[1], {}) if isSpecial(result): return result = newStrNodeT(result.info.toFileLineCol, n) of mNHint: result = evalAux(c, n.sons[1], {}) if isSpecial(result): return Message(n.info, hintUser, getStrValue(result)) result = emptyNode of mNWarning: result = evalAux(c, n.sons[1], {}) if isSpecial(result): return Message(n.info, warnUser, getStrValue(result)) result = emptyNode of mNError: result = evalAux(c, n.sons[1], {}) if isSpecial(result): return stackTrace(c, n, errUser, getStrValue(result)) result = emptyNode of mConStrStr: result = evalConStrStr(c, n) of mRepr: result = evalRepr(c, n) of mNewString: result = evalAux(c, n.sons[1], {}) if isSpecial(result): return var a = result result = newNodeIT(nkStrLit, n.info, n.typ) result.strVal = newString(int(getOrdValue(a))) of mNewStringOfCap: result = evalAux(c, n.sons[1], {}) if isSpecial(result): return var a = result result = newNodeIT(nkStrLit, n.info, n.typ) result.strVal = newString(0) of mNCallSite: if c.callsite != nil: result = c.callsite else: stackTrace(c, n, errFieldXNotFound, "callsite") else: result = evalAux(c, n.sons[1], {}) if isSpecial(result): return var a = result var b: PNode = nil var cc: PNode = nil if sonsLen(n) > 2: result = evalAux(c, n.sons[2], {}) if isSpecial(result): return b = result if sonsLen(n) > 3: result = evalAux(c, n.sons[3], {}) if isSpecial(result): return cc = result if isEmpty(a) or isEmpty(b) or isEmpty(cc): result = emptyNode else: result = evalOp(m, n, a, b, cc) proc evalAux(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode = result = emptyNode dec(gNestedEvals) if gNestedEvals <= 0: stackTrace(c, n, errTooManyIterations) case n.kind # atoms: of nkEmpty: result = n of nkSym: result = evalSym(c, n, flags) of nkType..nkNilLit: result = copyNode(n) # end of atoms of nkCall, nkHiddenCallConv, nkMacroStmt, nkCommand, nkCallStrLit, nkInfix, nkPrefix, nkPostfix: result = evalMagicOrCall(c, n) of nkCurly, nkBracket, nkRange: # flags need to be passed here for mNAddMultiple :-( # XXX this is not correct in every case! var a = copyNode(n) for i in countup(0, sonsLen(n) - 1): result = evalAux(c, n.sons[i], flags) if isSpecial(result): return addSon(a, result) result = a of nkPar, nkClosure: var a = copyTree(n) for i in countup(0, sonsLen(n) - 1): var it = n.sons[i] if it.kind == nkExprColonExpr: result = evalAux(c, it.sons[1], flags) if isSpecial(result): return a.sons[i].sons[1] = result else: result = evalAux(c, it, flags) if isSpecial(result): return a.sons[i] = result result = a of nkBracketExpr: result = evalArrayAccess(c, n, flags) of nkDotExpr: result = evalFieldAccess(c, n, flags) of nkDerefExpr, nkHiddenDeref: result = evalDeref(c, n, flags) of nkAddr, nkHiddenAddr: result = evalAddr(c, n, flags) of nkHiddenStdConv, nkHiddenSubConv, nkConv: result = evalConv(c, n) of nkAsgn, nkFastAsgn: result = evalAsgn(c, n) of nkWhenStmt, nkIfStmt, nkIfExpr: result = evalIf(c, n) of nkWhileStmt: result = evalWhile(c, n) of nkCaseStmt: result = evalCase(c, n) of nkVarSection, nkLetSection: result = evalVar(c, n) of nkTryStmt: result = evalTry(c, n) of nkRaiseStmt: result = evalRaise(c, n) of nkReturnStmt: result = evalReturn(c, n) of nkBreakStmt, nkReturnToken: result = n of nkBlockExpr, nkBlockStmt: result = evalBlock(c, n) of nkDiscardStmt: result = evalAux(c, n.sons[0], {}) of nkCheckedFieldExpr: result = evalCheckedFieldAccess(c, n, flags) of nkObjDownConv: result = evalAux(c, n.sons[0], flags) of nkObjUpConv: result = evalUpConv(c, n, flags) of nkChckRangeF, nkChckRange64, nkChckRange: result = evalRangeChck(c, n) of nkStringToCString: result = evalConvStrToCStr(c, n) of nkCStringToString: result = evalConvCStrToStr(c, n) of nkStmtListExpr, nkStmtList, nkModule: for i in countup(0, sonsLen(n) - 1): result = evalAux(c, n.sons[i], flags) case result.kind of nkExceptBranch, nkReturnToken, nkBreakStmt: break else: nil of nkProcDef, nkMethodDef, nkMacroDef, nkCommentStmt, nkPragma, nkTypeSection, nkTemplateDef, nkConstSection, nkIteratorDef, nkConverterDef, nkIncludeStmt, nkImportStmt, nkFromStmt: nil of nkMetaNode: result = copyTree(n.sons[0]) result.typ = n.typ of nkPragmaBlock: result = evalAux(c, n.sons[1], flags) of nkIdentDefs, nkCast, nkYieldStmt, nkAsmStmt, nkForStmt, nkPragmaExpr, nkLambdaKinds, nkContinueStmt, nkIdent, nkParForStmt, nkBindStmt: result = raiseCannotEval(c, n.info) of nkRefTy: result = evalAux(c, n.sons[0], flags) else: InternalError(n.info, "evalAux: " & $n.kind) if result == nil: InternalError(n.info, "evalAux: returned nil " & $n.kind) inc(gNestedEvals) proc tryEval(c: PEvalContext, n: PNode): PNode = #internalAssert nfTransf in n.flags var n = transformExpr(c.module, n) gWhileCounter = evalMaxIterations gNestedEvals = evalMaxRecDepth result = evalAux(c, n, {}) proc eval*(c: PEvalContext, n: PNode): PNode = ## eval never returns nil! This simplifies the code a lot and ## makes it faster too. result = tryEval(c, n) if result.kind == nkExceptBranch: if sonsLen(result) >= 1: stackTrace(c, n, errUnhandledExceptionX, typeToString(result.typ)) else: stackTrace(c, n, errCannotInterpretNodeX, renderTree(n)) proc evalConstExprAux(module: PSym, e: PNode, mode: TEvalMode): PNode = var p = newEvalContext(module, "", mode) var s = newStackFrame() s.call = e pushStackFrame(p, s) result = tryEval(p, e) if result != nil and result.kind == nkExceptBranch: result = nil popStackFrame(p) proc evalConstExpr*(module: PSym, e: PNode): PNode = result = evalConstExprAux(module, e, emConst) proc evalStaticExpr*(module: PSym, e: PNode): PNode = result = evalConstExprAux(module, e, emStatic) proc setupMacroParam(x: PNode): PNode = result = x if result.kind == nkHiddenStdConv: result = result.sons[1] proc evalMacroCall(c: PEvalContext, n, nOrig: PNode, sym: PSym): PNode = # XXX GlobalError() is ugly here, but I don't know a better solution for now inc(evalTemplateCounter) if evalTemplateCounter > 100: GlobalError(n.info, errTemplateInstantiationTooNested) c.callsite = nOrig var s = newStackFrame() s.call = n setlen(s.params, n.len) # return value: s.params[0] = newNodeIT(nkNilLit, n.info, sym.typ.sons[0]) # setup parameters: for i in 1 .. < n.len: s.params[i] = setupMacroParam(n.sons[i]) pushStackFrame(c, s) discard eval(c, sym.getBody) result = s.params[0] popStackFrame(c) if cyclicTree(result): GlobalError(n.info, errCyclicTree) dec(evalTemplateCounter) c.callsite = nil proc myOpen(module: PSym, filename: string): PPassContext = var c = newEvalContext(module, filename, emRepl) pushStackFrame(c, newStackFrame()) result = c proc myProcess(c: PPassContext, n: PNode): PNode = result = eval(PEvalContext(c), n) proc evalPass*(): TPass = initPass(result) result.open = myOpen result.close = myProcess result.process = myProcess