mirror of
https://github.com/nim-lang/Nim.git
synced 2026-01-01 10:52:14 +00:00
Merge branch 'devel' of https://github.com/Araq/Nimrod into devel
This commit is contained in:
@@ -409,7 +409,9 @@ type
|
||||
# efficiency
|
||||
nfTransf, # node has been transformed
|
||||
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
|
||||
nfIsRef # this node is a 'ref' node; used for the VM
|
||||
|
||||
@@ -843,7 +845,8 @@ const
|
||||
ExportableSymKinds* = {skVar, skConst, skProc, skMethod, skType, skIterator,
|
||||
skMacro, skTemplate, skConverter, skEnumField, skLet, skStub}
|
||||
PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16,
|
||||
nfAllConst, nfDelegate, nfIsRef}
|
||||
nfDotSetter, nfDotField,
|
||||
nfAllConst,nfIsRef}
|
||||
namePos* = 0
|
||||
patternPos* = 1 # empty except for term rewriting macros
|
||||
genericParamsPos* = 2
|
||||
@@ -1044,6 +1047,10 @@ proc newStrNode(kind: TNodeKind, strVal: string): PNode =
|
||||
result = newNode(kind)
|
||||
result.strVal = strVal
|
||||
|
||||
proc withInfo*(n: PNode, info: TLineInfo): PNode =
|
||||
n.info = info
|
||||
return n
|
||||
|
||||
proc newIdentNode(ident: PIdent, info: TLineInfo): PNode =
|
||||
result = newNode(nkIdent)
|
||||
result.ident = ident
|
||||
|
||||
@@ -540,7 +540,7 @@ proc typeAtom(p: var TParser): PNode =
|
||||
if p.tok.s == "unsigned":
|
||||
isUnsigned = true
|
||||
elif p.tok.s == "signed" or p.tok.s == "int":
|
||||
nil
|
||||
discard
|
||||
else:
|
||||
add(x, p.tok.s)
|
||||
getTok(p, nil)
|
||||
@@ -746,7 +746,7 @@ proc directDeclarator(p: var TParser, a: PNode, ident: ptr PNode): PNode =
|
||||
result = declarator(p, a, ident)
|
||||
eat(p, pxParRi, result)
|
||||
else:
|
||||
nil
|
||||
discard
|
||||
return parseTypeSuffix(p, a)
|
||||
|
||||
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) =
|
||||
case base
|
||||
of base10: nil
|
||||
of base10: discard
|
||||
of base2: incl(n.flags, nfBase2)
|
||||
of base8: incl(n.flags, nfBase8)
|
||||
of base16: incl(n.flags, nfBase16)
|
||||
@@ -1686,7 +1686,7 @@ proc switchStatement(p: var TParser): PNode =
|
||||
break
|
||||
of "case", "default":
|
||||
break
|
||||
else: nil
|
||||
else: discard
|
||||
addSon(result, statement(p))
|
||||
if sonsLen(result) == 0:
|
||||
# translate empty statement list to Nimrod's ``nil`` statement
|
||||
|
||||
@@ -103,7 +103,7 @@ proc parseDefBody(p: var TParser, m: var TMacro, params: seq[string]) =
|
||||
m.body.add(tok)
|
||||
of pxDirConc:
|
||||
# just ignore this token: this implements token merging correctly
|
||||
nil
|
||||
discard
|
||||
else:
|
||||
m.body.add(p.tok)
|
||||
# we do not want macro expansion here:
|
||||
@@ -166,7 +166,7 @@ proc parseStmtList(p: var TParser): PNode =
|
||||
of pxDirectiveParLe, pxDirective:
|
||||
case p.tok.s
|
||||
of "else", "endif", "elif": break
|
||||
else: nil
|
||||
else: discard
|
||||
addSon(result, statement(p))
|
||||
|
||||
proc eatEndif(p: var TParser) =
|
||||
|
||||
@@ -65,6 +65,7 @@ proc startBlock(p: BProc, start: TFormatStr = "{$n",
|
||||
setLen(p.blocks, result + 1)
|
||||
p.blocks[result].id = p.labels
|
||||
p.blocks[result].nestedTryStmts = p.nestedTryStmts.len.int16
|
||||
p.blocks[result].nestedExceptStmts = p.inExceptBlock.int16
|
||||
|
||||
proc assignLabel(b: var TBlock): PRope {.inline.} =
|
||||
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()")
|
||||
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
|
||||
var stack: seq[PNode]
|
||||
newSeq(stack, howMany)
|
||||
for i in countup(1, howMany):
|
||||
newSeq(stack, howManyTrys)
|
||||
for i in countup(1, howManyTrys):
|
||||
stack[i-1] = p.nestedTryStmts[L-i]
|
||||
setLen(p.nestedTryStmts, L-howMany)
|
||||
setLen(p.nestedTryStmts, L-howManyTrys)
|
||||
|
||||
var alreadyPoppedCnt = p.inExceptBlock
|
||||
for tryStmt in items(stack):
|
||||
@@ -276,21 +285,26 @@ proc blockLeaveActions(p: BProc, howMany: int) =
|
||||
dec alreadyPoppedCnt
|
||||
else:
|
||||
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)
|
||||
if finallyStmt.kind == nkFinally:
|
||||
genStmts(p, finallyStmt.sons[0])
|
||||
# push old elements again:
|
||||
for i in countdown(howMany-1, 0):
|
||||
for i in countdown(howManyTrys-1, 0):
|
||||
p.nestedTryStmts.add(stack[i])
|
||||
|
||||
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")
|
||||
|
||||
proc genReturnStmt(p: BProc, t: PNode) =
|
||||
p.beforeRetNeeded = true
|
||||
genLineDir(p, t)
|
||||
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", [])
|
||||
|
||||
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:
|
||||
internalError(t.info, "no loop to break")
|
||||
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)
|
||||
lineF(p, cpsStmts, "goto $1;$n", [label])
|
||||
|
||||
|
||||
@@ -58,6 +58,7 @@ type
|
||||
sections*: TCProcSections # the code beloging
|
||||
isLoop*: bool # whether block is a loop
|
||||
nestedTryStmts*: int16 # how many try statements is it nested into
|
||||
nestedExceptStmts*: int16 # how many except statements is it nested into
|
||||
frameLen*: int16
|
||||
|
||||
TCProc{.final.} = object # represents C proc that is currently generated
|
||||
|
||||
@@ -88,7 +88,8 @@ type
|
||||
errTemplateInstantiationTooNested, errInstantiationFrom,
|
||||
errInvalidIndexValueForTuple, errCommandExpectsFilename,
|
||||
errMainModuleMustBeSpecified,
|
||||
errXExpected,
|
||||
errXExpected,
|
||||
errTIsNotAConcreteType,
|
||||
errInvalidSectionStart, errGridTableNotImplemented, errGeneralParseError,
|
||||
errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile,
|
||||
errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitely,
|
||||
@@ -312,6 +313,7 @@ const
|
||||
errCommandExpectsFilename: "command expects a filename argument",
|
||||
errMainModuleMustBeSpecified: "please, specify a main module in the project configuration file",
|
||||
errXExpected: "\'$1\' expected",
|
||||
errTIsNotAConcreteType: "\'$1\' is not a concrete type.",
|
||||
errInvalidSectionStart: "invalid section start",
|
||||
errGridTableNotImplemented: "grid table is not implemented",
|
||||
errGeneralParseError: "general parse error",
|
||||
|
||||
@@ -281,7 +281,7 @@ proc parseSymbol(p: var TParser): PNode =
|
||||
add(result, newIdentNodeP(getIdent"{}", p))
|
||||
getTok(p)
|
||||
eat(p, tkCurlyRi)
|
||||
of tokKeywordLow..tokKeywordHigh, tkSymbol, tkOpr, tkDotDot:
|
||||
of tokKeywordLow..tokKeywordHigh, tkSymbol, tkOpr, tkDot, tkDotDot:
|
||||
add(result, newIdentNodeP(p.tok.ident, p))
|
||||
getTok(p)
|
||||
of tkIntLit..tkCharLit:
|
||||
|
||||
@@ -342,7 +342,7 @@ proc getSymbol(L: var TLexer, tok: var TToken) =
|
||||
h = h +% ord(c)
|
||||
h = h +% h shl 10
|
||||
h = h xor (h shr 6)
|
||||
of '_': nil
|
||||
of '_': discard
|
||||
else: break
|
||||
inc(pos)
|
||||
h = h +% h shl 3
|
||||
|
||||
@@ -335,7 +335,7 @@ proc exprColonEqExprList(p: var TParser, kind, elemKind: TNodeKind,
|
||||
|
||||
proc setBaseFlags(n: PNode, base: TNumericalBase) =
|
||||
case base
|
||||
of base10: nil
|
||||
of base10: discard
|
||||
of base2: incl(n.flags, nfBase2)
|
||||
of base8: incl(n.flags, nfBase8)
|
||||
of base16: incl(n.flags, nfBase16)
|
||||
@@ -466,7 +466,7 @@ proc lowestExprAux(p: var TParser, v: var PNode, limit: int): TTokKind =
|
||||
eat(p, pxCurlyDirRi)
|
||||
opNode.ident = getIdent("&")
|
||||
else:
|
||||
nil
|
||||
discard
|
||||
of pxMinus:
|
||||
if p.tok.xkind == pxPer:
|
||||
getTok(p)
|
||||
@@ -477,7 +477,7 @@ proc lowestExprAux(p: var TParser, v: var PNode, limit: int): TTokKind =
|
||||
of pxNeq:
|
||||
opNode.ident = getIdent("!=")
|
||||
else:
|
||||
nil
|
||||
discard
|
||||
skipCom(p, opNode) # read sub-expression with higher priority
|
||||
nextop = lowestExprAux(p, v2, opPred)
|
||||
addSon(node, opNode)
|
||||
@@ -505,7 +505,7 @@ proc fixExpr(n: PNode): PNode =
|
||||
(n.sons[2].kind in {nkCharLit, nkStrLit}):
|
||||
n.sons[0].ident = getIdent("&") # fix operator
|
||||
else:
|
||||
nil
|
||||
discard
|
||||
if not (n.kind in {nkEmpty..nkNilLit}):
|
||||
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:
|
||||
if not isHandledDirective(p): break
|
||||
else:
|
||||
nil
|
||||
discard
|
||||
addSon(result, parseStmt(p))
|
||||
if sonsLen(result) == 1: result = result.sons[0]
|
||||
|
||||
@@ -732,7 +732,7 @@ proc parseRepeat(p: var TParser): PNode =
|
||||
addSon(b, c)
|
||||
addSon(a, b)
|
||||
if b.sons[0].kind == nkIdent and b.sons[0].ident.id == getIdent("false").id:
|
||||
nil
|
||||
discard
|
||||
else:
|
||||
addSon(s, a)
|
||||
addSon(result, s)
|
||||
@@ -840,7 +840,7 @@ proc parseParam(p: var TParser): PNode =
|
||||
getTok(p)
|
||||
v = newNodeP(nkVarTy, p)
|
||||
else:
|
||||
nil
|
||||
discard
|
||||
while true:
|
||||
case p.tok.xkind
|
||||
of pxSymbol: a = createIdentNodeP(p.tok.ident, p)
|
||||
@@ -1133,7 +1133,7 @@ proc parseRecordPart(p: var TParser): PNode =
|
||||
proc exSymbol(n: var PNode) =
|
||||
case n.kind
|
||||
of nkPostfix:
|
||||
nil
|
||||
discard
|
||||
of nkPragmaExpr:
|
||||
exSymbol(n.sons[0])
|
||||
of nkIdent, nkAccQuoted:
|
||||
@@ -1154,7 +1154,7 @@ proc fixRecordDef(n: var PNode) =
|
||||
for i in countup(0, sonsLen(n) - 1): fixRecordDef(n.sons[i])
|
||||
of nkIdentDefs:
|
||||
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)
|
||||
|
||||
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))
|
||||
else: internalError(result.info, "anonymous record is not supported")
|
||||
else:
|
||||
nil
|
||||
discard
|
||||
opt(p, pxSemicolon)
|
||||
skipCom(p, result)
|
||||
|
||||
@@ -1399,7 +1399,7 @@ proc fixVarSection(p: var TParser, counter: PNode) =
|
||||
|
||||
proc exSymbols(n: PNode) =
|
||||
case n.kind
|
||||
of nkEmpty..nkNilLit: nil
|
||||
of nkEmpty..nkNilLit: discard
|
||||
of nkProcDef..nkIteratorDef: exSymbol(n.sons[namePos])
|
||||
of nkWhenStmt, nkStmtList:
|
||||
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])
|
||||
if n.sons[i].sons[2].kind == nkObjectTy:
|
||||
fixRecordDef(n.sons[i].sons[2])
|
||||
else: nil
|
||||
else: discard
|
||||
|
||||
proc parseBegin(p: var TParser, result: PNode) =
|
||||
getTok(p)
|
||||
|
||||
@@ -333,6 +333,7 @@ proc myOpen(module: PSym): PPassContext =
|
||||
c.semOperand = semOperand
|
||||
c.semConstBoolExpr = semConstBoolExpr
|
||||
c.semOverloadedCall = semOverloadedCall
|
||||
c.semInferredLambda = semInferredLambda
|
||||
c.semGenerateInstance = generateInstance
|
||||
c.semTypeNode = semTypeNode
|
||||
pushProcCon(c, module)
|
||||
|
||||
@@ -82,7 +82,7 @@ proc notFoundError*(c: PContext, n: PNode, errors: seq[string]) =
|
||||
# fail fast:
|
||||
globalError(n.info, 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, ')')
|
||||
|
||||
var candidates = ""
|
||||
@@ -138,17 +138,35 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
|
||||
|
||||
let overloadsState = result.state
|
||||
if overloadsState != csMatch:
|
||||
if nfDelegate in n.flags:
|
||||
internalAssert f.kind == nkIdent
|
||||
let calleeName = newStrNode(nkStrLit, f.ident.s)
|
||||
calleeName.info = n.info
|
||||
if nfDotField in n.flags:
|
||||
internalAssert f.kind == nkIdent and n.sonsLen >= 2
|
||||
let calleeName = newStrNode(nkStrLit, f.ident.s).withInfo(n.info)
|
||||
|
||||
let callOp = newIdentNode(idDelegator, n.info)
|
||||
n.sons[0..0] = [callOp, calleeName]
|
||||
orig.sons[0..0] = [callOp, calleeName]
|
||||
|
||||
# leave the op head symbol empty,
|
||||
# we are going to try multiple variants
|
||||
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)
|
||||
|
||||
|
||||
if overloadsState == csEmpty and result.state == csEmpty:
|
||||
localError(n.info, errUndeclaredIdentifier, considerAcc(f).s)
|
||||
return
|
||||
|
||||
@@ -81,6 +81,7 @@ type
|
||||
semOverloadedCall*: proc (c: PContext, n, nOrig: PNode,
|
||||
filter: TSymKinds): PNode {.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,
|
||||
info: TLineInfo): PSym
|
||||
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 =
|
||||
let typedesc = makeTypeDesc(c, typ)
|
||||
rawAddSon(typedesc, newTypeS(tyNone, c))
|
||||
let sym = newSym(skType, idAnon, getCurrOwner(), info).linkTo(typedesc)
|
||||
return newSymNode(sym, info)
|
||||
|
||||
|
||||
@@ -55,7 +55,9 @@ proc doDestructorStuff(c: PContext, s: PSym, n: PNode) =
|
||||
useSym(destructableT.destructor),
|
||||
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)
|
||||
if destructableT != nil:
|
||||
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):
|
||||
# of A, B:
|
||||
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, @[])
|
||||
template addField(f: expr): stmt =
|
||||
let stmt = destroyField(c, f, holder)
|
||||
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)
|
||||
|
||||
let stmt = destroyFieldOrFields(c, n[i].lastSon, holder)
|
||||
if stmt == nil:
|
||||
caseBranch.addSon(newNode(nkStmtList, n[i].info, @[]))
|
||||
else:
|
||||
internalAssert false
|
||||
|
||||
caseBranch.addSon(destroyRecList)
|
||||
caseBranch.addSon(stmt)
|
||||
nonTrivialFields += stmt.len
|
||||
|
||||
result.addSon(caseBranch)
|
||||
|
||||
# maybe no fields were destroyed?
|
||||
if nonTrivialFields == 0:
|
||||
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 =
|
||||
## generate a destructor for a user-defined object or tuple type
|
||||
## 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
|
||||
# Tposix_spawnattr
|
||||
if t.n == nil or t.n.sons == nil: return
|
||||
internalAssert t.n.kind == nkRecList
|
||||
let destructedObj = newIdentNode(destructorParam, unknownLineInfo())
|
||||
# call the destructods of all fields
|
||||
for s in countup(0, t.n.sons.len - 1):
|
||||
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
|
||||
result = destroyFieldOrFields(c, t.n, destructedObj)
|
||||
# base classes' destructors will be automatically called by
|
||||
# semProcAux for both auto-generated and user-defined destructors
|
||||
|
||||
|
||||
@@ -346,22 +346,21 @@ proc semIs(c: PContext, n: PNode): PNode =
|
||||
|
||||
result = n
|
||||
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}:
|
||||
let t2 = semTypeNode(c, n[2], nil)
|
||||
n.sons[2] = newNodeIT(nkType, n[2].info, t2)
|
||||
|
||||
if n[1].typ.kind != tyTypeDesc:
|
||||
n.sons[1] = makeTypeSymNode(c, n[1].typ, n[1].info)
|
||||
elif n[1].typ.sonsLen == 0:
|
||||
let lhsType = n[1].typ
|
||||
if lhsType.kind != tyTypeDesc:
|
||||
n.sons[1] = makeTypeSymNode(c, lhsType, n[1].info)
|
||||
elif lhsType.base.kind == tyNone:
|
||||
# this is a typedesc variable, leave for evals
|
||||
return
|
||||
|
||||
let t1 = n[1].typ.sons[0]
|
||||
# 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) =
|
||||
const flags = {efDetermineType}
|
||||
@@ -683,20 +682,21 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode,
|
||||
incl(c.p.owner.flags, sfSideEffect)
|
||||
|
||||
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
|
||||
checkMinSonsLen(n, 1)
|
||||
var prc = n.sons[0]
|
||||
if n.sons[0].kind == nkDotExpr:
|
||||
if n.sons[0].kind == nkDotExpr:
|
||||
checkSonsLen(n.sons[0], 2)
|
||||
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!
|
||||
result = n.sons[0]
|
||||
result.kind = nkCall
|
||||
result.flags.incl nfExplicitCall
|
||||
for i in countup(1, sonsLen(n) - 1): addSon(result, n.sons[i])
|
||||
return semExpr(c, result, flags)
|
||||
else:
|
||||
else:
|
||||
n.sons[0] = semExpr(c, n.sons[0])
|
||||
let nOrig = n.copyTree
|
||||
semOpAux(c, n)
|
||||
@@ -917,8 +917,8 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
var ty = n.sons[0].typ
|
||||
var f: PSym = nil
|
||||
result = nil
|
||||
if isTypeExpr(n.sons[0]) or ty.kind == tyTypeDesc and ty.len == 1:
|
||||
if ty.kind == tyTypeDesc: ty = ty.sons[0]
|
||||
if isTypeExpr(n.sons[0]) or ty.kind == tyTypeDesc and ty.base.kind != tyNone:
|
||||
if ty.kind == tyTypeDesc: ty = ty.base
|
||||
case ty.kind
|
||||
of tyEnum:
|
||||
# look up if the identifier belongs to the enum:
|
||||
@@ -999,7 +999,7 @@ proc dotTransformation(c: PContext, n: PNode): PNode =
|
||||
else:
|
||||
var i = considerAcc(n.sons[1])
|
||||
result = newNodeI(nkDotCall, n.info)
|
||||
result.flags.incl nfDelegate
|
||||
result.flags.incl nfDotField
|
||||
addSon(result, newIdentNode(i, n[1].info))
|
||||
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 =
|
||||
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``
|
||||
# this is ugly. XXX Semantic checking should use the ``nfSem`` flag for
|
||||
# nodes?
|
||||
let aOrig = nOrig[0]
|
||||
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]])
|
||||
result = semOverloadedCallAnalyseEffects(c, result, orig, {})
|
||||
|
||||
@@ -1777,22 +1778,6 @@ proc semBlock(c: PContext, n: PNode): PNode =
|
||||
closeScope(c)
|
||||
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 =
|
||||
result = n.kind == nkDo 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)
|
||||
if result.kind == nkSym:
|
||||
markIndirect(c, result.sym)
|
||||
if isGenericRoutine(result.sym):
|
||||
localError(n.info, errInstantiateXExplicitely, s.name.s)
|
||||
# if isGenericRoutine(result.sym):
|
||||
# localError(n.info, errInstantiateXExplicitely, s.name.s)
|
||||
of nkSym:
|
||||
# because of the changed symbol binding, this does not mean that we
|
||||
# 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:
|
||||
# check if it is an expression macro:
|
||||
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)
|
||||
if s != nil:
|
||||
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
|
||||
setGenericParams(c, n.sons[0])
|
||||
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)
|
||||
else:
|
||||
result = semIndirectOp(c, n, flags)
|
||||
|
||||
@@ -764,6 +764,29 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
|
||||
s.ast = a
|
||||
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) =
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
var a = n.sons[i]
|
||||
@@ -780,6 +803,8 @@ proc typeSectionFinalPass(c: PContext, n: PNode) =
|
||||
assignType(s.typ, t)
|
||||
s.typ.id = t.id # same id
|
||||
checkConstructedType(s.info, s.typ)
|
||||
if s.typ.kind in {tyObject, tyTuple}:
|
||||
checkForMetaFields(s.typ.n)
|
||||
let aa = a.sons[2]
|
||||
if aa.kind in {nkRefTy, nkPtrTy} and aa.len == 1 and
|
||||
aa.sons[0].kind == nkObjectTy:
|
||||
@@ -883,12 +908,19 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
s = n[namePos].sym
|
||||
pushOwner(s)
|
||||
openScope(c)
|
||||
if n.sons[genericParamsPos].kind != nkEmpty:
|
||||
illFormedAst(n) # process parameters:
|
||||
var gp: PNode
|
||||
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:
|
||||
var gp = newNodeI(nkGenericParams, n.info)
|
||||
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:
|
||||
s.typ = newTypeS(tyProc, c)
|
||||
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)
|
||||
#if efDetermineType notin flags:
|
||||
# XXX not good enough; see tnamedparamanonproc.nim
|
||||
pushProcCon(c, s)
|
||||
addResult(c, s.typ.sons[0], n.info, skProc)
|
||||
let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos]))
|
||||
n.sons[bodyPos] = transformBody(c.module, semBody, s)
|
||||
addResultNode(c, n)
|
||||
popProcCon(c)
|
||||
if n.sons[genericParamsPos].kind == nkEmpty:
|
||||
pushProcCon(c, s)
|
||||
addResult(c, s.typ.sons[0], n.info, skProc)
|
||||
let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos]))
|
||||
n.sons[bodyPos] = transformBody(c.module, semBody, s)
|
||||
addResultNode(c, n)
|
||||
popProcCon(c)
|
||||
sideEffectsCheck(c, s)
|
||||
else:
|
||||
localError(n.info, errImplOfXexpected, s.name.s)
|
||||
@@ -913,6 +946,34 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
popOwner()
|
||||
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) =
|
||||
# XXX: This proc is part of my plan for getting rid of
|
||||
# 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])
|
||||
if outer != nil:
|
||||
n.sons[i] = outer
|
||||
for j in countup(i+1, length-1):
|
||||
inner.addSon(semStmt(c, n.sons[j]))
|
||||
var rest = newNode(nkStmtList, n.info, n.sons[i+1 .. length-1])
|
||||
inner.addSon(semStmtList(c, rest, flags))
|
||||
n.sons.setLen(i+1)
|
||||
return
|
||||
of LastBlockStmts:
|
||||
|
||||
@@ -1030,9 +1030,8 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
if s.kind != skError: localError(n.info, errTypeExpected)
|
||||
result = newOrPrevType(tyError, prev, c)
|
||||
elif s.kind == skParam and s.typ.kind == tyTypeDesc:
|
||||
assert s.typ.len > 0
|
||||
internalAssert prev == nil
|
||||
result = s.typ.sons[0]
|
||||
internalAssert s.typ.base.kind != tyNone and prev == nil
|
||||
result = s.typ.base
|
||||
elif prev == nil:
|
||||
result = s.typ
|
||||
else:
|
||||
|
||||
@@ -29,6 +29,7 @@ proc checkConstructedType*(info: TLineInfo, typ: PType) =
|
||||
localError(info, errVarVarTypeNotAllowed)
|
||||
elif computeSize(t) == szIllegalRecursion:
|
||||
localError(info, errIllegalRecursionInTypeX, typeToString(t))
|
||||
|
||||
when false:
|
||||
if t.kind == tyObject and t.sons[0] != nil:
|
||||
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
|
||||
|
||||
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,
|
||||
t: PType): PType =
|
||||
var cl: TReplTypeVars
|
||||
initIdTable(cl.symMap)
|
||||
copyIdTable(cl.typeMap, pt)
|
||||
initIdTable(cl.localCache)
|
||||
cl.info = info
|
||||
cl.c = p
|
||||
var cl = initTypeVars(p, pt, info)
|
||||
pushInfoContext(info)
|
||||
result = replaceTypeVarsT(cl, t)
|
||||
popInfoContext()
|
||||
|
||||
@@ -343,53 +343,57 @@ proc allowsNil(f: PType): TTypeRelation {.inline.} =
|
||||
proc inconsistentVarTypes(f, a: PType): bool {.inline.} =
|
||||
result = f.kind != a.kind and (f.kind == tyVar or a.kind == tyVar)
|
||||
|
||||
proc procParamTypeRel(c: var TCandidate, f, a: PType,
|
||||
result: var TTypeRelation) =
|
||||
var
|
||||
m: TTypeRelation
|
||||
f = f
|
||||
|
||||
proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
|
||||
var f = f
|
||||
|
||||
if a.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
|
||||
# sigunature. there is a change that the target
|
||||
# signature. There is a change that the target
|
||||
# type is already fully-determined, so we are
|
||||
# going to try resolve it
|
||||
f = generateTypeInstance(c.c, c.bindings, c.call.info, f)
|
||||
if f == nil or f.isMetaType:
|
||||
# no luck resolving the type, so the inference fails
|
||||
result = isNone
|
||||
return
|
||||
return isNone
|
||||
let reverseRel = typeRel(c, a, f)
|
||||
if reverseRel == isGeneric:
|
||||
m = isInferred
|
||||
result = isInferred
|
||||
inc c.genericMatches
|
||||
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
|
||||
return
|
||||
else:
|
||||
result = minRel(m, result)
|
||||
|
||||
|
||||
if result == isEqual:
|
||||
inc c.exactMatches
|
||||
|
||||
proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
|
||||
case a.kind
|
||||
of tyProc:
|
||||
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
|
||||
# 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 a.sons[0] != nil:
|
||||
procParamTypeRel(c, f.sons[0], a.sons[0], result)
|
||||
checkParam(f.sons[0], a.sons[0])
|
||||
else:
|
||||
return isNone
|
||||
elif a.sons[0] != nil:
|
||||
return isNone
|
||||
|
||||
if tfNoSideEffect in f.flags and tfNoSideEffect notin a.flags:
|
||||
return isNone
|
||||
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:
|
||||
internalAssert a.sons != nil and a.sons.len > 0
|
||||
c.typedescMatched = true
|
||||
result = typeRel(c, f.sons[0], a.sons[0])
|
||||
result = typeRel(c, f.base, a.base)
|
||||
else:
|
||||
result = isNone
|
||||
else:
|
||||
@@ -896,22 +900,20 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
||||
result = isNone
|
||||
|
||||
of tyTypeDesc:
|
||||
if a.kind != tyTypeDesc: return isNone
|
||||
|
||||
var prev = PType(idTableGet(c.bindings, f))
|
||||
if prev == nil:
|
||||
if a.kind == tyTypeDesc:
|
||||
if f.sons[0].kind == tyNone:
|
||||
result = isGeneric
|
||||
else:
|
||||
result = typeRel(c, f.sons[0], a.sons[0])
|
||||
if result != isNone:
|
||||
put(c.bindings, f, a)
|
||||
if f.base.kind == tyNone:
|
||||
result = isGeneric
|
||||
else:
|
||||
result = isNone
|
||||
result = typeRel(c, f.base, a.base)
|
||||
if result != isNone:
|
||||
put(c.bindings, f, a)
|
||||
else:
|
||||
internalAssert prev.sonsLen == 1
|
||||
let toMatch = if tfUnresolved in f.flags: a
|
||||
else: a.sons[0]
|
||||
result = typeRel(c, prev.sons[0], toMatch)
|
||||
else: a.base
|
||||
result = typeRel(c, prev.base, toMatch)
|
||||
|
||||
of tyStmt:
|
||||
result = isGeneric
|
||||
@@ -1015,7 +1017,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
|
||||
argType = arg.typ
|
||||
|
||||
var
|
||||
a = if c.inTypeClass > 0: argType.skipTypes({tyTypeDesc})
|
||||
a = if c.inTypeClass > 0: argType.skipTypes({tyTypeDesc, tyFieldAccessor})
|
||||
else: argType
|
||||
|
||||
r = typeRel(m, f, a)
|
||||
@@ -1037,10 +1039,11 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
|
||||
#result = copyTree(arg)
|
||||
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c)
|
||||
of isInferred, isInferredConvertible:
|
||||
var prc = if arg.kind in nkLambdaKinds: arg[0].sym
|
||||
else: arg.sym
|
||||
let inferred = c.semGenerateInstance(c, prc, m.bindings, arg.info)
|
||||
result = newSymNode(inferred, arg.info)
|
||||
if arg.kind in {nkProcDef, nkIteratorDef} + nkLambdaKinds:
|
||||
result = c.semInferredLambda(c, m.bindings, arg)
|
||||
else:
|
||||
let inferred = c.semGenerateInstance(c, arg.sym, m.bindings, arg.info)
|
||||
result = newSymNode(inferred, arg.info)
|
||||
if r == isInferredConvertible:
|
||||
result = implicitConv(nkHiddenStdConv, f, result, m, c)
|
||||
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
|
||||
# this correctly is inefficient. We have to copy `m` here to be able to
|
||||
# roll back the side effects of the unification algorithm.
|
||||
|
||||
let c = m.c
|
||||
var x, y, z: TCandidate
|
||||
initCandidate(c, x, m.callee)
|
||||
@@ -1124,10 +1128,10 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
|
||||
x.state = csMatch
|
||||
of csMatch:
|
||||
var cmp = cmpCandidates(x, z)
|
||||
if cmp < 0:
|
||||
if cmp < 0:
|
||||
best = i
|
||||
x = z
|
||||
elif cmp == 0:
|
||||
elif cmp == 0:
|
||||
y = z # z is as good as x
|
||||
if x.state == csEmpty:
|
||||
result = nil
|
||||
|
||||
@@ -431,8 +431,8 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
|
||||
add(result, typeToString(t.sons[i]))
|
||||
add(result, ']')
|
||||
of tyTypeDesc:
|
||||
if t.len == 0: result = "typedesc"
|
||||
else: result = "typedesc[" & typeToString(t.sons[0]) & "]"
|
||||
if t.base.kind == tyNone: result = "typedesc"
|
||||
else: result = "typedesc[" & typeToString(t.base) & "]"
|
||||
of tyStatic:
|
||||
internalAssert t.len > 0
|
||||
result = "static[" & typeToString(t.sons[0]) & "]"
|
||||
@@ -1042,7 +1042,8 @@ proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind,
|
||||
of tyEmpty:
|
||||
result = taField in flags
|
||||
of tyTypeClasses:
|
||||
result = true
|
||||
result = tfGenericTypeParam in t.flags or
|
||||
taField notin flags
|
||||
of tyGenericBody, tyGenericParam, tyGenericInvokation,
|
||||
tyNone, tyForward, tyFromExpr, tyFieldAccessor:
|
||||
result = false
|
||||
@@ -1231,8 +1232,7 @@ proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt =
|
||||
of tyGenericInst, tyDistinct, tyGenericBody, tyMutable, tyConst, tyIter:
|
||||
result = computeSizeAux(lastSon(typ), a)
|
||||
of tyTypeDesc:
|
||||
result = if typ.len == 1: computeSizeAux(typ.sons[0], a)
|
||||
else: szUnknownSize
|
||||
result = computeSizeAux(typ.base, a)
|
||||
of tyForward: return szIllegalRecursion
|
||||
else:
|
||||
#internalError("computeSizeAux()")
|
||||
@@ -1258,7 +1258,7 @@ proc containsGenericTypeIter(t: PType, closure: PObject): bool =
|
||||
return true
|
||||
|
||||
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
|
||||
return false
|
||||
|
||||
|
||||
@@ -4106,6 +4106,59 @@ types that will match the typedesc param:
|
||||
|
||||
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
|
||||
=====================
|
||||
@@ -4758,42 +4811,6 @@ This may change in future versions of language, but for now use
|
||||
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
|
||||
--------------
|
||||
The `procvar`:idx: pragma is used to mark a proc that it can be passed to a
|
||||
|
||||
@@ -36,7 +36,7 @@ block mainLoop:
|
||||
case x.kind
|
||||
of xmlEof: break mainLoop
|
||||
of xmlElementClose: break
|
||||
else: nil
|
||||
else: discard
|
||||
x.next() # skip ``xmlElementClose``
|
||||
# now we have the description for the ``a`` element
|
||||
var desc = ""
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
import os, oids, tables, strutils
|
||||
import os, oids, tables, strutils, macros
|
||||
|
||||
import winlean
|
||||
|
||||
@@ -23,14 +23,13 @@ import sockets2, net
|
||||
# -- Futures
|
||||
|
||||
type
|
||||
PFutureVoid* = ref object of PObject
|
||||
cbVoid: proc () {.closure.}
|
||||
PFutureBase* = ref object of PObject
|
||||
cb: proc () {.closure.}
|
||||
finished: bool
|
||||
|
||||
PFuture*[T] = ref object of PFutureVoid
|
||||
PFuture*[T] = ref object of PFutureBase
|
||||
value: T
|
||||
error: ref EBase
|
||||
cb: proc (future: PFuture[T]) {.closure.}
|
||||
|
||||
proc newFuture*[T](): PFuture[T] =
|
||||
## Creates a new future.
|
||||
@@ -39,42 +38,38 @@ proc newFuture*[T](): PFuture[T] =
|
||||
|
||||
proc complete*[T](future: PFuture[T], val: T) =
|
||||
## Completes ``future`` with value ``val``.
|
||||
assert(not future.finished)
|
||||
assert(not future.finished, "Future already finished, cannot finish twice.")
|
||||
assert(future.error == nil)
|
||||
future.value = val
|
||||
future.finished = true
|
||||
if future.cb != nil:
|
||||
future.cb(future)
|
||||
if future.cbVoid != nil:
|
||||
future.cbVoid()
|
||||
future.cb()
|
||||
|
||||
proc fail*[T](future: PFuture[T], error: ref EBase) =
|
||||
## Completes ``future`` with ``error``.
|
||||
assert(not future.finished)
|
||||
assert(not future.finished, "Future already finished, cannot finish twice.")
|
||||
future.finished = true
|
||||
future.error = error
|
||||
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],
|
||||
cb: proc (future: PFuture[T]) {.closure.}) =
|
||||
## Sets the callback proc to be called when the future completes.
|
||||
##
|
||||
## If future has already completed then ``cb`` will be called immediately.
|
||||
future.cb = cb
|
||||
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()
|
||||
future.callback = proc () = cb(future)
|
||||
|
||||
proc read*[T](future: PFuture[T]): T =
|
||||
## Retrieves the value of ``future``. Future must be finished otherwise
|
||||
@@ -104,10 +99,12 @@ when defined(windows):
|
||||
|
||||
TCompletionData* = object
|
||||
sock: TSocketHandle
|
||||
cb: proc (sock: TSocketHandle, errcode: TOSErrorCode) {.closure.}
|
||||
cb: proc (sock: TSocketHandle, bytesTransferred: DWORD,
|
||||
errcode: TOSErrorCode) {.closure.}
|
||||
|
||||
PDispatcher* = ref object
|
||||
ioPort: THandle
|
||||
hasHandles: bool
|
||||
|
||||
TCustomOverlapped = object
|
||||
Internal*: DWORD
|
||||
@@ -129,9 +126,13 @@ when defined(windows):
|
||||
if CreateIOCompletionPort(sock.THandle, p.ioPort,
|
||||
cast[TCompletionKey](sock), 1) == 0:
|
||||
OSError(OSLastError())
|
||||
p.hasHandles = true
|
||||
|
||||
proc poll*(p: PDispatcher, timeout = 500) =
|
||||
## Waits for completion events and processes them.
|
||||
if not p.hasHandles:
|
||||
raise newException(EInvalidValue, "No handles registered in dispatcher.")
|
||||
|
||||
let llTimeout =
|
||||
if timeout == -1: winlean.INFINITE
|
||||
else: timeout.int32
|
||||
@@ -145,16 +146,19 @@ when defined(windows):
|
||||
# TODO: http://www.serverframework.com/handling-multiple-pending-socket-read-and-write-operations.html
|
||||
var customOverlapped = cast[PCustomOverlapped](lpOverlapped)
|
||||
if res:
|
||||
# This is useful for ensuring the reliability of the overlapped struct.
|
||||
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)
|
||||
else:
|
||||
let errCode = OSLastError()
|
||||
if lpOverlapped != nil:
|
||||
assert customOverlapped.data.sock == lpCompletionKey.TSocketHandle
|
||||
customOverlapped.data.cb(customOverlapped.data.sock,
|
||||
lpNumberOfBytesTransferred, errCode)
|
||||
dealloc(customOverlapped)
|
||||
customOverlapped.data.cb(customOverlapped.data.sock, errCode)
|
||||
else:
|
||||
if errCode.int32 == WAIT_TIMEOUT:
|
||||
# Timed out
|
||||
@@ -252,11 +256,12 @@ when defined(windows):
|
||||
# http://blogs.msdn.com/b/oldnewthing/archive/2011/02/02/10123392.aspx
|
||||
var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped)))
|
||||
ol.data = TCompletionData(sock: socket, cb:
|
||||
proc (sock: TSocketHandle, errcode: TOSErrorCode) =
|
||||
if errcode == TOSErrorCode(-1):
|
||||
retFuture.complete(0)
|
||||
else:
|
||||
retFuture.fail(newException(EOS, osErrorMsg(errcode)))
|
||||
proc (sock: TSocketHandle, bytesCount: DWord, errcode: TOSErrorCode) =
|
||||
if not retFuture.finished:
|
||||
if errcode == TOSErrorCode(-1):
|
||||
retFuture.complete(0)
|
||||
else:
|
||||
retFuture.fail(newException(EOS, osErrorMsg(errcode)))
|
||||
)
|
||||
|
||||
var ret = connectEx(socket, it.ai_addr, sizeof(TSockAddrIn).cint,
|
||||
@@ -265,7 +270,9 @@ when defined(windows):
|
||||
# Request to connect completed immediately.
|
||||
success = true
|
||||
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
|
||||
else:
|
||||
lastError = OSLastError()
|
||||
@@ -283,7 +290,8 @@ when defined(windows):
|
||||
retFuture.fail(newException(EOS, osErrorMsg(lastError)))
|
||||
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
|
||||
## all of the requested data is read.
|
||||
|
||||
@@ -293,31 +301,50 @@ when defined(windows):
|
||||
dataBuf.buf = newString(size)
|
||||
dataBuf.len = size
|
||||
|
||||
var bytesReceived, flags: DWord
|
||||
var bytesReceived: DWord
|
||||
var flagsio = flags.dword
|
||||
var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped)))
|
||||
ol.data = TCompletionData(sock: socket, cb:
|
||||
proc (sock: TSocketHandle, errcode: TOSErrorCode) =
|
||||
if errcode == TOSErrorCode(-1):
|
||||
var data = newString(size)
|
||||
copyMem(addr data[0], addr dataBuf.buf[0], size)
|
||||
retFuture.complete($data)
|
||||
else:
|
||||
retFuture.fail(newException(EOS, osErrorMsg(errcode)))
|
||||
proc (sock: TSocketHandle, bytesCount: DWord, errcode: TOSErrorCode) =
|
||||
if not retFuture.finished:
|
||||
if errcode == TOSErrorCode(-1):
|
||||
if bytesCount == 0 and dataBuf.buf[0] == '\0':
|
||||
retFuture.complete("")
|
||||
else:
|
||||
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,
|
||||
addr flags, cast[POverlapped](ol), nil)
|
||||
addr flagsio, cast[POverlapped](ol), nil)
|
||||
if ret == -1:
|
||||
let err = OSLastError()
|
||||
if err.int32 != ERROR_IO_PENDING:
|
||||
retFuture.fail(newException(EOS, osErrorMsg(err)))
|
||||
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:
|
||||
# Request to read completed immediately.
|
||||
var data = newString(size)
|
||||
copyMem(addr data[0], addr dataBuf.buf[0], size)
|
||||
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
|
||||
|
||||
proc send*(p: PDispatcher, socket: TSocketHandle, data: string): PFuture[int] =
|
||||
@@ -332,11 +359,12 @@ when defined(windows):
|
||||
var bytesReceived, flags: DWord
|
||||
var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped)))
|
||||
ol.data = TCompletionData(sock: socket, cb:
|
||||
proc (sock: TSocketHandle, errcode: TOSErrorCode) =
|
||||
if errcode == TOSErrorCode(-1):
|
||||
retFuture.complete(0)
|
||||
else:
|
||||
retFuture.fail(newException(EOS, osErrorMsg(errcode)))
|
||||
proc (sock: TSocketHandle, bytesCount: DWord, errcode: TOSErrorCode) =
|
||||
if not retFuture.finished:
|
||||
if errcode == TOSErrorCode(-1):
|
||||
retFuture.complete(0)
|
||||
else:
|
||||
retFuture.fail(newException(EOS, osErrorMsg(errcode)))
|
||||
)
|
||||
|
||||
let ret = WSASend(socket, addr dataBuf, 1, addr bytesReceived,
|
||||
@@ -348,7 +376,9 @@ when defined(windows):
|
||||
dealloc(ol)
|
||||
else:
|
||||
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
|
||||
|
||||
proc acceptAddr*(p: PDispatcher, socket: TSocketHandle):
|
||||
@@ -390,11 +420,12 @@ when defined(windows):
|
||||
|
||||
var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped)))
|
||||
ol.data = TCompletionData(sock: socket, cb:
|
||||
proc (sock: TSocketHandle, errcode: TOSErrorCode) =
|
||||
if errcode == TOSErrorCode(-1):
|
||||
completeAccept()
|
||||
else:
|
||||
retFuture.fail(newException(EOS, osErrorMsg(errcode)))
|
||||
proc (sock: TSocketHandle, bytesCount: DWord, errcode: TOSErrorCode) =
|
||||
if not retFuture.finished:
|
||||
if errcode == TOSErrorCode(-1):
|
||||
completeAccept()
|
||||
else:
|
||||
retFuture.fail(newException(EOS, osErrorMsg(errcode)))
|
||||
)
|
||||
|
||||
# http://msdn.microsoft.com/en-us/library/windows/desktop/ms737524%28v=vs.85%29.aspx
|
||||
@@ -411,7 +442,9 @@ when defined(windows):
|
||||
dealloc(ol)
|
||||
else:
|
||||
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
|
||||
|
||||
@@ -434,6 +467,199 @@ when defined(windows):
|
||||
else:
|
||||
# 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:
|
||||
|
||||
@@ -442,39 +668,65 @@ when isMainModule:
|
||||
#sock.setBlocking false
|
||||
p.register(sock)
|
||||
|
||||
when true:
|
||||
|
||||
var f = p.connect(sock, "irc.freenode.org", TPort(6667))
|
||||
f.callback =
|
||||
proc (future: PFuture[int]) =
|
||||
echo("Connected in future!")
|
||||
echo(future.read)
|
||||
for i in 0 .. 50:
|
||||
var recvF = p.recv(sock, 10)
|
||||
recvF.callback =
|
||||
proc (future: PFuture[string]) =
|
||||
echo("Read: ", future.read)
|
||||
when false:
|
||||
# Await tests
|
||||
proc main(p: PDispatcher): PFuture[int] {.async.} =
|
||||
discard await p.connect(sock, "irc.freenode.net", TPort(6667))
|
||||
while true:
|
||||
var line = await p.recvLine(sock)
|
||||
echo("Line is: ", line.repr)
|
||||
if line == "":
|
||||
echo "Disconnected"
|
||||
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:
|
||||
when false:
|
||||
|
||||
sock.bindAddr(TPort(6667))
|
||||
sock.listen()
|
||||
proc onAccept(future: PFuture[TSocketHandle]) =
|
||||
echo "Accepted"
|
||||
var t = p.send(future.read, "test\c\L")
|
||||
t.callback =
|
||||
var f = p.connect(sock, "irc.freenode.org", TPort(6667))
|
||||
f.callback =
|
||||
proc (future: PFuture[int]) =
|
||||
echo("Connected in future!")
|
||||
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)
|
||||
f.callback = onAccept
|
||||
|
||||
var f = p.accept(sock)
|
||||
f.callback = onAccept
|
||||
|
||||
while true:
|
||||
p.poll()
|
||||
echo "polled"
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -557,6 +557,119 @@ proc `$`*(m: TMonth): string =
|
||||
"November", "December"]
|
||||
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 =
|
||||
## This function formats `info` as specified by `f`. The following format
|
||||
## 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``
|
||||
## ========== ================================================================================= ================================================
|
||||
##
|
||||
## Other strings can be inserted by putting them in ``''``. For example ``hh'->'mm`` will give ``01->56``.
|
||||
## The following characters can be inserted without quoting them: ``:`` ``-`` ``(`` ``)`` ``/`` ``[`` ``]`` ``,``
|
||||
## Other strings can be inserted by putting them in ``''``. For example
|
||||
## ``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 = ""
|
||||
var i = 0
|
||||
@@ -600,112 +716,8 @@ proc format*(info: TTimeInfo, f: string): string =
|
||||
while true:
|
||||
case f[i]
|
||||
of ' ', '-', '/', ':', '\'', '\0', '(', ')', '[', ']', ',':
|
||||
case currentF
|
||||
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)
|
||||
|
||||
format_token(info, currentF, result)
|
||||
|
||||
currentF = ""
|
||||
if f[i] == '\0': break
|
||||
|
||||
@@ -716,7 +728,15 @@ proc format*(info: TTimeInfo, f: string): string =
|
||||
inc(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)
|
||||
|
||||
{.pop.}
|
||||
@@ -727,11 +747,15 @@ when isMainModule:
|
||||
|
||||
var t = getGMTime(fromSeconds(2147483647))
|
||||
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 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" &
|
||||
" 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"
|
||||
|
||||
assert t.format("yyyyMMddhhmmss") == "20380119031407"
|
||||
|
||||
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" &
|
||||
|
||||
@@ -199,14 +199,14 @@ else:
|
||||
importc: "GetCurrentDirectoryA", dynlib: "kernel32", stdcall.}
|
||||
proc setCurrentDirectoryA*(lpPathName: cstring): int32 {.
|
||||
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.}
|
||||
proc removeDirectoryA*(lpPathName: cstring): int32 {.
|
||||
importc: "RemoveDirectoryA", dynlib: "kernel32", stdcall.}
|
||||
proc setEnvironmentVariableA*(lpName, lpValue: cstring): int32 {.
|
||||
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.}
|
||||
|
||||
when useWinUnicode:
|
||||
@@ -304,7 +304,7 @@ else:
|
||||
dwFileAttributes: int32): WINBOOL {.
|
||||
stdcall, dynlib: "kernel32", importc: "SetFileAttributesA".}
|
||||
|
||||
proc copyFileA*(lpExistingFileName, lpNewFileName: cstring,
|
||||
proc copyFileA*(lpExistingFileName, lpNewFileName: CString,
|
||||
bFailIfExists: cint): cint {.
|
||||
importc: "CopyFileA", stdcall, dynlib: "kernel32".}
|
||||
|
||||
|
||||
@@ -7,4 +7,4 @@ type
|
||||
proc ha() =
|
||||
var
|
||||
x: TExport # no error
|
||||
nil
|
||||
discard
|
||||
|
||||
@@ -4,4 +4,4 @@ type
|
||||
TExport* = enum x, y, z
|
||||
|
||||
proc foo*(x: int) =
|
||||
nil
|
||||
discard
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
type
|
||||
TExport* = enum x, y, z # exactly the same type!
|
||||
|
||||
proc foo*(x: int) = nil
|
||||
proc foo*(x: int) = discard
|
||||
|
||||
64
tests/async/tasyncawait.nim
Normal file
64
tests/async/tasyncawait.nim
Normal 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
|
||||
@@ -19,8 +19,8 @@ of eB, eC: write(stdout, "b or c")
|
||||
case x
|
||||
of "Andreas", "Rumpf": write(stdout, "Hallo Meister!")
|
||||
of "aa", "bb": write(stdout, "Du bist nicht mein Meister")
|
||||
of "cc", "hash", "when": nil
|
||||
of "will", "it", "finally", "be", "generated": nil
|
||||
of "cc", "hash", "when": discard
|
||||
of "will", "it", "finally", "be", "generated": discard
|
||||
|
||||
var z = case i
|
||||
of 1..5, 8, 9: "aa"
|
||||
|
||||
@@ -12,7 +12,7 @@ var
|
||||
thr: array [0..5, TThread[tuple[a, b: int]]]
|
||||
L, M, N: TLock
|
||||
|
||||
proc doNothing() = nil
|
||||
proc doNothing() = discard
|
||||
|
||||
proc threadFunc(interval: tuple[a, b: int]) {.thread.} =
|
||||
doNothing()
|
||||
|
||||
@@ -12,6 +12,13 @@ myobj destroyed
|
||||
----
|
||||
mygeneric3 constructed
|
||||
mygeneric1 destroyed
|
||||
----
|
||||
mygeneric1 destroyed
|
||||
----
|
||||
myobj destroyed
|
||||
----
|
||||
----
|
||||
myobj destroyed
|
||||
'''
|
||||
"""
|
||||
|
||||
@@ -31,6 +38,22 @@ type
|
||||
x: A
|
||||
y: B
|
||||
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.} =
|
||||
if o.p != nil: dealloc o.p
|
||||
@@ -57,13 +80,13 @@ proc mygeneric1() =
|
||||
echo "mygeneric1 constructed"
|
||||
|
||||
proc mygeneric2[T](val: T) =
|
||||
var
|
||||
a = open()
|
||||
b = TMyGeneric2[int, T](x: 10, y: val)
|
||||
c = TMyGeneric3[int, int, string](x: 10, y: 20, z: "test")
|
||||
|
||||
var a = open()
|
||||
|
||||
var b = TMyGeneric2[int, T](x: 10, y: val)
|
||||
echo "mygeneric2 constructed"
|
||||
|
||||
var c = TMyGeneric3[int, int, string](x: 10, y: 20, z: "test")
|
||||
|
||||
proc mygeneric3 =
|
||||
var x = TMyGeneric3[int, string, TMyGeneric1[int]](
|
||||
x: 10, y: "test", z: TMyGeneric1[int](x: 10))
|
||||
@@ -82,3 +105,24 @@ mygeneric2[int](10)
|
||||
echo "----"
|
||||
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()
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ type
|
||||
PDict[TK, TV] = ref TDict[TK, TV]
|
||||
|
||||
proc fakeNew[T](x: var ref T, destroy: proc (a: ref T) {.nimcall.}) =
|
||||
nil
|
||||
discard
|
||||
|
||||
proc destroyDict[TK, TV](a: PDict[TK, TV]) =
|
||||
return
|
||||
|
||||
45
tests/exception/texceptionbreak.nim
Normal file
45
tests/exception/texceptionbreak.nim
Normal 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"
|
||||
@@ -32,7 +32,7 @@ const
|
||||
proc len[T,D] (n:PNode[T,D]): Int {.inline.} =
|
||||
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.} =
|
||||
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 cLenCenter : setLen(n.slots, cLenCenter)
|
||||
of cLen4 : setLen(n.slots, cLen4)
|
||||
else: nil
|
||||
else: discard
|
||||
Result = n
|
||||
|
||||
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 cLenCenter: setLen(APath.Nd.slots, cLen4)
|
||||
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])
|
||||
APath.Nd.slots[x] = setItem(AKey, AValue, ANode)
|
||||
|
||||
|
||||
10
tests/generics/tgenericlambda.nim
Normal file
10
tests/generics/tgenericlambda.nim
Normal 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
|
||||
18
tests/generics/tmetafield.nim
Normal file
18
tests/generics/tmetafield.nim
Normal 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
|
||||
@@ -1,28 +1,28 @@
|
||||
template foo(a: int, b: string) = nil
|
||||
template foo(a: int, b: string) = discard
|
||||
foo(1, "test")
|
||||
|
||||
proc bar(a: int, b: string) = nil
|
||||
proc bar(a: int, b: string) = discard
|
||||
bar(1, "test")
|
||||
|
||||
template foo(a: int, b: string) = bar(a, b)
|
||||
foo(1, "test")
|
||||
|
||||
block:
|
||||
proc bar(a: int, b: string) = nil
|
||||
template foo(a: int, b: string) = nil
|
||||
proc bar(a: int, b: string) = discard
|
||||
template foo(a: int, b: string) = discard
|
||||
foo(1, "test")
|
||||
bar(1, "test")
|
||||
|
||||
proc baz =
|
||||
proc foo(a: int, b: string) = nil
|
||||
proc foo(a: int, b: string) = discard
|
||||
proc foo(b: string) =
|
||||
template bar(a: int, b: string) = nil
|
||||
template bar(a: int, b: string) = discard
|
||||
bar(1, "test")
|
||||
|
||||
foo("test")
|
||||
|
||||
block:
|
||||
proc foo(b: string) = nil
|
||||
proc foo(b: string) = discard
|
||||
foo("test")
|
||||
foo(1, "test")
|
||||
|
||||
|
||||
@@ -26,3 +26,16 @@ foo 10
|
||||
foo "test"
|
||||
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))
|
||||
|
||||
|
||||
@@ -14,8 +14,8 @@ type
|
||||
TSomethingElse = object
|
||||
PSomethingElse = ref TSomethingElse
|
||||
|
||||
method foo(a: PNode, b: PSomethingElse) = nil
|
||||
method foo(a: PNodeFoo, b: PSomethingElse) = nil
|
||||
method foo(a: PNode, b: PSomethingElse) = discard
|
||||
method foo(a: PNodeFoo, b: PSomethingElse) = discard
|
||||
|
||||
var o: TObject
|
||||
o.somethin()
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
discard """
|
||||
file: "toverwr.nim"
|
||||
output: "hello"
|
||||
"""
|
||||
discard """
|
||||
file: "toverwr.nim"
|
||||
output: "hello"
|
||||
"""
|
||||
# Test the overloading resolution in connection with a qualifier
|
||||
|
||||
proc write(t: TFile, s: string) =
|
||||
nil # a nop
|
||||
discard # a nop
|
||||
|
||||
system.write(stdout, "hello")
|
||||
#OUT hello
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ discard """
|
||||
output: "12false3ha"
|
||||
"""
|
||||
|
||||
proc f(x: varargs[string, `$`]) = nil
|
||||
proc f(x: varargs[string, `$`]) = discard
|
||||
template optF{f(X)}(x: varargs[expr]) =
|
||||
writeln(stdout, x)
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ type
|
||||
TRange = range[0..40]
|
||||
|
||||
proc p(r: TRange) =
|
||||
nil
|
||||
discard
|
||||
|
||||
var
|
||||
r: TRange
|
||||
|
||||
@@ -8,7 +8,7 @@ type
|
||||
TRange = range[0..40]
|
||||
|
||||
proc p(r: TRange) =
|
||||
nil
|
||||
discard
|
||||
|
||||
var
|
||||
r: TRange
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
discard """
|
||||
file: "tsets.nim"
|
||||
output: "Ha ein F ist in s!"
|
||||
"""
|
||||
discard """
|
||||
file: "tsets.nim"
|
||||
output: "Ha ein F ist in s!"
|
||||
"""
|
||||
# Test the handling of sets
|
||||
|
||||
import
|
||||
@@ -38,7 +38,7 @@ type
|
||||
TTokTypes* = set[TTokTypeRange]
|
||||
|
||||
const
|
||||
toktypes: TTokTypes = {TTokTypeRange(tkSymbol)..pred(tkIntLit),
|
||||
toktypes: TTokTypes = {TTokTypeRange(tkSymbol)..pred(tkIntLit),
|
||||
tkStrLit..tkTripleStrLit}
|
||||
|
||||
var
|
||||
@@ -51,14 +51,14 @@ else: write(stdout, "BUG: F ist nicht in s!\n")
|
||||
a = {} #{'a'..'z'}
|
||||
for x in low(TAZ) .. high(TAZ):
|
||||
incl(a, x)
|
||||
if x in a: nil
|
||||
if x in a: discard
|
||||
else: write(stdout, "BUG: something not in a!\n")
|
||||
|
||||
for x in low(TTokTypeRange) .. high(TTokTypeRange):
|
||||
if x in tokTypes:
|
||||
nil
|
||||
discard
|
||||
#writeln(stdout, "the token '$1' is in the set" % repr(x))
|
||||
|
||||
#OUT Ha ein F ist in s!
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
66
tests/specialops/tdotops.nim
Normal file
66
tests/specialops/tdotops.nim
Normal 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)))
|
||||
|
||||
@@ -183,7 +183,7 @@ type
|
||||
channel: string
|
||||
timestamp: TTime
|
||||
case kind*: TSeenType
|
||||
of PSeenJoin: nil
|
||||
of PSeenJoin: discard
|
||||
of PSeenPart, PSeenQuit, PSeenMsg:
|
||||
msg: string
|
||||
of PSeenNick:
|
||||
@@ -200,7 +200,7 @@ proc setSeen(d: TDb, s: TSeen) =
|
||||
var hashToSet = @[("type", $s.kind.int), ("channel", s.channel),
|
||||
("timestamp", $s.timestamp.int)]
|
||||
case s.kind
|
||||
of PSeenJoin: nil
|
||||
of PSeenJoin: discard
|
||||
of PSeenPart, PSeenMsg, PSeenQuit:
|
||||
hashToSet.add(("msg", s.msg))
|
||||
of PSeenNick:
|
||||
@@ -338,7 +338,7 @@ proc hubConnect(state: PState) =
|
||||
|
||||
proc handleIrc(irc: PAsyncIRC, event: TIRCEvent, state: PState) =
|
||||
case event.typ
|
||||
of EvConnected: nil
|
||||
of EvConnected: discard
|
||||
of EvDisconnected:
|
||||
while not state.ircClient.isConnected:
|
||||
try:
|
||||
@@ -424,7 +424,7 @@ proc handleIrc(irc: PAsyncIRC, event: TIRCEvent, state: PState) =
|
||||
seenNick.newNick = event.params[0]
|
||||
state.database.setSeen(seenNick)
|
||||
else:
|
||||
nil # TODO: ?
|
||||
discard # TODO: ?
|
||||
|
||||
proc open(port: TPort = TPort(5123)): PState =
|
||||
var res: PState
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# tests for the interpreter
|
||||
|
||||
proc loops(a: var int) =
|
||||
nil
|
||||
discard
|
||||
#var
|
||||
# b: int
|
||||
#b = glob
|
||||
|
||||
@@ -7,6 +7,6 @@ proc walkDirTree(root: string) =
|
||||
case k
|
||||
of pcFile, pcLinkToFile: echo(f)
|
||||
of pcDir: walkDirTree(f)
|
||||
of pcLinkToDir: nil
|
||||
of pcLinkToDir: discard
|
||||
|
||||
walkDirTree(".")
|
||||
|
||||
@@ -72,7 +72,7 @@ type
|
||||
rule: TNode ## the rule that the symbol refers to
|
||||
TNode {.final, shallow.} = object
|
||||
case kind: TPegKind
|
||||
of pkEmpty..pkWhitespace: nil
|
||||
of pkEmpty..pkWhitespace: discard
|
||||
of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle: term: string
|
||||
of pkChar, pkGreedyRepChar: ch: 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 =
|
||||
result.kind = a.kind
|
||||
case a.kind
|
||||
of pkEmpty..pkWhitespace: nil
|
||||
of pkEmpty..pkWhitespace: discard
|
||||
of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle:
|
||||
result.term = a.term
|
||||
of pkChar, pkGreedyRepChar:
|
||||
@@ -229,7 +229,7 @@ when false:
|
||||
case a.kind
|
||||
of pkEmpty, pkAny, pkAnyRune, pkGreedyAny, pkNewLine, pkTerminal,
|
||||
pkTerminalIgnoreCase, pkTerminalIgnoreStyle, pkChar, pkGreedyRepChar,
|
||||
pkCharChoice, pkGreedyRepSet: nil
|
||||
pkCharChoice, pkGreedyRepSet: discard
|
||||
of pkNonTerminal: return true
|
||||
else:
|
||||
for i in 0..a.sons.len-1:
|
||||
@@ -318,7 +318,7 @@ proc backrefIgnoreStyle*(index: range[1..MaxSubPatterns]): TPeg {.
|
||||
|
||||
proc spaceCost(n: TPeg): int =
|
||||
case n.kind
|
||||
of pkEmpty: nil
|
||||
of pkEmpty: discard
|
||||
of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle, pkChar,
|
||||
pkGreedyRepChar, pkCharChoice, pkGreedyRepSet,
|
||||
pkAny..pkWhitespace, pkGreedyAny:
|
||||
@@ -1111,7 +1111,7 @@ proc handleHexChar(c: var TPegLexer, xi: var int) =
|
||||
of 'A'..'F':
|
||||
xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('A') + 10)
|
||||
inc(c.bufpos)
|
||||
else: nil
|
||||
else: discard
|
||||
|
||||
proc getEscapedChar(c: var TPegLexer, tok: var TToken) =
|
||||
inc(c.bufpos)
|
||||
@@ -1341,7 +1341,7 @@ proc getTok(c: var TPegLexer, tok: var TToken) =
|
||||
of "i": tok.modifier = modIgnoreCase
|
||||
of "y": tok.modifier = modIgnoreStyle
|
||||
of "v": tok.modifier = modVerbatim
|
||||
else: nil
|
||||
else: discard
|
||||
setLen(tok.literal, 0)
|
||||
if c.buf[c.bufpos] == '$':
|
||||
getDollar(c, tok)
|
||||
@@ -1488,7 +1488,7 @@ proc primary(p: var TPegParser): TPeg =
|
||||
of tkCurlyAt:
|
||||
getTok(p)
|
||||
return !*\primary(p).token(p)
|
||||
else: nil
|
||||
else: discard
|
||||
case p.tok.kind
|
||||
of tkIdentifier:
|
||||
if p.identIsVerbatim:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Test if the new table constructor syntax works:
|
||||
|
||||
template ignoreExpr(e: expr): stmt {.immediate.} =
|
||||
nil
|
||||
discard
|
||||
|
||||
# test first class '..' syntactical citizen:
|
||||
ignoreExpr x <> 2..4
|
||||
|
||||
@@ -9,7 +9,7 @@ proc init: TYourObj =
|
||||
result.y = -1
|
||||
|
||||
proc f(x: var TYourObj) =
|
||||
nil
|
||||
discard
|
||||
|
||||
var m: TMyObj = init()
|
||||
f(m)
|
||||
|
||||
@@ -249,7 +249,7 @@ proc walker(dir: string) =
|
||||
of pcDir:
|
||||
if optRecursive in options:
|
||||
walker(path)
|
||||
else: nil
|
||||
else: discard
|
||||
if existsFile(dir): processFile(dir)
|
||||
|
||||
proc writeHelp() =
|
||||
|
||||
@@ -68,8 +68,8 @@ Language Additions
|
||||
- Exported templates are allowed to access hidden fields.
|
||||
- The ``using statement`` enables you to more easily author domain-specific
|
||||
languages and libraries providing OOP-like syntactic sugar.
|
||||
- Added a new ``delegator pragma`` for handling calls to missing procs and
|
||||
fields at compile-time.
|
||||
- Added the possibility to override various dot operators in order to handle
|
||||
calls to missing procs and reads from undeclared fields at compile-time.
|
||||
- The overload resolution now supports ``static[T]`` params that must be
|
||||
evaluable at compile-time.
|
||||
- Support for user-defined type classes has been added.
|
||||
|
||||
Reference in New Issue
Block a user