mirror of
https://github.com/nim-lang/Nim.git
synced 2026-09-04 21:00:31 +00:00
term rewriting macros fully implemented; still buggy
This commit is contained in:
@@ -176,7 +176,6 @@ type
|
||||
nkFromStmt, # a from * import statement
|
||||
nkIncludeStmt, # an include statement
|
||||
nkBindStmt, # a bind statement
|
||||
nkPattern, # 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
|
||||
@@ -201,6 +200,8 @@ type
|
||||
nkProcTy, # proc type
|
||||
nkEnumTy, # enum body
|
||||
nkEnumFieldDef, # `ident = expr` in an enumeration
|
||||
nkArgList, # argument list
|
||||
nkPattern, # a special pattern; used for matching
|
||||
nkReturnToken, # token used for interpretation
|
||||
nkClosure, # (prc, env)-pair (internally used for code gen)
|
||||
nkGotoState, # used for the state machine (for iterators)
|
||||
@@ -584,7 +585,7 @@ type
|
||||
# for a conditional:
|
||||
# 1 iff the symbol is defined, else 0
|
||||
# (or not in symbol table)
|
||||
# for modules, a unique index correspinding
|
||||
# for modules, a unique index corresponding
|
||||
# to the order of compilation
|
||||
offset*: int # offset of record field
|
||||
loc*: TLoc
|
||||
@@ -616,6 +617,7 @@ type
|
||||
align*: int # the type's alignment requirements
|
||||
containerID*: int # used for type checking of generics
|
||||
loc*: TLoc
|
||||
constraint*: PNode # additional constraints like 'lit|result'
|
||||
|
||||
TPair*{.final.} = object
|
||||
key*, val*: PObject
|
||||
@@ -865,13 +867,22 @@ proc newSymNode*(sym: PSym, info: TLineInfo): PNode =
|
||||
result.typ = sym.typ
|
||||
result.info = info
|
||||
|
||||
proc newNodeI(kind: TNodeKind, info: TLineInfo): PNode =
|
||||
result = newNode(kind)
|
||||
proc newNodeI(kind: TNodeKind, info: TLineInfo): PNode =
|
||||
new(result)
|
||||
result.kind = kind
|
||||
result.info = info
|
||||
|
||||
proc newNodeI*(kind: TNodeKind, info: TLineInfo, children: int): PNode =
|
||||
new(result)
|
||||
result.kind = kind
|
||||
result.info = info
|
||||
if children > 0:
|
||||
newSeq(result.sons, children)
|
||||
|
||||
proc newNode*(kind: TNodeKind, info: TLineInfo, sons: TNodeSeq = @[],
|
||||
typ: PType = nil): PNode =
|
||||
result = newNode(kind)
|
||||
new(result)
|
||||
result.kind = kind
|
||||
result.info = info
|
||||
result.typ = typ
|
||||
# XXX use shallowCopy here for ownership transfer:
|
||||
@@ -1184,3 +1195,5 @@ proc hasPattern*(s: PSym): bool {.inline.} =
|
||||
iterator items*(n: PNode): PNode =
|
||||
for i in 0.. <n.len: yield n.sons[i]
|
||||
|
||||
proc isAtom*(n: PNode): bool {.inline.} =
|
||||
result = n.kind >= nkNone and n.kind <= nkNilLit
|
||||
|
||||
@@ -1246,10 +1246,10 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
of mIncl:
|
||||
var ts = "NI" & $(size * 8)
|
||||
binaryStmtInExcl(p, e, d,
|
||||
"$1 |=(1<<((" & ts & ")($2)%(sizeof(" & ts & ")*8)));$n")
|
||||
"$1 |=((" & ts & ")(1)<<(($2)%(sizeof(" & ts & ")*8)));$n")
|
||||
of mExcl:
|
||||
var ts = "NI" & $(size * 8)
|
||||
binaryStmtInExcl(p, e, d, "$1 &= ~(1 << ((" & ts & ")($2) % (sizeof(" &
|
||||
binaryStmtInExcl(p, e, d, "$1 &= ~((" & ts & ")(1) << (($2) % (sizeof(" &
|
||||
ts & ")*8)));$n")
|
||||
of mCard:
|
||||
if size <= 4: unaryExprChar(p, e, d, "#countBits32($1)")
|
||||
|
||||
@@ -19,14 +19,17 @@ type
|
||||
mapping: TIdTable # every gensym'ed symbol needs to be mapped to some
|
||||
# new symbol
|
||||
|
||||
proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx): PNode =
|
||||
#inc genSymBaseId
|
||||
proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
|
||||
case templ.kind
|
||||
of nkSym:
|
||||
var s = templ.sym
|
||||
if s.owner.id == c.owner.id:
|
||||
if s.kind == skParam:
|
||||
result = copyTree(actual.sons[s.position])
|
||||
let x = actual.sons[s.position]
|
||||
if x.kind == nkArgList:
|
||||
for y in items(x): result.add(y)
|
||||
else:
|
||||
result.add copyTree(x)
|
||||
else:
|
||||
InternalAssert sfGenSym in s.flags
|
||||
var x = PSym(IdTableGet(c.mapping, s))
|
||||
@@ -34,16 +37,42 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx): PNode =
|
||||
x = copySym(s, false)
|
||||
x.owner = c.genSymOwner
|
||||
IdTablePut(c.mapping, s, x)
|
||||
result = newSymNode(x, templ.info)
|
||||
result.add newSymNode(x, templ.info)
|
||||
else:
|
||||
result.add copyNode(templ)
|
||||
of nkNone..nkIdent, nkType..nkNilLit: # atom
|
||||
result.add copyNode(templ)
|
||||
else:
|
||||
var res = copyNode(templ)
|
||||
for i in countup(0, sonsLen(templ) - 1):
|
||||
evalTemplateAux(templ.sons[i], actual, c, res)
|
||||
result.add res
|
||||
|
||||
when false:
|
||||
proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx): PNode =
|
||||
case templ.kind
|
||||
of nkSym:
|
||||
var s = templ.sym
|
||||
if s.owner.id == c.owner.id:
|
||||
if s.kind == skParam:
|
||||
result = copyTree(actual.sons[s.position])
|
||||
else:
|
||||
InternalAssert sfGenSym in s.flags
|
||||
var x = PSym(IdTableGet(c.mapping, s))
|
||||
if x == nil:
|
||||
x = copySym(s, false)
|
||||
x.owner = c.genSymOwner
|
||||
IdTablePut(c.mapping, s, x)
|
||||
result = newSymNode(x, templ.info)
|
||||
else:
|
||||
result = copyNode(templ)
|
||||
of nkNone..nkIdent, nkType..nkNilLit: # atom
|
||||
result = copyNode(templ)
|
||||
else:
|
||||
result = copyNode(templ)
|
||||
of nkNone..nkIdent, nkType..nkNilLit: # atom
|
||||
result = copyNode(templ)
|
||||
else:
|
||||
result = copyNode(templ)
|
||||
newSons(result, sonsLen(templ))
|
||||
for i in countup(0, sonsLen(templ) - 1):
|
||||
result.sons[i] = evalTemplateAux(templ.sons[i], actual, c)
|
||||
newSons(result, sonsLen(templ))
|
||||
for i in countup(0, sonsLen(templ) - 1):
|
||||
result.sons[i] = evalTemplateAux(templ.sons[i], actual, c)
|
||||
|
||||
proc evalTemplateArgs(n: PNode, s: PSym): PNode =
|
||||
# if the template has zero arguments, it can be called without ``()``
|
||||
@@ -78,6 +107,19 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym): PNode =
|
||||
ctx.owner = tmpl
|
||||
ctx.genSymOwner = genSymOwner
|
||||
initIdTable(ctx.mapping)
|
||||
result = evalTemplateAux(tmpl.getBody, args, ctx)
|
||||
|
||||
let body = tmpl.getBody
|
||||
if isAtom(body):
|
||||
result = newNodeI(nkPar, body.info)
|
||||
evalTemplateAux(body, args, ctx, result)
|
||||
if result.len == 1: result = result.sons[0]
|
||||
else:
|
||||
GlobalError(result.info, errIllFormedAstX,
|
||||
renderTree(result, {renderNoComments}))
|
||||
else:
|
||||
result = copyNode(body)
|
||||
#evalTemplateAux(body, args, ctx, result)
|
||||
for i in countup(0, safeLen(body) - 1):
|
||||
evalTemplateAux(body.sons[i], args, ctx, result)
|
||||
|
||||
dec(evalTemplateCounter)
|
||||
|
||||
@@ -109,7 +109,7 @@ type
|
||||
hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded,
|
||||
hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled,
|
||||
hintProcessing, hintCodeBegin, hintCodeEnd, hintConf, hintPath,
|
||||
hintConditionAlwaysTrue,
|
||||
hintConditionAlwaysTrue, hintPattern,
|
||||
hintUser
|
||||
|
||||
const
|
||||
@@ -152,7 +152,7 @@ const
|
||||
errExceptionAlreadyHandled: "exception already handled",
|
||||
errYieldNotAllowedHere: "'yield' only allowed in an iterator",
|
||||
errYieldNotAllowedInTryStmt: "'yield' cannot be used within 'try' in a non-inlined iterator",
|
||||
errInvalidNumberOfYieldExpr: "invalid number of \'yield\' expresions",
|
||||
errInvalidNumberOfYieldExpr: "invalid number of \'yield\' expressions",
|
||||
errCannotReturnExpr: "current routine cannot return an expression",
|
||||
errAttemptToRedefine: "redefinition of \'$1\'",
|
||||
errStmtInvalidAfterReturn: "statement not allowed after \'return\', \'break\' or \'raise\'",
|
||||
@@ -366,6 +366,7 @@ const
|
||||
hintConf: "used config file \'$1\' [Conf]",
|
||||
hintPath: "added path: '$1' [Path]",
|
||||
hintConditionAlwaysTrue: "condition is always true: '$1' [CondTrue]",
|
||||
hintPattern: "$1 [Pattern]",
|
||||
hintUser: "$1 [User]"]
|
||||
|
||||
const
|
||||
@@ -378,10 +379,10 @@ const
|
||||
"AnalysisLoophole", "DifferentHeaps", "WriteToForeignHeap",
|
||||
"ImplicitClosure", "EachIdentIsTuple", "User"]
|
||||
|
||||
HintsToStr*: array[0..14, string] = ["Success", "SuccessX", "LineTooLong",
|
||||
HintsToStr*: array[0..15, string] = ["Success", "SuccessX", "LineTooLong",
|
||||
"XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded",
|
||||
"ExprAlwaysX", "QuitCalled", "Processing", "CodeBegin", "CodeEnd", "Conf",
|
||||
"Path", "CondTrue",
|
||||
"Path", "CondTrue", "Pattern",
|
||||
"User"]
|
||||
|
||||
const
|
||||
|
||||
205
compiler/parampatterns.nim
Normal file
205
compiler/parampatterns.nim
Normal file
@@ -0,0 +1,205 @@
|
||||
#
|
||||
#
|
||||
# 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 strutils, ast, astalgo, types, msgs, idents, renderer, wordrecg
|
||||
|
||||
# we precompile the pattern here for efficiency into some internal
|
||||
# stack based VM :-) Why? Because it's fun; I did no benchmarks to see if that
|
||||
# actually improves performance.
|
||||
type
|
||||
TAliasRequest* = enum # first byte of the bytecode determines alias checking
|
||||
aqNone = 1, # no alias analysis requested
|
||||
aqShouldAlias, # with what?
|
||||
aqNoAlias # request noalias
|
||||
TOpcode = enum
|
||||
ppEof = 1, # end of compiled pattern
|
||||
ppOr, # we could short-cut the evaluation for 'and' and 'or',
|
||||
ppAnd, # but currently we don't
|
||||
ppNot,
|
||||
ppSym,
|
||||
ppAtom,
|
||||
ppLit,
|
||||
ppIdent,
|
||||
ppCall,
|
||||
ppSymKind,
|
||||
ppNodeKind,
|
||||
ppSideEffect,
|
||||
ppNoSideEffect
|
||||
TPatternCode = string
|
||||
|
||||
const
|
||||
MaxStackSize* = 64 ## max required stack size by the VM
|
||||
|
||||
proc patternError(n: PNode) =
|
||||
LocalError(n.info, errIllFormedAstX, renderTree(n, {renderNoComments}))
|
||||
|
||||
proc add(code: var TPatternCode, op: TOpcode) {.inline.} =
|
||||
add(code, chr(ord(op)))
|
||||
|
||||
proc whichAlias*(p: PSym): TAliasRequest =
|
||||
if p.typ.constraint != nil:
|
||||
result = TAliasRequest(p.typ.constraint.strVal[0].ord)
|
||||
|
||||
proc compileConstraints(p: PNode, result: var TPatternCode) =
|
||||
case p.kind
|
||||
of nkCallKinds:
|
||||
if p.sons[0].kind != nkIdent:
|
||||
patternError(p.sons[0])
|
||||
return
|
||||
let op = p.sons[0].ident
|
||||
if p.len == 3:
|
||||
if op.s == "|" or op.id == ord(wOr):
|
||||
compileConstraints(p.sons[1], result)
|
||||
compileConstraints(p.sons[2], result)
|
||||
result.add(ppOr)
|
||||
elif op.s == "&" or op.id == ord(wAnd):
|
||||
compileConstraints(p.sons[1], result)
|
||||
compileConstraints(p.sons[2], result)
|
||||
result.add(ppAnd)
|
||||
else:
|
||||
patternError(p)
|
||||
elif p.len == 2 and (op.s == "~" or op.id == ord(wNot)):
|
||||
compileConstraints(p.sons[1], result)
|
||||
result.add(ppNot)
|
||||
else:
|
||||
patternError(p)
|
||||
of nkAccQuoted, nkPar:
|
||||
if p.len == 1:
|
||||
compileConstraints(p.sons[0], result)
|
||||
else:
|
||||
patternError(p)
|
||||
of nkIdent:
|
||||
let spec = p.ident.s.normalize
|
||||
case spec
|
||||
of "atom": result.add(ppAtom)
|
||||
of "lit": result.add(ppLit)
|
||||
of "sym": result.add(ppSym)
|
||||
of "ident": result.add(ppIdent)
|
||||
of "call": result.add(ppCall)
|
||||
of "alias": result[0] = chr(aqShouldAlias.ord)
|
||||
of "noalias": result[0] = chr(aqNoAlias.ord)
|
||||
of "sideeffect": result.add(ppSideEffect)
|
||||
of "nosideeffect": result.add(ppNoSideEffect)
|
||||
else:
|
||||
# check all symkinds:
|
||||
InternalAssert int(high(TSymKind)) < 255
|
||||
for i in low(TSymKind)..high(TSymKind):
|
||||
if cmpIgnoreStyle(($i).substr(2), spec) == 0:
|
||||
result.add(ppSymKind)
|
||||
result.add(chr(i.ord))
|
||||
return
|
||||
# check all nodekinds:
|
||||
InternalAssert int(high(TNodeKind)) < 255
|
||||
for i in low(TNodeKind)..high(TNodeKind):
|
||||
if cmpIgnoreStyle($i, spec) == 0:
|
||||
result.add(ppSymKind)
|
||||
result.add(chr(i.ord))
|
||||
return
|
||||
patternError(p)
|
||||
else:
|
||||
patternError(p)
|
||||
|
||||
proc semNodeKindConstraints*(p: PNode): PNode =
|
||||
## does semantic checking for a node kind pattern and compiles it into an
|
||||
## efficient internal format.
|
||||
assert p.kind == nkCurlyExpr
|
||||
result = newNodeI(nkStrLit, p.info)
|
||||
result.strVal = newStringOfCap(10)
|
||||
result.strVal.add(chr(aqNone.ord))
|
||||
if p.len >= 2:
|
||||
for i in 1.. <p.len:
|
||||
compileConstraints(p.sons[i], result.strVal)
|
||||
if result.strVal.len > maxStackSize-1:
|
||||
InternalError(p.info, "parameter pattern too complex")
|
||||
else:
|
||||
patternError(p)
|
||||
result.strVal.add(ppEof)
|
||||
|
||||
type
|
||||
TSideEffectAnalysis = enum
|
||||
seUnknown, seSideEffect, seNoSideEffect
|
||||
|
||||
proc checkForSideEffects(n: PNode): TSideEffectAnalysis =
|
||||
# XXX is 'raise' a side effect?
|
||||
case n.kind
|
||||
of nkCallKinds:
|
||||
# only calls can produce side effects:
|
||||
let op = n.sons[0]
|
||||
if op.kind == nkSym and isRoutine(op.sym):
|
||||
let s = n.sym
|
||||
if sfSideEffect in s.flags:
|
||||
return seSideEffect
|
||||
# assume no side effect:
|
||||
result = seNoSideEffect
|
||||
elif tfNoSideEffect in op.typ.flags:
|
||||
# indirect call without side effects:
|
||||
result = seNoSideEffect
|
||||
else:
|
||||
# indirect call: we don't know
|
||||
result = seUnknown
|
||||
# we need to check n[0] too: (FwithSideEffectButReturnsProcWithout)(args)
|
||||
for i in 0 .. <n.len:
|
||||
let ret = checkForSideEffects(n.sons[i])
|
||||
if ret == seSideEffect: return ret
|
||||
elif ret == seUnknown and result == seNoSideEffect:
|
||||
result = seUnknown
|
||||
of nkNone..nkNilLit:
|
||||
# an atom cannot produce a side effect:
|
||||
result = seNoSideEffect
|
||||
else:
|
||||
for i in 0 .. <n.len:
|
||||
let ret = checkForSideEffects(n.sons[i])
|
||||
if ret == seSideEffect: return ret
|
||||
elif ret == seUnknown and result == seNoSideEffect:
|
||||
result = seUnknown
|
||||
|
||||
proc matchNodeKinds*(p, n: PNode): bool =
|
||||
# matches the parameter constraint 'p' against the concrete AST 'n'.
|
||||
# Efficiency matters here.
|
||||
var stack {.noinit.}: array[0..maxStackSize, bool]
|
||||
# empty patterns are true:
|
||||
stack[0] = true
|
||||
var sp = 1
|
||||
|
||||
template push(x: bool) =
|
||||
stack[sp] = x
|
||||
inc sp
|
||||
|
||||
let code = p.strVal
|
||||
var pc = 1
|
||||
while true:
|
||||
case TOpcode(code[pc])
|
||||
of ppEof: break
|
||||
of ppOr:
|
||||
stack[sp-2] = stack[sp-1] or stack[sp-2]
|
||||
dec sp
|
||||
of ppAnd:
|
||||
stack[sp-2] = stack[sp-1] and stack[sp-2]
|
||||
dec sp
|
||||
of ppNot: stack[sp-1] = not stack[sp-1]
|
||||
of ppSym: push n.kind == nkSym
|
||||
of ppAtom: push isAtom(n)
|
||||
of ppLit: push n.kind in {nkCharLit..nkNilLit}
|
||||
of ppIdent: push n.kind == nkIdent
|
||||
of ppCall: push n.kind in nkCallKinds
|
||||
of ppSymKind:
|
||||
let kind = TSymKind(code[pc+1])
|
||||
push n.kind == nkSym and n.sym.kind == kind
|
||||
inc pc
|
||||
of ppNodeKind:
|
||||
let kind = TNodeKind(code[pc+1])
|
||||
push n.kind == kind
|
||||
inc pc
|
||||
of ppSideEffect: push checkForSideEffects(n) != seNoSideEffect
|
||||
of ppNoSideEffect: push checkForSideEffects(n) == seNoSideEffect
|
||||
inc pc
|
||||
result = stack[sp-1]
|
||||
@@ -10,13 +10,16 @@
|
||||
## This module implements the pattern matching features for term rewriting
|
||||
## macro support.
|
||||
|
||||
import ast, astalgo, types, semdata, sigmatch, msgs, idents
|
||||
import
|
||||
ast, astalgo, types, semdata, sigmatch, msgs, idents, aliases, parampatterns,
|
||||
trees
|
||||
|
||||
type
|
||||
TPatternContext = object
|
||||
owner: PSym
|
||||
mapping: TIdNodeTable # maps formal parameters to nodes
|
||||
c: PContext
|
||||
subMatch: bool # subnode matches are special
|
||||
PPatternContext = var TPatternContext
|
||||
|
||||
proc matches(c: PPatternContext, p, n: PNode): bool
|
||||
@@ -53,38 +56,88 @@ proc sameTrees(a, b: PNode): bool =
|
||||
result = true
|
||||
|
||||
proc inSymChoice(sc, x: PNode): bool =
|
||||
if sc.kind in {nkOpenSymChoice, nkClosedSymChoice}:
|
||||
if sc.kind == nkClosedSymChoice:
|
||||
for i in 0.. <sc.len:
|
||||
if sc.sons[i].sym == x.sym: return true
|
||||
elif sc.kind == nkOpenSymChoice:
|
||||
# same name suffices for open sym choices!
|
||||
result = sc.sons[0].sym.name.id == x.sym.name.id
|
||||
|
||||
proc checkTypes(c: PPatternContext, p: PSym, n: PNode): bool =
|
||||
# XXX tyVarargs is special here; lots of other special cases
|
||||
# check param constraints first here as this quite optimized:
|
||||
if p.typ.constraint != nil:
|
||||
result = matchNodeKinds(p.typ.constraint, n)
|
||||
if not result: return
|
||||
if isNil(n.typ):
|
||||
result = p.typ.kind == tyStmt
|
||||
result = p.typ.kind in {tyEmpty, tyStmt}
|
||||
else:
|
||||
result = sigmatch.argtypeMatches(c.c, p.typ, n.typ)
|
||||
|
||||
proc isPatternParam(c: PPatternContext, p: PNode): bool {.inline.} =
|
||||
result = p.kind == nkSym and p.sym.kind == skParam and p.sym.owner == c.owner
|
||||
|
||||
proc matchChoice(c: PPatternContext, p, n: PNode): bool =
|
||||
for i in 1 .. <p.len:
|
||||
if matches(c, p.sons[i], n): return true
|
||||
|
||||
proc bindOrCheck(c: PPatternContext, param: PSym, n: PNode): bool =
|
||||
var pp = IdNodeTableGetLazy(c.mapping, param)
|
||||
if pp != nil:
|
||||
# check if we got the same pattern (already unified):
|
||||
result = sameTrees(pp, n) #matches(c, pp, n)
|
||||
elif checkTypes(c, param, n) and
|
||||
(param.ast == nil or checkConstraints(c, param.ast, n)):
|
||||
IdNodeTablePutLazy(c.mapping, param, n)
|
||||
result = true
|
||||
|
||||
proc matchStar(c: PPatternContext, p, n: PNode): bool =
|
||||
# match ``op*param``
|
||||
# this is quite hard:
|
||||
# match against: f(a, ..., f(b, c, f(...)))
|
||||
# we have different semantics if there is a choice as left operand:
|
||||
|
||||
proc matchStarAux(c: PPatternContext, op, n, arglist: PNode) =
|
||||
if n.kind in nkCallKinds and matches(c, op, n.sons[0]):
|
||||
for i in 1..sonsLen(n)-1: matchStarAux(c, op, n.sons[i], arglist)
|
||||
else:
|
||||
add(arglist, n)
|
||||
|
||||
if n.kind notin nkCallKinds: return false
|
||||
if p.sons[0].kind != nkPattern:
|
||||
if matches(c, p.sons[0], n.sons[0]):
|
||||
var arglist = newNodeI(nkArgList, n.info)
|
||||
arglist.typ = p.sons[1].sym.typ
|
||||
matchStarAux(c, p.sons[0], n, arglist)
|
||||
result = bindOrCheck(c, p.sons[1].sym, arglist)
|
||||
else:
|
||||
# well it matches somehow ...
|
||||
if matches(c, p.sons[0], n.sons[0]):
|
||||
result = bindOrCheck(c, p.sons[1].sym, n)
|
||||
|
||||
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
|
||||
# hidden conversions (?)
|
||||
if isPatternParam(c, p):
|
||||
result = bindOrCheck(c, p.sym, n)
|
||||
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
|
||||
if p.kind == nkSym: result = p.sym == n.sym
|
||||
elif matches(c, p, n.sym.ast): result = true
|
||||
elif p.kind == nkPattern:
|
||||
# pattern operators: | *
|
||||
let opr = p.sons[0].ident.s
|
||||
case opr
|
||||
of "|": result = matchChoice(c, p, n)
|
||||
of "*": result = matchStar(c, p, n)
|
||||
of "~": result = not matches(c, p.sons[1], n)
|
||||
else: InternalError(p.info, "invalid pattern")
|
||||
# template {add(a, `&` * b)}(a: string{noalias}, b: varargs[string]) =
|
||||
# add(a, b)
|
||||
elif p.kind == nkCurlyExpr:
|
||||
assert isPatternParam(c, p.sons[1])
|
||||
if matches(c, p.sons[0], n):
|
||||
result = bindOrCheck(c, p.sons[1].sym, n)
|
||||
elif sameKinds(p, n):
|
||||
case p.kind
|
||||
of nkSym: result = p.sym == n.sym
|
||||
@@ -92,21 +145,52 @@ proc matches(c: PPatternContext, p, n: PNode): bool =
|
||||
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:
|
||||
of nkEmpty, nkNilLit, nkType:
|
||||
result = true
|
||||
# of nkStmtList:
|
||||
# both are statement lists; we need to ignore comment statements and
|
||||
# 'nil' statements and check whether p <: n which is however trivially
|
||||
# checked as 'applyRule' is checked after every created statement
|
||||
# already; We need to ensure that the matching span is passed to the
|
||||
# macro and NOT simply 'n'!
|
||||
# XXX
|
||||
else:
|
||||
if sonsLen(p) == sonsLen(n):
|
||||
var plen = sonsLen(p)
|
||||
# special rule for p(X) ~ f(...); this also works for stuff like
|
||||
# partial case statements, etc! - Not really ... :-/
|
||||
if plen <= sonsLen(n):
|
||||
let v = lastSon(p)
|
||||
if isPatternParam(c, v) and v.sym.typ.kind == tyVarargs:
|
||||
for i in countup(0, plen - 2):
|
||||
if not matches(c, p.sons[i], n.sons[i]): return
|
||||
var arglist = newNodeI(nkArgList, n.info, sonsLen(n) - plen + 1)
|
||||
# f(1, 2, 3)
|
||||
# p(X)
|
||||
for i in countup(0, sonsLen(n) - plen):
|
||||
arglist.sons[i] = n.sons[i + plen - 1]
|
||||
# check or bind 'X':
|
||||
return bindOrCheck(c, v.sym, arglist)
|
||||
if plen == sonsLen(n):
|
||||
for i in countup(0, sonsLen(p) - 1):
|
||||
if not matches(c, p.sons[i], n.sons[i]): return
|
||||
result = true
|
||||
|
||||
proc matchStmtList(c: PPatternContext, p, n: PNode): PNode =
|
||||
proc matchRange(c: PPatternContext, p, n: PNode, i: int): bool =
|
||||
for j in 0 .. <p.len:
|
||||
if not matches(c, p.sons[j], n.sons[i+j]):
|
||||
# we need to undo any bindings:
|
||||
if not isNil(c.mapping.data): reset(c.mapping)
|
||||
return false
|
||||
result = true
|
||||
|
||||
if p.kind == nkStmtList and n.kind == p.kind and p.len < n.len:
|
||||
let n = flattenStmts(n)
|
||||
# no need to flatten 'p' here as that has already been done
|
||||
for i in 0 .. n.len - p.len:
|
||||
if matchRange(c, p, n, i):
|
||||
c.subMatch = true
|
||||
result = newNodeI(nkStmtList, n.info, 3)
|
||||
result.sons[0] = extractRange(nkStmtList, n, 0, i-1)
|
||||
result.sons[1] = extractRange(nkStmtList, n, i, i+p.len-1)
|
||||
result.sons[2] = extractRange(nkStmtList, n, i+p.len, n.len-1)
|
||||
break
|
||||
elif matches(c, p, n):
|
||||
result = n
|
||||
|
||||
# writeln(X, a); writeln(X, b); --> writeln(X, a, b)
|
||||
|
||||
proc applyRule*(c: PContext, s: PSym, n: PNode): PNode =
|
||||
@@ -115,20 +199,48 @@ proc applyRule*(c: PContext, s: PSym, n: PNode): PNode =
|
||||
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
|
||||
var m = matchStmtList(ctx, s.ast.sons[patternPos], n)
|
||||
if isNil(m): return nil
|
||||
# each parameter should have been bound; we simply setup a call and
|
||||
# let semantic checking deal with the rest :-)
|
||||
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): return nil
|
||||
result.add(x)
|
||||
# perform alias analysis here:
|
||||
if params.len >= 2:
|
||||
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): return nil
|
||||
result.add(x)
|
||||
markUsed(n, s)
|
||||
case whichAlias(param)
|
||||
of aqNone: nil
|
||||
of aqShouldAlias:
|
||||
# it suffices that it aliases for sure with *some* other param:
|
||||
var ok = false
|
||||
for j in 1 .. < result.len:
|
||||
if j != i and result.sons[j].typ != nil:
|
||||
if aliases.isPartOf(result[i], result[j]) == arYes:
|
||||
ok = true
|
||||
break
|
||||
# constraint not fullfilled:
|
||||
if not ok: return nil
|
||||
of aqNoAlias:
|
||||
# it MUST not alias with any other param:
|
||||
var ok = true
|
||||
for j in 1 .. < result.len:
|
||||
if j != i and result.sons[j].typ != nil:
|
||||
if aliases.isPartOf(result[i], result[j]) != arNo:
|
||||
ok = false
|
||||
break
|
||||
# constraint not fullfilled:
|
||||
if not ok: return nil
|
||||
|
||||
markUsed(n, s)
|
||||
if ctx.subMatch:
|
||||
assert m.len == 3
|
||||
m.sons[1] = result
|
||||
result = m
|
||||
|
||||
@@ -367,7 +367,7 @@ proc lsub(n: PNode): int =
|
||||
else: result = len(atom(n))
|
||||
of succ(nkEmpty)..pred(nkTripleStrLit), succ(nkTripleStrLit)..nkNilLit:
|
||||
result = len(atom(n))
|
||||
of nkCall, nkBracketExpr, nkCurlyExpr, nkConv:
|
||||
of nkCall, nkBracketExpr, nkCurlyExpr, nkConv, nkPattern:
|
||||
result = lsub(n.sons[0]) + lcomma(n, 1) + 2
|
||||
of nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv: result = lsub(n[1])
|
||||
of nkCast: result = lsub(n.sons[0]) + lsub(n.sons[1]) + len("cast[]()")
|
||||
@@ -377,6 +377,7 @@ proc lsub(n: PNode): int =
|
||||
of nkCommand: result = lsub(n.sons[0]) + lcomma(n, 1) + 1
|
||||
of nkExprEqExpr, nkAsgn, nkFastAsgn: result = lsons(n) + 3
|
||||
of nkPar, nkCurly, nkBracket, nkClosure: result = lcomma(n) + 2
|
||||
of nkArgList: result = lcomma(n)
|
||||
of nkTableConstr:
|
||||
result = if n.len > 0: lcomma(n) + 2 else: len("{:}")
|
||||
of nkClosedSymChoice, nkOpenSymChoice:
|
||||
@@ -666,7 +667,9 @@ 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[patternPos])
|
||||
|
||||
if n.sons[patternPos].kind != nkEmpty:
|
||||
gpattern(g, n.sons[patternPos])
|
||||
gsub(g, n.sons[genericParamsPos])
|
||||
gsub(g, n.sons[paramsPos])
|
||||
gsub(g, n.sons[pragmasPos])
|
||||
@@ -774,7 +777,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
|
||||
of nkRStrLit: put(g, tkRStrLit, atom(n))
|
||||
of nkCharLit: put(g, tkCharLit, atom(n))
|
||||
of nkNilLit: put(g, tkNil, atom(n)) # complex expressions
|
||||
of nkCall, nkConv, nkDotCall:
|
||||
of nkCall, nkConv, nkDotCall, nkPattern:
|
||||
if sonsLen(n) >= 1: gsub(g, n.sons[0])
|
||||
put(g, tkParLe, "(")
|
||||
gcomma(g, n, 1)
|
||||
@@ -859,6 +862,8 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
|
||||
put(g, tkCurlyLe, "{")
|
||||
gcomma(g, n, c)
|
||||
put(g, tkCurlyRi, "}")
|
||||
of nkArgList:
|
||||
gcomma(g, n, c)
|
||||
of nkTableConstr:
|
||||
put(g, tkCurlyLe, "{")
|
||||
if n.len > 0: gcomma(g, n, c)
|
||||
@@ -922,8 +927,9 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
|
||||
gsub(g, n.sons[2])
|
||||
of nkPrefix:
|
||||
gsub(g, n.sons[0])
|
||||
put(g, tkSpaces, space)
|
||||
gsub(g, n.sons[1])
|
||||
if n.len > 1:
|
||||
put(g, tkSpaces, space)
|
||||
gsub(g, n.sons[1])
|
||||
of nkPostfix:
|
||||
gsub(g, n.sons[1])
|
||||
gsub(g, n.sons[0])
|
||||
@@ -1046,7 +1052,6 @@ 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 nkPattern: gpattern(g, n)
|
||||
of nkForStmt, nkParForStmt: gfor(g, n)
|
||||
of nkBlockStmt, nkBlockExpr: gblock(g, n)
|
||||
of nkStaticStmt: gstaticStmt(g, n)
|
||||
|
||||
@@ -330,6 +330,9 @@ proc decodeType(r: PRodReader, info: TLineInfo): PType =
|
||||
if r.s[r.pos] == '@':
|
||||
inc(r.pos)
|
||||
result.containerID = decodeVInt(r.s, r.pos)
|
||||
if r.s[r.pos] == '`':
|
||||
inc(r.pos)
|
||||
result.constraint = decodeNode(r, UnknownLineInfo())
|
||||
decodeLoc(r, result.loc, info)
|
||||
while r.s[r.pos] == '^':
|
||||
inc(r.pos)
|
||||
|
||||
@@ -233,6 +233,9 @@ proc encodeType(w: PRodWriter, t: PType, result: var string) =
|
||||
if t.containerID != 0:
|
||||
add(result, '@')
|
||||
encodeVInt(t.containerID, result)
|
||||
if t.constraint != nil:
|
||||
add(result, '`')
|
||||
encodeNode(w, UnknownLineInfo(), t.constraint, result)
|
||||
encodeLoc(w, t.loc, result)
|
||||
for i in countup(0, sonsLen(t) - 1):
|
||||
if t.sons[i] == nil:
|
||||
|
||||
@@ -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, patterns
|
||||
evaltempl, patterns, parampatterns
|
||||
|
||||
proc semPass*(): TPass
|
||||
# implementation
|
||||
@@ -83,6 +83,7 @@ proc semTemplateExpr(c: PContext, n: PNode, s: PSym, semCheck = true): PNode
|
||||
|
||||
proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
|
||||
semCheck: bool = true): PNode
|
||||
proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode
|
||||
|
||||
proc semWhen(c: PContext, n: PNode, semCheck: bool = true): PNode
|
||||
|
||||
@@ -102,6 +103,26 @@ proc semConstExpr(c: PContext, n: PNode): PNode =
|
||||
return n
|
||||
result = evalTypedExpr(c, e)
|
||||
|
||||
proc evalPattern(c: PContext, n: PNode, info: TLineInfo): PNode =
|
||||
InternalAssert n.kind == nkCall and n.sons[0].kind == nkSym
|
||||
# we need to ensure that the resulting AST is semchecked. However, it's
|
||||
# aweful to semcheck before macro invocation, so we don't and treat
|
||||
# templates and macros as immediate in this context.
|
||||
var rule: string
|
||||
if optHints in gOptions and hintPattern in gNotes:
|
||||
rule = renderTree(n, {renderNoComments})
|
||||
let s = n.sons[0].sym
|
||||
case s.kind
|
||||
of skMacro:
|
||||
result = semMacroExpr(c, n, n, s)
|
||||
of skTemplate:
|
||||
result = semTemplateExpr(c, n, s)
|
||||
else:
|
||||
result = semDirectOp(c, n, {})
|
||||
if optHints in gOptions and hintPattern in gNotes:
|
||||
Message(info, hintPattern, rule & " --> '" &
|
||||
renderTree(result, {renderNoComments}) & "'")
|
||||
|
||||
proc applyPatterns(c: PContext, n: PNode): PNode =
|
||||
# fast exit:
|
||||
if c.patterns.len == 0 or optPatterns notin gOptions: return n
|
||||
@@ -110,14 +131,25 @@ proc applyPatterns(c: PContext, n: PNode): PNode =
|
||||
# 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
|
||||
inc(evalTemplateCounter)
|
||||
if evalTemplateCounter > 100:
|
||||
GlobalError(n.info, errTemplateInstantiationTooNested)
|
||||
result = semExpr(c, x)
|
||||
dec(evalTemplateCounter)
|
||||
let pattern = c.patterns[i]
|
||||
if not isNil(pattern):
|
||||
let x = applyRule(c, pattern, result)
|
||||
if not isNil(x):
|
||||
assert x.kind in {nkStmtList, nkCall}
|
||||
inc(evalTemplateCounter)
|
||||
if evalTemplateCounter > 100:
|
||||
GlobalError(n.info, errTemplateInstantiationTooNested)
|
||||
# deactivate this pattern:
|
||||
c.patterns[i] = nil
|
||||
if x.kind == nkStmtList:
|
||||
assert x.len == 3
|
||||
x.sons[1] = evalPattern(c, x.sons[1], n.info)
|
||||
result = flattenStmts(x)
|
||||
else:
|
||||
result = evalPattern(c, x, n.info)
|
||||
dec(evalTemplateCounter)
|
||||
# activate this pattern again:
|
||||
c.patterns[i] = pattern
|
||||
|
||||
include seminst, semcall
|
||||
|
||||
|
||||
@@ -65,18 +65,17 @@ proc inlineConst(n: PNode, s: PSym): PNode {.inline.} =
|
||||
result.typ = s.typ
|
||||
result.info = n.info
|
||||
|
||||
proc performProcvarCheck(c: PContext, n: PNode, s: PSym) =
|
||||
# XXX this not correct; it's valid to pass to templates and macros.
|
||||
# We really need another post nkCallConv check for this. Or maybe do it
|
||||
# in transform().
|
||||
var smoduleId = getModule(s).id
|
||||
if sfProcVar notin s.flags and s.typ.callConv == ccDefault and
|
||||
smoduleId != c.module.id and smoduleId != c.friendModule.id:
|
||||
LocalError(n.info, errXCannotBePassedToProcVar, s.name.s)
|
||||
|
||||
proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
|
||||
case s.kind
|
||||
of skProc, skMethod, skIterator, skConverter:
|
||||
var smoduleId = getModule(s).id
|
||||
if sfProcVar notin s.flags and s.typ.callConv == ccDefault and
|
||||
smoduleId != c.module.id and smoduleId != c.friendModule.id:
|
||||
LocalError(n.info, errXCannotBePassedToProcVar, s.name.s)
|
||||
result = symChoice(c, n, s, scClosed)
|
||||
if result.kind == nkSym:
|
||||
markIndirect(c, result.sym)
|
||||
if isGenericRoutine(result.sym):
|
||||
LocalError(n.info, errInstantiateXExplicitely, s.name.s)
|
||||
of skConst:
|
||||
markUsed(n, s)
|
||||
case skipTypes(s.typ, abstractInst).kind
|
||||
@@ -105,7 +104,8 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
|
||||
# if a proc accesses a global variable, it is not side effect free:
|
||||
if sfGlobal in s.flags:
|
||||
incl(c.p.owner.flags, sfSideEffect)
|
||||
elif s.kind == skParam and s.typ.kind == tyExpr:
|
||||
elif s.kind == skParam and s.typ.kind == tyExpr and s.typ.n != nil:
|
||||
# XXX see the hack in sigmatch.nim ...
|
||||
return s.typ.n
|
||||
result = newSymNode(s, n.info)
|
||||
# We cannot check for access to outer vars for example because it's still
|
||||
@@ -1438,6 +1438,13 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||
var s = lookUp(c, n)
|
||||
semCaptureSym(s, c.p.owner)
|
||||
result = semSym(c, n, s, flags)
|
||||
if s.kind in {skProc, skMethod, skIterator, skConverter}:
|
||||
performProcvarCheck(c, n, s)
|
||||
result = symChoice(c, n, s, scClosed)
|
||||
if result.kind == nkSym:
|
||||
markIndirect(c, result.sym)
|
||||
if isGenericRoutine(result.sym):
|
||||
LocalError(n.info, errInstantiateXExplicitely, s.name.s)
|
||||
of nkSym:
|
||||
# because of the changed symbol binding, this does not mean that we
|
||||
# don't have to check the symbol for semantics here again!
|
||||
|
||||
@@ -196,8 +196,7 @@ proc getIntervalType*(m: TMagic, n: PNode): PType =
|
||||
let x = b.intVal|+|1
|
||||
if (x and -x) == x and x >= 0:
|
||||
result = makeRange(a.typ, 0, b.intVal)
|
||||
of mModI, mModI64, mModU:
|
||||
# so ... if you ever wondered about modulo's signedness; this defines it:
|
||||
of mModU:
|
||||
let a = n.sons[1]
|
||||
let b = n.sons[2]
|
||||
if b.kind in {nkIntLit..nkUInt64Lit}:
|
||||
@@ -205,6 +204,15 @@ proc getIntervalType*(m: TMagic, n: PNode): PType =
|
||||
result = makeRange(a.typ, 0, b.intVal-1)
|
||||
else:
|
||||
result = makeRange(a.typ, b.intVal+1, 0)
|
||||
of mModI, mModI64:
|
||||
# so ... if you ever wondered about modulo's signedness; this defines it:
|
||||
let a = n.sons[1]
|
||||
let b = n.sons[2]
|
||||
if b.kind in {nkIntLit..nkUInt64Lit}:
|
||||
if b.intVal >= 0:
|
||||
result = makeRange(a.typ, -(b.intVal-1), b.intVal-1)
|
||||
else:
|
||||
result = makeRange(a.typ, b.intVal+1, -(b.intVal+1))
|
||||
of mDivI, mDivI64, mDivU:
|
||||
binaryOp(`|div|`)
|
||||
of mMinI, mMinI64:
|
||||
|
||||
@@ -933,6 +933,8 @@ proc semStaticStmt(c: PContext, n: PNode): PNode =
|
||||
result = evalStaticExpr(c.module, a)
|
||||
if result.isNil:
|
||||
LocalError(n.info, errCannotInterpretNodeX, renderTree(n))
|
||||
elif result.kind == nkEmpty:
|
||||
result = newNodeI(nkNilLit, n.info)
|
||||
|
||||
# special marker values that indicates that we are
|
||||
# 1) AnalyzingDestructor: currenlty analyzing the type for destructor
|
||||
|
||||
@@ -89,7 +89,7 @@ proc semBindStmt(c: PContext, n: PNode, toBind: var TIntSet): PNode =
|
||||
for x in items(sc): toBind.incl(x.sym.id)
|
||||
else:
|
||||
illFormedAst(a)
|
||||
result = newNodeI(nkEmpty, n.info)
|
||||
result = newNodeI(nkNilLit, n.info)
|
||||
|
||||
proc replaceIdentBySym(n: var PNode, s: PNode) =
|
||||
case n.kind
|
||||
@@ -98,16 +98,10 @@ 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 =
|
||||
@@ -188,8 +182,6 @@ 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
|
||||
@@ -305,8 +297,9 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
|
||||
# so we use the generic code for nkDotExpr too
|
||||
if n.kind == nkDotExpr or n.kind == nkAccQuoted:
|
||||
let s = QualifiedLookUp(c.c, n, {})
|
||||
if s != nil and Contains(c.toBind, s.id):
|
||||
return symChoice(c.c, n, s, scClosed)
|
||||
if s != nil:
|
||||
if Contains(c.toBind, s.id):
|
||||
return symChoice(c.c, n, s, scClosed)
|
||||
result = n
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
result.sons[i] = semTemplBody(c, n.sons[i])
|
||||
@@ -405,7 +398,6 @@ 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:
|
||||
@@ -426,17 +418,109 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
|
||||
else:
|
||||
SymTabReplace(c.tab.stack[curScope], proto, s)
|
||||
|
||||
proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
|
||||
template templToExpand(s: expr): expr =
|
||||
s.kind == skTemplate and (s.typ.len == 1 or sfImmediate in s.flags)
|
||||
|
||||
proc handleSym(c: var TemplCtx, n: PNode, s: PSym): PNode =
|
||||
if s != nil:
|
||||
if s.owner == c.owner and s.kind == skParam:
|
||||
incl(s.flags, sfUsed)
|
||||
result = newSymNode(s, n.info)
|
||||
elif Contains(c.toBind, s.id):
|
||||
result = symChoice(c.c, n, s, scClosed)
|
||||
elif templToExpand(s):
|
||||
result = semPatternBody(c, semTemplateExpr(c.c, n, s, false))
|
||||
else:
|
||||
result = symChoice(c.c, n, s, scOpen)
|
||||
else:
|
||||
result = n
|
||||
|
||||
proc expectParam(c: var TemplCtx, n: PNode): PNode =
|
||||
let s = QualifiedLookUp(c.c, n, {})
|
||||
if s != nil and s.owner == c.owner and s.kind == skParam:
|
||||
incl(s.flags, sfUsed)
|
||||
result = newSymNode(s, n.info)
|
||||
else:
|
||||
localError(n.info, errInvalidExpression)
|
||||
result = n
|
||||
|
||||
result = n
|
||||
case n.kind
|
||||
of nkIdent:
|
||||
let s = QualifiedLookUp(c.c, n, {})
|
||||
result = handleSym(c, n, s)
|
||||
of nkBindStmt:
|
||||
result = semBindStmt(c.c, n, c.toBind)
|
||||
of nkEmpty, nkSym..nkNilLit: nil
|
||||
of nkCurlyExpr:
|
||||
# we support '(pattern){x}' to bind a subpattern to a parameter 'x':
|
||||
if n.len != 2 or n.sons[1].kind != nkIdent:
|
||||
localError(n.info, errInvalidExpression)
|
||||
else:
|
||||
n.sons[0] = semPatternBody(c, n.sons[0])
|
||||
n.sons[1] = expectParam(c, n.sons[1])
|
||||
of nkCallKinds:
|
||||
let s = QualifiedLookUp(c.c, n.sons[0], {})
|
||||
if s != nil:
|
||||
if s.owner == c.owner and s.kind == skParam: nil
|
||||
elif Contains(c.toBind, s.id): nil
|
||||
elif templToExpand(s):
|
||||
return semPatternBody(c, semTemplateExpr(c.c, n, s, false))
|
||||
|
||||
if n.kind == nkInfix and n.sons[0].kind == nkIdent:
|
||||
# we interpret `*` and `|` only as pattern operators if they occur in
|
||||
# infix notation, so that '`*`(a, b)' can be used for verbatim matching:
|
||||
let opr = n.sons[0]
|
||||
if opr.ident.s == "*":
|
||||
result = newNodeI(nkPattern, n.info, n.len)
|
||||
result.sons[0] = opr
|
||||
result.sons[1] = semPatternBody(c, n.sons[1])
|
||||
result.sons[2] = expectParam(c, n.sons[2])
|
||||
return
|
||||
elif opr.ident.s == "|":
|
||||
result = newNodeI(nkPattern, n.info, n.len)
|
||||
result.sons[0] = opr
|
||||
result.sons[1] = semPatternBody(c, n.sons[1])
|
||||
result.sons[2] = semPatternBody(c, n.sons[2])
|
||||
return
|
||||
|
||||
if n.kind == nkPrefix and n.sons[0].kind == nkIdent:
|
||||
let opr = n.sons[0]
|
||||
if opr.ident.s == "~":
|
||||
result = newNodeI(nkPattern, n.info, n.len)
|
||||
result.sons[0] = opr
|
||||
result.sons[1] = semPatternBody(c, n.sons[1])
|
||||
return
|
||||
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
result.sons[i] = semPatternBody(c, n.sons[i])
|
||||
else:
|
||||
# dotExpr is ambiguous: note that we explicitely allow 'x.TemplateParam',
|
||||
# so we use the generic code for nkDotExpr too
|
||||
case n.kind
|
||||
of nkDotExpr, nkAccQuoted:
|
||||
let s = QualifiedLookUp(c.c, n, {})
|
||||
if s != nil:
|
||||
if Contains(c.toBind, s.id):
|
||||
return symChoice(c.c, n, s, scClosed)
|
||||
return symChoice(c.c, n, s, scOpen)
|
||||
of nkPar:
|
||||
if n.len == 1: return semPatternBody(c, n.sons[0])
|
||||
else: nil
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
result.sons[i] = semPatternBody(c, n.sons[i])
|
||||
|
||||
proc semPattern(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)
|
||||
if result.kind in {nkStmtList, nkStmtListExpr} and result.len == 1:
|
||||
result = result.sons[0]
|
||||
result = flattenStmts(semPatternBody(ctx, n))
|
||||
if result.kind in {nkStmtList, nkStmtListExpr}:
|
||||
if result.len == 1:
|
||||
result = result.sons[0]
|
||||
elif result.len == 0:
|
||||
LocalError(n.info, errInvalidExpression)
|
||||
closeScope(c.tab)
|
||||
|
||||
@@ -826,9 +826,8 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
of nkCurlyExpr:
|
||||
result = semTypeNode(c, n.sons[0], nil)
|
||||
if result != nil:
|
||||
result = copyType(result, getCurrOwner(), false)
|
||||
for i in countup(1, n.len - 1):
|
||||
result.rawAddSon(semTypeNode(c, n.sons[i], nil))
|
||||
result = copyType(result, getCurrOwner(), true)
|
||||
result.constraint = semNodeKindConstraints(n)
|
||||
of nkWhenStmt:
|
||||
var whenResult = semWhen(c, n, false)
|
||||
if whenResult.kind == nkStmtList: whenResult.kind = nkStmtListType
|
||||
@@ -844,6 +843,12 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
of mOrdinal: result = semOrdinal(c, n, prev)
|
||||
of mSeq: result = semContainer(c, n, tySequence, "seq", prev)
|
||||
of mVarargs: result = semVarargs(c, n, prev)
|
||||
of mExpr, mTypeDesc:
|
||||
result = semTypeNode(c, n.sons[0], nil)
|
||||
if result != nil:
|
||||
result = copyType(result, getCurrOwner(), false)
|
||||
for i in countup(1, n.len - 1):
|
||||
result.rawAddSon(semTypeNode(c, n.sons[i], nil))
|
||||
else: result = semGeneric(c, n, s, prev)
|
||||
of nkIdent, nkDotExpr, nkAccQuoted:
|
||||
var s = semTypeIdent(c, n)
|
||||
|
||||
@@ -151,3 +151,19 @@ proc whichPragma*(n: PNode): TSpecialWord =
|
||||
let key = if n.kind == nkExprColonExpr: n.sons[0] else: n
|
||||
if key.kind == nkIdent: result = whichKeyword(key.ident)
|
||||
|
||||
proc unnestStmts(n, result: PNode) =
|
||||
if n.kind == nkStmtList:
|
||||
for x in items(n): unnestStmts(x, result)
|
||||
elif n.kind notin {nkCommentStmt, nkNilLit}:
|
||||
result.add(n)
|
||||
|
||||
proc flattenStmts*(n: PNode): PNode =
|
||||
## flattens a nested statement list; used for pattern matching
|
||||
result = newNodeI(nkStmtList, n.info)
|
||||
unnestStmts(n, result)
|
||||
if result.len == 1:
|
||||
result = result.sons[0]
|
||||
|
||||
proc extractRange*(k: TNodeKind, n: PNode, a, b: int): PNode =
|
||||
result = newNodeI(k, n.info, b-a+1)
|
||||
for i in 0 .. b-a: result.sons[i] = n.sons[i+a]
|
||||
|
||||
@@ -507,7 +507,8 @@ proc base(t: PType): PType =
|
||||
|
||||
proc firstOrd(t: PType): biggestInt =
|
||||
case t.kind
|
||||
of tyBool, tyChar, tySequence, tyOpenArray, tyString, tyVarargs: result = 0
|
||||
of tyBool, tyChar, tySequence, tyOpenArray, tyString, tyVarargs, tyProxy:
|
||||
result = 0
|
||||
of tySet, tyVar: result = firstOrd(t.sons[0])
|
||||
of tyArray, tyArrayConstr: result = firstOrd(t.sons[0])
|
||||
of tyRange:
|
||||
@@ -564,6 +565,7 @@ proc lastOrd(t: PType): biggestInt =
|
||||
result = t.n.sons[sonsLen(t.n) - 1].sym.position
|
||||
of tyGenericInst, tyDistinct, tyConst, tyMutable:
|
||||
result = lastOrd(lastSon(t))
|
||||
of tyProxy: result = 0
|
||||
else:
|
||||
InternalError("invalid kind for last(" & $t.kind & ')')
|
||||
result = 0
|
||||
@@ -591,7 +593,7 @@ type
|
||||
# (few elements expected)
|
||||
|
||||
proc initSameTypeClosure: TSameTypeClosure =
|
||||
# we do the initialization lazy for performance (avoids memory allocations)
|
||||
# we do the initialization lazily for performance (avoids memory allocations)
|
||||
nil
|
||||
|
||||
proc containsOrIncl(c: var TSameTypeClosure, a, b: PType): bool =
|
||||
@@ -613,7 +615,7 @@ proc SameTypeOrNil*(a, b: PType): bool =
|
||||
result = true
|
||||
else:
|
||||
if a == nil or b == nil: result = false
|
||||
else:
|
||||
else:
|
||||
var c = initSameTypeClosure()
|
||||
result = SameTypeAux(a, b, c)
|
||||
|
||||
|
||||
@@ -621,14 +621,14 @@ determined). Assignments from the base type to one of its subrange types
|
||||
A subrange type has the same size as its base type (``int`` in the example).
|
||||
|
||||
Nimrod requires `interval arithmetic`:idx: for subrange types over a set
|
||||
of built-in operators that involve constants: ``x mod 3`` is of
|
||||
of built-in operators that involve constants: ``x %% 3`` is of
|
||||
type ``range[0..2]``. The following built-in operators for integers are
|
||||
affected by this rule: ``-``, ``+``, ``*``, ``min``, ``max``, ``succ``,
|
||||
``pred``, ``mod``, ``div``, ``and`` (bitwise ``and``).
|
||||
``pred``, ``mod``, ``div``, ``%%``, ``and`` (bitwise ``and``).
|
||||
|
||||
Bitwise ``and`` only produces a ``range`` if one of its operands is a
|
||||
constant *x* so that (x+1) is a number of two.
|
||||
(Bitwise ``and`` is then a ``mod`` operation.)
|
||||
(Bitwise ``and`` is then a ``%%`` operation.)
|
||||
|
||||
This means that the following code is accepted:
|
||||
|
||||
|
||||
@@ -46,14 +46,17 @@ type
|
||||
nnkYieldStmt, nnkTryStmt, nnkFinally, nnkRaiseStmt,
|
||||
nnkReturnStmt, nnkBreakStmt, nnkContinueStmt, nnkBlockStmt, nnkStaticStmt,
|
||||
nnkDiscardStmt, nnkStmtList, nnkImportStmt, nnkFromStmt,
|
||||
nnkIncludeStmt, nnkBindStmt, nnkPattern,
|
||||
nnkIncludeStmt, nnkBindStmt,
|
||||
nnkCommentStmt, nnkStmtListExpr, nnkBlockExpr,
|
||||
nnkStmtListType, nnkBlockType, nnkTypeOfExpr, nnkObjectTy,
|
||||
nnkTupleTy, nnkRecList, nnkRecCase, nnkRecWhen,
|
||||
nnkRefTy, nnkPtrTy, nnkVarTy,
|
||||
nnkConstTy, nnkMutableTy,
|
||||
nnkDistinctTy,
|
||||
nnkProcTy, nnkEnumTy, nnkEnumFieldDef, nnkReturnToken
|
||||
nnkProcTy, nnkEnumTy,
|
||||
nnkEnumFieldDef,
|
||||
nnkArglist, nnkPattern
|
||||
nnkReturnToken
|
||||
TNimNodeKinds* = set[TNimrodNodeKind]
|
||||
TNimrodTypeKind* = enum
|
||||
ntyNone, ntyBool, ntyChar, ntyEmpty,
|
||||
|
||||
@@ -1314,10 +1314,11 @@ iterator items*(a: cstring): char {.inline.} =
|
||||
yield a[i]
|
||||
inc(i)
|
||||
|
||||
iterator items*(E: typedesc{enum}): E =
|
||||
## iterates over the values of the enum ``E``.
|
||||
for v in low(E)..high(E):
|
||||
yield v
|
||||
when not defined(booting):
|
||||
iterator items*(E: typedesc[enum]): E =
|
||||
## iterates over the values of the enum ``E``.
|
||||
for v in low(E)..high(E):
|
||||
yield v
|
||||
|
||||
iterator pairs*[T](a: openarray[T]): tuple[key: int, val: T] {.inline.} =
|
||||
## iterates over each item of `a`. Yields ``(index, a[index])`` pairs.
|
||||
@@ -1832,12 +1833,9 @@ when not defined(EcmaScript) and not defined(NimrodVM):
|
||||
## ``CRLF``. The newline character(s) are not part of the returned string.
|
||||
## Returns ``false`` if the end of the file has been reached, ``true``
|
||||
## otherwise. If ``false`` is returned `line` contains no new data.
|
||||
proc writeln*[Ty](f: TFile, x: Ty) {.inline.}
|
||||
## writes a value `x` to `f` and then writes "\n".
|
||||
## May throw an IO exception.
|
||||
|
||||
proc writeln*[Ty](f: TFile, x: varargs[Ty, `$`]) {.inline.}
|
||||
## writes a value `x` to `f` and then writes "\n".
|
||||
## writes the values `x` to `f` and then writes "\n".
|
||||
## May throw an IO exception.
|
||||
|
||||
proc getFileSize*(f: TFile): int64
|
||||
|
||||
@@ -146,10 +146,6 @@ proc EndOfFile(f: TFile): bool =
|
||||
ungetc(c, f)
|
||||
return c < 0'i32
|
||||
|
||||
proc writeln[Ty](f: TFile, x: Ty) =
|
||||
write(f, x)
|
||||
write(f, "\n")
|
||||
|
||||
proc writeln[Ty](f: TFile, x: varargs[Ty, `$`]) =
|
||||
for i in items(x): write(f, i)
|
||||
write(f, "\n")
|
||||
|
||||
3
todo.txt
3
todo.txt
@@ -2,8 +2,8 @@ version 0.9.0
|
||||
=============
|
||||
|
||||
- make 'm: stmt' use overloading resolution
|
||||
- document pattern matching
|
||||
|
||||
- improve pattern matching: introduce meta operators, statement list support
|
||||
- make 'bind' default for templates and introduce 'mixin'
|
||||
|
||||
- implement "closure tuple consists of a single 'ref'" optimization
|
||||
@@ -19,6 +19,7 @@ version 0.9.0
|
||||
Bugs
|
||||
----
|
||||
|
||||
- sneaking with qualifiedLookup() is really broken!
|
||||
- bug: aporia.nim(968, 5) Error: ambiguous identifier: 'DELETE' --
|
||||
use a qualifier
|
||||
- bug: pragma statements in combination with symbol files are evaluated twice
|
||||
|
||||
Reference in New Issue
Block a user