Merge branch 'master' of github.com:Araq/Nimrod into upstream

This commit is contained in:
Zahary Karadjov
2011-12-11 11:26:50 +02:00
25 changed files with 485 additions and 200 deletions

182
compiler/aliases.nim Normal file
View File

@@ -0,0 +1,182 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2011 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Simple alias analysis for the HLO and the code generators.
import
ast, astalgo, types, trees, intsets, msgs
type
TAnalysisResult* = enum
arNo, arMaybe, arYes
proc isPartOfAux(a, b: PType, marker: var TIntSet): TAnalysisResult
proc isPartOfAux(n: PNode, b: PType, marker: var TIntSet): TAnalysisResult =
result = arNo
case n.kind
of nkRecList:
for i in countup(0, sonsLen(n) - 1):
result = isPartOfAux(n.sons[i], b, marker)
if result == arYes: return
of nkRecCase:
assert(n.sons[0].kind == nkSym)
result = isPartOfAux(n.sons[0], b, marker)
if result == arYes: return
for i in countup(1, sonsLen(n) - 1):
case n.sons[i].kind
of nkOfBranch, nkElse:
result = isPartOfAux(lastSon(n.sons[i]), b, marker)
if result == arYes: return
else: internalError("isPartOfAux(record case branch)")
of nkSym:
result = isPartOfAux(n.sym.typ, b, marker)
else: internalError(n.info, "isPartOfAux()")
proc isPartOfAux(a, b: PType, marker: var TIntSet): TAnalysisResult =
result = arNo
if a == nil or b == nil: return
if ContainsOrIncl(marker, a.id): return
if compareTypes(a, b, dcEqIgnoreDistinct): return arYes
case a.kind
of tyObject:
result = isPartOfAux(a.sons[0], b, marker)
if result == arNo: result = isPartOfAux(a.n, b, marker)
of tyGenericInst, tyDistinct:
result = isPartOfAux(lastSon(a), b, marker)
of tyArray, tyArrayConstr, tySet, tyTuple:
for i in countup(0, sonsLen(a) - 1):
result = isPartOfAux(a.sons[i], b, marker)
if result == arYes: return
else: nil
proc isPartOf(a, b: PType): TAnalysisResult =
## checks iff 'a' can be part of 'b'. Iterates over VALUE types!
var marker = InitIntSet()
# watch out: parameters reversed because I'm too lazy to change the code...
result = isPartOfAux(b, a, marker)
proc isPartOf*(a, b: PNode): TAnalysisResult =
## checks if location `a` can be part of location `b`. We treat seqs and
## strings as pointers because the code gen often just passes them as such.
##
## Note: `a` can only be part of `b`, if `a`'s type can be part of `b`'s
## type. Since however type analysis is more expensive, we perform it only
## if necessary.
##
## cases:
##
## YES-cases:
## x <| x # for general trees
## x[] <| x
## x[i] <| x
## x.f <| x
##
## NO-cases:
## x !<| y # depending on type and symbol kind
## x[constA] !<| x[constB]
## x.f !<| x.g
## x.f !<| y.f iff x !<= y
##
## MAYBE-cases:
##
## x[] ?<| y[] iff compatible type
##
##
## x[] ?<| y depending on type
##
if a.kind == b.kind:
case a.kind
of nkSym:
const varKinds = {skVar, skTemp, skProc}
# same symbol: aliasing:
if a.sym.id == b.sym.id: result = arYes
elif a.sym.kind in varKinds or b.sym.kind in varKinds:
# actually, a param could alias a var but we know that cannot happen
# here. XXX make this more generic
result = arNo
else:
# use expensive type check:
if isPartOf(a.sym.typ, b.sym.typ) != arNo:
result = arMaybe
of nkBracketExpr:
result = isPartOf(a[0], b[0])
if len(a) >= 2 and len(b) >= 2:
# array accesses:
if result == arYes and isDeepConstExpr(a[1]) and isDeepConstExpr(b[1]):
# we know it's the same array and we have 2 constant indexes;
# if they are
var x = if a[1].kind == nkHiddenStdConv: a[1][1] else: a[1]
var y = if b[1].kind == nkHiddenStdConv: b[1][1] else: b[1]
if SameValue(x, y): result = arYes
else: result = arNo
# else: maybe and no are accurate
else:
# pointer derefs:
if result != arYes:
if isPartOf(a.typ, b.typ) != arNo: result = arMaybe
of nkDotExpr:
result = isPartOf(a[0], b[0])
if result != arNo:
# if the fields are different, it's not the same location
if a[1].sym.id != b[1].sym.id:
result = arNo
of nkHiddenDeref, nkDerefExpr:
result = isPartOf(a[0], b[0])
# weaken because of indirection:
if result != arYes:
if isPartOf(a.typ, b.typ) != arNo: result = arMaybe
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
result = isPartOf(a[1], b[1])
of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
result = isPartOf(a[0], b[0])
else: nil
# Calls return a new location, so a default of ``arNo`` is fine.
else:
# go down recursively; this is quite demanding:
const
Ix0Kinds = {nkDotExpr, nkBracketExpr, nkObjUpConv, nkObjDownConv,
nkCheckedFieldExpr}
Ix1Kinds = {nkHiddenStdConv, nkHiddenSubConv, nkConv}
DerefKinds = {nkHiddenDeref, nkDerefExpr}
case b.kind
of Ix0Kinds:
# a* !<| b.f iff a* !<| b
result = isPartOf(a, b[0])
of DerefKinds:
# a* !<| b[] iff
if isPartOf(a.typ, b.typ) != arNo:
result = isPartOf(a, b[0])
if result == arNo: result = arMaybe
of Ix1Kinds:
# a* !<| T(b) iff a* !<| b
result = isPartOf(a, b[1])
of nkSym:
# b is an atom, so we have to check a:
case a.kind
of Ix0Kinds:
# a.f !<| b* iff a.f !<| b*
result = isPartOf(a[0], b)
of Ix1Kinds:
result = isPartOf(a[1], b)
of DerefKinds:
if isPartOf(a.typ, b.typ) != arNo:
result = isPartOf(a[0], b)
if result == arNo: result = arMaybe
else: nil
else: nil

View File

