remove the old tester
This commit is contained in:
commit
efcbaa965e
21 changed files with 523 additions and 348 deletions
|
|
@ -336,26 +336,52 @@ type
|
||||||
tyConst, tyMutable, tyVarargs,
|
tyConst, tyMutable, tyVarargs,
|
||||||
tyIter, # unused
|
tyIter, # unused
|
||||||
tyProxy # used as errornous type (for idetools)
|
tyProxy # used as errornous type (for idetools)
|
||||||
tyTypeClass
|
|
||||||
tyParametricTypeClass # structured similarly to tyGenericInst
|
|
||||||
# lastSon is the body of the type class
|
|
||||||
|
|
||||||
tyBuiltInTypeClass # Type such as the catch-all object, tuple, seq, etc
|
tyBuiltInTypeClass #\
|
||||||
|
# Type such as the catch-all object, tuple, seq, etc
|
||||||
|
|
||||||
tyCompositeTypeClass #
|
tyUserTypeClass #\
|
||||||
|
# the body of a user-defined type class
|
||||||
|
|
||||||
tyAnd, tyOr, tyNot # boolean type classes such as `string|int`,`not seq`,
|
tyUserTypeClassInst #\
|
||||||
# `Sortable and Enumable`, etc
|
# Instance of a parametric user-defined type class.
|
||||||
|
# Structured similarly to tyGenericInst.
|
||||||
|
# tyGenericInst represents concrete types, while
|
||||||
|
# this is still a "generic param" that will bind types
|
||||||
|
# and resolves them during sigmatch and instantiation.
|
||||||
|
|
||||||
tyAnything # a type class matching any type
|
tyCompositeTypeClass #\
|
||||||
|
# Type such as seq[Number]
|
||||||
|
# The notes for tyUserTypeClassInst apply here as well
|
||||||
|
# sons[0]: the original expression used by the user.
|
||||||
|
# sons[1]: fully expanded and instantiated meta type
|
||||||
|
# (potentially following aliases)
|
||||||
|
|
||||||
tyStatic # a value known at compile type (the underlying type is .base)
|
tyAnd, tyOr, tyNot #\
|
||||||
|
# boolean type classes such as `string|int`,`not seq`,
|
||||||
|
# `Sortable and Enumable`, etc
|
||||||
|
|
||||||
tyFromExpr # This is a type representing an expression that depends
|
tyAnything #\
|
||||||
# on generic parameters (the exprsesion is stored in t.n)
|
# a type class matching any type
|
||||||
# It will be converted to a real type only during generic
|
|
||||||
# instantiation and prior to this it has the potential to
|
tyStatic #\
|
||||||
# be any type.
|
# a value known at compile type (the underlying type is .base)
|
||||||
|
|
||||||
|
tyFromExpr #\
|
||||||
|
# This is a type representing an expression that depends
|
||||||
|
# on generic parameters (the exprsesion is stored in t.n)
|
||||||
|
# It will be converted to a real type only during generic
|
||||||
|
# instantiation and prior to this it has the potential to
|
||||||
|
# be any type.
|
||||||
|
|
||||||
|
tyFieldAccessor #\
|
||||||
|
# Expressions such as Type.field (valid in contexts such
|
||||||
|
# as the `is` operator and magics like `high` and `low`).
|
||||||
|
# Could be lifted to a single argument proc returning the
|
||||||
|
# field value.
|
||||||
|
# sons[0]: type of containing object or tuple
|
||||||
|
# sons[1]: field type
|
||||||
|
# .n: nkDotExpr storing the field name
|
||||||
|
|
||||||
const
|
const
|
||||||
tyPureObject* = tyTuple
|
tyPureObject* = tyTuple
|
||||||
|
|
@ -364,8 +390,9 @@ const
|
||||||
|
|
||||||
tyUnknownTypes* = {tyError, tyFromExpr}
|
tyUnknownTypes* = {tyError, tyFromExpr}
|
||||||
|
|
||||||
tyTypeClasses* = {tyTypeClass, tyBuiltInTypeClass, tyCompositeTypeClass,
|
tyTypeClasses* = {tyBuiltInTypeClass, tyCompositeTypeClass,
|
||||||
tyParametricTypeClass, tyAnd, tyOr, tyNot, tyAnything}
|
tyUserTypeClass, tyUserTypeClassInst,
|
||||||
|
tyAnd, tyOr, tyNot, tyAnything}
|
||||||
|
|
||||||
tyMetaTypes* = {tyGenericParam, tyTypeDesc, tyStatic, tyExpr} + tyTypeClasses
|
tyMetaTypes* = {tyGenericParam, tyTypeDesc, tyStatic, tyExpr} + tyTypeClasses
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,7 @@ proc getUniqueType*(key: PType): PType =
|
||||||
gCanonicalTypes[k] = key
|
gCanonicalTypes[k] = key
|
||||||
result = key
|
result = key
|
||||||
of tyTypeDesc, tyTypeClasses, tyGenericParam,
|
of tyTypeDesc, tyTypeClasses, tyGenericParam,
|
||||||
tyFromExpr, tyStatic:
|
tyFromExpr, tyStatic, tyFieldAccessor:
|
||||||
internalError("GetUniqueType")
|
internalError("GetUniqueType")
|
||||||
of tyGenericInst, tyDistinct, tyOrdinal, tyMutable, tyConst, tyIter:
|
of tyGenericInst, tyDistinct, tyOrdinal, tyMutable, tyConst, tyIter:
|
||||||
result = getUniqueType(lastSon(key))
|
result = getUniqueType(lastSon(key))
|
||||||
|
|
|
||||||
|
|
@ -130,7 +130,7 @@ proc mapType(typ: PType): TJSTypeKind =
|
||||||
result = etyObject
|
result = etyObject
|
||||||
of tyNil: result = etyNull
|
of tyNil: result = etyNull
|
||||||
of tyGenericInst, tyGenericParam, tyGenericBody, tyGenericInvokation,
|
of tyGenericInst, tyGenericParam, tyGenericBody, tyGenericInvokation,
|
||||||
tyNone, tyFromExpr, tyForward, tyEmpty,
|
tyNone, tyFromExpr, tyForward, tyEmpty, tyFieldAccessor,
|
||||||
tyExpr, tyStmt, tyStatic, tyTypeDesc, tyTypeClasses:
|
tyExpr, tyStmt, tyStatic, tyTypeDesc, tyTypeClasses:
|
||||||
result = etyNone
|
result = etyNone
|
||||||
of tyProc: result = etyProc
|
of tyProc: result = etyProc
|
||||||
|
|
|
||||||
|
|
@ -239,7 +239,8 @@ proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode =
|
||||||
localError(n.info, errXExpectsTypeOrValue, opToStr[m])
|
localError(n.info, errXExpectsTypeOrValue, opToStr[m])
|
||||||
else:
|
else:
|
||||||
n.sons[1] = semExprWithType(c, n.sons[1], {efDetermineType})
|
n.sons[1] = semExprWithType(c, n.sons[1], {efDetermineType})
|
||||||
var typ = skipTypes(n.sons[1].typ, abstractVarRange+{tyTypeDesc})
|
var typ = skipTypes(n.sons[1].typ, abstractVarRange +
|
||||||
|
{tyTypeDesc, tyFieldAccessor})
|
||||||
case typ.kind
|
case typ.kind
|
||||||
of tySequence, tyString, tyOpenArray, tyVarargs:
|
of tySequence, tyString, tyOpenArray, tyVarargs:
|
||||||
n.typ = getSysType(tyInt)
|
n.typ = getSysType(tyInt)
|
||||||
|
|
@ -247,7 +248,7 @@ proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode =
|
||||||
n.typ = typ.sons[0] # indextype
|
n.typ = typ.sons[0] # indextype
|
||||||
of tyInt..tyInt64, tyChar, tyBool, tyEnum, tyUInt8, tyUInt16, tyUInt32:
|
of tyInt..tyInt64, tyChar, tyBool, tyEnum, tyUInt8, tyUInt16, tyUInt32:
|
||||||
# do not skip the range!
|
# do not skip the range!
|
||||||
n.typ = n.sons[1].typ.skipTypes(abstractVar)
|
n.typ = n.sons[1].typ.skipTypes(abstractVar + {tyFieldAccessor})
|
||||||
of tyGenericParam:
|
of tyGenericParam:
|
||||||
# prepare this for resolving in semtypinst:
|
# prepare this for resolving in semtypinst:
|
||||||
# we must use copyTree here in order to avoid creating a cycle
|
# we must use copyTree here in order to avoid creating a cycle
|
||||||
|
|
@ -306,7 +307,7 @@ proc isOpImpl(c: PContext, n: PNode): PNode =
|
||||||
n[1].typ != nil and n[1].typ.kind == tyTypeDesc and
|
n[1].typ != nil and n[1].typ.kind == tyTypeDesc and
|
||||||
n[2].kind in {nkStrLit..nkTripleStrLit, nkType}
|
n[2].kind in {nkStrLit..nkTripleStrLit, nkType}
|
||||||
|
|
||||||
let t1 = n[1].typ.skipTypes({tyTypeDesc})
|
let t1 = n[1].typ.skipTypes({tyTypeDesc, tyFieldAccessor})
|
||||||
|
|
||||||
if n[2].kind in {nkStrLit..nkTripleStrLit}:
|
if n[2].kind in {nkStrLit..nkTripleStrLit}:
|
||||||
case n[2].strVal.normalize
|
case n[2].strVal.normalize
|
||||||
|
|
@ -321,24 +322,13 @@ proc isOpImpl(c: PContext, n: PNode): PNode =
|
||||||
t.callConv == ccClosure and
|
t.callConv == ccClosure and
|
||||||
tfIterator in t.flags))
|
tfIterator in t.flags))
|
||||||
else:
|
else:
|
||||||
var match: bool
|
var t2 = n[2].typ.skipTypes({tyTypeDesc})
|
||||||
let t2 = n[2].typ
|
let lifted = liftParamType(c, skType, newNodeI(nkArgList, n.info),
|
||||||
case t2.kind
|
t2, ":anon", n.info)
|
||||||
of tyTypeClasses:
|
if lifted != nil: t2 = lifted
|
||||||
var m: TCandidate
|
var m: TCandidate
|
||||||
initCandidate(c, m, t2)
|
initCandidate(c, m, t2)
|
||||||
match = matchUserTypeClass(c, m, emptyNode, t2, t1) != nil
|
let match = typeRel(m, t2, t1) != isNone
|
||||||
of tyOrdinal:
|
|
||||||
var m: TCandidate
|
|
||||||
initCandidate(c, m, t2)
|
|
||||||
match = isOrdinalType(t1)
|
|
||||||
of tySequence, tyArray, tySet:
|
|
||||||
var m: TCandidate
|
|
||||||
initCandidate(c, m, t2)
|
|
||||||
match = typeRel(m, t2, t1) != isNone
|
|
||||||
else:
|
|
||||||
match = sameType(t1, t2)
|
|
||||||
|
|
||||||
result = newIntNode(nkIntLit, ord(match))
|
result = newIntNode(nkIntLit, ord(match))
|
||||||
|
|
||||||
result.typ = n.typ
|
result.typ = n.typ
|
||||||
|
|
@ -948,6 +938,13 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
let foundTyp = makeTypeDesc(c, rawTyp)
|
let foundTyp = makeTypeDesc(c, rawTyp)
|
||||||
return newSymNode(copySym(tParam.sym).linkTo(foundTyp), n.info)
|
return newSymNode(copySym(tParam.sym).linkTo(foundTyp), n.info)
|
||||||
return
|
return
|
||||||
|
of tyObject, tyTuple:
|
||||||
|
if ty.n.kind == nkRecList:
|
||||||
|
for field in ty.n.sons:
|
||||||
|
if field.sym.name == i:
|
||||||
|
n.typ = newTypeWithSons(c, tyFieldAccessor, @[ty, field.sym.typ])
|
||||||
|
n.typ.n = copyTree(n)
|
||||||
|
return n
|
||||||
else:
|
else:
|
||||||
# echo "TYPE FIELD ACCESS"
|
# echo "TYPE FIELD ACCESS"
|
||||||
# debug ty
|
# debug ty
|
||||||
|
|
|
||||||
|
|
@ -252,8 +252,7 @@ proc evalIs(n, a: PNode): PNode =
|
||||||
else:
|
else:
|
||||||
# XXX semexprs.isOpImpl is slightly different and requires a context. yay.
|
# XXX semexprs.isOpImpl is slightly different and requires a context. yay.
|
||||||
let t2 = n[2].typ
|
let t2 = n[2].typ
|
||||||
var match = if t2.kind == tyTypeClass: true
|
var match = sameType(t1, t2)
|
||||||
else: sameType(t1, t2)
|
|
||||||
result = newIntNode(nkIntLit, ord(match))
|
result = newIntNode(nkIntLit, ord(match))
|
||||||
result.typ = n.typ
|
result.typ = n.typ
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1247,7 +1247,7 @@ proc semStmtList(c: PContext, n: PNode): PNode =
|
||||||
if n.sons[i].typ == enforceVoidContext or usesResult(n.sons[i]):
|
if n.sons[i].typ == enforceVoidContext or usesResult(n.sons[i]):
|
||||||
voidContext = true
|
voidContext = true
|
||||||
n.typ = enforceVoidContext
|
n.typ = enforceVoidContext
|
||||||
if i != last or voidContext:
|
if i != last or voidContext or c.inTypeClass > 0:
|
||||||
discardCheck(c, n.sons[i])
|
discardCheck(c, n.sons[i])
|
||||||
else:
|
else:
|
||||||
n.typ = n.sons[i].typ
|
n.typ = n.sons[i].typ
|
||||||
|
|
|
||||||
|
|
@ -710,6 +710,11 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
||||||
result = addImplicitGeneric(result)
|
result = addImplicitGeneric(result)
|
||||||
|
|
||||||
of tyGenericInst:
|
of tyGenericInst:
|
||||||
|
if paramType.lastSon.kind == tyUserTypeClass:
|
||||||
|
var cp = copyType(paramType, getCurrOwner(), false)
|
||||||
|
cp.kind = tyUserTypeClassInst
|
||||||
|
return addImplicitGeneric(cp)
|
||||||
|
|
||||||
for i in 1 .. (paramType.sons.len - 2):
|
for i in 1 .. (paramType.sons.len - 2):
|
||||||
var lifted = liftingWalk(paramType.sons[i])
|
var lifted = liftingWalk(paramType.sons[i])
|
||||||
if lifted != nil:
|
if lifted != nil:
|
||||||
|
|
@ -731,7 +736,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
||||||
allowMetaTypes = true)
|
allowMetaTypes = true)
|
||||||
result = liftingWalk(expanded)
|
result = liftingWalk(expanded)
|
||||||
|
|
||||||
of tyTypeClass, tyBuiltInTypeClass, tyAnd, tyOr, tyNot:
|
of tyUserTypeClass, tyBuiltInTypeClass, tyAnd, tyOr, tyNot:
|
||||||
result = addImplicitGeneric(copyType(paramType, getCurrOwner(), true))
|
result = addImplicitGeneric(copyType(paramType, getCurrOwner(), true))
|
||||||
|
|
||||||
of tyExpr:
|
of tyExpr:
|
||||||
|
|
@ -923,7 +928,7 @@ proc freshType(res, prev: PType): PType {.inline.} =
|
||||||
|
|
||||||
proc semTypeClass(c: PContext, n: PNode, prev: PType): PType =
|
proc semTypeClass(c: PContext, n: PNode, prev: PType): PType =
|
||||||
# if n.sonsLen == 0: return newConstraint(c, tyTypeClass)
|
# if n.sonsLen == 0: return newConstraint(c, tyTypeClass)
|
||||||
result = newOrPrevType(tyTypeClass, prev, c)
|
result = newOrPrevType(tyUserTypeClass, prev, c)
|
||||||
result.n = n
|
result.n = n
|
||||||
|
|
||||||
let
|
let
|
||||||
|
|
|
||||||
|
|
@ -361,6 +361,9 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
|
||||||
elif t.sonsLen > 0:
|
elif t.sonsLen > 0:
|
||||||
result = makeTypeDesc(cl.c, replaceTypeVarsT(cl, t.sons[0]))
|
result = makeTypeDesc(cl.c, replaceTypeVarsT(cl, t.sons[0]))
|
||||||
|
|
||||||
|
of tyUserTypeClass:
|
||||||
|
result = t
|
||||||
|
|
||||||
of tyGenericInst:
|
of tyGenericInst:
|
||||||
result = instCopyType(cl, t)
|
result = instCopyType(cl, t)
|
||||||
for i in 1 .. <result.sonsLen:
|
for i in 1 .. <result.sonsLen:
|
||||||
|
|
|
||||||
|
|
@ -140,7 +140,7 @@ proc sumGeneric(t: PType): int =
|
||||||
result = ord(t.kind == tyGenericInvokation)
|
result = ord(t.kind == tyGenericInvokation)
|
||||||
for i in 0 .. <t.len: result += t.sons[i].sumGeneric
|
for i in 0 .. <t.len: result += t.sons[i].sumGeneric
|
||||||
break
|
break
|
||||||
of tyGenericParam, tyExpr, tyStatic, tyStmt, tyTypeDesc, tyTypeClass: break
|
of tyGenericParam, tyExpr, tyStatic, tyStmt, tyTypeDesc: break
|
||||||
else: return 0
|
else: return 0
|
||||||
|
|
||||||
proc complexDisambiguation(a, b: PType): int =
|
proc complexDisambiguation(a, b: PType): int =
|
||||||
|
|
@ -399,6 +399,70 @@ proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} =
|
||||||
else:
|
else:
|
||||||
result = isNone
|
result = isNone
|
||||||
|
|
||||||
|
proc matchUserTypeClass*(c: PContext, m: var TCandidate,
|
||||||
|
ff, a: PType): TTypeRelation =
|
||||||
|
#if f.n == nil:
|
||||||
|
# let r = typeRel(m, f, a)
|
||||||
|
# return if r == isGeneric: arg else: nil
|
||||||
|
|
||||||
|
var body = ff.skipTypes({tyUserTypeClassInst})
|
||||||
|
|
||||||
|
# var prev = PType(idTableGet(m.bindings, f))
|
||||||
|
# if prev != nil:
|
||||||
|
# if sameType(prev, a): return arg
|
||||||
|
# else: return nil
|
||||||
|
|
||||||
|
# pushInfoContext(arg.info)
|
||||||
|
openScope(c)
|
||||||
|
inc c.inTypeClass
|
||||||
|
|
||||||
|
finally:
|
||||||
|
dec c.inTypeClass
|
||||||
|
closeScope(c)
|
||||||
|
|
||||||
|
if ff.kind == tyUserTypeClassInst:
|
||||||
|
for i in 1 .. <(ff.len - 1):
|
||||||
|
var
|
||||||
|
typeParamName = ff.base.sons[i-1].sym.name
|
||||||
|
typ = ff.sons[i]
|
||||||
|
param = newSym(skType, typeParamName, body.sym, body.sym.info)
|
||||||
|
|
||||||
|
param.typ = makeTypeDesc(c, typ)
|
||||||
|
addDecl(c, param)
|
||||||
|
|
||||||
|
for param in body.n[0]:
|
||||||
|
var
|
||||||
|
dummyName: PNode
|
||||||
|
dummyType: PType
|
||||||
|
|
||||||
|
if param.kind == nkVarTy:
|
||||||
|
dummyName = param[0]
|
||||||
|
dummyType = makeVarType(c, a)
|
||||||
|
else:
|
||||||
|
dummyName = param
|
||||||
|
dummyType = a
|
||||||
|
|
||||||
|
internalAssert dummyName.kind == nkIdent
|
||||||
|
var dummyParam = newSym(skType, dummyName.ident, body.sym, body.sym.info)
|
||||||
|
dummyParam.typ = dummyType
|
||||||
|
addDecl(c, dummyParam)
|
||||||
|
|
||||||
|
var checkedBody = c.semTryExpr(c, copyTree(body.n[3]), bufferErrors = false)
|
||||||
|
m.errors = bufferedMsgs
|
||||||
|
clearBufferedMsgs()
|
||||||
|
if checkedBody == nil: return isNone
|
||||||
|
|
||||||
|
if checkedBody.kind == nkStmtList:
|
||||||
|
for stmt in checkedBody:
|
||||||
|
case stmt.kind
|
||||||
|
of nkReturnStmt: discard
|
||||||
|
of nkTypeSection: discard
|
||||||
|
of nkConstDef: discard
|
||||||
|
else: discard
|
||||||
|
|
||||||
|
return isGeneric
|
||||||
|
# put(m.bindings, f, a)
|
||||||
|
|
||||||
proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
||||||
# typeRel can be used to establish various relationships between types:
|
# typeRel can be used to establish various relationships between types:
|
||||||
#
|
#
|
||||||
|
|
@ -418,6 +482,11 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
||||||
|
|
||||||
result = isNone
|
result = isNone
|
||||||
assert(f != nil)
|
assert(f != nil)
|
||||||
|
|
||||||
|
if f.kind == tyExpr:
|
||||||
|
put(c.bindings, f, aOrig)
|
||||||
|
return isGeneric
|
||||||
|
|
||||||
assert(aOrig != nil)
|
assert(aOrig != nil)
|
||||||
|
|
||||||
# var and static arguments match regular modifier-free types
|
# var and static arguments match regular modifier-free types
|
||||||
|
|
@ -751,6 +820,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
||||||
else:
|
else:
|
||||||
return isNone
|
return isNone
|
||||||
|
|
||||||
|
of tyUserTypeClass, tyUserTypeClassInst:
|
||||||
|
considerPreviousT:
|
||||||
|
result = matchUserTypeClass(c.c, c, f, a)
|
||||||
|
if result == isGeneric:
|
||||||
|
put(c.bindings, f, a)
|
||||||
|
|
||||||
of tyCompositeTypeClass:
|
of tyCompositeTypeClass:
|
||||||
considerPreviousT:
|
considerPreviousT:
|
||||||
if typeRel(c, f.sons[1], a) != isNone:
|
if typeRel(c, f.sons[1], a) != isNone:
|
||||||
|
|
@ -759,7 +834,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
||||||
else:
|
else:
|
||||||
return isNone
|
return isNone
|
||||||
|
|
||||||
of tyGenericParam, tyTypeClass:
|
of tyGenericParam:
|
||||||
var x = PType(idTableGet(c.bindings, f))
|
var x = PType(idTableGet(c.bindings, f))
|
||||||
if x == nil:
|
if x == nil:
|
||||||
if c.calleeSym != nil and c.calleeSym.kind == skType and
|
if c.calleeSym != nil and c.calleeSym.kind == skType and
|
||||||
|
|
@ -822,7 +897,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
||||||
else: a.sons[0]
|
else: a.sons[0]
|
||||||
result = typeRel(c, prev.sons[0], toMatch)
|
result = typeRel(c, prev.sons[0], toMatch)
|
||||||
|
|
||||||
of tyExpr, tyStmt:
|
of tyStmt:
|
||||||
result = isGeneric
|
result = isGeneric
|
||||||
|
|
||||||
of tyProxy:
|
of tyProxy:
|
||||||
|
|
@ -904,57 +979,6 @@ 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 matchUserTypeClass*(c: PContext, m: var TCandidate,
|
|
||||||
arg: PNode, f, a: PType): PNode =
|
|
||||||
if f.n == nil:
|
|
||||||
let r = typeRel(m, f, a)
|
|
||||||
return if r == isGeneric: arg else: nil
|
|
||||||
|
|
||||||
var prev = PType(idTableGet(m.bindings, f))
|
|
||||||
if prev != nil:
|
|
||||||
if sameType(prev, a): return arg
|
|
||||||
else: return nil
|
|
||||||
|
|
||||||
# pushInfoContext(arg.info)
|
|
||||||
openScope(c)
|
|
||||||
inc c.inTypeClass
|
|
||||||
|
|
||||||
finally:
|
|
||||||
dec c.inTypeClass
|
|
||||||
closeScope(c)
|
|
||||||
|
|
||||||
for param in f.n[0]:
|
|
||||||
var
|
|
||||||
dummyName: PNode
|
|
||||||
dummyType: PType
|
|
||||||
|
|
||||||
if param.kind == nkVarTy:
|
|
||||||
dummyName = param[0]
|
|
||||||
dummyType = makeVarType(c, a)
|
|
||||||
else:
|
|
||||||
dummyName = param
|
|
||||||
dummyType = a
|
|
||||||
|
|
||||||
internalAssert dummyName.kind == nkIdent
|
|
||||||
var dummyParam = newSym(skType, dummyName.ident, f.sym, f.sym.info)
|
|
||||||
dummyParam.typ = dummyType
|
|
||||||
addDecl(c, dummyParam)
|
|
||||||
|
|
||||||
for stmt in f.n[3]:
|
|
||||||
var e = c.semTryExpr(c, copyTree(stmt), bufferErrors = false)
|
|
||||||
m.errors = bufferedMsgs
|
|
||||||
clearBufferedMsgs()
|
|
||||||
if e == nil: return nil
|
|
||||||
|
|
||||||
case e.kind
|
|
||||||
of nkReturnStmt: discard
|
|
||||||
of nkTypeSection: discard
|
|
||||||
of nkConstDef: discard
|
|
||||||
else: discard
|
|
||||||
|
|
||||||
result = arg
|
|
||||||
put(m.bindings, f, a)
|
|
||||||
|
|
||||||
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
|
||||||
|
|
@ -975,25 +999,9 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
|
||||||
argType = arg.typ
|
argType = arg.typ
|
||||||
|
|
||||||
var
|
var
|
||||||
r: TTypeRelation
|
|
||||||
a = if c.inTypeClass > 0: argType.skipTypes({tyTypeDesc})
|
a = if c.inTypeClass > 0: argType.skipTypes({tyTypeDesc})
|
||||||
else: argType
|
else: argType
|
||||||
|
|
||||||
case fMaybeStatic.kind
|
|
||||||
of tyTypeClass, tyParametricTypeClass:
|
|
||||||
if fMaybeStatic.n != nil:
|
|
||||||
let match = matchUserTypeClass(c, m, arg, fMaybeStatic, a)
|
|
||||||
if match != nil:
|
|
||||||
r = isGeneric
|
|
||||||
arg = match
|
|
||||||
else:
|
|
||||||
r = isNone
|
|
||||||
else:
|
|
||||||
r = typeRel(m, f, a)
|
|
||||||
of tyExpr:
|
|
||||||
r = isGeneric
|
|
||||||
put(m.bindings, f, arg.typ)
|
|
||||||
else:
|
|
||||||
r = typeRel(m, f, a)
|
r = typeRel(m, f, a)
|
||||||
|
|
||||||
case r
|
case r
|
||||||
|
|
|
||||||
|
|
@ -389,7 +389,7 @@ proc transformConv(c: PTransf, n: PNode): PTransNode =
|
||||||
result[0] = transform(c, n.sons[1])
|
result[0] = transform(c, n.sons[1])
|
||||||
else:
|
else:
|
||||||
result = transform(c, n.sons[1])
|
result = transform(c, n.sons[1])
|
||||||
of tyGenericParam, tyOrdinal, tyTypeClass:
|
of tyGenericParam, tyOrdinal:
|
||||||
result = transform(c, n.sons[1])
|
result = transform(c, n.sons[1])
|
||||||
# happens sometimes for generated assignments, etc.
|
# happens sometimes for generated assignments, etc.
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -404,9 +404,10 @@ const
|
||||||
"float", "float32", "float64", "float128",
|
"float", "float32", "float64", "float128",
|
||||||
"uint", "uint8", "uint16", "uint32", "uint64",
|
"uint", "uint8", "uint16", "uint32", "uint64",
|
||||||
"bignum", "const ",
|
"bignum", "const ",
|
||||||
"!", "varargs[$1]", "iter[$1]", "Error Type", "TypeClass",
|
"!", "varargs[$1]", "iter[$1]", "Error Type",
|
||||||
"ParametricTypeClass", "BuiltInTypeClass", "CompositeTypeClass",
|
"BuiltInTypeClass", "UserTypeClass",
|
||||||
"and", "or", "not", "any", "static", "TypeFromExpr"]
|
"UserTypeClassInst", "CompositeTypeClass",
|
||||||
|
"and", "or", "not", "any", "static", "TypeFromExpr", "FieldAccessor"]
|
||||||
|
|
||||||
proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
|
proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
|
||||||
var t = typ
|
var t = typ
|
||||||
|
|
@ -434,11 +435,30 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
|
||||||
of tyStatic:
|
of tyStatic:
|
||||||
internalAssert t.len > 0
|
internalAssert t.len > 0
|
||||||
result = "static[" & typeToString(t.sons[0]) & "]"
|
result = "static[" & typeToString(t.sons[0]) & "]"
|
||||||
of tyTypeClass:
|
of tyUserTypeClass:
|
||||||
internalAssert t.sym != nil and t.sym.owner != nil
|
internalAssert t.sym != nil and t.sym.owner != nil
|
||||||
return t.sym.owner.name.s
|
return t.sym.owner.name.s
|
||||||
of tyBuiltInTypeClass:
|
of tyBuiltInTypeClass:
|
||||||
return "TypeClass"
|
result = case t.base.kind:
|
||||||
|
of tyVar: "var"
|
||||||
|
of tyRef: "ref"
|
||||||
|
of tyPtr: "ptr"
|
||||||
|
of tySequence: "seq"
|
||||||
|
of tyArray: "array"
|
||||||
|
of tySet: "set"
|
||||||
|
of tyRange: "range"
|
||||||
|
of tyDistinct: "distinct"
|
||||||
|
of tyProc: "proc"
|
||||||
|
of tyObject: "object"
|
||||||
|
of tyTuple: "tuple"
|
||||||
|
else: (internalAssert(false); "")
|
||||||
|
of tyUserTypeClassInst:
|
||||||
|
let body = t.base
|
||||||
|
result = body.sym.name.s & "["
|
||||||
|
for i in countup(1, sonsLen(t) - 2):
|
||||||
|
if i > 1: add(result, ", ")
|
||||||
|
add(result, typeToString(t.sons[i]))
|
||||||
|
result.add "]"
|
||||||
of tyAnd:
|
of tyAnd:
|
||||||
result = typeToString(t.sons[0]) & " and " & typeToString(t.sons[1])
|
result = typeToString(t.sons[0]) & " and " & typeToString(t.sons[1])
|
||||||
of tyOr:
|
of tyOr:
|
||||||
|
|
@ -448,7 +468,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
|
||||||
of tyExpr:
|
of tyExpr:
|
||||||
internalAssert t.len == 0
|
internalAssert t.len == 0
|
||||||
result = "expr"
|
result = "expr"
|
||||||
of tyFromExpr:
|
of tyFromExpr, tyFieldAccessor:
|
||||||
result = renderTree(t.n)
|
result = renderTree(t.n)
|
||||||
of tyArray:
|
of tyArray:
|
||||||
if t.sons[0].kind == tyRange:
|
if t.sons[0].kind == tyRange:
|
||||||
|
|
@ -546,7 +566,8 @@ proc firstOrd(t: PType): BiggestInt =
|
||||||
else:
|
else:
|
||||||
assert(t.n.sons[0].kind == nkSym)
|
assert(t.n.sons[0].kind == nkSym)
|
||||||
result = t.n.sons[0].sym.position
|
result = t.n.sons[0].sym.position
|
||||||
of tyGenericInst, tyDistinct, tyConst, tyMutable, tyTypeDesc:
|
of tyGenericInst, tyDistinct, tyConst, tyMutable,
|
||||||
|
tyTypeDesc, tyFieldAccessor:
|
||||||
result = firstOrd(lastSon(t))
|
result = firstOrd(lastSon(t))
|
||||||
else:
|
else:
|
||||||
internalError("invalid kind for first(" & $t.kind & ')')
|
internalError("invalid kind for first(" & $t.kind & ')')
|
||||||
|
|
@ -579,7 +600,8 @@ proc lastOrd(t: PType): BiggestInt =
|
||||||
of tyEnum:
|
of tyEnum:
|
||||||
assert(t.n.sons[sonsLen(t.n) - 1].kind == nkSym)
|
assert(t.n.sons[sonsLen(t.n) - 1].kind == nkSym)
|
||||||
result = t.n.sons[sonsLen(t.n) - 1].sym.position
|
result = t.n.sons[sonsLen(t.n) - 1].sym.position
|
||||||
of tyGenericInst, tyDistinct, tyConst, tyMutable, tyTypeDesc:
|
of tyGenericInst, tyDistinct, tyConst, tyMutable,
|
||||||
|
tyTypeDesc, tyFieldAccessor:
|
||||||
result = lastOrd(lastSon(t))
|
result = lastOrd(lastSon(t))
|
||||||
of tyProxy: result = 0
|
of tyProxy: result = 0
|
||||||
else:
|
else:
|
||||||
|
|
@ -875,9 +897,9 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
|
||||||
of tyGenericInvokation, tyGenericBody, tySequence,
|
of tyGenericInvokation, tyGenericBody, tySequence,
|
||||||
tyOpenArray, tySet, tyRef, tyPtr, tyVar, tyArrayConstr,
|
tyOpenArray, tySet, tyRef, tyPtr, tyVar, tyArrayConstr,
|
||||||
tyArray, tyProc, tyConst, tyMutable, tyVarargs, tyIter,
|
tyArray, tyProc, tyConst, tyMutable, tyVarargs, tyIter,
|
||||||
tyOrdinal, tyTypeClasses:
|
tyOrdinal, tyTypeClasses, tyFieldAccessor:
|
||||||
cycleCheck()
|
cycleCheck()
|
||||||
if a.kind == tyTypeClass and a.n != nil: return a.n == b.n
|
if a.kind == tyUserTypeClass and a.n != nil: return a.n == b.n
|
||||||
result = sameChildrenAux(a, b, c) and sameFlags(a, b)
|
result = sameChildrenAux(a, b, c) and sameFlags(a, b)
|
||||||
if result and a.kind == tyProc:
|
if result and a.kind == tyProc:
|
||||||
result = ((IgnoreCC in c.flags) or a.callConv == b.callConv) and
|
result = ((IgnoreCC in c.flags) or a.callConv == b.callConv) and
|
||||||
|
|
@ -1021,7 +1043,7 @@ proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind,
|
||||||
of tyTypeClasses:
|
of tyTypeClasses:
|
||||||
result = true
|
result = true
|
||||||
of tyGenericBody, tyGenericParam, tyGenericInvokation,
|
of tyGenericBody, tyGenericParam, tyGenericInvokation,
|
||||||
tyNone, tyForward, tyFromExpr:
|
tyNone, tyForward, tyFromExpr, tyFieldAccessor:
|
||||||
result = false
|
result = false
|
||||||
of tyNil:
|
of tyNil:
|
||||||
result = kind == skConst
|
result = kind == skConst
|
||||||
|
|
@ -1231,8 +1253,15 @@ proc getSize(typ: PType): BiggestInt =
|
||||||
if result < 0: internalError("getSize: " & $typ.kind)
|
if result < 0: internalError("getSize: " & $typ.kind)
|
||||||
|
|
||||||
proc containsGenericTypeIter(t: PType, closure: PObject): bool =
|
proc containsGenericTypeIter(t: PType, closure: PObject): bool =
|
||||||
result = t.kind in GenericTypes + tyTypeClasses + {tyTypeDesc,tyFromExpr} or
|
if t.kind in GenericTypes + tyTypeClasses + {tyFromExpr}:
|
||||||
t.kind == tyStatic and t.n == nil
|
return true
|
||||||
|
|
||||||
|
if t.kind == tyTypeDesc:
|
||||||
|
if t.sonsLen == 0: return true
|
||||||
|
if containsGenericTypeIter(t.base, closure): return true
|
||||||
|
return false
|
||||||
|
|
||||||
|
return t.kind == tyStatic and t.n == nil
|
||||||
|
|
||||||
proc containsGenericType*(t: PType): bool =
|
proc containsGenericType*(t: PType): bool =
|
||||||
result = iterOverType(t, containsGenericTypeIter, nil)
|
result = iterOverType(t, containsGenericTypeIter, nil)
|
||||||
|
|
|
||||||
|
|
@ -812,7 +812,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): PNode =
|
||||||
let t1 = regs[rb].typ.skipTypes({tyTypeDesc})
|
let t1 = regs[rb].typ.skipTypes({tyTypeDesc})
|
||||||
let t2 = c.types[regs[rc].intVal.int]
|
let t2 = c.types[regs[rc].intVal.int]
|
||||||
# XXX: This should use the standard isOpImpl
|
# XXX: This should use the standard isOpImpl
|
||||||
let match = if t2.kind == tyTypeClass: true
|
let match = if t2.kind == tyUserTypeClass: true
|
||||||
else: sameType(t1, t2)
|
else: sameType(t1, t2)
|
||||||
regs[ra].intVal = ord(match)
|
regs[ra].intVal = ord(match)
|
||||||
of opcSetLenSeq:
|
of opcSetLenSeq:
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ span.LongStringLit {color: blue}
|
||||||
span.CharLit {color: blue}
|
span.CharLit {color: blue}
|
||||||
span.EscapeSequence {color: black}
|
span.EscapeSequence {color: black}
|
||||||
span.Operator {color: black}
|
span.Operator {color: black}
|
||||||
span.Punctation {color: black}
|
span.Punctuation {color: black}
|
||||||
span.Comment, span.LongComment {font-style:italic; color: green}
|
span.Comment, span.LongComment {font-style:italic; color: green}
|
||||||
span.RegularExpression {color: DarkViolet}
|
span.RegularExpression {color: DarkViolet}
|
||||||
span.TagStart {color: DarkViolet}
|
span.TagStart {color: DarkViolet}
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,7 @@ doc.file = """
|
||||||
\newcommand{\spanCharLit}[1]{#1}
|
\newcommand{\spanCharLit}[1]{#1}
|
||||||
\newcommand{\spanEscapeSequence}[1]{#1}
|
\newcommand{\spanEscapeSequence}[1]{#1}
|
||||||
\newcommand{\spanOperator}[1]{#1}
|
\newcommand{\spanOperator}[1]{#1}
|
||||||
\newcommand{\spanPunctation}[1]{#1}
|
\newcommand{\spanPunctuation}[1]{#1}
|
||||||
\newcommand{\spanComment}[1]{\emph{#1}}
|
\newcommand{\spanComment}[1]{\emph{#1}}
|
||||||
\newcommand{\spanLongComment}[1]{\emph{#1}}
|
\newcommand{\spanLongComment}[1]{\emph{#1}}
|
||||||
\newcommand{\spanRegularExpression}[1]{#1}
|
\newcommand{\spanRegularExpression}[1]{#1}
|
||||||
|
|
|
||||||
|
|
@ -1,210 +0,0 @@
|
||||||
#
|
|
||||||
#
|
|
||||||
# Nimrod's Runtime Library
|
|
||||||
# (c) Copyright 2012 Andreas Rumpf
|
|
||||||
#
|
|
||||||
# See the file "copying.txt", included in this
|
|
||||||
# distribution, for details about the copyright.
|
|
||||||
#
|
|
||||||
|
|
||||||
## This module implements a simple logger. It is based on the following design:
|
|
||||||
## * Runtime log formating is a bug: Sooner or later every log file is parsed.
|
|
||||||
## * Keep it simple: If this library does not fullfill your needs, write your
|
|
||||||
## own. Trying to support every logging feature just leads to bloat.
|
|
||||||
##
|
|
||||||
## Format is::
|
|
||||||
##
|
|
||||||
## DEBUG|INFO|... (2009-11-02 00:00:00)? (Component: )? Message
|
|
||||||
##
|
|
||||||
##
|
|
||||||
|
|
||||||
import strutils, os, times
|
|
||||||
|
|
||||||
type
|
|
||||||
TLevel* = enum ## logging level
|
|
||||||
lvlAll, ## all levels active
|
|
||||||
lvlDebug, ## debug level (and any above) active
|
|
||||||
lvlInfo, ## info level (and any above) active
|
|
||||||
lvlWarn, ## warn level (and any above) active
|
|
||||||
lvlError, ## error level (and any above) active
|
|
||||||
lvlFatal, ## fatal level (and any above) active
|
|
||||||
lvlNone
|
|
||||||
|
|
||||||
const
|
|
||||||
LevelNames*: array [TLevel, string] = [
|
|
||||||
"DEBUG", "DEBUG", "INFO", "WARN", "ERROR", "FATAL", "NONE"
|
|
||||||
]
|
|
||||||
|
|
||||||
defaultFmtStr = "" ## default string between log level and message per logger
|
|
||||||
verboseFmtStr = "$date $time "
|
|
||||||
|
|
||||||
type
|
|
||||||
TLogger* = object of TObject ## abstract logger; the base type of all loggers
|
|
||||||
levelThreshold*: TLevel ## only messages of level >= levelThreshold
|
|
||||||
## should be processed
|
|
||||||
fmtStr: string ## = defaultFmtStr by default, see substituteLog for $date etc.
|
|
||||||
|
|
||||||
TConsoleLogger* = object of TLogger ## logger that writes the messages to the
|
|
||||||
## console
|
|
||||||
|
|
||||||
TFileLogger* = object of TLogger ## logger that writes the messages to a file
|
|
||||||
f: TFile
|
|
||||||
|
|
||||||
# TODO: implement rolling log, will produce filename.1, filename.2 etc.
|
|
||||||
TRollingFileLogger* = object of TFileLogger ## logger that writes the
|
|
||||||
## message to a file
|
|
||||||
maxLines: int # maximum number of lines
|
|
||||||
curLine : int
|
|
||||||
baseName: string # initial filename
|
|
||||||
logFiles: int # how many log files already created, e.g. basename.1, basename.2...
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
proc substituteLog*(frmt: string): string =
|
|
||||||
## converts $date to the current date
|
|
||||||
## converts $time to the current time
|
|
||||||
## converts $app to getAppFilename()
|
|
||||||
## converts
|
|
||||||
result = newStringOfCap(frmt.len + 20)
|
|
||||||
var i = 0
|
|
||||||
while i < frmt.len:
|
|
||||||
if frmt[i] != '$':
|
|
||||||
result.add(frmt[i])
|
|
||||||
inc(i)
|
|
||||||
else:
|
|
||||||
inc(i)
|
|
||||||
var v = ""
|
|
||||||
var app = getAppFilename()
|
|
||||||
while frmt[i] in IdentChars:
|
|
||||||
v.add(toLower(frmt[i]))
|
|
||||||
inc(i)
|
|
||||||
case v
|
|
||||||
of "date": result.add(getDateStr())
|
|
||||||
of "time": result.add(getClockStr())
|
|
||||||
of "app": result.add(app)
|
|
||||||
of "appdir": result.add(app.splitFile.dir)
|
|
||||||
of "appname": result.add(app.splitFile.name)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
method log*(L: ref TLogger, level: TLevel,
|
|
||||||
frmt: string, args: varargs[string, `$`]) =
|
|
||||||
## override this method in custom loggers. Default implementation does
|
|
||||||
## nothing.
|
|
||||||
nil
|
|
||||||
|
|
||||||
method log*(L: ref TConsoleLogger, level: TLevel,
|
|
||||||
frmt: string, args: varargs[string, `$`]) =
|
|
||||||
Writeln(stdout, LevelNames[level], " ", substituteLog(L.fmtStr), frmt % args)
|
|
||||||
|
|
||||||
method log*(L: ref TFileLogger, level: TLevel,
|
|
||||||
frmt: string, args: varargs[string, `$`]) =
|
|
||||||
Writeln(L.f, LevelNames[level], " ", substituteLog(L.fmtStr), frmt % args)
|
|
||||||
|
|
||||||
proc defaultFilename*(): string =
|
|
||||||
## returns the default filename for a logger
|
|
||||||
var (path, name, ext) = splitFile(getAppFilename())
|
|
||||||
result = changeFileExt(path / name & "_" & getDateStr(), "log")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
proc newConsoleLogger*(levelThreshold = lvlAll) : ref TConsoleLogger =
|
|
||||||
new result
|
|
||||||
result.fmtStr = defaultFmtStr
|
|
||||||
result.levelThreshold = levelThreshold
|
|
||||||
|
|
||||||
proc newFileLogger*(filename = defaultFilename(),
|
|
||||||
mode: TFileMode = fmAppend,
|
|
||||||
levelThreshold = lvlAll): ref TFileLogger =
|
|
||||||
new(result)
|
|
||||||
result.levelThreshold = levelThreshold
|
|
||||||
result.f = open(filename, mode)
|
|
||||||
result.fmtStr = defaultFmtStr
|
|
||||||
|
|
||||||
# ------
|
|
||||||
|
|
||||||
proc readLogLines(logger : ref TRollingFileLogger) = nil
|
|
||||||
#f.readLine # TODO read all lines, update curLine
|
|
||||||
|
|
||||||
|
|
||||||
proc newRollingFileLogger*(filename = defaultFilename(),
|
|
||||||
mode: TFileMode = fmReadWrite,
|
|
||||||
levelThreshold = lvlAll,
|
|
||||||
maxLines = 1000): ref TRollingFileLogger =
|
|
||||||
new(result)
|
|
||||||
result.levelThreshold = levelThreshold
|
|
||||||
result.fmtStr = defaultFmtStr
|
|
||||||
result.maxLines = maxLines
|
|
||||||
result.f = open(filename, mode)
|
|
||||||
result.curLine = 0
|
|
||||||
|
|
||||||
# TODO count all number files
|
|
||||||
# count lines in existing filename file
|
|
||||||
# if >= maxLines then rename to next numbered file and create new file
|
|
||||||
|
|
||||||
#if mode in {fmReadWrite, fmReadWriteExisting}:
|
|
||||||
# readLogLines(result)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
method log*(L: ref TRollingFileLogger, level: TLevel,
|
|
||||||
frmt: string, args: varargs[string, `$`]) =
|
|
||||||
# TODO
|
|
||||||
# if more than maxlines, then set cursor to zero
|
|
||||||
|
|
||||||
Writeln(L.f, LevelNames[level], " ", frmt % args)
|
|
||||||
|
|
||||||
# --------
|
|
||||||
|
|
||||||
var
|
|
||||||
level* = lvlAll ## global log filter
|
|
||||||
handlers*: seq[ref TLogger] = @[] ## handlers with their own log levels
|
|
||||||
|
|
||||||
proc logLoop(level: TLevel, frmt: string, args: varargs[string, `$`]) =
|
|
||||||
for logger in items(handlers):
|
|
||||||
if level >= logger.levelThreshold:
|
|
||||||
log(logger, level, frmt, args)
|
|
||||||
|
|
||||||
template log*(level: TLevel, frmt: string, args: varargs[string, `$`]) =
|
|
||||||
## logs a message of the given level
|
|
||||||
bind logLoop
|
|
||||||
bind `%`
|
|
||||||
bind logging.Level
|
|
||||||
|
|
||||||
if level >= logging.Level:
|
|
||||||
logLoop(level, frmt, args)
|
|
||||||
|
|
||||||
template debug*(frmt: string, args: varargs[string, `$`]) =
|
|
||||||
## logs a debug message
|
|
||||||
log(lvlDebug, frmt, args)
|
|
||||||
|
|
||||||
template info*(frmt: string, args: varargs[string, `$`]) =
|
|
||||||
## logs an info message
|
|
||||||
log(lvlInfo, frmt, args)
|
|
||||||
|
|
||||||
template warn*(frmt: string, args: varargs[string, `$`]) =
|
|
||||||
## logs a warning message
|
|
||||||
log(lvlWarn, frmt, args)
|
|
||||||
|
|
||||||
template error*(frmt: string, args: varargs[string, `$`]) =
|
|
||||||
## logs an error message
|
|
||||||
log(lvlError, frmt, args)
|
|
||||||
|
|
||||||
template fatal*(frmt: string, args: varargs[string, `$`]) =
|
|
||||||
## logs a fatal error message
|
|
||||||
log(lvlFatal, frmt, args)
|
|
||||||
|
|
||||||
|
|
||||||
# --------------
|
|
||||||
|
|
||||||
when isMainModule:
|
|
||||||
var L = newConsoleLogger()
|
|
||||||
var fL = newFileLogger("test.log")
|
|
||||||
fL.fmtStr = verboseFmtStr
|
|
||||||
handlers.add(L)
|
|
||||||
handlers.add(fL)
|
|
||||||
info("hello", [])
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -341,6 +341,9 @@ Miscellaneous
|
||||||
* `endians <endians.html>`_
|
* `endians <endians.html>`_
|
||||||
This module contains helpers that deal with different byte orders.
|
This module contains helpers that deal with different byte orders.
|
||||||
|
|
||||||
|
* `logging <logging.html>`_
|
||||||
|
This module implements a simple logger.
|
||||||
|
|
||||||
|
|
||||||
Database support
|
Database support
|
||||||
----------------
|
----------------
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ type
|
||||||
gtEof, gtNone, gtWhitespace, gtDecNumber, gtBinNumber, gtHexNumber,
|
gtEof, gtNone, gtWhitespace, gtDecNumber, gtBinNumber, gtHexNumber,
|
||||||
gtOctNumber, gtFloatNumber, gtIdentifier, gtKeyword, gtStringLit,
|
gtOctNumber, gtFloatNumber, gtIdentifier, gtKeyword, gtStringLit,
|
||||||
gtLongStringLit, gtCharLit, gtEscapeSequence, # escape sequence like \xff
|
gtLongStringLit, gtCharLit, gtEscapeSequence, # escape sequence like \xff
|
||||||
gtOperator, gtPunctation, gtComment, gtLongComment, gtRegularExpression,
|
gtOperator, gtPunctuation, gtComment, gtLongComment, gtRegularExpression,
|
||||||
gtTagStart, gtTagEnd, gtKey, gtValue, gtRawData, gtAssembler,
|
gtTagStart, gtTagEnd, gtKey, gtValue, gtRawData, gtAssembler,
|
||||||
gtPreprocessor, gtDirective, gtCommand, gtRule, gtHyperlink, gtLabel,
|
gtPreprocessor, gtDirective, gtCommand, gtRule, gtHyperlink, gtLabel,
|
||||||
gtReference, gtOther
|
gtReference, gtOther
|
||||||
|
|
@ -39,7 +39,7 @@ const
|
||||||
tokenClassToStr*: array[TTokenClass, string] = ["Eof", "None", "Whitespace",
|
tokenClassToStr*: array[TTokenClass, string] = ["Eof", "None", "Whitespace",
|
||||||
"DecNumber", "BinNumber", "HexNumber", "OctNumber", "FloatNumber",
|
"DecNumber", "BinNumber", "HexNumber", "OctNumber", "FloatNumber",
|
||||||
"Identifier", "Keyword", "StringLit", "LongStringLit", "CharLit",
|
"Identifier", "Keyword", "StringLit", "LongStringLit", "CharLit",
|
||||||
"EscapeSequence", "Operator", "Punctation", "Comment", "LongComment",
|
"EscapeSequence", "Operator", "Punctuation", "Comment", "LongComment",
|
||||||
"RegularExpression", "TagStart", "TagEnd", "Key", "Value", "RawData",
|
"RegularExpression", "TagStart", "TagEnd", "Key", "Value", "RawData",
|
||||||
"Assembler", "Preprocessor", "Directive", "Command", "Rule", "Hyperlink",
|
"Assembler", "Preprocessor", "Directive", "Command", "Rule", "Hyperlink",
|
||||||
"Label", "Reference", "Other"]
|
"Label", "Reference", "Other"]
|
||||||
|
|
@ -258,7 +258,7 @@ proc nimNextToken(g: var TGeneralTokenizer) =
|
||||||
else: inc(pos)
|
else: inc(pos)
|
||||||
of '(', ')', '[', ']', '{', '}', '`', ':', ',', ';':
|
of '(', ')', '[', ']', '{', '}', '`', ':', ',', ';':
|
||||||
inc(pos)
|
inc(pos)
|
||||||
g.kind = gtPunctation
|
g.kind = gtPunctuation
|
||||||
of '\0':
|
of '\0':
|
||||||
g.kind = gtEof
|
g.kind = gtEof
|
||||||
else:
|
else:
|
||||||
|
|
@ -473,7 +473,7 @@ proc clikeNextToken(g: var TGeneralTokenizer, keywords: openArray[string],
|
||||||
else: inc(pos)
|
else: inc(pos)
|
||||||
of '(', ')', '[', ']', '{', '}', ':', ',', ';', '.':
|
of '(', ')', '[', ']', '{', '}', ':', ',', ';', '.':
|
||||||
inc(pos)
|
inc(pos)
|
||||||
g.kind = gtPunctation
|
g.kind = gtPunctuation
|
||||||
of '\0':
|
of '\0':
|
||||||
g.kind = gtEof
|
g.kind = gtEof
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -131,3 +131,36 @@ proc sort*[T](a: var openArray[T],
|
||||||
dec(m, s*2)
|
dec(m, s*2)
|
||||||
s = s*2
|
s = s*2
|
||||||
|
|
||||||
|
proc product*[T](x: openarray[seq[T]]): seq[seq[T]] =
|
||||||
|
## produces the Cartesian product of the array. Warning: complexity
|
||||||
|
## may explode.
|
||||||
|
result = @[]
|
||||||
|
if x.len == 0:
|
||||||
|
return
|
||||||
|
if x.len == 1:
|
||||||
|
result = @x
|
||||||
|
return
|
||||||
|
var
|
||||||
|
indexes = newSeq[int](x.len)
|
||||||
|
initial = newSeq[int](x.len)
|
||||||
|
index = 0
|
||||||
|
# replace with newSeq as soon as #853 is fixed
|
||||||
|
var next: seq[T] = @[]
|
||||||
|
next.setLen(x.len)
|
||||||
|
for i in 0..(x.len-1):
|
||||||
|
if len(x[i]) == 0: return
|
||||||
|
initial[i] = len(x[i])-1
|
||||||
|
indexes = initial
|
||||||
|
while true:
|
||||||
|
while indexes[index] == -1:
|
||||||
|
indexes[index] = initial[index]
|
||||||
|
index +=1
|
||||||
|
if index == x.len: return
|
||||||
|
indexes[index] -=1
|
||||||
|
for ni, i in indexes:
|
||||||
|
next[ni] = x[ni][i]
|
||||||
|
var res: seq[T]
|
||||||
|
shallowCopy(res, next)
|
||||||
|
result.add(res)
|
||||||
|
index = 0
|
||||||
|
indexes[index] -=1
|
||||||
|
|
|
||||||
267
lib/pure/logging.nim
Normal file
267
lib/pure/logging.nim
Normal file
|
|
@ -0,0 +1,267 @@
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Nimrod's Runtime Library
|
||||||
|
# (c) Copyright 2014 Andreas Rumpf, Dominik Picheta
|
||||||
|
#
|
||||||
|
# See the file "copying.txt", included in this
|
||||||
|
# distribution, for details about the copyright.
|
||||||
|
#
|
||||||
|
|
||||||
|
## This module implements a simple logger. It has been designed to be as simple
|
||||||
|
## as possible to avoid bloat, if this library does not fullfill your needs,
|
||||||
|
## write your own.
|
||||||
|
##
|
||||||
|
## Format strings support the following variables which must be prefixed with
|
||||||
|
## the dollar operator (``$``):
|
||||||
|
##
|
||||||
|
## ============ =======================
|
||||||
|
## Operator Output
|
||||||
|
## ============ =======================
|
||||||
|
## $date Current date
|
||||||
|
## $time Current time
|
||||||
|
## $app ``os.getAppFilename()``
|
||||||
|
## ============ =======================
|
||||||
|
##
|
||||||
|
##
|
||||||
|
## The following example demonstrates logging to three different handlers
|
||||||
|
## simultaneously:
|
||||||
|
##
|
||||||
|
## .. code-block:: nimrod
|
||||||
|
##
|
||||||
|
## var L = newConsoleLogger()
|
||||||
|
## var fL = newFileLogger("test.log", fmtStr = verboseFmtStr)
|
||||||
|
## var rL = newRollingFileLogger("rolling.log", fmtStr = verboseFmtStr)
|
||||||
|
## handlers.add(L)
|
||||||
|
## handlers.add(fL)
|
||||||
|
## handlers.add(rL)
|
||||||
|
## info("920410:52 accepted")
|
||||||
|
## warn("4 8 15 16 23 4-- Error")
|
||||||
|
## error("922044:16 SYSTEM FAILURE")
|
||||||
|
## fatal("SYSTEM FAILURE SYSTEM FAILURE")
|
||||||
|
|
||||||
|
import strutils, os, times
|
||||||
|
|
||||||
|
type
|
||||||
|
TLevel* = enum ## logging level
|
||||||
|
lvlAll, ## all levels active
|
||||||
|
lvlDebug, ## debug level (and any above) active
|
||||||
|
lvlInfo, ## info level (and any above) active
|
||||||
|
lvlWarn, ## warn level (and any above) active
|
||||||
|
lvlError, ## error level (and any above) active
|
||||||
|
lvlFatal, ## fatal level (and any above) active
|
||||||
|
lvlNone ## no levels active
|
||||||
|
|
||||||
|
const
|
||||||
|
LevelNames*: array [TLevel, string] = [
|
||||||
|
"DEBUG", "DEBUG", "INFO", "WARN", "ERROR", "FATAL", "NONE"
|
||||||
|
]
|
||||||
|
|
||||||
|
defaultFmtStr* = "" ## default string between log level and message per logger
|
||||||
|
verboseFmtStr* = "$date $time "
|
||||||
|
|
||||||
|
type
|
||||||
|
PLogger* = ref object of PObject ## abstract logger; the base type of all loggers
|
||||||
|
levelThreshold*: TLevel ## only messages of level >= levelThreshold
|
||||||
|
## should be processed
|
||||||
|
fmtStr: string ## = defaultFmtStr by default, see substituteLog for $date etc.
|
||||||
|
|
||||||
|
PConsoleLogger* = ref object of PLogger ## logger that writes the messages to the
|
||||||
|
## console
|
||||||
|
|
||||||
|
PFileLogger* = ref object of PLogger ## logger that writes the messages to a file
|
||||||
|
f: TFile
|
||||||
|
|
||||||
|
PRollingFileLogger* = ref object of PFileLogger ## logger that writes the
|
||||||
|
## messages to a file and
|
||||||
|
## performs log rotation
|
||||||
|
maxLines: int # maximum number of lines
|
||||||
|
curLine : int
|
||||||
|
baseName: string # initial filename
|
||||||
|
baseMode: TFileMode # initial file mode
|
||||||
|
logFiles: int # how many log files already created, e.g. basename.1, basename.2...
|
||||||
|
|
||||||
|
proc substituteLog(frmt: string): string =
|
||||||
|
## converts $date to the current date
|
||||||
|
## converts $time to the current time
|
||||||
|
## converts $app to getAppFilename()
|
||||||
|
## converts
|
||||||
|
result = newStringOfCap(frmt.len + 20)
|
||||||
|
var i = 0
|
||||||
|
while i < frmt.len:
|
||||||
|
if frmt[i] != '$':
|
||||||
|
result.add(frmt[i])
|
||||||
|
inc(i)
|
||||||
|
else:
|
||||||
|
inc(i)
|
||||||
|
var v = ""
|
||||||
|
var app = getAppFilename()
|
||||||
|
while frmt[i] in IdentChars:
|
||||||
|
v.add(toLower(frmt[i]))
|
||||||
|
inc(i)
|
||||||
|
case v
|
||||||
|
of "date": result.add(getDateStr())
|
||||||
|
of "time": result.add(getClockStr())
|
||||||
|
of "app": result.add(app)
|
||||||
|
of "appdir": result.add(app.splitFile.dir)
|
||||||
|
of "appname": result.add(app.splitFile.name)
|
||||||
|
|
||||||
|
method log*(logger: PLogger, level: TLevel,
|
||||||
|
frmt: string, args: varargs[string, `$`]) =
|
||||||
|
## Override this method in custom loggers. Default implementation does
|
||||||
|
## nothing.
|
||||||
|
nil
|
||||||
|
|
||||||
|
method log*(logger: PConsoleLogger, level: TLevel,
|
||||||
|
frmt: string, args: varargs[string, `$`]) =
|
||||||
|
## Logs to the console using ``logger`` only.
|
||||||
|
if level >= logger.levelThreshold:
|
||||||
|
writeln(stdout, LevelNames[level], " ", substituteLog(logger.fmtStr),
|
||||||
|
frmt % args)
|
||||||
|
|
||||||
|
method log*(logger: PFileLogger, level: TLevel,
|
||||||
|
frmt: string, args: varargs[string, `$`]) =
|
||||||
|
## Logs to a file using ``logger`` only.
|
||||||
|
if level >= logger.levelThreshold:
|
||||||
|
writeln(logger.f, LevelNames[level], " ",
|
||||||
|
substituteLog(logger.fmtStr), frmt % args)
|
||||||
|
|
||||||
|
proc defaultFilename*(): string =
|
||||||
|
## Returns the default filename for a logger.
|
||||||
|
var (path, name, ext) = splitFile(getAppFilename())
|
||||||
|
result = changeFileExt(path / name, "log")
|
||||||
|
|
||||||
|
proc newConsoleLogger*(levelThreshold = lvlAll, fmtStr = defaultFmtStr): PConsoleLogger =
|
||||||
|
## Creates a new console logger. This logger logs to the console.
|
||||||
|
new result
|
||||||
|
result.fmtStr = fmtStr
|
||||||
|
result.levelThreshold = levelThreshold
|
||||||
|
|
||||||
|
proc newFileLogger*(filename = defaultFilename(),
|
||||||
|
mode: TFileMode = fmAppend,
|
||||||
|
levelThreshold = lvlAll,
|
||||||
|
fmtStr = defaultFmtStr): PFileLogger =
|
||||||
|
## Creates a new file logger. This logger logs to a file.
|
||||||
|
new(result)
|
||||||
|
result.levelThreshold = levelThreshold
|
||||||
|
result.f = open(filename, mode)
|
||||||
|
result.fmtStr = fmtStr
|
||||||
|
|
||||||
|
# ------
|
||||||
|
|
||||||
|
proc countLogLines(logger: PRollingFileLogger): int =
|
||||||
|
result = 0
|
||||||
|
for line in logger.f.lines():
|
||||||
|
result.inc()
|
||||||
|
|
||||||
|
proc countFiles(filename: string): int =
|
||||||
|
# Example: file.log.1
|
||||||
|
result = 0
|
||||||
|
let (dir, name, ext) = splitFile(filename)
|
||||||
|
for kind, path in walkDir(dir):
|
||||||
|
if kind == pcFile:
|
||||||
|
let llfn = name & ext & ExtSep
|
||||||
|
if path.extractFilename.startsWith(llfn):
|
||||||
|
let numS = path.extractFilename[llfn.len .. -1]
|
||||||
|
try:
|
||||||
|
let num = parseInt(numS)
|
||||||
|
if num > result:
|
||||||
|
result = num
|
||||||
|
except EInvalidValue: discard
|
||||||
|
|
||||||
|
proc newRollingFileLogger*(filename = defaultFilename(),
|
||||||
|
mode: TFileMode = fmReadWrite,
|
||||||
|
levelThreshold = lvlAll,
|
||||||
|
fmtStr = defaultFmtStr,
|
||||||
|
maxLines = 1000): PRollingFileLogger =
|
||||||
|
## Creates a new rolling file logger. Once a file reaches ``maxLines`` lines
|
||||||
|
## a new log file will be started and the old will be renamed.
|
||||||
|
new(result)
|
||||||
|
result.levelThreshold = levelThreshold
|
||||||
|
result.fmtStr = defaultFmtStr
|
||||||
|
result.maxLines = maxLines
|
||||||
|
result.f = open(filename, mode)
|
||||||
|
result.curLine = 0
|
||||||
|
result.baseName = filename
|
||||||
|
result.baseMode = mode
|
||||||
|
|
||||||
|
result.logFiles = countFiles(filename)
|
||||||
|
|
||||||
|
if mode == fmAppend:
|
||||||
|
# We need to get a line count because we will be appending to the file.
|
||||||
|
result.curLine = countLogLines(result)
|
||||||
|
|
||||||
|
proc rotate(logger: PRollingFileLogger) =
|
||||||
|
let (dir, name, ext) = splitFile(logger.baseName)
|
||||||
|
for i in countdown(logger.logFiles, 0):
|
||||||
|
let srcSuff = if i != 0: ExtSep & $i else: ""
|
||||||
|
moveFile(dir / (name & ext & srcSuff),
|
||||||
|
dir / (name & ext & ExtSep & $(i+1)))
|
||||||
|
|
||||||
|
method log*(logger: PRollingFileLogger, level: TLevel,
|
||||||
|
frmt: string, args: varargs[string, `$`]) =
|
||||||
|
## Logs to a file using rolling ``logger`` only.
|
||||||
|
if level >= logger.levelThreshold:
|
||||||
|
if logger.curLine >= logger.maxLines:
|
||||||
|
logger.f.close()
|
||||||
|
rotate(logger)
|
||||||
|
logger.logFiles.inc
|
||||||
|
logger.curLine = 0
|
||||||
|
logger.f = open(logger.baseName, logger.baseMode)
|
||||||
|
|
||||||
|
writeln(logger.f, LevelNames[level], " ", frmt % args)
|
||||||
|
logger.curLine.inc
|
||||||
|
|
||||||
|
# --------
|
||||||
|
|
||||||
|
var
|
||||||
|
level* = lvlAll ## global log filter
|
||||||
|
handlers*: seq[PLogger] = @[] ## handlers with their own log levels
|
||||||
|
|
||||||
|
proc logLoop(level: TLevel, frmt: string, args: varargs[string, `$`]) =
|
||||||
|
for logger in items(handlers):
|
||||||
|
if level >= logger.levelThreshold:
|
||||||
|
log(logger, level, frmt, args)
|
||||||
|
|
||||||
|
template log*(level: TLevel, frmt: string, args: varargs[string, `$`]) =
|
||||||
|
## Logs a message to all registered handlers at the given level.
|
||||||
|
bind logLoop
|
||||||
|
bind `%`
|
||||||
|
bind logging.Level
|
||||||
|
|
||||||
|
if level >= logging.Level:
|
||||||
|
logLoop(level, frmt, args)
|
||||||
|
|
||||||
|
template debug*(frmt: string, args: varargs[string, `$`]) =
|
||||||
|
## Logs a debug message to all registered handlers.
|
||||||
|
log(lvlDebug, frmt, args)
|
||||||
|
|
||||||
|
template info*(frmt: string, args: varargs[string, `$`]) =
|
||||||
|
## Logs an info message to all registered handlers.
|
||||||
|
log(lvlInfo, frmt, args)
|
||||||
|
|
||||||
|
template warn*(frmt: string, args: varargs[string, `$`]) =
|
||||||
|
## Logs a warning message to all registered handlers.
|
||||||
|
log(lvlWarn, frmt, args)
|
||||||
|
|
||||||
|
template error*(frmt: string, args: varargs[string, `$`]) =
|
||||||
|
## Logs an error message to all registered handlers.
|
||||||
|
log(lvlError, frmt, args)
|
||||||
|
|
||||||
|
template fatal*(frmt: string, args: varargs[string, `$`]) =
|
||||||
|
## Logs a fatal error message to all registered handlers.
|
||||||
|
log(lvlFatal, frmt, args)
|
||||||
|
|
||||||
|
|
||||||
|
# --------------
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
var L = newConsoleLogger()
|
||||||
|
var fL = newFileLogger("test.log", fmtStr = verboseFmtStr)
|
||||||
|
var rL = newRollingFileLogger("rolling.log", fmtStr = verboseFmtStr)
|
||||||
|
handlers.add(L)
|
||||||
|
handlers.add(fL)
|
||||||
|
handlers.add(rL)
|
||||||
|
for i in 0 .. 25:
|
||||||
|
info("hello" & $i, [])
|
||||||
|
|
||||||
|
|
||||||
14
tests/stdlib/talgorithm.nim
Normal file
14
tests/stdlib/talgorithm.nim
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
import unittest
|
||||||
|
import algorithm
|
||||||
|
|
||||||
|
suite "product":
|
||||||
|
test "empty input":
|
||||||
|
check product[int](newSeq[seq[int]]()) == newSeq[seq[int]]()
|
||||||
|
test "bit more empty input":
|
||||||
|
check product[int](@[newSeq[int](), @[], @[]]) == newSeq[seq[int]]()
|
||||||
|
test "a simple case of one element":
|
||||||
|
check product(@[@[1,2]]) == @[@[1,2]]
|
||||||
|
test "two elements":
|
||||||
|
check product(@[@[1,2], @[3,4]]) == @[@[2,4],@[1,4],@[2,3],@[1,3]]
|
||||||
|
test "three elements":
|
||||||
|
check product(@[@[1,2], @[3,4], @[5,6]]) == @[@[2,4,6],@[1,4,6],@[2,3,6],@[1,3,6], @[2,4,5],@[1,4,5],@[2,3,5],@[1,3,5]]
|
||||||
|
|
@ -62,7 +62,7 @@ srcdoc2: "pure/ftpclient;pure/memfiles;pure/subexes;pure/collections/critbits"
|
||||||
srcdoc2: "pure/asyncio;pure/actors;core/locks;pure/oids;pure/endians;pure/uri"
|
srcdoc2: "pure/asyncio;pure/actors;core/locks;pure/oids;pure/endians;pure/uri"
|
||||||
srcdoc2: "pure/nimprof;pure/unittest;packages/docutils/highlite"
|
srcdoc2: "pure/nimprof;pure/unittest;packages/docutils/highlite"
|
||||||
srcdoc2: "packages/docutils/rst;packages/docutils/rstast"
|
srcdoc2: "packages/docutils/rst;packages/docutils/rstast"
|
||||||
srcdoc2: "packages/docutils/rstgen"
|
srcdoc2: "packages/docutils/rstgen;pure/logging"
|
||||||
|
|
||||||
webdoc: "wrappers/libcurl;pure/md5;wrappers/mysql;wrappers/iup"
|
webdoc: "wrappers/libcurl;pure/md5;wrappers/mysql;wrappers/iup"
|
||||||
webdoc: "wrappers/sqlite3;wrappers/postgres;wrappers/tinyc"
|
webdoc: "wrappers/sqlite3;wrappers/postgres;wrappers/tinyc"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue