diff options
40 files changed, 385 insertions, 114 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index 1755dcce9..461854496 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -511,6 +511,7 @@ const tfUncheckedArray* = tfVarargs tfUnion* = tfNoSideEffect tfGcSafe* = tfThread + tfObjHasKids* = tfEnumHasHoles skError* = skUnknown # type flags that are essential for type equality: diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 69e382f8d..e6b22819f 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1144,6 +1144,24 @@ proc genNewFinalize(p: BProc, e: PNode) = genObjectInit(p, cpsStmts, bt, a, false) gcUsage(e) +proc genOfHelper(p: BProc; dest: PType; a: PRope): PRope = + # unfortunately 'genTypeInfo' sets tfObjHasKids as a side effect, so we + # have to call it here first: + let ti = genTypeInfo(p.module, dest) + if tfFinal in dest.flags or (p.module.objHasKidsValid and + tfObjHasKids notin dest.flags): + result = ropef("$1.m_type == $2", a, ti) + else: + discard cgsym(p.module, "TNimType") + inc p.module.labels + let cache = con("Nim_OfCheck_CACHE", p.module.labels.toRope) + appf(p.module.s[cfsVars], "static TNimType* $#[2];$n", cache) + result = rfmt(p.module, "#isObjWithCache($#.m_type, $#, $#)", a, ti, cache) + when false: + # former version: + result = rfmt(p.module, "#isObj($1.m_type, $2)", + a, genTypeInfo(p.module, dest)) + proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) = var a: TLoc initLocExpr(p, x, a) @@ -1163,11 +1181,9 @@ proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) = globalError(x.info, errGenerated, "no 'of' operator available for pure objects") if nilCheck != nil: - r = rfmt(p.module, "(($1) && #isObj($2.m_type, $3))", - nilCheck, r, genTypeInfo(p.module, dest)) + r = rfmt(p.module, "(($1) && ($2))", nilCheck, genOfHelper(p, dest, r)) else: - r = rfmt(p.module, "#isObj($1.m_type, $2)", - r, genTypeInfo(p.module, dest)) + r = rfmt(p.module, "($1)", genOfHelper(p, dest, r)) putIntoDest(p, d, getSysType(tyBool), r) proc genOf(p: BProc, n: PNode, d: var TLoc) = diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 7a490082f..8e762ce27 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -796,6 +796,11 @@ proc genObjectInfo(m: BModule, typ: PType, name: PRope) = var tmp = getNimNode(m) genObjectFields(m, typ, typ.n, tmp) appf(m.s[cfsTypeInit3], "$1.node = &$2;$n", [name, tmp]) + var t = typ.sons[0] + while t != nil: + t = t.skipTypes(abstractInst) + t.flags.incl tfObjHasKids + t = t.sons[0] proc genTupleInfo(m: BModule, typ: PType, name: PRope) = genTypeInfoAuxBase(m, typ, name, toRope("0")) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 314af7784..a5852c735 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -1384,6 +1384,7 @@ proc myClose(b: PPassContext, n: PNode): PNode = registerModuleToMain(m.module) if sfMainModule in m.module.flags: + m.objHasKidsValid = true var disp = generateMethodDispatchers() for i in 0..sonsLen(disp)-1: genProcAux(m, disp.sons[i].sym) genMainProc(m) diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index e7d818556..12041c55b 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -96,6 +96,7 @@ type # a frame var twice in an init proc isHeaderFile*: bool # C source file is the header file includesStringh*: bool # C source file already includes ``<string.h>`` + objHasKidsValid*: bool # whether we can rely on tfObjHasKids cfilename*: string # filename of the module (including path, # without extension) typeCache*: TIdTable # cache the generated types diff --git a/compiler/guards.nim b/compiler/guards.nim index 813a30014..4cf06fe02 100644 --- a/compiler/guards.nim +++ b/compiler/guards.nim @@ -625,7 +625,7 @@ proc factImplies(fact, prop: PNode): TImplication = # == not a or not b == not (a and b) let arg = fact.sons[1] case arg.getMagic - of mIsNil: + of mIsNil, mEqRef: return ~factImplies(arg, prop) of mAnd: # not (a and b) means not a or not b: diff --git a/compiler/llstream.nim b/compiler/llstream.nim index 510880ffd..86bfeaabd 100644 --- a/compiler/llstream.nim +++ b/compiler/llstream.nim @@ -82,6 +82,9 @@ when not defined(readLineFromStdin): proc readLineFromStdin(prompt: string, line: var string): bool = stdout.write(prompt) result = readLine(stdin, line) + if not result: + stdout.write("\n") + quit(0) proc endsWith*(x: string, s: set[char]): bool = var i = x.len-1 diff --git a/compiler/parser.nim b/compiler/parser.nim index 2f9deb6b3..18de1570a 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -147,8 +147,10 @@ proc expectIdent(p: TParser) = proc eat(p: var TParser, tokType: TTokType) = ## Move the parser to the next token if the current token is of type ## `tokType`, otherwise error. - if p.tok.tokType == tokType: getTok(p) - else: lexMessage(p.lex, errTokenExpected, TokTypeToStr[tokType]) + if p.tok.tokType == tokType: + getTok(p) + else: + lexMessage(p.lex, errTokenExpected, TokTypeToStr[tokType]) proc parLineInfo(p: TParser): TLineInfo = ## Retrieve the line information associated with the parser's current state. @@ -285,7 +287,7 @@ proc colcom(p: var TParser, n: PNode) = skipComment(p, n) proc parseSymbol(p: var TParser, allowNil = false): PNode = - #| symbol = '`' (KEYW|IDENT|operator|'(' ')'|'[' ']'|'{' '}'|'='|literal)+ '`' + #| symbol = '`' (KEYW|IDENT|literal|(operator|'('|')'|'['|']'|'{'|'}'|'=')+)+ '`' #| | IDENT case p.tok.tokType of tkSymbol: @@ -296,31 +298,22 @@ proc parseSymbol(p: var TParser, allowNil = false): PNode = getTok(p) while true: case p.tok.tokType - of tkBracketLe: - add(result, newIdentNodeP(getIdent"[]", p)) - getTok(p) - eat(p, tkBracketRi) - of tkEquals: - add(result, newIdentNodeP(getIdent"=", p)) - getTok(p) - of tkParLe: - add(result, newIdentNodeP(getIdent"()", p)) - getTok(p) - eat(p, tkParRi) - of tkCurlyLe: - add(result, newIdentNodeP(getIdent"{}", p)) - getTok(p) - eat(p, tkCurlyRi) - of tokKeywordLow..tokKeywordHigh, tkSymbol, tkOpr, tkDot, tkDotDot: - add(result, newIdentNodeP(p.tok.ident, p)) - getTok(p) - of tkIntLit..tkCharLit: - add(result, newIdentNodeP(getIdent(tokToStr(p.tok)), p)) - getTok(p) - else: + of tkAccent: if result.len == 0: parMessage(p, errIdentifierExpected, p.tok) break + of tkOpr, tkDot, tkDotDot, tkEquals, tkParLe..tkParDotRi: + var accm = "" + while p.tok.tokType in {tkOpr, tkDot, tkDotDot, tkEquals, + tkParLe..tkParDotRi}: + accm.add(tokToStr(p.tok)) + getTok(p) + result.add(newIdentNodeP(getIdent(accm), p)) + of tokKeywordLow..tokKeywordHigh, tkSymbol, tkIntLit..tkCharLit: + result.add(newIdentNodeP(getIdent(tokToStr(p.tok)), p)) + getTok(p) + else: + parMessage(p, errIdentifierExpected, p.tok) eat(p, tkAccent) else: if allowNil and p.tok.tokType == tkNil: @@ -993,6 +986,7 @@ proc parseSymbolList(p: var TParser, result: PNode, allowNil = false) = proc parseTypeDescKAux(p: var TParser, kind: TNodeKind, mode: TPrimaryMode): PNode = + #| distinct = 'distinct' optInd typeDesc result = newNodeP(kind, p) getTok(p) optInd(p, result) diff --git a/compiler/sem.nim b/compiler/sem.nim index 2e3b10a40..e4ef6473f 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -269,11 +269,15 @@ include hlo, seminst, semcall proc semAfterMacroCall(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = + ## Semantically check the output of a macro. + ## This involves processes such as re-checking the macro output for type + ## coherence, making sure that variables declared with 'let' aren't + ## reassigned, and binding the unbound identifiers that the macro output + ## contains. inc(evalTemplateCounter) if evalTemplateCounter > 100: globalError(s.info, errTemplateInstantiationTooNested) - let oldFriend = c.friendModule - c.friendModule = s.owner.getModule + c.friendModules.add(s.owner.getModule) result = n if s.typ.sons[0] == nil: @@ -297,7 +301,7 @@ proc semAfterMacroCall(c: PContext, n: PNode, s: PSym, result = fitNode(c, s.typ.sons[0], result) #GlobalError(s.info, errInvalidParamKindX, typeToString(s.typ.sons[0])) dec(evalTemplateCounter) - c.friendModule = oldFriend + discard c.friendModules.pop() proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, flags: TExprFlags = {}): PNode = diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 19181d98e..abecc1b6d 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -52,7 +52,7 @@ type importTable*: PScope # scope for all imported symbols topLevelScope*: PScope # scope for all top-level symbols p*: PProcCon # procedure context - friendModule*: PSym # current friend module; may access private data; + friendModules*: seq[PSym] # friend modules; may access private data; # this is used so that generic instantiations # can access private object fields instCounter*: int # to prevent endless instantiations @@ -169,7 +169,7 @@ proc newContext(module: PSym): PContext = initLinkedList(result.libs) append(result.optionStack, newOptionEntry()) result.module = module - result.friendModule = module + result.friendModules = @[module] result.converters = @[] result.patterns = @[] result.includedFiles = initIntSet() diff --git a/compiler/seminst.nim b/compiler/seminst.nim index f7d5fa6f8..b93d7ca15 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -190,6 +190,9 @@ proc instantiateProcType(c: PContext, pt: TIdTable, proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, info: TLineInfo): PSym = + ## Generates a new instance of a generic procedure. + ## The `pt` parameter is a type-unsafe mapping table used to link generic + ## parameters to their concrete types within the generic instance. # no need to instantiate generic templates/macros: if fn.kind in {skTemplate, skMacro}: return fn # generates an instantiated proc @@ -199,8 +202,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, var n = copyTree(fn.ast) # NOTE: for access of private fields within generics from a different module # we set the friend module: - var oldFriend = c.friendModule - c.friendModule = getModule(fn) + c.friendModules.add(getModule(fn)) #let oldScope = c.currentScope #c.currentScope = fn.scope result = copySym(fn, false) @@ -236,6 +238,6 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, closeScope(c) # close scope for parameters popOwner() #c.currentScope = oldScope - c.friendModule = oldFriend + discard c.friendModules.pop() dec(c.instCounter) if result.kind == skMethod: finishMethod(c, result) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 43c7bf3fe..d17351988 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -66,10 +66,16 @@ proc toCover(t: PType): BiggestInt = result = lengthOrd(skipTypes(t, abstractVar-{tyTypeDesc})) proc performProcvarCheck(c: PContext, n: PNode, s: PSym) = + ## Checks that the given symbol is a proper procedure variable, meaning + ## that it var smoduleId = getModule(s).id if sfProcvar notin s.flags and s.typ.callConv == ccDefault and - smoduleId != c.module.id and smoduleId != c.friendModule.id: - localError(n.info, errXCannotBePassedToProcVar, s.name.s) + smoduleId != c.module.id: + block outer: + for module in c.friendModules: + if smoduleId == module.id: + break outer + localError(n.info, errXCannotBePassedToProcVar, s.name.s) proc semProcvarCheck(c: PContext, n: PNode) = let n = n.skipConv diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 3b9e82261..9d38c4619 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -70,9 +70,10 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = counter = x of nkSym: e = n.sons[i].sym - of nkIdent: + of nkIdent, nkAccQuoted: e = newSymS(skEnumField, n.sons[i], c) - else: illFormedAst(n) + else: + illFormedAst(n[i]) e.typ = result e.position = int(counter) if e.position == 0: hasNull = true diff --git a/compiler/suggest.nim b/compiler/suggest.nim index fc6ba2f77..db95c480f 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -63,8 +63,11 @@ proc filterSym(s: PSym): bool {.inline.} = proc fieldVisible*(c: PContext, f: PSym): bool {.inline.} = let fmoduleId = getModule(f).id - result = sfExported in f.flags or fmoduleId == c.module.id or - fmoduleId == c.friendModule.id + result = sfExported in f.flags or fmoduleId == c.module.id + for module in c.friendModules: + if fmoduleId == module.id: + result = true + break proc suggestField(c: PContext, s: PSym, outputs: var int) = if filterSym(s) and fieldVisible(c, s): @@ -319,7 +322,7 @@ proc suggestSym*(n: PNode, s: PSym) {.inline.} = findUsages(n, s) if optDef in gGlobalOptions: findDefinition(n, s) - if isServing: + if isServing and not n.isNil: addToSourceMap(s, n.info) proc markUsed(n: PNode, s: PSym) = diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim index 0e01f5031..9a213d813 100644 --- a/compiler/vmdeps.nim +++ b/compiler/vmdeps.nim @@ -15,6 +15,8 @@ proc readOutput(p: PProcess): string = discard p.waitForExit while not output.atEnd: result.add(output.readLine) + result.add("\n") + result.setLen(result.len - "\n".len) proc opGorge*(cmd, input: string): string = var p = startCmd(cmd) diff --git a/doc/grammar.txt b/doc/grammar.txt index 63e898e11..47ae095f6 100644 --- a/doc/grammar.txt +++ b/doc/grammar.txt @@ -24,7 +24,7 @@ ampExpr = plusExpr (OP6 optInd plusExpr)* plusExpr = mulExpr (OP7 optInd mulExpr)* mulExpr = dollarExpr (OP8 optInd dollarExpr)* dollarExpr = primary (OP9 optInd primary)* -symbol = '`' (KEYW|IDENT|operator|'(' ')'|'[' ']'|'{' '}'|'='|literal)+ '`' +symbol = '`' (KEYW|IDENT|literal|(operator|'('|')'|'['|']'|'{'|'}'|'=')+)+ '`' | IDENT indexExpr = expr indexExprList = indexExpr ^+ comma @@ -82,6 +82,7 @@ paramListColon = paramList? (':' optInd typeDesc)? doBlock = 'do' paramListArrow pragmas? colcom stmt doBlocks = doBlock ^* IND{=} procExpr = 'proc' paramListColon pragmas? ('=' COMMENT? stmt)? +distinct = 'distinct' optInd typeDesc expr = (ifExpr | whenExpr | caseExpr @@ -166,7 +167,6 @@ object = 'object' pragma? ('of' typeDesc)? COMMENT? objectPart typeClassParam = ('var')? symbol typeClass = typeClassParam ^* ',' (pragma)? ('of' typeDesc ^* ',')? &IND{>} stmt -distinct = 'distinct' optInd typeDesc typeDef = identWithPragma genericParamList? '=' optInd typeDefAux indAndComment? varTuple = '(' optInd identWithPragma ^+ comma optPar ')' '=' optInd expr diff --git a/doc/manual.txt b/doc/manual.txt index c6f50298c..e96e50999 100644 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -5485,7 +5485,7 @@ the argument is missing, the C name is the Nimrod identifier *exactly as spelled*: .. code-block:: - proc printf(formatstr: cstring) {.importc: "printf", varargs.} + proc printf(formatstr: cstring) {.header: "<stdio.h>", importc: "printf", varargs.} Note that this pragma is somewhat of a misnomer: Other backends will provide the same feature under the same name. diff --git a/doc/nimrodc.txt b/doc/nimrodc.txt index e4f1c41dc..fea1037da 100644 --- a/doc/nimrodc.txt +++ b/doc/nimrodc.txt @@ -87,6 +87,18 @@ Level Description for compiler developers. ===== ============================================ + +Compile time symbols +-------------------- + +Through the ``-d:x`` or ``--define:x`` switch you can define compile time +symbols for conditional compilation. The defined switches can be checked in +source code with the `when statement <manual.html#when-statement>`_ and +`defined proc <system.html#defined>`_. The typical use of this switch is to +enable builds in release mode (``-d:release``) where certain safety checks are +omitted for better performance. Another common use is the ``-d:ssl`` switch to +activate `SSL sockets <sockets.html>`_. + Configuration files ------------------- diff --git a/doc/tut1.txt b/doc/tut1.txt index 8c6f140eb..9874f267b 100644 --- a/doc/tut1.txt +++ b/doc/tut1.txt @@ -1385,8 +1385,8 @@ Tuples A tuple type defines various named *fields* and an *order* of the fields. The constructor ``()`` can be used to construct tuples. The order of the fields in the constructor must match the order in the tuple's definition. -Different tuple-types are *equivalent* if they specify the same fields of -the same type in the same order. +Different tuple-types are *equivalent* if they specify fields of +the same type and of the same name in the same order. The assignment operator for tuples copies each component. The notation ``t.field`` is used to access a tuple's field. Another notation is diff --git a/doc/tut2.txt b/doc/tut2.txt index 11b485f50..1e23618e0 100644 --- a/doc/tut2.txt +++ b/doc/tut2.txt @@ -501,7 +501,7 @@ with the file and line where the uncaught exception is being raised, which may help you locate the offending code which has changed. If you want to add the ``{.raises.}`` pragma to existing code, the compiler can -also help you. You can add the ``{.effect.}`` pragma statement to your proc and +also help you. You can add the ``{.effects.}`` pragma statement to your proc and the compiler will output all inferred effects up to that point (exception tracking is part of Nimrod's effect system). Another more roundabout way to find out the list of exceptions raised by a proc is to use the Nimrod ``doc2`` diff --git a/lib/pure/algorithm.nim b/lib/pure/algorithm.nim index 37fbc948c..86d329763 100644 --- a/lib/pure/algorithm.nim +++ b/lib/pure/algorithm.nim @@ -150,6 +150,15 @@ proc sort*[T](a: var openArray[T], ## # overload: ## sort(myStrArray, system.cmp) ## + ## You can inline adhoc comparison procs with the `do notation + ## <manual.html#do-notation>`_. Example: + ## + ## .. code-block:: nimrod + ## + ## people.sort do (x, y: Person) -> int: + ## result = cmp(x.surname, y.surname) + ## if result == 0: + ## result = cmp(x.name, y.name) var n = a.len var b: seq[T] newSeq(b, n div 2) diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index 87ee83ad9..6d9e605f1 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -27,9 +27,11 @@ export TPort # TODO: Discarded void PFutures need to checked for exception. -# TODO: Exceptions are currently uncatchable due to the limitation that -# you cannot have yield in a try stmt. Perhaps I can get the macro to put -# a user's try except around ``future.read``. +# TODO: ``except`` statement (without `try`) does not work. +# TODO: Multiple exception names in a ``except`` don't work. +# TODO: The effect system (raises: []) has trouble with my try transformation. +# TODO: Can't await in a 'except' body + # -- Futures @@ -922,14 +924,17 @@ proc getName(node: PNimrodNode): string {.compileTime.} = return $node[1].ident of nnkIdent: return $node.ident + of nnkEmpty: + return "anonymous" else: - assert false + error("Unknown name.") macro async*(prc: stmt): stmt {.immediate.} = ## Macro which processes async procedures into the appropriate ## iterators and yield statements. - - expectKind(prc, nnkProcDef) + if prc.kind notin {nnkProcDef, nnkLambda}: + error("Cannot transform this node kind into an async proc." & + " Proc definition or lambda node expected.") hint("Processing " & prc[0].getName & " as an async proc.") @@ -941,7 +946,9 @@ macro async*(prc: stmt): stmt {.immediate.} = if $returnType[0] != "PFuture": error("Expected return type of 'PFuture' got '" & $returnType[0] & "'") - let subtypeIsVoid = returnType.kind == nnkEmpty + let subtypeIsVoid = returnType.kind == nnkEmpty or + (returnType.kind == nnkBracketExpr and + returnType[1].kind == nnkIdent and returnType[1].ident == !"void") var outerProcBody = newNimNode(nnkStmtList) @@ -990,17 +997,19 @@ macro async*(prc: stmt): stmt {.immediate.} = # Remove the 'async' pragma. for i in 0 .. <result[4].len: - if result[4][i].ident == !"async": + if result[4][i].kind == nnkIdent and result[4][i].ident == !"async": result[4].del(i) if subtypeIsVoid: # Add discardable pragma. - result[4].add(newIdentNode("discardable")) + if prc.kind == nnkProcDef: # TODO: This is a workaround for #1287 + result[4].add(newIdentNode("discardable")) if returnType.kind == nnkEmpty: # Add PFuture[void] result[3][0] = parseExpr("PFuture[void]") result[6] = outerProcBody + #echo(treeRepr(result)) #echo(toStrLit(result)) proc recvLine*(socket: TAsyncFD): PFuture[string] {.async.} = diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index 6c2414d99..005c56ebc 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -14,12 +14,13 @@ import strtabs, asyncnet, asyncdispatch, parseutils, parseurl, strutils type TRequest* = object - client: PAsyncSocket # TODO: Separate this into a Response object? + client*: PAsyncSocket # TODO: Separate this into a Response object? reqMethod*: string headers*: PStringTable protocol*: tuple[orig: string, major, minor: int] url*: TURL hostname*: string ## The hostname of the client that made the request. + body*: string # TODO PAsyncHttpServer* = ref object socket: PAsyncSocket @@ -169,6 +170,10 @@ proc serve*(server: PAsyncHttpServer, port: TPort, var fut = await server.socket.acceptAddr() processClient(fut.client, fut.address, callback) +proc close*(server: PAsyncHttpServer) = + ## Terminates the async http server instance. + server.socket.close() + when isMainModule: var server = newAsyncHttpServer() proc cb(req: TRequest) {.async.} = diff --git a/lib/pure/collections/queues.nim b/lib/pure/collections/queues.nim index db1d50569..5481272f0 100644 --- a/lib/pure/collections/queues.nim +++ b/lib/pure/collections/queues.nim @@ -59,7 +59,7 @@ proc enqueue*[T](q: var TQueue[T], item: T) = proc dequeue*[T](q: var TQueue[T]): T = ## removes and returns the first element of the queue `q`. - assert q.len > 0 + assert q.count > 0 dec q.count result = q.data[q.rd] q.rd = (q.rd + 1) and q.mask diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index 848f4b8ba..ce9df09e1 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -37,7 +37,8 @@ ## ## Piggyback on the already available string hash proc. ## ## ## ## Without this proc nothing works! -## result = hash(x.firstName & x.lastName) +## result = x.firstName.hash !& x.lastName.hash +## result = !$result ## ## var ## salaries = initTable[Person, int]() @@ -145,6 +146,14 @@ proc mget*[A, B](t: var TTable[A, B], key: A): var B = if index >= 0: result = t.data[index].val else: raise newException(EInvalidKey, "key not found: " & $key) +iterator allValues*[A, B](t: TTable[A, B]; key: A): B = + ## iterates over any value in the table `t` that belongs to the given `key`. + var h: THash = hash(key) and high(t.data) + while t.data[h].slot != seEmpty: + if t.data[h].key == key and t.data[h].slot == seFilled: + yield t.data[h].val + h = nextTry(h, high(t.data)) + proc hasKey*[A, B](t: TTable[A, B], key: A): bool = ## returns true iff `key` is in the table `t`. result = rawGet(t, key) >= 0 @@ -313,8 +322,7 @@ proc newTable*[A, B](initialSize=64): PTable[A, B] = new(result) result[] = initTable[A, B](initialSize) -proc newTable*[A, B](pairs: openArray[tuple[key: A, - val: B]]): PTable[A, B] = +proc newTable*[A, B](pairs: openArray[tuple[key: A, val: B]]): PTable[A, B] = ## creates a new hash table that contains the given `pairs`. new(result) result[] = toTable[A, B](pairs) @@ -841,7 +849,8 @@ when isMainModule: ## Piggyback on the already available string hash proc. ## ## Without this proc nothing works! - result = hash(x.firstName & x.lastName) + result = x.firstName.hash !& x.lastName.hash + result = !$result var salaries = initTable[Person, int]() diff --git a/lib/pure/fsmonitor.nim b/lib/pure/fsmonitor.nim index d6584c1a0..b35466771 100644 --- a/lib/pure/fsmonitor.nim +++ b/lib/pure/fsmonitor.nim @@ -119,8 +119,8 @@ proc getEvent(m: PFSMonitor, fd: cint): seq[TMonitorEvent] = var mev: TMonitorEvent mev.wd = event.wd if event.len.int != 0: - mev.name = newString(event.len.int) - copyMem(addr(mev.name[0]), addr event.name, event.len.int-1) + let cstr = event.name.addr.cstring + mev.name = $cstr else: mev.name = "" @@ -211,4 +211,4 @@ when isMainModule: while true: if not disp.poll(): break - \ No newline at end of file + diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim index 5784a96c1..740355e55 100644 --- a/lib/pure/hashes.nim +++ b/lib/pure/hashes.nim @@ -8,7 +8,34 @@ # ## This module implements efficient computations of hash values for diverse -## Nimrod types. +## Nimrod types. All the procs are based on these two building blocks: the `!& +## proc <#!&>`_ used to start or mix a hash value, and the `!$ proc <#!$>`_ +## used to *finish* the hash value. If you want to implement hash procs for +## your custom types you will end up writing the following kind of skeleton of +## code: +## +## .. code-block:: nimrod +## proc hash(x: Something): THash = +## ## Computes a THash from `x`. +## var h: THash = 0 +## # Iterate over parts of `x`. +## for xAtom in x: +## # Mix the atom with the partial hash. +## h = h !& xAtom +## # Finish the hash. +## result = !$h +## +## If your custom types contain fields for which there already is a hash proc, +## like for example objects made up of ``strings``, you can simply hash +## together the hash value of the individual fields: +## +## .. code-block:: nimrod +## proc hash(x: Something): THash = +## ## Computes a THash from `x`. +## var h: THash = 0 +## h = h &! hash(x.foo) +## h = h &! hash(x.bar) +## result = !$h import strutils diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 306a8a2e2..508e564c5 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -619,6 +619,44 @@ proc `%`*(elements: openArray[PJsonNode]): PJsonNode = newSeq(result.elems, elements.len) for i, p in pairs(elements): result.elems[i] = p +proc `==`* (a,b: PJsonNode): bool = + ## Check two nodes for equality + if a.kind != b.kind: false + else: + case a.kind + of JString: + a.str == b.str + of JInt: + a.num == b.num + of JFloat: + a.fnum == b.fnum + of JBool: + a.bval == b.bval + of JNull: + true + of JArray: + a.elems == b.elems + of JObject: + a.fields == b.fields + +proc hash* (n:PJsonNode): THash = + ## Compute the hash for a JSON node + case n.kind + of JArray: + result = hash(n.elems) + of JObject: + result = hash(n.fields) + of JInt: + result = hash(n.num) + of JFloat: + result = hash(n.fnum) + of JBool: + result = hash(n.bval.int) + of JString: + result = hash(n.str) + of JNull: + result = hash(0) + proc len*(n: PJsonNode): int = ## If `n` is a `JArray`, it returns the number of elements. ## If `n` is a `JObject`, it returns the number of pairs. @@ -631,7 +669,7 @@ proc len*(n: PJsonNode): int = proc `[]`*(node: PJsonNode, name: string): PJsonNode = ## Gets a field from a `JObject`, which must not be nil. ## If the value at `name` does not exist, returns nil - assert(node != nil) + assert(not isNil(node)) assert(node.kind == JObject) for key, item in items(node.fields): if key == name: @@ -641,8 +679,8 @@ proc `[]`*(node: PJsonNode, name: string): PJsonNode = proc `[]`*(node: PJsonNode, index: int): PJsonNode = ## Gets the node at `index` in an Array. Result is undefined if `index` ## is out of bounds + assert(not isNil(node)) assert(node.kind == JArray) - assert(node != nil) return node.elems[index] proc hasKey*(node: PJsonNode, key: string): bool = @@ -675,14 +713,12 @@ proc `[]=`*(obj: PJsonNode, key: string, val: PJsonNode) = return obj.fields.add((key, val)) -proc `{}`*(node: PJsonNode, names: varargs[string]): PJsonNode = +proc `{}`*(node: PJsonNode, key: string): PJsonNode = ## Transverses the node and gets the given value. If any of the ## names does not exist, returns nil result = node - for name in names: - result = result[name] - if isNil(result): - return nil + if isNil(node): return nil + result = result[key] proc `{}=`*(node: PJsonNode, names: varargs[string], value: PJsonNode) = ## Transverses the node and tries to set the value at the given location @@ -1021,7 +1057,7 @@ when isMainModule: let testJson = parseJson"""{ "a": [1, 2, 3, 4], "b": "asd" }""" # nil passthrough - assert(testJson{"doesnt_exist", "anything"} == nil) + assert(testJson{"doesnt_exist"}{"anything"}.isNil) testJson{["c", "d"]} = %true assert(testJson["c"]["d"].bval) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index 2f1a6fa46..e34c88327 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -11,7 +11,7 @@ {.deadCodeElim: on.} import rawsockets, os, strutils, unsigned, parseutils, times -export TPort +export TPort, `$` const useWinVersion = defined(Windows) or defined(nimdoc) diff --git a/lib/pure/rawsockets.nim b/lib/pure/rawsockets.nim index 07b647b68..94189fd89 100644 --- a/lib/pure/rawsockets.nim +++ b/lib/pure/rawsockets.nim @@ -39,7 +39,6 @@ export MSG_PEEK type - TPort* = distinct uint16 ## port type TDomain* = enum ## domain, which specifies the protocol family of the diff --git a/lib/system.nim b/lib/system.nim index 6934d29ae..3e5308f90 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -90,6 +90,15 @@ proc defined*(x: expr): bool {.magic: "Defined", noSideEffect.} ## when not defined(strutils.toUpper): ## # provide our own toUpper proc here, because strutils is ## # missing it. + ## + ## You can also check external symbols introduced through the compiler's + ## `-d:x switch <nimrodc.html#compile-time-symbols>`_ to enable build time + ## conditionals: + ## + ## .. code-block:: Nimrod + ## when not defined(release): + ## # Do here programmer friendly expensive sanity checks. + ## # Put here the normal code when defined(useNimRtl): {.deadCodeElim: on.} @@ -1769,9 +1778,38 @@ iterator fields*[S:tuple|object, T:tuple|object](x: S, y: T): tuple[a,b: expr] { ## in the loop body. iterator fieldPairs*[T: tuple|object](x: T): TObject {. magic: "FieldPairs", noSideEffect.} - ## iterates over every field of `x`. Warning: This really transforms - ## the 'for' and unrolls the loop. The current implementation also has a bug - ## that affects symbol binding in the loop body. + ## Iterates over every field of `x` returning their name and value. + ## + ## When you iterate over objects with different field types you have to use + ## the compile time ``when`` instead of a runtime ``if`` to select the code + ## you want to run for each type. To perform the comparison use the `is + ## operator <manual.html#is-operator>`_. Example: + ## + ## .. code-block:: Nimrod + ## + ## type + ## Custom = object + ## foo: string + ## bar: bool + ## + ## proc `$`(x: Custom): string = + ## result = "Custom:" + ## for name, value in x.fieldPairs: + ## when value is bool: + ## result.add("\n\t" & name & " is " & $value) + ## else: + ## if value.isNil: + ## result.add("\n\t" & name & " (nil)") + ## else: + ## result.add("\n\t" & name & " '" & value & "'") + ## + ## Another way to do the same without ``when`` is to leave the task of + ## picking the appropriate code to a secondary proc which you overload for + ## each field type and pass the `value` to. + ## + ## Warning: This really transforms the 'for' and unrolls the loop. The + ## current implementation also has a bug that affects symbol binding in the + ## loop body. iterator fieldPairs*[S: tuple|object, T: tuple|object](x: S, y: T): tuple[ a, b: expr] {. magic: "FieldPairs", noSideEffect.} @@ -2785,10 +2823,15 @@ when true: THide(raiseAssert)(msg) template assert*(cond: bool, msg = "") = - ## provides a means to implement `programming by contracts`:idx: in Nimrod. + ## Raises ``EAssertionFailure`` with `msg` if `cond` is false. + ## + ## 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. + ## raises an ``EAssertionFailure`` exception. However, the compiler may not + ## generate any code at all for ``assert`` if it is advised to do so through + ## the ``-d:release`` or ``--assertions:off`` `command line switches + ## <nimrodc.html#command-line-switches>`_. + ## ## Use ``assert`` for debugging purposes only. bind instantiationInfo mixin failedAssertImpl diff --git a/lib/system/chcks.nim b/lib/system/chcks.nim index f29e222e8..387b54ef1 100644 --- a/lib/system/chcks.nim +++ b/lib/system/chcks.nim @@ -67,6 +67,28 @@ proc chckObjAsgn(a, b: PNimType) {.compilerproc, inline.} = if a != b: sysFatal(EInvalidObjectAssignment, "invalid object assignment") +type ObjCheckCache = array[0..1, PNimType] + +proc isObjSlowPath(obj, subclass: PNimType; + cache: var ObjCheckCache): bool {.noinline.} = + # checks if obj is of type subclass: + var x = obj.base + while x != subclass: + if x == nil: + cache[0] = obj + return false + x = x.base + cache[1] = obj + return true + +proc isObjWithCache(obj, subclass: PNimType; + cache: var ObjCheckCache): bool {.compilerProc, inline.} = + if obj == subclass: return true + if obj.base == subclass: return true + if cache[0] == obj: return false + if cache[1] == obj: return true + return isObjSlowPath(obj, subclass, cache) + proc isObj(obj, subclass: PNimType): bool {.compilerproc.} = # checks if obj is of type subclass: var x = obj diff --git a/tests/misc/tbug1217bracketquotes.nim b/tests/misc/tbug1217bracketquotes.nim new file mode 100644 index 000000000..90e67d45b --- /dev/null +++ b/tests/misc/tbug1217bracketquotes.nim @@ -0,0 +1,14 @@ +discard """ + output: "13{(.{}}{*4&*$**()&*@1235" +""" + +type + Test = enum + `1`, `3`, `{`, `(.`, `{}}{`, `*4&*$**()&*@` + +let `.}` = 1 +let `(}` = 2 +let `[` = 3 +let `]` = 5 + +echo `1`, `3`, `{`, `(.`, `{}}{`, `*4&*$**()&*@`, `.}`, `(}`, `[`, `]` diff --git a/tests/notnil/tnotnil4.nim b/tests/notnil/tnotnil4.nim new file mode 100644 index 000000000..23968ee48 --- /dev/null +++ b/tests/notnil/tnotnil4.nim @@ -0,0 +1,14 @@ +discard "" +type + TObj = ref object + +proc check(a: TObj not nil) = + echo repr(a) + +proc doit() = + var x : array[0..1, TObj] + + if x[0] != nil: + check(x[0]) + +doit() \ No newline at end of file diff --git a/tests/osproc/ta.nim b/tests/osproc/ta.nim new file mode 100644 index 000000000..6c1495590 --- /dev/null +++ b/tests/osproc/ta.nim @@ -0,0 +1,3 @@ +import strutils +let x = stdin.readLine() +echo x.parseInt + 5 \ No newline at end of file diff --git a/tests/osproc/tstdin.nim b/tests/osproc/tstdin.nim new file mode 100644 index 000000000..2ea939992 --- /dev/null +++ b/tests/osproc/tstdin.nim @@ -0,0 +1,16 @@ +discard """ + file: "tstdin.nim" + output: "10" +""" +import osproc, os, streams + +doAssert fileExists(getCurrentDir() / "tests" / "osproc" / "ta.exe") + +var p = startProcess("ta.exe", getCurrentDir() / "tests" / "osproc") +p.inputStream.write("5\n") +while true: + let line = p.outputStream.readLine() + if line != "": + echo line + else: + break \ No newline at end of file diff --git a/tests/testament/caasdriver.nim b/tests/testament/caasdriver.nim index 22c5ed6fa..ddfe88273 100644 --- a/tests/testament/caasdriver.nim +++ b/tests/testament/caasdriver.nim @@ -86,6 +86,10 @@ proc doProcCommand(session: var TNimrodSession, command: string): string = proc doCommand(session: var TNimrodSession, command: string) = if session.mode == CaasRun: + if not session.nim.running: + session.lastOutput = "FAILED TO EXECUTE: " & command & "\n" & + "Exit code " & $session.nim.peekExitCode + return session.lastOutput = doCaasCommand(session, command & " " & session.filename) else: @@ -102,7 +106,7 @@ proc close(session: var TNimrodSession) {.destructor.} = if session.mode == CaasRun: session.nim.close -proc doScenario(script: string, output: PStream, mode: TRunMode): bool = +proc doScenario(script: string, output: PStream, mode: TRunMode, verbose: bool): bool = result = true var f = open(script) @@ -134,7 +138,7 @@ proc doScenario(script: string, output: PStream, mode: TRunMode): bool = continue elif line.startsWith(">"): s.doCommand(line.substr(1).strip) - output.writeln line, "\n", s.lastOutput + output.writeln line, "\n", if verbose: s.lastOutput else: "" else: var expectMatch = true var pattern = s.replaceVars(line) @@ -151,13 +155,14 @@ proc doScenario(script: string, output: PStream, mode: TRunMode): bool = output.writeln "FAILURE ", line result = false -iterator caasTestsRunner*(filter = ""): tuple[test, output: string, - status: bool, mode: TRunMode] = +iterator caasTestsRunner*(filter = "", verbose = false): tuple[test, + output: string, status: bool, + mode: TRunMode] = for scenario in os.walkFiles(TesterDir / "caas/*.txt"): if filter.len > 0 and find(scenario, filter) == -1: continue for mode in modes: var outStream = newStringStream() - let r = doScenario(scenario, outStream, mode) + let r = doScenario(scenario, outStream, mode, verbose) yield (scenario, outStream.data, r, mode) when isMainModule: @@ -175,9 +180,12 @@ when isMainModule: if verbose and len(filter) > 0: echo "Running only test cases matching filter '$1'" % [filter] - for test, output, result, mode in caasTestsRunner(filter): + for test, output, result, mode in caasTestsRunner(filter, verbose): if not result or verbose: - echo test, "\n", output, "-> ", $mode, ":", $result, "\n-----" + echo "Mode ", $mode, " (", if result: "succeeded)" else: "failed)" + echo test + echo output + echo "---------\n" if not result: failures += 1 diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim index bb9c90d2a..841eb8159 100644 --- a/tests/testament/categories.nim +++ b/tests/testament/categories.nim @@ -282,26 +282,33 @@ proc testBabelPackages(r: var TResults, cat: Category, filter: PackageFilter) = echo("[Warning] - Cannot run babel tests: Babel update failed.") return - for name, url in listPackages(filter): - var test = makeTest(name, "", cat) - echo(url) - let - installProcess = startProcess(babelExe, "", ["install", "-y", name]) - installStatus = waitForExitEx(installProcess) - installProcess.close - if installStatus != quitSuccess: - r.addResult(test, "", "", reInstallFailed) - continue + let packageFileTest = makeTest("PackageFileParsed", "", cat) + try: + for name, url in listPackages(filter): + var test = makeTest(name, "", cat) + echo(url) + let + installProcess = startProcess(babelExe, "", ["install", "-y", name]) + installStatus = waitForExitEx(installProcess) + installProcess.close + if installStatus != quitSuccess: + r.addResult(test, "", "", reInstallFailed) + continue + + let + buildPath = getPackageDir(name)[0.. -3] + let + buildProcess = startProcess(babelExe, buildPath, ["build"]) + buildStatus = waitForExitEx(buildProcess) + buildProcess.close + if buildStatus != quitSuccess: + r.addResult(test, "", "", reBuildFailed) + r.addResult(test, "", "", reSuccess) + r.addResult(packageFileTest, "", "", reSuccess) + except EJsonParsingError: + echo("[Warning] - Cannot run babel tests: Invalid package file.") + r.addResult(packageFileTest, "", "", reBuildFailed) - let - buildPath = getPackageDir(name)[0.. -3] - let - buildProcess = startProcess(babelExe, buildPath, ["build"]) - buildStatus = waitForExitEx(buildProcess) - buildProcess.close - if buildStatus != quitSuccess: - r.addResult(test, "", "", reBuildFailed) - r.addResult(test, "", "", reSuccess) # ---------------------------------------------------------------------------- diff --git a/todo.txt b/todo.txt index 11fc2ef81..df18ff7e9 100644 --- a/todo.txt +++ b/todo.txt @@ -155,7 +155,6 @@ Not essential for 1.0.0 - implement the "snoopResult" pragma; no, make a strutils with string append semantics instead ... - implement "closure tuple consists of a single 'ref'" optimization -- optimize method dispatchers - new feature: ``distinct T with operations`` - arglist as a type (iterator chaining); variable length type lists for generics - implement marker procs for message passing diff --git a/web/assets/style.css b/web/assets/style.css index 5cee279fc..91bb4bd8a 100644 --- a/web/assets/style.css +++ b/web/assets/style.css @@ -93,7 +93,7 @@ html, body { border-left:10px solid #8f9698; background:#f3f6f8; font-size:15px; - font-family:courier; + font-family:courier, monospace; letter-spacing:0; line-height:17px; } |