first steps towards term rewriting macros
This commit is contained in:
parent
b4844a189d
commit
1786e30991
17 changed files with 264 additions and 58 deletions
128
compiler/patterns.nim
Normal file
128
compiler/patterns.nim
Normal file
|
|
@ -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)
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue