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

This commit is contained in:
Araq 2014-02-17 23:59:48 +01:00
commit 9b63fccf16
53 changed files with 1124 additions and 473 deletions

View file

@ -409,7 +409,9 @@ type
# efficiency # efficiency
nfTransf, # node has been transformed nfTransf, # node has been transformed
nfSem # node has been checked for semantics nfSem # node has been checked for semantics
nfDelegate # the call can use a delegator nfDotField # the call can use a dot operator
nfDotSetter # the call can use a setter dot operarator
nfExplicitCall # x.y() was used instead of x.y
nfExprCall # this is an attempt to call a regular expression nfExprCall # this is an attempt to call a regular expression
nfIsRef # this node is a 'ref' node; used for the VM nfIsRef # this node is a 'ref' node; used for the VM
@ -843,7 +845,8 @@ const
ExportableSymKinds* = {skVar, skConst, skProc, skMethod, skType, skIterator, ExportableSymKinds* = {skVar, skConst, skProc, skMethod, skType, skIterator,
skMacro, skTemplate, skConverter, skEnumField, skLet, skStub} skMacro, skTemplate, skConverter, skEnumField, skLet, skStub}
PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16, PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16,
nfAllConst, nfDelegate, nfIsRef} nfDotSetter, nfDotField,
nfAllConst,nfIsRef}
namePos* = 0 namePos* = 0
patternPos* = 1 # empty except for term rewriting macros patternPos* = 1 # empty except for term rewriting macros
genericParamsPos* = 2 genericParamsPos* = 2
@ -1044,6 +1047,10 @@ proc newStrNode(kind: TNodeKind, strVal: string): PNode =
result = newNode(kind) result = newNode(kind)
result.strVal = strVal result.strVal = strVal
proc withInfo*(n: PNode, info: TLineInfo): PNode =
n.info = info
return n
proc newIdentNode(ident: PIdent, info: TLineInfo): PNode = proc newIdentNode(ident: PIdent, info: TLineInfo): PNode =
result = newNode(nkIdent) result = newNode(nkIdent)
result.ident = ident result.ident = ident

View file

@ -540,7 +540,7 @@ proc typeAtom(p: var TParser): PNode =
if p.tok.s == "unsigned": if p.tok.s == "unsigned":
isUnsigned = true isUnsigned = true
elif p.tok.s == "signed" or p.tok.s == "int": elif p.tok.s == "signed" or p.tok.s == "int":
nil discard
else: else:
add(x, p.tok.s) add(x, p.tok.s)
getTok(p, nil) getTok(p, nil)
@ -746,7 +746,7 @@ proc directDeclarator(p: var TParser, a: PNode, ident: ptr PNode): PNode =
result = declarator(p, a, ident) result = declarator(p, a, ident)
eat(p, pxParRi, result) eat(p, pxParRi, result)
else: else:
nil discard
return parseTypeSuffix(p, a) return parseTypeSuffix(p, a)
proc declarator(p: var TParser, a: PNode, ident: ptr PNode): PNode = proc declarator(p: var TParser, a: PNode, ident: ptr PNode): PNode =
@ -1165,7 +1165,7 @@ proc enumSpecifier(p: var TParser): PNode =
proc setBaseFlags(n: PNode, base: TNumericalBase) = proc setBaseFlags(n: PNode, base: TNumericalBase) =
case base case base
of base10: nil of base10: discard
of base2: incl(n.flags, nfBase2) of base2: incl(n.flags, nfBase2)
of base8: incl(n.flags, nfBase8) of base8: incl(n.flags, nfBase8)
of base16: incl(n.flags, nfBase16) of base16: incl(n.flags, nfBase16)
@ -1686,7 +1686,7 @@ proc switchStatement(p: var TParser): PNode =
break break
of "case", "default": of "case", "default":
break break
else: nil else: discard
addSon(result, statement(p)) addSon(result, statement(p))
if sonsLen(result) == 0: if sonsLen(result) == 0:
# translate empty statement list to Nimrod's ``nil`` statement # translate empty statement list to Nimrod's ``nil`` statement

View file

@ -103,7 +103,7 @@ proc parseDefBody(p: var TParser, m: var TMacro, params: seq[string]) =
m.body.add(tok) m.body.add(tok)
of pxDirConc: of pxDirConc:
# just ignore this token: this implements token merging correctly # just ignore this token: this implements token merging correctly
nil discard
else: else:
m.body.add(p.tok) m.body.add(p.tok)
# we do not want macro expansion here: # we do not want macro expansion here:
@ -166,7 +166,7 @@ proc parseStmtList(p: var TParser): PNode =
of pxDirectiveParLe, pxDirective: of pxDirectiveParLe, pxDirective:
case p.tok.s case p.tok.s
of "else", "endif", "elif": break of "else", "endif", "elif": break
else: nil else: discard
addSon(result, statement(p)) addSon(result, statement(p))
proc eatEndif(p: var TParser) = proc eatEndif(p: var TParser) =

View file

@ -65,6 +65,7 @@ proc startBlock(p: BProc, start: TFormatStr = "{$n",
setLen(p.blocks, result + 1) setLen(p.blocks, result + 1)
p.blocks[result].id = p.labels p.blocks[result].id = p.labels
p.blocks[result].nestedTryStmts = p.nestedTryStmts.len.int16 p.blocks[result].nestedTryStmts = p.nestedTryStmts.len.int16
p.blocks[result].nestedExceptStmts = p.inExceptBlock.int16
proc assignLabel(b: var TBlock): PRope {.inline.} = proc assignLabel(b: var TBlock): PRope {.inline.} =
b.label = con("LA", b.id.toRope) b.label = con("LA", b.id.toRope)
@ -260,14 +261,22 @@ proc genIf(p: BProc, n: PNode, d: var TLoc) =
else: internalError(n.info, "genIf()") else: internalError(n.info, "genIf()")
if sonsLen(n) > 1: fixLabel(p, lend) if sonsLen(n) > 1: fixLabel(p, lend)
proc blockLeaveActions(p: BProc, howMany: int) =
var L = p.nestedTryStmts.len proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) =
# This is called by return and break stmts.
# When jumping out of try/except/finally stmts,
# we need to pop safe points from try statements,
# execute finally-stmts, and pop exceptions
# from except stmts
let L = p.nestedTryStmts.len
# danger of endless recursion! we workaround this here by a temp stack # danger of endless recursion! we workaround this here by a temp stack
var stack: seq[PNode] var stack: seq[PNode]
newSeq(stack, howMany) newSeq(stack, howManyTrys)
for i in countup(1, howMany): for i in countup(1, howManyTrys):
stack[i-1] = p.nestedTryStmts[L-i] stack[i-1] = p.nestedTryStmts[L-i]
setLen(p.nestedTryStmts, L-howMany) setLen(p.nestedTryStmts, L-howManyTrys)
var alreadyPoppedCnt = p.inExceptBlock var alreadyPoppedCnt = p.inExceptBlock
for tryStmt in items(stack): for tryStmt in items(stack):
@ -276,21 +285,26 @@ proc blockLeaveActions(p: BProc, howMany: int) =
dec alreadyPoppedCnt dec alreadyPoppedCnt
else: else:
linefmt(p, cpsStmts, "#popSafePoint();$n") linefmt(p, cpsStmts, "#popSafePoint();$n")
# Find finally-stmts for this try-stmt
# and generate a copy of the finally stmts here
var finallyStmt = lastSon(tryStmt) var finallyStmt = lastSon(tryStmt)
if finallyStmt.kind == nkFinally: if finallyStmt.kind == nkFinally:
genStmts(p, finallyStmt.sons[0]) genStmts(p, finallyStmt.sons[0])
# push old elements again: # push old elements again:
for i in countdown(howMany-1, 0): for i in countdown(howManyTrys-1, 0):
p.nestedTryStmts.add(stack[i]) p.nestedTryStmts.add(stack[i])
if gCmd != cmdCompileToCpp: if gCmd != cmdCompileToCpp:
for i in countdown(p.inExceptBlock-1, 0): # Pop exceptions that was handled by the
# except-blocks we are in
for i in countdown(howManyExcepts-1, 0):
linefmt(p, cpsStmts, "#popCurrentException();$n") linefmt(p, cpsStmts, "#popCurrentException();$n")
proc genReturnStmt(p: BProc, t: PNode) = proc genReturnStmt(p: BProc, t: PNode) =
p.beforeRetNeeded = true p.beforeRetNeeded = true
genLineDir(p, t) genLineDir(p, t)
if (t.sons[0].kind != nkEmpty): genStmts(p, t.sons[0]) if (t.sons[0].kind != nkEmpty): genStmts(p, t.sons[0])
blockLeaveActions(p, min(1, p.nestedTryStmts.len)) blockLeaveActions(p, min(1, p.nestedTryStmts.len), p.inExceptBlock)
lineFF(p, cpsStmts, "goto BeforeRet;$n", "br label %BeforeRet$n", []) lineFF(p, cpsStmts, "goto BeforeRet;$n", "br label %BeforeRet$n", [])
proc genComputedGoto(p: BProc; n: PNode) = proc genComputedGoto(p: BProc; n: PNode) =
@ -450,7 +464,9 @@ proc genBreakStmt(p: BProc, t: PNode) =
if idx < 0 or not p.blocks[idx].isLoop: if idx < 0 or not p.blocks[idx].isLoop:
internalError(t.info, "no loop to break") internalError(t.info, "no loop to break")
let label = assignLabel(p.blocks[idx]) let label = assignLabel(p.blocks[idx])
blockLeaveActions(p, p.nestedTryStmts.len - p.blocks[idx].nestedTryStmts) blockLeaveActions(p,
p.nestedTryStmts.len - p.blocks[idx].nestedTryStmts,
p.inExceptBlock - p.blocks[idx].nestedExceptStmts)
genLineDir(p, t) genLineDir(p, t)
lineF(p, cpsStmts, "goto $1;$n", [label]) lineF(p, cpsStmts, "goto $1;$n", [label])

View file

@ -58,6 +58,7 @@ type
sections*: TCProcSections # the code beloging sections*: TCProcSections # the code beloging
isLoop*: bool # whether block is a loop isLoop*: bool # whether block is a loop
nestedTryStmts*: int16 # how many try statements is it nested into nestedTryStmts*: int16 # how many try statements is it nested into
nestedExceptStmts*: int16 # how many except statements is it nested into
frameLen*: int16 frameLen*: int16
TCProc{.final.} = object # represents C proc that is currently generated TCProc{.final.} = object # represents C proc that is currently generated

View file

@ -88,7 +88,8 @@ type
errTemplateInstantiationTooNested, errInstantiationFrom, errTemplateInstantiationTooNested, errInstantiationFrom,
errInvalidIndexValueForTuple, errCommandExpectsFilename, errInvalidIndexValueForTuple, errCommandExpectsFilename,
errMainModuleMustBeSpecified, errMainModuleMustBeSpecified,
errXExpected, errXExpected,
errTIsNotAConcreteType,
errInvalidSectionStart, errGridTableNotImplemented, errGeneralParseError, errInvalidSectionStart, errGridTableNotImplemented, errGeneralParseError,
errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile, errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile,
errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitely, errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitely,
@ -312,6 +313,7 @@ const
errCommandExpectsFilename: "command expects a filename argument", errCommandExpectsFilename: "command expects a filename argument",
errMainModuleMustBeSpecified: "please, specify a main module in the project configuration file", errMainModuleMustBeSpecified: "please, specify a main module in the project configuration file",
errXExpected: "\'$1\' expected", errXExpected: "\'$1\' expected",
errTIsNotAConcreteType: "\'$1\' is not a concrete type.",
errInvalidSectionStart: "invalid section start", errInvalidSectionStart: "invalid section start",
errGridTableNotImplemented: "grid table is not implemented", errGridTableNotImplemented: "grid table is not implemented",
errGeneralParseError: "general parse error", errGeneralParseError: "general parse error",

View file

@ -281,7 +281,7 @@ proc parseSymbol(p: var TParser): PNode =
add(result, newIdentNodeP(getIdent"{}", p)) add(result, newIdentNodeP(getIdent"{}", p))
getTok(p) getTok(p)
eat(p, tkCurlyRi) eat(p, tkCurlyRi)
of tokKeywordLow..tokKeywordHigh, tkSymbol, tkOpr, tkDotDot: of tokKeywordLow..tokKeywordHigh, tkSymbol, tkOpr, tkDot, tkDotDot:
add(result, newIdentNodeP(p.tok.ident, p)) add(result, newIdentNodeP(p.tok.ident, p))
getTok(p) getTok(p)
of tkIntLit..tkCharLit: of tkIntLit..tkCharLit:

View file

@ -342,7 +342,7 @@ proc getSymbol(L: var TLexer, tok: var TToken) =
h = h +% ord(c) h = h +% ord(c)
h = h +% h shl 10 h = h +% h shl 10
h = h xor (h shr 6) h = h xor (h shr 6)
of '_': nil of '_': discard
else: break else: break
inc(pos) inc(pos)
h = h +% h shl 3 h = h +% h shl 3

View file

@ -335,7 +335,7 @@ proc exprColonEqExprList(p: var TParser, kind, elemKind: TNodeKind,
proc setBaseFlags(n: PNode, base: TNumericalBase) = proc setBaseFlags(n: PNode, base: TNumericalBase) =
case base case base
of base10: nil of base10: discard
of base2: incl(n.flags, nfBase2) of base2: incl(n.flags, nfBase2)
of base8: incl(n.flags, nfBase8) of base8: incl(n.flags, nfBase8)
of base16: incl(n.flags, nfBase16) of base16: incl(n.flags, nfBase16)
@ -466,7 +466,7 @@ proc lowestExprAux(p: var TParser, v: var PNode, limit: int): TTokKind =
eat(p, pxCurlyDirRi) eat(p, pxCurlyDirRi)
opNode.ident = getIdent("&") opNode.ident = getIdent("&")
else: else:
nil discard
of pxMinus: of pxMinus:
if p.tok.xkind == pxPer: if p.tok.xkind == pxPer:
getTok(p) getTok(p)
@ -477,7 +477,7 @@ proc lowestExprAux(p: var TParser, v: var PNode, limit: int): TTokKind =
of pxNeq: of pxNeq:
opNode.ident = getIdent("!=") opNode.ident = getIdent("!=")
else: else:
nil discard
skipCom(p, opNode) # read sub-expression with higher priority skipCom(p, opNode) # read sub-expression with higher priority
nextop = lowestExprAux(p, v2, opPred) nextop = lowestExprAux(p, v2, opPred)
addSon(node, opNode) addSon(node, opNode)
@ -505,7 +505,7 @@ proc fixExpr(n: PNode): PNode =
(n.sons[2].kind in {nkCharLit, nkStrLit}): (n.sons[2].kind in {nkCharLit, nkStrLit}):
n.sons[0].ident = getIdent("&") # fix operator n.sons[0].ident = getIdent("&") # fix operator
else: else:
nil discard
if not (n.kind in {nkEmpty..nkNilLit}): if not (n.kind in {nkEmpty..nkNilLit}):
for i in countup(0, sonsLen(n) - 1): result.sons[i] = fixExpr(n.sons[i]) for i in countup(0, sonsLen(n) - 1): result.sons[i] = fixExpr(n.sons[i])
@ -603,7 +603,7 @@ proc parseStmtList(p: var TParser): PNode =
of pxCurlyDirLe, pxStarDirLe: of pxCurlyDirLe, pxStarDirLe:
if not isHandledDirective(p): break if not isHandledDirective(p): break
else: else:
nil discard
addSon(result, parseStmt(p)) addSon(result, parseStmt(p))
if sonsLen(result) == 1: result = result.sons[0] if sonsLen(result) == 1: result = result.sons[0]
@ -732,7 +732,7 @@ proc parseRepeat(p: var TParser): PNode =
addSon(b, c) addSon(b, c)
addSon(a, b) addSon(a, b)
if b.sons[0].kind == nkIdent and b.sons[0].ident.id == getIdent("false").id: if b.sons[0].kind == nkIdent and b.sons[0].ident.id == getIdent("false").id:
nil discard
else: else:
addSon(s, a) addSon(s, a)
addSon(result, s) addSon(result, s)
@ -840,7 +840,7 @@ proc parseParam(p: var TParser): PNode =
getTok(p) getTok(p)
v = newNodeP(nkVarTy, p) v = newNodeP(nkVarTy, p)
else: else:
nil discard
while true: while true:
case p.tok.xkind case p.tok.xkind
of pxSymbol: a = createIdentNodeP(p.tok.ident, p) of pxSymbol: a = createIdentNodeP(p.tok.ident, p)
@ -1133,7 +1133,7 @@ proc parseRecordPart(p: var TParser): PNode =
proc exSymbol(n: var PNode) = proc exSymbol(n: var PNode) =
case n.kind case n.kind
of nkPostfix: of nkPostfix:
nil discard
of nkPragmaExpr: of nkPragmaExpr:
exSymbol(n.sons[0]) exSymbol(n.sons[0])
of nkIdent, nkAccQuoted: of nkIdent, nkAccQuoted:
@ -1154,7 +1154,7 @@ proc fixRecordDef(n: var PNode) =
for i in countup(0, sonsLen(n) - 1): fixRecordDef(n.sons[i]) for i in countup(0, sonsLen(n) - 1): fixRecordDef(n.sons[i])
of nkIdentDefs: of nkIdentDefs:
for i in countup(0, sonsLen(n) - 3): exSymbol(n.sons[i]) for i in countup(0, sonsLen(n) - 3): exSymbol(n.sons[i])
of nkNilLit, nkEmpty: nil of nkNilLit, nkEmpty: discard
else: internalError(n.info, "fixRecordDef(): " & $n.kind) else: internalError(n.info, "fixRecordDef(): " & $n.kind)
proc addPragmaToIdent(ident: var PNode, pragma: PNode) = proc addPragmaToIdent(ident: var PNode, pragma: PNode) =
@ -1191,7 +1191,7 @@ proc parseRecordBody(p: var TParser, result, definition: PNode) =
if definition != nil: addPragmaToIdent(definition.sons[0], parseCommand(p)) if definition != nil: addPragmaToIdent(definition.sons[0], parseCommand(p))
else: internalError(result.info, "anonymous record is not supported") else: internalError(result.info, "anonymous record is not supported")
else: else:
nil discard
opt(p, pxSemicolon) opt(p, pxSemicolon)
skipCom(p, result) skipCom(p, result)
@ -1399,7 +1399,7 @@ proc fixVarSection(p: var TParser, counter: PNode) =
proc exSymbols(n: PNode) = proc exSymbols(n: PNode) =
case n.kind case n.kind
of nkEmpty..nkNilLit: nil of nkEmpty..nkNilLit: discard
of nkProcDef..nkIteratorDef: exSymbol(n.sons[namePos]) of nkProcDef..nkIteratorDef: exSymbol(n.sons[namePos])
of nkWhenStmt, nkStmtList: of nkWhenStmt, nkStmtList:
for i in countup(0, sonsLen(n) - 1): exSymbols(n.sons[i]) for i in countup(0, sonsLen(n) - 1): exSymbols(n.sons[i])
@ -1410,7 +1410,7 @@ proc exSymbols(n: PNode) =
exSymbol(n.sons[i].sons[0]) exSymbol(n.sons[i].sons[0])
if n.sons[i].sons[2].kind == nkObjectTy: if n.sons[i].sons[2].kind == nkObjectTy:
fixRecordDef(n.sons[i].sons[2]) fixRecordDef(n.sons[i].sons[2])
else: nil else: discard
proc parseBegin(p: var TParser, result: PNode) = proc parseBegin(p: var TParser, result: PNode) =
getTok(p) getTok(p)

View file

@ -333,6 +333,7 @@ proc myOpen(module: PSym): PPassContext =
c.semOperand = semOperand c.semOperand = semOperand
c.semConstBoolExpr = semConstBoolExpr c.semConstBoolExpr = semConstBoolExpr
c.semOverloadedCall = semOverloadedCall c.semOverloadedCall = semOverloadedCall
c.semInferredLambda = semInferredLambda
c.semGenerateInstance = generateInstance c.semGenerateInstance = generateInstance
c.semTypeNode = semTypeNode c.semTypeNode = semTypeNode
pushProcCon(c, module) pushProcCon(c, module)

View file

@ -82,7 +82,7 @@ proc notFoundError*(c: PContext, n: PNode, errors: seq[string]) =
# fail fast: # fail fast:
globalError(n.info, errTypeMismatch, "") globalError(n.info, errTypeMismatch, "")
var result = msgKindToString(errTypeMismatch) var result = msgKindToString(errTypeMismatch)
add(result, describeArgs(c, n, 1 + ord(nfDelegate in n.flags))) add(result, describeArgs(c, n, 1 + ord(nfDotField in n.flags)))
add(result, ')') add(result, ')')
var candidates = "" var candidates = ""
@ -138,17 +138,35 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
let overloadsState = result.state let overloadsState = result.state
if overloadsState != csMatch: if overloadsState != csMatch:
if nfDelegate in n.flags: if nfDotField in n.flags:
internalAssert f.kind == nkIdent internalAssert f.kind == nkIdent and n.sonsLen >= 2
let calleeName = newStrNode(nkStrLit, f.ident.s) let calleeName = newStrNode(nkStrLit, f.ident.s).withInfo(n.info)
calleeName.info = n.info
let callOp = newIdentNode(idDelegator, n.info) # leave the op head symbol empty,
n.sons[0..0] = [callOp, calleeName] # we are going to try multiple variants
orig.sons[0..0] = [callOp, calleeName] n.sons[0..1] = [nil, n[1], calleeName]
orig.sons[0..1] = [nil, orig[1], calleeName]
template tryOp(x) =
let op = newIdentNode(getIdent(x), n.info)
n.sons[0] = op
orig.sons[0] = op
pickBest(op)
if nfExplicitCall in n.flags:
tryOp ".()"
if result.state in {csEmpty, csNoMatch}:
tryOp "."
elif nfDotSetter in n.flags:
internalAssert f.kind == nkIdent and n.sonsLen == 3
let calleeName = newStrNode(nkStrLit, f.ident.s[0.. -2]).withInfo(n.info)
let callOp = newIdentNode(getIdent".=", n.info)
n.sons[0..1] = [callOp, n[1], calleeName]
orig.sons[0..1] = [callOp, orig[1], calleeName]
pickBest(callOp) pickBest(callOp)
if overloadsState == csEmpty and result.state == csEmpty: if overloadsState == csEmpty and result.state == csEmpty:
localError(n.info, errUndeclaredIdentifier, considerAcc(f).s) localError(n.info, errUndeclaredIdentifier, considerAcc(f).s)
return return

View file

@ -81,6 +81,7 @@ type
semOverloadedCall*: proc (c: PContext, n, nOrig: PNode, semOverloadedCall*: proc (c: PContext, n, nOrig: PNode,
filter: TSymKinds): PNode {.nimcall.} filter: TSymKinds): PNode {.nimcall.}
semTypeNode*: proc(c: PContext, n: PNode, prev: PType): PType {.nimcall.} semTypeNode*: proc(c: PContext, n: PNode, prev: PType): PType {.nimcall.}
semInferredLambda*: proc(c: PContext, pt: TIdTable, n: PNode): PNode
semGenerateInstance*: proc (c: PContext, fn: PSym, pt: TIdTable, semGenerateInstance*: proc (c: PContext, fn: PSym, pt: TIdTable,
info: TLineInfo): PSym info: TLineInfo): PSym
includedFiles*: TIntSet # used to detect recursive include files includedFiles*: TIntSet # used to detect recursive include files
@ -213,7 +214,6 @@ proc makeTypeDesc*(c: PContext, typ: PType): PType =
proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode = proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode =
let typedesc = makeTypeDesc(c, typ) let typedesc = makeTypeDesc(c, typ)
rawAddSon(typedesc, newTypeS(tyNone, c))
let sym = newSym(skType, idAnon, getCurrOwner(), info).linkTo(typedesc) let sym = newSym(skType, idAnon, getCurrOwner(), info).linkTo(typedesc)
return newSymNode(sym, info) return newSymNode(sym, info)

View file

@ -55,7 +55,9 @@ proc doDestructorStuff(c: PContext, s: PSym, n: PNode) =
useSym(destructableT.destructor), useSym(destructableT.destructor),
n.sons[paramsPos][1][0]])) n.sons[paramsPos][1][0]]))
proc destroyField(c: PContext, field: PSym, holder: PNode): PNode = proc destroyFieldOrFields(c: PContext, field: PNode, holder: PNode): PNode
proc destroySym(c: PContext, field: PSym, holder: PNode): PNode =
let destructableT = instantiateDestructor(c, field.typ) let destructableT = instantiateDestructor(c, field.typ)
if destructableT != nil: if destructableT != nil:
result = newNode(nkCall, field.info, @[ result = newNode(nkCall, field.info, @[
@ -70,56 +72,49 @@ proc destroyCase(c: PContext, n: PNode, holder: PNode): PNode =
for i in countup(1, n.len - 1): for i in countup(1, n.len - 1):
# of A, B: # of A, B:
var caseBranch = newNode(n[i].kind, n[i].info, n[i].sons[0 .. -2]) var caseBranch = newNode(n[i].kind, n[i].info, n[i].sons[0 .. -2])
let recList = n[i].lastSon
var destroyRecList = newNode(nkStmtList, n[i].info, @[]) let stmt = destroyFieldOrFields(c, n[i].lastSon, holder)
template addField(f: expr): stmt = if stmt == nil:
let stmt = destroyField(c, f, holder) caseBranch.addSon(newNode(nkStmtList, n[i].info, @[]))
if stmt != nil:
destroyRecList.addSon(stmt)
inc nonTrivialFields
case recList.kind
of nkSym:
addField(recList.sym)
of nkRecList:
for j in countup(0, recList.len - 1):
addField(recList[j].sym)
else: else:
internalAssert false caseBranch.addSon(stmt)
nonTrivialFields += stmt.len
caseBranch.addSon(destroyRecList)
result.addSon(caseBranch) result.addSon(caseBranch)
# maybe no fields were destroyed? # maybe no fields were destroyed?
if nonTrivialFields == 0: if nonTrivialFields == 0:
result = nil result = nil
proc destroyFieldOrFields(c: PContext, field: PNode, holder: PNode): PNode =
template maybeAddLine(e: expr): stmt =
let stmt = e
if stmt != nil:
if result == nil: result = newNode(nkStmtList)
result.addSon(stmt)
case field.kind
of nkRecCase:
maybeAddLine destroyCase(c, field, holder)
of nkSym:
maybeAddLine destroySym(c, field.sym, holder)
of nkRecList:
for son in field:
maybeAddLine destroyFieldOrFields(c, son, holder)
else:
internalAssert false
proc generateDestructor(c: PContext, t: PType): PNode = proc generateDestructor(c: PContext, t: PType): PNode =
## generate a destructor for a user-defined object or tuple type ## generate a destructor for a user-defined object or tuple type
## returns nil if the destructor turns out to be trivial ## returns nil if the destructor turns out to be trivial
template addLine(e: expr): stmt =
if result == nil: result = newNode(nkStmtList)
result.addSon(e)
# XXX: This may be true for some C-imported types such as # XXX: This may be true for some C-imported types such as
# Tposix_spawnattr # Tposix_spawnattr
if t.n == nil or t.n.sons == nil: return if t.n == nil or t.n.sons == nil: return
internalAssert t.n.kind == nkRecList internalAssert t.n.kind == nkRecList
let destructedObj = newIdentNode(destructorParam, unknownLineInfo()) let destructedObj = newIdentNode(destructorParam, unknownLineInfo())
# call the destructods of all fields # call the destructods of all fields
for s in countup(0, t.n.sons.len - 1): result = destroyFieldOrFields(c, t.n, destructedObj)
case t.n.sons[s].kind
of nkRecCase:
let stmt = destroyCase(c, t.n.sons[s], destructedObj)
if stmt != nil: addLine(stmt)
of nkSym:
let stmt = destroyField(c, t.n.sons[s].sym, destructedObj)
if stmt != nil: addLine(stmt)
else:
# XXX just skip it for now so that the compiler doesn't crash, but
# please zahary fix it! arbitrary nesting of nkRecList/nkRecCase is
# possible. Any thread example seems to trigger this.
discard
# base classes' destructors will be automatically called by # base classes' destructors will be automatically called by
# semProcAux for both auto-generated and user-defined destructors # semProcAux for both auto-generated and user-defined destructors

View file

@ -346,22 +346,21 @@ proc semIs(c: PContext, n: PNode): PNode =
result = n result = n
n.typ = getSysType(tyBool) n.typ = getSysType(tyBool)
n.sons[1] = semExprWithType(c, n[1], {efDetermineType}) n.sons[1] = semExprWithType(c, n[1], {efDetermineType, efWantIterator})
if n[2].kind notin {nkStrLit..nkTripleStrLit}: if n[2].kind notin {nkStrLit..nkTripleStrLit}:
let t2 = semTypeNode(c, n[2], nil) let t2 = semTypeNode(c, n[2], nil)
n.sons[2] = newNodeIT(nkType, n[2].info, t2) n.sons[2] = newNodeIT(nkType, n[2].info, t2)
if n[1].typ.kind != tyTypeDesc: let lhsType = n[1].typ
n.sons[1] = makeTypeSymNode(c, n[1].typ, n[1].info) if lhsType.kind != tyTypeDesc:
elif n[1].typ.sonsLen == 0: n.sons[1] = makeTypeSymNode(c, lhsType, n[1].info)
elif lhsType.base.kind == tyNone:
# this is a typedesc variable, leave for evals # this is a typedesc variable, leave for evals
return return
let t1 = n[1].typ.sons[0]
# BUGFIX: don't evaluate this too early: ``T is void`` # BUGFIX: don't evaluate this too early: ``T is void``
if not containsGenericType(t1): result = isOpImpl(c, n) if not n[1].typ.base.containsGenericType: result = isOpImpl(c, n)
proc semOpAux(c: PContext, n: PNode) = proc semOpAux(c: PContext, n: PNode) =
const flags = {efDetermineType} const flags = {efDetermineType}
@ -683,20 +682,21 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode,
incl(c.p.owner.flags, sfSideEffect) incl(c.p.owner.flags, sfSideEffect)
proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode
proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
result = nil result = nil
checkMinSonsLen(n, 1) checkMinSonsLen(n, 1)
var prc = n.sons[0] var prc = n.sons[0]
if n.sons[0].kind == nkDotExpr: if n.sons[0].kind == nkDotExpr:
checkSonsLen(n.sons[0], 2) checkSonsLen(n.sons[0], 2)
n.sons[0] = semFieldAccess(c, n.sons[0]) n.sons[0] = semFieldAccess(c, n.sons[0])
if n.sons[0].kind == nkDotCall: if n.sons[0].kind == nkDotCall:
# it is a static call! # it is a static call!
result = n.sons[0] result = n.sons[0]
result.kind = nkCall result.kind = nkCall
result.flags.incl nfExplicitCall
for i in countup(1, sonsLen(n) - 1): addSon(result, n.sons[i]) for i in countup(1, sonsLen(n) - 1): addSon(result, n.sons[i])
return semExpr(c, result, flags) return semExpr(c, result, flags)
else: else:
n.sons[0] = semExpr(c, n.sons[0]) n.sons[0] = semExpr(c, n.sons[0])
let nOrig = n.copyTree let nOrig = n.copyTree
semOpAux(c, n) semOpAux(c, n)
@ -917,8 +917,8 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
var ty = n.sons[0].typ var ty = n.sons[0].typ
var f: PSym = nil var f: PSym = nil
result = nil result = nil
if isTypeExpr(n.sons[0]) or ty.kind == tyTypeDesc and ty.len == 1: if isTypeExpr(n.sons[0]) or ty.kind == tyTypeDesc and ty.base.kind != tyNone:
if ty.kind == tyTypeDesc: ty = ty.sons[0] if ty.kind == tyTypeDesc: ty = ty.base
case ty.kind case ty.kind
of tyEnum: of tyEnum:
# look up if the identifier belongs to the enum: # look up if the identifier belongs to the enum:
@ -999,7 +999,7 @@ proc dotTransformation(c: PContext, n: PNode): PNode =
else: else:
var i = considerAcc(n.sons[1]) var i = considerAcc(n.sons[1])
result = newNodeI(nkDotCall, n.info) result = newNodeI(nkDotCall, n.info)
result.flags.incl nfDelegate result.flags.incl nfDotField
addSon(result, newIdentNode(i, n[1].info)) addSon(result, newIdentNode(i, n[1].info))
addSon(result, copyTree(n[0])) addSon(result, copyTree(n[0]))
@ -1082,12 +1082,13 @@ proc semArrayAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode = proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode =
var id = considerAcc(a[1]) var id = considerAcc(a[1])
let setterId = newIdentNode(getIdent(id.s & '='), n.info) var setterId = newIdentNode(getIdent(id.s & '='), n.info)
# a[0] is already checked for semantics, that does ``builtinFieldAccess`` # a[0] is already checked for semantics, that does ``builtinFieldAccess``
# this is ugly. XXX Semantic checking should use the ``nfSem`` flag for # this is ugly. XXX Semantic checking should use the ``nfSem`` flag for
# nodes? # nodes?
let aOrig = nOrig[0] let aOrig = nOrig[0]
result = newNode(nkCall, n.info, sons = @[setterId, a[0], semExpr(c, n[1])]) result = newNode(nkCall, n.info, sons = @[setterId, a[0], semExpr(c, n[1])])
result.flags.incl nfDotSetter
let orig = newNode(nkCall, n.info, sons = @[setterId, aOrig[0], nOrig[1]]) let orig = newNode(nkCall, n.info, sons = @[setterId, aOrig[0], nOrig[1]])
result = semOverloadedCallAnalyseEffects(c, result, orig, {}) result = semOverloadedCallAnalyseEffects(c, result, orig, {})
@ -1777,22 +1778,6 @@ proc semBlock(c: PContext, n: PNode): PNode =
closeScope(c) closeScope(c)
dec(c.p.nestedBlockCounter) dec(c.p.nestedBlockCounter)
proc buildCall(n: PNode): PNode =
if n.kind == nkDotExpr and n.len == 2:
# x.y --> y(x)
result = newNodeI(nkCall, n.info, 2)
result.sons[0] = n.sons[1]
result.sons[1] = n.sons[0]
elif n.kind in nkCallKinds and n.sons[0].kind == nkDotExpr:
# x.y(a) -> y(x, a)
let a = n.sons[0]
result = newNodeI(nkCall, n.info, n.len+1)
result.sons[0] = a.sons[1]
result.sons[1] = a.sons[0]
for i in 1 .. <n.len: result.sons[i+1] = n.sons[i]
else:
result = n
proc doBlockIsStmtList(n: PNode): bool = proc doBlockIsStmtList(n: PNode): bool =
result = n.kind == nkDo and result = n.kind == nkDo and
n[paramsPos].sonsLen == 1 and n[paramsPos].sonsLen == 1 and
@ -1844,8 +1829,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
result = symChoice(c, n, s, scClosed) result = symChoice(c, n, s, scClosed)
if result.kind == nkSym: if result.kind == nkSym:
markIndirect(c, result.sym) markIndirect(c, result.sym)
if isGenericRoutine(result.sym): # if isGenericRoutine(result.sym):
localError(n.info, errInstantiateXExplicitely, s.name.s) # localError(n.info, errInstantiateXExplicitely, s.name.s)
of nkSym: of nkSym:
# because of the changed symbol binding, this does not mean that we # because of the changed symbol binding, this does not mean that we
# don't have to check the symbol for semantics here again! # don't have to check the symbol for semantics here again!
@ -1901,7 +1886,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit:
# check if it is an expression macro: # check if it is an expression macro:
checkMinSonsLen(n, 1) checkMinSonsLen(n, 1)
let mode = if nfDelegate in n.flags: {} else: {checkUndeclared} let mode = if nfDotField in n.flags: {} else: {checkUndeclared}
var s = qualifiedLookUp(c, n.sons[0], mode) var s = qualifiedLookUp(c, n.sons[0], mode)
if s != nil: if s != nil:
if gCmd == cmdPretty and n.sons[0].kind == nkDotExpr: if gCmd == cmdPretty and n.sons[0].kind == nkDotExpr:
@ -1940,7 +1925,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
# the 'newSeq[T](x)' bug # the 'newSeq[T](x)' bug
setGenericParams(c, n.sons[0]) setGenericParams(c, n.sons[0])
result = semDirectOp(c, n, flags) result = semDirectOp(c, n, flags)
elif isSymChoice(n.sons[0]) or nfDelegate in n.flags: elif isSymChoice(n.sons[0]) or nfDotField in n.flags:
result = semDirectOp(c, n, flags) result = semDirectOp(c, n, flags)
else: else:
result = semIndirectOp(c, n, flags) result = semIndirectOp(c, n, flags)

View file

@ -764,6 +764,29 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
s.ast = a s.ast = a
popOwner() popOwner()
proc checkForMetaFields(n: PNode) =
template checkMeta(t) =
if t != nil and t.isMetaType and tfGenericTypeParam notin t.flags:
localError(n.info, errTIsNotAConcreteType, t.typeToString)
case n.kind
of nkRecList, nkRecCase:
for s in n: checkForMetaFields(s)
of nkOfBranch, nkElse:
checkForMetaFields(n.lastSon)
of nkSym:
let t = n.sym.typ
case t.kind
of tySequence, tySet, tyArray, tyOpenArray, tyVar, tyPtr, tyRef,
tyProc, tyGenericInvokation, tyGenericInst:
let start = ord(t.kind in {tyGenericInvokation, tyGenericInst})
for i in start .. <t.sons.len:
checkMeta(t.sons[i])
else:
checkMeta(t)
else:
internalAssert false
proc typeSectionFinalPass(c: PContext, n: PNode) = proc typeSectionFinalPass(c: PContext, n: PNode) =
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
var a = n.sons[i] var a = n.sons[i]
@ -780,6 +803,8 @@ proc typeSectionFinalPass(c: PContext, n: PNode) =
assignType(s.typ, t) assignType(s.typ, t)
s.typ.id = t.id # same id s.typ.id = t.id # same id
checkConstructedType(s.info, s.typ) checkConstructedType(s.info, s.typ)
if s.typ.kind in {tyObject, tyTuple}:
checkForMetaFields(s.typ.n)
let aa = a.sons[2] let aa = a.sons[2]
if aa.kind in {nkRefTy, nkPtrTy} and aa.len == 1 and if aa.kind in {nkRefTy, nkPtrTy} and aa.len == 1 and
aa.sons[0].kind == nkObjectTy: aa.sons[0].kind == nkObjectTy:
@ -883,12 +908,19 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
s = n[namePos].sym s = n[namePos].sym
pushOwner(s) pushOwner(s)
openScope(c) openScope(c)
if n.sons[genericParamsPos].kind != nkEmpty: var gp: PNode
illFormedAst(n) # process parameters: if n.sons[genericParamsPos].kind != nkEmpty:
n.sons[genericParamsPos] = semGenericParamList(c, n.sons[genericParamsPos])
gp = n.sons[genericParamsPos]
else:
gp = newNodeI(nkGenericParams, n.info)
if n.sons[paramsPos].kind != nkEmpty: if n.sons[paramsPos].kind != nkEmpty:
var gp = newNodeI(nkGenericParams, n.info)
semParamList(c, n.sons[paramsPos], gp, s) semParamList(c, n.sons[paramsPos], gp, s)
paramsTypeCheck(c, s.typ) # paramsTypeCheck(c, s.typ)
if sonsLen(gp) > 0 and n.sons[genericParamsPos].kind == nkEmpty:
# we have a list of implicit type parameters:
n.sons[genericParamsPos] = gp
else: else:
s.typ = newTypeS(tyProc, c) s.typ = newTypeS(tyProc, c)
rawAddSon(s.typ, nil) rawAddSon(s.typ, nil)
@ -900,12 +932,13 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
localError(n.sons[bodyPos].info, errImplOfXNotAllowed, s.name.s) localError(n.sons[bodyPos].info, errImplOfXNotAllowed, s.name.s)
#if efDetermineType notin flags: #if efDetermineType notin flags:
# XXX not good enough; see tnamedparamanonproc.nim # XXX not good enough; see tnamedparamanonproc.nim
pushProcCon(c, s) if n.sons[genericParamsPos].kind == nkEmpty:
addResult(c, s.typ.sons[0], n.info, skProc) pushProcCon(c, s)
let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos])) addResult(c, s.typ.sons[0], n.info, skProc)
n.sons[bodyPos] = transformBody(c.module, semBody, s) let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos]))
addResultNode(c, n) n.sons[bodyPos] = transformBody(c.module, semBody, s)
popProcCon(c) addResultNode(c, n)
popProcCon(c)
sideEffectsCheck(c, s) sideEffectsCheck(c, s)
else: else:
localError(n.info, errImplOfXexpected, s.name.s) localError(n.info, errImplOfXexpected, s.name.s)
@ -913,6 +946,34 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
popOwner() popOwner()
result.typ = s.typ result.typ = s.typ
proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode =
var n = n
n = replaceTypesInBody(c, pt, n)
result = n
n.sons[genericParamsPos] = emptyNode
n.sons[paramsPos] = n.typ.n
openScope(c)
var s = n.sons[namePos].sym
addParams(c, n.typ.n, skProc)
pushProcCon(c, s)
addResult(c, n.typ.sons[0], n.info, skProc)
let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos]))
n.sons[bodyPos] = transformBody(c.module, semBody, n.sons[namePos].sym)
addResultNode(c, n)
popProcCon(c)
closeScope(c)
s.ast = result
# alternative variant (not quite working):
# var prc = arg[0].sym
# let inferred = c.semGenerateInstance(c, prc, m.bindings, arg.info)
# result = inferred.ast
# result.kind = arg.kind
proc activate(c: PContext, n: PNode) = proc activate(c: PContext, n: PNode) =
# XXX: This proc is part of my plan for getting rid of # XXX: This proc is part of my plan for getting rid of
# forward declarations. stay tuned. # forward declarations. stay tuned.
@ -1263,8 +1324,8 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
let (outer, inner) = insertDestructors(c, n.sons[i]) let (outer, inner) = insertDestructors(c, n.sons[i])
if outer != nil: if outer != nil:
n.sons[i] = outer n.sons[i] = outer
for j in countup(i+1, length-1): var rest = newNode(nkStmtList, n.info, n.sons[i+1 .. length-1])
inner.addSon(semStmt(c, n.sons[j])) inner.addSon(semStmtList(c, rest, flags))
n.sons.setLen(i+1) n.sons.setLen(i+1)
return return
of LastBlockStmts: of LastBlockStmts:

View file

@ -1030,9 +1030,8 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
if s.kind != skError: localError(n.info, errTypeExpected) if s.kind != skError: localError(n.info, errTypeExpected)
result = newOrPrevType(tyError, prev, c) result = newOrPrevType(tyError, prev, c)
elif s.kind == skParam and s.typ.kind == tyTypeDesc: elif s.kind == skParam and s.typ.kind == tyTypeDesc:
assert s.typ.len > 0 internalAssert s.typ.base.kind != tyNone and prev == nil
internalAssert prev == nil result = s.typ.base
result = s.typ.sons[0]
elif prev == nil: elif prev == nil:
result = s.typ result = s.typ
else: else:

View file

@ -29,6 +29,7 @@ proc checkConstructedType*(info: TLineInfo, typ: PType) =
localError(info, errVarVarTypeNotAllowed) localError(info, errVarVarTypeNotAllowed)
elif computeSize(t) == szIllegalRecursion: elif computeSize(t) == szIllegalRecursion:
localError(info, errIllegalRecursionInTypeX, typeToString(t)) localError(info, errIllegalRecursionInTypeX, typeToString(t))
when false: when false:
if t.kind == tyObject and t.sons[0] != nil: if t.kind == tyObject and t.sons[0] != nil:
if t.sons[0].kind != tyObject or tfFinal in t.sons[0].flags: if t.sons[0].kind != tyObject or tfFinal in t.sons[0].flags:
@ -409,14 +410,22 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
else: discard else: discard
proc initTypeVars(p: PContext, pt: TIdTable, info: TLineInfo): TReplTypeVars =
initIdTable(result.symMap)
copyIdTable(result.typeMap, pt)
initIdTable(result.localCache)
result.info = info
result.c = p
proc replaceTypesInBody*(p: PContext, pt: TIdTable, n: PNode): PNode =
var cl = initTypeVars(p, pt, n.info)
pushInfoContext(n.info)
result = replaceTypeVarsN(cl, n)
popInfoContext()
proc generateTypeInstance*(p: PContext, pt: TIdTable, info: TLineInfo, proc generateTypeInstance*(p: PContext, pt: TIdTable, info: TLineInfo,
t: PType): PType = t: PType): PType =
var cl: TReplTypeVars var cl = initTypeVars(p, pt, info)
initIdTable(cl.symMap)
copyIdTable(cl.typeMap, pt)
initIdTable(cl.localCache)
cl.info = info
cl.c = p
pushInfoContext(info) pushInfoContext(info)
result = replaceTypeVarsT(cl, t) result = replaceTypeVarsT(cl, t)
popInfoContext() popInfoContext()

