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
parent bc2eb0ea9b
commit 6216046bc6
6 changed files with 34 additions and 70 deletions

View File

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

View File

@@ -11,7 +11,7 @@
import idents, strutils, os, options
var gFrontEndId, gBackendId*: int
var gFrontEndId, gBackendId*, genSymBaseId*: int
const
debugIds* = false
@@ -25,7 +25,7 @@ proc registerID*(id: PIdObj) =
when debugIDs:
if id.id == -1 or ContainsOrIncl(usedIds, id.id):
InternalError("ID already used: " & $id.id)
proc getID*(): int {.inline.} =
result = gFrontEndId
inc(gFrontEndId)
@@ -34,6 +34,9 @@ proc backendId*(): int {.inline.} =
result = gBackendId
inc(gBackendId)
proc genSym*(basename: string): PIdent =
result = getIdent(basename & $genSymBaseId)
proc setId*(id: int) {.inline.} =
gFrontEndId = max(gFrontEndId, id + 1)

View File

@@ -11,7 +11,7 @@
import
intsets, ast, astalgo, idents, semdata, types, msgs, options, rodread,
renderer
renderer, wordrecg, idgen
proc considerAcc*(n: PNode): PIdent =
case n.kind
@@ -21,6 +21,11 @@ proc considerAcc*(n: PNode): PIdent =
case n.len
of 0: GlobalError(n.info, errIdentifierExpected, renderTree(n))
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:
var id = ""
for i in 0.. <n.len:

View File

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

View File

@@ -9,67 +9,6 @@
# 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 =
var
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