first steps to make templates hygienic

This commit is contained in:
Araq 2012-08-20 01:13:13 +02:00
commit 5e15dec175
23 changed files with 486 additions and 187 deletions

View file

@ -234,7 +234,7 @@ type
sfDiscriminant, # field is a discriminant in a record/object sfDiscriminant, # field is a discriminant in a record/object
sfDeprecated, # symbol is deprecated sfDeprecated, # symbol is deprecated
sfError, # usage of symbol should trigger a compile-time error sfError, # usage of symbol should trigger a compile-time error
sfInnerProc, # proc is an inner proc sfShadowed, # a symbol that was shadowed in some inner scope
sfThread, # proc will run as a thread sfThread, # proc will run as a thread
# variable is a thread variable # variable is a thread variable
sfCompileTime, # proc can be evaluated at compile time sfCompileTime, # proc can be evaluated at compile time
@ -247,7 +247,7 @@ type
# language; for interfacing with Objective C # language; for interfacing with Objective C
sfDiscardable # returned value may be discarded implicitely sfDiscardable # returned value may be discarded implicitely
sfDestructor # proc is destructor sfDestructor # proc is destructor
sfByCopy # a variable is to be captured by value in a closure sfGenSym # symbol is 'gensym'ed; do not add to symbol table
TSymFlags* = set[TSymFlag] TSymFlags* = set[TSymFlag]
@ -264,10 +264,7 @@ const
# symbol name that was generated by the compiler # symbol name that was generated by the compiler
# the compiler will avoid printing such names # the compiler will avoid printing such names
# in user messages. # in user messages.
sfShadowed* = sfInnerProc
# a variable that was shadowed in some inner scope
sfHoist* = sfVolatile ## proc return value can be hoisted sfHoist* = sfVolatile ## proc return value can be hoisted
const const
@ -785,15 +782,6 @@ const # for all kind of hash tables:
GrowthFactor* = 2 # must be power of 2, > 0 GrowthFactor* = 2 # must be power of 2, > 0
StartSize* = 8 # must be power of 2, > 0 StartSize* = 8 # must be power of 2, > 0
proc ValueToString*(a: PNode): string =
case a.kind
of nkCharLit..nkUInt64Lit: result = $(a.intVal)
of nkFloatLit..nkFloat128Lit: result = $(a.floatVal)
of nkStrLit..nkTripleStrLit: result = a.strVal
else:
InternalError(a.info, "valueToString")
result = ""
proc copyStrTable(dest: var TStrTable, src: TStrTable) = proc copyStrTable(dest: var TStrTable, src: TStrTable) =
dest.counter = src.counter dest.counter = src.counter
if isNil(src.data): return if isNil(src.data): return

View file

@ -166,7 +166,9 @@ proc SameValue*(a, b: PNode): bool =
of nkStrLit..nkTripleStrLit: of nkStrLit..nkTripleStrLit:
if b.kind in {nkStrLit..nkTripleStrLit}: result = a.strVal == b.strVal if b.kind in {nkStrLit..nkTripleStrLit}: result = a.strVal == b.strVal
else: else:
InternalError(a.info, "SameValue") # don't raise an internal error for 'nimrod check':
#InternalError(a.info, "SameValue")
nil
proc leValue*(a, b: PNode): bool = proc leValue*(a, b: PNode): bool =
# a <= b? # a <= b?
@ -178,7 +180,10 @@ proc leValue*(a, b: PNode): bool =
if b.kind in {nkFloatLit..nkFloat64Lit}: result = a.floatVal <= b.floatVal if b.kind in {nkFloatLit..nkFloat64Lit}: result = a.floatVal <= b.floatVal
of nkStrLit..nkTripleStrLit: of nkStrLit..nkTripleStrLit:
if b.kind in {nkStrLit..nkTripleStrLit}: result = a.strVal <= b.strVal if b.kind in {nkStrLit..nkTripleStrLit}: result = a.strVal <= b.strVal
else: InternalError(a.info, "leValue") else:
# don't raise an internal error for 'nimrod check':
#InternalError(a.info, "leValue")
nil
proc lookupInRecord(n: PNode, field: PIdent): PSym = proc lookupInRecord(n: PNode, field: PIdent): PSym =
result = nil result = nil

View file

@ -224,13 +224,16 @@ proc processMergeInfo(L: var TBaseLexer, m: BModule) =
of "labels": m.labels = decodeVInt(L.buf, L.bufpos) of "labels": m.labels = decodeVInt(L.buf, L.bufpos)
of "hasframe": m.FrameDeclared = decodeVInt(L.buf, L.bufpos) != 0 of "hasframe": m.FrameDeclared = decodeVInt(L.buf, L.bufpos) != 0
else: InternalError("ccgmerge: unkown key: " & k) else: InternalError("ccgmerge: unkown key: " & k)
when not defined(nimhygiene):
{.pragma: inject.}
template withCFile(cfilename: string, body: stmt) = template withCFile(cfilename: string, body: stmt) =
var s = LLStreamOpen(cfilename, fmRead) var s = LLStreamOpen(cfilename, fmRead)
if s == nil: return if s == nil: return
var L: TBaseLexer var L {.inject.}: TBaseLexer
openBaseLexer(L, s) openBaseLexer(L, s)
var k = newStringOfCap("NIM_merge_FORWARD_TYPES".len) var k {.inject.} = newStringOfCap("NIM_merge_FORWARD_TYPES".len)
while true: while true:
skipUntilCmd(L) skipUntilCmd(L)
if ^L.bufpos == '\0': break if ^L.bufpos == '\0': break

View file

@ -55,6 +55,8 @@ proc countDefinedSymbols*(): int =
proc InitDefines*() = proc InitDefines*() =
initStrTable(gSymbols) initStrTable(gSymbols)
DefineSymbol("nimrod") # 'nimrod' is always defined DefineSymbol("nimrod") # 'nimrod' is always defined
# for bootstrapping purposes and old code:
DefineSymbol("nimhygiene")
# add platform specific symbols: # add platform specific symbols:
case targetCPU case targetCPU

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, idgen, osproc, streams parser, ropes, rodread, idgen, osproc, streams, evaltempl
type type
PStackFrame* = ref TStackFrame PStackFrame* = ref TStackFrame
@ -850,56 +850,6 @@ proc evalParseStmt(c: PEvalContext, n: PNode): PNode =
result = parseString(code.getStrValue, code.info.toFilename, result = parseString(code.getStrValue, code.info.toFilename,
code.stringStartingLine) code.stringStartingLine)
result.typ = newType(tyStmt, c.module) 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
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(templ.sons[i], actual, sym)
proc evalTemplateArgs(n: PNode, s: PSym): PNode =
# if the template has zero arguments, it can be called without ``()``
# `n` is then a nkSym or something similar
var a: int
case n.kind
of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit:
a = sonsLen(n)
else: a = 0
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
# to prevent endless recursion in templates instantation
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 evalTypeTrait*(n: PNode, context: PSym): PNode = proc evalTypeTrait*(n: PNode, context: PSym): PNode =
## XXX: This should be pretty much guaranteed to be true ## XXX: This should be pretty much guaranteed to be true
@ -964,7 +914,11 @@ proc evalExpandToAst(c: PEvalContext, original: PNode): PNode =
case expandedSym.kind case expandedSym.kind
of skTemplate: of skTemplate:
result = evalTemplate(macroCall, expandedSym) let genSymOwner = if c.tos != nil and c.tos.prc != nil:
c.tos.prc
else:
c.module
result = evalTemplate(macroCall, expandedSym, genSymOwner)
of skMacro: of skMacro:
# At this point macroCall.sons[0] is nkSym node. # At this point macroCall.sons[0] is nkSym node.
# To be completely compatible with normal macro invocation, # To be completely compatible with normal macro invocation,