@@ -371,7 +371,7 @@ type
mFields, mFieldPairs,
mAppendStrCh, mAppendStrStr, mAppendSeqElem,
mInRange, mInSet, mRepr, mExit, mSetLengthStr, mSetLengthSeq,
mAssert, mAstToStr, mRand,
mIsPartOf, mAstToStr, mRand,
mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast,
mNewString, mNewStringOfCap,
mReset,
@@ -669,36 +669,7 @@ const # for all kind of hash tables:
GrowthFactor* = 2 # must be power of 2, > 0
StartSize* = 8 # must be power of 2, > 0
proc SameValue*(a, b: PNode): bool
# a, b are literals
proc leValue*(a, b: PNode): bool
# a <= b? a, b are literals
proc ValueToString*(a: PNode): string
proc leValue(a, b: PNode): bool =
# a <= b?
result = false
case a.kind
of nkCharLit..nkInt64Lit:
if b.kind in {nkCharLit..nkInt64Lit}: result = a.intVal <= b.intVal
of nkFloatLit..nkFloat64Lit:
if b.kind in {nkFloatLit..nkFloat64Lit}: result = a.floatVal <= b.floatVal
of nkStrLit..nkTripleStrLit:
if b.kind in {nkStrLit..nkTripleStrLit}: result = a.strVal <= b.strVal
else: InternalError(a.info, "leValue")
proc SameValue(a, b: PNode): bool =
result = false
case a.kind
of nkCharLit..nkInt64Lit:
if b.kind in {nkCharLit..nkInt64Lit}: result = a.intVal == b.intVal
of nkFloatLit..nkFloat64Lit:
if b.kind in {nkFloatLit..nkFloat64Lit}: result = a.floatVal == b.floatVal
of nkStrLit..nkTripleStrLit:
if b.kind in {nkStrLit..nkTripleStrLit}: result = a.strVal == b.strVal
else: InternalError(a.info, "SameValue")
proc ValueToString(a: PNode): string =
proc ValueToString*(a: PNode): string =
case a.kind
of nkCharLit..nkInt64Lit: result = $(a.intVal)
of nkFloatLit, nkFloat32Lit, nkFloat64Lit: result = $(a.floatVal)

View File

@@ -146,6 +146,30 @@ proc IITablePut*(t: var TIITable, key, val: int)
# implementation
proc SameValue*(a, b: PNode): bool =
result = false
case a.kind
of nkCharLit..nkInt64Lit:
if b.kind in {nkCharLit..nkInt64Lit}: result = a.intVal == b.intVal
of nkFloatLit..nkFloat64Lit:
if b.kind in {nkFloatLit..nkFloat64Lit}: result = a.floatVal == b.floatVal
of nkStrLit..nkTripleStrLit:
if b.kind in {nkStrLit..nkTripleStrLit}: result = a.strVal == b.strVal
else:
InternalError(a.info, "SameValue")
proc leValue*(a, b: PNode): bool =
# a <= b?
result = false
case a.kind
of nkCharLit..nkInt64Lit:
if b.kind in {nkCharLit..nkInt64Lit}: result = a.intVal <= b.intVal
of nkFloatLit..nkFloat64Lit:
if b.kind in {nkFloatLit..nkFloat64Lit}: result = a.floatVal <= b.floatVal
of nkStrLit..nkTripleStrLit:
if b.kind in {nkStrLit..nkTripleStrLit}: result = a.strVal <= b.strVal
else: InternalError(a.info, "leValue")
proc lookupInRecord(n: PNode, field: PIdent): PSym =
result = nil
case n.kind

View File

