Merge branch 'devel' of https://github.com/Araq/Nimrod into devel
This commit is contained in:
commit
3270676e77
34 changed files with 567 additions and 167 deletions
|
|
@ -818,6 +818,9 @@ type
|
||||||
counter*: int
|
counter*: int
|
||||||
data*: TObjectSeq
|
data*: TObjectSeq
|
||||||
|
|
||||||
|
TImplication* = enum
|
||||||
|
impUnknown, impNo, impYes
|
||||||
|
|
||||||
# BUGFIX: a module is overloadable so that a proc can have the
|
# BUGFIX: a module is overloadable so that a proc can have the
|
||||||
# same name as an imported module. This is necessary because of
|
# same name as an imported module. This is necessary because of
|
||||||
# the poor naming choices in the standard library.
|
# the poor naming choices in the standard library.
|
||||||
|
|
@ -865,6 +868,7 @@ const
|
||||||
nkCallKinds* = {nkCall, nkInfix, nkPrefix, nkPostfix,
|
nkCallKinds* = {nkCall, nkInfix, nkPrefix, nkPostfix,
|
||||||
nkCommand, nkCallStrLit, nkHiddenCallConv}
|
nkCommand, nkCallStrLit, nkHiddenCallConv}
|
||||||
|
|
||||||
|
nkLiterals* = {nkCharLit..nkTripleStrLit}
|
||||||
nkLambdaKinds* = {nkLambda, nkDo}
|
nkLambdaKinds* = {nkLambda, nkDo}
|
||||||
declarativeDefs* = {nkProcDef, nkMethodDef, nkIteratorDef, nkConverterDef}
|
declarativeDefs* = {nkProcDef, nkMethodDef, nkIteratorDef, nkConverterDef}
|
||||||
procDefs* = nkLambdaKinds + declarativeDefs
|
procDefs* = nkLambdaKinds + declarativeDefs
|
||||||
|
|
|
||||||
|
|
@ -157,6 +157,12 @@ proc leValue*(a, b: PNode): bool =
|
||||||
#InternalError(a.info, "leValue")
|
#InternalError(a.info, "leValue")
|
||||||
discard
|
discard
|
||||||
|
|
||||||
|
proc weakLeValue*(a, b: PNode): TImplication =
|
||||||
|
if a.kind notin nkLiterals or b.kind notin nkLiterals:
|
||||||
|
result = impUnknown
|
||||||
|
else:
|
||||||
|
result = if leValue(a, b): impYes else: impNo
|
||||||
|
|
||||||
proc lookupInRecord(n: PNode, field: PIdent): PSym =
|
proc lookupInRecord(n: PNode, field: PIdent): PSym =
|
||||||
result = nil
|
result = nil
|
||||||
case n.kind
|
case n.kind
|
||||||
|
|
|
||||||
|
|
@ -274,10 +274,6 @@ proc pred(n: PNode): PNode =
|
||||||
else:
|
else:
|
||||||
result = n
|
result = n
|
||||||
|
|
||||||
type
|
|
||||||
TImplication* = enum
|
|
||||||
impUnknown, impNo, impYes
|
|
||||||
|
|
||||||
proc impliesEq(fact, eq: PNode): TImplication =
|
proc impliesEq(fact, eq: PNode): TImplication =
|
||||||
let (loc, val) = if isLocation(eq.sons[1]): (1, 2) else: (2, 1)
|
let (loc, val) = if isLocation(eq.sons[1]): (1, 2) else: (2, 1)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,7 @@ type
|
||||||
errOnlyACallOpCanBeDelegator, errUsingNoSymbol,
|
errOnlyACallOpCanBeDelegator, errUsingNoSymbol,
|
||||||
errMacroBodyDependsOnGenericTypes,
|
errMacroBodyDependsOnGenericTypes,
|
||||||
errDestructorNotGenericEnough,
|
errDestructorNotGenericEnough,
|
||||||
|
errInlineIteratorsAsProcParams,
|
||||||
errXExpectsTwoArguments,
|
errXExpectsTwoArguments,
|
||||||
errXExpectsObjectTypes, errXcanNeverBeOfThisSubtype, errTooManyIterations,
|
errXExpectsObjectTypes, errXcanNeverBeOfThisSubtype, errTooManyIterations,
|
||||||
errCannotInterpretNodeX, errFieldXNotFound, errInvalidConversionFromTypeX,
|
errCannotInterpretNodeX, errFieldXNotFound, errInvalidConversionFromTypeX,
|
||||||
|
|
@ -331,6 +331,8 @@ const
|
||||||
"because the parameter '$1' has a generic type",
|
"because the parameter '$1' has a generic type",
|
||||||
errDestructorNotGenericEnough: "Destructor signarue is too specific. " &
|
errDestructorNotGenericEnough: "Destructor signarue is too specific. " &
|
||||||
"A destructor must be associated will all instantiations of a generic type",
|
"A destructor must be associated will all instantiations of a generic type",
|
||||||
|
errInlineIteratorsAsProcParams: "inline iterators can be used as parameters only for " &
|
||||||
|
"templates, macros and other inline iterators",
|
||||||
errXExpectsTwoArguments: "\'$1\' expects two arguments",
|
errXExpectsTwoArguments: "\'$1\' expects two arguments",
|
||||||
errXExpectsObjectTypes: "\'$1\' expects object types",
|
errXExpectsObjectTypes: "\'$1\' expects object types",
|
||||||
errXcanNeverBeOfThisSubtype: "\'$1\' can never be of this subtype",
|
errXcanNeverBeOfThisSubtype: "\'$1\' can never be of this subtype",
|
||||||
|
|
|
||||||
|
|
@ -139,6 +139,10 @@ proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym =
|
||||||
result = n.sym
|
result = n.sym
|
||||||
internalAssert sfGenSym in result.flags
|
internalAssert sfGenSym in result.flags
|
||||||
internalAssert result.kind == kind
|
internalAssert result.kind == kind
|
||||||
|
# when there is a nested proc inside a template, semtmpl
|
||||||
|
# will assign a wrong owner during the first pass over the
|
||||||
|
# template; we must fix it here: see #909
|
||||||
|
result.owner = getCurrOwner()
|
||||||
else:
|
else:
|
||||||
result = newSym(kind, considerAcc(n), getCurrOwner(), n.info)
|
result = newSym(kind, considerAcc(n), getCurrOwner(), n.info)
|
||||||
|
|
||||||
|
|
@ -194,7 +198,8 @@ proc fixupTypeAfterEval(c: PContext, evaluated, eOrig: PNode): PNode =
|
||||||
result = semExprWithType(c, evaluated)
|
result = semExprWithType(c, evaluated)
|
||||||
else:
|
else:
|
||||||
result = evaluated
|
result = evaluated
|
||||||
semmacrosanity.annotateType(result, eOrig.typ)
|
let expectedType = eOrig.typ.skipTypes({tyStatic})
|
||||||
|
semmacrosanity.annotateType(result, expectedType)
|
||||||
else:
|
else:
|
||||||
result = semExprWithType(c, evaluated)
|
result = semExprWithType(c, evaluated)
|
||||||
#result = fitNode(c, e.typ, result) inlined with special case:
|
#result = fitNode(c, e.typ, result) inlined with special case:
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ proc notFoundError*(c: PContext, n: PNode, errors: seq[string]) =
|
||||||
# fail fast:
|
# fail fast:
|
||||||
globalError(n.info, errTypeMismatch, "")
|
globalError(n.info, errTypeMismatch, "")
|
||||||
var result = msgKindToString(errTypeMismatch)
|
var result = msgKindToString(errTypeMismatch)
|
||||||
add(result, describeArgs(c, n, 1 + ord(nfDotField in n.flags)))
|
add(result, describeArgs(c, n, 1))
|
||||||
add(result, ')')
|
add(result, ')')
|
||||||
|
|
||||||
var candidates = ""
|
var candidates = ""
|
||||||
|
|
@ -175,9 +175,15 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
|
||||||
localError(n.info, errExprXCannotBeCalled,
|
localError(n.info, errExprXCannotBeCalled,
|
||||||
renderTree(n, {renderNoComments}))
|
renderTree(n, {renderNoComments}))
|
||||||
else:
|
else:
|
||||||
|
if {nfDotField, nfDotSetter} * n.flags != {}:
|
||||||
|
# clean up the inserted ops
|
||||||
|
n.sons.delete(2)
|
||||||
|
n.sons[0] = f
|
||||||
|
|
||||||
errors = @[]
|
errors = @[]
|
||||||
pickBest(f)
|
pickBest(f)
|
||||||
notFoundError(c, n, errors)
|
notFoundError(c, n, errors)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
if alt.state == csMatch and cmpCandidates(result, alt) == 0 and
|
if alt.state == csMatch and cmpCandidates(result, alt) == 0 and
|
||||||
|
|
|
||||||
|
|
@ -899,6 +899,21 @@ proc makeDeref(n: PNode): PNode =
|
||||||
addSon(result, a)
|
addSon(result, a)
|
||||||
t = skipTypes(t.sons[0], {tyGenericInst})
|
t = skipTypes(t.sons[0], {tyGenericInst})
|
||||||
|
|
||||||
|
proc readTypeParameter(c: PContext, ty: PType,
|
||||||
|
paramName: PIdent, info: TLineInfo): PNode =
|
||||||
|
internalAssert ty.kind == tyGenericInst
|
||||||
|
let ty = ty.skipGenericAlias
|
||||||
|
let tbody = ty.sons[0]
|
||||||
|
for s in countup(0, tbody.len-2):
|
||||||
|
let tParam = tbody.sons[s]
|
||||||
|
if tParam.sym.name == paramName:
|
||||||
|
let rawTyp = ty.sons[s + 1]
|
||||||
|
if rawTyp.kind == tyStatic:
|
||||||
|
return rawTyp.n
|
||||||
|
else:
|
||||||
|
let foundTyp = makeTypeDesc(c, rawTyp)
|
||||||
|
return newSymNode(copySym(tParam.sym).linkTo(foundTyp), info)
|
||||||
|
|
||||||
proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
## returns nil if it's not a built-in field access
|
## returns nil if it's not a built-in field access
|
||||||
checkSonsLen(n, 2)
|
checkSonsLen(n, 2)
|
||||||
|
|
@ -916,7 +931,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
var ty = n.sons[0].typ
|
var ty = n.sons[0].typ
|
||||||
var f: PSym = nil
|
var f: PSym = nil
|
||||||
result = nil
|
result = nil
|
||||||
if isTypeExpr(n.sons[0]) or ty.kind == tyTypeDesc and ty.base.kind != tyNone:
|
if isTypeExpr(n.sons[0]) or (ty.kind == tyTypeDesc and ty.base.kind != tyNone):
|
||||||
if ty.kind == tyTypeDesc: ty = ty.base
|
if ty.kind == tyTypeDesc: ty = ty.base
|
||||||
case ty.kind
|
case ty.kind
|
||||||
of tyEnum:
|
of tyEnum:
|
||||||
|
|
@ -932,21 +947,10 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
markUsed(n, f)
|
markUsed(n, f)
|
||||||
return
|
return
|
||||||
of tyGenericInst:
|
of tyGenericInst:
|
||||||
assert ty.sons[0].kind == tyGenericBody
|
return readTypeParameter(c, ty, i, n.info)
|
||||||
let tbody = ty.sons[0]
|
|
||||||
for s in countup(0, tbody.len-2):
|
|
||||||
let tParam = tbody.sons[s]
|
|
||||||
if tParam.sym.name == i:
|
|
||||||
let rawTyp = ty.sons[s + 1]
|
|
||||||
if rawTyp.kind == tyStatic:
|
|
||||||
return rawTyp.n
|
|
||||||
else:
|
|
||||||
let foundTyp = makeTypeDesc(c, rawTyp)
|
|
||||||
return newSymNode(copySym(tParam.sym).linkTo(foundTyp), n.info)
|
|
||||||
return
|
|
||||||
of tyObject, tyTuple:
|
of tyObject, tyTuple:
|
||||||
if ty.n.kind == nkRecList:
|
if ty.n.kind == nkRecList:
|
||||||
for field in ty.n.sons:
|
for field in ty.n:
|
||||||
if field.sym.name == i:
|
if field.sym.name == i:
|
||||||
n.typ = newTypeWithSons(c, tyFieldAccessor, @[ty, field.sym.typ])
|
n.typ = newTypeWithSons(c, tyFieldAccessor, @[ty, field.sym.typ])
|
||||||
n.typ.n = copyTree(n)
|
n.typ.n = copyTree(n)
|
||||||
|
|
@ -958,6 +962,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
# XXX: This is probably not relevant any more
|
# XXX: This is probably not relevant any more
|
||||||
# reset to prevent 'nil' bug: see "tests/reject/tenumitems.nim":
|
# reset to prevent 'nil' bug: see "tests/reject/tenumitems.nim":
|
||||||
ty = n.sons[0].typ
|
ty = n.sons[0].typ
|
||||||
|
return nil
|
||||||
|
|
||||||
ty = skipTypes(ty, {tyGenericInst, tyVar, tyPtr, tyRef})
|
ty = skipTypes(ty, {tyGenericInst, tyVar, tyPtr, tyRef})
|
||||||
var check: PNode = nil
|
var check: PNode = nil
|
||||||
|
|
@ -990,6 +995,10 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
n.typ = f.typ
|
n.typ = f.typ
|
||||||
result = n
|
result = n
|
||||||
|
|
||||||
|
# we didn't find any field, let's look for a generic param
|
||||||
|
if result == nil and n.sons[0].typ.kind == tyGenericInst:
|
||||||
|
result = readTypeParameter(c, n.sons[0].typ, i, n.info)
|
||||||
|
|
||||||
proc dotTransformation(c: PContext, n: PNode): PNode =
|
proc dotTransformation(c: PContext, n: PNode): PNode =
|
||||||
if isSymChoice(n.sons[1]):
|
if isSymChoice(n.sons[1]):
|
||||||
result = newNodeI(nkDotCall, n.info)
|
result = newNodeI(nkDotCall, n.info)
|
||||||
|
|
@ -1119,6 +1128,9 @@ proc asgnToResultVar(c: PContext, n, le, ri: PNode) {.inline.} =
|
||||||
n.sons[0] = x # 'result[]' --> 'result'
|
n.sons[0] = x # 'result[]' --> 'result'
|
||||||
n.sons[1] = takeImplicitAddr(c, ri)
|
n.sons[1] = takeImplicitAddr(c, ri)
|
||||||
|
|
||||||
|
template resultTypeIsInferrable(typ: PType): expr =
|
||||||
|
typ.isMetaType and typ.kind != tyTypeDesc
|
||||||
|
|
||||||
proc semAsgn(c: PContext, n: PNode): PNode =
|
proc semAsgn(c: PContext, n: PNode): PNode =
|
||||||
checkSonsLen(n, 2)
|
checkSonsLen(n, 2)
|
||||||
var a = n.sons[0]
|
var a = n.sons[0]
|
||||||
|
|
@ -1170,7 +1182,7 @@ proc semAsgn(c: PContext, n: PNode): PNode =
|
||||||
if lhsIsResult: {efAllowDestructor} else: {})
|
if lhsIsResult: {efAllowDestructor} else: {})
|
||||||
if lhsIsResult:
|
if lhsIsResult:
|
||||||
n.typ = enforceVoidContext
|
n.typ = enforceVoidContext
|
||||||
if lhs.sym.typ.isMetaType and lhs.sym.typ.kind != tyTypeDesc:
|
if resultTypeIsInferrable(lhs.sym.typ):
|
||||||
if cmpTypes(c, lhs.typ, rhs.typ) == isGeneric:
|
if cmpTypes(c, lhs.typ, rhs.typ) == isGeneric:
|
||||||
internalAssert c.p.resultSym != nil
|
internalAssert c.p.resultSym != nil
|
||||||
lhs.typ = rhs.typ
|
lhs.typ = rhs.typ
|
||||||
|
|
@ -1259,12 +1271,21 @@ proc semYield(c: PContext, n: PNode): PNode =
|
||||||
localError(n.info, errYieldNotAllowedInTryStmt)
|
localError(n.info, errYieldNotAllowedInTryStmt)
|
||||||
elif n.sons[0].kind != nkEmpty:
|
elif n.sons[0].kind != nkEmpty:
|
||||||
n.sons[0] = semExprWithType(c, n.sons[0]) # check for type compatibility:
|
n.sons[0] = semExprWithType(c, n.sons[0]) # check for type compatibility:
|
||||||
var restype = c.p.owner.typ.sons[0]
|
var iterType = c.p.owner.typ
|
||||||
|
var restype = iterType.sons[0]
|
||||||
if restype != nil:
|
if restype != nil:
|
||||||
let adjustedRes = if c.p.owner.kind == skIterator: restype.base
|
let adjustedRes = if c.p.owner.kind == skIterator: restype.base
|
||||||
else: restype
|
else: restype
|
||||||
n.sons[0] = fitNode(c, adjustedRes, n.sons[0])
|
n.sons[0] = fitNode(c, adjustedRes, n.sons[0])
|
||||||
if n.sons[0].typ == nil: internalError(n.info, "semYield")
|
if n.sons[0].typ == nil: internalError(n.info, "semYield")
|
||||||
|
|
||||||
|
if resultTypeIsInferrable(adjustedRes):
|
||||||
|
let inferred = n.sons[0].typ
|
||||||
|
if c.p.owner.kind == skIterator:
|
||||||
|
iterType.sons[0].sons[0] = inferred
|
||||||
|
else:
|
||||||
|
iterType.sons[0] = inferred
|
||||||
|
|
||||||
semYieldVarResult(c, n, adjustedRes)
|
semYieldVarResult(c, n, adjustedRes)
|
||||||
else:
|
else:
|
||||||
localError(n.info, errCannotReturnExpr)
|
localError(n.info, errCannotReturnExpr)
|
||||||
|
|
@ -1346,7 +1367,7 @@ proc expectString(c: PContext, n: PNode): string =
|
||||||
localError(n.info, errStringLiteralExpected)
|
localError(n.info, errStringLiteralExpected)
|
||||||
|
|
||||||
proc getMagicSym(magic: TMagic): PSym =
|
proc getMagicSym(magic: TMagic): PSym =
|
||||||
result = newSym(skProc, getIdent($magic), getCurrOwner(), gCodegenLineInfo)
|
result = newSym(skProc, getIdent($magic), systemModule, gCodegenLineInfo)
|
||||||
result.magic = magic
|
result.magic = magic
|
||||||
|
|
||||||
proc newAnonSym(kind: TSymKind, info: TLineInfo,
|
proc newAnonSym(kind: TSymKind, info: TLineInfo,
|
||||||
|
|
@ -1939,7 +1960,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||||
of nkBracketExpr:
|
of nkBracketExpr:
|
||||||
checkMinSonsLen(n, 1)
|
checkMinSonsLen(n, 1)
|
||||||
var s = qualifiedLookUp(c, n.sons[0], {checkUndeclared})
|
var s = qualifiedLookUp(c, n.sons[0], {checkUndeclared})
|
||||||
if s != nil and s.kind in {skProc, skMethod, skConverter}+skIterators:
|
if (s != nil and s.kind in {skProc, skMethod, skConverter}+skIterators) or
|
||||||
|
n[0].kind in nkSymChoices:
|
||||||
# type parameters: partial generic specialization
|
# type parameters: partial generic specialization
|
||||||
n.sons[0] = semSymGenericInstantiation(c, n.sons[0], s)
|
n.sons[0] = semSymGenericInstantiation(c, n.sons[0], s)
|
||||||
result = explicitGenericInstantiation(c, n, s)
|
result = explicitGenericInstantiation(c, n, s)
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable,
|
||||||
if a.kind != nkSym:
|
if a.kind != nkSym:
|
||||||
internalError(a.info, "instantiateGenericParamList; no symbol")
|
internalError(a.info, "instantiateGenericParamList; no symbol")
|
||||||
var q = a.sym
|
var q = a.sym
|
||||||
if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyStatic}+tyTypeClasses:
|
if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyStatic, tyIter}+tyTypeClasses:
|
||||||
continue
|
continue
|
||||||
var s = newSym(skType, q.name, getCurrOwner(), q.info)
|
var s = newSym(skType, q.name, getCurrOwner(), q.info)
|
||||||
s.flags = s.flags + {sfUsed, sfFromGeneric}
|
s.flags = s.flags + {sfUsed, sfFromGeneric}
|
||||||
|
|
|
||||||
|
|
@ -661,10 +661,18 @@ proc semFor(c: PContext, n: PNode): PNode =
|
||||||
openScope(c)
|
openScope(c)
|
||||||
n.sons[length-2] = semExprNoDeref(c, n.sons[length-2], {efWantIterator})
|
n.sons[length-2] = semExprNoDeref(c, n.sons[length-2], {efWantIterator})
|
||||||
var call = n.sons[length-2]
|
var call = n.sons[length-2]
|
||||||
if call.kind in nkCallKinds and call.sons[0].typ.callConv == ccClosure:
|
let isCallExpr = call.kind in nkCallKinds
|
||||||
|
if isCallExpr and call.sons[0].sym.magic != mNone:
|
||||||
|
if call.sons[0].sym.magic == mOmpParFor:
|
||||||
|
result = semForVars(c, n)
|
||||||
|
result.kind = nkParForStmt
|
||||||
|
else:
|
||||||
|
result = semForFields(c, n, call.sons[0].sym.magic)
|
||||||
|
elif (isCallExpr and call.sons[0].typ.callConv == ccClosure) or
|
||||||
|
call.typ.kind == tyIter:
|
||||||
# first class iterator:
|
# first class iterator:
|
||||||
result = semForVars(c, n)
|
result = semForVars(c, n)
|
||||||
elif call.kind notin nkCallKinds or call.sons[0].kind != nkSym or
|
elif not isCallExpr or call.sons[0].kind != nkSym or
|
||||||
call.sons[0].sym.kind notin skIterators:
|
call.sons[0].sym.kind notin skIterators:
|
||||||
if length == 3:
|
if length == 3:
|
||||||
n.sons[length-2] = implicitIterator(c, "items", n.sons[length-2])
|
n.sons[length-2] = implicitIterator(c, "items", n.sons[length-2])
|
||||||
|
|
@ -673,12 +681,6 @@ proc semFor(c: PContext, n: PNode): PNode =
|
||||||
else:
|
else:
|
||||||
localError(n.sons[length-2].info, errIteratorExpected)
|
localError(n.sons[length-2].info, errIteratorExpected)
|
||||||
result = semForVars(c, n)
|
result = semForVars(c, n)
|
||||||
elif call.sons[0].sym.magic != mNone:
|
|
||||||
if call.sons[0].sym.magic == mOmpParFor:
|
|
||||||
result = semForVars(c, n)
|
|
||||||
result.kind = nkParForStmt
|
|
||||||
else:
|
|
||||||
result = semForFields(c, n, call.sons[0].sym.magic)
|
|
||||||
else:
|
else:
|
||||||
result = semForVars(c, n)
|
result = semForVars(c, n)
|
||||||
# propagate any enforced VoidContext:
|
# propagate any enforced VoidContext:
|
||||||
|
|
|
||||||
|
|
@ -348,7 +348,9 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
|
||||||
of nkMethodDef:
|
of nkMethodDef:
|
||||||
result = semRoutineInTemplBody(c, n, skMethod)
|
result = semRoutineInTemplBody(c, n, skMethod)
|
||||||
of nkIteratorDef:
|
of nkIteratorDef:
|
||||||
result = semRoutineInTemplBody(c, n, n[namePos].sym.kind)
|
let kind = if hasPragma(n[pragmasPos], wClosure): skClosureIterator
|
||||||
|
else: skIterator
|
||||||
|
result = semRoutineInTemplBody(c, n, kind)
|
||||||
of nkTemplateDef:
|
of nkTemplateDef:
|
||||||
result = semRoutineInTemplBody(c, n, skTemplate)
|
result = semRoutineInTemplBody(c, n, skTemplate)
|
||||||
of nkMacroDef:
|
of nkMacroDef:
|
||||||
|
|
|
||||||
|
|
@ -154,19 +154,31 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType =
|
||||||
result.n = newNodeI(nkRange, n.info)
|
result.n = newNodeI(nkRange, n.info)
|
||||||
if (n[1].kind == nkEmpty) or (n[2].kind == nkEmpty):
|
if (n[1].kind == nkEmpty) or (n[2].kind == nkEmpty):
|
||||||
localError(n.info, errRangeIsEmpty)
|
localError(n.info, errRangeIsEmpty)
|
||||||
var a = semConstExpr(c, n[1])
|
|
||||||
var b = semConstExpr(c, n[2])
|
var range: array[2, PNode]
|
||||||
if not sameType(a.typ, b.typ):
|
range[0] = semExprWithType(c, n[1], {efDetermineType})
|
||||||
|
range[1] = semExprWithType(c, n[2], {efDetermineType})
|
||||||
|
|
||||||
|
var rangeT: array[2, PType]
|
||||||
|
for i in 0..1: rangeT[i] = range[i].typ.skipTypes({tyStatic}).skipIntLit
|
||||||
|
|
||||||
|
if not sameType(rangeT[0], rangeT[1]):
|
||||||
localError(n.info, errPureTypeMismatch)
|
localError(n.info, errPureTypeMismatch)
|
||||||
elif a.typ.kind notin {tyInt..tyInt64,tyEnum,tyBool,tyChar,
|
elif not rangeT[0].isOrdinalType:
|
||||||
tyFloat..tyFloat128,tyUInt8..tyUInt32}:
|
|
||||||
localError(n.info, errOrdinalTypeExpected)
|
localError(n.info, errOrdinalTypeExpected)
|
||||||
elif enumHasHoles(a.typ):
|
elif enumHasHoles(rangeT[0]):
|
||||||
localError(n.info, errEnumXHasHoles, a.typ.sym.name.s)
|
localError(n.info, errEnumXHasHoles, rangeT[0].sym.name.s)
|
||||||
elif not leValue(a, b): localError(n.info, errRangeIsEmpty)
|
|
||||||
addSon(result.n, a)
|
for i in 0..1:
|
||||||
addSon(result.n, b)
|
if hasGenericArguments(range[i]):
|
||||||
addSonSkipIntLit(result, b.typ)
|
result.n.addSon makeStaticExpr(c, range[i])
|
||||||
|
else:
|
||||||
|
result.n.addSon semConstExpr(c, range[i])
|
||||||
|
|
||||||
|
if weakLeValue(result.n[0], result.n[1]) == impNo:
|
||||||
|
localError(n.info, errRangeIsEmpty)
|
||||||
|
|
||||||
|
addSonSkipIntLit(result, rangeT[0])
|
||||||
|
|
||||||
proc semRange(c: PContext, n: PNode, prev: PType): PType =
|
proc semRange(c: PContext, n: PNode, prev: PType): PType =
|
||||||
result = nil
|
result = nil
|
||||||
|
|
@ -602,15 +614,24 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType): PType =
|
||||||
incl(result.flags, tfFinal)
|
incl(result.flags, tfFinal)
|
||||||
|
|
||||||
proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) =
|
proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) =
|
||||||
if kind == skMacro and param.typ.kind notin {tyTypeDesc, tyStatic}:
|
template addDecl(x) =
|
||||||
# within a macro, every param has the type PNimrodNode!
|
if sfGenSym notin x.flags: addDecl(c, x)
|
||||||
# and param.typ.kind in {tyTypeDesc, tyExpr, tyStmt}:
|
|
||||||
let nn = getSysSym"PNimrodNode"
|
if kind == skMacro:
|
||||||
var a = copySym(param)
|
if param.typ.kind == tyTypeDesc:
|
||||||
a.typ = nn.typ
|
addDecl(param)
|
||||||
if sfGenSym notin a.flags: addDecl(c, a)
|
elif param.typ.kind == tyStatic:
|
||||||
|
var a = copySym(param)
|
||||||
|
a.typ = param.typ.base
|
||||||
|
addDecl(a)
|
||||||
|
else:
|
||||||
|
# within a macro, every param has the type PNimrodNode!
|
||||||
|
let nn = getSysSym"PNimrodNode"
|
||||||
|
var a = copySym(param)
|
||||||
|
a.typ = nn.typ
|
||||||
|
addDecl(a)
|
||||||
else:
|
else:
|
||||||
if sfGenSym notin param.flags: addDecl(c, param)
|
addDecl(param)
|
||||||
|
|
||||||
let typedescId = getIdent"typedesc"
|
let typedescId = getIdent"typedesc"
|
||||||
|
|
||||||
|
|
@ -722,6 +743,15 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
||||||
result = newTypeWithSons(c, tyCompositeTypeClass, @[paramType, result])
|
result = newTypeWithSons(c, tyCompositeTypeClass, @[paramType, result])
|
||||||
result = addImplicitGeneric(result)
|
result = addImplicitGeneric(result)
|
||||||
|
|
||||||
|
of tyIter:
|
||||||
|
if paramType.callConv == ccInline:
|
||||||
|
if procKind notin {skTemplate, skMacro, skIterator}:
|
||||||
|
localError(info, errInlineIteratorsAsProcParams)
|
||||||
|
if paramType.len == 1:
|
||||||
|
let lifted = liftingWalk(paramType.base)
|
||||||
|
if lifted != nil: paramType.sons[0] = lifted
|
||||||
|
result = addImplicitGeneric(paramType)
|
||||||
|
|
||||||
of tyGenericInst:
|
of tyGenericInst:
|
||||||
if paramType.lastSon.kind == tyUserTypeClass:
|
if paramType.lastSon.kind == tyUserTypeClass:
|
||||||
var cp = copyType(paramType, getCurrOwner(), false)
|
var cp = copyType(paramType, getCurrOwner(), false)
|
||||||
|
|
@ -840,9 +870,13 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||||
addParamOrResult(c, arg, kind)
|
addParamOrResult(c, arg, kind)
|
||||||
if gCmd == cmdPretty: checkDef(a.sons[j], arg)
|
if gCmd == cmdPretty: checkDef(a.sons[j], arg)
|
||||||
|
|
||||||
|
var r: PType
|
||||||
if n.sons[0].kind != nkEmpty:
|
if n.sons[0].kind != nkEmpty:
|
||||||
var r = semTypeNode(c, n.sons[0], nil)
|
r = semTypeNode(c, n.sons[0], nil)
|
||||||
|
elif kind == skIterator:
|
||||||
|
r = newTypeS(tyAnything, c)
|
||||||
|
|
||||||
|
if r != nil:
|
||||||
# turn explicit 'void' return type into 'nil' because the rest of the
|
# turn explicit 'void' return type into 'nil' because the rest of the
|
||||||
# compiler only checks for 'nil':
|
# compiler only checks for 'nil':
|
||||||
if skipTypes(r, {tyGenericInst}).kind != tyEmpty:
|
if skipTypes(r, {tyGenericInst}).kind != tyEmpty:
|
||||||
|
|
@ -852,7 +886,11 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||||
if lifted != nil: r = lifted
|
if lifted != nil: r = lifted
|
||||||
r.flags.incl tfRetType
|
r.flags.incl tfRetType
|
||||||
r = skipIntLit(r)
|
r = skipIntLit(r)
|
||||||
if kind == skIterator: r = newTypeWithSons(c, tyIter, @[r])
|
if kind == skIterator:
|
||||||
|
# see tchainediterators
|
||||||
|
# in cases like iterator foo(it: iterator): type(it)
|
||||||
|
# we don't need to change the return type to iter[T]
|
||||||
|
if not r.isInlineIterator: r = newTypeWithSons(c, tyIter, @[r])
|
||||||
result.sons[0] = r
|
result.sons[0] = r
|
||||||
res.typ = r
|
res.typ = r
|
||||||
|
|
||||||
|
|
@ -984,7 +1022,8 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||||
of nkTypeOfExpr:
|
of nkTypeOfExpr:
|
||||||
# for ``type(countup(1,3))``, see ``tests/ttoseq``.
|
# for ``type(countup(1,3))``, see ``tests/ttoseq``.
|
||||||
checkSonsLen(n, 1)
|
checkSonsLen(n, 1)
|
||||||
result = semExprWithType(c, n.sons[0], {efInTypeof}).typ.skipTypes({tyIter})
|
let typExpr = semExprWithType(c, n.sons[0], {efInTypeof})
|
||||||
|
result = typExpr.typ.skipTypes({tyIter})
|
||||||
of nkPar:
|
of nkPar:
|
||||||
if sonsLen(n) == 1: result = semTypeNode(c, n.sons[0], prev)
|
if sonsLen(n) == 1: result = semTypeNode(c, n.sons[0], prev)
|
||||||
else:
|
else:
|
||||||
|
|
@ -1103,8 +1142,12 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||||
result = newConstraint(c, tyIter)
|
result = newConstraint(c, tyIter)
|
||||||
else:
|
else:
|
||||||
result = semProcTypeWithScope(c, n, prev, skClosureIterator)
|
result = semProcTypeWithScope(c, n, prev, skClosureIterator)
|
||||||
result.flags.incl(tfIterator)
|
if n.lastSon.kind == nkPragma and hasPragma(n.lastSon, wInline):
|
||||||
result.callConv = ccClosure
|
result.kind = tyIter
|
||||||
|
result.callConv = ccInline
|
||||||
|
else:
|
||||||
|
result.flags.incl(tfIterator)
|
||||||
|
result.callConv = ccClosure
|
||||||
of nkProcTy:
|
of nkProcTy:
|
||||||
if n.sonsLen == 0:
|
if n.sonsLen == 0:
|
||||||
result = newConstraint(c, tyProc)
|
result = newConstraint(c, tyProc)
|
||||||
|
|
|
||||||
|
|
@ -181,7 +181,8 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode): PNode =
|
||||||
of nkStaticExpr:
|
of nkStaticExpr:
|
||||||
var n = prepareNode(cl, n)
|
var n = prepareNode(cl, n)
|
||||||
n = reResolveCallsWithTypedescParams(cl, n)
|
n = reResolveCallsWithTypedescParams(cl, n)
|
||||||
result = cl.c.semExpr(cl.c, n)
|
result = if cl.allowMetaTypes: n
|
||||||
|
else: cl.c.semExpr(cl.c, n)
|
||||||
else:
|
else:
|
||||||
var length = sonsLen(n)
|
var length = sonsLen(n)
|
||||||
if length > 0:
|
if length > 0:
|
||||||
|
|
@ -306,6 +307,11 @@ proc skipIntLiteralParams(t: PType) =
|
||||||
t.sons[i] = skipped
|
t.sons[i] = skipped
|
||||||
if i > 0: t.n.sons[i].sym.typ = skipped
|
if i > 0: t.n.sons[i].sym.typ = skipped
|
||||||
|
|
||||||
|
# when the typeof operator is used on a static input
|
||||||
|
# param, the results gets infected with static as well:
|
||||||
|
if t.sons[0] != nil and t.sons[0].kind == tyStatic:
|
||||||
|
t.sons[0] = t.sons[0].base
|
||||||
|
|
||||||
proc propagateFieldFlags(t: PType, n: PNode) =
|
proc propagateFieldFlags(t: PType, n: PNode) =
|
||||||
# This is meant for objects and tuples
|
# This is meant for objects and tuples
|
||||||
# The type must be fully instantiated!
|
# The type must be fully instantiated!
|
||||||
|
|
@ -314,16 +320,15 @@ proc propagateFieldFlags(t: PType, n: PNode) =
|
||||||
of nkSym:
|
of nkSym:
|
||||||
propagateToOwner(t, n.sym.typ)
|
propagateToOwner(t, n.sym.typ)
|
||||||
of nkRecList, nkRecCase, nkOfBranch, nkElse:
|
of nkRecList, nkRecCase, nkOfBranch, nkElse:
|
||||||
if n.sons != nil:
|
for son in n:
|
||||||
for son in n.sons:
|
propagateFieldFlags(t, son)
|
||||||
propagateFieldFlags(t, son)
|
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
|
proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
|
||||||
result = t
|
result = t
|
||||||
if t == nil: return
|
if t == nil: return
|
||||||
|
|
||||||
if t.kind in {tyStatic, tyGenericParam} + tyTypeClasses:
|
if t.kind in {tyStatic, tyGenericParam, tyIter} + tyTypeClasses:
|
||||||
let lookup = PType(idTableGet(cl.typeMap, t))
|
let lookup = PType(idTableGet(cl.typeMap, t))
|
||||||
if lookup != nil: return lookup
|
if lookup != nil: return lookup
|
||||||
|
|
||||||
|
|
@ -336,6 +341,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
|
||||||
result = replaceTypeVarsT(cl, lastSon(t))
|
result = replaceTypeVarsT(cl, lastSon(t))
|
||||||
|
|
||||||
of tyFromExpr:
|
of tyFromExpr:
|
||||||
|
if cl.allowMetaTypes: return
|
||||||
var n = prepareNode(cl, t.n)
|
var n = prepareNode(cl, t.n)
|
||||||
n = cl.c.semConstExpr(cl.c, n)
|
n = cl.c.semConstExpr(cl.c, n)
|
||||||
if n.typ.kind == tyTypeDesc:
|
if n.typ.kind == tyTypeDesc:
|
||||||
|
|
|
||||||
|
|
@ -1014,6 +1014,10 @@ proc localConvMatch(c: PContext, m: var TCandidate, f, a: PType,
|
||||||
result.typ = getInstantiatedType(c, arg, m, base(f))
|
result.typ = getInstantiatedType(c, arg, m, base(f))
|
||||||
m.baseTypeMatch = true
|
m.baseTypeMatch = true
|
||||||
|
|
||||||
|
proc isInlineIterator*(t: PType): bool =
|
||||||
|
result = t.kind == tyIter or
|
||||||
|
(t.kind == tyBuiltInTypeClass and t.base.kind == tyIter)
|
||||||
|
|
||||||
proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
|
proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
|
||||||
argSemantized, argOrig: PNode): PNode =
|
argSemantized, argOrig: PNode): PNode =
|
||||||
var
|
var
|
||||||
|
|
@ -1061,6 +1065,13 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
|
||||||
else:
|
else:
|
||||||
return argOrig
|
return argOrig
|
||||||
|
|
||||||
|
if r != isNone and f.isInlineIterator:
|
||||||
|
var inlined = newTypeS(tyStatic, c)
|
||||||
|
inlined.sons = @[argType]
|
||||||
|
inlined.n = argSemantized
|
||||||
|
put(m.bindings, f, inlined)
|
||||||
|
return argSemantized
|
||||||
|
|
||||||
case r
|
case r
|
||||||
of isConvertible:
|
of isConvertible:
|
||||||
inc(m.convMatches)
|
inc(m.convMatches)
|
||||||
|
|
@ -1188,7 +1199,9 @@ proc prepareOperand(c: PContext; formal: PType; a: PNode): PNode =
|
||||||
# a.typ == nil is valid
|
# a.typ == nil is valid
|
||||||
result = a
|
result = a
|
||||||
elif a.typ.isNil:
|
elif a.typ.isNil:
|
||||||
result = c.semOperand(c, a, {efDetermineType})
|
let flags = if formal.kind == tyIter: {efDetermineType, efWantIterator}
|
||||||
|
else: {efDetermineType}
|
||||||
|
result = c.semOperand(c, a, flags)
|
||||||
else:
|
else:
|
||||||
result = a
|
result = a
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -433,8 +433,9 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
|
||||||
|
|
||||||
var length = sonsLen(n)
|
var length = sonsLen(n)
|
||||||
var call = n.sons[length - 2]
|
var call = n.sons[length - 2]
|
||||||
if call.kind notin nkCallKinds or call.sons[0].kind != nkSym or
|
if call.typ.kind != tyIter and
|
||||||
call.sons[0].sym.kind != skIterator:
|
(call.kind notin nkCallKinds or call.sons[0].kind != nkSym or
|
||||||
|
call.sons[0].sym.kind != skIterator):
|
||||||
n.sons[length-1] = transformLoopBody(c, n.sons[length-1]).PNode
|
n.sons[length-1] = transformLoopBody(c, n.sons[length-1]).PNode
|
||||||
return lambdalifting.liftForLoop(n).PTransNode
|
return lambdalifting.liftForLoop(n).PTransNode
|
||||||
#InternalError(call.info, "transformFor")
|
#InternalError(call.info, "transformFor")
|
||||||
|
|
@ -459,6 +460,7 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
|
||||||
for i in countup(1, sonsLen(call) - 1):
|
for i in countup(1, sonsLen(call) - 1):
|
||||||
var arg = transform(c, call.sons[i]).PNode
|
var arg = transform(c, call.sons[i]).PNode
|
||||||
var formal = skipTypes(iter.typ, abstractInst).n.sons[i].sym
|
var formal = skipTypes(iter.typ, abstractInst).n.sons[i].sym
|
||||||
|
if arg.typ.kind == tyIter: continue
|
||||||
case putArgInto(arg, formal.typ)
|
case putArgInto(arg, formal.typ)
|
||||||
of paDirectMapping:
|
of paDirectMapping:
|
||||||
idNodeTablePut(newC.mapping, formal, arg)
|
idNodeTablePut(newC.mapping, formal, arg)
|
||||||
|
|
@ -480,7 +482,7 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
|
||||||
dec(c.inlining)
|
dec(c.inlining)
|
||||||
popInfoContext()
|
popInfoContext()
|
||||||
popTransCon(c)
|
popTransCon(c)
|
||||||
#echo "transformed: ", renderTree(n)
|
# echo "transformed: ", result.PNode.renderTree
|
||||||
|
|
||||||
proc getMagicOp(call: PNode): TMagic =
|
proc getMagicOp(call: PNode): TMagic =
|
||||||
if call.sons[0].kind == nkSym and
|
if call.sons[0].kind == nkSym and
|
||||||
|
|
|
||||||
|
|
@ -425,7 +425,9 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||||
decodeBC(rkNode)
|
decodeBC(rkNode)
|
||||||
let src = regs[rb].node
|
let src = regs[rb].node
|
||||||
if src.kind notin {nkEmpty..nkNilLit}:
|
if src.kind notin {nkEmpty..nkNilLit}:
|
||||||
regs[ra].node = src.sons[rc]
|
let n = src.sons[rc]
|
||||||
|
regs[ra].node = if n.kind == nkExprColonExpr: n[1]
|
||||||
|
else: n
|
||||||
else:
|
else:
|
||||||
stackTrace(c, tos, pc, errIndexOutOfBounds)
|
stackTrace(c, tos, pc, errIndexOutOfBounds)
|
||||||
of opcWrObj:
|
of opcWrObj:
|
||||||
|
|
|
||||||
|
|
@ -2875,7 +2875,6 @@ as there are components in the tuple. The i'th iteration variable's type is
|
||||||
the type of the i'th component. In other words, implicit tuple unpacking in a
|
the type of the i'th component. In other words, implicit tuple unpacking in a
|
||||||
for loop context is supported.
|
for loop context is supported.
|
||||||
|
|
||||||
|
|
||||||
Implict items/pairs invocations
|
Implict items/pairs invocations
|
||||||
-------------------------------
|
-------------------------------
|
||||||
|
|
||||||
|
|
@ -2900,10 +2899,11 @@ First class iterators
|
||||||
There are 2 kinds of iterators in Nimrod: *inline* and *closure* iterators.
|
There are 2 kinds of iterators in Nimrod: *inline* and *closure* iterators.
|
||||||
An `inline iterator`:idx: is an iterator that's always inlined by the compiler
|
An `inline iterator`:idx: is an iterator that's always inlined by the compiler
|
||||||
leading to zero overhead for the abstraction, but may result in a heavy
|
leading to zero overhead for the abstraction, but may result in a heavy
|
||||||
increase in code size. Inline iterators are second class
|
increase in code size. Inline iterators are second class citizens;
|
||||||
citizens; one cannot pass them around like first class procs.
|
They can be passed as parameters only to other inlining code facilities like
|
||||||
|
templates, macros and other inline iterators.
|
||||||
|
|
||||||
In contrast to that, a `closure iterator`:idx: can be passed around:
|
In contrast to that, a `closure iterator`:idx: can be passed around more freely:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nimrod
|
||||||
iterator count0(): int {.closure.} =
|
iterator count0(): int {.closure.} =
|
||||||
|
|
@ -2926,9 +2926,7 @@ Closure iterators have other restrictions than inline iterators:
|
||||||
1. ``yield`` in a closure iterator can not occur in a ``try`` statement.
|
1. ``yield`` in a closure iterator can not occur in a ``try`` statement.
|
||||||
2. For now, a closure iterator cannot be evaluated at compile time.
|
2. For now, a closure iterator cannot be evaluated at compile time.
|
||||||
3. ``return`` is allowed in a closure iterator (but rarely useful).
|
3. ``return`` is allowed in a closure iterator (but rarely useful).
|
||||||
4. Since closure iterators can be used as a collaborative tasking
|
4. Both inline and closure iterators cannot be recursive.
|
||||||
system, ``void`` is a valid return type for them.
|
|
||||||
5. Both inline and closure iterators cannot be recursive.
|
|
||||||
|
|
||||||
Iterators that are neither marked ``{.closure.}`` nor ``{.inline.}`` explicitly
|
Iterators that are neither marked ``{.closure.}`` nor ``{.inline.}`` explicitly
|
||||||
default to being inline, but that this may change in future versions of the
|
default to being inline, but that this may change in future versions of the
|
||||||
|
|
@ -2990,6 +2988,14 @@ parameters of an outer factory proc:
|
||||||
for f in foo():
|
for f in foo():
|
||||||
echo f
|
echo f
|
||||||
|
|
||||||
|
Implicit return type
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
Since inline interators must always produce values that will be consumed in
|
||||||
|
a for loop, the compiler will implicity use the ``auto`` return type if no
|
||||||
|
type is given by the user. In contrast, since closure iterators can be used
|
||||||
|
as a collaborative tasking system, ``void`` is a valid return type for them.
|
||||||
|
|
||||||
|
|
||||||
Type sections
|
Type sections
|
||||||
=============
|
=============
|
||||||
|
|
@ -4069,8 +4075,8 @@ Static params can also appear in the signatures of generic types:
|
||||||
AffineTransform2D[T] = Matrix[3, 3, T]
|
AffineTransform2D[T] = Matrix[3, 3, T]
|
||||||
AffineTransform3D[T] = Matrix[4, 4, T]
|
AffineTransform3D[T] = Matrix[4, 4, T]
|
||||||
|
|
||||||
AffineTransform3D[float] # OK
|
var m1: AffineTransform3D[float] # OK
|
||||||
AffineTransform2D[string] # Error, `string` is not a `Number`
|
var m2: AffineTransform2D[string] # Error, `string` is not a `Number`
|
||||||
|
|
||||||
|
|
||||||
typedesc
|
typedesc
|
||||||
|
|
|
||||||
|
|
@ -195,8 +195,14 @@ proc open*(connection, user, password, database: string): TDbConn {.
|
||||||
## be established.
|
## be established.
|
||||||
result = mysql.Init(nil)
|
result = mysql.Init(nil)
|
||||||
if result == nil: dbError("could not open database connection")
|
if result == nil: dbError("could not open database connection")
|
||||||
if mysql.RealConnect(result, "", user, password, database,
|
let
|
||||||
0'i32, nil, 0) == nil:
|
colonPos = connection.find(':')
|
||||||
|
host = if colonPos < 0: connection
|
||||||
|
else: substr(connection, 0, colonPos-1)
|
||||||
|
port: int32 = if colonPos < 0: 0'i32
|
||||||
|
else: substr(connection, colonPos+1).parseInt.int32
|
||||||
|
if mysql.RealConnect(result, host, user, password, database,
|
||||||
|
port, nil, 0) == nil:
|
||||||
var errmsg = $mysql.error(result)
|
var errmsg = $mysql.error(result)
|
||||||
db_mysql.Close(result)
|
db_mysql.Close(result)
|
||||||
dbError(errmsg)
|
dbError(errmsg)
|
||||||
|
|
|
||||||
|
|
@ -314,6 +314,9 @@ static unsigned long nimNaN[2]={0xffffffff, 0x7fffffff};
|
||||||
# define INF INFINITY
|
# define INF INFINITY
|
||||||
# elif defined(HUGE_VAL)
|
# elif defined(HUGE_VAL)
|
||||||
# define INF HUGE_VAL
|
# define INF HUGE_VAL
|
||||||
|
# elif defined(_MSC_VER)
|
||||||
|
# include <float.h>
|
||||||
|
# define INF (DBL_MAX+DBL_MAX)
|
||||||
# else
|
# else
|
||||||
# define INF (1.0 / 0.0)
|
# define INF (1.0 / 0.0)
|
||||||
# endif
|
# endif
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ type
|
||||||
epoll_data* {.importc: "union epoll_data",
|
epoll_data* {.importc: "union epoll_data",
|
||||||
header: "<sys/epoll.h>", pure, final.} = object # TODO: This is actually a union.
|
header: "<sys/epoll.h>", pure, final.} = object # TODO: This is actually a union.
|
||||||
#thePtr* {.importc: "ptr".}: pointer
|
#thePtr* {.importc: "ptr".}: pointer
|
||||||
fd*: cint # \
|
fd* {.importc: "fd".}: cint # \
|
||||||
#u32*: uint32
|
#u32*: uint32
|
||||||
#u64*: uint64
|
#u64*: uint64
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -473,7 +473,6 @@ else:
|
||||||
|
|
||||||
proc update(p: PDispatcher, sock: TSocketHandle, events: set[TEvent]) =
|
proc update(p: PDispatcher, sock: TSocketHandle, events: set[TEvent]) =
|
||||||
assert sock in p.selector
|
assert sock in p.selector
|
||||||
echo("Update: ", events)
|
|
||||||
if events == {}:
|
if events == {}:
|
||||||
discard p.selector.unregister(sock)
|
discard p.selector.unregister(sock)
|
||||||
else:
|
else:
|
||||||
|
|
@ -499,23 +498,25 @@ else:
|
||||||
for info in p.selector.select(timeout):
|
for info in p.selector.select(timeout):
|
||||||
let data = PData(info.key.data)
|
let data = PData(info.key.data)
|
||||||
assert data.sock == info.key.fd
|
assert data.sock == info.key.fd
|
||||||
echo("R: ", data.readCBs.len, " W: ", data.writeCBs.len, ". ", info.events)
|
|
||||||
|
|
||||||
if EvRead in info.events:
|
if EvRead in info.events:
|
||||||
var newReadCBs: seq[TCallback] = @[]
|
# Callback may add items to ``data.readCBs`` which causes issues if
|
||||||
for cb in data.readCBs:
|
# we are iterating over ``data.readCBs`` at the same time. We therefore
|
||||||
|
# make a copy to iterate over.
|
||||||
|
let currentCBs = data.readCBs
|
||||||
|
data.readCBs = @[]
|
||||||
|
for cb in currentCBs:
|
||||||
if not cb(data.sock):
|
if not cb(data.sock):
|
||||||
# Callback wants to be called again.
|
# Callback wants to be called again.
|
||||||
newReadCBs.add(cb)
|
data.readCBs.add(cb)
|
||||||
data.readCBs = newReadCBs
|
|
||||||
|
|
||||||
if EvWrite in info.events:
|
if EvWrite in info.events:
|
||||||
var newWriteCBs: seq[TCallback] = @[]
|
let currentCBs = data.writeCBs
|
||||||
for cb in data.writeCBs:
|
data.writeCBs = @[]
|
||||||
|
for cb in currentCBs:
|
||||||
if not cb(data.sock):
|
if not cb(data.sock):
|
||||||
# Callback wants to be called again.
|
# Callback wants to be called again.
|
||||||
newWriteCBs.add(cb)
|
data.writeCBs.add(cb)
|
||||||
data.writeCBs = newWriteCBs
|
|
||||||
|
|
||||||
var newEvents: set[TEvent]
|
var newEvents: set[TEvent]
|
||||||
if data.readCBs.len != 0: newEvents = {EvRead}
|
if data.readCBs.len != 0: newEvents = {EvRead}
|
||||||
|
|
@ -616,7 +617,6 @@ else:
|
||||||
addWrite(p, socket, cb)
|
addWrite(p, socket, cb)
|
||||||
return retFuture
|
return retFuture
|
||||||
|
|
||||||
|
|
||||||
proc acceptAddr*(p: PDispatcher, socket: TSocketHandle):
|
proc acceptAddr*(p: PDispatcher, socket: TSocketHandle):
|
||||||
PFuture[tuple[address: string, client: TSocketHandle]] =
|
PFuture[tuple[address: string, client: TSocketHandle]] =
|
||||||
var retFuture = newFuture[tuple[address: string, client: TSocketHandle]]()
|
var retFuture = newFuture[tuple[address: string, client: TSocketHandle]]()
|
||||||
|
|
@ -854,7 +854,7 @@ when isMainModule:
|
||||||
sock.setBlocking false
|
sock.setBlocking false
|
||||||
|
|
||||||
|
|
||||||
when false:
|
when true:
|
||||||
# Await tests
|
# Await tests
|
||||||
proc main(p: PDispatcher): PFuture[int] {.async.} =
|
proc main(p: PDispatcher): PFuture[int] {.async.} =
|
||||||
discard await p.connect(sock, "irc.freenode.net", TPort(6667))
|
discard await p.connect(sock, "irc.freenode.net", TPort(6667))
|
||||||
|
|
@ -880,7 +880,7 @@ when isMainModule:
|
||||||
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
when false:
|
when true:
|
||||||
|
|
||||||
var f = p.connect(sock, "irc.freenode.org", TPort(6667))
|
var f = p.connect(sock, "irc.freenode.org", TPort(6667))
|
||||||
f.callback =
|
f.callback =
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,13 @@
|
||||||
# TODO: Docs.
|
# TODO: Docs.
|
||||||
|
|
||||||
import tables, os, unsigned, hashes
|
import tables, os, unsigned, hashes
|
||||||
|
import sockets2
|
||||||
|
|
||||||
when defined(linux): import posix, epoll
|
when defined(linux): import posix, epoll
|
||||||
elif defined(windows): import winlean
|
elif defined(windows): import winlean
|
||||||
|
|
||||||
proc hash*(x: TSocketHandle): THash {.borrow.}
|
proc hash*(x: TSocketHandle): THash {.borrow.}
|
||||||
|
proc `$`*(x: TSocketHandle): string {.borrow.}
|
||||||
|
|
||||||
type
|
type
|
||||||
TEvent* = enum
|
TEvent* = enum
|
||||||
|
|
@ -31,7 +33,7 @@ when defined(linux) or defined(nimdoc):
|
||||||
type
|
type
|
||||||
PSelector* = ref object
|
PSelector* = ref object
|
||||||
epollFD: cint
|
epollFD: cint
|
||||||
events: array[64, ptr epoll_event]
|
events: array[64, epoll_event]
|
||||||
fds: TTable[TSocketHandle, PSelectorKey]
|
fds: TTable[TSocketHandle, PSelectorKey]
|
||||||
|
|
||||||
proc createEventStruct(events: set[TEvent], fd: TSocketHandle): epoll_event =
|
proc createEventStruct(events: set[TEvent], fd: TSocketHandle): epoll_event =
|
||||||
|
|
@ -66,17 +68,25 @@ when defined(linux) or defined(nimdoc):
|
||||||
var event = createEventStruct(events, fd)
|
var event = createEventStruct(events, fd)
|
||||||
|
|
||||||
s.fds[fd].events = events
|
s.fds[fd].events = events
|
||||||
echo("About to update")
|
|
||||||
if epoll_ctl(s.epollFD, EPOLL_CTL_MOD, fd, addr(event)) != 0:
|
if epoll_ctl(s.epollFD, EPOLL_CTL_MOD, fd, addr(event)) != 0:
|
||||||
|
if OSLastError().cint == ENOENT:
|
||||||
|
# Socket has been closed. Epoll automatically removes disconnected
|
||||||
|
# sockets.
|
||||||
|
s.fds.del(fd)
|
||||||
|
osError("Socket has been disconnected")
|
||||||
|
|
||||||
OSError(OSLastError())
|
OSError(OSLastError())
|
||||||
echo("finished updating")
|
|
||||||
result = s.fds[fd]
|
result = s.fds[fd]
|
||||||
|
|
||||||
proc unregister*(s: PSelector, fd: TSocketHandle): PSelectorKey {.discardable.} =
|
proc unregister*(s: PSelector, fd: TSocketHandle): PSelectorKey {.discardable.} =
|
||||||
if not s.fds.hasKey(fd):
|
if not s.fds.hasKey(fd):
|
||||||
raise newException(EInvalidValue, "File descriptor not found.")
|
raise newException(EInvalidValue, "File descriptor not found.")
|
||||||
if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fd, nil) != 0:
|
if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fd, nil) != 0:
|
||||||
OSError(OSLastError())
|
if osLastError().cint == ENOENT:
|
||||||
|
# Socket has been closed. Epoll automatically removes disconnected
|
||||||
|
# sockets so its already been removed.
|
||||||
|
else:
|
||||||
|
OSError(OSLastError())
|
||||||
result = s.fds[fd]
|
result = s.fds[fd]
|
||||||
s.fds.del(fd)
|
s.fds.del(fd)
|
||||||
|
|
||||||
|
|
@ -92,21 +102,21 @@ when defined(linux) or defined(nimdoc):
|
||||||
## on the ``fd``.
|
## on the ``fd``.
|
||||||
result = @[]
|
result = @[]
|
||||||
|
|
||||||
let evNum = epoll_wait(s.epollFD, s.events[0], 64.cint, timeout.cint)
|
let evNum = epoll_wait(s.epollFD, addr s.events[0], 64.cint, timeout.cint)
|
||||||
if evNum < 0: OSError(OSLastError())
|
if evNum < 0: OSError(OSLastError())
|
||||||
if evNum == 0: return @[]
|
if evNum == 0: return @[]
|
||||||
for i in 0 .. <evNum:
|
for i in 0 .. <evNum:
|
||||||
var evSet: set[TEvent] = {}
|
var evSet: set[TEvent] = {}
|
||||||
if (s.events[i].events and EPOLLIN) != 0: evSet = evSet + {EvRead}
|
if (s.events[i].events and EPOLLIN) != 0: evSet = evSet + {EvRead}
|
||||||
if (s.events[i].events and EPOLLOUT) != 0: evSet = evSet + {EvWrite}
|
if (s.events[i].events and EPOLLOUT) != 0: evSet = evSet + {EvWrite}
|
||||||
|
|
||||||
let selectorKey = s.fds[s.events[i].data.fd.TSocketHandle]
|
let selectorKey = s.fds[s.events[i].data.fd.TSocketHandle]
|
||||||
|
assert selectorKey != nil
|
||||||
result.add((selectorKey, evSet))
|
result.add((selectorKey, evSet))
|
||||||
|
|
||||||
proc newSelector*(): PSelector =
|
proc newSelector*(): PSelector =
|
||||||
new result
|
new result
|
||||||
result.epollFD = epoll_create(64)
|
result.epollFD = epoll_create(64)
|
||||||
result.events = cast[array[64, ptr epoll_event]](alloc0(sizeof(epoll_event)*64))
|
result.events = cast[array[64, epoll_event]](alloc0(sizeof(epoll_event)*64))
|
||||||
result.fds = initTable[TSocketHandle, PSelectorKey]()
|
result.fds = initTable[TSocketHandle, PSelectorKey]()
|
||||||
if result.epollFD < 0:
|
if result.epollFD < 0:
|
||||||
OSError(OSLastError())
|
OSError(OSLastError())
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,10 @@ else:
|
||||||
export TSocketHandle, TSockaddr_in, TAddrinfo, INADDR_ANY, TSockAddr, TSockLen,
|
export TSocketHandle, TSockaddr_in, TAddrinfo, INADDR_ANY, TSockAddr, TSockLen,
|
||||||
inet_ntoa, recv, `==`, connect, send, accept
|
inet_ntoa, recv, `==`, connect, send, accept
|
||||||
|
|
||||||
|
export
|
||||||
|
SO_ERROR,
|
||||||
|
SOL_SOCKET
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
||||||
TPort* = distinct uint16 ## port type
|
TPort* = distinct uint16 ## port type
|
||||||
|
|
@ -208,6 +212,24 @@ proc htons*(x: int16): int16 =
|
||||||
## order, this is a no-op; otherwise, it performs a 2-byte swap operation.
|
## order, this is a no-op; otherwise, it performs a 2-byte swap operation.
|
||||||
result = sockets2.ntohs(x)
|
result = sockets2.ntohs(x)
|
||||||
|
|
||||||
|
proc getSockOptInt*(socket: TSocketHandle, level, optname: int): int {.
|
||||||
|
tags: [FReadIO].} =
|
||||||
|
## getsockopt for integer options.
|
||||||
|
var res: cint
|
||||||
|
var size = sizeof(res).TSocklen
|
||||||
|
if getsockopt(socket, cint(level), cint(optname),
|
||||||
|
addr(res), addr(size)) < 0'i32:
|
||||||
|
osError(osLastError())
|
||||||
|
result = int(res)
|
||||||
|
|
||||||
|
proc setSockOptInt*(socket: TSocketHandle, level, optname, optval: int) {.
|
||||||
|
tags: [FWriteIO].} =
|
||||||
|
## setsockopt for integer options.
|
||||||
|
var value = cint(optval)
|
||||||
|
if setsockopt(socket, cint(level), cint(optname), addr(value),
|
||||||
|
sizeof(value).TSocklen) < 0'i32:
|
||||||
|
osError(osLastError())
|
||||||
|
|
||||||
when defined(Windows):
|
when defined(Windows):
|
||||||
var wsa: TWSADATA
|
var wsa: TWSADATA
|
||||||
if WSAStartup(0x0101'i16, addr wsa) != 0: OSError(OSLastError())
|
if WSAStartup(0x0101'i16, addr wsa) != 0: OSError(OSLastError())
|
||||||
|
|
|
||||||
|
|
@ -1164,13 +1164,13 @@ when not defined(nimrodVM):
|
||||||
## from it before writing to it is undefined behaviour!
|
## from it before writing to it is undefined behaviour!
|
||||||
## The allocated memory belongs to its allocating thread!
|
## The allocated memory belongs to its allocating thread!
|
||||||
## Use `allocShared` to allocate from a shared heap.
|
## Use `allocShared` to allocate from a shared heap.
|
||||||
proc alloc*(T: typedesc, size = 1): ptr T {.inline.} =
|
proc createU*(T: typedesc, size = 1.Positive): ptr T {.inline.} =
|
||||||
## allocates a new memory block with at least ``T.sizeof * size``
|
## allocates a new memory block with at least ``T.sizeof * size``
|
||||||
## bytes. The block has to be freed with ``realloc(block, 0)`` or
|
## bytes. The block has to be freed with ``resize(block, 0)`` or
|
||||||
## ``dealloc(block)``. The block is not initialized, so reading
|
## ``free(block)``. The block is not initialized, so reading
|
||||||
## from it before writing to it is undefined behaviour!
|
## from it before writing to it is undefined behaviour!
|
||||||
## The allocated memory belongs to its allocating thread!
|
## The allocated memory belongs to its allocating thread!
|
||||||
## Use `allocShared` to allocate from a shared heap.
|
## Use `createSharedU` to allocate from a shared heap.
|
||||||
cast[ptr T](alloc(T.sizeof * size))
|
cast[ptr T](alloc(T.sizeof * size))
|
||||||
proc alloc0*(size: int): pointer {.noconv, rtl, tags: [].}
|
proc alloc0*(size: int): pointer {.noconv, rtl, tags: [].}
|
||||||
## allocates a new memory block with at least ``size`` bytes. The
|
## allocates a new memory block with at least ``size`` bytes. The
|
||||||
|
|
@ -1179,13 +1179,13 @@ when not defined(nimrodVM):
|
||||||
## containing zero, so it is somewhat safer than ``alloc``.
|
## containing zero, so it is somewhat safer than ``alloc``.
|
||||||
## The allocated memory belongs to its allocating thread!
|
## The allocated memory belongs to its allocating thread!
|
||||||
## Use `allocShared0` to allocate from a shared heap.
|
## Use `allocShared0` to allocate from a shared heap.
|
||||||
proc alloc0*(T: typedesc, size = 1): ptr T {.inline.} =
|
proc create*(T: typedesc, size = 1.Positive): ptr T {.inline.} =
|
||||||
## allocates a new memory block with at least ``T.sizeof * size``
|
## allocates a new memory block with at least ``T.sizeof * size``
|
||||||
## bytes. The block has to be freed with ``realloc(block, 0)`` or
|
## bytes. The block has to be freed with ``resize(block, 0)`` or
|
||||||
## ``dealloc(block)``. The block is initialized with all bytes
|
## ``free(block)``. The block is initialized with all bytes
|
||||||
## containing zero, so it is somewhat safer than ``alloc``.
|
## containing zero, so it is somewhat safer than ``createU``.
|
||||||
## The allocated memory belongs to its allocating thread!
|
## The allocated memory belongs to its allocating thread!
|
||||||
## Use `allocShared0` to allocate from a shared heap.
|
## Use `createShared` to allocate from a shared heap.
|
||||||
cast[ptr T](alloc0(T.sizeof * size))
|
cast[ptr T](alloc0(T.sizeof * size))
|
||||||
proc realloc*(p: pointer, newSize: int): pointer {.noconv, rtl, tags: [].}
|
proc realloc*(p: pointer, newSize: int): pointer {.noconv, rtl, tags: [].}
|
||||||
## grows or shrinks a given memory block. If p is **nil** then a new
|
## grows or shrinks a given memory block. If p is **nil** then a new
|
||||||
|
|
@ -1195,14 +1195,14 @@ when not defined(nimrodVM):
|
||||||
## be freed with ``dealloc``.
|
## be freed with ``dealloc``.
|
||||||
## The allocated memory belongs to its allocating thread!
|
## The allocated memory belongs to its allocating thread!
|
||||||
## Use `reallocShared` to reallocate from a shared heap.
|
## Use `reallocShared` to reallocate from a shared heap.
|
||||||
proc reallocType*[T](p: ptr T, newSize: int): ptr T {.inline.} =
|
proc resize*[T](p: ptr T, newSize: Natural): ptr T {.inline.} =
|
||||||
## grows or shrinks a given memory block. If p is **nil** then a new
|
## grows or shrinks a given memory block. If p is **nil** then a new
|
||||||
## memory block is returned. In either way the block has at least
|
## memory block is returned. In either way the block has at least
|
||||||
## ``T.sizeof * newSize`` bytes. If ``newSize == 0`` and p is not
|
## ``T.sizeof * newSize`` bytes. If ``newSize == 0`` and p is not
|
||||||
## **nil** ``realloc`` calls ``dealloc(p)``. In other cases the block
|
## **nil** ``resize`` calls ``free(p)``. In other cases the block
|
||||||
## has to be freed with ``dealloc``. The allocated memory belongs to
|
## has to be freed with ``free``. The allocated memory belongs to
|
||||||
## its allocating thread!
|
## its allocating thread!
|
||||||
## Use `reallocShared` to reallocate from a shared heap.
|
## Use `resizeShared` to reallocate from a shared heap.
|
||||||
cast[ptr T](realloc(p, T.sizeof * newSize))
|
cast[ptr T](realloc(p, T.sizeof * newSize))
|
||||||
proc dealloc*(p: pointer) {.noconv, rtl, tags: [].}
|
proc dealloc*(p: pointer) {.noconv, rtl, tags: [].}
|
||||||
## frees the memory allocated with ``alloc``, ``alloc0`` or
|
## frees the memory allocated with ``alloc``, ``alloc0`` or
|
||||||
|
|
@ -1212,16 +1212,18 @@ when not defined(nimrodVM):
|
||||||
## or other memory may be corrupted.
|
## or other memory may be corrupted.
|
||||||
## The freed memory must belong to its allocating thread!
|
## The freed memory must belong to its allocating thread!
|
||||||
## Use `deallocShared` to deallocate from a shared heap.
|
## Use `deallocShared` to deallocate from a shared heap.
|
||||||
|
proc free*[T](p: ptr T) {.inline.} =
|
||||||
|
dealloc(p)
|
||||||
proc allocShared*(size: int): pointer {.noconv, rtl.}
|
proc allocShared*(size: int): pointer {.noconv, rtl.}
|
||||||
## allocates a new memory block on the shared heap with at
|
## allocates a new memory block on the shared heap with at
|
||||||
## least ``size`` bytes. The block has to be freed with
|
## least ``size`` bytes. The block has to be freed with
|
||||||
## ``reallocShared(block, 0)`` or ``deallocShared(block)``. The block
|
## ``reallocShared(block, 0)`` or ``deallocShared(block)``. The block
|
||||||
## is not initialized, so reading from it before writing to it is
|
## is not initialized, so reading from it before writing to it is
|
||||||
## undefined behaviour!
|
## undefined behaviour!
|
||||||
proc allocShared*(T: typedesc, size: int): ptr T {.inline.} =
|
proc createSharedU*(T: typedesc, size = 1.Positive): ptr T {.inline.} =
|
||||||
## allocates a new memory block on the shared heap with at
|
## allocates a new memory block on the shared heap with at
|
||||||
## least ``T.sizeof * size`` bytes. The block has to be freed with
|
## least ``T.sizeof * size`` bytes. The block has to be freed with
|
||||||
## ``reallocShared(block, 0)`` or ``deallocShared(block)``. The block
|
## ``resizeShared(block, 0)`` or ``freeShared(block)``. The block
|
||||||
## is not initialized, so reading from it before writing to it is
|
## is not initialized, so reading from it before writing to it is
|
||||||
## undefined behaviour!
|
## undefined behaviour!
|
||||||
cast[ptr T](allocShared(T.sizeof * size))
|
cast[ptr T](allocShared(T.sizeof * size))
|
||||||
|
|
@ -1231,25 +1233,25 @@ when not defined(nimrodVM):
|
||||||
## ``reallocShared(block, 0)`` or ``deallocShared(block)``.
|
## ``reallocShared(block, 0)`` or ``deallocShared(block)``.
|
||||||
## The block is initialized with all bytes
|
## The block is initialized with all bytes
|
||||||
## containing zero, so it is somewhat safer than ``allocShared``.
|
## containing zero, so it is somewhat safer than ``allocShared``.
|
||||||
proc allocShared0*(T: typedesc, size: int): ptr T {.inline.} =
|
proc createShared*(T: typedesc, size = 1.Positive): ptr T {.inline.} =
|
||||||
## allocates a new memory block on the shared heap with at
|
## allocates a new memory block on the shared heap with at
|
||||||
## least ``T.sizeof * size`` bytes. The block has to be freed with
|
## least ``T.sizeof * size`` bytes. The block has to be freed with
|
||||||
## ``reallocShared(block, 0)`` or ``deallocShared(block)``.
|
## ``resizeShared(block, 0)`` or ``freeShared(block)``.
|
||||||
## The block is initialized with all bytes
|
## The block is initialized with all bytes
|
||||||
## containing zero, so it is somewhat safer than ``allocShared``.
|
## containing zero, so it is somewhat safer than ``createSharedU``.
|
||||||
cast[ptr T](allocShared(T.sizeof * size))
|
cast[ptr T](allocShared0(T.sizeof * size))
|
||||||
proc reallocShared*(p: pointer, newSize: int): pointer {.noconv, rtl.}
|
proc reallocShared*(p: pointer, newSize: int): pointer {.noconv, rtl.}
|
||||||
## grows or shrinks a given memory block on the heap. If p is **nil**
|
## grows or shrinks a given memory block on the heap. If p is **nil**
|
||||||
## then a new memory block is returned. In either way the block has at
|
## then a new memory block is returned. In either way the block has at
|
||||||
## least ``newSize`` bytes. If ``newSize == 0`` and p is not **nil**
|
## least ``newSize`` bytes. If ``newSize == 0`` and p is not **nil**
|
||||||
## ``reallocShared`` calls ``deallocShared(p)``. In other cases the
|
## ``reallocShared`` calls ``deallocShared(p)``. In other cases the
|
||||||
## block has to be freed with ``deallocShared``.
|
## block has to be freed with ``deallocShared``.
|
||||||
proc reallocSharedType*[T](p: ptr T, newSize: int): ptr T {.inline.} =
|
proc resizeShared*[T](p: ptr T, newSize: Natural): ptr T {.inline.} =
|
||||||
## grows or shrinks a given memory block on the heap. If p is **nil**
|
## grows or shrinks a given memory block on the heap. If p is **nil**
|
||||||
## then a new memory block is returned. In either way the block has at
|
## then a new memory block is returned. In either way the block has at
|
||||||
## least ``T.sizeof * newSize`` bytes. If ``newSize == 0`` and p is
|
## least ``T.sizeof * newSize`` bytes. If ``newSize == 0`` and p is
|
||||||
## not **nil** ``reallocShared`` calls ``deallocShared(p)``. In other
|
## not **nil** ``resizeShared`` calls ``freeShared(p)``. In other
|
||||||
## cases the block has to be freed with ``deallocShared``.
|
## cases the block has to be freed with ``freeShared``.
|
||||||
cast[ptr T](reallocShared(p, T.sizeof * newSize))
|
cast[ptr T](reallocShared(p, T.sizeof * newSize))
|
||||||
proc deallocShared*(p: pointer) {.noconv, rtl.}
|
proc deallocShared*(p: pointer) {.noconv, rtl.}
|
||||||
## frees the memory allocated with ``allocShared``, ``allocShared0`` or
|
## frees the memory allocated with ``allocShared``, ``allocShared0`` or
|
||||||
|
|
@ -1257,6 +1259,13 @@ when not defined(nimrodVM):
|
||||||
## free the memory a leak occurs; if one tries to access freed
|
## free the memory a leak occurs; if one tries to access freed
|
||||||
## memory (or just freeing it twice!) a core dump may happen
|
## memory (or just freeing it twice!) a core dump may happen
|
||||||
## or other memory may be corrupted.
|
## or other memory may be corrupted.
|
||||||
|
proc freeShared*[T](p: ptr T) {.inline.} =
|
||||||
|
## frees the memory allocated with ``createShared``, ``createSharedU`` or
|
||||||
|
## ``resizeShared``. This procedure is dangerous! If one forgets to
|
||||||
|
## free the memory a leak occurs; if one tries to access freed
|
||||||
|
## memory (or just freeing it twice!) a core dump may happen
|
||||||
|
## or other memory may be corrupted.
|
||||||
|
deallocShared(p)
|
||||||
|
|
||||||
proc swap*[T](a, b: var T) {.magic: "Swap", noSideEffect.}
|
proc swap*[T](a, b: var T) {.magic: "Swap", noSideEffect.}
|
||||||
## swaps the values `a` and `b`. This is often more efficient than
|
## swaps the values `a` and `b`. This is often more efficient than
|
||||||
|
|
|
||||||
|
|
@ -15,16 +15,24 @@ const
|
||||||
var clientCount = 0
|
var clientCount = 0
|
||||||
|
|
||||||
proc sendMessages(disp: PDispatcher, client: TSocketHandle): PFuture[int] {.async.} =
|
proc sendMessages(disp: PDispatcher, client: TSocketHandle): PFuture[int] {.async.} =
|
||||||
|
echo("entering sendMessages")
|
||||||
for i in 0 .. <messagesToSend:
|
for i in 0 .. <messagesToSend:
|
||||||
discard await disp.send(client, "Message " & $i & "\c\L")
|
discard await disp.send(client, "Message " & $i & "\c\L")
|
||||||
|
echo("returning sendMessages")
|
||||||
|
|
||||||
proc launchSwarm(disp: PDispatcher, port: TPort): PFuture[int] {.async.} =
|
proc launchSwarm(disp: PDispatcher, port: TPort): PFuture[int] {.async.} =
|
||||||
for i in 0 .. <swarmSize:
|
for i in 0 .. <swarmSize:
|
||||||
var sock = socket()
|
var sock = socket()
|
||||||
|
# TODO: We may need to explicitly register and unregister the fd.
|
||||||
|
# This is because when the socket is closed, selectors is not aware
|
||||||
|
# that it has been closed. While epoll is. Perhaps we should just unregister
|
||||||
|
# in close()?
|
||||||
|
echo(sock.cint)
|
||||||
#disp.register(sock)
|
#disp.register(sock)
|
||||||
discard await disp.connect(sock, "localhost", port)
|
discard await disp.connect(sock, "localhost", port)
|
||||||
when true:
|
when true:
|
||||||
discard await sendMessages(disp, sock)
|
discard await sendMessages(disp, sock)
|
||||||
|
echo("Calling close")
|
||||||
sock.close()
|
sock.close()
|
||||||
else:
|
else:
|
||||||
# Issue #932: https://github.com/Araq/Nimrod/issues/932
|
# Issue #932: https://github.com/Araq/Nimrod/issues/932
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,8 @@ macro selectType(a, b: typedesc): typedesc =
|
||||||
type
|
type
|
||||||
Foo[T] = object
|
Foo[T] = object
|
||||||
data1: array[T.high, int]
|
data1: array[T.high, int]
|
||||||
data2: array[typeNameLen(T), float] # data3: array[0..T.typeNameLen, selectType(float, int)]
|
data2: array[typeNameLen(T), float]
|
||||||
|
data3: array[0..T.typeNameLen, selectType(float, int)]
|
||||||
|
|
||||||
MyEnum = enum A, B, C, D
|
MyEnum = enum A, B, C, D
|
||||||
|
|
||||||
|
|
@ -27,10 +28,15 @@ echo high(f1.data2) # (MyEnum.len = 6) - 1 == 5
|
||||||
echo high(f2.data1) # 127 - 1 == 126
|
echo high(f2.data1) # 127 - 1 == 126
|
||||||
echo high(f2.data2) # int8.len - 1 == 3
|
echo high(f2.data2) # int8.len - 1 == 3
|
||||||
|
|
||||||
#static:
|
static:
|
||||||
# assert high(f1.data1) == ord(D)
|
assert high(f1.data1) == ord(C)
|
||||||
# assert high(f1.data2) == 6 # length of MyEnum
|
assert high(f1.data2) == 5 # length of MyEnum minus one, because we used T.high
|
||||||
|
|
||||||
# assert high(f2.data1) == 127
|
assert high(f2.data1) == 126
|
||||||
# assert high(f2.data2) == 4 # length of int8
|
assert high(f2.data2) == 3
|
||||||
|
|
||||||
|
assert high(f1.data3) == 6 # length of MyEnum
|
||||||
|
assert high(f2.data3) == 4 # length of int8
|
||||||
|
|
||||||
|
assert f2.data3[0] is float
|
||||||
|
|
||||||
|
|
|
||||||
38
tests/iter/tchainediterators.nim
Normal file
38
tests/iter/tchainediterators.nim
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
discard """
|
||||||
|
output: '''16
|
||||||
|
32
|
||||||
|
48
|
||||||
|
64
|
||||||
|
128
|
||||||
|
192
|
||||||
|
'''
|
||||||
|
"""
|
||||||
|
|
||||||
|
iterator gaz(it: iterator{.inline.}): type(it) =
|
||||||
|
for x in it:
|
||||||
|
yield x*2
|
||||||
|
|
||||||
|
iterator baz(it: iterator{.inline.}) =
|
||||||
|
for x in gaz(it):
|
||||||
|
yield x*2
|
||||||
|
|
||||||
|
type T1 = auto
|
||||||
|
|
||||||
|
iterator bar(it: iterator: T1{.inline.}): T1 =
|
||||||
|
for x in baz(it):
|
||||||
|
yield x*2
|
||||||
|
|
||||||
|
iterator foo[T](x: iterator: T{.inline.}): T =
|
||||||
|
for e in bar(x):
|
||||||
|
yield e*2
|
||||||
|
|
||||||
|
var s = @[1, 2, 3]
|
||||||
|
|
||||||
|
# pass an interator several levels deep:
|
||||||
|
for x in s.items.foo:
|
||||||
|
echo x
|
||||||
|
|
||||||
|
# use some complex iterator as an input for another one:
|
||||||
|
for x in s.items.baz.foo:
|
||||||
|
echo x
|
||||||
|
|
||||||
26
tests/iter/titerable.nim
Normal file
26
tests/iter/titerable.nim
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
discard """
|
||||||
|
output: '''2
|
||||||
|
4
|
||||||
|
6
|
||||||
|
4
|
||||||
|
8
|
||||||
|
12
|
||||||
|
'''
|
||||||
|
"""
|
||||||
|
|
||||||
|
iterator map[T, U](s: iterator:T{.inline.}, f: proc(x: T): U): U =
|
||||||
|
for e in s: yield f(e)
|
||||||
|
|
||||||
|
template toSeq(s: expr): expr =
|
||||||
|
var res = newSeq[type(s)](0)
|
||||||
|
for e in s: res.add(e)
|
||||||
|
res
|
||||||
|
|
||||||
|
var s1 = @[1, 2, 3]
|
||||||
|
for x in map(s1.items, proc (a:int): int = a*2):
|
||||||
|
echo x
|
||||||
|
|
||||||
|
var s2 = toSeq(map(s1.items, proc (a:int): int = a*4))
|
||||||
|
for x in s2:
|
||||||
|
echo x
|
||||||
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
discard """
|
discard """
|
||||||
file: "tstaticparams.nim"
|
file: "tstaticparams.nim"
|
||||||
output: "abracadabra\ntest\n3"
|
output: "abracadabra\ntest\n3\n15\4"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
@ -11,8 +11,8 @@ type
|
||||||
data: array[I, T]
|
data: array[I, T]
|
||||||
|
|
||||||
TA1[T; I: static[int]] = array[I, T]
|
TA1[T; I: static[int]] = array[I, T]
|
||||||
# TA2[T; I: static[int]] = array[0..I, T]
|
TA2[T; I: static[int]] = array[0..I, T]
|
||||||
# TA3[T; I: static[int]] = array[I-1, T]
|
TA3[T; I: static[int]] = array[I-1, T]
|
||||||
|
|
||||||
proc takeFoo(x: TFoo) =
|
proc takeFoo(x: TFoo) =
|
||||||
echo "abracadabra"
|
echo "abracadabra"
|
||||||
|
|
@ -26,6 +26,22 @@ echo high(y.data)
|
||||||
|
|
||||||
var
|
var
|
||||||
t1: TA1[float, 1]
|
t1: TA1[float, 1]
|
||||||
# t2: TA2[string, 4]
|
t2: TA2[string, 4]
|
||||||
# t3: TA3[int, 10]
|
t3: TA3[int, 10]
|
||||||
|
|
||||||
|
# example from the manual:
|
||||||
|
type
|
||||||
|
Matrix[M,N: static[int]; T] = array[0..(M*N - 1), T]
|
||||||
|
# Note how `Number` is just a type constraint here, while
|
||||||
|
# `static[int]` requires us to supply a compile-time int value
|
||||||
|
|
||||||
|
AffineTransform2D[T] = Matrix[3, 3, T]
|
||||||
|
AffineTransform3D[T] = Matrix[4, 4, T]
|
||||||
|
|
||||||
|
var m: AffineTransform3D[float]
|
||||||
|
echo high(m)
|
||||||
|
|
||||||
|
proc getRows(mtx: Matrix): int =
|
||||||
|
result = mtx.M
|
||||||
|
|
||||||
|
echo getRows(m)
|
||||||
|
|
|
||||||
12
tests/overload/tissue966.nim
Normal file
12
tests/overload/tissue966.nim
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
discard """
|
||||||
|
msg: 'type mismatch: got (PTest)'
|
||||||
|
"""
|
||||||
|
|
||||||
|
type
|
||||||
|
PTest = ref object
|
||||||
|
|
||||||
|
proc test(x: PTest, y: int) = nil
|
||||||
|
|
||||||
|
var buf: PTest
|
||||||
|
buf.test()
|
||||||
|
|
||||||
52
tests/static/tstaticparammacro.nim
Normal file
52
tests/static/tstaticparammacro.nim
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
discard """
|
||||||
|
msg: '''letters
|
||||||
|
aa
|
||||||
|
bb
|
||||||
|
numbers
|
||||||
|
11
|
||||||
|
22
|
||||||
|
AST a
|
||||||
|
[(11, 22), (33, 44)]
|
||||||
|
AST b
|
||||||
|
(e: [55, 66], f: [77, 88])
|
||||||
|
55
|
||||||
|
'''
|
||||||
|
"""
|
||||||
|
|
||||||
|
import macros
|
||||||
|
|
||||||
|
type
|
||||||
|
TConfig = tuple
|
||||||
|
letters: seq[string]
|
||||||
|
numbers:seq[int]
|
||||||
|
|
||||||
|
const data: Tconfig = (@["aa", "bb"], @[11, 22])
|
||||||
|
|
||||||
|
macro mymacro(data: static[TConfig]): stmt =
|
||||||
|
echo "letters"
|
||||||
|
for s in items(data.letters):
|
||||||
|
echo s
|
||||||
|
echo "numbers"
|
||||||
|
for n in items(data.numbers):
|
||||||
|
echo n
|
||||||
|
|
||||||
|
mymacro(data)
|
||||||
|
|
||||||
|
type
|
||||||
|
Ta = seq[tuple[c:int, d:int]]
|
||||||
|
Tb = tuple[e:seq[int], f:seq[int]]
|
||||||
|
|
||||||
|
const
|
||||||
|
a : Ta = @[(11, 22), (33, 44)]
|
||||||
|
b : Tb = (@[55,66], @[77, 88])
|
||||||
|
|
||||||
|
macro mA(data: static[Ta]): stmt =
|
||||||
|
echo "AST a \n", repr(data)
|
||||||
|
|
||||||
|
macro mB(data: static[Tb]): stmt =
|
||||||
|
echo "AST b \n", repr(data)
|
||||||
|
echo data.e[0]
|
||||||
|
|
||||||
|
mA(a)
|
||||||
|
mB(b)
|
||||||
|
|
||||||
|
|
@ -2,44 +2,51 @@ var x: ptr int
|
||||||
|
|
||||||
x = cast[ptr int](alloc(7))
|
x = cast[ptr int](alloc(7))
|
||||||
assert x != nil
|
assert x != nil
|
||||||
|
x = cast[ptr int](x.realloc(2))
|
||||||
x = alloc(int, 3)
|
|
||||||
assert x != nil
|
assert x != nil
|
||||||
x.dealloc()
|
x.dealloc()
|
||||||
|
|
||||||
x = alloc0(int, 4)
|
x = createU(int, 3)
|
||||||
|
assert x != nil
|
||||||
|
x.free()
|
||||||
|
|
||||||
|
x = create(int, 4)
|
||||||
assert cast[ptr array[4, int]](x)[0] == 0
|
assert cast[ptr array[4, int]](x)[0] == 0
|
||||||
assert cast[ptr array[4, int]](x)[1] == 0
|
assert cast[ptr array[4, int]](x)[1] == 0
|
||||||
assert cast[ptr array[4, int]](x)[2] == 0
|
assert cast[ptr array[4, int]](x)[2] == 0
|
||||||
assert cast[ptr array[4, int]](x)[3] == 0
|
assert cast[ptr array[4, int]](x)[3] == 0
|
||||||
|
|
||||||
x = cast[ptr int](x.realloc(2))
|
x = x.resize(4)
|
||||||
assert x != nil
|
assert x != nil
|
||||||
|
x.free()
|
||||||
x = x.reallocType(4)
|
|
||||||
assert x != nil
|
|
||||||
x.dealloc()
|
|
||||||
|
|
||||||
x = cast[ptr int](allocShared(100))
|
x = cast[ptr int](allocShared(100))
|
||||||
assert x != nil
|
assert x != nil
|
||||||
deallocShared(x)
|
deallocShared(x)
|
||||||
|
|
||||||
x = allocShared(int, 3)
|
x = createSharedU(int, 3)
|
||||||
assert x != nil
|
assert x != nil
|
||||||
x.deallocShared()
|
x.freeShared()
|
||||||
|
|
||||||
x = allocShared0(int, 3)
|
x = createShared(int, 3)
|
||||||
assert x != nil
|
assert x != nil
|
||||||
assert cast[ptr array[3, int]](x)[0] == 0
|
assert cast[ptr array[3, int]](x)[0] == 0
|
||||||
assert cast[ptr array[3, int]](x)[1] == 0
|
assert cast[ptr array[3, int]](x)[1] == 0
|
||||||
assert cast[ptr array[3, int]](x)[2] == 0
|
assert cast[ptr array[3, int]](x)[2] == 0
|
||||||
|
|
||||||
x = cast[ptr int](reallocShared(x, 2))
|
|
||||||
assert x != nil
|
assert x != nil
|
||||||
|
x = cast[ptr int](x.resizeShared(2))
|
||||||
|
assert x != nil
|
||||||
|
x.freeShared()
|
||||||
|
|
||||||
x = reallocType(x, 12)
|
x = create(int, 10)
|
||||||
assert x != nil
|
assert x != nil
|
||||||
|
x = x.resize(12)
|
||||||
|
assert x != nil
|
||||||
|
x.dealloc()
|
||||||
|
|
||||||
x = reallocSharedType(x, 1)
|
x = createShared(int, 1)
|
||||||
assert x != nil
|
assert x != nil
|
||||||
x.deallocShared()
|
x = x.resizeShared(1)
|
||||||
|
assert x != nil
|
||||||
|
x.freeShared()
|
||||||
|
|
|
||||||
16
tests/template/tissue909.nim
Normal file
16
tests/template/tissue909.nim
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
import macros
|
||||||
|
|
||||||
|
template baz() =
|
||||||
|
proc bar() =
|
||||||
|
var x = 5
|
||||||
|
iterator foo(): int {.closure.} =
|
||||||
|
echo x
|
||||||
|
var y = foo
|
||||||
|
discard y()
|
||||||
|
|
||||||
|
macro test(): stmt =
|
||||||
|
result = getAst(baz())
|
||||||
|
echo(treeRepr(result))
|
||||||
|
|
||||||
|
test()
|
||||||
|
bar()
|
||||||
|
|
@ -174,6 +174,10 @@ proc generateJson*(filename: string, commit: int) =
|
||||||
on A.name = B.name and A.category = B.category
|
on A.name = B.name and A.category = B.category
|
||||||
where A.[commit] = ? and B.[commit] = ? and A.machine = ?
|
where A.[commit] = ? and B.[commit] = ? and A.machine = ?
|
||||||
and A.result != B.result"""
|
and A.result != B.result"""
|
||||||
|
selResults = """select
|
||||||
|
name, category, target, action, result, expected, given
|
||||||
|
from TestResult
|
||||||
|
where [commit] = ?"""
|
||||||
var db = open(connection="testament.db", user="testament", password="",
|
var db = open(connection="testament.db", user="testament", password="",
|
||||||
database="testament")
|
database="testament")
|
||||||
let lastCommit = db.getCommit(commit)
|
let lastCommit = db.getCommit(commit)
|
||||||
|
|
@ -189,6 +193,20 @@ proc generateJson*(filename: string, commit: int) =
|
||||||
|
|
||||||
outfile.writeln("""{"total": $#, "passed": $#, "skipped": $#""" % data)
|
outfile.writeln("""{"total": $#, "passed": $#, "skipped": $#""" % data)
|
||||||
|
|
||||||
|
let results = newJArray()
|
||||||
|
for row in db.rows(sql(selResults), lastCommit):
|
||||||
|
var obj = newJObject()
|
||||||
|
obj["name"] = %row[0]
|
||||||
|
obj["category"] = %row[1]
|
||||||
|
obj["target"] = %row[2]
|
||||||
|
obj["action"] = %row[3]
|
||||||
|
obj["result"] = %row[4]
|
||||||
|
obj["expected"] = %row[5]
|
||||||
|
obj["given"] = %row[6]
|
||||||
|
results.add(obj)
|
||||||
|
outfile.writeln(""", "results": """)
|
||||||
|
outfile.write(results.pretty)
|
||||||
|
|
||||||
if not previousCommit.isNil:
|
if not previousCommit.isNil:
|
||||||
let diff = newJArray()
|
let diff = newJArray()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,17 @@ discard """
|
||||||
3
|
3
|
||||||
1
|
1
|
||||||
2
|
2
|
||||||
3'''
|
3
|
||||||
|
1
|
||||||
|
2
|
||||||
|
3
|
||||||
|
1
|
||||||
|
2
|
||||||
|
3
|
||||||
|
aa
|
||||||
|
bb
|
||||||
|
aa
|
||||||
|
bb'''
|
||||||
"""
|
"""
|
||||||
|
|
||||||
const s = @[1,2,3]
|
const s = @[1,2,3]
|
||||||
|
|
@ -19,3 +29,27 @@ static:
|
||||||
for e in s:
|
for e in s:
|
||||||
echo e
|
echo e
|
||||||
|
|
||||||
|
macro bar(x: static[seq[int]]): stmt =
|
||||||
|
for e in x:
|
||||||
|
echo e
|
||||||
|
|
||||||
|
bar s
|
||||||
|
bar(@[1, 2, 3])
|
||||||
|
|
||||||
|
type
|
||||||
|
TData = tuple
|
||||||
|
letters: seq[string]
|
||||||
|
numbers: seq[int]
|
||||||
|
|
||||||
|
const data: TData = (@["aa", "bb"], @[11, 22])
|
||||||
|
|
||||||
|
static:
|
||||||
|
var m = data
|
||||||
|
for x in m.letters:
|
||||||
|
echo x
|
||||||
|
|
||||||
|
macro ff(d: static[TData]): stmt =
|
||||||
|
for x in d.letters:
|
||||||
|
echo x
|
||||||
|
|
||||||
|
ff(data)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue