diff options
author | Araq <rumpf_a@web.de> | 2011-04-12 01:13:42 +0200 |
---|---|---|
committer | Araq <rumpf_a@web.de> | 2011-04-12 01:13:42 +0200 |
commit | cd292568d775d55d9abb51e962882ecda12c03a9 (patch) | |
tree | 85451f0e1f17dc0463350915f12bdd0a82a73455 /compiler/evals.nim | |
parent | 46c41e43690cba9bc1caff6a994bb6915df8a1b7 (diff) | |
download | Nim-cd292568d775d55d9abb51e962882ecda12c03a9.tar.gz |
big repo cleanup
Diffstat (limited to 'compiler/evals.nim')
-rwxr-xr-x | compiler/evals.nim | 1110 |
1 files changed, 1110 insertions, 0 deletions
diff --git a/compiler/evals.nim b/compiler/evals.nim new file mode 100755 index 000000000..7d0f9c801 --- /dev/null +++ b/compiler/evals.nim @@ -0,0 +1,1110 @@ +# +# +# The Nimrod Compiler +# (c) Copyright 2011 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, rnimsyn, types, passes, semfold + +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 + + TEvalContext* = object of passes.TPassContext + module*: PSym + tos*: PStackFrame # top of stack + lastException*: PNode + optEval*: bool # evaluation done for optimization purposes + + PEvalContext* = ref TEvalContext + + TEvalFlag = enum + efNone, efLValue + TEvalFlags = set[TEvalFlag] + +proc eval*(c: PEvalContext, n: PNode): PNode + # eval never returns nil! This simplifies the code a lot and + # makes it faster too. +proc evalConstExpr*(module: PSym, e: PNode): PNode + +const + evalMaxIterations = 500_000 # max iterations of all loops + evalMaxRecDepth = 10_000 # max recursion depth for evaluation + +# Much better: 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, + optEval: bool): PEvalContext = + new(result) + result.module = module + result.optEval = optEval + +proc pushStackFrame*(c: PEvalContext, t: PStackFrame) {.inline.} = + t.next = c.tos + c.tos = t + +proc popStackFrame*(c: PEvalContext) {.inline.} = + if (c.tos == nil): InternalError("popStackFrame") + c.tos = c.tos.next + +proc evalAux(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode + +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) + Fatal(n.info, msg, arg) + +proc isSpecial(n: PNode): bool {.inline.} = + result = (n.kind == nkExceptBranch) + # or (n.kind == nkEmpty) + # XXX this does not work yet! Better to compile too much than to compile to + # few programs + +proc myreset(n: PNode) {.inline.} = + when defined(system.reset): reset(n[]) + +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 + 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, tyChar, tyInt..tyInt64: + result = newNodeIT(nkIntLit, info, t) + of tyFloat..tyFloat128: + result = newNodeIt(nkFloatLit, info, t) + of tyVar, tyPointer, tyPtr, tyRef, tyCString, tySequence, tyString, tyExpr, + tyStmt, tyTypeDesc: + result = newNodeIT(nkNilLit, info, t) + of tyObject: + result = newNodeIT(nkPar, info, t) + getNullValueAux(t.n, result) + 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) + 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") + +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 + assert(a.kind == nkIdentDefs) + assert(a.sons[0].kind == nkSym) + var v = a.sons[0].sym + 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) + IdNodeTablePut(c.tos.mapping, v, result) + result = emptyNode + +proc evalCall(c: PEvalContext, n: PNode): PNode = + result = evalAux(c, n.sons[0], {}) + if isSpecial(result): return + var prc = result + # bind the actual params to the local parameter of a new binding + var d = newStackFrame() + d.call = n + if prc.kind == nkSym: + d.prc = prc.sym + if not (prc.sym.kind in {skProc, skConverter}): + InternalError(n.info, "evalCall") + setlen(d.params, sonsLen(n)) + 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, {}) + 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 sfResult in sym.flags: + 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 + result = emptyNode + +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] + else: stackTrace(c, n, errIndexOutOfBounds) + if not aliasNeeded(result, flags): result = copyTree(result) + of nkBracket, nkMetaNode: + if (idx >= 0) and (idx < sonsLen(x)): result = x.sons[int(idx)] + else: stackTrace(c, n, errIndexOutOfBounds) + if not aliasNeeded(result, flags): result = copyTree(result) + of nkStrLit..nkTripleStrLit: + if efLValue in flags: + InternalError(n.info, "cannot evaluate write access to char") + 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: InternalError(n.info, "evalFieldAccess") + var field = n.sons[1].sym + for i in countup(0, sonsLen(x) - 1): + var it = x.sons[i] + if it.kind != nkExprColonExpr: + 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 = + 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 + else: + if not (x.kind in {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: + var tmpi = x.intVal + x.intVal = result.intVal + result.intVal = tmpi + of nkFloatLit..nkFloat64Lit: + var tmpf = x.floatVal + x.floatVal = result.floatVal + result.floatVal = tmpf + of nkStrLit..nkTripleStrLit: + var tmps = x.strVal + x.strVal = result.strVal + result.strVal = tmps + 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 = + case n.sym.kind + of skProc, skConverter, skMacro: result = n.sym.ast.sons[codePos] + of skVar, skForVar, skTemp: result = evalVariable(c.tos, n.sym, flags) + of skParam: + # XXX what about LValue? + result = c.tos.params[n.sym.position + 1] + of skConst: result = n.sym.ast + else: + stackTrace(c, n, errCannotInterpretNodeX, $n.sym.kind) + result = emptyNode + if result == nil: stackTrace(c, n, errCannotInterpretNodeX, n.sym.name.s) + +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: internalError(n.info, "evalIncDec") + 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 = + result = evalAux(c, n.sons[1], {}) + if isSpecial(result): return + Message(n.info, hintQuitCalled) + quit(int(getOrdValue(result))) + +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") + if 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") + if result.intVal != 0: result = evalAux(c, n.sons[2], {}) + +proc evalNoOpt(c: PEvalContext, n: PNode): PNode = + result = newNodeI(nkExceptBranch, n.info) + # creating a nkExceptBranch without sons + # means that it could not be evaluated + +proc evalNew(c: PEvalContext, n: PNode): PNode = + if c.optEval: return evalNoOpt(c, n) + # 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: InternalError(n.info, "evalDeref " & $result.kind) + +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) + addSon(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 + 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 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) + +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: + 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, n.sons[codePos], {}) + if result.kind == nkReturnToken: + result = IdNodeTableGet(c.tos.mapping, v) + else: + result = evalAux(c, n.sons[codePos], {}) + 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: result = newIntNodeT(sonsLen(result), n) + of tyString: result = newIntNodeT(len(result.strVal) - 1, n) + else: InternalError(n.info, "evalHigh") + +proc evalIs(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") + 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 evalAssert(c: PEvalContext, n: PNode): PNode = + result = evalAux(c, n.sons[1], {}) + if isSpecial(result): return + if getOrdValue(result) != 0: result = emptyNode + else: stackTrace(c, n, errAssertionFailed) + +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: InternalError(n.info, "evalAppendStrCh") + 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(getStrValue(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 & getStrValue(b) + else: InternalError(n.info, "evalAppendStrStr") + 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: InternalError(n.info, "evalAppendSeqElem") + 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) + +proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode = + var m = getMagic(n) + case m + of mNone: result = evalCall(c, n) + of mIs: result = evalIs(c, n) + of mSizeOf: internalError(n.info, "sizeof() should have been evaluated") + of mHigh: result = evalHigh(c, n) + of mAssert: result = evalAssert(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 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: InternalError(n.info, "no int value") + 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: InternalError(n.info, "no float value") + of mNSymbol: + result = evalAux(c, n.sons[1], {efLValue}) + if isSpecial(result): return + if result.kind != nkSym: InternalError(n.info, "no symbol") + of mNIdent: + result = evalAux(c, n.sons[1], {}) + if isSpecial(result): return + if result.kind != nkIdent: InternalError(n.info, "no symbol") + of mNGetType: result = evalAux(c, n.sons[1], {}) + 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: InternalError(n.info, "no string value") + 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 + a.intVal = result.intVal # XXX: exception handling? + 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 + a.floatVal = result.floatVal # XXX: exception handling? + 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 + a.sym = result.sym # XXX: exception handling? + 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 + a.ident = result.ident # XXX: exception handling? + 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 + a.typ = result.typ # XXX: exception handling? + 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 + a.strVal = result.strVal # XXX: exception handling? + 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 mStrToIdent: + result = evalAux(c, n.sons[1], {}) + if isSpecial(result): return + if not (result.kind in {nkStrLit..nkTripleStrLit}): + InternalError(n.info, "no string node") + 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 + if result.kind != nkIdent: InternalError(n.info, "no ident node") + var a = result + result = newNodeIT(nkStrLit, n.info, n.typ) + 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 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))) + 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: + 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: + var a = copyTree(n) + for i in countup(0, sonsLen(n) - 1): + result = evalAux(c, n.sons[i].sons[1], flags) + if isSpecial(result): return + a.sons[i].sons[1] = 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: 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 nkPassAsOpenArray: result = evalAux(c, n.sons[0], flags) + 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 nkIdentDefs, nkCast, nkYieldStmt, nkAsmStmt, nkForStmt, nkPragmaExpr, + nkLambda, nkContinueStmt, nkIdent: + stackTrace(c, n, errCannotInterpretNodeX, $n.kind) + else: InternalError(n.info, "evalAux: " & $n.kind) + if result == nil: + InternalError(n.info, "evalAux: returned nil " & $n.kind) + inc(gNestedEvals) + +proc eval(c: PEvalContext, n: PNode): PNode = + gWhileCounter = evalMaxIterations + gNestedEvals = evalMaxRecDepth + result = evalAux(c, n, {}) + if (result.kind == nkExceptBranch) and (sonsLen(result) >= 1): + stackTrace(c, n, errUnhandledExceptionX, typeToString(result.typ)) + +proc evalConstExpr(module: PSym, e: PNode): PNode = + var p = newEvalContext(module, "", true) + var s = newStackFrame() + s.call = e + pushStackFrame(p, s) + result = eval(p, e) + if result != nil and result.kind == nkExceptBranch: result = nil + popStackFrame(p) + +proc myOpen(module: PSym, filename: string): PPassContext = + var c = newEvalContext(module, filename, false) + 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 + |