diff options
-rwxr-xr-x | compiler/ast.nim | 15 | ||||
-rwxr-xr-x | compiler/astalgo.nim | 12 | ||||
-rwxr-xr-x | compiler/importer.nim | 10 | ||||
-rwxr-xr-x | compiler/parser.nim | 22 | ||||
-rwxr-xr-x | compiler/passes.nim | 15 | ||||
-rw-r--r-- | compiler/patterns.nim | 128 | ||||
-rwxr-xr-x | compiler/renderer.nim | 23 | ||||
-rwxr-xr-x | compiler/rodread.nim | 6 | ||||
-rwxr-xr-x | compiler/rodwrite.nim | 4 | ||||
-rwxr-xr-x | compiler/sem.nim | 15 | ||||
-rwxr-xr-x | compiler/semdata.nim | 19 | ||||
-rwxr-xr-x | compiler/semexprs.nim | 1 | ||||
-rwxr-xr-x | compiler/semstmts.nim | 15 | ||||
-rwxr-xr-x | compiler/semtempl.nim | 27 | ||||
-rwxr-xr-x | compiler/sigmatch.nim | 2 | ||||
-rwxr-xr-x | lib/core/macros.nim | 2 | ||||
-rwxr-xr-x | todo.txt | 6 |
17 files changed, 264 insertions, 58 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index a8176501f..dcf60af6b 100755 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -176,6 +176,7 @@ type nkFromStmt, # a from * import statement nkIncludeStmt, # an include statement nkBindStmt, # a bind statement + nkPatternStmt, # a pattern statement ('as' statement) nkCommentStmt, # a comment statement nkStmtListExpr, # a statement list followed by an expr; this is used # to allow powerful multi-line templates @@ -246,8 +247,8 @@ type # for interfacing with C++, JS sfNamedParamCall, # symbol needs named parameter call syntax in target # language; for interfacing with Objective C - sfDiscardable # returned value may be discarded implicitely - sfDestructor # proc is destructor + sfDiscardable, # returned value may be discarded implicitely + sfDestructor, # proc is destructor sfGenSym # symbol is 'gensym'ed; do not add to symbol table TSymFlags* = set[TSymFlag] @@ -689,9 +690,10 @@ const genericParamsPos* = 1 paramsPos* = 2 pragmasPos* = 3 - bodyPos* = 4 # position of body; use rodread.getBody() instead! - resultPos* = 5 - dispatcherPos* = 6 # caution: if method has no 'result' it can be position 5! + patternPos* = 4 # empty except for term rewriting macros + bodyPos* = 5 # position of body; use rodread.getBody() instead! + resultPos* = 6 + dispatcherPos* = 7 # caution: if method has no 'result' it can be position 5! nkCallKinds* = {nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit} @@ -1175,6 +1177,9 @@ proc isRoutine*(s: PSym): bool {.inline.} = result = s.kind in {skProc, skTemplate, skMacro, skIterator, skMethod, skConverter} +proc hasPattern*(s: PSym): bool {.inline.} = + result = isRoutine(s) and s.ast.sons[patternPos].kind != nkEmpty + iterator items*(n: PNode): PNode = for i in 0.. <n.len: yield n.sons[i] diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index 9da3e21e4..da0de3e94 100755 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -834,8 +834,8 @@ proc writeIdNodeTable(t: TIdNodeTable) = proc IdNodeTableRawGet(t: TIdNodeTable, key: PIdObj): int = var h: THash h = key.id and high(t.data) # start with real hash value - while t.data[h].key != nil: - if (t.data[h].key.id == key.id): + while t.data[h].key != nil: + if t.data[h].key.id == key.id: return h h = nextTry(h, high(t.data)) result = - 1 @@ -845,6 +845,10 @@ proc IdNodeTableGet(t: TIdNodeTable, key: PIdObj): PNode = index = IdNodeTableRawGet(t, key) if index >= 0: result = t.data[index].val else: result = nil + +proc IdNodeTableGetLazy*(t: TIdNodeTable, key: PIdObj): PNode = + if not isNil(t.data): + result = IdNodeTableGet(t, key) proc IdNodeTableRawInsert(data: var TIdNodePairSeq, key: PIdObj, val: PNode) = var h: THash @@ -872,6 +876,10 @@ proc IdNodeTablePut(t: var TIdNodeTable, key: PIdObj, val: PNode) = IdNodeTableRawInsert(t.data, key, val) inc(t.counter) +proc IdNodeTablePutLazy*(t: var TIdNodeTable, key: PIdObj, val: PNode) = + if isNil(t.data): initIdNodeTable(t) + IdNodeTablePut(t, key, val) + iterator pairs*(t: TIdNodeTable): tuple[key: PIdObj, val: PNode] = for i in 0 .. high(t.data): if not isNil(t.data[i].key): yield (t.data[i].key, t.data[i].val) diff --git a/compiler/importer.nim b/compiler/importer.nim index 24f7cb5c6..aa6722a32 100755 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -45,8 +45,8 @@ proc rawImportSymbol(c: PContext, s: PSym) = var copy = s # do not copy symbols when importing! # check if we have already a symbol of the same name: var check = StrTableGet(c.tab.stack[importTablePos], s.name) - if (check != nil) and (check.id != copy.id): - if not (s.kind in OverloadableSyms): + if check != nil and check.id != copy.id: + if s.kind notin OverloadableSyms: # s and check need to be qualified: Incl(c.AmbiguousSymbols, copy.id) Incl(c.AmbiguousSymbols, check.id) @@ -70,8 +70,10 @@ proc rawImportSymbol(c: PContext, s: PSym) = check = NextIdentIter(it, c.tab.stack[importTablePos]) if e != nil: rawImportSymbol(c, e) - elif s.kind == skConverter: - addConverter(c, s) # rodgen assures that converters are no stubs + else: + # rodgen assures that converters and patterns are no stubs + if s.kind == skConverter: addConverter(c, s) + if hasPattern(s): addPattern(c, s) proc importSymbol(c: PContext, n: PNode, fromMod: PSym) = let ident = lookups.considerAcc(n) diff --git a/compiler/parser.nim b/compiler/parser.nim index 33a2b6aae..4623b6eed 100755 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -183,7 +183,7 @@ proc getPrecedence(tok: TToken): int = of '?': result = 2 else: considerAsgn(2) of tkDiv, tkMod, tkShl, tkShr: result = 9 - of tkIn, tkNotIn, tkIs, tkIsNot, tkNot, tkOf: result = 5 + of tkIn, tkNotIn, tkIs, tkIsNot, tkNot, tkOf, tkAs: result = 5 of tkDotDot: result = 6 of tkAnd: result = 4 of tkOr, tkXor: result = 3 @@ -969,9 +969,18 @@ proc parseBreakOrContinue(p: var TParser, kind: TNodeKind): PNode = of tkEof, tkSad, tkDed: addSon(result, ast.emptyNode) else: addSon(result, parseSymbol(p)) -proc parseIfOrWhen(p: var TParser, kind: TNodeKind): PNode = +proc parseAs(p: var TParser): PNode = + result = newNodeP(nkPatternStmt, p) + getTok(p) # skip `as` + if p.tok.tokType == tkColon: + eat(p, tkColon) + addSon(result, parseStmt(p)) + else: + addSon(result, parseExpr(p)) + +proc parseIfOrWhen(p: var TParser, kind: TNodeKind): PNode = result = newNodeP(kind, p) - while true: + while true: getTok(p) # skip `if`, `when`, `elif` var branch = newNodeP(nkElifBranch, p) optInd(p, branch) @@ -981,8 +990,8 @@ proc parseIfOrWhen(p: var TParser, kind: TNodeKind): PNode = addSon(branch, parseStmt(p)) skipComment(p, branch) addSon(result, branch) - if p.tok.tokType != tkElif: break - if p.tok.tokType == tkElse: + if p.tok.tokType != tkElif: break + if p.tok.tokType == tkElse: var branch = newNodeP(nkElse, p) eat(p, tkElse) eat(p, tkColon) @@ -1176,6 +1185,8 @@ proc parseRoutine(p: var TParser, kind: TNodeKind): PNode = addSon(result, parseParamList(p)) if p.tok.tokType == tkCurlyDotLe: addSon(result, parsePragma(p)) else: addSon(result, ast.emptyNode) + # empty pattern: + addSon(result, ast.emptyNode) if p.tok.tokType == tkEquals: getTok(p) skipComment(p, result) @@ -1511,6 +1522,7 @@ proc complexOrSimpleStmt(p: var TParser): PNode = of tkWhen: result = parseIfOrWhen(p, nkWhenStmt) of tkVar: result = parseSection(p, nkVarSection, parseVariable) of tkBind: result = parseBind(p) + of tkAs: result = parseAs(p) else: result = simpleStmt(p) proc parseStmt(p: var TParser): PNode = diff --git a/compiler/passes.nim b/compiler/passes.nim index bedcbb16e..c073047af 100755 --- a/compiler/passes.nim +++ b/compiler/passes.nim @@ -15,13 +15,8 @@ import condsyms, idents, renderer, types, extccomp, math, magicsys, nversion, nimsets, syntaxes, times, rodread, semthreads, idgen -type - TPassComm* = object {.pure.} ## communication object between passes - optimizers*: TSymSeq ## filled by semantic pass; used in HLO - PPassComm* = ref TPassComm - +type TPassContext* = object of TObject # the pass's context - comm*: PPassComm fromCache*: bool # true if created by "openCached" PPassContext* = ref TPassContext @@ -85,26 +80,18 @@ proc registerPass(p: TPass) = gPasses[gPassesLen] = p inc(gPassesLen) -proc newPassComm(): PPassComm = - new(result) - result.optimizers = @[] - proc openPasses(a: var TPassContextArray, module: PSym, filename: string) = - var comm = newPassComm() for i in countup(0, gPassesLen - 1): if not isNil(gPasses[i].open): a[i] = gPasses[i].open(module, filename) - if a[i] != nil: a[i].comm = comm else: a[i] = nil proc openPassesCached(a: var TPassContextArray, module: PSym, filename: string, rd: PRodReader) = - var comm = newPassComm() for i in countup(0, gPassesLen - 1): if not isNil(gPasses[i].openCached): a[i] = gPasses[i].openCached(module, filename, rd) if a[i] != nil: - a[i].comm = comm a[i].fromCache = true else: a[i] = nil diff --git a/compiler/patterns.nim b/compiler/patterns.nim new file mode 100644 index 000000000..51bb1fb69 --- /dev/null +++ b/compiler/patterns.nim @@ -0,0 +1,128 @@ +# +# +# The Nimrod Compiler +# (c) Copyright 2012 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module implements the pattern matching features for term rewriting +## macro support. + +import ast, astalgo, types, semdata, sigmatch, msgs, idents + +type + TPatternContext = object + owner: PSym + mapping: TIdNodeTable # maps formal parameters to nodes + c: PContext + PPatternContext = var TPatternContext + +proc matches(c: PPatternContext, p, n: PNode): bool +proc checkConstraints(c: PPatternContext, p, n: PNode): bool = + # XXX create a new mapping here? --> need use cases + result = matches(c, p, n) + +proc canonKind(n: PNode): TNodeKind = + ## nodekind canonilization for pattern matching + result = n.kind + case result + of nkCallKinds: result = nkCall + of nkStrLit..nkTripleStrLit: result = nkStrLit + of nkFastAsgn: result = nkAsgn + else: nil + +proc sameKinds(a, b: PNode): bool {.inline.} = + result = a.kind == b.kind or a.canonKind == b.canonKind + +proc sameTrees(a, b: PNode): bool = + if sameKinds(a, b): + case a.kind + of nkSym: result = a.sym == b.sym + of nkIdent: result = a.ident.id == b.ident.id + of nkCharLit..nkInt64Lit: result = a.intVal == b.intVal + of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal + of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal + of nkEmpty, nkNilLit: result = true + of nkType: result = sameTypeOrNil(a.typ, b.typ) + else: + if sonsLen(a) == sonsLen(b): + for i in countup(0, sonsLen(a) - 1): + if not sameTrees(a.sons[i], b.sons[i]): return + result = true + +proc inSymChoice(sc, x: PNode): bool = + if sc.kind in {nkOpenSymChoice, nkClosedSymChoice}: + for i in 0.. <sc.len: + if sc.sons[i].sym == x.sym: return true + +proc checkTypes(c: PPatternContext, p: PSym, n: PNode): bool = + # XXX tyVarargs is special here; lots of other special cases + if isNil(n.typ): + result = p.typ.kind == tyStmt + else: + result = sigmatch.argtypeMatches(c.c, p.typ, n.typ) + +proc matches(c: PPatternContext, p, n: PNode): bool = + # XXX special treatment: statement list, + # ignore comments, nkPar, hidden conversions + # f(..X) ~> how can 'X' stand for all remaining parameters? -> introduce + # a new local node kind (alias of nkReturnToken or something) + if p.kind == nkSym and p.sym.kind == skParam and p.sym.owner == c.owner: + var pp = IdNodeTableGetLazy(c.mapping, p.sym) + if pp != nil: + # check if we got the same pattern (already unified): + result = matches(c, pp, n) + elif checkTypes(c, p.sym, n) and + (p.sym.ast == nil or checkConstraints(c, p.sym.ast, n)): + IdNodeTablePutLazy(c.mapping, p.sym, n) + result = true + elif n.kind == nkSym and inSymChoice(p, n): + result = true + elif n.kind == nkSym and n.sym.kind == skConst: + # try both: + if sameTrees(p, n): result = true + elif matches(c, p, n.sym.ast): + result = true + elif sameKinds(p, n): + case p.kind + of nkSym: result = p.sym == n.sym + of nkIdent: result = p.ident.id == n.ident.id + of nkCharLit..nkInt64Lit: result = p.intVal == n.intVal + of nkFloatLit..nkFloat64Lit: result = p.floatVal == n.floatVal + of nkStrLit..nkTripleStrLit: result = p.strVal == n.strVal + of nkEmpty, nkNilLit, nkType: result = true + else: + if sonsLen(p) == sonsLen(n): + for i in countup(0, sonsLen(p) - 1): + if not matches(c, p.sons[i], n.sons[i]): return + result = true + +# writeln(X, a); writeln(X, b); --> writeln(X, a, b) + +proc applyRule*(c: PContext, s: PSym, n: PNode): PNode = + ## returns a tree to semcheck if the rule triggered; nil otherwise + var ctx: TPatternContext + ctx.owner = s + ctx.c = c + # we perform 'initIdNodeTable' lazily for performance + if matches(ctx, s.ast.sons[patternPos], n): + # each parameter should have been bound; we simply setup a call and + # let semantic checking deal with the rest :-) + # this also saves type checking if we allow for type checking errors + # as in 'system.compiles' and simply discard the results. But an error + # may have been desired in the first place! Meh, it's good enough for + # a first implementation: + result = newNodeI(nkCall, n.info) + result.add(newSymNode(s, n.info)) + let params = s.typ.n + for i in 1 .. < params.len: + let param = params.sons[i].sym + let x = IdNodeTableGetLazy(ctx.mapping, param) + # couldn't bind parameter: + if isNil(x): + echo "couldn't bind ", param.name.s + return nil + result.add(x) + diff --git a/compiler/renderer.nim b/compiler/renderer.nim index b6b34287a..7ceabaa33 100755 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -586,6 +586,16 @@ proc gwhile(g: var TSrcGen, n: PNode) = gcoms(g) # a good place for comments gstmts(g, n.sons[1], c) +proc gpattern(g: var TSrcGen, n: PNode) = + var c: TContext + put(g, tkAs, "as") + putWithSpace(g, tkColon, ":") + initContext(c) + if longMode(n) or (lsub(n.sons[0]) + g.lineLen > maxLineLen): + incl(c.flags, rfLongMode) + gcoms(g) # a good place for comments + gstmts(g, n.sons[0], c) + proc gpragmaBlock(g: var TSrcGen, n: PNode) = var c: TContext gsub(g, n.sons[0]) @@ -656,20 +666,20 @@ proc gproc(g: var TSrcGen, n: PNode) = put(g, tkSymbol, renderDefinitionName(n.sons[namePos].sym)) else: gsub(g, n.sons[namePos]) - gsub(g, n.sons[1]) - gsub(g, n.sons[2]) - gsub(g, n.sons[3]) + gsub(g, n.sons[genericParamsPos]) + gsub(g, n.sons[paramsPos]) + gsub(g, n.sons[pragmasPos]) if renderNoBody notin g.flags: - if n.sons[4].kind != nkEmpty: + if n.sons[bodyPos].kind != nkEmpty: put(g, tkSpaces, Space) putWithSpace(g, tkEquals, "=") indentNL(g) gcoms(g) dedent(g) initContext(c) - gstmts(g, n.sons[4], c) + gstmts(g, n.sons[bodyPos], c) putNL(g) - else: + else: indentNL(g) gcoms(g) dedent(g) @@ -1035,6 +1045,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = of nkCaseStmt, nkRecCase: gcase(g, n) of nkMacroStmt: gmacro(g, n) of nkTryStmt: gtry(g, n) + of nkPatternStmt: gpattern(g, n) of nkForStmt, nkParForStmt: gfor(g, n) of nkBlockStmt, nkBlockExpr: gblock(g, n) of nkStaticStmt: gstaticStmt(g, n) diff --git a/compiler/rodread.nim b/compiler/rodread.nim index 7e8584955..7c924511f 100755 --- a/compiler/rodread.nim +++ b/compiler/rodread.nim @@ -11,7 +11,7 @@ # # Reading and writing binary files are really hard to debug. Therefore we use # a "creative" text/binary hybrid format. ROD-files are more efficient -# to process because symbols are can be loaded on demand. +# to process because symbols can be loaded on demand. # # A ROD file consists of: # @@ -65,10 +65,12 @@ # semantic checking: # CONVERTERS:id id\n # symbol ID # +# This is a misnomer now; it's really a "load unconditionally" section as +# it is also used for pattern templates. +# # - a list of all (private or exported) methods because they are needed for # correct dispatcher generation: # METHODS: id id\n # symbol ID -# # - an AST section that contains the module's AST: # INIT( # idx\n # position of the node in the DATA section diff --git a/compiler/rodwrite.nim b/compiler/rodwrite.nim index 9af4349ab..75e8a02a7 100755 --- a/compiler/rodwrite.nim +++ b/compiler/rodwrite.nim @@ -357,10 +357,10 @@ proc symStack(w: PRodWriter): int = add(w.compilerProcs, ' ') encodeVInt(s.id, w.compilerProcs) add(w.compilerProcs, rodNL) - if s.kind == skConverter: + if s.kind == skConverter or hasPattern(s): if w.converters.len != 0: add(w.converters, ' ') encodeVInt(s.id, w.converters) - elif s.kind == skMethod and sfDispatcher notin s.flags: + if s.kind == skMethod and sfDispatcher notin s.flags: if w.methods.len != 0: add(w.methods, ' ') encodeVInt(s.id, w.methods) elif IiTableGet(w.imports.tab, s.id) == invalidKey: diff --git a/compiler/sem.nim b/compiler/sem.nim index d92c1657d..8d0e4e168 100755 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -15,7 +15,7 @@ import magicsys, parser, nversion, nimsets, semfold, importer, procfind, lookups, rodread, pragmas, passes, semdata, semtypinst, sigmatch, semthreads, intsets, transf, evals, idgen, aliases, cgmeth, lambdalifting, - evaltempl + evaltempl, patterns proc semPass*(): TPass # implementation @@ -102,6 +102,19 @@ proc semConstExpr(c: PContext, n: PNode): PNode = return n result = evalTypedExpr(c, e) +proc applyPatterns(c: PContext, n: PNode): PNode = + # fast exit: + if c.patterns.len == 0: return n + result = n + # we apply the last pattern first, so that pattern overriding is possible; + # however the resulting AST would better not trigger the old rule then + # anymore ;-) + for i in countdown(<c.patterns.len, 0): + let x = applyRule(c, c.patterns[i], result) + if not isNil(x): + assert x.kind == nkCall + result = semExpr(c, x) + include seminst, semcall proc semAfterMacroCall(c: PContext, n: PNode, s: PSym): PNode = diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 0fc5399d2..02768c90b 100755 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -67,6 +67,7 @@ type InUnrolledContext*: int # > 0 if we are unrolling a loop InCompilesContext*: int # > 0 if we are in a ``compiles`` magic converters*: TSymSeq # sequence of converters + patterns*: TSymSeq # sequence of pattern matchers optionStack*: TLinkedList libs*: TLinkedList # all libs used by this module semConstExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.} # for the pragmas @@ -93,7 +94,6 @@ proc newContext*(module: PSym, nimfile: string): PContext proc lastOptionEntry*(c: PContext): POptionEntry proc newOptionEntry*(): POptionEntry -proc addConverter*(c: PContext, conv: PSym) proc newLib*(kind: TLibKind): PLib proc addToLib*(lib: PLib, sym: PSym) proc makePtrType*(c: PContext, baseType: PType): PType @@ -158,6 +158,7 @@ proc newContext(module: PSym, nimfile: string): PContext = result.friendModule = module result.threadEntries = @[] result.converters = @[] + result.patterns = @[] result.filename = nimfile result.includedFiles = initIntSet() initStrTable(result.userPragmas) @@ -172,12 +173,18 @@ proc newContext(module: PSym, nimfile: string): PContext = assert gGenericsCache == nil result.UnknownIdents = initIntSet() -proc addConverter(c: PContext, conv: PSym) = - var L = len(c.converters) +proc inclSym(sq: var TSymSeq, s: PSym) = + var L = len(sq) for i in countup(0, L - 1): - if c.converters[i].id == conv.id: return - setlen(c.converters, L + 1) - c.converters[L] = conv + if sq[i].id == s.id: return + setlen(sq, L + 1) + sq[L] = s + +proc addConverter*(c: PContext, conv: PSym) = + inclSym(c.converters, conv) + +proc addPattern*(c: PContext, p: PSym) = + inclSym(c.patterns, p) proc newLib(kind: TLibKind): PLib = new(result) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 9219cacf6..1bf412a26 100755 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1591,3 +1591,4 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = LocalError(n.info, errInvalidExpressionX, renderTree(n, {renderNoComments})) incl(result.flags, nfSem) + result = applyPatterns(c, result) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 3a8424d9e..84d7b5b45 100755 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1202,6 +1202,16 @@ proc SemStmt(c: PContext, n: PNode): PNode = result = semPragmaBlock(c, n) of nkStaticStmt: result = semStaticStmt(c, n) + of nkPatternStmt: + let pat = semPatternStmt(c, n) + let s = getCurrOwner() + if s.kind in routineKinds and s.ast.sons[patternPos].kind == nkEmpty: + s.ast.sons[patternPos] = pat + c.patterns.add(s) + else: + LocalError(n.info, errXNotAllowedHere, "'as'") + # replace by an empty statement: + result = newNodeI(nkNilLit, n.info) else: # in interactive mode, we embed the expression in an 'echo': if gCmd == cmdInteractive: @@ -1214,10 +1224,11 @@ proc SemStmt(c: PContext, n: PNode): PNode = InternalError(n.info, "SemStmt: result = nil") # error correction: result = emptyNode - else: + else: incl(result.flags, nfSem) + result = applyPatterns(c, result) -proc semStmtScope(c: PContext, n: PNode): PNode = +proc semStmtScope(c: PContext, n: PNode): PNode = openScope(c.tab) result = semStmt(c, n) closeScope(c.tab) diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index 440fac6cc..ce5dfe3a2 100755 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -98,10 +98,16 @@ proc replaceIdentBySym(n: var PNode, s: PNode) = of nkIdent, nkAccQuoted, nkSym: n = s else: illFormedAst(n) +# This code here is the first pass over a template's body. The same code also +# implements the first pass over a pattern's body: + type + TBodyKind = enum + bkTemplate, bkPattern TemplCtx {.pure, final.} = object c: PContext toBind: TIntSet + bodyKind: TBodyKind owner: PSym proc getIdentNode(c: var TemplCtx, n: PNode): PNode = @@ -166,10 +172,8 @@ proc semRoutineInTemplBody(c: var TemplCtx, n: PNode, k: TSymKind): PNode = 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]) + for i in genericParamsPos..bodyPos: + n.sons[i] = semTemplBody(c, n.sons[i]) closeScope(c) proc semTemplBody(c: var TemplCtx, n: PNode): PNode = @@ -183,6 +187,8 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = result = newSymNode(s, n.info) elif Contains(c.toBind, s.id): result = symChoice(c.c, n, s, scClosed) + elif c.bodyKind == bkPattern: + result = symChoice(c.c, n, s, scOpen) elif s.owner == c.owner and sfGenSym in s.flags: # template tmp[T](x: var seq[T]) = # var yz: T @@ -395,6 +401,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode = ctx.toBind = initIntSet() ctx.c = c ctx.owner = s + ctx.bodyKind = bkTemplate if sfDirty in s.flags: n.sons[bodyPos] = semTemplBodyDirty(ctx, n.sons[bodyPos]) else: @@ -415,3 +422,15 @@ proc semTemplateDef(c: PContext, n: PNode): PNode = else: SymTabReplace(c.tab.stack[curScope], proto, s) +proc semPatternStmt(c: PContext, n: PNode): PNode = + # not much to do here: We don't replace operators ``$``, ``*``, ``+``, + # ``|``, ``~`` as meta operators and strip the leading ``\`` of all + # operators. + openScope(c.tab) + var ctx: TemplCtx + ctx.toBind = initIntSet() + ctx.c = c + ctx.owner = getCurrOwner() + ctx.bodyKind = bkPattern + result = semTemplBody(ctx, n.sons[0]) + closeScope(c.tab) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index a113696fe..1f4c9653e 100755 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -647,7 +647,7 @@ proc ParamTypesMatchAux(c: PContext, m: var TCandidate, f, a: PType, inc(m.convMatches) result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) of isIntConv: - # too lazy to introduce another ``*matches`` field, so we conflate + # I'm too lazy to introduce another ``*matches`` field, so we conflate # ``isIntConv`` and ``isIntLit`` here: inc(m.intConvMatches) result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 610360fb2..54d242238 100755 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -46,7 +46,7 @@ type nnkYieldStmt, nnkTryStmt, nnkFinally, nnkRaiseStmt, nnkReturnStmt, nnkBreakStmt, nnkContinueStmt, nnkBlockStmt, nnkStaticStmt, nnkDiscardStmt, nnkStmtList, nnkImportStmt, nnkFromStmt, - nnkIncludeStmt, nnkBindStmt, + nnkIncludeStmt, nnkBindStmt, nnkPatternStmt, nnkCommentStmt, nnkStmtListExpr, nnkBlockExpr, nnkStmtListType, nnkBlockType, nnkTypeOfExpr, nnkObjectTy, nnkTupleTy, nnkRecList, nnkRecCase, nnkRecWhen, diff --git a/todo.txt b/todo.txt index 8c5d172da..a64a5b951 100755 --- a/todo.txt +++ b/todo.txt @@ -14,9 +14,6 @@ version 0.9.0 - the lookup rules for generics really are too permissive; global scope only doesn't fly with closures though; for a start add a warning when processing generic code -- fix remaining closure bugs: - - test evals.nim with closures - - what about macros with closures? Bugs @@ -47,6 +44,9 @@ version 0.9.XX - JS gen: - fix exception handling +- fix remaining closure bugs: + - test evals.nim with closures + - what about macros with closures? - document 'do' notation - allow implicit forward declarations of procs via a pragma (so that the |