first steps towards term rewriting macros

This commit is contained in:
Araq 2012-08-30 10:55:40 +02:00
commit 1786e30991
17 changed files with 264 additions and 58 deletions

View file

@ -176,6 +176,7 @@ type
nkFromStmt, # a from * import statement nkFromStmt, # a from * import statement
nkIncludeStmt, # an include statement nkIncludeStmt, # an include statement
nkBindStmt, # a bind statement nkBindStmt, # a bind statement
nkPatternStmt, # a pattern statement ('as' statement)
nkCommentStmt, # a comment statement nkCommentStmt, # a comment statement
nkStmtListExpr, # a statement list followed by an expr; this is used nkStmtListExpr, # a statement list followed by an expr; this is used
# to allow powerful multi-line templates # to allow powerful multi-line templates
@ -246,8 +247,8 @@ type
# for interfacing with C++, JS # for interfacing with C++, JS
sfNamedParamCall, # symbol needs named parameter call syntax in target sfNamedParamCall, # symbol needs named parameter call syntax in target
# 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
sfGenSym # symbol is 'gensym'ed; do not add to symbol table sfGenSym # symbol is 'gensym'ed; do not add to symbol table
TSymFlags* = set[TSymFlag] TSymFlags* = set[TSymFlag]
@ -689,9 +690,10 @@ const
genericParamsPos* = 1 genericParamsPos* = 1
paramsPos* = 2 paramsPos* = 2
pragmasPos* = 3 pragmasPos* = 3
bodyPos* = 4 # position of body; use rodread.getBody() instead! patternPos* = 4 # empty except for term rewriting macros
resultPos* = 5 bodyPos* = 5 # position of body; use rodread.getBody() instead!
dispatcherPos* = 6 # caution: if method has no 'result' it can be position 5! resultPos* = 6
dispatcherPos* = 7 # caution: if method has no 'result' it can be position 5!
nkCallKinds* = {nkCall, nkInfix, nkPrefix, nkPostfix, nkCallKinds* = {nkCall, nkInfix, nkPrefix, nkPostfix,
nkCommand, nkCallStrLit} nkCommand, nkCallStrLit}
@ -1175,6 +1177,9 @@ proc isRoutine*(s: PSym): bool {.inline.} =
result = s.kind in {skProc, skTemplate, skMacro, skIterator, skMethod, result = s.kind in {skProc, skTemplate, skMacro, skIterator, skMethod,
skConverter} skConverter}
proc hasPattern*(s: PSym): bool {.inline.} =
result = isRoutine(s) and s.ast.sons[patternPos].kind != nkEmpty
iterator items*(n: PNode): PNode = iterator items*(n: PNode): PNode =
for i in 0.. <n.len: yield n.sons[i] for i in 0.. <n.len: yield n.sons[i]

View file

@ -835,7 +835,7 @@ proc IdNodeTableRawGet(t: TIdNodeTable, key: PIdObj): int =
var h: THash var h: THash
h = key.id and high(t.data) # start with real hash value h = key.id and high(t.data) # start with real hash value
while t.data[h].key != nil: while t.data[h].key != nil:
if (t.data[h].key.id == key.id): if t.data[h].key.id == key.id:
return h return h
h = nextTry(h, high(t.data)) h = nextTry(h, high(t.data))
result = - 1 result = - 1
@ -846,6 +846,10 @@ proc IdNodeTableGet(t: TIdNodeTable, key: PIdObj): PNode =
if index >= 0: result = t.data[index].val if index >= 0: result = t.data[index].val
else: result = nil 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) = proc IdNodeTableRawInsert(data: var TIdNodePairSeq, key: PIdObj, val: PNode) =
var h: THash var h: THash
h = key.id and high(data) h = key.id and high(data)
@ -872,6 +876,10 @@ proc IdNodeTablePut(t: var TIdNodeTable, key: PIdObj, val: PNode) =
IdNodeTableRawInsert(t.data, key, val) IdNodeTableRawInsert(t.data, key, val)
inc(t.counter) 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] = iterator pairs*(t: TIdNodeTable): tuple[key: PIdObj, val: PNode] =
for i in 0 .. high(t.data): for i in 0 .. high(t.data):
if not isNil(t.data[i].key): yield (t.data[i].key, t.data[i].val) if not isNil(t.data[i].key): yield (t.data[i].key, t.data[i].val)