View file

@ -343,53 +343,57 @@ proc allowsNil(f: PType): TTypeRelation {.inline.} =
proc inconsistentVarTypes(f, a: PType): bool {.inline.} = proc inconsistentVarTypes(f, a: PType): bool {.inline.} =
result = f.kind != a.kind and (f.kind == tyVar or a.kind == tyVar) result = f.kind != a.kind and (f.kind == tyVar or a.kind == tyVar)
proc procParamTypeRel(c: var TCandidate, f, a: PType, proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
result: var TTypeRelation) = var f = f
var
m: TTypeRelation
f = f
if a.isMetaType: if a.isMetaType:
if f.isMetaType: if f.isMetaType:
# we are matching a generic proc (as proc param) # We are matching a generic proc (as proc param)
# to another generic type appearing in the proc # to another generic type appearing in the proc
# sigunature. there is a change that the target # signature. There is a change that the target
# type is already fully-determined, so we are # type is already fully-determined, so we are
# going to try resolve it # going to try resolve it
f = generateTypeInstance(c.c, c.bindings, c.call.info, f) f = generateTypeInstance(c.c, c.bindings, c.call.info, f)
if f == nil or f.isMetaType: if f == nil or f.isMetaType:
# no luck resolving the type, so the inference fails # no luck resolving the type, so the inference fails
result = isNone return isNone
return
let reverseRel = typeRel(c, a, f) let reverseRel = typeRel(c, a, f)
if reverseRel == isGeneric: if reverseRel == isGeneric:
m = isInferred result = isInferred
inc c.genericMatches
else: else:
m = typeRel(c, f, a) result = typeRel(c, f, a)
if m <= isSubtype or inconsistentVarTypes(f, a): if result <= isSubtype or inconsistentVarTypes(f, a):
result = isNone result = isNone
return
else: if result == isEqual:
result = minRel(m, result) inc c.exactMatches
proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
case a.kind case a.kind
of tyProc: of tyProc:
if sonsLen(f) != sonsLen(a): return if sonsLen(f) != sonsLen(a): return
# Note: We have to do unification for the parameters before the
# return type!
result = isEqual # start with maximum; also correct for no result = isEqual # start with maximum; also correct for no
# params at all # params at all
for i in countup(1, sonsLen(f)-1):
procParamTypeRel(c, f.sons[i], a.sons[i], result) template checkParam(f, a) =
result = minRel(result, procParamTypeRel(c, f, a))
if result == isNone: return
# Note: We have to do unification for the parameters before the
# return type!
for i in 1 .. <f.sonsLen:
checkParam(f.sons[i], a.sons[i])
if f.sons[0] != nil: if f.sons[0] != nil:
if a.sons[0] != nil: if a.sons[0] != nil:
procParamTypeRel(c, f.sons[0], a.sons[0], result) checkParam(f.sons[0], a.sons[0])
else: else:
return isNone return isNone
elif a.sons[0] != nil: elif a.sons[0] != nil:
return isNone return isNone
if tfNoSideEffect in f.flags and tfNoSideEffect notin a.flags: if tfNoSideEffect in f.flags and tfNoSideEffect notin a.flags:
return isNone return isNone
elif tfThread in f.flags and a.flags * {tfThread, tfNoSideEffect} == {}: elif tfThread in f.flags and a.flags * {tfThread, tfNoSideEffect} == {}:
@ -866,7 +870,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
else: else:
internalAssert a.sons != nil and a.sons.len > 0 internalAssert a.sons != nil and a.sons.len > 0
c.typedescMatched = true c.typedescMatched = true
result = typeRel(c, f.sons[0], a.sons[0]) result = typeRel(c, f.base, a.base)
else: else:
result = isNone result = isNone
else: else:
@ -896,22 +900,20 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
result = isNone result = isNone
of tyTypeDesc: of tyTypeDesc:
if a.kind != tyTypeDesc: return isNone
var prev = PType(idTableGet(c.bindings, f)) var prev = PType(idTableGet(c.bindings, f))
if prev == nil: if prev == nil:
if a.kind == tyTypeDesc: if f.base.kind == tyNone:
if f.sons[0].kind == tyNone: result = isGeneric
result = isGeneric
else:
result = typeRel(c, f.sons[0], a.sons[0])
if result != isNone:
put(c.bindings, f, a)
else: else:
result = isNone result = typeRel(c, f.base, a.base)
if result != isNone:
put(c.bindings, f, a)
else: else:
internalAssert prev.sonsLen == 1
let toMatch = if tfUnresolved in f.flags: a let toMatch = if tfUnresolved in f.flags: a
else: a.sons[0] else: a.base
result = typeRel(c, prev.sons[0], toMatch) result = typeRel(c, prev.base, toMatch)
of tyStmt: of tyStmt:
result = isGeneric result = isGeneric
@ -1015,7 +1017,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
argType = arg.typ argType = arg.typ
var var
a = if c.inTypeClass > 0: argType.skipTypes({tyTypeDesc}) a = if c.inTypeClass > 0: argType.skipTypes({tyTypeDesc, tyFieldAccessor})
else: argType else: argType
r = typeRel(m, f, a) r = typeRel(m, f, a)
@ -1037,10 +1039,11 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
#result = copyTree(arg) #result = copyTree(arg)
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c)
of isInferred, isInferredConvertible: of isInferred, isInferredConvertible:
var prc = if arg.kind in nkLambdaKinds: arg[0].sym if arg.kind in {nkProcDef, nkIteratorDef} + nkLambdaKinds:
else: arg.sym result = c.semInferredLambda(c, m.bindings, arg)
let inferred = c.semGenerateInstance(c, prc, m.bindings, arg.info) else:
result = newSymNode(inferred, arg.info) let inferred = c.semGenerateInstance(c, arg.sym, m.bindings, arg.info)
result = newSymNode(inferred, arg.info)
if r == isInferredConvertible: if r == isInferredConvertible:
result = implicitConv(nkHiddenStdConv, f, result, m, c) result = implicitConv(nkHiddenStdConv, f, result, m, c)
of isGeneric: of isGeneric:
@ -1103,6 +1106,7 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
# incorrect to simply use the first fitting match. However, to implement # incorrect to simply use the first fitting match. However, to implement
# this correctly is inefficient. We have to copy `m` here to be able to # this correctly is inefficient. We have to copy `m` here to be able to
# roll back the side effects of the unification algorithm. # roll back the side effects of the unification algorithm.
let c = m.c let c = m.c
var x, y, z: TCandidate var x, y, z: TCandidate
initCandidate(c, x, m.callee) initCandidate(c, x, m.callee)
@ -1124,10 +1128,10 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
x.state = csMatch x.state = csMatch
of csMatch: of csMatch:
var cmp = cmpCandidates(x, z) var cmp = cmpCandidates(x, z)
if cmp < 0: if cmp < 0:
best = i best = i
x = z x = z
elif cmp == 0: elif cmp == 0:
y = z # z is as good as x y = z # z is as good as x
if x.state == csEmpty: if x.state == csEmpty:
result = nil result = nil

View file

@ -431,8 +431,8 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
add(result, typeToString(t.sons[i])) add(result, typeToString(t.sons[i]))
add(result, ']') add(result, ']')
of tyTypeDesc: of tyTypeDesc:
if t.len == 0: result = "typedesc" if t.base.kind == tyNone: result = "typedesc"
else: result = "typedesc[" & typeToString(t.sons[0]) & "]" else: result = "typedesc[" & typeToString(t.base) & "]"
of tyStatic: of tyStatic:
internalAssert t.len > 0 internalAssert t.len > 0
result = "static[" & typeToString(t.sons[0]) & "]" result = "static[" & typeToString(t.sons[0]) & "]"
@ -1042,7 +1042,8 @@ proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind,
of tyEmpty: of tyEmpty:
result = taField in flags result = taField in flags
of tyTypeClasses: of tyTypeClasses:
result = true result = tfGenericTypeParam in t.flags or
taField notin flags
of tyGenericBody, tyGenericParam, tyGenericInvokation, of tyGenericBody, tyGenericParam, tyGenericInvokation,
tyNone, tyForward, tyFromExpr, tyFieldAccessor: tyNone, tyForward, tyFromExpr, tyFieldAccessor:
result = false result = false
@ -1231,8 +1232,7 @@ proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt =
of tyGenericInst, tyDistinct, tyGenericBody, tyMutable, tyConst, tyIter: of tyGenericInst, tyDistinct, tyGenericBody, tyMutable, tyConst, tyIter:
result = computeSizeAux(lastSon(typ), a) result = computeSizeAux(lastSon(typ), a)
of tyTypeDesc: of tyTypeDesc:
result = if typ.len == 1: computeSizeAux(typ.sons[0], a) result = computeSizeAux(typ.base, a)
else: szUnknownSize
of tyForward: return szIllegalRecursion of tyForward: return szIllegalRecursion
else: else:
#internalError("computeSizeAux()") #internalError("computeSizeAux()")
@ -1258,7 +1258,7 @@ proc containsGenericTypeIter(t: PType, closure: PObject): bool =
return true return true
if t.kind == tyTypeDesc: if t.kind == tyTypeDesc:
if t.sons[0].kind == tyNone: return true if t.base.kind == tyNone: return true
if containsGenericTypeIter(t.base, closure): return true if containsGenericTypeIter(t.base, closure): return true
return false return false

View file

@ -4106,6 +4106,59 @@ types that will match the typedesc param:
The constraint can be a concrete type or a type class. The constraint can be a concrete type or a type class.
Special Operators
=================
dot operators
-------------
Nimrod offers a special family of dot operators that can be used to
intercept and rewrite proc call and field access attempts, referring
to previously undeclared symbol names. They can be used to provide a
fluent interface to objects lying outside the static confines of the
Nimrod's type system such as values from dynamic scripting languages
or dynamic file formats such as JSON or XML.
When Nimrod encounters an expression that cannot be resolved by the
standard overload resolution rules, the current scope will be searched
for a dot operator that can be matched against a re-written form of
the expression, where the unknown field or proc name is converted to
an additional static string parameter:
.. code-block:: nimrod
a.b # becomes `.`(a, "b")
a.b(c, d) # becomes `.`(a, "b", c, d)
The matched dot operators can be symbols of any callable kind (procs,
templates and macros), depending on the desired effect:
.. code-block:: nimrod
proc `.` (js: PJsonNode, field: string): JSON = js[field]
var js = parseJson("{ x: 1, y: 2}")
echo js.x # outputs 1
echo js.y # outputs 2
The following dot operators are available:
operator `.`
------------
This operator will be matched against both field accesses and method calls.
operator `.()`
---------------
This operator will be matched exclusively against method calls. It has higher
precedence than the `.` operator and this allows you to handle expressions like
`x.y` and `x.y()` differently if you are interfacing with a scripting language
for example.
operator `.=`
-------------
This operator will be matched against assignments to missing fields.
.. code-block:: nimrod
a.b = c # becomes `.=`(a, "b", c)
Term rewriting macros Term rewriting macros
===================== =====================
@ -4758,42 +4811,6 @@ This may change in future versions of language, but for now use
the ``finalizer`` parameter to ``new``. the ``finalizer`` parameter to ``new``.
delegator pragma
----------------
**Note**: The design of the delegator feature is subject to change.
The delegator pragma can be used to intercept and rewrite proc call and field
access attempts referring to previously undeclared symbol names. It can be used
to provide a fluent interface to objects lying outside the static confines of
the Nimrod's type system such as values from dynamic scripting languages or
dynamic file formats such as JSON or XML.
A delegator is a special form of the `()` operator marked with the delagator
pragma. When Nimrod encounters an expression that cannot be resolved by the
standard overload resolution, any delegators in the current scope will be
matched against a rewritten form of the expression following the standard
signature matching rules. In the rewritten expression, the name of the unknown
proc or field name is inserted as an additional static string parameter always
appearing in the leading position:
.. code-block:: nimrod
a.b => delegator("b", a)
a.b(c, d) => delegator("b", a, c)
a b, c, d => delegator("a", b, c, d)
The delegators can be any callable symbol type (procs, templates, macros)
depending on the desired effect:
.. code-block:: nimrod
proc `()` (field: string, js: PJsonNode): JSON {.delegator.} = js[field]
var js = parseJson("{ x: 1, y: 2}")
echo js.x # outputs 1
echo js.y # outputs 2
procvar pragma procvar pragma
-------------- --------------
The `procvar`:idx: pragma is used to mark a proc that it can be passed to a The `procvar`:idx: pragma is used to mark a proc that it can be passed to a

View file

@ -36,7 +36,7 @@ block mainLoop:
case x.kind case x.kind
of xmlEof: break mainLoop of xmlEof: break mainLoop
of xmlElementClose: break of xmlElementClose: break
else: nil else: discard
x.next() # skip ``xmlElementClose`` x.next() # skip ``xmlElementClose``
# now we have the description for the ``a`` element # now we have the description for the ``a`` element
var desc = "" var desc = ""

View file

@ -7,7 +7,7 @@
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
import os, oids, tables, strutils import os, oids, tables, strutils, macros
import winlean import winlean
@ -23,14 +23,13 @@ import sockets2, net
# -- Futures # -- Futures
type type
PFutureVoid* = ref object of PObject PFutureBase* = ref object of PObject
cbVoid: proc () {.closure.} cb: proc () {.closure.}
finished: bool finished: bool
PFuture*[T] = ref object of PFutureVoid PFuture*[T] = ref object of PFutureBase
value: T value: T
error: ref EBase error: ref EBase
cb: proc (future: PFuture[T]) {.closure.}
proc newFuture*[T](): PFuture[T] = proc newFuture*[T](): PFuture[T] =
## Creates a new future. ## Creates a new future.
@ -39,42 +38,38 @@ proc newFuture*[T](): PFuture[T] =
proc complete*[T](future: PFuture[T], val: T) = proc complete*[T](future: PFuture[T], val: T) =
## Completes ``future`` with value ``val``. ## Completes ``future`` with value ``val``.
assert(not future.finished) assert(not future.finished, "Future already finished, cannot finish twice.")
assert(future.error == nil) assert(future.error == nil)
future.value = val future.value = val
future.finished = true future.finished = true
if future.cb != nil: if future.cb != nil:
future.cb(future) future.cb()
if future.cbVoid != nil:
future.cbVoid()
proc fail*[T](future: PFuture[T], error: ref EBase) = proc fail*[T](future: PFuture[T], error: ref EBase) =
## Completes ``future`` with ``error``. ## Completes ``future`` with ``error``.
assert(not future.finished) assert(not future.finished, "Future already finished, cannot finish twice.")
future.finished = true future.finished = true
future.error = error future.error = error
if future.cb != nil: if future.cb != nil:
future.cb(future) future.cb()
proc `callback=`*(future: PFutureBase, cb: proc () {.closure.}) =
## Sets the callback proc to be called when the future completes.
##
## If future has already completed then ``cb`` will be called immediately.
##
## **Note**: You most likely want the other ``callback`` setter which
## passes ``future`` as a param to the callback.
future.cb = cb
if future.finished:
future.cb()
proc `callback=`*[T](future: PFuture[T], proc `callback=`*[T](future: PFuture[T],
cb: proc (future: PFuture[T]) {.closure.}) = cb: proc (future: PFuture[T]) {.closure.}) =
## Sets the callback proc to be called when the future completes. ## Sets the callback proc to be called when the future completes.
## ##
## If future has already completed then ``cb`` will be called immediately. ## If future has already completed then ``cb`` will be called immediately.
future.cb = cb future.callback = proc () = cb(future)
if future.finished:
future.cb(future)
proc `callbackVoid=`*(future: PFutureVoid, cb: proc () {.closure.}) =
## Sets the **void** callback proc to be called when the future completes.
##
## If future has already completed then ``cb`` will be called immediately.
##
## **Note**: This is used for the ``await`` functionality, you most likely
## want to use ``callback``.
future.cbVoid = cb
if future.finished:
future.cbVoid()
proc read*[T](future: PFuture[T]): T = proc read*[T](future: PFuture[T]): T =
## Retrieves the value of ``future``. Future must be finished otherwise ## Retrieves the value of ``future``. Future must be finished otherwise
@ -104,10 +99,12 @@ when defined(windows):
TCompletionData* = object TCompletionData* = object
sock: TSocketHandle sock: TSocketHandle
cb: proc (sock: TSocketHandle, errcode: TOSErrorCode) {.closure.} cb: proc (sock: TSocketHandle, bytesTransferred: DWORD,
errcode: TOSErrorCode) {.closure.}
PDispatcher* = ref object PDispatcher* = ref object
ioPort: THandle ioPort: THandle
hasHandles: bool
TCustomOverlapped = object TCustomOverlapped = object
Internal*: DWORD Internal*: DWORD
@ -129,9 +126,13 @@ when defined(windows):
if CreateIOCompletionPort(sock.THandle, p.ioPort, if CreateIOCompletionPort(sock.THandle, p.ioPort,
cast[TCompletionKey](sock), 1) == 0: cast[TCompletionKey](sock), 1) == 0:
OSError(OSLastError()) OSError(OSLastError())
p.hasHandles = true
proc poll*(p: PDispatcher, timeout = 500) = proc poll*(p: PDispatcher, timeout = 500) =
## Waits for completion events and processes them. ## Waits for completion events and processes them.
if not p.hasHandles:
raise newException(EInvalidValue, "No handles registered in dispatcher.")
let llTimeout = let llTimeout =
if timeout == -1: winlean.INFINITE if timeout == -1: winlean.INFINITE
else: timeout.int32 else: timeout.int32
@ -145,16 +146,19 @@ when defined(windows):
# TODO: http://www.serverframework.com/handling-multiple-pending-socket-read-and-write-operations.html # TODO: http://www.serverframework.com/handling-multiple-pending-socket-read-and-write-operations.html
var customOverlapped = cast[PCustomOverlapped](lpOverlapped) var customOverlapped = cast[PCustomOverlapped](lpOverlapped)
if res: if res:
# This is useful for ensuring the reliability of the overlapped struct.
assert customOverlapped.data.sock == lpCompletionKey.TSocketHandle assert customOverlapped.data.sock == lpCompletionKey.TSocketHandle
customOverlapped.data.cb(customOverlapped.data.sock, TOSErrorCode(-1)) customOverlapped.data.cb(customOverlapped.data.sock,
lpNumberOfBytesTransferred, TOSErrorCode(-1))
dealloc(customOverlapped) dealloc(customOverlapped)
else: else:
let errCode = OSLastError() let errCode = OSLastError()
if lpOverlapped != nil: if lpOverlapped != nil:
assert customOverlapped.data.sock == lpCompletionKey.TSocketHandle assert customOverlapped.data.sock == lpCompletionKey.TSocketHandle
customOverlapped.data.cb(customOverlapped.data.sock,
lpNumberOfBytesTransferred, errCode)
dealloc(customOverlapped) dealloc(customOverlapped)
customOverlapped.data.cb(customOverlapped.data.sock, errCode)
else: else:
if errCode.int32 == WAIT_TIMEOUT: if errCode.int32 == WAIT_TIMEOUT:
# Timed out # Timed out
@ -252,11 +256,12 @@ when defined(windows):
# http://blogs.msdn.com/b/oldnewthing/archive/2011/02/02/10123392.aspx # http://blogs.msdn.com/b/oldnewthing/archive/2011/02/02/10123392.aspx
var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped))) var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped)))
ol.data = TCompletionData(sock: socket, cb: ol.data = TCompletionData(sock: socket, cb:
proc (sock: TSocketHandle, errcode: TOSErrorCode) = proc (sock: TSocketHandle, bytesCount: DWord, errcode: TOSErrorCode) =
if errcode == TOSErrorCode(-1): if not retFuture.finished:
retFuture.complete(0) if errcode == TOSErrorCode(-1):
else: retFuture.complete(0)
retFuture.fail(newException(EOS, osErrorMsg(errcode))) else:
retFuture.fail(newException(EOS, osErrorMsg(errcode)))
) )
var ret = connectEx(socket, it.ai_addr, sizeof(TSockAddrIn).cint, var ret = connectEx(socket, it.ai_addr, sizeof(TSockAddrIn).cint,
@ -265,7 +270,9 @@ when defined(windows):
# Request to connect completed immediately. # Request to connect completed immediately.
success = true success = true
retFuture.complete(0) retFuture.complete(0)
dealloc(ol) # We don't deallocate ``ol`` here because even though this completed
# immediately poll will still be notified about its completion and it will
# free ``ol``.
break break
else: else:
lastError = OSLastError() lastError = OSLastError()
@ -283,7 +290,8 @@ when defined(windows):
retFuture.fail(newException(EOS, osErrorMsg(lastError))) retFuture.fail(newException(EOS, osErrorMsg(lastError)))
return retFuture return retFuture
proc recv*(p: PDispatcher, socket: TSocketHandle, size: int): PFuture[string] = proc recv*(p: PDispatcher, socket: TSocketHandle, size: int,
flags: int = 0): PFuture[string] =
## Reads ``size`` bytes from ``socket``. Returned future will complete once ## Reads ``size`` bytes from ``socket``. Returned future will complete once
## all of the requested data is read. ## all of the requested data is read.
@ -293,31 +301,50 @@ when defined(windows):
dataBuf.buf = newString(size) dataBuf.buf = newString(size)
dataBuf.len = size dataBuf.len = size
var bytesReceived, flags: DWord var bytesReceived: DWord
var flagsio = flags.dword
var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped))) var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped)))
ol.data = TCompletionData(sock: socket, cb: ol.data = TCompletionData(sock: socket, cb:
proc (sock: TSocketHandle, errcode: TOSErrorCode) = proc (sock: TSocketHandle, bytesCount: DWord, errcode: TOSErrorCode) =
if errcode == TOSErrorCode(-1): if not retFuture.finished:
var data = newString(size) if errcode == TOSErrorCode(-1):
copyMem(addr data[0], addr dataBuf.buf[0], size) if bytesCount == 0 and dataBuf.buf[0] == '\0':
retFuture.complete($data) retFuture.complete("")
else: else:
retFuture.fail(newException(EOS, osErrorMsg(errcode))) var data = newString(size)
copyMem(addr data[0], addr dataBuf.buf[0], size)
retFuture.complete($data)
else:
retFuture.fail(newException(EOS, osErrorMsg(errcode)))
) )
let ret = WSARecv(socket, addr dataBuf, 1, addr bytesReceived, let ret = WSARecv(socket, addr dataBuf, 1, addr bytesReceived,
addr flags, cast[POverlapped](ol), nil) addr flagsio, cast[POverlapped](ol), nil)
if ret == -1: if ret == -1:
let err = OSLastError() let err = OSLastError()
if err.int32 != ERROR_IO_PENDING: if err.int32 != ERROR_IO_PENDING:
retFuture.fail(newException(EOS, osErrorMsg(err))) retFuture.fail(newException(EOS, osErrorMsg(err)))
dealloc(ol) dealloc(ol)
elif ret == 0 and bytesReceived == 0 and dataBuf.buf[0] == '\0':
# We have to ensure that the buffer is empty because WSARecv will tell
# us immediatelly when it was disconnected, even when there is still
# data in the buffer.
# We want to give the user as much data as we can. So we only return
# the empty string (which signals a disconnection) when there is
# nothing left to read.
retFuture.complete("")
# TODO: "For message-oriented sockets, where a zero byte message is often
# allowable, a failure with an error code of WSAEDISCON is used to
# indicate graceful closure."
# ~ http://msdn.microsoft.com/en-us/library/ms741688%28v=vs.85%29.aspx
else: else:
# Request to read completed immediately. # Request to read completed immediately.
var data = newString(size) var data = newString(size)
copyMem(addr data[0], addr dataBuf.buf[0], size) copyMem(addr data[0], addr dataBuf.buf[0], size)
retFuture.complete($data) retFuture.complete($data)
dealloc(ol) # We don't deallocate ``ol`` here because even though this completed
# immediately poll will still be notified about its completion and it will
# free ``ol``.
return retFuture return retFuture
proc send*(p: PDispatcher, socket: TSocketHandle, data: string): PFuture[int] = proc send*(p: PDispatcher, socket: TSocketHandle, data: string): PFuture[int] =
@ -332,11 +359,12 @@ when defined(windows):
var bytesReceived, flags: DWord var bytesReceived, flags: DWord
var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped))) var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped)))
ol.data = TCompletionData(sock: socket, cb: ol.data = TCompletionData(sock: socket, cb:
proc (sock: TSocketHandle, errcode: TOSErrorCode) = proc (sock: TSocketHandle, bytesCount: DWord, errcode: TOSErrorCode) =
if errcode == TOSErrorCode(-1): if not retFuture.finished:
retFuture.complete(0) if errcode == TOSErrorCode(-1):
else: retFuture.complete(0)
retFuture.fail(newException(EOS, osErrorMsg(errcode))) else:
retFuture.fail(newException(EOS, osErrorMsg(errcode)))
) )
let ret = WSASend(socket, addr dataBuf, 1, addr bytesReceived, let ret = WSASend(socket, addr dataBuf, 1, addr bytesReceived,
@ -348,7 +376,9 @@ when defined(windows):
dealloc(ol) dealloc(ol)
else: else:
retFuture.complete(0) retFuture.complete(0)
dealloc(ol) # We don't deallocate ``ol`` here because even though this completed
# immediately poll will still be notified about its completion and it will
# free ``ol``.
return retFuture return retFuture
proc acceptAddr*(p: PDispatcher, socket: TSocketHandle): proc acceptAddr*(p: PDispatcher, socket: TSocketHandle):
@ -390,11 +420,12 @@ when defined(windows):
var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped))) var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped)))
ol.data = TCompletionData(sock: socket, cb: ol.data = TCompletionData(sock: socket, cb:
proc (sock: TSocketHandle, errcode: TOSErrorCode) = proc (sock: TSocketHandle, bytesCount: DWord, errcode: TOSErrorCode) =
if errcode == TOSErrorCode(-1): if not retFuture.finished:
completeAccept() if errcode == TOSErrorCode(-1):
else: completeAccept()
retFuture.fail(newException(EOS, osErrorMsg(errcode))) else:
retFuture.fail(newException(EOS, osErrorMsg(errcode)))
) )
# http://msdn.microsoft.com/en-us/library/windows/desktop/ms737524%28v=vs.85%29.aspx # http://msdn.microsoft.com/en-us/library/windows/desktop/ms737524%28v=vs.85%29.aspx
@ -411,7 +442,9 @@ when defined(windows):
dealloc(ol) dealloc(ol)
else: else:
completeAccept() completeAccept()
dealloc(ol) # We don't deallocate ``ol`` here because even though this completed
# immediately poll will still be notified about its completion and it will
# free ``ol``.
return retFuture return retFuture
@ -434,6 +467,199 @@ when defined(windows):
else: else:
# TODO: Selectors. # TODO: Selectors.
# -- Await Macro
template createCb*(cbName, varNameIterSym, retFutureSym: expr): stmt {.immediate, dirty.} =
proc cbName {.closure.} =
if not varNameIterSym.finished:
var next = varNameIterSym()
if next == nil:
assert retFutureSym.finished, "Async procedure's return Future was not finished."
else:
next.callback = cbName
template createVar(futSymName: string, asyncProc: PNimrodNode,
valueReceiver: expr) {.immediate, dirty.} =
# TODO: Used template here due to bug #926
result = newNimNode(nnkStmtList)
var futSym = newIdentNode(futSymName) #genSym(nskVar, "future")
result.add newVarStmt(futSym, asyncProc) # -> var future<x> = y
result.add newNimNode(nnkYieldStmt).add(futSym) # -> yield future<x>
valueReceiver = newDotExpr(futSym, newIdentNode("read")) # -> future<x>.read
proc processBody(node, retFutureSym: PNimrodNode): PNimrodNode {.compileTime.} =
result = node
case node.kind
of nnkReturnStmt:
result = newNimNode(nnkStmtList)
result.add newCall(newIdentNode("complete"), retFutureSym,
if node[0].kind == nnkEmpty: newIdentNode("result") else: node[0])
result.add newNimNode(nnkYieldStmt).add(newNilLit())
of nnkCommand:
if node[0].ident == !"await":
case node[1].kind
of nnkIdent:
# await x
result = newNimNode(nnkYieldStmt).add(node[1]) # -> yield x
of nnkCall:
# await foo(p, x)
var futureValue: PNimrodNode
createVar("future" & $node[1][0].toStrLit, node[1], futureValue)
result.add futureValue
else:
error("Invalid node kind in 'await', got: " & $node[1].kind)
elif node[1].kind == nnkCommand and node[1][0].kind == nnkIdent and
node[1][0].ident == !"await":
# foo await x
var newCommand = node
createVar("future" & $node[0].ident, node[1][0], newCommand[1])
result.add newCommand
of nnkVarSection, nnkLetSection:
case node[0][2].kind
of nnkCommand:
if node[0][2][0].ident == !"await":
# var x = await y
var newVarSection = node # TODO: Should this use copyNimNode?
createVar("future" & $node[0][0].ident, node[0][2][1],
newVarSection[0][2])
result.add newVarSection
else: discard
of nnkAsgn:
case node[1].kind
of nnkCommand:
if node[1][0].ident == !"await":
# x = await y
var newAsgn = node
createVar("future" & $node[0].ident, node[1][1], newAsgn[1])
result.add newAsgn
else: discard
of nnkDiscardStmt:
# discard await x
if node[0][0].ident == !"await":
var dummy = newNimNode(nnkStmtList)
createVar("futureDiscard_" & $toStrLit(node[0][1]), node[0][1], dummy)
else: discard
for i in 0 .. <result.len:
result[i] = processBody(result[i], retFutureSym)
#echo(treeRepr(result))
proc getName(node: PNimrodNode): string {.compileTime.} =
case node.kind
of nnkPostfix:
return $node[1].ident
of nnkIdent:
return $node.ident
else:
assert false
macro async*(prc: stmt): stmt {.immediate.} =
expectKind(prc, nnkProcDef)
hint("Processing " & prc[0].getName & " as an async proc.")
# Verify that the return type is a PFuture[T]
if prc[3][0].kind == nnkIdent:
error("Expected return type of 'PFuture' got '" & $prc[3][0] & "'")
elif prc[3][0].kind == nnkBracketExpr:
if $prc[3][0][0] != "PFuture":
error("Expected return type of 'PFuture' got '" & $prc[3][0][0] & "'")
# TODO: Why can't I use genSym? I get illegal capture errors for Syms.
# TODO: It seems genSym is broken. Change all usages back to genSym when fixed
var outerProcBody = newNimNode(nnkStmtList)
# -> var retFuture = newFuture[T]()
var retFutureSym = newIdentNode("retFuture") #genSym(nskVar, "retFuture")
outerProcBody.add(
newVarStmt(retFutureSym,
newCall(
newNimNode(nnkBracketExpr).add(
newIdentNode("newFuture"),
prc[3][0][1])))) # Get type from return type of this proc.
# -> iterator nameIter(): PFutureBase {.closure.} =
# -> var result: T
# -> <proc_body>
# -> complete(retFuture, result)
var iteratorNameSym = newIdentNode($prc[0].getName & "Iter") #genSym(nskIterator, $prc[0].ident & "Iter")
var procBody = prc[6].processBody(retFutureSym)
procBody.insert(0, newNimNode(nnkVarSection).add(
newIdentDefs(newIdentNode("result"), prc[3][0][1]))) # -> var result: T
procBody.add(
newCall(newIdentNode("complete"),
retFutureSym, newIdentNode("result"))) # -> complete(retFuture, result)
var closureIterator = newProc(iteratorNameSym, [newIdentNode("PFutureBase")],
procBody, nnkIteratorDef)
closureIterator[4] = newNimNode(nnkPragma).add(newIdentNode("closure"))
outerProcBody.add(closureIterator)
# -> var nameIterVar = nameIter
# -> var first = nameIterVar()
var varNameIterSym = newIdentNode($prc[0].getName & "IterVar") #genSym(nskVar, $prc[0].ident & "IterVar")
var varNameIter = newVarStmt(varNameIterSym, iteratorNameSym)
outerProcBody.add varNameIter
var varFirstSym = genSym(nskVar, "first")
var varFirst = newVarStmt(varFirstSym, newCall(varNameIterSym))
outerProcBody.add varFirst
# -> createCb(cb, nameIter, retFuture)
var cbName = newIdentNode("cb")
var procCb = newCall("createCb", cbName, varNameIterSym, retFutureSym)
outerProcBody.add procCb
# -> first.callback = cb
outerProcBody.add newAssignment(
newDotExpr(varFirstSym, newIdentNode("callback")),
cbName)
# -> return retFuture
outerProcBody.add newNimNode(nnkReturnStmt).add(retFutureSym)
result = prc
# Remove the 'async' pragma.
for i in 0 .. <result[4].len:
if result[4][i].ident == !"async":
result[4].del(i)
result[6] = outerProcBody
echo(toStrLit(result))
proc recvLine*(p: PDispatcher, socket: TSocketHandle): PFuture[string] {.async.} =
## Reads a line of data from ``socket``. Returned future will complete once
## a full line is read or an error occurs.
##
## If a full line is read ``\r\L`` is not
## added to ``line``, however if solely ``\r\L`` is read then ``line``
## will be set to it.
##
## If the socket is disconnected, ``line`` will be set to ``""``.
template addNLIfEmpty(): stmt =
if result.len == 0:
result.add("\c\L")
result = ""
var c = ""
while true:
c = await p.recv(socket, 1)
if c.len == 0:
return
if c == "\r":
c = await p.recv(socket, 1, MSG_PEEK)
if c.len > 0 and c == "\L":
discard await p.recv(socket, 1)
addNLIfEmpty()
return
elif c == "\L":
addNLIfEmpty()
return
add(result.string, c)
when isMainModule: when isMainModule:
@ -442,39 +668,65 @@ when isMainModule:
#sock.setBlocking false #sock.setBlocking false
p.register(sock) p.register(sock)
when true:
var f = p.connect(sock, "irc.freenode.org", TPort(6667)) when false:
f.callback = # Await tests
proc (future: PFuture[int]) = proc main(p: PDispatcher): PFuture[int] {.async.} =
echo("Connected in future!") discard await p.connect(sock, "irc.freenode.net", TPort(6667))
echo(future.read) while true:
for i in 0 .. 50: var line = await p.recvLine(sock)
var recvF = p.recv(sock, 10) echo("Line is: ", line.repr)
recvF.callback = if line == "":
proc (future: PFuture[string]) = echo "Disconnected"
echo("Read: ", future.read) break
proc peekTest(p: PDispatcher): PFuture[int] {.async.} =
discard await p.connect(sock, "localhost", TPort(6667))
while true:
var line = await p.recv(sock, 1, MSG_PEEK)
var line2 = await p.recv(sock, 1)
echo(line.repr)
echo(line2.repr)
echo("---")
if line2 == "": break
sleep(500)
var f = main(p)
else: else:
when false:
sock.bindAddr(TPort(6667)) var f = p.connect(sock, "irc.freenode.org", TPort(6667))
sock.listen() f.callback =
proc onAccept(future: PFuture[TSocketHandle]) =
echo "Accepted"
var t = p.send(future.read, "test\c\L")
t.callback =
proc (future: PFuture[int]) = proc (future: PFuture[int]) =
echo("Connected in future!")
echo(future.read) echo(future.read)
for i in 0 .. 50:
var recvF = p.recv(sock, 10)
recvF.callback =
proc (future: PFuture[string]) =
echo("Read: ", future.read)
else:
sock.bindAddr(TPort(6667))
sock.listen()
proc onAccept(future: PFuture[TSocketHandle]) =
echo "Accepted"
var t = p.send(future.read, "test\c\L")
t.callback =
proc (future: PFuture[int]) =
echo(future.read)
var f = p.accept(sock)
f.callback = onAccept
var f = p.accept(sock) var f = p.accept(sock)
f.callback = onAccept f.callback = onAccept
var f = p.accept(sock)
f.callback = onAccept
while true: while true:
p.poll() p.poll()
echo "polled"

