mirror of
https://github.com/nim-lang/Nim.git
synced 2026-06-06 11:54:11 +00:00
first steps to make templates hygienic
This commit is contained in:
@@ -234,7 +234,7 @@ type
|
||||
sfDiscriminant, # field is a discriminant in a record/object
|
||||
sfDeprecated, # symbol is deprecated
|
||||
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
|
||||
# variable is a thread variable
|
||||
sfCompileTime, # proc can be evaluated at compile time
|
||||
@@ -247,7 +247,7 @@ type
|
||||
# language; for interfacing with Objective C
|
||||
sfDiscardable # returned value may be discarded implicitely
|
||||
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]
|
||||
|
||||
@@ -264,10 +264,7 @@ const
|
||||
# symbol name that was generated by the compiler
|
||||
# the compiler will avoid printing such names
|
||||
# in user messages.
|
||||
|
||||
sfShadowed* = sfInnerProc
|
||||
# a variable that was shadowed in some inner scope
|
||||
|
||||
|
||||
sfHoist* = sfVolatile ## proc return value can be hoisted
|
||||
|
||||
const
|
||||
@@ -785,15 +782,6 @@ const # for all kind of hash tables:
|
||||
GrowthFactor* = 2 # 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) =
|
||||
dest.counter = src.counter
|
||||
if isNil(src.data): return
|
||||
|
||||
@@ -166,7 +166,9 @@ proc SameValue*(a, b: PNode): bool =
|
||||
of nkStrLit..nkTripleStrLit:
|
||||
if b.kind in {nkStrLit..nkTripleStrLit}: result = a.strVal == b.strVal
|
||||
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 =
|
||||
# a <= b?
|
||||
@@ -178,7 +180,10 @@ proc leValue*(a, b: PNode): bool =
|
||||
if b.kind in {nkFloatLit..nkFloat64Lit}: result = a.floatVal <= b.floatVal
|
||||
of nkStrLit..nkTripleStrLit:
|
||||
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 =
|
||||
result = nil
|
||||
|
||||
@@ -224,13 +224,16 @@ proc processMergeInfo(L: var TBaseLexer, m: BModule) =
|
||||
of "labels": m.labels = decodeVInt(L.buf, L.bufpos)
|
||||
of "hasframe": m.FrameDeclared = decodeVInt(L.buf, L.bufpos) != 0
|
||||
else: InternalError("ccgmerge: unkown key: " & k)
|
||||
|
||||
when not defined(nimhygiene):
|
||||
{.pragma: inject.}
|
||||
|
||||
template withCFile(cfilename: string, body: stmt) =
|
||||
var s = LLStreamOpen(cfilename, fmRead)
|
||||
if s == nil: return
|
||||
var L: TBaseLexer
|
||||
var L {.inject.}: TBaseLexer
|
||||
openBaseLexer(L, s)
|
||||
var k = newStringOfCap("NIM_merge_FORWARD_TYPES".len)
|
||||
var k {.inject.} = newStringOfCap("NIM_merge_FORWARD_TYPES".len)
|
||||
while true:
|
||||
skipUntilCmd(L)
|
||||
if ^L.bufpos == '\0': break
|
||||
|
||||
@@ -55,6 +55,8 @@ proc countDefinedSymbols*(): int =
|
||||
proc InitDefines*() =
|
||||
initStrTable(gSymbols)
|
||||
DefineSymbol("nimrod") # 'nimrod' is always defined
|
||||
# for bootstrapping purposes and old code:
|
||||
DefineSymbol("nimhygiene")
|
||||
|
||||
# add platform specific symbols:
|
||||
case targetCPU
|
||||
|
||||
@@ -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, idgen, osproc, streams
|
||||
parser, ropes, rodread, idgen, osproc, streams, evaltempl
|
||||
|
||||
type
|
||||
PStackFrame* = ref TStackFrame
|
||||
@@ -850,56 +850,6 @@ proc evalParseStmt(c: PEvalContext, n: PNode): PNode =
|
||||
result = parseString(code.getStrValue, code.info.toFilename,
|
||||
code.stringStartingLine)
|
||||
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 =
|
||||
## XXX: This should be pretty much guaranteed to be true
|
||||
@@ -964,7 +914,11 @@ proc evalExpandToAst(c: PEvalContext, original: PNode): PNode =
|
||||
|
||||
case expandedSym.kind
|
||||
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:
|
||||
# At this point macroCall.sons[0] is nkSym node.
|
||||
# To be completely compatible with normal macro invocation,
|
||||
|
||||
83
compiler/evaltempl.nim
Normal file
83
compiler/evaltempl.nim
Normal 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)
|
||||
@@ -471,7 +471,8 @@ proc generateClosureCreation(o: POuterContext, scope: PEnv): PNode =
|
||||
# add assignment statements:
|
||||
for local in scope.capturedVars:
|
||||
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``
|
||||
result.add(newAsgnStmt(fieldAccess, newSymNode(local)))
|
||||
IdNodeTablePut(o.localsToAccess, local, fieldAccess)
|
||||
@@ -515,9 +516,9 @@ proc transformOuterProc(o: POuterContext, n: PNode): PNode =
|
||||
scope.sons[0] = generateClosureCreation(o, env)
|
||||
|
||||
# change 'local' to 'closure.local', unless it's a 'byCopy' variable:
|
||||
if sfByCopy notin local.flags:
|
||||
result = IdNodeTableGet(o.localsToAccess, local)
|
||||
assert result != nil, "cannot find: " & local.name.s
|
||||
# if sfByCopy notin local.flags:
|
||||
result = IdNodeTableGet(o.localsToAccess, local)
|
||||
assert result != nil, "cannot find: " & local.name.s
|
||||
# else it is captured by copy and this means that 'outer' should continue
|
||||
# to access the local as a local.
|
||||
of nkLambdaKinds:
|
||||
|
||||
@@ -111,7 +111,7 @@ proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest) =
|
||||
proc parseDirective(L: var TLexer, tok: var TToken) =
|
||||
ppGetTok(L, tok) # skip @
|
||||
case whichKeyword(tok.ident)
|
||||
of wIf:
|
||||
of wIf:
|
||||
setlen(condStack, len(condStack) + 1)
|
||||
var res = EvalppIf(L, tok)
|
||||
condStack[high(condStack)] = res
|
||||
@@ -123,25 +123,27 @@ proc parseDirective(L: var TLexer, tok: var TToken) =
|
||||
ppGetTok(L, tok)
|
||||
msgs.MsgWriteln(tokToStr(tok))
|
||||
ppGetTok(L, tok)
|
||||
of wPutEnv:
|
||||
ppGetTok(L, tok)
|
||||
var key = tokToStr(tok)
|
||||
ppGetTok(L, tok)
|
||||
os.putEnv(key, tokToStr(tok))
|
||||
ppGetTok(L, tok)
|
||||
of wPrependEnv:
|
||||
ppGetTok(L, tok)
|
||||
var key = tokToStr(tok)
|
||||
ppGetTok(L, tok)
|
||||
os.putEnv(key, tokToStr(tok) & os.getenv(key))
|
||||
ppGetTok(L, tok)
|
||||
of wAppendenv:
|
||||
ppGetTok(L, tok)
|
||||
var key = tokToStr(tok)
|
||||
ppGetTok(L, tok)
|
||||
os.putEnv(key, os.getenv(key) & tokToStr(tok))
|
||||
ppGetTok(L, tok)
|
||||
else: lexMessage(L, errInvalidDirectiveX, tokToStr(tok))
|
||||
else:
|
||||
case tok.ident.s.normalize
|
||||
of "putenv":
|
||||
ppGetTok(L, tok)
|
||||
var key = tokToStr(tok)
|
||||
ppGetTok(L, tok)
|
||||
os.putEnv(key, tokToStr(tok))
|
||||
ppGetTok(L, tok)
|
||||
of "prependenv":
|
||||
ppGetTok(L, tok)
|
||||
var key = tokToStr(tok)
|
||||
ppGetTok(L, tok)
|
||||
os.putEnv(key, tokToStr(tok) & os.getenv(key))
|
||||
ppGetTok(L, tok)
|
||||
of "appendenv":
|
||||
ppGetTok(L, tok)
|
||||
var key = tokToStr(tok)
|
||||
ppGetTok(L, 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) =
|
||||
ppGetTok(L, tok)
|
||||
|
||||
@@ -23,16 +23,17 @@ const
|
||||
wMagic, wNosideEffect, wSideEffect, wNoreturn, wDynLib, wHeader,
|
||||
wCompilerProc, wProcVar, wDeprecated, wVarargs, wCompileTime, wMerge,
|
||||
wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC,
|
||||
wNoStackFrame, wError, wDiscardable, wNoInit, wDestructor, wHoist}
|
||||
wNoStackFrame, wError, wDiscardable, wNoInit, wDestructor, wHoist,
|
||||
wGenSym, wInject}
|
||||
converterPragmas* = procPragmas
|
||||
methodPragmas* = procPragmas
|
||||
templatePragmas* = {wImmediate, wDeprecated, wError}
|
||||
templatePragmas* = {wImmediate, wDeprecated, wError, wGenSym, wInject}
|
||||
macroPragmas* = {FirstCallConv..LastCallConv, wImmediate, wImportc, wExportc,
|
||||
wNodecl, wMagic, wNosideEffect, wCompilerProc, wDeprecated, wExtern,
|
||||
wImportcpp, wImportobjc, wError, wDiscardable}
|
||||
wImportcpp, wImportobjc, wError, wDiscardable, wGenSym, wInject}
|
||||
iteratorPragmas* = {FirstCallConv..LastCallConv, wNosideEffect, wSideEffect,
|
||||
wImportc, wExportc, wNodecl, wMagic, wDeprecated, wBorrow, wExtern,
|
||||
wImportcpp, wImportobjc, wError, wDiscardable}
|
||||
wImportcpp, wImportobjc, wError, wDiscardable, wGenSym, wInject}
|
||||
exprPragmas* = {wLine}
|
||||
stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangechecks,
|
||||
wBoundchecks, wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints,
|
||||
@@ -46,14 +47,16 @@ const
|
||||
wDeprecated, wExtern, wThread, wImportcpp, wImportobjc, wNoStackFrame}
|
||||
typePragmas* = {wImportc, wExportc, wDeprecated, wMagic, wAcyclic, wNodecl,
|
||||
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,
|
||||
wImportcpp, wImportobjc, wError}
|
||||
varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl,
|
||||
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,
|
||||
wExtern, wImportcpp, wImportobjc, wError}
|
||||
wExtern, wImportcpp, wImportobjc, wError, wGenSym, wInject}
|
||||
letPragmas* = varPragmas
|
||||
procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNosideEffect,
|
||||
wThread}
|
||||
@@ -655,9 +658,13 @@ proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) =
|
||||
incl(sym.typ.flags, tfByRef)
|
||||
of wByCopy:
|
||||
noVal(it)
|
||||
if sym.kind != skType: incl(sym.flags, sfByCopy)
|
||||
elif sym.typ == nil: invalidPragma(it)
|
||||
if sym.kind != skType or sym.typ == nil: invalidPragma(it)
|
||||
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)
|
||||
else: invalidPragma(it)
|
||||
else: invalidPragma(it)
|
||||
|
||||
@@ -14,7 +14,8 @@ import
|
||||
wordrecg, ropes, msgs, os, condsyms, idents, renderer, types, platform, math,
|
||||
magicsys, parser, nversion, nimsets, semfold, importer,
|
||||
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
|
||||
# implementation
|
||||
@@ -56,7 +57,17 @@ proc isTopLevel(c: PContext): bool {.inline.} =
|
||||
proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym =
|
||||
result = newSym(kind, considerAcc(n), getCurrOwner())
|
||||
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,
|
||||
allowed: TSymFlags): PSym
|
||||
# identifier with visability
|
||||
|
||||
@@ -24,7 +24,7 @@ proc restoreOldStyleType(n: PNode) =
|
||||
proc semTemplateExpr(c: PContext, n: PNode, s: PSym, semCheck = true): PNode =
|
||||
markUsed(n, s)
|
||||
pushInfoContext(n.info)
|
||||
result = evalTemplate(n, s)
|
||||
result = evalTemplate(n, s, getCurrOwner())
|
||||
if semCheck: result = semAfterMacroCall(c, result, s)
|
||||
popInfoContext()
|
||||
|
||||
@@ -1372,7 +1372,9 @@ proc semBlockExpr(c: PContext, n: PNode): PNode =
|
||||
Inc(c.p.nestedBlockCounter)
|
||||
checkSonsLen(n, 2)
|
||||
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.typ = n.sons[1].typ
|
||||
closeScope(c.tab)
|
||||
|
||||
@@ -17,11 +17,20 @@
|
||||
|
||||
# included from sem.nim
|
||||
|
||||
type
|
||||
TSemGenericFlag = enum
|
||||
type
|
||||
TSemGenericFlag = enum
|
||||
withinBind, withinTypeDesc
|
||||
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,
|
||||
toBind: var TIntSet): PNode
|
||||
proc semGenericStmtScope(c: PContext, n: PNode,
|
||||
@@ -54,15 +63,6 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym): PNode =
|
||||
result = n
|
||||
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,
|
||||
flags: TSemGenericFlags, toBind: var TIntSet): PNode =
|
||||
result = n
|
||||
|
||||
@@ -91,10 +91,11 @@ proc semBlock(c: PContext, n: PNode): PNode =
|
||||
Inc(c.p.nestedBlockCounter)
|
||||
checkSonsLen(n, 2)
|
||||
openScope(c.tab) # BUGFIX: label is in the scope of block!
|
||||
if n.sons[0].kind != nkEmpty:
|
||||
var labl = newSymS(skLabel, n.sons[0], c)
|
||||
addDecl(c, labl)
|
||||
n.sons[0] = newSymNode(labl)
|
||||
if n.sons[0].kind != nkEmpty:
|
||||
var labl = newSymG(skLabel, n.sons[0], c)
|
||||
if sfGenSym notin labl.flags:
|
||||
addDecl(c, labl)
|
||||
n.sons[0] = newSymNode(labl, n.sons[0].info)
|
||||
suggestSym(n.sons[0], labl)
|
||||
n.sons[1] = semStmt(c, n.sons[1])
|
||||
closeScope(c.tab)
|
||||
@@ -284,7 +285,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
||||
Message(a.info, warnEachIdentIsTuple)
|
||||
for j in countup(0, length-3):
|
||||
var v = semIdentDef(c, a.sons[j], symkind)
|
||||
addInterfaceDecl(c, v)
|
||||
if sfGenSym notin v.flags: addInterfaceDecl(c, v)
|
||||
when oKeepVariableNames:
|
||||
if c.InUnrolledContext > 0: v.flags.incl(sfShadowed)
|
||||
else:
|
||||
@@ -332,7 +333,7 @@ proc semConst(c: PContext, n: PNode): PNode =
|
||||
continue
|
||||
v.typ = typ
|
||||
v.ast = def # no need to copy
|
||||
addInterfaceDecl(c, v)
|
||||
if sfGenSym notin v.flags: addInterfaceDecl(c, v)
|
||||
var b = newNodeI(nkConstDef, a.info)
|
||||
addSon(b, newSymNode(v))
|
||||
addSon(b, ast.emptyNode) # no type description
|
||||
@@ -422,25 +423,25 @@ proc semForVars(c: PContext, n: PNode): PNode =
|
||||
# and thus no tuple unpacking:
|
||||
if iter.kind != tyTuple or 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)
|
||||
# BUGFIX: don't use `iter` here as that would strip away
|
||||
# the ``tyGenericInst``! See ``tests/compile/tgeneric.nim``
|
||||
# for an example:
|
||||
v.typ = n.sons[length-2].typ
|
||||
n.sons[0] = newSymNode(v)
|
||||
addDecl(c, v)
|
||||
if sfGenSym notin v.flags: addDecl(c, v)
|
||||
else:
|
||||
LocalError(n.info, errWrongNumberOfVariables)
|
||||
elif length-2 != sonsLen(iter):
|
||||
LocalError(n.info, errWrongNumberOfVariables)
|
||||
else:
|
||||
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)
|
||||
v.typ = iter.sons[i]
|
||||
n.sons[i] = newSymNode(v)
|
||||
addDecl(c, v)
|
||||
if sfGenSym notin v.flags: addDecl(c, v)
|
||||
Inc(c.p.nestedLoopCounter)
|
||||
n.sons[length-1] = SemStmt(c, n.sons[length-1])
|
||||
Dec(c.p.nestedLoopCounter)
|
||||
@@ -539,7 +540,7 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) =
|
||||
if a.sons[0].kind == nkPragmaExpr:
|
||||
pragma(c, s, a.sons[0].sons[1], typePragmas)
|
||||
# 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)
|
||||
|
||||
proc typeSectionRightSidePass(c: PContext, n: PNode) =
|
||||
@@ -780,7 +781,8 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
||||
s.typ.callConv = lastOptionEntry(c).defaultCC
|
||||
# add it here, so that recursive procs are possible:
|
||||
# -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)
|
||||
else:
|
||||
addInterfaceDeclAt(c, s, c.tab.tos - 2)
|
||||
|
||||
@@ -9,6 +9,37 @@
|
||||
|
||||
# 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 =
|
||||
var
|
||||
a: PSym
|
||||
@@ -20,17 +51,16 @@ proc symChoice(c: PContext, n: PNode, s: PSym): PNode =
|
||||
inc(i)
|
||||
if i > 1: break
|
||||
if i <= 1:
|
||||
result = newSymNode(s)
|
||||
result.info = n.info
|
||||
result = newSymNode(s, n.info)
|
||||
markUsed(n, s)
|
||||
else:
|
||||
else:
|
||||
# semantic checking requires a type; ``fitNode`` deals with it
|
||||
# appropriately
|
||||
result = newNodeIT(nkSymChoice, n.info, newTypeS(tyNone, c))
|
||||
a = initOverloadIter(o, c, n)
|
||||
while a != nil:
|
||||
incl(a.flags, sfUsed)
|
||||
addSon(result, newSymNode(a))
|
||||
addSon(result, newSymNode(a, n.info))
|
||||
a = nextOverloadIter(o, c, n)
|
||||
|
||||
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:
|
||||
illFormedAst(a)
|
||||
result = newNodeI(nkEmpty, n.info)
|
||||
|
||||
proc resolveTemplateParams(c: PContext, n: PNode, owner: PSym,
|
||||
toBind: var TIntSet): PNode =
|
||||
var s: PSym
|
||||
|
||||
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 {.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:
|
||||
result = n
|
||||
let s = QualifiedLookUp(c, n, {})
|
||||
let s = QualifiedLookUp(c.c, n, {})
|
||||
if s != nil:
|
||||
if s.owner == owner and s.kind == skParam:
|
||||
result = newSymNode(s)
|
||||
result.info = n.info
|
||||
elif Contains(toBind, s.id):
|
||||
result = symChoice(c, n, s)
|
||||
|
||||
of nkEmpty, nkSym..nkNilLit: # atom
|
||||
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(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:
|
||||
result = resolveTemplateParams(c, n.sons[0], owner, toBind)
|
||||
result = semTemplBody(c, n.sons[0])
|
||||
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:
|
||||
# dotExpr is ambiguous: note that we explicitely allow 'x.TemplateParam',
|
||||
# so we use the generic code for nkDotExpr too
|
||||
if n.kind == nkDotExpr or n.kind == nkAccQuoted:
|
||||
let s = QualifiedLookUp(c, n, {})
|
||||
if s != nil and Contains(toBind, s.id):
|
||||
return symChoice(c, n, s)
|
||||
let s = QualifiedLookUp(c.c, n, {})
|
||||
if s != nil and Contains(c.toBind, s.id):
|
||||
return symChoice(c.c, n, s)
|
||||
result = n
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
result.sons[i] = resolveTemplateParams(c, n.sons[i], owner, toBind)
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
result.sons[i] = semTemplBody(c, n.sons[i])
|
||||
|
||||
proc transformToExpr(n: PNode): PNode =
|
||||
var realStmt: int
|
||||
@@ -94,8 +304,8 @@ proc transformToExpr(n: PNode): PNode =
|
||||
else: n.kind = nkStmtListExpr
|
||||
of nkBlockStmt:
|
||||
n.kind = nkBlockExpr
|
||||
#nkIfStmt: n.kind := nkIfExpr; // this is not correct!
|
||||
else:
|
||||
#nkIfStmt: n.kind = nkIfExpr // this is not correct!
|
||||
else:
|
||||
nil
|
||||
|
||||
proc semTemplateDef(c: PContext, n: PNode): PNode =
|
||||
@@ -108,7 +318,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
|
||||
# check parameter list:
|
||||
pushOwner(s)
|
||||
openScope(c.tab)
|
||||
n.sons[namePos] = newSymNode(s)
|
||||
n.sons[namePos] = newSymNode(s, n.sons[namePos].info)
|
||||
if n.sons[pragmasPos].kind != nkEmpty:
|
||||
pragma(c, s, n.sons[pragmasPos], templatePragmas)
|
||||
# check that no generic parameters exist:
|
||||
@@ -126,8 +336,11 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
|
||||
# use ``stmt`` as implicit result type
|
||||
s.typ.sons[0] = newTypeS(tyStmt, c)
|
||||
s.typ.n.sons[0] = newNodeIT(nkType, n.info, s.typ.sons[0])
|
||||
var toBind = initIntSet()
|
||||
n.sons[bodyPos] = resolveTemplateParams(c, n.sons[bodyPos], s, toBind)
|
||||
var ctx: TemplCtx
|
||||
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}:
|
||||
n.sons[bodyPos] = transformToExpr(n.sons[bodyPos])
|
||||
# only parameters are resolved, no type checking is performed
|
||||
|
||||
@@ -78,7 +78,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
|
||||
incl(e.flags, sfExported) # BUGFIX
|
||||
StrTableAdd(c.module.tab, e) # BUGFIX
|
||||
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)
|
||||
|
||||
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:
|
||||
LocalError(a.sons[length - 1].info, errInitHereNotAllowed)
|
||||
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.position = counter
|
||||
inc(counter)
|
||||
@@ -279,7 +279,9 @@ proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
|
||||
# identifier with visibility
|
||||
if n.kind == nkPostfix:
|
||||
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
|
||||
if sfExported in allowed and v.id == ord(wStar):
|
||||
incl(result.flags, sfExported)
|
||||
@@ -288,7 +290,7 @@ proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
|
||||
else:
|
||||
illFormedAst(n)
|
||||
else:
|
||||
result = newSymS(kind, n, c)
|
||||
result = newSymG(kind, n, c)
|
||||
|
||||
proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode,
|
||||
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)
|
||||
for i in countup(0, sonsLen(n)-3):
|
||||
var f = semIdentWithPragma(c, skField, n.sons[i], {sfExported})
|
||||
suggestSym(n.sons[i], f)
|
||||
f.typ = typ
|
||||
f.position = pos
|
||||
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"
|
||||
var a = copySym(param)
|
||||
a.typ = nn.typ
|
||||
addDecl(c, a)
|
||||
if sfGenSym notin a.flags: addDecl(c, a)
|
||||
else:
|
||||
addDecl(c, param)
|
||||
if sfGenSym notin param.flags: addDecl(c, param)
|
||||
|
||||
proc paramTypeClass(c: PContext, paramType: PType, procKind: TSymKind):
|
||||
tuple[typ: PType, id: PIdent] =
|
||||
@@ -672,7 +675,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||
|
||||
if skipTypes(typ, {tyGenericInst}).kind == tyEmpty: continue
|
||||
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,
|
||||
arg.info).skipIntLit
|
||||
arg.typ = finalType
|
||||
@@ -711,7 +714,7 @@ proc semBlockType(c: PContext, n: PNode, prev: PType): PType =
|
||||
Inc(c.p.nestedBlockCounter)
|
||||
checkSonsLen(n, 2)
|
||||
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))
|
||||
result = semStmtListType(c, n.sons[1], prev)
|
||||
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 =
|
||||
result = copyNode(n)
|
||||
if n.kind != nkGenericParams:
|
||||
InternalError(n.info, "semGenericParamList")
|
||||
illFormedAst(n)
|
||||
return
|
||||
for i in countup(0, sonsLen(n)-1):
|
||||
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):
|
||||
var s: PSym
|
||||
if typ == nil:
|
||||
s = newSymS(skType, a.sons[j], c)
|
||||
s = newSymG(skType, a.sons[j], c)
|
||||
s.typ = newTypeS(tyGenericParam, c)
|
||||
else:
|
||||
case typ.kind
|
||||
of tyTypeDesc:
|
||||
s = newSymS(skType, a.sons[j], c)
|
||||
s = newSymG(skType, a.sons[j], c)
|
||||
s.typ = newTypeS(tyGenericParam, c)
|
||||
of tyExpr:
|
||||
#echo "GENERIC EXPR ", a.info.toFileLineCol
|
||||
# not a type param, but an expression
|
||||
# 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
|
||||
else:
|
||||
# This handles cases like proc foo[t: tuple]
|
||||
# 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
|
||||
if def.kind != nkEmpty: s.ast = def
|
||||
s.typ.sym = s
|
||||
if father != nil: addSonSkipIntLit(father, s.typ)
|
||||
s.position = i
|
||||
addSon(result, newSymNode(s))
|
||||
addDecl(c, s)
|
||||
if sfGenSym notin s.flags: addDecl(c, s)
|
||||
|
||||
@@ -54,9 +54,13 @@ proc suggestField(c: PContext, s: PSym, outputs: var int) =
|
||||
OutWriteln(SymToStr(s, isLocal=true, sectionSuggest))
|
||||
inc outputs
|
||||
|
||||
template wholeSymTab(cond, section: expr) {.immediate.} =
|
||||
for i in countdown(c.tab.tos-1, 0):
|
||||
for it in items(c.tab.stack[i]):
|
||||
when not defined(nimhygiene):
|
||||
{.pragma: inject.}
|
||||
|
||||
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:
|
||||
OutWriteln(SymToStr(it, isLocal = i > ModuleTablePos, section))
|
||||
inc outputs
|
||||
|
||||
@@ -380,6 +380,13 @@ proc mutateType(t: PType, iter: TTypeMutator, closure: PObject): PType =
|
||||
var marker = InitIntSet()
|
||||
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 =
|
||||
assert(n.kind == nkRange)
|
||||
result = ValueToString(n.sons[0]) & ".." & ValueToString(n.sons[1])
|
||||
|
||||
@@ -60,7 +60,7 @@ type
|
||||
wFieldChecks,
|
||||
wWatchPoint, wSubsChar,
|
||||
wAcyclic, wShallow, wUnroll, wLinearScanEnd,
|
||||
wWrite, wPutEnv, wPrependEnv, wAppendEnv, wThreadVar, wEmit, wNoStackFrame,
|
||||
wWrite, wGensym, wInject, wInheritable, wThreadVar, wEmit, wNoStackFrame,
|
||||
wImplicitStatic, wGlobal, wHoist
|
||||
|
||||
wAuto, wBool, wCatch, wChar, wClass,
|
||||
@@ -138,7 +138,7 @@ const
|
||||
"passc", "passl", "borrow", "discardable", "fieldchecks",
|
||||
"watchpoint",
|
||||
"subschar", "acyclic", "shallow", "unroll", "linearscanend",
|
||||
"write", "putenv", "prependenv", "appendenv", "threadvar", "emit",
|
||||
"write", "gensym", "inject", "inheritable", "threadvar", "emit",
|
||||
"nostackframe", "implicitstatic", "global", "hoist",
|
||||
|
||||
"auto", "bool", "catch", "char", "class",
|
||||
|
||||
@@ -908,7 +908,7 @@ converts the list of arguments to an array implicitely:
|
||||
write(f, "\n")
|
||||
|
||||
myWriteln(stdout, "abc", "def", "xyz")
|
||||
# is transformed by the compiler to:
|
||||
# is transformed to:
|
||||
myWriteln(stdout, ["abc", "def", "xyz"])
|
||||
|
||||
This transformation is only done if the varargs parameter is the
|
||||
@@ -922,7 +922,7 @@ type conversions in this context:
|
||||
write(f, "\n")
|
||||
|
||||
myWriteln(stdout, 123, "abc", 4.0)
|
||||
# is transformed by the compiler to:
|
||||
# is transformed to:
|
||||
myWriteln(stdout, [$123, $"def", $4.0])
|
||||
|
||||
In this example ``$`` is applied to any argument that is passed to the
|
||||
|
||||
@@ -218,6 +218,9 @@ proc findAll*(s: string, pattern: TRegEx, start = 0): seq[string] =
|
||||
## If it does not match, @[] is returned.
|
||||
accumulateResult(findAll(s, pattern, start))
|
||||
|
||||
when not defined(nimhygiene):
|
||||
{.pragma: inject.}
|
||||
|
||||
template `=~` *(s: string, pattern: TRegEx): expr =
|
||||
## This calls ``match`` with an implicit declared ``matches`` array that
|
||||
## can be used in the scope of the ``=~`` call:
|
||||
@@ -237,7 +240,7 @@ template `=~` *(s: string, pattern: TRegEx): expr =
|
||||
## echo("syntax error")
|
||||
##
|
||||
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)
|
||||
|
||||
# ------------------------- more string handling ------------------------------
|
||||
|
||||
@@ -845,6 +845,9 @@ proc findAll*(s: string, pattern: TPeg, start = 0): seq[string] {.
|
||||
## returns all matching *substrings* of `s` that match `pattern`.
|
||||
## If it does not match, @[] is returned.
|
||||
accumulateResult(findAll(s, pattern, start))
|
||||
|
||||
when not defined(nimhygiene):
|
||||
{.pragma: inject.}
|
||||
|
||||
template `=~`*(s: string, pattern: TPeg): bool =
|
||||
## This calls ``match`` with an implicit declared ``matches`` array that
|
||||
@@ -865,7 +868,7 @@ template `=~`*(s: string, pattern: TPeg): bool =
|
||||
## echo("syntax error")
|
||||
##
|
||||
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)
|
||||
|
||||
# ------------------------- more string handling ------------------------------
|
||||
|
||||
@@ -62,12 +62,15 @@ proc extractSpec(filename: string): string =
|
||||
#echo "warning: file does not contain spec: " & filename
|
||||
result = ""
|
||||
|
||||
when not defined(nimhygiene):
|
||||
{.pragma: inject.}
|
||||
|
||||
template parseSpecAux(fillResult: stmt) =
|
||||
var ss = newStringStream(extractSpec(filename))
|
||||
var p: TCfgParser
|
||||
var p {.inject.}: TCfgParser
|
||||
open(p, ss, filename, 1)
|
||||
while true:
|
||||
var e = next(p)
|
||||
var e {.inject.} = next(p)
|
||||
case e.kind
|
||||
of cfgEof: break
|
||||
of cfgSectionStart, cfgOption, cfgError:
|
||||
|
||||
9
todo.txt
9
todo.txt
@@ -1,16 +1,19 @@
|
||||
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 for loop transformation for first class iterators
|
||||
- make templates hygienic by default: 'gensym', 'inject' pragmas
|
||||
|
||||
- implicit deref for parameter matching
|
||||
- ``final`` should be the default for objects
|
||||
- optimize genericAssign in the code generator
|
||||
- the lookup rules for generics really are too permissive
|
||||
- fix remaining closure bugs:
|
||||
- fix evals.nim with closures
|
||||
- test evals.nim with closures
|
||||
- what about macros with closures?
|
||||
|
||||
|
||||
Bugs
|
||||
|
||||
Reference in New Issue
Block a user