term rewriting macros fully implemented; still buggy
This commit is contained in:
parent
b56df72a32
commit
af7c92c003
22 changed files with 676 additions and 138 deletions
|
|
@ -176,7 +176,6 @@ type
|
||||||
nkFromStmt, # a from * import statement
|
nkFromStmt, # a from * import statement
|
||||||
nkIncludeStmt, # an include statement
|
nkIncludeStmt, # an include statement
|
||||||
nkBindStmt, # a bind statement
|
nkBindStmt, # a bind statement
|
||||||
nkPattern, # a pattern statement ('as' statement)
|
|
||||||
nkCommentStmt, # a comment statement
|
nkCommentStmt, # a comment statement
|
||||||
nkStmtListExpr, # a statement list followed by an expr; this is used
|
nkStmtListExpr, # a statement list followed by an expr; this is used
|
||||||
# to allow powerful multi-line templates
|
# to allow powerful multi-line templates
|
||||||
|
|
@ -201,6 +200,8 @@ type
|
||||||
nkProcTy, # proc type
|
nkProcTy, # proc type
|
||||||
nkEnumTy, # enum body
|
nkEnumTy, # enum body
|
||||||
nkEnumFieldDef, # `ident = expr` in an enumeration
|
nkEnumFieldDef, # `ident = expr` in an enumeration
|
||||||
|
nkArgList, # argument list
|
||||||
|
nkPattern, # a special pattern; used for matching
|
||||||
nkReturnToken, # token used for interpretation
|
nkReturnToken, # token used for interpretation
|
||||||
nkClosure, # (prc, env)-pair (internally used for code gen)
|
nkClosure, # (prc, env)-pair (internally used for code gen)
|
||||||
nkGotoState, # used for the state machine (for iterators)
|
nkGotoState, # used for the state machine (for iterators)
|
||||||
|
|
@ -584,7 +585,7 @@ type
|
||||||
# for a conditional:
|
# for a conditional:
|
||||||
# 1 iff the symbol is defined, else 0
|
# 1 iff the symbol is defined, else 0
|
||||||
# (or not in symbol table)
|
# (or not in symbol table)
|
||||||
# for modules, a unique index correspinding
|
# for modules, a unique index corresponding
|
||||||
# to the order of compilation
|
# to the order of compilation
|
||||||
offset*: int # offset of record field
|
offset*: int # offset of record field
|
||||||
loc*: TLoc
|
loc*: TLoc
|
||||||
|
|
@ -616,6 +617,7 @@ type
|
||||||
align*: int # the type's alignment requirements
|
align*: int # the type's alignment requirements
|
||||||
containerID*: int # used for type checking of generics
|
containerID*: int # used for type checking of generics
|
||||||
loc*: TLoc
|
loc*: TLoc
|
||||||
|
constraint*: PNode # additional constraints like 'lit|result'
|
||||||
|
|
||||||
TPair*{.final.} = object
|
TPair*{.final.} = object
|
||||||
key*, val*: PObject
|
key*, val*: PObject
|
||||||
|
|
@ -865,13 +867,22 @@ proc newSymNode*(sym: PSym, info: TLineInfo): PNode =
|
||||||
result.typ = sym.typ
|
result.typ = sym.typ
|
||||||
result.info = info
|
result.info = info
|
||||||
|
|
||||||
proc newNodeI(kind: TNodeKind, info: TLineInfo): PNode =
|
proc newNodeI(kind: TNodeKind, info: TLineInfo): PNode =
|
||||||
result = newNode(kind)
|
new(result)
|
||||||
|
result.kind = kind
|
||||||
result.info = info
|
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 = @[],
|
proc newNode*(kind: TNodeKind, info: TLineInfo, sons: TNodeSeq = @[],
|
||||||
typ: PType = nil): PNode =
|
typ: PType = nil): PNode =
|
||||||
result = newNode(kind)
|
new(result)
|
||||||
|
result.kind = kind
|
||||||
result.info = info
|
result.info = info
|
||||||
result.typ = typ
|
result.typ = typ
|
||||||
# XXX use shallowCopy here for ownership transfer:
|
# XXX use shallowCopy here for ownership transfer:
|
||||||
|
|
@ -1184,3 +1195,5 @@ proc hasPattern*(s: PSym): bool {.inline.} =
|
||||||
iterator items*(n: PNode): PNode =
|
iterator items*(n: PNode): PNode =
|
||||||
for i in 0.. <n.len: yield n.sons[i]
|
for i in 0.. <n.len: yield n.sons[i]
|
||||||
|
|
||||||
|
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:
|
of mIncl:
|
||||||
var ts = "NI" & $(size * 8)
|
var ts = "NI" & $(size * 8)
|
||||||
binaryStmtInExcl(p, e, d,
|
binaryStmtInExcl(p, e, d,
|
||||||
"$1 |=(1<<((" & ts & ")($2)%(sizeof(" & ts & ")*8)));$n")
|
"$1 |=((" & ts & ")(1)<<(($2)%(sizeof(" & ts & ")*8)));$n")
|
||||||
of mExcl:
|
of mExcl:
|
||||||
var ts = "NI" & $(size * 8)
|
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")
|
ts & ")*8)));$n")
|
||||||
of mCard:
|
of mCard:
|
||||||
if size <= 4: unaryExprChar(p, e, d, "#countBits32($1)")
|
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
|
mapping: TIdTable # every gensym'ed symbol needs to be mapped to some
|
||||||
# new symbol
|
# new symbol
|
||||||
|
|
||||||
proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx): PNode =
|
proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
|
||||||
#inc genSymBaseId
|
|
||||||
case templ.kind
|
case templ.kind
|
||||||
of nkSym:
|
of nkSym:
|
||||||
var s = templ.sym
|
var s = templ.sym
|
||||||
if s.owner.id == c.owner.id:
|
if s.owner.id == c.owner.id:
|
||||||
if s.kind == skParam:
|
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:
|
else:
|
||||||
InternalAssert sfGenSym in s.flags
|
InternalAssert sfGenSym in s.flags
|
||||||
var x = PSym(IdTableGet(c.mapping, s))
|
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 = copySym(s, false)
|
||||||
x.owner = c.genSymOwner
|
x.owner = c.genSymOwner
|
||||||
IdTablePut(c.mapping, s, x)
|
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:
|
else:
|
||||||
result = copyNode(templ)
|
result = copyNode(templ)
|
||||||
of nkNone..nkIdent, nkType..nkNilLit: # atom
|
newSons(result, sonsLen(templ))
|
||||||
result = copyNode(templ)
|
for i in countup(0, sonsLen(templ) - 1):
|
||||||
else:
|
result.sons[i] = evalTemplateAux(templ.sons[i], actual, c)
|
||||||
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 =
|
proc evalTemplateArgs(n: PNode, s: PSym): PNode =
|
||||||
# if the template has zero arguments, it can be called without ``()``
|
# 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.owner = tmpl
|
||||||
ctx.genSymOwner = genSymOwner
|
ctx.genSymOwner = genSymOwner
|
||||||
initIdTable(ctx.mapping)
|
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)
|
dec(evalTemplateCounter)
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,7 @@ type
|
||||||
hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded,
|
hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded,
|
||||||
hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled,
|
hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled,
|
||||||
hintProcessing, hintCodeBegin, hintCodeEnd, hintConf, hintPath,
|
hintProcessing, hintCodeBegin, hintCodeEnd, hintConf, hintPath,
|
||||||
hintConditionAlwaysTrue,
|
hintConditionAlwaysTrue, hintPattern,
|
||||||
hintUser
|
hintUser
|
||||||
|
|
||||||
const
|
const
|
||||||
|
|
@ -152,7 +152,7 @@ const
|
||||||
errExceptionAlreadyHandled: "exception already handled",
|
errExceptionAlreadyHandled: "exception already handled",
|
||||||
errYieldNotAllowedHere: "'yield' only allowed in an iterator",
|
errYieldNotAllowedHere: "'yield' only allowed in an iterator",
|
||||||
errYieldNotAllowedInTryStmt: "'yield' cannot be used within 'try' in a non-inlined 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",
|
errCannotReturnExpr: "current routine cannot return an expression",
|
||||||
errAttemptToRedefine: "redefinition of \'$1\'",
|
errAttemptToRedefine: "redefinition of \'$1\'",
|
||||||
errStmtInvalidAfterReturn: "statement not allowed after \'return\', \'break\' or \'raise\'",
|
errStmtInvalidAfterReturn: "statement not allowed after \'return\', \'break\' or \'raise\'",
|
||||||
|
|
@ -366,6 +366,7 @@ const
|
||||||
hintConf: "used config file \'$1\' [Conf]",
|
hintConf: "used config file \'$1\' [Conf]",
|
||||||
hintPath: "added path: '$1' [Path]",
|
hintPath: "added path: '$1' [Path]",
|
||||||
hintConditionAlwaysTrue: "condition is always true: '$1' [CondTrue]",
|
hintConditionAlwaysTrue: "condition is always true: '$1' [CondTrue]",
|
||||||
|
hintPattern: "$1 [Pattern]",
|
||||||
hintUser: "$1 [User]"]
|
hintUser: "$1 [User]"]
|
||||||
|
|
||||||
const
|
const
|
||||||
|
|
@ -378,10 +379,10 @@ const
|
||||||
"AnalysisLoophole", "DifferentHeaps", "WriteToForeignHeap",
|
"AnalysisLoophole", "DifferentHeaps", "WriteToForeignHeap",
|
||||||
"ImplicitClosure", "EachIdentIsTuple", "User"]
|
"ImplicitClosure", "EachIdentIsTuple", "User"]
|
||||||
|
|
||||||
HintsToStr*: array[0..14, string] = ["Success", "SuccessX", "LineTooLong",
|
HintsToStr*: array[0..15, string] = ["Success", "SuccessX", "LineTooLong",
|
||||||
"XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded",
|
"XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded",
|
||||||
"ExprAlwaysX", "QuitCalled", "Processing", "CodeBegin", "CodeEnd", "Conf",
|
"ExprAlwaysX", "QuitCalled", "Processing", "CodeBegin", "CodeEnd", "Conf",
|
||||||
"Path", "CondTrue",
|
"Path", "CondTrue", "Pattern",
|
||||||
"User"]
|
"User"]
|
||||||
|
|
||||||
const
|
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
|
## This module implements the pattern matching features for term rewriting
|
||||||
## macro support.
|
## macro support.
|
||||||
|
|
||||||
import ast, astalgo, types, semdata, sigmatch, msgs, idents
|
import
|
||||||
|
ast, astalgo, types, semdata, sigmatch, msgs, idents, aliases, parampatterns,
|
||||||
|
trees
|
||||||
|
|
||||||
type
|
type
|
||||||
TPatternContext = object
|
TPatternContext = object
|
||||||
owner: PSym
|
owner: PSym
|
||||||
mapping: TIdNodeTable # maps formal parameters to nodes
|
mapping: TIdNodeTable # maps formal parameters to nodes
|
||||||
c: PContext
|
c: PContext
|
||||||
|
subMatch: bool # subnode matches are special
|
||||||
PPatternContext = var TPatternContext
|
PPatternContext = var TPatternContext
|
||||||
|
|
||||||
proc matches(c: PPatternContext, p, n: PNode): bool
|
proc matches(c: PPatternContext, p, n: PNode): bool
|
||||||
|
|
@ -53,38 +56,88 @@ proc sameTrees(a, b: PNode): bool =
|
||||||
result = true
|
result = true
|
||||||
|
|
||||||
proc inSymChoice(sc, x: PNode): bool =
|
proc inSymChoice(sc, x: PNode): bool =
|
||||||
if sc.kind in {nkOpenSymChoice, nkClosedSymChoice}:
|
if sc.kind == nkClosedSymChoice:
|
||||||
for i in 0.. <sc.len:
|
for i in 0.. <sc.len:
|
||||||
if sc.sons[i].sym == x.sym: return true
|
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 =
|
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):
|
if isNil(n.typ):
|
||||||
result = p.typ.kind == tyStmt
|
result = p.typ.kind in {tyEmpty, tyStmt}
|
||||||
else:
|
else:
|
||||||
result = sigmatch.argtypeMatches(c.c, p.typ, n.typ)
|
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 =
|
proc matches(c: PPatternContext, p, n: PNode): bool =
|
||||||
# XXX special treatment: statement list,
|
# hidden conversions (?)
|
||||||
# ignore comments, nkPar, hidden conversions
|
if isPatternParam(c, p):
|
||||||
# f(..X) ~> how can 'X' stand for all remaining parameters? -> introduce
|
result = bindOrCheck(c, p.sym, n)
|
||||||
# a new local node kind (alias of nkReturnToken or something)
|
|
||||||
if p.kind == nkSym and p.sym.kind == skParam and p.sym.owner == c.owner:
|
|
||||||
var pp = IdNodeTableGetLazy(c.mapping, p.sym)
|
|
||||||
if pp != nil:
|
|
||||||
# check if we got the same pattern (already unified):
|
|
||||||
result = matches(c, pp, n)
|
|
||||||
elif checkTypes(c, p.sym, n) and
|
|
||||||
(p.sym.ast == nil or checkConstraints(c, p.sym.ast, n)):
|
|
||||||
IdNodeTablePutLazy(c.mapping, p.sym, n)
|
|
||||||
result = true
|
|
||||||
elif n.kind == nkSym and inSymChoice(p, n):
|
elif n.kind == nkSym and inSymChoice(p, n):
|
||||||
result = true
|
result = true
|
||||||
elif n.kind == nkSym and n.sym.kind == skConst:
|
elif n.kind == nkSym and n.sym.kind == skConst:
|
||||||
# try both:
|
# try both:
|
||||||
if sameTrees(p, n): result = true
|
if p.kind == nkSym: result = p.sym == n.sym
|
||||||
elif matches(c, p, n.sym.ast):
|
elif matches(c, p, n.sym.ast): result = true
|
||||||
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):
|
elif sameKinds(p, n):
|
||||||
case p.kind
|
case p.kind
|
||||||
of nkSym: result = p.sym == n.sym
|
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 nkCharLit..nkInt64Lit: result = p.intVal == n.intVal
|
||||||
of nkFloatLit..nkFloat64Lit: result = p.floatVal == n.floatVal
|
of nkFloatLit..nkFloat64Lit: result = p.floatVal == n.floatVal
|
||||||
of nkStrLit..nkTripleStrLit: result = p.strVal == n.strVal
|
of nkStrLit..nkTripleStrLit: result = p.strVal == n.strVal
|
||||||
of nkEmpty, nkNilLit, nkType:
|
of nkEmpty, nkNilLit, nkType:
|
||||||
result = true
|
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:
|
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):
|
for i in countup(0, sonsLen(p) - 1):
|
||||||
if not matches(c, p.sons[i], n.sons[i]): return
|
if not matches(c, p.sons[i], n.sons[i]): return
|
||||||
result = true
|
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)
|
# writeln(X, a); writeln(X, b); --> writeln(X, a, b)
|
||||||
|
|
||||||
proc applyRule*(c: PContext, s: PSym, n: PNode): PNode =
|
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.owner = s
|
||||||
ctx.c = c
|
ctx.c = c
|
||||||
# we perform 'initIdNodeTable' lazily for performance
|
# we perform 'initIdNodeTable' lazily for performance
|
||||||
if matches(ctx, s.ast.sons[patternPos], n):
|
var m = matchStmtList(ctx, s.ast.sons[patternPos], n)
|
||||||
# each parameter should have been bound; we simply setup a call and
|
if isNil(m): return nil
|
||||||
# let semantic checking deal with the rest :-)
|
# each parameter should have been bound; we simply setup a call and
|
||||||
# this also saves type checking if we allow for type checking errors
|
# let semantic checking deal with the rest :-)
|
||||||
# as in 'system.compiles' and simply discard the results. But an error
|
result = newNodeI(nkCall, n.info)
|
||||||
# may have been desired in the first place! Meh, it's good enough for
|
result.add(newSymNode(s, n.info))
|
||||||
# a first implementation:
|
let params = s.typ.n
|
||||||
result = newNodeI(nkCall, n.info)
|
for i in 1 .. < params.len:
|
||||||
result.add(newSymNode(s, n.info))
|
let param = params.sons[i].sym
|
||||||
let params = s.typ.n
|
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:
|
for i in 1 .. < params.len:
|
||||||
let param = params.sons[i].sym
|
let param = params.sons[i].sym
|
||||||
let x = IdNodeTableGetLazy(ctx.mapping, param)
|
case whichAlias(param)
|
||||||
# couldn't bind parameter:
|
of aqNone: nil
|
||||||
if isNil(x): return nil
|
of aqShouldAlias:
|
||||||
result.add(x)
|
# it suffices that it aliases for sure with *some* other param:
|
||||||
markUsed(n, s)
|
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))
|
else: result = len(atom(n))
|
||||||
of succ(nkEmpty)..pred(nkTripleStrLit), succ(nkTripleStrLit)..nkNilLit:
|
of succ(nkEmpty)..pred(nkTripleStrLit), succ(nkTripleStrLit)..nkNilLit:
|
||||||
result = len(atom(n))
|
result = len(atom(n))
|
||||||
of nkCall, nkBracketExpr, nkCurlyExpr, nkConv:
|
of nkCall, nkBracketExpr, nkCurlyExpr, nkConv, nkPattern:
|
||||||
result = lsub(n.sons[0]) + lcomma(n, 1) + 2
|
result = lsub(n.sons[0]) + lcomma(n, 1) + 2
|
||||||
of nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv: result = lsub(n[1])
|
of nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv: result = lsub(n[1])
|
||||||
of nkCast: result = lsub(n.sons[0]) + lsub(n.sons[1]) + len("cast[]()")
|
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 nkCommand: result = lsub(n.sons[0]) + lcomma(n, 1) + 1
|
||||||
of nkExprEqExpr, nkAsgn, nkFastAsgn: result = lsons(n) + 3
|
of nkExprEqExpr, nkAsgn, nkFastAsgn: result = lsons(n) + 3
|
||||||
of nkPar, nkCurly, nkBracket, nkClosure: result = lcomma(n) + 2
|
of nkPar, nkCurly, nkBracket, nkClosure: result = lcomma(n) + 2
|
||||||
|
of nkArgList: result = lcomma(n)
|
||||||
of nkTableConstr:
|
of nkTableConstr:
|
||||||
result = if n.len > 0: lcomma(n) + 2 else: len("{:}")
|
result = if n.len > 0: lcomma(n) + 2 else: len("{:}")
|
||||||
of nkClosedSymChoice, nkOpenSymChoice:
|
of nkClosedSymChoice, nkOpenSymChoice:
|
||||||
|
|
@ -666,7 +667,9 @@ proc gproc(g: var TSrcGen, n: PNode) =
|
||||||
put(g, tkSymbol, renderDefinitionName(n.sons[namePos].sym))
|
put(g, tkSymbol, renderDefinitionName(n.sons[namePos].sym))
|
||||||
else:
|
else:
|
||||||
gsub(g, n.sons[namePos])
|
gsub(g, n.sons[namePos])
|
||||||
gsub(g, n.sons[patternPos])
|
|
||||||
|
if n.sons[patternPos].kind != nkEmpty:
|
||||||
|
gpattern(g, n.sons[patternPos])
|
||||||
gsub(g, n.sons[genericParamsPos])
|
gsub(g, n.sons[genericParamsPos])
|
||||||
gsub(g, n.sons[paramsPos])
|
gsub(g, n.sons[paramsPos])
|
||||||
gsub(g, n.sons[pragmasPos])
|
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 nkRStrLit: put(g, tkRStrLit, atom(n))
|
||||||
of nkCharLit: put(g, tkCharLit, atom(n))
|
of nkCharLit: put(g, tkCharLit, atom(n))
|
||||||
of nkNilLit: put(g, tkNil, atom(n)) # complex expressions
|
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])
|
if sonsLen(n) >= 1: gsub(g, n.sons[0])
|
||||||
put(g, tkParLe, "(")
|
put(g, tkParLe, "(")
|
||||||
gcomma(g, n, 1)
|
gcomma(g, n, 1)
|
||||||
|
|
@ -859,6 +862,8 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
|
||||||
put(g, tkCurlyLe, "{")
|
put(g, tkCurlyLe, "{")
|
||||||
gcomma(g, n, c)
|
gcomma(g, n, c)
|
||||||
put(g, tkCurlyRi, "}")
|
put(g, tkCurlyRi, "}")
|
||||||
|
of nkArgList:
|
||||||
|
gcomma(g, n, c)
|
||||||
of nkTableConstr:
|
of nkTableConstr:
|
||||||
put(g, tkCurlyLe, "{")
|
put(g, tkCurlyLe, "{")
|
||||||
if n.len > 0: gcomma(g, n, c)
|
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])
|
gsub(g, n.sons[2])
|
||||||
of nkPrefix:
|
of nkPrefix:
|
||||||
gsub(g, n.sons[0])
|
gsub(g, n.sons[0])
|
||||||
put(g, tkSpaces, space)
|
if n.len > 1:
|
||||||
gsub(g, n.sons[1])
|
put(g, tkSpaces, space)
|
||||||
|
gsub(g, n.sons[1])
|
||||||
of nkPostfix:
|
of nkPostfix:
|
||||||
gsub(g, n.sons[1])
|
gsub(g, n.sons[1])
|
||||||
gsub(g, n.sons[0])
|
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 nkCaseStmt, nkRecCase: gcase(g, n)
|
||||||
of nkMacroStmt: gmacro(g, n)
|
of nkMacroStmt: gmacro(g, n)
|
||||||
of nkTryStmt: gtry(g, n)
|
of nkTryStmt: gtry(g, n)
|
||||||
of nkPattern: gpattern(g, n)
|
|
||||||
of nkForStmt, nkParForStmt: gfor(g, n)
|
of nkForStmt, nkParForStmt: gfor(g, n)
|
||||||
of nkBlockStmt, nkBlockExpr: gblock(g, n)
|
of nkBlockStmt, nkBlockExpr: gblock(g, n)
|
||||||
of nkStaticStmt: gstaticStmt(g, n)
|
of nkStaticStmt: gstaticStmt(g, n)
|
||||||
|
|
|
||||||
|
|
@ -330,6 +330,9 @@ proc decodeType(r: PRodReader, info: TLineInfo): PType =
|
||||||
if r.s[r.pos] == '@':
|
if r.s[r.pos] == '@':
|
||||||
inc(r.pos)
|
inc(r.pos)
|
||||||
result.containerID = decodeVInt(r.s, 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)
|
decodeLoc(r, result.loc, info)
|
||||||
while r.s[r.pos] == '^':
|
while r.s[r.pos] == '^':
|
||||||
inc(r.pos)
|
inc(r.pos)
|
||||||
|
|
|
||||||
|
|
@ -233,6 +233,9 @@ proc encodeType(w: PRodWriter, t: PType, result: var string) =
|
||||||
if t.containerID != 0:
|
if t.containerID != 0:
|
||||||
add(result, '@')
|
add(result, '@')
|
||||||
encodeVInt(t.containerID, result)
|
encodeVInt(t.containerID, result)
|
||||||
|
if t.constraint != nil:
|
||||||
|
add(result, '`')
|
||||||
|
encodeNode(w, UnknownLineInfo(), t.constraint, result)
|
||||||
encodeLoc(w, t.loc, result)
|
encodeLoc(w, t.loc, result)
|
||||||
for i in countup(0, sonsLen(t) - 1):
|
for i in countup(0, sonsLen(t) - 1):
|
||||||
if t.sons[i] == nil:
|
if t.sons[i] == nil:
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ import
|
||||||
magicsys, parser, nversion, nimsets, semfold, importer,
|
magicsys, parser, nversion, nimsets, semfold, importer,
|
||||||
procfind, lookups, rodread, pragmas, passes, semdata, semtypinst, sigmatch,
|
procfind, lookups, rodread, pragmas, passes, semdata, semtypinst, sigmatch,
|
||||||
semthreads, intsets, transf, evals, idgen, aliases, cgmeth, lambdalifting,
|
semthreads, intsets, transf, evals, idgen, aliases, cgmeth, lambdalifting,
|
||||||
evaltempl, patterns
|
evaltempl, patterns, parampatterns
|
||||||
|
|
||||||
proc semPass*(): TPass
|
proc semPass*(): TPass
|
||||||
# implementation
|
# 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,
|
proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
|
||||||
semCheck: bool = true): PNode
|
semCheck: bool = true): PNode
|
||||||
|
proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode
|
||||||
|
|
||||||
proc semWhen(c: PContext, n: PNode, semCheck: bool = true): PNode
|
proc semWhen(c: PContext, n: PNode, semCheck: bool = true): PNode
|
||||||
|
|
||||||
|
|
@ -102,6 +103,26 @@ proc semConstExpr(c: PContext, n: PNode): PNode =
|
||||||
return n
|
return n
|
||||||
result = evalTypedExpr(c, e)
|
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 =
|
proc applyPatterns(c: PContext, n: PNode): PNode =
|
||||||
# fast exit:
|
# fast exit:
|
||||||
if c.patterns.len == 0 or optPatterns notin gOptions: return n
|
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
|
# however the resulting AST would better not trigger the old rule then
|
||||||
# anymore ;-)
|
# anymore ;-)
|
||||||
for i in countdown(<c.patterns.len, 0):
|
for i in countdown(<c.patterns.len, 0):
|
||||||
let x = applyRule(c, c.patterns[i], result)
|
let pattern = c.patterns[i]
|
||||||
if not isNil(x):
|
if not isNil(pattern):
|
||||||
assert x.kind == nkCall
|
let x = applyRule(c, pattern, result)
|
||||||
inc(evalTemplateCounter)
|
if not isNil(x):
|
||||||
if evalTemplateCounter > 100:
|
assert x.kind in {nkStmtList, nkCall}
|
||||||
GlobalError(n.info, errTemplateInstantiationTooNested)
|
inc(evalTemplateCounter)
|
||||||
result = semExpr(c, x)
|
if evalTemplateCounter > 100:
|
||||||
dec(evalTemplateCounter)
|
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
|
include seminst, semcall
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -65,18 +65,17 @@ proc inlineConst(n: PNode, s: PSym): PNode {.inline.} =
|
||||||
result.typ = s.typ
|
result.typ = s.typ
|
||||||
result.info = n.info
|
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 =
|
proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
|
||||||
case s.kind
|
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:
|
of skConst:
|
||||||
markUsed(n, s)
|
markUsed(n, s)
|
||||||
case skipTypes(s.typ, abstractInst).kind
|
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 a proc accesses a global variable, it is not side effect free:
|
||||||
if sfGlobal in s.flags:
|
if sfGlobal in s.flags:
|
||||||
incl(c.p.owner.flags, sfSideEffect)
|
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
|
return s.typ.n
|
||||||
result = newSymNode(s, n.info)
|
result = newSymNode(s, n.info)
|
||||||
# We cannot check for access to outer vars for example because it's still
|
# 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)
|
var s = lookUp(c, n)
|
||||||
semCaptureSym(s, c.p.owner)
|
semCaptureSym(s, c.p.owner)
|
||||||
result = semSym(c, n, s, flags)
|
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:
|
of nkSym:
|
||||||
# because of the changed symbol binding, this does not mean that we
|
# because of the changed symbol binding, this does not mean that we
|
||||||
# don't have to check the symbol for semantics here again!
|
# 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
|
let x = b.intVal|+|1
|
||||||
if (x and -x) == x and x >= 0:
|
if (x and -x) == x and x >= 0:
|
||||||
result = makeRange(a.typ, 0, b.intVal)
|
result = makeRange(a.typ, 0, b.intVal)
|
||||||
of mModI, mModI64, mModU:
|
of mModU:
|
||||||
# so ... if you ever wondered about modulo's signedness; this defines it:
|
|
||||||
let a = n.sons[1]
|
let a = n.sons[1]
|
||||||
let b = n.sons[2]
|
let b = n.sons[2]
|
||||||
if b.kind in {nkIntLit..nkUInt64Lit}:
|
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)
|
result = makeRange(a.typ, 0, b.intVal-1)
|
||||||
else:
|
else:
|
||||||
result = makeRange(a.typ, b.intVal+1, 0)
|
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:
|
of mDivI, mDivI64, mDivU:
|
||||||
binaryOp(`|div|`)
|
binaryOp(`|div|`)
|
||||||
of mMinI, mMinI64:
|
of mMinI, mMinI64:
|
||||||
|
|
|
||||||
|
|
@ -933,6 +933,8 @@ proc semStaticStmt(c: PContext, n: PNode): PNode =
|
||||||
result = evalStaticExpr(c.module, a)
|
result = evalStaticExpr(c.module, a)
|
||||||
if result.isNil:
|
if result.isNil:
|
||||||
LocalError(n.info, errCannotInterpretNodeX, renderTree(n))
|
LocalError(n.info, errCannotInterpretNodeX, renderTree(n))
|
||||||
|
elif result.kind == nkEmpty:
|
||||||
|
result = newNodeI(nkNilLit, n.info)
|
||||||
|
|
||||||
# special marker values that indicates that we are
|
# special marker values that indicates that we are
|
||||||
# 1) AnalyzingDestructor: currenlty analyzing the type for destructor
|
# 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)
|
for x in items(sc): toBind.incl(x.sym.id)
|
||||||
else:
|
else:
|
||||||
illFormedAst(a)
|
illFormedAst(a)
|
||||||
result = newNodeI(nkEmpty, n.info)
|
result = newNodeI(nkNilLit, n.info)
|
||||||
|
|
||||||
proc replaceIdentBySym(n: var PNode, s: PNode) =
|
proc replaceIdentBySym(n: var PNode, s: PNode) =
|
||||||
case n.kind
|
case n.kind
|
||||||
|
|
@ -98,16 +98,10 @@ proc replaceIdentBySym(n: var PNode, s: PNode) =
|
||||||
of nkIdent, nkAccQuoted, nkSym: n = s
|
of nkIdent, nkAccQuoted, nkSym: n = s
|
||||||
else: illFormedAst(n)
|
else: illFormedAst(n)
|
||||||
|
|
||||||
# This code here is the first pass over a template's body. The same code also
|
|
||||||
# implements the first pass over a pattern's body:
|
|
||||||
|
|
||||||
type
|
type
|
||||||
TBodyKind = enum
|
|
||||||
bkTemplate, bkPattern
|
|
||||||
TemplCtx {.pure, final.} = object
|
TemplCtx {.pure, final.} = object
|
||||||
c: PContext
|
c: PContext
|
||||||
toBind: TIntSet
|
toBind: TIntSet
|
||||||
bodyKind: TBodyKind
|
|
||||||
owner: PSym
|
owner: PSym
|
||||||
|
|
||||||
proc getIdentNode(c: var TemplCtx, n: PNode): PNode =
|
proc getIdentNode(c: var TemplCtx, n: PNode): PNode =
|
||||||
|
|
@ -188,8 +182,6 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
|
||||||
result = newSymNode(s, n.info)
|
result = newSymNode(s, n.info)
|
||||||
elif Contains(c.toBind, s.id):
|
elif Contains(c.toBind, s.id):
|
||||||
result = symChoice(c.c, n, s, scClosed)
|
result = symChoice(c.c, n, s, scClosed)
|
||||||
elif c.bodyKind == bkPattern:
|
|
||||||
result = symChoice(c.c, n, s, scOpen)
|
|
||||||
elif s.owner == c.owner and sfGenSym in s.flags:
|
elif s.owner == c.owner and sfGenSym in s.flags:
|
||||||
# template tmp[T](x: var seq[T]) =
|
# template tmp[T](x: var seq[T]) =
|
||||||
# var yz: T
|
# var yz: T
|
||||||
|
|
@ -305,8 +297,9 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
|
||||||
# so we use the generic code for nkDotExpr too
|
# so we use the generic code for nkDotExpr too
|
||||||
if n.kind == nkDotExpr or n.kind == nkAccQuoted:
|
if n.kind == nkDotExpr or n.kind == nkAccQuoted:
|
||||||
let s = QualifiedLookUp(c.c, n, {})
|
let s = QualifiedLookUp(c.c, n, {})
|
||||||
if s != nil and Contains(c.toBind, s.id):
|
if s != nil:
|
||||||
return symChoice(c.c, n, s, scClosed)
|
if Contains(c.toBind, s.id):
|
||||||
|
return symChoice(c.c, n, s, scClosed)
|
||||||
result = n
|
result = n
|
||||||
for i in countup(0, sonsLen(n) - 1):
|
for i in countup(0, sonsLen(n) - 1):
|
||||||
result.sons[i] = semTemplBody(c, n.sons[i])
|
result.sons[i] = semTemplBody(c, n.sons[i])
|
||||||
|
|
@ -405,7 +398,6 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
|
||||||
ctx.toBind = initIntSet()
|
ctx.toBind = initIntSet()
|
||||||
ctx.c = c
|
ctx.c = c
|
||||||
ctx.owner = s
|
ctx.owner = s
|
||||||
ctx.bodyKind = bkTemplate
|
|
||||||
if sfDirty in s.flags:
|
if sfDirty in s.flags:
|
||||||
n.sons[bodyPos] = semTemplBodyDirty(ctx, n.sons[bodyPos])
|
n.sons[bodyPos] = semTemplBodyDirty(ctx, n.sons[bodyPos])
|
||||||
else:
|
else:
|
||||||
|
|
@ -426,17 +418,109 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
|
||||||
else:
|
else:
|
||||||
SymTabReplace(c.tab.stack[curScope], proto, s)
|
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 =
|
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)
|
openScope(c.tab)
|
||||||
var ctx: TemplCtx
|
var ctx: TemplCtx
|
||||||
ctx.toBind = initIntSet()
|
ctx.toBind = initIntSet()
|
||||||
ctx.c = c
|
ctx.c = c
|
||||||
ctx.owner = getCurrOwner()
|
ctx.owner = getCurrOwner()
|
||||||
ctx.bodyKind = bkPattern
|
result = flattenStmts(semPatternBody(ctx, n))
|
||||||
result = semTemplBody(ctx, n)
|
if result.kind in {nkStmtList, nkStmtListExpr}:
|
||||||
if result.kind in {nkStmtList, nkStmtListExpr} and result.len == 1:
|
if result.len == 1:
|
||||||
result = result.sons[0]
|
result = result.sons[0]
|
||||||
|
elif result.len == 0:
|
||||||
|
LocalError(n.info, errInvalidExpression)
|
||||||
closeScope(c.tab)
|
closeScope(c.tab)
|
||||||
|
|
|
||||||
|
|
@ -826,9 +826,8 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||||
of nkCurlyExpr:
|
of nkCurlyExpr:
|
||||||
result = semTypeNode(c, n.sons[0], nil)
|
result = semTypeNode(c, n.sons[0], nil)
|
||||||
if result != nil:
|
if result != nil:
|
||||||
result = copyType(result, getCurrOwner(), false)
|
result = copyType(result, getCurrOwner(), true)
|
||||||
for i in countup(1, n.len - 1):
|
result.constraint = semNodeKindConstraints(n)
|
||||||
result.rawAddSon(semTypeNode(c, n.sons[i], nil))
|
|
||||||
of nkWhenStmt:
|
of nkWhenStmt:
|
||||||
var whenResult = semWhen(c, n, false)
|
var whenResult = semWhen(c, n, false)
|
||||||
if whenResult.kind == nkStmtList: whenResult.kind = nkStmtListType
|
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 mOrdinal: result = semOrdinal(c, n, prev)
|
||||||
of mSeq: result = semContainer(c, n, tySequence, "seq", prev)
|
of mSeq: result = semContainer(c, n, tySequence, "seq", prev)
|
||||||
of mVarargs: result = semVarargs(c, n, 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)
|
else: result = semGeneric(c, n, s, prev)
|
||||||
of nkIdent, nkDotExpr, nkAccQuoted:
|
of nkIdent, nkDotExpr, nkAccQuoted:
|
||||||
var s = semTypeIdent(c, n)
|
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
|
let key = if n.kind == nkExprColonExpr: n.sons[0] else: n
|
||||||
if key.kind == nkIdent: result = whichKeyword(key.ident)
|
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 =
|
proc firstOrd(t: PType): biggestInt =
|
||||||
case t.kind
|
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 tySet, tyVar: result = firstOrd(t.sons[0])
|
||||||
of tyArray, tyArrayConstr: result = firstOrd(t.sons[0])
|
of tyArray, tyArrayConstr: result = firstOrd(t.sons[0])
|
||||||
of tyRange:
|
of tyRange:
|
||||||
|
|
@ -564,6 +565,7 @@ proc lastOrd(t: PType): biggestInt =
|
||||||
result = t.n.sons[sonsLen(t.n) - 1].sym.position
|
result = t.n.sons[sonsLen(t.n) - 1].sym.position
|
||||||
of tyGenericInst, tyDistinct, tyConst, tyMutable:
|
of tyGenericInst, tyDistinct, tyConst, tyMutable:
|
||||||
result = lastOrd(lastSon(t))
|
result = lastOrd(lastSon(t))
|
||||||
|
of tyProxy: result = 0
|
||||||
else:
|
else:
|
||||||
InternalError("invalid kind for last(" & $t.kind & ')')
|
InternalError("invalid kind for last(" & $t.kind & ')')
|
||||||
result = 0
|
result = 0
|
||||||
|
|
@ -591,7 +593,7 @@ type
|
||||||
# (few elements expected)
|
# (few elements expected)
|
||||||
|
|
||||||
proc initSameTypeClosure: TSameTypeClosure =
|
proc initSameTypeClosure: TSameTypeClosure =
|
||||||
# we do the initialization lazy for performance (avoids memory allocations)
|
# we do the initialization lazily for performance (avoids memory allocations)
|
||||||
nil
|
nil
|
||||||
|
|
||||||
proc containsOrIncl(c: var TSameTypeClosure, a, b: PType): bool =
|
proc containsOrIncl(c: var TSameTypeClosure, a, b: PType): bool =
|
||||||
|
|
@ -613,7 +615,7 @@ proc SameTypeOrNil*(a, b: PType): bool =
|
||||||
result = true
|
result = true
|
||||||
else:
|
else:
|
||||||
if a == nil or b == nil: result = false
|
if a == nil or b == nil: result = false
|
||||||
else:
|
else:
|
||||||
var c = initSameTypeClosure()
|
var c = initSameTypeClosure()
|
||||||
result = SameTypeAux(a, b, c)
|
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).
|
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
|
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
|
type ``range[0..2]``. The following built-in operators for integers are
|
||||||
affected by this rule: ``-``, ``+``, ``*``, ``min``, ``max``, ``succ``,
|
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
|
Bitwise ``and`` only produces a ``range`` if one of its operands is a
|
||||||
constant *x* so that (x+1) is a number of two.
|
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:
|
This means that the following code is accepted:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -46,14 +46,17 @@ type
|
||||||
nnkYieldStmt, nnkTryStmt, nnkFinally, nnkRaiseStmt,
|
nnkYieldStmt, nnkTryStmt, nnkFinally, nnkRaiseStmt,
|
||||||
nnkReturnStmt, nnkBreakStmt, nnkContinueStmt, nnkBlockStmt, nnkStaticStmt,
|
nnkReturnStmt, nnkBreakStmt, nnkContinueStmt, nnkBlockStmt, nnkStaticStmt,
|
||||||
nnkDiscardStmt, nnkStmtList, nnkImportStmt, nnkFromStmt,
|
nnkDiscardStmt, nnkStmtList, nnkImportStmt, nnkFromStmt,
|
||||||
nnkIncludeStmt, nnkBindStmt, nnkPattern,
|
nnkIncludeStmt, nnkBindStmt,
|
||||||
nnkCommentStmt, nnkStmtListExpr, nnkBlockExpr,
|
nnkCommentStmt, nnkStmtListExpr, nnkBlockExpr,
|
||||||
nnkStmtListType, nnkBlockType, nnkTypeOfExpr, nnkObjectTy,
|
nnkStmtListType, nnkBlockType, nnkTypeOfExpr, nnkObjectTy,
|
||||||
nnkTupleTy, nnkRecList, nnkRecCase, nnkRecWhen,
|
nnkTupleTy, nnkRecList, nnkRecCase, nnkRecWhen,
|
||||||
nnkRefTy, nnkPtrTy, nnkVarTy,
|
nnkRefTy, nnkPtrTy, nnkVarTy,
|
||||||
nnkConstTy, nnkMutableTy,
|
nnkConstTy, nnkMutableTy,
|
||||||
nnkDistinctTy,
|
nnkDistinctTy,
|
||||||
nnkProcTy, nnkEnumTy, nnkEnumFieldDef, nnkReturnToken
|
nnkProcTy, nnkEnumTy,
|
||||||
|
nnkEnumFieldDef,
|
||||||
|
nnkArglist, nnkPattern
|
||||||
|
nnkReturnToken
|
||||||
TNimNodeKinds* = set[TNimrodNodeKind]
|
TNimNodeKinds* = set[TNimrodNodeKind]
|
||||||
TNimrodTypeKind* = enum
|
TNimrodTypeKind* = enum
|
||||||
ntyNone, ntyBool, ntyChar, ntyEmpty,
|
ntyNone, ntyBool, ntyChar, ntyEmpty,
|
||||||
|
|
|
||||||
|
|
@ -1314,10 +1314,11 @@ iterator items*(a: cstring): char {.inline.} =
|
||||||
yield a[i]
|
yield a[i]
|
||||||
inc(i)
|
inc(i)
|
||||||
|
|
||||||
iterator items*(E: typedesc{enum}): E =
|
when not defined(booting):
|
||||||
## iterates over the values of the enum ``E``.
|
iterator items*(E: typedesc[enum]): E =
|
||||||
for v in low(E)..high(E):
|
## iterates over the values of the enum ``E``.
|
||||||
yield v
|
for v in low(E)..high(E):
|
||||||
|
yield v
|
||||||
|
|
||||||
iterator pairs*[T](a: openarray[T]): tuple[key: int, val: T] {.inline.} =
|
iterator pairs*[T](a: openarray[T]): tuple[key: int, val: T] {.inline.} =
|
||||||
## iterates over each item of `a`. Yields ``(index, a[index])`` pairs.
|
## 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.
|
## ``CRLF``. The newline character(s) are not part of the returned string.
|
||||||
## Returns ``false`` if the end of the file has been reached, ``true``
|
## Returns ``false`` if the end of the file has been reached, ``true``
|
||||||
## otherwise. If ``false`` is returned `line` contains no new data.
|
## 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.}
|
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.
|
## May throw an IO exception.
|
||||||
|
|
||||||
proc getFileSize*(f: TFile): int64
|
proc getFileSize*(f: TFile): int64
|
||||||
|
|
|
||||||
|
|
@ -146,10 +146,6 @@ proc EndOfFile(f: TFile): bool =
|
||||||
ungetc(c, f)
|
ungetc(c, f)
|
||||||
return c < 0'i32
|
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, `$`]) =
|
proc writeln[Ty](f: TFile, x: varargs[Ty, `$`]) =
|
||||||
for i in items(x): write(f, i)
|
for i in items(x): write(f, i)
|
||||||
write(f, "\n")
|
write(f, "\n")
|
||||||
|
|
|
||||||
3
todo.txt
3
todo.txt
|
|
@ -2,8 +2,8 @@ version 0.9.0
|
||||||
=============
|
=============
|
||||||
|
|
||||||
- make 'm: stmt' use overloading resolution
|
- 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'
|
- make 'bind' default for templates and introduce 'mixin'
|
||||||
|
|
||||||
- implement "closure tuple consists of a single 'ref'" optimization
|
- implement "closure tuple consists of a single 'ref'" optimization
|
||||||
|
|
@ -19,6 +19,7 @@ version 0.9.0
|
||||||
Bugs
|
Bugs
|
||||||
----
|
----
|
||||||
|
|
||||||
|
- sneaking with qualifiedLookup() is really broken!
|
||||||
- bug: aporia.nim(968, 5) Error: ambiguous identifier: 'DELETE' --
|
- bug: aporia.nim(968, 5) Error: ambiguous identifier: 'DELETE' --
|
||||||
use a qualifier
|
use a qualifier
|
||||||
- bug: pragma statements in combination with symbol files are evaluated twice
|
- bug: pragma statements in combination with symbol files are evaluated twice
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue