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

This commit is contained in:
Araq 2012-06-03 10:11:01 +02:00
commit 70c283ac64
17 changed files with 261 additions and 73 deletions

View file

@ -371,7 +371,7 @@ type
TMagic* = enum # symbols that require compiler magic: TMagic* = enum # symbols that require compiler magic:
mNone, mNone,
mDefined, mDefinedInScope, mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf, mDefined, mDefinedInScope, mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf,
mEcho, mShallowCopy, mSlurp, mEcho, mShallowCopy, mSlurp, mStaticExec,
mParseExprToAst, mParseStmtToAst, mExpandToAst, mParseExprToAst, mParseStmtToAst, mExpandToAst,
mUnaryLt, mSucc, mUnaryLt, mSucc,
mPred, mInc, mDec, mOrd, mNew, mNewFinalize, mNewSeq, mLengthOpenArray, mPred, mInc, mDec, mOrd, mNew, mNewFinalize, mNewSeq, mLengthOpenArray,
@ -542,6 +542,9 @@ type
# for easy generation of proper error messages # for easy generation of proper error messages
# for variant record fields the discriminant # for variant record fields the discriminant
# expression # expression
# for modules, it's a placeholder for compiler
# generated code that will be appended to the
# module after the sem pass (see appendToModule)
options*: TOptions options*: TOptions
position*: int # used for many different things: position*: int # used for many different things:
# for enum fields its position; # for enum fields its position;
@ -568,6 +571,9 @@ type
# for record types a nkRecord node # for record types a nkRecord node
# for enum types a list of symbols # for enum types a list of symbols
# else: unused # else: unused
destructor*: PSym # destructor. warning: nil here may not necessary
# mean that there is no destructor.
# see instantiateDestructor in types.nim
owner*: PSym # the 'owner' of the type owner*: PSym # the 'owner' of the type
sym*: PSym # types have the sym associated with them sym*: PSym # types have the sym associated with them
# it is used for converting types to strings # it is used for converting types to strings
@ -740,6 +746,16 @@ proc linkTo*(s: PSym, t: PType): PSym {.discardable.} =
s.typ = t s.typ = t
result = s result = s
proc appendToModule*(m: PSym, n: PNode) =
## The compiler will use this internally to add nodes that will be
## appended to the module after the sem pass
if m.ast == nil:
m.ast = newNode(nkStmtList)
m.ast.sons = @[n]
else:
assert m.ast.kind == nkStmtList
m.ast.sons.add(n)
const # for all kind of hash tables: const # for all kind of hash tables:
GrowthFactor* = 2 # must be power of 2, > 0 GrowthFactor* = 2 # must be power of 2, > 0
StartSize* = 8 # must be power of 2, > 0 StartSize* = 8 # must be power of 2, > 0

View file

@ -1437,6 +1437,8 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
of mArrToSeq: genArrToSeq(p, e, d) of mArrToSeq: genArrToSeq(p, e, d)
of mNLen..mNError: of mNLen..mNError:
localError(e.info, errCannotGenerateCodeForX, e.sons[0].sym.name.s) localError(e.info, errCannotGenerateCodeForX, e.sons[0].sym.name.s)
of mSlurp, mStaticExec:
localError(e.info, errXMustBeCompileTime, e.sons[0].sym.name.s)
else: internalError(e.info, "genMagicExpr: " & $op) else: internalError(e.info, "genMagicExpr: " & $op)
proc genConstExpr(p: BProc, n: PNode): PRope proc genConstExpr(p: BProc, n: PNode): PRope

View file

@ -1173,6 +1173,8 @@ proc genMagic(p: var TProc, n: PNode, r: var TCompRes) =
localError(n.info, errCannotGenerateCodeForX, n.sons[0].sym.name.s) localError(n.info, errCannotGenerateCodeForX, n.sons[0].sym.name.s)
of mNewSeq: binaryStmt(p, n, r, "", "$1 = new Array($2)") of mNewSeq: binaryStmt(p, n, r, "", "$1 = new Array($2)")
of mEcho: genEcho(p, n, r) of mEcho: genEcho(p, n, r)
of mSlurp, mStaticExec:
localError(n.info, errXMustBeCompileTime, n.sons[0].sym.name.s)
else: else:
genCall(p, n, r) genCall(p, n, r)
#else internalError(e.info, 'genMagic: ' + magicToStr[op]); #else internalError(e.info, 'genMagic: ' + magicToStr[op]);

