mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-29 01:14:41 +00:00
1503 lines
50 KiB
Nim
1503 lines
50 KiB
Nim
#
|
|
#
|
|
# The Nimrod Compiler
|
|
# (c) Copyright 2013 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
|
|
|
|
when hasFFI:
|
|
import evalffi
|
|
|
|
type
|
|
PStackFrame* = ref TStackFrame
|
|
TStackFrame* = object
|
|
prc: PSym # current prc; proc that is evaluated
|
|
slots: TNodeSeq # parameters passed to the proc + locals;
|
|
# parameters come first
|
|
call: PNode
|
|
next: PStackFrame # for stacking
|
|
|
|
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)
|
|
|
|
TSandboxFlag* = enum ## what the evaluation engine should allow
|
|
allowCast, ## allow unsafe language feature: 'cast'
|
|
allowFFI, ## allow the FFI
|
|
allowInfiniteLoops ## allow endless loops
|
|
TSandboxFlags* = set[TSandboxFlag]
|
|
|
|
TEvalContext* = object of passes.TPassContext
|
|
module*: PSym
|
|
tos*: PStackFrame # top of stack
|
|
lastException*: PNode
|
|
callsite: PNode # for 'callsite' magic
|
|
mode*: TEvalMode
|
|
features: TSandboxFlags
|
|
globals*: TIdNodeTable # state of global vars
|
|
getType*: proc(n: PNode): PNode {.closure.}
|
|
handleIsOperator*: 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)
|
|
result.slots = @[]
|
|
|
|
proc newEvalContext*(module: PSym, mode: TEvalMode): PEvalContext =
|
|
new(result)
|
|
result.module = module
|
|
result.mode = mode
|
|
result.features = {allowFFI}
|
|
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 =
|
|
if defined(debug) and gVerbosity >= 3: writeStackTrace()
|
|
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, info: TLineInfo, msg: TMsgKind, arg = "") =
|
|
MsgWriteln("stack trace: (most recent call last)")
|
|
stackTraceAux(c.tos)
|
|
LocalError(info, msg, arg)
|
|
|
|
template isSpecial(n: PNode): bool = n.kind == nkExceptBranch
|
|
template bailout() {.dirty.} =
|
|
if isSpecial(result): return
|
|
|
|
template evalX(n, flags) {.dirty.} =
|
|
result = evalAux(c, n, flags)
|
|
bailout()
|
|
|
|
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):
|
|
evalX(n.sons[i].sons[0], {})
|
|
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 =
|
|
evalX(n.sons[0], {})
|
|
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:
|
|
evalX(n.sons[0], {})
|
|
if getOrdValue(result) == 0:
|
|
result = emptyNode; 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:
|
|
if allowInfiniteLoops in c.features:
|
|
gWhileCounter = 0
|
|
else:
|
|
stackTrace(c, n.info, 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)
|
|
let a = exc.typ.skipTypes(abstractPtrs)
|
|
let b = n.sons[i].sons[j].typ.skipTypes(abstractPtrs)
|
|
if a == b:
|
|
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-{tyTypeDesc})
|
|
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, tyStatic, 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:
|
|
# XXX nkExprColonExpr is out of fashion ...
|
|
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 evalVarValue(c: PEvalContext, n: PNode): PNode =
|
|
result = evalAux(c, n, {})
|
|
if result.kind in {nkType..nkNilLit}: result = result.copyNode
|
|
|
|
proc allocSlot(c: PStackFrame; sym: PSym): int =
|
|
result = sym.position + ord(sym.kind == skParam)
|
|
if result == 0 and sym.kind != skResult:
|
|
result = c.slots.len
|
|
if result == 0: result = 1
|
|
sym.position = result
|
|
setLen(c.slots, max(result+1, c.slots.len))
|
|
|
|
proc setSlot(c: PStackFrame, sym: PSym, val: PNode) =
|
|
assert sym.owner == c.prc or sfFromGeneric in sym.flags
|
|
let idx = allocSlot(c, sym)
|
|
c.slots[idx] = val
|
|
|
|
proc setVar(c: PEvalContext, v: PSym, n: PNode) =
|
|
if sfGlobal notin v.flags: setSlot(c.tos, v, n)
|
|
else: IdNodeTablePut(c.globals, v, n)
|
|
|
|
proc evalVar(c: PEvalContext, n: PNode): PNode =
|
|
for i in countup(0, sonsLen(n) - 1):
|
|
let a = n.sons[i]
|
|
if a.kind == nkCommentStmt: continue
|
|
#assert(a.sons[0].kind == nkSym) can happen for transformed vars
|
|
if a.kind == nkVarTuple:
|
|
result = evalVarValue(c, a.lastSon)
|
|
if result.kind in {nkType..nkNilLit}:
|
|
result = result.copyNode
|
|
bailout()
|
|
if result.kind != nkPar:
|
|
return raiseCannotEval(c, n.info)
|
|
for i in 0 .. a.len-3:
|
|
var v = a.sons[i].sym
|
|
setVar(c, v, result.sons[i])
|
|
else:
|
|
if a.sons[2].kind != nkEmpty:
|
|
result = evalVarValue(c, a.sons[2])
|
|
bailout()
|
|
else:
|
|
result = getNullValue(a.sons[0].typ, a.sons[0].info)
|
|
if a.sons[0].kind == nkSym:
|
|
var v = a.sons[0].sym
|
|
setVar(c, v, result)
|
|
else:
|
|
# assign to a.sons[0]:
|
|
var x = result
|
|
evalX(a.sons[0], {})
|
|
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 j in countup(0, sonsLen(result) - 1): addSon(x, result.sons[j])
|
|
result = emptyNode
|
|
|
|
proc aliasNeeded(n: PNode, flags: TEvalFlags): bool =
|
|
result = efLValue in flags or n.typ == nil or
|
|
n.typ.kind in {tyExpr, tyStatic, 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.
|
|
assert sym.position != 0 or skResult == sym.kind
|
|
var x = c
|
|
while x != nil:
|
|
if sym.owner == x.prc:
|
|
result = x.slots[sym.position]
|
|
assert result != nil
|
|
if not aliasNeeded(result, flags):
|
|
result = copyTree(result)
|
|
return
|
|
x = x.next
|
|
#internalError(sym.info, "cannot eval " & sym.name.s & " " & $sym.position)
|
|
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 or s.kind == skForVar:
|
|
result = IdNodeTableGet(c.globals, s)
|
|
if result != nil:
|
|
if not aliasNeeded(result, flags):
|
|
result = copyTree(result)
|
|
else:
|
|
when hasFFI:
|
|
if sfImportc in s.flags and allowFFI in c.features:
|
|
result = importcSymbol(s)
|
|
IdNodeTablePut(c.globals, s, result)
|
|
return result
|
|
|
|
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 optBody(c: PEvalContext, s: PSym): PNode =
|
|
result = s.getBody
|
|
|
|
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.slots, sonsLen(n) + ord(isClosure))
|
|
if isClosure:
|
|
#debug prc
|
|
evalX(prc.sons[1], {efLValue})
|
|
d.slots[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):
|
|
evalX(n.sons[i], {})
|
|
d.slots[i] = result
|
|
if n.typ != nil: d.slots[0] = getNullValue(n.typ, n.info)
|
|
|
|
when hasFFI:
|
|
if sfImportc in prc.sym.flags and allowFFI in c.features:
|
|
var newCall = newNodeI(nkCall, n.info, n.len)
|
|
newCall.sons[0] = evalGlobalVar(c, prc.sym, {})
|
|
for i in 1 .. <n.len:
|
|
newCall.sons[i] = d.slots[i]
|
|
return callForeignFunction(newCall)
|
|
|
|
pushStackFrame(c, d)
|
|
evalX(optBody(c, prc.sym), {})
|
|
if n.typ != nil: result = d.slots[0]
|
|
popStackFrame(c)
|
|
|
|
proc evalArrayAccess(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
|
|
evalX(n.sons[0], flags)
|
|
var x = result
|
|
evalX(n.sons[1], {})
|
|
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.info, 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.info, 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.info, errIndexOutOfBounds)
|
|
else: stackTrace(c, n.info, errNilAccess)
|
|
|
|
proc evalFieldAccess(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
|
|
# a real field access; proc calls have already been transformed
|
|
# XXX: field checks!
|
|
evalX(n.sons[0], flags)
|
|
var x = result
|
|
if x.kind != nkPar: return raiseCannotEval(c, n.info)
|
|
# this is performance critical:
|
|
var field = n.sons[1].sym
|
|
result = x.sons[field.position]
|
|
if result.kind == nkExprColonExpr: result = result.sons[1]
|
|
if not aliasNeeded(result, flags): result = copyTree(result)
|
|
|
|
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}:
|
|
evalX(a.sons[0], {efLValue})
|
|
var x = result
|
|
evalX(a.sons[1], {})
|
|
var idx = getOrdValue(result)
|
|
|
|
evalX(n.sons[1], {})
|
|
if result.kind notin {nkIntLit, nkCharLit}: return c.raiseCannotEval(n.info)
|
|
|
|
if idx >= 0 and idx < len(x.strVal):
|
|
x.strVal[int(idx)] = chr(int(result.intVal))
|
|
else:
|
|
stackTrace(c, n.info, errIndexOutOfBounds)
|
|
else:
|
|
evalX(n.sons[0], {efLValue})
|
|
var x = result
|
|
evalX(n.sons[1], {})
|
|
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 =
|
|
evalX(n.sons[0], {efLValue})
|
|
var x = result
|
|
evalX(n.sons[1], {efLValue})
|
|
if x.kind != result.kind:
|
|
stackTrace(c, n.info, 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.slots.len:
|
|
result = c.tos.slots[s.position + 1]
|
|
of skConst: result = s.ast
|
|
of skEnumField: result = newIntNodeT(s.position, n)
|
|
else: result = nil
|
|
let mask = if hasFFI and allowFFI in c.features: {sfForward}
|
|
else: {sfImportc, sfForward}
|
|
if result == nil or mask * s.flags != {}:
|
|
result = raiseCannotEval(c, n.info)
|
|
|
|
proc evalIncDec(c: PEvalContext, n: PNode, sign: biggestInt): PNode =
|
|
evalX(n.sons[1], {efLValue})
|
|
var a = result
|
|
evalX(n.sons[2], {})
|
|
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):
|
|
evalX(n.sons[i], {})
|
|
Write(stdout, getStrValue(result))
|
|
writeln(stdout, "")
|
|
result = emptyNode
|
|
|
|
proc evalExit(c: PEvalContext, n: PNode): PNode =
|
|
if c.mode in {emRepl, emStatic}:
|
|
evalX(n.sons[1], {})
|
|
Message(n.info, hintQuitCalled)
|
|
quit(int(getOrdValue(result)))
|
|
else:
|
|
result = raiseCannotEval(c, n.info)
|
|
|
|
proc evalOr(c: PEvalContext, n: PNode): PNode =
|
|
evalX(n.sons[1], {})
|
|
if result.intVal == 0: result = evalAux(c, n.sons[2], {})
|
|
|
|
proc evalAnd(c: PEvalContext, n: PNode): PNode =
|
|
evalX(n.sons[1], {})
|
|
if 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 :-)
|
|
evalX(n.sons[1], {efLValue})
|
|
var a = result
|
|
var t = skipTypes(n.sons[1].typ, abstractVar)
|
|
if a.kind == nkEmpty: InternalError(n.info, "first parameter is empty")
|
|
myreset(a)
|
|
let u = getNullValue(t.sons[0], n.info)
|
|
a.kind = u.kind
|
|
a.typ = t
|
|
shallowCopy(a.sons, u.sons)
|
|
result = emptyNode
|
|
when false:
|
|
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 =
|
|
evalX(n.sons[0], {efLValue})
|
|
case result.kind
|
|
of nkNilLit: stackTrace(c, n.info, errNilAccess)
|
|
of nkRefTy:
|
|
# XXX efLValue?
|
|
result = result.sons[0]
|
|
else:
|
|
if skipTypes(n.sons[0].typ, abstractInst).kind != tyRef:
|
|
result = raiseCannotEval(c, n.info)
|
|
|
|
proc evalAddr(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
|
|
evalX(n.sons[0], {efLValue})
|
|
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 evalCast(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
|
|
if allowCast in c.features:
|
|
when hasFFI:
|
|
result = evalAux(c, n.sons[1], {efLValue})
|
|
if isSpecial(result): return
|
|
InternalAssert result.typ != nil
|
|
result = fficast(result, n.typ)
|
|
else:
|
|
result = evalConv(c, n)
|
|
else:
|
|
result = raiseCannotEval(c, n.info)
|
|
|
|
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.info, errInvalidConversionFromTypeX, typeToString(src))
|
|
|
|
proc evalRangeChck(c: PEvalContext, n: PNode): PNode =
|
|
evalX(n.sons[0], {})
|
|
var x = result
|
|
evalX(n.sons[1], {})
|
|
var a = result
|
|
evalX(n.sons[2], {})
|
|
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.info, 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.info, 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)
|
|
if c.tos.slots.len == 0: setLen(c.tos.slots, 1)
|
|
c.tos.slots[0] = result
|
|
#IdNodeTablePut(c.tos.mapping, v, result)
|
|
result = evalAux(c, s.getBody, {})
|
|
if result.kind == nkReturnToken:
|
|
result = c.tos.slots[0]
|
|
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
|
|
|
|
proc evalParseExpr(c: PEvalContext, n: PNode): PNode =
|
|
var code = evalAux(c, n.sons[1], {})
|
|
var ast = parseString(code.getStrValue, code.info.toFilename,
|
|
code.info.line.int)
|
|
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.info.line.int)
|
|
#result.typ = newType(tyStmt, c.module)
|
|
|
|
proc evalTypeTrait*(trait, operand: PNode, context: PSym): PNode =
|
|
let typ = operand.typ.skipTypes({tyTypeDesc})
|
|
case trait.sym.name.s.normalize
|
|
of "name":
|
|
result = newStrNode(nkStrLit, typ.typeToString(preferName))
|
|
result.typ = newType(tyString, context)
|
|
result.info = trait.info
|
|
of "arity":
|
|
result = newIntNode(nkIntLit, typ.n.len-1)
|
|
result.typ = newType(tyInt, context)
|
|
result.info = trait.info
|
|
else:
|
|
internalAssert false
|
|
|
|
proc expectString(n: PNode) =
|
|
if n.kind notin nkStrKinds:
|
|
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:
|
|
let operand = evalAux(c, n.sons[1], {})
|
|
result = evalTypeTrait(n[0], operand, c.module)
|
|
of mIs:
|
|
n.sons[1] = evalAux(c, n.sons[1], {})
|
|
result = c.handleIsOperator(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.info, 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.info, 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 = a
|
|
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 = a
|
|
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.info, 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.info, errFieldXNotFound, "floatVal")
|
|
of mNSymbol:
|
|
result = evalAux(c, n.sons[1], {efLValue})
|
|
if isSpecial(result): return
|
|
if result.kind != nkSym: stackTrace(c, n.info, errFieldXNotFound, "symbol")
|
|
of mNIdent:
|
|
result = evalAux(c, n.sons[1], {})
|
|
if isSpecial(result): return
|
|
if result.kind != nkIdent: stackTrace(c, n.info, 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.info, 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.info, 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.info, 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.info, 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.info, 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.info, 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 mNGenSym:
|
|
evalX(n.sons[1], {efLValue})
|
|
let k = getOrdValue(result)
|
|
evalX(n.sons[2], {efLValue})
|
|
let b = result
|
|
let name = if b.strVal.len == 0: ":tmp" else: b.strVal
|
|
if k < 0 or k > ord(high(TSymKind)):
|
|
internalError(n.info, "request to create a symbol with invalid kind")
|
|
result = newSymNode(newSym(k.TSymKind, name.getIdent, c.module, n.info))
|
|
incl(result.sym.flags, sfGenSym)
|
|
of mStrToIdent:
|
|
result = evalAux(c, n.sons[1], {})
|
|
if isSpecial(result): return
|
|
if not (result.kind in {nkStrLit..nkTripleStrLit}):
|
|
stackTrace(c, n.info, 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.info, 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.info, 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.info, errTooManyIterations)
|
|
case n.kind
|
|
of nkSym: result = evalSym(c, n, flags)
|
|
of nkType..nkNilLit, nkTypeOfExpr:
|
|
# nkStrLit is VERY common in the traces, so we should avoid
|
|
# the 'copyNode' here.
|
|
result = n #.copyNode
|
|
of nkAsgn, nkFastAsgn: result = evalAsgn(c, n)
|
|
of nkCommand..nkHiddenCallConv:
|
|
result = evalMagicOrCall(c, n)
|
|
of nkDotExpr: result = evalFieldAccess(c, n, flags)
|
|
of nkBracketExpr:
|
|
result = evalArrayAccess(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 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 nkObjConstr:
|
|
let t = skipTypes(n.typ, abstractInst)
|
|
var a: PNode
|
|
if t.kind == tyRef:
|
|
result = newNodeIT(nkRefTy, n.info, t)
|
|
a = getNullValue(t.sons[0], n.info)
|
|
addSon(result, a)
|
|
else:
|
|
a = getNullValue(t, n.info)
|
|
result = a
|
|
for i in countup(1, sonsLen(n) - 1):
|
|
let it = n.sons[i]
|
|
if it.kind == nkExprColonExpr:
|
|
let value = evalAux(c, it.sons[1], flags)
|
|
if isSpecial(value): return value
|
|
a.sons[it.sons[0].sym.position] = value
|
|
else: return raiseCannotEval(c, n.info)
|
|
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:
|
|
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 nkCast:
|
|
result = evalCast(c, n, flags)
|
|
of nkIdentDefs, nkYieldStmt, nkAsmStmt, nkForStmt, nkPragmaExpr,
|
|
nkLambdaKinds, nkContinueStmt, nkIdent, nkParForStmt, nkBindStmt,
|
|
nkClosedSymChoice, nkOpenSymChoice:
|
|
result = raiseCannotEval(c, n.info)
|
|
of nkRefTy:
|
|
result = evalAux(c, n.sons[0], flags)
|
|
of nkEmpty:
|
|
# nkEmpty occurs once in each trace that I looked at
|
|
result = n
|
|
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.info, errUnhandledExceptionX, typeToString(result.typ))
|
|
else:
|
|
stackTrace(c, result.info, errCannotInterpretNodeX, renderTree(n))
|
|
|
|
proc evalConstExprAux*(p: PEvalContext, module, prc: PSym, e: PNode): PNode =
|
|
var s = newStackFrame()
|
|
s.call = e
|
|
s.prc = prc
|
|
pushStackFrame(p, s)
|
|
result = tryEval(p, e)
|
|
if result != nil and result.kind == nkExceptBranch: result = nil
|
|
popStackFrame(p)
|
|
|
|
proc setupMacroParam(x: PNode): PNode =
|
|
result = x
|
|
if result.kind in {nkHiddenSubConv, 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
|
|
s.prc = sym
|
|
var L = n.safeLen
|
|
if L == 0: L = 1
|
|
setlen(s.slots, L)
|
|
# return value:
|
|
s.slots[0] = newNodeIT(nkNilLit, n.info, sym.typ.sons[0])
|
|
# setup parameters:
|
|
for i in 1 .. < L: s.slots[i] = setupMacroParam(n.sons[i])
|
|
pushStackFrame(c, s)
|
|
discard eval(c, optBody(c, sym))
|
|
result = s.slots[0]
|
|
popStackFrame(c)
|
|
if cyclicTree(result): GlobalError(n.info, errCyclicTree)
|
|
dec(evalTemplateCounter)
|
|
c.callsite = nil
|
|
|
|
proc myOpen(module: PSym): PPassContext =
|
|
var c = newEvalContext(module, emRepl)
|
|
c.features = {allowCast, allowFFI, allowInfiniteLoops}
|
|
pushStackFrame(c, newStackFrame())
|
|
result = c
|
|
|
|
var oldErrorCount: int
|
|
|
|
proc myProcess(c: PPassContext, n: PNode): PNode =
|
|
# don't eval errornous code:
|
|
if oldErrorCount == msgs.gErrorCounter:
|
|
result = eval(PEvalContext(c), n)
|
|
else:
|
|
result = n
|
|
oldErrorCount = msgs.gErrorCounter
|
|
|
|
const evalPass* = makePass(myOpen, nil, myProcess, myProcess)
|
|
|