# # # The Nim Compiler # (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # ## Template evaluation engine. Now hygienic. import options, ast, astalgo, msgs, renderer, lineinfos, idents, trees import std/strutils type TemplCtx = object owner, genSymOwner: PSym instLines: bool # use the instantiation lines numbers isDeclarative: bool mapping: SymMapping # every gensym'ed symbol needs to be mapped to some # new symbol config: ConfigRef ic: IdentCache instID: int idgen: IdGenerator proc copyNode(ctx: TemplCtx, a, b: PNode): PNode = result = copyNode(a) if ctx.instLines: setInfoRecursive(result, b.info) proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) = template handleParam(param) = let x = param if x.kind == nkArgList: for y in items(x): result.add(y) elif nfDefaultRefsParam in x.flags: # value of default param needs to be evaluated like template body # if it contains other template params var res: PNode if isAtom(x): res = newNodeI(nkPar, x.info) evalTemplateAux(x, actual, c, res) if res.len == 1: res = res[0] else: res = copyNode(x) for i in 0.. expectedRegularParams + genericParams: globalError(conf, n.info, errWrongNumberOfArguments) if totalParams < genericParams: globalError(conf, n.info, errMissingGenericParamsForTemplate % n.renderTree) result = newNodeI(nkArgList, n.info) for i in 1..givenRegularParams: result.add n[i] # handle parameters with default values, which were # not supplied by the user for i in givenRegularParams+1..expectedRegularParams: let default = s.typ.n[i].sym.ast if default.isNil or default.kind == nkEmpty: localError(conf, n.info, errWrongNumberOfArguments) result.add newNodeI(nkEmpty, n.info) else: result.add default.copyTree # add any generic parameters for i in 1..genericParams: result.add n[givenRegularParams + i] # to prevent endless recursion in template instantiation const evalTemplateLimit* = 1000 proc wrapInComesFrom*(info: TLineInfo; sym: PSym; res: PNode): PNode = when true: result = res result.info = info if result.kind in {nkStmtList, nkStmtListExpr} and result.len > 0: result.lastSon.info = info when false: # this hack is required to var x = result while x.kind == nkStmtListExpr: x = x.lastSon if x.kind in nkCallKinds: for i in 1.. evalTemplateLimit: globalError(conf, n.info, errTemplateInstantiationTooNested) result = n # replace each param by the corresponding node: var args = evalTemplateArgs(n, tmpl, conf, fromHlo) var ctx = TemplCtx(owner: tmpl, genSymOwner: genSymOwner, config: conf, ic: ic, mapping: initSymMapping(), instID: instID[], idgen: idgen ) let body = tmpl.ast[bodyPos] #echo "instantion of ", renderTree(body, {renderIds}) if isAtom(body): result = newNodeI(nkPar, body.info) evalTemplateAux(body, args, ctx, result) if result.len == 1: result = result[0] else: localError(conf, result.info, "illformed AST: " & renderTree(result, {renderNoComments})) else: result = copyNode(body) ctx.instLines = sfCallsite in tmpl.flags if ctx.instLines: setInfoRecursive(result, n.info) for i in 0..