diff options
Diffstat (limited to 'compiler')
-rwxr-xr-x | compiler/ast.nim | 23 | ||||
-rwxr-xr-x | compiler/ccgexprs.nim | 4 | ||||
-rw-r--r-- | compiler/evaltempl.nim | 62 | ||||
-rwxr-xr-x | compiler/msgs.nim | 9 | ||||
-rw-r--r-- | compiler/parampatterns.nim | 205 | ||||
-rw-r--r-- | compiler/patterns.nim | 200 | ||||
-rwxr-xr-x | compiler/renderer.nim | 17 | ||||
-rwxr-xr-x | compiler/rodread.nim | 3 | ||||
-rwxr-xr-x | compiler/rodwrite.nim | 3 | ||||
-rwxr-xr-x | compiler/sem.nim | 50 | ||||
-rwxr-xr-x | compiler/semexprs.nim | 29 | ||||
-rwxr-xr-x | compiler/semfold.nim | 12 | ||||
-rwxr-xr-x | compiler/semstmts.nim | 2 | ||||
-rwxr-xr-x | compiler/semtempl.nim | 122 | ||||
-rwxr-xr-x | compiler/semtypes.nim | 11 | ||||
-rwxr-xr-x | compiler/trees.nim | 16 | ||||
-rwxr-xr-x | compiler/types.nim | 8 |
17 files changed, 658 insertions, 118 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index 52502c5de..2b0fe6470 100755 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -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 diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index ba4111e9e..d10b37432 100755 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -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)") diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim index d7006d34d..265fe3fe1 100644 --- a/compiler/evaltempl.nim +++ b/compiler/evaltempl.nim @@ -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 = copyNode(templ) + result.add copyNode(templ) of nkNone..nkIdent, nkType..nkNilLit: # atom - result = copyNode(templ) + result.add copyNode(templ) else: - result = copyNode(templ) - newSons(result, sonsLen(templ)) + var res = copyNode(templ) for i in countup(0, sonsLen(templ) - 1): - result.sons[i] = evalTemplateAux(templ.sons[i], actual, c) + 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) + 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) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index f61e03e79..d0ddf7721 100755 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -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 diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim new file mode 100644 index 000000000..83585dbd4 --- /dev/null +++ b/compiler/parampatterns.nim @@ -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] diff --git a/compiler/patterns.nim b/compiler/patterns.nim index 7109d9975..ceadfe350 100644 --- a/compiler/patterns.nim +++ b/compiler/patterns.nim @@ -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 diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 88fae67cc..8a8e1e849 100755 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -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) diff --git a/compiler/rodread.nim b/compiler/rodread.nim index 7c924511f..4461641db 100755 --- a/compiler/rodread.nim +++ b/compiler/rodread.nim @@ -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) diff --git a/compiler/rodwrite.nim b/compiler/rodwrite.nim index 75e8a02a7..5be9a2439 100755 --- a/compiler/rodwrite.nim +++ b/compiler/rodwrite.nim @@ -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: diff --git a/compiler/sem.nim b/compiler/sem.nim index 8052d8b00..24bee4a22 100755 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -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 diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 1bf412a26..fe698bffb 100755 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -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! diff --git a/compiler/semfold.nim b/compiler/semfold.nim index 2d611aac5..c2958ca5e 100755 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -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: diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 2a16c32e2..04766ae58 100755 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -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 diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index f77dbf1a2..efdfce78f 100755 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -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) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 8ec3c337c..7c1318ada 100755 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -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) diff --git a/compiler/trees.nim b/compiler/trees.nim index e2c9b1b16..08b89a76e 100755 --- a/compiler/trees.nim +++ b/compiler/trees.nim @@ -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] diff --git a/compiler/types.nim b/compiler/types.nim index aca168ba8..8a01e6c76 100755 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -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) |