summary refs log tree commit diff stats
path: root/rod/semtempl.nim
diff options
context:
space:
mode:
Diffstat (limited to 'rod/semtempl.nim')
-rwxr-xr-xrod/semtempl.nim204
1 files changed, 204 insertions, 0 deletions
diff --git a/rod/semtempl.nim b/rod/semtempl.nim
new file mode 100755
index 000000000..f866f77d2
--- /dev/null
+++ b/rod/semtempl.nim
@@ -0,0 +1,204 @@
+#
+#
+#           The Nimrod Compiler
+#        (c) Copyright 2009 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
+  if n == nil: 
+    return false
+  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
+  if n == nil: 
+    return false
+  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 = 
+  var p: PSym
+  if templ == nil: 
+    return nil
+  case templ.kind
+  of nkSym: 
+    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
+
+proc evalTemplateArgs(c: PContext, n: PNode, s: PSym): PNode = 
+  # to prevend endless recursion in templates
+  # instantation
+  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: liMessage(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: liMessage(n.info, errWrongNumberOfArguments)
+    if not (s.typ.sons[i].kind in {tyTypeDesc, tyStmt, tyExpr}): 
+      # concrete type means semantic checking for argument:
+      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: 
+    liMessage(n.info, errTemplateInstantiationTooNested) # replace each param by the corresponding node:
+  args = evalTemplateArgs(c, n, sym)
+  result = evalTemplateAux(c, sym.ast.sons[codePos], args, sym)
+  dec(evalTemplateCounter)
+
+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
+  if n == nil: 
+    return nil
+  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 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] != nil: 
+    liMessage(n.info, errNoPragmasAllowedForX, "template") # check that no generic parameters exist:
+  if n.sons[genericParamsPos] != nil: 
+    liMessage(n.info, errNoGenericParamsAllowedForX, "template")
+  if (n.sons[paramsPos] == nil): 
+    # 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] == nil: 
+      # 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] == nil: 
+    liMessage(n.info, errImplOfXexpected, s.name.s) # add identifier of template as a last step to not allow
+                                                    # recursive templates
+  addInterfaceDecl(c, s)