View file

@ -45,8 +45,8 @@ proc rawImportSymbol(c: PContext, s: PSym) =
var copy = s # do not copy symbols when importing! var copy = s # do not copy symbols when importing!
# check if we have already a symbol of the same name: # check if we have already a symbol of the same name:
var check = StrTableGet(c.tab.stack[importTablePos], s.name) var check = StrTableGet(c.tab.stack[importTablePos], s.name)
if (check != nil) and (check.id != copy.id): if check != nil and check.id != copy.id:
if not (s.kind in OverloadableSyms): if s.kind notin OverloadableSyms:
# s and check need to be qualified: # s and check need to be qualified:
Incl(c.AmbiguousSymbols, copy.id) Incl(c.AmbiguousSymbols, copy.id)
Incl(c.AmbiguousSymbols, check.id) Incl(c.AmbiguousSymbols, check.id)
@ -70,8 +70,10 @@ proc rawImportSymbol(c: PContext, s: PSym) =
check = NextIdentIter(it, c.tab.stack[importTablePos]) check = NextIdentIter(it, c.tab.stack[importTablePos])
if e != nil: if e != nil:
rawImportSymbol(c, e) rawImportSymbol(c, e)
elif s.kind == skConverter: else:
addConverter(c, s) # rodgen assures that converters are no stubs # 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) = proc importSymbol(c: PContext, n: PNode, fromMod: PSym) =
let ident = lookups.considerAcc(n) let ident = lookups.considerAcc(n)

View file

@ -183,7 +183,7 @@ proc getPrecedence(tok: TToken): int =
of '?': result = 2 of '?': result = 2
else: considerAsgn(2) else: considerAsgn(2)
of tkDiv, tkMod, tkShl, tkShr: result = 9 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 tkDotDot: result = 6
of tkAnd: result = 4 of tkAnd: result = 4
of tkOr, tkXor: result = 3 of tkOr, tkXor: result = 3
@ -969,6 +969,15 @@ proc parseBreakOrContinue(p: var TParser, kind: TNodeKind): PNode =
of tkEof, tkSad, tkDed: addSon(result, ast.emptyNode) of tkEof, tkSad, tkDed: addSon(result, ast.emptyNode)
else: addSon(result, parseSymbol(p)) else: addSon(result, parseSymbol(p))
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 = proc parseIfOrWhen(p: var TParser, kind: TNodeKind): PNode =
result = newNodeP(kind, p) result = newNodeP(kind, p)
while true: while true:
@ -1176,6 +1185,8 @@ proc parseRoutine(p: var TParser, kind: TNodeKind): PNode =
addSon(result, parseParamList(p)) addSon(result, parseParamList(p))
if p.tok.tokType == tkCurlyDotLe: addSon(result, parsePragma(p)) if p.tok.tokType == tkCurlyDotLe: addSon(result, parsePragma(p))
else: addSon(result, ast.emptyNode) else: addSon(result, ast.emptyNode)
# empty pattern:
addSon(result, ast.emptyNode)
if p.tok.tokType == tkEquals: if p.tok.tokType == tkEquals:
getTok(p) getTok(p)
skipComment(p, result) skipComment(p, result)
@ -1511,6 +1522,7 @@ proc complexOrSimpleStmt(p: var TParser): PNode =
of tkWhen: result = parseIfOrWhen(p, nkWhenStmt) of tkWhen: result = parseIfOrWhen(p, nkWhenStmt)
of tkVar: result = parseSection(p, nkVarSection, parseVariable) of tkVar: result = parseSection(p, nkVarSection, parseVariable)
of tkBind: result = parseBind(p) of tkBind: result = parseBind(p)
of tkAs: result = parseAs(p)
else: result = simpleStmt(p) else: result = simpleStmt(p)
proc parseStmt(p: var TParser): PNode = proc parseStmt(p: var TParser): PNode =

View file

@ -16,12 +16,7 @@ import
nimsets, syntaxes, times, rodread, semthreads, idgen nimsets, syntaxes, times, rodread, semthreads, idgen
type type
TPassComm* = object {.pure.} ## communication object between passes
optimizers*: TSymSeq ## filled by semantic pass; used in HLO
PPassComm* = ref TPassComm
TPassContext* = object of TObject # the pass's context TPassContext* = object of TObject # the pass's context
comm*: PPassComm
fromCache*: bool # true if created by "openCached" fromCache*: bool # true if created by "openCached"
PPassContext* = ref TPassContext PPassContext* = ref TPassContext
@ -85,26 +80,18 @@ proc registerPass(p: TPass) =
gPasses[gPassesLen] = p gPasses[gPassesLen] = p
inc(gPassesLen) inc(gPassesLen)
proc newPassComm(): PPassComm =
new(result)
result.optimizers = @[]
proc openPasses(a: var TPassContextArray, module: PSym, filename: string) = proc openPasses(a: var TPassContextArray, module: PSym, filename: string) =
var comm = newPassComm()
for i in countup(0, gPassesLen - 1): for i in countup(0, gPassesLen - 1):
if not isNil(gPasses[i].open): if not isNil(gPasses[i].open):
a[i] = gPasses[i].open(module, filename) a[i] = gPasses[i].open(module, filename)
if a[i] != nil: a[i].comm = comm
else: a[i] = nil else: a[i] = nil
proc openPassesCached(a: var TPassContextArray, module: PSym, filename: string, proc openPassesCached(a: var TPassContextArray, module: PSym, filename: string,
rd: PRodReader) = rd: PRodReader) =
var comm = newPassComm()
for i in countup(0, gPassesLen - 1): for i in countup(0, gPassesLen - 1):
if not isNil(gPasses[i].openCached): if not isNil(gPasses[i].openCached):
a[i] = gPasses[i].openCached(module, filename, rd) a[i] = gPasses[i].openCached(module, filename, rd)
if a[i] != nil: if a[i] != nil:
a[i].comm = comm
a[i].fromCache = true a[i].fromCache = true
else: else:
a[i] = nil a[i] = nil

128
compiler/patterns.nim Normal file
View 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)

View file

@ -586,6 +586,16 @@ proc gwhile(g: var TSrcGen, n: PNode) =
gcoms(g) # a good place for comments gcoms(g) # a good place for comments
gstmts(g, n.sons[1], c) 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) = proc gpragmaBlock(g: var TSrcGen, n: PNode) =
var c: TContext var c: TContext
gsub(g, n.sons[0]) gsub(g, n.sons[0])
@ -656,18 +666,18 @@ proc gproc(g: var TSrcGen, n: PNode) =
put(g, tkSymbol, renderDefinitionName(n.sons[namePos].sym)) put(g, tkSymbol, renderDefinitionName(n.sons[namePos].sym))
else: else:
gsub(g, n.sons[namePos]) gsub(g, n.sons[namePos])
gsub(g, n.sons[1]) gsub(g, n.sons[genericParamsPos])
gsub(g, n.sons[2]) gsub(g, n.sons[paramsPos])
gsub(g, n.sons[3]) gsub(g, n.sons[pragmasPos])
if renderNoBody notin g.flags: if renderNoBody notin g.flags:
if n.sons[4].kind != nkEmpty: if n.sons[bodyPos].kind != nkEmpty:
put(g, tkSpaces, Space) put(g, tkSpaces, Space)
putWithSpace(g, tkEquals, "=") putWithSpace(g, tkEquals, "=")
indentNL(g) indentNL(g)
gcoms(g) gcoms(g)
dedent(g) dedent(g)
initContext(c) initContext(c)
gstmts(g, n.sons[4], c) gstmts(g, n.sons[bodyPos], c)
putNL(g) putNL(g)
else: else:
indentNL(g) indentNL(g)
@ -1035,6 +1045,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
of nkCaseStmt, nkRecCase: gcase(g, n) of nkCaseStmt, nkRecCase: gcase(g, n)
of nkMacroStmt: gmacro(g, n) of nkMacroStmt: gmacro(g, n)
of nkTryStmt: gtry(g, n) of nkTryStmt: gtry(g, n)
of nkPatternStmt: gpattern(g, n)
of nkForStmt, nkParForStmt: gfor(g, n) of nkForStmt, nkParForStmt: gfor(g, n)
of nkBlockStmt, nkBlockExpr: gblock(g, n) of nkBlockStmt, nkBlockExpr: gblock(g, n)
of nkStaticStmt: gstaticStmt(g, n) of nkStaticStmt: gstaticStmt(g, n)

