From 1786e3099141f6548e9f6cd4f4998d32f602b71d Mon Sep 17 00:00:00 2001 From: Araq Date: Thu, 30 Aug 2012 10:55:40 +0200 Subject: first steps towards term rewriting macros --- compiler/ast.nim | 15 ++++-- compiler/astalgo.nim | 12 ++++- compiler/importer.nim | 10 ++-- compiler/parser.nim | 22 +++++++-- compiler/passes.nim | 15 +----- compiler/patterns.nim | 128 ++++++++++++++++++++++++++++++++++++++++++++++++++ compiler/renderer.nim | 23 ++++++--- compiler/rodread.nim | 6 ++- compiler/rodwrite.nim | 4 +- compiler/sem.nim | 15 +++++- compiler/semdata.nim | 19 +++++--- compiler/semexprs.nim | 1 + compiler/semstmts.nim | 15 +++++- compiler/semtempl.nim | 27 +++++++++-- compiler/sigmatch.nim | 2 +- 15 files changed, 260 insertions(+), 54 deletions(-) create mode 100644 compiler/patterns.nim (limited to 'compiler') 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.. = 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..