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
commit 85a1d896c2
40 changed files with 385 additions and 114 deletions

View file

@ -511,6 +511,7 @@ const
tfUncheckedArray* = tfVarargs tfUncheckedArray* = tfVarargs
tfUnion* = tfNoSideEffect tfUnion* = tfNoSideEffect
tfGcSafe* = tfThread tfGcSafe* = tfThread
tfObjHasKids* = tfEnumHasHoles
skError* = skUnknown skError* = skUnknown
# type flags that are essential for type equality: # 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) genObjectInit(p, cpsStmts, bt, a, false)
gcUsage(e) 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) = proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) =
var a: TLoc var a: TLoc
initLocExpr(p, x, a) initLocExpr(p, x, a)
@ -1163,11 +1181,9 @@ proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) =
globalError(x.info, errGenerated, globalError(x.info, errGenerated,
"no 'of' operator available for pure objects") "no 'of' operator available for pure objects")
if nilCheck != nil: if nilCheck != nil:
r = rfmt(p.module, "(($1) && #isObj($2.m_type, $3))", r = rfmt(p.module, "(($1) && ($2))", nilCheck, genOfHelper(p, dest, r))
nilCheck, r, genTypeInfo(p.module, dest))
else: else:
r = rfmt(p.module, "#isObj($1.m_type, $2)", r = rfmt(p.module, "($1)", genOfHelper(p, dest, r))
r, genTypeInfo(p.module, dest))
putIntoDest(p, d, getSysType(tyBool), r) putIntoDest(p, d, getSysType(tyBool), r)
proc genOf(p: BProc, n: PNode, d: var TLoc) = 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) var tmp = getNimNode(m)
genObjectFields(m, typ, typ.n, tmp) genObjectFields(m, typ, typ.n, tmp)
appf(m.s[cfsTypeInit3], "$1.node = &$2;$n", [name, 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) = proc genTupleInfo(m: BModule, typ: PType, name: PRope) =
genTypeInfoAuxBase(m, typ, name, toRope("0")) genTypeInfoAuxBase(m, typ, name, toRope("0"))

View file

@ -1384,6 +1384,7 @@ proc myClose(b: PPassContext, n: PNode): PNode =
registerModuleToMain(m.module) registerModuleToMain(m.module)
if sfMainModule in m.module.flags: if sfMainModule in m.module.flags:
m.objHasKidsValid = true
var disp = generateMethodDispatchers() var disp = generateMethodDispatchers()
for i in 0..sonsLen(disp)-1: genProcAux(m, disp.sons[i].sym) for i in 0..sonsLen(disp)-1: genProcAux(m, disp.sons[i].sym)
genMainProc(m) genMainProc(m)

View file

@ -96,6 +96,7 @@ type
# a frame var twice in an init proc # a frame var twice in an init proc
isHeaderFile*: bool # C source file is the header file isHeaderFile*: bool # C source file is the header file
includesStringh*: bool # C source file already includes ``<string.h>`` 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, cfilename*: string # filename of the module (including path,
# without extension) # without extension)
typeCache*: TIdTable # cache the generated types 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) # == not a or not b == not (a and b)
let arg = fact.sons[1] let arg = fact.sons[1]
case arg.getMagic case arg.getMagic
of mIsNil: of mIsNil, mEqRef:
return ~factImplies(arg, prop) return ~factImplies(arg, prop)
of mAnd: of mAnd:
# not (a and b) means not a or not b: # 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 = proc readLineFromStdin(prompt: string, line: var string): bool =
stdout.write(prompt) stdout.write(prompt)
result = readLine(stdin, line) result = readLine(stdin, line)
if not result:
stdout.write("\n")
quit(0)
proc endsWith*(x: string, s: set[char]): bool = proc endsWith*(x: string, s: set[char]): bool =
var i = x.len-1 var i = x.len-1

View file

@ -147,8 +147,10 @@ proc expectIdent(p: TParser) =
proc eat(p: var TParser, tokType: TTokType) = proc eat(p: var TParser, tokType: TTokType) =
## Move the parser to the next token if the current token is of type ## Move the parser to the next token if the current token is of type
## `tokType`, otherwise error. ## `tokType`, otherwise error.
if p.tok.tokType == tokType: getTok(p) if p.tok.tokType == tokType:
else: lexMessage(p.lex, errTokenExpected, TokTypeToStr[tokType]) getTok(p)
else:
lexMessage(p.lex, errTokenExpected, TokTypeToStr[tokType])
proc parLineInfo(p: TParser): TLineInfo = proc parLineInfo(p: TParser): TLineInfo =
## Retrieve the line information associated with the parser's current state. ## 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) skipComment(p, n)
proc parseSymbol(p: var TParser, allowNil = false): PNode = proc parseSymbol(p: var TParser, allowNil = false): PNode =
#| symbol = '`' (KEYW|IDENT|operator|'(' ')'|'[' ']'|'{' '}'|'='|literal)+ '`' #| symbol = '`' (KEYW|IDENT|literal|(operator|'('|')'|'['|']'|'{'|'}'|'=')+)+ '`'
#| | IDENT #| | IDENT
case p.tok.tokType case p.tok.tokType
of tkSymbol: of tkSymbol:
@ -296,31 +298,22 @@ proc parseSymbol(p: var TParser, allowNil = false): PNode =
getTok(p) getTok(p)
while true: while true:
case p.tok.tokType case p.tok.tokType
of tkBracketLe: of tkAccent:
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:
if result.len == 0: if result.len == 0:
parMessage(p, errIdentifierExpected, p.tok) parMessage(p, errIdentifierExpected, p.tok)
break 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) eat(p, tkAccent)
else: else:
if allowNil and p.tok.tokType == tkNil: 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, proc parseTypeDescKAux(p: var TParser, kind: TNodeKind,
mode: TPrimaryMode): PNode = mode: TPrimaryMode): PNode =
#| distinct = 'distinct' optInd typeDesc
result = newNodeP(kind, p) result = newNodeP(kind, p)
getTok(p) getTok(p)
optInd(p, result) optInd(p, result)

View file

@ -269,11 +269,15 @@ include hlo, seminst, semcall
proc semAfterMacroCall(c: PContext, n: PNode, s: PSym, proc semAfterMacroCall(c: PContext, n: PNode, s: PSym,
flags: TExprFlags): PNode = 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) inc(evalTemplateCounter)
if evalTemplateCounter > 100: if evalTemplateCounter > 100:
globalError(s.info, errTemplateInstantiationTooNested) globalError(s.info, errTemplateInstantiationTooNested)
let oldFriend = c.friendModule c.friendModules.add(s.owner.getModule)
c.friendModule = s.owner.getModule
result = n result = n
if s.typ.sons[0] == nil: 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) result = fitNode(c, s.typ.sons[0], result)
#GlobalError(s.info, errInvalidParamKindX, typeToString(s.typ.sons[0])) #GlobalError(s.info, errInvalidParamKindX, typeToString(s.typ.sons[0]))
dec(evalTemplateCounter) dec(evalTemplateCounter)
c.friendModule = oldFriend discard c.friendModules.pop()
proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
flags: TExprFlags = {}): PNode = flags: TExprFlags = {}): PNode =

View file

@ -52,7 +52,7 @@ type
importTable*: PScope # scope for all imported symbols importTable*: PScope # scope for all imported symbols
topLevelScope*: PScope # scope for all top-level symbols topLevelScope*: PScope # scope for all top-level symbols
p*: PProcCon # procedure context 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 # this is used so that generic instantiations
# can access private object fields # can access private object fields
instCounter*: int # to prevent endless instantiations instCounter*: int # to prevent endless instantiations
@ -169,7 +169,7 @@ proc newContext(module: PSym): PContext =
initLinkedList(result.libs) initLinkedList(result.libs)
append(result.optionStack, newOptionEntry()) append(result.optionStack, newOptionEntry())
result.module = module result.module = module
result.friendModule = module result.friendModules = @[module]
result.converters = @[] result.converters = @[]
result.patterns = @[] result.patterns = @[]
result.includedFiles = initIntSet() result.includedFiles = initIntSet()

View file

@ -190,6 +190,9 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
info: TLineInfo): PSym = 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: # no need to instantiate generic templates/macros:
if fn.kind in {skTemplate, skMacro}: return fn if fn.kind in {skTemplate, skMacro}: return fn
# generates an instantiated proc # generates an instantiated proc
@ -199,8 +202,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
var n = copyTree(fn.ast) var n = copyTree(fn.ast)
# NOTE: for access of private fields within generics from a different module # NOTE: for access of private fields within generics from a different module
# we set the friend module: # we set the friend module:
var oldFriend = c.friendModule c.friendModules.add(getModule(fn))
c.friendModule = getModule(fn)
#let oldScope = c.currentScope #let oldScope = c.currentScope
#c.currentScope = fn.scope #c.currentScope = fn.scope
result = copySym(fn, false) result = copySym(fn, false)
@ -236,6 +238,6 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
closeScope(c) # close scope for parameters closeScope(c) # close scope for parameters
popOwner() popOwner()
#c.currentScope = oldScope #c.currentScope = oldScope
c.friendModule = oldFriend discard c.friendModules.pop()
dec(c.instCounter) dec(c.instCounter)
if result.kind == skMethod: finishMethod(c, result) if result.kind == skMethod: finishMethod(c, result)

View file

@ -66,10 +66,16 @@ proc toCover(t: PType): BiggestInt =
result = lengthOrd(skipTypes(t, abstractVar-{tyTypeDesc})) result = lengthOrd(skipTypes(t, abstractVar-{tyTypeDesc}))
proc performProcvarCheck(c: PContext, n: PNode, s: PSym) = 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 var smoduleId = getModule(s).id
if sfProcvar notin s.flags and s.typ.callConv == ccDefault and if sfProcvar notin s.flags and s.typ.callConv == ccDefault and
smoduleId != c.module.id and smoduleId != c.friendModule.id: smoduleId != c.module.id:
localError(n.info, errXCannotBePassedToProcVar, s.name.s) 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) = proc semProcvarCheck(c: PContext, n: PNode) =
let n = n.skipConv let n = n.skipConv

View file

@ -70,9 +70,10 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
counter = x counter = x
of nkSym: of nkSym:
e = n.sons[i].sym e = n.sons[i].sym
of nkIdent: of nkIdent, nkAccQuoted:
e = newSymS(skEnumField, n.sons[i], c) e = newSymS(skEnumField, n.sons[i], c)
else: illFormedAst(n) else:
illFormedAst(n[i])
e.typ = result e.typ = result
e.position = int(counter) e.position = int(counter)
if e.position == 0: hasNull = true 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.} = proc fieldVisible*(c: PContext, f: PSym): bool {.inline.} =
let fmoduleId = getModule(f).id let fmoduleId = getModule(f).id
result = sfExported in f.flags or fmoduleId == c.module.id or result = sfExported in f.flags or fmoduleId == c.module.id
fmoduleId == c.friendModule.id for module in c.friendModules:
if fmoduleId == module.id:
result = true
break
proc suggestField(c: PContext, s: PSym, outputs: var int) = proc suggestField(c: PContext, s: PSym, outputs: var int) =
if filterSym(s) and fieldVisible(c, s): if filterSym(s) and fieldVisible(c, s):
@ -319,7 +322,7 @@ proc suggestSym*(n: PNode, s: PSym) {.inline.} =
findUsages(n, s) findUsages(n, s)
if optDef in gGlobalOptions: if optDef in gGlobalOptions:
findDefinition(n, s) findDefinition(n, s)
if isServing: if isServing and not n.isNil:
addToSourceMap(s, n.info) addToSourceMap(s, n.info)
proc markUsed(n: PNode, s: PSym) = proc markUsed(n: PNode, s: PSym) =

