mirror of
https://github.com/nim-lang/Nim.git
synced 2026-01-04 12:07:51 +00:00
reference implementation of a vector swizzle library
This also provides the initial steps towards support for type class "filtered" type inference fixes an "ordinal type expected" ICE, related to the use of static params
This commit is contained in:
@@ -435,6 +435,8 @@ type
|
||||
tfUnresolved, # marks unresolved typedesc/static params: e.g.
|
||||
# proc foo(T: typedesc, list: seq[T]): var T
|
||||
# proc foo(L: static[int]): array[L, int]
|
||||
# can be attached to ranges to indicate that the range
|
||||
# depends on unresolved static params.
|
||||
tfRetType, # marks return types in proc (used to detect type classes
|
||||
# used as return types for return type inference)
|
||||
tfCapturesEnv, # whether proc really captures some environment
|
||||
|
||||
@@ -66,6 +66,9 @@ proc fitNode(c: PContext, formal: PType, arg: PNode): PNode =
|
||||
result = copyTree(arg)
|
||||
result.typ = formal
|
||||
|
||||
proc inferWithMetatype(c: PContext, formal: PType,
|
||||
arg: PNode, coerceDistincts = false): PNode
|
||||
|
||||
var commonTypeBegin = PType(kind: tyExpr)
|
||||
|
||||
proc commonType*(x, y: PType): PType =
|
||||
|
||||
@@ -228,12 +228,25 @@ proc indexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode =
|
||||
if m.genericConverter and result != nil:
|
||||
instGenericConvertersArg(c, result, m)
|
||||
|
||||
proc convertTo*(c: PContext, f: PType, n: PNode): PNode =
|
||||
proc inferWithMetatype(c: PContext, formal: PType,
|
||||
arg: PNode, coerceDistincts = false): PNode =
|
||||
var m: TCandidate
|
||||
initCandidate(c, m, f)
|
||||
result = paramTypesMatch(m, f, n.typ, n, nil)
|
||||
initCandidate(c, m, formal)
|
||||
m.coerceDistincts = coerceDistincts
|
||||
result = paramTypesMatch(m, formal, arg.typ, arg, nil)
|
||||
if m.genericConverter and result != nil:
|
||||
instGenericConvertersArg(c, result, m)
|
||||
if result != nil:
|
||||
# This almost exactly replicates the steps taken by the compiler during
|
||||
# param matching. It performs an embarassing ammount of back-and-forth
|
||||
# type jugling, but it's the price to pay for consistency and correctness
|
||||
result.typ = generateTypeInstance(c, m.bindings, arg.info,
|
||||
formal.skipTypes({tyCompositeTypeClass}))
|
||||
else:
|
||||
typeMismatch(arg, formal, arg.typ)
|
||||
# error correction:
|
||||
result = copyTree(arg)
|
||||
result.typ = formal
|
||||
|
||||
proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode =
|
||||
assert x.state == csMatch
|
||||
|
||||
@@ -251,6 +251,27 @@ proc makeNotType*(c: PContext, t1: PType): PType =
|
||||
propagateToOwner(result, t1)
|
||||
result.flags.incl(t1.flags * {tfHasStatic})
|
||||
|
||||
proc nMinusOne*(n: PNode): PNode =
|
||||
result = newNode(nkCall, n.info, @[
|
||||
newSymNode(getSysMagic("<", mUnaryLt)),
|
||||
n])
|
||||
|
||||
# Remember to fix the procs below this one when you make changes!
|
||||
proc makeRangeWithStaticExpr*(c: PContext, n: PNode): PType =
|
||||
let intType = getSysType tyInt
|
||||
result = newTypeS(tyRange, c)
|
||||
result.sons = @[intType]
|
||||
result.n = newNode(nkRange, n.info, @[
|
||||
newIntTypeNode(nkIntLit, 0, intType),
|
||||
makeStaticExpr(c, n.nMinusOne)])
|
||||
|
||||
template rangeHasStaticIf*(t: PType): bool =
|
||||
# this accepts the ranges's node
|
||||
t.n[1].kind == nkStaticExpr
|
||||
|
||||
template getStaticTypeFromRange*(t: PType): PType =
|
||||
t.n[1][0][1].typ
|
||||
|
||||
proc newTypeS(kind: TTypeKind, c: PContext): PType =
|
||||
result = newType(kind, getCurrOwner())
|
||||
|
||||
|
||||
@@ -121,6 +121,8 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
|
||||
return n
|
||||
of skType:
|
||||
markUsed(n, s)
|
||||
if s.typ.kind == tyStatic and s.typ.n != nil:
|
||||
return s.typ.n
|
||||
result = newSymNode(s, n.info)
|
||||
result.typ = makeTypeDesc(c, s.typ)
|
||||
else:
|
||||
@@ -191,15 +193,35 @@ proc isCastable(dst, src: PType): bool =
|
||||
proc isSymChoice(n: PNode): bool {.inline.} =
|
||||
result = n.kind in nkSymChoices
|
||||
|
||||
proc maybeLiftType(t: var PType, c: PContext, info: TLineInfo) =
|
||||
# XXX: liftParamType started to perform addDecl
|
||||
# we could do that instead in semTypeNode by snooping for added
|
||||
# gnrc. params, then it won't be necessary to open a new scope here
|
||||
openScope(c)
|
||||
var lifted = liftParamType(c, skType, newNodeI(nkArgList, info),
|
||||
t, ":anon", info)
|
||||
closeScope(c)
|
||||
if lifted != nil: t = lifted
|
||||
|
||||
proc semConv(c: PContext, n: PNode): PNode =
|
||||
if sonsLen(n) != 2:
|
||||
localError(n.info, errConvNeedsOneArg)
|
||||
return n
|
||||
|
||||
result = newNodeI(nkConv, n.info)
|
||||
result.typ = semTypeNode(c, n.sons[0], nil).skipTypes({tyGenericInst})
|
||||
addSon(result, copyTree(n.sons[0]))
|
||||
addSon(result, semExprWithType(c, n.sons[1]))
|
||||
var op = result.sons[1]
|
||||
var targetType = semTypeNode(c, n.sons[0], nil)
|
||||
maybeLiftType(targetType, c, n[0].info)
|
||||
result.addSon copyTree(n.sons[0])
|
||||
var op = semExprWithType(c, n.sons[1])
|
||||
|
||||
if targetType.isMetaType:
|
||||
let final = inferWithMetatype(c, targetType, op, true)
|
||||
result.addSon final
|
||||
result.typ = final.typ
|
||||
return
|
||||
|
||||
result.typ = targetType
|
||||
addSon(result, op)
|
||||
|
||||
if not isSymChoice(op):
|
||||
let status = checkConvertible(c, result.typ, op.typ)
|
||||
@@ -221,7 +243,7 @@ proc semConv(c: PContext, n: PNode): PNode =
|
||||
for i in countup(0, sonsLen(op) - 1):
|
||||
let it = op.sons[i]
|
||||
let status = checkConvertible(c, result.typ, it.typ)
|
||||
if status == convOK:
|
||||
if status in {convOK, convNotNeedeed}:
|
||||
markUsed(n, it.sym)
|
||||
markIndirect(c, it.sym)
|
||||
return it
|
||||
@@ -325,14 +347,7 @@ proc isOpImpl(c: PContext, n: PNode): PNode =
|
||||
tfIterator notin t.flags))
|
||||
else:
|
||||
var t2 = n[2].typ.skipTypes({tyTypeDesc})
|
||||
# XXX: liftParamType started to perform addDecl
|
||||
# we could do that instead in semTypeNode by snooping for added
|
||||
# gnrc. params, then it won't be necessary to open a new scope here
|
||||
openScope(c)
|
||||
let lifted = liftParamType(c, skType, newNodeI(nkArgList, n.info),
|
||||
t2, ":anon", n.info)
|
||||
closeScope(c)
|
||||
if lifted != nil: t2 = lifted
|
||||
maybeLiftType(t2, c, n.info)
|
||||
var m: TCandidate
|
||||
initCandidate(c, m, t2)
|
||||
let match = typeRel(m, t2, t1) != isNone
|
||||
@@ -1202,7 +1217,7 @@ proc semAsgn(c: PContext, n: PNode): PNode =
|
||||
if lhsIsResult: {efAllowDestructor} else: {})
|
||||
if lhsIsResult:
|
||||
n.typ = enforceVoidContext
|
||||
if resultTypeIsInferrable(lhs.sym.typ):
|
||||
if c.p.owner.kind != skMacro and resultTypeIsInferrable(lhs.sym.typ):
|
||||
if cmpTypes(c, lhs.typ, rhs.typ) == isGeneric:
|
||||
internalAssert c.p.resultSym != nil
|
||||
lhs.typ = rhs.typ
|
||||
|
||||
@@ -12,9 +12,6 @@
|
||||
|
||||
var enforceVoidContext = PType(kind: tyStmt)
|
||||
|
||||
proc semCommand(c: PContext, n: PNode): PNode =
|
||||
result = semExprNoType(c, n)
|
||||
|
||||
proc semDiscard(c: PContext, n: PNode): PNode =
|
||||
result = n
|
||||
checkSonsLen(n, 1)
|
||||
@@ -133,6 +130,7 @@ proc fixNilType(n: PNode) =
|
||||
n.typ = nil
|
||||
|
||||
proc discardCheck(c: PContext, result: PNode) =
|
||||
if c.inTypeClass > 0: return
|
||||
if result.typ != nil and result.typ.kind notin {tyStmt, tyEmpty}:
|
||||
if result.kind == nkNilLit:
|
||||
result.typ = nil
|
||||
@@ -143,11 +141,6 @@ proc discardCheck(c: PContext, result: PNode) =
|
||||
while n.kind in skipForDiscardable:
|
||||
n = n.lastSon
|
||||
n.typ = nil
|
||||
elif c.inTypeClass > 0:
|
||||
if result.typ.kind == tyBool:
|
||||
let verdict = semConstExpr(c, result)
|
||||
if verdict.intVal == 0:
|
||||
localError(result.info, "type class predicate failed")
|
||||
elif result.typ.kind != tyError and gCmd != cmdInteractive:
|
||||
if result.typ.kind == tyNil:
|
||||
fixNilType(result)
|
||||
@@ -1324,13 +1317,17 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
return
|
||||
else:
|
||||
n.sons[i] = semExpr(c, n.sons[i])
|
||||
if c.inTypeClass > 0 and n[i].typ != nil and n[i].typ.kind == tyBool:
|
||||
let verdict = semConstExpr(c, n[i])
|
||||
if verdict.intVal == 0:
|
||||
localError(result.info, "type class predicate failed")
|
||||
if n.sons[i].typ == enforceVoidContext or usesResult(n.sons[i]):
|
||||
voidContext = true
|
||||
n.typ = enforceVoidContext
|
||||
if i == last and (length == 1 or efWantValue in flags):
|
||||
n.typ = n.sons[i].typ
|
||||
if not isEmptyType(n.typ): n.kind = nkStmtListExpr
|
||||
elif i != last or voidContext or c.inTypeClass > 0:
|
||||
elif i != last or voidContext:
|
||||
discardCheck(c, n.sons[i])
|
||||
else:
|
||||
n.typ = n.sons[i].typ
|
||||
|
||||
@@ -198,19 +198,6 @@ proc semRange(c: PContext, n: PNode, prev: PType): PType =
|
||||
localError(n.info, errXExpectsOneTypeParam, "range")
|
||||
result = newOrPrevType(tyError, prev, c)
|
||||
|
||||
proc nMinusOne(n: PNode): PNode =
|
||||
result = newNode(nkCall, n.info, @[
|
||||
newSymNode(getSysMagic("<", mUnaryLt)),
|
||||
n])
|
||||
|
||||
proc makeRangeWithStaticExpr(c: PContext, n: PNode): PType =
|
||||
let intType = getSysType(tyInt)
|
||||
result = newTypeS(tyRange, c)
|
||||
result.sons = @[intType]
|
||||
result.n = newNode(nkRange, n.info, @[
|
||||
newIntTypeNode(nkIntLit, 0, intType),
|
||||
makeStaticExpr(c, n.nMinusOne)])
|
||||
|
||||
proc semArray(c: PContext, n: PNode, prev: PType): PType =
|
||||
var indx, base: PType
|
||||
result = newOrPrevType(tyArray, prev, c)
|
||||
@@ -229,6 +216,7 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType =
|
||||
if not isOrdinalType(e.typ.lastSon):
|
||||
localError(n[1].info, errOrdinalTypeExpected)
|
||||
indx = makeRangeWithStaticExpr(c, e)
|
||||
indx.flags.incl tfUnresolved
|
||||
elif e.kind in nkCallKinds and hasGenericArguments(e):
|
||||
if not isOrdinalType(e.typ):
|
||||
localError(n[1].info, errOrdinalTypeExpected)
|
||||
@@ -628,17 +616,28 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
if base == nil and tfInheritable notin result.flags:
|
||||
incl(result.flags, tfFinal)
|
||||
|
||||
proc findEnforcedStaticType(t: PType): PType =
|
||||
# This handles types such as `static[T] and Foo`,
|
||||
# which are subset of `static[T]`, hence they could
|
||||
# be treated in the same way
|
||||
if t.kind == tyStatic: return t
|
||||
if t.kind == tyAnd:
|
||||
for s in t.sons:
|
||||
let t = findEnforcedStaticType(s)
|
||||
if t != nil: return t
|
||||
|
||||
proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) =
|
||||
template addDecl(x) =
|
||||
if sfGenSym notin x.flags: addDecl(c, x)
|
||||
|
||||
if kind == skMacro:
|
||||
if param.typ.kind == tyTypeDesc:
|
||||
addDecl(param)
|
||||
elif param.typ.kind == tyStatic:
|
||||
let staticType = findEnforcedStaticType(param.typ)
|
||||
if staticType != nil:
|
||||
var a = copySym(param)
|
||||
a.typ = param.typ.base
|
||||
a.typ = staticType.base
|
||||
addDecl(a)
|
||||
elif param.typ.kind == tyTypeDesc:
|
||||
addDecl(param)
|
||||
else:
|
||||
# within a macro, every param has the type PNimrodNode!
|
||||
let nn = getSysSym"PNimrodNode"
|
||||
@@ -944,14 +943,18 @@ proc semBlockType(c: PContext, n: PNode, prev: PType): PType =
|
||||
proc semGenericParamInInvokation(c: PContext, n: PNode): PType =
|
||||
result = semTypeNode(c, n, nil)
|
||||
|
||||
proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
||||
proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
||||
if s.typ == nil:
|
||||
localError(n.info, "cannot instantiate the '$1' $2" %
|
||||
[s.name.s, ($s.kind).substr(2).toLower])
|
||||
return newOrPrevType(tyError, prev, c)
|
||||
|
||||
var t = s.typ
|
||||
if t.kind == tyCompositeTypeClass and t.base.kind == tyGenericBody:
|
||||
t = t.base
|
||||
|
||||
result = newOrPrevType(tyGenericInvokation, prev, c)
|
||||
addSonSkipIntLit(result, s.typ)
|
||||
addSonSkipIntLit(result, t)
|
||||
|
||||
template addToResult(typ) =
|
||||
if typ.isNil:
|
||||
@@ -959,23 +962,24 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
||||
rawAddSon(result, typ)
|
||||
else: addSonSkipIntLit(result, typ)
|
||||
|
||||
if s.typ.kind == tyForward:
|
||||
if t.kind == tyForward:
|
||||
for i in countup(1, sonsLen(n)-1):
|
||||
var elem = semGenericParamInInvokation(c, n.sons[i])
|
||||
addToResult(elem)
|
||||
elif s.typ.kind != tyGenericBody:
|
||||
return
|
||||
elif t.kind != tyGenericBody:
|
||||
#we likely got code of the form TypeA[TypeB] where TypeA is
|
||||
#not generic.
|
||||
localError(n.info, errNoGenericParamsAllowedForX, s.name.s)
|
||||
return newOrPrevType(tyError, prev, c)
|
||||
else:
|
||||
var m = newCandidate(c, s, n)
|
||||
var m = newCandidate(c, t)
|
||||
matches(c, n, copyTree(n), m)
|
||||
|
||||
if m.state != csMatch:
|
||||
var err = "cannot instantiate " & typeToString(s.typ) & "\n" &
|
||||
var err = "cannot instantiate " & typeToString(t) & "\n" &
|
||||
"got: (" & describeArgs(c, n) & ")\n" &
|
||||
"but expected: (" & describeArgs(c, s.typ.n, 0) & ")"
|
||||
"but expected: (" & describeArgs(c, t.n, 0) & ")"
|
||||
localError(n.info, errGenerated, err)
|
||||
return newOrPrevType(tyError, prev, c)
|
||||
|
||||
@@ -987,7 +991,8 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
||||
addToResult(typ)
|
||||
|
||||
if isConcrete:
|
||||
if s.ast == nil:
|
||||
if s.ast == nil and s.typ.kind != tyCompositeTypeClass:
|
||||
# XXX: What kind of error is this? is it still relevant?
|
||||
localError(n.info, errCannotInstantiateX, s.name.s)
|
||||
result = newOrPrevType(tyError, prev, c)
|
||||
else:
|
||||
|
||||
@@ -40,6 +40,8 @@ type
|
||||
proxyMatch*: bool # to prevent instantiations
|
||||
genericConverter*: bool # true if a generic converter needs to
|
||||
# be instantiated
|
||||
coerceDistincts*: bool # this is an explicit coercion that can strip away
|
||||
# a distrinct type
|
||||
typedescMatched: bool
|
||||
inheritancePenalty: int # to prefer closest father object type
|
||||
errors*: seq[string] # additional clarifications to be displayed to the
|
||||
@@ -114,6 +116,9 @@ proc newCandidate*(ctx: PContext, callee: PSym,
|
||||
binding: PNode, calleeScope = -1): TCandidate =
|
||||
initCandidate(ctx, result, callee, binding, calleeScope)
|
||||
|
||||
proc newCandidate*(ctx: PContext, callee: PType): TCandidate =
|
||||
initCandidate(ctx, result, callee)
|
||||
|
||||
proc copyCandidate(a: var TCandidate, b: TCandidate) =
|
||||
a.c = b.c
|
||||
a.exactMatches = b.exactMatches
|
||||
@@ -460,7 +465,8 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
|
||||
|
||||
if param.kind == nkVarTy:
|
||||
dummyName = param[0]
|
||||
dummyType = makeVarType(c, a)
|
||||
dummyType = if a.kind != tyVar: makeVarType(c, a)
|
||||
else: a
|
||||
else:
|
||||
dummyName = param
|
||||
dummyType = a
|
||||
@@ -470,7 +476,7 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
|
||||
dummyParam.typ = dummyType
|
||||
addDecl(c, dummyParam)
|
||||
|
||||
var checkedBody = c.semTryExpr(c, copyTree(body.n[3]), bufferErrors = false)
|
||||
var checkedBody = c.semTryExpr(c, body.n[3].copyTree, bufferErrors = false)
|
||||
m.errors = bufferedMsgs
|
||||
clearBufferedMsgs()
|
||||
if checkedBody == nil: return isNone
|
||||
@@ -623,6 +629,20 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
||||
result = typeRel(c, f.sons[1], a.sons[1])
|
||||
if result < isGeneric:
|
||||
result = isNone
|
||||
elif tfUnresolved in fRange.flags and
|
||||
rangeHasStaticIf(fRange):
|
||||
# This is a range from an array instantiated with a generic
|
||||
# static param. We must extract the static param here and bind
|
||||
# it to the size of the currently supplied array.
|
||||
var
|
||||
rangeStaticT = fRange.getStaticTypeFromRange
|
||||
replacementT = newTypeWithSons(c.c, tyStatic, @[tyInt.getSysType])
|
||||
inputUpperBound = a.sons[0].n[1].intVal
|
||||
# we must correct for the off-by-one discrepancy between
|
||||
# ranges and static params:
|
||||
replacementT.n = newIntNode(nkIntLit, inputUpperBound + 1)
|
||||
put(c.bindings, rangeStaticT, replacementT)
|
||||
result = isGeneric
|
||||
elif lengthOrd(fRange) != lengthOrd(a):
|
||||
result = isNone
|
||||
else: discard
|
||||
@@ -686,6 +706,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
||||
result = isSubtype
|
||||
of tyDistinct:
|
||||
if (a.kind == tyDistinct) and sameDistinctTypes(f, a): result = isEqual
|
||||
elif c.coerceDistincts: result = typeRel(c, f.base, a)
|
||||
of tySet:
|
||||
if a.kind == tySet:
|
||||
if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty):
|
||||
@@ -848,7 +869,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
||||
|
||||
of tyUserTypeClass, tyUserTypeClassInst:
|
||||
considerPreviousT:
|
||||
result = matchUserTypeClass(c.c, c, f, a)
|
||||
result = matchUserTypeClass(c.c, c, f, aOrig)
|
||||
if result == isGeneric:
|
||||
put(c.bindings, f, a)
|
||||
|
||||
@@ -863,7 +884,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
||||
of tyGenericParam:
|
||||
var x = PType(idTableGet(c.bindings, f))
|
||||
if x == nil:
|
||||
if c.calleeSym != nil and c.calleeSym.kind == skType and
|
||||
if c.calleeSym != nil and c.callee.kind == tyGenericBody and
|
||||
f.kind == tyGenericParam and not c.typedescMatched:
|
||||
# XXX: The fact that generic types currently use tyGenericParam for
|
||||
# their parameters is really a misnomer. tyGenericParam means "match
|
||||
@@ -1051,7 +1072,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
|
||||
# XXX: When implicit statics are the default
|
||||
# this will be done earlier - we just have to
|
||||
# make sure that static types enter here
|
||||
|
||||
|
||||
# XXX: weaken tyGenericParam and call it tyGenericPlaceholder
|
||||
# and finally start using tyTypedesc for generic types properly.
|
||||
if argType.kind == tyGenericParam and tfWildcard in argType.flags:
|
||||
@@ -1060,7 +1081,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
|
||||
return argSemantized
|
||||
|
||||
if argType.kind == tyStatic:
|
||||
if m.calleeSym.kind == skType:
|
||||
if m.callee.kind == tyGenericBody:
|
||||
result = newNodeI(nkType, argOrig.info)
|
||||
result.typ = makeTypeFromExpr(c, arg)
|
||||
return
|
||||
|
||||
@@ -436,6 +436,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
|
||||
of tyStatic:
|
||||
internalAssert t.len > 0
|
||||
result = "static[" & typeToString(t.sons[0]) & "]"
|
||||
if t.n != nil: result.add "(" & renderTree(t.n) & ")"
|
||||
of tyUserTypeClass:
|
||||
internalAssert t.sym != nil and t.sym.owner != nil
|
||||
return t.sym.owner.name.s
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
discard """
|
||||
line: 11
|
||||
line: 10
|
||||
errormsg: "type mismatch: got (typedesc[float], string)"
|
||||
"""
|
||||
|
||||
|
||||
79
tests/metatype/swizzle.nim
Normal file
79
tests/metatype/swizzle.nim
Normal file
@@ -0,0 +1,79 @@
|
||||
discard """
|
||||
output: '''3
|
||||
[1, 3]
|
||||
[2, 1, 2]
|
||||
'''
|
||||
"""
|
||||
|
||||
import macros, strutils
|
||||
|
||||
template accept(e: expr) =
|
||||
static: assert(compiles(e))
|
||||
|
||||
template reject(e: expr) =
|
||||
static: assert(not compiles(e))
|
||||
|
||||
proc swizzleIdx(c: char): int =
|
||||
return case c
|
||||
of 'x': 0
|
||||
of 'y': 1
|
||||
of 'z': 2
|
||||
of 'w': 3
|
||||
of 'r': 0
|
||||
of 'g': 1
|
||||
of 'b': 2
|
||||
of 'a': 3
|
||||
else: 0
|
||||
|
||||
proc isSwizzle(s: string): bool =
|
||||
template trySet(name, set) =
|
||||
block search:
|
||||
for c in s:
|
||||
if c notin set:
|
||||
break search
|
||||
return true
|
||||
|
||||
trySet coords, {'x', 'y', 'z', 'w'}
|
||||
trySet colors, {'r', 'g', 'b', 'a'}
|
||||
|
||||
return false
|
||||
|
||||
type
|
||||
StringIsSwizzle = generic value
|
||||
value.isSwizzle
|
||||
|
||||
SwizzleStr = static[string] and StringIsSwizzle
|
||||
|
||||
proc foo(x: SwizzleStr) =
|
||||
echo "sw"
|
||||
|
||||
accept foo("xx")
|
||||
reject foo("xe")
|
||||
|
||||
type
|
||||
Vec[N: static[int]; T] = array[N, T]
|
||||
|
||||
|
||||
proc card(x: Vec): int = x.N
|
||||
proc `$`(x: Vec): string = x.repr.strip
|
||||
|
||||
macro `.`(x: Vec, swizzle: SwizzleStr): expr =
|
||||
var
|
||||
cardinality = swizzle.len
|
||||
values = newNimNode(nnkBracket)
|
||||
v = genSym()
|
||||
|
||||
for c in swizzle:
|
||||
values.add newNimNode(nnkBracketExpr).add(
|
||||
v, c.swizzleIdx.newIntLitNode)
|
||||
|
||||
return quote do:
|
||||
let `v` = `x`
|
||||
Vec[`cardinality`, `v`.T](`values`)
|
||||
|
||||
var z = Vec([1, 2, 3])
|
||||
|
||||
echo z.card
|
||||
echo z.xz
|
||||
echo z.yxy
|
||||
|
||||
10
tests/metatype/typeclassinference.nim
Normal file
10
tests/metatype/typeclassinference.nim
Normal file
@@ -0,0 +1,10 @@
|
||||
import typetraits
|
||||
|
||||
type
|
||||
Vec[N: static[int]; T] = distinct array[N, T]
|
||||
|
||||
var x = Vec([1, 2, 3])
|
||||
|
||||
static:
|
||||
assert x.type.name == "Vec[static[int](3), int]"
|
||||
|
||||
Reference in New Issue
Block a user