summary refs log tree commit diff stats
path: root/compiler/semtempl.nim
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/semtempl.nim')
-rwxr-xr-xcompiler/semtempl.nim204
1 files changed, 204 insertions, 0 deletions
diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim
new file mode 100755
index 000000000..7782c7b42
--- /dev/null
+++ b/compiler/semtempl.nim
@@ -0,0 +1,204 @@
+#
+#
+#           The Nimrod Compiler
+#        (c) Copyright 2011 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+proc isExpr(n: PNode): bool = 
+  # returns true if ``n`` looks like an expression
+  case n.kind
+  of nkIdent..nkNilLit: 
+    result = true
+  of nkCall..nkPassAsOpenArray: 
+    for i in countup(0, sonsLen(n) - 1): 
+      if not isExpr(n.sons[i]): 
+        return false
+    result = true
+  else: result = false
+  
+proc isTypeDesc(n: PNode): bool = 
+  # returns true if ``n`` looks like a type desc
+  case n.kind
+  of nkIdent, nkSym, nkType: 
+    result = true
+  of nkDotExpr, nkBracketExpr: 
+    for i in countup(0, sonsLen(n) - 1): 
+      if not isTypeDesc(n.sons[i]): 
+        return false
+    result = true
+  of nkTypeOfExpr..nkEnumTy: 
+    result = true
+  else: result = false
+  
+proc evalTemplateAux(c: PContext, templ, actual: PNode, sym: PSym): PNode = 
+  case templ.kind
+  of nkSym: 
+    var p = templ.sym
+    if (p.kind == skParam) and (p.owner.id == sym.id): 
+      result = copyTree(actual.sons[p.position])
+    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(c, templ.sons[i], actual, sym)
+  
+var evalTemplateCounter: int = 0
+  # to prevend endless recursion in templates instantation
+
+proc evalTemplateArgs(c: PContext, n: PNode, s: PSym): PNode = 
+  var 
+    f, a: int
+    arg: PNode
+  f = sonsLen(s.typ) 
+  # if the template has zero arguments, it can be called without ``()``
+  # `n` is then a nkSym or something similar
+  case n.kind
+  of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: 
+    a = sonsLen(n)
+  else: a = 0
+  if a > f: LocalError(n.info, errWrongNumberOfArguments)
+  result = copyNode(n)
+  for i in countup(1, f - 1): 
+    if i < a: arg = n.sons[i]
+    else: arg = copyTree(s.typ.n.sons[i].sym.ast)
+    if arg == nil or arg.kind == nkEmpty: 
+      LocalError(n.info, errWrongNumberOfArguments)
+    elif not (s.typ.sons[i].kind in {tyTypeDesc, tyStmt, tyExpr}): 
+      # concrete type means semantic checking for argument:
+      # XXX This is horrible! Better make semantic checking use some kind
+      # of fixpoint iteration ...
+      arg = fitNode(c, s.typ.sons[i], semExprWithType(c, arg))
+    addSon(result, arg)
+
+proc evalTemplate(c: PContext, n: PNode, sym: PSym): PNode = 
+  var args: PNode
+  inc(evalTemplateCounter)
+  if evalTemplateCounter <= 100: 
+    # replace each param by the corresponding node:
+    args = evalTemplateArgs(c, n, sym)
+    result = evalTemplateAux(c, sym.ast.sons[codePos], args, sym)
+    dec(evalTemplateCounter)
+  else:
+    GlobalError(n.info, errTemplateInstantiationTooNested)
+    result = n
+
+proc symChoice(c: PContext, n: PNode, s: PSym): PNode = 
+  var 
+    a: PSym
+    o: TOverloadIter
+    i: int
+  i = 0
+  a = initOverloadIter(o, c, n)
+  while a != nil: 
+    a = nextOverloadIter(o, c, n)
+    inc(i)
+  if i <= 1: 
+    result = newSymNode(s)
+    result.info = n.info
+    markUsed(n, s)
+  else: 
+    # semantic checking requires a type; ``fitNode`` deals with it
+    # appropriately
+    result = newNodeIT(nkSymChoice, n.info, newTypeS(tyNone, c))
+    a = initOverloadIter(o, c, n)
+    while a != nil: 
+      addSon(result, newSymNode(a))
+      a = nextOverloadIter(o, c, n)
+
+proc resolveTemplateParams(c: PContext, n: PNode, withinBind: bool, 
+                           toBind: var TIntSet): PNode = 
+  var s: PSym
+  case n.kind
+  of nkIdent: 
+    if not withinBind and not IntSetContains(toBind, n.ident.id): 
+      s = SymTabLocalGet(c.Tab, n.ident)
+      if (s != nil): 
+        result = newSymNode(s)
+        result.info = n.info
+      else: 
+        result = n
+    else: 
+      IntSetIncl(toBind, n.ident.id)
+      result = symChoice(c, n, lookup(c, n))
+  of nkEmpty, nkSym..nkNilLit:         # atom
+    result = n
+  of nkBind: 
+    result = resolveTemplateParams(c, n.sons[0], true, toBind)
+  else: 
+    result = n
+    for i in countup(0, sonsLen(n) - 1): 
+      result.sons[i] = resolveTemplateParams(c, n.sons[i], withinBind, toBind)
+  
+proc transformToExpr(n: PNode): PNode = 
+  var realStmt: int
+  result = n
+  case n.kind
+  of nkStmtList: 
+    realStmt = - 1
+    for i in countup(0, sonsLen(n) - 1): 
+      case n.sons[i].kind
+      of nkCommentStmt, nkEmpty, nkNilLit: 
+        nil
+      else: 
+        if realStmt == - 1: realStmt = i
+        else: realStmt = - 2
+    if realStmt >= 0: result = transformToExpr(n.sons[realStmt])
+    else: n.kind = nkStmtListExpr
+  of nkBlockStmt: 
+    n.kind = nkBlockExpr
+    #nkIfStmt: n.kind := nkIfExpr; // this is not correct!
+  else: 
+    nil
+
+proc semTemplateDef(c: PContext, n: PNode): PNode = 
+  var 
+    s: PSym
+    toBind: TIntSet
+  if c.p.owner.kind == skModule: 
+    s = semIdentVis(c, skTemplate, n.sons[0], {sfStar})
+    incl(s.flags, sfGlobal)
+  else: 
+    s = semIdentVis(c, skTemplate, n.sons[0], {})
+  if sfStar in s.flags: 
+    incl(s.flags, sfInInterface) # check parameter list:
+  pushOwner(s)
+  openScope(c.tab)
+  n.sons[namePos] = newSymNode(s) # check that no pragmas exist:
+  if n.sons[pragmasPos].kind != nkEmpty: 
+    LocalError(n.info, errNoPragmasAllowedForX, "template") 
+  # check that no generic parameters exist:
+  if n.sons[genericParamsPos].kind != nkEmpty: 
+    LocalError(n.info, errNoGenericParamsAllowedForX, "template")
+  if n.sons[paramsPos].kind == nkEmpty: 
+    # use ``stmt`` as implicit result type
+    s.typ = newTypeS(tyProc, c)
+    s.typ.n = newNodeI(nkFormalParams, n.info)
+    addSon(s.typ, newTypeS(tyStmt, c))
+    addSon(s.typ.n, newNodeIT(nkType, n.info, s.typ.sons[0]))
+  else: 
+    semParamList(c, n.sons[ParamsPos], nil, s)
+    if n.sons[paramsPos].sons[0].kind == nkEmpty: 
+      # use ``stmt`` as implicit result type
+      s.typ.sons[0] = newTypeS(tyStmt, c)
+      s.typ.n.sons[0] = newNodeIT(nkType, n.info, s.typ.sons[0])
+  addParams(c, s.typ.n)       # resolve parameters:
+  IntSetInit(toBind)
+  n.sons[codePos] = resolveTemplateParams(c, n.sons[codePos], false, toBind)
+  if not (s.typ.sons[0].kind in {tyStmt, tyTypeDesc}): 
+    n.sons[codePos] = transformToExpr(n.sons[codePos]) 
+    # only parameters are resolved, no type checking is performed
+  closeScope(c.tab)
+  popOwner()
+  s.ast = n
+  result = n
+  if n.sons[codePos].kind == nkEmpty: 
+    LocalError(n.info, errImplOfXexpected, s.name.s)
+  # add identifier of template as a last step to not allow recursive templates:
+  addInterfaceDecl(c, s)