View file

@ -16,7 +16,7 @@
import import
strutils, magicsys, lists, options, ast, astalgo, trees, treetab, nimsets, strutils, magicsys, lists, options, ast, astalgo, trees, treetab, nimsets,
msgs, os, condsyms, idents, renderer, types, passes, semfold, transf, msgs, os, condsyms, idents, renderer, types, passes, semfold, transf,
parser, ropes, rodread, idgen parser, ropes, rodread, idgen, osproc, streams
type type
PStackFrame* = ref TStackFrame PStackFrame* = ref TStackFrame
@ -910,6 +910,43 @@ proc evalTypeTrait*(n: PNode, context: PSym): PNode =
else: else:
internalAssert false internalAssert false
proc expectString(n: PNode) =
if n.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit}:
GlobalError(n.info, errStringLiteralExpected)
proc evalSlurp*(e: PNode, module: PSym): PNode =
expectString(e)
try:
var filename = e.strVal.FindFile
var content = readFile(filename)
result = newStrNode(nkStrLit, content)
result.typ = getSysType(tyString)
result.info = e.info
# we produce a fake include statement for every slurped filename, so that
# the module dependencies are accurate:
appendToModule(module, newNode(nkIncludeStmt, e.info, @[
newStrNode(nkStrLit, filename)]))
except EIO:
GlobalError(e.info, errCannotOpenFile, e.strVal)
proc readOutput(p: PProcess): string =
result = ""
var output = p.outputStream
discard p.waitForExit
while not output.atEnd:
result.add(output.readLine)
proc evalStaticExec*(cmd, input: PNode): PNode =
expectString(cmd)
var p = startCmd(cmd.strVal)
if input != nil:
expectString(input)
p.inputStream.write(input.strVal)
p.inputStream.close()
result = newStrNode(nkStrLit, p.readOutput)
result.typ = getSysType(tyString)
result.info = cmd.info
proc evalExpandToAst(c: PEvalContext, original: PNode): PNode = proc evalExpandToAst(c: PEvalContext, original: PNode): PNode =
var var
n = original.copyTree n = original.copyTree
@ -960,6 +997,11 @@ proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode =
of mParseStmtToAst: result = evalParseStmt(c, n) of mParseStmtToAst: result = evalParseStmt(c, n)
of mExpandToAst: result = evalExpandToAst(c, n) of mExpandToAst: result = evalExpandToAst(c, n)
of mTypeTrait: result = evalTypeTrait(n, c.module) of mTypeTrait: result = evalTypeTrait(n, c.module)
of mSlurp: result = evalSlurp(evalAux(c, n.sons[1], {}), c.module)
of mStaticExec:
let cmd = evalAux(c, n.sons[1], {})
let input = if n.sonsLen == 3: evalAux(c, n.sons[2], {}) else: nil
result = evalStaticExec(cmd, input)
of mNLen: of mNLen:
result = evalAux(c, n.sons[1], {efLValue}) result = evalAux(c, n.sons[1], {efLValue})
if isSpecial(result): return if isSpecial(result): return

View file

@ -94,7 +94,7 @@ type
errUnhandledExceptionX, errCyclicTree, errXisNoMacroOrTemplate, errUnhandledExceptionX, errCyclicTree, errXisNoMacroOrTemplate,
errXhasSideEffects, errIteratorExpected, errLetNeedsInit, errXhasSideEffects, errIteratorExpected, errLetNeedsInit,
errThreadvarCannotInit, errWrongSymbolX, errIllegalCaptureX, errThreadvarCannotInit, errWrongSymbolX, errIllegalCaptureX,
errXCannotBeClosure, errXCannotBeClosure, errXMustBeCompileTime,
errUser, errUser,
warnCannotOpenFile, warnCannotOpenFile,
warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit, warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit,
@ -327,6 +327,7 @@ const
errWrongSymbolX: "usage of \'$1\' is a user-defined error", errWrongSymbolX: "usage of \'$1\' is a user-defined error",
errIllegalCaptureX: "illegal capture '$1'", errIllegalCaptureX: "illegal capture '$1'",
errXCannotBeClosure: "'$1' cannot have 'closure' calling convention", errXCannotBeClosure: "'$1' cannot have 'closure' calling convention",
errXMustBeCompileTime: "'$1' can only be used in compile-time context",
errUser: "$1", errUser: "$1",
warnCannotOpenFile: "cannot open \'$1\' [CannotOpenFile]", warnCannotOpenFile: "cannot open \'$1\' [CannotOpenFile]",
warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored [OctalEscape]", warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored [OctalEscape]",

View file

@ -57,6 +57,8 @@ proc optInd*(p: var TParser, n: PNode)
proc indAndComment*(p: var TParser, n: PNode) proc indAndComment*(p: var TParser, n: PNode)
proc setBaseFlags*(n: PNode, base: TNumericalBase) proc setBaseFlags*(n: PNode, base: TNumericalBase)
proc parseSymbol*(p: var TParser): PNode proc parseSymbol*(p: var TParser): PNode
proc parseTry(p: var TParser): PNode
proc parseCase(p: var TParser): PNode
# implementation # implementation
proc getTok(p: var TParser) = proc getTok(p: var TParser) =
@ -468,6 +470,11 @@ proc primarySuffix(p: var TParser, r: PNode): PNode =
addSon(result, a) addSon(result, a)
exprColonEqExprListAux(p, nkExprEqExpr, tkParRi, tkEquals, result) exprColonEqExprListAux(p, nkExprEqExpr, tkParRi, tkEquals, result)
parseDoBlocks(p, result) parseDoBlocks(p, result)
of tkDo:
var a = result
result = newNodeP(nkCall, p)
addSon(result, a)
parseDoBlocks(p, result)
of tkDot: of tkDot:
result = dotExpr(p, result) result = dotExpr(p, result)
result = parseGStrLit(p, result) result = parseGStrLit(p, result)
@ -704,6 +711,8 @@ proc parseExpr(p: var TParser): PNode =
case p.tok.tokType: case p.tok.tokType:
of tkIf: result = parseIfExpr(p, nkIfExpr) of tkIf: result = parseIfExpr(p, nkIfExpr)
of tkWhen: result = parseIfExpr(p, nkWhenExpr) of tkWhen: result = parseIfExpr(p, nkWhenExpr)
of tkTry: result = parseTry(p)
of tkCase: result = parseCase(p)
else: result = lowestExpr(p) else: result = lowestExpr(p)
proc primary(p: var TParser, skipSuffix = false): PNode = proc primary(p: var TParser, skipSuffix = false): PNode =
@ -944,13 +953,19 @@ proc parseWhile(p: var TParser): PNode =
proc parseCase(p: var TParser): PNode = proc parseCase(p: var TParser): PNode =
var var
b: PNode b: PNode
inElif: bool inElif= false
wasIndented = false
result = newNodeP(nkCaseStmt, p) result = newNodeP(nkCaseStmt, p)
getTok(p) getTok(p)
addSon(result, parseExpr(p)) addSon(result, parseExpr(p))
if p.tok.tokType == tkColon: getTok(p) if p.tok.tokType == tkColon: getTok(p)
skipComment(p, result) skipComment(p, result)
inElif = false
if p.tok.tokType == tkInd:
pushInd(p.lex, p.tok.indent)
getTok(p)
wasIndented = true
while true: while true:
if p.tok.tokType == tkSad: getTok(p) if p.tok.tokType == tkSad: getTok(p)
case p.tok.tokType case p.tok.tokType
@ -975,6 +990,10 @@ proc parseCase(p: var TParser): PNode =
addSon(result, b) addSon(result, b)
if b.kind == nkElse: break if b.kind == nkElse: break
if wasIndented:
eat(p, tkDed)
popInd(p.lex)
proc parseTry(p: var TParser): PNode = proc parseTry(p: var TParser): PNode =
result = newNodeP(nkTryStmt, p) result = newNodeP(nkTryStmt, p)
getTok(p) getTok(p)
@ -999,6 +1018,13 @@ proc parseTry(p: var TParser): PNode =
if b.kind == nkFinally: break if b.kind == nkFinally: break
if b == nil: parMessage(p, errTokenExpected, "except") if b == nil: parMessage(p, errTokenExpected, "except")
proc parseExceptBlock(p: var TParser, kind: TNodeKind): PNode =
result = newNodeP(kind, p)
getTok(p)
eat(p, tkColon)
skipComment(p, result)
addSon(result, parseStmt(p))
proc parseFor(p: var TParser): PNode = proc parseFor(p: var TParser): PNode =
result = newNodeP(nkForStmt, p) result = newNodeP(nkForStmt, p)
getTok(p) getTok(p)
@ -1393,6 +1419,8 @@ proc complexOrSimpleStmt(p: var TParser): PNode =
of tkWhile: result = parseWhile(p) of tkWhile: result = parseWhile(p)
of tkCase: result = parseCase(p) of tkCase: result = parseCase(p)
of tkTry: result = parseTry(p) of tkTry: result = parseTry(p)
of tkFinally: result = parseExceptBlock(p, nkFinally)
of tkExcept: result = parseExceptBlock(p, nkExceptBranch)
of tkFor: result = parseFor(p) of tkFor: result = parseFor(p)
of tkBlock: result = parseBlock(p) of tkBlock: result = parseBlock(p)
of tkStatic: result = parseStatic(p) of tkStatic: result = parseStatic(p)