View file

@ -11,7 +11,7 @@
# #
# Reading and writing binary files are really hard to debug. Therefore we use # Reading and writing binary files are really hard to debug. Therefore we use
# a "creative" text/binary hybrid format. ROD-files are more efficient # 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: # A ROD file consists of:
# #
@ -65,10 +65,12 @@
# semantic checking: # semantic checking:
# CONVERTERS:id id\n # symbol ID # 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 # - a list of all (private or exported) methods because they are needed for
# correct dispatcher generation: # correct dispatcher generation:
# METHODS: id id\n # symbol ID # METHODS: id id\n # symbol ID
#
# - an AST section that contains the module's AST: # - an AST section that contains the module's AST:
# INIT( # INIT(
# idx\n # position of the node in the DATA section # idx\n # position of the node in the DATA section

View file

@ -357,10 +357,10 @@ proc symStack(w: PRodWriter): int =
add(w.compilerProcs, ' ') add(w.compilerProcs, ' ')
encodeVInt(s.id, w.compilerProcs) encodeVInt(s.id, w.compilerProcs)
add(w.compilerProcs, rodNL) add(w.compilerProcs, rodNL)
if s.kind == skConverter: if s.kind == skConverter or hasPattern(s):
if w.converters.len != 0: add(w.converters, ' ') if w.converters.len != 0: add(w.converters, ' ')
encodeVInt(s.id, 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, ' ') if w.methods.len != 0: add(w.methods, ' ')
encodeVInt(s.id, w.methods) encodeVInt(s.id, w.methods)
elif IiTableGet(w.imports.tab, s.id) == invalidKey: elif IiTableGet(w.imports.tab, s.id) == invalidKey:

View file

@ -15,7 +15,7 @@ import
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 evaltempl, patterns
proc semPass*(): TPass proc semPass*(): TPass
# implementation # implementation
@ -102,6 +102,19 @@ proc semConstExpr(c: PContext, n: PNode): PNode =
return n return n
result = evalTypedExpr(c, e) 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 include seminst, semcall
proc semAfterMacroCall(c: PContext, n: PNode, s: PSym): PNode = proc semAfterMacroCall(c: PContext, n: PNode, s: PSym): PNode =

View file