@@ -10,16 +10,30 @@
type
TAfterCallActions = tuple[p: BProc, actions: PRope]
proc fixupCall(p: BProc, t: PNode, d: var TLoc, pl: PRope) =
proc leftAppearsOnRightSide(le, ri: PNode): bool =
if le != nil:
for i in 1 .. <ri.len:
if le.isPartOf(ri[i]) != arNo: return true
proc hasNoInit(call: PNode): bool {.inline.} =
result = call.sons[0].kind == nkSym and sfNoInit in call.sons[0].sym.flags
proc resetLoc(p: BProc, d: var TLoc) =
zeroVar(p, d, containsGarbageCollectedRef(d.t))
proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc, pl: PRope) =
var pl = pl
var typ = t.sons[0].typ # getUniqueType() is too expensive here!
var typ = ri.sons[0].typ # getUniqueType() is too expensive here!
if typ.sons[0] != nil:
if isInvalidReturnType(typ.sons[0]):
if sonsLen(t) > 1: app(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 sonsLen(ri) > 1: app(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':
if d.k == locNone: getTemp(p, typ.sons[0], d)
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, ")")
app(p.s[cpsStmts], pl)
@@ -117,40 +131,40 @@ proc genArgNoParam(aca: var TAfterCallActions, n: PNode): PRope =
initLocExpr(aca.p, n, a)
result = rdLoc(a)
proc genCall(p: BProc, t: PNode, d: var TLoc) =
proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) =
var op: TLoc
var aca: TAfterCallActions
aca.p = p
# this is a hotspot in the compiler
initLocExpr(p, t.sons[0], op)
initLocExpr(p, ri.sons[0], op)
var pl = con(op.r, "(")
var typ = t.sons[0].typ # getUniqueType() is too expensive here!
var typ = ri.sons[0].typ # getUniqueType() is too expensive here!
assert(typ.kind == tyProc)
var length = sonsLen(t)
var length = sonsLen(ri)
for i in countup(1, length - 1):
assert(sonsLen(typ) == sonsLen(typ.n))
if i < sonsLen(typ):
assert(typ.n.sons[i].kind == nkSym)
app(pl, genArg(aca, t.sons[i], typ.n.sons[i].sym))
app(pl, genArg(aca, ri.sons[i], typ.n.sons[i].sym))
else:
app(pl, genArgNoParam(aca, t.sons[i]))
app(pl, genArgNoParam(aca, ri.sons[i]))
if i < length - 1: app(pl, ", ")
fixupCall(p, t, d, pl)
fixupCall(p, le, ri, d, pl)
emitAfterCallActions(aca)
proc genInfixCall(p: BProc, t: PNode, d: var TLoc) =
proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
var op, a: TLoc
var aca: TAfterCallActions
aca.p = p
initLocExpr(p, t.sons[0], op)
initLocExpr(p, ri.sons[0], op)
var pl: PRope = nil
var typ = t.sons[0].typ # getUniqueType() is too expensive here!
var typ = ri.sons[0].typ # getUniqueType() is too expensive here!
assert(typ.kind == tyProc)
var length = sonsLen(t)
var length = sonsLen(ri)
assert(sonsLen(typ) == sonsLen(typ.n))
var param = typ.n.sons[1].sym
app(pl, genArg(aca, t.sons[1], param))
app(pl, genArg(aca, ri.sons[1], param))
if skipTypes(param.typ, {tyGenericInst}).kind == tyPtr: app(pl, "->")
else: app(pl, ".")
@@ -160,45 +174,45 @@ proc genInfixCall(p: BProc, t: PNode, d: var TLoc) =
assert(sonsLen(typ) == sonsLen(typ.n))
if i < sonsLen(typ):
assert(typ.n.sons[i].kind == nkSym)
app(pl, genArg(aca, t.sons[i], typ.n.sons[i].sym))
app(pl, genArg(aca, ri.sons[i], typ.n.sons[i].sym))
else:
app(pl, genArgNoParam(aca, t.sons[i]))
app(pl, genArgNoParam(aca, ri.sons[i]))
if i < length - 1: app(pl, ", ")
fixupCall(p, t, d, pl)
fixupCall(p, le, ri, d, pl)
emitAfterCallActions(aca)
proc genNamedParamCall(p: BProc, t: PNode, d: var TLoc) =
proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
# generates a crappy ObjC call
var op, a: TLoc
var aca: TAfterCallActions
aca.p = p
initLocExpr(p, t.sons[0], op)
initLocExpr(p, ri.sons[0], op)
var pl = toRope"["
var typ = t.sons[0].typ # getUniqueType() is too expensive here!
var typ = ri.sons[0].typ # getUniqueType() is too expensive here!
assert(typ.kind == tyProc)
var length = sonsLen(t)
var length = sonsLen(ri)
assert(sonsLen(typ) == sonsLen(typ.n))
if length > 1:
app(pl, genArg(aca, t.sons[1], typ.n.sons[1].sym))
app(pl, genArg(aca, ri.sons[1], typ.n.sons[1].sym))
app(pl, " ")
app(pl, op.r)
if length > 2:
app(pl, ": ")
app(pl, genArg(aca, t.sons[2], typ.n.sons[2].sym))
app(pl, genArg(aca, ri.sons[2], typ.n.sons[2].sym))
for i in countup(3, length-1):
assert(sonsLen(typ) == sonsLen(typ.n))
if i >= sonsLen(typ):
InternalError(t.info, "varargs for objective C method?")
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(aca, t.sons[i], param))
app(pl, genArg(aca, ri.sons[i], param))
if typ.sons[0] != nil:
if isInvalidReturnType(typ.sons[0]):
if sonsLen(t) > 1: app(pl, " ")
if sonsLen(ri) > 1: app(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:
@@ -230,3 +244,21 @@ proc genNamedParamCall(p: BProc, t: PNode, d: var TLoc) =
appf(p.s[cpsStmts], ";$n")
emitAfterCallActions(aca)
proc genCall(p: BProc, e: PNode, d: var TLoc) =
if e.sons[0].kind == nkSym and sfInfixCall in e.sons[0].sym.flags and
e.len >= 2:
genInfixCall(p, nil, e, d)
elif e.sons[0].kind == nkSym and sfNamedParamCall in e.sons[0].sym.flags:
genNamedParamCall(p, e, d)
else:
genPrefixCall(p, nil, e, d)
proc genAsgnCall(p: BProc, le, ri: PNode, d: var TLoc) =
if ri.sons[0].kind == nkSym and sfInfixCall in ri.sons[0].sym.flags and
ri.len >= 2:
genInfixCall(p, le, ri, d)
elif ri.sons[0].kind == nkSym and sfNamedParamCall in ri.sons[0].sym.flags:
genNamedParamCall(p, ri, d)
else:
genPrefixCall(p, le, ri, d)

View File

@@ -1378,13 +1378,6 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
of mCStrToStr: genDollar(p, e, d, "#cstrToNimstr($1)")
of mStrToStr: expr(p, e.sons[1], d)
of mEnumToStr: genRepr(p, e, d)
of mAssert:
if optAssert in p.Options:
expr(p, e.sons[1], d)
line = toRope(toLinenumber(e.info))
filen = makeCString(ToFilename(e.info))
appcg(p, cpsStmts, "#internalAssert($1, $2, $3);$n",
[filen, line, rdLoc(d)])
of mOf: genOf(p, e, d)
of mNew: genNew(p, e)
of mNewFinalize: genNewFinalize(p, e)
@@ -1635,11 +1628,6 @@ proc expr(p: BProc, e: PNode, d: var TLoc) =
nkCallStrLit:
if e.sons[0].kind == nkSym and e.sons[0].sym.magic != mNone:
genMagicExpr(p, e, d, e.sons[0].sym.magic)
elif e.sons[0].kind == nkSym and sfInfixCall in e.sons[0].sym.flags and
e.len >= 2:
genInfixCall(p, e, d)
elif e.sons[0].kind == nkSym and sfNamedParamCall in e.sons[0].sym.flags:
genNamedParamCall(p, e, d)
else:
genCall(p, e, d)
of nkCurly:

View File

@@ -18,16 +18,16 @@ proc genVarTuple(p: BProc, n: PNode) =
if n.kind != nkVarTuple: InternalError(n.info, "genVarTuple")
var L = sonsLen(n)
genLineDir(p, n)
initLocExpr(p, n.sons[L - 1], tup)
initLocExpr(p, n.sons[L-1], tup)
var t = tup.t
for i in countup(0, L - 3):
for i in countup(0, L-3):
var v = n.sons[i].sym
if sfGlobal in v.flags:
assignGlobalVar(p, v)
genObjectInit(p, cpsInit, v.typ, v.loc, true)
else:
else:
assignLocalVar(p, v)
initVariable(p, v)
initLocalVar(p, v, immediateAsgn=true)
initLoc(field, locExpr, t.sons[i], tup.s)
if t.n == nil:
field.r = ropef("$1.Field$2", [rdLoc(tup), toRope(i)])
@@ -37,17 +37,25 @@ proc genVarTuple(p: BProc, n: PNode) =
[rdLoc(tup), mangleRecFieldName(t.n.sons[i].sym, t)])
putLocIntoDest(p, v.loc, field)
proc loadInto(p: BProc, le, ri: PNode, a: var TLoc) {.inline.} =
if ri.kind in nkCallKinds and (ri.sons[0].kind != nkSym or
ri.sons[0].sym.magic == mNone):
genAsgnCall(p, le, ri, a)
else:
expr(p, ri, a)
proc genSingleVar(p: BProc, a: PNode) =
var v = a.sons[0].sym
var immediateAsgn = a.sons[2].kind != nkEmpty
if sfGlobal in v.flags:
assignGlobalVar(p, v)
genObjectInit(p, cpsInit, v.typ, v.loc, true)
else:
else:
assignLocalVar(p, v)
initVariable(p, v)
if a.sons[2].kind != nkEmpty:
initLocalVar(p, v, immediateAsgn)
if immediateAsgn:
genLineDir(p, a)
expr(p, a.sons[2], v.loc)
loadInto(p, a.sons[0], a.sons[2], v.loc)
proc genVarStmt(p: BProc, n: PNode) =
for i in countup(0, sonsLen(n) - 1):
@@ -658,7 +666,7 @@ proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) =
InitLocExpr(p, e.sons[0], a)
if fastAsgn: incl(a.flags, lfNoDeepCopy)
assert(a.t != nil)
expr(p, e.sons[1], a)
loadInto(p, e.sons[0], e.sons[1], a)
else:
asgnFieldDiscriminant(p, e)