View file

@ -89,9 +89,6 @@ proc semConstExpr(c: PContext, n: PNode): PNode =
return nil return nil
result = evalTypedExpr(c, e) result = evalTypedExpr(c, e)
proc semAndEvalConstExpr(c: PContext, n: PNode): PNode =
result = semConstExpr(c, n)
include seminst, semcall include seminst, semcall
proc semAfterMacroCall(c: PContext, n: PNode, s: PSym): PNode = proc semAfterMacroCall(c: PContext, n: PNode, s: PSym): PNode =
@ -219,12 +216,8 @@ proc myClose(context: PPassContext, n: PNode): PNode =
else: else:
InternalError(n.info, "n is not nil") #result := n; InternalError(n.info, "n is not nil") #result := n;
addCodeForGenerics(c, result) addCodeForGenerics(c, result)
# we produce a fake include statement for every slurped filename, so that if c.module.ast != nil:
# the module dependencies are accurate: result.add(c.module.ast)
var ics = newNode(nkIncludeStmt)
for s in items(c.slurpedFiles): ics.add(newStrNode(nkStrLit, s))
result.add(ics)
checkThreads(c) checkThreads(c)
popOwner() popOwner()
popProcCon(c) popProcCon(c)

View file

@ -73,7 +73,6 @@ type
filename*: string # the module's filename filename*: string # the module's filename
userPragmas*: TStrTable userPragmas*: TStrTable
evalContext*: PEvalContext evalContext*: PEvalContext
slurpedFiles*: seq[string]
var var
gGenericsCache: PGenericsCache # save for modularity gGenericsCache: PGenericsCache # save for modularity
@ -153,7 +152,6 @@ proc newContext(module: PSym, nimfile: string): PContext =
result.filename = nimfile result.filename = nimfile
result.includedFiles = initIntSet() result.includedFiles = initIntSet()
initStrTable(result.userPragmas) initStrTable(result.userPragmas)
result.slurpedFiles = @[]
if optSymbolFiles notin gGlobalOptions: if optSymbolFiles notin gGlobalOptions:
# re-usage of generic instantiations across module boundaries is # re-usage of generic instantiations across module boundaries is
# very nice for code size: # very nice for code size:

View file

@ -491,12 +491,6 @@ proc analyseIfAddressTakenInCall(c: PContext, n: PNode) =
skipTypes(t.sons[i], abstractInst).kind == tyVar: skipTypes(t.sons[i], abstractInst).kind == tyVar:
n.sons[i] = analyseIfAddressTaken(c, n.sons[i]) 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 include semmagic
proc evalAtCompileTime(c: PContext, n: PNode): PNode = proc evalAtCompileTime(c: PContext, n: PNode): PNode =
@ -901,7 +895,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
checkSonsLen(n, 2) checkSonsLen(n, 2)
n.sons[0] = makeDeref(n.sons[0]) n.sons[0] = makeDeref(n.sons[0])
# [] operator for tuples requires constant expression: # [] operator for tuples requires constant expression:
n.sons[1] = semAndEvalConstExpr(c, n.sons[1]) n.sons[1] = semConstExpr(c, n.sons[1])
if skipTypes(n.sons[1].typ, {tyGenericInst, tyRange, tyOrdinal}).kind in if skipTypes(n.sons[1].typ, {tyGenericInst, tyRange, tyOrdinal}).kind in
{tyInt..tyInt64}: {tyInt..tyInt64}:
var idx = getOrdValue(n.sons[1]) var idx = getOrdValue(n.sons[1])