83
compiler/evaltempl.nim Normal file
View file

@ -0,0 +1,83 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2012 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Template evaluation engine. Now hygienic.
import
strutils, options, ast, astalgo, msgs, os, idents, wordrecg, renderer,
rodread
type
TemplCtx {.pure, final.} = object
owner, genSymOwner: PSym
mapping: TIdTable # every gensym'ed symbol needs to be mapped to some
# new symbol
proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx): PNode =
#inc genSymBaseId
case templ.kind
of nkSym:
var s = templ.sym
if s.owner.id == c.owner.id:
if s.kind == skParam:
result = copyTree(actual.sons[s.position])
else:
InternalAssert sfGenSym in s.flags
var x = PSym(IdTableGet(c.mapping, s))
if x == nil:
x = copySym(s, false)
x.owner = c.genSymOwner
IdTablePut(c.mapping, s, x)
result = newSymNode(x, templ.info)
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(templ.sons[i], actual, c)
proc evalTemplateArgs(n: PNode, s: PSym): PNode =
# if the template has zero arguments, it can be called without ``()``
# `n` is then a nkSym or something similar
var a: int
case n.kind
of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit:
a = sonsLen(n)
else: a = 0
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
# to prevent endless recursion in templates instantiation
proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym): PNode =
inc(evalTemplateCounter)
if evalTemplateCounter > 100:
GlobalError(n.info, errTemplateInstantiationTooNested)
result = n
# replace each param by the corresponding node:
var args = evalTemplateArgs(n, tmpl)
var ctx: TemplCtx
ctx.owner = tmpl
ctx.genSymOwner = genSymOwner
initIdTable(ctx.mapping)
result = evalTemplateAux(tmpl.getBody, args, ctx)
dec(evalTemplateCounter)

View file

@ -471,7 +471,8 @@ proc generateClosureCreation(o: POuterContext, scope: PEnv): PNode =
# add assignment statements: # add assignment statements:
for local in scope.capturedVars: for local in scope.capturedVars:
let fieldAccess = indirectAccess(env, local, env.info) let fieldAccess = indirectAccess(env, local, env.info)
if sfByCopy in local.flags or local.kind == skParam: if local.kind == skParam:
# maybe later: (sfByCopy in local.flags)
# add ``env.param = param`` # add ``env.param = param``
result.add(newAsgnStmt(fieldAccess, newSymNode(local))) result.add(newAsgnStmt(fieldAccess, newSymNode(local)))
IdNodeTablePut(o.localsToAccess, local, fieldAccess) IdNodeTablePut(o.localsToAccess, local, fieldAccess)
@ -515,9 +516,9 @@ proc transformOuterProc(o: POuterContext, n: PNode): PNode =
scope.sons[0] = generateClosureCreation(o, env) scope.sons[0] = generateClosureCreation(o, env)
# change 'local' to 'closure.local', unless it's a 'byCopy' variable: # change 'local' to 'closure.local', unless it's a 'byCopy' variable:
if sfByCopy notin local.flags: # if sfByCopy notin local.flags:
result = IdNodeTableGet(o.localsToAccess, local) result = IdNodeTableGet(o.localsToAccess, local)
assert result != nil, "cannot find: " & local.name.s assert result != nil, "cannot find: " & local.name.s
# else it is captured by copy and this means that 'outer' should continue # else it is captured by copy and this means that 'outer' should continue
# to access the local as a local. # to access the local as a local.
of nkLambdaKinds: of nkLambdaKinds:

View file

@ -111,7 +111,7 @@ proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest) =
proc parseDirective(L: var TLexer, tok: var TToken) = proc parseDirective(L: var TLexer, tok: var TToken) =
ppGetTok(L, tok) # skip @ ppGetTok(L, tok) # skip @
case whichKeyword(tok.ident) case whichKeyword(tok.ident)
of wIf: of wIf:
setlen(condStack, len(condStack) + 1) setlen(condStack, len(condStack) + 1)
var res = EvalppIf(L, tok) var res = EvalppIf(L, tok)
condStack[high(condStack)] = res condStack[high(condStack)] = res
@ -123,25 +123,27 @@ proc parseDirective(L: var TLexer, tok: var TToken) =
ppGetTok(L, tok) ppGetTok(L, tok)
msgs.MsgWriteln(tokToStr(tok)) msgs.MsgWriteln(tokToStr(tok))
ppGetTok(L, tok) ppGetTok(L, tok)
of wPutEnv: else:
ppGetTok(L, tok) case tok.ident.s.normalize
var key = tokToStr(tok) of "putenv":
ppGetTok(L, tok) ppGetTok(L, tok)
os.putEnv(key, tokToStr(tok)) var key = tokToStr(tok)
ppGetTok(L, tok) ppGetTok(L, tok)
of wPrependEnv: os.putEnv(key, tokToStr(tok))
ppGetTok(L, tok) ppGetTok(L, tok)
var key = tokToStr(tok) of "prependenv":
ppGetTok(L, tok) ppGetTok(L, tok)
os.putEnv(key, tokToStr(tok) & os.getenv(key)) var key = tokToStr(tok)
ppGetTok(L, tok) ppGetTok(L, tok)
of wAppendenv: os.putEnv(key, tokToStr(tok) & os.getenv(key))
ppGetTok(L, tok) ppGetTok(L, tok)
var key = tokToStr(tok) of "appendenv":
ppGetTok(L, tok) ppGetTok(L, tok)
os.putEnv(key, os.getenv(key) & tokToStr(tok)) var key = tokToStr(tok)
ppGetTok(L, tok) ppGetTok(L, tok)
else: lexMessage(L, errInvalidDirectiveX, tokToStr(tok)) os.putEnv(key, os.getenv(key) & tokToStr(tok))
ppGetTok(L, tok)
else: lexMessage(L, errInvalidDirectiveX, tokToStr(tok))
proc confTok(L: var TLexer, tok: var TToken) = proc confTok(L: var TLexer, tok: var TToken) =
ppGetTok(L, tok) ppGetTok(L, tok)

View file