View File

@@ -14,7 +14,7 @@ import
options, intsets,
nversion, nimsets, msgs, crc, bitsets, idents, lists, types, ccgutils, os,
times, ropes, math, passes, rodread, wordrecg, treetab, cgmeth,
rodutils, renderer, idgen, cgendata, ccgmerge, semfold
rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases
when options.hasTinyCBackend:
import tccgen
@@ -235,16 +235,17 @@ proc zeroVar(p: BProc, loc: TLoc, containsGCref: bool) =
genRefAssign(p, loc, nilLoc, {afSrcIsNil})
else:
appf(p.s[cpsStmts], "$1 = 0;$n", [rdLoc(loc)])
else:
else:
if containsGcref and p.WithInLoop > 0:
appf(p.s[cpsInit], "memset((void*)$1, 0, sizeof($2));$n",
[addrLoc(loc), rdLoc(loc)])
genObjectInit(p, cpsInit, loc.t, loc, true)
appcg(p, cpsStmts, "#genericReset((void*)$1, $2);$n",
[addrLoc(loc), genTypeInfo(p.module, loc.t)])
else:
appf(p.s[cpsStmts], "memset((void*)$1, 0, sizeof($2));$n",
[addrLoc(loc), rdLoc(loc)])
genObjectInit(p, cpsInit, loc.t, loc, true)
genObjectInit(p, cpsStmts, loc.t, loc, true)
proc zeroTemp(p: BProc, loc: TLoc) =
if skipTypes(loc.t, abstractVarRange).Kind notin
@@ -259,15 +260,22 @@ proc zeroTemp(p: BProc, loc: TLoc) =
else:
appf(p.s[cpsStmts], "memset((void*)$1, 0, sizeof($2));$n",
[addrLoc(loc), rdLoc(loc)])
# XXX no object init necessary for temporaries?
when false:
appcg(p, cpsStmts, "#genericReset((void*)$1, $2);$n",
[addrLoc(loc), genTypeInfo(p.module, loc.t)])
proc initVariable(p: BProc, v: PSym) =
proc initLocalVar(p: BProc, v: PSym, immediateAsgn: bool) =
if sfNoInit notin v.flags:
var b = containsGarbageCollectedRef(v.typ)
if b or v.ast == nil:
zeroVar(p, v.loc, b)
# we know it is a local variable and thus on the stack!
# If ``not immediateAsgn`` it is not initialized in a binding like
# ``var v = X`` and thus we need to init it.
# If ``v`` contains a GC-ref we may pass it to ``unsureAsgnRef`` somehow
# which requires initialization. However this can really only happen if
# ``var v = X()`` gets transformed into ``X(&v)``.
# Nowadays the logic in ccgcalls deals with this case however.
if not immediateAsgn:
zeroVar(p, v.loc, containsGarbageCollectedRef(v.typ))
proc initTemp(p: BProc, tmp: var TLoc) =
if containsGarbageCollectedRef(tmp.t) or isInvalidReturnType(tmp.t):
@@ -534,13 +542,14 @@ proc genProcAux(m: BModule, prc: PSym) =
assert(prc.ast != nil)
if sfPure notin prc.flags and prc.typ.sons[0] != nil:
var res = prc.ast.sons[resultPos].sym # get result symbol
if not isInvalidReturnType(prc.typ.sons[0]):
if not isInvalidReturnType(prc.typ.sons[0]):
if sfNoInit in prc.flags: incl(res.flags, sfNoInit)
# declare the result symbol:
assignLocalVar(p, res)
assert(res.loc.r != nil)
returnStmt = ropeff("return $1;$n", "ret $1$n", [rdLoc(res.loc)])
initVariable(p, res)
else:
initLocalVar(p, res, immediateAsgn=false)
else:
fillResult(res)
assignParam(p, res)
if skipTypes(res.typ, abstractInst).kind == tyArray:

View File

@@ -720,7 +720,8 @@ proc generateHeader(p: var TProc, typ: PType): PRope =
const
nodeKindsNeedNoCopy = {nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit,
nkFloatLit..nkFloat64Lit, nkCurly, nkPar, nkStringToCString,
nkCStringToString, nkCall, nkCommand, nkHiddenCallConv, nkCallStrLit}
nkCStringToString, nkCall, nkPrefix, nkPostfix, nkInfix,
nkCommand, nkHiddenCallConv, nkCallStrLit}
proc needsNoCopy(y: PNode): bool =
result = (y.kind in nodeKindsNeedNoCopy) or
@@ -1131,14 +1132,6 @@ proc genMagic(p: var TProc, n: PNode, r: var TCompRes) =
of mLtStr: binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) < 0)")
of mIsNil: unaryExpr(p, n, r, "", "$1 == null")
of mEnumToStr: genRepr(p, n, r)
of mAssert:
if (optAssert in p.Options):
useMagic(p, "internalAssert")
gen(p, n.sons[1], a)
line = toRope(toLinenumber(n.info))
filen = makeCString(ToFilename(n.info))
appf(r.com, "if (!($3)) internalAssert($1, $2)",
[filen, line, mergeExpr(a)])
of mNew, mNewFinalize: genNew(p, n, r)
of mSizeOf: r.res = toRope(getSize(n.sons[1].typ))
of mChr, mArrToSeq: gen(p, n.sons[1], r) # nothing to do
@@ -1402,7 +1395,7 @@ proc gen(p: var TProc, n: PNode, r: var TCompRes) =
else: r.res = toRope(f.ToStrMaxPrecision)
of nkBlockExpr: genBlock(p, n, r)
of nkIfExpr: genIfExpr(p, n, r)
of nkCall, nkHiddenCallConv, nkCommand, nkCallStrLit:
of nkCallKinds:
if (n.sons[0].kind == nkSym) and (n.sons[0].sym.magic != mNone):
genMagic(p, n, r)
else:

View File

@@ -695,13 +695,7 @@ proc evalNewSeq(c: PEvalContext, n: PNode): PNode =
for i in countup(0, L-1):
a.sons[i] = getNullValue(t.sons[0], n.info)
result = emptyNode
proc evalAssert(c: PEvalContext, n: PNode): PNode =
result = evalAux(c, n.sons[1], {})
if isSpecial(result): return
if getOrdValue(result) != 0: result = emptyNode
else: stackTrace(c, n, errAssertionFailed)
proc evalIncl(c: PEvalContext, n: PNode): PNode =
result = evalAux(c, n.sons[1], {efLValue})
if isSpecial(result): return
@@ -876,7 +870,6 @@ proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode =
of mOf: result = evalOf(c, n)
of mSizeOf: result = raiseCannotEval(c, n)
of mHigh: result = evalHigh(c, n)
of mAssert: result = evalAssert(c, n)
of mExit: result = evalExit(c, n)
of mNew, mNewFinalize: result = evalNew(c, n)
of mNewSeq: result = evalNewSeq(c, n)

View File

@@ -23,7 +23,7 @@ const
wMagic, wNosideEffect, wSideEffect, wNoreturn, wDynLib, wHeader,
wCompilerProc, wProcVar, wDeprecated, wVarargs, wCompileTime, wMerge,
wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC,
wNoStackFrame, wError, wDiscardable}
wNoStackFrame, wError, wDiscardable, wNoInit}
converterPragmas* = procPragmas
methodPragmas* = procPragmas
macroPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,

View File

@@ -14,7 +14,7 @@ import
wordrecg, ropes, msgs, os, condsyms, idents, renderer, types, platform, math,
magicsys, parser, nversion, semdata, nimsets, semfold, importer,
procfind, lookups, rodread, pragmas, passes, semtypinst, sigmatch, suggest,
semthreads, intsets, transf, evals, idgen
semthreads, intsets, transf, evals, idgen, aliases
proc semPass*(): TPass
# implementation

View File