@ -67,6 +67,7 @@ type
InUnrolledContext*: int # > 0 if we are unrolling a loop InUnrolledContext*: int # > 0 if we are unrolling a loop
InCompilesContext*: int # > 0 if we are in a ``compiles`` magic InCompilesContext*: int # > 0 if we are in a ``compiles`` magic
converters*: TSymSeq # sequence of converters converters*: TSymSeq # sequence of converters
patterns*: TSymSeq # sequence of pattern matchers
optionStack*: TLinkedList optionStack*: TLinkedList
libs*: TLinkedList # all libs used by this module libs*: TLinkedList # all libs used by this module
semConstExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.} # for the pragmas 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 lastOptionEntry*(c: PContext): POptionEntry
proc newOptionEntry*(): POptionEntry proc newOptionEntry*(): POptionEntry
proc addConverter*(c: PContext, conv: PSym)
proc newLib*(kind: TLibKind): PLib proc newLib*(kind: TLibKind): PLib
proc addToLib*(lib: PLib, sym: PSym) proc addToLib*(lib: PLib, sym: PSym)
proc makePtrType*(c: PContext, baseType: PType): PType proc makePtrType*(c: PContext, baseType: PType): PType
@ -158,6 +158,7 @@ proc newContext(module: PSym, nimfile: string): PContext =
result.friendModule = module result.friendModule = module
result.threadEntries = @[] result.threadEntries = @[]
result.converters = @[] result.converters = @[]
result.patterns = @[]
result.filename = nimfile result.filename = nimfile
result.includedFiles = initIntSet() result.includedFiles = initIntSet()
initStrTable(result.userPragmas) initStrTable(result.userPragmas)
@ -172,12 +173,18 @@ proc newContext(module: PSym, nimfile: string): PContext =
assert gGenericsCache == nil assert gGenericsCache == nil
result.UnknownIdents = initIntSet() result.UnknownIdents = initIntSet()
proc addConverter(c: PContext, conv: PSym) = proc inclSym(sq: var TSymSeq, s: PSym) =
var L = len(c.converters) var L = len(sq)
for i in countup(0, L - 1): for i in countup(0, L - 1):
if c.converters[i].id == conv.id: return if sq[i].id == s.id: return
setlen(c.converters, L + 1) setlen(sq, L + 1)
c.converters[L] = conv 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 = proc newLib(kind: TLibKind): PLib =
new(result) new(result)

View file

@ -1591,3 +1591,4 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
LocalError(n.info, errInvalidExpressionX, LocalError(n.info, errInvalidExpressionX,
renderTree(n, {renderNoComments})) renderTree(n, {renderNoComments}))
incl(result.flags, nfSem) incl(result.flags, nfSem)
result = applyPatterns(c, result)

View file

@ -1202,6 +1202,16 @@ proc SemStmt(c: PContext, n: PNode): PNode =
result = semPragmaBlock(c, n) result = semPragmaBlock(c, n)
of nkStaticStmt: of nkStaticStmt:
result = semStaticStmt(c, n) 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: else:
# in interactive mode, we embed the expression in an 'echo': # in interactive mode, we embed the expression in an 'echo':
if gCmd == cmdInteractive: if gCmd == cmdInteractive:
@ -1216,6 +1226,7 @@ proc SemStmt(c: PContext, n: PNode): PNode =
result = emptyNode result = emptyNode
else: else:
incl(result.flags, nfSem) 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) openScope(c.tab)

View file