@ -23,16 +23,17 @@ const
wMagic, wNosideEffect, wSideEffect, wNoreturn, wDynLib, wHeader, wMagic, wNosideEffect, wSideEffect, wNoreturn, wDynLib, wHeader,
wCompilerProc, wProcVar, wDeprecated, wVarargs, wCompileTime, wMerge, wCompilerProc, wProcVar, wDeprecated, wVarargs, wCompileTime, wMerge,
wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC, wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC,
wNoStackFrame, wError, wDiscardable, wNoInit, wDestructor, wHoist} wNoStackFrame, wError, wDiscardable, wNoInit, wDestructor, wHoist,
wGenSym, wInject}
converterPragmas* = procPragmas converterPragmas* = procPragmas
methodPragmas* = procPragmas methodPragmas* = procPragmas
templatePragmas* = {wImmediate, wDeprecated, wError} templatePragmas* = {wImmediate, wDeprecated, wError, wGenSym, wInject}
macroPragmas* = {FirstCallConv..LastCallConv, wImmediate, wImportc, wExportc, macroPragmas* = {FirstCallConv..LastCallConv, wImmediate, wImportc, wExportc,
wNodecl, wMagic, wNosideEffect, wCompilerProc, wDeprecated, wExtern, wNodecl, wMagic, wNosideEffect, wCompilerProc, wDeprecated, wExtern,
wImportcpp, wImportobjc, wError, wDiscardable} wImportcpp, wImportobjc, wError, wDiscardable, wGenSym, wInject}
iteratorPragmas* = {FirstCallConv..LastCallConv, wNosideEffect, wSideEffect, iteratorPragmas* = {FirstCallConv..LastCallConv, wNosideEffect, wSideEffect,
wImportc, wExportc, wNodecl, wMagic, wDeprecated, wBorrow, wExtern, wImportc, wExportc, wNodecl, wMagic, wDeprecated, wBorrow, wExtern,
wImportcpp, wImportobjc, wError, wDiscardable} wImportcpp, wImportobjc, wError, wDiscardable, wGenSym, wInject}
exprPragmas* = {wLine} exprPragmas* = {wLine}
stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangechecks, stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangechecks,
wBoundchecks, wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints, wBoundchecks, wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints,
@ -46,14 +47,16 @@ const
wDeprecated, wExtern, wThread, wImportcpp, wImportobjc, wNoStackFrame} wDeprecated, wExtern, wThread, wImportcpp, wImportobjc, wNoStackFrame}
typePragmas* = {wImportc, wExportc, wDeprecated, wMagic, wAcyclic, wNodecl, typePragmas* = {wImportc, wExportc, wDeprecated, wMagic, wAcyclic, wNodecl,
wPure, wHeader, wCompilerProc, wFinal, wSize, wExtern, wShallow, wPure, wHeader, wCompilerProc, wFinal, wSize, wExtern, wShallow,
wImportcpp, wImportobjc, wError, wIncompleteStruct, wByCopy, wByRef} wImportcpp, wImportobjc, wError, wIncompleteStruct, wByCopy, wByRef,
wGenSym, wInject}
fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern, fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern,
wImportcpp, wImportobjc, wError} wImportcpp, wImportobjc, wError}
varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl, varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl,
wMagic, wHeader, wDeprecated, wCompilerProc, wDynLib, wExtern, wMagic, wHeader, wDeprecated, wCompilerProc, wDynLib, wExtern,
wImportcpp, wImportobjc, wError, wNoInit, wCompileTime, wGlobal} wImportcpp, wImportobjc, wError, wNoInit, wCompileTime, wGlobal,
wGenSym, wInject}
constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl, constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl,
wExtern, wImportcpp, wImportobjc, wError} wExtern, wImportcpp, wImportobjc, wError, wGenSym, wInject}
letPragmas* = varPragmas letPragmas* = varPragmas
procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNosideEffect, procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNosideEffect,
wThread} wThread}
@ -655,9 +658,13 @@ proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) =
incl(sym.typ.flags, tfByRef) incl(sym.typ.flags, tfByRef)
of wByCopy: of wByCopy:
noVal(it) noVal(it)
if sym.kind != skType: incl(sym.flags, sfByCopy) if sym.kind != skType or sym.typ == nil: invalidPragma(it)
elif sym.typ == nil: invalidPragma(it)
else: incl(sym.typ.flags, tfByCopy) else: incl(sym.typ.flags, tfByCopy)
of wInject, wGenSym:
# We check for errors, but do nothing with these pragmas otherwise
# as they are handled directly in 'evalTemplate'.
noVal(it)
if sym == nil: invalidPragma(it)
of wLine: PragmaLine(c, it) of wLine: PragmaLine(c, it)
else: invalidPragma(it) else: invalidPragma(it)
else: invalidPragma(it) else: invalidPragma(it)

View file

@ -14,7 +14,8 @@ import
wordrecg, ropes, msgs, os, condsyms, idents, renderer, types, platform, math, wordrecg, ropes, msgs, os, condsyms, idents, renderer, types, platform, math,
magicsys, parser, nversion, nimsets, semfold, importer, magicsys, parser, nversion, nimsets, semfold, importer,
procfind, lookups, rodread, pragmas, passes, semdata, semtypinst, sigmatch, procfind, lookups, rodread, pragmas, passes, semdata, semtypinst, sigmatch,
semthreads, intsets, transf, evals, idgen, aliases, cgmeth, lambdalifting semthreads, intsets, transf, evals, idgen, aliases, cgmeth, lambdalifting,
evaltempl
proc semPass*(): TPass proc semPass*(): TPass
# implementation # implementation
@ -56,7 +57,17 @@ proc isTopLevel(c: PContext): bool {.inline.} =
proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym = proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym =
result = newSym(kind, considerAcc(n), getCurrOwner()) result = newSym(kind, considerAcc(n), getCurrOwner())
result.info = n.info result.info = n.info
proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym =
# like newSymS, but considers gensym'ed symbols
if n.kind == nkSym:
result = n.sym
InternalAssert sfGenSym in result.flags
InternalAssert result.kind == kind
else:
result = newSym(kind, considerAcc(n), getCurrOwner())
result.info = n.info
proc semIdentVis(c: PContext, kind: TSymKind, n: PNode, proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
allowed: TSymFlags): PSym allowed: TSymFlags): PSym
# identifier with visability # identifier with visability

View file

@ -24,7 +24,7 @@ proc restoreOldStyleType(n: PNode) =
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(n, s) result = evalTemplate(n, s, getCurrOwner())
if semCheck: result = semAfterMacroCall(c, result, s) if semCheck: result = semAfterMacroCall(c, result, s)
popInfoContext() popInfoContext()
@ -1372,7 +1372,9 @@ proc semBlockExpr(c: PContext, n: PNode): PNode =
Inc(c.p.nestedBlockCounter) Inc(c.p.nestedBlockCounter)
checkSonsLen(n, 2) checkSonsLen(n, 2)
openScope(c.tab) # BUGFIX: label is in the scope of block! openScope(c.tab) # BUGFIX: label is in the scope of block!
if n.sons[0].kind != nkEmpty: addDecl(c, newSymS(skLabel, n.sons[0], c)) if n.sons[0].kind notin {nkEmpty, nkSym}:
# nkSym for gensym'ed labels:
addDecl(c, newSymS(skLabel, n.sons[0], c))
n.sons[1] = semStmtListExpr(c, n.sons[1]) n.sons[1] = semStmtListExpr(c, n.sons[1])
n.typ = n.sons[1].typ n.typ = n.sons[1].typ
closeScope(c.tab) closeScope(c.tab)

View file

