# # # The Nim Compiler # (c) Copyright 2015 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # # included from sem.nim discard """ hygienic templates: template `||` (a, b: expr): expr = let aa = a if aa: aa else: b var a, b: T a || b || a Each evaluation context has to be different and we need to perform some form of preliminary symbol lookup in template definitions. Hygiene is a way to achieve lexical scoping at compile time. """ type TSymBinding = enum spNone, spGenSym, spInject proc symBinding(n: PNode): TSymBinding = for i in countup(0, sonsLen(n) - 1): var it = n.sons[i] var key = if it.kind == nkExprColonExpr: it.sons[0] else: it if key.kind == nkIdent: case whichKeyword(key.ident) of wGensym: return spGenSym of wInject: return spInject else: discard type TSymChoiceRule = enum scClosed, scOpen, scForceOpen proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule): PNode = var a: PSym o: TOverloadIter var i = 0 a = initOverloadIter(o, c, n) while a != nil: a = nextOverloadIter(o, c, n) inc(i) if i > 1: break if i <= 1 and r != scForceOpen: # XXX this makes more sense but breaks bootstrapping for now: # (s.kind notin routineKinds or s.magic != mNone): # for instance 'nextTry' is both in tables.nim and astalgo.nim ... result = newSymNode(s, n.info) markUsed(n.info, s) else: # semantic checking requires a type; ``fitNode`` deals with it # appropriately let kind = if r == scClosed or n.kind == nkDotExpr: nkClosedSymChoice else: nkOpenSymChoice result = newNodeIT(kind, n.info, newTypeS(tyNone, c)) a = initOverloadIter(o, c, n) while a != nil: incl(a.flags, sfUsed) addSon(result, newSymNode(a, n.info)) a = nextOverloadIter(o, c, n) proc semBindStmt(c: PContext, n: PNode, toBind: var IntSet): PNode = for i in 0 .. < n.len: var a = n.sons[i] # If 'a' is an overloaded symbol, we used to use the first symbol # as a 'witness' and use the fact that subsequent lookups will yield # the same symbol! # This is however not true anymore for hygienic templates as semantic # processing for them changes the symbol table... let s = qualifiedLookUp(c, a) if s != nil: # we need to mark all symbols: let sc = symChoice(c, n, s, scClosed) if sc.kind == nkSym: toBind.incl(sc.sym.id) else: for x in items(sc): toBind.incl(x.sym.id) else: illFormedAst(a) result = newNodeI(nkEmpty, n.info) proc semMixinStmt(c: PContext, n: PNode, toMixin: var IntSet): PNode = for i in 0 .. < n.len: toMixin.incl(considerQuotedIdent(n.sons[i]).id) result = newNodeI(nkEmpty, n.info) proc replaceIdentBySym(n: var PNode, s: PNode) = case n.kind of nkPostfix: replaceIdentBySym(n.sons[1], s) of nkPragmaExpr: replaceIdentBySym(n.sons[0], s) of nkIdent, nkAccQuoted, nkSym: n = s else: illFormedAst(n) type TemplCtx = object c: PContext toBind, toMixin, toInject: IntSet owner: PSym cursorInBody: bool # only for nimsuggest bracketExpr: PNode template withBracketExpr(ctx, x, body: untyped) = let old = ctx.bracketExpr ctx.bracketExpr = x body ctx.bracketExpr = old proc getIdentNode(c: var TemplCtx, n: PNode): PNode = case n.kind of nkPostfix: result = getIdentNode(c, n.sons[1]) of nkPragmaExpr: result = getIdentNode(c, n.sons[0]) of nkIdent: result = n let s = qualifiedLookUp(c.c, n, {}) if s != nil: if s.owner == c.owner and s.kind == skParam: result = newSymNode(s, n.info) of nkAccQuoted, nkSym: result = n else: illFormedAst(n) result = n proc isTemplParam(c: TemplCtx, n: PNode): bool {.inline.} = result = n.kind == nkSym and n.sym.kind == skParam and n.sym.owner == c.owner and sfGenSym notin n.sym.flags proc semTemplBody(c: var TemplCtx, n: PNode): PNode proc openScope(c: var TemplCtx) = openScope(c.c) proc closeScope(c: var TemplCtx) = closeScope(c.c) proc semTemplBodyScope(c: var TemplCtx, n: PNode): PNode = openScope(c) result = semTemplBody(c, n) closeScope(c) proc onlyReplaceParams(c: var TemplCtx, n: PNode): PNode = result = n if n.kind == nkIdent: let s = qualifiedLookUp(c.c, n, {}) if s != nil: if s.owner == c.owner and s.kind == skParam: incl(s.flags, sfUsed) result = newSymNode(s, n.info) styleCheckUse(n.info, s) else: for i in 0 ..