View file

@ -207,7 +207,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
mExit, mInc, ast.mDec, mEcho, mSwap, mAppendStrCh, mExit, mInc, ast.mDec, mEcho, mSwap, mAppendStrCh,
mAppendStrStr, mAppendSeqElem, mSetLengthStr, mSetLengthSeq, mAppendStrStr, mAppendSeqElem, mSetLengthStr, mSetLengthSeq,
mParseExprToAst, mParseStmtToAst, mExpandToAst, mTypeTrait, mParseExprToAst, mParseStmtToAst, mExpandToAst, mTypeTrait,
mNLen..mNError, mEqRef: mNLen..mNError, mEqRef, mSlurp, mStaticExec:
nil nil
of mRand: of mRand:
result = newIntNodeT(math.random(a.getInt.int), n) result = newIntNodeT(math.random(a.getInt.int), n)

View file

@ -14,19 +14,6 @@ proc semIsPartOf(c: PContext, n: PNode, flags: TExprFlags): PNode =
var r = isPartOf(n[1], n[2]) var r = isPartOf(n[1], n[2])
result = newIntNodeT(ord(r), n) 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(a.strVal)
except EIO:
GlobalError(a.info, errCannotOpenFile, a.strVal)
proc expectIntLit(c: PContext, n: PNode): int = proc expectIntLit(c: PContext, n: PNode): int =
let x = c.semConstExpr(c, n) let x = c.semConstExpr(c, n)
case x.kind case x.kind
@ -56,7 +43,6 @@ proc semTypeTraits(c: PContext, n: PNode): PNode =
proc magicsAfterOverloadResolution(c: PContext, n: PNode, proc magicsAfterOverloadResolution(c: PContext, n: PNode,
flags: TExprFlags): PNode = flags: TExprFlags): PNode =
case n[0].sym.magic case n[0].sym.magic
of mSlurp: result = semSlurp(c, n, flags)
of mIsPartOf: result = semIsPartOf(c, n, flags) of mIsPartOf: result = semIsPartOf(c, n, flags)
of mTypeTrait: result = semTypeTraits(c, n) of mTypeTrait: result = semTypeTraits(c, n)
of mAstToStr: of mAstToStr:

View file

@ -27,7 +27,7 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
case it.kind case it.kind
of nkElifBranch, nkElifExpr: of nkElifBranch, nkElifExpr:
checkSonsLen(it, 2) checkSonsLen(it, 2)
var e = semAndEvalConstExpr(c, it.sons[0]) var e = semConstExpr(c, it.sons[0])
if e.kind != nkIntLit: InternalError(n.info, "semWhen") if e.kind != nkIntLit: InternalError(n.info, "semWhen")
if e.intVal != 0 and result == nil: if e.intVal != 0 and result == nil:
setResult(it.sons[1]) setResult(it.sons[1])
@ -772,6 +772,10 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
incl(s.flags, sfForward) incl(s.flags, sfForward)
elif sfBorrow in s.flags: semBorrow(c, n, s) elif sfBorrow in s.flags: semBorrow(c, n, s)
sideEffectsCheck(c, s) sideEffectsCheck(c, s)
if result.sons[namePos].sym.name.id == ord(wDestroy):
if s.typ.sons.len == 2:
let typ = s.typ.sons[1].skipTypes({tyVar})
typ.destructor = s
if s.typ.callConv == ccClosure and s.owner.kind == skModule: if s.typ.callConv == ccClosure and s.owner.kind == skModule:
localError(s.info, errXCannotBeClosure, s.name.s) localError(s.info, errXCannotBeClosure, s.name.s)
closeScope(c.tab) # close scope for parameters closeScope(c.tab) # close scope for parameters
@ -860,6 +864,62 @@ proc semStaticStmt(c: PContext, n: PNode): PNode =
if result.isNil: if result.isNil:
LocalError(n.info, errCannotInterpretNodeX, renderTree(n)) LocalError(n.info, errCannotInterpretNodeX, renderTree(n))
proc insertDestructors(c: PContext, varSection: PNode):
tuple[outer: PNode, inner: PNode] =
# Accepts a var or let section.
#
# When a var section has variables with destructors
# the var section is split up and finally blocks are inserted
# immediately after all "destructable" vars
#
# In case there were no destrucable variables, the proc returns
# (nil, nil) and the enclosing stmt-list requires no modifications.
#
# Otherwise, after the try blocks are created, the rest of the enclosing
# stmt-list should be inserted in the most `inner` such block (corresponding
# to the last variable).
#
# `outer` is a statement list that should replace the original var section.
# It will include the new truncated var section followed by the outermost
# try block.
let totalVars = varSection.sonsLen
for j in countup(0, totalVars - 1):
let
varId = varSection[j][0]
varTyp = varId.sym.typ
info = varId.info
if varTyp != nil and instantiateDestructor(varTyp):
var tryStmt = newNodeI(nkTryStmt, info)
if j < totalVars - 1:
var remainingVars = newNodeI(varSection.kind, info)
remainingVars.sons = varSection.sons[(j+1)..(-1)]
let (outer, inner) = insertDestructors(c, remainingVars)
if outer != nil:
tryStmt.addSon(outer)
result.inner = inner
else:
result.inner = newNodeI(nkStmtList, info)
result.inner.addSon(remainingVars)
tryStmt.addSon(result.inner)
else:
result.inner = newNodeI(nkStmtList, info)
tryStmt.addSon(result.inner)
tryStmt.addSon(
newNode(nkFinally, info, @[
semStmt(c, newNode(nkCall, info, @[
semSym(c, varId, varTyp.destructor, {}),
semSym(c, varId, varId.sym, {})]))]))
result.outer = newNodeI(nkStmtList, info)
varSection.sons.setLen(j+1)
result.outer.addSon(varSection)
result.outer.addSon(tryStmt)
return
proc SemStmt(c: PContext, n: PNode): PNode = proc SemStmt(c: PContext, n: PNode): PNode =
const # must be last statements in a block: const # must be last statements in a block:
LastBlockStmts = {nkRaiseStmt, nkReturnStmt, nkBreakStmt, nkContinueStmt} LastBlockStmts = {nkRaiseStmt, nkReturnStmt, nkBreakStmt, nkContinueStmt}
@ -876,12 +936,42 @@ proc SemStmt(c: PContext, n: PNode): PNode =
of nkStmtList: of nkStmtList:
var length = sonsLen(n) var length = sonsLen(n)
for i in countup(0, length - 1): for i in countup(0, length - 1):
case n.sons[i].kind
of nkFinally, nkExceptBranch:
# stand-alone finally and except blocks are
# transformed into regular try blocks:
#
# var f = fopen("somefile") | var f = fopen("somefile")
# finally: fcsole(f) | try:
# ... | ...
# | finally:
# | fclose(f)
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)]
tryStmt.addSon(body)
tryStmt.addSon(n.sons[i])
n.sons[i] = semTry(c, tryStmt)
n.sons.setLen(i+1)
return
else:
n.sons[i] = semStmt(c, n.sons[i]) n.sons[i] = semStmt(c, n.sons[i])
if n.sons[i].kind in LastBlockStmts: case n.sons[i].kind
of nkVarSection, nkLetSection:
let (outer, inner) = insertDestructors(c, n.sons[i])
if outer != nil:
n.sons[i] = outer
for j in countup(i+1, length-1):
inner.addSon(SemStmt(c, n.sons[j]))
n.sons.setLen(i+1)
return
of LastBlockStmts:
for j in countup(i + 1, length - 1): for j in countup(i + 1, length - 1):
case n.sons[j].kind case n.sons[j].kind
of nkPragma, nkCommentStmt, nkNilLit, nkEmpty: nil of nkPragma, nkCommentStmt, nkNilLit, nkEmpty: nil
else: localError(n.sons[j].info, errStmtInvalidAfterReturn) else: localError(n.sons[j].info, errStmtInvalidAfterReturn)
else: nil
of nkRaiseStmt: result = semRaise(c, n) of nkRaiseStmt: result = semRaise(c, n)
of nkVarSection: result = semVarOrLet(c, n, skVar) of nkVarSection: result = semVarOrLet(c, n, skVar)
of nkLetSection: result = semVarOrLet(c, n, skLet) of nkLetSection: result = semVarOrLet(c, n, skLet)

View file

@ -1062,3 +1062,10 @@ proc getSize(typ: PType): biggestInt =
result = computeSize(typ) result = computeSize(typ)
if result < 0: InternalError("getSize(" & $typ.kind & ')') if result < 0: InternalError("getSize(" & $typ.kind & ')')
proc instantiateDestructor*(typ: PType): bool =
# return true if the type already had a user-defined
# destructor or if the compiler generated a default
# member-wise one
if typ.destructor != nil: return true
return false

