macros and templates can be expanded anywhere where a type is expected.
This allows for various type selection algorithms to be implemented. See tests / accept / compile / ttypeselectors.nim for examples.
This commit is contained in:
parent
ecd3c80e7e
commit
12bac28d23
6 changed files with 119 additions and 36 deletions
|
|
@ -633,6 +633,12 @@ proc copyNode*(src: PNode): PNode
|
||||||
proc copyTree*(src: PNode): PNode
|
proc copyTree*(src: PNode): PNode
|
||||||
# does copy its sons!
|
# does copy its sons!
|
||||||
|
|
||||||
|
const nkCallKinds* = {nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand,
|
||||||
|
nkCallStrLit}
|
||||||
|
|
||||||
|
proc isCallExpr*(n: PNode): bool =
|
||||||
|
result = n.kind in nkCallKinds
|
||||||
|
|
||||||
proc discardSons*(father: PNode)
|
proc discardSons*(father: PNode)
|
||||||
|
|
||||||
proc len*(n: PNode): int {.inline.} =
|
proc len*(n: PNode): int {.inline.} =
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,15 @@ proc ParamsTypeCheck(c: PContext, typ: PType) {.inline.} =
|
||||||
if not typeAllowed(typ, skConst):
|
if not typeAllowed(typ, skConst):
|
||||||
GlobalError(typ.n.info, errXisNoType, typeToString(typ))
|
GlobalError(typ.n.info, errXisNoType, typeToString(typ))
|
||||||
|
|
||||||
|
proc expectMacroOrTemplateCall(c: PContext, n: PNode): PSym
|
||||||
|
|
||||||
|
proc semTemplateExpr(c: PContext, n: PNode, s: PSym, semCheck = true): PNode
|
||||||
|
|
||||||
|
proc semMacroExpr(c: PContext, n: PNode, sym: PSym,
|
||||||
|
semCheck: bool = true): PNode
|
||||||
|
|
||||||
|
proc semWhen(c: PContext, n: PNode, semCheck: bool = true): PNode
|
||||||
|
|
||||||
include semtempl
|
include semtempl
|
||||||
|
|
||||||
proc semConstExpr(c: PContext, n: PNode): PNode =
|
proc semConstExpr(c: PContext, n: PNode): PNode =
|
||||||
|
|
@ -97,8 +106,11 @@ proc semAfterMacroCall(c: PContext, n: PNode, s: PSym): PNode =
|
||||||
# work then (see the ``tmodulealias`` test)
|
# work then (see the ``tmodulealias`` test)
|
||||||
# semExprWithType(c, result)
|
# semExprWithType(c, result)
|
||||||
result = semExpr(c, result)
|
result = semExpr(c, result)
|
||||||
of tyStmt: result = semStmt(c, result)
|
of tyStmt:
|
||||||
of tyTypeDesc: result.typ = semTypeNode(c, result, nil)
|
result = semStmt(c, result)
|
||||||
|
of tyTypeDesc:
|
||||||
|
if n.kind == nkStmtList: result.kind = nkStmtListType
|
||||||
|
result.typ = semTypeNode(c, result, nil)
|
||||||
else:
|
else:
|
||||||
result = semExpr(c, result)
|
result = semExpr(c, result)
|
||||||
result = fitNode(c, s.typ.sons[0], result)
|
result = fitNode(c, s.typ.sons[0], result)
|
||||||
|
|
|
||||||
|
|
@ -400,10 +400,6 @@ proc isAssignable(c: PContext, n: PNode): TAssignableResult =
|
||||||
else:
|
else:
|
||||||
nil
|
nil
|
||||||
|
|
||||||
proc isCallExpr(n: PNode): bool =
|
|
||||||
result = n.kind in {nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand,
|
|
||||||
nkCallStrLit}
|
|
||||||
|
|
||||||
proc newHiddenAddrTaken(c: PContext, n: PNode): PNode =
|
proc newHiddenAddrTaken(c: PContext, n: PNode): PNode =
|
||||||
if n.kind == nkHiddenDeref:
|
if n.kind == nkHiddenDeref:
|
||||||
checkSonsLen(n, 1)
|
checkSonsLen(n, 1)
|
||||||
|
|
@ -922,32 +918,35 @@ proc expectStringArg(c: PContext, n: PNode, i: int): PNode =
|
||||||
if result.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit}:
|
if result.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit}:
|
||||||
GlobalError(result.info, errStringLiteralExpected)
|
GlobalError(result.info, errStringLiteralExpected)
|
||||||
|
|
||||||
proc semExpandToAst(c: PContext, n: PNode, magicSym: PSym,
|
proc expectMacroOrTemplateCall(c: PContext, n: PNode): PSym =
|
||||||
flags: TExprFlags): PNode =
|
## The argument to the proc should be nkCall(...) or similar
|
||||||
if sonsLen(n) == 2:
|
## Returns the macro/template symbol
|
||||||
if not isCallExpr(n.sons[1]):
|
if not isCallExpr(n):
|
||||||
GlobalError(n.info, errXisNoMacroOrTemplate, n.renderTree)
|
GlobalError(n.info, errXisNoMacroOrTemplate, n.renderTree)
|
||||||
|
|
||||||
var macroCall = n.sons[1]
|
var expandedSym = qualifiedLookup(c, n[0], {checkUndeclared})
|
||||||
|
|
||||||
var expandedSym = qualifiedLookup(c, macroCall.sons[0], {checkUndeclared})
|
|
||||||
if expandedSym == nil:
|
if expandedSym == nil:
|
||||||
GlobalError(n.info, errUndeclaredIdentifier, macroCall[0].renderTree)
|
GlobalError(n.info, errUndeclaredIdentifier, n[0].renderTree)
|
||||||
|
|
||||||
if expandedSym.kind notin {skMacro, skTemplate}:
|
if expandedSym.kind notin {skMacro, skTemplate}:
|
||||||
GlobalError(n.info, errXisNoMacroOrTemplate, expandedSym.name.s)
|
GlobalError(n.info, errXisNoMacroOrTemplate, expandedSym.name.s)
|
||||||
|
|
||||||
macroCall.sons[0] = newNodeI(nkSym, macroCall.info)
|
result = expandedSym
|
||||||
macroCall.sons[0].sym = expandedSym
|
|
||||||
|
proc semExpandToAst(c: PContext, n: PNode, magicSym: PSym,
|
||||||
|
flags: TExprFlags): PNode =
|
||||||
|
if sonsLen(n) == 2:
|
||||||
|
var macroCall = n[1]
|
||||||
|
var expandedSym = expectMacroOrTemplateCall(c, macroCall)
|
||||||
|
|
||||||
|
macroCall.sons[0] = newSymNode(expandedSym, macroCall.info)
|
||||||
markUsed(n, expandedSym)
|
markUsed(n, expandedSym)
|
||||||
|
|
||||||
for i in countup(1, macroCall.len-1):
|
for i in countup(1, macroCall.len-1):
|
||||||
macroCall.sons[i] = semExprWithType(c, macroCall.sons[i], {efAllowType})
|
macroCall.sons[i] = semExprWithType(c, macroCall[i], {efAllowType})
|
||||||
|
|
||||||
# Preserve the magic symbol in order to handled in evals.nim
|
|
||||||
n.sons[0] = newNodeI(nkSym, n.info)
|
|
||||||
n.sons[0].sym = magicSym
|
|
||||||
|
|
||||||
|
# Preserve the magic symbol in order to be handled in evals.nim
|
||||||
|
n.sons[0] = newSymNode(magicSym, n.info)
|
||||||
n.typ = expandedSym.getReturnType
|
n.typ = expandedSym.getReturnType
|
||||||
result = n
|
result = n
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,15 @@
|
||||||
proc semCommand(c: PContext, n: PNode): PNode =
|
proc semCommand(c: PContext, n: PNode): PNode =
|
||||||
result = semExprNoType(c, n)
|
result = semExprNoType(c, n)
|
||||||
|
|
||||||
proc semWhen(c: PContext, n: PNode): PNode =
|
proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
|
||||||
|
# If semCheck is set to false, ``when`` will return the verbatim AST of
|
||||||
|
# the correct branch. Otherwise the AST will be passed through semStmt.
|
||||||
result = nil
|
result = nil
|
||||||
|
|
||||||
|
template set_result(e: expr) =
|
||||||
|
if semCheck: result = semStmt(c, e) # do not open a new scope!
|
||||||
|
else: result = e
|
||||||
|
|
||||||
for i in countup(0, sonsLen(n) - 1):
|
for i in countup(0, sonsLen(n) - 1):
|
||||||
var it = n.sons[i]
|
var it = n.sons[i]
|
||||||
case it.kind
|
case it.kind
|
||||||
|
|
@ -22,11 +29,11 @@ proc semWhen(c: PContext, n: PNode): PNode =
|
||||||
var e = semAndEvalConstExpr(c, it.sons[0])
|
var e = semAndEvalConstExpr(c, it.sons[0])
|
||||||
if (e.kind != nkIntLit): InternalError(n.info, "semWhen")
|
if (e.kind != nkIntLit): InternalError(n.info, "semWhen")
|
||||||
if (e.intVal != 0) and (result == nil):
|
if (e.intVal != 0) and (result == nil):
|
||||||
result = semStmt(c, it.sons[1]) # do not open a new scope!
|
set_result(it.sons[1])
|
||||||
of nkElse:
|
of nkElse:
|
||||||
checkSonsLen(it, 1)
|
checkSonsLen(it, 1)
|
||||||
if result == nil:
|
if result == nil:
|
||||||
result = semStmt(c, it.sons[0]) # do not open a new scope!
|
set_result(it.sons[0])
|
||||||
else: illFormedAst(n)
|
else: illFormedAst(n)
|
||||||
if result == nil:
|
if result == nil:
|
||||||
result = newNodeI(nkNilLit, n.info)
|
result = newNodeI(nkNilLit, n.info)
|
||||||
|
|
|
||||||
|
|
@ -630,6 +630,18 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
||||||
if s.ast == nil: GlobalError(n.info, errCannotInstantiateX, s.name.s)
|
if s.ast == nil: GlobalError(n.info, errCannotInstantiateX, s.name.s)
|
||||||
result = instGenericContainer(c, n, result)
|
result = instGenericContainer(c, n, result)
|
||||||
|
|
||||||
|
proc semExpandToType(c: PContext, n: PNode, sym: PSym): PType =
|
||||||
|
# Expands a macro or template until a type is returned
|
||||||
|
# results in GlobalError if the macro expands to something different
|
||||||
|
markUsed(n, sym)
|
||||||
|
case sym.kind
|
||||||
|
of skMacro:
|
||||||
|
result = semTypeNode(c, semMacroExpr(c, n, sym), nil)
|
||||||
|
of skTemplate:
|
||||||
|
result = semTypeNode(c, semTemplateExpr(c, n, sym), nil)
|
||||||
|
else:
|
||||||
|
GlobalError(n.info, errXisNoMacroOrTemplate, n.renderTree)
|
||||||
|
|
||||||
proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||||
result = nil
|
result = nil
|
||||||
if gCmd == cmdIdeTools: suggestExpr(c, n)
|
if gCmd == cmdIdeTools: suggestExpr(c, n)
|
||||||
|
|
@ -642,6 +654,14 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||||
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: GlobalError(n.info, errTypeExpected)
|
else: GlobalError(n.info, errTypeExpected)
|
||||||
|
of nkCallKinds:
|
||||||
|
# expand macros and templates
|
||||||
|
var expandedSym = expectMacroOrTemplateCall(c, n)
|
||||||
|
result = semExpandToType(c, n, expandedSym)
|
||||||
|
of nkWhenStmt:
|
||||||
|
var whenResult = semWhen(c, n, false)
|
||||||
|
if whenResult.kind == nkStmtList: whenResult.kind = nkStmtListType
|
||||||
|
result = semTypeNode(c, whenResult, prev)
|
||||||
of nkBracketExpr:
|
of nkBracketExpr:
|
||||||
checkMinSonsLen(n, 2)
|
checkMinSonsLen(n, 2)
|
||||||
var s = semTypeIdent(c, n.sons[0])
|
var s = semTypeIdent(c, n.sons[0])
|
||||||
|
|
|
||||||
39
tests/accept/compile/ttypeselectors.nim
Normal file
39
tests/accept/compile/ttypeselectors.nim
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
import macros
|
||||||
|
|
||||||
|
template selectType(x: int): typeDesc =
|
||||||
|
when x < 10:
|
||||||
|
int
|
||||||
|
else:
|
||||||
|
string
|
||||||
|
|
||||||
|
template simpleTypeTempl: typeDesc =
|
||||||
|
string
|
||||||
|
|
||||||
|
macro typeFromMacro(s: expr): typeDesc =
|
||||||
|
result = newNimNode(nnkIdent)
|
||||||
|
result.ident = !"string"
|
||||||
|
# result = newIdentNode"string"
|
||||||
|
|
||||||
|
proc t1*(x: int): simpleTypeTempl() =
|
||||||
|
result = "test"
|
||||||
|
|
||||||
|
proc t2*(x: int): selectType(100) =
|
||||||
|
result = "test"
|
||||||
|
|
||||||
|
proc t3*(x: int): selectType(1) =
|
||||||
|
result = 10
|
||||||
|
|
||||||
|
proc t4*(x: int): typeFromMacro() =
|
||||||
|
result = "test"
|
||||||
|
|
||||||
|
var x*: selectType(50) = "test"
|
||||||
|
|
||||||
|
proc t5*(x: selectType(5)) =
|
||||||
|
var y = x + 10
|
||||||
|
echo y
|
||||||
|
|
||||||
|
var y*: type(t2(100)) = "test"
|
||||||
|
|
||||||
|
proc t6*(x: type(t3(0))): type(t1(0)) =
|
||||||
|
result = $x
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue