codegen uses alias analysis to generate better code

This commit is contained in:
Araq 2011-12-10 01:06:32 +01:00
commit af792da0bb
14 changed files with 166 additions and 113 deletions

View file

@ -41,7 +41,7 @@ proc isPartOfAux(n: PNode, b: PType, marker: var TIntSet): TAnalysisResult =
proc isPartOfAux(a, b: PType, marker: var TIntSet): TAnalysisResult = proc isPartOfAux(a, b: PType, marker: var TIntSet): TAnalysisResult =
result = arNo result = arNo
if a == nil: return if a == nil or b == nil: return
if ContainsOrIncl(marker, a.id): return if ContainsOrIncl(marker, a.id): return
if compareTypes(a, b, dcEqIgnoreDistinct): return arYes if compareTypes(a, b, dcEqIgnoreDistinct): return arYes
case a.kind case a.kind

View file

@ -156,8 +156,6 @@ proc SameValue*(a, b: PNode): bool =
of nkStrLit..nkTripleStrLit: of nkStrLit..nkTripleStrLit:
if b.kind in {nkStrLit..nkTripleStrLit}: result = a.strVal == b.strVal if b.kind in {nkStrLit..nkTripleStrLit}: result = a.strVal == b.strVal
else: else:
debug a
debug b
InternalError(a.info, "SameValue") InternalError(a.info, "SameValue")
proc leValue*(a, b: PNode): bool = proc leValue*(a, b: PNode): bool =

View file

@ -10,16 +10,30 @@
type type
TAfterCallActions = tuple[p: BProc, actions: PRope] 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 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 typ.sons[0] != nil:
if isInvalidReturnType(typ.sons[0]): 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: # beware of 'result = p(result)'. We may need to allocate a temporary:
if d.k in {locTemp, locNone}: if d.k in {locTemp, locNone} or not leftAppearsOnRightSide(le, ri):
# We already got a temp. Great, special case it: # Great, we can use 'd':
if d.k == locNone: getTemp(p, typ.sons[0], 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, addrLoc(d))
app(pl, ")") app(pl, ")")
app(p.s[cpsStmts], pl) app(p.s[cpsStmts], pl)
@ -117,40 +131,40 @@ proc genArgNoParam(aca: var TAfterCallActions, n: PNode): PRope =
initLocExpr(aca.p, n, a) initLocExpr(aca.p, n, a)
result = rdLoc(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 op: TLoc
var aca: TAfterCallActions var aca: TAfterCallActions
aca.p = p aca.p = p
# this is a hotspot in the compiler # 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 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) assert(typ.kind == tyProc)
var length = sonsLen(t) var length = sonsLen(ri)
for i in countup(1, length - 1): for i in countup(1, length - 1):
assert(sonsLen(typ) == sonsLen(typ.n)) assert(sonsLen(typ) == sonsLen(typ.n))
if i < sonsLen(typ): if i < sonsLen(typ):
assert(typ.n.sons[i].kind == nkSym) 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: else:
app(pl, genArgNoParam(aca, t.sons[i])) app(pl, genArgNoParam(aca, ri.sons[i]))
if i < length - 1: app(pl, ", ") if i < length - 1: app(pl, ", ")
fixupCall(p, t, d, pl) fixupCall(p, le, ri, d, pl)
emitAfterCallActions(aca) 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 op, a: TLoc
var aca: TAfterCallActions var aca: TAfterCallActions
aca.p = p aca.p = p
initLocExpr(p, t.sons[0], op) initLocExpr(p, ri.sons[0], op)
var pl: PRope = nil 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) assert(typ.kind == tyProc)
var length = sonsLen(t) var length = sonsLen(ri)
assert(sonsLen(typ) == sonsLen(typ.n)) assert(sonsLen(typ) == sonsLen(typ.n))
var param = typ.n.sons[1].sym 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, "->") if skipTypes(param.typ, {tyGenericInst}).kind == tyPtr: app(pl, "->")
else: app(pl, ".") else: app(pl, ".")
@ -160,45 +174,45 @@ proc genInfixCall(p: BProc, t: PNode, d: var TLoc) =
assert(sonsLen(typ) == sonsLen(typ.n)) assert(sonsLen(typ) == sonsLen(typ.n))
if i < sonsLen(typ): if i < sonsLen(typ):
assert(typ.n.sons[i].kind == nkSym) 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: else:
app(pl, genArgNoParam(aca, t.sons[i])) app(pl, genArgNoParam(aca, ri.sons[i]))
if i < length - 1: app(pl, ", ") if i < length - 1: app(pl, ", ")
fixupCall(p, t, d, pl) fixupCall(p, le, ri, d, pl)
emitAfterCallActions(aca) 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 # generates a crappy ObjC call
var op, a: TLoc var op, a: TLoc
var aca: TAfterCallActions var aca: TAfterCallActions
aca.p = p aca.p = p
initLocExpr(p, t.sons[0], op) initLocExpr(p, ri.sons[0], op)
var pl = toRope"[" 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) assert(typ.kind == tyProc)
var length = sonsLen(t) var length = sonsLen(ri)
assert(sonsLen(typ) == sonsLen(typ.n)) assert(sonsLen(typ) == sonsLen(typ.n))
if length > 1: 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, " ")
app(pl, op.r) app(pl, op.r)
if length > 2: if length > 2:
app(pl, ": ") 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): for i in countup(3, length-1):
assert(sonsLen(typ) == sonsLen(typ.n)) assert(sonsLen(typ) == sonsLen(typ.n))
if i >= sonsLen(typ): 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) assert(typ.n.sons[i].kind == nkSym)
var param = typ.n.sons[i].sym var param = typ.n.sons[i].sym
app(pl, " ") app(pl, " ")
app(pl, param.name.s) app(pl, param.name.s)
app(pl, ": ") app(pl, ": ")
app(pl, genArg(aca, t.sons[i], param)) app(pl, genArg(aca, ri.sons[i], param))
if typ.sons[0] != nil: if typ.sons[0] != nil:
if isInvalidReturnType(typ.sons[0]): 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: # beware of 'result = p(result)'. We always allocate a temporary:
if d.k in {locTemp, locNone}: if d.k in {locTemp, locNone}:
# We already got a temp. Great, special case it: # 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") appf(p.s[cpsStmts], ";$n")
emitAfterCallActions(aca) 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

