mirror of
https://github.com/nim-lang/Nim.git
synced 2026-06-04 10:54:42 +00:00
first steps towards term rewriting macros
This commit is contained in:
@@ -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]
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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
|
||||
|
||||
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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
6
todo.txt
6
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
|
||||
|
||||
Reference in New Issue
Block a user