View file

@ -557,6 +557,119 @@ proc `$`*(m: TMonth): string =
"November", "December"] "November", "December"]
return lookup[m] return lookup[m]
proc format_token(info: TTimeInfo, token: string, buf: var string) =
## Helper of the format proc to parse individual tokens.
##
## Pass the found token in the user input string, and the buffer where the
## final string is being built. This has to be a var value because certain
## formatting tokens require modifying the previous characters.
case token
of "d":
buf.add($info.monthday)
of "dd":
if info.monthday < 10:
buf.add("0")
buf.add($info.monthday)
of "ddd":
buf.add(($info.weekday)[0 .. 2])
of "dddd":
buf.add($info.weekday)
of "h":
buf.add($(if info.hour > 12: info.hour - 12 else: info.hour))
of "hh":
let amerHour = if info.hour > 12: info.hour - 12 else: info.hour
if amerHour < 10:
buf.add('0')
buf.add($amerHour)
of "H":
buf.add($info.hour)
of "HH":
if info.hour < 10:
buf.add('0')
buf.add($info.hour)
of "m":
buf.add($info.minute)
of "mm":
if info.minute < 10:
buf.add('0')
buf.add($info.minute)
of "M":
buf.add($(int(info.month)+1))
of "MM":
if info.month < mOct:
buf.add('0')
buf.add($(int(info.month)+1))
of "MMM":
buf.add(($info.month)[0..2])
of "MMMM":
buf.add($info.month)
of "s":
buf.add($info.second)
of "ss":
if info.second < 10:
buf.add('0')
buf.add($info.second)
of "t":
if info.hour >= 12:
buf.add('P')
else: buf.add('A')
of "tt":
if info.hour >= 12:
buf.add("PM")
else: buf.add("AM")
of "y":
var fr = ($info.year).len()-1
if fr < 0: fr = 0
buf.add(($info.year)[fr .. ($info.year).len()-1])
of "yy":
var fr = ($info.year).len()-2
if fr < 0: fr = 0
var fyear = ($info.year)[fr .. ($info.year).len()-1]
if fyear.len != 2: fyear = repeatChar(2-fyear.len(), '0') & fyear
buf.add(fyear)
of "yyy":
var fr = ($info.year).len()-3
if fr < 0: fr = 0
var fyear = ($info.year)[fr .. ($info.year).len()-1]
if fyear.len != 3: fyear = repeatChar(3-fyear.len(), '0') & fyear
buf.add(fyear)
of "yyyy":
var fr = ($info.year).len()-4
if fr < 0: fr = 0
var fyear = ($info.year)[fr .. ($info.year).len()-1]
if fyear.len != 4: fyear = repeatChar(4-fyear.len(), '0') & fyear
buf.add(fyear)
of "yyyyy":
var fr = ($info.year).len()-5
if fr < 0: fr = 0
var fyear = ($info.year)[fr .. ($info.year).len()-1]
if fyear.len != 5: fyear = repeatChar(5-fyear.len(), '0') & fyear
buf.add(fyear)
of "z":
let hrs = (info.timezone div 60) div 60
buf.add($hrs)
of "zz":
let hrs = (info.timezone div 60) div 60
buf.add($hrs)
if hrs.abs < 10:
var atIndex = buf.len-(($hrs).len-(if hrs < 0: 1 else: 0))
buf.insert("0", atIndex)
of "zzz":
let hrs = (info.timezone div 60) div 60
buf.add($hrs & ":00")
if hrs.abs < 10:
var atIndex = buf.len-(($hrs & ":00").len-(if hrs < 0: 1 else: 0))
buf.insert("0", atIndex)
of "ZZZ":
buf.add(info.tzname)
of "":
discard
else:
raise newException(EInvalidValue, "Invalid format string: " & token)
proc format*(info: TTimeInfo, f: string): string = proc format*(info: TTimeInfo, f: string): string =
## This function formats `info` as specified by `f`. The following format ## This function formats `info` as specified by `f`. The following format
## specifiers are available: ## specifiers are available:
@ -591,8 +704,11 @@ proc format*(info: TTimeInfo, f: string): string =
## ZZZ Displays the name of the timezone. ``GMT -> GMT``, ``EST -> EST`` ## ZZZ Displays the name of the timezone. ``GMT -> GMT``, ``EST -> EST``
## ========== ================================================================================= ================================================ ## ========== ================================================================================= ================================================
## ##
## Other strings can be inserted by putting them in ``''``. For example ``hh'->'mm`` will give ``01->56``. ## Other strings can be inserted by putting them in ``''``. For example
## The following characters can be inserted without quoting them: ``:`` ``-`` ``(`` ``)`` ``/`` ``[`` ``]`` ``,`` ## ``hh'->'mm`` will give ``01->56``. The following characters can be
## inserted without quoting them: ``:`` ``-`` ``(`` ``)`` ``/`` ``[`` ``]``
## ``,``. However you don't need to necessarily separate format specifiers, a
## unambiguous format string like ``yyyyMMddhhmmss`` is valid too.
result = "" result = ""
var i = 0 var i = 0
@ -600,112 +716,8 @@ proc format*(info: TTimeInfo, f: string): string =
while true: while true:
case f[i] case f[i]
of ' ', '-', '/', ':', '\'', '\0', '(', ')', '[', ']', ',': of ' ', '-', '/', ':', '\'', '\0', '(', ')', '[', ']', ',':
case currentF format_token(info, currentF, result)
of "d":
result.add($info.monthday)
of "dd":
if info.monthday < 10:
result.add("0")
result.add($info.monthday)
of "ddd":
result.add(($info.weekday)[0 .. 2])
of "dddd":
result.add($info.weekday)
of "h":
result.add($(if info.hour > 12: info.hour - 12 else: info.hour))
of "hh":
let amerHour = if info.hour > 12: info.hour - 12 else: info.hour
if amerHour < 10:
result.add('0')
result.add($amerHour)
of "H":
result.add($info.hour)
of "HH":
if info.hour < 10:
result.add('0')
result.add($info.hour)
of "m":
result.add($info.minute)
of "mm":
if info.minute < 10:
result.add('0')
result.add($info.minute)
of "M":
result.add($(int(info.month)+1))
of "MM":
if info.month < mOct:
result.add('0')
result.add($(int(info.month)+1))
of "MMM":
result.add(($info.month)[0..2])
of "MMMM":
result.add($info.month)
of "s":
result.add($info.second)
of "ss":
if info.second < 10:
result.add('0')
result.add($info.second)
of "t":
if info.hour >= 12:
result.add('P')
else: result.add('A')
of "tt":
if info.hour >= 12:
result.add("PM")
else: result.add("AM")
of "y":
var fr = ($info.year).len()-1
if fr < 0: fr = 0
result.add(($info.year)[fr .. ($info.year).len()-1])
of "yy":
var fr = ($info.year).len()-2
if fr < 0: fr = 0
var fyear = ($info.year)[fr .. ($info.year).len()-1]
if fyear.len != 2: fyear = repeatChar(2-fyear.len(), '0') & fyear
result.add(fyear)
of "yyy":
var fr = ($info.year).len()-3
if fr < 0: fr = 0
var fyear = ($info.year)[fr .. ($info.year).len()-1]
if fyear.len != 3: fyear = repeatChar(3-fyear.len(), '0') & fyear
result.add(fyear)
of "yyyy":
var fr = ($info.year).len()-4
if fr < 0: fr = 0
var fyear = ($info.year)[fr .. ($info.year).len()-1]
if fyear.len != 4: fyear = repeatChar(4-fyear.len(), '0') & fyear
result.add(fyear)
of "yyyyy":
var fr = ($info.year).len()-5
if fr < 0: fr = 0
var fyear = ($info.year)[fr .. ($info.year).len()-1]
if fyear.len != 5: fyear = repeatChar(5-fyear.len(), '0') & fyear
result.add(fyear)
of "z":
let hrs = (info.timezone div 60) div 60
result.add($hrs)
of "zz":
let hrs = (info.timezone div 60) div 60
result.add($hrs)
if hrs.abs < 10:
var atIndex = result.len-(($hrs).len-(if hrs < 0: 1 else: 0))
result.insert("0", atIndex)
of "zzz":
let hrs = (info.timezone div 60) div 60
result.add($hrs & ":00")
if hrs.abs < 10:
var atIndex = result.len-(($hrs & ":00").len-(if hrs < 0: 1 else: 0))
result.insert("0", atIndex)
of "ZZZ":
result.add(info.tzname)
of "":
discard
else:
raise newException(EInvalidValue, "Invalid format string: " & currentF)
currentF = "" currentF = ""
if f[i] == '\0': break if f[i] == '\0': break
@ -716,7 +728,15 @@ proc format*(info: TTimeInfo, f: string): string =
inc(i) inc(i)
else: result.add(f[i]) else: result.add(f[i])
else: currentF.add(f[i]) else:
# Check if the letter being added matches previous accumulated buffer.
if currentF.len < 1 or currentF[high(currentF)] == f[i]:
currentF.add(f[i])
else:
format_token(info, currentF, result)
dec(i) # Move position back to re-process the character separately.
currentF = ""
inc(i) inc(i)
{.pop.} {.pop.}
@ -727,11 +747,15 @@ when isMainModule:
var t = getGMTime(fromSeconds(2147483647)) var t = getGMTime(fromSeconds(2147483647))
echo t.format("ddd dd MMM hh:mm:ss ZZZ yyyy") echo t.format("ddd dd MMM hh:mm:ss ZZZ yyyy")
echo t.format("ddd ddMMMhhmmssZZZyyyy")
assert t.format("ddd dd MMM hh:mm:ss ZZZ yyyy") == "Tue 19 Jan 03:14:07 UTC 2038" assert t.format("ddd dd MMM hh:mm:ss ZZZ yyyy") == "Tue 19 Jan 03:14:07 UTC 2038"
assert t.format("ddd ddMMMhh:mm:ssZZZyyyy") == "Tue 19Jan03:14:07UTC2038"
assert t.format("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" & assert t.format("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" &
" ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") == " ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") ==
"19 19 Tue Tuesday 3 03 3 03 14 14 1 01 Jan January 7 07 A AM 8 38 038 2038 02038 0 00 00:00 UTC" "19 19 Tue Tuesday 3 03 3 03 14 14 1 01 Jan January 7 07 A AM 8 38 038 2038 02038 0 00 00:00 UTC"
assert t.format("yyyyMMddhhmmss") == "20380119031407"
var t2 = getGMTime(fromSeconds(160070789)) # Mon 27 Jan 16:06:29 GMT 1975 var t2 = getGMTime(fromSeconds(160070789)) # Mon 27 Jan 16:06:29 GMT 1975
assert t2.format("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" & assert t2.format("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" &

View file

@ -199,14 +199,14 @@ else:
importc: "GetCurrentDirectoryA", dynlib: "kernel32", stdcall.} importc: "GetCurrentDirectoryA", dynlib: "kernel32", stdcall.}
proc setCurrentDirectoryA*(lpPathName: cstring): int32 {. proc setCurrentDirectoryA*(lpPathName: cstring): int32 {.
importc: "SetCurrentDirectoryA", dynlib: "kernel32", stdcall.} importc: "SetCurrentDirectoryA", dynlib: "kernel32", stdcall.}
proc createDirectoryA*(pathName: cstring, security: pointer=nil): int32 {. proc createDirectoryA*(pathName: cstring, security: Pointer=nil): int32 {.
importc: "CreateDirectoryA", dynlib: "kernel32", stdcall.} importc: "CreateDirectoryA", dynlib: "kernel32", stdcall.}
proc removeDirectoryA*(lpPathName: cstring): int32 {. proc removeDirectoryA*(lpPathName: cstring): int32 {.
importc: "RemoveDirectoryA", dynlib: "kernel32", stdcall.} importc: "RemoveDirectoryA", dynlib: "kernel32", stdcall.}
proc setEnvironmentVariableA*(lpName, lpValue: cstring): int32 {. proc setEnvironmentVariableA*(lpName, lpValue: cstring): int32 {.
stdcall, dynlib: "kernel32", importc: "SetEnvironmentVariableA".} stdcall, dynlib: "kernel32", importc: "SetEnvironmentVariableA".}
proc getModuleFileNameA*(handle: THandle, buf: cstring, size: int32): int32 {. proc getModuleFileNameA*(handle: THandle, buf: CString, size: int32): int32 {.
importc: "GetModuleFileNameA", dynlib: "kernel32", stdcall.} importc: "GetModuleFileNameA", dynlib: "kernel32", stdcall.}
when useWinUnicode: when useWinUnicode:
@ -304,7 +304,7 @@ else:
dwFileAttributes: int32): WINBOOL {. dwFileAttributes: int32): WINBOOL {.
stdcall, dynlib: "kernel32", importc: "SetFileAttributesA".} stdcall, dynlib: "kernel32", importc: "SetFileAttributesA".}
proc copyFileA*(lpExistingFileName, lpNewFileName: cstring, proc copyFileA*(lpExistingFileName, lpNewFileName: CString,
bFailIfExists: cint): cint {. bFailIfExists: cint): cint {.
importc: "CopyFileA", stdcall, dynlib: "kernel32".} importc: "CopyFileA", stdcall, dynlib: "kernel32".}