View file

@ -15,6 +15,8 @@ proc readOutput(p: PProcess): string =
discard p.waitForExit discard p.waitForExit
while not output.atEnd: while not output.atEnd:
result.add(output.readLine) result.add(output.readLine)
result.add("\n")
result.setLen(result.len - "\n".len)
proc opGorge*(cmd, input: string): string = proc opGorge*(cmd, input: string): string =
var p = startCmd(cmd) var p = startCmd(cmd)

View file

@ -24,7 +24,7 @@ ampExpr = plusExpr (OP6 optInd plusExpr)*
plusExpr = mulExpr (OP7 optInd mulExpr)* plusExpr = mulExpr (OP7 optInd mulExpr)*
mulExpr = dollarExpr (OP8 optInd dollarExpr)* mulExpr = dollarExpr (OP8 optInd dollarExpr)*
dollarExpr = primary (OP9 optInd primary)* dollarExpr = primary (OP9 optInd primary)*
symbol = '`' (KEYW|IDENT|operator|'(' ')'|'[' ']'|'{' '}'|'='|literal)+ '`' symbol = '`' (KEYW|IDENT|literal|(operator|'('|')'|'['|']'|'{'|'}'|'=')+)+ '`'
| IDENT | IDENT
indexExpr = expr indexExpr = expr
indexExprList = indexExpr ^+ comma indexExprList = indexExpr ^+ comma
@ -82,6 +82,7 @@ paramListColon = paramList? (':' optInd typeDesc)?
doBlock = 'do' paramListArrow pragmas? colcom stmt doBlock = 'do' paramListArrow pragmas? colcom stmt
doBlocks = doBlock ^* IND{=} doBlocks = doBlock ^* IND{=}
procExpr = 'proc' paramListColon pragmas? ('=' COMMENT? stmt)? procExpr = 'proc' paramListColon pragmas? ('=' COMMENT? stmt)?
distinct = 'distinct' optInd typeDesc
expr = (ifExpr expr = (ifExpr
| whenExpr | whenExpr
| caseExpr | caseExpr
@ -166,7 +167,6 @@ object = 'object' pragma? ('of' typeDesc)? COMMENT? objectPart
typeClassParam = ('var')? symbol typeClassParam = ('var')? symbol
typeClass = typeClassParam ^* ',' (pragma)? ('of' typeDesc ^* ',')? typeClass = typeClassParam ^* ',' (pragma)? ('of' typeDesc ^* ',')?
&IND{>} stmt &IND{>} stmt
distinct = 'distinct' optInd typeDesc
typeDef = identWithPragma genericParamList? '=' optInd typeDefAux typeDef = identWithPragma genericParamList? '=' optInd typeDefAux
indAndComment? indAndComment?
varTuple = '(' optInd identWithPragma ^+ comma optPar ')' '=' optInd expr 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*: spelled*:
.. code-block:: .. 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 Note that this pragma is somewhat of a misnomer: Other backends will provide
the same feature under the same name. the same feature under the same name.

View file

@ -88,6 +88,18 @@ Level Description
===== ============================================ ===== ============================================
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 Configuration files
------------------- -------------------

View file

@ -1385,8 +1385,8 @@ Tuples
A tuple type defines various named *fields* and an *order* of the fields. 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 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. 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 Different tuple-types are *equivalent* if they specify fields of
the same type in the same order. the same type and of the same name in the same order.
The assignment operator for tuples copies each component. The notation The assignment operator for tuples copies each component. The notation
``t.field`` is used to access a tuple's field. Another notation is ``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. help you locate the offending code which has changed.
If you want to add the ``{.raises.}`` pragma to existing code, the compiler can 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 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 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`` 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: ## # overload:
## sort(myStrArray, system.cmp) ## 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 n = a.len
var b: seq[T] var b: seq[T]
newSeq(b, n div 2) newSeq(b, n div 2)

View file

@ -27,9 +27,11 @@ export TPort
# TODO: Discarded void PFutures need to checked for exception. # TODO: Discarded void PFutures need to checked for exception.
# TODO: Exceptions are currently uncatchable due to the limitation that # TODO: ``except`` statement (without `try`) does not work.
# you cannot have yield in a try stmt. Perhaps I can get the macro to put # TODO: Multiple exception names in a ``except`` don't work.
# a user's try except around ``future.read``. # TODO: The effect system (raises: []) has trouble with my try transformation.
# TODO: Can't await in a 'except' body
# -- Futures # -- Futures
@ -922,14 +924,17 @@ proc getName(node: PNimrodNode): string {.compileTime.} =
return $node[1].ident return $node[1].ident
of nnkIdent: of nnkIdent:
return $node.ident return $node.ident
of nnkEmpty:
return "anonymous"
else: else:
assert false error("Unknown name.")
macro async*(prc: stmt): stmt {.immediate.} = macro async*(prc: stmt): stmt {.immediate.} =
## Macro which processes async procedures into the appropriate ## Macro which processes async procedures into the appropriate
## iterators and yield statements. ## iterators and yield statements.
if prc.kind notin {nnkProcDef, nnkLambda}:
expectKind(prc, nnkProcDef) 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.") hint("Processing " & prc[0].getName & " as an async proc.")
@ -941,7 +946,9 @@ macro async*(prc: stmt): stmt {.immediate.} =
if $returnType[0] != "PFuture": if $returnType[0] != "PFuture":
error("Expected return type of 'PFuture' got '" & $returnType[0] & "'") 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) var outerProcBody = newNimNode(nnkStmtList)
@ -990,17 +997,19 @@ macro async*(prc: stmt): stmt {.immediate.} =
# Remove the 'async' pragma. # Remove the 'async' pragma.
for i in 0 .. <result[4].len: 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) result[4].del(i)
if subtypeIsVoid: if subtypeIsVoid:
# Add discardable pragma. # 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: if returnType.kind == nnkEmpty:
# Add PFuture[void] # Add PFuture[void]
result[3][0] = parseExpr("PFuture[void]") result[3][0] = parseExpr("PFuture[void]")
result[6] = outerProcBody result[6] = outerProcBody
#echo(treeRepr(result))
#echo(toStrLit(result)) #echo(toStrLit(result))
proc recvLine*(socket: TAsyncFD): PFuture[string] {.async.} = proc recvLine*(socket: TAsyncFD): PFuture[string] {.async.} =

