summary refs log blame commit diff stats
path: root/compiler/evaltempl.nim
blob: 265fe3fe1e668c86ae80231ab23aaa2efa567134 (plain) (tree)




















                                                                         
                                                                            




                                




                                          






                                              
                                            
         
                                
                                              
                              
       
                             
                                             



























                                                                      

































                                                                         













                                                         

                          
#
#
#           The Nimrod Compiler
#        (c) Copyright 2012 Andreas Rumpf
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

## Template evaluation engine. Now hygienic.

import
  strutils, options, ast, astalgo, msgs, os, idents, wordrecg, renderer, 
  rodread

type
  TemplCtx {.pure, final.} = object
    owner, genSymOwner: PSym
    mapping: TIdTable # every gensym'ed symbol needs to be mapped to some
                      # new symbol

proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
  case templ.kind
  of nkSym:
    var s = templ.sym
    if s.owner.id == c.owner.id:
      if s.kind == skParam:
        let x = actual.sons[s.position]
        if x.kind == nkArgList:
          for y in items(x): result.add(y)
        else:
          result.add copyTree(x)
      else:
        InternalAssert sfGenSym in s.flags
        var x = PSym(IdTableGet(c.mapping, s))
        if x == nil:
          x = copySym(s, false)
          x.owner = c.genSymOwner
          IdTablePut(c.mapping, s, x)
        result.add newSymNode(x, templ.info)
    else:
      result.add copyNode(templ)
  of nkNone..nkIdent, nkType..nkNilLit: # atom
    result.add copyNode(templ)
  else:
    var res = copyNode(templ)
    for i in countup(0, sonsLen(templ) - 1): 
      evalTemplateAux(templ.sons[i], actual, c, res)
    result.add res

when false:
  proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx): PNode =
    case templ.kind
    of nkSym:
      var s = templ.sym
      if s.owner.id == c.owner.id:
        if s.kind == skParam:
          result = copyTree(actual.sons[s.position])
        else:
          InternalAssert sfGenSym in s.flags
          var x = PSym(IdTableGet(c.mapping, s))
          if x == nil:
            x = copySym(s, false)
            x.owner = c.genSymOwner
            IdTablePut(c.mapping, s, x)
          result = newSymNode(x, templ.info)
      else:
        result = copyNode(templ)
    of nkNone..nkIdent, nkType..nkNilLit: # atom
      result = copyNode(templ)
    else:
      result = copyNode(templ)
      newSons(result, sonsLen(templ))
      for i in countup(0, sonsLen(templ) - 1): 
        result.sons[i] = evalTemplateAux(templ.sons[i], actual, c)

proc evalTemplateArgs(n: PNode, s: PSym): PNode =
  # if the template has zero arguments, it can be called without ``()``
  # `n` is then a nkSym or something similar
  var a: int
  case n.kind
  of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit:
    a = sonsLen(n)
  else: a = 0
  var f = s.typ.sonsLen
  if a > f: GlobalError(n.info, errWrongNumberOfArguments)

  result = copyNode(n)
  for i in countup(1, f - 1):
    var arg = if i < a: n.sons[i] else: copyTree(s.typ.n.sons[i].sym.ast)
    if arg == nil or arg.kind == nkEmpty:
      LocalError(n.info, errWrongNumberOfArguments)
    addSon(result, arg)

var evalTemplateCounter* = 0
  # to prevent endless recursion in templates instantiation

proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym): PNode =
  inc(evalTemplateCounter)
  if evalTemplateCounter > 100:
    GlobalError(n.info, errTemplateInstantiationTooNested)
    result = n

  # replace each param by the corresponding node:
  var args = evalTemplateArgs(n, tmpl)
  var ctx: TemplCtx
  ctx.owner = tmpl
  ctx.genSymOwner = genSymOwner
  initIdTable(ctx.mapping)
  
  let body = tmpl.getBody
  if isAtom(body): 
    result = newNodeI(nkPar, body.info)
    evalTemplateAux(body, args, ctx, result)
    if result.len == 1: result = result.sons[0]
    else:
      GlobalError(result.info, errIllFormedAstX,
                  renderTree(result, {renderNoComments}))
  else:
    result = copyNode(body)
    #evalTemplateAux(body, args, ctx, result)
    for i in countup(0, safeLen(body) - 1):
      evalTemplateAux(body.sons[i], args, ctx, result)
  
  dec(evalTemplateCounter)