diff options
-rwxr-xr-x | compiler/ast.nim | 18 | ||||
-rwxr-xr-x | compiler/ccgexprs.nim | 2 | ||||
-rwxr-xr-x | compiler/ecmasgen.nim | 2 | ||||
-rwxr-xr-x | compiler/evals.nim | 44 | ||||
-rwxr-xr-x | compiler/msgs.nim | 3 | ||||
-rwxr-xr-x | compiler/parser.nim | 36 | ||||
-rwxr-xr-x | compiler/sem.nim | 11 | ||||
-rwxr-xr-x | compiler/semdata.nim | 2 | ||||
-rwxr-xr-x | compiler/semexprs.nim | 8 | ||||
-rwxr-xr-x | compiler/semfold.nim | 2 | ||||
-rw-r--r-- | compiler/semmagic.nim | 14 | ||||
-rwxr-xr-x | compiler/semstmts.nim | 106 | ||||
-rwxr-xr-x | compiler/types.nim | 7 | ||||
-rwxr-xr-x | compiler/wordrecg.nim | 7 | ||||
-rwxr-xr-x | doc/manual.txt | 53 | ||||
-rwxr-xr-x | lib/system.nim | 15 | ||||
-rwxr-xr-x | web/news.txt | 3 |
17 files changed, 260 insertions, 73 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index 978b43b5e..d9ec70450 100755 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -371,7 +371,7 @@ type TMagic* = enum # symbols that require compiler magic: mNone, mDefined, mDefinedInScope, mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf, - mEcho, mShallowCopy, mSlurp, + mEcho, mShallowCopy, mSlurp, mStaticExec, mParseExprToAst, mParseStmtToAst, mExpandToAst, mUnaryLt, mSucc, mPred, mInc, mDec, mOrd, mNew, mNewFinalize, mNewSeq, mLengthOpenArray, @@ -542,6 +542,9 @@ type # for easy generation of proper error messages # for variant record fields the discriminant # 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 position*: int # used for many different things: # for enum fields its position; @@ -568,6 +571,9 @@ type # for record types a nkRecord node # for enum types a list of symbols # 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 sym*: PSym # types have the sym associated with them # it is used for converting types to strings @@ -740,6 +746,16 @@ proc linkTo*(s: PSym, t: PType): PSym {.discardable.} = s.typ = t 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: GrowthFactor* = 2 # must be power of 2, > 0 StartSize* = 8 # must be power of 2, > 0 diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index dbe528ab5..7a0face5e 100755 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1437,6 +1437,8 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mArrToSeq: genArrToSeq(p, e, d) of mNLen..mNError: 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) proc genConstExpr(p: BProc, n: PNode): PRope diff --git a/compiler/ecmasgen.nim b/compiler/ecmasgen.nim index ecdbb022e..07754ee13 100755 --- a/compiler/ecmasgen.nim +++ b/compiler/ecmasgen.nim @@ -1173,6 +1173,8 @@ proc genMagic(p: var TProc, n: PNode, r: var TCompRes) = localError(n.info, errCannotGenerateCodeForX, n.sons[0].sym.name.s) of mNewSeq: binaryStmt(p, n, r, "", "$1 = new Array($2)") of mEcho: genEcho(p, n, r) + of mSlurp, mStaticExec: + localError(n.info, errXMustBeCompileTime, n.sons[0].sym.name.s) else: genCall(p, n, r) #else internalError(e.info, 'genMagic: ' + magicToStr[op]); diff --git a/compiler/evals.nim b/compiler/evals.nim index b2fded071..5c77a4d94 100755 --- a/compiler/evals.nim +++ b/compiler/evals.nim @@ -16,7 +16,7 @@ import strutils, magicsys, lists, options, ast, astalgo, trees, treetab, nimsets, msgs, os, condsyms, idents, renderer, types, passes, semfold, transf, - parser, ropes, rodread, idgen + parser, ropes, rodread, idgen, osproc, streams type PStackFrame* = ref TStackFrame @@ -910,6 +910,43 @@ proc evalTypeTrait*(n: PNode, context: PSym): PNode = else: 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 = var n = original.copyTree @@ -960,6 +997,11 @@ proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode = of mParseStmtToAst: result = evalParseStmt(c, n) of mExpandToAst: result = evalExpandToAst(c, n) 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: result = evalAux(c, n.sons[1], {efLValue}) if isSpecial(result): return diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 17cfeae4d..2af512af7 100755 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -94,7 +94,7 @@ type errUnhandledExceptionX, errCyclicTree, errXisNoMacroOrTemplate, errXhasSideEffects, errIteratorExpected, errLetNeedsInit, errThreadvarCannotInit, errWrongSymbolX, errIllegalCaptureX, - errXCannotBeClosure, + errXCannotBeClosure, errXMustBeCompileTime, errUser, warnCannotOpenFile, warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit, @@ -327,6 +327,7 @@ const errWrongSymbolX: "usage of \'$1\' is a user-defined error", errIllegalCaptureX: "illegal capture '$1'", errXCannotBeClosure: "'$1' cannot have 'closure' calling convention", + errXMustBeCompileTime: "'$1' can only be used in compile-time context", errUser: "$1", warnCannotOpenFile: "cannot open \'$1\' [CannotOpenFile]", warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored [OctalEscape]", diff --git a/compiler/parser.nim b/compiler/parser.nim index 78e39bcd2..4baee5b43 100755 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -57,6 +57,8 @@ proc optInd*(p: var TParser, n: PNode) proc indAndComment*(p: var TParser, n: PNode) proc setBaseFlags*(n: PNode, base: TNumericalBase) proc parseSymbol*(p: var TParser): PNode +proc parseTry(p: var TParser): PNode +proc parseCase(p: var TParser): PNode # implementation proc getTok(p: var TParser) = @@ -468,6 +470,11 @@ proc primarySuffix(p: var TParser, r: PNode): PNode = addSon(result, a) exprColonEqExprListAux(p, nkExprEqExpr, tkParRi, tkEquals, result) parseDoBlocks(p, result) + of tkDo: + var a = result + result = newNodeP(nkCall, p) + addSon(result, a) + parseDoBlocks(p, result) of tkDot: result = dotExpr(p, result) result = parseGStrLit(p, result) @@ -704,6 +711,8 @@ proc parseExpr(p: var TParser): PNode = case p.tok.tokType: of tkIf: result = parseIfExpr(p, nkIfExpr) of tkWhen: result = parseIfExpr(p, nkWhenExpr) + of tkTry: result = parseTry(p) + of tkCase: result = parseCase(p) else: result = lowestExpr(p) proc primary(p: var TParser, skipSuffix = false): PNode = @@ -944,13 +953,19 @@ proc parseWhile(p: var TParser): PNode = proc parseCase(p: var TParser): PNode = var b: PNode - inElif: bool + inElif= false + wasIndented = false result = newNodeP(nkCaseStmt, p) getTok(p) addSon(result, parseExpr(p)) if p.tok.tokType == tkColon: getTok(p) skipComment(p, result) - inElif = false + + if p.tok.tokType == tkInd: + pushInd(p.lex, p.tok.indent) + getTok(p) + wasIndented = true + while true: if p.tok.tokType == tkSad: getTok(p) case p.tok.tokType @@ -973,8 +988,12 @@ proc parseCase(p: var TParser): PNode = skipComment(p, b) addSon(b, parseStmt(p)) 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 = result = newNodeP(nkTryStmt, p) getTok(p) @@ -998,7 +1017,14 @@ proc parseTry(p: var TParser): PNode = addSon(result, b) if b.kind == nkFinally: break 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 = result = newNodeP(nkForStmt, p) getTok(p) @@ -1393,6 +1419,8 @@ proc complexOrSimpleStmt(p: var TParser): PNode = of tkWhile: result = parseWhile(p) of tkCase: result = parseCase(p) of tkTry: result = parseTry(p) + of tkFinally: result = parseExceptBlock(p, nkFinally) + of tkExcept: result = parseExceptBlock(p, nkExceptBranch) of tkFor: result = parseFor(p) of tkBlock: result = parseBlock(p) of tkStatic: result = parseStatic(p) diff --git a/compiler/sem.nim b/compiler/sem.nim index 8afbe3c51..721b4b040 100755 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -89,9 +89,6 @@ proc semConstExpr(c: PContext, n: PNode): PNode = return nil result = evalTypedExpr(c, e) -proc semAndEvalConstExpr(c: PContext, n: PNode): PNode = - result = semConstExpr(c, n) - include seminst, semcall proc semAfterMacroCall(c: PContext, n: PNode, s: PSym): PNode = @@ -219,12 +216,8 @@ proc myClose(context: PPassContext, n: PNode): PNode = else: InternalError(n.info, "n is not nil") #result := n; addCodeForGenerics(c, result) - # we produce a fake include statement for every slurped filename, so that - # the module dependencies are accurate: - var ics = newNode(nkIncludeStmt) - for s in items(c.slurpedFiles): ics.add(newStrNode(nkStrLit, s)) - result.add(ics) - + if c.module.ast != nil: + result.add(c.module.ast) checkThreads(c) popOwner() popProcCon(c) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index f97da4717..81e45f71c 100755 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -73,7 +73,6 @@ type filename*: string # the module's filename userPragmas*: TStrTable evalContext*: PEvalContext - slurpedFiles*: seq[string] var gGenericsCache: PGenericsCache # save for modularity @@ -153,7 +152,6 @@ proc newContext(module: PSym, nimfile: string): PContext = result.filename = nimfile result.includedFiles = initIntSet() initStrTable(result.userPragmas) - result.slurpedFiles = @[] if optSymbolFiles notin gGlobalOptions: # re-usage of generic instantiations across module boundaries is # very nice for code size: diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index fe35607cb..ac9075d4f 100755 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -491,12 +491,6 @@ 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 evalAtCompileTime(c: PContext, n: PNode): PNode = @@ -901,7 +895,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = checkSonsLen(n, 2) n.sons[0] = makeDeref(n.sons[0]) # [] 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 {tyInt..tyInt64}: var idx = getOrdValue(n.sons[1]) diff --git a/compiler/semfold.nim b/compiler/semfold.nim index 82ee7de13..565155791 100755 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -207,7 +207,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode = mExit, mInc, ast.mDec, mEcho, mSwap, mAppendStrCh, mAppendStrStr, mAppendSeqElem, mSetLengthStr, mSetLengthSeq, mParseExprToAst, mParseStmtToAst, mExpandToAst, mTypeTrait, - mNLen..mNError, mEqRef: + mNLen..mNError, mEqRef, mSlurp, mStaticExec: nil of mRand: result = newIntNodeT(math.random(a.getInt.int), n) diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index e6861f2e4..b7e890e67 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -14,19 +14,6 @@ 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(a.strVal) - except EIO: - GlobalError(a.info, errCannotOpenFile, a.strVal) - proc expectIntLit(c: PContext, n: PNode): int = let x = c.semConstExpr(c, n) case x.kind @@ -56,7 +43,6 @@ proc semTypeTraits(c: PContext, n: PNode): PNode = 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 mTypeTrait: result = semTypeTraits(c, n) of mAstToStr: diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 202b7416a..82f43e787 100755 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -27,7 +27,7 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode = case it.kind of nkElifBranch, nkElifExpr: 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.intVal != 0 and result == nil: setResult(it.sons[1]) @@ -772,6 +772,10 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, incl(s.flags, sfForward) elif sfBorrow in s.flags: semBorrow(c, n, 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: localError(s.info, errXCannotBeClosure, s.name.s) closeScope(c.tab) # close scope for parameters @@ -860,6 +864,62 @@ proc semStaticStmt(c: PContext, n: PNode): PNode = if result.isNil: 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 = const # must be last statements in a block: LastBlockStmts = {nkRaiseStmt, nkReturnStmt, nkBreakStmt, nkContinueStmt} @@ -875,13 +935,43 @@ proc SemStmt(c: PContext, n: PNode): PNode = of nkBlockStmt: result = semBlock(c, n) of nkStmtList: var length = sonsLen(n) - for i in countup(0, length - 1): - n.sons[i] = semStmt(c, n.sons[i]) - if n.sons[i].kind in LastBlockStmts: - for j in countup(i + 1, length - 1): - case n.sons[j].kind - of nkPragma, nkCommentStmt, nkNilLit, nkEmpty: nil - else: localError(n.sons[j].info, errStmtInvalidAfterReturn) + 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]) + 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): + case n.sons[j].kind + of nkPragma, nkCommentStmt, nkNilLit, nkEmpty: nil + else: localError(n.sons[j].info, errStmtInvalidAfterReturn) + else: nil of nkRaiseStmt: result = semRaise(c, n) of nkVarSection: result = semVarOrLet(c, n, skVar) of nkLetSection: result = semVarOrLet(c, n, skLet) diff --git a/compiler/types.nim b/compiler/types.nim index b3737b134..2f201b9de 100755 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -1061,4 +1061,11 @@ proc getReturnType*(s: PSym): PType = proc getSize(typ: PType): biggestInt = result = computeSize(typ) 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 diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index 30a6b3c2c..af482966b 100755 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -36,6 +36,9 @@ type wColon, wColonColon, wEquals, wDot, wDotDot, wStar, wMinus, wMagic, wThread, wFinal, wProfiler, wObjChecks, + + wDestroy, + wImmediate, wImportCpp, wImportObjC, wImportCompilerProc, wImportc, wExportc, wIncompleteStruct, @@ -110,7 +113,9 @@ const ":", "::", "=", ".", "..", "*", "-", - "magic", "thread", "final", "profiler", "objchecks", + "magic", "thread", "final", "profiler", "objchecks", + + "destroy", "immediate", "importcpp", "importobjc", "importcompilerproc", "importc", "exportc", "incompletestruct", diff --git a/doc/manual.txt b/doc/manual.txt index 98a8d7122..5359dfc57 100755 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -388,37 +388,37 @@ indentation tokens is already described in the `Lexical Analysis`_ section. Nimrod allows user-definable operators. Binary operators have 10 different levels of precedence. - -Relevant character ------------------- - + +Relevant character +------------------ + An operator symbol's *relevant character* is its first character unless the first character is ``\`` and its length is greater than 1 -then it is the second character. - -This rule allows to escape operator symbols with ``\`` and keeps the operator's -precedence and associativity; this is useful for meta programming. - - -Associativity -------------- - -All binary operators are left-associative, except binary operators whose +then it is the second character. + +This rule allows to escape operator symbols with ``\`` and keeps the operator's +precedence and associativity; this is useful for meta programming. + + +Associativity +------------- + +All binary operators are left-associative, except binary operators whose relevant char is ``^``. - -Precedence ----------- + +Precedence +---------- For operators that are not keywords the precedence is determined by the following rules: If the operator ends with ``=`` and its relevant character is none of ``<``, ``>``, ``!``, ``=``, ``~``, ``?``, it is an *assignment operator* which -has the lowest precedence. - -If the operator's relevant character is ``@`` it is a `sigil-like`:idx: -operator which binds stronger than a ``primarySuffix``: ``@x.abc`` is parsed -as ``(@x).abc`` whereas ``$x.abc`` is parsed as ``$(x.abc)``. +has the lowest precedence. + +If the operator's relevant character is ``@`` it is a `sigil-like`:idx: +operator which binds stronger than a ``primarySuffix``: ``@x.abc`` is parsed +as ``(@x).abc`` whereas ``$x.abc`` is parsed as ``$(x.abc)``. Otherwise precedence is determined by the relevant character. @@ -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 - 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 ~~~~~~~~~~~~~~~~ diff --git a/lib/system.nim b/lib/system.nim index 9a9984911..449b56c2f 100755 --- a/lib/system.nim +++ b/lib/system.nim @@ -2168,10 +2168,21 @@ proc getTypeInfo*[T](x: T): pointer {.magic: "GetTypeInfo".} ## the `typeinfo` module instead. 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 ## - ## const myResource = slurp"mydatafile.bin" + ## const myResource = staticRead"mydatafile.bin" + ## + +proc staticExec*(command: string, input = ""): string {.magic: "StaticExec".} + ## 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.} diff --git a/web/news.txt b/web/news.txt index 3ad0eb4d8..847c36561 100755 --- a/web/news.txt +++ b/web/news.txt @@ -27,6 +27,8 @@ Library Additions assignments. - Added ``system.eval`` that can execute an anonymous block of code at 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 code during compilation. - Added ``strutils.parseEnum``. @@ -102,6 +104,7 @@ Language Additions - ``when`` expressions are now allowed just like ``if`` expressions. - The precedence for operators starting with ``@`` is different now allowing for *sigil-like* operators. +- Stand-alone ``finally`` and ``except`` blocks are now supported. 2012-02-09 Version 0.8.14 released |