Merge branch 'devel' of https://github.com/Araq/Nimrod into new_spawn

Conflicts:
	lib/system.nim
This commit is contained in:
Araq
2014-06-26 17:19:28 +02:00
40 changed files with 385 additions and 114 deletions

View File

@@ -511,6 +511,7 @@ const
tfUncheckedArray* = tfVarargs
tfUnion* = tfNoSideEffect
tfGcSafe* = tfThread
tfObjHasKids* = tfEnumHasHoles
skError* = skUnknown
# type flags that are essential for type equality:

View File

@@ -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) =

View File

@@ -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"))

View File

@@ -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)

View File

@@ -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

View File

@@ -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:

View File

@@ -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

View File

@@ -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)

View File

@@ -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 =

View File

@@ -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()

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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) =

View File

@@ -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)

View File

@@ -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

View File

@@ -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.

View File

@@ -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
-------------------

View File

@@ -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

View File

@@ -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``

View File

@@ -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)

View File

@@ -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.} =

View File

@@ -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.} =

View File

@@ -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

View File

@@ -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]()

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -11,7 +11,7 @@
{.deadCodeElim: on.}
import rawsockets, os, strutils, unsigned, parseutils, times
export TPort
export TPort, `$`
const useWinVersion = defined(Windows) or defined(nimdoc)

View File

@@ -39,7 +39,6 @@ export
MSG_PEEK
type
TPort* = distinct uint16 ## port type
TDomain* = enum ## domain, which specifies the protocol family of the

View File

@@ -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

View File

@@ -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

View File

@@ -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&*$**()&*@`, `.}`, `(}`, `[`, `]`

14
tests/notnil/tnotnil4.nim Normal file
View File

@@ -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()

3
tests/osproc/ta.nim Normal file
View File

@@ -0,0 +1,3 @@
import strutils
let x = stdin.readLine()
echo x.parseInt + 5

16
tests/osproc/tstdin.nim Normal file
View File

@@ -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

View File

@@ -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

View File

@@ -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)
# ----------------------------------------------------------------------------

View File

@@ -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

View File

@@ -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;
}