@ -17,11 +17,20 @@
# included from sem.nim # included from sem.nim
type type
TSemGenericFlag = enum TSemGenericFlag = enum
withinBind, withinTypeDesc withinBind, withinTypeDesc
TSemGenericFlags = set[TSemGenericFlag] TSemGenericFlags = set[TSemGenericFlag]
proc getIdentNode(n: PNode): PNode =
case n.kind
of nkPostfix: result = getIdentNode(n.sons[1])
of nkPragmaExpr: result = getIdentNode(n.sons[0])
of nkIdent, nkAccQuoted, nkSym: result = n
else:
illFormedAst(n)
result = n
proc semGenericStmt(c: PContext, n: PNode, flags: TSemGenericFlags, proc semGenericStmt(c: PContext, n: PNode, flags: TSemGenericFlags,
toBind: var TIntSet): PNode toBind: var TIntSet): PNode
proc semGenericStmtScope(c: PContext, n: PNode, proc semGenericStmtScope(c: PContext, n: PNode,
@ -54,15 +63,6 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym): PNode =
result = n result = n
else: result = newSymNode(s, n.info) else: result = newSymNode(s, n.info)
proc getIdentNode(n: PNode): PNode =
case n.kind
of nkPostfix: result = getIdentNode(n.sons[1])
of nkPragmaExpr: result = getIdentNode(n.sons[0])
of nkIdent, nkAccQuoted: result = n
else:
illFormedAst(n)
result = n
proc semGenericStmt(c: PContext, n: PNode, proc semGenericStmt(c: PContext, n: PNode,
flags: TSemGenericFlags, toBind: var TIntSet): PNode = flags: TSemGenericFlags, toBind: var TIntSet): PNode =
result = n result = n

View file

@ -91,10 +91,11 @@ proc semBlock(c: PContext, n: PNode): PNode =
Inc(c.p.nestedBlockCounter) Inc(c.p.nestedBlockCounter)
checkSonsLen(n, 2) checkSonsLen(n, 2)
openScope(c.tab) # BUGFIX: label is in the scope of block! openScope(c.tab) # BUGFIX: label is in the scope of block!
if n.sons[0].kind != nkEmpty: if n.sons[0].kind != nkEmpty:
var labl = newSymS(skLabel, n.sons[0], c) var labl = newSymG(skLabel, n.sons[0], c)
addDecl(c, labl) if sfGenSym notin labl.flags:
n.sons[0] = newSymNode(labl) addDecl(c, labl)
n.sons[0] = newSymNode(labl, n.sons[0].info)
suggestSym(n.sons[0], labl) suggestSym(n.sons[0], labl)
n.sons[1] = semStmt(c, n.sons[1]) n.sons[1] = semStmt(c, n.sons[1])
closeScope(c.tab) closeScope(c.tab)
@ -284,7 +285,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
Message(a.info, warnEachIdentIsTuple) Message(a.info, warnEachIdentIsTuple)
for j in countup(0, length-3): for j in countup(0, length-3):
var v = semIdentDef(c, a.sons[j], symkind) var v = semIdentDef(c, a.sons[j], symkind)
addInterfaceDecl(c, v) if sfGenSym notin v.flags: addInterfaceDecl(c, v)
when oKeepVariableNames: when oKeepVariableNames:
if c.InUnrolledContext > 0: v.flags.incl(sfShadowed) if c.InUnrolledContext > 0: v.flags.incl(sfShadowed)
else: else:
@ -332,7 +333,7 @@ proc semConst(c: PContext, n: PNode): PNode =
continue continue
v.typ = typ v.typ = typ
v.ast = def # no need to copy v.ast = def # no need to copy
addInterfaceDecl(c, v) if sfGenSym notin v.flags: addInterfaceDecl(c, v)
var b = newNodeI(nkConstDef, a.info) var b = newNodeI(nkConstDef, a.info)
addSon(b, newSymNode(v)) addSon(b, newSymNode(v))
addSon(b, ast.emptyNode) # no type description addSon(b, ast.emptyNode) # no type description
@ -422,25 +423,25 @@ proc semForVars(c: PContext, n: PNode): PNode =
# and thus no tuple unpacking: # and thus no tuple unpacking:
if iter.kind != tyTuple or length == 3: if iter.kind != tyTuple or length == 3:
if length == 3: if length == 3:
var v = newSymS(skForVar, n.sons[0], c) var v = newSymG(skForVar, n.sons[0], c)
if getCurrOwner().kind == skModule: incl(v.flags, sfGlobal) if getCurrOwner().kind == skModule: incl(v.flags, sfGlobal)
# BUGFIX: don't use `iter` here as that would strip away # BUGFIX: don't use `iter` here as that would strip away
# the ``tyGenericInst``! See ``tests/compile/tgeneric.nim`` # the ``tyGenericInst``! See ``tests/compile/tgeneric.nim``
# for an example: # for an example:
v.typ = n.sons[length-2].typ v.typ = n.sons[length-2].typ
n.sons[0] = newSymNode(v) n.sons[0] = newSymNode(v)
addDecl(c, v) if sfGenSym notin v.flags: addDecl(c, v)
else: else:
LocalError(n.info, errWrongNumberOfVariables) LocalError(n.info, errWrongNumberOfVariables)
elif length-2 != sonsLen(iter): elif length-2 != sonsLen(iter):
LocalError(n.info, errWrongNumberOfVariables) LocalError(n.info, errWrongNumberOfVariables)
else: else:
for i in countup(0, length - 3): for i in countup(0, length - 3):
var v = newSymS(skForVar, n.sons[i], c) var v = newSymG(skForVar, n.sons[i], c)
if getCurrOwner().kind == skModule: incl(v.flags, sfGlobal) if getCurrOwner().kind == skModule: incl(v.flags, sfGlobal)
v.typ = iter.sons[i] v.typ = iter.sons[i]
n.sons[i] = newSymNode(v) n.sons[i] = newSymNode(v)
addDecl(c, v) if sfGenSym notin v.flags: addDecl(c, v)
Inc(c.p.nestedLoopCounter) Inc(c.p.nestedLoopCounter)
n.sons[length-1] = SemStmt(c, n.sons[length-1]) n.sons[length-1] = SemStmt(c, n.sons[length-1])
Dec(c.p.nestedLoopCounter) Dec(c.p.nestedLoopCounter)
@ -539,7 +540,7 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) =
if a.sons[0].kind == nkPragmaExpr: if a.sons[0].kind == nkPragmaExpr:
pragma(c, s, a.sons[0].sons[1], typePragmas) pragma(c, s, a.sons[0].sons[1], typePragmas)
# add it here, so that recursive types are possible: # add it here, so that recursive types are possible:
addInterfaceDecl(c, s) if sfGenSym notin s.flags: addInterfaceDecl(c, s)
a.sons[0] = newSymNode(s) a.sons[0] = newSymNode(s)
proc typeSectionRightSidePass(c: PContext, n: PNode) = proc typeSectionRightSidePass(c: PContext, n: PNode) =
@ -780,7 +781,8 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
s.typ.callConv = lastOptionEntry(c).defaultCC s.typ.callConv = lastOptionEntry(c).defaultCC
# add it here, so that recursive procs are possible: # add it here, so that recursive procs are possible:
# -2 because we have a scope open for parameters # -2 because we have a scope open for parameters
if kind in OverloadableSyms: if sfGenSym in s.flags: nil
elif kind in OverloadableSyms:
addInterfaceOverloadableSymAt(c, s, c.tab.tos - 2) addInterfaceOverloadableSymAt(c, s, c.tab.tos - 2)
else: else:
addInterfaceDeclAt(c, s, c.tab.tos - 2) addInterfaceDeclAt(c, s, c.tab.tos - 2)

View file