View file

@ -7,4 +7,4 @@ type
proc ha() = proc ha() =
var var
x: TExport # no error x: TExport # no error
nil discard

View file

@ -4,4 +4,4 @@ type
TExport* = enum x, y, z TExport* = enum x, y, z
proc foo*(x: int) = proc foo*(x: int) =
nil discard

View file

@ -1,4 +1,4 @@
type type
TExport* = enum x, y, z # exactly the same type! TExport* = enum x, y, z # exactly the same type!
proc foo*(x: int) = nil proc foo*(x: int) = discard

View file

@ -0,0 +1,64 @@
discard """
file: "tasyncawait.nim"
cmd: "nimrod cc --hints:on $# $#"
output: "5000"
"""
import asyncio2, sockets2, net, strutils
var disp = newDispatcher()
var msgCount = 0
const
swarmSize = 50
messagesToSend = 100
var clientCount = 0
proc sendMessages(disp: PDispatcher, client: TSocketHandle): PFuture[int] {.async.} =
for i in 0 .. <messagesToSend:
discard await disp.send(client, "Message " & $i & "\c\L")
proc launchSwarm(disp: PDispatcher, port: TPort): PFuture[int] {.async.} =
for i in 0 .. <swarmSize:
var sock = socket()
disp.register(sock)
discard await disp.connect(sock, "localhost", port)
when true:
discard await sendMessages(disp, sock)
sock.close()
else:
# Issue #932: https://github.com/Araq/Nimrod/issues/932
var msgFut = sendMessages(disp, sock)
msgFut.callback =
proc () =
sock.close()
proc readMessages(disp: PDispatcher, client: TSocketHandle): PFuture[int] {.async.} =
while true:
var line = await disp.recvLine(client)
if line == "":
client.close()
clientCount.inc
break
else:
if line.startswith("Message "):
msgCount.inc
else:
doAssert false
proc createServer(disp: PDispatcher, port: TPort): PFuture[int] {.async.} =
var server = socket()
disp.register(server)
server.bindAddr(port)
server.listen()
while true:
discard readMessages(disp, await disp.accept(server))
discard disp.createServer(TPort(10335))
discard disp.launchSwarm(TPort(10335))
while true:
disp.poll()
if clientCount == swarmSize: break
assert msgCount == swarmSize * messagesToSend
echo msgCount

View file

@ -19,8 +19,8 @@ of eB, eC: write(stdout, "b or c")
case x case x
of "Andreas", "Rumpf": write(stdout, "Hallo Meister!") of "Andreas", "Rumpf": write(stdout, "Hallo Meister!")
of "aa", "bb": write(stdout, "Du bist nicht mein Meister") of "aa", "bb": write(stdout, "Du bist nicht mein Meister")
of "cc", "hash", "when": nil of "cc", "hash", "when": discard
of "will", "it", "finally", "be", "generated": nil of "will", "it", "finally", "be", "generated": discard
var z = case i var z = case i
of 1..5, 8, 9: "aa" of 1..5, 8, 9: "aa"

View file

@ -12,7 +12,7 @@ var
thr: array [0..5, TThread[tuple[a, b: int]]] thr: array [0..5, TThread[tuple[a, b: int]]]
L, M, N: TLock L, M, N: TLock
proc doNothing() = nil proc doNothing() = discard
proc threadFunc(interval: tuple[a, b: int]) {.thread.} = proc threadFunc(interval: tuple[a, b: int]) {.thread.} =
doNothing() doNothing()

View file

@ -12,6 +12,13 @@ myobj destroyed
---- ----
mygeneric3 constructed mygeneric3 constructed
mygeneric1 destroyed mygeneric1 destroyed
----
mygeneric1 destroyed
----
myobj destroyed
----
----
myobj destroyed
''' '''
""" """
@ -31,6 +38,22 @@ type
x: A x: A
y: B y: B
z: C z: C
TObjKind = enum A, B, C, D
TCaseObj = object
case kind: TObjKind
of A:
x: TMyGeneric1[int]
of B, C:
y: TMyObj
else:
case innerKind: TObjKind
of A, B, C:
p: TMyGeneric3[int, float, string]
of D:
q: TMyGeneric3[TMyObj, int, int]
r: string
proc destruct(o: var TMyObj) {.destructor.} = proc destruct(o: var TMyObj) {.destructor.} =
if o.p != nil: dealloc o.p if o.p != nil: dealloc o.p
@ -57,13 +80,13 @@ proc mygeneric1() =
echo "mygeneric1 constructed" echo "mygeneric1 constructed"
proc mygeneric2[T](val: T) = proc mygeneric2[T](val: T) =
var var a = open()
a = open()
b = TMyGeneric2[int, T](x: 10, y: val) var b = TMyGeneric2[int, T](x: 10, y: val)
c = TMyGeneric3[int, int, string](x: 10, y: 20, z: "test")
echo "mygeneric2 constructed" echo "mygeneric2 constructed"
var c = TMyGeneric3[int, int, string](x: 10, y: 20, z: "test")
proc mygeneric3 = proc mygeneric3 =
var x = TMyGeneric3[int, string, TMyGeneric1[int]]( var x = TMyGeneric3[int, string, TMyGeneric1[int]](
x: 10, y: "test", z: TMyGeneric1[int](x: 10)) x: 10, y: "test", z: TMyGeneric1[int](x: 10))
@ -82,3 +105,24 @@ mygeneric2[int](10)
echo "----" echo "----"
mygeneric3() mygeneric3()
proc caseobj =
block:
echo "----"
var o1 = TCaseObj(kind: A, x: TMyGeneric1[int](x: 10))
block:
echo "----"
var o2 = TCaseObj(kind: B, y: open())
block:
echo "----"
var o3 = TCaseObj(kind: D, innerKind: B, r: "test",
p: TMyGeneric3[int, float, string](x: 10, y: 1.0, z: "test"))
block:
echo "----"
var o4 = TCaseObj(kind: D, innerKind: D, r: "test",
q: TMyGeneric3[TMyObj, int, int](x: open(), y: 1, z: 0))
caseobj()

View file

@ -6,7 +6,7 @@ type
PDict[TK, TV] = ref TDict[TK, TV] PDict[TK, TV] = ref TDict[TK, TV]
proc fakeNew[T](x: var ref T, destroy: proc (a: ref T) {.nimcall.}) = proc fakeNew[T](x: var ref T, destroy: proc (a: ref T) {.nimcall.}) =
nil discard
proc destroyDict[TK, TV](a: PDict[TK, TV]) = proc destroyDict[TK, TV](a: PDict[TK, TV]) =
return return

View file

@ -0,0 +1,45 @@
discard """
file: "tnestedbreak.nim"
output: "1\n2\n3\n4"
"""
# First variety
try:
raise newException(EOS, "Problem")
except EOS:
for y in [1, 2, 3]:
discard
try:
discard
except EOS:
discard
echo "1"
# Second Variety
try:
raise newException(EOS, "Problem")
except EOS:
for y in [1, 2, 3]:
discard
for y in [1, 2, 3]:
discard
echo "2"
# Third Variety
try:
raise newException(EOS, "Problem")
except EOS:
block:
break
echo "3"
# Fourth Variety
block:
try:
raise newException(EOS, "Problem")
except EOS:
break
echo "4"

View file

@ -32,7 +32,7 @@ const
proc len[T,D] (n:PNode[T,D]): Int {.inline.} = proc len[T,D] (n:PNode[T,D]): Int {.inline.} =
return n.Count return n.Count
proc clean[T: TOrdinal|TNumber](o: var T) {.inline.} = nil proc clean[T: TOrdinal|TNumber](o: var T) {.inline.} = discard
proc clean[T: string|seq](o: var T) {.inline.} = proc clean[T: string|seq](o: var T) {.inline.} =
o = nil o = nil
@ -98,7 +98,7 @@ proc DeleteItem[T,D] (n: PNode[T,D], x: Int): PNode[T,D] {.inline.} =
of cLen3 : setLen(n.slots, cLen3) of cLen3 : setLen(n.slots, cLen3)
of cLenCenter : setLen(n.slots, cLenCenter) of cLenCenter : setLen(n.slots, cLenCenter)
of cLen4 : setLen(n.slots, cLen4) of cLen4 : setLen(n.slots, cLen4)
else: nil else: discard
Result = n Result = n
else : else :
@ -232,7 +232,7 @@ proc InsertItem[T,D](APath: RPath[T,D], ANode:PNode[T,D], AKey: T, AValue: D) =
of cLen3: setLen(APath.Nd.slots, cLenCenter) of cLen3: setLen(APath.Nd.slots, cLenCenter)
of cLenCenter: setLen(APath.Nd.slots, cLen4) of cLenCenter: setLen(APath.Nd.slots, cLen4)
of cLen4: setLen(APath.Nd.slots, cLenMax) of cLen4: setLen(APath.Nd.slots, cLenMax)
else: nil else: discard
for i in countdown(APath.Nd.Count.int - 1, x + 1): shallowCopy(APath.Nd.slots[i], APath.Nd.slots[i - 1]) for i in countdown(APath.Nd.Count.int - 1, x + 1): shallowCopy(APath.Nd.slots[i], APath.Nd.slots[i - 1])
APath.Nd.slots[x] = setItem(AKey, AValue, ANode) APath.Nd.slots[x] = setItem(AKey, AValue, ANode)

View file