@ -98,10 +98,16 @@ proc replaceIdentBySym(n: var PNode, s: PNode) =
of nkIdent, nkAccQuoted, nkSym: n = s of nkIdent, nkAccQuoted, nkSym: n = s
else: illFormedAst(n) 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 type
TBodyKind = enum
bkTemplate, bkPattern
TemplCtx {.pure, final.} = object TemplCtx {.pure, final.} = object
c: PContext c: PContext
toBind: TIntSet toBind: TIntSet
bodyKind: TBodyKind
owner: PSym owner: PSym
proc getIdentNode(c: var TemplCtx, n: PNode): PNode = proc getIdentNode(c: var TemplCtx, n: PNode): PNode =
@ -166,10 +172,8 @@ proc semRoutineInTemplBody(c: var TemplCtx, n: PNode, k: TSymKind): PNode =
else: else:
n.sons[namePos] = semTemplBody(c, n.sons[namePos]) n.sons[namePos] = semTemplBody(c, n.sons[namePos])
openScope(c) openScope(c)
n.sons[genericParamsPos] = semTemplBody(c, n.sons[genericParamsPos]) for i in genericParamsPos..bodyPos:
n.sons[paramsPos] = semTemplBody(c, n.sons[paramsPos]) n.sons[i] = semTemplBody(c, n.sons[i])
n.sons[pragmasPos] = semTemplBody(c, n.sons[pragmasPos])
n.sons[bodyPos] = semTemplBodyScope(c, n.sons[bodyPos])
closeScope(c) closeScope(c)
proc semTemplBody(c: var TemplCtx, n: PNode): PNode = 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) result = newSymNode(s, n.info)
elif Contains(c.toBind, s.id): elif Contains(c.toBind, s.id):
result = symChoice(c.c, n, s, scClosed) 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: elif s.owner == c.owner and sfGenSym in s.flags:
# template tmp[T](x: var seq[T]) = # template tmp[T](x: var seq[T]) =
# var yz: T # var yz: T
@ -395,6 +401,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
ctx.toBind = initIntSet() ctx.toBind = initIntSet()
ctx.c = c ctx.c = c
ctx.owner = s ctx.owner = s
ctx.bodyKind = bkTemplate
if sfDirty in s.flags: if sfDirty in s.flags:
n.sons[bodyPos] = semTemplBodyDirty(ctx, n.sons[bodyPos]) n.sons[bodyPos] = semTemplBodyDirty(ctx, n.sons[bodyPos])
else: else:
@ -415,3 +422,15 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
else: else:
SymTabReplace(c.tab.stack[curScope], proto, s) 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)

View file

@ -647,7 +647,7 @@ proc ParamTypesMatchAux(c: PContext, m: var TCandidate, f, a: PType,
inc(m.convMatches) inc(m.convMatches)
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c)
of isIntConv: 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: # ``isIntConv`` and ``isIntLit`` here:
inc(m.intConvMatches) inc(m.intConvMatches)
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c)

View file

@ -46,7 +46,7 @@ type
nnkYieldStmt, nnkTryStmt, nnkFinally, nnkRaiseStmt, nnkYieldStmt, nnkTryStmt, nnkFinally, nnkRaiseStmt,
nnkReturnStmt, nnkBreakStmt, nnkContinueStmt, nnkBlockStmt, nnkStaticStmt, nnkReturnStmt, nnkBreakStmt, nnkContinueStmt, nnkBlockStmt, nnkStaticStmt,
nnkDiscardStmt, nnkStmtList, nnkImportStmt, nnkFromStmt, nnkDiscardStmt, nnkStmtList, nnkImportStmt, nnkFromStmt,
nnkIncludeStmt, nnkBindStmt, nnkIncludeStmt, nnkBindStmt, nnkPatternStmt,
nnkCommentStmt, nnkStmtListExpr, nnkBlockExpr, nnkCommentStmt, nnkStmtListExpr, nnkBlockExpr,
nnkStmtListType, nnkBlockType, nnkTypeOfExpr, nnkObjectTy, nnkStmtListType, nnkBlockType, nnkTypeOfExpr, nnkObjectTy,
nnkTupleTy, nnkRecList, nnkRecCase, nnkRecWhen, nnkTupleTy, nnkRecList, nnkRecCase, nnkRecWhen,

View file

@ -14,9 +14,6 @@ version 0.9.0
- the lookup rules for generics really are too permissive; global scope only - 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 doesn't fly with closures though; for a start add a warning when processing
generic code generic code
- fix remaining closure bugs:
- test evals.nim with closures
- what about macros with closures?
Bugs Bugs
@ -47,6 +44,9 @@ version 0.9.XX
- JS gen: - JS gen:
- fix exception handling - fix exception handling
- fix remaining closure bugs:
- test evals.nim with closures
- what about macros with closures?
- document 'do' notation - document 'do' notation
- allow implicit forward declarations of procs via a pragma (so that the - allow implicit forward declarations of procs via a pragma (so that the