@ -1628,11 +1628,6 @@ proc expr(p: BProc, e: PNode, d: var TLoc) =
nkCallStrLit: nkCallStrLit:
if e.sons[0].kind == nkSym and e.sons[0].sym.magic != mNone: if e.sons[0].kind == nkSym and e.sons[0].sym.magic != mNone:
genMagicExpr(p, e, d, e.sons[0].sym.magic) 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: else:
genCall(p, e, d) genCall(p, e, d)
of nkCurly: of nkCurly:

View file

@ -18,16 +18,16 @@ proc genVarTuple(p: BProc, n: PNode) =
if n.kind != nkVarTuple: InternalError(n.info, "genVarTuple") if n.kind != nkVarTuple: InternalError(n.info, "genVarTuple")
var L = sonsLen(n) var L = sonsLen(n)
genLineDir(p, n) genLineDir(p, n)
initLocExpr(p, n.sons[L - 1], tup) initLocExpr(p, n.sons[L-1], tup)
var t = tup.t var t = tup.t
for i in countup(0, L - 3): for i in countup(0, L-3):
var v = n.sons[i].sym var v = n.sons[i].sym
if sfGlobal in v.flags: if sfGlobal in v.flags:
assignGlobalVar(p, v) assignGlobalVar(p, v)
genObjectInit(p, cpsInit, v.typ, v.loc, true) genObjectInit(p, cpsInit, v.typ, v.loc, true)
else: else:
assignLocalVar(p, v) assignLocalVar(p, v)
initVariable(p, v) initLocalVar(p, v, immediateAsgn=true)
initLoc(field, locExpr, t.sons[i], tup.s) initLoc(field, locExpr, t.sons[i], tup.s)
if t.n == nil: if t.n == nil:
field.r = ropef("$1.Field$2", [rdLoc(tup), toRope(i)]) 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)]) [rdLoc(tup), mangleRecFieldName(t.n.sons[i].sym, t)])
putLocIntoDest(p, v.loc, field) 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) = proc genSingleVar(p: BProc, a: PNode) =
var v = a.sons[0].sym var v = a.sons[0].sym
var immediateAsgn = a.sons[2].kind != nkEmpty
if sfGlobal in v.flags: if sfGlobal in v.flags:
assignGlobalVar(p, v) assignGlobalVar(p, v)
genObjectInit(p, cpsInit, v.typ, v.loc, true) genObjectInit(p, cpsInit, v.typ, v.loc, true)
else: else:
assignLocalVar(p, v) assignLocalVar(p, v)
initVariable(p, v) initLocalVar(p, v, immediateAsgn)
if a.sons[2].kind != nkEmpty: if immediateAsgn:
genLineDir(p, a) 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) = proc genVarStmt(p: BProc, n: PNode) =
for i in countup(0, sonsLen(n) - 1): 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) InitLocExpr(p, e.sons[0], a)
if fastAsgn: incl(a.flags, lfNoDeepCopy) if fastAsgn: incl(a.flags, lfNoDeepCopy)
assert(a.t != nil) assert(a.t != nil)
expr(p, e.sons[1], a) loadInto(p, e.sons[0], e.sons[1], a)
else: else:
asgnFieldDiscriminant(p, e) asgnFieldDiscriminant(p, e)