View file

@ -14,12 +14,13 @@
import strtabs, asyncnet, asyncdispatch, parseutils, parseurl, strutils import strtabs, asyncnet, asyncdispatch, parseutils, parseurl, strutils
type type
TRequest* = object TRequest* = object
client: PAsyncSocket # TODO: Separate this into a Response object? client*: PAsyncSocket # TODO: Separate this into a Response object?
reqMethod*: string reqMethod*: string
headers*: PStringTable headers*: PStringTable
protocol*: tuple[orig: string, major, minor: int] protocol*: tuple[orig: string, major, minor: int]
url*: TURL url*: TURL
hostname*: string ## The hostname of the client that made the request. hostname*: string ## The hostname of the client that made the request.
body*: string # TODO
PAsyncHttpServer* = ref object PAsyncHttpServer* = ref object
socket: PAsyncSocket socket: PAsyncSocket
@ -169,6 +170,10 @@ proc serve*(server: PAsyncHttpServer, port: TPort,
var fut = await server.socket.acceptAddr() var fut = await server.socket.acceptAddr()
processClient(fut.client, fut.address, callback) processClient(fut.client, fut.address, callback)
proc close*(server: PAsyncHttpServer) =
## Terminates the async http server instance.
server.socket.close()
when isMainModule: when isMainModule:
var server = newAsyncHttpServer() var server = newAsyncHttpServer()
proc cb(req: TRequest) {.async.} = 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 = proc dequeue*[T](q: var TQueue[T]): T =
## removes and returns the first element of the queue `q`. ## removes and returns the first element of the queue `q`.
assert q.len > 0 assert q.count > 0
dec q.count dec q.count
result = q.data[q.rd] result = q.data[q.rd]
q.rd = (q.rd + 1) and q.mask q.rd = (q.rd + 1) and q.mask

View file

@ -37,7 +37,8 @@
## ## Piggyback on the already available string hash proc. ## ## Piggyback on the already available string hash proc.
## ## ## ##
## ## Without this proc nothing works! ## ## Without this proc nothing works!
## result = hash(x.firstName & x.lastName) ## result = x.firstName.hash !& x.lastName.hash
## result = !$result
## ##
## var ## var
## salaries = initTable[Person, int]() ## 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 if index >= 0: result = t.data[index].val
else: raise newException(EInvalidKey, "key not found: " & $key) 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 = proc hasKey*[A, B](t: TTable[A, B], key: A): bool =
## returns true iff `key` is in the table `t`. ## returns true iff `key` is in the table `t`.
result = rawGet(t, key) >= 0 result = rawGet(t, key) >= 0
@ -313,8 +322,7 @@ proc newTable*[A, B](initialSize=64): PTable[A, B] =
new(result) new(result)
result[] = initTable[A, B](initialSize) result[] = initTable[A, B](initialSize)
proc newTable*[A, B](pairs: openArray[tuple[key: A, proc newTable*[A, B](pairs: openArray[tuple[key: A, val: B]]): PTable[A, B] =
val: B]]): PTable[A, B] =
## creates a new hash table that contains the given `pairs`. ## creates a new hash table that contains the given `pairs`.
new(result) new(result)
result[] = toTable[A, B](pairs) result[] = toTable[A, B](pairs)
@ -841,7 +849,8 @@ when isMainModule:
## Piggyback on the already available string hash proc. ## Piggyback on the already available string hash proc.
## ##
## Without this proc nothing works! ## Without this proc nothing works!
result = hash(x.firstName & x.lastName) result = x.firstName.hash !& x.lastName.hash
result = !$result
var var
salaries = initTable[Person, int]() salaries = initTable[Person, int]()

View file

@ -119,8 +119,8 @@ proc getEvent(m: PFSMonitor, fd: cint): seq[TMonitorEvent] =
var mev: TMonitorEvent var mev: TMonitorEvent
mev.wd = event.wd mev.wd = event.wd
if event.len.int != 0: if event.len.int != 0:
mev.name = newString(event.len.int) let cstr = event.name.addr.cstring
copyMem(addr(mev.name[0]), addr event.name, event.len.int-1) mev.name = $cstr
else: else:
mev.name = "" mev.name = ""

View file

@ -8,7 +8,34 @@
# #
## This module implements efficient computations of hash values for diverse ## 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 import
strutils strutils

View file

@ -619,6 +619,44 @@ proc `%`*(elements: openArray[PJsonNode]): PJsonNode =
newSeq(result.elems, elements.len) newSeq(result.elems, elements.len)
for i, p in pairs(elements): result.elems[i] = p 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 = proc len*(n: PJsonNode): int =
## If `n` is a `JArray`, it returns the number of elements. ## If `n` is a `JArray`, it returns the number of elements.
## If `n` is a `JObject`, it returns the number of pairs. ## 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 = proc `[]`*(node: PJsonNode, name: string): PJsonNode =
## Gets a field from a `JObject`, which must not be nil. ## Gets a field from a `JObject`, which must not be nil.
## If the value at `name` does not exist, returns nil ## If the value at `name` does not exist, returns nil
assert(node != nil) assert(not isNil(node))
assert(node.kind == JObject) assert(node.kind == JObject)
for key, item in items(node.fields): for key, item in items(node.fields):
if key == name: if key == name:
@ -641,8 +679,8 @@ proc `[]`*(node: PJsonNode, name: string): PJsonNode =
proc `[]`*(node: PJsonNode, index: int): PJsonNode = proc `[]`*(node: PJsonNode, index: int): PJsonNode =
## Gets the node at `index` in an Array. Result is undefined if `index` ## Gets the node at `index` in an Array. Result is undefined if `index`
## is out of bounds ## is out of bounds
assert(not isNil(node))
assert(node.kind == JArray) assert(node.kind == JArray)
assert(node != nil)
return node.elems[index] return node.elems[index]
proc hasKey*(node: PJsonNode, key: string): bool = proc hasKey*(node: PJsonNode, key: string): bool =
@ -675,14 +713,12 @@ proc `[]=`*(obj: PJsonNode, key: string, val: PJsonNode) =
return return
obj.fields.add((key, val)) 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 ## Transverses the node and gets the given value. If any of the
## names does not exist, returns nil ## names does not exist, returns nil
result = node result = node
for name in names: if isNil(node): return nil
result = result[name] result = result[key]
if isNil(result):
return nil
proc `{}=`*(node: PJsonNode, names: varargs[string], value: PJsonNode) = proc `{}=`*(node: PJsonNode, names: varargs[string], value: PJsonNode) =
## Transverses the node and tries to set the value at the given location ## 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" }""" let testJson = parseJson"""{ "a": [1, 2, 3, 4], "b": "asd" }"""
# nil passthrough # nil passthrough
assert(testJson{"doesnt_exist", "anything"} == nil) assert(testJson{"doesnt_exist"}{"anything"}.isNil)
testJson{["c", "d"]} = %true testJson{["c", "d"]} = %true
assert(testJson["c"]["d"].bval) assert(testJson["c"]["d"].bval)

View file

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

View file

@ -39,7 +39,6 @@ export
MSG_PEEK MSG_PEEK
type type
TPort* = distinct uint16 ## port type TPort* = distinct uint16 ## port type
TDomain* = enum ## domain, which specifies the protocol family of the 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): ## when not defined(strutils.toUpper):
## # provide our own toUpper proc here, because strutils is ## # provide our own toUpper proc here, because strutils is
## # missing it. ## # 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): when defined(useNimRtl):
{.deadCodeElim: on.} {.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. ## in the loop body.
iterator fieldPairs*[T: tuple|object](x: T): TObject {. iterator fieldPairs*[T: tuple|object](x: T): TObject {.
magic: "FieldPairs", noSideEffect.} magic: "FieldPairs", noSideEffect.}
## iterates over every field of `x`. Warning: This really transforms ## Iterates over every field of `x` returning their name and value.
## the 'for' and unrolls the loop. The current implementation also has a bug ##
## that affects symbol binding in the loop body. ## 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[ iterator fieldPairs*[S: tuple|object, T: tuple|object](x: S, y: T): tuple[
a, b: expr] {. a, b: expr] {.
magic: "FieldPairs", noSideEffect.} magic: "FieldPairs", noSideEffect.}
@ -2785,10 +2823,15 @@ when true:
THide(raiseAssert)(msg) THide(raiseAssert)(msg)
template assert*(cond: bool, 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 ## ``assert`` evaluates expression ``cond`` and if ``cond`` is false, it
## raises an ``EAssertionFailure`` exception. However, the compiler may ## raises an ``EAssertionFailure`` exception. However, the compiler may not
## not generate any code at all for ``assert`` if it is advised to do so. ## 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. ## Use ``assert`` for debugging purposes only.
bind instantiationInfo bind instantiationInfo
mixin failedAssertImpl mixin failedAssertImpl

View file

@ -67,6 +67,28 @@ proc chckObjAsgn(a, b: PNimType) {.compilerproc, inline.} =
if a != b: if a != b:
sysFatal(EInvalidObjectAssignment, "invalid object assignment") 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.} = proc isObj(obj, subclass: PNimType): bool {.compilerproc.} =
# checks if obj is of type subclass: # checks if obj is of type subclass:
var x = obj 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) = proc doCommand(session: var TNimrodSession, command: string) =
if session.mode == CaasRun: 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, session.lastOutput = doCaasCommand(session,
command & " " & session.filename) command & " " & session.filename)
else: else:
@ -102,7 +106,7 @@ proc close(session: var TNimrodSession) {.destructor.} =
if session.mode == CaasRun: if session.mode == CaasRun:
session.nim.close 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 result = true
var f = open(script) var f = open(script)
@ -134,7 +138,7 @@ proc doScenario(script: string, output: PStream, mode: TRunMode): bool =
continue continue
elif line.startsWith(">"): elif line.startsWith(">"):
s.doCommand(line.substr(1).strip) s.doCommand(line.substr(1).strip)
output.writeln line, "\n", s.lastOutput output.writeln line, "\n", if verbose: s.lastOutput else: ""
else: else:
var expectMatch = true var expectMatch = true
var pattern = s.replaceVars(line) var pattern = s.replaceVars(line)
@ -151,13 +155,14 @@ proc doScenario(script: string, output: PStream, mode: TRunMode): bool =
output.writeln "FAILURE ", line output.writeln "FAILURE ", line
result = false result = false
iterator caasTestsRunner*(filter = ""): tuple[test, output: string, iterator caasTestsRunner*(filter = "", verbose = false): tuple[test,
status: bool, mode: TRunMode] = output: string, status: bool,
mode: TRunMode] =
for scenario in os.walkFiles(TesterDir / "caas/*.txt"): for scenario in os.walkFiles(TesterDir / "caas/*.txt"):
if filter.len > 0 and find(scenario, filter) == -1: continue if filter.len > 0 and find(scenario, filter) == -1: continue
for mode in modes: for mode in modes:
var outStream = newStringStream() var outStream = newStringStream()
let r = doScenario(scenario, outStream, mode) let r = doScenario(scenario, outStream, mode, verbose)
yield (scenario, outStream.data, r, mode) yield (scenario, outStream.data, r, mode)
when isMainModule: when isMainModule:
@ -175,9 +180,12 @@ when isMainModule:
if verbose and len(filter) > 0: if verbose and len(filter) > 0:
echo "Running only test cases matching filter '$1'" % [filter] 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: 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: if not result:
failures += 1 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.") echo("[Warning] - Cannot run babel tests: Babel update failed.")
return return
for name, url in listPackages(filter): let packageFileTest = makeTest("PackageFileParsed", "", cat)
var test = makeTest(name, "", cat) try:
echo(url) for name, url in listPackages(filter):
let var test = makeTest(name, "", cat)
installProcess = startProcess(babelExe, "", ["install", "-y", name]) echo(url)
installStatus = waitForExitEx(installProcess) let
installProcess.close installProcess = startProcess(babelExe, "", ["install", "-y", name])
if installStatus != quitSuccess: installStatus = waitForExitEx(installProcess)
r.addResult(test, "", "", reInstallFailed) installProcess.close
continue 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 - implement the "snoopResult" pragma; no, make a strutils with string append
semantics instead ... semantics instead ...
- implement "closure tuple consists of a single 'ref'" optimization - implement "closure tuple consists of a single 'ref'" optimization
- optimize method dispatchers
- new feature: ``distinct T with operations`` - new feature: ``distinct T with operations``
- arglist as a type (iterator chaining); variable length type lists for generics - arglist as a type (iterator chaining); variable length type lists for generics
- implement marker procs for message passing - implement marker procs for message passing

View file

@ -93,7 +93,7 @@ html, body {
border-left:10px solid #8f9698; border-left:10px solid #8f9698;
background:#f3f6f8; background:#f3f6f8;
font-size:15px; font-size:15px;
font-family:courier; font-family:courier, monospace;
letter-spacing:0; letter-spacing:0;
line-height:17px; line-height:17px;
} }