genSym support for hygienic macros and templates.

example:
template hygienic(val: expr) =
  var `*x` = val
  echo `*x`

*x was chosen as mnemonic for "opposite of public" and thus private
This commit is contained in:
Zahary Karadjov 2012-03-26 04:36:26 +03:00
commit 6216046bc6
6 changed files with 34 additions and 70 deletions

View file

@ -16,7 +16,7 @@
import import
strutils, magicsys, lists, options, ast, astalgo, trees, treetab, nimsets, strutils, magicsys, lists, options, ast, astalgo, trees, treetab, nimsets,
msgs, os, condsyms, idents, renderer, types, passes, semfold, transf, msgs, os, condsyms, idents, renderer, types, passes, semfold, transf,
parser, ropes, rodread parser, ropes, rodread, idgen
type type
PStackFrame* = ref TStackFrame PStackFrame* = ref TStackFrame
@ -843,6 +843,7 @@ proc evalParseStmt(c: PEvalContext, n: PNode): PNode =
result.typ = newType(tyStmt, c.module) result.typ = newType(tyStmt, c.module)
proc evalTemplateAux*(templ, actual: PNode, sym: PSym): PNode = proc evalTemplateAux*(templ, actual: PNode, sym: PSym): PNode =
inc genSymBaseId
case templ.kind case templ.kind
of nkSym: of nkSym:
var p = templ.sym var p = templ.sym
@ -866,21 +867,24 @@ proc evalTemplateArgs(n: PNode, s: PSym): PNode =
of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit:
a = sonsLen(n) a = sonsLen(n)
else: a = 0 else: a = 0
var f = sonsLen(s.typ) var f = s.typ.sonsLen
if a > f: GlobalError(n.info, errWrongNumberOfArguments) if a > f: GlobalError(n.info, errWrongNumberOfArguments)
result = copyNode(n) result = copyNode(n)
for i in countup(1, f - 1): for i in countup(1, f - 1):
var arg = if i < a: n.sons[i] else: copyTree(s.typ.n.sons[i].sym.ast) 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) addSon(result, arg)
var evalTemplateCounter = 0 var evalTemplateCounter* = 0
# to prevent endless recursion in templates instantation # to prevent endless recursion in templates instantation
proc evalTemplate(n: PNode, sym: PSym): PNode = proc evalTemplate*(n: PNode, sym: PSym): PNode =
inc(evalTemplateCounter) inc(evalTemplateCounter)
if evalTemplateCounter > 100: if evalTemplateCounter > 100:
GlobalError(n.info, errTemplateInstantiationTooNested) GlobalError(n.info, errTemplateInstantiationTooNested)
result = n
# replace each param by the corresponding node: # replace each param by the corresponding node:
var args = evalTemplateArgs(n, sym) var args = evalTemplateArgs(n, sym)
@ -1312,6 +1316,7 @@ proc evalMacroCall*(c: PEvalContext, n: PNode, sym: PSym): PNode =
if evalTemplateCounter > 100: if evalTemplateCounter > 100:
GlobalError(n.info, errTemplateInstantiationTooNested) GlobalError(n.info, errTemplateInstantiationTooNested)
inc genSymBaseId
var s = newStackFrame() var s = newStackFrame()
s.call = n s.call = n
setlen(s.params, 2) setlen(s.params, 2)

View file

@ -11,7 +11,7 @@
import idents, strutils, os, options import idents, strutils, os, options
var gFrontEndId, gBackendId*: int var gFrontEndId, gBackendId*, genSymBaseId*: int
const const
debugIds* = false debugIds* = false
@ -34,6 +34,9 @@ proc backendId*(): int {.inline.} =
result = gBackendId result = gBackendId
inc(gBackendId) inc(gBackendId)
proc genSym*(basename: string): PIdent =
result = getIdent(basename & $genSymBaseId)
proc setId*(id: int) {.inline.} = proc setId*(id: int) {.inline.} =
gFrontEndId = max(gFrontEndId, id + 1) gFrontEndId = max(gFrontEndId, id + 1)

View file

@ -11,7 +11,7 @@
import import
intsets, ast, astalgo, idents, semdata, types, msgs, options, rodread, intsets, ast, astalgo, idents, semdata, types, msgs, options, rodread,
renderer renderer, wordrecg, idgen
proc considerAcc*(n: PNode): PIdent = proc considerAcc*(n: PNode): PIdent =
case n.kind case n.kind
@ -21,6 +21,11 @@ proc considerAcc*(n: PNode): PIdent =
case n.len case n.len
of 0: GlobalError(n.info, errIdentifierExpected, renderTree(n)) of 0: GlobalError(n.info, errIdentifierExpected, renderTree(n))
of 1: result = considerAcc(n.sons[0]) of 1: result = considerAcc(n.sons[0])
of 2:
if n[0].ident.id == ord(wStar):
result = genSym(n[1].ident.s)
else:
result = getIdent(n[0].ident.s & n[1].ident.s)
else: else:
var id = "" var id = ""
for i in 0.. <n.len: for i in 0.. <n.len:

View file

@ -13,7 +13,7 @@
proc semTemplateExpr(c: PContext, n: PNode, s: PSym, semCheck = true): PNode = proc semTemplateExpr(c: PContext, n: PNode, s: PSym, semCheck = true): PNode =
markUsed(n, s) markUsed(n, s)
pushInfoContext(n.info) pushInfoContext(n.info)
result = evalTemplate(c, n, s) result = evalTemplate(n, s)
if semCheck: result = semAfterMacroCall(c, result, s) if semCheck: result = semAfterMacroCall(c, result, s)
popInfoContext() popInfoContext()

View file

@ -9,67 +9,6 @@
# included from sem.nim # included from sem.nim
proc isExpr(n: PNode): bool =
# returns true if ``n`` looks like an expression
case n.kind
of nkIdent..nkNilLit:
result = true
of nkCall..pred(nkAsgn):
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
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)
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(sym.getBody, args, sym)
dec(evalTemplateCounter)
else:
GlobalError(n.info, errTemplateInstantiationTooNested)
result = n
proc symChoice(c: PContext, n: PNode, s: PSym): PNode = proc symChoice(c: PContext, n: PNode, s: PSym): PNode =
var var
a: PSym a: PSym

12
tests/compile/tgensym.nim Normal file
View file

@ -0,0 +1,12 @@
template hygienic(val: expr) =
var `*x` = val
stdout.write `*x`
var x = 100
hygienic 1
hygienic 2
hygienic 3
echo x