mirror of
https://github.com/nim-lang/Nim.git
synced 2026-04-20 22:35:24 +00:00
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:
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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
12
tests/compile/tgensym.nim
Normal 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
|
||||
|
||||
Reference in New Issue
Block a user