Merge branch 'devel'

This commit is contained in:
Dominik Picheta
2015-04-30 16:09:27 +01:00
669 changed files with 28587 additions and 20697 deletions

1
.gitignore vendored
View File

@@ -41,3 +41,4 @@ xcuserdata/
/testresults.html
/testresults.json
testament.db
/csources/

11
compiler.nimble Normal file
View File

@@ -0,0 +1,11 @@
[Package]
name = "compiler"
version = "0.10.3"
author = "Andreas Rumpf"
description = "Compiler package providing the compiler sources as a library."
license = "MIT"
InstallDirs = "doc, compiler"
[Deps]
Requires: "nim >= 0.10.3"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
#
#
# The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -119,8 +119,8 @@ proc hashType(c: var MD5Context, t: PType) =
c.hashSym(t.sym)
case t.kind
of tyGenericBody, tyGenericInst, tyGenericInvokation:
for i in countup(0, sonsLen(t) -1 -ord(t.kind != tyGenericInvokation)):
of tyGenericBody, tyGenericInst, tyGenericInvocation:
for i in countup(0, sonsLen(t) -1 -ord(t.kind != tyGenericInvocation)):
c.hashType t.sons[i]
of tyUserTypeClass:
internalAssert t.sym != nil and t.sym.owner != nil
@@ -243,24 +243,24 @@ proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
encodeNode(w, n.info, n.sons[i], result)
add(result, ')')
proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) =
proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) =
var oldLen = result.len
result.add('<')
if loc.k != low(loc.k): encodeVInt(ord(loc.k), result)
if loc.s != low(loc.s):
if loc.s != low(loc.s):
add(result, '*')
encodeVInt(ord(loc.s), result)
if loc.flags != {}:
if loc.flags != {}:
add(result, '$')
encodeVInt(cast[int32](loc.flags), result)
if loc.t != nil:
add(result, '^')
encodeVInt(cast[int32](loc.t.id), result)
pushType(w, loc.t)
if loc.r != nil:
if loc.r != nil:
add(result, '!')
encodeStr(ropeToStr(loc.r), result)
if loc.a != 0:
encodeStr($loc.r, result)
if loc.a != 0:
add(result, '?')
encodeVInt(loc.a, result)
if oldLen + 1 == result.len:
@@ -317,7 +317,7 @@ proc encodeLib(w: PRodWriter, lib: PLib, info: TLineInfo, result: var string) =
add(result, '|')
encodeVInt(ord(lib.kind), result)
add(result, '|')
encodeStr(ropeToStr(lib.name), result)
encodeStr($lib.name, result)
add(result, '|')
encodeNode(w, info, lib.path, result)

View File

@@ -1,7 +1,7 @@
#
#
# The Nim Compiler
# (c) Copyright 2013 Andreas Rumpf
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -19,13 +19,13 @@ proc hasNoInit(call: PNode): bool {.inline.} =
result = call.sons[0].kind == nkSym and sfNoInit in call.sons[0].sym.flags
proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
callee, params: PRope) =
var pl = con(callee, ~"(", params)
callee, params: Rope) =
var pl = callee & ~"(" & params
# getUniqueType() is too expensive here:
var typ = skipTypes(ri.sons[0].typ, abstractInst)
if typ.sons[0] != nil:
if isInvalidReturnType(typ.sons[0]):
if params != nil: pl.app(~", ")
if params != nil: pl.add(~", ")
# beware of 'result = p(result)'. We may need to allocate a temporary:
if d.k in {locTemp, locNone} or not leftAppearsOnRightSide(le, ri):
# Great, we can use 'd':
@@ -33,26 +33,34 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
elif d.k notin {locExpr, locTemp} and not hasNoInit(ri):
# reset before pass as 'result' var:
resetLoc(p, d)
app(pl, addrLoc(d))
app(pl, ~");$n")
add(pl, addrLoc(d))
add(pl, ~");$n")
line(p, cpsStmts, pl)
else:
var tmp: TLoc
getTemp(p, typ.sons[0], tmp, needsInit=true)
app(pl, addrLoc(tmp))
app(pl, ~");$n")
add(pl, addrLoc(tmp))
add(pl, ~");$n")
line(p, cpsStmts, pl)
genAssignment(p, d, tmp, {}) # no need for deep copying
else:
app(pl, ~")")
if d.k == locNone: getTemp(p, typ.sons[0], d)
assert(d.t != nil) # generate an assignment to d:
var list: TLoc
initLoc(list, locCall, d.t, OnUnknown)
list.r = pl
genAssignment(p, d, list, {}) # no need for deep copying
add(pl, ~")")
if p.module.compileToCpp and lfSingleUse in d.flags:
# do not generate spurious temporaries for C++! For C we're better off
# with them to prevent undefined behaviour and because the codegen
# is free to emit expressions multiple times!
d.k = locCall
d.r = pl
excl d.flags, lfSingleUse
else:
if d.k == locNone: getTemp(p, typ.sons[0], d)
assert(d.t != nil) # generate an assignment to d:
var list: TLoc
initLoc(list, locCall, d.t, OnUnknown)
list.r = pl
genAssignment(p, d, list, {}) # no need for deep copying
else:
app(pl, ~");$n")
add(pl, ~");$n")
line(p, cpsStmts, pl)
proc isInCurrentFrame(p: BProc, n: PNode): bool =
@@ -75,7 +83,7 @@ proc isInCurrentFrame(p: BProc, n: PNode): bool =
result = isInCurrentFrame(p, n.sons[0])
else: discard
proc openArrayLoc(p: BProc, n: PNode): PRope =
proc openArrayLoc(p: BProc, n: PNode): Rope =
var a: TLoc
let q = skipConv(n)
@@ -90,33 +98,34 @@ proc openArrayLoc(p: BProc, n: PNode): PRope =
of tyOpenArray, tyVarargs, tyArray, tyArrayConstr:
"($1)+($2), ($3)-($2)+1"
of tyString, tySequence:
if skipTypes(n.typ, abstractInst).kind == tyVar:
if skipTypes(n.typ, abstractInst).kind == tyVar and
not compileToCpp(p.module):
"(*$1)->data+($2), ($3)-($2)+1"
else:
"$1->data+($2), ($3)-($2)+1"
else: (internalError("openArrayLoc: " & typeToString(a.t)); "")
result = ropef(fmt, [rdLoc(a), rdLoc(b), rdLoc(c)])
result = fmt % [rdLoc(a), rdLoc(b), rdLoc(c)]
else:
initLocExpr(p, n, a)
case skipTypes(a.t, abstractVar).kind
of tyOpenArray, tyVarargs:
result = ropef("$1, $1Len0", [rdLoc(a)])
result = "$1, $1Len0" % [rdLoc(a)]
of tyString, tySequence:
if skipTypes(n.typ, abstractInst).kind == tyVar:
result = ropef("(*$1)->data, (*$1)->$2", [a.rdLoc, lenField(p)])
if skipTypes(n.typ, abstractInst).kind == tyVar and
not compileToCpp(p.module):
result = "(*$1)->data, (*$1)->$2" % [a.rdLoc, lenField(p)]
else:
result = ropef("$1->data, $1->$2", [a.rdLoc, lenField(p)])
result = "$1->data, $1->$2" % [a.rdLoc, lenField(p)]
of tyArray, tyArrayConstr:
result = ropef("$1, $2", [rdLoc(a), toRope(lengthOrd(a.t))])
result = "$1, $2" % [rdLoc(a), rope(lengthOrd(a.t))]
else: internalError("openArrayLoc: " & typeToString(a.t))
proc genArgStringToCString(p: BProc,
n: PNode): PRope {.inline.} =
proc genArgStringToCString(p: BProc, n: PNode): Rope {.inline.} =
var a: TLoc
initLocExpr(p, n.sons[0], a)
result = ropef("$1->data", [a.rdLoc])
proc genArg(p: BProc, n: PNode, param: PSym): PRope =
result = "$1->data" % [a.rdLoc]
proc genArg(p: BProc, n: PNode, param: PSym; call: PNode): Rope =
var a: TLoc
if n.kind == nkStringToCString:
result = genArgStringToCString(p, n)
@@ -126,23 +135,35 @@ proc genArg(p: BProc, n: PNode, param: PSym): PRope =
elif ccgIntroducedPtr(param):
initLocExpr(p, n, a)
result = addrLoc(a)
elif p.module.compileToCpp and param.typ.kind == tyVar and
n.kind == nkHiddenAddr:
initLocExprSingleUse(p, n.sons[0], a)
# if the proc is 'importc'ed but not 'importcpp'ed then 'var T' still
# means '*T'. See posix.nim for lots of examples that do that in the wild.
let callee = call.sons[0]
if callee.kind == nkSym and
{sfImportC, sfInfixCall, sfCompilerProc} * callee.sym.flags == {sfImportC} and
{lfHeader, lfNoDecl} * callee.sym.loc.flags != {}:
result = addrLoc(a)
else:
result = rdLoc(a)
else:
initLocExpr(p, n, a)
initLocExprSingleUse(p, n, a)
result = rdLoc(a)
proc genArgNoParam(p: BProc, n: PNode): PRope =
proc genArgNoParam(p: BProc, n: PNode): Rope =
var a: TLoc
if n.kind == nkStringToCString:
result = genArgStringToCString(p, n)
else:
initLocExpr(p, n, a)
initLocExprSingleUse(p, n, a)
result = rdLoc(a)
proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) =
var op: TLoc
# this is a hotspot in the compiler
initLocExpr(p, ri.sons[0], op)
var params: PRope
var params: Rope
# getUniqueType() is too expensive here:
var typ = skipTypes(ri.sons[0].typ, abstractInst)
assert(typ.kind == tyProc)
@@ -150,48 +171,49 @@ proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) =
var length = sonsLen(ri)
for i in countup(1, length - 1):
if ri.sons[i].typ.isCompileTimeOnly: continue
if params != nil: app(params, ~", ")
if params != nil: add(params, ~", ")
if i < sonsLen(typ):
assert(typ.n.sons[i].kind == nkSym)
app(params, genArg(p, ri.sons[i], typ.n.sons[i].sym))
add(params, genArg(p, ri.sons[i], typ.n.sons[i].sym, ri))
else:
app(params, genArgNoParam(p, ri.sons[i]))
add(params, genArgNoParam(p, ri.sons[i]))
fixupCall(p, le, ri, d, op.r, params)
proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
proc getRawProcType(p: BProc, t: PType): PRope =
proc getRawProcType(p: BProc, t: PType): Rope =
result = getClosureType(p.module, t, clHalf)
proc addComma(r: PRope): PRope =
result = if r == nil: r else: con(r, ~", ")
proc addComma(r: Rope): Rope =
result = if r == nil: r else: r & ~", "
const PatProc = "$1.ClEnv? $1.ClPrc($3$1.ClEnv):(($4)($1.ClPrc))($2)"
const PatIter = "$1.ClPrc($3$1.ClEnv)" # we know the env exists
var op: TLoc
initLocExpr(p, ri.sons[0], op)
var pl: PRope
var pl: Rope
var typ = skipTypes(ri.sons[0].typ, abstractInst)
assert(typ.kind == tyProc)
var length = sonsLen(ri)
for i in countup(1, length - 1):
assert(sonsLen(typ) == sonsLen(typ.n))
if ri.sons[i].typ.isCompileTimeOnly: continue
if i < sonsLen(typ):
assert(typ.n.sons[i].kind == nkSym)
app(pl, genArg(p, ri.sons[i], typ.n.sons[i].sym))
add(pl, genArg(p, ri.sons[i], typ.n.sons[i].sym, ri))
else:
app(pl, genArgNoParam(p, ri.sons[i]))
if i < length - 1: app(pl, ~", ")
add(pl, genArgNoParam(p, ri.sons[i]))
if i < length - 1: add(pl, ~", ")
template genCallPattern {.dirty.} =
lineF(p, cpsStmts, callPattern & ";$n", op.r, pl, pl.addComma, rawProc)
lineF(p, cpsStmts, callPattern & ";$n", [op.r, pl, pl.addComma, rawProc])
let rawProc = getRawProcType(p, typ)
let callPattern = if tfIterator in typ.flags: PatIter else: PatProc
if typ.sons[0] != nil:
if isInvalidReturnType(typ.sons[0]):
if sonsLen(ri) > 1: app(pl, ~", ")
if sonsLen(ri) > 1: add(pl, ~", ")
# beware of 'result = p(result)'. We may need to allocate a temporary:
if d.k in {locTemp, locNone} or not leftAppearsOnRightSide(le, ri):
# Great, we can use 'd':
@@ -200,12 +222,12 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
elif d.k notin {locExpr, locTemp} and not hasNoInit(ri):
# reset before pass as 'result' var:
resetLoc(p, d)
app(pl, addrLoc(d))
add(pl, addrLoc(d))
genCallPattern()
else:
var tmp: TLoc
getTemp(p, typ.sons[0], tmp, needsInit=true)
app(pl, addrLoc(tmp))
add(pl, addrLoc(tmp))
genCallPattern()
genAssignment(p, d, tmp, {}) # no need for deep copying
else:
@@ -213,37 +235,214 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
assert(d.t != nil) # generate an assignment to d:
var list: TLoc
initLoc(list, locCall, d.t, OnUnknown)
list.r = ropef(callPattern, op.r, pl, pl.addComma, rawProc)
list.r = callPattern % [op.r, pl, pl.addComma, rawProc]
genAssignment(p, d, list, {}) # no need for deep copying
else:
genCallPattern()
proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): Rope =
if ri.sons[i].typ.isCompileTimeOnly:
result = nil
elif i < sonsLen(typ):
# 'var T' is 'T&' in C++. This means we ignore the request of
# any nkHiddenAddr when it's a 'var T'.
assert(typ.n.sons[i].kind == nkSym)
if typ.sons[i].kind == tyVar and ri.sons[i].kind == nkHiddenAddr:
result = genArgNoParam(p, ri.sons[i][0])
else:
result = genArgNoParam(p, ri.sons[i]) #, typ.n.sons[i].sym)
else:
result = genArgNoParam(p, ri.sons[i])
discard """
Dot call syntax in C++
======================
so c2nim translates 'this' sometimes to 'T' and sometimes to 'var T'
both of which are wrong, but often more convenient to use.
For manual wrappers it can also be 'ptr T'
Fortunately we know which parameter is the 'this' parameter and so can fix this
mess in the codegen.
now ... if the *argument* is a 'ptr' the codegen shall emit -> and otherwise .
but this only depends on the argument and not on how the 'this' was declared
however how the 'this' was declared affects whether we end up with
wrong 'addr' and '[]' ops...
Since I'm tired I'll enumerate all the cases here:
var
x: ptr T
y: T
proc t(x: T)
x[].t() --> (*x).t() is correct.
y.t() --> y.t() is correct
proc u(x: ptr T)
x.u() --> needs to become x->u()
(addr y).u() --> needs to become y.u()
proc v(x: var T)
--> first skip the implicit 'nkAddr' node
x[].v() --> (*x).v() is correct, but might have been eliminated due
to the nkAddr node! So for this case we need to generate '->'
y.v() --> y.v() is correct
"""
proc skipAddrDeref(node: PNode): PNode =
var n = node
var isAddr = false
case n.kind
of nkAddr, nkHiddenAddr:
n = n.sons[0]
isAddr = true
of nkDerefExpr, nkHiddenDeref:
n = n.sons[0]
else: return n
if n.kind == nkObjDownConv: n = n.sons[0]
if isAddr and n.kind in {nkDerefExpr, nkHiddenDeref}:
result = n.sons[0]
elif n.kind in {nkAddr, nkHiddenAddr}:
result = n.sons[0]
else:
result = node
proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType): Rope =
# for better or worse c2nim translates the 'this' argument to a 'var T'.
# However manual wrappers may also use 'ptr T'. In any case we support both
# for convenience.
internalAssert i < sonsLen(typ)
assert(typ.n.sons[i].kind == nkSym)
# if the parameter is lying (tyVar) and thus we required an additional deref,
# skip the deref:
var ri = ri[i]
while ri.kind == nkObjDownConv: ri = ri[0]
let t = typ.sons[i].skipTypes({tyGenericInst})
if t.kind == tyVar:
let x = if ri.kind == nkHiddenAddr: ri[0] else: ri
if x.typ.kind == tyPtr:
result = genArgNoParam(p, x)
result.add("->")
elif x.kind in {nkHiddenDeref, nkDerefExpr} and x[0].typ.kind == tyPtr:
result = genArgNoParam(p, x[0])
result.add("->")
else:
result = genArgNoParam(p, x)
result.add(".")
elif t.kind == tyPtr:
if ri.kind in {nkAddr, nkHiddenAddr}:
result = genArgNoParam(p, ri[0])
result.add(".")
else:
result = genArgNoParam(p, ri)
result.add("->")
else:
ri = skipAddrDeref(ri)
if ri.kind in {nkAddr, nkHiddenAddr}: ri = ri[0]
result = genArgNoParam(p, ri) #, typ.n.sons[i].sym)
result.add(".")
proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType): Rope =
var i = 0
var j = 1
while i < pat.len:
case pat[i]
of '@':
if j < ri.len:
result.add genOtherArg(p, ri, j, typ)
for k in j+1 .. < ri.len:
result.add(~", ")
result.add genOtherArg(p, ri, k, typ)
inc i
of '#':
if pat[i+1] in {'+', '@'}:
let ri = ri[j]
if ri.kind in nkCallKinds:
let typ = skipTypes(ri.sons[0].typ, abstractInst)
if pat[i+1] == '+': result.add genArgNoParam(p, ri.sons[0])
result.add(~"(")
if 1 < ri.len:
result.add genOtherArg(p, ri, 1, typ)
for k in j+1 .. < ri.len:
result.add(~", ")
result.add genOtherArg(p, ri, k, typ)
result.add(~")")
else:
localError(ri.info, "call expression expected for C++ pattern")
inc i
elif pat[i+1] == '.':
result.add genThisArg(p, ri, j, typ)
inc i
elif pat[i+1] == '[':
var arg = ri.sons[j].skipAddrDeref
while arg.kind in {nkAddr, nkHiddenAddr, nkObjDownConv}: arg = arg[0]
result.add genArgNoParam(p, arg)
#result.add debugTree(arg, 0, 10)
else:
result.add genOtherArg(p, ri, j, typ)
inc j
inc i
of '\'':
var idx, stars: int
if scanCppGenericSlot(pat, i, idx, stars):
var t = resolveStarsInCppType(typ, idx, stars)
if t == nil: result.add(~"void")
else: result.add(getTypeDesc(p.module, t))
else:
let start = i
while i < pat.len:
if pat[i] notin {'@', '#', '\''}: inc(i)
else: break
if i - 1 >= start:
add(result, substr(pat, start, i - 1))
proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
var op, a: TLoc
initLocExpr(p, ri.sons[0], op)
var pl: PRope = nil
# getUniqueType() is too expensive here:
var typ = skipTypes(ri.sons[0].typ, abstractInst)
assert(typ.kind == tyProc)
var length = sonsLen(ri)
assert(sonsLen(typ) == sonsLen(typ.n))
var param = typ.n.sons[1].sym
app(pl, genArg(p, ri.sons[1], param))
if skipTypes(param.typ, {tyGenericInst}).kind == tyPtr: app(pl, ~"->")
else: app(pl, ~".")
app(pl, op.r)
var params: PRope
for i in countup(2, length - 1):
if params != nil: params.app(~", ")
assert(sonsLen(typ) == sonsLen(typ.n))
if i < sonsLen(typ):
assert(typ.n.sons[i].kind == nkSym)
app(params, genArg(p, ri.sons[i], typ.n.sons[i].sym))
# don't call '$' here for efficiency:
let pat = ri.sons[0].sym.loc.r.data
internalAssert pat != nil
if pat.contains({'#', '(', '@', '\''}):
var pl = genPatternCall(p, ri, pat, typ)
# simpler version of 'fixupCall' that works with the pl+params combination:
var typ = skipTypes(ri.sons[0].typ, abstractInst)
if typ.sons[0] != nil:
if p.module.compileToCpp and lfSingleUse in d.flags:
# do not generate spurious temporaries for C++! For C we're better off
# with them to prevent undefined behaviour and because the codegen
# is free to emit expressions multiple times!
d.k = locCall
d.r = pl
excl d.flags, lfSingleUse
else:
if d.k == locNone: getTemp(p, typ.sons[0], d)
assert(d.t != nil) # generate an assignment to d:
var list: TLoc
initLoc(list, locCall, d.t, OnUnknown)
list.r = pl
genAssignment(p, d, list, {}) # no need for deep copying
else:
app(params, genArgNoParam(p, ri.sons[i]))
fixupCall(p, le, ri, d, pl, params)
add(pl, ~";$n")
line(p, cpsStmts, pl)
else:
var pl: Rope = nil
#var param = typ.n.sons[1].sym
if 1 < ri.len:
add(pl, genThisArg(p, ri, 1, typ))
add(pl, op.r)
var params: Rope
for i in countup(2, length - 1):
if params != nil: params.add(~", ")
assert(sonsLen(typ) == sonsLen(typ.n))
add(params, genOtherArg(p, ri, i, typ))
fixupCall(p, le, ri, d, pl, params)
proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
# generates a crappy ObjC call
@@ -255,44 +454,56 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
assert(typ.kind == tyProc)
var length = sonsLen(ri)
assert(sonsLen(typ) == sonsLen(typ.n))
if length > 1:
app(pl, genArg(p, ri.sons[1], typ.n.sons[1].sym))
app(pl, ~" ")
app(pl, op.r)
if length > 2:
app(pl, ~": ")
app(pl, genArg(p, ri.sons[2], typ.n.sons[2].sym))
for i in countup(3, length-1):
# don't call '$' here for efficiency:
let pat = ri.sons[0].sym.loc.r.data
internalAssert pat != nil
var start = 3
if ' ' in pat:
start = 1
add(pl, op.r)
if length > 1:
add(pl, ~": ")
add(pl, genArg(p, ri.sons[1], typ.n.sons[1].sym, ri))
start = 2
else:
if length > 1:
add(pl, genArg(p, ri.sons[1], typ.n.sons[1].sym, ri))
add(pl, ~" ")
add(pl, op.r)
if length > 2:
add(pl, ~": ")
add(pl, genArg(p, ri.sons[2], typ.n.sons[2].sym, ri))
for i in countup(start, length-1):
assert(sonsLen(typ) == sonsLen(typ.n))
if i >= sonsLen(typ):
internalError(ri.info, "varargs for objective C method?")
assert(typ.n.sons[i].kind == nkSym)
var param = typ.n.sons[i].sym
app(pl, ~" ")
app(pl, param.name.s)
app(pl, ~": ")
app(pl, genArg(p, ri.sons[i], param))
add(pl, ~" ")
add(pl, param.name.s)
add(pl, ~": ")
add(pl, genArg(p, ri.sons[i], param, ri))
if typ.sons[0] != nil:
if isInvalidReturnType(typ.sons[0]):
if sonsLen(ri) > 1: app(pl, ~" ")
if sonsLen(ri) > 1: add(pl, ~" ")
# beware of 'result = p(result)'. We always allocate a temporary:
if d.k in {locTemp, locNone}:
# We already got a temp. Great, special case it:
if d.k == locNone: getTemp(p, typ.sons[0], d, needsInit=true)
app(pl, ~"Result: ")
app(pl, addrLoc(d))
app(pl, ~"];$n")
add(pl, ~"Result: ")
add(pl, addrLoc(d))
add(pl, ~"];$n")
line(p, cpsStmts, pl)
else:
var tmp: TLoc
getTemp(p, typ.sons[0], tmp, needsInit=true)
app(pl, addrLoc(tmp))
app(pl, ~"];$n")
add(pl, addrLoc(tmp))
add(pl, ~"];$n")
line(p, cpsStmts, pl)
genAssignment(p, d, tmp, {}) # no need for deep copying
else:
app(pl, ~"]")
add(pl, ~"]")
if d.k == locNone: getTemp(p, typ.sons[0], d)
assert(d.t != nil) # generate an assignment to d:
var list: TLoc
@@ -300,14 +511,13 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
list.r = pl
genAssignment(p, d, list, {}) # no need for deep copying
else:
app(pl, ~"];$n")
add(pl, ~"];$n")
line(p, cpsStmts, pl)
proc genCall(p: BProc, e: PNode, d: var TLoc) =
if e.sons[0].typ.callConv == ccClosure:
genClosureCall(p, nil, e, d)
elif e.sons[0].kind == nkSym and sfInfixCall in e.sons[0].sym.flags and
e.len >= 2:
elif e.sons[0].kind == nkSym and sfInfixCall in e.sons[0].sym.flags:
genInfixCall(p, nil, e, d)
elif e.sons[0].kind == nkSym and sfNamedParamCall in e.sons[0].sym.flags:
genNamedParamCall(p, e, d)
@@ -320,8 +530,7 @@ proc genCall(p: BProc, e: PNode, d: var TLoc) =
proc genAsgnCall(p: BProc, le, ri: PNode, d: var TLoc) =
if ri.sons[0].typ.callConv == ccClosure:
genClosureCall(p, le, ri, d)
elif ri.sons[0].kind == nkSym and sfInfixCall in ri.sons[0].sym.flags and
ri.len >= 2:
elif ri.sons[0].kind == nkSym and sfInfixCall in ri.sons[0].sym.flags:
genInfixCall(p, le, ri, d)
elif ri.sons[0].kind == nkSym and sfNamedParamCall in ri.sons[0].sym.flags:
genNamedParamCall(p, ri, d)

File diff suppressed because it is too large Load Diff

View File

@@ -45,29 +45,29 @@ const
]
NimMergeEndMark = "/*\tNIM_merge_END:*/"
proc genSectionStart*(fs: TCFileSection): PRope =
proc genSectionStart*(fs: TCFileSection): Rope =
if compilationCachePresent:
result = toRope(tnl)
app(result, "/*\t")
app(result, CFileSectionNames[fs])
app(result, ":*/")
app(result, tnl)
result = rope(tnl)
add(result, "/*\t")
add(result, CFileSectionNames[fs])
add(result, ":*/")
add(result, tnl)
proc genSectionEnd*(fs: TCFileSection): PRope =
proc genSectionEnd*(fs: TCFileSection): Rope =
if compilationCachePresent:
result = toRope(NimMergeEndMark & tnl)
result = rope(NimMergeEndMark & tnl)
proc genSectionStart*(ps: TCProcSection): PRope =
proc genSectionStart*(ps: TCProcSection): Rope =
if compilationCachePresent:
result = toRope(tnl)
app(result, "/*\t")
app(result, CProcSectionNames[ps])
app(result, ":*/")
app(result, tnl)
result = rope(tnl)
add(result, "/*\t")
add(result, CProcSectionNames[ps])
add(result, ":*/")
add(result, tnl)
proc genSectionEnd*(ps: TCProcSection): PRope =
proc genSectionEnd*(ps: TCProcSection): Rope =
if compilationCachePresent:
result = toRope(NimMergeEndMark & tnl)
result = rope(NimMergeEndMark & tnl)
proc writeTypeCache(a: TIdTable, s: var string) =
var i = 0
@@ -79,7 +79,7 @@ proc writeTypeCache(a: TIdTable, s: var string) =
s.add(' ')
encodeVInt(id, s)
s.add(':')
encodeStr(PRope(value).ropeToStr, s)
encodeStr($Rope(value), s)
inc i
s.add('}')
@@ -94,8 +94,8 @@ proc writeIntSet(a: IntSet, s: var string) =
encodeVInt(x, s)
inc i
s.add('}')
proc genMergeInfo*(m: BModule): PRope =
proc genMergeInfo*(m: BModule): Rope =
if optSymbolFiles notin gGlobalOptions: return nil
var s = "/*\tNIM_merge_INFO:"
s.add(tnl)
@@ -111,9 +111,9 @@ proc genMergeInfo*(m: BModule): PRope =
encodeVInt(ord(m.frameDeclared), s)
s.add(tnl)
s.add("*/")
result = s.toRope
result = s.rope
template `^`(pos: expr): expr = L.buf[pos]
template `^`(pos: int): expr = L.buf[pos]
proc skipWhite(L: var TBaseLexer) =
var pos = L.bufpos
@@ -132,7 +132,7 @@ proc skipUntilCmd(L: var TBaseLexer) =
of CR: pos = nimlexbase.handleCR(L, pos)
of LF: pos = nimlexbase.handleLF(L, pos)
of '\0': break
of '/':
of '/':
if ^(pos+1) == '*' and ^(pos+2) == '\t':
inc pos, 3
break
@@ -145,7 +145,7 @@ proc atEndMark(buf: cstring, pos: int): bool =
while s < NimMergeEndMark.len and buf[pos+s] == NimMergeEndMark[s]: inc s
result = s == NimMergeEndMark.len
proc readVerbatimSection(L: var TBaseLexer): PRope =
proc readVerbatimSection(L: var TBaseLexer): Rope =
var pos = L.bufpos
var buf = L.buf
var r = newStringOfCap(30_000)
@@ -162,14 +162,14 @@ proc readVerbatimSection(L: var TBaseLexer): PRope =
of '\0':
internalError("ccgmerge: expected: " & NimMergeEndMark)
break
else:
else:
if atEndMark(buf, pos):
inc pos, NimMergeEndMark.len
break
r.add(buf[pos])
inc pos
L.bufpos = pos
result = r.toRope
result = r.rope
proc readKey(L: var TBaseLexer, result: var string) =
var pos = L.bufpos
@@ -181,7 +181,7 @@ proc readKey(L: var TBaseLexer, result: var string) =
if buf[pos] != ':': internalError("ccgmerge: ':' expected")
L.bufpos = pos + 1 # skip ':'
proc newFakeType(id: int): PType =
proc newFakeType(id: int): PType =
new(result)
result.id = id
@@ -197,7 +197,7 @@ proc readTypeCache(L: var TBaseLexer, result: var TIdTable) =
# XXX little hack: we create a "fake" type object with the correct Id
# better would be to adapt the data structure to not even store the
# object as key, but only the Id
idTablePut(result, newFakeType(key), value.toRope)
idTablePut(result, newFakeType(key), value.rope)
inc L.bufpos
proc readIntSet(L: var TBaseLexer, result: var IntSet) =
@@ -223,12 +223,12 @@ proc processMergeInfo(L: var TBaseLexer, m: BModule) =
of "typeInfo": readIntSet(L, m.typeInfoMarker)
of "labels": m.labels = decodeVInt(L.buf, L.bufpos)
of "hasframe": m.frameDeclared = decodeVInt(L.buf, L.bufpos) != 0
else: internalError("ccgmerge: unkown key: " & k)
else: internalError("ccgmerge: unknown key: " & k)
when not defined(nimhygiene):
{.pragma: inject.}
template withCFile(cfilename: string, body: stmt) {.immediate.} =
template withCFile(cfilename: string, body: stmt) {.immediate.} =
var s = llStreamOpen(cfilename, fmRead)
if s == nil: return
var L {.inject.}: TBaseLexer
@@ -239,7 +239,7 @@ template withCFile(cfilename: string, body: stmt) {.immediate.} =
if ^L.bufpos == '\0': break
body
closeBaseLexer(L)
proc readMergeInfo*(cfilename: string, m: BModule) =
## reads the merge meta information into `m`.
withCFile(cfilename):
@@ -257,7 +257,7 @@ proc readMergeSections(cfilename: string, m: var TMergeSections) =
## reads the merge sections into `m`.
withCFile(cfilename):
readKey(L, k)
if k == "NIM_merge_INFO":
if k == "NIM_merge_INFO":
discard
elif ^L.bufpos == '*' and ^(L.bufpos+1) == '/':
inc(L.bufpos, 2)
@@ -280,19 +280,19 @@ proc readMergeSections(cfilename: string, m: var TMergeSections) =
proc mergeRequired*(m: BModule): bool =
for i in cfsHeaders..cfsProcs:
if m.s[i] != nil:
#echo "not empty: ", i, " ", ropeToStr(m.s[i])
#echo "not empty: ", i, " ", m.s[i]
return true
for i in low(TCProcSection)..high(TCProcSection):
if m.initProc.s(i) != nil:
#echo "not empty: ", i, " ", ropeToStr(m.initProc.s[i])
if m.initProc.s(i) != nil:
#echo "not empty: ", i, " ", m.initProc.s[i]
return true
proc mergeFiles*(cfilename: string, m: BModule) =
## merges the C file with the old version on hard disc.
var old: TMergeSections
readMergeSections(cfilename, old)
# do the merge; old section before new section:
# do the merge; old section before new section:
for i in low(TCFileSection)..high(TCFileSection):
m.s[i] = con(old.f[i], m.s[i])
m.s[i] = old.f[i] & m.s[i]
for i in low(TCProcSection)..high(TCProcSection):
m.initProc.s(i) = con(old.p[i], m.initProc.s(i))
m.initProc.s(i) = old.p[i] & m.initProc.s(i)

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@
# distribution, for details about the copyright.
#
## Thread var support for crappy architectures that lack native support for
## Thread var support for crappy architectures that lack native support for
## thread local storage. (**Thank you Mac OS X!**)
# included from cgen.nim
@@ -19,12 +19,12 @@ proc accessThreadLocalVar(p: BProc, s: PSym) =
if emulatedThreadVars() and not p.threadVarAccessed:
p.threadVarAccessed = true
p.module.usesThreadVars = true
appf(p.procSec(cpsLocals), "\tNimThreadVars* NimTV;$n")
app(p.procSec(cpsInit),
addf(p.procSec(cpsLocals), "\tNimThreadVars* NimTV;$n", [])
add(p.procSec(cpsInit),
ropecg(p.module, "\tNimTV = (NimThreadVars*) #GetThreadLocalVars();$n"))
var
nimtv: PRope # nimrod thread vars; the struct body
nimtv: Rope # nimrod thread vars; the struct body
nimtvDeps: seq[PType] = @[] # type deps: every module needs whole struct
nimtvDeclared = initIntSet() # so that every var/field exists only once
# in the struct
@@ -43,23 +43,23 @@ proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) =
# allocator for it :-(
if not containsOrIncl(nimtvDeclared, s.id):
nimtvDeps.add(s.loc.t)
appf(nimtv, "$1 $2;$n", [getTypeDesc(m, s.loc.t), s.loc.r])
addf(nimtv, "$1 $2;$n", [getTypeDesc(m, s.loc.t), s.loc.r])
else:
if isExtern: app(m.s[cfsVars], "extern ")
if optThreads in gGlobalOptions: app(m.s[cfsVars], "NIM_THREADVAR ")
app(m.s[cfsVars], getTypeDesc(m, s.loc.t))
appf(m.s[cfsVars], " $1;$n", [s.loc.r])
if isExtern: add(m.s[cfsVars], "extern ")
if optThreads in gGlobalOptions: add(m.s[cfsVars], "NIM_THREADVAR ")
add(m.s[cfsVars], getTypeDesc(m, s.loc.t))
addf(m.s[cfsVars], " $1;$n", [s.loc.r])
proc generateThreadLocalStorage(m: BModule) =
if nimtv != nil and (m.usesThreadVars or sfMainModule in m.module.flags):
for t in items(nimtvDeps): discard getTypeDesc(m, t)
appf(m.s[cfsSeqTypes], "typedef struct {$1} NimThreadVars;$n", [nimtv])
addf(m.s[cfsSeqTypes], "typedef struct {$1} NimThreadVars;$n", [nimtv])
proc generateThreadVarsSize(m: BModule) =
if nimtv != nil:
let externc = if gCmd != cmdCompileToCpp and
sfCompileToCpp in m.module.flags: "extern \"C\""
else: ""
appf(m.s[cfsProcs],
addf(m.s[cfsProcs],
"$#NI NimThreadVarsSize(){return (NI)sizeof(NimThreadVars);}$n",
[externc.toRope])
[externc.rope])

View File

@@ -17,11 +17,11 @@ type
p: BProc
visitorFrmt: string
proc genTraverseProc(c: var TTraversalClosure, accessor: PRope, typ: PType)
proc genTraverseProc(c: var TTraversalClosure, accessor: Rope, typ: PType)
proc genCaseRange(p: BProc, branch: PNode)
proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false)
proc genTraverseProc(c: var TTraversalClosure, accessor: PRope, n: PNode) =
proc genTraverseProc(c: var TTraversalClosure, accessor: Rope, n: PNode) =
if n == nil: return
case n.kind
of nkRecList:
@@ -31,31 +31,31 @@ proc genTraverseProc(c: var TTraversalClosure, accessor: PRope, n: PNode) =
if (n.sons[0].kind != nkSym): internalError(n.info, "genTraverseProc")
var p = c.p
let disc = n.sons[0].sym
lineF(p, cpsStmts, "switch ($1.$2) {$n", accessor, disc.loc.r)
lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.r])
for i in countup(1, sonsLen(n) - 1):
let branch = n.sons[i]
assert branch.kind in {nkOfBranch, nkElse}
if branch.kind == nkOfBranch:
genCaseRange(c.p, branch)
else:
lineF(p, cpsStmts, "default:$n")
lineF(p, cpsStmts, "default:$n", [])
genTraverseProc(c, accessor, lastSon(branch))
lineF(p, cpsStmts, "break;$n")
lineF(p, cpsStmts, "} $n")
lineF(p, cpsStmts, "break;$n", [])
lineF(p, cpsStmts, "} $n", [])
of nkSym:
let field = n.sym
if field.loc.t == nil:
internalError(n.info, "genTraverseProc()")
genTraverseProc(c, ropef("$1.$2", accessor, field.loc.r), field.loc.t)
genTraverseProc(c, "$1.$2" % [accessor, field.loc.r], field.loc.t)
else: internalError(n.info, "genTraverseProc()")
proc parentObj(accessor: PRope; m: BModule): PRope {.inline.} =
proc parentObj(accessor: Rope; m: BModule): Rope {.inline.} =
if not m.compileToCpp:
result = ropef("$1.Sup", accessor)
result = "$1.Sup" % [accessor]
else:
result = accessor
proc genTraverseProc(c: var TTraversalClosure, accessor: PRope, typ: PType) =
proc genTraverseProc(c: var TTraversalClosure, accessor: Rope, typ: PType) =
if typ == nil: return
var p = c.p
case typ.kind
@@ -66,9 +66,9 @@ proc genTraverseProc(c: var TTraversalClosure, accessor: PRope, typ: PType) =
var i: TLoc
getTemp(p, getSysType(tyInt), i)
linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
i.r, arraySize.toRope)
i.r, arraySize.rope)
genTraverseProc(c, rfmt(nil, "$1[$2]", accessor, i.r), typ.sons[1])
lineF(p, cpsStmts, "}$n")
lineF(p, cpsStmts, "}$n", [])
of tyObject:
for i in countup(0, sonsLen(typ) - 1):
genTraverseProc(c, accessor.parentObj(c.p.module), typ.sons[i])
@@ -76,7 +76,7 @@ proc genTraverseProc(c: var TTraversalClosure, accessor: PRope, typ: PType) =
of tyTuple:
let typ = getUniqueType(typ)
for i in countup(0, sonsLen(typ) - 1):
genTraverseProc(c, rfmt(nil, "$1.Field$2", accessor, i.toRope), typ.sons[i])
genTraverseProc(c, rfmt(nil, "$1.Field$2", accessor, i.rope), typ.sons[i])
of tyRef, tyString, tySequence:
lineCg(p, cpsStmts, c.visitorFrmt, accessor)
of tyProc:
@@ -85,67 +85,67 @@ proc genTraverseProc(c: var TTraversalClosure, accessor: PRope, typ: PType) =
else:
discard
proc genTraverseProcSeq(c: var TTraversalClosure, accessor: PRope, typ: PType) =
proc genTraverseProcSeq(c: var TTraversalClosure, accessor: Rope, typ: PType) =
var p = c.p
assert typ.kind == tySequence
assert typ.kind == tySequence
var i: TLoc
getTemp(p, getSysType(tyInt), i)
lineF(p, cpsStmts, "for ($1 = 0; $1 < $2->$3; $1++) {$n",
i.r, accessor, toRope(if c.p.module.compileToCpp: "len" else: "Sup.len"))
genTraverseProc(c, ropef("$1->data[$2]", accessor, i.r), typ.sons[0])
lineF(p, cpsStmts, "}$n")
proc genTraverseProc(m: BModule, typ: PType, reason: TTypeInfoReason): PRope =
[i.r, accessor, rope(if c.p.module.compileToCpp: "len" else: "Sup.len")])
genTraverseProc(c, "$1->data[$2]" % [accessor, i.r], typ.sons[0])
lineF(p, cpsStmts, "}$n", [])
proc genTraverseProc(m: BModule, typ: PType, reason: TTypeInfoReason): Rope =
var c: TTraversalClosure
var p = newProc(nil, m)
result = getGlobalTempName()
case reason
of tiNew: c.visitorFrmt = "#nimGCvisit((void*)$1, op);$n"
else: assert false
let header = ropef("N_NIMCALL(void, $1)(void* p, NI op)", result)
let header = "N_NIMCALL(void, $1)(void* p, NI op)" % [result]
let t = getTypeDesc(m, typ)
lineF(p, cpsLocals, "$1 a;$n", t)
lineF(p, cpsInit, "a = ($1)p;$n", t)
lineF(p, cpsLocals, "$1 a;$n", [t])
lineF(p, cpsInit, "a = ($1)p;$n", [t])
c.p = p
assert typ.kind != tyTypeDesc
if typ.kind == tySequence:
genTraverseProcSeq(c, "a".toRope, typ)
genTraverseProcSeq(c, "a".rope, typ)
else:
if skipTypes(typ.sons[0], typedescInst).kind in {tyArrayConstr, tyArray}:
# C's arrays are broken beyond repair:
genTraverseProc(c, "a".toRope, typ.sons[0])
genTraverseProc(c, "a".rope, typ.sons[0])
else:
genTraverseProc(c, "(*a)".toRope, typ.sons[0])
let generatedProc = ropef("$1 {$n$2$3$4}$n",
[header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)])
m.s[cfsProcHeaders].appf("$1;$n", header)
m.s[cfsProcs].app(generatedProc)
genTraverseProc(c, "(*a)".rope, typ.sons[0])
proc genTraverseProcForGlobal(m: BModule, s: PSym): PRope =
let generatedProc = "$1 {$n$2$3$4}$n" %
[header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)]
m.s[cfsProcHeaders].addf("$1;$n", [header])
m.s[cfsProcs].add(generatedProc)
proc genTraverseProcForGlobal(m: BModule, s: PSym): Rope =
discard genTypeInfo(m, s.loc.t)
var c: TTraversalClosure
var p = newProc(nil, m)
var sLoc = s.loc.r
result = getGlobalTempName()
if sfThread in s.flags and emulatedThreadVars():
accessThreadLocalVar(p, s)
sLoc = con("NimTV->", sLoc)
sLoc = "NimTV->" & sLoc
c.visitorFrmt = "#nimGCvisit((void*)$1, 0);$n"
c.p = p
let header = ropef("N_NIMCALL(void, $1)()", result)
let header = "N_NIMCALL(void, $1)()" % [result]
genTraverseProc(c, sLoc, s.loc.t)
let generatedProc = ropef("$1 {$n$2$3$4}$n",
[header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)])
m.s[cfsProcHeaders].appf("$1;$n", header)
m.s[cfsProcs].app(generatedProc)
let generatedProc = "$1 {$n$2$3$4}$n" %
[header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)]
m.s[cfsProcHeaders].addf("$1;$n", [header])
m.s[cfsProcs].add(generatedProc)

File diff suppressed because it is too large Load Diff

View File

@@ -9,31 +9,31 @@
# This module declares some helpers for the C code generator.
import
ast, astalgo, ropes, lists, hashes, strutils, types, msgs, wordrecg,
import
ast, astalgo, ropes, lists, hashes, strutils, types, msgs, wordrecg,
platform, trees
proc getPragmaStmt*(n: PNode, w: TSpecialWord): PNode =
case n.kind
of nkStmtList:
for i in 0 .. < n.len:
of nkStmtList:
for i in 0 .. < n.len:
result = getPragmaStmt(n[i], w)
if result != nil: break
of nkPragma:
for i in 0 .. < n.len:
for i in 0 .. < n.len:
if whichPragma(n[i]) == w: return n[i]
else: discard
proc stmtsContainPragma*(n: PNode, w: TSpecialWord): bool =
result = getPragmaStmt(n, w) != nil
proc hashString*(s: string): BiggestInt =
proc hashString*(s: string): BiggestInt =
# has to be the same algorithm as system.hashString!
if CPU[targetCPU].bit == 64:
if CPU[targetCPU].bit == 64:
# we have to use the same bitwidth
# as the target CPU
var b = 0'i64
for i in countup(0, len(s) - 1):
for i in countup(0, len(s) - 1):
b = b +% ord(s[i])
b = b +% `shl`(b, 10)
b = b xor `shr`(b, 6)
@@ -41,9 +41,9 @@ proc hashString*(s: string): BiggestInt =
b = b xor `shr`(b, 11)
b = b +% `shl`(b, 15)
result = b
else:
else:
var a = 0'i32
for i in countup(0, len(s) - 1):
for i in countup(0, len(s) - 1):
a = a +% ord(s[i]).int32
a = a +% `shl`(a, 10'i32)
a = a xor `shr`(a, 6'i32)
@@ -52,11 +52,11 @@ proc hashString*(s: string): BiggestInt =
a = a +% `shl`(a, 15'i32)
result = a
var
var
gTypeTable: array[TTypeKind, TIdTable]
gCanonicalTypes: array[TTypeKind, PType]
proc initTypeTables() =
proc initTypeTables() =
for i in countup(low(TTypeKind), high(TTypeKind)): initIdTable(gTypeTable[i])
proc resetCaches* =
@@ -67,28 +67,39 @@ proc resetCaches* =
when false:
proc echoStats*() =
for i in countup(low(TTypeKind), high(TTypeKind)):
for i in countup(low(TTypeKind), high(TTypeKind)):
echo i, " ", gTypeTable[i].counter
proc getUniqueType*(key: PType): PType =
proc slowSearch(key: PType; k: TTypeKind): PType =
# tuples are quite horrible as C does not support them directly and
# tuple[string, string] is a (strange) subtype of
# tuple[nameA, nameB: string]. This bites us here, so we
# use 'sameBackendType' instead of 'sameType'.
if idTableHasObjectAsKey(gTypeTable[k], key): return key
for h in countup(0, high(gTypeTable[k].data)):
var t = PType(gTypeTable[k].data[h].key)
if t != nil and sameBackendType(t, key):
return t
idTablePut(gTypeTable[k], key, key)
result = key
proc getUniqueType*(key: PType): PType =
# this is a hotspot in the compiler!
if key == nil: return
if key == nil: return
var k = key.kind
case k
of tyBool, tyChar,
tyInt..tyUInt64:
of tyBool, tyChar, tyInt..tyUInt64:
# no canonicalization for integral types, so that e.g. ``pid_t`` is
# produced instead of ``NI``.
result = key
of tyEmpty, tyNil, tyExpr, tyStmt, tyPointer, tyString,
of tyEmpty, tyNil, tyExpr, tyStmt, tyPointer, tyString,
tyCString, tyNone, tyBigNum:
result = gCanonicalTypes[k]
if result == nil:
gCanonicalTypes[k] = key
result = key
of tyTypeDesc, tyTypeClasses, tyGenericParam,
tyFromExpr, tyFieldAccessor:
internalError("GetUniqueType")
of tyTypeDesc, tyTypeClasses, tyGenericParam, tyFromExpr, tyFieldAccessor:
internalError("getUniqueType")
of tyDistinct:
if key.deepCopy != nil: result = key
else: result = getUniqueType(lastSon(key))
@@ -98,42 +109,39 @@ proc getUniqueType*(key: PType): PType =
#if obj.sym != nil and obj.sym.name.s == "TOption":
# echo "for ", typeToString(key), " I returned "
# debug result
of tyArrayConstr, tyGenericInvokation, tyGenericBody,
of tyPtr, tyRef, tyVar:
let elemType = lastSon(key)
if elemType.kind in {tyBool, tyChar, tyInt..tyUInt64}:
# no canonicalization for integral types, so that e.g. ``ptr pid_t`` is
# produced instead of ``ptr NI``.
result = key
else:
result = slowSearch(key, k)
of tyArrayConstr, tyGenericInvocation, tyGenericBody,
tyOpenArray, tyArray, tySet, tyRange, tyTuple,
tyPtr, tyRef, tySequence, tyForward, tyVarargs, tyProxy, tyVar:
# tuples are quite horrible as C does not support them directly and
# tuple[string, string] is a (strange) subtype of
# tuple[nameA, nameB: string]. This bites us here, so we
# use 'sameBackendType' instead of 'sameType'.
tySequence, tyForward, tyVarargs, tyProxy:
# we have to do a slow linear search because types may need
# to be compared by their structure:
if idTableHasObjectAsKey(gTypeTable[k], key): return key
for h in countup(0, high(gTypeTable[k].data)):
var t = PType(gTypeTable[k].data[h].key)
if t != nil and sameBackendType(t, key):
return t
idTablePut(gTypeTable[k], key, key)
result = key
result = slowSearch(key, k)
of tyObject:
if tfFromGeneric notin key.flags:
# fast case; lookup per id suffices:
result = PType(idTableGet(gTypeTable[k], key))
if result == nil:
if result == nil:
idTablePut(gTypeTable[k], key, key)
result = key
else:
# ugly slow case: need to compare by structure
if idTableHasObjectAsKey(gTypeTable[k], key): return key
for h in countup(0, high(gTypeTable[k].data)):
for h in countup(0, high(gTypeTable[k].data)):
var t = PType(gTypeTable[k].data[h].key)
if t != nil and sameType(t, key):
if t != nil and sameBackendType(t, key):
return t
idTablePut(gTypeTable[k], key, key)
result = key
result = key
of tyEnum:
result = PType(idTableGet(gTypeTable[k], key))
if result == nil:
if result == nil:
idTablePut(gTypeTable[k], key, key)
result = key
of tyProc:
@@ -141,24 +149,18 @@ proc getUniqueType*(key: PType): PType =
result = key
else:
# ugh, we need the canon here:
if idTableHasObjectAsKey(gTypeTable[k], key): return key
for h in countup(0, high(gTypeTable[k].data)):
var t = PType(gTypeTable[k].data[h].key)
if t != nil and sameBackendType(t, key):
return t
idTablePut(gTypeTable[k], key, key)
result = key
proc tableGetType*(tab: TIdTable, key: PType): RootRef =
result = slowSearch(key, k)
proc tableGetType*(tab: TIdTable, key: PType): RootRef =
# returns nil if we need to declare this type
result = idTableGet(tab, key)
if (result == nil) and (tab.counter > 0):
if (result == nil) and (tab.counter > 0):
# we have to do a slow linear search because types may need
# to be compared by their structure:
for h in countup(0, high(tab.data)):
for h in countup(0, high(tab.data)):
var t = PType(tab.data[h].key)
if t != nil:
if sameType(t, key):
if t != nil:
if sameType(t, key):
return tab.data[h].val
proc makeSingleLineCString*(s: string): string =
@@ -174,7 +176,7 @@ proc mangle*(name: string): string =
result = newStringOfCap(name.len)
case name[0]
of Letters:
result.add(name[0].toLower)
result.add(name[0])
of Digits:
result.add("N" & name[0])
else:
@@ -191,20 +193,20 @@ proc mangle*(name: string): string =
else:
add(result, "HEX" & toHex(ord(c), 2))
proc makeLLVMString*(s: string): PRope =
proc makeLLVMString*(s: string): Rope =
const MaxLineLength = 64
result = nil
var res = "c\""
for i in countup(0, len(s) - 1):
if (i + 1) mod MaxLineLength == 0:
app(result, toRope(res))
for i in countup(0, len(s) - 1):
if (i + 1) mod MaxLineLength == 0:
add(result, rope(res))
setLen(res, 0)
case s[i]
of '\0'..'\x1F', '\x80'..'\xFF', '\"', '\\':
of '\0'..'\x1F', '\x80'..'\xFF', '\"', '\\':
add(res, '\\')
add(res, toHex(ord(s[i]), 2))
else: add(res, s[i])
add(res, "\\00\"")
app(result, toRope(res))
add(result, rope(res))
initTypeTables()

File diff suppressed because it is too large Load Diff

View File

@@ -9,11 +9,13 @@
## This module contains the data structures for the C code generation phase.
import
import
ast, astalgo, ropes, passes, options, intsets, lists, platform
from msgs import TLineInfo
type
TLabel* = PRope # for the C generator a label is just a rope
TLabel* = Rope # for the C generator a label is just a rope
TCFileSection* = enum # the sections a generated C file consists of
cfsMergeInfo, # section containing merge information
cfsHeaders, # section for C include file headers
@@ -43,34 +45,35 @@ type
ctUInt, ctUInt8, ctUInt16, ctUInt32, ctUInt64,
ctArray, ctPtrToArray, ctStruct, ctPtr, ctNimStr, ctNimSeq, ctProc,
ctCString
TCFileSections* = array[TCFileSection, PRope] # represents a generated C file
TCFileSections* = array[TCFileSection, Rope] # represents a generated C file
TCProcSection* = enum # the sections a generated C proc consists of
cpsLocals, # section of local variables for C proc
cpsInit, # section for init of variables for C proc
cpsStmts # section of local statements for C proc
TCProcSections* = array[TCProcSection, PRope] # represents a generated C proc
TCProcSections* = array[TCProcSection, Rope] # represents a generated C proc
BModule* = ref TCGen
BProc* = ref TCProc
TBlock*{.final.} = object
TBlock*{.final.} = object
id*: int # the ID of the label; positive means that it
label*: PRope # generated text for the label
label*: Rope # generated text for the label
# nil if label is not used
sections*: TCProcSections # the code beloging
isLoop*: bool # whether block is a loop
nestedTryStmts*: int16 # how many try statements is it nested into
nestedExceptStmts*: int16 # how many except statements is it nested into
frameLen*: int16
TCProc{.final.} = object # represents C proc that is currently generated
prc*: PSym # the Nim proc that this C proc belongs to
beforeRetNeeded*: bool # true iff 'BeforeRet' label for proc is needed
threadVarAccessed*: bool # true if the proc already accessed some threadvar
lastLineInfo*: TLineInfo # to avoid generating excessive 'nimln' statements
nestedTryStmts*: seq[PNode] # in how many nested try statements we are
# (the vars must be volatile then)
inExceptBlock*: int # are we currently inside an except block?
# leaving such scopes by raise or by return must
# execute any applicable finally blocks
finallySafePoints*: seq[PRope] # For correctly cleaning up exceptions when
finallySafePoints*: seq[Rope] # For correctly cleaning up exceptions when
# using return in finally statements
labels*: Natural # for generating unique labels in the C proc
blocks*: seq[TBlock] # nested blocks
@@ -82,9 +85,12 @@ type
maxFrameLen*: int # max length of frame descriptor
module*: BModule # used to prevent excessive parameter passing
withinLoop*: int # > 0 if we are within a loop
splitDecls*: int # > 0 if we are in some context for C++ that
# requires 'T x = T()' to become 'T x; x = T()'
# (yes, C++ is weird like that)
gcFrameId*: Natural # for the GC stack marking
gcFrameType*: PRope # the struct {} we put the GC markers into
gcFrameType*: Rope # the struct {} we put the GC markers into
TTypeSeq* = seq[PType]
TCGen = object of TPassContext # represents a C source file
module*: PSym
@@ -112,24 +118,24 @@ type
dataCache*: TNodeTable
forwardedProcs*: TSymSeq # keep forwarded procs here
typeNodes*, nimTypes*: int # used for type info generation
typeNodesName*, nimTypesName*: PRope # used for type info generation
typeNodesName*, nimTypesName*: Rope # used for type info generation
labels*: Natural # for generating unique module-scope names
extensionLoaders*: array['0'..'9', PRope] # special procs for the
extensionLoaders*: array['0'..'9', Rope] # special procs for the
# OpenGL wrapper
injectStmt*: PRope
injectStmt*: Rope
var
mainModProcs*, mainModInit*, otherModsInit*, mainDatInit*: PRope
mainModProcs*, mainModInit*, otherModsInit*, mainDatInit*: Rope
# varuious parts of the main module
gMapping*: PRope # the generated mapping file (if requested)
gMapping*: Rope # the generated mapping file (if requested)
gModules*: seq[BModule] = @[] # list of all compiled modules
gForwardedProcsCounter*: int = 0
proc s*(p: BProc, s: TCProcSection): var PRope {.inline.} =
proc s*(p: BProc, s: TCProcSection): var Rope {.inline.} =
# section in the current block
result = p.blocks[p.blocks.len - 1].sections[s]
proc procSec*(p: BProc, s: TCProcSection): var PRope {.inline.} =
proc procSec*(p: BProc, s: TCProcSection): var Rope {.inline.} =
# top level proc sections
result = p.blocks[0].sections[s]
@@ -137,7 +143,7 @@ proc bmod*(module: PSym): BModule =
# obtains the BModule for a given module PSym
result = gModules[module.position]
proc newProc*(prc: PSym, module: BModule): BProc =
proc newProc*(prc: PSym, module: BModule): BProc =
new(result)
result.prc = prc
result.module = module

View File

@@ -1,7 +1,7 @@
#
#
# The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -24,8 +24,8 @@ bootSwitch(usedMarkAndSweep, defined(gcmarkandsweep), "--gc:markAndSweep")
bootSwitch(usedGenerational, defined(gcgenerational), "--gc:generational")
bootSwitch(usedNoGC, defined(nogc), "--gc:none")
import
os, msgs, options, nversion, condsyms, strutils, extccomp, platform, lists,
import
os, msgs, options, nversion, condsyms, strutils, extccomp, platform, lists,
wordrecg, parseutils, nimblecmd, idents, parseopt
# but some have deps to imported modules. Yay.
@@ -39,8 +39,8 @@ bootSwitch(usedFFI, hasFFI, "-d:useFFI")
proc writeCommandLineUsage*()
type
TCmdLinePass* = enum
type
TCmdLinePass* = enum
passCmd1, # first pass over the command line
passCmd2, # second pass over the command line
passPP # preprocessor called processCommand()
@@ -52,48 +52,48 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo)
const
HelpMessage = "Nim Compiler Version $1 (" & CompileDate & ") [$2: $3]\n" &
"Copyright (c) 2006-2014 by Andreas Rumpf\n"
"Copyright (c) 2006-2015 by Andreas Rumpf\n"
const
const
Usage = slurp"doc/basicopt.txt".replace("//", "")
AdvancedUsage = slurp"doc/advopt.txt".replace("//", "")
proc getCommandLineDesc(): string =
result = (HelpMessage % [VersionAsString, platform.OS[platform.hostOS].name,
proc getCommandLineDesc(): string =
result = (HelpMessage % [VersionAsString, platform.OS[platform.hostOS].name,
CPU[platform.hostCPU].name]) & Usage
proc helpOnError(pass: TCmdLinePass) =
proc helpOnError(pass: TCmdLinePass) =
if pass == passCmd1:
msgWriteln(getCommandLineDesc())
quit(0)
msgQuit(0)
proc writeAdvancedUsage(pass: TCmdLinePass) =
proc writeAdvancedUsage(pass: TCmdLinePass) =
if pass == passCmd1:
msgWriteln(`%`(HelpMessage, [VersionAsString,
platform.OS[platform.hostOS].name,
msgWriteln(`%`(HelpMessage, [VersionAsString,
platform.OS[platform.hostOS].name,
CPU[platform.hostCPU].name]) & AdvancedUsage)
quit(0)
msgQuit(0)
proc writeVersionInfo(pass: TCmdLinePass) =
proc writeVersionInfo(pass: TCmdLinePass) =
if pass == passCmd1:
msgWriteln(`%`(HelpMessage, [VersionAsString,
platform.OS[platform.hostOS].name,
msgWriteln(`%`(HelpMessage, [VersionAsString,
platform.OS[platform.hostOS].name,
CPU[platform.hostCPU].name]))
discard """const gitHash = gorge("git log -n 1 --format=%H")
if gitHash.strip.len == 40:
msgWriteln("git hash: " & gitHash)"""
const gitHash = gorge("git log -n 1 --format=%H").strip
when gitHash.len == 40:
msgWriteln("git hash: " & gitHash)
msgWriteln("active boot switches:" & usedRelease & usedAvoidTimeMachine &
usedTinyC & usedGnuReadline & usedNativeStacktrace & usedNoCaas &
usedFFI & usedBoehm & usedMarkAndSweep & usedGenerational & usedNoGC)
quit(0)
msgQuit(0)
var
helpWritten: bool
proc writeCommandLineUsage() =
if not helpWritten:
proc writeCommandLineUsage() =
if not helpWritten:
msgWriteln(getCommandLineDesc())
helpWritten = true
@@ -101,71 +101,71 @@ proc addPrefix(switch: string): string =
if len(switch) == 1: result = "-" & switch
else: result = "--" & switch
proc invalidCmdLineOption(pass: TCmdLinePass, switch: string, info: TLineInfo) =
proc invalidCmdLineOption(pass: TCmdLinePass, switch: string, info: TLineInfo) =
if switch == " ": localError(info, errInvalidCmdLineOption, "-")
else: localError(info, errInvalidCmdLineOption, addPrefix(switch))
proc splitSwitch(switch: string, cmd, arg: var string, pass: TCmdLinePass,
info: TLineInfo) =
proc splitSwitch(switch: string, cmd, arg: var string, pass: TCmdLinePass,
info: TLineInfo) =
cmd = ""
var i = 0
if i < len(switch) and switch[i] == '-': inc(i)
if i < len(switch) and switch[i] == '-': inc(i)
while i < len(switch):
while i < len(switch):
case switch[i]
of 'a'..'z', 'A'..'Z', '0'..'9', '_', '.': add(cmd, switch[i])
else: break
else: break
inc(i)
if i >= len(switch): arg = ""
elif switch[i] in {':', '=', '['}: arg = substr(switch, i + 1)
else: invalidCmdLineOption(pass, switch, info)
proc processOnOffSwitch(op: TOptions, arg: string, pass: TCmdLinePass,
info: TLineInfo) =
proc processOnOffSwitch(op: TOptions, arg: string, pass: TCmdLinePass,
info: TLineInfo) =
case whichKeyword(arg)
of wOn: gOptions = gOptions + op
of wOff: gOptions = gOptions - op
else: localError(info, errOnOrOffExpectedButXFound, arg)
proc processOnOffSwitchG(op: TGlobalOptions, arg: string, pass: TCmdLinePass,
info: TLineInfo) =
proc processOnOffSwitchG(op: TGlobalOptions, arg: string, pass: TCmdLinePass,
info: TLineInfo) =
case whichKeyword(arg)
of wOn: gGlobalOptions = gGlobalOptions + op
of wOff: gGlobalOptions = gGlobalOptions - op
else: localError(info, errOnOrOffExpectedButXFound, arg)
proc expectArg(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
proc expectArg(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
if arg == "": localError(info, errCmdLineArgExpected, addPrefix(switch))
proc expectNoArg(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
proc expectNoArg(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
if arg != "": localError(info, errCmdLineNoArgExpected, addPrefix(switch))
proc processSpecificNote(arg: string, state: TSpecialWord, pass: TCmdLinePass,
info: TLineInfo) =
proc processSpecificNote(arg: string, state: TSpecialWord, pass: TCmdLinePass,
info: TLineInfo; orig: string) =
var id = "" # arg = "X]:on|off"
var i = 0
var n = hintMin
while i < len(arg) and (arg[i] != ']'):
while i < len(arg) and (arg[i] != ']'):
add(id, arg[i])
inc(i)
if i < len(arg) and (arg[i] == ']'): inc(i)
else: invalidCmdLineOption(pass, arg, info)
else: invalidCmdLineOption(pass, orig, info)
if i < len(arg) and (arg[i] in {':', '='}): inc(i)
else: invalidCmdLineOption(pass, arg, info)
if state == wHint:
else: invalidCmdLineOption(pass, orig, info)
if state == wHint:
var x = findStr(msgs.HintsToStr, id)
if x >= 0: n = TNoteKind(x + ord(hintMin))
else: invalidCmdLineOption(pass, arg, info)
else:
else: localError(info, "unknown hint: " & id)
else:
var x = findStr(msgs.WarningsToStr, id)
if x >= 0: n = TNoteKind(x + ord(warnMin))
else: invalidCmdLineOption(pass, arg, info)
else: localError(info, "unknown warning: " & id)
case whichKeyword(substr(arg, i))
of wOn: incl(gNotes, n)
of wOff: excl(gNotes, n)
else: localError(info, errOnOrOffExpectedButXFound, arg)
proc processCompile(filename: string) =
proc processCompile(filename: string) =
var found = findFile(filename)
if found == "": found = filename
var trunc = changeFileExt(found, "")
@@ -191,7 +191,7 @@ proc testCompileOptionArg*(switch, arg: string, info: TLineInfo): bool =
else: localError(info, errNoneSpeedOrSizeExpectedButXFound, arg)
else: invalidCmdLineOption(passCmd1, switch, info)
proc testCompileOption*(switch: string, info: TLineInfo): bool =
proc testCompileOption*(switch: string, info: TLineInfo): bool =
case switch.normalize
of "debuginfo": result = contains(gGlobalOptions, optCDebug)
of "compileonly", "c": result = contains(gGlobalOptions, optCompileOnly)
@@ -228,11 +228,11 @@ proc testCompileOption*(switch: string, info: TLineInfo): bool =
of "patterns": result = contains(gOptions, optPatterns)
of "experimental": result = gExperimentalMode
else: invalidCmdLineOption(passCmd1, switch, info)
proc processPath(path: string, notRelativeToProj = false): string =
let p = if notRelativeToProj or os.isAbsolute(path) or
'$' in path or path[0] == '.':
path
'$' in path or path[0] == '.':
path
else:
options.gProjectPath / path
result = unixToNativePath(p % ["nimrod", getPrefixDir(),
@@ -251,14 +251,14 @@ proc trackDirty(arg: string, info: TLineInfo) =
localError(info, errInvalidNumber, a[1])
if parseUtils.parseInt(a[3], column) <= 0:
localError(info, errInvalidNumber, a[2])
gDirtyBufferIdx = a[0].fileInfoIdx
gDirtyOriginalIdx = a[1].fileInfoIdx
optTrackPos = newLineInfo(gDirtyBufferIdx, line, column)
msgs.addCheckpoint(optTrackPos)
proc track(arg: string, info: TLineInfo) =
let dirtyOriginalIdx = a[1].fileInfoIdx
if dirtyOriginalIdx >= 0:
msgs.setDirtyFile(dirtyOriginalIdx, a[0])
gTrackPos = newLineInfo(dirtyOriginalIdx, line, column)
proc track(arg: string, info: TLineInfo) =
var a = arg.split(',')
if a.len != 3: localError(info, errTokenExpected, "FILE,LINE,COLUMN")
var line, column: int
@@ -266,21 +266,20 @@ proc track(arg: string, info: TLineInfo) =
localError(info, errInvalidNumber, a[1])
if parseUtils.parseInt(a[2], column) <= 0:
localError(info, errInvalidNumber, a[2])
optTrackPos = newLineInfo(a[0], line, column)
msgs.addCheckpoint(optTrackPos)
gTrackPos = newLineInfo(a[0], line, column)
proc dynlibOverride(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
if pass in {passCmd2, passPP}:
expectArg(switch, arg, pass, info)
options.inclDynlibOverride(arg)
proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
var
proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
var
theOS: TSystemOS
cpu: TSystemCPU
key, val: string
case switch.normalize
of "path", "p":
of "path", "p":
expectArg(switch, arg, pass, info)
addPath(processPath(arg), info)
of "nimblepath", "babelpath":
@@ -304,7 +303,7 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
of "nimcache":
expectArg(switch, arg, pass, info)
options.nimcacheDir = processPath(arg)
of "out", "o":
of "out", "o":
expectArg(switch, arg, pass, info)
options.outFile = arg
of "docseesrcurl":
@@ -312,46 +311,46 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
options.docSeeSrcUrl = arg
of "mainmodule", "m":
discard "allow for backwards compatibility, but don't do anything"
of "define", "d":
of "define", "d":
expectArg(switch, arg, pass, info)
defineSymbol(arg)
of "undef", "u":
of "undef", "u":
expectArg(switch, arg, pass, info)
undefSymbol(arg)
of "symbol":
expectArg(switch, arg, pass, info)
declareSymbol(arg)
of "compile":
# deprecated, do nothing
of "compile":
expectArg(switch, arg, pass, info)
if pass in {passCmd2, passPP}: processCompile(arg)
of "link":
of "link":
expectArg(switch, arg, pass, info)
if pass in {passCmd2, passPP}: addFileToLink(arg)
of "debuginfo":
of "debuginfo":
expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optCDebug)
of "embedsrc":
expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optEmbedOrigSrc)
of "compileonly", "c":
of "compileonly", "c":
expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optCompileOnly)
of "nolinking":
of "nolinking":
expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optNoLinking)
of "nomain":
of "nomain":
expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optNoMain)
of "forcebuild", "f":
of "forcebuild", "f":
expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optForceFullMake)
of "project":
expectNoArg(switch, arg, pass, info)
gWholeProject = true
of "gc":
of "gc":
expectArg(switch, arg, pass, info)
case arg.normalize
of "boehm":
of "boehm":
gSelectedGC = gcBoehm
defineSymbol("boehmgc")
of "refc":
@@ -369,17 +368,27 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
defineSymbol("nogc")
else: localError(info, errNoneBoehmRefcExpectedButXFound, arg)
of "warnings", "w": processOnOffSwitch({optWarns}, arg, pass, info)
of "warning": processSpecificNote(arg, wWarning, pass, info)
of "hint": processSpecificNote(arg, wHint, pass, info)
of "warning": processSpecificNote(arg, wWarning, pass, info, switch)
of "hint": processSpecificNote(arg, wHint, pass, info, switch)
of "hints": processOnOffSwitch({optHints}, arg, pass, info)
of "threadanalysis": processOnOffSwitchG({optThreadAnalysis}, arg, pass, info)
of "stacktrace": processOnOffSwitch({optStackTrace}, arg, pass, info)
of "linetrace": processOnOffSwitch({optLineTrace}, arg, pass, info)
of "debugger":
processOnOffSwitch({optEndb}, arg, pass, info)
if optEndb in gOptions: defineSymbol("endb")
else: undefSymbol("endb")
of "profiler":
of "debugger":
case arg.normalize
of "on", "endb":
gOptions.incl optEndb
defineSymbol("endb")
of "off":
gOptions.excl optEndb
undefSymbol("endb")
of "native", "gdb":
incl(gGlobalOptions, optCDebug)
gOptions = gOptions + {optLineDir} - {optEndb}
undefSymbol("endb")
else:
localError(info, "expected endb|gdb but found " & arg)
of "profiler":
processOnOffSwitch({optProfiler}, arg, pass, info)
if optProfiler in gOptions: defineSymbol("profiler")
else: undefSymbol("profiler")
@@ -398,7 +407,7 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
of "deadcodeelim": processOnOffSwitchG({optDeadCodeElim}, arg, pass, info)
of "threads":
processOnOffSwitchG({optThreads}, arg, pass, info)
if optThreads in gGlobalOptions: incl(gNotes, warnGcUnsafe)
#if optThreads in gGlobalOptions: incl(gNotes, warnGcUnsafe)
of "tlsemulation": processOnOffSwitchG({optTlsEmulation}, arg, pass, info)
of "taintmode": processOnOffSwitchG({optTaintMode}, arg, pass, info)
of "implicitstatic":
@@ -408,17 +417,17 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
of "opt":
expectArg(switch, arg, pass, info)
case arg.normalize
of "speed":
of "speed":
incl(gOptions, optOptimizeSpeed)
excl(gOptions, optOptimizeSize)
of "size":
of "size":
excl(gOptions, optOptimizeSpeed)
incl(gOptions, optOptimizeSize)
of "none":
excl(gOptions, optOptimizeSpeed)
excl(gOptions, optOptimizeSize)
else: localError(info, errNoneSpeedOrSizeExpectedButXFound, arg)
of "app":
of "app":
expectArg(switch, arg, pass, info)
case arg.normalize
of "gui":
@@ -440,10 +449,10 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
defineSymbol("library")
defineSymbol("staticlib")
else: localError(info, errGuiConsoleOrLibExpectedButXFound, arg)
of "passc", "t":
of "passc", "t":
expectArg(switch, arg, pass, info)
if pass in {passCmd2, passPP}: extccomp.addCompileOption(arg)
of "passl", "l":
of "passl", "l":
expectArg(switch, arg, pass, info)
if pass in {passCmd2, passPP}: extccomp.addLinkOption(arg)
of "cincludes":
@@ -466,52 +475,50 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
of "include":
expectArg(switch, arg, pass, info)
if pass in {passCmd2, passPP}: implicitIncludes.add arg
of "listcmd":
of "listcmd":
expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optListCmd)
of "genmapping":
of "genmapping":
expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optGenMapping)
of "os":
of "os":
expectArg(switch, arg, pass, info)
if pass in {passCmd1, passPP}:
if pass in {passCmd1, passPP}:
theOS = platform.nameToOS(arg)
if theOS == osNone: localError(info, errUnknownOS, arg)
elif theOS != platform.hostOS:
elif theOS != platform.hostOS:
setTarget(theOS, targetCPU)
condsyms.initDefines()
of "cpu":
of "cpu":
expectArg(switch, arg, pass, info)
if pass in {passCmd1, passPP}:
if pass in {passCmd1, passPP}:
cpu = platform.nameToCPU(arg)
if cpu == cpuNone: localError(info, errUnknownCPU, arg)
elif cpu != platform.hostCPU:
elif cpu != platform.hostCPU:
setTarget(targetOS, cpu)
condsyms.initDefines()
of "run", "r":
of "run", "r":
expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optRun)
of "verbosity":
of "verbosity":
expectArg(switch, arg, pass, info)
gVerbosity = parseInt(arg)
of "parallelbuild":
of "parallelbuild":
expectArg(switch, arg, pass, info)
gNumberOfProcessors = parseInt(arg)
of "version", "v":
of "version", "v":
expectNoArg(switch, arg, pass, info)
writeVersionInfo(pass)
of "advanced":
of "advanced":
expectNoArg(switch, arg, pass, info)
writeAdvancedUsage(pass)
of "help", "h":
of "help", "h":
expectNoArg(switch, arg, pass, info)
helpOnError(pass)
of "symbolfiles":
of "symbolfiles":
processOnOffSwitchG({optSymbolFiles}, arg, pass, info)
of "skipcfg":
of "skipcfg":
expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optSkipConfigFile)
of "skipprojcfg":
of "skipprojcfg":
expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optSkipProjConfigFile)
of "skipusercfg":
@@ -520,17 +527,17 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
of "skipparentcfg":
expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optSkipParentConfigFiles)
of "genscript":
of "genscript":
expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optGenScript)
of "lib":
expectArg(switch, arg, pass, info)
libpath = processPath(arg, notRelativeToProj=true)
of "putenv":
of "putenv":
expectArg(switch, arg, pass, info)
splitSwitch(arg, key, val, pass, info)
os.putEnv(key, val)
of "cc":
of "cc":
expectArg(switch, arg, pass, info)
setCC(arg)
of "track":
@@ -539,21 +546,21 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
of "trackdirty":
expectArg(switch, arg, pass, info)
trackDirty(arg, info)
of "suggest":
of "suggest":
expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optSuggest)
gIdeCmd = ideSug
of "def":
expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optDef)
gIdeCmd = ideDef
of "eval":
expectArg(switch, arg, pass, info)
gEvalExpr = arg
of "context":
expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optContext)
gIdeCmd = ideCon
of "usages":
expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optUsages)
gIdeCmd = ideUse
of "stdout":
expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optStdout)
@@ -575,7 +582,7 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
else:
if strutils.find(switch, '.') >= 0: options.setConfigVar(switch, arg)
else: invalidCmdLineOption(pass, switch, info)
proc processCommand(switch: string, pass: TCmdLinePass) =
var cmd, arg: string
splitSwitch(switch, cmd, arg, pass, gCmdLineInfo)
@@ -591,14 +598,14 @@ proc processSwitch*(pass: TCmdLinePass; p: OptParser) =
# hint[X]:off is parsed as (p.key = "hint[X]", p.val = "off")
# we fix this here
var bracketLe = strutils.find(p.key, '[')
if bracketLe >= 0:
if bracketLe >= 0:
var key = substr(p.key, 0, bracketLe - 1)
var val = substr(p.key, bracketLe + 1) & ':' & p.val
processSwitch(key, val, pass, gCmdLineInfo)
else:
else:
processSwitch(p.key, p.val, pass, gCmdLineInfo)
proc processArgument*(pass: TCmdLinePass; p: OptParser;
proc processArgument*(pass: TCmdLinePass; p: OptParser;
argsCount: var int): bool =
if argsCount == 0:
options.command = p.key

View File

@@ -1,7 +1,7 @@
#
#
# The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -9,71 +9,69 @@
# This module handles the conditional symbols.
import
import
strtabs, platform, strutils, idents
# We need to use a PStringTable here as defined symbols are always guaranteed
# We need to use a StringTableRef here as defined symbols are always guaranteed
# to be style insensitive. Otherwise hell would break lose.
var gSymbols: StringTableRef
proc defineSymbol*(symbol: string) =
const
catNone = "false"
proc defineSymbol*(symbol: string) =
gSymbols[symbol] = "true"
proc declareSymbol*(symbol: string) =
gSymbols[symbol] = "unknown"
proc undefSymbol*(symbol: string) =
gSymbols[symbol] = catNone
proc undefSymbol*(symbol: string) =
gSymbols[symbol] = "false"
proc isDefined*(symbol: string): bool =
proc isDefined*(symbol: string): bool =
if gSymbols.hasKey(symbol):
result = gSymbols[symbol] == "true"
result = gSymbols[symbol] != catNone
elif cmpIgnoreStyle(symbol, CPU[targetCPU].name) == 0:
result = true
elif cmpIgnoreStyle(symbol, platform.OS[targetOS].name) == 0:
result = true
else:
case symbol.normalize
of "x86": result = targetCPU == cpuI386
of "itanium": result = targetCPU == cpuIa64
of "x8664": result = targetCPU == cpuAmd64
of "posix", "unix":
result = targetOS in {osLinux, osMorphos, osSkyos, osIrix, osPalmos,
osQnx, osAtari, osAix,
osHaiku, osVxWorks, osSolaris, osNetbsd,
osFreebsd, osOpenbsd, osMacosx}
of "bsd":
result = targetOS in {osNetbsd, osFreebsd, osOpenbsd}
of "emulatedthreadvars":
result = platform.OS[targetOS].props.contains(ospLacksThreadVars)
of "msdos": result = targetOS == osDos
of "mswindows", "win32": result = targetOS == osWindows
of "macintosh": result = targetOS in {osMacos, osMacosx}
of "sunos": result = targetOS == osSolaris
of "littleendian": result = CPU[targetCPU].endian == platform.littleEndian
of "bigendian": result = CPU[targetCPU].endian == platform.bigEndian
of "cpu8": result = CPU[targetCPU].bit == 8
of "cpu16": result = CPU[targetCPU].bit == 16
of "cpu32": result = CPU[targetCPU].bit == 32
of "cpu64": result = CPU[targetCPU].bit == 64
of "nimrawsetjmp":
result = targetOS in {osSolaris, osNetbsd, osFreebsd, osOpenbsd, osMacosx}
else: discard
proc isDefined*(symbol: PIdent): bool = isDefined(symbol.s)
proc isDeclared*(symbol: PIdent): bool = gSymbols.hasKey(symbol.s)
iterator definedSymbolNames*: string =
for key, val in pairs(gSymbols):
if val == "true": yield key
if val != catNone: yield key
proc countDefinedSymbols*(): int =
proc countDefinedSymbols*(): int =
result = 0
for key, val in pairs(gSymbols):
if val == "true": inc(result)
if val != catNone: inc(result)
# For ease of bootstrapping, we keep them here and not in the global config
# file for now:
const
additionalSymbols = """
x86 itanium x8664
msdos mswindows win32 unix posix sunos bsd macintosh RISCOS hpux
mac
hppa hp9000 hp9000s300 hp9000s700 hp9000s800 hp9000s820 ELATE sparcv9
ecmascript js nimrodvm nimffi nimdoc cpp objc
gcc llvmgcc clang lcc bcc dmc wcc vcc tcc pcc ucc icl
boehmgc gcmarkandsweep gcgenerational nogc gcUseBitvectors
endb profiler
executable guiapp consoleapp library dll staticlib
quick
release debug
useWinAnsi useFork useNimRtl useMalloc useRealtimeGC ssl memProfiler
nodejs kwin nimfix
usesysassert usegcassert tinyC useFFI
useStdoutAsStdmsg createNimRtl
booting fulldebug corruption nimsuperops noSignalHandler useGnuReadline
noCaas noDocGen noBusyWaiting nativeStackTrace useNodeIds selftest
reportMissedDeadlines avoidTimeMachine useClone ignoreAllocationSize
debugExecProcesses pcreDll useLipzipSrc
preventDeadlocks UNICODE winUnicode trackGcHeaders posixRealtime
nimStdSetjmp nimRawSetjmp nimSigSetjmp
""".split
proc initDefines*() =
proc initDefines*() =
gSymbols = newStringTable(modeStyleInsensitive)
defineSymbol("nimrod") # 'nimrod' is always defined
# for bootstrapping purposes and old code:
@@ -89,58 +87,4 @@ proc initDefines*() =
defineSymbol("nimparsebiggestfloatmagic")
defineSymbol("nimalias")
defineSymbol("nimlocks")
# add platform specific symbols:
for c in low(CPU)..high(CPU):
declareSymbol("cpu" & $CPU[c].bit)
declareSymbol(normalize(EndianToStr[CPU[c].endian]))
declareSymbol(CPU[c].name)
for o in low(platform.OS)..high(platform.OS):
declareSymbol(platform.OS[o].name)
for a in additionalSymbols:
declareSymbol(a)
# -----------------------------------------------------------
case targetCPU
of cpuI386: defineSymbol("x86")
of cpuIa64: defineSymbol("itanium")
of cpuAmd64: defineSymbol("x8664")
else: discard
case targetOS
of osDos:
defineSymbol("msdos")
of osWindows:
defineSymbol("mswindows")
defineSymbol("win32")
of osLinux, osMorphos, osSkyos, osIrix, osPalmos, osQnx, osAtari, osAix,
osHaiku:
# these are all 'unix-like'
defineSymbol("unix")
defineSymbol("posix")
of osSolaris:
defineSymbol("sunos")
defineSymbol("unix")
defineSymbol("posix")
of osNetbsd, osFreebsd, osOpenbsd:
defineSymbol("unix")
defineSymbol("bsd")
defineSymbol("posix")
of osMacos:
defineSymbol("macintosh")
of osMacosx:
defineSymbol("macintosh")
defineSymbol("unix")
defineSymbol("posix")
else: discard
defineSymbol("cpu" & $CPU[targetCPU].bit)
defineSymbol(normalize(EndianToStr[CPU[targetCPU].endian]))
defineSymbol(CPU[targetCPU].name)
defineSymbol(platform.OS[targetOS].name)
declareSymbol("emulatedthreadvars")
if platform.OS[targetOS].props.contains(ospLacksThreadVars):
defineSymbol("emulatedthreadvars")
case targetOS
of osSolaris, osNetbsd, osFreebsd, osOpenbsd, osMacosx:
defineSymbol("nimRawSetjmp")
else: discard
defineSymbol("nimnode")

View File

@@ -9,41 +9,41 @@
# This module implements a dependency file generator.
import
import
os, options, ast, astalgo, msgs, ropes, idents, passes, importer
proc generateDot*(project: string)
type
type
TGen = object of TPassContext
module*: PSym
PGen = ref TGen
var gDotGraph: PRope # the generated DOT file; we need a global variable
var gDotGraph: Rope # the generated DOT file; we need a global variable
proc addDependencyAux(importing, imported: string) =
appf(gDotGraph, "$1 -> $2;$n", [toRope(importing), toRope(imported)])
proc addDependencyAux(importing, imported: string) =
addf(gDotGraph, "$1 -> $2;$n", [rope(importing), rope(imported)])
# s1 -> s2_4[label="[0-9]"];
proc addDotDependency(c: PPassContext, n: PNode): PNode =
proc addDotDependency(c: PPassContext, n: PNode): PNode =
result = n
var g = PGen(c)
case n.kind
of nkImportStmt:
for i in countup(0, sonsLen(n) - 1):
of nkImportStmt:
for i in countup(0, sonsLen(n) - 1):
var imported = getModuleName(n.sons[i])
addDependencyAux(g.module.name.s, imported)
of nkFromStmt, nkImportExceptStmt:
of nkFromStmt, nkImportExceptStmt:
var imported = getModuleName(n.sons[0])
addDependencyAux(g.module.name.s, imported)
of nkStmtList, nkBlockStmt, nkStmtListExpr, nkBlockExpr:
of nkStmtList, nkBlockStmt, nkStmtListExpr, nkBlockExpr:
for i in countup(0, sonsLen(n) - 1): discard addDotDependency(c, n.sons[i])
else:
else:
discard
proc generateDot(project: string) =
writeRope(ropef("digraph $1 {$n$2}$n", [
toRope(changeFileExt(extractFilename(project), "")), gDotGraph]),
proc generateDot(project: string) =
writeRope("digraph $1 {$n$2}$n" % [
rope(changeFileExt(extractFilename(project), "")), gDotGraph],
changeFileExt(project, "dot"))
proc myOpen(module: PSym): PPassContext =

View File

@@ -17,12 +17,13 @@ import
importer, sempass2, json, xmltree, cgi, typesrenderer
type
TSections = array[TSymKind, PRope]
TSections = array[TSymKind, Rope]
TDocumentor = object of rstgen.TRstGenerator
modDesc: PRope # module description
modDesc: Rope # module description
id: int # for generating IDs
toc, section: TSections
indexValFilename: string
analytics: string # Google Analytics javascript, "" if doesn't exist
seenSymbols: StringTableRef # avoids duplicate symbol generation for HTML.
PDoc* = ref TDocumentor ## Alias to type less.
@@ -61,12 +62,29 @@ proc newDocumentor*(filename: string, config: StringTableRef): PDoc =
initRstGenerator(result[], (if gCmd != cmdRst2tex: outHtml else: outLatex),
options.gConfigVars, filename, {roSupportRawDirective},
docgenFindFile, compilerMsgHandler)
if config.hasKey("doc.googleAnalytics"):
result.analytics = """
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', '$1', 'auto');
ga('send', 'pageview');
</script>
""" % [config["doc.googleAnalytics"]]
else:
result.analytics = ""
result.seenSymbols = newStringTable(modeCaseInsensitive)
result.id = 100
proc dispA(dest: var PRope, xml, tex: string, args: openArray[PRope]) =
if gCmd != cmdRst2tex: appf(dest, xml, args)
else: appf(dest, tex, args)
proc dispA(dest: var Rope, xml, tex: string, args: openArray[Rope]) =
if gCmd != cmdRst2tex: addf(dest, xml, args)
else: addf(dest, tex, args)
proc getVarIdx(varnames: openArray[string], id: string): int =
for i in countup(0, high(varnames)):
@@ -74,8 +92,8 @@ proc getVarIdx(varnames: openArray[string], id: string): int =
return i
result = -1
proc ropeFormatNamedVars(frmt: TFormatStr, varnames: openArray[string],
varvalues: openArray[PRope]): PRope =
proc ropeFormatNamedVars(frmt: FormatStr, varnames: openArray[string],
varvalues: openArray[Rope]): Rope =
var i = 0
var L = len(frmt)
result = nil
@@ -85,11 +103,11 @@ proc ropeFormatNamedVars(frmt: TFormatStr, varnames: openArray[string],
inc(i) # skip '$'
case frmt[i]
of '#':
app(result, varvalues[num])
add(result, varvalues[num])
inc(num)
inc(i)
of '$':
app(result, "$")
add(result, "$")
inc(i)
of '0'..'9':
var j = 0
@@ -99,7 +117,7 @@ proc ropeFormatNamedVars(frmt: TFormatStr, varnames: openArray[string],
if (i > L + 0 - 1) or not (frmt[i] in {'0'..'9'}): break
if j > high(varvalues) + 1: internalError("ropeFormatNamedVars")
num = j
app(result, varvalues[j - 1])
add(result, varvalues[j - 1])
of 'A'..'Z', 'a'..'z', '\x80'..'\xFF':
var id = ""
while true:
@@ -107,8 +125,8 @@ proc ropeFormatNamedVars(frmt: TFormatStr, varnames: openArray[string],
inc(i)
if not (frmt[i] in {'A'..'Z', '_', 'a'..'z', '\x80'..'\xFF'}): break
var idx = getVarIdx(varnames, id)
if idx >= 0: app(result, varvalues[idx])
else: rawMessage(errUnkownSubstitionVar, id)
if idx >= 0: add(result, varvalues[idx])
else: rawMessage(errUnknownSubstitionVar, id)
of '{':
var id = ""
inc(i)
@@ -119,14 +137,14 @@ proc ropeFormatNamedVars(frmt: TFormatStr, varnames: openArray[string],
inc(i) # skip }
# search for the variable:
var idx = getVarIdx(varnames, id)
if idx >= 0: app(result, varvalues[idx])
else: rawMessage(errUnkownSubstitionVar, id)
if idx >= 0: add(result, varvalues[idx])
else: rawMessage(errUnknownSubstitionVar, id)
else: internalError("ropeFormatNamedVars")
var start = i
while i < L:
if frmt[i] != '$': inc(i)
else: break
if i - 1 >= start: app(result, substr(frmt, start, i - 1))
if i - 1 >= start: add(result, substr(frmt, start, i - 1))
proc genComment(d: PDoc, n: PNode): string =
result = ""
@@ -136,9 +154,9 @@ proc genComment(d: PDoc, n: PNode): string =
toLinenumber(n.info), toColumn(n.info),
dummyHasToc, d.options + {roSkipPounds}), result)
proc genRecComment(d: PDoc, n: PNode): PRope =
proc genRecComment(d: PDoc, n: PNode): Rope =
if n == nil: return nil
result = genComment(d, n).toRope
result = genComment(d, n).rope
if result == nil:
if n.kind notin {nkEmpty..nkNilLit}:
for i in countup(0, len(n)-1):
@@ -254,7 +272,7 @@ proc complexName(k: TSymKind, n: PNode, baseName: string): string =
## type)?(,param type)*``. The callable type part will be added only if the
## node is not a proc, as those are the common ones. The suffix will be a dot
## and a single letter representing the type of the callable. The parameter
## types will be added with a preceeding dash. Return types won't be added.
## types will be added with a preceding dash. Return types won't be added.
##
## If you modify the output of this proc, please update the anchor generation
## section of ``doc/docgen.txt``.
@@ -313,9 +331,9 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
if not isVisible(nameNode): return
let
name = getName(d, nameNode)
nameRope = name.toRope
nameRope = name.rope
plainDocstring = getPlainDocstring(n) # call here before genRecComment!
var result: PRope = nil
var result: Rope = nil
var literal, plainName = ""
var kind = tkEof
var comm = genRecComment(d, n) # call this here for the side-effect!
@@ -338,69 +356,69 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
break
of tkComment:
dispA(result, "<span class=\"Comment\">$1</span>", "\\spanComment{$1}",
[toRope(esc(d.target, literal))])
[rope(esc(d.target, literal))])
of tokKeywordLow..tokKeywordHigh:
dispA(result, "<span class=\"Keyword\">$1</span>", "\\spanKeyword{$1}",
[toRope(literal)])
[rope(literal)])
of tkOpr:
dispA(result, "<span class=\"Operator\">$1</span>", "\\spanOperator{$1}",
[toRope(esc(d.target, literal))])
[rope(esc(d.target, literal))])
of tkStrLit..tkTripleStrLit:
dispA(result, "<span class=\"StringLit\">$1</span>",
"\\spanStringLit{$1}", [toRope(esc(d.target, literal))])
"\\spanStringLit{$1}", [rope(esc(d.target, literal))])
of tkCharLit:
dispA(result, "<span class=\"CharLit\">$1</span>", "\\spanCharLit{$1}",
[toRope(esc(d.target, literal))])
[rope(esc(d.target, literal))])
of tkIntLit..tkUInt64Lit:
dispA(result, "<span class=\"DecNumber\">$1</span>",
"\\spanDecNumber{$1}", [toRope(esc(d.target, literal))])
"\\spanDecNumber{$1}", [rope(esc(d.target, literal))])
of tkFloatLit..tkFloat128Lit:
dispA(result, "<span class=\"FloatNumber\">$1</span>",
"\\spanFloatNumber{$1}", [toRope(esc(d.target, literal))])
"\\spanFloatNumber{$1}", [rope(esc(d.target, literal))])
of tkSymbol:
dispA(result, "<span class=\"Identifier\">$1</span>",
"\\spanIdentifier{$1}", [toRope(esc(d.target, literal))])
"\\spanIdentifier{$1}", [rope(esc(d.target, literal))])
of tkSpaces, tkInvalid:
app(result, literal)
add(result, literal)
of tkParLe, tkParRi, tkBracketLe, tkBracketRi, tkCurlyLe, tkCurlyRi,
tkBracketDotLe, tkBracketDotRi, tkCurlyDotLe, tkCurlyDotRi, tkParDotLe,
tkParDotRi, tkComma, tkSemiColon, tkColon, tkEquals, tkDot, tkDotDot,
tkAccent, tkColonColon,
tkGStrLit, tkGTripleStrLit, tkInfixOpr, tkPrefixOpr, tkPostfixOpr:
dispA(result, "<span class=\"Other\">$1</span>", "\\spanOther{$1}",
[toRope(esc(d.target, literal))])
[rope(esc(d.target, literal))])
inc(d.id)
let
plainNameRope = toRope(xmltree.escape(plainName.strip))
plainNameRope = rope(xmltree.escape(plainName.strip))
cleanPlainSymbol = renderPlainSymbolName(nameNode)
complexSymbol = complexName(k, n, cleanPlainSymbol)
plainSymbolRope = toRope(cleanPlainSymbol)
plainSymbolEncRope = toRope(encodeUrl(cleanPlainSymbol))
itemIDRope = toRope(d.id)
plainSymbolRope = rope(cleanPlainSymbol)
plainSymbolEncRope = rope(encodeUrl(cleanPlainSymbol))
itemIDRope = rope(d.id)
symbolOrId = d.newUniquePlainSymbol(complexSymbol)
symbolOrIdRope = symbolOrId.toRope
symbolOrIdEncRope = encodeUrl(symbolOrId).toRope
symbolOrIdRope = symbolOrId.rope
symbolOrIdEncRope = encodeUrl(symbolOrId).rope
var seeSrcRope: PRope = nil
var seeSrcRope: Rope = nil
let docItemSeeSrc = getConfigVar("doc.item.seesrc")
if docItemSeeSrc.len > 0 and options.docSeeSrcUrl.len > 0:
# XXX toFilename doesn't really work. We need to ensure that this keeps
# returning a relative path.
let urlRope = ropeFormatNamedVars(options.docSeeSrcUrl,
["path", "line"], [n.info.toFilename.toRope, toRope($n.info.line)])
["path", "line"], [n.info.toFilename.rope, rope($n.info.line)])
dispA(seeSrcRope, "$1", "", [ropeFormatNamedVars(docItemSeeSrc,
["path", "line", "url"], [n.info.toFilename.toRope,
toRope($n.info.line), urlRope])])
["path", "line", "url"], [n.info.toFilename.rope,
rope($n.info.line), urlRope])])
app(d.section[k], ropeFormatNamedVars(getConfigVar("doc.item"),
add(d.section[k], ropeFormatNamedVars(getConfigVar("doc.item"),
["name", "header", "desc", "itemID", "header_plain", "itemSym",
"itemSymOrID", "itemSymEnc", "itemSymOrIDEnc", "seeSrc"],
[nameRope, result, comm, itemIDRope, plainNameRope, plainSymbolRope,
symbolOrIdRope, plainSymbolEncRope, symbolOrIdEncRope, seeSrcRope]))
app(d.toc[k], ropeFormatNamedVars(getConfigVar("doc.item.toc"),
add(d.toc[k], ropeFormatNamedVars(getConfigVar("doc.item.toc"),
["name", "header", "desc", "itemID", "header_plain", "itemSym",
"itemSymOrID", "itemSymEnc", "itemSymOrIDEnc"],
[toRope(getName(d, nameNode, d.splitAfter)), result, comm,
[rope(getName(d, nameNode, d.splitAfter)), result, comm,
itemIDRope, plainNameRope, plainSymbolRope, symbolOrIdRope,
plainSymbolEncRope, symbolOrIdEncRope]))
@@ -418,7 +436,7 @@ proc genJSONItem(d: PDoc, n, nameNode: PNode, k: TSymKind): JsonNode =
if not isVisible(nameNode): return
var
name = getName(d, nameNode)
comm = genRecComment(d, n).ropeToStr()
comm = $genRecComment(d, n)
r: TSrcGen
initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments})
@@ -435,14 +453,14 @@ proc checkForFalse(n: PNode): bool =
proc traceDeps(d: PDoc, n: PNode) =
const k = skModule
if d.section[k] != nil: app(d.section[k], ", ")
if d.section[k] != nil: add(d.section[k], ", ")
dispA(d.section[k],
"<a class=\"reference external\" href=\"$1.html\">$1</a>",
"$1", [toRope(getModuleName(n))])
"$1", [rope(getModuleName(n))])
proc generateDoc*(d: PDoc, n: PNode) =
case n.kind
of nkCommentStmt: app(d.modDesc, genComment(d, n))
of nkCommentStmt: add(d.modDesc, genComment(d, n))
of nkProcDef:
when useEffectSystem: documentRaises(n)
genItem(d, n, n.sons[namePos], skProc)
@@ -503,13 +521,13 @@ proc generateJson(d: PDoc, n: PNode, jArray: JsonNode = nil): JsonNode =
result = genJSONItem(d, n.sons[i], n.sons[i].sons[0],
succ(skType, ord(n.kind)-ord(nkTypeSection)))
of nkStmtList:
var elem = jArray
if elem == nil: elem = newJArray()
result = if jArray != nil: jArray else: newJArray()
for i in countup(0, sonsLen(n) - 1):
var r = generateJson(d, n.sons[i], elem)
var r = generateJson(d, n.sons[i], result)
if r != nil:
elem.add(r)
if result == nil: result = elem
result.add(r)
of nkWhenStmt:
# generate documentation for the first branch only:
if not checkForFalse(n.sons[0].sons[0]) and jArray != nil:
@@ -522,28 +540,28 @@ proc genSection(d: PDoc, kind: TSymKind) =
"Iterators", "Iterators", "Converters", "Macros", "Templates"
]
if d.section[kind] == nil: return
var title = sectionNames[kind].toRope
var title = sectionNames[kind].rope
d.section[kind] = ropeFormatNamedVars(getConfigVar("doc.section"), [
"sectionid", "sectionTitle", "sectionTitleID", "content"], [
ord(kind).toRope, title, toRope(ord(kind) + 50), d.section[kind]])
ord(kind).rope, title, rope(ord(kind) + 50), d.section[kind]])
d.toc[kind] = ropeFormatNamedVars(getConfigVar("doc.section.toc"), [
"sectionid", "sectionTitle", "sectionTitleID", "content"], [
ord(kind).toRope, title, toRope(ord(kind) + 50), d.toc[kind]])
ord(kind).rope, title, rope(ord(kind) + 50), d.toc[kind]])
proc genOutFile(d: PDoc): PRope =
proc genOutFile(d: PDoc): Rope =
var
code, content: PRope
code, content: Rope
title = ""
var j = 0
var tmp = ""
renderTocEntries(d[], j, 1, tmp)
var toc = tmp.toRope
var toc = tmp.rope
for i in countup(low(TSymKind), high(TSymKind)):
genSection(d, i)
app(toc, d.toc[i])
add(toc, d.toc[i])
if toc != nil:
toc = ropeFormatNamedVars(getConfigVar("doc.toc"), ["content"], [toc])
for i in countup(low(TSymKind), high(TSymKind)): app(code, d.section[i])
for i in countup(low(TSymKind), high(TSymKind)): add(code, d.section[i])
# Extract the title. Non API modules generate an entry in the index table.
if d.meta[metaTitle].len != 0:
@@ -556,16 +574,16 @@ proc genOutFile(d: PDoc): PRope =
let bodyname = if d.hasToc: "doc.body_toc" else: "doc.body_no_toc"
content = ropeFormatNamedVars(getConfigVar(bodyname), ["title",
"tableofcontents", "moduledesc", "date", "time", "content"],
[title.toRope, toc, d.modDesc, toRope(getDateStr()),
toRope(getClockStr()), code])
[title.rope, toc, d.modDesc, rope(getDateStr()),
rope(getClockStr()), code])
if optCompileOnly notin gGlobalOptions:
# XXX what is this hack doing here? 'optCompileOnly' means raw output!?
code = ropeFormatNamedVars(getConfigVar("doc.file"), ["title",
"tableofcontents", "moduledesc", "date", "time",
"content", "author", "version"],
[title.toRope, toc, d.modDesc, toRope(getDateStr()),
toRope(getClockStr()), content, d.meta[metaAuthor].toRope,
d.meta[metaVersion].toRope])
"content", "author", "version", "analytics"],
[title.rope, toc, d.modDesc, rope(getDateStr()),
rope(getClockStr()), content, d.meta[metaAuthor].rope,
d.meta[metaVersion].rope, d.analytics.rope])
else:
code = content
result = code
@@ -600,7 +618,7 @@ proc commandRstAux(filename, outExt: string) =
#d.modDesc = newMutableRope(30_000)
renderRstToOut(d[], rst, modDesc)
#freezeMutableRope(d.modDesc)
d.modDesc = toRope(modDesc)
d.modDesc = rope(modDesc)
writeOutput(d, filename, outExt)
generateIndex(d)
@@ -617,7 +635,7 @@ proc commandJSON*() =
var d = newDocumentor(gProjectFull, options.gConfigVars)
d.hasToc = true
var json = generateJson(d, ast)
var content = newRope(pretty(json))
var content = rope(pretty(json))
if optStdout in gGlobalOptions:
writeRope(stdout, content)
@@ -626,11 +644,12 @@ proc commandJSON*() =
writeRope(content, getOutFile(gProjectFull, JsonExt), useWarning = false)
proc commandBuildIndex*() =
var content = mergeIndexes(gProjectFull).toRope
var content = mergeIndexes(gProjectFull).rope
let code = ropeFormatNamedVars(getConfigVar("doc.file"), ["title",
"tableofcontents", "moduledesc", "date", "time",
"content", "author", "version"],
["Index".toRope, nil, nil, toRope(getDateStr()),
toRope(getClockStr()), content, nil, nil])
"content", "author", "version", "analytics"],
["Index".rope, nil, nil, rope(getDateStr()),
rope(getClockStr()), content, nil, nil, nil])
# no analytics because context is not available
writeRope(code, getOutFile("theindex", HtmlExt))

View File

@@ -1,495 +0,0 @@
#
#
# The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This file implements the FFI part of the evaluator for Nim code.
import ast, astalgo, ropes, types, options, tables, dynlib, libffi, msgs, os
when defined(windows):
const libcDll = "msvcrt.dll"
else:
const libcDll = "libc.so(.6|.5|)"
type
TDllCache = tables.TTable[string, TLibHandle]
var
gDllCache = initTable[string, TLibHandle]()
when defined(windows):
var gExeHandle = loadLib(os.getAppFilename())
else:
var gExeHandle = loadLib()
proc getDll(cache: var TDllCache; dll: string; info: TLineInfo): pointer =
result = cache[dll]
if result.isNil:
var libs: seq[string] = @[]
libCandidates(dll, libs)
for c in libs:
result = loadLib(c)
if not result.isNil: break
if result.isNil:
globalError(info, "cannot load: " & dll)
cache[dll] = result
const
nkPtrLit = nkIntLit # hopefully we can get rid of this hack soon
var myerrno {.importc: "errno", header: "<errno.h>".}: cint ## error variable
proc importcSymbol*(sym: PSym): PNode =
let name = ropeToStr(sym.loc.r)
# the AST does not support untyped pointers directly, so we use an nkIntLit
# that contains the address instead:
result = newNodeIT(nkPtrLit, sym.info, sym.typ)
case name
of "stdin": result.intVal = cast[TAddress](system.stdin)
of "stdout": result.intVal = cast[TAddress](system.stdout)
of "stderr": result.intVal = cast[TAddress](system.stderr)
of "vmErrnoWrapper": result.intVal = cast[TAddress](myerrno)
else:
let lib = sym.annex
if lib != nil and lib.path.kind notin {nkStrLit..nkTripleStrLit}:
globalError(sym.info, "dynlib needs to be a string lit for the REPL")
var theAddr: pointer
if lib.isNil and not gExehandle.isNil:
# first try this exe itself:
theAddr = gExehandle.symAddr(name)
# then try libc:
if theAddr.isNil:
let dllhandle = gDllCache.getDll(libcDll, sym.info)
theAddr = dllhandle.symAddr(name)
elif not lib.isNil:
let dllhandle = gDllCache.getDll(if lib.kind == libHeader: libcDll
else: lib.path.strVal, sym.info)
theAddr = dllhandle.symAddr(name)
if theAddr.isNil: globalError(sym.info, "cannot import: " & sym.name.s)
result.intVal = cast[TAddress](theAddr)
proc mapType(t: ast.PType): ptr libffi.TType =
if t == nil: return addr libffi.type_void
case t.kind
of tyBool, tyEnum, tyChar, tyInt..tyInt64, tyUInt..tyUInt64, tySet:
case t.getSize
of 1: result = addr libffi.type_uint8
of 2: result = addr libffi.type_sint16
of 4: result = addr libffi.type_sint32
of 8: result = addr libffi.type_sint64
else: result = nil
of tyFloat, tyFloat64: result = addr libffi.type_double
of tyFloat32: result = addr libffi.type_float
of tyVar, tyPointer, tyPtr, tyRef, tyCString, tySequence, tyString, tyExpr,
tyStmt, tyTypeDesc, tyProc, tyArray, tyArrayConstr, tyStatic, tyNil:
result = addr libffi.type_pointer
of tyDistinct:
result = mapType(t.sons[0])
else:
result = nil
# too risky:
#of tyFloat128: result = addr libffi.type_longdouble
proc mapCallConv(cc: TCallingConvention, info: TLineInfo): TABI =
case cc
of ccDefault: result = DEFAULT_ABI
of ccStdCall: result = when defined(windows): STDCALL else: DEFAULT_ABI
of ccCDecl: result = DEFAULT_ABI
else:
globalError(info, "cannot map calling convention to FFI")
template rd(T, p: expr): expr {.immediate.} = (cast[ptr T](p))[]
template wr(T, p, v: expr) {.immediate.} = (cast[ptr T](p))[] = v
template `+!`(x, y: expr): expr {.immediate.} =
cast[pointer](cast[TAddress](x) + y)
proc packSize(v: PNode, typ: PType): int =
## computes the size of the blob
case typ.kind
of tyPtr, tyRef, tyVar:
if v.kind in {nkNilLit, nkPtrLit}:
result = sizeof(pointer)
else:
result = sizeof(pointer) + packSize(v.sons[0], typ.lastSon)
of tyDistinct, tyGenericInst:
result = packSize(v, typ.sons[0])
of tyArray, tyArrayConstr:
# consider: ptr array[0..1000_000, int] which is common for interfacing;
# we use the real length here instead
if v.kind in {nkNilLit, nkPtrLit}:
result = sizeof(pointer)
elif v.len != 0:
result = v.len * packSize(v.sons[0], typ.sons[1])
else:
result = typ.getSize.int
proc pack(v: PNode, typ: PType, res: pointer)
proc getField(n: PNode; position: int): PSym =
case n.kind
of nkRecList:
for i in countup(0, sonsLen(n) - 1):
result = getField(n.sons[i], position)
if result != nil: return
of nkRecCase:
result = getField(n.sons[0], position)
if result != nil: return
for i in countup(1, sonsLen(n) - 1):
case n.sons[i].kind
of nkOfBranch, nkElse:
result = getField(lastSon(n.sons[i]), position)
if result != nil: return
else: internalError(n.info, "getField(record case branch)")
of nkSym:
if n.sym.position == position: result = n.sym
else: discard
proc packObject(x: PNode, typ: PType, res: pointer) =
internalAssert x.kind in {nkObjConstr, nkPar}
# compute the field's offsets:
discard typ.getSize
for i in countup(ord(x.kind == nkObjConstr), sonsLen(x) - 1):
var it = x.sons[i]
if it.kind == nkExprColonExpr:
internalAssert it.sons[0].kind == nkSym
let field = it.sons[0].sym
pack(it.sons[1], field.typ, res +! field.offset)
elif typ.n != nil:
let field = getField(typ.n, i)
pack(it, field.typ, res +! field.offset)
else:
globalError(x.info, "cannot pack unnamed tuple")
const maxPackDepth = 20
var packRecCheck = 0
proc pack(v: PNode, typ: PType, res: pointer) =
template awr(T, v: expr) {.immediate, dirty.} =
wr(T, res, v)
case typ.kind
of tyBool: awr(bool, v.intVal != 0)
of tyChar: awr(char, v.intVal.chr)
of tyInt: awr(int, v.intVal.int)
of tyInt8: awr(int8, v.intVal.int8)
of tyInt16: awr(int16, v.intVal.int16)
of tyInt32: awr(int32, v.intVal.int32)
of tyInt64: awr(int64, v.intVal.int64)
of tyUInt: awr(uint, v.intVal.uint)
of tyUInt8: awr(uint8, v.intVal.uint8)
of tyUInt16: awr(uint16, v.intVal.uint16)
of tyUInt32: awr(uint32, v.intVal.uint32)
of tyUInt64: awr(uint64, v.intVal.uint64)
of tyEnum, tySet:
case v.typ.getSize
of 1: awr(uint8, v.intVal.uint8)
of 2: awr(uint16, v.intVal.uint16)
of 4: awr(int32, v.intVal.int32)
of 8: awr(int64, v.intVal.int64)
else:
globalError(v.info, "cannot map value to FFI (tyEnum, tySet)")
of tyFloat: awr(float, v.floatVal)
of tyFloat32: awr(float32, v.floatVal)
of tyFloat64: awr(float64, v.floatVal)
of tyPointer, tyProc, tyCString, tyString:
if v.kind == nkNilLit:
# nothing to do since the memory is 0 initialized anyway
discard
elif v.kind == nkPtrLit:
awr(pointer, cast[pointer](v.intVal))
elif v.kind in {nkStrLit..nkTripleStrLit}:
awr(cstring, cstring(v.strVal))
else:
globalError(v.info, "cannot map pointer/proc value to FFI")
of tyPtr, tyRef, tyVar:
if v.kind == nkNilLit:
# nothing to do since the memory is 0 initialized anyway
discard
elif v.kind == nkPtrLit:
awr(pointer, cast[pointer](v.intVal))
else:
if packRecCheck > maxPackDepth:
packRecCheck = 0
globalError(v.info, "cannot map value to FFI " & typeToString(v.typ))
inc packRecCheck
pack(v.sons[0], typ.lastSon, res +! sizeof(pointer))
dec packRecCheck
awr(pointer, res +! sizeof(pointer))
of tyArray, tyArrayConstr:
let baseSize = typ.sons[1].getSize
for i in 0 .. <v.len:
pack(v.sons[i], typ.sons[1], res +! i * baseSize)
of tyObject, tyTuple:
packObject(v, typ, res)
of tyNil:
discard
of tyDistinct, tyGenericInst:
pack(v, typ.sons[0], res)
else:
globalError(v.info, "cannot map value to FFI " & typeToString(v.typ))
proc unpack(x: pointer, typ: PType, n: PNode): PNode
proc unpackObjectAdd(x: pointer, n, result: PNode) =
case n.kind
of nkRecList:
for i in countup(0, sonsLen(n) - 1):
unpackObjectAdd(x, n.sons[i], result)
of nkRecCase:
globalError(result.info, "case objects cannot be unpacked")
of nkSym:
var pair = newNodeI(nkExprColonExpr, result.info, 2)
pair.sons[0] = n
pair.sons[1] = unpack(x +! n.sym.offset, n.sym.typ, nil)
#echo "offset: ", n.sym.name.s, " ", n.sym.offset
result.add pair
else: discard
proc unpackObject(x: pointer, typ: PType, n: PNode): PNode =
# compute the field's offsets:
discard typ.getSize
# iterate over any actual field of 'n' ... if n is nil we need to create
# the nkPar node:
if n.isNil:
result = newNode(nkPar)
result.typ = typ
if typ.n.isNil:
internalError("cannot unpack unnamed tuple")
unpackObjectAdd(x, typ.n, result)
else:
result = n
if result.kind notin {nkObjConstr, nkPar}:
globalError(n.info, "cannot map value from FFI")
if typ.n.isNil:
globalError(n.info, "cannot unpack unnamed tuple")
for i in countup(ord(n.kind == nkObjConstr), sonsLen(n) - 1):
var it = n.sons[i]
if it.kind == nkExprColonExpr:
internalAssert it.sons[0].kind == nkSym
let field = it.sons[0].sym
it.sons[1] = unpack(x +! field.offset, field.typ, it.sons[1])
else:
let field = getField(typ.n, i)
n.sons[i] = unpack(x +! field.offset, field.typ, it)
proc unpackArray(x: pointer, typ: PType, n: PNode): PNode =
if n.isNil:
result = newNode(nkBracket)
result.typ = typ
newSeq(result.sons, lengthOrd(typ).int)
else:
result = n
if result.kind != nkBracket:
globalError(n.info, "cannot map value from FFI")
let baseSize = typ.sons[1].getSize
for i in 0 .. < result.len:
result.sons[i] = unpack(x +! i * baseSize, typ.sons[1], result.sons[i])
proc canonNodeKind(k: TNodeKind): TNodeKind =
case k
of nkCharLit..nkUInt64Lit: result = nkIntLit
of nkFloatLit..nkFloat128Lit: result = nkFloatLit
of nkStrLit..nkTripleStrLit: result = nkStrLit
else: result = k
proc unpack(x: pointer, typ: PType, n: PNode): PNode =
template aw(k, v, field: expr) {.immediate, dirty.} =
if n.isNil:
result = newNode(k)
result.typ = typ
else:
# check we have the right field:
result = n
if result.kind.canonNodeKind != k.canonNodeKind:
#echo "expected ", k, " but got ", result.kind
#debug result
return newNodeI(nkExceptBranch, n.info)
#globalError(n.info, "cannot map value from FFI")
result.field = v
template setNil() =
if n.isNil:
result = newNode(nkNilLit)
result.typ = typ
else:
reset n[]
result = n
result.kind = nkNilLit
result.typ = typ
template awi(kind, v: expr) {.immediate, dirty.} = aw(kind, v, intVal)
template awf(kind, v: expr) {.immediate, dirty.} = aw(kind, v, floatVal)
template aws(kind, v: expr) {.immediate, dirty.} = aw(kind, v, strVal)
case typ.kind
of tyBool: awi(nkIntLit, rd(bool, x).ord)
of tyChar: awi(nkCharLit, rd(char, x).ord)
of tyInt: awi(nkIntLit, rd(int, x))
of tyInt8: awi(nkInt8Lit, rd(int8, x))
of tyInt16: awi(nkInt16Lit, rd(int16, x))
of tyInt32: awi(nkInt32Lit, rd(int32, x))
of tyInt64: awi(nkInt64Lit, rd(int64, x))
of tyUInt: awi(nkUIntLit, rd(uint, x).BiggestInt)
of tyUInt8: awi(nkUInt8Lit, rd(uint8, x).BiggestInt)
of tyUInt16: awi(nkUInt16Lit, rd(uint16, x).BiggestInt)
of tyUInt32: awi(nkUInt32Lit, rd(uint32, x).BiggestInt)
of tyUInt64: awi(nkUInt64Lit, rd(uint64, x).BiggestInt)
of tyEnum:
case typ.getSize
of 1: awi(nkIntLit, rd(uint8, x).BiggestInt)
of 2: awi(nkIntLit, rd(uint16, x).BiggestInt)
of 4: awi(nkIntLit, rd(int32, x).BiggestInt)
of 8: awi(nkIntLit, rd(int64, x).BiggestInt)
else:
globalError(n.info, "cannot map value from FFI (tyEnum, tySet)")
of tyFloat: awf(nkFloatLit, rd(float, x))
of tyFloat32: awf(nkFloat32Lit, rd(float32, x))
of tyFloat64: awf(nkFloat64Lit, rd(float64, x))
of tyPointer, tyProc:
let p = rd(pointer, x)
if p.isNil:
setNil()
elif n != nil and n.kind == nkStrLit:
# we passed a string literal as a pointer; however strings are already
# in their unboxed representation so nothing it to be unpacked:
result = n
else:
awi(nkPtrLit, cast[TAddress](p))
of tyPtr, tyRef, tyVar:
let p = rd(pointer, x)
if p.isNil:
setNil()
elif n == nil or n.kind == nkPtrLit:
awi(nkPtrLit, cast[TAddress](p))
elif n != nil and n.len == 1:
internalAssert n.kind == nkRefTy
n.sons[0] = unpack(p, typ.lastSon, n.sons[0])
result = n
else:
globalError(n.info, "cannot map value from FFI " & typeToString(typ))
of tyObject, tyTuple:
result = unpackObject(x, typ, n)
of tyArray, tyArrayConstr:
result = unpackArray(x, typ, n)
of tyCString, tyString:
let p = rd(cstring, x)
if p.isNil:
setNil()
else:
aws(nkStrLit, $p)
of tyNil:
setNil()
of tyDistinct, tyGenericInst:
result = unpack(x, typ.sons[0], n)
else:
# XXX what to do with 'array' here?
globalError(n.info, "cannot map value from FFI " & typeToString(typ))
proc fficast*(x: PNode, destTyp: PType): PNode =
if x.kind == nkPtrLit and x.typ.kind in {tyPtr, tyRef, tyVar, tyPointer,
tyProc, tyCString, tyString,
tySequence}:
result = newNodeIT(x.kind, x.info, destTyp)
result.intVal = x.intVal
elif x.kind == nkNilLit:
result = newNodeIT(x.kind, x.info, destTyp)
else:
# we play safe here and allocate the max possible size:
let size = max(packSize(x, x.typ), packSize(x, destTyp))
var a = alloc0(size)
pack(x, x.typ, a)
# cast through a pointer needs a new inner object:
let y = if x.kind == nkRefTy: newNodeI(nkRefTy, x.info, 1)
else: x.copyTree
y.typ = x.typ
result = unpack(a, destTyp, y)
dealloc a
proc callForeignFunction*(call: PNode): PNode =
internalAssert call.sons[0].kind == nkPtrLit
var cif: TCif
var sig: TParamList
# use the arguments' types for varargs support:
for i in 1..call.len-1:
sig[i-1] = mapType(call.sons[i].typ)
if sig[i-1].isNil:
globalError(call.info, "cannot map FFI type")
let typ = call.sons[0].typ
if prep_cif(cif, mapCallConv(typ.callConv, call.info), cuint(call.len-1),
mapType(typ.sons[0]), sig) != OK:
globalError(call.info, "error in FFI call")
var args: TArgList
let fn = cast[pointer](call.sons[0].intVal)
for i in 1 .. call.len-1:
var t = call.sons[i].typ
args[i-1] = alloc0(packSize(call.sons[i], t))
pack(call.sons[i], t, args[i-1])
let retVal = if isEmptyType(typ.sons[0]): pointer(nil)
else: alloc(typ.sons[0].getSize.int)
libffi.call(cif, fn, retVal, args)
if retVal.isNil:
result = emptyNode
else:
result = unpack(retVal, typ.sons[0], nil)
result.info = call.info
if retVal != nil: dealloc retVal
for i in 1 .. call.len-1:
call.sons[i] = unpack(args[i-1], typ.sons[i], call[i])
dealloc args[i-1]
proc callForeignFunction*(fn: PNode, fntyp: PType,
args: var TNodeSeq, start, len: int,
info: TLineInfo): PNode =
internalAssert fn.kind == nkPtrLit
var cif: TCif
var sig: TParamList
for i in 0..len-1:
var aTyp = args[i+start].typ
if aTyp.isNil:
internalAssert i+1 < fntyp.len
aTyp = fntyp.sons[i+1]
args[i+start].typ = aTyp
sig[i] = mapType(aTyp)
if sig[i].isNil: globalError(info, "cannot map FFI type")
if prep_cif(cif, mapCallConv(fntyp.callConv, info), cuint(len),
mapType(fntyp.sons[0]), sig) != OK:
globalError(info, "error in FFI call")
var cargs: TArgList
let fn = cast[pointer](fn.intVal)
for i in 0 .. len-1:
let t = args[i+start].typ
cargs[i] = alloc0(packSize(args[i+start], t))
pack(args[i+start], t, cargs[i])
let retVal = if isEmptyType(fntyp.sons[0]): pointer(nil)
else: alloc(fntyp.sons[0].getSize.int)
libffi.call(cif, fn, retVal, cargs)
if retVal.isNil:
result = emptyNode
else:
result = unpack(retVal, fntyp.sons[0], nil)
result.info = info
if retVal != nil: dealloc retVal
for i in 0 .. len-1:
let t = args[i+start].typ
args[i+start] = unpack(cargs[i], t, args[i+start])
dealloc cargs[i]

View File

@@ -10,7 +10,7 @@
## Template evaluation engine. Now hygienic.
import
strutils, options, ast, astalgo, msgs, os, idents, wordrecg, renderer,
strutils, options, ast, astalgo, msgs, os, idents, wordrecg, renderer,
rodread
type
@@ -29,7 +29,7 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
of nkSym:
var s = templ.sym
if s.owner.id == c.owner.id:
if s.kind == skParam:
if s.kind == skParam and sfGenSym notin s.flags:
let x = actual.sons[s.position]
if x.kind == nkArgList:
for y in items(x): result.add(y)
@@ -49,7 +49,7 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
result.add copyNode(c, templ, actual)
else:
var res = copyNode(c, templ, actual)
for i in countup(0, sonsLen(templ) - 1):
for i in countup(0, sonsLen(templ) - 1):
evalTemplateAux(templ.sons[i], actual, c, res)
result.add res
@@ -86,14 +86,14 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym): PNode =
ctx.owner = tmpl
ctx.genSymOwner = genSymOwner
initIdTable(ctx.mapping)
let body = tmpl.getBody
if isAtom(body):
if isAtom(body):
result = newNodeI(nkPar, body.info)
evalTemplateAux(body, args, ctx, result)
if result.len == 1: result = result.sons[0]
else:
globalError(result.info, errIllFormedAstX,
localError(result.info, errIllFormedAstX,
renderTree(result, {renderNoComments}))
else:
result = copyNode(body)
@@ -102,5 +102,5 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym): PNode =
if ctx.instLines: result.info = n.info
for i in countup(0, safeLen(body) - 1):
evalTemplateAux(body.sons[i], args, ctx, result)
dec(evalTemplateCounter)

View File

@@ -15,9 +15,9 @@
import
lists, ropes, os, strutils, osproc, platform, condsyms, options, msgs, crc
type
TSystemCC* = enum
ccNone, ccGcc, ccLLVM_Gcc, ccCLang, ccLcc, ccBcc, ccDmc, ccWcc, ccVcc,
type
TSystemCC* = enum
ccNone, ccGcc, ccLLVM_Gcc, ccCLang, ccLcc, ccBcc, ccDmc, ccWcc, ccVcc,
ccTcc, ccPcc, ccUcc, ccIcl
TInfoCCProp* = enum # properties of the C compiler:
hasSwitchRange, # CC allows ranges in switch statements (GNU C)
@@ -54,7 +54,7 @@ type
props: TInfoCCProps] # properties of the C compiler
# Configuration settings for various compilers.
# Configuration settings for various compilers.
# When adding new compilers, the cmake sources could be a good reference:
# http://cmake.org/gitweb?p=cmake.git;a=tree;f=Modules/Platform;
@@ -136,7 +136,7 @@ compiler icl:
result = vcc()
else:
result = gcc()
result.name = "icl"
result.compilerExe = "icl"
result.linkerExe = "icl"
@@ -317,7 +317,7 @@ compiler ucc:
packedPragma: "", # XXX: not supported yet
props: {})
const
const
CC*: array[succ(low(TSystemCC))..high(TSystemCC), TInfoCC] = [
gcc(),
llvmGcc(),
@@ -332,7 +332,6 @@ const
ucc(),
icl()]
const
hExt* = ".h"
var
@@ -347,7 +346,7 @@ var
proc libNameTmpl(): string {.inline.} =
result = if targetOS == osWindows: "$1.lib" else: "lib$1.a"
var
var
toLink, toCompile, externalToCompile: TLinkedList
linkOptions: string = ""
compileOptions: string = ""
@@ -356,19 +355,28 @@ var
proc nameToCC*(name: string): TSystemCC =
## Returns the kind of compiler referred to by `name`, or ccNone
## if the name doesn't refer to any known compiler.
for i in countup(succ(ccNone), high(TSystemCC)):
if cmpIgnoreStyle(name, CC[i].name) == 0:
for i in countup(succ(ccNone), high(TSystemCC)):
if cmpIgnoreStyle(name, CC[i].name) == 0:
return i
result = ccNone
proc getConfigVar(c: TSystemCC, suffix: string): string =
# use ``cpu.os.cc`` for cross compilation, unless ``--compileOnly`` is given
# for niminst support
let fullSuffix = (if gCmd == cmdCompileToCpp: ".cpp" & suffix else: suffix)
let fullSuffix =
if gCmd == cmdCompileToCpp:
".cpp" & suffix
elif gCmd == cmdCompileToOC:
".objc" & suffix
elif gCmd == cmdCompileToJS:
".js" & suffix
else:
suffix
if (platform.hostOS != targetOS or platform.hostCPU != targetCPU) and
optCompileOnly notin gGlobalOptions:
let fullCCname = platform.CPU[targetCPU].name & '.' &
platform.OS[targetOS].name & '.' &
let fullCCname = platform.CPU[targetCPU].name & '.' &
platform.OS[targetOS].name & '.' &
CC[c].name & fullSuffix
result = getConfigVar(fullCCname)
if result.len == 0:
@@ -377,39 +385,39 @@ proc getConfigVar(c: TSystemCC, suffix: string): string =
else:
result = getConfigVar(CC[c].name & fullSuffix)
proc setCC*(ccname: string) =
proc setCC*(ccname: string) =
cCompiler = nameToCC(ccname)
if cCompiler == ccNone: rawMessage(errUnknownCcompiler, ccname)
compileOptions = getConfigVar(cCompiler, ".options.always")
linkOptions = getConfigVar(cCompiler, ".options.linker")
linkOptions = ""
ccompilerpath = getConfigVar(cCompiler, ".path")
for i in countup(low(CC), high(CC)): undefSymbol(CC[i].name)
defineSymbol(CC[cCompiler].name)
proc addOpt(dest: var string, src: string) =
proc addOpt(dest: var string, src: string) =
if len(dest) == 0 or dest[len(dest)-1] != ' ': add(dest, " ")
add(dest, src)
proc addLinkOption*(option: string) =
if find(linkOptions, option, 0) < 0: addOpt(linkOptions, option)
proc addLinkOption*(option: string) =
addOpt(linkOptions, option)
proc addCompileOption*(option: string) =
if strutils.find(compileOptions, option, 0) < 0:
proc addCompileOption*(option: string) =
if strutils.find(compileOptions, option, 0) < 0:
addOpt(compileOptions, option)
proc initVars*() =
proc initVars*() =
# we need to define the symbol here, because ``CC`` may have never been set!
for i in countup(low(CC), high(CC)): undefSymbol(CC[i].name)
defineSymbol(CC[cCompiler].name)
addCompileOption(getConfigVar(cCompiler, ".options.always"))
addLinkOption(getConfigVar(cCompiler, ".options.linker"))
#addLinkOption(getConfigVar(cCompiler, ".options.linker"))
if len(ccompilerpath) == 0:
ccompilerpath = getConfigVar(cCompiler, ".path")
proc completeCFilePath*(cfile: string, createSubDir: bool = true): string =
proc completeCFilePath*(cfile: string, createSubDir: bool = true): string =
result = completeGeneratedFilePath(cfile, createSubDir)
proc toObjFile*(filename: string): string =
proc toObjFile*(filename: string): string =
# Object file for compilation
result = changeFileExt(filename, CC[cCompiler].objExt)
@@ -429,30 +437,34 @@ proc addFileToLink*(filename: string) =
prependStr(toLink, filename)
# BUGFIX: was ``appendStr``
proc execExternalProgram*(cmd: string, prettyCmd = "") =
proc execWithEcho(cmd: string, prettyCmd = ""): int =
if optListCmd in gGlobalOptions or gVerbosity > 0:
if prettyCmd != "":
msgWriteln(prettyCmd)
else:
msgWriteln(cmd)
if execCmd(cmd) != 0: rawMessage(errExecutionOfProgramFailed, "")
result = execCmd(cmd)
proc generateScript(projectFile: string, script: PRope) =
proc execExternalProgram*(cmd: string, prettyCmd = "") =
if execWithEcho(cmd, prettyCmd) != 0:
rawMessage(errExecutionOfProgramFailed, "")
proc generateScript(projectFile: string, script: Rope) =
let (dir, name, ext) = splitFile(projectFile)
writeRope(script, dir / addFileExt("compile_" & name,
writeRope(script, dir / addFileExt("compile_" & name,
platform.OS[targetOS].scriptExt))
proc getOptSpeed(c: TSystemCC): string =
proc getOptSpeed(c: TSystemCC): string =
result = getConfigVar(c, ".options.speed")
if result == "":
result = CC[c].optSpeed # use default settings from this file
proc getDebug(c: TSystemCC): string =
proc getDebug(c: TSystemCC): string =
result = getConfigVar(c, ".options.debug")
if result == "":
result = CC[c].debug # use default settings from this file
proc getOptSize(c: TSystemCC): string =
proc getOptSize(c: TSystemCC): string =
result = getConfigVar(c, ".options.size")
if result == "":
result = CC[c].optSize # use default settings from this file
@@ -464,7 +476,7 @@ proc noAbsolutePaths: bool {.inline.} =
# `optGenMapping` is included here for niminst.
result = gGlobalOptions * {optGenScript, optGenMapping} != {}
const
const
specialFileA = 42
specialFileB = 42
@@ -476,7 +488,7 @@ proc add(s: var string, many: openArray[string]) =
proc cFileSpecificOptions(cfilename: string): string =
result = compileOptions
var trunk = splitFile(cfilename).name
if optCDebug in gGlobalOptions:
if optCDebug in gGlobalOptions:
var key = trunk & ".debug"
if existsConfigVar(key): addOpt(result, getConfigVar(key))
else: addOpt(result, getDebug(cCompiler))
@@ -516,17 +528,17 @@ proc getLinkerExe(compiler: TSystemCC): string =
elif gMixedMode and gCmd != cmdCompileToCpp: CC[compiler].cppCompiler
else: compiler.getCompilerExe
proc getCompileCFileCmd*(cfilename: string, isExternal = false): string =
proc getCompileCFileCmd*(cfilename: string, isExternal = false): string =
var c = cCompiler
var options = cFileSpecificOptions(cfilename)
var exe = getConfigVar(c, ".exe")
if exe.len == 0: exe = c.getCompilerExe
if needsExeExt(): exe = addFileExt(exe, "exe")
if optGenDynLib in gGlobalOptions and
ospNeedsPIC in platform.OS[targetOS].props:
add(options, ' ' & CC[c].pic)
var includeCmd, compilePattern: string
if not noAbsolutePaths():
# compute include paths:
@@ -539,7 +551,7 @@ proc getCompileCFileCmd*(cfilename: string, isExternal = false): string =
else:
includeCmd = ""
compilePattern = c.getCompilerExe
var cfile = if noAbsolutePaths(): extractFilename(cfilename)
else: cfilename
var objfile = if not isExternal or noAbsolutePaths():
@@ -547,15 +559,16 @@ proc getCompileCFileCmd*(cfilename: string, isExternal = false): string =
else:
completeCFilePath(toObjFile(cfile))
objfile = quoteShell(objfile)
cfile = quoteShell(cfile)
result = quoteShell(compilePattern % [
"file", cfile, "objfile", objfile, "options", options,
"include", includeCmd, "nimrod", getPrefixDir(),
"include", includeCmd, "nim", getPrefixDir(),
"nim", getPrefixDir(), "lib", libpath])
add(result, ' ')
addf(result, CC[c].compileTmpl, [
"file", cfile, "objfile", objfile,
"options", options, "include", includeCmd,
"nimrod", quoteShell(getPrefixDir()),
"nim", quoteShell(getPrefixDir()),
"nim", quoteShell(getPrefixDir()),
"lib", quoteShell(libpath)])
@@ -567,11 +580,14 @@ proc footprint(filename: string): TCrc32 =
extccomp.CC[extccomp.cCompiler].name ><
getCompileCFileCmd(filename, true)
proc externalFileChanged(filename: string): bool =
proc externalFileChanged(filename: string): bool =
if gCmd notin {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToLLVM}:
return false
var crcFile = toGeneratedFile(filename.withPackageName, "crc")
var currentCrc = int(footprint(filename))
var f: File
if open(f, crcFile, fmRead):
if open(f, crcFile, fmRead):
var line = newStringOfCap(40)
if not f.readLine(line): line = "0"
close(f)
@@ -579,7 +595,7 @@ proc externalFileChanged(filename: string): bool =
result = oldCrc != currentCrc
else:
result = true
if result:
if result:
if open(f, crcFile, fmWrite):
f.writeln($currentCrc)
close(f)
@@ -588,49 +604,51 @@ proc addExternalFileToCompile*(filename: string) =
if optForceFullMake in gGlobalOptions or externalFileChanged(filename):
appendStr(externalToCompile, filename)
proc compileCFile(list: TLinkedList, script: var PRope, cmds: var TStringSeq,
proc compileCFile(list: TLinkedList, script: var Rope, cmds: var TStringSeq,
prettyCmds: var TStringSeq, isExternal: bool) =
var it = PStrEntry(list.head)
while it != nil:
while it != nil:
inc(fileCounter) # call the C compiler for the .c file:
var compileCmd = getCompileCFileCmd(it.data, isExternal)
if optCompileOnly notin gGlobalOptions:
if optCompileOnly notin gGlobalOptions:
add(cmds, compileCmd)
let (dir, name, ext) = splitFile(it.data)
add(prettyCmds, "CC: " & name)
if optGenScript in gGlobalOptions:
app(script, compileCmd)
app(script, tnl)
if optGenScript in gGlobalOptions:
add(script, compileCmd)
add(script, tnl)
it = PStrEntry(it.next)
proc callCCompiler*(projectfile: string) =
var
var
linkCmd, buildgui, builddll: string
if gGlobalOptions * {optCompileOnly, optGenScript} == {optCompileOnly}:
if gGlobalOptions * {optCompileOnly, optGenScript} == {optCompileOnly}:
return # speed up that call if only compiling and no script shall be
# generated
fileCounter = 0
var c = cCompiler
var script: PRope = nil
var script: Rope = nil
var cmds: TStringSeq = @[]
var prettyCmds: TStringSeq = @[]
let prettyCb = proc (idx: int) =
echo prettyCmds[idx]
compileCFile(toCompile, script, cmds, prettyCmds, false)
compileCFile(externalToCompile, script, cmds, prettyCmds, true)
if optCompileOnly notin gGlobalOptions:
if optCompileOnly notin gGlobalOptions:
if gNumberOfProcessors == 0: gNumberOfProcessors = countProcessors()
var res = 0
if gNumberOfProcessors <= 1:
for i in countup(0, high(cmds)): res = max(execCmd(cmds[i]), res)
if gNumberOfProcessors <= 1:
for i in countup(0, high(cmds)):
res = execWithEcho(cmds[i])
if res != 0: rawMessage(errExecutionOfProgramFailed, [])
elif optListCmd in gGlobalOptions or gVerbosity > 1:
res = execProcesses(cmds, {poEchoCmd, poUseShell, poParentStreams},
res = execProcesses(cmds, {poEchoCmd, poUsePath, poParentStreams},
gNumberOfProcessors)
elif gVerbosity == 1:
res = execProcesses(cmds, {poUseShell, poParentStreams},
res = execProcesses(cmds, {poUsePath, poParentStreams},
gNumberOfProcessors, prettyCb)
else:
res = execProcesses(cmds, {poUseShell, poParentStreams},
res = execProcesses(cmds, {poUsePath, poParentStreams},
gNumberOfProcessors)
if res != 0:
if gNumberOfProcessors <= 1:
@@ -667,21 +685,22 @@ proc callCCompiler*(projectfile: string) =
else:
exefile = splitFile(projectfile).name & platform.OS[targetOS].exeExt
builddll = ""
if options.outFile.len > 0:
if options.outFile.len > 0:
exefile = options.outFile.expandTilde
if not noAbsolutePaths():
if not exefile.isAbsolute():
exefile = joinPath(splitFile(projectfile).dir, exefile)
exefile = quoteShell(exefile)
let linkOptions = getLinkOptions()
let linkOptions = getLinkOptions() & " " &
getConfigVar(cCompiler, ".options.linker")
linkCmd = quoteShell(linkCmd % ["builddll", builddll,
"buildgui", buildgui, "options", linkOptions, "objfiles", objfiles,
"exefile", exefile, "nimrod", getPrefixDir(), "lib", libpath])
"exefile", exefile, "nim", getPrefixDir(), "lib", libpath])
linkCmd.add ' '
addf(linkCmd, CC[c].linkTmpl, ["builddll", builddll,
"buildgui", buildgui, "options", linkOptions,
"objfiles", objfiles, "exefile", exefile,
"nimrod", quoteShell(getPrefixDir()),
"nim", quoteShell(getPrefixDir()),
"lib", quoteShell(libpath)])
if optCompileOnly notin gGlobalOptions:
if gVerbosity == 1:
@@ -691,30 +710,30 @@ proc callCCompiler*(projectfile: string) =
else:
linkCmd = ""
if optGenScript in gGlobalOptions:
app(script, linkCmd)
app(script, tnl)
add(script, linkCmd)
add(script, tnl)
generateScript(projectfile, script)
proc genMappingFiles(list: TLinkedList): PRope =
proc genMappingFiles(list: TLinkedList): Rope =
var it = PStrEntry(list.head)
while it != nil:
appf(result, "--file:r\"$1\"$N", [toRope(it.data)])
while it != nil:
addf(result, "--file:r\"$1\"$N", [rope(it.data)])
it = PStrEntry(it.next)
proc writeMapping*(gSymbolMapping: PRope) =
if optGenMapping notin gGlobalOptions: return
var code = toRope("[C_Files]\n")
app(code, genMappingFiles(toCompile))
app(code, genMappingFiles(externalToCompile))
app(code, "\n[C_Compiler]\nFlags=")
app(code, strutils.escape(getCompileOptions()))
app(code, "\n[Linker]\nFlags=")
app(code, strutils.escape(getLinkOptions()))
proc writeMapping*(gSymbolMapping: Rope) =
if optGenMapping notin gGlobalOptions: return
var code = rope("[C_Files]\n")
add(code, genMappingFiles(toCompile))
add(code, genMappingFiles(externalToCompile))
add(code, "\n[C_Compiler]\nFlags=")
add(code, strutils.escape(getCompileOptions()))
app(code, "\n[Environment]\nlibpath=")
app(code, strutils.escape(libpath))
appf(code, "\n[Symbols]$n$1", [gSymbolMapping])
add(code, "\n[Linker]\nFlags=")
add(code, strutils.escape(getLinkOptions() & " " &
getConfigVar(cCompiler, ".options.linker")))
add(code, "\n[Environment]\nlibpath=")
add(code, strutils.escape(libpath))
addf(code, "\n[Symbols]$n$1", [gSymbolMapping])
writeRope(code, joinPath(gProjectPath, "mapping.txt"))

View File

@@ -37,11 +37,11 @@ const
PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF', '.', '_'}
proc newLine(p: var TTmplParser) =
llStreamWrite(p.outp, repeatChar(p.emitPar, ')'))
llStreamWrite(p.outp, repeat(')', p.emitPar))
p.emitPar = 0
if p.info.line > int16(1): llStreamWrite(p.outp, "\n")
if p.pendingExprLine:
llStreamWrite(p.outp, repeatChar(2))
llStreamWrite(p.outp, spaces(2))
p.pendingExprLine = false
proc scanPar(p: var TTmplParser, d: int) =
@@ -88,24 +88,24 @@ proc parseLine(p: var TTmplParser) =
else:
p.info.col = int16(j)
localError(p.info, errXNotAllowedHere, "end")
llStreamWrite(p.outp, repeatChar(p.indent))
llStreamWrite(p.outp, spaces(p.indent))
llStreamWrite(p.outp, "#end")
of wIf, wWhen, wTry, wWhile, wFor, wBlock, wCase, wProc, wIterator,
wConverter, wMacro, wTemplate, wMethod:
llStreamWrite(p.outp, repeatChar(p.indent))
llStreamWrite(p.outp, spaces(p.indent))
llStreamWrite(p.outp, substr(p.x, d))
inc(p.indent, 2)
of wElif, wOf, wElse, wExcept, wFinally:
llStreamWrite(p.outp, repeatChar(p.indent - 2))
llStreamWrite(p.outp, spaces(p.indent - 2))
llStreamWrite(p.outp, substr(p.x, d))
of wLet, wVar, wConst, wType:
llStreamWrite(p.outp, repeatChar(p.indent))
llStreamWrite(p.outp, spaces(p.indent))
llStreamWrite(p.outp, substr(p.x, d))
if not p.x.contains({':', '='}):
# no inline element --> treat as block:
inc(p.indent, 2)
else:
llStreamWrite(p.outp, repeatChar(p.indent))
llStreamWrite(p.outp, spaces(p.indent))
llStreamWrite(p.outp, substr(p.x, d))
p.state = psDirective
else:
@@ -120,11 +120,11 @@ proc parseLine(p: var TTmplParser) =
# next line of string literal:
llStreamWrite(p.outp, p.conc)
llStreamWrite(p.outp, "\n")
llStreamWrite(p.outp, repeatChar(p.indent + 2))
llStreamWrite(p.outp, spaces(p.indent + 2))
llStreamWrite(p.outp, "\"")
of psDirective:
newLine(p)
llStreamWrite(p.outp, repeatChar(p.indent))
llStreamWrite(p.outp, spaces(p.indent))
llStreamWrite(p.outp, p.emit)
llStreamWrite(p.outp, "(\"")
inc(p.emitPar)

89
compiler/forloops.nim Normal file
View File

@@ -0,0 +1,89 @@
#
#
# The Nim Compiler
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This module implements for loop detection for better C code generation.
import ast, astalgo
const
someCmp = {mEqI, mEqI64, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc,
mEqUntracedRef, mLeI, mLeI64, mLeF64, mLeU, mLeU64, mLeEnum,
mLeCh, mLeB, mLePtr, mLtI, mLtI64, mLtF64, mLtU, mLtU64, mLtEnum,
mLtCh, mLtB, mLtPtr}
proc isCounter(s: PSym): bool {.inline.} =
s.kind in {skResult, skVar, skLet, skTemp} and
{sfGlobal, sfAddrTaken} * s.flags == {}
proc isCall(n: PNode): bool {.inline.} =
n.kind in nkCallKinds and n[0].kind == nkSym
proc fromSystem(op: PSym): bool = sfSystemModule in getModule(op).flags
proc getCounter(lastStmt: PNode): PSym =
if lastStmt.isCall:
let op = lastStmt.sym
if op.magic in {mDec, mInc} or
((op.name.s == "+=" or op.name.s == "-=") and op.fromSystem):
if op[1].kind == nkSym and isCounter(op[1].sym):
result = op[1].sym
proc counterInTree(n, loop: PNode; counter: PSym): bool =
# prune the search tree: within the loop the counter may be used:
if n == loop: return
case n.kind
of nkSym:
if n.sym == counter: return true
of nkVarSection, nkLetSection:
# definitions are fine!
for it in n:
if counterInTree(it.lastSon): return true
else:
for i in 0 .. <safeLen(n):
if counterInTree(n[i], loop, counter): return true
proc copyExcept(n: PNode, x, dest: PNode) =
if x == n: return
if n.kind in {nkStmtList, nkStmtListExpr}:
for i in 0 .. <n.len: copyExcept(n[i], x, dest)
else:
dest.add n
type
ForLoop* = object
counter*: PSym
init*, cond*, increment*, body*: PNode
proc extractForLoop*(loop, fullTree: PNode): ForLoop =
## returns 'counter == nil' if the while loop 'n' is not a for loop:
assert loop.kind == nkWhileStmt
let cond == loop[0]
if not cond.isCall: return
if cond[0].sym.magic notin someCmp: return
var lastStmt = loop[1]
while lastStmt.kind in {nkStmtList, nkStmtListExpr}:
lastStmt = lastStmt.lastSon
let counter = getCounter(lastStmt)
if counter.isNil or counter.ast.isNil: return
template `=~`(a, b): expr = a.kind == nkSym and a.sym == b
if cond[1] =~ counter or cond[2] =~ counter:
# ok, now check 'counter' is not used *after* the loop
if counterInTree(fullTree, loop, counter): return
# ok, success, fill in the fields:
result.counter = counter
result.init = counter.ast
result.cond = cond
result.increment = lastStmt
result.body = newNodeI(nkStmtList, loop[1].info)
copyExcept(loop[1], lastStmt, result.body)

View File

@@ -1,7 +1,7 @@
#
#
# The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -15,14 +15,15 @@ import ast, astalgo, msgs, magicsys, nimsets, trees, types, renderer, idents,
const
someEq = {mEqI, mEqI64, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc,
mEqUntracedRef, mEqStr, mEqSet, mEqCString}
# set excluded here as the semantics are vastly different:
someLe = {mLeI, mLeI64, mLeF64, mLeU, mLeU64, mLeEnum,
mLeCh, mLeB, mLePtr, mLeStr}
someLt = {mLtI, mLtI64, mLtF64, mLtU, mLtU64, mLtEnum,
someLt = {mLtI, mLtI64, mLtF64, mLtU, mLtU64, mLtEnum,
mLtCh, mLtB, mLtPtr, mLtStr}
someLen = {mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq}
someLen = {mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq,
mXLenStr, mXLenSeq}
someIn = {mInRange, mInSet}
@@ -34,8 +35,8 @@ const
someMul = {mMulI, mMulI64, mMulF64}
someDiv = {mDivI, mDivI64, mDivF64}
someMod = {mModI, mModI64}
someMax = {mMaxI, mMaxI64, mMaxF64}
someMin = {mMinI, mMinI64, mMinF64}
someMax = {mMaxI, mMaxF64}
someMin = {mMinI, mMinF64}
proc isValue(n: PNode): bool = n.kind in {nkCharLit..nkNilLit}
proc isLocation(n: PNode): bool = not n.isValue
@@ -44,7 +45,7 @@ proc isLet(n: PNode): bool =
if n.kind == nkSym:
if n.sym.kind in {skLet, skTemp, skForVar}:
result = true
elif n.sym.kind == skParam and skipTypes(n.sym.typ,
elif n.sym.kind == skParam and skipTypes(n.sym.typ,
abstractInst).kind != tyVar:
result = true
@@ -81,18 +82,12 @@ proc isLetLocation(m: PNode, isApprox: bool): bool =
proc interestingCaseExpr*(m: PNode): bool = isLetLocation(m, true)
proc createMagic*(name: string, m: TMagic): PSym =
result = newSym(skProc, getIdent(name), nil, unknownLineInfo())
result.magic = m
let
opLe = createMagic("<=", mLeI)
opLt = createMagic("<", mLtI)
opAnd = createMagic("and", mAnd)
opOr = createMagic("or", mOr)
opNot = createMagic("not", mNot)
opIsNil = createMagic("isnil", mIsNil)
opContains = createMagic("contains", mInSet)
opEq = createMagic("==", mEqI)
opAdd = createMagic("+", mAddI)
opSub = createMagic("-", mSubI)
@@ -123,12 +118,12 @@ proc neg(n: PNode): PNode =
result.sons[0] = n.sons[0]
result.sons[2] = n.sons[2]
if t.kind == tyEnum:
var s = newNodeIT(nkCurly, n.info, n.sons[1].typ)
var s = newNodeIT(nkCurly, n.info, n.sons[1].typ)
for e in t.n:
let eAsNode = newIntNode(nkIntLit, e.sym.position)
if not inSet(n.sons[1], eAsNode): s.add eAsNode
result.sons[1] = s
elif lengthOrd(t) < 1000:
elif t.kind notin {tyString, tySequence} and lengthOrd(t) < 1000:
result.sons[1] = complement(n.sons[1])
else:
# not ({2, 3, 4}.contains(x)) x != 2 and x != 3 and x != 4
@@ -195,13 +190,17 @@ proc zero(): PNode = nkIntLit.newIntNode(0)
proc one(): PNode = nkIntLit.newIntNode(1)
proc minusOne(): PNode = nkIntLit.newIntNode(-1)
proc lowBound*(x: PNode): PNode =
proc lowBound*(x: PNode): PNode =
result = nkIntLit.newIntNode(firstOrd(x.typ))
result.info = x.info
proc highBound*(x: PNode): PNode =
result = if x.typ.skipTypes(abstractInst).kind == tyArray:
nkIntLit.newIntNode(lastOrd(x.typ))
let typ = x.typ.skipTypes(abstractInst)
result = if typ.kind in {tyArrayConstr, tyArray}:
nkIntLit.newIntNode(lastOrd(typ))
elif typ.kind == tySequence and x.kind == nkSym and
x.sym.kind == skConst:
nkIntLit.newIntNode(x.sym.ast.len-1)
else:
opAdd.buildCall(opLen.buildCall(x), minusOne())
result.info = x.info
@@ -211,21 +210,32 @@ proc reassociation(n: PNode): PNode =
# (foo+5)+5 --> foo+10; same for '*'
case result.getMagic
of someAdd:
if result[2].isValue and
if result[2].isValue and
result[1].getMagic in someAdd and result[1][2].isValue:
result = opAdd.buildCall(result[1][1], result[1][2] |+| result[2])
of someMul:
if result[2].isValue and
if result[2].isValue and
result[1].getMagic in someMul and result[1][2].isValue:
result = opAdd.buildCall(result[1][1], result[1][2] |*| result[2])
else: discard
proc pred(n: PNode): PNode =
if n.kind in {nkCharLit..nkUInt64Lit} and n.intVal != low(BiggestInt):
result = copyNode(n)
dec result.intVal
else:
result = n
proc canon*(n: PNode): PNode =
# XXX for now only the new code in 'semparallel' uses this
if n.safeLen >= 1:
result = shallowCopy(n)
for i in 0 .. < n.len:
result.sons[i] = canon(n.sons[i])
elif n.kind == nkSym and n.sym.kind == skLet and
n.sym.ast.getMagic in (someEq + someAdd + someMul + someMin +
someMax + someHigh + {mUnaryLt} + someSub + someLen):
result = n.sym.ast.copyTree
else:
result = n
case result.getMagic
@@ -237,34 +247,40 @@ proc canon*(n: PNode): PNode =
of someHigh:
# high == len+(-1)
result = opAdd.buildCall(opLen.buildCall(result[1]), minusOne())
of mUnaryMinusI, mUnaryMinusI64:
of mUnaryLt:
result = buildCall(opAdd, result[1], newIntNode(nkIntLit, -1))
of someSub:
# x - 4 --> x + (-4)
result = negate(result[1], result[2], result)
of someLen:
result.sons[0] = opLen.newSymNode
of someLt:
# x < y same as x <= y-1:
let y = n[2].canon
let p = pred(y)
let minus = if p != y: p else: opAdd.buildCall(y, minusOne()).canon
result = opLe.buildCall(n[1].canon, minus)
else: discard
result = skipConv(result)
result = reassociation(result)
# most important rule: (x-4) < a.len --> x < a.len+4
# most important rule: (x-4) <= a.len --> x <= a.len+4
case result.getMagic
of someLe, someLt:
of someLe:
let x = result[1]
let y = result[2]
if x.kind in nkCallKinds and x.len == 3 and x[2].isValue and
if x.kind in nkCallKinds and x.len == 3 and x[2].isValue and
isLetLocation(x[1], true):
case x.getMagic
of someSub:
result = buildCall(result[0].sym, x[1],
result = buildCall(result[0].sym, x[1],
reassociation(opAdd.buildCall(y, x[2])))
of someAdd:
# Rule A:
let plus = negate(y, x[2], nil).reassociation
if plus != nil: result = buildCall(result[0].sym, x[1], plus)
else: discard
elif y.kind in nkCallKinds and y.len == 3 and y[2].isValue and
elif y.kind in nkCallKinds and y.len == 3 and y[2].isValue and
isLetLocation(y[1], true):
# a.len < x-3
case y.getMagic
@@ -323,7 +339,7 @@ proc usefulFact(n: PNode): PNode =
of mOr:
# 'or' sucks! (p.isNil or q.isNil) --> hard to do anything
# with that knowledge...
# DeMorgan helps a little though:
# DeMorgan helps a little though:
# not a or not b --> not (a and b)
# (x == 3) or (y == 2) ---> not ( not (x==3) and not (y == 2))
# not (x != 3 and y != 2)
@@ -354,17 +370,32 @@ proc addFact*(m: var TModel, n: PNode) =
let n = usefulFact(n)
if n != nil: m.add n
proc addFactNeg*(m: var TModel, n: PNode) =
proc addFactNeg*(m: var TModel, n: PNode) =
let n = n.neg
if n != nil: addFact(m, n)
proc sameTree*(a, b: PNode): bool =
proc canonOpr(opr: PSym): PSym =
case opr.magic
of someEq: result = opEq
of someLe: result = opLe
of someLt: result = opLt
of someLen: result = opLen
of someAdd: result = opAdd
of someSub: result = opSub
of someMul: result = opMul
of someDiv: result = opDiv
else: result = opr
proc sameTree*(a, b: PNode): bool =
result = false
if a == b:
result = true
elif (a != nil) and (b != nil) and (a.kind == b.kind):
elif a != nil and b != nil and a.kind == b.kind:
case a.kind
of nkSym: result = a.sym == b.sym
of nkSym:
result = a.sym == b.sym
if not result and a.sym.magic != mNone:
result = a.sym.magic == b.sym.magic or canonOpr(a.sym) == canonOpr(b.sym)
of nkIdent: result = a.ident.id == b.ident.id
of nkCharLit..nkInt64Lit: result = a.intVal == b.intVal
of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal
@@ -388,8 +419,8 @@ proc invalidateFacts*(m: var TModel, n: PNode) =
# 'while p != nil: f(p); p = p.next'
# This is actually quite easy to do:
# Re-assignments (incl. pass to a 'var' param) trigger an invalidation
# of every fact that contains 'v'.
#
# of every fact that contains 'v'.
#
# if x < 4:
# if y < 5
# x = unknown()
@@ -408,16 +439,9 @@ proc valuesUnequal(a, b: PNode): bool =
if a.isValue and b.isValue:
result = not sameValue(a, b)
proc pred(n: PNode): PNode =
if n.kind in {nkCharLit..nkUInt64Lit} and n.intVal != low(BiggestInt):
result = copyNode(n)
dec result.intVal
else:
result = n
proc impliesEq(fact, eq: PNode): TImplication =
let (loc, val) = if isLocation(eq.sons[1]): (1, 2) else: (2, 1)
case fact.sons[0].sym.magic
of someEq:
if sameTree(fact.sons[1], eq.sons[loc]):
@@ -434,12 +458,12 @@ proc impliesEq(fact, eq: PNode): TImplication =
else: result = impNo
of mNot, mOr, mAnd: internalError(eq.info, "impliesEq")
else: discard
proc leImpliesIn(x, c, aSet: PNode): TImplication =
if c.kind in {nkCharLit..nkUInt64Lit}:
# fact: x <= 4; question x in {56}?
# --> true if every value <= 4 is in the set {56}
#
#
var value = newIntNode(c.kind, firstOrd(x.typ))
# don't iterate too often:
if c.intVal - value.intVal < 1000:
@@ -455,7 +479,7 @@ proc geImpliesIn(x, c, aSet: PNode): TImplication =
if c.kind in {nkCharLit..nkUInt64Lit}:
# fact: x >= 4; question x in {56}?
# --> true iff every value >= 4 is in the set {56}
#
#
var value = newIntNode(c.kind, c.intVal)
let max = lastOrd(x.typ)
# don't iterate too often:
@@ -574,19 +598,19 @@ proc impliesLe(fact, x, c: PNode): TImplication =
# fact: x < 4; question x <= 2? --> we don't know
elif sameTree(fact.sons[2], x):
# fact: 3 < x; question: x <= 1 ? --> false iff 1 <= 3
if isValue(fact.sons[1]) and isValue(c):
if isValue(fact.sons[1]) and isValue(c):
if leValue(c, fact.sons[1]): result = impNo
of someLe:
if sameTree(fact.sons[1], x):
if isValue(fact.sons[2]) and isValue(c):
# fact: x <= 4; question x <= 56? --> true iff 4 <= 56
if leValue(fact.sons[2], c): result = impYes
# fact: x <= 4; question x <= 2? --> we don't know
elif sameTree(fact.sons[2], x):
# fact: 3 <= x; question: x <= 2 ? --> false iff 2 < 3
if isValue(fact.sons[1]) and isValue(c):
if isValue(fact.sons[1]) and isValue(c):
if leValue(c, fact.sons[1].pred): result = impNo
of mNot, mOr, mAnd: internalError(x.info, "impliesLe")
@@ -620,7 +644,7 @@ proc factImplies(fact, prop: PNode): TImplication =
# it's provably wrong if every value > 4 is in the set {56}
# That's because we compute the implication and 'a -> not b' cannot
# be treated the same as 'not a -> b'
# (not a) -> b compute as not (a -> b) ???
# == not a or not b == not (a and b)
let arg = fact.sons[1]
@@ -635,13 +659,13 @@ proc factImplies(fact, prop: PNode): TImplication =
if a == b: return ~a
return impUnknown
else:
internalError(fact.info, "invalid fact")
return impUnknown
of mAnd:
result = factImplies(fact.sons[1], prop)
if result != impUnknown: return result
return factImplies(fact.sons[2], prop)
else: discard
case prop.sons[0].sym.magic
of mNot: result = ~fact.factImplies(prop.sons[1])
of mIsNil: result = impliesIsNil(fact, prop)
@@ -649,7 +673,7 @@ proc factImplies(fact, prop: PNode): TImplication =
of someLe: result = impliesLe(fact, prop.sons[1], prop.sons[2])
of someLt: result = impliesLt(fact, prop.sons[1], prop.sons[2])
of mInSet: result = impliesIn(fact, prop.sons[2], prop.sons[1])
else: internalError(prop.info, "invalid proposition")
else: result = impUnknown
proc doesImply*(facts: TModel, prop: PNode): TImplication =
assert prop.kind in nkCallKinds
@@ -677,6 +701,7 @@ proc pleViaModel(model: TModel; aa, bb: PNode): TImplication
proc ple(m: TModel; a, b: PNode): TImplication =
template `<=?`(a,b): expr = ple(m,a,b) == impYes
# 0 <= 3
if a.isValue and b.isValue:
return if leValue(a, b): impYes else: impNo
@@ -750,16 +775,21 @@ proc pleViaModelRec(m: var TModel; a, b: PNode): TImplication =
# mark as used:
m[i] = nil
if ple(m, a, x) == impYes:
if ple(m, y, b) == impYes: return impYes
if ple(m, y, b) == impYes:
return impYes
#if pleViaModelRec(m, y, b): return impYes
# fact: 16 <= i
# x y
# question: i <= 15? no!
result = impliesLe(fact, a, b)
if result != impUnknown: return result
if sameTree(y, a):
result = ple(m, b, x)
if result != impUnknown: return result
if result != impUnknown:
return result
when false:
# given: x <= y; y==a; x <= a this means: a <= b if x <= b
if sameTree(y, a):
result = ple(m, b, x)
if result != impUnknown:
return result
proc pleViaModel(model: TModel; aa, bb: PNode): TImplication =
# compute replacements:
@@ -878,5 +908,5 @@ proc buildProperFieldCheck(access, check: PNode): PNode =
proc checkFieldAccess*(m: TModel, n: PNode) =
for i in 1..n.len-1:
let check = buildProperFieldCheck(n.sons[0], n.sons[i])
if m.doesImply(check) != impYes:
if check != nil and m.doesImply(check) != impYes:
message(n.info, warnProveField, renderTree(n.sons[0])); break

View File

@@ -25,7 +25,7 @@ type
next*: PIdent # for hash-table chaining
h*: THash # hash value of s
var firstCharIsCS*: bool
var firstCharIsCS*: bool = true
var buckets*: array[0..4096 * 2 - 1, PIdent]
proc cmpIgnoreStyle(a, b: cstring, blen: int): int =

View File

@@ -9,7 +9,7 @@
# This module implements the symbol importing mechanism.
import
import
intsets, strutils, os, ast, astalgo, msgs, options, idents, rodread, lookups,
semdata, passes, renderer
@@ -27,7 +27,7 @@ proc getModuleName*(n: PNode): string =
result = n.ident.s
of nkSym:
result = n.sym.name.s
of nkInfix:
of nkInfix, nkPrefix:
if n.sons[0].kind == nkIdent and n.sons[0].ident.id == getIdent("as").id:
# XXX hack ahead:
n.kind = nkImportAs
@@ -73,12 +73,12 @@ proc rawImportSymbol(c: PContext, s: PSym) =
if etyp.kind in {tyBool, tyEnum} and sfPure notin s.flags:
for j in countup(0, sonsLen(etyp.n) - 1):
var e = etyp.n.sons[j].sym
if e.kind != skEnumField:
internalError(s.info, "rawImportSymbol")
if e.kind != skEnumField:
internalError(s.info, "rawImportSymbol")
# BUGFIX: because of aliases for enums the symbol may already
# have been put into the symbol table
# BUGFIX: but only iff they are the same symbols!
var it: TIdentIter
var it: TIdentIter
check = initIdentIter(it, c.importTable.symbols, e.name)
while check != nil:
if check.id == e.id:
@@ -92,7 +92,7 @@ proc rawImportSymbol(c: PContext, s: PSym) =
if s.kind == skConverter: addConverter(c, s)
if hasPattern(s): addPattern(c, s)
proc importSymbol(c: PContext, n: PNode, fromMod: PSym) =
proc importSymbol(c: PContext, n: PNode, fromMod: PSym) =
let ident = lookups.considerQuotedIdent(n)
let s = strTableGet(fromMod.tab, ident)
if s == nil:
@@ -143,7 +143,7 @@ proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet) =
of nkExportExceptStmt:
localError(n.info, errGenerated, "'export except' not implemented")
else:
for i in 0 ..safeLen(n)-1:
for i in 0..safeLen(n)-1:
importForwarded(c, n.sons[i], exceptSet)
proc importModuleAs(n: PNode, realModule: PSym): PSym =
@@ -155,7 +155,7 @@ proc importModuleAs(n: PNode, realModule: PSym): PSym =
# some misguided guy will write 'import abc.foo as foo' ...
result = createModuleAlias(realModule, n.sons[1].ident, realModule.info)
proc myImportModule(c: PContext, n: PNode): PSym =
proc myImportModule(c: PContext, n: PNode): PSym =
var f = checkModuleName(n)
if f != InvalidFileIDX:
result = importModuleAs(n, gImportModule(c.module, f))
@@ -164,10 +164,10 @@ proc myImportModule(c: PContext, n: PNode): PSym =
if sfDeprecated in result.flags:
message(n.info, warnDeprecated, result.name.s)
proc evalImport(c: PContext, n: PNode): PNode =
proc evalImport(c: PContext, n: PNode): PNode =
result = n
var emptySet: IntSet
for i in countup(0, sonsLen(n) - 1):
for i in countup(0, sonsLen(n) - 1):
var m = myImportModule(c, n.sons[i])
if m != nil:
# ``addDecl`` needs to be done before ``importAllSymbols``!
@@ -175,7 +175,7 @@ proc evalImport(c: PContext, n: PNode): PNode =
importAllSymbolsExcept(c, m, emptySet)
#importForwarded(c, m.ast, emptySet)
proc evalFrom(c: PContext, n: PNode): PNode =
proc evalFrom(c: PContext, n: PNode): PNode =
result = n
checkMinSonsLen(n, 2)
var m = myImportModule(c, n.sons[0])
@@ -186,7 +186,7 @@ proc evalFrom(c: PContext, n: PNode): PNode =
if n.sons[i].kind != nkNilLit:
importSymbol(c, n.sons[i], m)
proc evalImportExcept*(c: PContext, n: PNode): PNode =
proc evalImportExcept*(c: PContext, n: PNode): PNode =
result = n
checkMinSonsLen(n, 2)
var m = myImportModule(c, n.sons[0])
@@ -194,7 +194,7 @@ proc evalImportExcept*(c: PContext, n: PNode): PNode =
n.sons[0] = newSymNode(m)
addDecl(c, m) # add symbol to symbol table of module
var exceptSet = initIntSet()
for i in countup(1, sonsLen(n) - 1):
for i in countup(1, sonsLen(n) - 1):
let ident = lookups.considerQuotedIdent(n.sons[i])
exceptSet.incl(ident.id)
importAllSymbolsExcept(c, m, exceptSet)

View File

@@ -51,23 +51,35 @@ Files: "configure;makefile"
Files: "*.ini"
Files: "koch.nim"
Files: "icons/nimrod.ico"
Files: "icons/nimrod.rc"
Files: "icons/nimrod.res"
Files: "icons/nimrod_icon.o"
Files: "icons/nim.ico"
Files: "icons/nim.rc"
Files: "icons/nim.res"
Files: "icons/nim_icon.o"
Files: "icons/koch.ico"
Files: "icons/koch.rc"
Files: "icons/koch.res"
Files: "icons/koch_icon.o"
Files: "compiler/readme.txt"
Files: "compiler/nim.ini"
Files: "compiler/nim.nimrod.cfg"
Files: "compiler/installer.ini"
Files: "compiler/nim.nim.cfg"
Files: "compiler/*.nim"
Files: "doc/*.txt"
Files: "doc/manual/*.txt"
Files: "doc/*.nim"
Files: "doc/*.cfg"
Files: "compiler/nimfix/*.nim"
Files: "compiler/nimfix/*.cfg"
Files: "tools/*.nim"
Files: "tools/*.cfg"
Files: "tools/*.tmpl"
Files: "tools/niminst/*.nim"
Files: "tools/niminst/*.cfg"
Files: "tools/niminst/*.tmpl"
Files: "tools/niminst/*.nsh"
Files: "web/website.ini"
Files: "web/*.nim"
Files: "web/*.txt"
[Lib]
Files: "lib/nimbase.h"
@@ -77,8 +89,11 @@ Files: "lib/*.cfg"
Files: "lib/system/*.nim"
Files: "lib/core/*.nim"
Files: "lib/pure/*.nim"
Files: "lib/pure/*.cfg"
Files: "lib/pure/collections/*.nim"
Files: "lib/pure/concurrency/*.nim"
Files: "lib/pure/unidecode/*.nim"
Files: "lib/pure/concurrency/*.cfg"
Files: "lib/impure/*.nim"
Files: "lib/wrappers/*.nim"
@@ -111,12 +126,109 @@ Files: "examples/*.txt"
Files: "examples/*.cfg"
Files: "examples/*.tmpl"
Files: "tests/actiontable/*.nim"
Files: "tests/alias/*.nim"
Files: "tests/ambsym/*.nim"
Files: "tests/array/*.nim"
Files: "tests/assign/*.nim"
Files: "tests/astoverload/*.nim"
Files: "tests/async/*.nim"
Files: "tests/benchmarks/*.nim"
Files: "tests/bind/*.nim"
Files: "tests/borrow/*.nim"
Files: "tests/casestmt/*.nim"
Files: "tests/ccgbugs/*.nim"
Files: "tests/clearmsg/*.nim"
Files: "tests/closure/*.nim"
Files: "tests/cnstseq/*.nim"
Files: "tests/collections/*.nim"
Files: "tests/compiles/*.nim"
Files: "tests/concat/*.nim"
Files: "tests/concepts/*.nim"
Files: "tests/constr/*.nim"
Files: "tests/constraints/*.nim"
Files: "tests/controlflow/*.nim"
Files: "tests/converter/*.nim"
Files: "tests/cpp/*.nim"
Files: "tests/defaultprocparam/*.nim"
Files: "tests/deprecated/*.nim"
Files: "tests/destructor/*.nim"
Files: "tests/dir with space/*.nim"
Files: "tests/discard/*.nim"
Files: "tests/distinct/*.nim"
Files: "tests/dll/*.nim"
Files: "tests/effects/*.nim"
Files: "tests/enum/*.nim"
Files: "tests/exception/*.nim"
Files: "tests/exprs/*.nim"
Files: "tests/fields/*.nim"
Files: "tests/float/*.nim"
Files: "tests/friends/*.nim"
Files: "tests/gc/*.nim"
Files: "tests/generics/*.nim"
Files: "tests/gensym/*.nim"
Files: "tests/global/*.nim"
Files: "tests/implicit/*.nim"
Files: "tests/init/*.nim"
Files: "tests/iter/*.nim"
Files: "tests/js/*.nim"
Files: "tests/js/*.cfg"
Files: "tests/let/*.nim"
Files: "tests/lexer/*.nim"
Files: "tests/lookups/*.nim"
Files: "tests/macros/*.nim"
Files: "tests/magics/*.nim"
Files: "tests/metatype/*.nim"
Files: "tests/method/*.nim"
Files: "tests/misc/*.nim"
Files: "tests/modules/*.nim"
Files: "tests/namedparams/*.nim"
Files: "tests/notnil/*.nim"
Files: "tests/objects/*.nim"
Files: "tests/objvariant/*.nim"
Files: "tests/openarray/*.nim"
Files: "tests/osproc/*.nim"
Files: "tests/overflw/*.nim"
Files: "tests/overload/*.nim"
Files: "tests/parallel/*.nim"
Files: "tests/parallel/*.cfg"
Files: "tests/parser/*.nim"
Files: "tests/pragmas/*.nim"
Files: "tests/proc/*.nim"
Files: "tests/procvar/*.nim"
Files: "tests/range/*.nim"
Files: "tests/rodfiles/*.nim"
Files: "tests/seq/*.nim"
Files: "tests/sets/*.nim"
Files: "tests/showoff/*.nim"
Files: "tests/specialops/*.nim"
Files: "tests/stdlib/*.nim"
Files: "tests/system/*.nim"
Files: "tests/template/*.nim"
Files: "tests/testament/*.nim"
Files: "tests/testdata/*.nim"
Files: "tests/threads/*.nim"
Files: "tests/threads/*.cfg"
Files: "tests/trmacros/*.nim"
Files: "tests/tuples/*.nim"
Files: "tests/typerel/*.nim"
Files: "tests/types/*.nim"
Files: "tests/usingstmt/*.nim"
Files: "tests/varres/*.nim"
Files: "tests/varstmt/*.nim"
Files: "tests/vm/*.nim"
Files: "tests/readme.txt"
Files: "tests/testament/css/*.css"
Files: "tests/testament/*.cfg"
Files: "lib/pure/unidecode/unidecode.dat"
[Windows]
Files: "bin/nim.exe"
Files: "bin/nim_debug.exe"
Files: "bin/c2nim.exe"
Files: "bin/nimgrep.exe"
Files: "bin/nimsuggest.exe"
Files: "bin/nimble.exe"
Files: "bin/*.dll"
Files: "dist/*.dll"
Files: "koch.exe"
@@ -125,9 +237,9 @@ Files: "start.bat"
BinPath: r"bin;dist\mingw\bin;dist"
; Section | dir | zipFile | size hint (in KB) | url | exe start menu entry
Download: r"Documentation|doc|docs.zip|13824|http://nim-lang.org/download/docs-${version}.zip|doc\overview.html"
Download: r"Documentation|doc|docs.zip|13824|http://nim-lang.org/download/docs-${version}.zip|overview.html"
Download: r"C Compiler (MingW)|dist|mingw.zip|82944|http://nim-lang.org/download/${mingw}.zip"
Download: r"Aporia IDE|dist|aporia.zip|97997|http://nim-lang.org/download/aporia-0.1.3.zip|aporia\bin\aporia.exe"
Download: r"Aporia IDE|dist|aporia.zip|97997|http://nim-lang.org/download/aporia-0.3.0.zip|aporia\bin\aporia.exe"
; for now only NSIS supports optional downloads
[UnixBin]

File diff suppressed because it is too large Load Diff

View File

@@ -9,138 +9,138 @@
## Type info generation for the JS backend.
proc genTypeInfo(p: PProc, typ: PType): PRope
proc genObjectFields(p: PProc, typ: PType, n: PNode): PRope =
var
s, u: PRope
proc genTypeInfo(p: PProc, typ: PType): Rope
proc genObjectFields(p: PProc, typ: PType, n: PNode): Rope =
var
s, u: Rope
length: int
field: PSym
b: PNode
result = nil
case n.kind
of nkRecList:
of nkRecList:
length = sonsLen(n)
if length == 1:
if length == 1:
result = genObjectFields(p, typ, n.sons[0])
else:
else:
s = nil
for i in countup(0, length - 1):
if i > 0: app(s, ", " & tnl)
app(s, genObjectFields(p, typ, n.sons[i]))
result = ropef("{kind: 2, len: $1, offset: 0, " &
"typ: null, name: null, sons: [$2]}", [toRope(length), s])
of nkSym:
for i in countup(0, length - 1):
if i > 0: add(s, ", " & tnl)
add(s, genObjectFields(p, typ, n.sons[i]))
result = ("{kind: 2, len: $1, offset: 0, " &
"typ: null, name: null, sons: [$2]}") % [rope(length), s]
of nkSym:
field = n.sym
s = genTypeInfo(p, field.typ)
result = ropef("{kind: 1, offset: \"$1\", len: 0, " &
"typ: $2, name: $3, sons: null}",
[mangleName(field), s, makeJSString(field.name.s)])
of nkRecCase:
result = ("{kind: 1, offset: \"$1\", len: 0, " &
"typ: $2, name: $3, sons: null}") %
[mangleName(field), s, makeJSString(field.name.s)]
of nkRecCase:
length = sonsLen(n)
if (n.sons[0].kind != nkSym): internalError(n.info, "genObjectFields")
field = n.sons[0].sym
s = genTypeInfo(p, field.typ)
for i in countup(1, length - 1):
for i in countup(1, length - 1):
b = n.sons[i] # branch
u = nil
case b.kind
of nkOfBranch:
if sonsLen(b) < 2:
of nkOfBranch:
if sonsLen(b) < 2:
internalError(b.info, "genObjectFields; nkOfBranch broken")
for j in countup(0, sonsLen(b) - 2):
if u != nil: app(u, ", ")
if b.sons[j].kind == nkRange:
appf(u, "[$1, $2]", [toRope(getOrdValue(b.sons[j].sons[0])),
toRope(getOrdValue(b.sons[j].sons[1]))])
else:
app(u, toRope(getOrdValue(b.sons[j])))
of nkElse:
u = toRope(lengthOrd(field.typ))
for j in countup(0, sonsLen(b) - 2):
if u != nil: add(u, ", ")
if b.sons[j].kind == nkRange:
addf(u, "[$1, $2]", [rope(getOrdValue(b.sons[j].sons[0])),
rope(getOrdValue(b.sons[j].sons[1]))])
else:
add(u, rope(getOrdValue(b.sons[j])))
of nkElse:
u = rope(lengthOrd(field.typ))
else: internalError(n.info, "genObjectFields(nkRecCase)")
if result != nil: app(result, ", " & tnl)
appf(result, "[SetConstr($1), $2]",
if result != nil: add(result, ", " & tnl)
addf(result, "[SetConstr($1), $2]",
[u, genObjectFields(p, typ, lastSon(b))])
result = ropef("{kind: 3, offset: \"$1\", len: $3, " &
"typ: $2, name: $4, sons: [$5]}", [mangleName(field), s,
toRope(lengthOrd(field.typ)), makeJSString(field.name.s), result])
result = ("{kind: 3, offset: \"$1\", len: $3, " &
"typ: $2, name: $4, sons: [$5]}") % [mangleName(field), s,
rope(lengthOrd(field.typ)), makeJSString(field.name.s), result]
else: internalError(n.info, "genObjectFields")
proc genObjectInfo(p: PProc, typ: PType, name: PRope) =
var s = ropef("var $1 = {size: 0, kind: $2, base: null, node: null, " &
"finalizer: null};$n", [name, toRope(ord(typ.kind))])
proc genObjectInfo(p: PProc, typ: PType, name: Rope) =
var s = ("var $1 = {size: 0, kind: $2, base: null, node: null, " &
"finalizer: null};$n") % [name, rope(ord(typ.kind))]
prepend(p.g.typeInfo, s)
appf(p.g.typeInfo, "var NNI$1 = $2;$n",
[toRope(typ.id), genObjectFields(p, typ, typ.n)])
appf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, toRope(typ.id)])
if (typ.kind == tyObject) and (typ.sons[0] != nil):
appf(p.g.typeInfo, "$1.base = $2;$n",
addf(p.g.typeInfo, "var NNI$1 = $2;$n",
[rope(typ.id), genObjectFields(p, typ, typ.n)])
addf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, rope(typ.id)])
if (typ.kind == tyObject) and (typ.sons[0] != nil):
addf(p.g.typeInfo, "$1.base = $2;$n",
[name, genTypeInfo(p, typ.sons[0])])
proc genTupleFields(p: PProc, typ: PType): PRope =
var s: PRope = nil
proc genTupleFields(p: PProc, typ: PType): Rope =
var s: Rope = nil
for i in 0 .. <typ.len:
if i > 0: app(s, ", " & tnl)
s.appf("{kind: 1, offset: \"Field$1\", len: 0, " &
if i > 0: add(s, ", " & tnl)
s.addf("{kind: 1, offset: \"Field$1\", len: 0, " &
"typ: $2, name: \"Field$1\", sons: null}",
[i.toRope, genTypeInfo(p, typ.sons[i])])
result = ropef("{kind: 2, len: $1, offset: 0, " &
"typ: null, name: null, sons: [$2]}", [toRope(typ.len), s])
[i.rope, genTypeInfo(p, typ.sons[i])])
result = ("{kind: 2, len: $1, offset: 0, " &
"typ: null, name: null, sons: [$2]}") % [rope(typ.len), s]
proc genTupleInfo(p: PProc, typ: PType, name: PRope) =
var s = ropef("var $1 = {size: 0, kind: $2, base: null, node: null, " &
"finalizer: null};$n", [name, toRope(ord(typ.kind))])
proc genTupleInfo(p: PProc, typ: PType, name: Rope) =
var s = ("var $1 = {size: 0, kind: $2, base: null, node: null, " &
"finalizer: null};$n") % [name, rope(ord(typ.kind))]
prepend(p.g.typeInfo, s)
appf(p.g.typeInfo, "var NNI$1 = $2;$n",
[toRope(typ.id), genTupleFields(p, typ)])
appf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, toRope(typ.id)])
addf(p.g.typeInfo, "var NNI$1 = $2;$n",
[rope(typ.id), genTupleFields(p, typ)])
addf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, rope(typ.id)])
proc genEnumInfo(p: PProc, typ: PType, name: PRope) =
proc genEnumInfo(p: PProc, typ: PType, name: Rope) =
let length = sonsLen(typ.n)
var s: PRope = nil
for i in countup(0, length - 1):
var s: Rope = nil
for i in countup(0, length - 1):
if (typ.n.sons[i].kind != nkSym): internalError(typ.n.info, "genEnumInfo")
let field = typ.n.sons[i].sym
if i > 0: app(s, ", " & tnl)
if i > 0: add(s, ", " & tnl)
let extName = if field.ast == nil: field.name.s else: field.ast.strVal
appf(s, "{kind: 1, offset: $1, typ: $2, name: $3, len: 0, sons: null}",
[toRope(field.position), name, makeJSString(extName)])
var n = ropef("var NNI$1 = {kind: 2, offset: 0, typ: null, " &
"name: null, len: $2, sons: [$3]};$n", [toRope(typ.id), toRope(length), s])
s = ropef("var $1 = {size: 0, kind: $2, base: null, node: null, " &
"finalizer: null};$n", [name, toRope(ord(typ.kind))])
addf(s, "{kind: 1, offset: $1, typ: $2, name: $3, len: 0, sons: null}",
[rope(field.position), name, makeJSString(extName)])
var n = ("var NNI$1 = {kind: 2, offset: 0, typ: null, " &
"name: null, len: $2, sons: [$3]};$n") % [rope(typ.id), rope(length), s]
s = ("var $1 = {size: 0, kind: $2, base: null, node: null, " &
"finalizer: null};$n") % [name, rope(ord(typ.kind))]
prepend(p.g.typeInfo, s)
app(p.g.typeInfo, n)
appf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, toRope(typ.id)])
add(p.g.typeInfo, n)
addf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, rope(typ.id)])
if typ.sons[0] != nil:
appf(p.g.typeInfo, "$1.base = $2;$n",
addf(p.g.typeInfo, "$1.base = $2;$n",
[name, genTypeInfo(p, typ.sons[0])])
proc genTypeInfo(p: PProc, typ: PType): PRope =
proc genTypeInfo(p: PProc, typ: PType): Rope =
var t = typ
if t.kind == tyGenericInst: t = lastSon(t)
result = ropef("NTI$1", [toRope(t.id)])
if containsOrIncl(p.g.typeInfoGenerated, t.id): return
result = "NTI$1" % [rope(t.id)]
if containsOrIncl(p.g.typeInfoGenerated, t.id): return
case t.kind
of tyDistinct:
of tyDistinct:
result = genTypeInfo(p, typ.sons[0])
of tyPointer, tyProc, tyBool, tyChar, tyCString, tyString, tyInt..tyFloat128:
var s = ropef(
"var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n",
[result, toRope(ord(t.kind))])
of tyPointer, tyProc, tyBool, tyChar, tyCString, tyString, tyInt..tyUInt64:
var s =
"var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" %
[result, rope(ord(t.kind))]
prepend(p.g.typeInfo, s)
of tyVar, tyRef, tyPtr, tySequence, tyRange, tySet:
var s = ropef(
"var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n",
[result, toRope(ord(t.kind))])
of tyVar, tyRef, tyPtr, tySequence, tyRange, tySet:
var s =
"var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" %
[result, rope(ord(t.kind))]
prepend(p.g.typeInfo, s)
appf(p.g.typeInfo, "$1.base = $2;$n",
addf(p.g.typeInfo, "$1.base = $2;$n",
[result, genTypeInfo(p, typ.lastSon)])
of tyArrayConstr, tyArray:
var s = ropef(
"var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n",
[result, toRope(ord(t.kind))])
of tyArrayConstr, tyArray:
var s =
"var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" %
[result, rope(ord(t.kind))]
prepend(p.g.typeInfo, s)
appf(p.g.typeInfo, "$1.base = $2;$n",
addf(p.g.typeInfo, "$1.base = $2;$n",
[result, genTypeInfo(p, typ.sons[1])])
of tyEnum: genEnumInfo(p, t, result)
of tyObject: genObjectInfo(p, t, result)

View File

@@ -1,7 +1,7 @@
#
#
# The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -9,92 +9,92 @@
# This include file implements lambda lifting for the transformator.
import
intsets, strutils, lists, options, ast, astalgo, trees, treetab, msgs, os,
import
intsets, strutils, lists, options, ast, astalgo, trees, treetab, msgs, os,
idents, renderer, types, magicsys, rodread, lowerings
discard """
The basic approach is that captured vars need to be put on the heap and
that the calling chain needs to be explicitely modelled. Things to consider:
that the calling chain needs to be explicitly modelled. Things to consider:
proc a =
var v = 0
proc b =
var w = 2
for x in 0..3:
proc c = capture v, w, x
c()
b()
for x in 0..4:
proc d = capture x
d()
Needs to be translated into:
proc a =
var cl: *
new cl
cl.v = 0
proc b(cl) =
var bcl: *
new bcl
bcl.w = 2
bcl.up = cl
for x in 0..3:
var bcl2: *
new bcl2
bcl2.up = bcl
bcl2.up2 = cl
bcl2.x = x
proc c(cl) = capture cl.up2.v, cl.up.w, cl.x
c(bcl2)
c(bcl)
b(cl)
for x in 0..4:
var acl2: *
new acl2
acl2.x = x
proc d(cl) = capture cl.x
d(acl2)
Closures as interfaces:
proc outer: T =
var captureMe: TObject # value type required for efficiency
proc getter(): int = result = captureMe.x
proc setter(x: int) = captureMe.x = x
result = (getter, setter)
Is translated to:
proc outer: T =
var cl: *
new cl
proc getter(cl): int = result = cl.captureMe.x
proc setter(cl: *, x: int) = cl.captureMe.x = x
result = ((cl, getter), (cl, setter))
For 'byref' capture, the outer proc needs to access the captured var through
the indirection too. For 'bycopy' capture, the outer proc accesses the var
not through the indirection.
Possible optimizations:
Possible optimizations:
1) If the closure contains a single 'ref' and this
reference is not re-assigned (check ``sfAddrTaken`` flag) make this the
closure. This is an important optimization if closures are used as
closure. This is an important optimization if closures are used as
interfaces.
2) If the closure does not escape, put it onto the stack, not on the heap.
3) Dataflow analysis would help to eliminate the 'up' indirections.
@@ -126,7 +126,7 @@ type
fn, closureParam, state, resultSym: PSym # most are only valid if
# fn.kind == skClosureIterator
obj: PType
PEnv = ref TEnv
TEnv {.final.} = object of RootObj
attachedNode, replacementNode: PNode
@@ -141,7 +141,7 @@ type
# if up.fn != fn then we cross function boundaries.
# This is an important case to consider.
vars: IntSet # variables belonging to this environment
TOuterContext = object
fn: PSym # may also be a module!
head: PEnv
@@ -187,6 +187,7 @@ proc addHiddenParam(routine: PSym, param: PSym) =
param.position = params.len-1
addSon(params, newSymNode(param))
incl(routine.typ.flags, tfCapturesEnv)
assert sfFromGeneric in param.flags
#echo "produced environment: ", param.id, " for ", routine.name.s
proc getHiddenParam(routine: PSym): PSym =
@@ -194,12 +195,14 @@ proc getHiddenParam(routine: PSym): PSym =
let hidden = lastSon(params)
internalAssert hidden.kind == nkSym and hidden.sym.kind == skParam
result = hidden.sym
assert sfFromGeneric in result.flags
proc getEnvParam(routine: PSym): PSym =
let params = routine.ast.sons[paramsPos]
let hidden = lastSon(params)
if hidden.kind == nkSym and hidden.sym.name.s == paramName:
result = hidden.sym
assert sfFromGeneric in result.flags
proc initIter(iter: PSym): TIter =
result.fn = iter
@@ -281,7 +284,7 @@ proc addClosureParam(fn: PSym; e: PEnv) =
#assert e.obj.kind == tyObject
proc illegalCapture(s: PSym): bool {.inline.} =
result = skipTypes(s.typ, abstractInst).kind in
result = skipTypes(s.typ, abstractInst).kind in
{tyVar, tyOpenArray, tyVarargs} or
s.kind == skResult
@@ -341,7 +344,7 @@ proc createUpField(obj, fieldType: PType): PSym =
#rawAddField(obj, result)
addField(obj, result)
proc captureVar(o: POuterContext; top: PEnv; local: PSym;
proc captureVar(o: POuterContext; top: PEnv; local: PSym;
info: TLineInfo): bool =
# first check if we should be concerned at all:
var it = top
@@ -405,7 +408,7 @@ proc gatherVars(o: POuterContext; e: PEnv; n: PNode): int =
var s = n.sym
if interestingVar(s) and e.fn != s.owner:
if captureVar(o, e, s, n.info): result = 1
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit, nkClosure, nkProcDef,
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit, nkClosure, nkProcDef,
nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef, nkTypeSection:
discard
else:
@@ -415,7 +418,7 @@ proc gatherVars(o: POuterContext; e: PEnv; n: PNode): int =
proc generateThunk(prc: PNode, dest: PType): PNode =
## Converts 'prc' into '(thunk, nil)' so that it's compatible with
## a closure.
# we cannot generate a proper thunk here for GC-safety reasons (see internal
# documentation):
if gCmd == cmdCompileToJS: return prc
@@ -512,7 +515,7 @@ proc closureCreationPoint(n: PNode): PNode =
proc addParamsToEnv(fn: PSym; env: PEnv) =
let params = fn.typ.n
for i in 1.. <params.len:
for i in 1.. <params.len:
if params.sons[i].kind != nkSym:
internalError(params.info, "liftLambdas: strange params")
let param = params.sons[i].sym
@@ -538,7 +541,7 @@ proc searchForInnerProcs(o: POuterContext, n: PNode, env: PEnv) =
addParamsToEnv(fn, envB)
searchForInnerProcs(o, body, envB)
fn.ast.sons[bodyPos] = ex
let capturedCounter = gatherVars(o, envB, body)
# dummy closure param needed?
if capturedCounter == 0 and fn.typ.callConv == ccClosure:
@@ -557,7 +560,7 @@ proc searchForInnerProcs(o: POuterContext, n: PNode, env: PEnv) =
of nkWhileStmt, nkForStmt, nkParForStmt, nkBlockStmt:
# some nodes open a new scope, so they are candidates for the insertion
# of closure creation; however for simplicity we merge closures between
# branches, in fact, only loop bodies are of interest here as only they
# branches, in fact, only loop bodies are of interest here as only they
# yield observable changes in semantics. For Zahary we also
# include ``nkBlock``. We don't do this for closure iterators because
# 'yield' can produce wrong code otherwise (XXX show example):
@@ -580,7 +583,7 @@ proc searchForInnerProcs(o: POuterContext, n: PNode, env: PEnv) =
elif it.kind == nkIdentDefs:
var L = sonsLen(it)
if it.sons[0].kind == nkSym:
# this can be false for recursive invokations that already
# this can be false for recursive invocations that already
# transformed it into 'env.varName':
env.vars.incl(it.sons[0].sym.id)
searchForInnerProcs(o, it.sons[L-1], env)
@@ -595,7 +598,7 @@ proc searchForInnerProcs(o: POuterContext, n: PNode, env: PEnv) =
internalError(it.info, "searchForInnerProcs")
of nkClosure:
searchForInnerProcs(o, n.sons[0], env)
of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef,
of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef,
nkTypeSection:
# don't recurse here:
discard
@@ -603,7 +606,7 @@ proc searchForInnerProcs(o: POuterContext, n: PNode, env: PEnv) =
for i in countup(0, sonsLen(n) - 1):
searchForInnerProcs(o, n.sons[i], env)
proc newAsgnStmt(le, ri: PNode, info: TLineInfo): PNode =
proc newAsgnStmt(le, ri: PNode, info: TLineInfo): PNode =
# Bugfix: unfortunately we cannot use 'nkFastAsgn' here as that would
# mean to be able to capture string literals which have no GC header.
# However this can only happen if the capture happens through a parameter,
@@ -621,7 +624,7 @@ proc rawClosureCreation(o: POuterContext, scope: PEnv; env: PNode): PNode =
result.add(v)
# add 'new' statement:
result.add(newCall(getSysSym"internalNew", env))
# add assignment statements:
for local in scope.capturedVars:
let fieldAccess = indirectAccess(env, local, env.info)
@@ -693,10 +696,10 @@ proc transformYield(c: POuterContext, n: PNode, it: TIter): PNode =
retStmt.add(a)
else:
retStmt.add(emptyNode)
var stateLabelStmt = newNodeI(nkState, n.info)
stateLabelStmt.add(newIntTypeNode(nkIntLit, stateNo, getSysType(tyInt)))
result = newNodeI(nkStmtList, n.info)
result.add(stateAsgnStmt)
result.add(retStmt)
@@ -716,15 +719,17 @@ proc outerProcSons(o: POuterContext, n: PNode, it: TIter) =
let x = transformOuterProc(o, n.sons[i], it)
if x != nil: n.sons[i] = x
proc liftIterSym*(n: PNode): PNode =
# transforms (iter) to (let env = newClosure[iter](); (iter, env))
proc liftIterSym(n: PNode; owner: PSym): PNode =
# transforms (iter) to (let env = newClosure[iter](); (iter, env))
let iter = n.sym
assert iter.kind == skClosureIterator
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
var env = copySym(getHiddenParam(iter))
env.kind = skLet
let hp = getHiddenParam(iter)
let env = newSym(skLet, iter.name, owner, n.info)
env.typ = hp.typ
env.flags = hp.flags
var v = newNodeI(nkVarSection, n.info)
addVar(v, newSymNode(env))
result.add(v)
@@ -795,7 +800,7 @@ proc transformOuterProcBody(o: POuterContext, n: PNode; it: TIter): PNode =
# with some rather primitive check for now:
if n.kind == nkStmtList and n.len > 0:
if n.sons[0].kind == nkGotoState: return nil
if n.len > 1 and n[1].kind == nkStmtList and n[1].len > 0 and
if n.len > 1 and n[1].kind == nkStmtList and n[1].len > 0 and
n[1][0].kind == nkGotoState:
return nil
result = newNodeI(nkStmtList, it.fn.info)
@@ -807,7 +812,7 @@ proc transformOuterProcBody(o: POuterContext, n: PNode; it: TIter): PNode =
var state0 = newNodeI(nkState, it.fn.info)
state0.add(newIntNode(nkIntLit, 0))
result.add(state0)
let newBody = transformOuterProc(o, n, it)
if newBody != nil:
result.add(newBody)
@@ -853,7 +858,6 @@ proc transformOuterProc(o: POuterContext, n: PNode; it: TIter): PNode =
addUniqueField(it.obj, local)
return indirectAccess(newSymNode(it.closureParam), local, n.info)
var closure = PEnv(idTableGet(o.lambdasToEnv, local))
if local.kind == skClosureIterator:
# consider: [i1, i2, i1] Since we merged the iterator's closure
# with the captured owning variables, we need to generate the
@@ -861,13 +865,25 @@ proc transformOuterProc(o: POuterContext, n: PNode; it: TIter): PNode =
if local == o.fn or local == it.fn:
message(n.info, errRecursiveDependencyX, local.name.s)
# XXX why doesn't this work?
var closure = PEnv(idTableGet(o.lambdasToEnv, local))
if closure.isNil:
return liftIterSym(n)
return liftIterSym(n, o.fn)
else:
let createdVar = generateIterClosureCreation(o, closure,
closure.attachedNode)
let lpt = getHiddenParam(local).typ
if lpt != createdVar.typ:
assert lpt.kind == tyRef and createdVar.typ.kind == tyRef
# fix bug 'tshallowcopy_closures' but report if this gets any weirder:
if createdVar.typ.sons[0].len == 1 and lpt.sons[0].len >= 1:
createdVar.typ = lpt
if createdVar.kind == nkSym: createdVar.sym.typ = lpt
closure.obj = lpt.sons[0]
else:
internalError(n.info, "environment computation failed")
return makeClosure(local, createdVar, n.info)
var closure = PEnv(idTableGet(o.lambdasToEnv, local))
if closure != nil:
# we need to replace the lambda with '(lambda, env)':
let a = closure.createdVar
@@ -883,7 +899,7 @@ proc transformOuterProc(o: POuterContext, n: PNode; it: TIter): PNode =
let x = closure.createdVar
assert x != nil
return makeClosure(local, x, n.info)
if not contains(o.capturedVars, local.id): return
# change 'local' to 'closure.local', unless it's a 'byCopy' variable:
# if sfByCopy notin local.flags:
@@ -930,12 +946,12 @@ proc transformOuterProc(o: POuterContext, n: PNode; it: TIter): PNode =
proc liftLambdas*(fn: PSym, body: PNode): PNode =
# XXX gCmd == cmdCompileToJS does not suffice! The compiletime stuff needs
# the transformation even when compiling to JS ...
if body.kind == nkEmpty or gCmd == cmdCompileToJS or
if body.kind == nkEmpty or gCmd == cmdCompileToJS or
fn.skipGenericOwner.kind != skModule:
# ignore forward declaration:
result = body
else:
#if fn.name.s == "cbOuter":
#if fn.name.s == "sort":
# echo rendertree(fn.ast, {renderIds})
var o = newOuterContext(fn)
let ex = closureCreationPoint(body)
@@ -969,26 +985,26 @@ proc liftLambdasForTopLevel*(module: PSym, body: PNode): PNode =
proc liftForLoop*(body: PNode): PNode =
# problem ahead: the iterator could be invoked indirectly, but then
# we don't know what environment to create here:
#
# we don't know what environment to create here:
#
# iterator count(): int =
# yield 0
#
#
# iterator count2(): int =
# var x = 3
# yield x
# inc x
# yield x
#
#
# proc invoke(iter: iterator(): int) =
# for x in iter(): echo x
#
# --> When to create the closure? --> for the (count) occurence!
# --> When to create the closure? --> for the (count) occurrence!
discard """
for i in foo(): ...
Is transformed to:
cl = createClosure()
while true:
let i = foo(cl)
@@ -1000,7 +1016,7 @@ proc liftForLoop*(body: PNode): PNode =
var call = body[L-2]
result = newNodeI(nkStmtList, body.info)
# static binding?
var env: PSym
if call[0].kind == nkSym and call[0].sym.kind == skClosureIterator:
@@ -1014,18 +1030,18 @@ proc liftForLoop*(body: PNode): PNode =
result.add(v)
# add 'new' statement:
result.add(newCall(getSysSym"internalNew", env.newSymNode))
var loopBody = newNodeI(nkStmtList, body.info, 3)
var whileLoop = newNodeI(nkWhileStmt, body.info, 2)
whileLoop.sons[0] = newIntTypeNode(nkIntLit, 1, getSysType(tyBool))
whileLoop.sons[1] = loopBody
result.add whileLoop
# setup loopBody:
# gather vars in a tuple:
var v2 = newNodeI(nkLetSection, body.info)
var vpart = newNodeI(if L == 3: nkIdentDefs else: nkVarTuple, body.info)
for i in 0 .. L-3:
for i in 0 .. L-3:
assert body[i].kind == nkSym
body[i].sym.kind = skLet
addSon(vpart, body[i])

View File

@@ -1,7 +1,7 @@
#
#
# The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -10,79 +10,79 @@
# This scanner is handwritten for efficiency. I used an elegant buffering
# scheme which I have not seen anywhere else:
# We guarantee that a whole line is in the buffer. Thus only when scanning
# the \n or \r character we have to check wether we need to read in the next
# the \n or \r character we have to check wether we need to read in the next
# chunk. (\n or \r already need special handling for incrementing the line
# counter; choosing both \n and \r allows the scanner to properly read Unix,
# DOS or Macintosh text files, even when it is not the native format.
import
import
hashes, options, msgs, strutils, platform, idents, nimlexbase, llstream,
wordrecg
const
const
MaxLineLength* = 80 # lines longer than this lead to a warning
numChars*: set[char] = {'0'..'9', 'a'..'z', 'A'..'Z'}
SymChars*: set[char] = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF'}
SymStartChars*: set[char] = {'a'..'z', 'A'..'Z', '\x80'..'\xFF'}
OpChars*: set[char] = {'+', '-', '*', '/', '\\', '<', '>', '!', '?', '^', '.',
OpChars*: set[char] = {'+', '-', '*', '/', '\\', '<', '>', '!', '?', '^', '.',
'|', '=', '%', '&', '$', '@', '~', ':', '\x80'..'\xFF'}
# don't forget to update the 'highlite' module if these charsets should change
type
TTokType* = enum
type
TTokType* = enum
tkInvalid, tkEof, # order is important here!
tkSymbol, # keywords:
tkAddr, tkAnd, tkAs, tkAsm, tkAtomic,
tkBind, tkBlock, tkBreak, tkCase, tkCast,
tkConst, tkContinue, tkConverter,
tkAddr, tkAnd, tkAs, tkAsm, tkAtomic,
tkBind, tkBlock, tkBreak, tkCase, tkCast,
tkConcept, tkConst, tkContinue, tkConverter,
tkDefer, tkDiscard, tkDistinct, tkDiv, tkDo,
tkElif, tkElse, tkEnd, tkEnum, tkExcept, tkExport,
tkFinally, tkFor, tkFrom, tkFunc,
tkGeneric, tkIf, tkImport, tkIn, tkInclude, tkInterface,
tkGeneric, tkIf, tkImport, tkIn, tkInclude, tkInterface,
tkIs, tkIsnot, tkIterator,
tkLet,
tkMacro, tkMethod, tkMixin, tkMod, tkNil, tkNot, tkNotin,
tkObject, tkOf, tkOr, tkOut,
tkMacro, tkMethod, tkMixin, tkMod, tkNil, tkNot, tkNotin,
tkObject, tkOf, tkOr, tkOut,
tkProc, tkPtr, tkRaise, tkRef, tkReturn, tkShl, tkShr, tkStatic,
tkTemplate,
tkTry, tkTuple, tkType, tkUsing,
tkTemplate,
tkTry, tkTuple, tkType, tkUsing,
tkVar, tkWhen, tkWhile, tkWith, tkWithout, tkXor,
tkYield, # end of keywords
tkIntLit, tkInt8Lit, tkInt16Lit, tkInt32Lit, tkInt64Lit,
tkUIntLit, tkUInt8Lit, tkUInt16Lit, tkUInt32Lit, tkUInt64Lit,
tkFloatLit, tkFloat32Lit, tkFloat64Lit, tkFloat128Lit,
tkStrLit, tkRStrLit, tkTripleStrLit,
tkGStrLit, tkGTripleStrLit, tkCharLit, tkParLe, tkParRi, tkBracketLe,
tkBracketRi, tkCurlyLe, tkCurlyRi,
tkGStrLit, tkGTripleStrLit, tkCharLit, tkParLe, tkParRi, tkBracketLe,
tkBracketRi, tkCurlyLe, tkCurlyRi,
tkBracketDotLe, tkBracketDotRi, # [. and .]
tkCurlyDotLe, tkCurlyDotRi, # {. and .}
tkParDotLe, tkParDotRi, # (. and .)
tkComma, tkSemiColon,
tkColon, tkColonColon, tkEquals, tkDot, tkDotDot,
tkOpr, tkComment, tkAccent,
tkSpaces, tkInfixOpr, tkPrefixOpr, tkPostfixOpr,
tkSpaces, tkInfixOpr, tkPrefixOpr, tkPostfixOpr
TTokTypes* = set[TTokType]
const
const
tokKeywordLow* = succ(tkSymbol)
tokKeywordHigh* = pred(tkIntLit)
TokTypeToStr*: array[TTokType, string] = ["tkInvalid", "[EOF]",
TokTypeToStr*: array[TTokType, string] = ["tkInvalid", "[EOF]",
"tkSymbol",
"addr", "and", "as", "asm", "atomic",
"bind", "block", "break", "case", "cast",
"const", "continue", "converter",
"addr", "and", "as", "asm", "atomic",
"bind", "block", "break", "case", "cast",
"concept", "const", "continue", "converter",
"defer", "discard", "distinct", "div", "do",
"elif", "else", "end", "enum", "except", "export",
"finally", "for", "from", "func", "generic", "if",
"finally", "for", "from", "func", "generic", "if",
"import", "in", "include", "interface", "is", "isnot", "iterator",
"let",
"macro", "method", "mixin", "mod",
"nil", "not", "notin", "object", "of", "or",
"out", "proc", "ptr", "raise", "ref", "return",
"macro", "method", "mixin", "mod",
"nil", "not", "notin", "object", "of", "or",
"out", "proc", "ptr", "raise", "ref", "return",
"shl", "shr", "static",
"template",
"template",
"try", "tuple", "type", "using",
"var", "when", "while", "with", "without", "xor",
"yield",
@@ -90,7 +90,7 @@ const
"tkUIntLit", "tkUInt8Lit", "tkUInt16Lit", "tkUInt32Lit", "tkUInt64Lit",
"tkFloatLit", "tkFloat32Lit", "tkFloat64Lit", "tkFloat128Lit",
"tkStrLit", "tkRStrLit",
"tkTripleStrLit", "tkGStrLit", "tkGTripleStrLit", "tkCharLit", "(",
"tkTripleStrLit", "tkGStrLit", "tkGTripleStrLit", "tkCharLit", "(",
")", "[", "]", "{", "}", "[.", ".]", "{.", ".}", "(.", ".)",
",", ";",
":", "::", "=", ".", "..",
@@ -98,8 +98,8 @@ const
"tkSpaces", "tkInfixOpr",
"tkPrefixOpr", "tkPostfixOpr"]
type
TNumericalBase* = enum
type
TNumericalBase* = enum
base10, # base10 is listed as the first element,
# so that it is the correct default value
base2, base8, base16
@@ -107,7 +107,7 @@ type
TToken* = object # a Nim token
tokType*: TTokType # the type of the token
indent*: int # the indentation; != -1 if the token has been
# preceeded with indentation
# preceded with indentation
ident*: PIdent # the parsed identifier
iNumber*: BiggestInt # the parsed integer literal
fNumber*: BiggestFloat # the parsed floating point literal
@@ -131,64 +131,48 @@ type
var gLinesCompiled*: int # all lines that have been compiled
proc isKeyword*(kind: TTokType): bool
proc openLexer*(lex: var TLexer, fileidx: int32, inputstream: PLLStream)
proc rawGetTok*(L: var TLexer, tok: var TToken)
# reads in the next token into tok and skips it
proc getLineInfo*(L: TLexer, tok: TToken): TLineInfo {.inline.} =
newLineInfo(L.fileIdx, tok.line, tok.col)
proc closeLexer*(lex: var TLexer)
proc printTok*(tok: TToken)
proc tokToStr*(tok: TToken): string
proc openLexer*(lex: var TLexer, filename: string, inputstream: PLLStream) =
openLexer(lex, filename.fileInfoIdx, inputstream)
proc lexMessage*(L: TLexer, msg: TMsgKind, arg = "")
proc isKeyword(kind: TTokType): bool =
proc isKeyword*(kind: TTokType): bool =
result = (kind >= tokKeywordLow) and (kind <= tokKeywordHigh)
proc isNimIdentifier*(s: string): bool =
if s[0] in SymStartChars:
var i = 1
while i < s.len:
if s[i] == '_':
if s[i] == '_':
inc(i)
if s[i] notin SymChars: return
if s[i] notin SymChars: return
inc(i)
result = true
proc tokToStr*(tok: TToken): string =
proc tokToStr*(tok: TToken): string =
case tok.tokType
of tkIntLit..tkInt64Lit: result = $tok.iNumber
of tkFloatLit..tkFloat64Lit: result = $tok.fNumber
of tkInvalid, tkStrLit..tkCharLit, tkComment: result = tok.literal
of tkParLe..tkColon, tkEof, tkAccent:
of tkParLe..tkColon, tkEof, tkAccent:
result = TokTypeToStr[tok.tokType]
else:
if tok.ident != nil:
result = tok.ident.s
else:
else:
internalError("tokToStr")
result = ""
proc prettyTok*(tok: TToken): string =
if isKeyword(tok.tokType): result = "keyword " & tok.ident.s
else: result = tokToStr(tok)
proc printTok*(tok: TToken) =
write(stdout, tok.line, ":", tok.col, "\t")
write(stdout, TokTypeToStr[tok.tokType])
write(stdout, " ")
writeln(stdout, tokToStr(tok))
proc printTok*(tok: TToken) =
msgWriteln($tok.line & ":" & $tok.col & "\t" &
TokTypeToStr[tok.tokType] & " " & tokToStr(tok))
var dummyIdent: PIdent
proc initToken*(L: var TToken) =
proc initToken*(L: var TToken) =
L.tokType = tkInvalid
L.iNumber = 0
L.indent = 0
@@ -198,7 +182,7 @@ proc initToken*(L: var TToken) =
L.base = base10
L.ident = dummyIdent
proc fillToken(L: var TToken) =
proc fillToken(L: var TToken) =
L.tokType = tkInvalid
L.iNumber = 0
L.indent = 0
@@ -207,22 +191,25 @@ proc fillToken(L: var TToken) =
L.fNumber = 0.0
L.base = base10
L.ident = dummyIdent
proc openLexer(lex: var TLexer, fileIdx: int32, inputstream: PLLStream) =
proc openLexer*(lex: var TLexer, fileIdx: int32, inputstream: PLLStream) =
openBaseLexer(lex, inputstream)
lex.fileIdx = fileidx
lex.indentAhead = - 1
lex.currLineIndent = 0
inc(lex.lineNumber, inputstream.lineOffset)
inc(lex.lineNumber, inputstream.lineOffset)
proc closeLexer(lex: var TLexer) =
proc openLexer*(lex: var TLexer, filename: string, inputstream: PLLStream) =
openLexer(lex, filename.fileInfoIdx, inputstream)
proc closeLexer*(lex: var TLexer) =
inc(gLinesCompiled, lex.lineNumber)
closeBaseLexer(lex)
proc getColumn(L: TLexer): int =
proc getColumn(L: TLexer): int =
result = getColNumber(L, L.bufpos)
proc getLineInfo(L: TLexer): TLineInfo =
proc getLineInfo(L: TLexer): TLineInfo =
result = newLineInfo(L.fileIdx, L.lineNumber, getColNumber(L, L.bufpos))
proc dispMessage(L: TLexer; info: TLineInfo; msg: TMsgKind; arg: string) =
@@ -231,31 +218,35 @@ proc dispMessage(L: TLexer; info: TLineInfo; msg: TMsgKind; arg: string) =
else:
L.errorHandler(info, msg, arg)
proc lexMessage(L: TLexer, msg: TMsgKind, arg = "") =
proc lexMessage*(L: TLexer, msg: TMsgKind, arg = "") =
L.dispMessage(getLineInfo(L), msg, arg)
proc lexMessageTok*(L: TLexer, msg: TMsgKind, tok: TToken, arg = "") =
var info = newLineInfo(L.fileIdx, tok.line, tok.col)
L.dispMessage(info, msg, arg)
proc lexMessagePos(L: var TLexer, msg: TMsgKind, pos: int, arg = "") =
var info = newLineInfo(L.fileIdx, L.lineNumber, pos - L.lineStart)
L.dispMessage(info, msg, arg)
proc matchUnderscoreChars(L: var TLexer, tok: var TToken, chars: set[char]) =
proc matchUnderscoreChars(L: var TLexer, tok: var TToken, chars: set[char]) =
var pos = L.bufpos # use registers for pos, buf
var buf = L.buf
while true:
if buf[pos] in chars:
while true:
if buf[pos] in chars:
add(tok.literal, buf[pos])
inc(pos)
else:
break
if buf[pos] == '_':
if buf[pos+1] notin chars:
else:
break
if buf[pos] == '_':
if buf[pos+1] notin chars:
lexMessage(L, errInvalidToken, "_")
break
add(tok.literal, '_')
inc(pos)
L.bufpos = pos
proc matchTwoChars(L: TLexer, first: char, second: set[char]): bool =
proc matchTwoChars(L: TLexer, first: char, second: set[char]): bool =
result = (L.buf[L.bufpos] == first) and (L.buf[L.bufpos + 1] in second)
proc isFloatLiteral(s: string): bool =
@@ -264,8 +255,21 @@ proc isFloatLiteral(s: string): bool =
return true
result = false
proc getNumber(L: var TLexer): TToken =
var
{.push overflowChecks: off.}
# We need to parse the largest uint literal without overflow checks
proc unsafeParseUInt(s: string, b: var BiggestInt, start = 0): int =
var i = start
if s[i] in {'0'..'9'}:
b = 0
while s[i] in {'0'..'9'}:
b = b * 10 + (ord(s[i]) - ord('0'))
inc(i)
while s[i] == '_': inc(i) # underscores are allowed and ignored
result = i - start
{.pop.} # overflowChecks
proc getNumber(L: var TLexer): TToken =
var
pos, endpos: int
xi: BiggestInt
# get the base:
@@ -279,15 +283,15 @@ proc getNumber(L: var TLexer): TToken =
else:
matchUnderscoreChars(L, result, {'0'..'9', 'b', 'B', 'o', 'c', 'C'})
eallowed = true
if (L.buf[L.bufpos] == '.') and (L.buf[L.bufpos + 1] in {'0'..'9'}):
if (L.buf[L.bufpos] == '.') and (L.buf[L.bufpos + 1] in {'0'..'9'}):
add(result.literal, '.')
inc(L.bufpos)
matchUnderscoreChars(L, result, {'0'..'9'})
eallowed = true
if eallowed and L.buf[L.bufpos] in {'e', 'E'}:
if eallowed and L.buf[L.bufpos] in {'e', 'E'}:
add(result.literal, 'e')
inc(L.bufpos)
if L.buf[L.bufpos] in {'+', '-'}:
if L.buf[L.bufpos] in {'+', '-'}:
add(result.literal, L.buf[L.bufpos])
inc(L.bufpos)
matchUnderscoreChars(L, result, {'0'..'9'})
@@ -296,7 +300,7 @@ proc getNumber(L: var TLexer): TToken =
if L.buf[endpos] == '\'': inc(endpos)
L.bufpos = pos # restore position
case L.buf[endpos]
of 'f', 'F':
of 'f', 'F':
inc(endpos)
if (L.buf[endpos] == '3') and (L.buf[endpos + 1] == '2'):
result.tokType = tkFloat32Lit
@@ -309,36 +313,36 @@ proc getNumber(L: var TLexer): TToken =
(L.buf[endpos + 2] == '8'):
result.tokType = tkFloat128Lit
inc(endpos, 3)
else:
else:
lexMessage(L, errInvalidNumber, result.literal & "'f" & L.buf[endpos])
of 'i', 'I':
of 'i', 'I':
inc(endpos)
if (L.buf[endpos] == '6') and (L.buf[endpos + 1] == '4'):
if (L.buf[endpos] == '6') and (L.buf[endpos + 1] == '4'):
result.tokType = tkInt64Lit
inc(endpos, 2)
elif (L.buf[endpos] == '3') and (L.buf[endpos + 1] == '2'):
elif (L.buf[endpos] == '3') and (L.buf[endpos + 1] == '2'):
result.tokType = tkInt32Lit
inc(endpos, 2)
elif (L.buf[endpos] == '1') and (L.buf[endpos + 1] == '6'):
elif (L.buf[endpos] == '1') and (L.buf[endpos + 1] == '6'):
result.tokType = tkInt16Lit
inc(endpos, 2)
elif (L.buf[endpos] == '8'):
elif (L.buf[endpos] == '8'):
result.tokType = tkInt8Lit
inc(endpos)
else:
else:
lexMessage(L, errInvalidNumber, result.literal & "'i" & L.buf[endpos])
of 'u', 'U':
inc(endpos)
if (L.buf[endpos] == '6') and (L.buf[endpos + 1] == '4'):
if (L.buf[endpos] == '6') and (L.buf[endpos + 1] == '4'):
result.tokType = tkUInt64Lit
inc(endpos, 2)
elif (L.buf[endpos] == '3') and (L.buf[endpos + 1] == '2'):
elif (L.buf[endpos] == '3') and (L.buf[endpos + 1] == '2'):
result.tokType = tkUInt32Lit
inc(endpos, 2)
elif (L.buf[endpos] == '1') and (L.buf[endpos + 1] == '6'):
elif (L.buf[endpos] == '1') and (L.buf[endpos + 1] == '6'):
result.tokType = tkUInt16Lit
inc(endpos, 2)
elif (L.buf[endpos] == '8'):
elif (L.buf[endpos] == '8'):
result.tokType = tkUInt8Lit
inc(endpos)
else:
@@ -346,45 +350,45 @@ proc getNumber(L: var TLexer): TToken =
else: lexMessage(L, errInvalidNumber, result.literal & "'" & L.buf[endpos])
else:
L.bufpos = pos # restore position
try:
try:
if (L.buf[pos] == '0') and
(L.buf[pos + 1] in {'x', 'X', 'b', 'B', 'o', 'O', 'c', 'C'}):
(L.buf[pos + 1] in {'x', 'X', 'b', 'B', 'o', 'O', 'c', 'C'}):
inc(pos, 2)
xi = 0 # it may be a base prefix
case L.buf[pos - 1] # now look at the optional type suffix:
of 'b', 'B':
of 'b', 'B':
result.base = base2
while true:
while true:
case L.buf[pos]
of '2'..'9', '.':
of '2'..'9', '.':
lexMessage(L, errInvalidNumber, result.literal)
inc(pos)
of '_':
if L.buf[pos+1] notin {'0'..'1'}:
of '_':
if L.buf[pos+1] notin {'0'..'1'}:
lexMessage(L, errInvalidToken, "_")
break
inc(pos)
of '0', '1':
of '0', '1':
xi = `shl`(xi, 1) or (ord(L.buf[pos]) - ord('0'))
inc(pos)
else: break
of 'o', 'c', 'C':
else: break
of 'o', 'c', 'C':
result.base = base8
while true:
while true:
case L.buf[pos]
of '8'..'9', '.':
of '8'..'9', '.':
lexMessage(L, errInvalidNumber, result.literal)
inc(pos)
of '_':
of '_':
if L.buf[pos+1] notin {'0'..'7'}:
lexMessage(L, errInvalidToken, "_")
break
inc(pos)
of '0'..'7':
of '0'..'7':
xi = `shl`(xi, 3) or (ord(L.buf[pos]) - ord('0'))
inc(pos)
else: break
of 'O':
else: break
of 'O':
lexMessage(L, errInvalidNumber, result.literal)
of 'x', 'X':
result.base = base16
@@ -404,7 +408,7 @@ proc getNumber(L: var TLexer): TToken =
of 'A'..'F':
xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('A') + 10)
inc(pos)
else: break
else: break
else: internalError(getLineInfo(L), "getNumber")
case result.tokType
of tkIntLit, tkInt64Lit: result.iNumber = xi
@@ -415,16 +419,22 @@ proc getNumber(L: var TLexer): TToken =
of tkUInt8Lit: result.iNumber = BiggestInt(int8(toU8(int(xi))))
of tkUInt16Lit: result.iNumber = BiggestInt(toU16(int(xi)))
of tkUInt32Lit: result.iNumber = BiggestInt(toU32(xi))
of tkFloat32Lit:
result.fNumber = (cast[PFloat32](addr(xi)))[]
of tkFloat32Lit:
result.fNumber = (cast[PFloat32](addr(xi)))[]
# note: this code is endian neutral!
# XXX: Test this on big endian machine!
of tkFloat64Lit: result.fNumber = (cast[PFloat64](addr(xi)))[]
of tkFloat64Lit: result.fNumber = (cast[PFloat64](addr(xi)))[]
else: internalError(getLineInfo(L), "getNumber")
elif isFloatLiteral(result.literal) or (result.tokType == tkFloat32Lit) or
(result.tokType == tkFloat64Lit):
(result.tokType == tkFloat64Lit):
result.fNumber = parseFloat(result.literal)
if result.tokType == tkIntLit: result.tokType = tkFloatLit
elif result.tokType == tkUint64Lit:
xi = 0
let len = unsafeParseUInt(result.literal, xi)
if len != result.literal.len or len == 0:
raise newException(ValueError, "invalid integer: " & $xi)
result.iNumber = xi
else:
result.iNumber = parseBiggestInt(result.literal)
if (result.iNumber < low(int32)) or (result.iNumber > high(int32)):
@@ -444,69 +454,69 @@ proc getNumber(L: var TLexer): TToken =
lexMessage(L, errNumberOutOfRange, result.literal)
L.bufpos = endpos
proc handleHexChar(L: var TLexer, xi: var int) =
proc handleHexChar(L: var TLexer, xi: var int) =
case L.buf[L.bufpos]
of '0'..'9':
of '0'..'9':
xi = (xi shl 4) or (ord(L.buf[L.bufpos]) - ord('0'))
inc(L.bufpos)
of 'a'..'f':
of 'a'..'f':
xi = (xi shl 4) or (ord(L.buf[L.bufpos]) - ord('a') + 10)
inc(L.bufpos)
of 'A'..'F':
of 'A'..'F':
xi = (xi shl 4) or (ord(L.buf[L.bufpos]) - ord('A') + 10)
inc(L.bufpos)
else: discard
proc handleDecChars(L: var TLexer, xi: var int) =
while L.buf[L.bufpos] in {'0'..'9'}:
proc handleDecChars(L: var TLexer, xi: var int) =
while L.buf[L.bufpos] in {'0'..'9'}:
xi = (xi * 10) + (ord(L.buf[L.bufpos]) - ord('0'))
inc(L.bufpos)
proc getEscapedChar(L: var TLexer, tok: var TToken) =
proc getEscapedChar(L: var TLexer, tok: var TToken) =
inc(L.bufpos) # skip '\'
case L.buf[L.bufpos]
of 'n', 'N':
of 'n', 'N':
if tok.tokType == tkCharLit: lexMessage(L, errNnotAllowedInCharacter)
add(tok.literal, tnl)
inc(L.bufpos)
of 'r', 'R', 'c', 'C':
of 'r', 'R', 'c', 'C':
add(tok.literal, CR)
inc(L.bufpos)
of 'l', 'L':
of 'l', 'L':
add(tok.literal, LF)
inc(L.bufpos)
of 'f', 'F':
of 'f', 'F':
add(tok.literal, FF)
inc(L.bufpos)
of 'e', 'E':
of 'e', 'E':
add(tok.literal, ESC)
inc(L.bufpos)
of 'a', 'A':
of 'a', 'A':
add(tok.literal, BEL)
inc(L.bufpos)
of 'b', 'B':
of 'b', 'B':
add(tok.literal, BACKSPACE)
inc(L.bufpos)
of 'v', 'V':
of 'v', 'V':
add(tok.literal, VT)
inc(L.bufpos)
of 't', 'T':
of 't', 'T':
add(tok.literal, '\t')
inc(L.bufpos)
of '\'', '\"':
of '\'', '\"':
add(tok.literal, L.buf[L.bufpos])
inc(L.bufpos)
of '\\':
of '\\':
add(tok.literal, '\\')
inc(L.bufpos)
of 'x', 'X':
of 'x', 'X':
inc(L.bufpos)
var xi = 0
handleHexChar(L, xi)
handleHexChar(L, xi)
add(tok.literal, chr(xi))
of '0'..'9':
if matchTwoChars(L, '0', {'0'..'9'}):
of '0'..'9':
if matchTwoChars(L, '0', {'0'..'9'}):
lexMessage(L, warnOctalEscape)
var xi = 0
handleDecChars(L, xi)
@@ -523,7 +533,7 @@ proc newString(s: cstring, len: int): string =
proc handleCRLF(L: var TLexer, pos: int): int =
template registerLine =
let col = L.getColNumber(pos)
if col > MaxLineLength:
lexMessagePos(L, hintLineTooLong, pos)
@@ -531,7 +541,7 @@ proc handleCRLF(L: var TLexer, pos: int): int =
let lineStart = cast[ByteAddress](L.buf) + L.lineStart
let line = newString(cast[cstring](lineStart), col)
addSourceLine(L.fileIdx, line)
case L.buf[pos]
of CR:
registerLine()
@@ -540,12 +550,12 @@ proc handleCRLF(L: var TLexer, pos: int): int =
registerLine()
result = nimlexbase.handleLF(L, pos)
else: result = pos
proc getString(L: var TLexer, tok: var TToken, rawMode: bool) =
proc getString(L: var TLexer, tok: var TToken, rawMode: bool) =
var pos = L.bufpos + 1 # skip "
var buf = L.buf # put `buf` in a register
var line = L.lineNumber # save linenumber for better error message
if buf[pos] == '\"' and buf[pos+1] == '\"':
if buf[pos] == '\"' and buf[pos+1] == '\"':
tok.tokType = tkTripleStrLit # long string literal:
inc(pos, 2) # skip ""
# skip leading newline:
@@ -555,112 +565,112 @@ proc getString(L: var TLexer, tok: var TToken, rawMode: bool) =
if buf[newpos] in {CR, LF}: pos = newpos
pos = handleCRLF(L, pos)
buf = L.buf
while true:
while true:
case buf[pos]
of '\"':
of '\"':
if buf[pos+1] == '\"' and buf[pos+2] == '\"' and
buf[pos+3] != '\"':
buf[pos+3] != '\"':
L.bufpos = pos + 3 # skip the three """
break
break
add(tok.literal, '\"')
inc(pos)
of CR, LF:
of CR, LF:
pos = handleCRLF(L, pos)
buf = L.buf
add(tok.literal, tnl)
of nimlexbase.EndOfFile:
of nimlexbase.EndOfFile:
var line2 = L.lineNumber
L.lineNumber = line
lexMessagePos(L, errClosingTripleQuoteExpected, L.lineStart)
L.lineNumber = line2
break
else:
break
else:
add(tok.literal, buf[pos])
inc(pos)
else:
else:
# ordinary string literal
if rawMode: tok.tokType = tkRStrLit
else: tok.tokType = tkStrLit
while true:
while true:
var c = buf[pos]
if c == '\"':
if c == '\"':
if rawMode and buf[pos+1] == '\"':
inc(pos, 2)
add(tok.literal, '"')
else:
inc(pos) # skip '"'
break
elif c in {CR, LF, nimlexbase.EndOfFile}:
elif c in {CR, LF, nimlexbase.EndOfFile}:
lexMessage(L, errClosingQuoteExpected)
break
elif (c == '\\') and not rawMode:
break
elif (c == '\\') and not rawMode:
L.bufpos = pos
getEscapedChar(L, tok)
pos = L.bufpos
else:
else:
add(tok.literal, c)
inc(pos)
L.bufpos = pos
proc getCharacter(L: var TLexer, tok: var TToken) =
proc getCharacter(L: var TLexer, tok: var TToken) =
inc(L.bufpos) # skip '
var c = L.buf[L.bufpos]
case c
of '\0'..pred(' '), '\'': lexMessage(L, errInvalidCharacterConstant)
of '\\': getEscapedChar(L, tok)
else:
else:
tok.literal = $c
inc(L.bufpos)
if L.buf[L.bufpos] != '\'': lexMessage(L, errMissingFinalQuote)
inc(L.bufpos) # skip '
proc getSymbol(L: var TLexer, tok: var TToken) =
proc getSymbol(L: var TLexer, tok: var TToken) =
var h: THash = 0
var pos = L.bufpos
var buf = L.buf
while true:
while true:
var c = buf[pos]
case c
of 'a'..'z', '0'..'9', '\x80'..'\xFF':
of 'a'..'z', '0'..'9', '\x80'..'\xFF':
h = h !& ord(c)
of 'A'..'Z':
of 'A'..'Z':
c = chr(ord(c) + (ord('a') - ord('A'))) # toLower()
h = h !& ord(c)
of '_':
if buf[pos+1] notin SymChars:
if buf[pos+1] notin SymChars:
lexMessage(L, errInvalidToken, "_")
break
else: break
else: break
inc(pos)
h = !$h
tok.ident = getIdent(addr(L.buf[L.bufpos]), pos - L.bufpos, h)
L.bufpos = pos
if (tok.ident.id < ord(tokKeywordLow) - ord(tkSymbol)) or
(tok.ident.id > ord(tokKeywordHigh) - ord(tkSymbol)):
(tok.ident.id > ord(tokKeywordHigh) - ord(tkSymbol)):
tok.tokType = tkSymbol
else:
else:
tok.tokType = TTokType(tok.ident.id + ord(tkSymbol))
proc endOperator(L: var TLexer, tok: var TToken, pos: int,
hash: THash) {.inline.} =
hash: THash) {.inline.} =
var h = !$hash
tok.ident = getIdent(addr(L.buf[L.bufpos]), pos - L.bufpos, h)
if (tok.ident.id < oprLow) or (tok.ident.id > oprHigh): tok.tokType = tkOpr
else: tok.tokType = TTokType(tok.ident.id - oprLow + ord(tkColon))
L.bufpos = pos
proc getOperator(L: var TLexer, tok: var TToken) =
proc getOperator(L: var TLexer, tok: var TToken) =
var pos = L.bufpos
var buf = L.buf
var h: THash = 0
while true:
while true:
var c = buf[pos]
if c notin OpChars: break
h = h !& ord(c)
inc(pos)
endOperator(L, tok, pos, h)
# advance pos but don't store it in L.bufpos so the next token (which might
# be an operator too) gets the preceeding spaces:
# be an operator too) gets the preceding spaces:
tok.strongSpaceB = 0
while buf[pos] == ' ':
inc pos
@@ -674,7 +684,15 @@ proc scanComment(L: var TLexer, tok: var TToken) =
when not defined(nimfix):
assert buf[pos+1] == '#'
if buf[pos+2] == '[':
lexMessagePos(L, warnDeprecated, pos, "use '## [' instead; '##['")
if buf[pos+3] == ']':
# ##[] is the (rather complex) "cursor token" for idetools
tok.tokType = tkComment
tok.literal = "[]"
inc(L.bufpos, 4)
return
else:
lexMessagePos(L, warnDeprecated, pos, "use '## [' instead; '##['")
tok.tokType = tkComment
# iNumber contains the number of '\n' in the token
tok.iNumber = 0
@@ -698,7 +716,7 @@ proc scanComment(L: var TLexer, tok: var TToken) =
pos = handleCRLF(L, pos)
buf = L.buf
var indent = 0
while buf[pos] == ' ':
while buf[pos] == ' ':
inc(pos)
inc(indent)
@@ -713,7 +731,7 @@ proc scanComment(L: var TLexer, tok: var TToken) =
when defined(nimfix): col = indent
inc tok.iNumber
else:
if buf[pos] > ' ':
if buf[pos] > ' ':
L.indentAhead = indent
break
L.bufpos = pos
@@ -760,7 +778,7 @@ proc skip(L: var TLexer, tok: var TToken) =
break # EndOfFile also leaves the loop
L.bufpos = pos
proc rawGetTok(L: var TLexer, tok: var TToken) =
proc rawGetTok*(L: var TLexer, tok: var TToken) =
fillToken(tok)
if L.indentAhead >= 0:
tok.indent = L.indentAhead
@@ -776,10 +794,10 @@ proc rawGetTok(L: var TLexer, tok: var TToken) =
getSymbol(L, tok)
else:
case c
of '#':
of '#':
scanComment(L, tok)
of '*':
# '*:' is unfortunately a special case, because it is two tokens in
# '*:' is unfortunately a special case, because it is two tokens in
# 'var v*: int'.
if L.buf[L.bufpos+1] == ':' and L.buf[L.bufpos+2] notin OpChars:
var h = 0 !& ord('*')
@@ -789,29 +807,29 @@ proc rawGetTok(L: var TLexer, tok: var TToken) =
of ',':
tok.tokType = tkComma
inc(L.bufpos)
of 'l':
of 'l':
# if we parsed exactly one character and its a small L (l), this
# is treated as a warning because it may be confused with the number 1
if L.buf[L.bufpos+1] notin (SymChars + {'_'}):
lexMessage(L, warnSmallLshouldNotBeUsed)
getSymbol(L, tok)
of 'r', 'R':
if L.buf[L.bufpos + 1] == '\"':
if L.buf[L.bufpos + 1] == '\"':
inc(L.bufpos)
getString(L, tok, true)
else:
else:
getSymbol(L, tok)
of '(':
of '(':
inc(L.bufpos)
if L.buf[L.bufpos] == '.' and L.buf[L.bufpos+1] != '.':
if L.buf[L.bufpos] == '.' and L.buf[L.bufpos+1] != '.':
tok.tokType = tkParDotLe
inc(L.bufpos)
else:
else:
tok.tokType = tkParLe
of ')':
of ')':
tok.tokType = tkParRi
inc(L.bufpos)
of '[':
of '[':
inc(L.bufpos)
if L.buf[L.bufpos] == '.' and L.buf[L.bufpos+1] != '.':
tok.tokType = tkBracketDotLe
@@ -822,34 +840,43 @@ proc rawGetTok(L: var TLexer, tok: var TToken) =
tok.tokType = tkBracketRi
inc(L.bufpos)
of '.':
if L.buf[L.bufpos+1] == ']':
if L.buf[L.bufpos+1] == ']':
tok.tokType = tkBracketDotRi
inc(L.bufpos, 2)
elif L.buf[L.bufpos+1] == '}':
elif L.buf[L.bufpos+1] == '}':
tok.tokType = tkCurlyDotRi
inc(L.bufpos, 2)
elif L.buf[L.bufpos+1] == ')':
elif L.buf[L.bufpos+1] == ')':
tok.tokType = tkParDotRi
inc(L.bufpos, 2)
else:
else:
getOperator(L, tok)
of '{':
of '{':
inc(L.bufpos)
if L.buf[L.bufpos] == '.' and L.buf[L.bufpos+1] != '.':
tok.tokType = tkCurlyDotLe
inc(L.bufpos)
else:
else:
tok.tokType = tkCurlyLe
of '}':
of '}':
tok.tokType = tkCurlyRi
inc(L.bufpos)
of ';':
of ';':
tok.tokType = tkSemiColon
inc(L.bufpos)
of '`':
of '`':
tok.tokType = tkAccent
inc(L.bufpos)
of '\"':
of '_':
inc(L.bufpos)
if L.buf[L.bufpos] notin SymChars:
tok.tokType = tkSymbol
tok.ident = getIdent("_")
else:
tok.literal = $c
tok.tokType = tkInvalid
lexMessage(L, errInvalidToken, c & " (\\" & $(ord(c)) & ')')
of '\"':
# check for extended raw string literal:
var rawMode = L.bufpos > 0 and L.buf[L.bufpos-1] in SymChars
getString(L, tok, rawMode)
@@ -864,7 +891,7 @@ proc rawGetTok(L: var TLexer, tok: var TToken) =
of '0'..'9':
tok = getNumber(L)
else:
if c in OpChars:
if c in OpChars:
getOperator(L, tok)
elif c == nimlexbase.EndOfFile:
tok.tokType = tkEof

View File

@@ -30,47 +30,32 @@ type
PLLStream* = ref TLLStream
proc llStreamOpen*(data: string): PLLStream
proc llStreamOpen*(f: var File): PLLStream
proc llStreamOpen*(filename: string, mode: FileMode): PLLStream
proc llStreamOpen*(): PLLStream
proc llStreamOpenStdIn*(): PLLStream
proc llStreamClose*(s: PLLStream)
proc llStreamRead*(s: PLLStream, buf: pointer, bufLen: int): int
proc llStreamReadLine*(s: PLLStream, line: var string): bool
proc llStreamReadAll*(s: PLLStream): string
proc llStreamWrite*(s: PLLStream, data: string)
proc llStreamWrite*(s: PLLStream, data: char)
proc llStreamWrite*(s: PLLStream, buf: pointer, buflen: int)
proc llStreamWriteln*(s: PLLStream, data: string)
# implementation
proc llStreamOpen(data: string): PLLStream =
proc llStreamOpen*(data: string): PLLStream =
new(result)
result.s = data
result.kind = llsString
proc llStreamOpen(f: var File): PLLStream =
proc llStreamOpen*(f: File): PLLStream =
new(result)
result.f = f
result.kind = llsFile
proc llStreamOpen(filename: string, mode: FileMode): PLLStream =
proc llStreamOpen*(filename: string, mode: FileMode): PLLStream =
new(result)
result.kind = llsFile
if not open(result.f, filename, mode): result = nil
proc llStreamOpen(): PLLStream =
proc llStreamOpen*(): PLLStream =
new(result)
result.kind = llsNone
proc llStreamOpenStdIn(): PLLStream =
proc llStreamOpenStdIn*(): PLLStream =
new(result)
result.kind = llsStdIn
result.s = ""
result.lineOffset = -1
proc llStreamClose(s: PLLStream) =
proc llStreamClose*(s: PLLStream) =
case s.kind
of llsNone, llsString, llsStdIn:
discard
@@ -130,7 +115,7 @@ proc llReadFromStdin(s: PLLStream, buf: pointer, bufLen: int): int =
copyMem(buf, addr(s.s[s.rd]), result)
inc(s.rd, result)
proc llStreamRead(s: PLLStream, buf: pointer, bufLen: int): int =
proc llStreamRead*(s: PLLStream, buf: pointer, bufLen: int): int =
case s.kind
of llsNone:
result = 0
@@ -144,7 +129,7 @@ proc llStreamRead(s: PLLStream, buf: pointer, bufLen: int): int =
of llsStdIn:
result = llReadFromStdin(s, buf, bufLen)
proc llStreamReadLine(s: PLLStream, line: var string): bool =
proc llStreamReadLine*(s: PLLStream, line: var string): bool =
setLen(line, 0)
case s.kind
of llsNone:
@@ -168,7 +153,7 @@ proc llStreamReadLine(s: PLLStream, line: var string): bool =
of llsStdIn:
result = readLine(stdin, line)
proc llStreamWrite(s: PLLStream, data: string) =
proc llStreamWrite*(s: PLLStream, data: string) =
case s.kind
of llsNone, llsStdIn:
discard
@@ -178,11 +163,11 @@ proc llStreamWrite(s: PLLStream, data: string) =
of llsFile:
write(s.f, data)
proc llStreamWriteln(s: PLLStream, data: string) =
proc llStreamWriteln*(s: PLLStream, data: string) =
llStreamWrite(s, data)
llStreamWrite(s, "\n")
proc llStreamWrite(s: PLLStream, data: char) =
proc llStreamWrite*(s: PLLStream, data: char) =
var c: char
case s.kind
of llsNone, llsStdIn:
@@ -194,7 +179,7 @@ proc llStreamWrite(s: PLLStream, data: char) =
c = data
discard writeBuffer(s.f, addr(c), sizeof(c))
proc llStreamWrite(s: PLLStream, buf: pointer, buflen: int) =
proc llStreamWrite*(s: PLLStream, buf: pointer, buflen: int) =
case s.kind
of llsNone, llsStdIn:
discard
@@ -206,7 +191,7 @@ proc llStreamWrite(s: PLLStream, buf: pointer, buflen: int) =
of llsFile:
discard writeBuffer(s.f, buf, buflen)
proc llStreamReadAll(s: PLLStream): string =
proc llStreamReadAll*(s: PLLStream): string =
const
bufSize = 2048
case s.kind

View File

@@ -1,7 +1,7 @@
#
#
# The Nim Compiler
# (c) Copyright 2012 Andreas Rumpf
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -9,8 +9,8 @@
# This module implements lookup helpers.
import
intsets, ast, astalgo, idents, semdata, types, msgs, options, rodread,
import
intsets, ast, astalgo, idents, semdata, types, msgs, options, rodread,
renderer, wordrecg, idgen, nimfix.prettybase
proc ensureNoMissingOrUnusedSymbols(scope: PScope)
@@ -22,7 +22,9 @@ proc considerQuotedIdent*(n: PNode): PIdent =
of nkSym: result = n.sym.name
of nkAccQuoted:
case n.len
of 0: globalError(n.info, errIdentifierExpected, renderTree(n))
of 0:
localError(n.info, errIdentifierExpected, renderTree(n))
result = getIdent"<Error>"
of 1: result = considerQuotedIdent(n.sons[0])
else:
var id = ""
@@ -31,12 +33,15 @@ proc considerQuotedIdent*(n: PNode): PIdent =
case x.kind
of nkIdent: id.add(x.ident.s)
of nkSym: id.add(x.sym.name.s)
else: globalError(n.info, errIdentifierExpected, renderTree(n))
else:
localError(n.info, errIdentifierExpected, renderTree(n))
return getIdent"<Error>"
result = getIdent(id)
of nkOpenSymChoice, nkClosedSymChoice: result = n.sons[0].sym.name
else:
globalError(n.info, errIdentifierExpected, renderTree(n))
localError(n.info, errIdentifierExpected, renderTree(n))
result = getIdent"<Error>"
template addSym*(scope: PScope, s: PSym) =
strTableAdd(scope.symbols, s)
@@ -82,6 +87,16 @@ proc searchInScopes*(c: PContext, s: PIdent): PSym =
if result != nil: return
result = nil
proc debugScopes*(c: PContext; limit=0) {.deprecated.} =
var i = 0
for scope in walkScopes(c.currentScope):
echo "scope ", i
for h in 0 .. high(scope.symbols.data):
if scope.symbols.data[h] != nil:
echo scope.symbols.data[h].name.s
if i == limit: break
inc i
proc searchInScopes*(c: PContext, s: PIdent, filter: TSymKinds): PSym =
for scope in walkScopes(c.currentScope):
result = strTableGet(scope.symbols, s)
@@ -93,7 +108,7 @@ proc errorSym*(c: PContext, n: PNode): PSym =
var m = n
# ensure that 'considerQuotedIdent' can't fail:
if m.kind == nkDotExpr: m = m.sons[1]
let ident = if m.kind in {nkIdent, nkSym, nkAccQuoted}:
let ident = if m.kind in {nkIdent, nkSym, nkAccQuoted}:
considerQuotedIdent(m)
else:
getIdent("err:" & renderTree(m))
@@ -104,11 +119,11 @@ proc errorSym*(c: PContext, n: PNode): PSym =
if gCmd != cmdInteractive and c.inCompilesContext == 0:
c.importTable.addSym(result)
type
TOverloadIterMode* = enum
type
TOverloadIterMode* = enum
oimDone, oimNoQualifier, oimSelfModule, oimOtherModule, oimSymChoice,
oimSymChoiceLocalLookup
TOverloadIter*{.final.} = object
TOverloadIter*{.final.} = object
it*: TIdentIter
m*: PSym
mode*: TOverloadIterMode
@@ -116,7 +131,7 @@ type
scope*: PScope
inSymChoice: IntSet
proc getSymRepr*(s: PSym): string =
proc getSymRepr*(s: PSym): string =
case s.kind
of skProc, skMethod, skConverter, skIterators: result = getProcHeader(s)
else: result = s.name.s
@@ -133,7 +148,7 @@ proc ensureNoMissingOrUnusedSymbols(scope: PScope) =
if missingImpls == 0:
localError(s.info, errImplOfXexpected, getSymRepr(s))
inc missingImpls
elif {sfUsed, sfExported} * s.flags == {} and optHints in s.options:
elif {sfUsed, sfExported} * s.flags == {} and optHints in s.options:
# BUGFIX: check options in s!
if s.kind notin {skForVar, skParam, skMethod, skUnknown, skGenericParam}:
# XXX: implicit type params are currently skTypes
@@ -141,11 +156,11 @@ proc ensureNoMissingOrUnusedSymbols(scope: PScope) =
if s.typ != nil and tfImplicitTypeParam notin s.typ.flags:
message(s.info, hintXDeclaredButNotUsed, getSymRepr(s))
s = nextIter(it, scope.symbols)
proc wrongRedefinition*(info: TLineInfo, s: string) =
if gCmd != cmdInteractive:
localError(info, errAttemptToRedefine, s)
proc addDecl*(c: PContext, sym: PSym) =
if not c.currentScope.addUniqueSym(sym):
wrongRedefinition(sym.info, sym.name.s)
@@ -157,27 +172,27 @@ proc addDeclAt*(scope: PScope, sym: PSym) =
if not scope.addUniqueSym(sym):
wrongRedefinition(sym.info, sym.name.s)
proc addInterfaceDeclAux(c: PContext, sym: PSym) =
proc addInterfaceDeclAux(c: PContext, sym: PSym) =
if sfExported in sym.flags:
# add to interface:
if c.module != nil: strTableAdd(c.module.tab, sym)
else: internalError(sym.info, "AddInterfaceDeclAux")
else: internalError(sym.info, "addInterfaceDeclAux")
proc addInterfaceDeclAt*(c: PContext, scope: PScope, sym: PSym) =
addDeclAt(scope, sym)
addInterfaceDeclAux(c, sym)
proc addOverloadableSymAt*(scope: PScope, fn: PSym) =
if fn.kind notin OverloadableSyms:
if fn.kind notin OverloadableSyms:
internalError(fn.info, "addOverloadableSymAt")
return
let check = strTableGet(scope.symbols, fn.name)
if check != nil and check.kind notin OverloadableSyms:
if check != nil and check.kind notin OverloadableSyms:
wrongRedefinition(fn.info, fn.name.s)
else:
scope.addSym(fn)
proc addInterfaceDecl*(c: PContext, sym: PSym) =
proc addInterfaceDecl*(c: PContext, sym: PSym) =
# it adds the symbol to the interface if appropriate
addDecl(c, sym)
addInterfaceDeclAux(c, sym)
@@ -206,7 +221,7 @@ when defined(nimfix):
else:
template fixSpelling(n: PNode; ident: PIdent; op: expr) = discard
proc lookUp*(c: PContext, n: PNode): PSym =
proc lookUp*(c: PContext, n: PNode): PSym =
# Looks up a symbol. Generates an error in case of nil.
case n.kind
of nkIdent:
@@ -227,12 +242,12 @@ proc lookUp*(c: PContext, n: PNode): PSym =
else:
internalError(n.info, "lookUp")
return
if contains(c.ambiguousSymbols, result.id):
if contains(c.ambiguousSymbols, result.id):
localError(n.info, errUseQualifier, result.name.s)
if result.kind == skStub: loadStub(result)
type
TLookupFlag* = enum
type
TLookupFlag* = enum
checkAmbiguity, checkUndeclared
proc qualifiedLookUp*(c: PContext, n: PNode, flags = {checkUndeclared}): PSym =
@@ -279,7 +294,7 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags = {checkUndeclared}): PSym =
else:
result = nil
if result != nil and result.kind == skStub: loadStub(result)
proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
case n.kind
of nkIdent, nkAccQuoted:
@@ -296,17 +311,17 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
of nkSym:
result = n.sym
o.mode = oimDone
of nkDotExpr:
of nkDotExpr:
o.mode = oimOtherModule
o.m = qualifiedLookUp(c, n.sons[0])
if o.m != nil and o.m.kind == skModule:
var ident: PIdent = nil
if n.sons[1].kind == nkIdent:
if n.sons[1].kind == nkIdent:
ident = n.sons[1].ident
elif n.sons[1].kind == nkAccQuoted:
ident = considerQuotedIdent(n.sons[1])
if ident != nil:
if o.m == c.module:
if ident != nil:
if o.m == c.module:
# a module may access its private members:
result = initIdentIter(o.it, c.topLevelScope.symbols,
ident).skipAlias(n)
@@ -314,7 +329,7 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
else:
result = initIdentIter(o.it, o.m.tab, ident).skipAlias(n)
else:
localError(n.sons[1].info, errIdentifierExpected,
localError(n.sons[1].info, errIdentifierExpected,
renderTree(n.sons[1]))
result = errorSym(c, n.sons[1])
of nkClosedSymChoice, nkOpenSymChoice:
@@ -332,12 +347,12 @@ proc lastOverloadScope*(o: TOverloadIter): int =
of oimSelfModule: result = 1
of oimOtherModule: result = 0
else: result = -1
proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
case o.mode
of oimDone:
of oimDone:
result = nil
of oimNoQualifier:
of oimNoQualifier:
if o.scope != nil:
result = nextIdentIter(o.it, o.scope.symbols).skipAlias(n)
while result == nil:
@@ -345,13 +360,13 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
if o.scope == nil: break
result = initIdentIter(o.it, o.scope.symbols, o.it.name).skipAlias(n)
# BUGFIX: o.it.name <-> n.ident
else:
else:
result = nil
of oimSelfModule:
of oimSelfModule:
result = nextIdentIter(o.it, c.topLevelScope.symbols).skipAlias(n)
of oimOtherModule:
of oimOtherModule:
result = nextIdentIter(o.it, o.m.tab).skipAlias(n)
of oimSymChoice:
of oimSymChoice:
if o.symChoiceIndex < sonsLen(n):
result = n.sons[o.symChoiceIndex].sym
incl(o.inSymChoice, result.id)
@@ -374,7 +389,7 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
if o.scope == nil: break
result = firstIdentExcluding(o.it, o.scope.symbols,
n.sons[0].sym.name, o.inSymChoice).skipAlias(n)
if result != nil and result.kind == skStub: loadStub(result)
proc pickSym*(c: PContext, n: PNode; kind: TSymKind;

View File

@@ -1,7 +1,7 @@
#
#
# The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -13,9 +13,12 @@ const
genPrefix* = ":tmp" # prefix for generated names
import ast, astalgo, types, idents, magicsys, msgs, options
from guards import createMagic
from trees import getMagic
proc newDeref*(n: PNode): PNode {.inline.} =
result = newNodeIT(nkHiddenDeref, n.info, n.typ.sons[0])
addSon(result, n)
proc newTupleAccess*(tup: PNode, i: int): PNode =
result = newNodeIT(nkBracketExpr, tup.info, tup.typ.skipTypes(
abstractInst).sons[i])
@@ -24,7 +27,7 @@ proc newTupleAccess*(tup: PNode, i: int): PNode =
lit.intVal = i
addSon(result, lit)
proc addVar*(father, v: PNode) =
proc addVar*(father, v: PNode) =
var vpart = newNodeI(nkIdentDefs, v.info, 3)
vpart.sons[0] = v
vpart.sons[1] = ast.emptyNode
@@ -54,7 +57,7 @@ proc lowerTupleUnpacking*(n: PNode; owner: PSym): PNode =
let tempAsNode = newSymNode(temp)
v.addVar(tempAsNode)
result.add(v)
result.add newAsgnStmt(tempAsNode, value)
for i in 0 .. n.len-3:
if n.sons[i].kind == nkSym: v.addVar(n.sons[i])
@@ -71,7 +74,7 @@ proc rawAddField*(obj: PType; field: PSym) =
field.position = sonsLen(obj.n)
addSon(obj.n, newSymNode(field))
proc rawIndirectAccess*(a: PNode; field: PSym; info: TLineInfo): PNode =
proc rawIndirectAccess*(a: PNode; field: PSym; info: TLineInfo): PNode =
# returns a[].field as a node
assert field.kind == skField
var deref = newNodeI(nkHiddenDeref, info)
@@ -110,7 +113,7 @@ proc newDotExpr(obj, b: PSym): PNode =
addSon(result, newSymNode(field))
result.typ = field.typ
proc indirectAccess*(a: PNode, b: string, info: TLineInfo): PNode =
proc indirectAccess*(a: PNode, b: string, info: TLineInfo): PNode =
# returns a[].b as a node
var deref = newNodeI(nkHiddenDeref, info)
deref.typ = a.typ.skipTypes(abstractInst).sons[0]
@@ -145,7 +148,7 @@ proc getFieldFromObj*(t: PType; v: PSym): PSym =
if t == nil: break
t = t.skipTypes(abstractInst)
proc indirectAccess*(a: PNode, b: PSym, info: TLineInfo): PNode =
proc indirectAccess*(a: PNode, b: PSym, info: TLineInfo): PNode =
# returns a[].b as a node
result = indirectAccess(a, b.name.s & $b.id, info)
@@ -159,11 +162,11 @@ proc genAddrOf*(n: PNode): PNode =
result.typ.rawAddSon(n.typ)
proc genDeref*(n: PNode): PNode =
result = newNodeIT(nkHiddenDeref, n.info,
result = newNodeIT(nkHiddenDeref, n.info,
n.typ.skipTypes(abstractInst).sons[0])
result.add n
proc callCodegenProc*(name: string, arg1: PNode;
proc callCodegenProc*(name: string, arg1: PNode;
arg2, arg3: PNode = nil): PNode =
result = newNodeI(nkCall, arg1.info)
let sym = magicsys.getCompilerProc(name)
@@ -204,6 +207,17 @@ proc flowVarKind(t: PType): TFlowVarKind =
elif containsGarbageCollectedRef(t): fvInvalid
else: fvBlob
proc typeNeedsNoDeepCopy(t: PType): bool =
var t = t.skipTypes(abstractInst)
# for the tconvexhull example (and others) we're a bit lax here and pretend
# seqs and strings are *by value* only and 'shallow' doesn't exist!
if t.kind == tyString: return true
# note that seq[T] is fine, but 'var seq[T]' is not, so we need to skip 'var'
# for the stricter check and likewise we can skip 'seq' for a less
# strict check:
if t.kind in {tyVar, tySequence}: t = t.sons[0]
result = not containsGarbageCollectedRef(t)
proc addLocalVar(varSection, varInit: PNode; owner: PSym; typ: PType;
v: PNode; useShallowCopy=false): PSym =
result = newSym(skTemp, getIdent(genPrefix), owner, varSection.info)
@@ -216,7 +230,7 @@ proc addLocalVar(varSection, varInit: PNode; owner: PSym; typ: PType;
vpart.sons[2] = if varInit.isNil: v else: ast.emptyNode
varSection.add vpart
if varInit != nil:
if useShallowCopy:
if useShallowCopy and typeNeedsNoDeepCopy(typ):
varInit.add newFastAsgnStmt(newSymNode(result), v)
else:
let deepCopyCall = newNodeI(nkCall, varInit.info, 3)
@@ -237,10 +251,10 @@ proc f_wrapper(thread, args) =
fv.owner = thread # optional
nimArgsPassingDone() # signal parent that the work is done
#
#
args.fv.blob = f(a, b, ...)
nimFlowVarSignal(args.fv)
# - or -
f(a, b, ...)
barrierLeave(args.barrier) # for parallel statement
@@ -262,7 +276,7 @@ proc createWrapperProc(f: PNode; threadParam, argsParam: PSym;
var threadLocalBarrier: PSym
if barrier != nil:
var varSection2 = newNodeI(nkVarSection, barrier.info)
threadLocalBarrier = addLocalVar(varSection2, nil, argsParam.owner,
threadLocalBarrier = addLocalVar(varSection2, nil, argsParam.owner,
barrier.typ, barrier)
body.add varSection2
body.add callCodegenProc("barrierEnter", threadLocalBarrier.newSymNode)
@@ -286,7 +300,7 @@ proc createWrapperProc(f: PNode; threadParam, argsParam: PSym;
elif fv != nil:
let fk = fv.typ.sons[1].flowVarKind
if fk == fvInvalid:
localError(f.info, "cannot create a flowVar of type: " &
localError(f.info, "cannot create a flowVar of type: " &
typeToString(fv.typ.sons[1]))
body.add newAsgnStmt(indirectAccess(threadLocalProm.newSymNode,
if fk == fvGC: "data" else: "blob", fv.info), call)
@@ -331,8 +345,8 @@ proc createCastExpr(argsParam: PSym; objType: PType): PNode =
result.typ = newType(tyPtr, objType.owner)
result.typ.rawAddSon(objType)
proc setupArgsForConcurrency(n: PNode; objType: PType; scratchObj: PSym,
castExpr, call,
proc setupArgsForConcurrency(n: PNode; objType: PType; scratchObj: PSym,
castExpr, call,
varSection, varInit, result: PNode) =
let formals = n[0].typ.n
let tmpName = getIdent(genPrefix)
@@ -372,11 +386,11 @@ proc getRoot*(n: PNode): PSym =
if getMagic(n) == mSlice: result = getRoot(n.sons[1])
else: discard
proc newIntLit(value: BiggestInt): PNode =
proc newIntLit*(value: BiggestInt): PNode =
result = nkIntLit.newIntNode(value)
result.typ = getSysType(tyInt)
proc genHigh(n: PNode): PNode =
proc genHigh*(n: PNode): PNode =
if skipTypes(n.typ, abstractVar).kind in {tyArrayConstr, tyArray}:
result = newIntLit(lastOrd(skipTypes(n.typ, abstractVar)))
else:
@@ -386,7 +400,7 @@ proc genHigh(n: PNode): PNode =
result.sons[1] = n
proc setupArgsForParallelism(n: PNode; objType: PType; scratchObj: PSym;
castExpr, call,
castExpr, call,
varSection, varInit, result: PNode) =
let formals = n[0].typ.n
let tmpName = getIdent(genPrefix)
@@ -410,7 +424,7 @@ proc setupArgsForParallelism(n: PNode; objType: PType; scratchObj: PSym;
var fieldB = newSym(skField, tmpName, objType.owner, n.info)
fieldB.typ = getSysType(tyInt)
objType.addField(fieldB)
if getMagic(n) == mSlice:
let a = genAddrOf(n[1])
field.typ = a.typ
@@ -465,7 +479,7 @@ proc setupArgsForParallelism(n: PNode; objType: PType; scratchObj: PSym;
useShallowCopy=true)
call.add(threadLocal.newSymNode)
proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType;
proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType;
barrier, dest: PNode = nil): PNode =
# if 'barrier' != nil, then it is in a 'parallel' section and we
# generate quite different code
@@ -531,10 +545,10 @@ proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType;
var varSection = newNodeI(nkVarSection, n.info)
var varInit = newNodeI(nkStmtList, n.info)
if barrier.isNil:
setupArgsForConcurrency(n, objType, scratchObj, castExpr, call,
setupArgsForConcurrency(n, objType, scratchObj, castExpr, call,
varSection, varInit, result)
else:
setupArgsForParallelism(n, objType, scratchObj, castExpr, call,
setupArgsForParallelism(n, objType, scratchObj, castExpr, call,
varSection, varInit, result)
var barrierAsExpr: PNode = nil
@@ -567,7 +581,7 @@ proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType;
fvAsExpr = indirectAccess(castExpr, field, n.info)
result.add newFastAsgnStmt(newDotExpr(scratchObj, field), genAddrOf(dest))
let wrapper = createWrapperProc(fn, threadParam, argsParam,
let wrapper = createWrapperProc(fn, threadParam, argsParam,
varSection, varInit, call,
barrierAsExpr, fvAsExpr, spawnKind)
result.add callCodegenProc("nimSpawn", wrapper.newSymNode,

View File

@@ -1,7 +1,7 @@
#
#
# The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -19,12 +19,6 @@ import
from magicsys import systemModule, resetSysTypes
const
hasLLVM_Backend = false
when hasLLVM_Backend:
import llvmgen
proc rodPass =
if optSymbolFiles in gGlobalOptions:
registerPass(rodwritePass)
@@ -60,20 +54,16 @@ proc commandDoc2 =
finishDoc2Pass(gProjectName)
proc commandCompileToC =
extccomp.initVars()
semanticPasses()
registerPass(cgenPass)
rodPass()
#registerPass(cleanupPass())
if optCaasEnabled in gGlobalOptions:
# echo "BEFORE CHECK DEP"
# discard checkDepMem(gProjectMainIdx)
# echo "CHECK DEP COMPLETE"
discard
compileProject()
cgenWriteModules()
if gCmd != cmdRun:
extccomp.callCCompiler(changeFileExt(gProjectFull, ""))
extccomp.callCCompiler(if gProjectName == "-": "stdinfile" else: changeFileExt(gProjectFull, ""))
if isServing:
# caas will keep track only of the compilation commands
@@ -111,14 +101,6 @@ proc commandCompileToC =
ccgutils.resetCaches()
GC_fullCollect()
when hasLLVM_Backend:
proc commandCompileToLLVM =
semanticPasses()
registerPass(llvmgen.llvmgenPass())
rodPass()
#registerPass(cleanupPass())
compileProject()
proc commandCompileToJS =
#incl(gGlobalOptions, optSafeCode)
setTarget(osJS, cpuJS)
@@ -188,20 +170,18 @@ proc commandSuggest =
# cache in a state where "no recompilation is necessary", but the
# cgen pass was never executed at all.
commandCompileToC()
if gDirtyBufferIdx != 0:
discard compileModule(gDirtyBufferIdx, {sfDirty})
resetModule(gDirtyBufferIdx)
if optDef in gGlobalOptions:
defFromSourceMap(optTrackPos)
let gDirtyBufferIdx = gTrackPos.fileIndex
discard compileModule(gDirtyBufferIdx, {sfDirty})
resetModule(gDirtyBufferIdx)
else:
msgs.gErrorMax = high(int) # do not stop after first error
semanticPasses()
rodPass()
# XXX: this handles the case when the dirty buffer is the main file,
# but doesn't handle the case when it's imported module
var projFile = if gProjectMainIdx == gDirtyOriginalIdx: gDirtyBufferIdx
else: gProjectMainIdx
compileProject(projFile)
#var projFile = if gProjectMainIdx == gDirtyOriginalIdx: gDirtyBufferIdx
# else: gProjectMainIdx
compileProject() #(projFile)
proc resetMemory =
resetCompilationLists()
@@ -232,7 +212,6 @@ proc resetMemory =
# rodread.gMods
# !! ropes.cache
# semthreads.computed?
#
# suggest.usageSym
#
@@ -273,7 +252,6 @@ proc mainCommand* =
commandCompileToC()
of "cpp", "compiletocpp":
gCmd = cmdCompileToCpp
if cCompiler == ccGcc: setCC("gcc")
defineSymbol("cpp")
commandCompileToC()
of "objc", "compiletooc":
@@ -290,12 +268,6 @@ proc mainCommand* =
of "js", "compiletojs":
gCmd = cmdCompileToJS
commandCompileToJS()
of "compiletollvm":
gCmd = cmdCompileToLLVM
when hasLLVM_Backend:
CommandCompileToLLVM()
else:
rawMessage(errInvalidCommandX, command)
of "doc":
wantMainModule()
gCmd = cmdDoc
@@ -392,7 +364,9 @@ proc mainCommand* =
gVerbosity > 0):
rawMessage(hintSuccessX, [$gLinesCompiled,
formatFloat(epochTime() - gLastCmdTime, ffDecimal, 3),
formatSize(getTotalMem())])
formatSize(getTotalMem()),
if condSyms.isDefined("release"): "Release Build"
else: "Debug Build"])
when PrintRopeCacheStats:
echo "rope cache stats: "

View File

@@ -1,7 +1,7 @@
#
#
# The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -34,9 +34,6 @@ proc getModule(fileIdx: int32): PSym =
if fileIdx >= 0 and fileIdx < gCompiledModules.len:
result = gCompiledModules[fileIdx]
template compiledAt(x: PSym): expr =
gMemCacheData[x.position].compiledAt
template crc(x: PSym): expr =
gMemCacheData[x.position].crc
@@ -74,10 +71,12 @@ proc addDep(x: PSym, dep: int32) =
proc resetModule*(fileIdx: int32) =
# echo "HARD RESETTING ", fileIdx.toFilename
gMemCacheData[fileIdx].needsRecompile = Yes
gCompiledModules[fileIdx] = nil
cgendata.gModules[fileIdx] = nil
resetSourceMap(fileIdx)
if fileIdx <% gMemCacheData.len:
gMemCacheData[fileIdx].needsRecompile = Yes
if fileIdx <% gCompiledModules.len:
gCompiledModules[fileIdx] = nil
if fileIdx <% cgendata.gModules.len:
cgendata.gModules[fileIdx] = nil
proc resetAllModules* =
for i in 0..gCompiledModules.high:
@@ -117,7 +116,7 @@ proc newModule(fileIdx: int32): PSym =
result.kind = skModule
let filename = fileIdx.toFullPath
result.name = getIdent(splitFile(filename).name)
if not isNimIdentifier(result.name.s):
if result.name.s != "-" and not isNimIdentifier(result.name.s):
rawMessage(errInvalidModuleName, result.name.s)
result.info = newLineInfo(fileIdx, 1, 1)

File diff suppressed because it is too large Load Diff

View File

@@ -9,13 +9,14 @@
when defined(gcc) and defined(windows):
when defined(x86):
{.link: "icons/nimrod.res".}
{.link: "icons/nim.res".}
else:
{.link: "icons/nimrod_icon.o".}
{.link: "icons/nim_icon.o".}
import
commands, lexer, condsyms, options, msgs, nversion, nimconf, ropes,
extccomp, strutils, os, osproc, platform, main, parseopt, service
extccomp, strutils, os, osproc, platform, main, parseopt, service,
nodejs
when hasTinyCBackend:
import tccgen
@@ -60,6 +61,8 @@ proc handleCmdLine() =
if gCmd == cmdRun:
tccgen.run(commands.arguments)
if optRun in gGlobalOptions:
if gProjectName == "-":
gProjectFull = "stdinfile"
if gCmd == cmdCompileToJS:
var ex: string
if options.outFile.len > 0:
@@ -67,7 +70,7 @@ proc handleCmdLine() =
else:
ex = quoteShell(
completeCFilePath(changeFileExt(gProjectFull, "js").prependCurDir))
execExternalProgram("node " & ex & ' ' & commands.arguments)
execExternalProgram(findNodeJs() & " " & ex & ' ' & commands.arguments)
else:
var binPath: string
if options.outFile.len > 0:
@@ -89,4 +92,4 @@ condsyms.initDefines()
when not defined(selftest):
handleCmdLine()
quit(int8(msgs.gErrorCounter > 0))
msgQuit(int8(msgs.gErrorCounter > 0))

View File

@@ -1,7 +1,5 @@
# Special configuration file for the Nim project
# gc:markAndSweep
hint[XDeclaredButNotUsed]:off
path:"llvm"
path:"$projectPath/.."
@@ -20,3 +18,4 @@ define:useStdoutAsStdmsg
cs:partial
#define:useNodeIds
symbol:nimfix
#gc:markAndSweep

View File

@@ -33,7 +33,7 @@ proc `<.`(a, b: string): bool =
while true:
let ii = parseInt(a, verA, i)
let jj = parseInt(b, verB, j)
# if A has no number left, but B has, B is prefered: 0.8 vs 0.8.3
# if A has no number left, but B has, B is preferred: 0.8 vs 0.8.3
if ii <= 0 or jj <= 0: return jj > 0
if verA < verB: return true
elif verA > verB: return false

View File

@@ -11,10 +11,10 @@
import
llstream, nversion, commands, os, strutils, msgs, platform, condsyms, lexer,
options, idents, wordrecg
options, idents, wordrecg, strtabs
# ---------------- configuration file parser -----------------------------
# we use Nim's scanner here to safe space and work
# we use Nim's scanner here to save space and work
proc ppGetTok(L: var TLexer, tok: var TToken) =
# simple filter
@@ -82,17 +82,17 @@ proc doElif(L: var TLexer, tok: var TToken) =
proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest) =
var nestedIfs = 0
while true:
if (tok.ident != nil) and (tok.ident.s == "@"):
if tok.ident != nil and tok.ident.s == "@":
ppGetTok(L, tok)
case whichKeyword(tok.ident)
of wIf:
inc(nestedIfs)
of wElse:
if (dest == jdElseEndif) and (nestedIfs == 0):
if dest == jdElseEndif and nestedIfs == 0:
doElse(L, tok)
break
of wElif:
if (dest == jdElseEndif) and (nestedIfs == 0):
if dest == jdElseEndif and nestedIfs == 0:
doElif(L, tok)
break
of wEnd:
@@ -119,9 +119,10 @@ proc parseDirective(L: var TLexer, tok: var TToken) =
of wElif: doElif(L, tok)
of wElse: doElse(L, tok)
of wEnd: doEnd(L, tok)
of wWrite:
of wWrite:
ppGetTok(L, tok)
msgs.msgWriteln(tokToStr(tok))
msgs.msgWriteln(strtabs.`%`(tokToStr(tok), options.gConfigVars,
{useEnvironment, useKey}))
ppGetTok(L, tok)
else:
case tok.ident.s.normalize
@@ -157,7 +158,7 @@ proc checkSymbol(L: TLexer, tok: TToken) =
proc parseAssignment(L: var TLexer, tok: var TToken) =
if tok.ident.id == getIdent("-").id or tok.ident.id == getIdent("--").id:
confTok(L, tok) # skip unnecessary prefix
var info = getLineInfo(L, tok) # safe for later in case of an error
var info = getLineInfo(L, tok) # save for later in case of an error
checkSymbol(L, tok)
var s = tokToStr(tok)
confTok(L, tok) # skip symbol
@@ -178,9 +179,10 @@ proc parseAssignment(L: var TLexer, tok: var TToken) =
if tok.tokType == tkBracketRi: confTok(L, tok)
else: lexMessage(L, errTokenExpected, "']'")
add(val, ']')
if tok.tokType in {tkColon, tkEquals}:
let percent = tok.ident.id == getIdent("%=").id
if tok.tokType in {tkColon, tkEquals} or percent:
if len(val) > 0: add(val, ':')
confTok(L, tok) # skip ':' or '='
confTok(L, tok) # skip ':' or '=' or '%'
checkSymbol(L, tok)
add(val, tokToStr(tok))
confTok(L, tok) # skip symbol
@@ -189,7 +191,11 @@ proc parseAssignment(L: var TLexer, tok: var TToken) =
checkSymbol(L, tok)
add(val, tokToStr(tok))
confTok(L, tok)
processSwitch(s, val, passPP, info)
if percent:
processSwitch(s, strtabs.`%`(val, options.gConfigVars,
{useEnvironment, useEmpty}), passPP, info)
else:
processSwitch(s, val, passPP, info)
proc readConfigFile(filename: string) =
var
@@ -246,6 +252,11 @@ proc loadConfigs*(cfg: string) =
if gProjectName.len != 0:
# new project wide config file:
let projectConfig = changeFileExt(gProjectFull, "nim.cfg")
if fileExists(projectConfig): readConfigFile(projectConfig)
else: readConfigFile(changeFileExt(gProjectFull, "nimrod.cfg"))
var projectConfig = changeFileExt(gProjectFull, "nimcfg")
if not fileExists(projectConfig):
projectConfig = changeFileExt(gProjectFull, "nim.cfg")
if not fileExists(projectConfig):
projectConfig = changeFileExt(gProjectFull, "nimrod.cfg")
if fileExists(projectConfig):
rawMessage(warnDeprecated, projectConfig)
readConfigFile(projectConfig)

View File

@@ -24,7 +24,7 @@ proc execute*(program: string) =
when hasFFI: defineSymbol("nimffi")
registerPass(verbosePass)
registerPass(semPass)
registerPass(vmPass)
registerPass(evalPass)
appendStr(searchPaths, options.libpath)
compileSystemModule()

View File

@@ -1,7 +1,7 @@
#
#
# The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.

View File

@@ -2,10 +2,10 @@
# gc:markAndSweep
hint[XDeclaredButNotUsed]:off
path:"$projectPath/../.."
path:"$projectPath/.."
path:"$lib/packages/docutils"
path:"$nim/compiler"
path:"../../compiler"
define:useStdoutAsStdmsg
symbol:nimfix

View File

@@ -1,7 +1,7 @@
#
#
# The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.

View File

@@ -1,7 +1,7 @@
#
#
# The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.

View File

@@ -166,4 +166,4 @@ proc getCurrentLine(L: TBaseLexer, marker: bool = true): string =
inc(i)
result.add("\n")
if marker:
result.add(repeatChar(getColNumber(L, L.bufpos)) & '^' & "\n")
result.add(spaces(getColNumber(L, L.bufpos)) & '^' & "\n")

View File

@@ -0,0 +1,198 @@
#
#
# The Nim Compiler
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Nimsuggest is a tool that helps to give editors IDE like capabilities.
import strutils, os, parseopt, parseUtils
import options, commands, modules, sem, passes, passaux, msgs, nimconf,
extccomp, condsyms, lists, net, rdstdin
const Usage = """
Nimsuggest - Tool to give every editor IDE like capabilities for Nim
Usage:
nimsuggest [options] projectfile.nim
Options:
--port:PORT port, by default 6000
--address:HOST binds to that address, by default ""
--stdin read commands from stdin and write results to
stdout instead of using sockets
The server then listens to the connection and takes line-based commands.
In addition, all command line options of Nim that do not affect code generation
are supported.
"""
var
gPort = 6000.Port
gAddress = ""
gUseStdin: bool
const
seps = {':', ';', ' ', '\t'}
Help = "usage: sug|con|def|use file.nim[;dirtyfile.nim]:line:col\n"&
"type 'quit' to quit\n" &
"type 'debug' to toggle debug mode on/off\n" &
"type 'terse' to toggle terse mode on/off"
proc parseQuoted(cmd: string; outp: var string; start: int): int =
var i = start
i += skipWhitespace(cmd, i)
if cmd[i] == '"':
i += parseUntil(cmd, outp, '"', i+1)+2
else:
i += parseUntil(cmd, outp, seps, i)
result = i
proc action(cmd: string) =
template toggle(sw) =
if sw in gGlobalOptions:
excl(gGlobalOptions, sw)
else:
incl(gGlobalOptions, sw)
return
template err() =
echo Help
return
var opc = ""
var i = parseIdent(cmd, opc, 0)
case opc.normalize
of "sug": gIdeCmd = ideSug
of "con": gIdeCmd = ideCon
of "def": gIdeCmd = ideDef
of "use":
modules.resetAllModules()
gIdeCmd = ideUse
of "quit": quit()
of "debug": toggle optIdeDebug
of "terse": toggle optIdeTerse
else: err()
var dirtyfile = ""
var orig = ""
i = parseQuoted(cmd, orig, i)
if cmd[i] == ';':
i = parseQuoted(cmd, dirtyfile, i+1)
i += skipWhile(cmd, seps, i)
var line = -1
var col = 0
i += parseInt(cmd, line, i)
i += skipWhile(cmd, seps, i)
i += parseInt(cmd, col, i)
var isKnownFile = true
if orig.len == 0: err()
let dirtyIdx = orig.fileInfoIdx(isKnownFile)
if dirtyfile.len != 0: msgs.setDirtyFile(dirtyIdx, dirtyfile)
else: msgs.setDirtyFile(dirtyIdx, nil)
resetModule dirtyIdx
if dirtyIdx != gProjectMainIdx:
resetModule gProjectMainIdx
gTrackPos = newLineInfo(dirtyIdx, line, col-1)
#echo dirtyfile, gDirtyBufferIdx, " project ", gProjectMainIdx
gErrorCounter = 0
if not isKnownFile:
compileProject(dirtyIdx)
else:
compileProject()
proc serve() =
# do not stop after the first error:
msgs.gErrorMax = high(int)
if gUseStdin:
echo Help
var line = ""
while readLineFromStdin("> ", line):
action line
echo ""
flushFile(stdout)
else:
var server = newSocket()
server.bindAddr(gPort, gAddress)
var inp = "".TaintedString
server.listen()
while true:
var stdoutSocket = newSocket()
msgs.writelnHook = proc (line: string) =
stdoutSocket.send(line & "\c\L")
accept(server, stdoutSocket)
stdoutSocket.readLine(inp)
action inp.string
stdoutSocket.send("\c\L")
stdoutSocket.close()
proc mainCommand =
registerPass verbosePass
registerPass semPass
gCmd = cmdIdeTools
incl gGlobalOptions, optCaasEnabled
isServing = true
wantMainModule()
appendStr(searchPaths, options.libpath)
if gProjectFull.len != 0:
# current path is always looked first for modules
prependStr(searchPaths, gProjectPath)
serve()
proc processCmdLine*(pass: TCmdLinePass, cmd: string) =
var p = parseopt.initOptParser(cmd)
while true:
parseopt.next(p)
case p.kind
of cmdEnd: break
of cmdLongoption, cmdShortOption:
case p.key.normalize
of "port": gPort = parseInt(p.val).Port
of "address": gAddress = p.val
of "stdin": gUseStdin = true
else: processSwitch(pass, p)
of cmdArgument:
options.gProjectName = unixToNativePath(p.key)
# if processArgument(pass, p, argsCount): break
proc handleCmdLine() =
if paramCount() == 0:
stdout.writeln(Usage)
else:
processCmdLine(passCmd1, "")
if gProjectName != "":
try:
gProjectFull = canonicalizePath(gProjectName)
except OSError:
gProjectFull = gProjectName
var p = splitFile(gProjectFull)
gProjectPath = p.dir
gProjectName = p.name
else:
gProjectPath = getCurrentDir()
loadConfigs(DefaultConfig) # load all config files
# now process command line arguments again, because some options in the
# command line can overwite the config file's settings
extccomp.initVars()
processCmdLine(passCmd2, "")
mainCommand()
when false:
proc quitCalled() {.noconv.} =
writeStackTrace()
addQuitProc(quitCalled)
condsyms.initDefines()
defineSymbol "nimsuggest"
handleCmdline()

View File

@@ -0,0 +1,17 @@
# Special configuration file for the Nim project
gc:markAndSweep
hint[XDeclaredButNotUsed]:off
path:"$projectPath/../.."
path:"$lib/packages/docutils"
path:"../../compiler"
define:useStdoutAsStdmsg
define:nimsuggest
cs:partial
#define:useNodeIds
define:booting
#define:noDocgen

6
compiler/nodejs.nim Normal file
View File

@@ -0,0 +1,6 @@
import os
proc findNodeJs*(): string =
result = findExe("nodejs")
if result == "":
result = findExe("node")

View File

@@ -1,7 +1,7 @@
#
#
# The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.

View File

@@ -9,7 +9,7 @@
import
os, lists, strutils, strtabs, osproc, sets
const
hasTinyCBackend* = defined(tinyc)
useEffectSystem* = true
@@ -21,10 +21,10 @@ const
type # please make sure we have under 32 options
# (improves code efficiency a lot!)
TOption* = enum # **keep binary compatible**
optNone, optObjCheck, optFieldCheck, optRangeCheck, optBoundsCheck,
optNone, optObjCheck, optFieldCheck, optRangeCheck, optBoundsCheck,
optOverflowCheck, optNilCheck,
optNaNCheck, optInfCheck,
optAssert, optLineDir, optWarns, optHints,
optAssert, optLineDir, optWarns, optHints,
optOptimizeSpeed, optOptimizeSize, optStackTrace, # stack tracing support
optLineTrace, # line tracing support (includes stack tracing)
optEndb, # embedded debugger
@@ -37,8 +37,8 @@ type # please make sure we have under 32 options
TOptions* = set[TOption]
TGlobalOption* = enum # **keep binary compatible**
gloptNone, optForceFullMake, optDeadCodeElim,
optListCmd, optCompileOnly, optNoLinking,
gloptNone, optForceFullMake, optDeadCodeElim,
optListCmd, optCompileOnly, optNoLinking,
optSafeCode, # only allow safe code
optCDebug, # turn on debugging information
optGenDynLib, # generate a dynamic library
@@ -56,23 +56,20 @@ type # please make sure we have under 32 options
optNoMain, # do not generate a "main" proc
optThreads, # support for multi-threading
optStdout, # output to stdout
optSuggest, # ideTools: 'suggest'
optContext, # ideTools: 'context'
optDef, # ideTools: 'def'
optUsages, # ideTools: 'usages'
optThreadAnalysis, # thread analysis pass
optTaintMode, # taint mode turned on
optTlsEmulation, # thread var emulation turned on
optGenIndex # generate index file for documentation;
optEmbedOrigSrc # embed the original source in the generated code
# also: generate header file
optIdeDebug # idetools: debug mode
optIdeTerse # idetools: use terse descriptions
TGlobalOptions* = set[TGlobalOption]
TCommands* = enum # Nim's commands
# **keep binary compatible**
cmdNone, cmdCompileToC, cmdCompileToCpp, cmdCompileToOC,
cmdCompileToJS, cmdCompileToLLVM, cmdInterpret, cmdPretty, cmdDoc,
cmdGenDepend, cmdDump,
cmdNone, cmdCompileToC, cmdCompileToCpp, cmdCompileToOC,
cmdCompileToJS, cmdCompileToLLVM, cmdInterpret, cmdPretty, cmdDoc,
cmdGenDepend, cmdDump,
cmdCheck, # semantic checking for whole project
cmdParse, # parse a single file (for debugging)
cmdScan, # scan a single file (for debugging)
@@ -86,13 +83,19 @@ type # please make sure we have under 32 options
TGCMode* = enum # the selected GC
gcNone, gcBoehm, gcMarkAndSweep, gcRefc, gcV2, gcGenerational
TIdeCmd* = enum
ideNone, ideSug, ideCon, ideDef, ideUse
var
gIdeCmd*: TIdeCmd
const
ChecksOptions* = {optObjCheck, optFieldCheck, optRangeCheck, optNilCheck,
ChecksOptions* = {optObjCheck, optFieldCheck, optRangeCheck, optNilCheck,
optOverflowCheck, optBoundsCheck, optAssert, optNaNCheck, optInfCheck}
var
gOptions*: TOptions = {optObjCheck, optFieldCheck, optRangeCheck,
optBoundsCheck, optOverflowCheck, optAssert, optWarns,
var
gOptions*: TOptions = {optObjCheck, optFieldCheck, optRangeCheck,
optBoundsCheck, optOverflowCheck, optAssert, optWarns,
optHints, optStackTrace, optLineTrace,
optPatterns, optNilCheck}
gGlobalOptions*: TGlobalOptions = {optThreadAnalysis}
@@ -111,18 +114,12 @@ var
gLastCmdTime*: float # when caas is enabled, we measure each command
gListFullPaths*: bool
isServing*: bool = false
gDirtyBufferIdx* = 0'i32 # indicates the fileIdx of the dirty version of
# the tracked source X, saved by the CAAS client.
gDirtyOriginalIdx* = 0'i32 # the original source file of the dirtified buffer.
gNoNimblePath* = false
gExperimentalMode*: bool
proc importantComments*(): bool {.inline.} = gCmd in {cmdDoc, cmdIdeTools}
proc usesNativeGC*(): bool {.inline.} = gSelectedGC >= gcRefc
template isWorkingWithDirtyBuffer*: expr =
gDirtyBufferIdx != 0
template compilationCachePresent*: expr =
{optCaasEnabled, optSymbolFiles} * gGlobalOptions != {}
@@ -132,7 +129,7 @@ template optPreserveOrigSource*: expr =
template optPrintSurroundingSrc*: expr =
gVerbosity >= 2
const
const
genSubDir* = "nimcache"
NimExt* = "nim"
RodExt* = "rod"
@@ -171,20 +168,20 @@ proc mainCommandArg*: string =
else:
result = gProjectName
proc existsConfigVar*(key: string): bool =
proc existsConfigVar*(key: string): bool =
result = hasKey(gConfigVars, key)
proc getConfigVar*(key: string): string =
proc getConfigVar*(key: string): string =
result = gConfigVars[key]
proc setConfigVar*(key, val: string) =
proc setConfigVar*(key, val: string) =
gConfigVars[key] = val
proc getOutFile*(filename, ext: string): string =
proc getOutFile*(filename, ext: string): string =
if options.outFile != "": result = options.outFile
else: result = changeFileExt(filename, ext)
proc getPrefixDir*(): string =
proc getPrefixDir*(): string =
## gets the application directory
result = splitPath(getAppDir()).head
@@ -192,22 +189,22 @@ proc canonicalizePath*(path: string): string =
when not FileSystemCaseSensitive: result = path.expandFilename.toLower
else: result = path.expandFilename
proc shortenDir*(dir: string): string =
proc shortenDir*(dir: string): string =
## returns the interesting part of a dir
var prefix = getPrefixDir() & DirSep
if startsWith(dir, prefix):
if startsWith(dir, prefix):
return substr(dir, len(prefix))
prefix = gProjectPath & DirSep
if startsWith(dir, prefix):
return substr(dir, len(prefix))
result = dir
proc removeTrailingDirSep*(path: string): string =
if (len(path) > 0) and (path[len(path) - 1] == DirSep):
proc removeTrailingDirSep*(path: string): string =
if (len(path) > 0) and (path[len(path) - 1] == DirSep):
result = substr(path, 0, len(path) - 2)
else:
else:
result = path
proc getGeneratedPath: string =
result = if nimcacheDir.len > 0: nimcacheDir else: gProjectPath.shortenDir /
genSubDir
@@ -260,7 +257,7 @@ proc withPackageName*(path: string): string =
let (p, file, ext) = path.splitFile
result = (p / (x & '_' & file)) & ext
proc toGeneratedFile*(path, ext: string): string =
proc toGeneratedFile*(path, ext: string): string =
## converts "/home/a/mymodule.nim", "rod" to "/home/a/nimcache/mymodule.rod"
var (head, tail) = splitPath(path)
#if len(head) > 0: head = shortenDir(head & dirSep)
@@ -286,25 +283,25 @@ when noTimeMachine:
var p = startProcess("/usr/bin/tmutil", args = ["addexclusion", dir])
discard p.waitForExit
p.close
except E_Base, EOS:
except Exception:
discard
proc completeGeneratedFilePath*(f: string, createSubDir: bool = true): string =
proc completeGeneratedFilePath*(f: string, createSubDir: bool = true): string =
var (head, tail) = splitPath(f)
#if len(head) > 0: head = removeTrailingDirSep(shortenDir(head & dirSep))
var subdir = getGeneratedPath() # / head
if createSubDir:
try:
try:
createDir(subdir)
when noTimeMachine:
excludeDirFromTimeMachine(subdir)
except OSError:
except OSError:
writeln(stdout, "cannot create directory: " & subdir)
quit(1)
result = joinPath(subdir, tail)
#echo "completeGeneratedFilePath(", f, ") = ", result
iterator iterSearchPath*(searchPaths: TLinkedList): string =
iterator iterSearchPath*(searchPaths: TLinkedList): string =
var it = PStrEntry(searchPaths.head)
while it != nil:
yield it.data
@@ -327,7 +324,7 @@ proc rawFindFile2(f: string): string =
it = PStrEntry(it.next)
result = ""
proc findFile*(f: string): string {.procvar.} =
proc findFile*(f: string): string {.procvar.} =
result = f.rawFindFile
if result.len == 0:
result = f.toLower.rawFindFile
@@ -354,7 +351,7 @@ proc findModule*(modulename, currentModule: string): string =
if not existsFile(result):
result = findFile(m)
proc libCandidates*(s: string, dest: var seq[string]) =
proc libCandidates*(s: string, dest: var seq[string]) =
var le = strutils.find(s, '(')
var ri = strutils.find(s, ')', le+1)
if le >= 0 and ri > le:
@@ -362,7 +359,7 @@ proc libCandidates*(s: string, dest: var seq[string]) =
var suffix = substr(s, ri + 1)
for middle in split(substr(s, le + 1, ri - 1), '|'):
libCandidates(prefix & middle & suffix, dest)
else:
else:
add(dest, s)
proc canonDynlibName(s: string): string =
@@ -379,17 +376,17 @@ proc inclDynlibOverride*(lib: string) =
proc isDynlibOverride*(lib: string): bool =
result = gDllOverrides.hasKey(lib.canonDynlibName)
proc binaryStrSearch*(x: openArray[string], y: string): int =
proc binaryStrSearch*(x: openArray[string], y: string): int =
var a = 0
var b = len(x) - 1
while a <= b:
while a <= b:
var mid = (a + b) div 2
var c = cmpIgnoreCase(x[mid], y)
if c < 0:
if c < 0:
a = mid + 1
elif c > 0:
elif c > 0:
b = mid - 1
else:
else:
return mid
result = - 1

View File

@@ -24,7 +24,7 @@ type
ppEof = 1, # end of compiled pattern
ppOr, # we could short-cut the evaluation for 'and' and 'or',
ppAnd, # but currently we don't
ppNot,
ppNot,
ppSym,
ppAtom,
ppLit,
@@ -41,7 +41,7 @@ type
const
MaxStackSize* = 64 ## max required stack size by the VM
proc patternError(n: PNode) =
proc patternError(n: PNode) =
localError(n.info, errIllFormedAstX, renderTree(n, {renderNoComments}))
proc add(code: var TPatternCode, op: TOpcode) {.inline.} =
@@ -56,7 +56,7 @@ proc whichAlias*(p: PSym): TAliasRequest =
proc compileConstraints(p: PNode, result: var TPatternCode) =
case p.kind
of nkCallKinds:
if p.sons[0].kind != nkIdent:
if p.sons[0].kind != nkIdent:
patternError(p.sons[0])
return
let op = p.sons[0].ident
@@ -131,11 +131,10 @@ proc semNodeKindConstraints*(p: PNode): PNode =
result.strVal.add(ppEof)
type
TSideEffectAnalysis = enum
TSideEffectAnalysis* = enum
seUnknown, seSideEffect, seNoSideEffect
proc checkForSideEffects(n: PNode): TSideEffectAnalysis =
# XXX is 'raise' a side effect?
proc checkForSideEffects*(n: PNode): TSideEffectAnalysis =
case n.kind
of nkCallKinds:
# only calls can produce side effects:
@@ -162,19 +161,22 @@ proc checkForSideEffects(n: PNode): TSideEffectAnalysis =
# an atom cannot produce a side effect:
result = seNoSideEffect
else:
# assume no side effect:
result = seNoSideEffect
for i in 0 .. <n.len:
let ret = checkForSideEffects(n.sons[i])
if ret == seSideEffect: return ret
elif ret == seUnknown and result == seNoSideEffect:
result = seUnknown
type
TAssignableResult* = enum
type
TAssignableResult* = enum
arNone, # no l-value and no discriminant
arLValue, # is an l-value
arLocalLValue, # is an l-value, but local var; must not escape
# its stack frame!
arDiscriminant # is a discriminant
arDiscriminant, # is a discriminant
arStrange # it is a strange beast like 'typedesc[var T]'
proc isAssignable*(owner: PSym, n: PNode): TAssignableResult =
## 'owner' can be nil!
@@ -183,26 +185,31 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult =
of nkSym:
# don't list 'skLet' here:
if n.sym.kind in {skVar, skResult, skTemp}:
if owner != nil and owner.id == n.sym.owner.id and
if owner != nil and owner.id == n.sym.owner.id and
sfGlobal notin n.sym.flags:
result = arLocalLValue
else:
result = arLValue
of nkDotExpr:
if skipTypes(n.sons[0].typ, abstractInst-{tyTypeDesc}).kind in
{tyVar, tyPtr, tyRef}:
elif n.sym.kind == skParam and n.sym.typ.kind == tyVar:
result = arLValue
else:
result = isAssignable(owner, n.sons[0])
if result != arNone and sfDiscriminant in n.sons[1].sym.flags:
result = arDiscriminant
of nkBracketExpr:
elif n.sym.kind == skType:
let t = n.sym.typ.skipTypes({tyTypeDesc})
if t.kind == tyVar: result = arStrange
of nkDotExpr:
if skipTypes(n.sons[0].typ, abstractInst-{tyTypeDesc}).kind in
{tyVar, tyPtr, tyRef}:
{tyVar, tyPtr, tyRef}:
result = arLValue
else:
result = isAssignable(owner, n.sons[0])
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
if result != arNone and sfDiscriminant in n.sons[1].sym.flags:
result = arDiscriminant
of nkBracketExpr:
if skipTypes(n.sons[0].typ, abstractInst-{tyTypeDesc}).kind in
{tyVar, tyPtr, tyRef}:
result = arLValue
else:
result = isAssignable(owner, n.sons[0])
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
# Object and tuple conversions are still addressable, so we skip them
# XXX why is 'tyOpenArray' allowed here?
if skipTypes(n.typ, abstractPtrs-{tyTypeDesc}).kind in
@@ -211,9 +218,9 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult =
elif compareTypes(n.typ, n.sons[1].typ, dcEqIgnoreDistinct):
# types that are equal modulo distinction preserve l-value:
result = isAssignable(owner, n.sons[1])
of nkHiddenDeref, nkDerefExpr:
of nkHiddenDeref, nkDerefExpr, nkHiddenAddr:
result = arLValue
of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
result = isAssignable(owner, n.sons[0])
of nkCallKinds:
# builtin slice keeps lvalue-ness:
@@ -221,24 +228,27 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult =
else:
discard
proc isLValue*(n: PNode): bool =
isAssignable(nil, n) in {arLValue, arLocalLValue, arStrange}
proc matchNodeKinds*(p, n: PNode): bool =
# matches the parameter constraint 'p' against the concrete AST 'n'.
# matches the parameter constraint 'p' against the concrete AST 'n'.
# Efficiency matters here.
var stack {.noinit.}: array[0..MaxStackSize, bool]
# empty patterns are true:
stack[0] = true
var sp = 1
template push(x: bool) =
stack[sp] = x
inc sp
let code = p.strVal
var pc = 1
while true:
case TOpcode(code[pc])
of ppEof: break
of ppOr:
of ppOr:
stack[sp-2] = stack[sp-1] or stack[sp-2]
dec sp
of ppAnd:
@@ -264,4 +274,4 @@ proc matchNodeKinds*(p, n: PNode): bool =
of ppNoSideEffect: push checkForSideEffects(n) != seSideEffect
inc pc
result = stack[sp-1]

File diff suppressed because it is too large Load Diff

View File

@@ -10,15 +10,15 @@
# This module implements the passes functionality. A pass must implement the
# `TPass` interface.
import
strutils, lists, options, ast, astalgo, llstream, msgs, platform, os,
condsyms, idents, renderer, types, extccomp, math, magicsys, nversion,
import
strutils, lists, options, ast, astalgo, llstream, msgs, platform, os,
condsyms, idents, renderer, types, extccomp, math, magicsys, nversion,
nimsets, syntaxes, times, rodread, idgen
type
type
TPassContext* = object of RootObj # the pass's context
fromCache*: bool # true if created by "openCached"
PPassContext* = ref TPassContext
TPassOpen* = proc (module: PSym): PPassContext {.nimcall.}
@@ -33,8 +33,8 @@ type
TPassData* = tuple[input: PNode, closeOutput: PNode]
TPasses* = openArray[TPass]
# a pass is a tuple of procedure vars ``TPass.close`` may produce additional
# nodes. These are passed to the other close procedures.
# a pass is a tuple of procedure vars ``TPass.close`` may produce additional
# nodes. These are passed to the other close procedures.
# This mechanism used to be used for the instantiation of generics.
proc makePass*(open: TPassOpen = nil,
@@ -53,46 +53,46 @@ proc makePass*(open: TPassOpen = nil,
proc processModule*(module: PSym, stream: PLLStream, rd: PRodReader)
# the semantic checker needs these:
var
var
gImportModule*: proc (m: PSym, fileIdx: int32): PSym {.nimcall.}
gIncludeFile*: proc (m: PSym, fileIdx: int32): PNode {.nimcall.}
# implementation
proc skipCodegen*(n: PNode): bool {.inline.} =
# can be used by codegen passes to determine whether they should do
proc skipCodegen*(n: PNode): bool {.inline.} =
# can be used by codegen passes to determine whether they should do
# something with `n`. Currently, this ignores `n` and uses the global
# error count instead.
result = msgs.gErrorCounter > 0
proc astNeeded*(s: PSym): bool =
proc astNeeded*(s: PSym): bool =
# The ``rodwrite`` module uses this to determine if the body of a proc
# needs to be stored. The passes manager frees s.sons[codePos] when
# appropriate to free the procedure body's memory. This is important
# to keep memory usage down.
if (s.kind in {skMethod, skProc}) and
({sfCompilerProc, sfCompileTime} * s.flags == {}) and
(s.typ.callConv != ccInline) and
(s.ast.sons[genericParamsPos].kind == nkEmpty):
(s.typ.callConv != ccInline) and
(s.ast.sons[genericParamsPos].kind == nkEmpty):
result = false
# XXX this doesn't really make sense with excessive CTFE
else:
result = true
const
const
maxPasses = 10
type
type
TPassContextArray = array[0..maxPasses - 1, PPassContext]
var
var
gPasses: array[0..maxPasses - 1, TPass]
gPassesLen*: int
proc clearPasses* =
gPassesLen = 0
proc registerPass*(p: TPass) =
proc registerPass*(p: TPass) =
gPasses[gPassesLen] = p
inc(gPassesLen)
@@ -109,48 +109,48 @@ proc carryPasses*(nodes: PNode, module: PSym, passes: TPasses) =
passdata = carryPass(pass, module, passdata)
proc openPasses(a: var TPassContextArray, module: PSym) =
for i in countup(0, gPassesLen - 1):
if not isNil(gPasses[i].open):
for i in countup(0, gPassesLen - 1):
if not isNil(gPasses[i].open):
a[i] = gPasses[i].open(module)
else: a[i] = nil
proc openPassesCached(a: var TPassContextArray, module: PSym, rd: PRodReader) =
for i in countup(0, gPassesLen - 1):
if not isNil(gPasses[i].openCached):
for i in countup(0, gPassesLen - 1):
if not isNil(gPasses[i].openCached):
a[i] = gPasses[i].openCached(module, rd)
if a[i] != nil:
if a[i] != nil:
a[i].fromCache = true
else:
a[i] = nil
proc closePasses(a: var TPassContextArray) =
proc closePasses(a: var TPassContextArray) =
var m: PNode = nil
for i in countup(0, gPassesLen - 1):
for i in countup(0, gPassesLen - 1):
if not isNil(gPasses[i].close): m = gPasses[i].close(a[i], m)
a[i] = nil # free the memory here
proc processTopLevelStmt(n: PNode, a: var TPassContextArray): bool =
proc processTopLevelStmt(n: PNode, a: var TPassContextArray): bool =
# this implements the code transformation pipeline
var m = n
for i in countup(0, gPassesLen - 1):
if not isNil(gPasses[i].process):
for i in countup(0, gPassesLen - 1):
if not isNil(gPasses[i].process):
m = gPasses[i].process(a[i], m)
if isNil(m): return false
result = true
proc processTopLevelStmtCached(n: PNode, a: var TPassContextArray) =
proc processTopLevelStmtCached(n: PNode, a: var TPassContextArray) =
# this implements the code transformation pipeline
var m = n
for i in countup(0, gPassesLen - 1):
for i in countup(0, gPassesLen - 1):
if not isNil(gPasses[i].openCached): m = gPasses[i].process(a[i], m)
proc closePassesCached(a: var TPassContextArray) =
proc closePassesCached(a: var TPassContextArray) =
var m: PNode = nil
for i in countup(0, gPassesLen - 1):
if not isNil(gPasses[i].openCached) and not isNil(gPasses[i].close):
for i in countup(0, gPassesLen - 1):
if not isNil(gPasses[i].openCached) and not isNil(gPasses[i].close):
m = gPasses[i].close(a[i], m)
a[i] = nil # free the memory here
proc processImplicits(implicits: seq[string], nodeKind: TNodeKind,
a: var TPassContextArray) =
for module in items(implicits):
@@ -159,41 +159,45 @@ proc processImplicits(implicits: seq[string], nodeKind: TNodeKind,
str.info = gCmdLineInfo
importStmt.addSon str
if not processTopLevelStmt(importStmt, a): break
proc processModule(module: PSym, stream: PLLStream, rd: PRodReader) =
var
var
p: TParsers
a: TPassContextArray
s: PLLStream
fileIdx = module.fileIdx
if rd == nil:
if rd == nil:
openPasses(a, module)
if stream == nil:
let filename = fileIdx.toFullPath
s = llStreamOpen(filename, fmRead)
if s == nil:
if stream == nil:
let filename = fileIdx.toFullPathConsiderDirty
if module.name.s == "-":
module.name.s = "stdinfile"
s = llStreamOpen(stdin)
else:
s = llStreamOpen(filename, fmRead)
if s == nil:
rawMessage(errCannotOpenFile, filename)
return
else:
else:
s = stream
while true:
while true:
openParsers(p, fileIdx, s)
if sfSystemModule notin module.flags:
# XXX what about caching? no processing then? what if I change the
# XXX what about caching? no processing then? what if I change the
# modules to include between compilation runs? we'd need to track that
# in ROD files. I think we should enable this feature only
# for the interactive mode.
processImplicits implicitImports, nkImportStmt, a
processImplicits implicitIncludes, nkIncludeStmt, a
while true:
while true:
var n = parseTopLevelStmt(p)
if n.kind == nkEmpty: break
if n.kind == nkEmpty: break
if not processTopLevelStmt(n, a): break
closeParsers(p)
if s.kind != llsStdIn: break
if s.kind != llsStdIn: break
closePasses(a)
# id synchronization point for more consistent code generation:
idSynchronizationPoint(1000)

View File

@@ -275,7 +275,7 @@ proc applyRule*(c: PContext, s: PSym, n: PNode): PNode =
if arg != rs and aliases.isPartOf(rs, arg) == arYes:
ok = true
break
# constraint not fullfilled:
# constraint not fulfilled:
if not ok: return nil
of aqNoAlias:
# it MUST not alias with any other param:
@@ -284,7 +284,7 @@ proc applyRule*(c: PContext, s: PSym, n: PNode): PNode =
if arg != rs and aliases.isPartOf(rs, arg) != arNo:
ok = false
break
# constraint not fullfilled:
# constraint not fulfilled:
if not ok: return nil
markUsed(n.info, s)

View File

@@ -13,188 +13,192 @@
# Nimrod has been tested on this platform or that the RTL has been ported.
# Feel free to test for your excentric platform!
import
import
strutils
type
type
TSystemOS* = enum # Also add OS in initialization section and alias
# conditionals to condsyms (end of module).
osNone, osDos, osWindows, osOs2, osLinux, osMorphos, osSkyos, osSolaris,
osIrix, osNetbsd, osFreebsd, osOpenbsd, osAix, osPalmos, osQnx, osAmiga,
osAtari, osNetware, osMacos, osMacosx, osHaiku, osJS, osNimrodVM,
osStandalone
osNone, osDos, osWindows, osOs2, osLinux, osMorphos, osSkyos, osSolaris,
osIrix, osNetbsd, osFreebsd, osOpenbsd, osAix, osPalmos, osQnx, osAmiga,
osAtari, osNetware, osMacos, osMacosx, osHaiku, osVxworks,
osJS, osNimrodVM, osStandalone
type
TInfoOSProp* = enum
type
TInfoOSProp* = enum
ospNeedsPIC, # OS needs PIC for libraries
ospCaseInsensitive, # OS filesystem is case insensitive
ospPosix, # OS is posix-like
ospLacksThreadVars # OS lacks proper __threadvar support
TInfoOSProps* = set[TInfoOSProp]
TInfoOS* = tuple[name: string, parDir: string, dllFrmt: string,
altDirSep: string, objExt: string, newLine: string,
pathSep: string, dirSep: string, scriptExt: string,
curDir: string, exeExt: string, extSep: string,
TInfoOS* = tuple[name: string, parDir: string, dllFrmt: string,
altDirSep: string, objExt: string, newLine: string,
pathSep: string, dirSep: string, scriptExt: string,
curDir: string, exeExt: string, extSep: string,
props: TInfoOSProps]
const
const
OS*: array[succ(low(TSystemOS))..high(TSystemOS), TInfoOS] = [
(name: "DOS",
parDir: "..", dllFrmt: "$1.dll", altDirSep: "/", objExt: ".obj",
newLine: "\x0D\x0A", pathSep: ";", dirSep: "\\", scriptExt: ".bat",
curDir: ".", exeExt: ".exe", extSep: ".", props: {ospCaseInsensitive}),
(name: "Windows", parDir: "..", dllFrmt: "$1.dll", altDirSep: "/",
objExt: ".obj", newLine: "\x0D\x0A", pathSep: ";", dirSep: "\\",
scriptExt: ".bat", curDir: ".", exeExt: ".exe", extSep: ".",
props: {ospCaseInsensitive}),
(name: "OS2", parDir: "..",
dllFrmt: "$1.dll", altDirSep: "/",
objExt: ".obj", newLine: "\x0D\x0A",
pathSep: ";", dirSep: "\\",
scriptExt: ".bat", curDir: ".",
exeExt: ".exe", extSep: ".",
props: {ospCaseInsensitive}),
(name: "Linux", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".",
props: {ospNeedsPIC, ospPosix}),
(name: "MorphOS", parDir: "..",
dllFrmt: "lib$1.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A",
pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".",
exeExt: "", extSep: ".",
props: {ospNeedsPIC, ospPosix}),
(name: "SkyOS", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".",
props: {ospNeedsPIC, ospPosix}),
(name: "Solaris", parDir: "..",
dllFrmt: "lib$1.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A",
pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".",
exeExt: "", extSep: ".",
props: {ospNeedsPIC, ospPosix}),
(name: "Irix", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".",
props: {ospNeedsPIC, ospPosix}),
(name: "NetBSD", parDir: "..",
dllFrmt: "lib$1.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A",
pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".",
exeExt: "", extSep: ".",
props: {ospNeedsPIC, ospPosix}),
(name: "FreeBSD", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".",
props: {ospNeedsPIC, ospPosix}),
(name: "OpenBSD", parDir: "..",
dllFrmt: "lib$1.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A",
pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".",
exeExt: "", extSep: ".",
props: {ospNeedsPIC, ospPosix}),
(name: "AIX", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".",
props: {ospNeedsPIC, ospPosix}),
(name: "PalmOS", parDir: "..",
dllFrmt: "lib$1.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A",
pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".",
exeExt: "", extSep: ".",
props: {ospNeedsPIC}),
(name: "QNX",
parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/", objExt: ".o",
newLine: "\x0A", pathSep: ":", dirSep: "/", scriptExt: ".sh", curDir: ".",
exeExt: "", extSep: ".", props: {ospNeedsPIC, ospPosix}),
(name: "Amiga",
parDir: "..", dllFrmt: "$1.library", altDirSep: "/", objExt: ".o",
newLine: "\x0A", pathSep: ":", dirSep: "/", scriptExt: ".sh", curDir: ".",
exeExt: "", extSep: ".", props: {ospNeedsPIC}),
(name: "Atari",
parDir: "..", dllFrmt: "$1.dll", altDirSep: "/", objExt: ".o",
newLine: "\x0A", pathSep: ":", dirSep: "/", scriptExt: "", curDir: ".",
exeExt: ".tpp", extSep: ".", props: {ospNeedsPIC}),
(name: "Netware",
parDir: "..", dllFrmt: "$1.nlm", altDirSep: "/", objExt: "",
newLine: "\x0D\x0A", pathSep: ":", dirSep: "/", scriptExt: ".sh",
curDir: ".", exeExt: ".nlm", extSep: ".", props: {ospCaseInsensitive}),
(name: "MacOS", parDir: "::", dllFrmt: "$1Lib", altDirSep: ":",
objExt: ".o", newLine: "\x0D", pathSep: ",", dirSep: ":", scriptExt: "",
curDir: ":", exeExt: "", extSep: ".", props: {ospCaseInsensitive}),
(name: "MacOSX", parDir: "..", dllFrmt: "lib$1.dylib", altDirSep: ":",
objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".",
props: {ospNeedsPIC, ospPosix, ospLacksThreadVars}),
(name: "Haiku", parDir: "..", dllFrmt: "lib$1.so", altDirSep: ":",
objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".",
props: {ospNeedsPIC, ospPosix, ospLacksThreadVars}),
(name: "JS", parDir: "..",
dllFrmt: "lib$1.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A",
pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".",
exeExt: "", extSep: ".", props: {}),
(name: "NimrodVM", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
(name: "DOS",
parDir: "..", dllFrmt: "$1.dll", altDirSep: "/", objExt: ".obj",
newLine: "\x0D\x0A", pathSep: ";", dirSep: "\\", scriptExt: ".bat",
curDir: ".", exeExt: ".exe", extSep: ".", props: {ospCaseInsensitive}),
(name: "Windows", parDir: "..", dllFrmt: "$1.dll", altDirSep: "/",
objExt: ".obj", newLine: "\x0D\x0A", pathSep: ";", dirSep: "\\",
scriptExt: ".bat", curDir: ".", exeExt: ".exe", extSep: ".",
props: {ospCaseInsensitive}),
(name: "OS2", parDir: "..",
dllFrmt: "$1.dll", altDirSep: "/",
objExt: ".obj", newLine: "\x0D\x0A",
pathSep: ";", dirSep: "\\",
scriptExt: ".bat", curDir: ".",
exeExt: ".exe", extSep: ".",
props: {ospCaseInsensitive}),
(name: "Linux", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".",
props: {ospNeedsPIC, ospPosix}),
(name: "MorphOS", parDir: "..",
dllFrmt: "lib$1.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A",
pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".",
exeExt: "", extSep: ".",
props: {ospNeedsPIC, ospPosix}),
(name: "SkyOS", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".",
props: {ospNeedsPIC, ospPosix}),
(name: "Solaris", parDir: "..",
dllFrmt: "lib$1.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A",
pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".",
exeExt: "", extSep: ".",
props: {ospNeedsPIC, ospPosix}),
(name: "Irix", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".",
props: {ospNeedsPIC, ospPosix}),
(name: "NetBSD", parDir: "..",
dllFrmt: "lib$1.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A",
pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".",
exeExt: "", extSep: ".",
props: {ospNeedsPIC, ospPosix}),
(name: "FreeBSD", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".",
props: {ospNeedsPIC, ospPosix}),
(name: "OpenBSD", parDir: "..",
dllFrmt: "lib$1.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A",
pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".",
exeExt: "", extSep: ".",
props: {ospNeedsPIC, ospPosix}),
(name: "AIX", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".",
props: {ospNeedsPIC, ospPosix}),
(name: "PalmOS", parDir: "..",
dllFrmt: "lib$1.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A",
pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".",
exeExt: "", extSep: ".",
props: {ospNeedsPIC}),
(name: "QNX",
parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/", objExt: ".o",
newLine: "\x0A", pathSep: ":", dirSep: "/", scriptExt: ".sh", curDir: ".",
exeExt: "", extSep: ".", props: {ospNeedsPIC, ospPosix}),
(name: "Amiga",
parDir: "..", dllFrmt: "$1.library", altDirSep: "/", objExt: ".o",
newLine: "\x0A", pathSep: ":", dirSep: "/", scriptExt: ".sh", curDir: ".",
exeExt: "", extSep: ".", props: {ospNeedsPIC}),
(name: "Atari",
parDir: "..", dllFrmt: "$1.dll", altDirSep: "/", objExt: ".o",
newLine: "\x0A", pathSep: ":", dirSep: "/", scriptExt: "", curDir: ".",
exeExt: ".tpp", extSep: ".", props: {ospNeedsPIC}),
(name: "Netware",
parDir: "..", dllFrmt: "$1.nlm", altDirSep: "/", objExt: "",
newLine: "\x0D\x0A", pathSep: ":", dirSep: "/", scriptExt: ".sh",
curDir: ".", exeExt: ".nlm", extSep: ".", props: {ospCaseInsensitive}),
(name: "MacOS", parDir: "::", dllFrmt: "$1Lib", altDirSep: ":",
objExt: ".o", newLine: "\x0D", pathSep: ",", dirSep: ":", scriptExt: "",
curDir: ":", exeExt: "", extSep: ".", props: {ospCaseInsensitive}),
(name: "MacOSX", parDir: "..", dllFrmt: "lib$1.dylib", altDirSep: ":",
objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".",
props: {ospNeedsPIC, ospPosix, ospLacksThreadVars}),
(name: "Haiku", parDir: "..", dllFrmt: "lib$1.so", altDirSep: ":",
objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".",
props: {ospNeedsPIC, ospPosix, ospLacksThreadVars}),
(name: "VxWorks", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A", pathSep: ";", dirSep: "\\",
scriptExt: ".sh", curDir: ".", exeExt: ".vxe", extSep: ".",
props: {ospNeedsPIC, ospPosix, ospLacksThreadVars}),
(name: "JS", parDir: "..",
dllFrmt: "lib$1.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A",
pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".",
exeExt: "", extSep: ".", props: {}),
(name: "NimrodVM", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".", props: {}),
(name: "Standalone", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".",
scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".",
props: {})]
type
TSystemCPU* = enum # Also add CPU for in initialization section and
type
TSystemCPU* = enum # Also add CPU for in initialization section and
# alias conditionals to condsyms (end of module).
cpuNone, cpuI386, cpuM68k, cpuAlpha, cpuPowerpc, cpuPowerpc64,
cpuSparc, cpuVm, cpuIa64, cpuAmd64, cpuMips, cpuArm,
cpuSparc, cpuVm, cpuIa64, cpuAmd64, cpuMips, cpuArm,
cpuJS, cpuNimrodVM, cpuAVR
type
TEndian* = enum
type
TEndian* = enum
littleEndian, bigEndian
TInfoCPU* = tuple[name: string, intSize: int, endian: TEndian,
TInfoCPU* = tuple[name: string, intSize: int, endian: TEndian,
floatSize, bit: int]
const
EndianToStr*: array[TEndian, string] = ["littleEndian", "bigEndian"]
CPU*: array[succ(low(TSystemCPU))..high(TSystemCPU), TInfoCPU] = [
(name: "i386", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32),
(name: "m68k", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32),
(name: "alpha", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64),
(name: "i386", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32),
(name: "m68k", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32),
(name: "alpha", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64),
(name: "powerpc", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32),
(name: "powerpc64", intSize: 64, endian: bigEndian, floatSize: 64,bit: 64),
(name: "sparc", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32),
(name: "vm", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32),
(name: "ia64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64),
(name: "amd64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64),
(name: "mips", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32),
(name: "arm", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32),
(name: "js", intSize: 32, endian: bigEndian,floatSize: 64,bit: 32),
(name: "powerpc64", intSize: 64, endian: bigEndian, floatSize: 64,bit: 64),
(name: "sparc", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32),
(name: "vm", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32),
(name: "ia64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64),
(name: "amd64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64),
(name: "mips", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32),
(name: "arm", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32),
(name: "js", intSize: 32, endian: bigEndian,floatSize: 64,bit: 32),
(name: "nimrodvm", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32),
(name: "avr", intSize: 16, endian: littleEndian, floatSize: 32, bit: 16)]
var
var
targetCPU*, hostCPU*: TSystemCPU
targetOS*, hostOS*: TSystemOS
proc nameToOS*(name: string): TSystemOS
proc nameToCPU*(name: string): TSystemCPU
var
var
intSize*: int
floatSize*: int
ptrSize*: int
tnl*: string # target newline
proc setTarget*(o: TSystemOS, c: TSystemCPU) =
proc setTarget*(o: TSystemOS, c: TSystemCPU) =
assert(c != cpuNone)
assert(o != osNone)
#echo "new Target: OS: ", o, " CPU: ", c
@@ -205,15 +209,15 @@ proc setTarget*(o: TSystemOS, c: TSystemCPU) =
ptrSize = CPU[c].bit div 8
tnl = OS[o].newLine
proc nameToOS(name: string): TSystemOS =
for i in countup(succ(osNone), high(TSystemOS)):
if cmpIgnoreStyle(name, OS[i].name) == 0:
proc nameToOS(name: string): TSystemOS =
for i in countup(succ(osNone), high(TSystemOS)):
if cmpIgnoreStyle(name, OS[i].name) == 0:
return i
result = osNone
proc nameToCPU(name: string): TSystemCPU =
for i in countup(succ(cpuNone), high(TSystemCPU)):
if cmpIgnoreStyle(name, CPU[i].name) == 0:
proc nameToCPU(name: string): TSystemCPU =
for i in countup(succ(cpuNone), high(TSystemCPU)):
if cmpIgnoreStyle(name, CPU[i].name) == 0:
return i
result = cpuNone

43
compiler/plugins.nim Normal file
View File

@@ -0,0 +1,43 @@
#
#
# The Nim Compiler
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Plugin support for the Nim compiler. Right now there are no plugins and they
## need to be build with the compiler, no DLL support.
import ast, semdata, idents
type
Transformation* = proc (c: PContext; n: PNode): PNode {.nimcall.}
Plugin = ref object
fn, module, package: PIdent
t: Transformation
next: Plugin
proc pluginMatches(p: Plugin; s: PSym): bool =
if s.name.id != p.fn.id: return false
let module = s.owner
if module == nil or module.kind != skModule or
module.name.id != p.module.id: return false
let package = module.owner
if package == nil or package.kind != skPackage or
package.name.id != p.package.id: return false
return true
var head: Plugin
proc getPlugin*(fn: PSym): Transformation =
var it = head
while it != nil:
if pluginMatches(it, fn): return it.t
it = it.next
proc registerPlugin*(package, module, fn: string; t: Transformation) =
let oldHead = head
head = Plugin(fn: getIdent(fn), module: getIdent(module),
package: getIdent(package), t: t, next: oldHead)

View File

@@ -0,0 +1,13 @@
#
#
# The Nim Compiler
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Include file that imports all plugins that are active.
import
locals.locals

View File

@@ -0,0 +1,42 @@
#
#
# The Nim Compiler
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## The builtin 'system.locals' implemented as a plugin.
import plugins, ast, astalgo, magicsys, lookups, semdata, lowerings
proc semLocals(c: PContext, n: PNode): PNode =
var counter = 0
var tupleType = newTypeS(tyTuple, c)
result = newNodeIT(nkPar, n.info, tupleType)
tupleType.n = newNodeI(nkRecList, n.info)
# for now we skip openarrays ...
for scope in walkScopes(c.currentScope):
if scope == c.topLevelScope: break
for it in items(scope.symbols):
# XXX parameters' owners are wrong for generics; this caused some pain
# for closures too; we should finally fix it.
#if it.owner != c.p.owner: return result
if it.kind in skLocalVars and
it.typ.skipTypes({tyGenericInst, tyVar}).kind notin
{tyVarargs, tyOpenArray, tyTypeDesc, tyStatic, tyExpr, tyStmt, tyEmpty}:
var field = newSym(skField, it.name, getCurrOwner(), n.info)
field.typ = it.typ.skipTypes({tyGenericInst, tyVar})
field.position = counter
inc(counter)
addSon(tupleType.n, newSymNode(field))
addSonSkipIntLit(tupleType, field.typ)
var a = newSymNode(it, result.info)
if it.typ.skipTypes({tyGenericInst}).kind == tyVar: a = newDeref(a)
result.add(a)
registerPlugin("stdlib", "system", "locals", semLocals)

View File

@@ -1,7 +1,7 @@
#
#
# The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -9,23 +9,23 @@
# This module implements semantic checking for pragmas
import
os, platform, condsyms, ast, astalgo, idents, semdata, msgs, renderer,
import
os, platform, condsyms, ast, astalgo, idents, semdata, msgs, renderer,
wordrecg, ropes, options, strutils, lists, extccomp, math, magicsys, trees,
rodread, types, lookups
const
const
FirstCallConv* = wNimcall
LastCallConv* = wNoconv
const
procPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,
wMagic, wNosideeffect, wSideeffect, wNoreturn, wDynlib, wHeader,
wCompilerproc, wProcVar, wDeprecated, wVarargs, wCompileTime, wMerge,
procPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,
wMagic, wNosideeffect, wSideeffect, wNoreturn, wDynlib, wHeader,
wCompilerproc, wProcVar, wDeprecated, wVarargs, wCompileTime, wMerge,
wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC,
wAsmNoStackFrame, wError, wDiscardable, wNoInit, wDestructor, wCodegenDecl,
wGensym, wInject, wRaises, wTags, wLocks, wDelegator, wGcSafe,
wOverride}
wOverride, wConstructor}
converterPragmas* = procPragmas
methodPragmas* = procPragmas
templatePragmas* = {wImmediate, wDeprecated, wError, wGensym, wInject, wDirty,
@@ -33,7 +33,7 @@ const
macroPragmas* = {FirstCallConv..LastCallConv, wImmediate, wImportc, wExportc,
wNodecl, wMagic, wNosideeffect, wCompilerproc, wDeprecated, wExtern,
wImportCpp, wImportObjC, wError, wDiscardable, wGensym, wInject, wDelegator}
iteratorPragmas* = {FirstCallConv..LastCallConv, wNosideeffect, wSideeffect,
iteratorPragmas* = {FirstCallConv..LastCallConv, wNosideeffect, wSideeffect,
wImportc, wExportc, wNodecl, wMagic, wDeprecated, wBorrow, wExtern,
wImportCpp, wImportObjC, wError, wDiscardable, wGensym, wInject, wRaises,
wTags, wLocks, wGcSafe}
@@ -46,21 +46,21 @@ const
wFloatchecks, wInfChecks, wNanChecks, wPragma, wEmit, wUnroll,
wLinearScanEnd, wPatterns, wEffects, wNoForward, wComputedGoto,
wInjectStmt, wDeprecated, wExperimental}
lambdaPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,
wNosideeffect, wSideeffect, wNoreturn, wDynlib, wHeader,
lambdaPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,
wNosideeffect, wSideeffect, wNoreturn, wDynlib, wHeader,
wDeprecated, wExtern, wThread, wImportCpp, wImportObjC, wAsmNoStackFrame,
wRaises, wLocks, wTags, wGcSafe}
typePragmas* = {wImportc, wExportc, wDeprecated, wMagic, wAcyclic, wNodecl,
typePragmas* = {wImportc, wExportc, wDeprecated, wMagic, wAcyclic, wNodecl,
wPure, wHeader, wCompilerproc, wFinal, wSize, wExtern, wShallow,
wImportCpp, wImportObjC, wError, wIncompleteStruct, wByCopy, wByRef,
wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked,
wBorrow, wGcSafe}
fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern,
fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern,
wImportCpp, wImportObjC, wError, wGuard}
varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl,
varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl,
wMagic, wHeader, wDeprecated, wCompilerproc, wDynlib, wExtern,
wImportCpp, wImportObjC, wError, wNoInit, wCompileTime, wGlobal,
wGensym, wInject, wCodegenDecl, wGuard}
wGensym, wInject, wCodegenDecl, wGuard, wGoto}
constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl,
wExtern, wImportCpp, wImportObjC, wError, wGensym, wInject}
letPragmas* = varPragmas
@@ -74,27 +74,27 @@ proc pragma*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords)
proc invalidPragma(n: PNode) =
localError(n.info, errInvalidPragmaX, renderTree(n, {renderNoComments}))
proc pragmaAsm*(c: PContext, n: PNode): char =
proc pragmaAsm*(c: PContext, n: PNode): char =
result = '\0'
if n != nil:
for i in countup(0, sonsLen(n) - 1):
if n != nil:
for i in countup(0, sonsLen(n) - 1):
let it = n.sons[i]
if it.kind == nkExprColonExpr and it.sons[0].kind == nkIdent:
case whichKeyword(it.sons[0].ident)
of wSubsChar:
of wSubsChar:
if it.sons[1].kind == nkCharLit: result = chr(int(it.sons[1].intVal))
else: invalidPragma(it)
else: invalidPragma(it)
else:
else:
invalidPragma(it)
proc setExternName(s: PSym, extname: string) =
s.loc.r = toRope(extname % s.name.s)
s.loc.r = rope(extname % s.name.s)
if gCmd == cmdPretty and '$' notin extname:
# note that '{.importc.}' is transformed into '{.importc: "$1".}'
s.loc.flags.incl(lfFullExternalName)
proc makeExternImport(s: PSym, extname: string) =
proc makeExternImport(s: PSym, extname: string) =
setExternName(s, extname)
incl(s.flags, sfImportc)
excl(s.flags, sfForward)
@@ -105,7 +105,7 @@ proc validateExternCName(s: PSym, info: TLineInfo) =
## Valid identifiers are those alphanumeric including the underscore not
## starting with a number. If the check fails, a generic error will be
## displayed to the user.
let target = ropeToStr(s.loc.r)
let target = $s.loc.r
if target.len < 1 or target[0] notin IdentStartChars or
not target.allCharsInSet(IdentChars):
localError(info, errGenerated, "invalid exported symbol")
@@ -145,7 +145,7 @@ proc newEmptyStrNode(n: PNode): PNode {.noinline.} =
result.strVal = ""
proc getStrLitNode(c: PContext, n: PNode): PNode =
if n.kind != nkExprColonExpr:
if n.kind != nkExprColonExpr:
localError(n.info, errStringLiteralExpected)
# error correction:
result = newEmptyStrNode(n)
@@ -153,62 +153,62 @@ proc getStrLitNode(c: PContext, n: PNode): PNode =
n.sons[1] = c.semConstExpr(c, n.sons[1])
case n.sons[1].kind
of nkStrLit, nkRStrLit, nkTripleStrLit: result = n.sons[1]
else:
else:
localError(n.info, errStringLiteralExpected)
# error correction:
result = newEmptyStrNode(n)
proc expectStrLit(c: PContext, n: PNode): string =
proc expectStrLit(c: PContext, n: PNode): string =
result = getStrLitNode(c, n).strVal
proc expectIntLit(c: PContext, n: PNode): int =
if n.kind != nkExprColonExpr:
proc expectIntLit(c: PContext, n: PNode): int =
if n.kind != nkExprColonExpr:
localError(n.info, errIntLiteralExpected)
else:
else:
n.sons[1] = c.semConstExpr(c, n.sons[1])
case n.sons[1].kind
of nkIntLit..nkInt64Lit: result = int(n.sons[1].intVal)
else: localError(n.info, errIntLiteralExpected)
proc getOptionalStr(c: PContext, n: PNode, defaultStr: string): string =
proc getOptionalStr(c: PContext, n: PNode, defaultStr: string): string =
if n.kind == nkExprColonExpr: result = expectStrLit(c, n)
else: result = defaultStr
proc processCodegenDecl(c: PContext, n: PNode, sym: PSym) =
sym.constraint = getStrLitNode(c, n)
proc processMagic(c: PContext, n: PNode, s: PSym) =
proc processMagic(c: PContext, n: PNode, s: PSym) =
#if sfSystemModule notin c.module.flags:
# liMessage(n.info, errMagicOnlyInSystem)
if n.kind != nkExprColonExpr:
if n.kind != nkExprColonExpr:
localError(n.info, errStringLiteralExpected)
return
var v: string
if n.sons[1].kind == nkIdent: v = n.sons[1].ident.s
else: v = expectStrLit(c, n)
for m in countup(low(TMagic), high(TMagic)):
if substr($m, 1) == v:
for m in countup(low(TMagic), high(TMagic)):
if substr($m, 1) == v:
s.magic = m
break
if s.magic == mNone: message(n.info, warnUnknownMagic, v)
proc wordToCallConv(sw: TSpecialWord): TCallingConvention =
proc wordToCallConv(sw: TSpecialWord): TCallingConvention =
# this assumes that the order of special words and calling conventions is
# the same
result = TCallingConvention(ord(ccDefault) + ord(sw) - ord(wNimcall))
proc isTurnedOn(c: PContext, n: PNode): bool =
proc isTurnedOn(c: PContext, n: PNode): bool =
if n.kind == nkExprColonExpr:
let x = c.semConstBoolExpr(c, n.sons[1])
n.sons[1] = x
if x.kind == nkIntLit: return x.intVal != 0
localError(n.info, errOnOrOffExpected)
proc onOff(c: PContext, n: PNode, op: TOptions) =
proc onOff(c: PContext, n: PNode, op: TOptions) =
if isTurnedOn(c, n): gOptions = gOptions + op
else: gOptions = gOptions - op
proc pragmaDeadCodeElim(c: PContext, n: PNode) =
proc pragmaDeadCodeElim(c: PContext, n: PNode) =
if isTurnedOn(c, n): incl(c.module.flags, sfDeadCodeElim)
else: excl(c.module.flags, sfDeadCodeElim)
@@ -216,20 +216,20 @@ proc pragmaNoForward(c: PContext, n: PNode) =
if isTurnedOn(c, n): incl(c.module.flags, sfNoForward)
else: excl(c.module.flags, sfNoForward)
proc processCallConv(c: PContext, n: PNode) =
if (n.kind == nkExprColonExpr) and (n.sons[1].kind == nkIdent):
proc processCallConv(c: PContext, n: PNode) =
if (n.kind == nkExprColonExpr) and (n.sons[1].kind == nkIdent):
var sw = whichKeyword(n.sons[1].ident)
case sw
of FirstCallConv..LastCallConv:
of FirstCallConv..LastCallConv:
POptionEntry(c.optionStack.tail).defaultCC = wordToCallConv(sw)
else: localError(n.info, errCallConvExpected)
else:
else:
localError(n.info, errCallConvExpected)
proc getLib(c: PContext, kind: TLibKind, path: PNode): PLib =
proc getLib(c: PContext, kind: TLibKind, path: PNode): PLib =
var it = PLib(c.libs.head)
while it != nil:
if it.kind == kind:
while it != nil:
if it.kind == kind:
if trees.exprStructuralEquivalent(it.path, path): return it
it = PLib(it.next)
result = newLib(kind)
@@ -252,10 +252,10 @@ proc expectDynlibNode(c: PContext, n: PNode): PNode =
if result.typ == nil or result.typ.kind notin {tyPointer, tyString, tyProc}:
localError(n.info, errStringLiteralExpected)
result = newEmptyStrNode(n)
proc processDynLib(c: PContext, n: PNode, sym: PSym) =
proc processDynLib(c: PContext, n: PNode, sym: PSym) =
if (sym == nil) or (sym.kind == skModule):
POptionEntry(c.optionStack.tail).dynlib = getLib(c, libDynamic,
POptionEntry(c.optionStack.tail).dynlib = getLib(c, libDynamic,
expectDynlibNode(c, n))
else:
if n.kind == nkExprColonExpr:
@@ -268,7 +268,7 @@ proc processDynLib(c: PContext, n: PNode, sym: PSym) =
# since we'll be loading the dynlib symbols dynamically, we must use
# a calling convention that doesn't introduce custom name mangling
# cdecl is the default - the user can override this explicitly
if sym.kind in routineKinds and sym.typ != nil and
if sym.kind in routineKinds and sym.typ != nil and
sym.typ.callConv == ccDefault:
sym.typ.callConv = ccCDecl
@@ -295,10 +295,10 @@ proc processNote(c: PContext, n: PNode) =
n.sons[1] = x
if x.kind == nkIntLit and x.intVal != 0: incl(gNotes, nk)
else: excl(gNotes, nk)
else:
else:
invalidPragma(n)
proc processOption(c: PContext, n: PNode): bool =
proc processOption(c: PContext, n: PNode): bool =
if n.kind != nkExprColonExpr: result = true
elif n.sons[0].kind == nkBracketExpr: processNote(c, n)
elif n.sons[0].kind != nkIdent: result = true
@@ -318,34 +318,34 @@ proc processOption(c: PContext, n: PNode): bool =
of wAssertions: onOff(c, n, {optAssert})
of wWarnings: onOff(c, n, {optWarns})
of wHints: onOff(c, n, {optHints})
of wCallconv: processCallConv(c, n)
of wCallconv: processCallConv(c, n)
of wLinedir: onOff(c, n, {optLineDir})
of wStacktrace: onOff(c, n, {optStackTrace})
of wLinetrace: onOff(c, n, {optLineTrace})
of wDebugger: onOff(c, n, {optEndb})
of wProfiler: onOff(c, n, {optProfiler})
of wByRef: onOff(c, n, {optByRef})
of wDynlib: processDynLib(c, n, nil)
of wOptimization:
if n.sons[1].kind != nkIdent:
of wDynlib: processDynLib(c, n, nil)
of wOptimization:
if n.sons[1].kind != nkIdent:
invalidPragma(n)
else:
else:
case n.sons[1].ident.s.normalize
of "speed":
of "speed":
incl(gOptions, optOptimizeSpeed)
excl(gOptions, optOptimizeSize)
of "size":
excl(gOptions, optOptimizeSpeed)
incl(gOptions, optOptimizeSize)
of "none":
of "none":
excl(gOptions, optOptimizeSpeed)
excl(gOptions, optOptimizeSize)
else: localError(n.info, errNoneSpeedOrSizeExpected)
of wImplicitStatic: onOff(c, n, {optImplicitStatic})
of wPatterns: onOff(c, n, {optPatterns})
else: result = true
proc processPush(c: PContext, n: PNode, start: int) =
proc processPush(c: PContext, n: PNode, start: int) =
if n.sons[start-1].kind == nkExprColonExpr:
localError(n.info, errGenerated, "':' after 'push' not supported")
var x = newOptionEntry()
@@ -355,41 +355,41 @@ proc processPush(c: PContext, n: PNode, start: int) =
x.dynlib = y.dynlib
x.notes = gNotes
append(c.optionStack, x)
for i in countup(start, sonsLen(n) - 1):
for i in countup(start, sonsLen(n) - 1):
if processOption(c, n.sons[i]):
# simply store it somewhere:
if x.otherPragmas.isNil:
x.otherPragmas = newNodeI(nkPragma, n.info)
x.otherPragmas.add n.sons[i]
#localError(n.info, errOptionExpected)
proc processPop(c: PContext, n: PNode) =
if c.optionStack.counter <= 1:
proc processPop(c: PContext, n: PNode) =
if c.optionStack.counter <= 1:
localError(n.info, errAtPopWithoutPush)
else:
gOptions = POptionEntry(c.optionStack.tail).options
else:
gOptions = POptionEntry(c.optionStack.tail).options
gNotes = POptionEntry(c.optionStack.tail).notes
remove(c.optionStack, c.optionStack.tail)
proc processDefine(c: PContext, n: PNode) =
if (n.kind == nkExprColonExpr) and (n.sons[1].kind == nkIdent):
proc processDefine(c: PContext, n: PNode) =
if (n.kind == nkExprColonExpr) and (n.sons[1].kind == nkIdent):
defineSymbol(n.sons[1].ident.s)
message(n.info, warnDeprecated, "define")
else:
else:
invalidPragma(n)
proc processUndef(c: PContext, n: PNode) =
if (n.kind == nkExprColonExpr) and (n.sons[1].kind == nkIdent):
proc processUndef(c: PContext, n: PNode) =
if (n.kind == nkExprColonExpr) and (n.sons[1].kind == nkIdent):
undefSymbol(n.sons[1].ident.s)
message(n.info, warnDeprecated, "undef")
else:
else:
invalidPragma(n)
type
TLinkFeature = enum
type
TLinkFeature = enum
linkNormal, linkSys
proc processCompile(c: PContext, n: PNode) =
proc processCompile(c: PContext, n: PNode) =
var s = expectStrLit(c, n)
var found = findFile(s)
if found == "": found = s
@@ -397,7 +397,7 @@ proc processCompile(c: PContext, n: PNode) =
extccomp.addExternalFileToCompile(found)
extccomp.addFileToLink(completeCFilePath(trunc, false))
proc processCommonLink(c: PContext, n: PNode, feature: TLinkFeature) =
proc processCommonLink(c: PContext, n: PNode, feature: TLinkFeature) =
var f = expectStrLit(c, n)
if splitFile(f).ext == "": f = addFileExt(f, CC[cCompiler].objExt)
var found = findFile(f)
@@ -407,15 +407,9 @@ proc processCommonLink(c: PContext, n: PNode, feature: TLinkFeature) =
of linkSys:
extccomp.addFileToLink(libpath / completeCFilePath(found, false))
else: internalError(n.info, "processCommonLink")
proc pragmaBreakpoint(c: PContext, n: PNode) =
discard getOptionalStr(c, n, "")
proc pragmaCheckpoint(c: PContext, n: PNode) =
# checkpoints can be used to debug the compiler; they are not documented
var info = n.info
inc(info.line) # next line is affected!
msgs.addCheckpoint(info)
proc pragmaBreakpoint(c: PContext, n: PNode) =
discard getOptionalStr(c, n, "")
proc pragmaWatchpoint(c: PContext, n: PNode) =
if n.kind == nkExprColonExpr:
@@ -433,57 +427,59 @@ proc semAsmOrEmit*(con: PContext, n: PNode, marker: char): PNode =
return
# now parse the string literal and substitute symbols:
var a = 0
while true:
while true:
var b = strutils.find(str, marker, a)
var sub = if b < 0: substr(str, a) else: substr(str, a, b - 1)
if sub != "": addSon(result, newStrNode(nkStrLit, sub))
if b < 0: break
if b < 0: break
var c = strutils.find(str, marker, b + 1)
if c < 0: sub = substr(str, b + 1)
else: sub = substr(str, b + 1, c - 1)
if sub != "":
if sub != "":
var e = searchInScopes(con, getIdent(sub))
if e != nil:
if e != nil:
if e.kind == skStub: loadStub(e)
addSon(result, newSymNode(e))
else:
else:
addSon(result, newStrNode(nkStrLit, sub))
else:
# an empty '``' produces a single '`'
addSon(result, newStrNode(nkStrLit, $marker))
if c < 0: break
if c < 0: break
a = c + 1
else: illFormedAst(n)
proc pragmaEmit(c: PContext, n: PNode) =
else:
illFormedAstLocal(n)
result = newNode(nkAsmStmt, n.info)
proc pragmaEmit(c: PContext, n: PNode) =
discard getStrLitNode(c, n)
n.sons[1] = semAsmOrEmit(c, n, '`')
proc noVal(n: PNode) =
proc noVal(n: PNode) =
if n.kind == nkExprColonExpr: invalidPragma(n)
proc pragmaUnroll(c: PContext, n: PNode) =
if c.p.nestedLoopCounter <= 0:
proc pragmaUnroll(c: PContext, n: PNode) =
if c.p.nestedLoopCounter <= 0:
invalidPragma(n)
elif n.kind == nkExprColonExpr:
var unrollFactor = expectIntLit(c, n)
if unrollFactor <% 32:
if unrollFactor <% 32:
n.sons[1] = newIntNode(nkIntLit, unrollFactor)
else:
else:
invalidPragma(n)
proc pragmaLine(c: PContext, n: PNode) =
if n.kind == nkExprColonExpr:
n.sons[1] = c.semConstExpr(c, n.sons[1])
let a = n.sons[1]
if a.kind == nkPar:
if a.kind == nkPar:
var x = a.sons[0]
var y = a.sons[1]
if x.kind == nkExprColonExpr: x = x.sons[1]
if y.kind == nkExprColonExpr: y = y.sons[1]
if x.kind != nkStrLit:
if x.kind != nkStrLit:
localError(n.info, errStringLiteralExpected)
elif y.kind != nkIntLit:
elif y.kind != nkIntLit:
localError(n.info, errIntLiteralExpected)
else:
n.info.fileIndex = msgs.fileInfoIdx(x.strVal)
@@ -494,12 +490,12 @@ proc pragmaLine(c: PContext, n: PNode) =
# sensible default:
n.info = getInfoContext(-1)
proc processPragma(c: PContext, n: PNode, i: int) =
proc processPragma(c: PContext, n: PNode, i: int) =
var it = n.sons[i]
if it.kind != nkExprColonExpr: invalidPragma(n)
elif it.sons[0].kind != nkIdent: invalidPragma(n)
elif it.sons[1].kind != nkIdent: invalidPragma(n)
var userPragma = newSym(skTemplate, it.sons[1].ident, nil, it.info)
var body = newNodeI(nkPragma, n.info)
for j in i+1 .. sonsLen(n)-1: addSon(body, n.sons[j])
@@ -512,7 +508,7 @@ proc pragmaRaisesOrTags(c: PContext, n: PNode) =
if t.kind != tyObject:
localError(x.info, errGenerated, "invalid type for raises/tags list")
x.typ = t
if n.kind == nkExprColonExpr:
let it = n.sons[1]
if it.kind notin {nkCurly, nkBracket}:
@@ -573,7 +569,7 @@ proc deprecatedStmt(c: PContext; pragma: PNode) =
localError(n.info, "key:value pair expected")
proc pragmaGuard(c: PContext; it: PNode; kind: TSymKind): PSym =
if it.kind != nkExprColonExpr:
if it.kind != nkExprColonExpr:
invalidPragma(it); return
let n = it[1]
if n.kind == nkSym:
@@ -596,9 +592,9 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
var key = if it.kind == nkExprColonExpr: it.sons[0] else: it
if key.kind == nkIdent:
var userPragma = strTableGet(c.userPragmas, key.ident)
if userPragma != nil:
if userPragma != nil:
inc c.instCounter
if c.instCounter > 100:
if c.instCounter > 100:
globalError(it.info, errRecursiveDependencyX, userPragma.name.s)
pragma(c, sym, userPragma.ast, validPragmas)
# ensure the pragma is also remember for generic instantiations in other
@@ -607,9 +603,9 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
dec c.instCounter
else:
var k = whichKeyword(key.ident)
if k in validPragmas:
if k in validPragmas:
case k
of wExportc:
of wExportc:
makeExternExport(sym, getOptionalStr(c, it, "$1"), it.info)
incl(sym.flags, sfUsed) # avoid wrong hints
of wImportc: makeExternImport(sym, getOptionalStr(c, it, "$1"))
@@ -631,16 +627,16 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
var align = expectIntLit(c, it)
if (not isPowerOfTwo(align) and align != 0) or align >% high(int16):
localError(it.info, errPowerOfTwoExpected)
else:
else:
sym.typ.align = align.int16
of wSize:
if sym.typ == nil: invalidPragma(it)
var size = expectIntLit(c, it)
if not isPowerOfTwo(size) or size <= 0 or size > 8:
if not isPowerOfTwo(size) or size <= 0 or size > 8:
localError(it.info, errPowerOfTwoExpected)
else:
sym.typ.size = size
of wNodecl:
of wNodecl:
noVal(it)
incl(sym.loc.flags, lfNoDecl)
of wPure, wAsmNoStackFrame:
@@ -648,19 +644,19 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
if sym != nil:
if k == wPure and sym.kind in routineKinds: invalidPragma(it)
else: incl(sym.flags, sfPure)
of wVolatile:
of wVolatile:
noVal(it)
incl(sym.flags, sfVolatile)
of wRegister:
of wRegister:
noVal(it)
incl(sym.flags, sfRegister)
of wThreadVar:
of wThreadVar:
noVal(it)
incl(sym.flags, sfThread)
of wDeadCodeElim: pragmaDeadCodeElim(c, it)
of wNoForward: pragmaNoForward(c, it)
of wMagic: processMagic(c, it, sym)
of wCompileTime:
of wCompileTime:
noVal(it)
incl(sym.flags, sfCompileTime)
incl(sym.loc.flags, lfNoDecl)
@@ -668,17 +664,20 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
noVal(it)
incl(sym.flags, sfGlobal)
incl(sym.flags, sfPure)
of wMerge:
of wMerge:
# only supported for backwards compat, doesn't do anything anymore
noVal(it)
incl(sym.flags, sfMerge)
of wHeader:
of wConstructor:
noVal(it)
incl(sym.flags, sfConstructor)
of wHeader:
var lib = getLib(c, libHeader, getStrLitNode(c, it))
addToLib(lib, sym)
incl(sym.flags, sfImportc)
incl(sym.loc.flags, lfHeader)
incl(sym.loc.flags, lfNoDecl)
incl(sym.loc.flags, lfNoDecl)
# implies nodecl, because otherwise header would not make sense
if sym.loc.r == nil: sym.loc.r = toRope(sym.name.s)
if sym.loc.r == nil: sym.loc.r = rope(sym.name.s)
of wDestructor:
sym.flags.incl sfOverriden
if sym.name.s.normalize != "destroy":
@@ -689,13 +688,13 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
noVal(it)
incl(sym.flags, sfNoSideEffect)
if sym.typ != nil: incl(sym.typ.flags, tfNoSideEffect)
of wSideeffect:
of wSideeffect:
noVal(it)
incl(sym.flags, sfSideEffect)
of wNoreturn:
of wNoreturn:
noVal(it)
incl(sym.flags, sfNoReturn)
of wDynlib:
of wDynlib:
processDynLib(c, it, sym)
of wCompilerproc:
noVal(it) # compilerproc may not get a string!
@@ -707,7 +706,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
if it.kind == nkExprColonExpr: deprecatedStmt(c, it)
elif sym != nil: incl(sym.flags, sfDeprecated)
else: incl(c.module.flags, sfDeprecated)
of wVarargs:
of wVarargs:
noVal(it)
if sym.typ == nil: invalidPragma(it)
else: incl(sym.typ.flags, tfVarargs)
@@ -717,7 +716,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
else:
noVal(it)
incl(sym.flags, sfBorrow)
of wFinal:
of wFinal:
noVal(it)
if sym.typ == nil: invalidPragma(it)
else: incl(sym.typ.flags, tfFinal)
@@ -739,21 +738,20 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
incl(sym.flags, sfProcvar)
if sym.typ != nil: incl(sym.typ.flags, tfThread)
of wGcSafe:
if optThreadAnalysis in gGlobalOptions:
noVal(it)
if sym.kind != skType: incl(sym.flags, sfThread)
if sym.typ != nil: incl(sym.typ.flags, tfGcSafe)
else: invalidPragma(it)
noVal(it)
if sym.kind != skType: incl(sym.flags, sfThread)
if sym.typ != nil: incl(sym.typ.flags, tfGcSafe)
else: invalidPragma(it)
of wPacked:
noVal(it)
if sym.typ == nil: invalidPragma(it)
else: incl(sym.typ.flags, tfPacked)
of wHint: message(it.info, hintUser, expectStrLit(c, it))
of wWarning: message(it.info, warnUser, expectStrLit(c, it))
of wError:
of wError:
if sym != nil and sym.isRoutine:
# This is subtle but correct: the error *statement* is only
# allowed for top level statements. Seems to be easier than
# allowed for top level statements. Seems to be easier than
# distinguishing properly between
# ``proc p() {.error}`` and ``proc p() = {.error: "msg".}``
noVal(it)
@@ -770,11 +768,11 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
of wPassc: extccomp.addCompileOption(expectStrLit(c, it))
of wBreakpoint: pragmaBreakpoint(c, it)
of wWatchPoint: pragmaWatchpoint(c, it)
of wPush:
of wPush:
processPush(c, n, i + 1)
result = true
result = true
of wPop: processPop(c, it)
of wPragma:
of wPragma:
processPragma(c, n, i)
result = true
of wDiscardable:
@@ -784,16 +782,16 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
noVal(it)
if sym != nil: incl(sym.flags, sfNoInit)
of wCodegenDecl: processCodegenDecl(c, it, sym)
of wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks,
wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints,
of wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks,
wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints,
wLinedir, wStacktrace, wLinetrace, wOptimization,
wCallconv,
wCallconv,
wDebugger, wProfiler, wFloatchecks, wNanChecks, wInfChecks,
wPatterns:
if processOption(c, it):
# calling conventions (boring...):
localError(it.info, errOptionExpected)
of FirstCallConv..LastCallConv:
of FirstCallConv..LastCallConv:
assert(sym != nil)
if sym.typ == nil: invalidPragma(it)
else: sym.typ.callConv = wordToCallConv(k)
@@ -845,17 +843,22 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
invalidPragma(it)
else:
sym.guard = pragmaGuard(c, it, sym.kind)
of wGoto:
if sym == nil or sym.kind notin {skVar, skLet}:
invalidPragma(it)
else:
sym.flags.incl sfGoto
of wInjectStmt:
if it.kind != nkExprColonExpr:
localError(it.info, errExprExpected)
else:
else:
it.sons[1] = c.semExpr(c, it.sons[1])
of wExperimental:
noVal(it)
if isTopLevel(c):
c.module.flags.incl sfExperimental
else:
localError(it.info, "'experimental' pragma only valid as toplevel statement")
localError(it.info, "'experimental' pragma only valid as toplevel statement")
else: invalidPragma(it)
else: invalidPragma(it)
else: processNote(c, it)
@@ -867,9 +870,11 @@ proc implicitPragmas*(c: PContext, sym: PSym, n: PNode,
while it != nil:
let o = it.otherPragmas
if not o.isNil:
pushInfoContext(n.info)
for i in countup(0, sonsLen(o) - 1):
if singlePragma(c, sym, o, i, validPragmas):
internalError(n.info, "implicitPragmas")
popInfoContext()
it = it.next.POptionEntry
if lfExportLib in sym.loc.flags and sfExportc notin sym.flags:
@@ -879,7 +884,7 @@ proc implicitPragmas*(c: PContext, sym: PSym, n: PNode,
sfImportc in sym.flags and lib != nil:
incl(sym.loc.flags, lfDynamicLib)
addToLib(lib, sym)
if sym.loc.r == nil: sym.loc.r = toRope(sym.name.s)
if sym.loc.r == nil: sym.loc.r = rope(sym.name.s)
proc hasPragma*(n: PNode, pragma: TSpecialWord): bool =
if n == nil or n.sons == nil:
@@ -889,7 +894,7 @@ proc hasPragma*(n: PNode, pragma: TSpecialWord): bool =
var key = if p.kind == nkExprColonExpr: p[0] else: p
if key.kind == nkIdent and whichKeyword(key.ident) == pragma:
return true
return false
proc pragmaRec(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) =

File diff suppressed because it is too large Load Diff

View File

@@ -277,7 +277,7 @@ proc decodeLoc(r: PRodReader, loc: var TLoc, info: TLineInfo) =
loc.t = nil
if r.s[r.pos] == '!':
inc(r.pos)
loc.r = toRope(decodeStr(r.s, r.pos))
loc.r = rope(decodeStr(r.s, r.pos))
else:
loc.r = nil
if r.s[r.pos] == '>': inc(r.pos)
@@ -344,7 +344,7 @@ proc decodeLib(r: PRodReader, info: TLineInfo): PLib =
result.kind = TLibKind(decodeVInt(r.s, r.pos))
if r.s[r.pos] != '|': internalError("decodeLib: 1")
inc(r.pos)
result.name = toRope(decodeStr(r.s, r.pos))
result.name = rope(decodeStr(r.s, r.pos))
if r.s[r.pos] != '|': internalError("decodeLib: 2")
inc(r.pos)
result.path = decodeNode(r, info)
@@ -666,7 +666,7 @@ proc newRodReader(modfilename: string, crc: TCrc32,
r.readerIndex = readerIndex
r.filename = modfilename
initIdTable(r.syms)
# we terminate the file explicitely with ``\0``, so the cast to `cstring`
# we terminate the file explicitly with ``\0``, so the cast to `cstring`
# is safe:
r.s = cast[cstring](r.memfile.mem)
if startsWith(r.s, "NIM:"):

View File

@@ -186,7 +186,7 @@ proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) =
pushType(w, loc.t)
if loc.r != nil:
add(result, '!')
encodeStr(ropeToStr(loc.r), result)
encodeStr($loc.r, result)
if oldLen + 1 == result.len:
# no data was necessary, so remove the '<' again:
setLen(result, oldLen)
@@ -200,7 +200,7 @@ proc encodeType(w: PRodWriter, t: PType, result: var string) =
return
# we need no surrounding [] here because the type is in a line of its own
if t.kind == tyForward: internalError("encodeType: tyForward")
# for the new rodfile viewer we use a preceeding [ so that the data section
# for the new rodfile viewer we use a preceding [ so that the data section
# can easily be disambiguated:
add(result, '[')
encodeVInt(ord(t.kind), result)
@@ -241,7 +241,7 @@ proc encodeLib(w: PRodWriter, lib: PLib, info: TLineInfo, result: var string) =
add(result, '|')
encodeVInt(ord(lib.kind), result)
add(result, '|')
encodeStr(ropeToStr(lib.name), result)
encodeStr($lib.name, result)
add(result, '|')
encodeNode(w, info, lib.path, result)

View File

@@ -52,84 +52,66 @@
# Note that the left and right pointers are not needed for leaves.
# Leaves have relatively high memory overhead (~30 bytes on a 32
# bit machines) and we produce many of them. This is why we cache and
# share leaves accross different rope trees.
# share leaves across different rope trees.
# To cache them they are inserted in a `cache` array.
import
strutils, platform, hashes, crc, options
import
platform, hashes
type
TFormatStr* = string # later we may change it to CString for better
# performance of the code generator (assignments
FormatStr* = string # later we may change it to CString for better
# performance of the code generator (assignments
# copy the format strings
# though it is not necessary)
PRope* = ref TRope
TRope*{.acyclic.} = object of RootObj # the empty rope is represented
# by nil to safe space
left*, right*: PRope
Rope* = ref RopeObj
RopeObj*{.acyclic.} = object of RootObj # the empty rope is represented
# by nil to safe space
left*, right*: Rope
length*: int
data*: string # != nil if a leaf
TRopeSeq* = seq[PRope]
TRopesError* = enum
RopeSeq* = seq[Rope]
RopesError* = enum
rCannotOpenFile
rInvalidFormatStr
rTokenTooLong
proc con*(a, b: PRope): PRope
proc con*(a: PRope, b: string): PRope
proc con*(a: string, b: PRope): PRope
proc con*(a: varargs[PRope]): PRope
proc app*(a: var PRope, b: PRope)
proc app*(a: var PRope, b: string)
proc prepend*(a: var PRope, b: PRope)
proc toRope*(s: string): PRope
proc toRope*(i: BiggestInt): PRope
proc ropeLen*(a: PRope): int
proc writeRopeIfNotEqual*(r: PRope, filename: string): bool
proc ropeToStr*(p: PRope): string
proc ropef*(frmt: TFormatStr, args: varargs[PRope]): PRope
proc appf*(c: var PRope, frmt: TFormatStr, args: varargs[PRope])
proc ropeEqualsFile*(r: PRope, f: string): bool
# returns true if the rope r is the same as the contents of file f
proc ropeInvariant*(r: PRope): bool
# exported for debugging
# implementation
var errorHandler*: proc(err: TRopesError, msg: string, useWarning = false)
var errorHandler*: proc(err: RopesError, msg: string, useWarning = false)
# avoid dependency on msgs.nim
proc ropeLen(a: PRope): int =
proc len*(a: Rope): int =
## the rope's length
if a == nil: result = 0
else: result = a.length
proc newRope*(data: string = nil): PRope =
proc newRope(data: string = nil): Rope =
new(result)
if data != nil:
if data != nil:
result.length = len(data)
result.data = data
proc newMutableRope*(capacity = 30): PRope =
proc newMutableRope*(capacity = 30): Rope =
## creates a new rope that supports direct modifications of the rope's
## 'data' and 'length' fields.
new(result)
result.data = newStringOfCap(capacity)
proc freezeMutableRope*(r: PRope) {.inline.} =
proc freezeMutableRope*(r: Rope) {.inline.} =
r.length = r.data.len
var
cache: array[0..2048*2 -1, PRope]
var
cache: array[0..2048*2 - 1, Rope]
proc resetRopeCache* =
for i in low(cache)..high(cache):
cache[i] = nil
proc ropeInvariant(r: PRope): bool =
if r == nil:
proc ropeInvariant(r: Rope): bool =
if r == nil:
result = true
else:
else:
result = true #
# if r.data <> snil then
# result := true
@@ -137,13 +119,13 @@ proc ropeInvariant(r: PRope): bool =
# result := (r.left <> nil) and (r.right <> nil);
# if result then result := ropeInvariant(r.left);
# if result then result := ropeInvariant(r.right);
# end
# end
var gCacheTries* = 0
var gCacheMisses* = 0
var gCacheIntTries* = 0
proc insertInCache(s: string): PRope =
proc insertInCache(s: string): Rope =
inc gCacheTries
var h = hash(s) and high(cache)
result = cache[h]
@@ -151,83 +133,78 @@ proc insertInCache(s: string): PRope =
inc gCacheMisses
result = newRope(s)
cache[h] = result
proc toRope(s: string): PRope =
proc rope*(s: string): Rope =
## Converts a string to a rope.
if s.len == 0:
result = nil
else:
result = insertInCache(s)
assert(ropeInvariant(result))
proc ropeSeqInsert(rs: var TRopeSeq, r: PRope, at: Natural) =
var length = len(rs)
if at > length:
setLen(rs, at + 1)
else:
setLen(rs, length + 1) # move old rope elements:
for i in countdown(length, at + 1):
rs[i] = rs[i - 1] # this is correct, I used pen and paper to validate it
rs[at] = r
proc rope*(i: BiggestInt): Rope =
## Converts an int to a rope.
inc gCacheIntTries
result = rope($i)
proc newRecRopeToStr(result: var string, resultLen: var int, r: PRope) =
var stack = @[r]
while len(stack) > 0:
var it = pop(stack)
while it.data == nil:
add(stack, it.right)
it = it.left
assert(it.data != nil)
copyMem(addr(result[resultLen]), addr(it.data[0]), it.length)
inc(resultLen, it.length)
assert(resultLen <= len(result))
proc rope*(f: BiggestFloat): Rope =
## Converts a float to a rope.
result = rope($f)
proc ropeToStr(p: PRope): string =
if p == nil:
result = ""
else:
result = newString(p.length)
var resultLen = 0
newRecRopeToStr(result, resultLen, p)
proc con(a, b: PRope): PRope =
if a == nil: result = b
elif b == nil: result = a
proc `&`*(a, b: Rope): Rope =
if a == nil:
result = b
elif b == nil:
result = a
else:
result = newRope()
result.length = a.length + b.length
result.left = a
result.right = b
proc con(a: PRope, b: string): PRope = result = con(a, toRope(b))
proc con(a: string, b: PRope): PRope = result = con(toRope(a), b)
proc `&`*(a: Rope, b: string): Rope =
## the concatenation operator for ropes.
result = a & rope(b)
proc con(a: varargs[PRope]): PRope =
for i in countup(0, high(a)): result = con(result, a[i])
proc `&`*(a: string, b: Rope): Rope =
## the concatenation operator for ropes.
result = rope(a) & b
proc ropeConcat*(a: varargs[PRope]): PRope =
# not overloaded version of concat to speed-up `rfmt` a little bit
for i in countup(0, high(a)): result = con(result, a[i])
proc `&`*(a: openArray[Rope]): Rope =
## the concatenation operator for an openarray of ropes.
for i in countup(0, high(a)): result = result & a[i]
proc toRope(i: BiggestInt): PRope =
inc gCacheIntTries
result = toRope($i)
proc add*(a: var Rope, b: Rope) =
## adds `b` to the rope `a`.
a = a & b
proc app(a: var PRope, b: PRope) = a = con(a, b)
proc app(a: var PRope, b: string) = a = con(a, b)
proc prepend(a: var PRope, b: PRope) = a = con(b, a)
proc add*(a: var Rope, b: string) =
## adds `b` to the rope `a`.
a = a & b
proc writeRope*(f: File, c: PRope) =
var stack = @[c]
while len(stack) > 0:
var it = pop(stack)
while it.data == nil:
add(stack, it.right)
it = it.left
assert(it != nil)
assert(it.data != nil)
write(f, it.data)
iterator leaves*(r: Rope): string =
## iterates over any leaf string in the rope `r`.
if r != nil:
var stack = @[r]
while stack.len > 0:
var it = stack.pop
while isNil(it.data):
stack.add(it.right)
it = it.left
assert(it != nil)
assert(it.data != nil)
yield it.data
proc writeRope*(head: PRope, filename: string, useWarning = false) =
iterator items*(r: Rope): char =
## iterates over any character in the rope `r`.
for s in leaves(r):
for c in items(s): yield c
proc writeRope*(f: File, r: Rope) =
## writes a rope to a file.
for s in leaves(r): write(f, s)
proc writeRope*(head: Rope, filename: string, useWarning = false) =
var f: File
if open(f, filename, fmWrite):
if head != nil: writeRope(f, head)
@@ -235,42 +212,69 @@ proc writeRope*(head: PRope, filename: string, useWarning = false) =
else:
errorHandler(rCannotOpenFile, filename, useWarning)
proc `$`*(r: Rope): string =
## converts a rope back to a string.
result = newString(r.len)
setLen(result, 0)
for s in leaves(r): add(result, s)
proc ropeConcat*(a: varargs[Rope]): Rope =
# not overloaded version of concat to speed-up `rfmt` a little bit
for i in countup(0, high(a)): result = result & a[i]
proc prepend*(a: var Rope, b: Rope) = a = b & a
proc prepend*(a: var Rope, b: string) = a = b & a
var
rnl* = tnl.newRope
softRnl* = tnl.newRope
proc ropef(frmt: TFormatStr, args: varargs[PRope]): PRope =
proc `%`*(frmt: FormatStr, args: openArray[Rope]): Rope =
var i = 0
var length = len(frmt)
result = nil
var num = 0
while i <= length - 1:
if frmt[i] == '$':
while i < length:
if frmt[i] == '$':
inc(i) # skip '$'
case frmt[i]
of '$':
app(result, "$")
of '$':
add(result, "$")
inc(i)
of '#':
of '#':
inc(i)
app(result, args[num])
add(result, args[num])
inc(num)
of '0'..'9':
of '0'..'9':
var j = 0
while true:
j = (j * 10) + ord(frmt[i]) - ord('0')
while true:
j = j * 10 + ord(frmt[i]) - ord('0')
inc(i)
if (i > length + 0 - 1) or not (frmt[i] in {'0'..'9'}): break
if frmt[i] notin {'0'..'9'}: break
num = j
if j > high(args) + 1:
errorHandler(rInvalidFormatStr, $(j))
else:
app(result, args[j - 1])
add(result, args[j-1])
of '{':
inc(i)
var j = 0
while frmt[i] in {'0'..'9'}:
j = j * 10 + ord(frmt[i]) - ord('0')
inc(i)
num = j
if frmt[i] == '}': inc(i)
else: errorHandler(rInvalidFormatStr, $(frmt[i]))
if j > high(args) + 1:
errorHandler(rInvalidFormatStr, $(j))
else:
add(result, args[j-1])
of 'n':
app(result, softRnl)
inc i
add(result, softRnl)
inc(i)
of 'N':
app(result, rnl)
add(result, rnl)
inc(i)
else:
errorHandler(rInvalidFormatStr, $(frmt[i]))
@@ -278,85 +282,69 @@ proc ropef(frmt: TFormatStr, args: varargs[PRope]): PRope =
while i < length:
if frmt[i] != '$': inc(i)
else: break
if i - 1 >= start:
app(result, substr(frmt, start, i - 1))
if i - 1 >= start:
add(result, substr(frmt, start, i - 1))
assert(ropeInvariant(result))
proc addf*(c: var Rope, frmt: FormatStr, args: openArray[Rope]) =
## shortcut for ``add(c, frmt % args)``.
add(c, frmt % args)
when true:
template `~`*(r: string): PRope = r.ropef
template `~`*(r: string): Rope = r % []
else:
{.push stack_trace: off, line_trace: off.}
proc `~`*(r: static[string]): PRope =
proc `~`*(r: static[string]): Rope =
# this is the new optimized "to rope" operator
# the mnemonic is that `~` looks a bit like a rope :)
var r {.global.} = r.ropef
var r {.global.} = r % []
return r
{.pop.}
proc appf(c: var PRope, frmt: TFormatStr, args: varargs[PRope]) =
app(c, ropef(frmt, args))
const
const
bufSize = 1024 # 1 KB is reasonable
proc auxRopeEqualsFile(r: PRope, bin: var File, buf: pointer): bool =
if r.data != nil:
if r.length > bufSize:
errorHandler(rTokenTooLong, r.data)
return
var readBytes = readBuffer(bin, buf, r.length)
result = readBytes == r.length and
equalMem(buf, addr(r.data[0]), r.length) # BUGFIX
else:
result = auxRopeEqualsFile(r.left, bin, buf)
if result: result = auxRopeEqualsFile(r.right, bin, buf)
proc ropeEqualsFile(r: PRope, f: string): bool =
var bin: File
result = open(bin, f)
if not result:
return # not equal if file does not exist
var buf = alloc(bufSize)
result = auxRopeEqualsFile(r, bin, buf)
if result:
result = readBuffer(bin, buf, bufSize) == 0 # really at the end of file?
dealloc(buf)
close(bin)
proc equalsFile*(r: Rope, f: File): bool =
## returns true if the contents of the file `f` equal `r`.
var
buf: array[bufSize, char]
bpos = buf.len
blen = buf.len
proc crcFromRopeAux(r: PRope, startVal: TCrc32): TCrc32 =
if r.data != nil:
result = startVal
for i in countup(0, len(r.data) - 1):
result = updateCrc32(r.data[i], result)
else:
result = crcFromRopeAux(r.left, startVal)
result = crcFromRopeAux(r.right, result)
for s in leaves(r):
var spos = 0
let slen = s.len
while spos < slen:
if bpos == blen:
# Read more data
bpos = 0
blen = readBuffer(f, addr(buf[0]), buf.len)
if blen == 0: # no more data in file
result = false
return
let n = min(blen - bpos, slen - spos)
# TODO There's gotta be a better way of comparing here...
if not equalMem(addr(buf[bpos]), cast[pointer](cast[int](cstring(s))+spos), n):
result = false
return
spos += n
bpos += n
proc newCrcFromRopeAux(r: PRope, startVal: TCrc32): TCrc32 =
# XXX profiling shows this is actually expensive
var stack: TRopeSeq = @[r]
result = startVal
while len(stack) > 0:
var it = pop(stack)
while it.data == nil:
add(stack, it.right)
it = it.left
assert(it.data != nil)
var i = 0
var L = len(it.data)
while i < L:
result = updateCrc32(it.data[i], result)
inc(i)
result = readBuffer(f, addr(buf[0]), 1) == 0 # check that we've read all
proc crcFromRope(r: PRope): TCrc32 =
result = newCrcFromRopeAux(r, InitCrc32)
proc equalsFile*(r: Rope, filename: string): bool =
## returns true if the contents of the file `f` equal `r`. If `f` does not
## exist, false is returned.
var f: File
result = open(f, filename)
if result:
result = equalsFile(r, f)
close(f)
proc writeRopeIfNotEqual(r: PRope, filename: string): bool =
proc writeRopeIfNotEqual*(r: Rope, filename: string): bool =
# returns true if overwritten
var c: TCrc32
c = crcFromFile(filename)
if c != crcFromRope(r):
if not equalsFile(r, filename):
writeRope(r, filename)
result = true
else:
else:
result = false

View File

@@ -16,7 +16,7 @@ import
procfind, lookups, rodread, pragmas, passes, semdata, semtypinst, sigmatch,
intsets, transf, vmdef, vm, idgen, aliases, cgmeth, lambdalifting,
evaltempl, patterns, parampatterns, sempass2, nimfix.pretty, semmacrosanity,
semparallel, lowerings
semparallel, lowerings, plugins, plugins.active
when defined(nimfix):
import nimfix.prettybase
@@ -47,8 +47,26 @@ proc finishMethod(c: PContext, s: PSym)
proc indexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode
proc typeMismatch(n: PNode, formal, actual: PType) =
if formal.kind != tyError and actual.kind != tyError:
template semIdeForTemplateOrGenericCheck(n, requiresCheck) =
# we check quickly if the node is where the cursor is
when defined(nimsuggest):
if n.info.fileIndex == gTrackPos.fileIndex and n.info.line == gTrackPos.line:
requiresCheck = true
template semIdeForTemplateOrGeneric(c: PContext; n: PNode;
requiresCheck: bool) =
# use only for idetools support; this is pretty slow so generics and
# templates perform some quick check whether the cursor is actually in
# the generic or template.
when defined(nimsuggest):
assert gCmd == cmdIdeTools
if requiresCheck:
if optIdeDebug in gGlobalOptions:
echo "passing to safeSemExpr: ", renderTree(n)
discard safeSemExpr(c, n)
proc typeMismatch(n: PNode, formal, actual: PType) =
if formal.kind != tyError and actual.kind != tyError:
localError(n.info, errGenerated, msgKindToString(errTypeMismatch) &
typeToString(actual) & ") " &
`%`(msgKindToString(errButExpectedX), [typeToString(formal)]))
@@ -71,21 +89,16 @@ proc fitNode(c: PContext, formal: PType, arg: PNode): PNode =
let x = result.skipConv
if x.kind == nkPar and formal.kind != tyExpr:
changeType(x, formal, check=true)
else:
result = skipHiddenSubConv(result)
#result.typ = takeType(formal, arg.typ)
#echo arg.info, " picked ", result.typ.typeToString
proc inferWithMetatype(c: PContext, formal: PType,
arg: PNode, coerceDistincts = false): PNode
var commonTypeBegin = PType(kind: tyExpr)
proc isEmptyContainer(t: PType): bool =
case t.kind
of tyExpr, tyNil: result = true
of tyArray, tyArrayConstr: result = t.sons[1].kind == tyEmpty
of tySet, tySequence, tyOpenArray, tyVarargs:
result = t.sons[0].kind == tyEmpty
of tyGenericInst: result = isEmptyContainer(t.lastSon)
else: result = false
proc commonType*(x, y: PType): PType =
# new type relation that is used for array constructors,
# if expressions, etc.:
@@ -104,7 +117,7 @@ proc commonType*(x, y: PType): PType =
else:
result = newType(tyTypeDesc, a.owner)
rawAddSon(result, newType(tyNone, a.owner))
elif b.kind in {tyArray, tyArrayConstr, tySet, tySequence} and
elif b.kind in {tyArray, tyArrayConstr, tySet, tySequence} and
a.kind == b.kind:
# check for seq[empty] vs. seq[int]
let idx = ord(b.kind in {tyArray, tyArrayConstr})
@@ -112,9 +125,11 @@ proc commonType*(x, y: PType): PType =
elif a.kind == tyTuple and b.kind == tyTuple and a.len == b.len:
var nt: PType
for i in 0.. <a.len:
if isEmptyContainer(a.sons[i]) and not isEmptyContainer(b.sons[i]):
let aEmpty = isEmptyContainer(a.sons[i])
let bEmpty = isEmptyContainer(b.sons[i])
if aEmpty != bEmpty:
if nt.isNil: nt = copyType(a, a.owner, false)
nt.sons[i] = b.sons[i]
nt.sons[i] = if aEmpty: b.sons[i] else: a.sons[i]
if not nt.isNil: result = nt
#elif b.sons[idx].kind == tyEmpty: return x
elif a.kind == tyRange and b.kind == tyRange:
@@ -152,7 +167,7 @@ proc commonType*(x, y: PType): PType =
result = newType(k, r.owner)
result.addSonSkipIntLit(r)
proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym =
proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym =
result = newSym(kind, considerQuotedIdent(n), getCurrOwner(), n.info)
proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym =
@@ -171,13 +186,19 @@ proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym =
proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
allowed: TSymFlags): PSym
# identifier with visability
proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode,
proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode,
allowed: TSymFlags): PSym
proc semStmtScope(c: PContext, n: PNode): PNode
proc typeAllowedCheck(info: TLineInfo; typ: PType; kind: TSymKind) =
let t = typeAllowed(typ, kind)
if t != nil:
if t == typ: localError(info, "invalid type: '" & typeToString(typ) & "'")
else: localError(info, "invalid type: '" & typeToString(t) &
"' in this context: '" & typeToString(typ) & "'")
proc paramsTypeCheck(c: PContext, typ: PType) {.inline.} =
if not typeAllowed(typ, skConst):
localError(typ.n.info, errXisNoType, typeToString(typ))
typeAllowedCheck(typ.n.info, typ, skConst)
proc expectMacroOrTemplateCall(c: PContext, n: PNode): PSym
proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode
@@ -209,7 +230,7 @@ when false:
result = newSymNode(getSysSym"void")
else:
result.typ = makeTypeDesc(c, result.typ)
result.handleIsOperator = proc (n: PNode): PNode =
result = isOpImpl(c, n)
@@ -231,7 +252,7 @@ proc fixupTypeAfterEval(c: PContext, evaluated, eOrig: PNode): PNode =
if result == nil:
result = arg
# for 'tcnstseq' we support [] to become 'seq'
if eOrig.typ.skipTypes(abstractInst).kind == tySequence and
if eOrig.typ.skipTypes(abstractInst).kind == tySequence and
arg.typ.skipTypes(abstractInst).kind == tyArrayConstr:
arg.typ = eOrig.typ
@@ -270,6 +291,7 @@ proc semConstExpr(c: PContext, n: PNode): PNode =
return n
result = getConstExpr(c.module, e)
if result == nil:
#if e.kind == nkEmpty: globalError(n.info, errConstExprExpected)
result = evalConstExpr(c.module, e)
if result == nil or result.kind == nkEmpty:
if e.info != n.info:
@@ -303,7 +325,7 @@ proc semAfterMacroCall(c: PContext, n: PNode, s: PSym,
else:
case s.typ.sons[0].kind
of tyExpr:
# BUGFIX: we cannot expect a type here, because module aliases would not
# BUGFIX: we cannot expect a type here, because module aliases would not
# work then (see the ``tmodulealias`` test)
# semExprWithType(c, result)
result = semExpr(c, result, flags)
@@ -338,28 +360,22 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
result = semAfterMacroCall(c, result, sym, flags)
popInfoContext()
proc forceBool(c: PContext, n: PNode): PNode =
proc forceBool(c: PContext, n: PNode): PNode =
result = fitNode(c, getSysType(tyBool), n)
if result == nil: result = n
proc semConstBoolExpr(c: PContext, n: PNode): PNode =
proc semConstBoolExpr(c: PContext, n: PNode): PNode =
let nn = semExprWithType(c, n)
result = fitNode(c, getSysType(tyBool), nn)
if result == nil:
localError(n.info, errConstExprExpected)
return nn
result = getConstExpr(c.module, result)
if result == nil:
if result == nil:
localError(n.info, errConstExprExpected)
result = nn
type
TSemGenericFlag = enum
withinBind, withinTypeDesc, withinMixin
TSemGenericFlags = set[TSemGenericFlag]
proc semGenericStmt(c: PContext, n: PNode, flags: TSemGenericFlags,
ctx: var IntSet): PNode
proc semGenericStmt(c: PContext, n: PNode): PNode
include semtypes, semtempl, semgnrc, semstmts, semexprs
@@ -386,15 +402,15 @@ proc myOpen(module: PSym): PPassContext =
c.semInferredLambda = semInferredLambda
c.semGenerateInstance = generateInstance
c.semTypeNode = semTypeNode
c.instDeepCopy = sigmatch.instDeepCopy
c.instTypeBoundOp = sigmatch.instTypeBoundOp
pushProcCon(c, module)
pushOwner(c.module)
c.importTable = openScope(c)
c.importTable.addSym(module) # a module knows itself
if sfSystemModule in module.flags:
if sfSystemModule in module.flags:
magicsys.systemModule = module # set global variable!
else:
else:
c.importTable.addSym magicsys.systemModule # import the "System" identifier
importAllSymbols(c, magicsys.systemModule)
c.topLevelScope = openScope(c)
@@ -404,13 +420,13 @@ proc myOpenCached(module: PSym, rd: PRodReader): PPassContext =
result = myOpen(module)
for m in items(rd.methods): methodDef(m, true)
proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
result = semStmt(c, n)
# BUGFIX: process newly generated generics here, not at the end!
if c.lastGenericIdx < c.generics.len:
var a = newNodeI(nkStmtList, n.info)
addCodeForGenerics(c, a)
if sonsLen(a) > 0:
if sonsLen(a) > 0:
# a generic has been added to `a`:
if result.kind != nkEmpty: addSon(a, result)
result = a
@@ -418,17 +434,17 @@ proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
if gCmd == cmdInteractive and not isEmptyType(result.typ):
result = buildEchoStmt(c, result)
result = transformStmt(c.module, result)
proc recoverContext(c: PContext) =
proc recoverContext(c: PContext) =
# clean up in case of a semantic error: We clean up the stacks, etc. This is
# faster than wrapping every stack operation in a 'try finally' block and
# faster than wrapping every stack operation in a 'try finally' block and
# requires far less code.
c.currentScope = c.topLevelScope
while getCurrOwner().kind != skModule: popOwner()
while c.p != nil and c.p.owner.kind != skModule: c.p = c.p.next
proc myProcess(context: PPassContext, n: PNode): PNode =
var c = PContext(context)
proc myProcess(context: PPassContext, n: PNode): PNode =
var c = PContext(context)
# no need for an expensive 'try' if we stop after the first error anyway:
if msgs.gErrorMax <= 1:
result = semStmtAndGenerateGenerics(c, n)
@@ -444,8 +460,8 @@ proc myProcess(context: PPassContext, n: PNode): PNode =
if getCurrentException() of ESuggestDone: result = nil
else: result = ast.emptyNode
#if gCmd == cmdIdeTools: findSuggest(c, n)
proc myClose(context: PPassContext, n: PNode): PNode =
proc myClose(context: PPassContext, n: PNode): PNode =
var c = PContext(context)
closeScope(c) # close module's scope
rawCloseScope(c) # imported symbols; don't check for unused ones!

View File

@@ -1,13 +1,14 @@
#
#
# The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This module implements lifting for assignments and ``deepCopy``.
## This module implements lifting for assignments. Later versions of this code
## will be able to also lift ``=deepCopy`` and ``=destroy``.
# included from sem.nim
@@ -15,98 +16,75 @@ type
TLiftCtx = object
c: PContext
info: TLineInfo # for construction
result: PNode
kind: TTypeAttachedOp
fn: PSym
asgnForType: PType
recurse: bool
type
TFieldInstCtx = object # either 'tup[i]' or 'field' is valid
tupleType: PType # if != nil we're traversing a tuple
tupleIndex: int
field: PSym
replaceByFieldName: bool
proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode)
proc liftBody(c: PContext; typ: PType; info: TLineInfo): PSym
proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
proc at(a, i: PNode, elemType: PType): PNode =
result = newNodeI(nkBracketExpr, a.info, 2)
result.sons[0] = a
result.sons[1] = i
result.typ = elemType
proc liftBodyTup(c: var TLiftCtx; t: PType; body, x, y: PNode) =
for i in 0 .. <t.len:
let lit = lowerings.newIntLit(i)
liftBodyAux(c, t.sons[i], body, x.at(lit, t.sons[i]), y.at(lit, t.sons[i]))
proc dotField(x: PNode, f: PSym): PNode =
result = newNodeI(nkDotExpr, x.info, 2)
result.sons[0] = x
result.sons[1] = newSymNode(f, x.info)
result.typ = f.typ
proc liftBodyObj(c: var TLiftCtx; n, body, x, y: PNode) =
case n.kind
of nkEmpty..pred(nkIdent), succ(nkIdent)..nkNilLit: result = n
of nkIdent:
result = n
var L = sonsLen(forLoop)
if c.replaceByFieldName:
if n.ident.id == forLoop[0].ident.id:
let fieldName = if c.tupleType.isNil: c.field.name.s
elif c.tupleType.n.isNil: "Field" & $c.tupleIndex
else: c.tupleType.n.sons[c.tupleIndex].sym.name.s
result = newStrNode(nkStrLit, fieldName)
return
# other fields:
for i in ord(c.replaceByFieldName)..L-3:
if n.ident.id == forLoop[i].ident.id:
var call = forLoop.sons[L-2]
var tupl = call.sons[i+1-ord(c.replaceByFieldName)]
if c.field.isNil:
result = newNodeI(nkBracketExpr, n.info)
result.add(tupl)
result.add(newIntNode(nkIntLit, c.tupleIndex))
else:
result = newNodeI(nkDotExpr, n.info)
result.add(tupl)
result.add(newSymNode(c.field, n.info))
break
else:
if n.kind == nkContinueStmt:
localError(n.info, errGenerated,
"'continue' not supported in a 'fields' loop")
result = copyNode(n)
newSons(result, sonsLen(n))
for i in countup(0, sonsLen(n)-1):
result.sons[i] = instFieldLoopBody(c, n.sons[i], forLoop)
proc liftBodyObj(c: TLiftCtx; typ, x, y: PNode) =
case typ.kind
of nkSym:
var fc: TFieldInstCtx # either 'tup[i]' or 'field' is valid
fc.field = typ.sym
fc.replaceByFieldName = c.m == mFieldPairs
openScope(c.c)
inc c.c.inUnrolledContext
let body = instFieldLoopBody(fc, lastSon(forLoop), forLoop)
father.add(semStmt(c.c, body))
dec c.c.inUnrolledContext
closeScope(c.c)
let f = n.sym
liftBodyAux(c, f.typ, body, x.dotField(f), y.dotField(f))
of nkNilLit: discard
of nkRecCase:
let L = forLoop.len
let call = forLoop.sons[L-2]
if call.len > 2:
localError(forLoop.info, errGenerated,
"parallel 'fields' iterator does not work for 'case' objects")
return
# iterate over the selector:
asgnForObjectFields(c, typ[0], forLoop, father)
# copy the selector:
liftBodyObj(c, n[0], body, x, y)
# we need to generate a case statement:
var caseStmt = newNodeI(nkCaseStmt, c.info)
# XXX generate 'if' that checks same branches
# generate selector:
var access = newNodeI(nkDotExpr, forLoop.info, 2)
access.sons[0] = call.sons[1]
access.sons[1] = newSymNode(typ.sons[0].sym, forLoop.info)
caseStmt.add(semExprWithType(c.c, access))
var access = dotField(x, n[0].sym)
caseStmt.add(access)
# copy the branches over, but replace the fields with the for loop body:
for i in 1 .. <typ.len:
var branch = copyTree(typ[i])
for i in 1 .. <n.len:
var branch = copyTree(n[i])
let L = branch.len
branch.sons[L-1] = newNodeI(nkStmtList, forLoop.info)
semForObjectFields(c, typ[i].lastSon, forLoop, branch[L-1])
caseStmt.add(branch)
father.add(caseStmt)
of nkRecList:
for t in items(typ): liftBodyObj(c, t, x, y)
else:
illFormedAst(typ)
branch.sons[L-1] = newNodeI(nkStmtList, c.info)
proc newAsgnCall(op: PSym; x, y: PNode): PNode =
liftBodyObj(c, n[i].lastSon, branch.sons[L-1], x, y)
caseStmt.add(branch)
body.add(caseStmt)
localError(c.info, "cannot lift assignment operator to 'case' object")
of nkRecList:
for t in items(n): liftBodyObj(c, t, body, x, y)
else:
illFormedAstLocal(n)
proc genAddr(c: PContext; x: PNode): PNode =
if x.kind == nkHiddenDeref:
checkSonsLen(x, 1)
result = x.sons[0]
else:
result = newNodeIT(nkHiddenAddr, x.info, makeVarType(c, x.typ))
addSon(result, x)
proc newAsgnCall(c: PContext; op: PSym; x, y: PNode): PNode =
if sfError in op.flags:
localError(x.info, errWrongSymbolX, op.name.s)
result = newNodeI(nkCall, x.info)
result.add(newSymNode(op))
result.add x
result.add newSymNode(op)
result.add genAddr(c, x)
result.add y
proc newAsgnStmt(le, ri: PNode): PNode =
@@ -117,81 +95,190 @@ proc newAsgnStmt(le, ri: PNode): PNode =
proc newDestructorCall(op: PSym; x: PNode): PNode =
result = newNodeIT(nkCall, x.info, op.typ.sons[0])
result.add(newSymNode(op))
result.add x
result.add x
proc newDeepCopyCall(op: PSym; x, y: PNode): PNode =
result = newAsgnStmt(x, newDestructorCall(op, y))
proc considerOverloadedOp(c: TLiftCtx; t: PType; x, y: PNode): bool =
let op = t.attachedOps[c.kind]
if op != nil:
markUsed(c.info, op)
styleCheckUse(c.info, op)
case c.kind
of attachedDestructor:
c.result.add newDestructorCall(op, x)
of attachedAsgn:
c.result.add newAsgnCall(op, x, y)
of attachedDeepCopy:
c.result.add newDeepCopyCall(op, x, y)
result = true
proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
case c.kind
of attachedDestructor:
let op = t.destructor
if op != nil:
markUsed(c.info, op)
styleCheckUse(c.info, op)
body.add newDestructorCall(op, x)
result = true
of attachedAsgn:
if tfHasAsgn in t.flags:
var op: PSym
if sameType(t, c.asgnForType):
# generate recursive call:
if c.recurse:
op = c.fn
else:
c.recurse = true
return false
else:
op = t.assignment
if op == nil:
op = liftBody(c.c, t, c.info)
markUsed(c.info, op)
styleCheckUse(c.info, op)
body.add newAsgnCall(c.c, op, x, y)
result = true
of attachedDeepCopy:
let op = t.deepCopy
if op != nil:
markUsed(c.info, op)
styleCheckUse(c.info, op)
body.add newDeepCopyCall(op, x, y)
result = true
proc defaultOp(c: TLiftCtx; t: PType; x, y: PNode) =
proc defaultOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
if c.kind != attachedDestructor:
c.result.add newAsgnStmt(x, y)
body.add newAsgnStmt(x, y)
proc liftBodyAux(c: TLiftCtx; t: PType; x, y: PNode) =
const hasAttachedOp: array[TTypeAttachedOp, TTypeIter] = [
(proc (t: PType, closure: PObject): bool =
t.attachedOp[attachedDestructor] != nil),
(proc (t: PType, closure: PObject): bool =
t.attachedOp[attachedAsgn] != nil),
(proc (t: PType, closure: PObject): bool =
t.attachedOp[attachedDeepCopy] != nil)]
proc addVar(father, v, value: PNode) =
var vpart = newNodeI(nkIdentDefs, v.info, 3)
vpart.sons[0] = v
vpart.sons[1] = ast.emptyNode
vpart.sons[2] = value
addSon(father, vpart)
proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode =
var temp = newSym(skTemp, getIdent(lowerings.genPrefix), c.fn, c.info)
temp.typ = getSysType(tyInt)
incl(temp.flags, sfFromGeneric)
var v = newNodeI(nkVarSection, c.info)
result = newSymNode(temp)
v.addVar(result, lowerings.newIntLit(first))
body.add v
proc genBuiltin(magic: TMagic; name: string; i: PNode): PNode =
result = newNodeI(nkCall, i.info)
result.add createMagic(name, magic).newSymNode
result.add i
proc genWhileLoop(c: var TLiftCtx; i, dest: PNode): PNode =
result = newNodeI(nkWhileStmt, c.info, 2)
let cmp = genBuiltin(mLeI, "<=", i)
cmp.add genHigh(dest)
cmp.typ = getSysType(tyBool)
result.sons[0] = cmp
result.sons[1] = newNodeI(nkStmtList, c.info)
proc addIncStmt(body, i: PNode) =
let incCall = genBuiltin(mInc, "inc", i)
incCall.add lowerings.newIntLit(1)
body.add incCall
proc newSeqCall(c: PContext; x, y: PNode): PNode =
# don't call genAddr(c, x) here:
result = genBuiltin(mNewSeq, "newSeq", x)
let lenCall = genBuiltin(mLengthSeq, "len", y)
lenCall.typ = getSysType(tyInt)
result.add lenCall
proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
case t.kind
of tyNone, tyEmpty: discard
of tyPointer, tySet, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString:
defaultOp(c, t, x, y)
of tyPtr, tyString:
if not considerOverloadedOp(c, t, x, y):
defaultOp(c, t, x, y)
of tyPointer, tySet, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString,
tyPtr, tyString, tyRef:
defaultOp(c, t, body, x, y)
of tyArrayConstr, tyArray, tySequence:
if iterOverType(lastSon(t), hasAttachedOp[c.kind], nil):
# generate loop and call the attached Op:
if tfHasAsgn in t.flags:
if t.kind == tySequence:
# XXX add 'nil' handling here
body.add newSeqCall(c.c, x, y)
let i = declareCounter(c, body, firstOrd(t))
let whileLoop = genWhileLoop(c, i, x)
let elemType = t.lastSon
liftBodyAux(c, elemType, whileLoop.sons[1], x.at(i, elemType),
y.at(i, elemType))
addIncStmt(whileLoop.sons[1], i)
body.add whileLoop
else:
defaultOp(c, t, x, y)
of tyObject:
liftBodyObj(c, t.n, x, y)
defaultOp(c, t, body, x, y)
of tyObject, tyDistinct:
if not considerOverloadedOp(c, t, body, x, y):
if t.sons[0] != nil: liftBodyAux(c, t.sons[0], body, x, y)
if t.kind == tyObject: liftBodyObj(c, t.n, body, x, y)
of tyTuple:
liftBodyTup(c, t, x, y)
of tyRef:
# we MUST not check for acyclic here as a DAG might still share nodes:
liftBodyTup(c, t, body, x, y)
of tyProc:
if t.callConv != ccClosure or c.kind != attachedDeepCopy:
defaultOp(c, t, x, y)
defaultOp(c, t, body, x, y)
else:
# a big problem is that we don't know the enviroment's type here, so we
# have to go through some indirection; we delegate this to the codegen:
call = newNodeI(nkCall, n.info, 2)
let call = newNodeI(nkCall, c.info, 2)
call.typ = t
call.sons[0] = newSymNode(createMagic("deepCopy", mDeepCopy))
call.sons[1] = y
c.result.add newAsgnStmt(x, call)
body.add newAsgnStmt(x, call)
of tyVarargs, tyOpenArray:
localError(c.info, errGenerated, "cannot copy openArray")
of tyFromExpr, tyIter, tyProxy, tyBuiltInTypeClass, tyUserTypeClass,
tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot, tyAnything,
tyMutable, tyGenericParam, tyGenericBody, tyNil, tyExpr, tyStmt,
tyTypeDesc, tyGenericInvokation, tyBigNum, tyConst, tyForward:
tyTypeDesc, tyGenericInvocation, tyBigNum, tyConst, tyForward:
internalError(c.info, "assignment requested for type: " & typeToString(t))
of tyDistinct, tyOrdinal, tyRange,
of tyOrdinal, tyRange,
tyGenericInst, tyFieldAccessor, tyStatic, tyVar:
liftBodyAux(c, lastSon(t))
liftBodyAux(c, lastSon(t), body, x, y)
proc liftBody(c: PContext; typ: PType; info: TLineInfo): PNode =
proc newProcType(info: TLineInfo; owner: PSym): PType =
result = newType(tyProc, owner)
result.n = newNodeI(nkFormalParams, info)
rawAddSon(result, nil) # return type
# result.n[0] used to be `nkType`, but now it's `nkEffectList` because
# the effects are now stored in there too ... this is a bit hacky, but as
# usual we desperately try to save memory:
addSon(result.n, newNodeI(nkEffectList, info))
proc addParam(procType: PType; param: PSym) =
param.position = procType.len-1
addSon(procType.n, newSymNode(param))
rawAddSon(procType, param.typ)
proc liftBody(c: PContext; typ: PType; info: TLineInfo): PSym =
var a: TLiftCtx
a.info = info
a.result = newNodeI(nkStmtList, info)
liftBodyAux(a, typ)
let body = newNodeI(nkStmtList, info)
result = newSym(skProc, getIdent":lifted=", typ.owner, info)
a.fn = result
a.asgnForType = typ
let dest = newSym(skParam, getIdent"dest", result, info)
let src = newSym(skParam, getIdent"src", result, info)
dest.typ = makeVarType(c, typ)
src.typ = typ
result.typ = newProcType(info, typ.owner)
result.typ.addParam dest
result.typ.addParam src
liftBodyAux(a, typ, body, newSymNode(dest).newDeref, newSymNode(src))
var n = newNodeI(nkProcDef, info, bodyPos+1)
for i in 0 .. < n.len: n.sons[i] = emptyNode
n.sons[namePos] = newSymNode(result)
n.sons[paramsPos] = result.typ.n
n.sons[bodyPos] = body
result.ast = n
# register late as recursion is handled differently
typ.assignment = result
#echo "Produced this ", n
proc getAsgnOrLiftBody(c: PContext; typ: PType; info: TLineInfo): PSym =
let t = typ.skipTypes({tyGenericInst, tyVar})
result = t.assignment
if result.isNil:
result = liftBody(c, t, info)
proc overloadedAsgn(c: PContext; dest, src: PNode): PNode =
let a = getAsgnOrLiftBody(c, dest.typ, dest.info)
result = newAsgnCall(c, a, dest, src)

View File

@@ -7,16 +7,16 @@
# distribution, for details about the copyright.
#
## This module implements semantic checking for calls.
## This module implements semantic checking for calls.
# included from sem.nim
proc sameMethodDispatcher(a, b: PSym): bool =
result = false
if a.kind == skMethod and b.kind == skMethod:
if a.kind == skMethod and b.kind == skMethod:
var aa = lastSon(a.ast)
var bb = lastSon(b.ast)
if aa.kind == nkSym and bb.kind == nkSym:
if aa.sym == bb.sym:
if aa.sym == bb.sym:
result = true
else:
discard
@@ -31,7 +31,7 @@ proc sameMethodDispatcher(a, b: PSym): bool =
# to avoid subtle problems, the call remains ambiguous and needs to
# be disambiguated by the programmer; this way the right generic is
# instantiated.
proc determineType(c: PContext, s: PSym)
proc pickBestCandidate(c: PContext, headSymbol: PNode,
@@ -41,62 +41,80 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
best, alt: var TCandidate,
errors: var CandidateErrors) =
var o: TOverloadIter
var sym = initOverloadIter(o, c, headSymbol)
var symScope = o.lastOverloadScope
# thanks to the lazy semchecking for operands, we need to iterate over the
# symbol table *before* any call to 'initCandidate' which might invoke
# semExpr which might modify the symbol table in cases like
# 'init(a, 1, (var b = new(Type2); b))'.
var symx = initOverloadIter(o, c, headSymbol)
let symScope = o.lastOverloadScope
var syms: seq[tuple[a: PSym, b: int]] = @[]
while symx != nil:
if symx.kind in filter: syms.add((symx, o.lastOverloadScope))
symx = nextOverloadIter(o, c, headSymbol)
if syms.len == 0: return
var z: TCandidate
if sym == nil: return
initCandidate(c, best, sym, initialBinding, symScope)
initCandidate(c, alt, sym, initialBinding, symScope)
initCandidate(c, best, syms[0][0], initialBinding, symScope)
initCandidate(c, alt, syms[0][0], initialBinding, symScope)
best.state = csNoMatch
while sym != nil:
if sym.kind in filter:
determineType(c, sym)
initCandidate(c, z, sym, initialBinding, o.lastOverloadScope)
z.calleeSym = sym
matches(c, n, orig, z)
if errors != nil:
errors.safeAdd(sym)
if z.errors != nil:
for err in z.errors:
errors.add(err)
if z.state == csMatch:
# little hack so that iterators are preferred over everything else:
if sym.kind in skIterators: inc(z.exactMatches, 200)
case best.state
of csEmpty, csNoMatch: best = z
of csMatch:
var cmp = cmpCandidates(best, z)
if cmp < 0: best = z # x is better than the best so far
elif cmp == 0: alt = z # x is as good as the best so far
else: discard
sym = nextOverloadIter(o, c, headSymbol)
for i in 0 .. <syms.len:
let sym = syms[i][0]
determineType(c, sym)
initCandidate(c, z, sym, initialBinding, syms[i][1])
z.calleeSym = sym
#if sym.name.s == "*" and (n.info ?? "temp5.nim") and n.info.line == 140:
# gDebug = true
matches(c, n, orig, z)
if errors != nil:
errors.safeAdd(sym)
if z.errors != nil:
for err in z.errors:
errors.add(err)
if z.state == csMatch:
# little hack so that iterators are preferred over everything else:
if sym.kind in skIterators: inc(z.exactMatches, 200)
case best.state
of csEmpty, csNoMatch: best = z
of csMatch:
var cmp = cmpCandidates(best, z)
if cmp < 0: best = z # x is better than the best so far
elif cmp == 0: alt = z # x is as good as the best so far
else: discard
#if sym.name.s == "cmp" and (n.info ?? "rstgen.nim") and n.info.line == 516:
# echo "Matches ", n.info, " ", typeToString(sym.typ)
# debug sym
# writeMatches(z)
# for i in 1 .. <len(z.call):
# z.call[i].typ.debug
# quit 1
proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
# Gives a detailed error message; this is separated from semOverloadedCall,
# as semOverlodedCall is already pretty slow (and we need this information
# only in case of an error).
if c.inCompilesContext > 0:
if c.inCompilesContext > 0:
# fail fast:
globalError(n.info, errTypeMismatch, "")
if errors.len == 0:
if errors.isNil or errors.len == 0:
localError(n.info, errExprXCannotBeCalled, n[0].renderTree)
return
# to avoid confusing errors like:
# to avoid confusing errors like:
# got (SslPtr, SocketHandle)
# but expected one of:
# but expected one of:
# openssl.SSL_set_fd(ssl: SslPtr, fd: SocketHandle): cint
# we do a pre-analysis. If all types produce the same string, we will add
# module information.
let proto = describeArgs(c, n, 1, preferName)
var prefer = preferName
for err in errors:
var errProto = ""
let n = err.typ.n
for i in countup(1, n.len - 1):
for i in countup(1, n.len - 1):
var p = n.sons[i]
if p.kind == nkSym:
add(errProto, typeToString(p.sym.typ, preferName))
@@ -123,7 +141,8 @@ proc gatherUsedSyms(c: PContext, usedSyms: var seq[PNode]) =
for s in scope.usingSyms: usedSyms.safeAdd(s)
proc resolveOverloads(c: PContext, n, orig: PNode,
filter: TSymKinds): TCandidate =
filter: TSymKinds;
errors: var CandidateErrors): TCandidate =
var initialBinding: PNode
var alt: TCandidate
var f = n.sons[0]
@@ -134,7 +153,6 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
else:
initialBinding = nil
var errors: CandidateErrors
var usedSyms: seq[PNode]
template pickBest(headSymbol: expr) =
@@ -148,9 +166,9 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
n.sons.insert(hiddenArg, 1)
orig.sons.insert(hiddenArg, 1)
pickBest(f)
if result.state != csMatch:
n.sons.delete(1)
orig.sons.delete(1)
@@ -168,7 +186,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
# we are going to try multiple variants
n.sons[0..1] = [nil, n[1], calleeName]
orig.sons[0..1] = [nil, orig[1], calleeName]
template tryOp(x) =
let op = newIdentNode(getIdent(x), n.info)
n.sons[0] = op
@@ -177,18 +195,19 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
if nfExplicitCall in n.flags:
tryOp ".()"
if result.state in {csEmpty, csNoMatch}:
tryOp "."
elif nfDotSetter in n.flags:
internalAssert f.kind == nkIdent and n.sonsLen == 3
let calleeName = newStrNode(nkStrLit, f.ident.s[0.. -2]).withInfo(n.info)
let calleeName = newStrNode(nkStrLit,
f.ident.s[0..f.ident.s.len-2]).withInfo(n.info)
let callOp = newIdentNode(getIdent".=", n.info)
n.sons[0..1] = [callOp, n[1], calleeName]
orig.sons[0..1] = [callOp, orig[1], calleeName]
pickBest(callOp)
if overloadsState == csEmpty and result.state == csEmpty:
localError(n.info, errUndeclaredIdentifier, considerQuotedIdent(f).s)
return
@@ -204,7 +223,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
errors = @[]
pickBest(f)
notFoundError(c, n, errors)
#notFoundError(c, n, errors)
return
@@ -213,7 +232,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
internalAssert result.state == csMatch
#writeMatches(result)
#writeMatches(alt)
if c.inCompilesContext > 0:
if c.inCompilesContext > 0:
# quick error message for performance of 'compiles' built-in:
globalError(n.info, errGenerated, "ambiguous call")
elif gErrorCounter == 0:
@@ -243,7 +262,7 @@ proc instGenericConvertersSons*(c: PContext, n: PNode, x: TCandidate) =
for i in 1 .. <n.len:
instGenericConvertersArg(c, n.sons[i], x)
proc indexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode =
proc indexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode =
var m: TCandidate
initCandidate(c, m, f)
result = paramTypesMatch(m, f, a, arg, nil)
@@ -260,7 +279,7 @@ proc inferWithMetatype(c: PContext, formal: PType,
instGenericConvertersArg(c, result, m)
if result != nil:
# This almost exactly replicates the steps taken by the compiler during
# param matching. It performs an embarassing ammount of back-and-forth
# param matching. It performs an embarrassing amount of back-and-forth
# type jugling, but it's the price to pay for consistency and correctness
result.typ = generateTypeInstance(c, m.bindings, arg.info,
formal.skipTypes({tyCompositeTypeClass}))
@@ -277,27 +296,47 @@ proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode =
styleCheckUse(n.sons[0].info, finalCallee)
if finalCallee.ast == nil:
internalError(n.info, "calleeSym.ast is nil") # XXX: remove this check!
if x.hasFauxMatch:
result = x.call
result.sons[0] = newSymNode(finalCallee, result.sons[0].info)
if containsGenericType(result.typ) or x.fauxMatch == tyUnknown:
result.typ = newTypeS(x.fauxMatch, c)
return
if finalCallee.ast.sons[genericParamsPos].kind != nkEmpty:
# a generic proc!
if not x.proxyMatch:
finalCallee = generateInstance(c, x.calleeSym, x.bindings, n.info)
else:
result = x.call
result.sons[0] = newSymNode(finalCallee, result.sons[0].info)
result.typ = finalCallee.typ.sons[0]
if containsGenericType(result.typ): result.typ = errorType(c)
return
finalCallee = generateInstance(c, x.calleeSym, x.bindings, n.info)
result = x.call
instGenericConvertersSons(c, result, x)
result.sons[0] = newSymNode(finalCallee, result.sons[0].info)
result.typ = finalCallee.typ.sons[0]
proc canDeref(n: PNode): bool {.inline.} =
result = n.len >= 2 and (let t = n[1].typ;
t != nil and t.skipTypes({tyGenericInst}).kind in {tyPtr, tyRef})
proc tryDeref(n: PNode): PNode =
result = newNodeI(nkHiddenDeref, n.info)
result.typ = n.typ.skipTypes(abstractInst).sons[0]
result.addSon(n)
proc semOverloadedCall(c: PContext, n, nOrig: PNode,
filter: TSymKinds): PNode =
var r = resolveOverloads(c, n, nOrig, filter)
var errors: CandidateErrors
var r = resolveOverloads(c, n, nOrig, filter, errors)
if r.state == csMatch: result = semResolvedCall(c, n, r)
elif experimentalMode(c) and canDeref(n):
# try to deref the first argument and then try overloading resolution again:
n.sons[1] = n.sons[1].tryDeref
var r = resolveOverloads(c, n, nOrig, filter, errors)
if r.state == csMatch: result = semResolvedCall(c, n, r)
else:
# get rid of the deref again for a better error message:
n.sons[1] = n.sons[1].sons[0]
notFoundError(c, n, errors)
else:
notFoundError(c, n, errors)
# else: result = errorNode(c, n)
proc explicitGenericInstError(n: PNode): PNode =
localError(n.info, errCannotInstantiateX, renderTree(n))
result = n
@@ -310,7 +349,7 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode =
styleCheckUse(n.info, s)
result = newSymNode(newInst, n.info)
proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
assert n.kind == nkBracketExpr
for i in 1..sonsLen(n)-1:
n.sons[i].typ = semTypeNode(c, n.sons[i], nil)
@@ -330,11 +369,11 @@ proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
# XXX I think this could be improved by reusing sigmatch.paramTypesMatch.
# It's good enough for now.
result = newNodeI(a.kind, n.info)
for i in countup(0, len(a)-1):
for i in countup(0, len(a)-1):
var candidate = a.sons[i].sym
if candidate.kind in {skProc, skMethod, skConverter,
skIterator, skClosureIterator}:
# it suffices that the candidate has the proper number of generic
# it suffices that the candidate has the proper number of generic
# type parameters:
if safeLen(candidate.ast.sons[genericParamsPos]) == n.len-1:
result.add(explicitGenericSym(c, n, candidate))

View File

@@ -9,13 +9,13 @@
## This module contains the data structures for the semantic checking phase.
import
import
strutils, lists, intsets, options, lexer, ast, astalgo, trees, treetab,
wordrecg,
ropes, msgs, platform, os, condsyms, idents, renderer, types, extccomp, math,
wordrecg,
ropes, msgs, platform, os, condsyms, idents, renderer, types, extccomp, math,
magicsys, nversion, nimsets, parser, times, passes, rodread, vmdef
type
type
TOptionEntry* = object of lists.TListEntry # entries to put on a
# stack for pragma parsing
options*: TOptions
@@ -26,7 +26,7 @@ type
POptionEntry* = ref TOptionEntry
PProcCon* = ref TProcCon
TProcCon*{.final.} = object # procedure context; also used for top-level
TProcCon* = object # procedure context; also used for top-level
# statements
owner*: PSym # the symbol this context belongs to
resultSym*: PSym # the result symbol (if we are in a proc)
@@ -35,16 +35,24 @@ type
inTryStmt*: int # whether we are in a try statement; works also
# in standalone ``except`` and ``finally``
next*: PProcCon # used for stacking procedure contexts
wasForwarded*: bool # whether the current proc has a separate header
bracketExpr*: PNode # current bracket expression (for ^ support)
TInstantiationPair* = object
genericSym*: PSym
inst*: PInstantiation
TExprFlag* = enum
efLValue, efWantIterator, efInTypeof, efWantStmt, efDetermineType,
TExprFlag* = enum
efLValue, efWantIterator, efInTypeof,
efWantStmt, efAllowStmt, efDetermineType,
efAllowDestructor, efWantValue, efOperand, efNoSemCheck
TExprFlags* = set[TExprFlag]
TTypeAttachedOp* = enum
attachedAsgn,
attachedDeepCopy,
attachedDestructor
PContext* = ref TContext
TContext* = object of TPassContext # a context represents a module
module*: PSym # the module sym belonging to the context
@@ -56,7 +64,7 @@ type
# this is used so that generic instantiations
# can access private object fields
instCounter*: int # to prevent endless instantiations
ambiguousSymbols*: IntSet # ids of all ambiguous symbols (cannot
# store this info in the syms themselves!)
inTypeClass*: int # > 0 if we are in a user-defined type class
@@ -91,10 +99,10 @@ type
lastGenericIdx*: int # used for the generics stack
hloLoopDetector*: int # used to prevent endless loops in the HLO
inParallelStmt*: int
instDeepCopy*: proc (c: PContext; dc: PSym; t: PType;
info: TLineInfo): PSym {.nimcall.}
instTypeBoundOp*: proc (c: PContext; dc: PSym; t: PType; info: TLineInfo;
op: TTypeAttachedOp): PSym {.nimcall.}
proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair =
result.genericSym = s
result.inst = inst
@@ -110,7 +118,6 @@ proc newOptionEntry*(): POptionEntry
proc newLib*(kind: TLibKind): PLib
proc addToLib*(lib: PLib, sym: PSym)
proc makePtrType*(c: PContext, baseType: PType): PType
proc makeVarType*(c: PContext, baseType: PType): PType
proc newTypeS*(kind: TTypeKind, c: PContext): PType
proc fillTypeS*(dest: PType, kind: TTypeKind, c: PContext)
@@ -126,7 +133,7 @@ proc popOwner*()
var gOwners*: seq[PSym] = @[]
proc getCurrOwner(): PSym =
proc getCurrOwner(): PSym =
# owner stack (used for initializing the
# owner field of syms)
# the documentation comment always gets
@@ -134,19 +141,19 @@ proc getCurrOwner(): PSym =
# BUGFIX: global array is needed!
result = gOwners[high(gOwners)]
proc pushOwner(owner: PSym) =
proc pushOwner(owner: PSym) =
add(gOwners, owner)
proc popOwner() =
proc popOwner() =
var length = len(gOwners)
if length > 0: setLen(gOwners, length - 1)
else: internalError("popOwner")
proc lastOptionEntry(c: PContext): POptionEntry =
proc lastOptionEntry(c: PContext): POptionEntry =
result = POptionEntry(c.optionStack.tail)
proc pushProcCon*(c: PContext, owner: PSym) {.inline.} =
if owner == nil:
proc pushProcCon*(c: PContext, owner: PSym) {.inline.} =
if owner == nil:
internalError("owner is nil")
return
var x: PProcCon
@@ -157,7 +164,7 @@ proc pushProcCon*(c: PContext, owner: PSym) {.inline.} =
proc popProcCon*(c: PContext) {.inline.} = c.p = c.p.next
proc newOptionEntry(): POptionEntry =
proc newOptionEntry(): POptionEntry =
new(result)
result.options = gOptions
result.defaultCC = ccDefault
@@ -181,8 +188,8 @@ proc newContext(module: PSym): PContext =
proc inclSym(sq: var TSymSeq, s: PSym) =
var L = len(sq)
for i in countup(0, L - 1):
if sq[i].id == s.id: return
for i in countup(0, L - 1):
if sq[i].id == s.id: return
setLen(sq, L + 1)
sq[L] = s
@@ -192,22 +199,25 @@ proc addConverter*(c: PContext, conv: PSym) =
proc addPattern*(c: PContext, p: PSym) =
inclSym(c.patterns, p)
proc newLib(kind: TLibKind): PLib =
proc newLib(kind: TLibKind): PLib =
new(result)
result.kind = kind #initObjectSet(result.syms)
proc addToLib(lib: PLib, sym: PSym) =
#if sym.annex != nil and not isGenericRoutine(sym):
# LocalError(sym.info, errInvalidPragma)
sym.annex = lib
proc makePtrType(c: PContext, baseType: PType): PType =
proc makePtrType(c: PContext, baseType: PType): PType =
result = newTypeS(tyPtr, c)
addSonSkipIntLit(result, baseType.assertNotNil)
proc makeVarType(c: PContext, baseType: PType): PType =
result = newTypeS(tyVar, c)
addSonSkipIntLit(result, baseType.assertNotNil)
proc makeVarType*(c: PContext, baseType: PType): PType =
if baseType.kind == tyVar:
result = baseType
else:
result = newTypeS(tyVar, c)
addSonSkipIntLit(result, baseType.assertNotNil)
proc makeTypeDesc*(c: PContext, typ: PType): PType =
result = newTypeS(tyTypeDesc, c)
@@ -220,6 +230,7 @@ proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode =
proc makeTypeFromExpr*(c: PContext, n: PNode): PType =
result = newTypeS(tyFromExpr, c)
assert n != nil
result.n = n
proc newTypeWithSons*(c: PContext, kind: TTypeKind,
@@ -238,6 +249,7 @@ proc makeAndType*(c: PContext, t1, t2: PType): PType =
propagateToOwner(result, t1)
propagateToOwner(result, t2)
result.flags.incl((t1.flags + t2.flags) * {tfHasStatic})
result.flags.incl tfHasMeta
proc makeOrType*(c: PContext, t1, t2: PType): PType =
result = newTypeS(tyOr, c)
@@ -245,12 +257,14 @@ proc makeOrType*(c: PContext, t1, t2: PType): PType =
propagateToOwner(result, t1)
propagateToOwner(result, t2)
result.flags.incl((t1.flags + t2.flags) * {tfHasStatic})
result.flags.incl tfHasMeta
proc makeNotType*(c: PContext, t1: PType): PType =
result = newTypeS(tyNot, c)
result.sons = @[t1]
propagateToOwner(result, t1)
result.flags.incl(t1.flags * {tfHasStatic})
result.flags.incl tfHasMeta
proc nMinusOne*(n: PNode): PNode =
result = newNode(nkCall, n.info, @[
@@ -268,7 +282,7 @@ proc makeRangeWithStaticExpr*(c: PContext, n: PNode): PType =
template rangeHasStaticIf*(t: PType): bool =
# this accepts the ranges's node
t.n[1].kind == nkStaticExpr
t.n != nil and t.n.len > 1 and t.n[1].kind == nkStaticExpr
template getStaticTypeFromRange*(t: PType): PType =
t.n[1][0][1].typ
@@ -284,7 +298,7 @@ proc errorNode*(c: PContext, n: PNode): PNode =
result = newNodeI(nkEmpty, n.info)
result.typ = errorType(c)
proc fillTypeS(dest: PType, kind: TTypeKind, c: PContext) =
proc fillTypeS(dest: PType, kind: TTypeKind, c: PContext) =
dest.kind = kind
dest.owner = getCurrOwner()
dest.size = - 1
@@ -306,13 +320,16 @@ proc markIndirect*(c: PContext, s: PSym) {.inline.} =
proc illFormedAst*(n: PNode) =
globalError(n.info, errIllFormedAstX, renderTree(n, {renderNoComments}))
proc checkSonsLen*(n: PNode, length: int) =
proc illFormedAstLocal*(n: PNode) =
localError(n.info, errIllFormedAstX, renderTree(n, {renderNoComments}))
proc checkSonsLen*(n: PNode, length: int) =
if sonsLen(n) != length: illFormedAst(n)
proc checkMinSonsLen*(n: PNode, length: int) =
proc checkMinSonsLen*(n: PNode, length: int) =
if sonsLen(n) < length: illFormedAst(n)
proc isTopLevel*(c: PContext): bool {.inline.} =
proc isTopLevel*(c: PContext): bool {.inline.} =
result = c.currentScope.depthLevel <= 2
proc experimentalMode*(c: PContext): bool {.inline.} =

View File

@@ -12,7 +12,7 @@
# included from sem.nim
# special marker values that indicates that we are
# 1) AnalyzingDestructor: currently analyzing the type for destructor
# 1) AnalyzingDestructor: currently analyzing the type for destructor
# generation (needed for recursive types)
# 2) DestructorIsTrivial: completed the analysis before and determined
# that the type has a trivial destructor
@@ -30,7 +30,7 @@ proc instantiateDestructor(c: PContext, typ: PType): PType
proc doDestructorStuff(c: PContext, s: PSym, n: PNode) =
var t = s.typ.sons[1].skipTypes({tyVar})
if t.kind == tyGenericInvokation:
if t.kind == tyGenericInvocation:
for i in 1 .. <t.sonsLen:
if t.sons[i].kind != tyGenericParam:
localError(n.info, errDestructorNotGenericEnough)
@@ -41,7 +41,7 @@ proc doDestructorStuff(c: PContext, s: PSym, n: PNode) =
if t.kind != tyGenericBody:
localError(n.info, errDestructorNotGenericEnough)
return
t.destructor = s
# automatically insert calls to base classes' destructors
if n.sons[bodyPos].kind != nkEmpty:
@@ -71,17 +71,18 @@ proc destroyCase(c: PContext, n: PNode, holder: PNode): PNode =
result.addSon(newNode(nkDotExpr, n.info, @[holder, n.sons[0]]))
for i in countup(1, n.len - 1):
# of A, B:
var caseBranch = newNode(n[i].kind, n[i].info, n[i].sons[0 .. -2])
let stmt = destroyFieldOrFields(c, n[i].lastSon, holder)
let ni = n[i]
var caseBranch = newNode(ni.kind, ni.info, ni.sons[0..ni.len-2])
let stmt = destroyFieldOrFields(c, ni.lastSon, holder)
if stmt == nil:
caseBranch.addSon(newNode(nkStmtList, n[i].info, @[]))
caseBranch.addSon(newNode(nkStmtList, ni.info, @[]))
else:
caseBranch.addSon(stmt)
nonTrivialFields += stmt.len
result.addSon(caseBranch)
# maybe no fields were destroyed?
if nonTrivialFields == 0:
result = nil
@@ -107,7 +108,7 @@ proc destroyFieldOrFields(c: PContext, field: PNode, holder: PNode): PNode =
proc generateDestructor(c: PContext, t: PType): PNode =
## generate a destructor for a user-defined object or tuple type
## returns nil if the destructor turns out to be trivial
# XXX: This may be true for some C-imported types such as
# Tposix_spawnattr
if t.n == nil or t.n.sons == nil: return
@@ -120,13 +121,13 @@ proc generateDestructor(c: PContext, t: PType): PNode =
proc instantiateDestructor(c: PContext, typ: PType): PType =
# returns nil if a variable of type `typ` doesn't require a
# destructor. Otherwise, returns the type, which holds the
# destructor. Otherwise, returns the type, which holds the
# destructor that must be used for the varialbe.
# The destructor is either user-defined or automatically
# generated by the compiler in a member-wise fashion.
var t = skipTypes(typ, {tyConst, tyMutable}).skipGenericAlias
let typeHoldingUserDefinition = if t.kind == tyGenericInst: t.base else: t
if typeHoldingUserDefinition.destructor != nil:
# XXX: This is not entirely correct for recursive types, but we need
# it temporarily to hide the "destroy is already defined" problem
@@ -135,7 +136,7 @@ proc instantiateDestructor(c: PContext, typ: PType): PType =
return typeHoldingUserDefinition
else:
return nil
t = t.skipTypes({tyGenericInst})
case t.kind
of tySequence, tyArray, tyArrayConstr, tyOpenArray, tyVarargs:
@@ -200,16 +201,16 @@ proc insertDestructors(c: PContext,
varId = varSection[j][0]
varTyp = varId.sym.typ
info = varId.info
if varTyp == nil or sfGlobal in varId.sym.flags: continue
let destructableT = instantiateDestructor(c, varTyp)
if destructableT != nil:
var tryStmt = newNodeI(nkTryStmt, info)
if j < totalVars - 1:
var remainingVars = newNodeI(varSection.kind, info)
remainingVars.sons = varSection.sons[(j+1)..(-1)]
remainingVars.sons = varSection.sons[(j+1)..varSection.len-1]
let (outer, inner) = insertDestructors(c, remainingVars)
if outer != nil:
tryStmt.addSon(outer)
@@ -221,7 +222,7 @@ proc insertDestructors(c: PContext,
else:
result.inner = newNodeI(nkStmtList, info)
tryStmt.addSon(result.inner)
tryStmt.addSon(
newNode(nkFinally, info, @[
semStmt(c, newNode(nkCall, info, @[

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
#
#
# The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -19,12 +19,13 @@ type
proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
case n.kind
of nkEmpty..pred(nkIdent), succ(nkIdent)..nkNilLit: result = n
of nkIdent:
of nkEmpty..pred(nkIdent), succ(nkSym)..nkNilLit: result = n
of nkIdent, nkSym:
result = n
let ident = considerQuotedIdent(n)
var L = sonsLen(forLoop)
if c.replaceByFieldName:
if n.ident.id == forLoop[0].ident.id:
if ident.id == considerQuotedIdent(forLoop[0]).id:
let fieldName = if c.tupleType.isNil: c.field.name.s
elif c.tupleType.n.isNil: "Field" & $c.tupleIndex
else: c.tupleType.n.sons[c.tupleIndex].sym.name.s
@@ -32,7 +33,7 @@ proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
return
# other fields:
for i in ord(c.replaceByFieldName)..L-3:
if n.ident.id == forLoop[i].ident.id:
if ident.id == considerQuotedIdent(forLoop[i]).id:
var call = forLoop.sons[L-2]
var tupl = call.sons[i+1-ord(c.replaceByFieldName)]
if c.field.isNil:
@@ -98,7 +99,7 @@ proc semForObjectFields(c: TFieldsCtx, typ, forLoop, father: PNode) =
of nkRecList:
for t in items(typ): semForObjectFields(c, t, forLoop, father)
else:
illFormedAst(typ)
illFormedAstLocal(typ)
proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
# so that 'break' etc. work as expected, we produce
@@ -155,7 +156,7 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
dec(c.p.nestedLoopCounter)
# for TR macros this 'while true: ...; break' loop is pretty bad, so
# we avoid it now if we can:
if hasSonWith(stmts, nkBreakStmt):
if containsNode(stmts, {nkBreakStmt}):
var b = newNodeI(nkBreakStmt, n.info)
b.add(ast.emptyNode)
stmts.add(b)

View File

@@ -1,7 +1,7 @@
#
#
# The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -10,8 +10,8 @@
# this module folds constants; used by semantic checking phase
# and evaluation phase
import
strutils, lists, options, ast, astalgo, trees, treetab, nimsets, times,
import
strutils, lists, options, ast, astalgo, trees, treetab, nimsets, times,
nversion, platform, math, msgs, os, condsyms, idents, renderer, types,
commands, magicsys, saturate
@@ -41,7 +41,7 @@ proc newIntNodeT(intVal: BiggestInt, n: PNode): PNode =
result.typ = n.typ
result.info = n.info
proc newFloatNodeT(floatVal: BiggestFloat, n: PNode): PNode =
proc newFloatNodeT(floatVal: BiggestFloat, n: PNode): PNode =
result = newFloatNode(nkFloatLit, floatVal)
if skipTypes(n.typ, abstractVarRange).kind == tyFloat:
result.typ = getFloatLitType(result)
@@ -49,27 +49,27 @@ proc newFloatNodeT(floatVal: BiggestFloat, n: PNode): PNode =
result.typ = n.typ
result.info = n.info
proc newStrNodeT(strVal: string, n: PNode): PNode =
proc newStrNodeT(strVal: string, n: PNode): PNode =
result = newStrNode(nkStrLit, strVal)
result.typ = n.typ
result.info = n.info
proc ordinalValToString*(a: PNode): string =
proc ordinalValToString*(a: PNode): string =
# because $ has the param ordinal[T], `a` is not necessarily an enum, but an
# ordinal
var x = getInt(a)
var t = skipTypes(a.typ, abstractRange)
case t.kind
of tyChar:
of tyChar:
result = $chr(int(x) and 0xff)
of tyEnum:
var n = t.n
for i in countup(0, sonsLen(n) - 1):
for i in countup(0, sonsLen(n) - 1):
if n.sons[i].kind != nkSym: internalError(a.info, "ordinalValToString")
var field = n.sons[i].sym
if field.position == x:
if field.ast == nil:
if field.position == x:
if field.ast == nil:
return field.name.s
else:
return field.ast.strVal
@@ -112,12 +112,15 @@ proc pickMaxInt(n: PNode): BiggestInt =
else:
internalError(n.info, "pickMaxInt")
proc makeRange(typ: PType, first, last: BiggestInt): PType =
proc makeRange(typ: PType, first, last: BiggestInt): PType =
let minA = min(first, last)
let maxA = max(first, last)
let lowerNode = newIntNode(nkIntLit, minA)
if typ.kind == tyInt and minA == maxA:
result = getIntLitType(lowerNode)
elif typ.kind in {tyUint, tyUInt64}:
# these are not ordinal types, so you get no subrange type for these:
result = typ
else:
var n = newNode(nkRange)
addSon(n, lowerNode)
@@ -135,10 +138,11 @@ proc makeRangeF(typ: PType, first, last: BiggestFloat): PType =
addSonSkipIntLit(result, skipTypes(typ, {tyRange}))
proc getIntervalType*(m: TMagic, n: PNode): PType =
# Nimrod requires interval arithmetic for ``range`` types. Lots of tedious
# Nim requires interval arithmetic for ``range`` types. Lots of tedious
# work but the feature is very nice for reducing explicit conversions.
const ordIntLit = {nkIntLit..nkUInt64Lit}
result = n.typ
template commutativeOp(opr: expr) {.immediate.} =
let a = n.sons[1]
let b = n.sons[2]
@@ -146,7 +150,7 @@ proc getIntervalType*(m: TMagic, n: PNode): PType =
result = makeRange(pickIntRange(a.typ, b.typ),
opr(pickMinInt(a), pickMinInt(b)),
opr(pickMaxInt(a), pickMaxInt(b)))
template binaryOp(opr: expr) {.immediate.} =
let a = n.sons[1]
let b = n.sons[2]
@@ -154,7 +158,7 @@ proc getIntervalType*(m: TMagic, n: PNode): PType =
result = makeRange(a.typ,
opr(pickMinInt(a), pickMinInt(b)),
opr(pickMaxInt(a), pickMaxInt(b)))
case m
of mUnaryMinusI, mUnaryMinusI64:
let a = n.sons[1].typ
@@ -170,13 +174,19 @@ proc getIntervalType*(m: TMagic, n: PNode): PType =
let a = n.sons[1].typ
if isFloatRange(a):
# abs(-5.. 1) == (1..5)
result = makeRangeF(a, abs(getFloat(a.n.sons[1])),
abs(getFloat(a.n.sons[0])))
if a.n[0].floatVal <= 0.0:
result = makeRangeF(a, 0.0, abs(getFloat(a.n.sons[0])))
else:
result = makeRangeF(a, abs(getFloat(a.n.sons[1])),
abs(getFloat(a.n.sons[0])))
of mAbsI, mAbsI64:
let a = n.sons[1].typ
if isIntRange(a):
result = makeRange(a, `|abs|`(getInt(a.n.sons[1])),
`|abs|`(getInt(a.n.sons[0])))
if a.n[0].intVal <= 0:
result = makeRange(a, 0, `|abs|`(getInt(a.n.sons[0])))
else:
result = makeRange(a, `|abs|`(getInt(a.n.sons[1])),
`|abs|`(getInt(a.n.sons[0])))
of mSucc:
let a = n.sons[1].typ
let b = n.sons[2].typ
@@ -202,15 +212,15 @@ proc getIntervalType*(m: TMagic, n: PNode): PType =
var a = n.sons[1]
var b = n.sons[2]
# symmetrical:
if b.kind notin {nkIntLit..nkUInt32Lit}: swap(a, b)
if b.kind in {nkIntLit..nkUInt32Lit}:
if b.kind notin ordIntLit: swap(a, b)
if b.kind in ordIntLit:
let x = b.intVal|+|1
if (x and -x) == x and x >= 0:
result = makeRange(a.typ, 0, b.intVal)
of mModU:
let a = n.sons[1]
let b = n.sons[2]
if b.kind in {nkIntLit..nkUInt32Lit}:
if a.kind in ordIntLit:
if b.intVal >= 0:
result = makeRange(a.typ, 0, b.intVal-1)
else:
@@ -226,12 +236,12 @@ proc getIntervalType*(m: TMagic, n: PNode): PType =
result = makeRange(a.typ, b.intVal+1, -(b.intVal+1))
of mDivI, mDivI64, mDivU:
binaryOp(`|div|`)
of mMinI, mMinI64:
of mMinI:
commutativeOp(min)
of mMaxI, mMaxI64:
of mMaxI:
commutativeOp(max)
else: discard
discard """
mShlI, mShlI64,
mShrI, mShrI64, mAddF64, mSubF64, mMulF64, mDivF64, mMaxF64, mMinF64
@@ -242,7 +252,7 @@ proc evalIs(n, a: PNode): PNode =
internalAssert a.kind == nkSym and a.sym.kind == skType
internalAssert n.sonsLen == 3 and
n[2].kind in {nkStrLit..nkTripleStrLit, nkType}
let t1 = a.sym.typ
if n[2].kind in {nkStrLit..nkTripleStrLit}:
@@ -250,12 +260,12 @@ proc evalIs(n, a: PNode): PNode =
of "closure":
let t = skipTypes(t1, abstractRange)
result = newIntNode(nkIntLit, ord(t.kind == tyProc and
t.callConv == ccClosure and
t.callConv == ccClosure and
tfIterator notin t.flags))
of "iterator":
let t = skipTypes(t1, abstractRange)
result = newIntNode(nkIntLit, ord(t.kind == tyProc and
t.callConv == ccClosure and
t.callConv == ccClosure and
tfIterator in t.flags))
else: discard
else:
@@ -265,7 +275,7 @@ proc evalIs(n, a: PNode): PNode =
result = newIntNode(nkIntLit, ord(match))
result.typ = n.typ
proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
# b and c may be nil
result = nil
case m
@@ -276,18 +286,19 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
of mNot: result = newIntNodeT(1 - getInt(a), n)
of mCard: result = newIntNodeT(nimsets.cardSet(a), n)
of mBitnotI, mBitnotI64: result = newIntNodeT(not getInt(a), n)
of mLengthStr: result = newIntNodeT(len(getStr(a)), n)
of mLengthStr, mXLenStr: result = newIntNodeT(len(getStr(a)), n)
of mLengthArray: result = newIntNodeT(lengthOrd(a.typ), n)
of mLengthSeq, mLengthOpenArray: result = newIntNodeT(sonsLen(a), n) # BUGFIX
of mUnaryPlusI, mUnaryPlusI64, mUnaryPlusF64: result = a # throw `+` away
of mToFloat, mToBiggestFloat:
of mLengthSeq, mLengthOpenArray, mXLenSeq:
result = newIntNodeT(sonsLen(a), n) # BUGFIX
of mUnaryPlusI, mUnaryPlusF64: result = a # throw `+` away
of mToFloat, mToBiggestFloat:
result = newFloatNodeT(toFloat(int(getInt(a))), n)
of mToInt, mToBiggestInt: result = newIntNodeT(system.toInt(getFloat(a)), n)
of mAbsF64: result = newFloatNodeT(abs(getFloat(a)), n)
of mAbsI, mAbsI64:
of mAbsI, mAbsI64:
if getInt(a) >= 0: result = a
else: result = newIntNodeT(- getInt(a), n)
of mZe8ToI, mZe8ToI64, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64:
of mZe8ToI, mZe8ToI64, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64:
# byte(-128) = 1...1..1000_0000'64 --> 0...0..1000_0000'64
result = newIntNodeT(getInt(a) and (`shl`(1, getSize(a.typ) * 8) - 1), n)
of mToU8: result = newIntNodeT(getInt(a) and 0x000000FF, n)
@@ -299,21 +310,21 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
of mAddI, mAddI64: result = newIntNodeT(getInt(a) + getInt(b), n)
of mSubI, mSubI64: result = newIntNodeT(getInt(a) - getInt(b), n)
of mMulI, mMulI64: result = newIntNodeT(getInt(a) * getInt(b), n)
of mMinI, mMinI64:
of mMinI:
if getInt(a) > getInt(b): result = newIntNodeT(getInt(b), n)
else: result = newIntNodeT(getInt(a), n)
of mMaxI, mMaxI64:
of mMaxI:
if getInt(a) > getInt(b): result = newIntNodeT(getInt(a), n)
else: result = newIntNodeT(getInt(b), n)
of mShlI, mShlI64:
of mShlI, mShlI64:
case skipTypes(n.typ, abstractRange).kind
of tyInt8: result = newIntNodeT(int8(getInt(a)) shl int8(getInt(b)), n)
of tyInt16: result = newIntNodeT(int16(getInt(a)) shl int16(getInt(b)), n)
of tyInt32: result = newIntNodeT(int32(getInt(a)) shl int32(getInt(b)), n)
of tyInt64, tyInt, tyUInt..tyUInt64:
of tyInt64, tyInt, tyUInt..tyUInt64:
result = newIntNodeT(`shl`(getInt(a), getInt(b)), n)
else: internalError(n.info, "constant folding for shl")
of mShrI, mShrI64:
of mShrI, mShrI64:
case skipTypes(n.typ, abstractRange).kind
of tyInt8: result = newIntNodeT(int8(getInt(a)) shr int8(getInt(b)), n)
of tyInt16: result = newIntNodeT(int16(getInt(a)) shr int16(getInt(b)), n)
@@ -332,34 +343,34 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
of mAddF64: result = newFloatNodeT(getFloat(a) + getFloat(b), n)
of mSubF64: result = newFloatNodeT(getFloat(a) - getFloat(b), n)
of mMulF64: result = newFloatNodeT(getFloat(a) * getFloat(b), n)
of mDivF64:
if getFloat(b) == 0.0:
of mDivF64:
if getFloat(b) == 0.0:
if getFloat(a) == 0.0: result = newFloatNodeT(NaN, n)
else: result = newFloatNodeT(Inf, n)
else:
else:
result = newFloatNodeT(getFloat(a) / getFloat(b), n)
of mMaxF64:
of mMaxF64:
if getFloat(a) > getFloat(b): result = newFloatNodeT(getFloat(a), n)
else: result = newFloatNodeT(getFloat(b), n)
of mMinF64:
of mMinF64:
if getFloat(a) > getFloat(b): result = newFloatNodeT(getFloat(b), n)
else: result = newFloatNodeT(getFloat(a), n)
of mIsNil: result = newIntNodeT(ord(a.kind == nkNilLit), n)
of mLtI, mLtI64, mLtB, mLtEnum, mLtCh:
of mLtI, mLtI64, mLtB, mLtEnum, mLtCh:
result = newIntNodeT(ord(getOrdValue(a) < getOrdValue(b)), n)
of mLeI, mLeI64, mLeB, mLeEnum, mLeCh:
of mLeI, mLeI64, mLeB, mLeEnum, mLeCh:
result = newIntNodeT(ord(getOrdValue(a) <= getOrdValue(b)), n)
of mEqI, mEqI64, mEqB, mEqEnum, mEqCh:
result = newIntNodeT(ord(getOrdValue(a) == getOrdValue(b)), n)
of mEqI, mEqI64, mEqB, mEqEnum, mEqCh:
result = newIntNodeT(ord(getOrdValue(a) == getOrdValue(b)), n)
of mLtF64: result = newIntNodeT(ord(getFloat(a) < getFloat(b)), n)
of mLeF64: result = newIntNodeT(ord(getFloat(a) <= getFloat(b)), n)
of mEqF64: result = newIntNodeT(ord(getFloat(a) == getFloat(b)), n)
of mEqF64: result = newIntNodeT(ord(getFloat(a) == getFloat(b)), n)
of mLtStr: result = newIntNodeT(ord(getStr(a) < getStr(b)), n)
of mLeStr: result = newIntNodeT(ord(getStr(a) <= getStr(b)), n)
of mEqStr: result = newIntNodeT(ord(getStr(a) == getStr(b)), n)
of mLtU, mLtU64:
of mLtU, mLtU64:
result = newIntNodeT(ord(`<%`(getOrdValue(a), getOrdValue(b))), n)
of mLeU, mLeU64:
of mLeU, mLeU64:
result = newIntNodeT(ord(`<=%`(getOrdValue(a), getOrdValue(b))), n)
of mBitandI, mBitandI64, mAnd: result = newIntNodeT(a.getInt and b.getInt, n)
of mBitorI, mBitorI64, mOr: result = newIntNodeT(getInt(a) or getInt(b), n)
@@ -377,18 +388,18 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
result = newIntNodeT(`/%`(getInt(a), y), n)
of mLeSet: result = newIntNodeT(ord(containsSets(a, b)), n)
of mEqSet: result = newIntNodeT(ord(equalSets(a, b)), n)
of mLtSet:
of mLtSet:
result = newIntNodeT(ord(containsSets(a, b) and not equalSets(a, b)), n)
of mMulSet:
of mMulSet:
result = nimsets.intersectSets(a, b)
result.info = n.info
of mPlusSet:
of mPlusSet:
result = nimsets.unionSets(a, b)
result.info = n.info
of mMinusSet:
of mMinusSet:
result = nimsets.diffSets(a, b)
result.info = n.info
of mSymDiffSet:
of mSymDiffSet:
result = nimsets.symdiffSets(a, b)
result.info = n.info
of mConStrStr: result = newStrNodeT(getStrOrChar(a) & getStrOrChar(b), n)
@@ -397,104 +408,105 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
# BUGFIX: we cannot eval mRepr here for reasons that I forgot.
discard
of mIntToStr, mInt64ToStr: result = newStrNodeT($(getOrdValue(a)), n)
of mBoolToStr:
of mBoolToStr:
if getOrdValue(a) == 0: result = newStrNodeT("false", n)
else: result = newStrNodeT("true", n)
of mCopyStr: result = newStrNodeT(substr(getStr(a), int(getOrdValue(b))), n)
of mCopyStrLast:
result = newStrNodeT(substr(getStr(a), int(getOrdValue(b)),
of mCopyStrLast:
result = newStrNodeT(substr(getStr(a), int(getOrdValue(b)),
int(getOrdValue(c))), n)
of mFloatToStr: result = newStrNodeT($getFloat(a), n)
of mCStrToStr, mCharToStr: result = newStrNodeT(getStrOrChar(a), n)
of mStrToStr: result = a
of mEnumToStr: result = newStrNodeT(ordinalValToString(a), n)
of mArrToSeq:
of mArrToSeq:
result = copyTree(a)
result.typ = n.typ
of mCompileOption:
result = newIntNodeT(ord(commands.testCompileOption(a.getStr, n.info)), n)
result = newIntNodeT(ord(commands.testCompileOption(a.getStr, n.info)), n)
of mCompileOptionArg:
result = newIntNodeT(ord(
testCompileOptionArg(getStr(a), getStr(b), n.info)), n)
of mNewString, mNewStringOfCap,
mExit, mInc, ast.mDec, mEcho, mSwap, mAppendStrCh,
mAppendStrStr, mAppendSeqElem, mSetLengthStr, mSetLengthSeq,
mParseExprToAst, mParseStmtToAst, mExpandToAst, mTypeTrait,
mNLen..mNError, mEqRef, mSlurp, mStaticExec, mNGenSym, mSpawn, mParallel:
of mNewString, mNewStringOfCap,
mExit, mInc, ast.mDec, mEcho, mSwap, mAppendStrCh,
mAppendStrStr, mAppendSeqElem, mSetLengthStr, mSetLengthSeq,
mParseExprToAst, mParseStmtToAst, mExpandToAst, mTypeTrait, mDotDot,
mNLen..mNError, mEqRef, mSlurp, mStaticExec, mNGenSym, mSpawn,
mParallel, mPlugin:
discard
else: internalError(a.info, "evalOp(" & $m & ')')
proc getConstIfExpr(c: PSym, n: PNode): PNode =
proc getConstIfExpr(c: PSym, n: PNode): PNode =
result = nil
for i in countup(0, sonsLen(n) - 1):
for i in countup(0, sonsLen(n) - 1):
var it = n.sons[i]
if it.len == 2:
var e = getConstExpr(c, it.sons[0])
if e == nil: return nil
if getOrdValue(e) != 0:
if result == nil:
if getOrdValue(e) != 0:
if result == nil:
result = getConstExpr(c, it.sons[1])
if result == nil: return
if result == nil: return
elif it.len == 1:
if result == nil: result = getConstExpr(c, it.sons[0])
else: internalError(it.info, "getConstIfExpr()")
proc partialAndExpr(c: PSym, n: PNode): PNode =
proc partialAndExpr(c: PSym, n: PNode): PNode =
# partial evaluation
result = n
var a = getConstExpr(c, n.sons[1])
var b = getConstExpr(c, n.sons[2])
if a != nil:
if a != nil:
if getInt(a) == 0: result = a
elif b != nil: result = b
else: result = n.sons[2]
elif b != nil:
elif b != nil:
if getInt(b) == 0: result = b
else: result = n.sons[1]
proc partialOrExpr(c: PSym, n: PNode): PNode =
proc partialOrExpr(c: PSym, n: PNode): PNode =
# partial evaluation
result = n
var a = getConstExpr(c, n.sons[1])
var b = getConstExpr(c, n.sons[2])
if a != nil:
if a != nil:
if getInt(a) != 0: result = a
elif b != nil: result = b
else: result = n.sons[2]
elif b != nil:
elif b != nil:
if getInt(b) != 0: result = b
else: result = n.sons[1]
proc leValueConv(a, b: PNode): bool =
proc leValueConv(a, b: PNode): bool =
result = false
case a.kind
of nkCharLit..nkUInt64Lit:
of nkCharLit..nkUInt64Lit:
case b.kind
of nkCharLit..nkUInt64Lit: result = a.intVal <= b.intVal
of nkFloatLit..nkFloat128Lit: result = a.intVal <= round(b.floatVal)
else: internalError(a.info, "leValueConv")
of nkFloatLit..nkFloat128Lit:
of nkFloatLit..nkFloat128Lit:
case b.kind
of nkFloatLit..nkFloat128Lit: result = a.floatVal <= b.floatVal
of nkCharLit..nkUInt64Lit: result = a.floatVal <= toFloat(int(b.intVal))
else: internalError(a.info, "leValueConv")
else: internalError(a.info, "leValueConv")
proc magicCall(m: PSym, n: PNode): PNode =
if sonsLen(n) <= 1: return
var s = n.sons[0].sym
var a = getConstExpr(m, n.sons[1])
var b, c: PNode
if a == nil: return
if sonsLen(n) > 2:
if a == nil: return
if sonsLen(n) > 2:
b = getConstExpr(m, n.sons[2])
if b == nil: return
if sonsLen(n) > 3:
if b == nil: return
if sonsLen(n) > 3:
c = getConstExpr(m, n.sons[3])
if c == nil: return
if c == nil: return
result = evalOp(s.magic, n, a, b, c)
proc getAppType(n: PNode): PNode =
if gGlobalOptions.contains(optGenDynLib):
result = newStrNodeT("lib", n)
@@ -510,48 +522,48 @@ proc rangeCheck(n: PNode, value: BiggestInt) =
localError(n.info, errGenerated, "cannot convert " & $value &
" to " & typeToString(n.typ))
proc foldConv*(n, a: PNode; check = false): PNode =
proc foldConv*(n, a: PNode; check = false): PNode =
# XXX range checks?
case skipTypes(n.typ, abstractRange).kind
of tyInt..tyInt64:
of tyInt..tyInt64, tyUInt..tyUInt64:
case skipTypes(a.typ, abstractRange).kind
of tyFloat..tyFloat64:
result = newIntNodeT(int(getFloat(a)), n)
of tyChar: result = newIntNodeT(getOrdValue(a), n)
else:
else:
result = a
result.typ = n.typ
if check: rangeCheck(n, result.intVal)
of tyFloat..tyFloat64:
case skipTypes(a.typ, abstractRange).kind
of tyInt..tyInt64, tyEnum, tyBool, tyChar:
of tyInt..tyInt64, tyEnum, tyBool, tyChar:
result = newFloatNodeT(toFloat(int(getOrdValue(a))), n)
else:
result = a
result.typ = n.typ
of tyOpenArray, tyVarargs, tyProc:
of tyOpenArray, tyVarargs, tyProc:
discard
else:
else:
result = a
result.typ = n.typ
result.typ = takeType(n.typ, a.typ)
proc getArrayConstr(m: PSym, n: PNode): PNode =
if n.kind == nkBracket:
result = n
else:
result = getConstExpr(m, n)
if result == nil: result = n
proc foldArrayAccess(m: PSym, n: PNode): PNode =
proc foldArrayAccess(m: PSym, n: PNode): PNode =
var x = getConstExpr(m, n.sons[0])
if x == nil or x.typ.skipTypes({tyGenericInst}).kind == tyTypeDesc: return
var y = getConstExpr(m, n.sons[1])
if y == nil: return
var idx = getOrdValue(y)
case x.kind
of nkPar:
of nkPar:
if idx >= 0 and idx < sonsLen(x):
result = x.sons[int(idx)]
if result.kind == nkExprColonExpr: result = result.sons[1]
@@ -563,14 +575,14 @@ proc foldArrayAccess(m: PSym, n: PNode): PNode =
else: localError(n.info, errIndexOutOfBounds)
of nkStrLit..nkTripleStrLit:
result = newNodeIT(nkCharLit, x.info, n.typ)
if idx >= 0 and idx < len(x.strVal):
if idx >= 0 and idx < len(x.strVal):
result.intVal = ord(x.strVal[int(idx)])
elif idx == len(x.strVal):
elif idx == len(x.strVal):
discard
else:
else:
localError(n.info, errIndexOutOfBounds)
else: discard
proc foldFieldAccess(m: PSym, n: PNode): PNode =
# a real field access; proc calls have already been transformed
var x = getConstExpr(m, n.sons[0])
@@ -584,15 +596,15 @@ proc foldFieldAccess(m: PSym, n: PNode): PNode =
result = x.sons[field.position]
if result.kind == nkExprColonExpr: result = result.sons[1]
return
if it.sons[0].sym.name.id == field.name.id:
if it.sons[0].sym.name.id == field.name.id:
result = x.sons[i].sons[1]
return
localError(n.info, errFieldXNotFound, field.name.s)
proc foldConStrStr(m: PSym, n: PNode): PNode =
proc foldConStrStr(m: PSym, n: PNode): PNode =
result = newNodeIT(nkStrLit, n.info, n.typ)
result.strVal = ""
for i in countup(1, sonsLen(n) - 1):
for i in countup(1, sonsLen(n) - 1):
let a = getConstExpr(m, n.sons[i])
if a == nil: return nil
result.strVal.add(getStrOrChar(a))
@@ -602,10 +614,10 @@ proc newSymNodeTypeDesc*(s: PSym; info: TLineInfo): PNode =
result.typ = newType(tyTypeDesc, s.owner)
result.typ.addSonSkipIntLit(s.typ)
proc getConstExpr(m: PSym, n: PNode): PNode =
proc getConstExpr(m: PSym, n: PNode): PNode =
result = nil
case n.kind
of nkSym:
of nkSym:
var s = n.sym
case s.kind
of skEnumField:
@@ -636,14 +648,14 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
else:
result = newSymNodeTypeDesc(s, n.info)
else: discard
of nkCharLit..nkNilLit:
of nkCharLit..nkNilLit:
result = copyNode(n)
of nkIfExpr:
of nkIfExpr:
result = getConstIfExpr(m, n)
of nkCall, nkCommand, nkCallStrLit, nkPrefix, nkInfix:
if n.sons[0].kind != nkSym: return
of nkCall, nkCommand, nkCallStrLit, nkPrefix, nkInfix:
if n.sons[0].kind != nkSym: return
var s = n.sons[0].sym
if s.kind != skProc: return
if s.kind != skProc: return
try:
case s.magic
of mNone:
@@ -651,8 +663,8 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
return
of mSizeOf:
var a = n.sons[1]
if computeSize(a.typ) < 0:
localError(a.info, errCannotEvalXBecauseIncompletelyDefined,
if computeSize(a.typ) < 0:
localError(a.info, errCannotEvalXBecauseIncompletelyDefined,
"sizeof")
result = nil
elif skipTypes(a.typ, typedescInst).kind in
@@ -662,21 +674,21 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
else:
result = nil
# XXX: size computation for complex types is still wrong
of mLow:
of mLow:
result = newIntNodeT(firstOrd(n.sons[1].typ), n)
of mHigh:
if skipTypes(n.sons[1].typ, abstractVar).kind notin
{tyOpenArray, tyVarargs, tySequence, tyString}:
of mHigh:
if skipTypes(n.sons[1].typ, abstractVar).kind notin
{tySequence, tyString, tyCString, tyOpenArray, tyVarargs}:
result = newIntNodeT(lastOrd(skipTypes(n[1].typ, abstractVar)), n)
else:
var a = getArrayConstr(m, n.sons[1])
if a.kind == nkBracket:
# we can optimize it away:
# we can optimize it away:
result = newIntNodeT(sonsLen(a)-1, n)
of mLengthOpenArray:
var a = getArrayConstr(m, n.sons[1])
if a.kind == nkBracket:
# we can optimize it away! This fixes the bug ``len(134)``.
# we can optimize it away! This fixes the bug ``len(134)``.
result = newIntNodeT(sonsLen(a), n)
else:
result = magicCall(m, n)
@@ -694,33 +706,33 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
result = evalIs(n, a)
else:
result = magicCall(m, n)
except OverflowError:
except OverflowError:
localError(n.info, errOverOrUnderflow)
except DivByZeroError:
except DivByZeroError:
localError(n.info, errConstantDivisionByZero)
of nkAddr:
of nkAddr:
var a = getConstExpr(m, n.sons[0])
if a != nil:
if a != nil:
result = n
n.sons[0] = a
of nkBracket:
of nkBracket:
result = copyTree(n)
for i in countup(0, sonsLen(n) - 1):
for i in countup(0, sonsLen(n) - 1):
var a = getConstExpr(m, n.sons[i])
if a == nil: return nil
result.sons[i] = a
incl(result.flags, nfAllConst)
of nkRange:
of nkRange:
var a = getConstExpr(m, n.sons[0])
if a == nil: return
if a == nil: return
var b = getConstExpr(m, n.sons[1])
if b == nil: return
if b == nil: return
result = copyNode(n)
addSon(result, a)
addSon(result, b)
of nkCurly:
of nkCurly:
result = copyTree(n)
for i in countup(0, sonsLen(n) - 1):
for i in countup(0, sonsLen(n) - 1):
var a = getConstExpr(m, n.sons[i])
if a == nil: return nil
result.sons[i] = a
@@ -735,33 +747,33 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
of nkPar:
# tuple constructor
result = copyTree(n)
if (sonsLen(n) > 0) and (n.sons[0].kind == nkExprColonExpr):
for i in countup(0, sonsLen(n) - 1):
if (sonsLen(n) > 0) and (n.sons[0].kind == nkExprColonExpr):
for i in countup(0, sonsLen(n) - 1):
var a = getConstExpr(m, n.sons[i].sons[1])
if a == nil: return nil
result.sons[i].sons[1] = a
else:
for i in countup(0, sonsLen(n) - 1):
else:
for i in countup(0, sonsLen(n) - 1):
var a = getConstExpr(m, n.sons[i])
if a == nil: return nil
result.sons[i] = a
incl(result.flags, nfAllConst)
of nkChckRangeF, nkChckRange64, nkChckRange:
of nkChckRangeF, nkChckRange64, nkChckRange:
var a = getConstExpr(m, n.sons[0])
if a == nil: return
if leValueConv(n.sons[1], a) and leValueConv(a, n.sons[2]):
if a == nil: return
if leValueConv(n.sons[1], a) and leValueConv(a, n.sons[2]):
result = a # a <= x and x <= b
result.typ = n.typ
else:
else:
localError(n.info, errGenerated, `%`(
msgKindToString(errIllegalConvFromXtoY),
msgKindToString(errIllegalConvFromXtoY),
[typeToString(n.sons[0].typ), typeToString(n.typ)]))
of nkStringToCString, nkCStringToString:
of nkStringToCString, nkCStringToString:
var a = getConstExpr(m, n.sons[0])
if a == nil: return
if a == nil: return
result = a
result.typ = n.typ
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
var a = getConstExpr(m, n.sons[1])
if a == nil: return
result = foldConv(n, a, check=n.kind == nkHiddenStdConv)

View File

@@ -1,7 +1,7 @@
#
#
# The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -25,10 +25,23 @@ proc getIdentNode(n: PNode): PNode =
else:
illFormedAst(n)
result = n
type
GenericCtx = object
toMixin: IntSet
cursorInBody: bool # only for nimsuggest
type
TSemGenericFlag = enum
withinBind, withinTypeDesc, withinMixin
TSemGenericFlags = set[TSemGenericFlag]
proc semGenericStmt(c: PContext, n: PNode,
flags: TSemGenericFlags, ctx: var GenericCtx): PNode
proc semGenericStmtScope(c: PContext, n: PNode,
flags: TSemGenericFlags,
ctx: var IntSet): PNode =
ctx: var GenericCtx): PNode =
openScope(c)
result = semGenericStmt(c, n, flags, ctx)
closeScope(c)
@@ -37,7 +50,8 @@ template macroToExpand(s: expr): expr =
s.kind in {skMacro, skTemplate} and (s.typ.len == 1 or sfImmediate in s.flags)
proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
ctx: var IntSet): PNode =
ctx: var GenericCtx): PNode =
semIdeForTemplateOrGenericCheck(n, ctx.cursorInBody)
incl(s.flags, sfUsed)
case s.kind
of skUnknown:
@@ -60,13 +74,20 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
else:
result = symChoice(c, n, s, scOpen)
of skGenericParam:
result = newSymNodeTypeDesc(s, n.info)
if s.typ != nil and s.typ.kind == tyStatic:
if s.typ.n != nil:
result = s.typ.n
else:
result = n
else:
result = newSymNodeTypeDesc(s, n.info)
styleCheckUse(n.info, s)
of skParam:
result = n
styleCheckUse(n.info, s)
of skType:
if (s.typ != nil) and (s.typ.kind != tyGenericParam):
if (s.typ != nil) and
(s.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} == {}):
result = newSymNodeTypeDesc(s, n.info)
else:
result = n
@@ -76,17 +97,17 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
styleCheckUse(n.info, s)
proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags,
ctx: var IntSet): PNode =
ctx: var GenericCtx): PNode =
result = n
let ident = considerQuotedIdent(n)
var s = searchInScopes(c, ident).skipAlias(n)
if s == nil:
if ident.id notin ctx and withinMixin notin flags:
if ident.id notin ctx.toMixin and withinMixin notin flags:
localError(n.info, errUndeclaredIdentifier, ident.s)
else:
if withinBind in flags:
result = symChoice(c, n, s, scClosed)
elif s.name.id in ctx:
elif s.name.id in ctx.toMixin:
result = symChoice(c, n, s, scForceOpen)
else:
result = semGenericStmtSymbol(c, n, s, ctx)
@@ -98,8 +119,10 @@ proc newDot(n, b: PNode): PNode =
result.add(b)
proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
ctx: var IntSet; isMacro: var bool): PNode =
ctx: var GenericCtx; isMacro: var bool): PNode =
assert n.kind == nkDotExpr
semIdeForTemplateOrGenericCheck(n, ctx.cursorInBody)
let luf = if withinMixin notin flags: {checkUndeclared} else: {}
var s = qualifiedLookUp(c, n, luf)
@@ -115,7 +138,7 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
isMacro = s.kind in {skTemplate, skMacro}
if withinBind in flags:
result = newDot(result, symChoice(c, n, s, scClosed))
elif s.name.id in ctx:
elif s.name.id in ctx.toMixin:
result = newDot(result, symChoice(c, n, s, scForceOpen))
else:
let sym = semGenericStmtSymbol(c, n, s, ctx)
@@ -130,9 +153,11 @@ proc addTempDecl(c: PContext; n: PNode; kind: TSymKind) =
styleCheckDef(n.info, s, kind)
proc semGenericStmt(c: PContext, n: PNode,
flags: TSemGenericFlags, ctx: var IntSet): PNode =
flags: TSemGenericFlags, ctx: var GenericCtx): PNode =
result = n
if gCmd == cmdIdeTools: suggestStmt(c, n)
#if gCmd == cmdIdeTools: suggestStmt(c, n)
semIdeForTemplateOrGenericCheck(n, ctx.cursorInBody)
case n.kind
of nkIdent, nkAccQuoted:
result = lookup(c, n, flags, ctx)
@@ -155,14 +180,15 @@ proc semGenericStmt(c: PContext, n: PNode,
of nkBind:
result = semGenericStmt(c, n.sons[0], flags+{withinBind}, ctx)
of nkMixinStmt:
result = semMixinStmt(c, n, ctx)
result = semMixinStmt(c, n, ctx.toMixin)
of nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkCommand, nkCallStrLit:
# check if it is an expression macro:
checkMinSonsLen(n, 1)
let fn = n.sons[0]
var s = qualifiedLookUp(c, fn, {})
if s == nil and withinMixin notin flags and
fn.kind in {nkIdent, nkAccQuoted} and considerQuotedIdent(fn).id notin ctx:
fn.kind in {nkIdent, nkAccQuoted} and
considerQuotedIdent(fn).id notin ctx.toMixin:
localError(n.info, errUndeclaredIdentifier, fn.renderTree)
var first = 0
@@ -170,7 +196,7 @@ proc semGenericStmt(c: PContext, n: PNode,
if s != nil:
incl(s.flags, sfUsed)
mixinContext = s.magic in {mDefined, mDefinedInScope, mCompiles}
let scOption = if s.name.id in ctx: scForceOpen else: scOpen
let scOption = if s.name.id in ctx.toMixin: scForceOpen else: scOpen
case s.kind
of skMacro:
if macroToExpand(s):
@@ -217,7 +243,7 @@ proc semGenericStmt(c: PContext, n: PNode,
elif fn.kind == nkDotExpr:
result.sons[0] = fuzzyLookup(c, fn, flags, ctx, mixinContext)
first = 1
# Consider 'when defined(globalsSlot): ThreadVarSetValue(globalsSlot, ...)'
# Consider 'when declared(globalsSlot): ThreadVarSetValue(globalsSlot, ...)'
# in threads.nim: the subtle preprocessing here binds 'globalsSlot' which
# is not exported and yet the generic 'threadProcWrapper' works correctly.
let flags = if mixinContext: flags+{withinMixin} else: flags
@@ -330,7 +356,7 @@ proc semGenericStmt(c: PContext, n: PNode,
of nkIdent: a = n.sons[i]
else: illFormedAst(n)
addDecl(c, newSymS(skUnknown, getIdentNode(a.sons[i]), c))
of nkObjectTy, nkTupleTy:
of nkObjectTy, nkTupleTy, nkTupleClassTy:
discard
of nkFormalParams:
checkMinSonsLen(n, 1)
@@ -370,4 +396,9 @@ proc semGenericStmt(c: PContext, n: PNode,
else:
for i in countup(0, sonsLen(n) - 1):
result.sons[i] = semGenericStmt(c, n.sons[i], flags, ctx)
proc semGenericStmt(c: PContext, n: PNode): PNode =
var ctx: GenericCtx
ctx.toMixin = initIntset()
result = semGenericStmt(c, n, {}, ctx)
semIdeForTemplateOrGeneric(c, result, ctx.cursorInBody)

View File

@@ -11,35 +11,37 @@
# included from sem.nim
proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable,
entry: var TInstantiation) =
if n.kind != nkGenericParams:
entry: var TInstantiation) =
if n.kind != nkGenericParams:
internalError(n.info, "instantiateGenericParamList; no generic params")
newSeq(entry.concreteTypes, n.len)
for i, a in n.pairs:
if a.kind != nkSym:
if a.kind != nkSym:
internalError(a.info, "instantiateGenericParamList; no symbol")
var q = a.sym
if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyStatic, tyIter}+tyTypeClasses:
continue
var s = newSym(skType, q.name, getCurrOwner(), q.info)
let symKind = if q.typ.kind == tyStatic: skConst else: skType
var s = newSym(symKind, q.name, getCurrOwner(), q.info)
s.flags = s.flags + {sfUsed, sfFromGeneric}
var t = PType(idTableGet(pt, q.typ))
if t == nil:
if tfRetType in q.typ.flags:
# keep the generic type and allow the return type to be bound
# keep the generic type and allow the return type to be bound
# later by semAsgn in return type inference scenario
t = q.typ
else:
localError(a.info, errCannotInstantiateX, s.name.s)
t = errorType(c)
elif t.kind == tyGenericParam:
elif t.kind == tyGenericParam:
localError(a.info, errCannotInstantiateX, q.name.s)
t = errorType(c)
elif t.kind == tyGenericInvokation:
elif t.kind == tyGenericInvocation:
#t = instGenericContainer(c, a, t)
t = generateTypeInstance(c, pt, a, t)
#t = ReplaceTypeVarsT(cl, t)
s.typ = t
if t.kind == tyStatic: s.ast = t.n
addDecl(c, s)
entry.concreteTypes[i] = t
@@ -56,17 +58,17 @@ proc genericCacheGet(genericSym: PSym, entry: TInstantiation): PSym =
if sameInstantiation(entry, inst[]):
return inst.sym
proc removeDefaultParamValues(n: PNode) =
proc removeDefaultParamValues(n: PNode) =
# we remove default params, because they cannot be instantiated properly
# and they are not needed anyway for instantiation (each param is already
# provided).
when false:
for i in countup(1, sonsLen(n)-1):
for i in countup(1, sonsLen(n)-1):
var a = n.sons[i]
if a.kind != nkIdentDefs: IllFormedAst(a)
var L = a.len
if a.sons[L-1].kind != nkEmpty and a.sons[L-2].kind != nkEmpty:
# ``param: typ = defaultVal``.
# ``param: typ = defaultVal``.
# We don't need defaultVal for semantic checking and it's wrong for
# ``cmp: proc (a, b: T): int = cmp``. Hm, for ``cmp = cmp`` that is
# not possible... XXX We don't solve this issue here.
@@ -75,11 +77,12 @@ proc removeDefaultParamValues(n: PNode) =
proc freshGenSyms(n: PNode, owner: PSym, symMap: var TIdTable) =
# we need to create a fresh set of gensym'ed symbols:
if n.kind == nkSym and sfGenSym in n.sym.flags:
var x = PSym(idTableGet(symMap, n.sym))
let s = n.sym
var x = PSym(idTableGet(symMap, s))
if x == nil:
x = copySym(n.sym, false)
x = copySym(s, false)
x.owner = owner
idTablePut(symMap, n.sym, x)
idTablePut(symMap, s, x)
n.sym = x
else:
for i in 0 .. <safeLen(n): freshGenSyms(n.sons[i], owner, symMap)
@@ -94,16 +97,21 @@ proc addProcDecls(c: PContext, fn: PSym) =
var param = fn.typ.n.sons[i].sym
param.owner = fn
addParamOrResult(c, param, fn.kind)
maybeAddResult(c, fn, fn.ast)
proc instantiateBody(c: PContext, n: PNode, result: PSym) =
proc instantiateBody(c: PContext, n, params: PNode, result: PSym) =
if n.sons[bodyPos].kind != nkEmpty:
inc c.inGenericInst
# add it here, so that recursive generic procs are possible:
var b = n.sons[bodyPos]
var symMap: TIdTable
initIdTable symMap
if params != nil:
for i in 1 .. <params.len:
let param = params[i].sym
if sfGenSym in param.flags:
idTablePut(symMap, params[i].sym, result.typ.n[param.position+1].sym)
freshGenSyms(b, result, symMap)
b = semProcBody(c, b)
b = hloBody(c, b)
@@ -120,13 +128,13 @@ proc fixupInstantiatedSymbols(c: PContext, s: PSym) =
openScope(c)
var n = oldPrc.ast
n.sons[bodyPos] = copyTree(s.getBody)
instantiateBody(c, n, oldPrc)
instantiateBody(c, n, nil, oldPrc)
closeScope(c)
popInfoContext()
proc sideEffectsCheck(c: PContext, s: PSym) =
proc sideEffectsCheck(c: PContext, s: PSym) =
if {sfNoSideEffect, sfSideEffect} * s.flags ==
{sfNoSideEffect, sfSideEffect}:
{sfNoSideEffect, sfSideEffect}:
localError(s.info, errXhasSideEffects, s.name.s)
proc instGenericContainer(c: PContext, info: TLineInfo, header: PType,
@@ -154,36 +162,51 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
# Alas, doing this here is probably not enough, because another
# proc signature could appear in the params:
# proc foo[T](a: proc (x: T, b: type(x.y))
#
#
# The solution would be to move this logic into semtypinst, but
# at this point semtypinst have to become part of sem, because it
# will need to use openScope, addDecl, etc.
addDecl(c, prc)
pushInfoContext(info)
var cl = initTypeVars(c, pt, info)
var result = instCopyType(cl, prc.typ)
let originalParams = result.n
result.n = originalParams.shallowCopy
for i in 1 .. <result.len:
# twrong_field_caching requires these 'resetIdTable' calls:
if i > 1:
resetIdTable(cl.symMap)
resetIdTable(cl.localCache)
result.sons[i] = replaceTypeVarsT(cl, result.sons[i])
propagateToOwner(result, result.sons[i])
let param = replaceTypeVarsN(cl, originalParams[i])
result.n.sons[i] = param
if param.kind == nkSym:
# XXX: this won't be true for void params
# implement pass-through of void params and
# the "sort by distance to point" container
internalAssert originalParams[i].kind == nkSym
when true:
let oldParam = originalParams[i].sym
let param = copySym(oldParam)
param.owner = prc
param.typ = result.sons[i]
if oldParam.ast != nil:
param.ast = fitNode(c, param.typ, oldParam.ast)
# don't be lazy here and call replaceTypeVarsN(cl, originalParams[i])!
result.n.sons[i] = newSymNode(param)
addDecl(c, param)
else:
let param = replaceTypeVarsN(cl, originalParams[i])
result.n.sons[i] = param
param.sym.owner = prc
addDecl(c, param.sym)
addDecl(c, result.n.sons[i].sym)
resetIdTable(cl.symMap)
resetIdTable(cl.localCache)
result.sons[0] = replaceTypeVarsT(cl, result.sons[0])
result.n.sons[0] = originalParams[0].copyTree
eraseVoidParams(result)
skipIntLiteralParams(result)
prc.typ = result
maybeAddResult(c, prc, prc.ast)
popInfoContext()
@@ -232,7 +255,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
pragma(c, result, n.sons[pragmasPos], allRoutinePragmas)
if isNil(n.sons[bodyPos]):
n.sons[bodyPos] = copyTree(fn.getBody)
instantiateBody(c, n, result)
instantiateBody(c, n, fn.typ.n, result)
sideEffectsCheck(c, result)
paramsTypeCheck(c, result.typ)
else:

View File

@@ -1,7 +1,7 @@
#
#
# The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -16,9 +16,9 @@ proc ithField(n: PNode, field: int): PSym =
result = nil
case n.kind
of nkRecList:
for i in countup(0, sonsLen(n) - 1):
for i in countup(0, sonsLen(n) - 1):
result = ithField(n.sons[i], field-i)
if result != nil: return
if result != nil: return
of nkRecCase:
if n.sons[0].kind != nkSym: internalError(n.info, "ithField")
result = ithField(n.sons[0], field-1)
@@ -34,7 +34,7 @@ proc ithField(n: PNode, field: int): PSym =
else: discard
proc annotateType*(n: PNode, t: PType) =
let x = t.skipTypes(abstractInst)
let x = t.skipTypes(abstractInst+{tyRange})
# Note: x can be unequal to t and we need to be careful to use 't'
# to not to skip tyGenericInst
case n.kind
@@ -80,7 +80,7 @@ proc annotateType*(n: PNode, t: PType) =
if x.kind in {tyString, tyCString}:
n.typ = t
else:
globalError(n.info, "string literal must be of some string type")
globalError(n.info, "string literal must be of some string type")
of nkNilLit:
if x.kind in NilableTypes:
n.typ = t

View File

@@ -1,7 +1,7 @@
#
#
# The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -10,10 +10,24 @@
# This include file implements the semantic checking for magics.
# included from sem.nim
proc semAddr(c: PContext; n: PNode): PNode =
result = newNodeI(nkAddr, n.info)
let x = semExprWithType(c, n)
if isAssignable(c, x) notin {arLValue, arLocalLValue}:
localError(n.info, errExprHasNoAddress)
result.add x
result.typ = makePtrType(c, x.typ)
proc semTypeOf(c: PContext; n: PNode): PNode =
result = newNodeI(nkTypeOfExpr, n.info)
let typExpr = semExprWithType(c, n, {efInTypeof})
result.add typExpr
result.typ = makeTypeDesc(c, typExpr.typ.skipTypes({tyTypeDesc, tyIter}))
proc semIsPartOf(c: PContext, n: PNode, flags: TExprFlags): PNode =
var r = isPartOf(n[1], n[2])
result = newIntNodeT(ord(r), n)
proc expectIntLit(c: PContext, n: PNode): int =
let x = c.semConstExpr(c, n)
case x.kind
@@ -31,7 +45,7 @@ proc semInstantiationInfo(c: PContext, n: PNode): PNode =
line.intVal = toLinenumber(info)
result.add(filename)
result.add(line)
proc evalTypeTrait(trait: PNode, operand: PType, context: PSym): PNode =
let typ = operand.skipTypes({tyTypeDesc})
case trait.sym.name.s.normalize
@@ -40,7 +54,7 @@ proc evalTypeTrait(trait: PNode, operand: PType, context: PSym): PNode =
result.typ = newType(tyString, context)
result.info = trait.info
of "arity":
result = newIntNode(nkIntLit, typ.n.len-1)
result = newIntNode(nkIntLit, typ.len - ord(typ.kind==tyProc))
result.typ = newType(tyInt, context)
result.info = trait.info
else:
@@ -66,18 +80,18 @@ proc semOrd(c: PContext, n: PNode): PNode =
proc semBindSym(c: PContext, n: PNode): PNode =
result = copyNode(n)
result.add(n.sons[0])
let sl = semConstExpr(c, n.sons[1])
if sl.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit}:
if sl.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit}:
localError(n.sons[1].info, errStringLiteralExpected)
return errorNode(c, n)
let isMixin = semConstExpr(c, n.sons[2])
if isMixin.kind != nkIntLit or isMixin.intVal < 0 or
isMixin.intVal > high(TSymChoiceRule).int:
localError(n.sons[2].info, errConstExprExpected)
return errorNode(c, n)
let id = newIdentNode(getIdent(sl.strVal), n.info)
let s = qualifiedLookUp(c, id)
if s != nil:
@@ -87,38 +101,28 @@ proc semBindSym(c: PContext, n: PNode): PNode =
else:
localError(n.sons[1].info, errUndeclaredIdentifier, sl.strVal)
proc semLocals(c: PContext, n: PNode): PNode =
var counter = 0
var tupleType = newTypeS(tyTuple, c)
result = newNodeIT(nkPar, n.info, tupleType)
tupleType.n = newNodeI(nkRecList, n.info)
# for now we skip openarrays ...
for scope in walkScopes(c.currentScope):
if scope == c.topLevelScope: break
for it in items(scope.symbols):
# XXX parameters' owners are wrong for generics; this caused some pain
# for closures too; we should finally fix it.
#if it.owner != c.p.owner: return result
if it.kind in skLocalVars and
it.typ.skipTypes({tyGenericInst, tyVar}).kind notin
{tyVarargs, tyOpenArray, tyTypeDesc, tyStatic, tyExpr, tyStmt, tyEmpty}:
var field = newSym(skField, it.name, getCurrOwner(), n.info)
field.typ = it.typ.skipTypes({tyGenericInst, tyVar})
field.position = counter
inc(counter)
addSon(tupleType.n, newSymNode(field))
addSonSkipIntLit(tupleType, field.typ)
var a = newSymNode(it, result.info)
if it.typ.skipTypes({tyGenericInst}).kind == tyVar: a = newDeref(a)
result.add(a)
proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode
proc magicsAfterOverloadResolution(c: PContext, n: PNode,
proc isStrangeArray(t: PType): bool =
let t = t.skipTypes(abstractInst)
result = t.kind == tyArray and t.firstOrd != 0
proc isNegative(n: PNode): bool =
let n = n.skipConv
if n.kind in {nkCharLit..nkUInt64Lit}:
result = n.intVal < 0
elif n.kind in nkCallKinds and n.sons[0].kind == nkSym:
result = n.sons[0].sym.magic in {mUnaryMinusI, mUnaryMinusI64}
proc magicsAfterOverloadResolution(c: PContext, n: PNode,
flags: TExprFlags): PNode =
case n[0].sym.magic
of mAddr:
checkSonsLen(n, 2)
result = semAddr(c, n.sons[1])
of mTypeOf:
checkSonsLen(n, 2)
result = semTypeOf(c, n.sons[1])
of mIsPartOf: result = semIsPartOf(c, n, flags)
of mTypeTrait: result = semTypeTraits(c, n)
of mAstToStr:
@@ -129,8 +133,45 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
of mHigh, mLow: result = semLowHigh(c, n, n[0].sym.magic)
of mShallowCopy: result = semShallowCopy(c, n, flags)
of mNBindSym: result = semBindSym(c, n)
of mLocals: result = semLocals(c, n)
of mProcCall:
result = n
result.typ = n[1].typ
of mDotDot:
result = n
# disallow negative indexing for now:
if not c.p.bracketExpr.isNil:
if isNegative(n.sons[1]) or (n.len > 2 and isNegative(n.sons[2])):
localError(n.info, "use '^' instead of '-'; negative indexing is obsolete")
of mRoof:
# error correction:
result = n.sons[1]
if c.p.bracketExpr.isNil:
localError(n.info, "no surrounding array access context for '^'")
elif c.p.bracketExpr.checkForSideEffects != seNoSideEffect:
localError(n.info, "invalid context for '^' as '$#' has side effects" %
renderTree(c.p.bracketExpr))
elif c.p.bracketExpr.typ.isStrangeArray:
localError(n.info, "invalid context for '^' as len!=high+1 for '$#'" %
renderTree(c.p.bracketExpr))
else:
# ^x is rewritten to: len(a)-x
let lenExpr = newNodeI(nkCall, n.info)
lenExpr.add newIdentNode(getIdent"len", n.info)
lenExpr.add c.p.bracketExpr
let lenExprB = semExprWithType(c, lenExpr)
if lenExprB.typ.isNil or not isOrdinalType(lenExprB.typ):
localError(n.info, "'$#' has to be of an ordinal type for '^'" %
renderTree(lenExpr))
else:
result = newNodeIT(nkCall, n.info, getSysType(tyInt))
result.add newSymNode(createMagic("-", mSubI), n.info)
result.add lenExprB
result.add n.sons[1]
of mPlugin:
let plugin = getPlugin(n[0].sym)
if plugin.isNil:
localError(n.info, "cannot find plugin " & n[0].sym.name.s)
result = n
else:
result = plugin(c, n)
else: result = n

View File

@@ -1,7 +1,7 @@
#
#
# The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -37,7 +37,7 @@ is valid, but
spawn f(a[i])
spawn f(a[i])
inc i
is not! However,
is not! However,
spawn f(a[i])
if guard: inc i
spawn f(a[i])
@@ -317,8 +317,9 @@ proc analyseIf(c: var AnalysisCtx; n: PNode) =
proc analyse(c: var AnalysisCtx; n: PNode) =
case n.kind
of nkAsgn, nkFastAsgn:
if n[0].isSingleAssignable and n[1].isLocal:
let slot = c.getSlot(n[1].sym)
let y = n[1].skipConv
if n[0].isSingleAssignable and y.isLocal:
let slot = c.getSlot(y.sym)
slot.alias = n[0].sym
elif n[0].isLocal:
# since we already ensure sfAddrTaken is not in s.flags, we only need to
@@ -334,7 +335,7 @@ proc analyse(c: var AnalysisCtx; n: PNode) =
analyse(c, n[0])
else:
analyseSons(c, n)
addAsgnFact(c.guards, n[0], n[1])
addAsgnFact(c.guards, n[0], y)
of nkCallKinds:
# direct call:
if n[0].kind == nkSym: analyseCall(c, n, n[0].sym)
@@ -460,7 +461,7 @@ proc liftParallel*(owner: PSym; n: PNode): PNode =
# - detect used slices
# - detect used arguments
#echo "PAR ", renderTree(n)
var a = initAnalysisCtx()
let body = n.lastSon
analyse(a, body)

View File

@@ -1,19 +1,19 @@
#
#
# The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
import
intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
wordrecg, strutils, options, guards
# Second semantic checking pass over the AST. Necessary because the old
# way had some inherent problems. Performs:
#
#
# * effect+exception tracking
# * "usage before definition" checking
# * checks for invalid usages of compiletime magics (not implemented)
@@ -23,7 +23,7 @@ import
# Predefined effects:
# io, time (time dependent), gc (performs GC'ed allocation), exceptions,
# side effect (accesses global), store (stores into *type*),
# store_unkown (performs some store) --> store(any)|store(x)
# store_unknown (performs some store) --> store(any)|store(x)
# load (loads from *type*), recursive (recursive call), unsafe,
# endless (has endless loops), --> user effects are defined over *patterns*
# --> a TR macro can annotate the proc with user defined annotations
@@ -31,31 +31,31 @@ import
# Load&Store analysis is performed on *paths*. A path is an access like
# obj.x.y[i].z; splitting paths up causes some problems:
#
#
# var x = obj.x
# var z = x.y[i].z
#
# Alias analysis is affected by this too! A good solution is *type splitting*:
# T becomes T1 and T2 if it's known that T1 and T2 can't alias.
#
# T becomes T1 and T2 if it's known that T1 and T2 can't alias.
#
# An aliasing problem and a race condition are effectively the same problem.
# Type based alias analysis is nice but not sufficient; especially splitting
# an array and filling it in parallel should be supported but is not easily
# done: It essentially requires a built-in 'indexSplit' operation and dependent
# typing.
# ------------------------ exception and tag tracking -------------------------
discard """
exception tracking:
a() # raises 'x', 'e'
try:
b() # raises 'e'
except e:
# must not undo 'e' here; hrm
c()
--> we need a stack of scopes for this analysis
# XXX enhance the algorithm to care about 'dirty' expressions:
@@ -138,7 +138,7 @@ proc guardDotAccess(a: PEffects; n: PNode) =
if g.kind == skUnknown:
var field: PSym = nil
var ty = n.sons[0].typ.skipTypes(abstractPtrs)
if ty.kind == tyTuple:
if ty.kind == tyTuple and not ty.n.isNil:
field = lookupInRecord(ty.n, g.name)
else:
while ty != nil and ty.kind == tyObject:
@@ -194,6 +194,39 @@ proc warnAboutGcUnsafe(n: PNode) =
#assert false
message(n.info, warnGcUnsafe, renderTree(n))
proc markGcUnsafe(a: PEffects; reason: PSym) =
a.gcUnsafe = true
if a.owner.kind in routineKinds: a.owner.gcUnsafetyReason = reason
proc markGcUnsafe(a: PEffects; reason: PNode) =
a.gcUnsafe = true
if a.owner.kind in routineKinds:
if reason.kind == nkSym:
a.owner.gcUnsafetyReason = reason.sym
else:
a.owner.gcUnsafetyReason = newSym(skUnknown, getIdent("<unknown>"),
a.owner, reason.info)
proc listGcUnsafety(s: PSym; onlyWarning: bool) =
let u = s.gcUnsafetyReason
if u != nil:
let msgKind = if onlyWarning: warnGcUnsafe2 else: errGenerated
if u.kind in {skLet, skVar}:
message(s.info, msgKind,
("'$#' is not GC-safe as it accesses '$#'" &
" which is a global using GC'ed memory") % [s.name.s, u.name.s])
elif u.kind in routineKinds:
# recursive call *always* produces only a warning so the full error
# message is printed:
listGcUnsafety(u, true)
message(s.info, msgKind,
"'$#' is not GC-safe as it calls '$#'" %
[s.name.s, u.name.s])
else:
internalAssert u.kind == skUnknown
message(u.info, msgKind,
"'$#' is not GC-safe as it performs an indirect call here" % s.name.s)
proc useVar(a: PEffects, n: PNode) =
let s = n.sym
if isLocalVar(a, s):
@@ -206,10 +239,9 @@ proc useVar(a: PEffects, n: PNode) =
a.init.add s.id
if {sfGlobal, sfThread} * s.flags == {sfGlobal} and s.kind in {skVar, skLet}:
if s.guard != nil: guardGlobal(a, n, s.guard)
if (tfHasGCedMem in s.typ.flags or s.typ.isGCedMem) and
tfGcSafe notin s.typ.flags:
if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n)
a.gcUnsafe = true
if (tfHasGCedMem in s.typ.flags or s.typ.isGCedMem):
#if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n)
markGcUnsafe(a, s)
type
TIntersection = seq[tuple[id, count: int]] # a simple count table
@@ -230,7 +262,7 @@ proc getEbase(): PType =
proc excType(n: PNode): PType =
# reraise is like raising E_Base:
let t = if n.kind == nkEmpty: getEbase() else: n.typ
let t = if n.kind == nkEmpty or n.typ.isNil: getEbase() else: n.typ
result = skipTypes(t, skipPtrs)
proc createRaise(n: PNode): PNode =
@@ -318,7 +350,7 @@ proc trackTryStmt(tracked: PEffects, n: PNode) =
dec tracked.inTryStmt
for i in oldState.. <tracked.init.len:
addToIntersection(inter, tracked.init[i])
var branches = 1
var hasFinally = false
for i in 1 .. < n.len:
@@ -342,7 +374,7 @@ proc trackTryStmt(tracked: PEffects, n: PNode) =
setLen(tracked.init, oldState)
track(tracked, b.sons[blen-1])
hasFinally = true
tracked.bottom = oldBottom
if not hasFinally:
setLen(tracked.init, oldState)
@@ -353,7 +385,7 @@ proc isIndirectCall(n: PNode, owner: PSym): bool =
# we don't count f(...) as an indirect call if 'f' is an parameter.
# Instead we track expressions of type tyProc too. See the manual for
# details:
if n.kind != nkSym:
if n.kind != nkSym:
result = true
elif n.sym.kind == skParam:
result = owner != n.sym.owner or owner == nil
@@ -363,13 +395,13 @@ proc isIndirectCall(n: PNode, owner: PSym): bool =
proc isForwardedProc(n: PNode): bool =
result = n.kind == nkSym and sfForward in n.sym.flags
proc trackPragmaStmt(tracked: PEffects, n: PNode) =
for i in countup(0, sonsLen(n) - 1):
proc trackPragmaStmt(tracked: PEffects, n: PNode) =
for i in countup(0, sonsLen(n) - 1):
var it = n.sons[i]
if whichPragma(it) == wEffects:
# list the computed effects up to here:
listEffects(tracked)
proc effectSpec(n: PNode, effectType: TSpecialWord): PNode =
for i in countup(0, sonsLen(n) - 1):
var it = n.sons[i]
@@ -384,12 +416,12 @@ proc documentEffect(n, x: PNode, effectType: TSpecialWord, idx: int): PNode =
let spec = effectSpec(x, effectType)
if isNil(spec):
let s = n.sons[namePos].sym
let actual = s.typ.n.sons[0]
if actual.len != effectListLen: return
let real = actual.sons[idx]
# warning: hack ahead:
# warning: hack ahead:
var effects = newNodeI(nkBracket, n.info, real.len)
for i in 0 .. <real.len:
var t = typeToString(real[i].typ)
@@ -406,7 +438,7 @@ proc documentRaises*(n: PNode) =
let pragmas = n.sons[pragmasPos]
let p1 = documentEffect(n, pragmas, wRaises, exceptionEffects)
let p2 = documentEffect(n, pragmas, wTags, tagEffects)
if p1 != nil or p2 != nil:
if pragmas.kind == nkEmpty:
n.sons[pragmasPos] = newNodeI(nkPragma, n.info)
@@ -442,28 +474,28 @@ proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) =
let pragma = s.ast.sons[pragmasPos]
let spec = effectSpec(pragma, wRaises)
mergeEffects(tracked, spec, n)
let tagSpec = effectSpec(pragma, wTags)
mergeTags(tracked, tagSpec, n)
if notGcSafe(s.typ) and sfImportc notin s.flags:
if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n)
tracked.gcUnsafe = true
markGcUnsafe(tracked, s)
mergeLockLevels(tracked, n, s.getLockLevel)
proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) =
let n = n.skipConv
if paramType != nil and tfNotNil in paramType.flags and
if paramType != nil and tfNotNil in paramType.flags and
n.typ != nil and tfNotNil notin n.typ.flags:
if n.kind == nkAddr:
# addr(x[]) can't be proven, but addr(x) can:
if not containsNode(n, {nkDerefExpr, nkHiddenDeref}): return
elif n.kind == nkSym and n.sym.kind in routineKinds:
elif (n.kind == nkSym and n.sym.kind in routineKinds) or n.kind in procDefs:
# 'p' is not nil obviously:
return
case impliesNotNil(tracked.guards, n)
of impUnknown:
message(n.info, errGenerated,
message(n.info, errGenerated,
"cannot prove '$1' is not nil" % n.renderTree)
of impNo:
message(n.info, errGenerated, "'$1' is provably nil" % n.renderTree)
@@ -502,19 +534,19 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) =
# assume GcUnsafe unless in its type; 'forward' does not matter:
if notGcSafe(op) and not isOwnedProcVar(a, tracked.owner):
if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n)
tracked.gcUnsafe = true
markGcUnsafe(tracked, a)
else:
mergeEffects(tracked, effectList.sons[exceptionEffects], n)
mergeTags(tracked, effectList.sons[tagEffects], n)
if notGcSafe(op):
if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n)
tracked.gcUnsafe = true
markGcUnsafe(tracked, a)
notNilCheck(tracked, n, paramType)
proc breaksBlock(n: PNode): bool =
case n.kind
of nkStmtList, nkStmtListExpr:
for c in n:
for c in n:
if breaksBlock(c): return true
of nkBreakStmt, nkReturnStmt, nkRaiseStmt:
return true
@@ -528,7 +560,10 @@ proc trackCase(tracked: PEffects, n: PNode) =
track(tracked, n.sons[0])
let oldState = tracked.init.len
let oldFacts = tracked.guards.len
let interesting = interestingCaseExpr(n.sons[0]) and warnProveField in gNotes
let stringCase = skipTypes(n.sons[0].typ,
abstractVarRange-{tyTypeDesc}).kind in {tyFloat..tyFloat128, tyString}
let interesting = not stringCase and interestingCaseExpr(n.sons[0]) and
warnProveField in gNotes
var inter: TIntersection = @[]
var toCover = 0
for i in 1.. <n.len:
@@ -542,14 +577,9 @@ proc trackCase(tracked: PEffects, n: PNode) =
if not breaksBlock(branch.lastSon): inc toCover
for i in oldState.. <tracked.init.len:
addToIntersection(inter, tracked.init[i])
let exh = case skipTypes(n.sons[0].typ, abstractVarRange-{tyTypeDesc}).kind
of tyFloat..tyFloat128, tyString:
lastSon(n).kind == nkElse
else:
true
setLen(tracked.init, oldState)
if exh:
if not stringCase or lastSon(n).kind == nkElse:
for id, count in items(inter):
if count >= toCover: tracked.init.add id
# else we can't merge
@@ -587,7 +617,7 @@ proc trackIf(tracked: PEffects, n: PNode) =
if count >= toCover: tracked.init.add id
# else we can't merge as it is not exhaustive
setLen(tracked.guards, oldFacts)
proc trackBlock(tracked: PEffects, n: PNode) =
if n.kind in {nkStmtList, nkStmtListExpr}:
var oldState = -1
@@ -656,7 +686,7 @@ proc track(tracked: PEffects, n: PNode) =
# and it's not a recursive call:
if not (a.kind == nkSym and a.sym == tracked.owner):
warnAboutGcUnsafe(n)
tracked.gcUnsafe = true
markGcUnsafe(tracked, a)
for i in 1 .. <len(n): trackOperand(tracked, n.sons[i], paramType(op, i))
if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize, mNewSeq}:
# may not look like an assignment, but it is:
@@ -682,8 +712,8 @@ proc track(tracked: PEffects, n: PNode) =
of nkVarSection, nkLetSection:
for child in n:
let last = lastSon(child)
if last.kind != nkEmpty: track(tracked, last)
if child.kind == nkIdentDefs and last.kind != nkEmpty:
track(tracked, last)
for i in 0 .. child.len-3:
initVar(tracked, child.sons[i], volatileCheck=false)
addAsgnFact(tracked.guards, child.sons[i], last)
@@ -732,7 +762,7 @@ proc track(tracked: PEffects, n: PNode) =
setLen(tracked.locked, oldLocked)
tracked.currLockLevel = oldLockLevel
of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef,
nkMacroDef, nkTemplateDef:
nkMacroDef, nkTemplateDef, nkLambda, nkDo:
discard
else:
for i in 0 .. <safeLen(n): track(tracked, n.sons[i])
@@ -779,7 +809,7 @@ proc checkMethodEffects*(disp, branch: PSym) =
checkRaisesSpec(tagsSpec, actual.sons[tagEffects],
"can have an unlisted effect: ", hints=off, subtypeRelation)
if sfThread in disp.flags and notGcSafe(branch.typ):
localError(branch.info, "base method is GC-safe, but '$1' is not" %
localError(branch.info, "base method is GC-safe, but '$1' is not" %
branch.name.s)
if branch.typ.lockLevel > disp.typ.lockLevel:
when true:
@@ -811,21 +841,21 @@ proc initEffects(effects: PNode; s: PSym; t: var TEffects) =
newSeq(effects.sons, effectListLen)
effects.sons[exceptionEffects] = newNodeI(nkArgList, s.info)
effects.sons[tagEffects] = newNodeI(nkArgList, s.info)
t.exc = effects.sons[exceptionEffects]
t.tags = effects.sons[tagEffects]
t.owner = s
t.init = @[]
t.guards = @[]
t.locked = @[]
proc trackProc*(s: PSym, body: PNode) =
var effects = s.typ.n.sons[0]
internalAssert effects.kind == nkEffectList
# effects already computed?
if sfForward in s.flags: return
if effects.len == effectListLen: return
var t: TEffects
initEffects(effects, s, t)
track(t, body)
@@ -849,19 +879,22 @@ proc trackProc*(s: PSym, body: PNode) =
# after the check, use the formal spec:
effects.sons[tagEffects] = tagsSpec
if optThreadAnalysis in gGlobalOptions:
if sfThread in s.flags and t.gcUnsafe:
if optThreads in gGlobalOptions:
localError(s.info, "'$1' is not GC-safe" % s.name.s)
else:
localError(s.info, warnGcUnsafe2, s.name.s)
if not t.gcUnsafe: s.typ.flags.incl tfGcSafe
if s.typ.lockLevel == UnspecifiedLockLevel:
s.typ.lockLevel = t.maxLockLevel
elif t.maxLockLevel > s.typ.lockLevel:
localError(s.info,
"declared lock level is $1, but real lock level is $2" %
[$s.typ.lockLevel, $t.maxLockLevel])
if sfThread in s.flags and t.gcUnsafe:
if optThreads in gGlobalOptions and optThreadAnalysis in gGlobalOptions:
#localError(s.info, "'$1' is not GC-safe" % s.name.s)
listGcUnsafety(s, onlyWarning=false)
else:
listGcUnsafety(s, onlyWarning=true)
#localError(s.info, warnGcUnsafe2, s.name.s)
if not t.gcUnsafe:
s.typ.flags.incl tfGcSafe
if s.typ.lockLevel == UnspecifiedLockLevel:
s.typ.lockLevel = t.maxLockLevel
elif t.maxLockLevel > s.typ.lockLevel:
#localError(s.info,
message(s.info, warnLockLevel,
"declared lock level is $1, but real lock level is $2" %
[$s.typ.lockLevel, $t.maxLockLevel])
proc trackTopLevelStmt*(module: PSym; n: PNode) =
if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef,

View File

@@ -12,13 +12,13 @@
var enforceVoidContext = PType(kind: tyStmt)
proc semDiscard(c: PContext, n: PNode): PNode =
proc semDiscard(c: PContext, n: PNode): PNode =
result = n
checkSonsLen(n, 1)
if n.sons[0].kind != nkEmpty:
n.sons[0] = semExprWithType(c, n.sons[0])
if isEmptyType(n.sons[0].typ): localError(n.info, errInvalidDiscard)
proc semBreakOrContinue(c: PContext, n: PNode): PNode =
result = n
checkSonsLen(n, 1)
@@ -29,7 +29,7 @@ proc semBreakOrContinue(c: PContext, n: PNode): PNode =
of nkIdent: s = lookUp(c, n.sons[0])
of nkSym: s = n.sons[0].sym
else: illFormedAst(n)
if s.kind == skLabel and s.owner.id == c.p.owner.id:
if s.kind == skLabel and s.owner.id == c.p.owner.id:
var x = newSymNode(s)
x.info = n.info
incl(s.flags, sfUsed)
@@ -41,16 +41,16 @@ proc semBreakOrContinue(c: PContext, n: PNode): PNode =
else:
localError(n.info, errGenerated, "'continue' cannot have a label")
elif (c.p.nestedLoopCounter <= 0) and (c.p.nestedBlockCounter <= 0):
localError(n.info, errInvalidControlFlowX,
localError(n.info, errInvalidControlFlowX,
renderTree(n, {renderNoComments}))
proc semAsm(con: PContext, n: PNode): PNode =
proc semAsm(con: PContext, n: PNode): PNode =
checkSonsLen(n, 2)
var marker = pragmaAsm(con, n.sons[0])
if marker == '\0': marker = '`' # default marker
result = semAsmOrEmit(con, n, marker)
proc semWhile(c: PContext, n: PNode): PNode =
proc semWhile(c: PContext, n: PNode): PNode =
result = n
checkSonsLen(n, 2)
openScope(c)
@@ -62,16 +62,16 @@ proc semWhile(c: PContext, n: PNode): PNode =
if n.sons[1].typ == enforceVoidContext:
result.typ = enforceVoidContext
proc toCover(t: PType): BiggestInt =
proc toCover(t: PType): BiggestInt =
var t2 = skipTypes(t, abstractVarRange-{tyTypeDesc})
if t2.kind == tyEnum and enumHasHoles(t2):
if t2.kind == tyEnum and enumHasHoles(t2):
result = sonsLen(t2.n)
else:
result = lengthOrd(skipTypes(t, abstractVar-{tyTypeDesc}))
proc performProcvarCheck(c: PContext, n: PNode, s: PSym) =
## Checks that the given symbol is a proper procedure variable, meaning
## that it
## that it
var smoduleId = getModule(s).id
if sfProcvar notin s.flags and s.typ.callConv == ccDefault and
smoduleId != c.module.id:
@@ -92,20 +92,16 @@ proc semProc(c: PContext, n: PNode): PNode
include semdestruct
proc semDestructorCheck(c: PContext, n: PNode, flags: TExprFlags) {.inline.} =
if efAllowDestructor notin flags and n.kind in nkCallKinds+{nkObjConstr}:
if efAllowDestructor notin flags and
n.kind in nkCallKinds+{nkObjConstr,nkBracket}:
if instantiateDestructor(c, n.typ) != nil:
localError(n.info, errGenerated,
"usage of a type with a destructor in a non destructible context")
localError(n.info, warnDestructor)
# This still breaks too many things:
when false:
if efDetermineType notin flags and n.typ.kind == tyTypeDesc and
if efDetermineType notin flags and n.typ.kind == tyTypeDesc and
c.p.owner.kind notin {skTemplate, skMacro}:
localError(n.info, errGenerated, "value expected, but got a type")
proc newDeref(n: PNode): PNode {.inline.} =
result = newNodeIT(nkHiddenDeref, n.info, n.typ.sons[0])
addSon(result, n)
proc semExprBranch(c: PContext, n: PNode): PNode =
result = semExpr(c, n)
if result.typ != nil:
@@ -127,7 +123,7 @@ const
proc implicitlyDiscardable(n: PNode): bool =
var n = n
while n.kind in skipForDiscardable: n = n.lastSon
result = isCallExpr(n) and n.sons[0].kind == nkSym and
result = isCallExpr(n) and n.sons[0].kind == nkSym and
sfDiscardable in n.sons[0].sym.flags
proc fixNilType(n: PNode) =
@@ -160,11 +156,11 @@ proc discardCheck(c: PContext, result: PNode) =
while n.kind in skipForDiscardable: n = n.lastSon
localError(n.info, errDiscardValueX, result.typ.typeToString)
proc semIf(c: PContext, n: PNode): PNode =
proc semIf(c: PContext, n: PNode): PNode =
result = n
var typ = commonTypeBegin
var hasElse = false
for i in countup(0, sonsLen(n) - 1):
for i in countup(0, sonsLen(n) - 1):
var it = n.sons[i]
if it.len == 2:
when newScopeForIf: openScope(c)
@@ -208,10 +204,10 @@ proc semCase(c: PContext, n: PNode): PNode =
else:
localError(n.info, errSelectorMustBeOfCertainTypes)
return
for i in countup(1, sonsLen(n) - 1):
for i in countup(1, sonsLen(n) - 1):
var x = n.sons[i]
case x.kind
of nkOfBranch:
of nkOfBranch:
checkMinSonsLen(x, 2)
semCaseBranch(c, n, x, i, covered)
var last = sonsLen(x)-1
@@ -304,9 +300,9 @@ proc semTry(c: PContext, n: PNode): PNode =
it.sons[j] = fitNode(c, typ, it.sons[j])
result.typ = typ
proc fitRemoveHiddenConv(c: PContext, typ: PType, n: PNode): PNode =
proc fitRemoveHiddenConv(c: PContext, typ: PType, n: PNode): PNode =
result = fitNode(c, typ, n)
if result.kind in {nkHiddenStdConv, nkHiddenSubConv}:
if result.kind in {nkHiddenStdConv, nkHiddenSubConv}:
changeType(result.sons[1], typ, check=true)
result = result.sons[1]
elif not sameType(result.typ, typ):
@@ -325,7 +321,7 @@ proc identWithin(n: PNode, s: PIdent): bool =
result = n.kind == nkSym and n.sym.name.id == s.id
proc semIdentDef(c: PContext, n: PNode, kind: TSymKind): PSym =
if isTopLevel(c):
if isTopLevel(c):
result = semIdentWithPragma(c, kind, n, {sfExported})
incl(result.flags, sfGlobal)
else:
@@ -340,14 +336,52 @@ proc checkNilable(v: PSym) =
elif tfNotNil in v.typ.flags and tfNotNil notin v.ast.typ.flags:
message(v.info, warnProveInit, v.name.s)
proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
include semasgn
proc addToVarSection(c: PContext; result: var PNode; orig, identDefs: PNode) =
# consider this:
# var
# x = 0
# withOverloadedAssignment = foo()
# y = use(withOverloadedAssignment)
# We need to split this into a statement list with multiple 'var' sections
# in order for this transformation to be correct.
let L = identDefs.len
let value = identDefs[L-1]
if value.typ != nil and tfHasAsgn in value.typ.flags:
# the spec says we need to rewrite 'var x = T()' to 'var x: T; x = T()':
identDefs.sons[L-1] = emptyNode
if result.kind != nkStmtList:
let oldResult = result
oldResult.add identDefs
result = newNodeI(nkStmtList, result.info)
result.add oldResult
else:
let o = copyNode(orig)
o.add identDefs
result.add o
for i in 0 .. L-3:
result.add overloadedAsgn(c, identDefs[i], value)
elif result.kind == nkStmtList:
let o = copyNode(orig)
o.add identDefs
result.add o
else:
result.add identDefs
proc isDiscardUnderscore(v: PSym): bool =
if v.name.s == "_":
v.flags.incl(sfGenSym)
result = true
proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
var b: PNode
result = copyNode(n)
var hasCompileTime = false
for i in countup(0, sonsLen(n)-1):
for i in countup(0, sonsLen(n)-1):
var a = n.sons[i]
if gCmd == cmdIdeTools: suggestStmt(c, a)
if a.kind == nkCommentStmt: continue
if a.kind == nkCommentStmt: continue
if a.kind notin {nkIdentDefs, nkVarTuple, nkConstDef}: illFormedAst(a)
checkMinSonsLen(a, 3)
var length = sonsLen(a)
@@ -359,13 +393,17 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
var def: PNode
if a.sons[length-1].kind != nkEmpty:
def = semExprWithType(c, a.sons[length-1], {efAllowDestructor})
if def.typ.kind == tyTypeDesc and c.p.owner.kind != skMacro:
# prevent the all too common 'var x = int' bug:
localError(def.info, "'typedesc' metatype is not valid here; typed '=' instead of ':'?")
def.typ = errorType(c)
if typ != nil:
if typ.isMetaType:
def = inferWithMetatype(c, typ, def)
typ = def.typ
else:
# BUGFIX: ``fitNode`` is needed here!
# check type compability between def.typ and typ
# check type compatibility between def.typ and typ
def = fitNode(c, typ, def)
#changeType(def.skipConv, typ, check=true)
else:
@@ -377,35 +415,38 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
else:
def = ast.emptyNode
if symkind == skLet: localError(a.info, errLetNeedsInit)
# this can only happen for errornous var statements:
if typ == nil: continue
if not typeAllowed(typ, symkind):
localError(a.info, errXisNoType, typeToString(typ))
typeAllowedCheck(a.info, typ, symkind)
var tup = skipTypes(typ, {tyGenericInst})
if a.kind == nkVarTuple:
if tup.kind != tyTuple:
if a.kind == nkVarTuple:
if tup.kind != tyTuple:
localError(a.info, errXExpected, "tuple")
elif length-2 != sonsLen(tup):
elif length-2 != sonsLen(tup):
localError(a.info, errWrongNumberOfVariables)
else:
b = newNodeI(nkVarTuple, a.info)
newSons(b, length)
b.sons[length-2] = a.sons[length-2] # keep type desc for doc generator
b.sons[length-1] = def
addSon(result, b)
elif tup.kind == tyTuple and def.kind == nkPar and
addToVarSection(c, result, n, b)
elif tup.kind == tyTuple and def.kind == nkPar and
a.kind == nkIdentDefs and a.len > 3:
message(a.info, warnEachIdentIsTuple)
for j in countup(0, length-3):
var v = semIdentDef(c, a.sons[j], symkind)
if sfGenSym notin v.flags: addInterfaceDecl(c, v)
if sfGenSym notin v.flags and not isDiscardUnderscore(v):
addInterfaceDecl(c, v)
when oKeepVariableNames:
if c.inUnrolledContext > 0: v.flags.incl(sfShadowed)
else:
let shadowed = findShadowedVar(c, v)
if shadowed != nil:
shadowed.flags.incl(sfShadowed)
if shadowed.kind == skResult and sfGenSym notin v.flags:
message(a.info, warnResultShadowed)
# a shadowed variable is an error unless it appears on the right
# side of the '=':
if warnShadowIdent in gNotes and not identWithin(def, v.name):
@@ -423,7 +464,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
addSon(b, newSymNode(v))
addSon(b, a.sons[length-2]) # keep type desc for doc generator
addSon(b, copyTree(def))
addSon(result, b)
addToVarSection(c, result, n, b)
else:
if def.kind == nkPar: v.ast = def[j]
v.typ = tup.sons[j]
@@ -432,12 +473,12 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
if sfCompileTime in v.flags: hasCompileTime = true
if hasCompileTime: vm.setupCompileTimeVar(c.module, result)
proc semConst(c: PContext, n: PNode): PNode =
proc semConst(c: PContext, n: PNode): PNode =
result = copyNode(n)
for i in countup(0, sonsLen(n) - 1):
for i in countup(0, sonsLen(n) - 1):
var a = n.sons[i]
if gCmd == cmdIdeTools: suggestStmt(c, a)
if a.kind == nkCommentStmt: continue
if a.kind == nkCommentStmt: continue
if (a.kind != nkConstDef): illFormedAst(a)
checkSonsLen(a, 3)
var v = semIdentDef(c, a.sons[0], skConst)
@@ -456,7 +497,7 @@ proc semConst(c: PContext, n: PNode): PNode =
if typ == nil:
localError(a.sons[2].info, errConstExprExpected)
continue
if not typeAllowed(typ, skConst) and def.kind != nkNilLit:
if typeAllowed(typ, skConst) != nil and def.kind != nkNilLit:
localError(a.info, errXisNoType, typeToString(typ))
continue
v.typ = typ
@@ -492,7 +533,7 @@ proc semForVars(c: PContext, n: PNode): PNode =
var iter = skipTypes(iterBase, {tyGenericInst})
# length == 3 means that there is one for loop variable
# and thus no tuple unpacking:
if iter.kind != tyTuple or length == 3:
if iter.kind != tyTuple or length == 3:
if length == 3:
var v = symForVar(c, n.sons[0])
if getCurrOwner().kind == skModule: incl(v.flags, sfGlobal)
@@ -512,7 +553,8 @@ proc semForVars(c: PContext, n: PNode): PNode =
if getCurrOwner().kind == skModule: incl(v.flags, sfGlobal)
v.typ = iter.sons[i]
n.sons[i] = newSymNode(v)
if sfGenSym notin v.flags: addForVarDecl(c, v)
if sfGenSym notin v.flags and not isDiscardUnderscore(v):
addForVarDecl(c, v)
inc(c.p.nestedLoopCounter)
n.sons[length-1] = semStmt(c, n.sons[length-1])
dec(c.p.nestedLoopCounter)
@@ -520,13 +562,13 @@ proc semForVars(c: PContext, n: PNode): PNode =
proc implicitIterator(c: PContext, it: string, arg: PNode): PNode =
result = newNodeI(nkCall, arg.info)
result.add(newIdentNode(it.getIdent, arg.info))
if arg.typ != nil and arg.typ.kind == tyVar:
if arg.typ != nil and arg.typ.kind == tyVar:
result.add newDeref(arg)
else:
result.add arg
result = semExprNoDeref(c, result, {efWantIterator})
proc semFor(c: PContext, n: PNode): PNode =
proc semFor(c: PContext, n: PNode): PNode =
result = n
checkMinSonsLen(n, 3)
var length = sonsLen(n)
@@ -561,13 +603,13 @@ proc semFor(c: PContext, n: PNode): PNode =
result.typ = enforceVoidContext
closeScope(c)
proc semRaise(c: PContext, n: PNode): PNode =
proc semRaise(c: PContext, n: PNode): PNode =
result = n
checkSonsLen(n, 1)
if n.sons[0].kind != nkEmpty:
if n.sons[0].kind != nkEmpty:
n.sons[0] = semExprWithType(c, n.sons[0])
var typ = n.sons[0].typ
if typ.kind != tyRef or typ.sons[0].kind != tyObject:
if typ.kind != tyRef or typ.sons[0].kind != tyObject:
localError(n.info, errExprCannotBeRaised)
proc addGenericParamListToScope(c: PContext, n: PNode) =
@@ -577,13 +619,13 @@ proc addGenericParamListToScope(c: PContext, n: PNode) =
if a.kind == nkSym: addDecl(c, a.sym)
else: illFormedAst(a)
proc typeSectionLeftSidePass(c: PContext, n: PNode) =
proc typeSectionLeftSidePass(c: PContext, n: PNode) =
# process the symbols on the left side for the whole type section, before
# we even look at the type definitions on the right
for i in countup(0, sonsLen(n) - 1):
for i in countup(0, sonsLen(n) - 1):
var a = n.sons[i]
if gCmd == cmdIdeTools: suggestStmt(c, a)
if a.kind == nkCommentStmt: continue
if a.kind == nkCommentStmt: continue
if a.kind != nkTypeDef: illFormedAst(a)
checkSonsLen(a, 3)
var s = semIdentDef(c, a.sons[0], skType)
@@ -596,29 +638,29 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) =
a.sons[0] = newSymNode(s)
proc typeSectionRightSidePass(c: PContext, n: PNode) =
for i in countup(0, sonsLen(n) - 1):
for i in countup(0, sonsLen(n) - 1):
var a = n.sons[i]
if a.kind == nkCommentStmt: continue
if a.kind == nkCommentStmt: continue
if (a.kind != nkTypeDef): illFormedAst(a)
checkSonsLen(a, 3)
if (a.sons[0].kind != nkSym): illFormedAst(a)
var s = a.sons[0].sym
if s.magic == mNone and a.sons[2].kind == nkEmpty:
if s.magic == mNone and a.sons[2].kind == nkEmpty:
localError(a.info, errImplOfXexpected, s.name.s)
if s.magic != mNone: processMagicType(c, s)
if a.sons[1].kind != nkEmpty:
if a.sons[1].kind != nkEmpty:
# We have a generic type declaration here. In generic types,
# symbol lookup needs to be done here.
openScope(c)
pushOwner(s)
if s.magic == mNone: s.typ.kind = tyGenericBody
# XXX for generic type aliases this is not correct! We need the
# underlying Id really:
# underlying Id really:
#
# type
# TGObj[T] = object
# TAlias[T] = TGObj[T]
#
#
s.typ.n = semGenericParamList(c, a.sons[1], s.typ)
a.sons[1] = s.typ.n
s.typ.size = -1 # could not be computed properly
@@ -636,60 +678,18 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
s.typ.sons[sonsLen(s.typ) - 1] = body
popOwner()
closeScope(c)
elif a.sons[2].kind != nkEmpty:
elif a.sons[2].kind != nkEmpty:
# process the type's body:
pushOwner(s)
var t = semTypeNode(c, a.sons[2], s.typ)
if s.typ == nil:
if s.typ == nil:
s.typ = t
elif t != s.typ:
elif t != s.typ:
# this can happen for e.g. tcan_alias_specialised_generic:
assignType(s.typ, t)
#debug s.typ
s.ast = a
popOwner()
proc checkForMetaFields(n: PNode) =
template checkMeta(t) =
if t != nil and t.isMetaType and tfGenericTypeParam notin t.flags:
localError(n.info, errTIsNotAConcreteType, t.typeToString)
case n.kind
of nkRecList, nkRecCase:
for s in n: checkForMetaFields(s)
of nkOfBranch, nkElse:
checkForMetaFields(n.lastSon)
of nkSym:
let t = n.sym.typ
case t.kind
of tySequence, tySet, tyArray, tyOpenArray, tyVar, tyPtr, tyRef,
tyProc, tyGenericInvokation, tyGenericInst:
let start = ord(t.kind in {tyGenericInvokation, tyGenericInst})
for i in start .. <t.sons.len:
checkMeta(t.sons[i])
else:
checkMeta(t)
else:
internalAssert false
proc typeSectionFinalPass(c: PContext, n: PNode) =
for i in countup(0, sonsLen(n) - 1):
var a = n.sons[i]
if a.kind == nkCommentStmt: continue
if a.sons[0].kind != nkSym: illFormedAst(a)
var s = a.sons[0].sym
# compute the type's size and check for illegal recursions:
if a.sons[1].kind == nkEmpty:
if a.sons[2].kind in {nkSym, nkIdent, nkAccQuoted}:
# type aliases are hard:
#MessageOut('for type ' + typeToString(s.typ));
var t = semTypeNode(c, a.sons[2], nil)
if t.kind in {tyObject, tyEnum}:
assignType(s.typ, t)
s.typ.id = t.id # same id
checkConstructedType(s.info, s.typ)
if s.typ.kind in {tyObject, tyTuple}:
checkForMetaFields(s.typ.n)
let aa = a.sons[2]
if aa.kind in {nkRefTy, nkPtrTy} and aa.len == 1 and
aa.sons[0].kind == nkObjectTy:
@@ -701,6 +701,49 @@ proc typeSectionFinalPass(c: PContext, n: PNode) =
st.lastSon.sym = newSym(skType, getIdent(s.name.s & ":ObjectType"),
getCurrOwner(), s.info)
proc checkForMetaFields(n: PNode) =
template checkMeta(t) =
if t != nil and t.isMetaType and tfGenericTypeParam notin t.flags:
localError(n.info, errTIsNotAConcreteType, t.typeToString)
if n.isNil: return
case n.kind
of nkRecList, nkRecCase:
for s in n: checkForMetaFields(s)
of nkOfBranch, nkElse:
checkForMetaFields(n.lastSon)
of nkSym:
let t = n.sym.typ
case t.kind
of tySequence, tySet, tyArray, tyOpenArray, tyVar, tyPtr, tyRef,
tyProc, tyGenericInvocation, tyGenericInst:
let start = ord(t.kind in {tyGenericInvocation, tyGenericInst})
for i in start .. <t.sons.len:
checkMeta(t.sons[i])
else:
checkMeta(t)
else:
internalAssert false
proc typeSectionFinalPass(c: PContext, n: PNode) =
for i in countup(0, sonsLen(n) - 1):
var a = n.sons[i]
if a.kind == nkCommentStmt: continue
if a.sons[0].kind != nkSym: illFormedAst(a)
var s = a.sons[0].sym
# compute the type's size and check for illegal recursions:
if a.sons[1].kind == nkEmpty:
if a.sons[2].kind in {nkSym, nkIdent, nkAccQuoted}:
# type aliases are hard:
#MessageOut('for type ' + typeToString(s.typ));
var t = semTypeNode(c, a.sons[2], nil)
if t.kind in {tyObject, tyEnum}:
assignType(s.typ, t)
s.typ.id = t.id # same id
checkConstructedType(s.info, s.typ)
if s.typ.kind in {tyObject, tyTuple} and not s.typ.n.isNil:
checkForMetaFields(s.typ.n)
proc semTypeSection(c: PContext, n: PNode): PNode =
## Processes a type section. This must be done in separate passes, in order
## to allow the type definitions in the section to reference each other
@@ -716,29 +759,29 @@ proc semParamList(c: PContext, n, genericParams: PNode, s: PSym) =
if s.typ.sons[0] != nil and s.typ.sons[0].kind == tyStmt:
localError(n.info, errGenerated, "invalid return type: 'stmt'")
proc addParams(c: PContext, n: PNode, kind: TSymKind) =
for i in countup(1, sonsLen(n)-1):
proc addParams(c: PContext, n: PNode, kind: TSymKind) =
for i in countup(1, sonsLen(n)-1):
if n.sons[i].kind == nkSym: addParamOrResult(c, n.sons[i].sym, kind)
else: illFormedAst(n)
proc semBorrow(c: PContext, n: PNode, s: PSym) =
proc semBorrow(c: PContext, n: PNode, s: PSym) =
# search for the correct alias:
var b = searchForBorrowProc(c, c.currentScope.parent, s)
if b != nil:
if b != nil:
# store the alias:
n.sons[bodyPos] = newSymNode(b)
else:
localError(n.info, errNoSymbolToBorrowFromFound)
proc addResult(c: PContext, t: PType, info: TLineInfo, owner: TSymKind) =
if t != nil:
localError(n.info, errNoSymbolToBorrowFromFound)
proc addResult(c: PContext, t: PType, info: TLineInfo, owner: TSymKind) =
if t != nil:
var s = newSym(skResult, getIdent"result", getCurrOwner(), info)
s.typ = t
incl(s.flags, sfUsed)
addParamOrResult(c, s, owner)
c.p.resultSym = s
proc addResultNode(c: PContext, n: PNode) =
proc addResultNode(c: PContext, n: PNode) =
if c.p.resultSym != nil: addSon(n, newSymNode(c.p.resultSym))
proc copyExcept(n: PNode, i: int): PNode =
@@ -753,7 +796,8 @@ proc lookupMacro(c: PContext, n: PNode): PSym =
else:
result = searchInScopes(c, considerQuotedIdent(n), {skMacro, skTemplate})
proc semProcAnnotation(c: PContext, prc: PNode): PNode =
proc semProcAnnotation(c: PContext, prc: PNode;
validPragmas: TSpecialWords): PNode =
var n = prc.sons[pragmasPos]
if n == nil or n.kind == nkEmpty: return
for i in countup(0, <n.len):
@@ -778,12 +822,18 @@ proc semProcAnnotation(c: PContext, prc: PNode): PNode =
x.add(it.sons[1])
x.add(prc)
# recursion assures that this works for multiple macro annotations too:
return semStmt(c, x)
result = semStmt(c, x)
# since a proc annotation can set pragmas, we process these here again.
# This is required for SqueakNim-like export pragmas.
if result.kind in procDefs and result[namePos].kind == nkSym and
result[pragmasPos].kind != nkEmpty:
pragma(c, result[namePos].sym, result[pragmasPos], validPragmas)
return
proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
# XXX semProcAux should be good enough for this now, we will eventually
# remove semLambda
result = semProcAnnotation(c, n)
result = semProcAnnotation(c, n, lambdaPragmas)
if result != nil: return result
result = n
checkSonsLen(n, bodyPos + 1)
@@ -797,7 +847,7 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
pushOwner(s)
openScope(c)
var gp: PNode
if n.sons[genericParamsPos].kind != nkEmpty:
if n.sons[genericParamsPos].kind != nkEmpty:
n.sons[genericParamsPos] = semGenericParamList(c, n.sons[genericParamsPos])
gp = n.sons[genericParamsPos]
else:
@@ -813,8 +863,7 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
# we have a list of implicit type parameters:
n.sons[genericParamsPos] = gp
else:
s.typ = newTypeS(tyProc, c)
rawAddSon(s.typ, nil)
s.typ = newProcType(c, n.info)
if n.sons[pragmasPos].kind != nkEmpty:
pragma(c, s, n.sons[pragmasPos], lambdaPragmas)
s.options = gOptions
@@ -826,9 +875,9 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
if gp.len == 0 or (gp.len == 1 and tfRetType in gp[0].typ.flags):
pushProcCon(c, s)
addResult(c, s.typ.sons[0], n.info, skProc)
addResultNode(c, n)
let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos]))
n.sons[bodyPos] = transformBody(c.module, semBody, s)
addResultNode(c, n)
popProcCon(c)
elif efOperand notin flags:
localError(n.info, errGenericLambdaNotAllowed)
@@ -839,26 +888,35 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
popOwner()
result.typ = s.typ
proc semDo(c: PContext, n: PNode, flags: TExprFlags): PNode =
# 'do' without params produces a stmt:
if n[genericParamsPos].kind == nkEmpty and n[paramsPos].kind == nkEmpty:
result = semStmt(c, n[bodyPos])
else:
result = semLambda(c, n, flags)
proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode =
var n = n
n = replaceTypesInBody(c, pt, n)
result = n
n.sons[genericParamsPos] = emptyNode
n.sons[paramsPos] = n.typ.n
openScope(c)
var s = n.sons[namePos].sym
pushOwner(s)
addParams(c, n.typ.n, skProc)
pushProcCon(c, s)
addResult(c, n.typ.sons[0], n.info, skProc)
addResultNode(c, n)
let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos]))
n.sons[bodyPos] = transformBody(c.module, semBody, n.sons[namePos].sym)
addResultNode(c, n)
popProcCon(c)
popOwner()
closeScope(c)
s.ast = result
# alternative variant (not quite working):
@@ -887,11 +945,12 @@ proc maybeAddResult(c: PContext, s: PSym, n: PNode) =
proc semOverride(c: PContext, s: PSym, n: PNode) =
case s.name.s.normalize
of "destroy":
of "destroy", "=destroy":
doDestructorStuff(c, s, n)
if not experimentalMode(c):
localError n.info, "use the {.experimental.} pragma to enable destructors"
of "deepcopy":
incl(s.flags, sfUsed)
of "deepcopy", "=deepcopy":
if s.typ.len == 2 and
s.typ.sons[1].skipTypes(abstractInst).kind in {tyRef, tyPtr} and
sameType(s.typ.sons[1], s.typ.sons[0]):
@@ -900,11 +959,11 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
var t = s.typ.sons[1].skipTypes(abstractInst).lastSon.skipTypes(abstractInst)
while true:
if t.kind == tyGenericBody: t = t.lastSon
elif t.kind == tyGenericInvokation: t = t.sons[0]
elif t.kind == tyGenericInvocation: t = t.sons[0]
else: break
if t.kind in {tyObject, tyDistinct, tyEnum}:
if t.deepCopy.isNil: t.deepCopy = s
else:
else:
localError(n.info, errGenerated,
"cannot bind another 'deepCopy' to: " & typeToString(t))
else:
@@ -913,10 +972,35 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
else:
localError(n.info, errGenerated,
"signature for 'deepCopy' must be proc[T: ptr|ref](x: T): T")
of "=": discard
else: localError(n.info, errGenerated,
"'destroy' or 'deepCopy' expected for 'override'")
incl(s.flags, sfUsed)
incl(s.flags, sfUsed)
of "=":
incl(s.flags, sfUsed)
let t = s.typ
if t.len == 3 and t.sons[0] == nil and t.sons[1].kind == tyVar:
var obj = t.sons[1].sons[0]
while true:
incl(obj.flags, tfHasAsgn)
if obj.kind == tyGenericBody: obj = obj.lastSon
elif obj.kind == tyGenericInvocation: obj = obj.sons[0]
else: break
var objB = t.sons[2]
while true:
if objB.kind == tyGenericBody: objB = objB.lastSon
elif objB.kind == tyGenericInvocation: objB = objB.sons[0]
else: break
if obj.kind in {tyObject, tyDistinct} and sameType(obj, objB):
if obj.assignment.isNil:
obj.assignment = s
else:
localError(n.info, errGenerated,
"cannot bind another '=' to: " & typeToString(obj))
return
localError(n.info, errGenerated,
"signature for '=' must be proc[T: object](x: var T; y: T)")
else:
if sfOverriden in s.flags:
localError(n.info, errGenerated,
"'destroy' or 'deepCopy' expected for 'override'")
type
TProcCompilationSteps = enum
@@ -931,7 +1015,7 @@ proc isForwardDecl(s: PSym): bool =
proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
validPragmas: TSpecialWords,
phase = stepRegisterSymbol): PNode =
result = semProcAnnotation(c, n)
result = semProcAnnotation(c, n, validPragmas)
if result != nil: return result
result = n
checkSonsLen(n, bodyPos + 1)
@@ -948,7 +1032,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
s = semIdentDef(c, n.sons[0], kind)
n.sons[namePos] = newSymNode(s)
s.ast = n
s.scope = c.currentScope
#s.scope = c.currentScope
if sfNoForward in c.module.flags and
sfSystemModule notin c.module.flags:
@@ -960,40 +1044,39 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
s.owner = getCurrOwner()
typeIsDetermined = s.typ == nil
s.ast = n
s.scope = c.currentScope
#s.scope = c.currentScope
# if typeIsDetermined: assert phase == stepCompileBody
# else: assert phase == stepDetermineType
# before compiling the proc body, set as current the scope
# where the proc was declared
let oldScope = c.currentScope
c.currentScope = s.scope
#c.currentScope = s.scope
pushOwner(s)
openScope(c)
var gp: PNode
if n.sons[genericParamsPos].kind != nkEmpty:
if n.sons[genericParamsPos].kind != nkEmpty:
n.sons[genericParamsPos] = semGenericParamList(c, n.sons[genericParamsPos])
gp = n.sons[genericParamsPos]
else:
else:
gp = newNodeI(nkGenericParams, n.info)
# process parameters:
if n.sons[paramsPos].kind != nkEmpty:
semParamList(c, n.sons[paramsPos], gp, s)
if sonsLen(gp) > 0:
if sonsLen(gp) > 0:
if n.sons[genericParamsPos].kind == nkEmpty:
# we have a list of implicit type parameters:
n.sons[genericParamsPos] = gp
# check for semantics again:
# semParamList(c, n.sons[ParamsPos], nil, s)
else:
s.typ = newTypeS(tyProc, c)
rawAddSon(s.typ, nil)
s.typ = newProcType(c, n.info)
if n.sons[patternPos].kind != nkEmpty:
n.sons[patternPos] = semPattern(c, n.sons[patternPos])
if s.kind in skIterators:
s.typ.flags.incl(tfIterator)
var proto = searchForProc(c, s.scope, s)
var proto = searchForProc(c, oldScope, s)
if proto == nil:
if s.kind == skClosureIterator: s.typ.callConv = ccClosure
else: s.typ.callConv = lastOptionEntry(c).defaultCC
@@ -1001,23 +1084,23 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
if sfGenSym in s.flags: discard
elif kind in OverloadableSyms:
if not typeIsDetermined:
addInterfaceOverloadableSymAt(c, s.scope, s)
addInterfaceOverloadableSymAt(c, oldScope, s)
else:
if not typeIsDetermined:
addInterfaceDeclAt(c, s.scope, s)
addInterfaceDeclAt(c, oldScope, s)
if n.sons[pragmasPos].kind != nkEmpty:
pragma(c, s, n.sons[pragmasPos], validPragmas)
else:
implicitPragmas(c, s, n, validPragmas)
else:
if n.sons[pragmasPos].kind != nkEmpty:
if n.sons[pragmasPos].kind != nkEmpty:
localError(n.sons[pragmasPos].info, errPragmaOnlyInHeaderOfProc)
if sfForward notin proto.flags:
if sfForward notin proto.flags:
wrongRedefinition(n.info, proto.name.s)
excl(proto.flags, sfForward)
closeScope(c) # close scope with wrong parameter symbols
openScope(c) # open scope for old (correct) parameter symbols
if proto.ast.sons[genericParamsPos].kind != nkEmpty:
if proto.ast.sons[genericParamsPos].kind != nkEmpty:
addGenericParamListToScope(c, proto.ast.sons[genericParamsPos])
addParams(c, proto.typ.n, proto.kind)
proto.info = s.info # more accurate line information
@@ -1034,7 +1117,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
popOwner()
pushOwner(s)
s.options = gOptions
if sfOverriden in s.flags: semOverride(c, s, n)
if sfOverriden in s.flags or s.name.s[0] == '=': semOverride(c, s, n)
if n.sons[bodyPos].kind != nkEmpty:
# for DLL generation it is annoying to check for sfImportc!
if sfBorrow in s.flags:
@@ -1046,6 +1129,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
if n.sons[genericParamsPos].kind == nkEmpty or usePseudoGenerics:
if not usePseudoGenerics: paramsTypeCheck(c, s.typ)
pushProcCon(c, s)
c.p.wasForwarded = proto != nil
maybeAddResult(c, s, n)
if sfImportc notin s.flags:
# no semantic checking for importc:
@@ -1057,20 +1141,21 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
else:
if s.typ.sons[0] != nil and kind notin skIterators:
addDecl(c, newSym(skUnknown, getIdent"result", nil, n.info))
var toBind = initIntSet()
n.sons[bodyPos] = semGenericStmtScope(c, n.sons[bodyPos], {}, toBind)
openScope(c)
n.sons[bodyPos] = semGenericStmt(c, n.sons[bodyPos])
closeScope(c)
fixupInstantiatedSymbols(c, s)
if sfImportc in s.flags:
# so we just ignore the body after semantic checking for importc:
n.sons[bodyPos] = ast.emptyNode
else:
if proto != nil: localError(n.info, errImplOfXexpected, proto.name.s)
if {sfImportc, sfBorrow} * s.flags == {} and s.magic == mNone:
if {sfImportc, sfBorrow} * s.flags == {} and s.magic == mNone:
incl(s.flags, sfForward)
elif sfBorrow in s.flags: semBorrow(c, n, s)
sideEffectsCheck(c, s)
closeScope(c) # close scope for parameters
c.currentScope = oldScope
# c.currentScope = oldScope
popOwner()
if n.sons[patternPos].kind != nkEmpty:
c.patterns.add(s)
@@ -1103,14 +1188,14 @@ proc semIterator(c: PContext, n: PNode): PNode =
else:
s.typ.callConv = ccInline
when false:
if s.typ.callConv != ccInline:
if s.typ.callConv != ccInline:
s.typ.callConv = ccClosure
# and they always at least use the 'env' for the state field:
incl(s.typ.flags, tfCapturesEnv)
if n.sons[bodyPos].kind == nkEmpty and s.magic == mNone:
localError(n.info, errImplOfXexpected, s.name.s)
proc semProc(c: PContext, n: PNode): PNode =
proc semProc(c: PContext, n: PNode): PNode =
result = semProcAux(c, n, skProc, procPragmas)
proc hasObjParam(s: PSym): bool =
@@ -1123,18 +1208,21 @@ proc finishMethod(c: PContext, s: PSym) =
if hasObjParam(s):
methodDef(s, false)
proc semMethod(c: PContext, n: PNode): PNode =
proc semMethod(c: PContext, n: PNode): PNode =
if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "method")
result = semProcAux(c, n, skMethod, methodPragmas)
var s = result.sons[namePos].sym
if not isGenericRoutine(s) and result.sons[bodyPos].kind != nkEmpty:
if not isGenericRoutine(s):
# why check for the body? bug #2400 has none. Checking for sfForward makes
# no sense either.
# and result.sons[bodyPos].kind != nkEmpty:
if hasObjParam(s):
methodDef(s, fromCache=false)
else:
localError(n.info, errXNeedsParamObjectType, "method")
proc semConverterDef(c: PContext, n: PNode): PNode =
proc semConverterDef(c: PContext, n: PNode): PNode =
if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "converter")
checkSonsLen(n, bodyPos + 1)
result = semProcAux(c, n, skConverter, converterPragmas)
@@ -1144,7 +1232,7 @@ proc semConverterDef(c: PContext, n: PNode): PNode =
if sonsLen(t) != 2: localError(n.info, errXRequiresOneArgument, "converter")
addConverter(c, s)
proc semMacroDef(c: PContext, n: PNode): PNode =
proc semMacroDef(c: PContext, n: PNode): PNode =
checkSonsLen(n, bodyPos + 1)
result = semProcAux(c, n, skMacro, macroPragmas)
var s = result.sons[namePos].sym
@@ -1152,23 +1240,23 @@ proc semMacroDef(c: PContext, n: PNode): PNode =
if t.sons[0] == nil: localError(n.info, errXNeedsReturnType, "macro")
if n.sons[bodyPos].kind == nkEmpty:
localError(n.info, errImplOfXexpected, s.name.s)
proc evalInclude(c: PContext, n: PNode): PNode =
result = newNodeI(nkStmtList, n.info)
addSon(result, n)
for i in countup(0, sonsLen(n) - 1):
for i in countup(0, sonsLen(n) - 1):
var f = checkModuleName(n.sons[i])
if f != InvalidFileIDX:
if containsOrIncl(c.includedFiles, f):
if containsOrIncl(c.includedFiles, f):
localError(n.info, errRecursiveDependencyX, f.toFilename)
else:
addSon(result, semStmt(c, gIncludeFile(c.module, f)))
excl(c.includedFiles, f)
proc setLine(n: PNode, info: TLineInfo) =
for i in 0 .. <safeLen(n): setLine(n.sons[i], info)
n.info = info
proc semPragmaBlock(c: PContext, n: PNode): PNode =
let pragmaList = n.sons[0]
pragma(c, nil, pragmaList, exprPragmas)
@@ -1177,12 +1265,14 @@ proc semPragmaBlock(c: PContext, n: PNode): PNode =
for i in 0 .. <pragmaList.len:
case whichPragma(pragmaList.sons[i])
of wLine: setLine(result, pragmaList.sons[i].info)
of wLocks:
of wLocks:
result = n
result.typ = n.sons[1].typ
else: discard
proc semStaticStmt(c: PContext, n: PNode): PNode =
#echo "semStaticStmt"
#writeStackTrace()
let a = semStmt(c, n.sons[0])
n.sons[0] = a
evalStaticStmt(c.module, a, c.p.owner)
@@ -1200,7 +1290,8 @@ proc semStaticStmt(c: PContext, n: PNode): PNode =
proc usesResult(n: PNode): bool =
# nkStmtList(expr) properly propagates the void context,
# so we don't need to process that all over again:
if n.kind notin {nkStmtList, nkStmtListExpr} + procDefs:
if n.kind notin {nkStmtList, nkStmtListExpr,
nkMacroDef, nkTemplateDef} + procDefs:
if isAtom(n):
result = n.kind == nkSym and n.sym.kind == skResult
elif n.kind == nkReturnStmt:
@@ -1252,7 +1343,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
var tryStmt = newNodeI(nkTryStmt, n.sons[i].info)
var body = newNodeI(nkStmtList, n.sons[i].info)
if i < n.sonsLen - 1:
body.sons = n.sons[(i+1)..(-1)]
body.sons = n.sons[(i+1)..n.len-1]
tryStmt.addSon(body)
tryStmt.addSon(deferPart)
n.sons[i] = semTry(c, tryStmt)
@@ -1261,10 +1352,14 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
return
else:
n.sons[i] = semExpr(c, n.sons[i])
if c.inTypeClass > 0 and n[i].typ != nil and n[i].typ.kind == tyBool:
let verdict = semConstExpr(c, n[i])
if verdict.intVal == 0:
localError(result.info, "type class predicate failed")
if c.inTypeClass > 0 and n[i].typ != nil:
case n[i].typ.kind
of tyBool:
let verdict = semConstExpr(c, n[i])
if verdict.intVal == 0:
localError(result.info, "type class predicate failed")
of tyUnknown: continue
else: discard
if n.sons[i].typ == enforceVoidContext or usesResult(n.sons[i]):
voidContext = true
n.typ = enforceVoidContext
@@ -1285,8 +1380,8 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
inner.addSon(semStmtList(c, rest, flags))
n.sons.setLen(i+1)
return
of LastBlockStmts:
for j in countup(i + 1, length - 1):
of LastBlockStmts:
for j in countup(i + 1, length - 1):
case n.sons[j].kind
of nkPragma, nkCommentStmt, nkNilLit, nkEmpty: discard
else: localError(n.sons[j].info, errStmtInvalidAfterReturn)
@@ -1308,7 +1403,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
# "Last expression must be explicitly returned if it " &
# "is discardable or discarded")
proc semStmt(c: PContext, n: PNode): PNode =
proc semStmt(c: PContext, n: PNode): PNode =
# now: simply an alias:
result = semExprNoType(c, n)

View File

@@ -1,7 +1,7 @@
#
#
# The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -10,17 +10,17 @@
# included from sem.nim
discard """
hygienic templates:
hygienic templates:
template `||` (a, b: expr): expr =
let aa = a
if aa: aa else: b
var
a, b: T
a || b || a
Each evaluation context has to be different and we need to perform
some form of preliminary symbol lookup in template definitions. Hygiene is
a way to achieve lexical scoping at compile time.
@@ -50,7 +50,7 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule): PNode =
o: TOverloadIter
var i = 0
a = initOverloadIter(o, c, n)
while a != nil:
while a != nil:
a = nextOverloadIter(o, c, n)
inc(i)
if i > 1: break
@@ -96,7 +96,7 @@ proc semMixinStmt(c: PContext, n: PNode, toMixin: var IntSet): PNode =
for i in 0 .. < n.len:
toMixin.incl(considerQuotedIdent(n.sons[i]).id)
result = newNodeI(nkEmpty, n.info)
proc replaceIdentBySym(n: var PNode, s: PNode) =
case n.kind
of nkPostfix: replaceIdentBySym(n.sons[1], s)
@@ -105,10 +105,11 @@ proc replaceIdentBySym(n: var PNode, s: PNode) =
else: illFormedAst(n)
type
TemplCtx {.pure, final.} = object
TemplCtx = object
c: PContext
toBind, toMixin, toInject: IntSet
owner: PSym
cursorInBody: bool # only for nimsuggest
proc getIdentNode(c: var TemplCtx, n: PNode): PNode =
case n.kind
@@ -127,14 +128,14 @@ proc getIdentNode(c: var TemplCtx, n: PNode): PNode =
proc isTemplParam(c: TemplCtx, n: PNode): bool {.inline.} =
result = n.kind == nkSym and n.sym.kind == skParam and
n.sym.owner == c.owner
n.sym.owner == c.owner and sfGenSym notin n.sym.flags
proc semTemplBody(c: var TemplCtx, n: PNode): PNode
proc openScope(c: var TemplCtx) = openScope(c.c)
proc closeScope(c: var TemplCtx) = closeScope(c.c)
proc semTemplBodyScope(c: var TemplCtx, n: PNode): PNode =
proc semTemplBodyScope(c: var TemplCtx, n: PNode): PNode =
openScope(c)
result = semTemplBody(c, n)
closeScope(c)
@@ -190,24 +191,24 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) =
else:
replaceIdentBySym(n, ident)
proc semTemplSymbol(c: PContext, n: PNode, s: PSym): PNode =
proc semTemplSymbol(c: PContext, n: PNode, s: PSym): PNode =
incl(s.flags, sfUsed)
# we do not call styleCheckUse here, as the identifier is not really
# resolved here. We will fixup the used identifiers later.
case s.kind
of skUnknown:
of skUnknown:
# Introduced in this pass! Leave it as an identifier.
result = n
of OverloadableSyms:
result = symChoice(c, n, s, scOpen)
of skGenericParam:
of skGenericParam:
result = newSymNodeTypeDesc(s, n.info)
of skParam:
of skParam:
result = n
of skType:
if (s.typ != nil) and (s.typ.kind != tyGenericParam):
of skType:
if (s.typ != nil) and (s.typ.kind != tyGenericParam):
result = newSymNodeTypeDesc(s, n.info)
else:
else:
result = n
else:
result = newSymNode(s, n.info)
@@ -246,8 +247,8 @@ proc semRoutineInTemplBody(c: var TemplCtx, n: PNode, k: TSymKind): PNode =
n.sons[i] = semTemplBody(c, n.sons[i])
closeScope(c)
proc semTemplSomeDecl(c: var TemplCtx, n: PNode, symKind: TSymKind) =
for i in countup(0, sonsLen(n) - 1):
proc semTemplSomeDecl(c: var TemplCtx, n: PNode, symKind: TSymKind; start=0) =
for i in countup(start, sonsLen(n) - 1):
var a = n.sons[i]
if a.kind == nkCommentStmt: continue
if (a.kind != nkIdentDefs) and (a.kind != nkVarTuple): illFormedAst(a)
@@ -259,8 +260,9 @@ proc semTemplSomeDecl(c: var TemplCtx, n: PNode, symKind: TSymKind) =
addLocalDecl(c, a.sons[j], symKind)
proc semPattern(c: PContext, n: PNode): PNode
proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
result = n
semIdeForTemplateOrGenericCheck(n, c.cursorInBody)
case n.kind
of nkIdent:
if n.ident.id in c.toInject: return n
@@ -303,21 +305,21 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
n.sons[i] = semTemplBodyScope(c, it)
of nkWhileStmt:
openScope(c)
for i in countup(0, sonsLen(n)-1):
for i in countup(0, sonsLen(n)-1):
n.sons[i] = semTemplBody(c, n.sons[i])
closeScope(c)
of nkCaseStmt:
openScope(c)
n.sons[0] = semTemplBody(c, n.sons[0])
for i in countup(1, sonsLen(n)-1):
for i in countup(1, sonsLen(n)-1):
var a = n.sons[i]
checkMinSonsLen(a, 1)
var L = sonsLen(a)
for j in countup(0, L-2):
for j in countup(0, L-2):
a.sons[j] = semTemplBody(c, a.sons[j])
a.sons[L-1] = semTemplBodyScope(c, a.sons[L-1])
closeScope(c)
of nkForStmt, nkParForStmt:
of nkForStmt, nkParForStmt:
var L = sonsLen(n)
openScope(c)
n.sons[L-2] = semTemplBody(c, n.sons[L-2])
@@ -336,45 +338,49 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
n.sons[0] = newSymNode(s, n.sons[0].info)
n.sons[1] = semTemplBody(c, n.sons[1])
closeScope(c)
of nkTryStmt:
of nkTryStmt:
checkMinSonsLen(n, 2)
n.sons[0] = semTemplBodyScope(c, n.sons[0])
for i in countup(1, sonsLen(n)-1):
for i in countup(1, sonsLen(n)-1):
var a = n.sons[i]
checkMinSonsLen(a, 1)
var L = sonsLen(a)
for j in countup(0, L-2):
for j in countup(0, L-2):
a.sons[j] = semTemplBody(c, a.sons[j])
a.sons[L-1] = semTemplBodyScope(c, a.sons[L-1])
of nkVarSection: semTemplSomeDecl(c, n, skVar)
of nkLetSection: semTemplSomeDecl(c, n, skLet)
of nkFormalParams:
checkMinSonsLen(n, 1)
n.sons[0] = semTemplBody(c, n.sons[0])
semTemplSomeDecl(c, n, skParam, 1)
of nkConstSection:
for i in countup(0, sonsLen(n) - 1):
for i in countup(0, sonsLen(n) - 1):
var a = n.sons[i]
if a.kind == nkCommentStmt: continue
if a.kind == nkCommentStmt: continue
if (a.kind != nkConstDef): illFormedAst(a)
checkSonsLen(a, 3)
addLocalDecl(c, a.sons[0], skConst)
a.sons[1] = semTemplBody(c, a.sons[1])
a.sons[2] = semTemplBody(c, a.sons[2])
of nkTypeSection:
for i in countup(0, sonsLen(n) - 1):
of nkTypeSection:
for i in countup(0, sonsLen(n) - 1):
var a = n.sons[i]
if a.kind == nkCommentStmt: continue
if a.kind == nkCommentStmt: continue
if (a.kind != nkTypeDef): illFormedAst(a)
checkSonsLen(a, 3)
addLocalDecl(c, a.sons[0], skType)
for i in countup(0, sonsLen(n) - 1):
var a = n.sons[i]
if a.kind == nkCommentStmt: continue
if a.kind == nkCommentStmt: continue
if (a.kind != nkTypeDef): illFormedAst(a)
checkSonsLen(a, 3)
if a.sons[1].kind != nkEmpty:
if a.sons[1].kind != nkEmpty:
openScope(c)
a.sons[1] = semTemplBody(c, a.sons[1])
a.sons[2] = semTemplBody(c, a.sons[2])
closeScope(c)
else:
else:
a.sons[2] = semTemplBody(c, a.sons[2])
of nkProcDef, nkLambdaKinds:
result = semRoutineInTemplBody(c, n, skProc)
@@ -402,7 +408,13 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
if n.kind == nkDotExpr or n.kind == nkAccQuoted:
let s = qualifiedLookUp(c.c, n, {})
if s != nil:
if contains(c.toBind, s.id):
# do not symchoice a quoted template parameter (bug #2390):
if s.owner == c.owner and s.kind == skParam and
n.kind == nkAccQuoted and n.len == 1:
incl(s.flags, sfUsed)
styleCheckUse(n.info, s)
return newSymNode(s, n.info)
elif contains(c.toBind, s.id):
return symChoice(c.c, n, s, scClosed)
elif contains(c.toMixin, s.name.id):
return symChoice(c.c, n, s, scForceOpen)
@@ -412,8 +424,9 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
for i in countup(0, sonsLen(n) - 1):
result.sons[i] = semTemplBody(c, n.sons[i])
proc semTemplBodyDirty(c: var TemplCtx, n: PNode): PNode =
proc semTemplBodyDirty(c: var TemplCtx, n: PNode): PNode =
result = n
semIdeForTemplateOrGenericCheck(n, c.cursorInBody)
case n.kind
of nkIdent:
let s = qualifiedLookUp(c.c, n, {})
@@ -429,7 +442,7 @@ proc semTemplBodyDirty(c: var TemplCtx, n: PNode): PNode =
of nkEmpty, nkSym..nkNilLit:
discard
else:
# dotExpr is ambiguous: note that we explicitely allow 'x.TemplateParam',
# dotExpr is ambiguous: note that we explicitly allow 'x.TemplateParam',
# so we use the generic code for nkDotExpr too
if n.kind == nkDotExpr or n.kind == nkAccQuoted:
let s = qualifiedLookUp(c.c, n, {})
@@ -438,38 +451,38 @@ proc semTemplBodyDirty(c: var TemplCtx, n: PNode): PNode =
result = n
for i in countup(0, sonsLen(n) - 1):
result.sons[i] = semTemplBodyDirty(c, n.sons[i])
proc transformToExpr(n: PNode): PNode =
proc transformToExpr(n: PNode): PNode =
var realStmt: int
result = n
case n.kind
of nkStmtList:
of nkStmtList:
realStmt = - 1
for i in countup(0, sonsLen(n) - 1):
for i in countup(0, sonsLen(n) - 1):
case n.sons[i].kind
of nkCommentStmt, nkEmpty, nkNilLit:
of nkCommentStmt, nkEmpty, nkNilLit:
discard
else:
else:
if realStmt == - 1: realStmt = i
else: realStmt = - 2
if realStmt >= 0: result = transformToExpr(n.sons[realStmt])
else: n.kind = nkStmtListExpr
of nkBlockStmt:
of nkBlockStmt:
n.kind = nkBlockExpr
#nkIfStmt: n.kind = nkIfExpr // this is not correct!
else:
discard
proc semTemplateDef(c: PContext, n: PNode): PNode =
proc semTemplateDef(c: PContext, n: PNode): PNode =
var s: PSym
if c.p.owner.kind == skModule:
if c.p.owner.kind == skModule:
s = semIdentVis(c, skTemplate, n.sons[0], {sfExported})
incl(s.flags, sfGlobal)
else:
s = semIdentVis(c, skTemplate, n.sons[0], {})
styleCheckDef(s)
# check parameter list:
s.scope = c.currentScope
#s.scope = c.currentScope
pushOwner(s)
openScope(c)
n.sons[namePos] = newSymNode(s, n.sons[namePos].info)
@@ -477,14 +490,19 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
pragma(c, s, n.sons[pragmasPos], templatePragmas)
var gp: PNode
if n.sons[genericParamsPos].kind != nkEmpty:
if n.sons[genericParamsPos].kind != nkEmpty:
n.sons[genericParamsPos] = semGenericParamList(c, n.sons[genericParamsPos])
gp = n.sons[genericParamsPos]
else:
else:
gp = newNodeI(nkGenericParams, n.info)
# process parameters:
if n.sons[paramsPos].kind != nkEmpty:
semParamList(c, n.sons[paramsPos], gp, s)
# a template's parameters are not gensym'ed even if that was originally the
# case as we determine whether it's a template parameter in the template
# body by the absence of the sfGenSym flag:
for i in 1 .. s.typ.n.len-1:
s.typ.n.sons[i].sym.flags.excl sfGenSym
if sonsLen(gp) > 0:
if n.sons[genericParamsPos].kind == nkEmpty:
# we have a list of implicit type parameters:
@@ -513,13 +531,14 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
else:
n.sons[bodyPos] = semTemplBody(ctx, n.sons[bodyPos])
if s.typ.sons[0].kind notin {tyStmt, tyTypeDesc}:
n.sons[bodyPos] = transformToExpr(n.sons[bodyPos])
n.sons[bodyPos] = transformToExpr(n.sons[bodyPos])
# only parameters are resolved, no type checking is performed
semIdeForTemplateOrGeneric(c, n.sons[bodyPos], ctx.cursorInBody)
closeScope(c)
popOwner()
s.ast = n
result = n
if n.sons[bodyPos].kind == nkEmpty:
if n.sons[bodyPos].kind == nkEmpty:
localError(n.info, errImplOfXexpected, s.name.s)
var proto = searchForProc(c, c.currentScope, s)
if proto == nil:
@@ -532,7 +551,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
template templToExpand(s: expr): expr =
s.kind == skTemplate and (s.typ.len == 1 or sfImmediate in s.flags)
proc newParam(c: var TemplCtx, n: PNode, s: PSym): PNode =
# the param added in the current scope is actually wrong here for
# macros because they have a shadowed param of type 'PNimNode' (see
@@ -543,7 +562,7 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
let x = c.owner.typ.n.sons[s.position+1].sym
assert x.name == s.name
result = newSymNode(x, n.info)
proc handleSym(c: var TemplCtx, n: PNode, s: PSym): PNode =
result = n
if s != nil:
@@ -557,7 +576,7 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
discard
# we keep the ident unbound for matching instantiated symbols and
# more flexibility
proc expectParam(c: var TemplCtx, n: PNode): PNode =
let s = qualifiedLookUp(c.c, n, {})
if s != nil and s.owner == c.owner and s.kind == skParam:
@@ -565,7 +584,7 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
else:
localError(n.info, errInvalidExpression)
result = n
result = n
case n.kind
of nkIdent:
@@ -575,7 +594,7 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
result = semBindStmt(c.c, n, c.toBind)
of nkEmpty, nkSym..nkNilLit: discard
of nkCurlyExpr:
# we support '(pattern){x}' to bind a subpattern to a parameter 'x';
# we support '(pattern){x}' to bind a subpattern to a parameter 'x';
# '(pattern){|x}' does the same but the matches will be gathered in 'x'
if n.len != 2:
localError(n.info, errInvalidExpression)
@@ -598,7 +617,7 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
elif contains(c.toBind, s.id): discard
elif templToExpand(s):
return semPatternBody(c, semTemplateExpr(c.c, n, s, {efNoSemCheck}))
if n.kind == nkInfix and n.sons[0].kind == nkIdent:
# we interpret `*` and `|` only as pattern operators if they occur in
# infix notation, so that '`*`(a, b)' can be used for verbatim matching:
@@ -615,7 +634,7 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
result.sons[1] = semPatternBody(c, n.sons[1])
result.sons[2] = semPatternBody(c, n.sons[2])
return
if n.kind == nkPrefix and n.sons[0].kind == nkIdent:
let opr = n.sons[0]
if opr.ident.s == "~":
@@ -623,13 +642,13 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
result.sons[0] = opr
result.sons[1] = semPatternBody(c, n.sons[1])
return
for i in countup(0, sonsLen(n) - 1):
result.sons[i] = semPatternBody(c, n.sons[i])
else:
# dotExpr is ambiguous: note that we explicitely allow 'x.TemplateParam',
# dotExpr is ambiguous: note that we explicitly allow 'x.TemplateParam',
# so we use the generic code for nkDotExpr too
case n.kind
case n.kind
of nkDotExpr, nkAccQuoted:
let s = qualifiedLookUp(c.c, n, {})
if s != nil:

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
#
#
# The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -16,9 +16,9 @@ const
proc sharedPtrCheck(info: TLineInfo, t: PType) =
if t.kind == tyPtr and t.len > 1:
if t.sons[0].sym.magic in {mShared, mGuarded}:
if t.sons[0].sym.magic == mShared:
incl(t.flags, tfShared)
if t.sons[0].sym.magic == mGuarded: incl(t.flags, tfGuarded)
#if t.sons[0].sym.magic == mGuarded: incl(t.flags, tfGuarded)
if tfHasGCedMem in t.flags or t.isGCedMem:
localError(info, errGenerated,
"shared memory may not refer to GC'ed thread local memory")
@@ -34,9 +34,9 @@ proc checkPartialConstructedType(info: TLineInfo, t: PType) =
proc checkConstructedType*(info: TLineInfo, typ: PType) =
var t = typ.skipTypes({tyDistinct})
if t.kind in tyTypeClasses: discard
elif tfAcyclic in t.flags and skipTypes(t, abstractInst).kind != tyObject:
elif tfAcyclic in t.flags and skipTypes(t, abstractInst).kind != tyObject:
localError(info, errInvalidPragmaX, "acyclic")
elif t.kind == tyVar and t.sons[0].kind == tyVar:
elif t.kind == tyVar and t.sons[0].kind == tyVar:
localError(info, errVarVarTypeNotAllowed)
elif computeSize(t) == szIllegalRecursion:
localError(info, errIllegalRecursionInTypeX, typeToString(t))
@@ -44,7 +44,7 @@ proc checkConstructedType*(info: TLineInfo, typ: PType) =
sharedPtrCheck(info, t)
when false:
if t.kind == tyObject and t.sons[0] != nil:
if t.sons[0].kind != tyObject or tfFinal in t.sons[0].flags:
if t.sons[0].kind != tyObject or tfFinal in t.sons[0].flags:
localError(info, errInheritanceOnlyWithNonFinalObjects)
proc searchInstTypes*(key: PType): PType =
@@ -61,7 +61,7 @@ proc searchInstTypes*(key: PType): PType =
if inst.sons.len < key.sons.len:
# XXX: This happens for prematurely cached
# types such as TChannel[empty]. Why?
# See the notes for PActor in handleGenericInvokation
# See the notes for PActor in handleGenericInvocation
return
block matchType:
for j in 1 .. high(key.sons):
@@ -69,7 +69,7 @@ proc searchInstTypes*(key: PType): PType =
if not compareTypes(inst.sons[j], key.sons[j],
flags = {ExactGenericParams}):
break matchType
return inst
proc cacheTypeInst*(inst: PType) =
@@ -79,7 +79,7 @@ proc cacheTypeInst*(inst: PType) =
genericTyp.sym.typeInstCache.safeAdd(inst)
type
TReplTypeVars* {.final.} = object
TReplTypeVars* {.final.} = object
c*: PContext
typeMap*: TIdTable # map PType to PType
symMap*: TIdTable # map PSym to PSym
@@ -89,6 +89,7 @@ type
info*: TLineInfo
allowMetaTypes*: bool # allow types such as seq[Number]
# i.e. the result contains unresolved generics
skipTypedesc*: bool # wether we should skip typeDescs
proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType
proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym
@@ -101,7 +102,6 @@ template checkMetaInvariants(cl: TReplTypeVars, t: PType) =
echo "UNEXPECTED META ", t.id, " ", instantiationInfo(-1)
debug t
writeStackTrace()
quit 1
proc replaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType =
result = replaceTypeVarsTAux(cl, t)
@@ -152,7 +152,7 @@ proc reResolveCallsWithTypedescParams(cl: var TReplTypeVars, n: PNode): PNode =
if needsFixing:
n.sons[0] = newSymNode(n.sons[0].sym.owner)
return cl.c.semOverloadedCall(cl.c, n, n, {skProc})
for i in 0 .. <n.safeLen:
n.sons[i] = reResolveCallsWithTypedescParams(cl, n[i])
@@ -204,18 +204,18 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode): PNode =
newSons(result, length)
for i in countup(0, length - 1):
result.sons[i] = replaceTypeVarsN(cl, n.sons[i])
proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym =
proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym =
if s == nil: return nil
result = PSym(idTableGet(cl.symMap, s))
if result == nil:
if result == nil:
result = copySym(s, false)
incl(result.flags, sfFromGeneric)
idTablePut(cl.symMap, s, result)
result.owner = s.owner
result.typ = replaceTypeVarsT(cl, s.typ)
result.ast = replaceTypeVarsN(cl, s.ast)
proc lookupTypeVar(cl: var TReplTypeVars, t: PType): PType =
result = PType(idTableGet(cl.typeMap, t))
if result == nil:
@@ -224,7 +224,7 @@ proc lookupTypeVar(cl: var TReplTypeVars, t: PType): PType =
result = errorType(cl.c)
# In order to prevent endless recursions, we must remember
# this bad lookup and replace it with errorType everywhere.
# These code paths are only active in nimrod check
# These code paths are only active in "nim check"
idTablePut(cl.typeMap, t, result)
elif result.kind == tyGenericParam and not cl.allowMetaTypes:
internalError(cl.info, "substitution with generic parameter")
@@ -233,11 +233,13 @@ proc instCopyType*(cl: var TReplTypeVars, t: PType): PType =
# XXX: relying on allowMetaTypes is a kludge
result = copyType(t, t.owner, cl.allowMetaTypes)
result.flags.incl tfFromGeneric
result.flags.excl tfInstClearedFlags
if not (t.kind in tyMetaTypes or
(t.kind == tyStatic and t.n == nil)):
result.flags.excl tfInstClearedFlags
proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
# tyGenericInvokation[A, tyGenericInvokation[A, B]]
# is difficult to handle:
proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
# tyGenericInvocation[A, tyGenericInvocation[A, B]]
# is difficult to handle:
var body = t.sons[0]
if body.kind != tyGenericBody: internalError(cl.info, "no generic body")
var header: PType = t
@@ -246,7 +248,7 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
result = PType(idTableGet(cl.localCache, t))
else:
result = searchInstTypes(t)
if result != nil: return
if result != nil and eqTypeFlags*result.flags == eqTypeFlags*t.flags: return
for i in countup(1, sonsLen(t) - 1):
var x = t.sons[i]
if x.kind == tyGenericParam:
@@ -257,14 +259,14 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
propagateToOwner(header, x)
else:
propagateToOwner(header, x)
if header != t:
# search again after first pass:
result = searchInstTypes(header)
if result != nil: return
if result != nil and eqTypeFlags*result.flags == eqTypeFlags*t.flags: return
else:
header = instCopyType(cl, t)
result = newType(tyGenericInst, t.sons[0].owner)
result.flags = header.flags
# be careful not to propagate unnecessary flags here (don't use rawAddSon)
@@ -277,26 +279,29 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
else:
idTablePut(cl.localCache, t, result)
let oldSkipTypedesc = cl.skipTypedesc
cl.skipTypedesc = true
for i in countup(1, sonsLen(t) - 1):
var x = replaceTypeVarsT(cl, t.sons[i])
assert x.kind != tyGenericInvokation
assert x.kind != tyGenericInvocation
header.sons[i] = x
propagateToOwner(header, x)
idTablePut(cl.typeMap, body.sons[i-1], x)
for i in countup(1, sonsLen(t) - 1):
# if one of the params is not concrete, we cannot do anything
# but we already raised an error!
rawAddSon(result, header.sons[i])
var newbody = replaceTypeVarsT(cl, lastSon(body))
cl.skipTypedesc = oldSkipTypedesc
newbody.flags = newbody.flags + (t.flags + body.flags - tfInstClearedFlags)
result.flags = result.flags + newbody.flags - tfInstClearedFlags
# This is actually wrong: tgeneric_closure fails with this line:
#newbody.callConv = body.callConv
# This type may be a generic alias and we want to resolve it here.
# One step is enough, because the recursive nature of
# handleGenericInvokation will handle the alias-to-alias-to-alias case
# handleGenericInvocation will handle the alias-to-alias-to-alias case
if newbody.isGenericAlias: newbody = newbody.skipGenericAlias
rawAddSon(result, newbody)
checkPartialConstructedType(cl.info, newbody)
@@ -304,14 +309,20 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
if dc != nil and sfFromGeneric notin newbody.deepCopy.flags:
# 'deepCopy' needs to be instantiated for
# generics *when the type is constructed*:
newbody.deepCopy = cl.c.instDeepCopy(cl.c, dc, result, cl.info)
newbody.deepCopy = cl.c.instTypeBoundOp(cl.c, dc, result, cl.info,
attachedDeepCopy)
let asgn = newbody.assignment
if asgn != nil and sfFromGeneric notin asgn.flags:
# '=' needs to be instantiated for generics when the type is constructed:
newbody.assignment = cl.c.instTypeBoundOp(cl.c, asgn, result, cl.info,
attachedAsgn)
proc eraseVoidParams*(t: PType) =
# transform '(): void' into '()' because old parts of the compiler really
# don't deal with '(): void':
if t.sons[0] != nil and t.sons[0].kind == tyEmpty:
t.sons[0] = nil
for i in 1 .. <t.sonsLen:
# don't touch any memory unless necessary
if t.sons[i].kind == tyEmpty:
@@ -333,7 +344,7 @@ proc skipIntLiteralParams*(t: PType) =
if skipped != p:
t.sons[i] = skipped
if i > 0: t.n.sons[i].sym.typ = skipped
# when the typeof operator is used on a static input
# param, the results gets infected with static as well:
if t.sons[0] != nil and t.sons[0].kind == tyStatic:
@@ -342,6 +353,8 @@ proc skipIntLiteralParams*(t: PType) =
proc propagateFieldFlags(t: PType, n: PNode) =
# This is meant for objects and tuples
# The type must be fully instantiated!
if n.isNil:
return
internalAssert n.kind != nkRecWhen
case n.kind
of nkSym:
@@ -358,10 +371,10 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
if t.kind in {tyStatic, tyGenericParam, tyIter} + tyTypeClasses:
let lookup = PType(idTableGet(cl.typeMap, t))
if lookup != nil: return lookup
case t.kind
of tyGenericInvokation:
result = handleGenericInvokation(cl, t)
of tyGenericInvocation:
result = handleGenericInvocation(cl, t)
of tyGenericBody:
localError(cl.info, errCannotInstantiateX, typeToString(t))
@@ -370,8 +383,10 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
of tyFromExpr:
if cl.allowMetaTypes: return
assert t.n.typ != t
var n = prepareNode(cl, t.n)
n = cl.c.semConstExpr(cl.c, n)
if n.kind != nkEmpty:
n = cl.c.semConstExpr(cl.c, n)
if n.typ.kind == tyTypeDesc:
# XXX: sometimes, chained typedescs enter here.
# It may be worth investigating why this is happening,
@@ -390,51 +405,58 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
else:
result = n.typ
of tyInt:
of tyInt, tyFloat:
result = skipIntLit(t)
# XXX now there are also float literals
of tyTypeDesc:
let lookup = PType(idTableGet(cl.typeMap, t)) # lookupTypeVar(cl, t)
if lookup != nil:
result = lookup
if tfUnresolved in t.flags: result = result.base
if tfUnresolved in t.flags or cl.skipTypedesc: result = result.base
elif t.sons[0].kind != tyNone:
result = makeTypeDesc(cl.c, replaceTypeVarsT(cl, t.sons[0]))
of tyUserTypeClass:
result = t
of tyGenericInst:
result = PType(idTableGet(cl.localCache, t))
if result != nil: return result
result = instCopyType(cl, t)
idTablePut(cl.localCache, t, result)
for i in 1 .. <result.sonsLen:
result.sons[i] = replaceTypeVarsT(cl, result.sons[i])
propagateToOwner(result, result.lastSon)
else:
if containsGenericType(t):
#if not cl.allowMetaTypes:
result = PType(idTableGet(cl.localCache, t))
if result != nil: return result
result = instCopyType(cl, t)
result.size = -1 # needs to be recomputed
#if not cl.allowMetaTypes:
idTablePut(cl.localCache, t, result)
for i in countup(0, sonsLen(result) - 1):
if result.sons[i] != nil:
result.sons[i] = replaceTypeVarsT(cl, result.sons[i])
propagateToOwner(result, result.sons[i])
result.n = replaceTypeVarsN(cl, result.n)
case result.kind
of tyArray:
let idx = result.sons[0]
internalAssert idx.kind != tyStatic
of tyObject, tyTuple:
propagateFieldFlags(result, result.n)
of tyProc:
eraseVoidParams(result)
skipIntLiteralParams(result)
else: discard
proc initTypeVars*(p: PContext, pt: TIdTable, info: TLineInfo): TReplTypeVars =
@@ -449,7 +471,7 @@ proc replaceTypesInBody*(p: PContext, pt: TIdTable, n: PNode): PNode =
pushInfoContext(n.info)
result = replaceTypeVarsN(cl, n)
popInfoContext()
proc generateTypeInstance*(p: PContext, pt: TIdTable, info: TLineInfo,
t: PType): PType =
var cl = initTypeVars(p, pt, info)

View File

@@ -1,7 +1,7 @@
#
#
# The Nim Compiler
# (c) Copyright 2012 Andreas Rumpf
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -14,7 +14,7 @@ import
extccomp, strutils, os, platform, parseopt
when useCaas:
import sockets
import net
# We cache modules and the dependency graph. However, we don't check for
# file changes but expect the client to tell us about them, otherwise the
@@ -33,7 +33,12 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string) =
parseopt.next(p)
case p.kind
of cmdEnd: break
of cmdLongoption, cmdShortOption: processSwitch(pass, p)
of cmdLongoption, cmdShortOption:
if p.key == " ":
p.key = "-"
if processArgument(pass, p, argsCount): break
else:
processSwitch(pass, p)
of cmdArgument:
if processArgument(pass, p, argsCount): break
if pass == passCmd2:
@@ -45,8 +50,6 @@ proc serve*(action: proc (){.nimcall.}) =
curCaasCmd = cmd
processCmdLine(passCmd2, cmd)
action()
gDirtyBufferIdx = 0
gDirtyOriginalIdx = 0
gErrorCounter = 0
let typ = getConfigVar("server.type")
@@ -61,14 +64,16 @@ proc serve*(action: proc (){.nimcall.}) =
of "tcp", "":
when useCaas:
var server = socket()
if server == invalidSocket: raiseOSError(osLastError())
var server = newSocket()
let p = getConfigVar("server.port")
let port = if p.len > 0: parseInt(p).Port else: 6000.Port
server.bindAddr(port, getConfigVar("server.address"))
var inp = "".TaintedString
server.listen()
new(stdoutSocket)
var stdoutSocket = newSocket()
msgs.writelnHook = proc (line: string) =
stdoutSocket.send(line & "\c\L")
while true:
accept(server, stdoutSocket)
stdoutSocket.readLine(inp)
@@ -76,7 +81,7 @@ proc serve*(action: proc (){.nimcall.}) =
stdoutSocket.send("\c\L")
stdoutSocket.close()
else:
quit "server.type not supported; compiler built without caas support"
msgQuit "server.type not supported; compiler built without caas support"
else:
echo "Invalid server.type:", typ
quit 1
msgQuit 1

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
#
#
# The Nim Compiler
# (c) Copyright 2013 Andreas Rumpf
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -22,44 +22,57 @@ const
#template sectionSuggest(): expr = "##begin\n" & getStackTrace() & "##end\n"
proc origModuleName(m: PSym): string =
result = if m.position == gDirtyBufferIdx:
fileInfos[gDirtyOriginalIdx].shortName
else:
m.name.s
template origModuleName(m: PSym): string = m.name.s
proc symToStr(s: PSym, isLocal: bool, section: string, li: TLineInfo): string =
result = section
result.add(sep)
result.add($s.kind)
result.add(sep)
if not isLocal and s.kind != skModule:
let ow = s.owner
if ow.kind != skModule and ow.owner != nil:
let ow2 = ow.owner
result.add(ow2.origModuleName)
if optIdeTerse in gGlobalOptions:
if s.kind in routineKinds:
result.add renderTree(s.ast, {renderNoBody, renderNoComments,
renderDocComments, renderNoPragmas})
else:
result.add s.name.s
result.add(sep)
result.add(toFullPath(li))
result.add(sep)
result.add($toLinenumber(li))
result.add(sep)
result.add($toColumn(li))
else:
result.add($s.kind)
result.add(sep)
if not isLocal and s.kind != skModule:
let ow = s.owner
if ow.kind != skModule and ow.owner != nil:
let ow2 = ow.owner
result.add(ow2.origModuleName)
result.add('.')
result.add(ow.origModuleName)
result.add('.')
result.add(ow.origModuleName)
result.add('.')
result.add(s.name.s)
result.add(sep)
if s.typ != nil:
result.add(typeToString(s.typ))
result.add(sep)
result.add(toFullPath(li))
result.add(sep)
result.add($toLinenumber(li))
result.add(sep)
result.add($toColumn(li))
result.add(sep)
when not defined(noDocgen):
result.add(s.extractDocComment.escape)
result.add(s.name.s)
result.add(sep)
if s.typ != nil:
result.add(typeToString(s.typ))
result.add(sep)
result.add(toFullPath(li))
result.add(sep)
result.add($toLinenumber(li))
result.add(sep)
result.add($toColumn(li))
result.add(sep)
when not defined(noDocgen):
result.add(s.extractDocComment.escape)
proc symToStr(s: PSym, isLocal: bool, section: string): string =
result = symToStr(s, isLocal, section, s.info)
proc filterSym(s: PSym): bool {.inline.} =
result = s.name.s[0] in lexer.SymChars and s.kind != skModule
result = s.kind != skModule
proc filterSymNoOpr(s: PSym): bool {.inline.} =
result = s.kind != skModule and s.name.s[0] in lexer.SymChars and
not isKeyword(s.name)
proc fieldVisible*(c: PContext, f: PSym): bool {.inline.} =
let fmoduleId = getModule(f).id
@@ -74,9 +87,6 @@ proc suggestField(c: PContext, s: PSym, outputs: var int) =
suggestWriteln(symToStr(s, isLocal=true, sectionSuggest))
inc outputs
when not defined(nimhygiene):
{.pragma: inject.}
template wholeSymTab(cond, section: expr) {.immediate.} =
var isLocal = true
for scope in walkScopes(c.currentScope):
@@ -134,11 +144,20 @@ proc suggestCall(c: PContext, n, nOrig: PNode, outputs: var int) =
proc typeFits(c: PContext, s: PSym, firstArg: PType): bool {.inline.} =
if s.typ != nil and sonsLen(s.typ) > 1 and s.typ.sons[1] != nil:
# special rule: if system and some weird generic match via 'tyExpr'
# or 'tyGenericParam' we won't list it either to reduce the noise (nobody
# wants 'system.`-|` as suggestion
let m = s.getModule()
if m != nil and sfSystemModule in m.flags:
if s.kind == skType: return
var exp = s.typ.sons[1].skipTypes({tyGenericInst, tyVar})
if exp.kind == tyVarargs: exp = elemType(exp)
if exp.kind in {tyExpr, tyStmt, tyGenericParam, tyAnything}: return
result = sigmatch.argtypeMatches(c, s.typ.sons[1], firstArg)
proc suggestOperations(c: PContext, n: PNode, typ: PType, outputs: var int) =
assert typ != nil
wholeSymTab(filterSym(it) and typeFits(c, it, typ), sectionSuggest)
wholeSymTab(filterSymNoOpr(it) and typeFits(c, it, typ), sectionSuggest)
proc suggestEverything(c: PContext, n: PNode, outputs: var int) =
# do not produce too many symbols:
@@ -194,19 +213,28 @@ proc suggestFieldAccess(c: PContext, n: PNode, outputs: var int) =
else:
suggestOperations(c, n, typ, outputs)
type
TCheckPointResult = enum
cpNone, cpFuzzy, cpExact
proc inCheckpoint(current: TLineInfo): TCheckPointResult =
if current.fileIndex == gTrackPos.fileIndex:
if current.line == gTrackPos.line and
abs(current.col-gTrackPos.col) < 4:
return cpExact
if current.line >= gTrackPos.line:
return cpFuzzy
proc findClosestDot(n: PNode): PNode =
if n.kind == nkDotExpr and msgs.inCheckpoint(n.info) == cpExact:
if n.kind == nkDotExpr and inCheckpoint(n.info) == cpExact:
result = n
else:
for i in 0.. <safeLen(n):
result = findClosestDot(n.sons[i])
if result != nil: return
const
CallNodes = {nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit}
proc findClosestCall(n: PNode): PNode =
if n.kind in CallNodes and msgs.inCheckpoint(n.info) == cpExact:
if n.kind in nkCallKinds and inCheckpoint(n.info) == cpExact:
result = n
else:
for i in 0.. <safeLen(n):
@@ -214,34 +242,20 @@ proc findClosestCall(n: PNode): PNode =
if result != nil: return
proc isTracked(current: TLineInfo, tokenLen: int): bool =
for i in countup(0, high(checkPoints)):
if current.fileIndex == checkPoints[i].fileIndex:
if current.line == checkPoints[i].line:
let col = checkPoints[i].col
if col >= current.col and col <= current.col+tokenLen-1:
return true
if current.fileIndex == gTrackPos.fileIndex:
if current.line == gTrackPos.line:
let col = gTrackPos.col
if col >= current.col and col <= current.col+tokenLen-1:
return true
proc findClosestSym(n: PNode): PNode =
if n.kind == nkSym and msgs.inCheckpoint(n.info) == cpExact:
if n.kind == nkSym and inCheckpoint(n.info) == cpExact:
result = n
elif n.kind notin {nkNone..nkNilLit}:
for i in 0.. <sonsLen(n):
result = findClosestSym(n.sons[i])
if result != nil: return
proc safeSemExpr(c: PContext, n: PNode): PNode =
try:
result = c.semExpr(c, n)
except ERecoverableError:
result = ast.emptyNode
proc fuzzySemCheck(c: PContext, n: PNode): PNode =
result = safeSemExpr(c, n)
if result == nil or result.kind == nkEmpty:
result = newNodeI(n.kind, n.info)
if n.kind notin {nkNone..nkNilLit}:
for i in 0 .. < sonsLen(n): result.addSon(fuzzySemCheck(c, n.sons[i]))
var
usageSym*: PSym
lastLineInfo: TLineInfo
@@ -261,69 +275,18 @@ proc findDefinition(info: TLineInfo; s: PSym) =
suggestWriteln(symToStr(s, isLocal=false, sectionDef))
suggestQuit()
type
TSourceMap = object
lines: seq[TLineMap]
TEntry = object
pos: int
sym: PSym
TLineMap = object
entries: seq[TEntry]
var
gSourceMaps: seq[TSourceMap] = @[]
proc ensureIdx[T](x: var T, y: int) =
if x.len <= y: x.setLen(y+1)
proc ensureSeq[T](x: var seq[T]) =
if x == nil: newSeq(x, 0)
proc resetSourceMap*(fileIdx: int32) =
ensureIdx(gSourceMaps, fileIdx)
gSourceMaps[fileIdx].lines = @[]
proc addToSourceMap(sym: PSym, info: TLineInfo) =
ensureIdx(gSourceMaps, info.fileIndex)
ensureSeq(gSourceMaps[info.fileIndex].lines)
ensureIdx(gSourceMaps[info.fileIndex].lines, info.line)
ensureSeq(gSourceMaps[info.fileIndex].lines[info.line].entries)
gSourceMaps[info.fileIndex].lines[info.line].entries.add(TEntry(pos: info.col, sym: sym))
proc defFromLine(entries: var seq[TEntry], col: int32) =
if entries == nil: return
# The sorting is done lazily here on purpose.
# No need to pay the price for it unless the user requests
# "goto definition" on a particular line
sort(entries) do (a,b: TEntry) -> int:
return cmp(a.pos, b.pos)
for e in entries:
# currently, the line-infos for most expressions point to
# one position past the end of the expression. This means
# that the first expr that ends after the cursor column is
# the one we are looking for.
if e.pos >= col:
suggestWriteln(symToStr(e.sym, isLocal=false, sectionDef))
return
proc defFromSourceMap*(i: TLineInfo) =
if not ((i.fileIndex < gSourceMaps.len) and
(gSourceMaps[i.fileIndex].lines != nil) and
(i.line < gSourceMaps[i.fileIndex].lines.len)): return
defFromLine(gSourceMaps[i.fileIndex].lines[i.line].entries, i.col)
proc suggestSym*(info: TLineInfo; s: PSym) {.inline.} =
## misnamed: should be 'symDeclared'
if optUsages in gGlobalOptions:
if gIdeCmd == ideUse:
findUsages(info, s)
if optDef in gGlobalOptions:
elif gIdeCmd == ideDef:
findDefinition(info, s)
if isServing:
addToSourceMap(s, info)
proc markUsed(info: TLineInfo; s: PSym) =
incl(s.flags, sfUsed)
@@ -336,31 +299,39 @@ proc useSym*(sym: PSym): PNode =
result = newSymNode(sym)
markUsed(result.info, sym)
proc safeSemExpr*(c: PContext, n: PNode): PNode =
# use only for idetools support!
try:
result = c.semExpr(c, n)
except ERecoverableError:
result = ast.emptyNode
proc suggestExpr*(c: PContext, node: PNode) =
var cp = msgs.inCheckpoint(node.info)
if cp == cpNone: return
if nfIsCursor notin node.flags:
if gTrackPos.line < 0: return
var cp = inCheckpoint(node.info)
if cp == cpNone: return
var outputs = 0
# This keeps semExpr() from coming here recursively:
if c.inCompilesContext > 0: return
inc(c.inCompilesContext)
if optSuggest in gGlobalOptions:
var n = findClosestDot(node)
if gIdeCmd == ideSug:
var n = if nfIsCursor in node.flags: node else: findClosestDot(node)
if n == nil: n = node
else: cp = cpExact
if n.kind == nkDotExpr and cp == cpExact:
if n.kind == nkDotExpr:
var obj = safeSemExpr(c, n.sons[0])
suggestFieldAccess(c, obj, outputs)
if optIdeDebug in gGlobalOptions:
echo "expression ", renderTree(obj), " has type ", typeToString(obj.typ)
#writeStackTrace()
else:
#debug n
suggestEverything(c, n, outputs)
if optContext in gGlobalOptions:
var n = findClosestCall(node)
elif gIdeCmd == ideCon:
var n = if nfIsCursor in node.flags: node else: findClosestCall(node)
if n == nil: n = node
else: cp = cpExact
if n.kind in CallNodes:
if n.kind in nkCallKinds:
var a = copyNode(n)
var x = safeSemExpr(c, n.sons[0])
if x.kind == nkEmpty or x.typ == nil: x = n.sons[0]
@@ -371,15 +342,9 @@ proc suggestExpr*(c: PContext, node: PNode) =
if x.kind == nkEmpty or x.typ == nil: break
addSon(a, x)
suggestCall(c, a, n, outputs)
dec(c.inCompilesContext)
if outputs > 0 and optUsages notin gGlobalOptions: suggestQuit()
if outputs > 0 and gIdeCmd != ideUse: suggestQuit()
proc suggestStmt*(c: PContext, n: PNode) =
suggestExpr(c, n)
proc findSuggest*(c: PContext, n: PNode) =
if n == nil: return
suggestExpr(c, n)
for i in 0.. <safeLen(n):
findSuggest(c, n.sons[i])

View File

@@ -45,7 +45,7 @@ proc parseFile(fileIdx: int32): PNode =
var
p: TParsers
f: File
let filename = fileIdx.toFullPath
let filename = fileIdx.toFullPathConsiderDirty
if not open(f, filename):
rawMessage(errCannotOpenFile, filename)
return
@@ -163,7 +163,7 @@ proc evalPipe(p: var TParsers, n: PNode, filename: string,
proc openParsers(p: var TParsers, fileIdx: int32, inputstream: PLLStream) =
var s: PLLStream
p.skin = skinStandard
let filename = fileIdx.toFullPath
let filename = fileIdx.toFullPathConsiderDirty
var pipe = parsePipe(filename, inputstream)
if pipe != nil: s = evalPipe(p, pipe, filename, inputstream)
else: s = inputstream

View File

@@ -1,7 +1,7 @@
#
#
# The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -17,27 +17,27 @@
# * introduces method dispatchers
# * performs lambda lifting for closure support
import
intsets, strutils, lists, options, ast, astalgo, trees, treetab, msgs, os,
import
intsets, strutils, lists, options, ast, astalgo, trees, treetab, msgs, os,
idents, renderer, types, passes, semfold, magicsys, cgmeth, rodread,
lambdalifting, sempass2, lowerings
# implementation
type
type
PTransNode* = distinct PNode
PTransCon = ref TTransCon
TTransCon{.final.} = object # part of TContext; stackable
mapping: TIdNodeTable # mapping from symbols to nodes
owner: PSym # current owner
forStmt: PNode # current for stmt
forLoopBody: PTransNode # transformed for loop body
yieldStmts: int # we count the number of yield statements,
forLoopBody: PTransNode # transformed for loop body
yieldStmts: int # we count the number of yield statements,
# because we need to introduce new variables
# if we encounter the 2nd yield statement
next: PTransCon # for stacking
TTransfContext = object of passes.TPassContext
module: PSym
transCon: PTransCon # top of a TransCon stack
@@ -46,52 +46,52 @@ type
contSyms, breakSyms: seq[PSym] # to transform 'continue' and 'break'
PTransf = ref TTransfContext
proc newTransNode(a: PNode): PTransNode {.inline.} =
proc newTransNode(a: PNode): PTransNode {.inline.} =
result = PTransNode(shallowCopy(a))
proc newTransNode(kind: TNodeKind, info: TLineInfo,
sons: int): PTransNode {.inline.} =
proc newTransNode(kind: TNodeKind, info: TLineInfo,
sons: int): PTransNode {.inline.} =
var x = newNodeI(kind, info)
newSeq(x.sons, sons)
result = x.PTransNode
proc newTransNode(kind: TNodeKind, n: PNode,
sons: int): PTransNode {.inline.} =
proc newTransNode(kind: TNodeKind, n: PNode,
sons: int): PTransNode {.inline.} =
var x = newNodeIT(kind, n.info, n.typ)
newSeq(x.sons, sons)
x.typ = n.typ
result = x.PTransNode
proc `[]=`(a: PTransNode, i: int, x: PTransNode) {.inline.} =
proc `[]=`(a: PTransNode, i: int, x: PTransNode) {.inline.} =
var n = PNode(a)
n.sons[i] = PNode(x)
proc `[]`(a: PTransNode, i: int): PTransNode {.inline.} =
proc `[]`(a: PTransNode, i: int): PTransNode {.inline.} =
var n = PNode(a)
result = n.sons[i].PTransNode
proc add(a, b: PTransNode) {.inline.} = addSon(PNode(a), PNode(b))
proc len(a: PTransNode): int {.inline.} = result = sonsLen(a.PNode)
proc newTransCon(owner: PSym): PTransCon =
proc newTransCon(owner: PSym): PTransCon =
assert owner != nil
new(result)
initIdNodeTable(result.mapping)
result.owner = owner
proc pushTransCon(c: PTransf, t: PTransCon) =
proc pushTransCon(c: PTransf, t: PTransCon) =
t.next = c.transCon
c.transCon = t
proc popTransCon(c: PTransf) =
proc popTransCon(c: PTransf) =
if (c.transCon == nil): internalError("popTransCon")
c.transCon = c.transCon.next
proc getCurrOwner(c: PTransf): PSym =
proc getCurrOwner(c: PTransf): PSym =
if c.transCon != nil: result = c.transCon.owner
else: result = c.module
proc newTemp(c: PTransf, typ: PType, info: TLineInfo): PSym =
proc newTemp(c: PTransf, typ: PType, info: TLineInfo): PSym =
result = newSym(skTemp, getIdent(genPrefix), getCurrOwner(c), info)
result.typ = skipTypes(typ, {tyGenericInst})
incl(result.flags, sfFromGeneric)
@@ -100,10 +100,10 @@ proc transform(c: PTransf, n: PNode): PTransNode
proc transformSons(c: PTransf, n: PNode): PTransNode =
result = newTransNode(n)
for i in countup(0, sonsLen(n)-1):
for i in countup(0, sonsLen(n)-1):
result[i] = transform(c, n.sons[i])
proc newAsgnStmt(c: PTransf, le: PNode, ri: PTransNode): PTransNode =
proc newAsgnStmt(c: PTransf, le: PNode, ri: PTransNode): PTransNode =
result = newTransNode(nkFastAsgn, PNode(ri).info, 2)
result[0] = PTransNode(le)
result[1] = ri
@@ -113,30 +113,30 @@ proc transformSymAux(c: PTransf, n: PNode): PNode =
# return liftIterSym(n)
var b: PNode
var tc = c.transCon
if sfBorrow in n.sym.flags:
if sfBorrow in n.sym.flags:
# simply exchange the symbol:
b = n.sym.getBody
if b.kind != nkSym: internalError(n.info, "wrong AST for borrowed symbol")
b = newSymNode(b.sym)
b.info = n.info
else:
else:
b = n
while tc != nil:
while tc != nil:
result = idNodeTableGet(tc.mapping, b.sym)
if result != nil: return
tc = tc.next
result = b
proc transformSym(c: PTransf, n: PNode): PTransNode =
proc transformSym(c: PTransf, n: PNode): PTransNode =
result = PTransNode(transformSymAux(c, n))
proc transformVarSection(c: PTransf, v: PNode): PTransNode =
result = newTransNode(v)
for i in countup(0, sonsLen(v)-1):
var it = v.sons[i]
if it.kind == nkCommentStmt:
if it.kind == nkCommentStmt:
result[i] = PTransNode(it)
elif it.kind == nkIdentDefs:
elif it.kind == nkIdentDefs:
if it.sons[0].kind != nkSym: internalError(it.info, "transformVarSection")
internalAssert(it.len == 3)
var newVar = copySym(it.sons[0].sym)
@@ -152,13 +152,14 @@ proc transformVarSection(c: PTransf, v: PNode): PTransNode =
defs[0] = newSymNode(newVar).PTransNode
defs[1] = it.sons[1].PTransNode
defs[2] = transform(c, it.sons[2])
newVar.ast = defs[2].PNode
result[i] = defs
else:
if it.kind != nkVarTuple:
else:
if it.kind != nkVarTuple:
internalError(it.info, "transformVarSection: not nkVarTuple")
var L = sonsLen(it)
var defs = newTransNode(it.kind, it.info, L)
for j in countup(0, L-3):
for j in countup(0, L-3):
var newVar = copySym(it.sons[j].sym)
incl(newVar.flags, sfFromGeneric)
newVar.owner = getCurrOwner(c)
@@ -188,12 +189,12 @@ proc transformConstSection(c: PTransf, v: PNode): PTransNode =
else:
result[i] = PTransNode(it)
proc hasContinue(n: PNode): bool =
proc hasContinue(n: PNode): bool =
case n.kind
of nkEmpty..nkNilLit, nkForStmt, nkParForStmt, nkWhileStmt: discard
of nkContinueStmt: result = true
else:
for i in countup(0, sonsLen(n) - 1):
else:
for i in countup(0, sonsLen(n) - 1):
if hasContinue(n.sons[i]): return true
proc newLabel(c: PTransf, n: PNode): PSym =
@@ -224,10 +225,10 @@ proc transformBlock(c: PTransf, n: PNode): PTransNode =
discard c.breakSyms.pop
result[0] = newSymNode(labl).PTransNode
proc transformLoopBody(c: PTransf, n: PNode): PTransNode =
# What if it contains "continue" and "break"? "break" needs
proc transformLoopBody(c: PTransf, n: PNode): PTransNode =
# What if it contains "continue" and "break"? "break" needs
# an explicit label too, but not the same!
# We fix this here by making every 'break' belong to its enclosing loop
# and changing all breaks that belong to a 'block' by annotating it with
# a label (if it hasn't one already).
@@ -239,7 +240,7 @@ proc transformLoopBody(c: PTransf, n: PNode): PTransNode =
result[0] = newSymNode(labl).PTransNode
result[1] = transform(c, n)
discard c.contSyms.pop()
else:
else:
result = transform(c, n)
proc transformWhile(c: PTransf; n: PNode): PTransNode =
@@ -273,27 +274,27 @@ proc transformBreak(c: PTransf, n: PNode): PTransNode =
result = transformSons(c, n)
result[0] = newSymNode(labl).PTransNode
proc unpackTuple(c: PTransf, n: PNode, father: PTransNode) =
proc unpackTuple(c: PTransf, n: PNode, father: PTransNode) =
# XXX: BUG: what if `n` is an expression with side-effects?
for i in countup(0, sonsLen(c.transCon.forStmt) - 3):
add(father, newAsgnStmt(c, c.transCon.forStmt.sons[i],
for i in countup(0, sonsLen(c.transCon.forStmt) - 3):
add(father, newAsgnStmt(c, c.transCon.forStmt.sons[i],
transform(c, newTupleAccess(n, i))))
proc introduceNewLocalVars(c: PTransf, n: PNode): PTransNode =
proc introduceNewLocalVars(c: PTransf, n: PNode): PTransNode =
case n.kind
of nkSym:
of nkSym:
result = transformSym(c, n)
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit:
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit:
# nothing to be done for leaves:
result = PTransNode(n)
of nkVarSection, nkLetSection:
result = transformVarSection(c, n)
else:
result = newTransNode(n)
for i in countup(0, sonsLen(n)-1):
for i in countup(0, sonsLen(n)-1):
result[i] = introduceNewLocalVars(c, n.sons[i])
proc transformYield(c: PTransf, n: PNode): PTransNode =
proc transformYield(c: PTransf, n: PNode): PTransNode =
result = newTransNode(nkStmtList, n.info, 0)
var e = n.sons[0]
# c.transCon.forStmt.len == 3 means that there is one for loop variable
@@ -301,26 +302,27 @@ proc transformYield(c: PTransf, n: PNode): PTransNode =
if skipTypes(e.typ, {tyGenericInst}).kind == tyTuple and
c.transCon.forStmt.len != 3:
e = skipConv(e)
if e.kind == nkPar:
for i in countup(0, sonsLen(e) - 1):
add(result, newAsgnStmt(c, c.transCon.forStmt.sons[i],
if e.kind == nkPar:
for i in countup(0, sonsLen(e) - 1):
add(result, newAsgnStmt(c, c.transCon.forStmt.sons[i],
transform(c, e.sons[i])))
else:
else:
unpackTuple(c, e, result)
else:
else:
var x = transform(c, e)
add(result, newAsgnStmt(c, c.transCon.forStmt.sons[0], x))
inc(c.transCon.yieldStmts)
if c.transCon.yieldStmts <= 1:
# common case
add(result, c.transCon.forLoopBody)
else:
else:
# we need to introduce new local variables:
add(result, introduceNewLocalVars(c, c.transCon.forLoopBody.PNode))
proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PTransNode =
result = transformSons(c, n)
if gCmd == cmdCompileToCpp or sfCompileToCpp in c.module.flags: return
var n = result.PNode
case n.sons[0].kind
of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64:
@@ -339,25 +341,25 @@ proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PTransNode =
if n.sons[0].kind == a or n.sons[0].kind == b:
# addr ( deref ( x )) --> x
result = PTransNode(n.sons[0].sons[0])
proc transformConv(c: PTransf, n: PNode): PTransNode =
proc transformConv(c: PTransf, n: PNode): PTransNode =
# numeric types need range checks:
var dest = skipTypes(n.typ, abstractVarRange)
var source = skipTypes(n.sons[1].typ, abstractVarRange)
case dest.kind
of tyInt..tyInt64, tyEnum, tyChar, tyBool, tyUInt8..tyUInt32:
of tyInt..tyInt64, tyEnum, tyChar, tyBool, tyUInt8..tyUInt32:
# we don't include uint and uint64 here as these are no ordinal types ;-)
if not isOrdinalType(source):
# float -> int conversions. ugh.
result = transformSons(c, n)
elif firstOrd(n.typ) <= firstOrd(n.sons[1].typ) and
lastOrd(n.sons[1].typ) <= lastOrd(n.typ):
lastOrd(n.sons[1].typ) <= lastOrd(n.typ):
# BUGFIX: simply leave n as it is; we need a nkConv node,
# but no range check:
result = transformSons(c, n)
else:
else:
# generate a range check:
if dest.kind == tyInt64 or source.kind == tyInt64:
if dest.kind == tyInt64 or source.kind == tyInt64:
result = newTransNode(nkChckRange64, n, 3)
else:
result = newTransNode(nkChckRange, n, 3)
@@ -367,7 +369,7 @@ proc transformConv(c: PTransf, n: PNode): PTransNode =
result[2] = newIntTypeNode(nkIntLit, lastOrd(dest), source).PTransNode
of tyFloat..tyFloat128:
# XXX int64 -> float conversion?
if skipTypes(n.typ, abstractVar).kind == tyRange:
if skipTypes(n.typ, abstractVar).kind == tyRange:
result = newTransNode(nkChckRangeF, n, 3)
dest = skipTypes(n.typ, abstractVar)
result[0] = transform(c, n.sons[1])
@@ -377,81 +379,84 @@ proc transformConv(c: PTransf, n: PNode): PTransNode =
result = transformSons(c, n)
of tyOpenArray, tyVarargs:
result = transform(c, n.sons[1])
of tyCString:
if source.kind == tyString:
PNode(result).typ = takeType(n.typ, n.sons[1].typ)
#echo n.info, " came here and produced ", typeToString(PNode(result).typ),
# " from ", typeToString(n.typ), " and ", typeToString(n.sons[1].typ)
of tyCString:
if source.kind == tyString:
result = newTransNode(nkStringToCString, n, 1)
result[0] = transform(c, n.sons[1])
else:
result = transformSons(c, n)
of tyString:
if source.kind == tyCString:
of tyString:
if source.kind == tyCString:
result = newTransNode(nkCStringToString, n, 1)
result[0] = transform(c, n.sons[1])
else:
result = transformSons(c, n)
of tyRef, tyPtr:
of tyRef, tyPtr:
dest = skipTypes(dest, abstractPtrs)
source = skipTypes(source, abstractPtrs)
if source.kind == tyObject:
if source.kind == tyObject:
var diff = inheritanceDiff(dest, source)
if diff < 0:
if diff < 0:
result = newTransNode(nkObjUpConv, n, 1)
result[0] = transform(c, n.sons[1])
elif diff > 0:
elif diff > 0 and diff != high(int):
result = newTransNode(nkObjDownConv, n, 1)
result[0] = transform(c, n.sons[1])
else:
else:
result = transform(c, n.sons[1])
else:
result = transformSons(c, n)
of tyObject:
of tyObject:
var diff = inheritanceDiff(dest, source)
if diff < 0:
if diff < 0:
result = newTransNode(nkObjUpConv, n, 1)
result[0] = transform(c, n.sons[1])
elif diff > 0:
elif diff > 0 and diff != high(int):
result = newTransNode(nkObjDownConv, n, 1)
result[0] = transform(c, n.sons[1])
else:
else:
result = transform(c, n.sons[1])
of tyGenericParam, tyOrdinal:
result = transform(c, n.sons[1])
# happens sometimes for generated assignments, etc.
else:
else:
result = transformSons(c, n)
type
TPutArgInto = enum
type
TPutArgInto = enum
paDirectMapping, paFastAsgn, paVarAsgn
proc putArgInto(arg: PNode, formal: PType): TPutArgInto =
proc putArgInto(arg: PNode, formal: PType): TPutArgInto =
# This analyses how to treat the mapping "formal <-> arg" in an
# inline context.
if skipTypes(formal, abstractInst).kind in {tyOpenArray, tyVarargs}:
return paDirectMapping # XXX really correct?
# what if ``arg`` has side-effects?
case arg.kind
of nkEmpty..nkNilLit:
of nkEmpty..nkNilLit:
result = paDirectMapping
of nkPar, nkCurly, nkBracket:
of nkPar, nkCurly, nkBracket:
result = paFastAsgn
for i in countup(0, sonsLen(arg) - 1):
if putArgInto(arg.sons[i], formal) != paDirectMapping: return
for i in countup(0, sonsLen(arg) - 1):
if putArgInto(arg.sons[i], formal) != paDirectMapping: return
result = paDirectMapping
else:
else:
if skipTypes(formal, abstractInst).kind == tyVar: result = paVarAsgn
else: result = paFastAsgn
proc findWrongOwners(c: PTransf, n: PNode) =
if n.kind == nkVarSection:
let x = n.sons[0].sons[0]
if x.kind == nkSym and x.sym.owner != getCurrOwner(c):
internalError(x.info, "bah " & x.sym.name.s & " " &
internalError(x.info, "bah " & x.sym.name.s & " " &
x.sym.owner.name.s & " " & getCurrOwner(c).name.s)
else:
for i in 0 .. <safeLen(n): findWrongOwners(c, n.sons[i])
proc transformFor(c: PTransf, n: PNode): PTransNode =
proc transformFor(c: PTransf, n: PNode): PTransNode =
# generate access statements for the parameters (unless they are constant)
# put mapping from formal parameters to actual parameters
if n.kind != nkForStmt: internalError(n.info, "transformFor")
@@ -465,38 +470,39 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
result[0] = newSymNode(labl).PTransNode
if call.typ.kind != tyIter and
(call.kind notin nkCallKinds or call.sons[0].kind != nkSym or
(call.kind notin nkCallKinds or call.sons[0].kind != nkSym or
call.sons[0].sym.kind != skIterator):
n.sons[length-1] = transformLoopBody(c, n.sons[length-1]).PNode
result[1] = lambdalifting.liftForLoop(n).PTransNode
discard c.breakSyms.pop
return result
#echo "transforming: ", renderTree(n)
var stmtList = newTransNode(nkStmtList, n.info, 0)
var loopBody = transformLoopBody(c, n.sons[length-1])
result[1] = stmtList
discard c.breakSyms.pop
var v = newNodeI(nkVarSection, n.info)
for i in countup(0, length - 3):
for i in countup(0, length - 3):
addVar(v, copyTree(n.sons[i])) # declare new vars
add(stmtList, v.PTransNode)
# Bugfix: inlined locals belong to the invoking routine, not to the invoked
# iterator!
let iter = call.sons[0].sym
var newC = newTransCon(getCurrOwner(c))
newC.forStmt = n
newC.forLoopBody = loopBody
internalAssert iter.kind == skIterator
# this can fail for 'nimsuggest' and 'check':
if iter.kind != skIterator: return result
# generate access statements for the parameters (unless they are constant)
pushTransCon(c, newC)
for i in countup(1, sonsLen(call) - 1):
for i in countup(1, sonsLen(call) - 1):
var arg = transform(c, call.sons[i]).PNode
var formal = skipTypes(iter.typ, abstractInst).n.sons[i].sym
var formal = skipTypes(iter.typ, abstractInst).n.sons[i].sym
if arg.typ.kind == tyIter: continue
case putArgInto(arg, formal.typ)
of paDirectMapping:
@@ -525,20 +531,20 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
popInfoContext()
popTransCon(c)
# echo "transformed: ", stmtList.PNode.renderTree
proc getMagicOp(call: PNode): TMagic =
proc getMagicOp(call: PNode): TMagic =
if call.sons[0].kind == nkSym and
call.sons[0].sym.kind in {skProc, skMethod, skConverter}:
call.sons[0].sym.kind in {skProc, skMethod, skConverter}:
result = call.sons[0].sym.magic
else:
result = mNone
proc transformCase(c: PTransf, n: PNode): PTransNode =
proc transformCase(c: PTransf, n: PNode): PTransNode =
# removes `elif` branches of a case stmt
# adds ``else: nil`` if needed for the code generator
result = newTransNode(nkCaseStmt, n, 0)
var ifs = PTransNode(nil)
for i in 0 .. sonsLen(n)-1:
for i in 0 .. sonsLen(n)-1:
var it = n.sons[i]
var e = transform(c, it)
case it.kind
@@ -562,8 +568,8 @@ proc transformCase(c: PTransf, n: PNode): PTransNode =
var elseBranch = newTransNode(nkElse, n.info, 1)
elseBranch[0] = newTransNode(nkNilLit, n.info, 0)
add(result, elseBranch)
proc transformArrayAccess(c: PTransf, n: PNode): PTransNode =
proc transformArrayAccess(c: PTransf, n: PNode): PTransNode =
# XXX this is really bad; transf should use a proper AST visitor
if n.sons[0].kind == nkSym and n.sons[0].sym.kind == skType:
result = n.PTransNode
@@ -571,45 +577,44 @@ proc transformArrayAccess(c: PTransf, n: PNode): PTransNode =
result = newTransNode(n)
for i in 0 .. < n.len:
result[i] = transform(c, skipConv(n.sons[i]))
proc getMergeOp(n: PNode): PSym =
proc getMergeOp(n: PNode): PSym =
case n.kind
of nkCall, nkHiddenCallConv, nkCommand, nkInfix, nkPrefix, nkPostfix,
nkCallStrLit:
if (n.sons[0].kind == nkSym) and (n.sons[0].sym.kind == skProc) and
(sfMerge in n.sons[0].sym.flags):
of nkCall, nkHiddenCallConv, nkCommand, nkInfix, nkPrefix, nkPostfix,
nkCallStrLit:
if n.sons[0].kind == nkSym and n.sons[0].sym.magic == mConStrStr:
result = n.sons[0].sym
else: discard
proc flattenTreeAux(d, a: PNode, op: PSym) =
proc flattenTreeAux(d, a: PNode, op: PSym) =
let op2 = getMergeOp(a)
if op2 != nil and
(op2.id == op.id or op.magic != mNone and op2.magic == op.magic):
(op2.id == op.id or op.magic != mNone and op2.magic == op.magic):
for i in countup(1, sonsLen(a)-1): flattenTreeAux(d, a.sons[i], op)
else:
else:
addSon(d, copyTree(a))
proc flattenTree(root: PNode): PNode =
proc flattenTree(root: PNode): PNode =
let op = getMergeOp(root)
if op != nil:
if op != nil:
result = copyNode(root)
addSon(result, copyTree(root.sons[0]))
flattenTreeAux(result, root, op)
else:
else:
result = root
proc transformCall(c: PTransf, n: PNode): PTransNode =
proc transformCall(c: PTransf, n: PNode): PTransNode =
var n = flattenTree(n)
let op = getMergeOp(n)
let magic = getMagic(n)
if op != nil and op.magic != mNone and n.len >= 3:
if op != nil and op.magic != mNone and n.len >= 3:
result = newTransNode(nkCall, n, 0)
add(result, transform(c, n.sons[0]))
var j = 1
while j < sonsLen(n):
while j < sonsLen(n):
var a = transform(c, n.sons[j]).PNode
inc(j)
if isConstExpr(a):
if isConstExpr(a):
while (j < sonsLen(n)):
let b = transform(c, n.sons[j]).PNode
if not isConstExpr(b): break
@@ -638,7 +643,7 @@ proc transformCall(c: PTransf, n: PNode): PTransNode =
proc dontInlineConstant(orig, cnst: PNode): bool {.inline.} =
# symbols that expand to a complex constant (array, etc.) should not be
# inlined, unless it's the empty array:
result = orig.kind == nkSym and cnst.kind in {nkCurly, nkPar, nkBracket} and
result = orig.kind == nkSym and cnst.kind in {nkCurly, nkPar, nkBracket} and
cnst.len != 0
proc commonOptimizations*(c: PSym, n: PNode): PNode =
@@ -671,11 +676,11 @@ proc commonOptimizations*(c: PSym, n: PNode): PNode =
else:
result = n
proc transform(c: PTransf, n: PNode): PTransNode =
proc transform(c: PTransf, n: PNode): PTransNode =
case n.kind
of nkSym:
of nkSym:
result = transformSym(c, n)
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit:
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit:
# nothing to be done for leaves:
result = PTransNode(n)
of nkBracketExpr: result = transformArrayAccess(c, n)
@@ -700,7 +705,7 @@ proc transform(c: PTransf, n: PNode): PTransNode =
n.sons[bodyPos] = PNode(transform(c, s.getBody))
if n.kind == nkMethodDef: methodDef(s, false)
result = PTransNode(n)
of nkForStmt:
of nkForStmt:
result = transformFor(c, n)
of nkParForStmt:
result = transformSons(c, n)
@@ -711,14 +716,14 @@ proc transform(c: PTransf, n: PNode): PTransNode =
add(result, PTransNode(newSymNode(labl)))
of nkBreakStmt: result = transformBreak(c, n)
of nkWhileStmt: result = transformWhile(c, n)
of nkCall, nkHiddenCallConv, nkCommand, nkInfix, nkPrefix, nkPostfix,
nkCallStrLit:
of nkCall, nkHiddenCallConv, nkCommand, nkInfix, nkPrefix, nkPostfix,
nkCallStrLit:
result = transformCall(c, n)
of nkAddr, nkHiddenAddr:
of nkAddr, nkHiddenAddr:
result = transformAddrDeref(c, n, nkDerefExpr, nkHiddenDeref)
of nkDerefExpr, nkHiddenDeref:
of nkDerefExpr, nkHiddenDeref:
result = transformAddrDeref(c, n, nkAddr, nkHiddenAddr)
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
result = transformConv(c, n)
of nkDiscardStmt:
result = PTransNode(n)
@@ -728,7 +733,7 @@ proc transform(c: PTransf, n: PNode): PTransNode =
# ensure that e.g. discard "some comment" gets optimized away
# completely:
result = PTransNode(newNode(nkCommentStmt))
of nkCommentStmt, nkTemplateDef:
of nkCommentStmt, nkTemplateDef:
return n.PTransNode
of nkConstSection:
# do not replace ``const c = 3`` with ``const 3 = 3``
@@ -742,10 +747,10 @@ proc transform(c: PTransf, n: PNode): PTransNode =
result = transformVarSection(c, n)
else:
result = transformSons(c, n)
of nkYieldStmt:
of nkYieldStmt:
if c.inlining > 0:
result = transformYield(c, n)
else:
else:
result = transformSons(c, n)
of nkBlockStmt, nkBlockExpr:
result = transformBlock(c, n)
@@ -762,7 +767,7 @@ proc transform(c: PTransf, n: PNode): PTransNode =
if cnst != nil and not dontInlineConstant(n, cnst):
result = PTransNode(cnst) # do not miss an optimization
proc processTransf(c: PTransf, n: PNode, owner: PSym): PNode =
proc processTransf(c: PTransf, n: PNode, owner: PSym): PNode =
# Note: For interactive mode we cannot call 'passes.skipCodegen' and skip
# this step! We have to rely that the semantic pass transforms too errornous
# nodes into an empty node.
@@ -772,7 +777,7 @@ proc processTransf(c: PTransf, n: PNode, owner: PSym): PNode =
popTransCon(c)
incl(result.flags, nfTransf)
proc openTransf(module: PSym, filename: string): PTransf =
proc openTransf(module: PSym, filename: string): PTransf =
new(result)
result.contSyms = @[]
result.breakSyms = @[]

View File

@@ -9,40 +9,40 @@
# tree helper routines
import
import
ast, astalgo, lexer, msgs, strutils, wordrecg
proc hasSon(father, son: PNode): bool =
for i in countup(0, sonsLen(father) - 1):
if father.sons[i] == son:
proc hasSon(father, son: PNode): bool =
for i in countup(0, sonsLen(father) - 1):
if father.sons[i] == son:
return true
result = false
proc cyclicTreeAux(n, s: PNode): bool =
if n == nil:
proc cyclicTreeAux(n, s: PNode): bool =
if n == nil:
return false
if hasSon(s, n):
if hasSon(s, n):
return true
var m = sonsLen(s)
addSon(s, n)
if not (n.kind in {nkEmpty..nkNilLit}):
for i in countup(0, sonsLen(n) - 1):
if cyclicTreeAux(n.sons[i], s):
if not (n.kind in {nkEmpty..nkNilLit}):
for i in countup(0, sonsLen(n) - 1):
if cyclicTreeAux(n.sons[i], s):
return true
result = false
delSon(s, m)
proc cyclicTree*(n: PNode): bool =
proc cyclicTree*(n: PNode): bool =
var s = newNodeI(nkEmpty, n.info)
result = cyclicTreeAux(n, s)
proc exprStructuralEquivalent*(a, b: PNode): bool =
proc exprStructuralEquivalent*(a, b: PNode): bool =
result = false
if a == b:
if a == b:
result = true
elif (a != nil) and (b != nil) and (a.kind == b.kind):
elif (a != nil) and (b != nil) and (a.kind == b.kind):
case a.kind
of nkSym:
of nkSym:
# don't go nuts here: same symbol as string is enough:
result = a.sym.name.id == b.sym.name.id
of nkIdent: result = a.ident.id == b.ident.id
@@ -50,12 +50,12 @@ proc exprStructuralEquivalent*(a, b: PNode): bool =
of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal
of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal
of nkEmpty, nkNilLit, nkType: result = true
else:
if sonsLen(a) == sonsLen(b):
for i in countup(0, sonsLen(a) - 1):
if not exprStructuralEquivalent(a.sons[i], b.sons[i]): return
else:
if sonsLen(a) == sonsLen(b):
for i in countup(0, sonsLen(a) - 1):
if not exprStructuralEquivalent(a.sons[i], b.sons[i]): return
result = true
proc sameTree*(a, b: PNode): bool =
result = false
if a == b:
@@ -66,7 +66,7 @@ proc sameTree*(a, b: PNode): bool =
if a.info.col != b.info.col:
return #if a.info.fileIndex <> b.info.fileIndex then exit;
case a.kind
of nkSym:
of nkSym:
# don't go nuts here: same symbol as string is enough:
result = a.sym.name.id == b.sym.name.id
of nkIdent: result = a.ident.id == b.ident.id
@@ -75,15 +75,15 @@ proc sameTree*(a, b: PNode): bool =
of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal
of nkEmpty, nkNilLit, nkType: result = true
else:
if sonsLen(a) == sonsLen(b):
for i in countup(0, sonsLen(a) - 1):
if not sameTree(a.sons[i], b.sons[i]): return
if sonsLen(a) == sonsLen(b):
for i in countup(0, sonsLen(a) - 1):
if not sameTree(a.sons[i], b.sons[i]): return
result = true
proc getProcSym*(call: PNode): PSym =
proc getProcSym*(call: PNode): PSym =
result = call.sons[0].sym
proc getOpSym*(op: PNode): PSym =
proc getOpSym*(op: PNode): PSym =
if op.kind notin {nkCall, nkHiddenCallConv, nkCommand, nkCallStrLit}:
result = nil
else:
@@ -91,25 +91,25 @@ proc getOpSym*(op: PNode): PSym =
elif op.sons[0].kind == nkSym: result = op.sons[0].sym
else: result = nil
proc getMagic*(op: PNode): TMagic =
proc getMagic*(op: PNode): TMagic =
case op.kind
of nkCallKinds:
case op.sons[0].kind
of nkSym: result = op.sons[0].sym.magic
else: result = mNone
else: result = mNone
proc treeToSym*(t: PNode): PSym =
proc treeToSym*(t: PNode): PSym =
result = t.sym
proc isConstExpr*(n: PNode): bool =
proc isConstExpr*(n: PNode): bool =
result = (n.kind in
{nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit,
{nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit,
nkFloatLit..nkFloat64Lit, nkNilLit}) or (nfAllConst in n.flags)
proc isDeepConstExpr*(n: PNode): bool =
case n.kind
of nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit,
of nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit,
nkFloatLit..nkFloat64Lit, nkNilLit:
result = true
of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv:
@@ -122,33 +122,33 @@ proc isDeepConstExpr*(n: PNode): bool =
result = n.typ.isNil or n.typ.skipTypes({tyGenericInst, tyDistinct}).kind != tyObject
else: discard
proc flattenTreeAux(d, a: PNode, op: TMagic) =
proc flattenTreeAux(d, a: PNode, op: TMagic) =
if (getMagic(a) == op): # a is a "leaf", so add it:
for i in countup(1, sonsLen(a) - 1): # BUGFIX
flattenTreeAux(d, a.sons[i], op)
else:
else:
addSon(d, copyTree(a))
proc flattenTree*(root: PNode, op: TMagic): PNode =
proc flattenTree*(root: PNode, op: TMagic): PNode =
result = copyNode(root)
if getMagic(root) == op:
# BUGFIX: forget to copy prc
addSon(result, copyNode(root.sons[0]))
flattenTreeAux(result, root, op)
proc swapOperands*(op: PNode) =
proc swapOperands*(op: PNode) =
var tmp = op.sons[1]
op.sons[1] = op.sons[2]
op.sons[2] = tmp
proc isRange*(n: PNode): bool {.inline.} =
if n.kind == nkInfix:
proc isRange*(n: PNode): bool {.inline.} =
if n.kind in nkCallKinds:
if n[0].kind == nkIdent and n[0].ident.id == ord(wDotDot) or
n[0].kind in {nkClosedSymChoice, nkOpenSymChoice} and
n[0].kind in {nkClosedSymChoice, nkOpenSymChoice} and
n[0][1].sym.name.id == ord(wDotDot):
result = true
proc whichPragma*(n: PNode): TSpecialWord =
proc whichPragma*(n: PNode): TSpecialWord =
let key = if n.kind == nkExprColonExpr: n.sons[0] else: n
if key.kind == nkIdent: result = whichKeyword(key.ident)

View File

@@ -28,8 +28,9 @@ proc hashTree(n: PNode): THash =
of nkFloatLit..nkFloat64Lit:
if (n.floatVal >= - 1000000.0) and (n.floatVal <= 1000000.0):
result = result !& toInt(n.floatVal)
of nkStrLit..nkTripleStrLit:
result = result !& hash(n.strVal)
of nkStrLit..nkTripleStrLit:
if not n.strVal.isNil:
result = result !& hash(n.strVal)
else:
for i in countup(0, sonsLen(n) - 1):
result = result !& hashTree(n.sons[i])

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
#
#
# The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -68,7 +68,6 @@ proc renderType(n: PNode): string =
assert n[i].kind == nkIdent
result.add(',' & typeStr)
of nkTupleTy:
assert len(n) > 0
result = "tuple["
for i in 0 .. <len(n): result.add(renderType(n[i]) & ',')
result[<len(result)] = ']'

View File

@@ -1,7 +1,7 @@
#
#
# The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -16,7 +16,8 @@ import ast except getstr
import
strutils, astalgo, msgs, vmdef, vmgen, nimsets, types, passes, unsigned,
parser, vmdeps, idents, trees, renderer, options, transf, parseutils
parser, vmdeps, idents, trees, renderer, options, transf, parseutils,
vmmarshal
from semfold import leValueConv, ordinalValToString
from evaltempl import evalTemplate
@@ -123,8 +124,12 @@ proc createStrKeepNode(x: var TFullReg) =
if x.node.isNil:
x.node = newNode(nkStrLit)
elif x.node.kind == nkNilLit:
when defined(useNodeIds):
let id = x.node.id
system.reset(x.node[])
x.node.kind = nkStrLit
when defined(useNodeIds):
x.node.id = id
elif x.node.kind notin {nkStrLit..nkTripleStrLit} or
nfAllConst in x.node.flags:
# XXX this is hacky; tests/txmlgen triggers it:
@@ -154,7 +159,7 @@ proc moveConst(x: var TFullReg, y: TFullReg) =
of rkNodeAddr: x.nodeAddr = y.nodeAddr
# this seems to be the best way to model the reference semantics
# of PNimrodNode:
# of system.NimNode:
template asgnRef(x, y: expr) = moveConst(x, y)
proc copyValue(src: PNode): PNode =
@@ -235,7 +240,7 @@ proc pushSafePoint(f: PStackFrame; pc: int) =
proc popSafePoint(f: PStackFrame) = discard f.safePoints.pop()
proc cleanUpOnException(c: PCtx; tos: PStackFrame):
proc cleanUpOnException(c: PCtx; tos: PStackFrame):
tuple[pc: int, f: PStackFrame] =
let raisedType = c.currentExceptionA.typ.skipTypes(abstractPtrs)
var f = tos
@@ -253,7 +258,7 @@ proc cleanUpOnException(c: PCtx; tos: PStackFrame):
let exceptType = c.types[c.code[pc2].regBx-wordExcess].skipTypes(
abstractPtrs)
if inheritanceDiff(exceptType, raisedType) <= 0:
# mark exception as handled but keep it in B for
# mark exception as handled but keep it in B for
# the getCurrentException() builtin:
c.currentExceptionB = c.currentExceptionA
c.currentExceptionA = nil
@@ -345,14 +350,14 @@ proc opConv*(dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType): bool =
if dest.kind != rkFloat:
myreset(dest); dest.kind = rkFloat
case skipTypes(srctyp, abstractRange).kind
of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyBool, tyChar:
of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyBool, tyChar:
dest.floatVal = toFloat(src.intVal.int)
else:
dest.floatVal = src.floatVal
else:
asgnComplex(dest, src)
proc compile(c: PCtx, s: PSym): int =
proc compile(c: PCtx, s: PSym): int =
result = vmgen.genProc(c, s)
when debugEchoCode: c.echoCode result
#c.echoCode
@@ -367,11 +372,6 @@ template handleJmpBack() {.dirty.} =
globalError(c.debug[pc], errTooManyIterations)
dec(c.loopIterations)
proc skipColon(n: PNode): PNode =
result = n
if n.kind == nkExprColonExpr:
result = n.sons[1]
proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
var pc = start
var tos = tos
@@ -392,10 +392,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
pc = tos.comesFrom
tos = tos.next
let retVal = regs[0]
if tos.isNil:
if tos.isNil:
#echo "RET ", retVal.rendertree
return retVal
move(regs, tos.slots)
assert c.code[pc].opcode in {opcIndCall, opcIndCallAsgn}
if c.code[pc].opcode == opcIndCallAsgn:
@@ -649,7 +649,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
of opcSubu:
decodeBC(rkInt)
regs[ra].intVal = regs[rb].intVal -% regs[rc].intVal
of opcMulu:
of opcMulu:
decodeBC(rkInt)
regs[ra].intVal = regs[rb].intVal *% regs[rc].intVal
of opcDivu:
@@ -722,7 +722,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
of opcLeSet:
decodeBC(rkInt)
regs[ra].intVal = ord(containsSets(regs[rb].node, regs[rc].node))
of opcEqSet:
of opcEqSet:
decodeBC(rkInt)
regs[ra].intVal = ord(equalSets(regs[rb].node, regs[rc].node))
of opcLtSet:
@@ -733,9 +733,9 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
of opcMulSet:
decodeBC(rkNode)
createSet(regs[ra])
move(regs[ra].node.sons,
move(regs[ra].node.sons,
nimsets.intersectSets(regs[rb].node, regs[rc].node).sons)
of opcPlusSet:
of opcPlusSet:
decodeBC(rkNode)
createSet(regs[ra])
move(regs[ra].node.sons,
@@ -749,7 +749,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
decodeBC(rkNode)
createSet(regs[ra])
move(regs[ra].node.sons,
nimsets.symdiffSets(regs[rb].node, regs[rc].node).sons)
nimsets.symdiffSets(regs[rb].node, regs[rc].node).sons)
of opcConcatStr:
decodeBC(rkNode)
createStr regs[ra]
@@ -772,10 +772,14 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
stackTrace(c, tos, pc, errNilAccess)
of opcEcho:
let rb = instr.regB
for i in ra..ra+rb-1:
#if regs[i].kind != rkNode: debug regs[i]
write(stdout, regs[i].node.strVal)
writeln(stdout, "")
if rb == 1:
msgWriteln(regs[ra].node.strVal)
else:
var outp = ""
for i in ra..ra+rb-1:
#if regs[i].kind != rkNode: debug regs[i]
outp.add(regs[i].node.strVal)
msgWriteln(outp)
of opcContainsSet:
decodeBC(rkInt)
regs[ra].intVal = ord(inSet(regs[rb].node, regs[rc].regToNode))
@@ -785,7 +789,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
assert c.code[pc].opcode == opcSubStr
let rd = c.code[pc].regA
createStr regs[ra]
regs[ra].node.strVal = substr(regs[rb].node.strVal,
regs[ra].node.strVal = substr(regs[rb].node.strVal,
regs[rc].intVal.int, regs[rd].intVal.int)
of opcParseFloat:
decodeBC(rkInt)
@@ -806,7 +810,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
leValueConv(regs[ra].regToNode, regs[rc].regToNode)):
stackTrace(c, tos, pc, errGenerated,
msgKindToString(errIllegalConvFromXtoY) % [
"unknown type" , "unknown type"])
$regs[ra].regToNode, "[" & $regs[rb].regToNode & ".." & $regs[rc].regToNode & "]"])
of opcIndCall, opcIndCallAsgn:
# dest = call regStart, n; where regStart = fn, arg1, ...
let rb = instr.regB
@@ -888,12 +892,12 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
# we know the next instruction is a 'fjmp':
let branch = c.constants[instr.regBx-wordExcess]
var cond = false
for j in countup(0, sonsLen(branch) - 2):
if overlap(regs[ra].regToNode, branch.sons[j]):
for j in countup(0, sonsLen(branch) - 2):
if overlap(regs[ra].regToNode, branch.sons[j]):
cond = true
break
assert c.code[pc+1].opcode == opcFJmp
inc pc
inc pc
# we skip this instruction so that the final 'inc(pc)' skips
# the following jump
if not cond:
@@ -984,6 +988,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
let rb = instr.regBx - wordExcess
let cnst = c.constants.sons[rb]
if fitsRegister(cnst.typ):
myreset(regs[ra])
putIntoReg(regs[ra], cnst)
else:
ensureKind(rkNode)
@@ -1011,7 +1016,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
of opcQuit:
if c.mode in {emRepl, emStaticExpr, emStaticStmt}:
message(c.debug[pc], hintQuitCalled)
quit(int(getOrdValue(regs[ra].regToNode)))
msgQuit(int8(getOrdValue(regs[ra].regToNode)))
else:
return TFullReg(kind: rkNone)
of opcSetLenStr:
@@ -1034,7 +1039,14 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
decodeB(rkNode)
let newLen = regs[rb].intVal.int
if regs[ra].node.isNil: stackTrace(c, tos, pc, errNilAccess)
else: setLen(regs[ra].node.sons, newLen)
else:
let oldLen = regs[ra].node.len
setLen(regs[ra].node.sons, newLen)
if oldLen < newLen:
# XXX This is still not entirely correct
# set to default value:
for i in oldLen .. <newLen:
regs[ra].node.sons[i] = newNodeI(nkEmpty, c.debug[pc])
of opcSwap:
let rb = instr.regB
if regs[ra].kind == regs[rb].kind:
@@ -1086,14 +1098,20 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
of opcNAdd:
decodeBC(rkNode)
var u = regs[rb].node
u.add(regs[rc].node)
if u.kind notin {nkEmpty..nkNilLit}:
u.add(regs[rc].node)
else:
stackTrace(c, tos, pc, errGenerated, "cannot add to node kind: " & $u.kind)
regs[ra].node = u
of opcNAddMultiple:
decodeBC(rkNode)
let x = regs[rc].node
var u = regs[rb].node
# XXX can be optimized:
for i in 0.. <x.len: u.add(x.sons[i])
if u.kind notin {nkEmpty..nkNilLit}:
# XXX can be optimized:
for i in 0.. <x.len: u.add(x.sons[i])
else:
stackTrace(c, tos, pc, errGenerated, "cannot add to node kind: " & $u.kind)
regs[ra].node = u
of opcNKind:
decodeB(rkInt)
@@ -1126,7 +1144,21 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
else:
stackTrace(c, tos, pc, errFieldXNotFound, "ident")
of opcNGetType:
internalError(c.debug[pc], "unknown opcode " & $instr.opcode)
let rb = instr.regB
let rc = instr.regC
if rc == 0:
ensureKind(rkNode)
if regs[rb].kind == rkNode and regs[rb].node.typ != nil:
regs[ra].node = opMapTypeToAst(regs[rb].node.typ, c.debug[pc])
else:
stackTrace(c, tos, pc, errGenerated, "node has no type")
else:
# typeKind opcode:
ensureKind(rkInt)
if regs[rb].kind == rkNode and regs[rb].node.typ != nil:
regs[ra].intVal = ord(regs[rb].node.typ.kind)
#else:
# stackTrace(c, tos, pc, errGenerated, "node has no type")
of opcNStrVal:
decodeB(rkNode)
createStr regs[ra]
@@ -1244,7 +1276,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
of opcNSetIntVal:
decodeB(rkNode)
var dest = regs[ra].node
if dest.kind in {nkCharLit..nkInt64Lit} and
if dest.kind in {nkCharLit..nkInt64Lit} and
regs[rb].kind in {rkInt}:
dest.intVal = regs[rb].intVal
else:
@@ -1252,24 +1284,24 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
of opcNSetFloatVal:
decodeB(rkNode)
var dest = regs[ra].node
if dest.kind in {nkFloatLit..nkFloat64Lit} and
if dest.kind in {nkFloatLit..nkFloat64Lit} and
regs[rb].kind in {rkFloat}:
dest.floatVal = regs[rb].floatVal
else:
else:
stackTrace(c, tos, pc, errFieldXNotFound, "floatVal")
of opcNSetSymbol:
decodeB(rkNode)
var dest = regs[ra].node
if dest.kind == nkSym and regs[rb].node.kind == nkSym:
dest.sym = regs[rb].node.sym
else:
else:
stackTrace(c, tos, pc, errFieldXNotFound, "symbol")
of opcNSetIdent:
decodeB(rkNode)
var dest = regs[ra].node
if dest.kind == nkIdent and regs[rb].node.kind == nkIdent:
dest.ident = regs[rb].node.ident
else:
else:
stackTrace(c, tos, pc, errFieldXNotFound, "ident")
of opcNSetType:
decodeB(rkNode)
@@ -1280,7 +1312,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
of opcNSetStrVal:
decodeB(rkNode)
var dest = regs[ra].node
if dest.kind in {nkStrLit..nkTripleStrLit} and
if dest.kind in {nkStrLit..nkTripleStrLit} and
regs[rb].kind in {rkNode}:
dest.strVal = regs[rb].node.strVal
else:
@@ -1333,6 +1365,19 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
while typ.kind == tyTypeDesc and typ.len > 0: typ = typ.sons[0]
createStr regs[ra]
regs[ra].node.strVal = typ.typeToString(preferExported)
of opcMarshalLoad:
let ra = instr.regA
let rb = instr.regB
inc pc
let typ = c.types[c.code[pc].regBx - wordExcess]
putIntoReg(regs[ra], loadAny(regs[rb].node.strVal, typ))
of opcMarshalStore:
decodeB(rkNode)
inc pc
let typ = c.types[c.code[pc].regBx - wordExcess]
createStrKeepNode(regs[ra])
if regs[ra].node.strVal.isNil: regs[ra].node.strVal = newStringOfCap(1000)
storeAny(regs[ra].node.strVal, typ, regs[rb].regToNode)
inc pc
proc execute(c: PCtx, start: int): PNode =
@@ -1363,9 +1408,11 @@ var
globalCtx: PCtx
proc setupGlobalCtx(module: PSym) =
if globalCtx.isNil: globalCtx = newCtx(module)
else: refresh(globalCtx, module)
registerAdditionalOps(globalCtx)
if globalCtx.isNil:
globalCtx = newCtx(module)
registerAdditionalOps(globalCtx)
else:
refresh(globalCtx, module)
proc myOpen(module: PSym): PPassContext =
#var c = newEvalContext(module, emRepl)
@@ -1404,8 +1451,9 @@ proc evalConstExprAux(module, prc: PSym, n: PNode, mode: TEvalMode): PNode =
newSeq(tos.slots, c.prc.maxSlots)
#for i in 0 .. <c.prc.maxSlots: tos.slots[i] = newNode(nkEmpty)
result = rawExecute(c, start, tos).regToNode
if result.info.line < 0: result.info = n.info
proc evalConstExpr*(module: PSym, e: PNode): PNode =
proc evalConstExpr*(module: PSym, e: PNode): PNode =
result = evalConstExprAux(module, nil, e, emConst)
proc evalStaticExpr*(module: PSym, e: PNode, prc: PSym): PNode =
@@ -1435,7 +1483,9 @@ proc evalMacroCall*(module: PSym, n, nOrig: PNode, sym: PSym): PNode =
# immediate macros can bypass any type and arity checking so we check the
# arity here too:
if sym.typ.len > n.safeLen and sym.typ.len > 1:
globalError(n.info, "got $#, but expected $# argument(s)" % [$ <n.safeLen, $ <sym.typ.len])
globalError(n.info, "in call '$#' got $#, but expected $# argument(s)" % [
n.renderTree,
$ <n.safeLen, $ <sym.typ.len])
setupGlobalCtx(module)
var c = globalCtx
@@ -1463,6 +1513,7 @@ proc evalMacroCall*(module: PSym, n, nOrig: PNode, sym: PSym): PNode =
# temporary storage:
#for i in L .. <maxSlots: tos.slots[i] = newNode(nkEmpty)
result = rawExecute(c, start, tos).regToNode
if result.info.line < 0: result.info = n.info
if cyclicTree(result): globalError(n.info, errCyclicTree)
dec(evalMacroCounter)
c.callsite = nil

View File

@@ -16,7 +16,7 @@ const
byteExcess* = 128 # we use excess-K for immediates
wordExcess* = 32768
MaxLoopIterations* = 500_000 # max iterations of all loops
MaxLoopIterations* = 1500_000 # max iterations of all loops
type
@@ -29,7 +29,7 @@ type
opcRet, # return
opcYldYoid, # yield with no value
opcYldVal, # yield with a value
opcAsgnInt,
opcAsgnStr,
opcAsgnFloat,
@@ -48,8 +48,8 @@ type
opcWrDeref,
opcWrStrIdx,
opcLdStrIdx, # a = b[c]
opcAddInt,
opcAddInt,
opcAddImmInt,
opcSubInt,
opcSubImmInt,
@@ -58,36 +58,37 @@ type
opcIncl, opcInclRange, opcExcl, opcCard, opcMulInt, opcDivInt, opcModInt,
opcAddFloat, opcSubFloat, opcMulFloat, opcDivFloat, opcShrInt, opcShlInt,
opcBitandInt, opcBitorInt, opcBitxorInt, opcAddu, opcSubu, opcMulu,
opcDivu, opcModu, opcEqInt, opcLeInt, opcLtInt, opcEqFloat,
opcLeFloat, opcLtFloat, opcLeu, opcLtu, opcEqRef, opcEqNimrodNode, opcXor,
opcNot, opcUnaryMinusInt, opcUnaryMinusFloat, opcBitnotInt,
opcBitandInt, opcBitorInt, opcBitxorInt, opcAddu, opcSubu, opcMulu,
opcDivu, opcModu, opcEqInt, opcLeInt, opcLtInt, opcEqFloat,
opcLeFloat, opcLtFloat, opcLeu, opcLtu, opcEqRef, opcEqNimrodNode, opcXor,
opcNot, opcUnaryMinusInt, opcUnaryMinusFloat, opcBitnotInt,
opcEqStr, opcLeStr, opcLtStr, opcEqSet, opcLeSet, opcLtSet,
opcMulSet, opcPlusSet, opcMinusSet, opcSymdiffSet, opcConcatStr,
opcContainsSet, opcRepr, opcSetLenStr, opcSetLenSeq,
opcSwap, opcIsNil, opcOf, opcIs,
opcSubStr, opcParseFloat, opcConv, opcCast, opcQuit, opcReset,
opcSubStr, opcParseFloat, opcConv, opcCast,
opcQuit, opcReset,
opcNarrowS, opcNarrowU,
opcAddStrCh,
opcAddStrStr,
opcAddSeqElem,
opcRangeChck,
opcNAdd,
opcNAddMultiple,
opcNKind,
opcNIntVal,
opcNFloatVal,
opcNSymbol,
opcNKind,
opcNIntVal,
opcNFloatVal,
opcNSymbol,
opcNIdent,
opcNGetType,
opcNStrVal,
opcNSetIntVal,
opcNSetFloatVal, opcNSetSymbol, opcNSetIdent, opcNSetType, opcNSetStrVal,
opcNNewNimNode, opcNCopyNimNode, opcNCopyNimTree, opcNDel, opcGenSym,
opcSlurp,
opcGorge,
opcParseExprToAst,
@@ -100,7 +101,7 @@ type
opcEqIdent,
opcStrToIdent,
opcIdentToStr,
opcEcho,
opcIndCall, # dest = call regStart, n; where regStart = fn, arg1, ...
opcIndCallAsgn, # dest = call regStart, n; where regStart = fn, arg1, ...
@@ -110,7 +111,7 @@ type
opcNSetChild,
opcCallSite,
opcNewStr,
opcTJmp, # jump Bx if A != 0
opcFJmp, # jump Bx if A == 0
opcJmp, # jump Bx
@@ -132,7 +133,8 @@ type
opcLdImmInt, # dest = immediate value
opcNBindSym,
opcSetType, # dest.typ = types[Bx]
opcTypeTrait
opcTypeTrait,
opcMarshalLoad, opcMarshalStore
TBlock* = object
label*: PSym
@@ -178,13 +180,13 @@ type
slots*: pointer
currentException*: PNode
VmCallback* = proc (args: VmArgs) {.closure.}
PCtx* = ref TCtx
TCtx* = object of passes.TPassContext # code gen context
code*: seq[TInstr]
debug*: seq[TLineInfo] # line info for every instruction; kept separate
# to not slow down interpretation
globals*: PNode #
globals*: PNode #
constants*: PNode # constant data
types*: seq[PType] # some instructions reference types (e.g. 'except')
currentExceptionA*, currentExceptionB*: PNode
@@ -203,7 +205,7 @@ type
TPosition* = distinct int
PEvalContext* = PCtx
proc newCtx*(module: PSym): PCtx =
PCtx(code: @[], debug: @[],
globals: newNode(nkStmtListExpr), constants: newNode(nkStmtList), types: @[],
@@ -213,6 +215,7 @@ proc newCtx*(module: PSym): PCtx =
proc refresh*(c: PCtx, module: PSym) =
c.module = module
c.prc = PProc(blocks: @[])
c.loopIterations = MaxLoopIterations
proc registerCallback*(c: PCtx; name: string; callback: VmCallback) =
c.callbacks.add((name, callback))
@@ -220,7 +223,8 @@ proc registerCallback*(c: PCtx; name: string; callback: VmCallback) =
const
firstABxInstr* = opcTJmp
largeInstrs* = { # instructions which use 2 int32s instead of 1:
opcSubStr, opcConv, opcCast, opcNewSeq, opcOf}
opcSubStr, opcConv, opcCast, opcNewSeq, opcOf,
opcMarshalLoad, opcMarshalStore}
slotSomeTemp* = slotTempUnknown
relativeJumps* = {opcTJmp, opcFJmp, opcJmp, opcJmpBack}

View File

@@ -1,13 +1,13 @@
#
#
# The Nim Compiler
# (c) Copyright 2013 Andreas Rumpf
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
import ast, types, msgs, osproc, streams, options
import ast, types, msgs, osproc, streams, options, idents
proc readOutput(p: Process): string =
result = ""
@@ -19,13 +19,16 @@ proc readOutput(p: Process): string =
discard p.waitForExit
proc opGorge*(cmd, input: string): string =
var p = startCmd(cmd)
if input.len != 0:
p.inputStream.write(input)
p.inputStream.close()
result = p.readOutput
try:
var p = startProcess(cmd, options={poEvalCommand})
if input.len != 0:
p.inputStream.write(input)
p.inputStream.close()
result = p.readOutput
except IOError, OSError:
result = ""
proc opSlurp*(file: string, info: TLineInfo, module: PSym): string =
proc opSlurp*(file: string, info: TLineInfo, module: PSym): string =
try:
let filename = file.findFile
result = readFile(filename)
@@ -36,3 +39,124 @@ proc opSlurp*(file: string, info: TLineInfo, module: PSym): string =
except IOError:
localError(info, errCannotOpenFile, file)
result = ""
proc atomicTypeX(name: string; t: PType; info: TLineInfo): PNode =
let sym = newSym(skType, getIdent(name), t.owner, info)
sym.typ = t
result = newSymNode(sym)
result.typ = t
proc mapTypeToAst(t: PType, info: TLineInfo; allowRecursion=false): PNode
proc mapTypeToBracket(name: string; t: PType; info: TLineInfo): PNode =
result = newNodeIT(nkBracketExpr, info, t)
result.add atomicTypeX(name, t, info)
for i in 0 .. < t.len:
if t.sons[i] == nil:
let void = atomicTypeX("void", t, info)
void.typ = newType(tyEmpty, t.owner)
result.add void
else:
result.add mapTypeToAst(t.sons[i], info)
proc mapTypeToAst(t: PType, info: TLineInfo; allowRecursion=false): PNode =
template atomicType(name): expr = atomicTypeX(name, t, info)
case t.kind
of tyNone: result = atomicType("none")
of tyBool: result = atomicType("bool")
of tyChar: result = atomicType("char")
of tyNil: result = atomicType("nil")
of tyExpr: result = atomicType("expr")
of tyStmt: result = atomicType("stmt")
of tyEmpty: result = atomicType"void"
of tyArrayConstr, tyArray:
result = newNodeIT(nkBracketExpr, info, t)
result.add atomicType("array")
result.add mapTypeToAst(t.sons[0], info)
result.add mapTypeToAst(t.sons[1], info)
of tyTypeDesc:
if t.base != nil:
result = newNodeIT(nkBracketExpr, info, t)
result.add atomicType("typeDesc")
result.add mapTypeToAst(t.base, info)
else:
result = atomicType"typeDesc"
of tyGenericInvocation:
result = newNodeIT(nkBracketExpr, info, t)
for i in 0 .. < t.len:
result.add mapTypeToAst(t.sons[i], info)
of tyGenericInst, tyGenericBody, tyOrdinal, tyUserTypeClassInst:
result = mapTypeToAst(t.lastSon, info)
of tyDistinct:
if allowRecursion:
result = mapTypeToBracket("distinct", t, info)
else:
result = atomicType(t.sym.name.s)
of tyGenericParam, tyForward: result = atomicType(t.sym.name.s)
of tyObject:
if allowRecursion:
result = newNodeIT(nkObjectTy, info, t)
if t.sons[0] == nil:
result.add ast.emptyNode
else:
result.add mapTypeToAst(t.sons[0], info)
result.add copyTree(t.n)
else:
result = atomicType(t.sym.name.s)
of tyEnum:
result = newNodeIT(nkEnumTy, info, t)
result.add copyTree(t.n)
of tyTuple: result = mapTypeToBracket("tuple", t, info)
of tySet: result = mapTypeToBracket("set", t, info)
of tyPtr: result = mapTypeToBracket("ptr", t, info)
of tyRef: result = mapTypeToBracket("ref", t, info)
of tyVar: result = mapTypeToBracket("var", t, info)
of tySequence: result = mapTypeToBracket("seq", t, info)
of tyProc: result = mapTypeToBracket("proc", t, info)
of tyOpenArray: result = mapTypeToBracket("openArray", t, info)
of tyRange:
result = newNodeIT(nkBracketExpr, info, t)
result.add atomicType("range")
result.add t.n.sons[0].copyTree
result.add t.n.sons[1].copyTree
of tyPointer: result = atomicType"pointer"
of tyString: result = atomicType"string"
of tyCString: result = atomicType"cstring"
of tyInt: result = atomicType"int"
of tyInt8: result = atomicType"int8"
of tyInt16: result = atomicType"int16"
of tyInt32: result = atomicType"int32"
of tyInt64: result = atomicType"int64"
of tyFloat: result = atomicType"float"
of tyFloat32: result = atomicType"float32"
of tyFloat64: result = atomicType"float64"
of tyFloat128: result = atomicType"float128"
of tyUInt: result = atomicType"uint"
of tyUInt8: result = atomicType"uint8"
of tyUInt16: result = atomicType"uint16"
of tyUInt32: result = atomicType"uint32"
of tyUInt64: result = atomicType"uint64"
of tyBigNum: result = atomicType"bignum"
of tyConst: result = mapTypeToBracket("const", t, info)
of tyMutable: result = mapTypeToBracket("mutable", t, info)
of tyVarargs: result = mapTypeToBracket("varargs", t, info)
of tyIter: result = mapTypeToBracket("iter", t, info)
of tyProxy: result = atomicType"error"
of tyBuiltInTypeClass: result = mapTypeToBracket("builtinTypeClass", t, info)
of tyUserTypeClass:
result = mapTypeToBracket("concept", t, info)
result.add t.n.copyTree
of tyCompositeTypeClass: result = mapTypeToBracket("compositeTypeClass", t, info)
of tyAnd: result = mapTypeToBracket("and", t, info)
of tyOr: result = mapTypeToBracket("or", t, info)
of tyNot: result = mapTypeToBracket("not", t, info)
of tyAnything: result = atomicType"anything"
of tyStatic, tyFromExpr, tyFieldAccessor:
result = newNodeIT(nkBracketExpr, info, t)
result.add atomicType("static")
if t.n != nil:
result.add t.n.copyTree
proc opMapTypeToAst*(t: PType; info: TLineInfo): PNode =
result = mapTypeToAst(t, info, true)

View File

@@ -1,7 +1,7 @@
#
#
# The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -15,7 +15,7 @@
# this doesn't matter. However it matters for strings and other complex
# types that use the 'node' field; the reason is that slots are
# re-used in a register based VM. Example:
#
#
# .. code-block:: nim
# let s = a & b # no matter what, create fresh node
# s = a & b # no matter what, keep the node
@@ -64,18 +64,23 @@ proc codeListing(c: PCtx, result: var string, start=0; last = -1) =
let y = c.code[i+1]
let z = c.code[i+2]
result.addf("\t$#\tr$#, r$#, $#, $#", ($opc).substr(3), x.regA, x.regB,
c.types[y.regBx-wordExcess].typeToString,
c.types[y.regBx-wordExcess].typeToString,
c.types[z.regBx-wordExcess].typeToString)
inc i, 2
elif opc < firstABxInstr:
result.addf("\t$#\tr$#, r$#, r$#", ($opc).substr(3), x.regA,
result.addf("\t$#\tr$#, r$#, r$#", ($opc).substr(3), x.regA,
x.regB, x.regC)
elif opc in relativeJumps:
result.addf("\t$#\tr$#, L$#", ($opc).substr(3), x.regA,
i+x.regBx-wordExcess)
elif opc in {opcLdConst, opcAsgnConst}:
result.addf("\t$#\tr$#, $#", ($opc).substr(3), x.regA,
result.addf("\t$#\tr$#, $#", ($opc).substr(3), x.regA,
c.constants[x.regBx-wordExcess].renderTree)
elif opc in {opcMarshalLoad, opcMarshalStore}:
let y = c.code[i+1]
result.addf("\t$#\tr$#, r$#, $#", ($opc).substr(3), x.regA, x.regB,
c.types[y.regBx-wordExcess].typeToString)
inc i
else:
result.addf("\t$#\tr$#, $#", ($opc).substr(3), x.regA, x.regBx-wordExcess)
result.add("\t#")
@@ -117,7 +122,7 @@ proc gABx(c: PCtx; n: PNode; opc: TOpcode; a: TRegister = 0; bx: int) =
# Applies `opc` to `bx` and stores it into register `a`
# `bx` must be signed and in the range [-32767, 32768]
if bx >= -32767 and bx <= 32768:
let ins = (opc.uint32 or a.uint32 shl 8'u32 or
let ins = (opc.uint32 or a.uint32 shl 8'u32 or
(bx+wordExcess).uint32 shl 16'u32).TInstr
c.code.add(ins)
c.debug.add(n.info)
@@ -174,7 +179,7 @@ proc getTemp(c: PCtx; typ: PType): TRegister =
if c.slots[i].kind == k and not c.slots[i].inUse:
c.slots[i].inUse = true
return TRegister(i)
# if register pressure is high, we re-use more aggressively:
if c.maxSlots >= HighRegisterPressure:
for i in 0 .. c.maxSlots-1:
@@ -208,7 +213,7 @@ proc getTempRange(c: PCtx; n: int; kind: TSlotKind): TRegister =
result = TRegister(c.maxSlots)
inc c.maxSlots, n
for k in result .. result+n-1: c.slots[k] = (inUse: true, kind: kind)
proc freeTempRange(c: PCtx; start: TRegister, n: int) =
for i in start .. start+n-1: c.freeTemp(TRegister(i))
@@ -217,7 +222,7 @@ template withTemp(tmp, typ: expr, body: stmt) {.immediate, dirty.} =
body
c.freeTemp(tmp)
proc popBlock(c: PCtx; oldLen: int) =
proc popBlock(c: PCtx; oldLen: int) =
for f in c.prc.blocks[oldLen].fixups:
c.patch(f)
c.prc.blocks.setLen(oldLen)
@@ -368,7 +373,7 @@ proc sameConstant*(a, b: PNode): bool =
case a.kind
of nkSym: result = a.sym == b.sym
of nkIdent: result = a.ident.id == b.ident.id
of nkCharLit..nkInt64Lit: result = a.intVal == b.intVal
of nkCharLit..nkUInt64Lit: result = a.intVal == b.intVal
of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal
of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal
of nkType, nkNilLit: result = a.typ == b.typ
@@ -386,7 +391,7 @@ proc genLiteral(c: PCtx; n: PNode): int =
result = rawGenLiteral(c, n)
proc unused(n: PNode; x: TDest) {.inline.} =
if x >= 0:
if x >= 0:
#debug(n)
internalError(n.info, "not unused")
@@ -446,11 +451,11 @@ proc genTry(c: PCtx; n: PNode; dest: var TDest) =
var blen = len(it)
# first opcExcept contains the end label of the 'except' block:
let endExcept = c.xjmp(it, opcExcept, 0)
for j in countup(0, blen - 2):
for j in countup(0, blen - 2):
assert(it.sons[j].kind == nkType)
let typ = it.sons[j].typ.skipTypes(abstractPtrs-{tyTypeDesc})
c.gABx(it, opcExcept, 0, c.genType(typ))
if blen == 1:
if blen == 1:
# general except section:
c.gABx(it, opcExcept, 0, 0)
c.gen(it.lastSon, dest)
@@ -498,7 +503,7 @@ proc genCall(c: PCtx; n: PNode; dest: var TDest) =
template isGlobal(s: PSym): bool = sfGlobal in s.flags and s.kind != skForVar
proc isGlobal(n: PNode): bool = n.kind == nkSym and isGlobal(n.sym)
proc needsAsgnPatch(n: PNode): bool =
proc needsAsgnPatch(n: PNode): bool =
n.kind in {nkBracketExpr, nkDotExpr, nkCheckedFieldExpr,
nkDerefExpr, nkHiddenDeref} or (n.kind == nkSym and n.sym.isGlobal)
@@ -552,9 +557,9 @@ proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) =
proc genNew(c: PCtx; n: PNode) =
let dest = if needsAsgnPatch(n.sons[1]): c.getTemp(n.sons[1].typ)
else: c.genx(n.sons[1])
# we use the ref's base type here as the VM conflates 'ref object'
# we use the ref's base type here as the VM conflates 'ref object'
# and 'object' since internally we already have a pointer.
c.gABx(n, opcNew, dest,
c.gABx(n, opcNew, dest,
c.genType(n.sons[1].typ.skipTypes(abstractVar-{tyTypeDesc}).sons[0]))
c.genAsgnPatch(n.sons[1], dest)
c.freeTemp(dest)
@@ -604,7 +609,8 @@ proc genNarrowU(c: PCtx; n: PNode; dest: TDest) =
let t = skipTypes(n.typ, abstractVar-{tyTypeDesc})
# uint is uint64 in the VM, we we only need to mask the result for
# other unsigned types:
if t.kind in {tyUInt8..tyUInt32, tyInt8..tyInt32}:
if t.kind in {tyUInt8..tyUInt32, tyInt8..tyInt32} or
(t.kind == tyInt and t.size == 4):
c.gABC(n, opcNarrowU, dest, TRegister(t.size*8))
proc genBinaryABCnarrow(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
@@ -656,7 +662,7 @@ proc genUnaryStmt(c: PCtx; n: PNode; opc: TOpcode) =
proc genVarargsABC(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
if dest < 0: dest = getTemp(c, n.typ)
var x = c.getTempRange(n.len-1, slotTempStr)
for i in 1..n.len-1:
for i in 1..n.len-1:
var r: TRegister = x+i-1
c.gen(n.sons[i], r)
c.gABC(n, opc, dest, x, n.len-1)
@@ -680,7 +686,7 @@ proc genAddSubInt(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
genBinaryABC(c, n, dest, opc)
c.genNarrow(n, dest)
proc genConv(c: PCtx; n, arg: PNode; dest: var TDest; opc=opcConv) =
proc genConv(c: PCtx; n, arg: PNode; dest: var TDest; opc=opcConv) =
let tmp = c.genx(arg)
if dest < 0: dest = c.getTemp(n.typ)
c.gABC(n, opc, dest, tmp)
@@ -695,8 +701,7 @@ proc genCard(c: PCtx; n: PNode; dest: var TDest) =
c.gABC(n, opcCard, dest, tmp)
c.freeTemp(tmp)
proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
let m = n.sons[0].sym.magic
proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
case m
of mAnd: c.genAndOr(n, opcFJmp, dest)
of mOr: c.genAndOr(n, opcTJmp, dest)
@@ -741,9 +746,9 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
c.gABC(n, opcNewStr, dest, tmp)
c.freeTemp(tmp)
# XXX buggy
of mLengthOpenArray, mLengthArray, mLengthSeq:
of mLengthOpenArray, mLengthArray, mLengthSeq, mXLenSeq:
genUnaryABI(c, n, dest, opcLenSeq)
of mLengthStr:
of mLengthStr, mXLenStr:
genUnaryABI(c, n, dest, opcLenStr)
of mIncl, mExcl:
unused(n, dest)
@@ -790,13 +795,13 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
genUnaryABC(c, n, dest, opcUnaryMinusInt)
genNarrow(c, n, dest)
of mUnaryMinusF64: genUnaryABC(c, n, dest, opcUnaryMinusFloat)
of mUnaryPlusI, mUnaryPlusI64, mUnaryPlusF64: gen(c, n.sons[1], dest)
of mBitnotI, mBitnotI64:
of mUnaryPlusI, mUnaryPlusF64: gen(c, n.sons[1], dest)
of mBitnotI, mBitnotI64:
genUnaryABC(c, n, dest, opcBitnotInt)
genNarrowU(c, n, dest)
of mZe8ToI, mZe8ToI64, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64,
mToU8, mToU16, mToU32, mToFloat, mToBiggestFloat, mToInt,
mToBiggestInt, mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr,
mToU8, mToU16, mToU32, mToFloat, mToBiggestFloat, mToInt,
mToBiggestInt, mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr,
mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr:
genConv(c, n, n.sons[1], dest)
of mEqStr: genBinaryABC(c, n, dest, opcEqStr)
@@ -824,7 +829,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
c.gABC(n, if m == mSetLengthStr: opcSetLenStr else: opcSetLenSeq, d, tmp)
c.genAsgnPatch(n.sons[1], d)
c.freeTemp(tmp)
of mSwap:
of mSwap:
unused(n, dest)
var
d1 = c.genx(n.sons[1])
@@ -873,7 +878,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
c.freeTemp(tmp1)
c.freeTemp(tmp3)
c.genAsgnPatch(d2AsNode, d2)
c.freeTemp(d2)
c.freeTemp(d2)
of mReset:
unused(n, dest)
var d = c.genx(n.sons[1])
@@ -893,7 +898,8 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
of mHigh:
if dest < 0: dest = c.getTemp(n.typ)
let tmp = c.genx(n.sons[1])
if n.sons[1].typ.skipTypes(abstractVar-{tyTypeDesc}).kind == tyString:
case n.sons[1].typ.skipTypes(abstractVar-{tyTypeDesc}).kind:
of tyString, tyCString:
c.gABI(n, opcLenStr, dest, tmp, 1)
else:
c.gABI(n, opcLenSeq, dest, tmp, 1)
@@ -911,7 +917,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
of mAppendStrCh:
unused(n, dest)
genBinaryStmtVar(c, n, opcAddStrCh)
of mAppendStrStr:
of mAppendStrStr:
unused(n, dest)
genBinaryStmtVar(c, n, opcAddStrStr)
of mAppendSeqElem:
@@ -921,7 +927,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
genUnaryABC(c, n, dest, opcParseExprToAst)
of mParseStmtToAst:
genUnaryABC(c, n, dest, opcParseStmtToAst)
of mTypeTrait:
of mTypeTrait:
let tmp = c.genx(n.sons[1])
if dest < 0: dest = c.getTemp(n.typ)
c.gABx(n, opcSetType, tmp, c.genType(n.sons[1].typ))
@@ -948,24 +954,29 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
of mNFloatVal: genUnaryABC(c, n, dest, opcNFloatVal)
of mNSymbol: genUnaryABC(c, n, dest, opcNSymbol)
of mNIdent: genUnaryABC(c, n, dest, opcNIdent)
of mNGetType: genUnaryABC(c, n, dest, opcNGetType)
of mNGetType:
let tmp = c.genx(n.sons[1])
if dest < 0: dest = c.getTemp(n.typ)
c.gABC(n, opcNGetType, dest, tmp, if n[0].sym.name.s == "typeKind": 1 else: 0)
c.freeTemp(tmp)
#genUnaryABC(c, n, dest, opcNGetType)
of mNStrVal: genUnaryABC(c, n, dest, opcNStrVal)
of mNSetIntVal:
unused(n, dest)
genBinaryStmt(c, n, opcNSetIntVal)
of mNSetFloatVal:
of mNSetFloatVal:
unused(n, dest)
genBinaryStmt(c, n, opcNSetFloatVal)
of mNSetSymbol:
unused(n, dest)
genBinaryStmt(c, n, opcNSetSymbol)
of mNSetIdent:
of mNSetIdent:
unused(n, dest)
genBinaryStmt(c, n, opcNSetIdent)
of mNSetType:
unused(n, dest)
genBinaryStmt(c, n, opcNSetType)
of mNSetStrVal:
of mNSetStrVal:
unused(n, dest)
genBinaryStmt(c, n, opcNSetStrVal)
of mNNewNimNode: genBinaryABC(c, n, dest, opcNNewNimNode)
@@ -983,10 +994,10 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
of mEqIdent: genBinaryABC(c, n, dest, opcEqIdent)
of mEqNimrodNode: genBinaryABC(c, n, dest, opcEqNimrodNode)
of mNLineInfo: genUnaryABC(c, n, dest, opcNLineInfo)
of mNHint:
of mNHint:
unused(n, dest)
genUnaryStmt(c, n, opcNHint)
of mNWarning:
of mNWarning:
unused(n, dest)
genUnaryStmt(c, n, opcNWarning)
of mNError:
@@ -1001,7 +1012,8 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
if dest < 0: dest = c.getTemp(n.typ)
c.gABC(n, opcCallSite, dest)
of mNGenSym: genBinaryABC(c, n, dest, opcGenSym)
of mMinI, mMaxI, mMinI64, mMaxI64, mAbsF64, mMinF64, mMaxF64, mAbsI, mAbsI64:
of mMinI, mMaxI, mAbsF64, mMinF64, mMaxF64, mAbsI,
mAbsI64, mDotDot:
c.genCall(n, dest)
of mExpandToAst:
if n.len != 2:
@@ -1017,9 +1029,25 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
else:
globalError(n.info, "expandToAst requires a call expression")
else:
# mGCref, mGCunref,
# mGCref, mGCunref,
internalError(n.info, "cannot generate code for: " & $m)
proc genMarshalLoad(c: PCtx, n: PNode, dest: var TDest) =
## Signature: proc to*[T](data: string): T
if dest < 0: dest = c.getTemp(n.typ)
var tmp = c.genx(n.sons[1])
c.gABC(n, opcMarshalLoad, dest, tmp)
c.gABx(n, opcMarshalLoad, 0, c.genType(n.typ))
c.freeTemp(tmp)
proc genMarshalStore(c: PCtx, n: PNode, dest: var TDest) =
## Signature: proc `$$`*[T](x: T): string
if dest < 0: dest = c.getTemp(n.typ)
var tmp = c.genx(n.sons[1])
c.gABC(n, opcMarshalStore, dest, tmp)
c.gABx(n, opcMarshalStore, 0, c.genType(n.sons[1].typ))
c.freeTemp(tmp)
const
atomicTypes = {tyBool, tyChar,
tyExpr, tyStmt, tyTypeDesc, tyStatic,
@@ -1049,7 +1077,7 @@ proc unneededIndirection(n: PNode): bool =
n.typ.skipTypes(abstractInst-{tyTypeDesc}).kind == tyRef
proc genAddrDeref(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode;
flags: TGenFlags) =
flags: TGenFlags) =
# a nop for certain types
let isAddr = opc in {opcAddrNode, opcAddrReg}
let newflags = if isAddr: flags+{gfAddrOf} else: flags
@@ -1137,7 +1165,7 @@ proc checkCanEval(c: PCtx; n: PNode) =
# proc foo() = var x ...
let s = n.sym
if {sfCompileTime, sfGlobal} <= s.flags: return
if s.kind in {skVar, skTemp, skLet, skParam, skResult} and
if s.kind in {skVar, skTemp, skLet, skParam, skResult} and
not s.isOwnedBy(c.prc.sym) and s.owner != c.module:
cannotEval(n)
@@ -1244,8 +1272,8 @@ proc genGlobalInit(c: PCtx; n: PNode; s: PSym) =
c.globals.add(getNullValue(s.typ, n.info))
s.position = c.globals.len
# This is rather hard to support, due to the laziness of the VM code
# generator. See tests/compile/tmacro2 for why this is necesary:
# var decls{.compileTime.}: seq[PNimrodNode] = @[]
# generator. See tests/compile/tmacro2 for why this is necessary:
# var decls{.compileTime.}: seq[NimNode] = @[]
let dest = c.getTemp(s.typ)
c.gABx(n, opcLdGlobal, dest, s.position)
let tmp = c.genx(s.ast)
@@ -1331,32 +1359,32 @@ proc genArrAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
else:
genArrAccess2(c, n, dest, opcLdArr, flags)
proc getNullValueAux(obj: PNode, result: 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):
for i in countup(1, sonsLen(obj) - 1):
getNullValueAux(lastSon(obj.sons[i]), result)
of nkSym:
addSon(result, getNullValue(obj.sym.typ, result.info))
else: internalError(result.info, "getNullValueAux")
proc getNullValue(typ: PType, info: TLineInfo): PNode =
proc getNullValue(typ: PType, info: TLineInfo): PNode =
var t = skipTypes(typ, abstractRange-{tyTypeDesc})
result = emptyNode
case t.kind
of tyBool, tyEnum, tyChar, tyInt..tyInt64:
of tyBool, tyEnum, tyChar, tyInt..tyInt64:
result = newNodeIT(nkIntLit, info, t)
of tyUInt..tyUInt64:
result = newNodeIT(nkUIntLit, info, t)
of tyFloat..tyFloat128:
of tyFloat..tyFloat128:
result = newNodeIT(nkFloatLit, info, t)
of tyCString, tyString:
result = newNodeIT(nkStrLit, info, t)
of tyVar, tyPointer, tyPtr, tySequence, tyExpr,
tyStmt, tyTypeDesc, tyStatic, tyRef:
tyStmt, tyTypeDesc, tyStatic, tyRef, tyNil:
result = newNodeIT(nkNilLit, info, t)
of tyProc:
if t.callConv != ccClosure:
@@ -1365,7 +1393,7 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode =
result = newNodeIT(nkPar, info, t)
result.add(newNodeIT(nkNilLit, info, t))
result.add(newNodeIT(nkNilLit, info, t))
of tyObject:
of tyObject:
result = newNodeIT(nkPar, info, t)
getNullValueAux(t.n, result)
# initialize inherited fields:
@@ -1373,9 +1401,9 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode =
while base != nil:
getNullValueAux(skipTypes(base, skipPtrs).n, result)
base = base.sons[0]
of tyArray, tyArrayConstr:
of tyArray, tyArrayConstr:
result = newNodeIT(nkBracket, info, t)
for i in countup(0, int(lengthOrd(t)) - 1):
for i in countup(0, int(lengthOrd(t)) - 1):
addSon(result, getNullValue(elemType(t), info))
of tyTuple:
result = newNodeIT(nkPar, info, t)
@@ -1383,7 +1411,7 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode =
addSon(result, getNullValue(t.sons[i], info))
of tySet:
result = newNodeIT(nkCurly, info, t)
else: internalError("getNullValue: " & $t.kind)
else: internalError(info, "getNullValue: " & $t.kind)
proc ldNullOpcode(t: PType): TOpcode =
if fitsRegister(t): opcLdNullReg else: opcLdNull
@@ -1452,7 +1480,7 @@ proc genArrayConstr(c: PCtx, n: PNode, dest: var TDest) =
c.gABx(n, opcNewSeq, dest, c.genType(seqType))
c.gABx(n, opcNewSeq, tmp, 0)
c.freeTemp(tmp)
if n.len > 0:
var tmp = getTemp(c, intType)
c.gABx(n, opcLdNullReg, tmp, c.genType(intType))
@@ -1525,11 +1553,20 @@ proc matches(s: PSym; x: string): bool =
dec L
result = true
proc matches(s: PSym; y: varargs[string]): bool =
var s = s
var L = y.len-1
while L >= 0:
if s == nil or y[L].cmpIgnoreStyle(s.name.s) != 0: return false
s = if sfFromGeneric in s.flags: s.owner.owner else: s.owner
dec L
result = true
proc procIsCallback(c: PCtx; s: PSym): bool =
if s.offset < -1: return true
var i = -2
for key, value in items(c.callbacks):
if s.matches(key):
if s.matches(key):
doAssert s.offset == -1
s.offset = i
return true
@@ -1562,8 +1599,17 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
else:
internalError(n.info, "cannot generate code for: " & s.name.s)
of nkCallKinds:
if n.sons[0].kind == nkSym and n.sons[0].sym.magic != mNone:
genMagic(c, n, dest)
if n.sons[0].kind == nkSym:
let s = n.sons[0].sym
if s.magic != mNone:
genMagic(c, n, dest, s.magic)
elif matches(s, "stdlib", "marshal", "to"):
genMarshalLoad(c, n, dest)
elif matches(s, "stdlib", "marshal", "$$"):
genMarshalStore(c, n, dest)
else:
genCall(c, n, dest)
clearDest(c, n, dest)
else:
genCall(c, n, dest)
clearDest(c, n, dest)
@@ -1577,7 +1623,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
of nkNilLit:
if not n.typ.isEmptyType: genLit(c, getNullValue(n.typ, n.info), dest)
else: unused(n, dest)
of nkAsgn, nkFastAsgn:
of nkAsgn, nkFastAsgn:
unused(n, dest)
genAsgn(c, n.sons[0], n.sons[1], n.kind == nkAsgn)
of nkDotExpr: genObjAccess(c, n, dest, flags)
@@ -1602,7 +1648,8 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
genBreak(c, n)
of nkTryStmt: genTry(c, n, dest)
of nkStmtList:
unused(n, dest)
#unused(n, dest)
# XXX Fix this bug properly, lexim triggers it
for x in n: gen(c, x)
of nkStmtListExpr:
let L = n.len-1
@@ -1626,7 +1673,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
let s = n.sons[namePos].sym
discard genProc(c, s)
genLit(c, n.sons[namePos], dest)
of nkChckRangeF, nkChckRange64, nkChckRange:
of nkChckRangeF, nkChckRange64, nkChckRange:
let
tmp0 = c.genx(n.sons[0])
tmp1 = c.genx(n.sons[1])

View File

@@ -1,7 +1,7 @@
#
#
# The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.

283
compiler/vmmarshal.nim Normal file
View File

@@ -0,0 +1,283 @@
#
#
# The Nim Compiler
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Implements marshaling for the VM.
import streams, json, intsets, tables, ast, astalgo, idents, types, msgs
proc ptrToInt(x: PNode): int {.inline.} =
result = cast[int](x) # don't skip alignment
proc getField(n: PNode; position: int): PSym =
case n.kind
of nkRecList:
for i in countup(0, sonsLen(n) - 1):
result = getField(n.sons[i], position)
if result != nil: return
of nkRecCase:
result = getField(n.sons[0], position)
if result != nil: return
for i in countup(1, sonsLen(n) - 1):
case n.sons[i].kind
of nkOfBranch, nkElse:
result = getField(lastSon(n.sons[i]), position)
if result != nil: return
else: internalError(n.info, "getField(record case branch)")
of nkSym:
if n.sym.position == position: result = n.sym
else: discard
proc storeAny(s: var string; t: PType; a: PNode; stored: var IntSet)
proc storeObj(s: var string; typ: PType; x: PNode; stored: var IntSet) =
internalAssert x.kind in {nkObjConstr, nkPar}
let start = ord(x.kind == nkObjConstr)
for i in countup(start, sonsLen(x) - 1):
if i > start: s.add(", ")
var it = x.sons[i]
if it.kind == nkExprColonExpr:
internalAssert it.sons[0].kind == nkSym
let field = it.sons[0].sym
s.add(escapeJson(field.name.s))
s.add(": ")
storeAny(s, field.typ, it.sons[1], stored)
elif typ.n != nil:
let field = getField(typ.n, i)
s.add(escapeJson(field.name.s))
s.add(": ")
storeAny(s, field.typ, it, stored)
proc skipColon*(n: PNode): PNode =
result = n
if n.kind == nkExprColonExpr:
result = n.sons[1]
proc storeAny(s: var string; t: PType; a: PNode; stored: var IntSet) =
case t.kind
of tyNone: assert false
of tyBool: s.add($(a.intVal != 0))
of tyChar:
let ch = char(a.intVal)
if ch < '\128':
s.add(escapeJson($ch))
else:
s.add($int(ch))
of tyArray, tySequence:
if t.kind == tySequence and a.kind == nkNilLit: s.add("null")
else:
s.add("[")
for i in 0 .. a.len-1:
if i > 0: s.add(", ")
storeAny(s, t.elemType, a[i], stored)
s.add("]")
of tyTuple:
s.add("{")
for i in 0.. <t.len:
if i > 0: s.add(", ")
s.add("\"Field" & $i)
s.add("\": ")
storeAny(s, t.sons[i], a[i].skipColon, stored)
s.add("}")
of tyObject:
s.add("{")
storeObj(s, t, a, stored)
s.add("}")
of tySet:
s.add("[")
for i in 0.. <a.len:
if i > 0: s.add(", ")
if a[i].kind == nkRange:
var x = copyNode(a[i][0])
storeAny(s, t.lastSon, x, stored)
while x.intVal+1 <= a[i][1].intVal:
s.add(", ")
storeAny(s, t.lastSon, x, stored)
inc x.intVal
else:
storeAny(s, t.lastSon, a[i], stored)
s.add("]")
of tyRange, tyGenericInst: storeAny(s, t.lastSon, a, stored)
of tyEnum:
# we need a slow linear search because of enums with holes:
for e in items(t.n):
if e.sym.position == a.intVal:
s.add e.sym.name.s.escapeJson
break
of tyPtr, tyRef:
var x = a
if isNil(x) or x.kind == nkNilLit: s.add("null")
elif stored.containsOrIncl(x.ptrToInt):
# already stored, so we simply write out the pointer as an int:
s.add($x.ptrToInt)
else:
# else as a [value, key] pair:
# (reversed order for convenient x[0] access!)
s.add("[")
s.add($x.ptrToInt)
s.add(", ")
storeAny(s, t.lastSon, a, stored)
s.add("]")
of tyString, tyCString:
if a.kind == nkNilLit or a.strVal.isNil: s.add("null")
else: s.add(escapeJson(a.strVal))
of tyInt..tyInt64, tyUInt..tyUInt64: s.add($a.intVal)
of tyFloat..tyFloat128: s.add($a.floatVal)
else:
internalError a.info, "cannot marshal at compile-time " & t.typeToString
proc storeAny*(s: var string; t: PType; a: PNode) =
var stored = initIntSet()
storeAny(s, t, a, stored)
proc loadAny(p: var JsonParser, t: PType,
tab: var Table[BiggestInt, PNode]): PNode =
case t.kind
of tyNone: assert false
of tyBool:
case p.kind
of jsonFalse: result = newIntNode(nkIntLit, 0)
of jsonTrue: result = newIntNode(nkIntLit, 1)
else: raiseParseErr(p, "'true' or 'false' expected for a bool")
next(p)
of tyChar:
if p.kind == jsonString:
var x = p.str
if x.len == 1:
result = newIntNode(nkIntLit, ord(x[0]))
next(p)
return
elif p.kind == jsonInt:
result = newIntNode(nkIntLit, getInt(p))
next(p)
return
raiseParseErr(p, "string of length 1 expected for a char")
of tyEnum:
if p.kind == jsonString:
for e in items(t.n):
if e.sym.name.s == p.str:
result = newIntNode(nkIntLit, e.sym.position)
next(p)
return
raiseParseErr(p, "string expected for an enum")
of tyArray:
if p.kind != jsonArrayStart: raiseParseErr(p, "'[' expected for an array")
next(p)
result = newNode(nkBracket)
while p.kind != jsonArrayEnd and p.kind != jsonEof:
result.add loadAny(p, t.elemType, tab)
if p.kind == jsonArrayEnd: next(p)
else: raiseParseErr(p, "']' end of array expected")
of tySequence:
case p.kind
of jsonNull:
result = newNode(nkNilLit)
next(p)
of jsonArrayStart:
next(p)
result = newNode(nkBracket)
while p.kind != jsonArrayEnd and p.kind != jsonEof:
result.add loadAny(p, t.elemType, tab)
if p.kind == jsonArrayEnd: next(p)
else: raiseParseErr(p, "")
else:
raiseParseErr(p, "'[' expected for a seq")
of tyTuple:
if p.kind != jsonObjectStart: raiseParseErr(p, "'{' expected for an object")
next(p)
result = newNode(nkPar)
var i = 0
while p.kind != jsonObjectEnd and p.kind != jsonEof:
if p.kind != jsonString:
raiseParseErr(p, "string expected for a field name")
next(p)
if i >= t.len:
raiseParseErr(p, "too many fields to tuple type " & typeToString(t))
result.add loadAny(p, t.sons[i], tab)
inc i
if p.kind == jsonObjectEnd: next(p)
else: raiseParseErr(p, "'}' end of object expected")
of tyObject:
if p.kind != jsonObjectStart: raiseParseErr(p, "'{' expected for an object")
next(p)
result = newNode(nkPar)
result.sons = @[]
while p.kind != jsonObjectEnd and p.kind != jsonEof:
if p.kind != jsonString:
raiseParseErr(p, "string expected for a field name")
let field = lookupInRecord(t.n, getIdent(p.str))
if field.isNil:
raiseParseErr(p, "unknown field for object of type " & typeToString(t))
next(p)
if field.position >= result.sons.len:
setLen(result.sons, field.position+1)
result.sons[field.position] = loadAny(p, field.typ, tab)
if p.kind == jsonObjectEnd: next(p)
else: raiseParseErr(p, "'}' end of object expected")
of tySet:
if p.kind != jsonArrayStart: raiseParseErr(p, "'[' expected for a set")
next(p)
result = newNode(nkCurly)
while p.kind != jsonArrayEnd and p.kind != jsonEof:
result.add loadAny(p, t.lastSon, tab)
next(p)
if p.kind == jsonArrayEnd: next(p)
else: raiseParseErr(p, "']' end of array expected")
of tyPtr, tyRef:
case p.kind
of jsonNull:
result = newNode(nkNilLit)
next(p)
of jsonInt:
result = tab[p.getInt]
if result.isNil:
raiseParseErr(p, "cannot load object with address " & $p.getInt)
next(p)
of jsonArrayStart:
next(p)
if p.kind == jsonInt:
let idx = p.getInt
next(p)
result = loadAny(p, t.lastSon, tab)
tab[idx] = result
else: raiseParseErr(p, "index for ref type expected")
if p.kind == jsonArrayEnd: next(p)
else: raiseParseErr(p, "']' end of ref-address pair expected")
else: raiseParseErr(p, "int for pointer type expected")
of tyString, tyCString:
case p.kind
of jsonNull:
result = newNode(nkNilLit)
next(p)
of jsonString:
result = newStrNode(nkStrLit, p.str)
next(p)
else: raiseParseErr(p, "string expected")
of tyInt..tyInt64, tyUInt..tyUInt64:
if p.kind == jsonInt:
result = newIntNode(nkIntLit, getInt(p))
next(p)
return
raiseParseErr(p, "int expected")
of tyFloat..tyFloat128:
if p.kind == jsonFloat:
result = newFloatNode(nkFloatLit, getFloat(p))
next(p)
return
raiseParseErr(p, "float expected")
of tyRange, tyGenericInst: result = loadAny(p, t.lastSon, tab)
else:
internalError "cannot marshal at compile-time " & t.typeToString
proc loadAny*(s: string; t: PType): PNode =
var tab = initTable[BiggestInt, PNode]()
var p: JsonParser
open(p, newStringStream(s), "unknown file")
next(p)
result = loadAny(p, t, tab)
close(p)

View File

@@ -1,7 +1,7 @@
#
#
# The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -10,7 +10,7 @@
# Unforunately this cannot be a module yet:
#import vmdeps, vm
from math import sqrt, ln, log10, log2, exp, round, arccos, arcsin,
arctan, arctan2, cos, cosh, hypot, sinh, sin, tan, tanh, pow, trunc,
arctan, arctan2, cos, cosh, hypot, sinh, sin, tan, tanh, pow, trunc,
floor, ceil, fmod
from os import getEnv, existsEnv, dirExists, fileExists

Some files were not shown because too many files have changed in this diff Show More