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:
Zahary Karadjov 2011-11-16 02:18:10 +02:00
commit 12bac28d23
6 changed files with 119 additions and 36 deletions

View file

@ -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.} =

View file

@ -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)

View file

@ -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 expectMacroOrTemplateCall(c: PContext, n: PNode): PSym =
## The argument to the proc should be nkCall(...) or similar
## Returns the macro/template symbol
if not isCallExpr(n):
GlobalError(n.info, errXisNoMacroOrTemplate, n.renderTree)
var expandedSym = qualifiedLookup(c, n[0], {checkUndeclared})
if expandedSym == nil:
GlobalError(n.info, errUndeclaredIdentifier, n[0].renderTree)
if expandedSym.kind notin {skMacro, skTemplate}:
GlobalError(n.info, errXisNoMacroOrTemplate, expandedSym.name.s)
result = expandedSym
proc semExpandToAst(c: PContext, n: PNode, magicSym: PSym, proc semExpandToAst(c: PContext, n: PNode, magicSym: PSym,
flags: TExprFlags): PNode = flags: TExprFlags): PNode =
if sonsLen(n) == 2: if sonsLen(n) == 2:
if not isCallExpr(n.sons[1]): var macroCall = n[1]
GlobalError(n.info, errXisNoMacroOrTemplate, n.renderTree) var expandedSym = expectMacroOrTemplateCall(c, macroCall)
var macroCall = n.sons[1] macroCall.sons[0] = newSymNode(expandedSym, macroCall.info)
var expandedSym = qualifiedLookup(c, macroCall.sons[0], {checkUndeclared})
if expandedSym == nil:
GlobalError(n.info, errUndeclaredIdentifier, macroCall[0].renderTree)
if expandedSym.kind notin {skMacro, skTemplate}:
GlobalError(n.info, errXisNoMacroOrTemplate, expandedSym.name.s)
macroCall.sons[0] = newNodeI(nkSym, macroCall.info)
macroCall.sons[0].sym = expandedSym
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:

View file

@ -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)

View file

@ -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])

View 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