@ -9,6 +9,37 @@
# included from sem.nim # 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: nil
proc symChoice(c: PContext, n: PNode, s: PSym): PNode = proc symChoice(c: PContext, n: PNode, s: PSym): PNode =
var var
a: PSym a: PSym
@ -20,17 +51,16 @@ proc symChoice(c: PContext, n: PNode, s: PSym): PNode =
inc(i) inc(i)
if i > 1: break if i > 1: break
if i <= 1: if i <= 1:
result = newSymNode(s) result = newSymNode(s, n.info)
result.info = n.info
markUsed(n, s) markUsed(n, s)
else: else:
# semantic checking requires a type; ``fitNode`` deals with it # semantic checking requires a type; ``fitNode`` deals with it
# appropriately # appropriately
result = newNodeIT(nkSymChoice, n.info, newTypeS(tyNone, c)) result = newNodeIT(nkSymChoice, n.info, newTypeS(tyNone, c))
a = initOverloadIter(o, c, n) a = initOverloadIter(o, c, n)
while a != nil: while a != nil:
incl(a.flags, sfUsed) incl(a.flags, sfUsed)
addSon(result, newSymNode(a)) addSon(result, newSymNode(a, n.info))
a = nextOverloadIter(o, c, n) a = nextOverloadIter(o, c, n)
proc semBindStmt(c: PContext, n: PNode, toBind: var TIntSet): PNode = proc semBindStmt(c: PContext, n: PNode, toBind: var TIntSet): PNode =
@ -45,37 +75,217 @@ proc semBindStmt(c: PContext, n: PNode, toBind: var TIntSet): PNode =
else: else:
illFormedAst(a) illFormedAst(a)
result = newNodeI(nkEmpty, n.info) result = newNodeI(nkEmpty, n.info)
proc resolveTemplateParams(c: PContext, n: PNode, owner: PSym, proc replaceIdentBySym(n: var PNode, s: PNode) =
toBind: var TIntSet): PNode =
var s: PSym
case n.kind 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 {.pure, final.} = object
c: PContext
toBind: TIntSet
owner: PSym
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: of nkIdent:
result = n result = n
let s = QualifiedLookUp(c, n, {}) let s = QualifiedLookUp(c.c, n, {})
if s != nil: if s != nil:
if s.owner == owner and s.kind == skParam: if s.owner == c.owner and s.kind == skParam:
result = newSymNode(s) result = newSymNode(s, n.info)
result.info = n.info of nkAccQuoted, nkSym: result = n
elif Contains(toBind, s.id): else:
result = symChoice(c, n, s) illFormedAst(n)
of nkEmpty, nkSym..nkNilLit: # atom
result = n result = n
proc isTemplParam(n: PNode): bool {.inline.} =
result = n.kind == nkSym and n.sym.kind == skParam and
n.sym.owner.kind == skTemplate
proc semTemplBody(c: var TemplCtx, n: PNode): PNode
proc openScope(c: var TemplCtx) = openScope(c.c.tab)
proc closeScope(c: var TemplCtx) = closeScope(c.c.tab)
proc semTemplBodyScope(c: var TemplCtx, n: PNode): PNode =
openScope(c)
result = semTemplBody(c, n)
closeScope(c)
proc newGenSym(kind: TSymKind, n: PNode, c: var TemplCtx): PSym =
result = newSym(kind, considerAcc(n), c.owner)
incl(result.flags, sfGenSym)
incl(result.flags, sfShadowed)
proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) =
# locals default to 'gensym':
if n.kind != nkPragmaExpr or symBinding(n.sons[1]) != spInject:
let ident = getIdentNode(c, n)
if not isTemplParam(ident):
let local = newGenSym(k, ident, c)
addPrelimDecl(c.c, local)
replaceIdentBySym(n, newSymNode(local, n.info))
else:
replaceIdentBySym(n, ident)
else:
n = semTemplBody(c, n)
proc semRoutineInTemplBody(c: var TemplCtx, n: PNode, k: TSymKind): PNode =
result = n
checkSonsLen(n, bodyPos + 1)
# routines default to 'inject':
if n.kind notin nkLambdaKinds and symBinding(n.sons[pragmasPos]) == spGenSym:
let ident = getIdentNode(c, n.sons[namePos])
if not isTemplParam(ident):
let s = newGenSym(k, ident, c)
addPrelimDecl(c.c, s)
n.sons[namePos] = newSymNode(s, n.sons[namePos].info)
else:
n.sons[namePos] = ident
else:
n.sons[namePos] = semTemplBody(c, n.sons[namePos])
openScope(c)
n.sons[genericParamsPos] = semTemplBody(c, n.sons[genericParamsPos])
n.sons[paramsPos] = semTemplBody(c, n.sons[paramsPos])
n.sons[pragmasPos] = semTemplBody(c, n.sons[pragmasPos])
n.sons[bodyPos] = semTemplBodyScope(c, n.sons[bodyPos])
closeScope(c)
proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
result = n
case n.kind
of nkIdent:
let s = QualifiedLookUp(c.c, n, {})
if s != nil:
if s.owner == c.owner and s.kind == skParam:
result = newSymNode(s, n.info)
elif Contains(c.toBind, s.id):
result = symChoice(c.c, n, s)
elif s.owner == c.owner:
InternalAssert sfGenSym in s.flags
incl(s.flags, sfUsed)
result = newSymNode(s, n.info)
of nkBind: of nkBind:
result = resolveTemplateParams(c, n.sons[0], owner, toBind) result = semTemplBody(c, n.sons[0])
of nkBindStmt: of nkBindStmt:
result = semBindStmt(c, n, toBind) result = semBindStmt(c.c, n, c.toBind)
of nkEmpty, nkSym..nkNilLit:
nil
of nkIfStmt:
for i in countup(0, sonsLen(n)-1):
n.sons[i] = semTemplBodyScope(c, n.sons[i])
of nkWhileStmt:
openScope(c)
for i in countup(0, sonsLen(n)-1):
n.sons[i] = semTemplBody(c, n.sons[i])
closeScope(c)
of nkCaseStmt:
openScope(c)
n.sons[0] = semTemplBody(c, n.sons[0])
for i in countup(1, sonsLen(n)-1):
var a = n.sons[i]
checkMinSonsLen(a, 1)
var L = sonsLen(a)
for j in countup(0, L-2):
a.sons[j] = semTemplBody(c, a.sons[j])
a.sons[L-1] = semTemplBodyScope(c, a.sons[L-1])
closeScope(c)
of nkForStmt, nkParForStmt:
var L = sonsLen(n)
openScope(c)
n.sons[L-2] = semTemplBody(c, n.sons[L-2])
for i in countup(0, L - 3):
addLocalDecl(c, n.sons[i], skForVar)
n.sons[L-1] = semTemplBody(c, n.sons[L-1])
closeScope(c)
of nkBlockStmt, nkBlockExpr, nkBlockType:
checkSonsLen(n, 2)
openScope(c)
if n.sons[0].kind != nkEmpty:
# labels are always 'gensym'ed:
let s = newGenSym(skLabel, n.sons[0], c)
addPrelimDecl(c.c, s)
n.sons[0] = newSymNode(s, n.sons[0].info)
n.sons[1] = semTemplBody(c, n.sons[1])
closeScope(c)
of nkTryStmt:
checkMinSonsLen(n, 2)
n.sons[0] = semTemplBodyScope(c, n.sons[0])
for i in countup(1, sonsLen(n)-1):
var a = n.sons[i]
checkMinSonsLen(a, 1)
var L = sonsLen(a)
for j in countup(0, L-2):
a.sons[j] = semTemplBody(c, a.sons[j])
a.sons[L-1] = semTemplBodyScope(c, a.sons[L-1])
of nkVarSection, nkLetSection:
let symKind = if n.kind == nkLetSection: skLet else: skVar
for i in countup(0, sonsLen(n) - 1):
var a = n.sons[i]
if a.kind == nkCommentStmt: continue
if (a.kind != nkIdentDefs) and (a.kind != nkVarTuple): IllFormedAst(a)
checkMinSonsLen(a, 3)
var L = sonsLen(a)
a.sons[L-2] = semTemplBody(c, a.sons[L-2])
a.sons[L-1] = semTemplBody(c, a.sons[L-1])
for j in countup(0, L-3):
addLocalDecl(c, a.sons[j], symKind)
of nkConstSection:
for i in countup(0, sonsLen(n) - 1):
var a = n.sons[i]
if a.kind == nkCommentStmt: continue
if (a.kind != nkConstDef): IllFormedAst(a)
checkSonsLen(a, 3)
addLocalDecl(c, a.sons[0], skConst)
a.sons[1] = semTemplBody(c, a.sons[1])
a.sons[2] = semTemplBody(c, a.sons[2])
of nkTypeSection:
for i in countup(0, sonsLen(n) - 1):
var a = n.sons[i]
if a.kind == nkCommentStmt: continue
if (a.kind != nkTypeDef): IllFormedAst(a)
checkSonsLen(a, 3)
addLocalDecl(c, a.sons[0], skType)
for i in countup(0, sonsLen(n) - 1):
var a = n.sons[i]
if a.kind == nkCommentStmt: continue
if (a.kind != nkTypeDef): IllFormedAst(a)
checkSonsLen(a, 3)
if a.sons[1].kind != nkEmpty:
openScope(c)
a.sons[1] = semTemplBody(c, a.sons[1])
a.sons[2] = semTemplBody(c, a.sons[2])
closeScope(c)
else:
a.sons[2] = semTemplBody(c, a.sons[2])
of nkProcDef, nkLambdaKinds:
result = semRoutineInTemplBody(c, n, skProc)
of nkMethodDef:
result = semRoutineInTemplBody(c, n, skMethod)
of nkIteratorDef:
result = semRoutineInTemplBody(c, n, skIterator)
of nkTemplateDef:
result = semRoutineInTemplBody(c, n, skTemplate)
of nkMacroDef:
result = semRoutineInTemplBody(c, n, skMacro)
of nkConverterDef:
result = semRoutineInTemplBody(c, n, skConverter)
else: else:
# dotExpr is ambiguous: note that we explicitely allow 'x.TemplateParam', # dotExpr is ambiguous: note that we explicitely allow 'x.TemplateParam',
# so we use the generic code for nkDotExpr too # so we use the generic code for nkDotExpr too
if n.kind == nkDotExpr or n.kind == nkAccQuoted: if n.kind == nkDotExpr or n.kind == nkAccQuoted:
let s = QualifiedLookUp(c, n, {}) let s = QualifiedLookUp(c.c, n, {})
if s != nil and Contains(toBind, s.id): if s != nil and Contains(c.toBind, s.id):
return symChoice(c, n, s) return symChoice(c.c, n, s)
result = n result = n
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
result.sons[i] = resolveTemplateParams(c, n.sons[i], owner, toBind) result.sons[i] = semTemplBody(c, n.sons[i])
proc transformToExpr(n: PNode): PNode = proc transformToExpr(n: PNode): PNode =
var realStmt: int var realStmt: int
@ -94,8 +304,8 @@ proc transformToExpr(n: PNode): PNode =
else: n.kind = nkStmtListExpr else: n.kind = nkStmtListExpr
of nkBlockStmt: of nkBlockStmt:
n.kind = nkBlockExpr n.kind = nkBlockExpr
#nkIfStmt: n.kind := nkIfExpr; // this is not correct! #nkIfStmt: n.kind = nkIfExpr // this is not correct!
else: else:
nil nil
proc semTemplateDef(c: PContext, n: PNode): PNode = proc semTemplateDef(c: PContext, n: PNode): PNode =
@ -108,7 +318,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
# check parameter list: # check parameter list:
pushOwner(s) pushOwner(s)
openScope(c.tab) openScope(c.tab)
n.sons[namePos] = newSymNode(s) n.sons[namePos] = newSymNode(s, n.sons[namePos].info)
if n.sons[pragmasPos].kind != nkEmpty: if n.sons[pragmasPos].kind != nkEmpty:
pragma(c, s, n.sons[pragmasPos], templatePragmas) pragma(c, s, n.sons[pragmasPos], templatePragmas)
# check that no generic parameters exist: # check that no generic parameters exist:
@ -126,8 +336,11 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
# use ``stmt`` as implicit result type # use ``stmt`` as implicit result type
s.typ.sons[0] = newTypeS(tyStmt, c) s.typ.sons[0] = newTypeS(tyStmt, c)
s.typ.n.sons[0] = newNodeIT(nkType, n.info, s.typ.sons[0]) s.typ.n.sons[0] = newNodeIT(nkType, n.info, s.typ.sons[0])
var toBind = initIntSet() var ctx: TemplCtx
n.sons[bodyPos] = resolveTemplateParams(c, n.sons[bodyPos], s, toBind) ctx.toBind = initIntSet()
ctx.c = c
ctx.owner = s
n.sons[bodyPos] = semTemplBody(ctx, n.sons[bodyPos])
if s.typ.sons[0].kind notin {tyStmt, tyTypeDesc}: if s.typ.sons[0].kind notin {tyStmt, tyTypeDesc}:
n.sons[bodyPos] = transformToExpr(n.sons[bodyPos]) n.sons[bodyPos] = transformToExpr(n.sons[bodyPos])
# only parameters are resolved, no type checking is performed # only parameters are resolved, no type checking is performed

View file

@ -78,7 +78,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
incl(e.flags, sfExported) # BUGFIX incl(e.flags, sfExported) # BUGFIX
StrTableAdd(c.module.tab, e) # BUGFIX StrTableAdd(c.module.tab, e) # BUGFIX
addSon(result.n, newSymNode(e)) addSon(result.n, newSymNode(e))
addDeclAt(c, e, c.tab.tos - 1) if sfGenSym notin e.flags: addDeclAt(c, e, c.tab.tos - 1)
inc(counter) inc(counter)
proc semSet(c: PContext, n: PNode, prev: PType): PType = proc semSet(c: PContext, n: PNode, prev: PType): PType =
@ -264,7 +264,7 @@ proc semTuple(c: PContext, n: PNode, prev: PType): PType =
if a.sons[length - 1].kind != nkEmpty: if a.sons[length - 1].kind != nkEmpty:
LocalError(a.sons[length - 1].info, errInitHereNotAllowed) LocalError(a.sons[length - 1].info, errInitHereNotAllowed)
for j in countup(0, length - 3): for j in countup(0, length - 3):
var field = newSymS(skField, a.sons[j], c) var field = newSymG(skField, a.sons[j], c)
field.typ = typ field.typ = typ
field.position = counter field.position = counter
inc(counter) inc(counter)
@ -279,7 +279,9 @@ proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
# identifier with visibility # identifier with visibility
if n.kind == nkPostfix: if n.kind == nkPostfix:
if sonsLen(n) == 2 and n.sons[0].kind == nkIdent: if sonsLen(n) == 2 and n.sons[0].kind == nkIdent:
result = newSymS(kind, n.sons[1], c) # for gensym'ed identifiers the identifier may already have been
# transformed to a symbol and we need to use that here:
result = newSymG(kind, n.sons[1], c)
var v = n.sons[0].ident var v = n.sons[0].ident
if sfExported in allowed and v.id == ord(wStar): if sfExported in allowed and v.id == ord(wStar):
incl(result.flags, sfExported) incl(result.flags, sfExported)
@ -288,7 +290,7 @@ proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
else: else:
illFormedAst(n) illFormedAst(n)
else: else:
result = newSymS(kind, n, c) result = newSymG(kind, n, c)
proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode, proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode,
allowed: TSymFlags): PSym = allowed: TSymFlags): PSym =
@ -469,6 +471,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var TIntSet, pos: var int,
typ = semTypeNode(c, n.sons[length-2], nil) typ = semTypeNode(c, n.sons[length-2], nil)
for i in countup(0, sonsLen(n)-3): for i in countup(0, sonsLen(n)-3):
var f = semIdentWithPragma(c, skField, n.sons[i], {sfExported}) var f = semIdentWithPragma(c, skField, n.sons[i], {sfExported})
suggestSym(n.sons[i], f)
f.typ = typ f.typ = typ
f.position = pos f.position = pos
if (rectype != nil) and ({sfImportc, sfExportc} * rectype.flags != {}) and if (rectype != nil) and ({sfImportc, sfExportc} * rectype.flags != {}) and
@ -548,9 +551,9 @@ proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) =
let nn = getSysSym"PNimrodNode" let nn = getSysSym"PNimrodNode"
var a = copySym(param) var a = copySym(param)
a.typ = nn.typ a.typ = nn.typ
addDecl(c, a) if sfGenSym notin a.flags: addDecl(c, a)
else: else:
addDecl(c, param) if sfGenSym notin param.flags: addDecl(c, param)
proc paramTypeClass(c: PContext, paramType: PType, procKind: TSymKind): proc paramTypeClass(c: PContext, paramType: PType, procKind: TSymKind):
tuple[typ: PType, id: PIdent] = tuple[typ: PType, id: PIdent] =
@ -672,7 +675,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
if skipTypes(typ, {tyGenericInst}).kind == tyEmpty: continue if skipTypes(typ, {tyGenericInst}).kind == tyEmpty: continue
for j in countup(0, length-3): for j in countup(0, length-3):
var arg = newSymS(skParam, a.sons[j], c) var arg = newSymG(skParam, a.sons[j], c)
var finalType = liftParamType(c, kind, genericParams, typ, arg.name.s, var finalType = liftParamType(c, kind, genericParams, typ, arg.name.s,
arg.info).skipIntLit arg.info).skipIntLit
arg.typ = finalType arg.typ = finalType
@ -711,7 +714,7 @@ proc semBlockType(c: PContext, n: PNode, prev: PType): PType =
Inc(c.p.nestedBlockCounter) Inc(c.p.nestedBlockCounter)
checkSonsLen(n, 2) checkSonsLen(n, 2)
openScope(c.tab) openScope(c.tab)
if n.sons[0].kind != nkEmpty: if n.sons[0].kind notin {nkEmpty, nkSym}:
addDecl(c, newSymS(skLabel, n.sons[0], c)) addDecl(c, newSymS(skLabel, n.sons[0], c))
result = semStmtListType(c, n.sons[1], prev) result = semStmtListType(c, n.sons[1], prev)
n.sons[1].typ = result n.sons[1].typ = result
@ -947,7 +950,7 @@ proc semGenericConstraints(c: PContext, n: PNode, result: PType) =
proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode = proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
result = copyNode(n) result = copyNode(n)
if n.kind != nkGenericParams: if n.kind != nkGenericParams:
InternalError(n.info, "semGenericParamList") illFormedAst(n)
return return
for i in countup(0, sonsLen(n)-1): for i in countup(0, sonsLen(n)-1):
var a = n.sons[i] var a = n.sons[i]
@ -965,27 +968,27 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
for j in countup(0, L-3): for j in countup(0, L-3):
var s: PSym var s: PSym
if typ == nil: if typ == nil:
s = newSymS(skType, a.sons[j], c) s = newSymG(skType, a.sons[j], c)
s.typ = newTypeS(tyGenericParam, c) s.typ = newTypeS(tyGenericParam, c)
else: else:
case typ.kind case typ.kind
of tyTypeDesc: of tyTypeDesc:
s = newSymS(skType, a.sons[j], c) s = newSymG(skType, a.sons[j], c)
s.typ = newTypeS(tyGenericParam, c) s.typ = newTypeS(tyGenericParam, c)
of tyExpr: of tyExpr:
#echo "GENERIC EXPR ", a.info.toFileLineCol #echo "GENERIC EXPR ", a.info.toFileLineCol
# not a type param, but an expression # not a type param, but an expression
# proc foo[x: expr](bar: int) what is this? # proc foo[x: expr](bar: int) what is this?
s = newSymS(skGenericParam, a.sons[j], c) s = newSymG(skGenericParam, a.sons[j], c)
s.typ = typ s.typ = typ
else: else:
# This handles cases like proc foo[t: tuple] # This handles cases like proc foo[t: tuple]
# XXX: we want to turn that into a type class # XXX: we want to turn that into a type class
s = newSymS(skType, a.sons[j], c) s = newSymG(skType, a.sons[j], c)
s.typ = typ s.typ = typ
if def.kind != nkEmpty: s.ast = def if def.kind != nkEmpty: s.ast = def
s.typ.sym = s s.typ.sym = s
if father != nil: addSonSkipIntLit(father, s.typ) if father != nil: addSonSkipIntLit(father, s.typ)
s.position = i s.position = i
addSon(result, newSymNode(s)) addSon(result, newSymNode(s))
addDecl(c, s) if sfGenSym notin s.flags: addDecl(c, s)