@@ -453,6 +453,14 @@ proc analyseIfAddressTakenInCall(c: PContext, n: PNode) =
skipTypes(t.sons[i], abstractInst).kind == tyVar:
n.sons[i] = analyseIfAddressTaken(c, n.sons[i])
proc expectStringArg(c: PContext, n: PNode, i: int): PNode =
result = c.semAndEvalConstExpr(n.sons[i+1])
if result.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit}:
GlobalError(result.info, errStringLiteralExpected)
include semmagic
proc semDirectCallAnalyseEffects(c: PContext, n: PNode,
flags: TExprFlags): PNode =
if efWantIterator in flags:
@@ -520,6 +528,8 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
renderTree(n, {renderNoComments}))
fixAbstractType(c, result)
analyseIfAddressTakenInCall(c, result)
if result.sons[0].kind == nkSym and result.sons[0].sym.magic != mNone:
result = magicsAfterOverloadResolution(c, result, flags)
proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
# this seems to be a hotspot in the compiler!
@@ -530,6 +540,8 @@ proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
if result == nil: GlobalError(n.Info, errGenerated, getNotFoundError(c, n))
fixAbstractType(c, result)
analyseIfAddressTakenInCall(c, result)
if result.sons[0].sym.magic != mNone:
result = magicsAfterOverloadResolution(c, result, flags)
proc buildStringify(c: PContext, arg: PNode): PNode =
if arg.typ != nil and skipTypes(arg.typ, abstractInst).kind == tyString:
@@ -914,12 +926,6 @@ proc setMs(n: PNode, s: PSym): PNode =
n.sons[0] = newSymNode(s)
n.sons[0].info = n.info
proc expectStringArg(c: PContext, n: PNode, i: int): PNode =
result = c.semAndEvalConstExpr(n.sons[i+1])
if result.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit}:
GlobalError(result.info, errStringLiteralExpected)
proc expectMacroOrTemplateCall(c: PContext, n: PNode): PSym =
## The argument to the proc should be nkCall(...) or similar
## Returns the macro/template symbol
@@ -954,21 +960,6 @@ proc semExpandToAst(c: PContext, n: PNode, magicSym: PSym,
else:
result = semDirectOp(c, n, flags)
proc semSlurp(c: PContext, n: PNode, flags: TExprFlags): PNode =
if sonsLen(n) == 2:
var a = expectStringArg(c, n, 0)
try:
var filename = a.strVal.FindFile
var content = readFile(filename)
result = newStrNode(nkStrLit, content)
result.typ = getSysType(tyString)
result.info = n.info
c.slurpedFiles.add(filename)
except EIO:
GlobalError(a.info, errCannotOpenFile, a.strVal)
else:
result = semDirectOp(c, n, flags)
proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
# this is a hotspot in the compiler!
result = n
@@ -991,14 +982,7 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
result = semAsgn(c, result)
else:
result = semDirectOp(c, n, flags)
of mSlurp: result = semSlurp(c, n, flags)
of mExpandToAst: result = semExpandToAst(c, n, s, flags)
of mAstToStr:
if sonsLen(n) == 2:
result = newStrNodeT(renderTree(n[1], {renderNoComments}), n)
result.typ = getSysType(tyString)
else:
result = semDirectOp(c, n, flags)
else: result = semDirectOp(c, n, flags)
proc semIfExpr(c: PContext, n: PNode): PNode =

View File

@@ -204,7 +204,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
result = newIntNodeT(Ord(
testCompileOptionArg(getStr(a), getStr(b), n.info)), n)
of mNewString, mNewStringOfCap,
mExit, mInc, ast.mDec, mEcho, mAssert, mSwap, mAppendStrCh,
mExit, mInc, ast.mDec, mEcho, mSwap, mAppendStrCh,
mAppendStrStr, mAppendSeqElem, mSetLengthStr, mSetLengthSeq,
mParseExprToAst, mParseStmtToAst, mExpandToAst,
mNLen..mNError, mEqRef:

38
compiler/semmagic.nim Normal file
View File

@@ -0,0 +1,38 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2011 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# This include file implements the semantic checking for magics.
proc semIsPartOf(c: PContext, n: PNode, flags: TExprFlags): PNode =
var r = isPartOf(n[1], n[2])
result = newIntNodeT(ord(r), n)
proc semSlurp(c: PContext, n: PNode, flags: TExprFlags): PNode =
assert sonsLen(n) == 2
var a = expectStringArg(c, n, 0)
try:
var filename = a.strVal.FindFile
var content = readFile(filename)
result = newStrNode(nkStrLit, content)
result.typ = getSysType(tyString)
result.info = n.info
c.slurpedFiles.add(filename)
except EIO:
GlobalError(a.info, errCannotOpenFile, a.strVal)
proc magicsAfterOverloadResolution(c: PContext, n: PNode,
flags: TExprFlags): PNode =
case n[0].sym.magic
of mSlurp: result = semSlurp(c, n, flags)
of mIsPartOf: result = semIsPartOf(c, n, flags)
of mAstToStr:
result = newStrNodeT(renderTree(n[1], {renderNoComments}), n)
result.typ = getSysType(tyString)
else: result = n

View File

@@ -1024,7 +1024,6 @@ proc computeSize(typ: PType): biggestInt =
proc getReturnType*(s: PSym): PType =
# Obtains the return type of a iterator/proc/macro/template
assert s.kind in {skProc, skTemplate, skMacro, skIterator}
# XXX ask Zahary if this change broke something
result = s.typ.sons[0]
proc getSize(typ: PType): biggestInt =

View File

@@ -1584,6 +1584,11 @@ The implicit initialization can be avoided for optimization reasons with the
var
a {.noInit.}: array [0..1023, char]
If a proc is annotated with the ``noinit`` pragma this refers to its implicit
``result`` variable:
.. code-block:: nimrod
proc returnUndefinedValue: int {.noinit.} = nil
let statement
@@ -2849,6 +2854,7 @@ exported.
The algorithm for compiling modules is:
- compile the whole module as usual, following import statements recursively
- if there is a cycle only import the already parsed symbols (that are
exported); if an unknown identifier occurs then abort

View File

@@ -11,6 +11,8 @@
## a channel as its inbox. This module requires the ``--threads:on``
## command line switch.
from os import sleep
type
TTask*[TIn, TOut] = object{.pure, final.}
when TOut isnot void:
@@ -29,7 +31,7 @@ type
proc spawn*[TIn, TOut](action: proc(
self: PActor[TIn, TOut]){.thread.}): PActor[TIn, TOut] =
## creates an actor; that is a thread with an inbox. The caller MUST call
## ``join`` because that also frees the associated resources with the actor.
## ``join`` because that also frees the actor's associated resources.
result = cast[PActor[TIn, TOut]](allocShared0(sizeof(result[])))
open(result.i)
createThread(result.t, action, result)
@@ -42,6 +44,10 @@ proc running*[TIn, TOut](a: PActor[TIn, TOut]) =
## returns true if the actor `a` is running.
result = running(a.t)
proc ready*[TIn, TOut](a: PActor[TIn, TOut]): bool =
## returns true if the actor `a` is ready to process new messages.
result = ready(a.i)
proc join*[TIn, TOut](a: PActor[TIn, TOut]) =
## joins an actor.
joinThread(a.t)
@@ -111,17 +117,35 @@ proc createActorPool*[TIn, TOut](a: var TActorPool[TIn, TOut], poolSize = 4) =
for i in 0 .. < a.actors.len:
a.actors[i] = spawn(poolWorker[TIn, TOut])
proc join*[TIn, TOut](a: var TActorPool[TIn, TOut]) =
## waits for each actor in the actor pool `a` to finish and frees the
proc sync*[TIn, TOut](a: var TActorPool[TIn, TOut], polling=50) =
## waits for every actor of `a` to finish with its work. Currently this is
## implemented as polling every `polling` ms. This will change in a later
## version, however.
while true:
var wait = false
for i in 0..high(a.actors):
if not a.actors[i].i.ready:
wait = true
break
if not wait: break
sleep(polling)
proc terminate*[TIn, TOut](a: var TActorPool[TIn, TOut]) =
## terminates each actor in the actor pool `a` and frees the
## resources attached to `a`.
var t: TTask[TIn, TOut]
t.shutdown = true
for i in 0 .. < a.actors.len: send(a.actors[i].i, t)
for i in 0 .. < a.actors.len: join(a.actors[i])
for i in 0.. <a.actors.len: send(a.actors[i].i, t)
for i in 0.. <a.actors.len: join(a.actors[i])
when TOut isnot void:
close(a.outputs)
a.actors = nil
proc join*[TIn, TOut](a: var TActorPool[TIn, TOut]) =
## short-cut for `sync` and then `terminate`.
sync(a)
terminate(a)
template setupTask =
t.action = action
shallowCopy(t.data, input)
@@ -135,7 +159,7 @@ template schedule =
return
# no thread ready :-( --> send message to the thread which has the least
# messages pending:
var minIdx = 0
var minIdx = -1
var minVal = high(int)
for i in 0..high(p.actors):
var curr = p.actors[i].i.peek
@@ -143,10 +167,13 @@ template schedule =
# ok, is ready now:
p.actors[i].i.send(t)
return
if curr < minVal:
if curr < minVal and curr >= 0:
minVal = curr
minIdx = i
p.actors[minIdx].i.send(t)
if minIdx >= 0:
p.actors[minIdx].i.send(t)
else:
raise newException(EDeadThread, "cannot send message; thread died")
proc spawn*[TIn, TOut](p: var TActorPool[TIn, TOut], input: TIn,
action: proc (input: TIn): TOut {.thread.}

View File

@@ -2071,14 +2071,24 @@ proc astToStr*[T](x: T): string {.magic: "AstToStr", noSideEffect.}
## converts the AST of `x` into a string representation. This is very useful
## for debugging.
proc raiseAssert(msg: string) {.noinline.} =
raise newException(EAssertionFailed, msg)
template assert*(cond: expr, msg = "") =
## provides a means to implement `programming by contracts`:idx: in Nimrod.
## ``assert`` evaluates expression ``cond`` and if ``cond`` is false, it
## raises an ``EAssertionFailure`` exception. However, the compiler may
## not generate any code at all for ``assert`` if it is advised to do so.
## Use ``assert`` for debugging purposes only.
bind raiseAssert
when compileOption("assertions"):
if not cond:
raise newException(EAssertionFailed, astToStr(cond) & ' ' & msg)
raiseAssert(astToStr(cond) & ' ' & msg)
template doAssert*(cond: expr, msg = "") =
## same as `assert' but is always turned on and not affected by the
## ``--assertions`` command line switch.
bind raiseAssert
if not cond:
raiseAssert(astToStr(cond) & ' ' & msg)

View File

@@ -222,11 +222,14 @@ proc recv*[TMsg](c: var TChannel[TMsg]): TMsg =
llRecv(q, addr(result), cast[PNimType](getTypeInfo(result)))
proc peek*[TMsg](c: var TChannel[TMsg]): int =
## returns the current number of messages in the channel `c`.
## returns the current number of messages in the channel `c`. Returns -1
## if the channel has been closed.
var q = cast[PRawChannel](addr(c))
if q.mask != ChannelDeadMask:
lockChannel(q):
result = q.count
else:
result = -1
proc open*[TMsg](c: var TChannel[TMsg]) =
## opens a channel `c` for inter thread communication.

View File

@@ -1669,25 +1669,25 @@ proc escapePeg*(s: string): string =
if inQuote: result.add('\'')
when isMainModule:
assert escapePeg("abc''def'") == r"'abc'\x27\x27'def'\x27"
#assert match("(a b c)", peg"'(' @ ')'")
assert match("W_HI_Le", peg"\y 'while'")
assert(not match("W_HI_L", peg"\y 'while'"))
assert(not match("W_HI_Le", peg"\y v'while'"))
assert match("W_HI_Le", peg"y'while'")
doAssert escapePeg("abc''def'") == r"'abc'\x27\x27'def'\x27"
#doAssert match("(a b c)", peg"'(' @ ')'")
doAssert match("W_HI_Le", peg"\y 'while'")
doAssert(not match("W_HI_L", peg"\y 'while'"))
doAssert(not match("W_HI_Le", peg"\y v'while'"))
doAssert match("W_HI_Le", peg"y'while'")
assert($ +digits == $peg"\d+")
assert "0158787".match(peg"\d+")
assert "ABC 0232".match(peg"\w+\s+\d+")
assert "ABC".match(peg"\d+ / \w+")
doAssert($ +digits == $peg"\d+")
doAssert "0158787".match(peg"\d+")
doAssert "ABC 0232".match(peg"\w+\s+\d+")
doAssert "ABC".match(peg"\d+ / \w+")
for word in split("00232this02939is39an22example111", peg"\d+"):
writeln(stdout, word)
assert matchLen("key", ident) == 3
doAssert matchLen("key", ident) == 3
var pattern = sequence(ident, *whitespace, term('='), *whitespace, ident)
assert matchLen("key1= cal9", pattern) == 11
doAssert matchLen("key1= cal9", pattern) == 11
var ws = newNonTerminal("ws", 1, 1)
ws.rule = *whitespace
@@ -1698,24 +1698,24 @@ when isMainModule:
var c: TCaptures
var s = "a+b + c +d+e+f"
assert rawMatch(s, expr.rule, 0, c) == len(s)
doAssert rawMatch(s, expr.rule, 0, c) == len(s)
var a = ""
for i in 0..c.ml-1:
a.add(substr(s, c.matches[i][0], c.matches[i][1]))
assert a == "abcdef"
doAssert a == "abcdef"
#echo expr.rule
#const filename = "lib/devel/peg/grammar.txt"
#var grammar = parsePeg(newFileStream(filename, fmRead), filename)
#echo "a <- [abc]*?".match(grammar)
assert find("_____abc_______", term("abc"), 2) == 5
assert match("_______ana", peg"A <- 'ana' / . A")
assert match("abcs%%%", peg"A <- ..A / .A / '%'")
doAssert find("_____abc_______", term("abc"), 2) == 5
doAssert match("_______ana", peg"A <- 'ana' / . A")
doAssert match("abcs%%%", peg"A <- ..A / .A / '%'")
if "abc" =~ peg"{'a'}'bc' 'xyz' / {\ident}":
assert matches[0] == "abc"
doAssert matches[0] == "abc"
else:
assert false
doAssert false
var g2 = peg"""S <- A B / C D
A <- 'a'+
@@ -1723,44 +1723,44 @@ when isMainModule:
C <- 'c'+
D <- 'd'+
"""
assert($g2 == "((A B) / (C D))")
assert match("cccccdddddd", g2)
assert("var1=key; var2=key2".replacef(peg"{\ident}'='{\ident}", "$1<-$2$2") ==
doAssert($g2 == "((A B) / (C D))")
doAssert match("cccccdddddd", g2)
doAssert("var1=key; var2=key2".replacef(peg"{\ident}'='{\ident}", "$1<-$2$2") ==
"var1<-keykey; var2<-key2key2")
assert "var1=key; var2=key2".endsWith(peg"{\ident}'='{\ident}")
doAssert "var1=key; var2=key2".endsWith(peg"{\ident}'='{\ident}")
if "aaaaaa" =~ peg"'aa' !. / ({'a'})+":
assert matches[0] == "a"
doAssert matches[0] == "a"
else:
assert false
doAssert false
var matches: array[0..5, string]
if match("abcdefg", peg"c {d} ef {g}", matches, 2):
assert matches[0] == "d"
assert matches[1] == "g"
doAssert matches[0] == "d"
doAssert matches[1] == "g"
else:
assert false
doAssert false
for x in findAll("abcdef", peg"{.}", 3):
echo x
if "f(a, b)" =~ peg"{[0-9]+} / ({\ident} '(' {@} ')')":
assert matches[0] == "f"
assert matches[1] == "a, b"
doAssert matches[0] == "f"
doAssert matches[1] == "a, b"
else:
assert false
doAssert false
assert match("eine übersicht und außerdem", peg"(\letter \white*)+")
doAssert match("eine übersicht und außerdem", peg"(\letter \white*)+")
# ß is not a lower cased letter?!
assert match("eine übersicht und auerdem", peg"(\lower \white*)+")
assert match("EINE ÜBERSICHT UND AUSSERDEM", peg"(\upper \white*)+")
assert(not match("456678", peg"(\letter)+"))
doAssert match("eine übersicht und auerdem", peg"(\lower \white*)+")
doAssert match("EINE ÜBERSICHT UND AUSSERDEM", peg"(\upper \white*)+")
doAssert(not match("456678", peg"(\letter)+"))
assert("var1 = key; var2 = key2".replacef(
doAssert("var1 = key; var2 = key2".replacef(
peg"\skip(\s*) {\ident}'='{\ident}", "$1<-$2$2") ==
"var1<-keykey;var2<-key2key2")
assert match("prefix/start", peg"^start$", 7)
doAssert match("prefix/start", peg"^start$", 7)
# tricky test to check for false aliasing:
block:

View File

@@ -5,7 +5,6 @@ discard """
template myAssert(cond: expr) =
when rand(3) < 2:
let c = cond.astToStr
{.warning: "code activated: " & c.}
if not cond:
echo c, "ugh"

View File

@@ -118,6 +118,7 @@ proc runThreadTests(r: var TResults, options: string) =
test "tactors"
test "threadex"
test "trecursive_actor"
#test "threadring"
#test "tthreadanalysis"
#test "tthreadsort"

View File

@@ -0,0 +1,19 @@
discard """
outputsub: "0"
"""
import actors
var
a: TActorPool[int, void]
createActorPool(a)
proc task(i: int) {.thread.} =
echo i
if i != 0: a.spawn (i-1, task)
# count from 9 till 0 and check 0 is somewhere in the output
a.spawn(9, task)
a.join()

View File

@@ -1,7 +1,9 @@
version 0.8.14
==============
- find a proper bugfix for C compilers optimizing away stack roots
- implement a proper bugfix for C compilers optimizing away stack roots
- cleanup file path handling in the compiler
- warning for implicit openArray -> varargs convention
- implement explicit varargs; **but** ``len(varargs)`` problem remains!
--> solve by implicit conversion from varargs to openarray
@@ -36,9 +38,6 @@ version 0.9.0
Bugs
----
- bug: generic assign still buggy
- Optimization: If we use a temporary for the result anyway the code gen
should make use of this fact to generate better code...
- bug: memset() without type field initialization?
- special case the generic assign that needs to care about case objects
- bug: returning a tyVar does not mean it is save to return it:
@@ -124,7 +123,6 @@ Low priority
need to recompile clients; er ... what about templates, macros or anything
that has inlining semantics?
- codegen should use "NIM_CAST" macro and respect aliasing rules for GCC
- remove unused mAssert magic
Version 2
=========

View File

@@ -29,7 +29,7 @@ Changes affecting backwards compatibility
``os.iterOverEnvironment``, ``os.pcDirectory``, ``os.pcLinkToDirectory``,
``os.SplitPath``, ``os.extractDir``, ``os.SplitFilename``,
``os.extractFileTrunk``, ``os.extractFileExt``, ``osproc.executeProcess``,
``osproc.executeCommand``.
``osproc.executeCommand``.
- Removed deprecated ``parseopt.init``, ``parseopt.getRestOfCommandLine``.
- Moved ``strutils.validEmailAddress`` to ``matchers.validEmailAddress``.
- The pointer dereference operator ``^`` has been removed, so that ``^``
@@ -53,8 +53,8 @@ Changes affecting backwards compatibility
because they should not be used directly anymore.
Wrapper procs have been created that should be used instead.
- ``export`` is now a keyword.
- ``assert`` is now implemented in pure Nimrod; it's easy to implement your
own assertion schemes now.
- ``assert`` is now implemented in pure Nimrod as a template; it's easy
to implement your own assertion templates with ``system.astToStr``.
Language Additions
@@ -112,6 +112,7 @@ Compiler Additions
- Added ``--import:file`` and ``--include:file`` configuration options
for specifying modules that will be automatically imported/incluced.
- ``nimrod i`` can now optionally be given a module to execute.
- The compiler now performs a simple aliases analysis to generate better code.
Library Additions
@@ -128,7 +129,7 @@ Library Additions
- Added ``system.program_result``.
- Added ``xmltree.innerText``.
- Added ``os.isAbsolute``, ``os.dynLibFormat``, ``os.isRootDir``,
``os.parentDirs``.
``os.parentDirs``.
- Added ``parseutils.interpolatedFragments``.
- Added ``macros.treeRepr``, ``macros.lispRepr``, ``macros.dumpTree``,
``macros.dumpLisp``, ``macros.parseExpr``, ``macros.parseStmt``,
@@ -142,7 +143,7 @@ Library Additions
and ``exec`` on Posix systems. Define the symbol ``useFork`` to revert to
the old implementation.
- Added ``intsets.assign``.
- Added ``system.astToStr`` and ``system.rand``.
- Added ``system.astToStr`` and ``system.rand``, ``system.doAssert``.
2011-07-10 Version 0.8.12 released