View file

@ -14,7 +14,7 @@ import
options, intsets, options, intsets,
nversion, nimsets, msgs, crc, bitsets, idents, lists, types, ccgutils, os, nversion, nimsets, msgs, crc, bitsets, idents, lists, types, ccgutils, os,
times, ropes, math, passes, rodread, wordrecg, treetab, cgmeth, times, ropes, math, passes, rodread, wordrecg, treetab, cgmeth,
rodutils, renderer, idgen, cgendata, ccgmerge, semfold rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases
when options.hasTinyCBackend: when options.hasTinyCBackend:
import tccgen import tccgen
@ -239,12 +239,13 @@ proc zeroVar(p: BProc, loc: TLoc, containsGCref: bool) =
if containsGcref and p.WithInLoop > 0: if containsGcref and p.WithInLoop > 0:
appf(p.s[cpsInit], "memset((void*)$1, 0, sizeof($2));$n", appf(p.s[cpsInit], "memset((void*)$1, 0, sizeof($2));$n",
[addrLoc(loc), rdLoc(loc)]) [addrLoc(loc), rdLoc(loc)])
genObjectInit(p, cpsInit, loc.t, loc, true)
appcg(p, cpsStmts, "#genericReset((void*)$1, $2);$n", appcg(p, cpsStmts, "#genericReset((void*)$1, $2);$n",
[addrLoc(loc), genTypeInfo(p.module, loc.t)]) [addrLoc(loc), genTypeInfo(p.module, loc.t)])
else: else:
appf(p.s[cpsStmts], "memset((void*)$1, 0, sizeof($2));$n", appf(p.s[cpsStmts], "memset((void*)$1, 0, sizeof($2));$n",
[addrLoc(loc), rdLoc(loc)]) [addrLoc(loc), rdLoc(loc)])
genObjectInit(p, cpsInit, loc.t, loc, true) genObjectInit(p, cpsStmts, loc.t, loc, true)
proc zeroTemp(p: BProc, loc: TLoc) = proc zeroTemp(p: BProc, loc: TLoc) =
if skipTypes(loc.t, abstractVarRange).Kind notin if skipTypes(loc.t, abstractVarRange).Kind notin
@ -259,15 +260,22 @@ proc zeroTemp(p: BProc, loc: TLoc) =
else: else:
appf(p.s[cpsStmts], "memset((void*)$1, 0, sizeof($2));$n", appf(p.s[cpsStmts], "memset((void*)$1, 0, sizeof($2));$n",
[addrLoc(loc), rdLoc(loc)]) [addrLoc(loc), rdLoc(loc)])
# XXX no object init necessary for temporaries?
when false: when false:
appcg(p, cpsStmts, "#genericReset((void*)$1, $2);$n", appcg(p, cpsStmts, "#genericReset((void*)$1, $2);$n",
[addrLoc(loc), genTypeInfo(p.module, loc.t)]) [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: if sfNoInit notin v.flags:
var b = containsGarbageCollectedRef(v.typ) # we know it is a local variable and thus on the stack!
if b or v.ast == nil: # If ``not immediateAsgn`` it is not initialized in a binding like
zeroVar(p, v.loc, b) # ``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) = proc initTemp(p: BProc, tmp: var TLoc) =
if containsGarbageCollectedRef(tmp.t) or isInvalidReturnType(tmp.t): if containsGarbageCollectedRef(tmp.t) or isInvalidReturnType(tmp.t):
@ -535,11 +543,12 @@ proc genProcAux(m: BModule, prc: PSym) =
if sfPure notin prc.flags and prc.typ.sons[0] != nil: if sfPure notin prc.flags and prc.typ.sons[0] != nil:
var res = prc.ast.sons[resultPos].sym # get result symbol 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: # declare the result symbol:
assignLocalVar(p, res) assignLocalVar(p, res)
assert(res.loc.r != nil) assert(res.loc.r != nil)
returnStmt = ropeff("return $1;$n", "ret $1$n", [rdLoc(res.loc)]) returnStmt = ropeff("return $1;$n", "ret $1$n", [rdLoc(res.loc)])
initVariable(p, res) initLocalVar(p, res, immediateAsgn=false)
else: else:
fillResult(res) fillResult(res)
assignParam(p, res) assignParam(p, res)

View file

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

View file

@ -983,12 +983,6 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
else: else:
result = semDirectOp(c, n, flags) result = semDirectOp(c, n, flags)
of mExpandToAst: result = semExpandToAst(c, n, s, 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) else: result = semDirectOp(c, n, flags)
proc semIfExpr(c: PContext, n: PNode): PNode = proc semIfExpr(c: PContext, n: PNode): PNode =

View file

@ -31,6 +31,8 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
case n[0].sym.magic case n[0].sym.magic
of mSlurp: result = semSlurp(c, n, flags) of mSlurp: result = semSlurp(c, n, flags)
of mIsPartOf: result = semIsPartOf(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 else: result = n

View file

@ -1584,6 +1584,11 @@ The implicit initialization can be avoided for optimization reasons with the
var var
a {.noInit.}: array [0..1023, char] 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 let statement
@ -2849,6 +2854,7 @@ exported.
The algorithm for compiling modules is: The algorithm for compiling modules is:
- compile the whole module as usual, following import statements recursively - compile the whole module as usual, following import statements recursively
- if there is a cycle only import the already parsed symbols (that are - if there is a cycle only import the already parsed symbols (that are
exported); if an unknown identifier occurs then abort exported); if an unknown identifier occurs then abort

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 ## converts the AST of `x` into a string representation. This is very useful
## for debugging. ## for debugging.
proc raiseAssert(msg: string) {.noinline.} =
raise newException(EAssertionFailed, msg)
template assert*(cond: expr, msg = "") = template assert*(cond: expr, msg = "") =
## provides a means to implement `programming by contracts`:idx: in Nimrod. ## provides a means to implement `programming by contracts`:idx: in Nimrod.
## ``assert`` evaluates expression ``cond`` and if ``cond`` is false, it ## ``assert`` evaluates expression ``cond`` and if ``cond`` is false, it
## raises an ``EAssertionFailure`` exception. However, the compiler may ## raises an ``EAssertionFailure`` exception. However, the compiler may
## not generate any code at all for ``assert`` if it is advised to do so. ## not generate any code at all for ``assert`` if it is advised to do so.
## Use ``assert`` for debugging purposes only. ## Use ``assert`` for debugging purposes only.
bind raiseAssert
when compileOption("assertions"): when compileOption("assertions"):
if not cond: 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

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

View file

@ -1,7 +1,8 @@
version 0.8.14 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 - cleanup file path handling in the compiler
- warning for implicit openArray -> varargs convention - warning for implicit openArray -> varargs convention
- implement explicit varargs; **but** ``len(varargs)`` problem remains! - implement explicit varargs; **but** ``len(varargs)`` problem remains!
@ -37,9 +38,6 @@ version 0.9.0
Bugs Bugs
---- ----
- bug: generic assign still buggy - 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 - 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: - bug: returning a tyVar does not mean it is save to return it:

View file

@ -53,8 +53,8 @@ Changes affecting backwards compatibility
because they should not be used directly anymore. because they should not be used directly anymore.
Wrapper procs have been created that should be used instead. Wrapper procs have been created that should be used instead.
- ``export`` is now a keyword. - ``export`` is now a keyword.
- ``assert`` is now implemented in pure Nimrod; it's easy to implement your - ``assert`` is now implemented in pure Nimrod as a template; it's easy
own assertion schemes now. to implement your own assertion templates with ``system.astToStr``.
Language Additions Language Additions
@ -112,6 +112,7 @@ Compiler Additions
- Added ``--import:file`` and ``--include:file`` configuration options - Added ``--import:file`` and ``--include:file`` configuration options
for specifying modules that will be automatically imported/incluced. for specifying modules that will be automatically imported/incluced.
- ``nimrod i`` can now optionally be given a module to execute. - ``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 Library Additions
@ -142,7 +143,7 @@ Library Additions
and ``exec`` on Posix systems. Define the symbol ``useFork`` to revert to and ``exec`` on Posix systems. Define the symbol ``useFork`` to revert to
the old implementation. the old implementation.
- Added ``intsets.assign``. - 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 2011-07-10 Version 0.8.12 released