View file

@ -36,6 +36,9 @@ type
wColon, wColonColon, wEquals, wDot, wDotDot, wColon, wColonColon, wEquals, wDot, wDotDot,
wStar, wMinus, wStar, wMinus,
wMagic, wThread, wFinal, wProfiler, wObjChecks, wMagic, wThread, wFinal, wProfiler, wObjChecks,
wDestroy,
wImmediate, wImportCpp, wImportObjC, wImmediate, wImportCpp, wImportObjC,
wImportCompilerProc, wImportCompilerProc,
wImportc, wExportc, wIncompleteStruct, wImportc, wExportc, wIncompleteStruct,
@ -112,6 +115,8 @@ const
"*", "-", "*", "-",
"magic", "thread", "final", "profiler", "objchecks", "magic", "thread", "final", "profiler", "objchecks",
"destroy",
"immediate", "importcpp", "importobjc", "immediate", "importcpp", "importobjc",
"importcompilerproc", "importc", "exportc", "incompletestruct", "importcompilerproc", "importc", "exportc", "incompletestruct",
"align", "nodecl", "pure", "sideeffect", "align", "nodecl", "pure", "sideeffect",

View file

@ -1879,6 +1879,15 @@ handled, it is propagated through the call stack. This means that often
the rest of the procedure - that is not within a ``finally`` clause - the rest of the procedure - that is not within a ``finally`` clause -
is not executed (if an exception occurs). is not executed (if an exception occurs).
`except`:idx: and `finally`:idx: can also be used as a stand-alone statements.
Any statements following them in the current block will be considered to be
in an implicit try block:
.. code-block:: nimrod
var f = fopen("numbers.txt", "r")
finally: fcsole(f)
...
Return statement Return statement
~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~

View file

@ -2168,10 +2168,22 @@ proc getTypeInfo*[T](x: T): pointer {.magic: "GetTypeInfo".}
## the `typeinfo` module instead. ## the `typeinfo` module instead.
proc slurp*(filename: string): string {.magic: "Slurp".} proc slurp*(filename: string): string {.magic: "Slurp".}
## compiletime ``readFile`` proc for easy `resource`:idx: embedding: proc staticRead*(filename: string): string {.magic: "Slurp".}
## compile-time ``readFile`` proc for easy `resource`:idx: embedding:
## .. code-block:: nimrod ## .. code-block:: nimrod
## ##
## const myResource = slurp"mydatafile.bin" ## const myResource = staticRead"mydatafile.bin"
##
proc staticExec*(command: string, input = ""): string {.
magic: "StaticExec".} = nil
## executes an external process at compile-time.
## if `input` is not an empty string, it will be passed as a standard input
## to the executed program.
## .. code-block:: nimrod
##
## const buildInfo = "Revision " & staticExec("git rev-parse HEAD") &
## "\nCompiled on " & staticExec("uname -v")
## ##
proc `+=`*[T](x, y: ordinal[T]) {.magic: "Inc", noSideEffect.} proc `+=`*[T](x, y: ordinal[T]) {.magic: "Inc", noSideEffect.}

View file

@ -27,6 +27,8 @@ Library Additions
assignments. assignments.
- Added ``system.eval`` that can execute an anonymous block of code at - Added ``system.eval`` that can execute an anonymous block of code at
compile time as if was a macro. compile time as if was a macro.
- Added ``system.staticExec`` for compile-time execution of external programs
- Added ``system.staticRead`` as a synonym for slurp
- Added ``macros.emit`` that can emit an arbitrary computed string as nimrod - Added ``macros.emit`` that can emit an arbitrary computed string as nimrod
code during compilation. code during compilation.
- Added ``strutils.parseEnum``. - Added ``strutils.parseEnum``.
@ -102,6 +104,7 @@ Language Additions
- ``when`` expressions are now allowed just like ``if`` expressions. - ``when`` expressions are now allowed just like ``if`` expressions.
- The precedence for operators starting with ``@`` is different now - The precedence for operators starting with ``@`` is different now
allowing for *sigil-like* operators. allowing for *sigil-like* operators.
- Stand-alone ``finally`` and ``except`` blocks are now supported.
2012-02-09 Version 0.8.14 released 2012-02-09 Version 0.8.14 released