mirror of
https://github.com/nim-lang/Nim.git
synced 2026-05-31 09:05:37 +00:00
Merge branch 'devel'
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -41,3 +41,4 @@ xcuserdata/
|
||||
/testresults.html
|
||||
/testresults.json
|
||||
testament.db
|
||||
/csources/
|
||||
|
||||
11
compiler.nimble
Normal file
11
compiler.nimble
Normal 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"
|
||||
686
compiler/ast.nim
686
compiler/ast.nim
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -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)
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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])
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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]
|
||||
@@ -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)
|
||||
|
||||
@@ -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"))
|
||||
|
||||
|
||||
@@ -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
89
compiler/forloops.nim
Normal 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)
|
||||
@@ -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
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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: "
|
||||
|
||||
@@ -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
@@ -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))
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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")
|
||||
|
||||
198
compiler/nimsuggest/nimsuggest.nim
Normal file
198
compiler/nimsuggest/nimsuggest.nim
Normal 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()
|
||||
17
compiler/nimsuggest/nimsuggest.nim.cfg
Normal file
17
compiler/nimsuggest/nimsuggest.nim.cfg
Normal 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
6
compiler/nodejs.nim
Normal file
@@ -0,0 +1,6 @@
|
||||
import os
|
||||
|
||||
proc findNodeJs*(): string =
|
||||
result = findExe("nodejs")
|
||||
if result == "":
|
||||
result = findExe("node")
|
||||
@@ -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,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
|
||||
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
43
compiler/plugins.nim
Normal 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)
|
||||
13
compiler/plugins/active.nim
Normal file
13
compiler/plugins/active.nim
Normal 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
|
||||
42
compiler/plugins/locals/locals.nim
Normal file
42
compiler/plugins/locals/locals.nim
Normal 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)
|
||||
@@ -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
@@ -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:"):
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
104
compiler/sem.nim
104
compiler/sem.nim
@@ -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!
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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.} =
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
@@ -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])
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 = @[]
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
@@ -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)] = ']'
|
||||
|
||||
141
compiler/vm.nim
141
compiler/vm.nim
@@ -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
|
||||
|
||||
@@ -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}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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
283
compiler/vmmarshal.nim
Normal 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)
|
||||
@@ -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
Reference in New Issue
Block a user