@ -0,0 +1,10 @@
discard """
output: "10\n10"
"""
proc test(x: proc (a, b: int): int) =
echo x(5, 5)
test(proc (a, b): auto = a + b)
test do (a, b) -> auto: a + b

View file

@ -0,0 +1,18 @@
discard """
cmd: "nimrod check $# $#"
msg: "'proc' is not a concrete type"
msg: "'Foo' is not a concrete type."
msg: "invalid type: 'TBaseMed'"
"""
type
Foo[T] = object
x: T
TBaseMed = object
doSmth: proc
data: seq[Foo]
var a: TBaseMed
# issue 188

View file

@ -1,28 +1,28 @@
template foo(a: int, b: string) = nil template foo(a: int, b: string) = discard
foo(1, "test") foo(1, "test")
proc bar(a: int, b: string) = nil proc bar(a: int, b: string) = discard
bar(1, "test") bar(1, "test")
template foo(a: int, b: string) = bar(a, b) template foo(a: int, b: string) = bar(a, b)
foo(1, "test") foo(1, "test")
block: block:
proc bar(a: int, b: string) = nil proc bar(a: int, b: string) = discard
template foo(a: int, b: string) = nil template foo(a: int, b: string) = discard
foo(1, "test") foo(1, "test")
bar(1, "test") bar(1, "test")
proc baz = proc baz =
proc foo(a: int, b: string) = nil proc foo(a: int, b: string) = discard
proc foo(b: string) = proc foo(b: string) =
template bar(a: int, b: string) = nil template bar(a: int, b: string) = discard
bar(1, "test") bar(1, "test")
foo("test") foo("test")
block: block:
proc foo(b: string) = nil proc foo(b: string) = discard
foo("test") foo("test")
foo(1, "test") foo(1, "test")

View file

@ -26,3 +26,16 @@ foo 10
foo "test" foo "test"
foo(@[TObj(x: 10), TObj(x: 20)]) foo(@[TObj(x: 10), TObj(x: 20)])
proc intval(x: int) = discard
# check real and virtual fields
type
TFoo = generic T
intval T.x
intval T.y
proc y(x: TObj): int = 10
proc testFoo(x: TFoo) = discard
testFoo(TObj(x: 10))

View file

@ -14,8 +14,8 @@ type
TSomethingElse = object TSomethingElse = object
PSomethingElse = ref TSomethingElse PSomethingElse = ref TSomethingElse
method foo(a: PNode, b: PSomethingElse) = nil method foo(a: PNode, b: PSomethingElse) = discard
method foo(a: PNodeFoo, b: PSomethingElse) = nil method foo(a: PNodeFoo, b: PSomethingElse) = discard
var o: TObject var o: TObject
o.somethin() o.somethin()

View file

@ -1,13 +1,13 @@
discard """ discard """
file: "toverwr.nim" file: "toverwr.nim"
output: "hello" output: "hello"
""" """
# Test the overloading resolution in connection with a qualifier # Test the overloading resolution in connection with a qualifier
proc write(t: TFile, s: string) = proc write(t: TFile, s: string) =
nil # a nop discard # a nop
system.write(stdout, "hello") system.write(stdout, "hello")
#OUT hello #OUT hello

View file

@ -2,7 +2,7 @@ discard """
output: "12false3ha" output: "12false3ha"
""" """
proc f(x: varargs[string, `$`]) = nil proc f(x: varargs[string, `$`]) = discard
template optF{f(X)}(x: varargs[expr]) = template optF{f(X)}(x: varargs[expr]) =
writeln(stdout, x) writeln(stdout, x)

View file

@ -8,7 +8,7 @@ type
TRange = range[0..40] TRange = range[0..40]
proc p(r: TRange) = proc p(r: TRange) =
nil discard
var var
r: TRange r: TRange

View file

@ -8,7 +8,7 @@ type
TRange = range[0..40] TRange = range[0..40]
proc p(r: TRange) = proc p(r: TRange) =
nil discard
var var
r: TRange r: TRange

View file

@ -1,7 +1,7 @@
discard """ discard """
file: "tsets.nim" file: "tsets.nim"
output: "Ha ein F ist in s!" output: "Ha ein F ist in s!"
""" """
# Test the handling of sets # Test the handling of sets
import import
@ -38,7 +38,7 @@ type
TTokTypes* = set[TTokTypeRange] TTokTypes* = set[TTokTypeRange]
const const
toktypes: TTokTypes = {TTokTypeRange(tkSymbol)..pred(tkIntLit), toktypes: TTokTypes = {TTokTypeRange(tkSymbol)..pred(tkIntLit),
tkStrLit..tkTripleStrLit} tkStrLit..tkTripleStrLit}
var var
@ -51,14 +51,14 @@ else: write(stdout, "BUG: F ist nicht in s!\n")
a = {} #{'a'..'z'} a = {} #{'a'..'z'}
for x in low(TAZ) .. high(TAZ): for x in low(TAZ) .. high(TAZ):
incl(a, x) incl(a, x)
if x in a: nil if x in a: discard
else: write(stdout, "BUG: something not in a!\n") else: write(stdout, "BUG: something not in a!\n")
for x in low(TTokTypeRange) .. high(TTokTypeRange): for x in low(TTokTypeRange) .. high(TTokTypeRange):
if x in tokTypes: if x in tokTypes:
nil discard
#writeln(stdout, "the token '$1' is in the set" % repr(x)) #writeln(stdout, "the token '$1' is in the set" % repr(x))
#OUT Ha ein F ist in s! #OUT Ha ein F ist in s!

View file

@ -0,0 +1,66 @@
discard """
output: '''
10
assigning z = 20
reading field y
20
call to y
dot call
no params call to a
100
no params call to b
100
one param call to c with 10
100'''
"""
type
T1 = object
x*: int
TD = distinct T1
T2 = object
x: int
proc `.`*(v: T1, f: string): int =
echo "reading field ", f
return v.x
proc `.=`(x: var T1, f: string{lit}, v: int) =
echo "assigning ", f, " = ", v
x.x = v
template `.()`(x: T1, f: string, args: varargs[expr]): string =
echo "call to ", f
"dot call"
echo ""
var t = T1(x: 10)
echo t.x
t.z = 20
echo t.y
echo t.y()
var d = TD(t)
assert(not compiles(d.y))
proc `.`(v: T2, f: string): int =
echo "no params call to ", f
return v.x
proc `.`*(v: T2, f: string, a: int): int =
echo "one param call to ", f, " with ", a
return v.x
var tt = T2(x: 100)
echo tt.a
echo tt.b()
echo tt.c(10)
assert(not compiles(tt.d("x")))
assert(not compiles(tt.d(1, 2)))

View file

@ -183,7 +183,7 @@ type
channel: string channel: string
timestamp: TTime timestamp: TTime
case kind*: TSeenType case kind*: TSeenType
of PSeenJoin: nil of PSeenJoin: discard
of PSeenPart, PSeenQuit, PSeenMsg: of PSeenPart, PSeenQuit, PSeenMsg:
msg: string msg: string
of PSeenNick: of PSeenNick:
@ -200,7 +200,7 @@ proc setSeen(d: TDb, s: TSeen) =
var hashToSet = @[("type", $s.kind.int), ("channel", s.channel), var hashToSet = @[("type", $s.kind.int), ("channel", s.channel),
("timestamp", $s.timestamp.int)] ("timestamp", $s.timestamp.int)]
case s.kind case s.kind
of PSeenJoin: nil of PSeenJoin: discard
of PSeenPart, PSeenMsg, PSeenQuit: of PSeenPart, PSeenMsg, PSeenQuit:
hashToSet.add(("msg", s.msg)) hashToSet.add(("msg", s.msg))
of PSeenNick: of PSeenNick:
@ -338,7 +338,7 @@ proc hubConnect(state: PState) =
proc handleIrc(irc: PAsyncIRC, event: TIRCEvent, state: PState) = proc handleIrc(irc: PAsyncIRC, event: TIRCEvent, state: PState) =
case event.typ case event.typ
of EvConnected: nil of EvConnected: discard
of EvDisconnected: of EvDisconnected:
while not state.ircClient.isConnected: while not state.ircClient.isConnected:
try: try:
@ -424,7 +424,7 @@ proc handleIrc(irc: PAsyncIRC, event: TIRCEvent, state: PState) =
seenNick.newNick = event.params[0] seenNick.newNick = event.params[0]
state.database.setSeen(seenNick) state.database.setSeen(seenNick)
else: else:
nil # TODO: ? discard # TODO: ?
proc open(port: TPort = TPort(5123)): PState = proc open(port: TPort = TPort(5123)): PState =
var res: PState var res: PState

View file

@ -1,7 +1,7 @@
# tests for the interpreter # tests for the interpreter
proc loops(a: var int) = proc loops(a: var int) =
nil discard
#var #var
# b: int # b: int
#b = glob #b = glob

View file

@ -7,6 +7,6 @@ proc walkDirTree(root: string) =
case k case k
of pcFile, pcLinkToFile: echo(f) of pcFile, pcLinkToFile: echo(f)
of pcDir: walkDirTree(f) of pcDir: walkDirTree(f)
of pcLinkToDir: nil of pcLinkToDir: discard
walkDirTree(".") walkDirTree(".")

View file

@ -72,7 +72,7 @@ type
rule: TNode ## the rule that the symbol refers to rule: TNode ## the rule that the symbol refers to
TNode {.final, shallow.} = object TNode {.final, shallow.} = object
case kind: TPegKind case kind: TPegKind
of pkEmpty..pkWhitespace: nil of pkEmpty..pkWhitespace: discard
of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle: term: string of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle: term: string
of pkChar, pkGreedyRepChar: ch: char of pkChar, pkGreedyRepChar: ch: char
of pkCharChoice, pkGreedyRepSet: charChoice: ref set[char] of pkCharChoice, pkGreedyRepSet: charChoice: ref set[char]
@ -123,7 +123,7 @@ proc add(d: var TPeg, s: TPeg) {.inline.} = add(d.sons, s)
proc copyPeg(a: TPeg): TPeg = proc copyPeg(a: TPeg): TPeg =
result.kind = a.kind result.kind = a.kind
case a.kind case a.kind
of pkEmpty..pkWhitespace: nil of pkEmpty..pkWhitespace: discard
of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle: of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle:
result.term = a.term result.term = a.term
of pkChar, pkGreedyRepChar: of pkChar, pkGreedyRepChar:
@ -229,7 +229,7 @@ when false:
case a.kind case a.kind
of pkEmpty, pkAny, pkAnyRune, pkGreedyAny, pkNewLine, pkTerminal, of pkEmpty, pkAny, pkAnyRune, pkGreedyAny, pkNewLine, pkTerminal,
pkTerminalIgnoreCase, pkTerminalIgnoreStyle, pkChar, pkGreedyRepChar, pkTerminalIgnoreCase, pkTerminalIgnoreStyle, pkChar, pkGreedyRepChar,
pkCharChoice, pkGreedyRepSet: nil pkCharChoice, pkGreedyRepSet: discard
of pkNonTerminal: return true of pkNonTerminal: return true
else: else:
for i in 0..a.sons.len-1: for i in 0..a.sons.len-1:
@ -318,7 +318,7 @@ proc backrefIgnoreStyle*(index: range[1..MaxSubPatterns]): TPeg {.
proc spaceCost(n: TPeg): int = proc spaceCost(n: TPeg): int =
case n.kind case n.kind
of pkEmpty: nil of pkEmpty: discard
of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle, pkChar, of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle, pkChar,
pkGreedyRepChar, pkCharChoice, pkGreedyRepSet, pkGreedyRepChar, pkCharChoice, pkGreedyRepSet,
pkAny..pkWhitespace, pkGreedyAny: pkAny..pkWhitespace, pkGreedyAny:
@ -1111,7 +1111,7 @@ proc handleHexChar(c: var TPegLexer, xi: var int) =
of 'A'..'F': of 'A'..'F':
xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('A') + 10) xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('A') + 10)
inc(c.bufpos) inc(c.bufpos)
else: nil else: discard
proc getEscapedChar(c: var TPegLexer, tok: var TToken) = proc getEscapedChar(c: var TPegLexer, tok: var TToken) =
inc(c.bufpos) inc(c.bufpos)
@ -1341,7 +1341,7 @@ proc getTok(c: var TPegLexer, tok: var TToken) =
of "i": tok.modifier = modIgnoreCase of "i": tok.modifier = modIgnoreCase
of "y": tok.modifier = modIgnoreStyle of "y": tok.modifier = modIgnoreStyle
of "v": tok.modifier = modVerbatim of "v": tok.modifier = modVerbatim
else: nil else: discard
setLen(tok.literal, 0) setLen(tok.literal, 0)
if c.buf[c.bufpos] == '$': if c.buf[c.bufpos] == '$':
getDollar(c, tok) getDollar(c, tok)
@ -1488,7 +1488,7 @@ proc primary(p: var TPegParser): TPeg =
of tkCurlyAt: of tkCurlyAt:
getTok(p) getTok(p)
return !*\primary(p).token(p) return !*\primary(p).token(p)
else: nil else: discard
case p.tok.kind case p.tok.kind
of tkIdentifier: of tkIdentifier:
if p.identIsVerbatim: if p.identIsVerbatim:

View file

@ -1,7 +1,7 @@
# Test if the new table constructor syntax works: # Test if the new table constructor syntax works:
template ignoreExpr(e: expr): stmt {.immediate.} = template ignoreExpr(e: expr): stmt {.immediate.} =
nil discard
# test first class '..' syntactical citizen: # test first class '..' syntactical citizen:
ignoreExpr x <> 2..4 ignoreExpr x <> 2..4

View file

@ -9,7 +9,7 @@ proc init: TYourObj =
result.y = -1 result.y = -1
proc f(x: var TYourObj) = proc f(x: var TYourObj) =
nil discard
var m: TMyObj = init() var m: TMyObj = init()
f(m) f(m)

View file

@ -249,7 +249,7 @@ proc walker(dir: string) =
of pcDir: of pcDir:
if optRecursive in options: if optRecursive in options:
walker(path) walker(path)
else: nil else: discard
if existsFile(dir): processFile(dir) if existsFile(dir): processFile(dir)
proc writeHelp() = proc writeHelp() =

View file

@ -68,8 +68,8 @@ Language Additions
- Exported templates are allowed to access hidden fields. - Exported templates are allowed to access hidden fields.
- The ``using statement`` enables you to more easily author domain-specific - The ``using statement`` enables you to more easily author domain-specific
languages and libraries providing OOP-like syntactic sugar. languages and libraries providing OOP-like syntactic sugar.
- Added a new ``delegator pragma`` for handling calls to missing procs and - Added the possibility to override various dot operators in order to handle
fields at compile-time. calls to missing procs and reads from undeclared fields at compile-time.
- The overload resolution now supports ``static[T]`` params that must be - The overload resolution now supports ``static[T]`` params that must be
evaluable at compile-time. evaluable at compile-time.
- Support for user-defined type classes has been added. - Support for user-defined type classes has been added.