View file

@ -54,9 +54,13 @@ proc suggestField(c: PContext, s: PSym, outputs: var int) =
OutWriteln(SymToStr(s, isLocal=true, sectionSuggest)) OutWriteln(SymToStr(s, isLocal=true, sectionSuggest))
inc outputs inc outputs
template wholeSymTab(cond, section: expr) {.immediate.} = when not defined(nimhygiene):
for i in countdown(c.tab.tos-1, 0): {.pragma: inject.}
for it in items(c.tab.stack[i]):
template wholeSymTab(cond, section: expr) {.immediate.} =
for i in countdown(c.tab.tos-1, 0):
for item in items(c.tab.stack[i]):
let it {.inject.} = item
if cond: if cond:
OutWriteln(SymToStr(it, isLocal = i > ModuleTablePos, section)) OutWriteln(SymToStr(it, isLocal = i > ModuleTablePos, section))
inc outputs inc outputs

View file

@ -380,6 +380,13 @@ proc mutateType(t: PType, iter: TTypeMutator, closure: PObject): PType =
var marker = InitIntSet() var marker = InitIntSet()
result = mutateTypeAux(marker, t, iter, closure) result = mutateTypeAux(marker, t, iter, closure)
proc ValueToString(a: PNode): string =
case a.kind
of nkCharLit..nkUInt64Lit: result = $(a.intVal)
of nkFloatLit..nkFloat128Lit: result = $(a.floatVal)
of nkStrLit..nkTripleStrLit: result = a.strVal
else: result = "<invalid value>"
proc rangeToStr(n: PNode): string = proc rangeToStr(n: PNode): string =
assert(n.kind == nkRange) assert(n.kind == nkRange)
result = ValueToString(n.sons[0]) & ".." & ValueToString(n.sons[1]) result = ValueToString(n.sons[0]) & ".." & ValueToString(n.sons[1])

View file

@ -60,7 +60,7 @@ type
wFieldChecks, wFieldChecks,
wWatchPoint, wSubsChar, wWatchPoint, wSubsChar,
wAcyclic, wShallow, wUnroll, wLinearScanEnd, wAcyclic, wShallow, wUnroll, wLinearScanEnd,
wWrite, wPutEnv, wPrependEnv, wAppendEnv, wThreadVar, wEmit, wNoStackFrame, wWrite, wGensym, wInject, wInheritable, wThreadVar, wEmit, wNoStackFrame,
wImplicitStatic, wGlobal, wHoist wImplicitStatic, wGlobal, wHoist
wAuto, wBool, wCatch, wChar, wClass, wAuto, wBool, wCatch, wChar, wClass,
@ -138,7 +138,7 @@ const
"passc", "passl", "borrow", "discardable", "fieldchecks", "passc", "passl", "borrow", "discardable", "fieldchecks",
"watchpoint", "watchpoint",
"subschar", "acyclic", "shallow", "unroll", "linearscanend", "subschar", "acyclic", "shallow", "unroll", "linearscanend",
"write", "putenv", "prependenv", "appendenv", "threadvar", "emit", "write", "gensym", "inject", "inheritable", "threadvar", "emit",
"nostackframe", "implicitstatic", "global", "hoist", "nostackframe", "implicitstatic", "global", "hoist",
"auto", "bool", "catch", "char", "class", "auto", "bool", "catch", "char", "class",

View file

@ -908,7 +908,7 @@ converts the list of arguments to an array implicitely:
write(f, "\n") write(f, "\n")
myWriteln(stdout, "abc", "def", "xyz") myWriteln(stdout, "abc", "def", "xyz")
# is transformed by the compiler to: # is transformed to:
myWriteln(stdout, ["abc", "def", "xyz"]) myWriteln(stdout, ["abc", "def", "xyz"])
This transformation is only done if the varargs parameter is the This transformation is only done if the varargs parameter is the
@ -922,7 +922,7 @@ type conversions in this context:
write(f, "\n") write(f, "\n")
myWriteln(stdout, 123, "abc", 4.0) myWriteln(stdout, 123, "abc", 4.0)
# is transformed by the compiler to: # is transformed to:
myWriteln(stdout, [$123, $"def", $4.0]) myWriteln(stdout, [$123, $"def", $4.0])
In this example ``$`` is applied to any argument that is passed to the In this example ``$`` is applied to any argument that is passed to the

View file

@ -218,6 +218,9 @@ proc findAll*(s: string, pattern: TRegEx, start = 0): seq[string] =
## If it does not match, @[] is returned. ## If it does not match, @[] is returned.
accumulateResult(findAll(s, pattern, start)) accumulateResult(findAll(s, pattern, start))
when not defined(nimhygiene):
{.pragma: inject.}
template `=~` *(s: string, pattern: TRegEx): expr = template `=~` *(s: string, pattern: TRegEx): expr =
## This calls ``match`` with an implicit declared ``matches`` array that ## This calls ``match`` with an implicit declared ``matches`` array that
## can be used in the scope of the ``=~`` call: ## can be used in the scope of the ``=~`` call:
@ -237,7 +240,7 @@ template `=~` *(s: string, pattern: TRegEx): expr =
## echo("syntax error") ## echo("syntax error")
## ##
when not definedInScope(matches): when not definedInScope(matches):
var matches: array[0..re.maxSubPatterns-1, string] var matches {.inject.}: array[0..re.maxSubPatterns-1, string]
match(s, pattern, matches) match(s, pattern, matches)
# ------------------------- more string handling ------------------------------ # ------------------------- more string handling ------------------------------

View file

@ -845,6 +845,9 @@ proc findAll*(s: string, pattern: TPeg, start = 0): seq[string] {.
## returns all matching *substrings* of `s` that match `pattern`. ## returns all matching *substrings* of `s` that match `pattern`.
## If it does not match, @[] is returned. ## If it does not match, @[] is returned.
accumulateResult(findAll(s, pattern, start)) accumulateResult(findAll(s, pattern, start))
when not defined(nimhygiene):
{.pragma: inject.}
template `=~`*(s: string, pattern: TPeg): bool = template `=~`*(s: string, pattern: TPeg): bool =
## This calls ``match`` with an implicit declared ``matches`` array that ## This calls ``match`` with an implicit declared ``matches`` array that
@ -865,7 +868,7 @@ template `=~`*(s: string, pattern: TPeg): bool =
## echo("syntax error") ## echo("syntax error")
## ##
when not definedInScope(matches): when not definedInScope(matches):
var matches: array[0..pegs.maxSubpatterns-1, string] var matches {.inject.}: array[0..pegs.maxSubpatterns-1, string]
match(s, pattern, matches) match(s, pattern, matches)
# ------------------------- more string handling ------------------------------ # ------------------------- more string handling ------------------------------

View file

@ -62,12 +62,15 @@ proc extractSpec(filename: string): string =
#echo "warning: file does not contain spec: " & filename #echo "warning: file does not contain spec: " & filename
result = "" result = ""
when not defined(nimhygiene):
{.pragma: inject.}
template parseSpecAux(fillResult: stmt) = template parseSpecAux(fillResult: stmt) =
var ss = newStringStream(extractSpec(filename)) var ss = newStringStream(extractSpec(filename))
var p: TCfgParser var p {.inject.}: TCfgParser
open(p, ss, filename, 1) open(p, ss, filename, 1)
while true: while true:
var e = next(p) var e {.inject.} = next(p)
case e.kind case e.kind
of cfgEof: break of cfgEof: break
of cfgSectionStart, cfgOption, cfgError: of cfgSectionStart, cfgOption, cfgError:

View file

@ -1,16 +1,19 @@
version 0.9.0 version 0.9.0
============= =============
- make templates hygienic by default: 'gensym', 'inject' pragmas
- make 'bind' default for templates and introduce 'mixin'
- use ``\`` for comment continuations
- ``final`` should be the default for objects
- implement "closure tuple consists of a single 'ref'" optimization - implement "closure tuple consists of a single 'ref'" optimization
- implement for loop transformation for first class iterators - implement for loop transformation for first class iterators
- make templates hygienic by default: 'gensym', 'inject' pragmas
- implicit deref for parameter matching - implicit deref for parameter matching
- ``final`` should be the default for objects
- optimize genericAssign in the code generator - optimize genericAssign in the code generator
- the lookup rules for generics really are too permissive - the lookup rules for generics really are too permissive
- fix remaining closure bugs: - fix remaining closure bugs:
- fix evals.nim with closures - test evals.nim with closures
- what about macros with closures?
Bugs Bugs