term rewriting improvements
This commit is contained in:
parent
355ae07b8f
commit
b64eeeb430
18 changed files with 284 additions and 144 deletions
91
compiler/hlo.nim
Normal file
91
compiler/hlo.nim
Normal file
|
|
@ -0,0 +1,91 @@
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# The Nimrod Compiler
|
||||||
|
# (c) Copyright 2012 Andreas Rumpf
|
||||||
|
#
|
||||||
|
# See the file "copying.txt", included in this
|
||||||
|
# distribution, for details about the copyright.
|
||||||
|
#
|
||||||
|
|
||||||
|
# This include implements the high level optimization pass.
|
||||||
|
|
||||||
|
proc hlo(c: PContext, n: PNode): PNode
|
||||||
|
|
||||||
|
proc evalPattern(c: PContext, n, orig: PNode): 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, orig, s)
|
||||||
|
of skTemplate:
|
||||||
|
result = semTemplateExpr(c, n, s)
|
||||||
|
else:
|
||||||
|
result = semDirectOp(c, n, {})
|
||||||
|
if optHints in gOptions and hintPattern in gNotes:
|
||||||
|
Message(orig.info, hintPattern, rule & " --> '" &
|
||||||
|
renderTree(result, {renderNoComments}) & "'")
|
||||||
|
# check the resulting AST for optimization rules again:
|
||||||
|
result = hlo(c, result)
|
||||||
|
|
||||||
|
proc applyPatterns(c: PContext, n: PNode): PNode =
|
||||||
|
result = n
|
||||||
|
# we apply the last pattern first, so that pattern overriding is possible;
|
||||||
|
# however the resulting AST would better not trigger the old rule then
|
||||||
|
# anymore ;-)
|
||||||
|
for i in countdown(<c.patterns.len, 0):
|
||||||
|
let 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], result)
|
||||||
|
result = flattenStmts(x)
|
||||||
|
else:
|
||||||
|
result = evalPattern(c, x, result)
|
||||||
|
dec(evalTemplateCounter)
|
||||||
|
# activate this pattern again:
|
||||||
|
c.patterns[i] = pattern
|
||||||
|
|
||||||
|
proc hlo(c: PContext, n: PNode): PNode =
|
||||||
|
case n.kind
|
||||||
|
of nkMacroDef, nkTemplateDef, procDefs:
|
||||||
|
# already processed (special cases in semstmts.nim)
|
||||||
|
result = n
|
||||||
|
else:
|
||||||
|
result = applyPatterns(c, n)
|
||||||
|
if result == n:
|
||||||
|
# no optimization applied, try subtrees:
|
||||||
|
for i in 0 .. < safeLen(result):
|
||||||
|
let a = result.sons[i]
|
||||||
|
let h = hlo(c, a)
|
||||||
|
if h != a: result.sons[i] = h
|
||||||
|
else:
|
||||||
|
# perform type checking, so that the replacement still fits:
|
||||||
|
if n.typ == nil and (result.typ == nil or
|
||||||
|
result.typ.kind in {tyStmt, tyEmpty}):
|
||||||
|
nil
|
||||||
|
else:
|
||||||
|
result = fitNode(c, n.typ, result)
|
||||||
|
|
||||||
|
proc hloBody(c: PContext, n: PNode): PNode =
|
||||||
|
# fast exit:
|
||||||
|
if c.patterns.len == 0 or optPatterns notin gOptions: return n
|
||||||
|
result = hlo(c, n)
|
||||||
|
|
||||||
|
proc hloStmt(c: PContext, n: PNode): PNode =
|
||||||
|
# fast exit:
|
||||||
|
if c.patterns.len == 0 or optPatterns notin gOptions: return n
|
||||||
|
result = hlo(c, n)
|
||||||
|
|
@ -18,7 +18,7 @@ import strutils, ast, astalgo, types, msgs, idents, renderer, wordrecg
|
||||||
type
|
type
|
||||||
TAliasRequest* = enum # first byte of the bytecode determines alias checking
|
TAliasRequest* = enum # first byte of the bytecode determines alias checking
|
||||||
aqNone = 1, # no alias analysis requested
|
aqNone = 1, # no alias analysis requested
|
||||||
aqShouldAlias, # with what?
|
aqShouldAlias, # with some other param
|
||||||
aqNoAlias # request noalias
|
aqNoAlias # request noalias
|
||||||
TOpcode = enum
|
TOpcode = enum
|
||||||
ppEof = 1, # end of compiled pattern
|
ppEof = 1, # end of compiled pattern
|
||||||
|
|
@ -32,6 +32,8 @@ type
|
||||||
ppCall,
|
ppCall,
|
||||||
ppSymKind,
|
ppSymKind,
|
||||||
ppNodeKind,
|
ppNodeKind,
|
||||||
|
ppLValue,
|
||||||
|
ppLocal,
|
||||||
ppSideEffect,
|
ppSideEffect,
|
||||||
ppNoSideEffect
|
ppNoSideEffect
|
||||||
TPatternCode = string
|
TPatternCode = string
|
||||||
|
|
@ -87,6 +89,8 @@ proc compileConstraints(p: PNode, result: var TPatternCode) =
|
||||||
of "call": result.add(ppCall)
|
of "call": result.add(ppCall)
|
||||||
of "alias": result[0] = chr(aqShouldAlias.ord)
|
of "alias": result[0] = chr(aqShouldAlias.ord)
|
||||||
of "noalias": result[0] = chr(aqNoAlias.ord)
|
of "noalias": result[0] = chr(aqNoAlias.ord)
|
||||||
|
of "lvalue": result.add(ppLValue)
|
||||||
|
of "local": result.add(ppLocal)
|
||||||
of "sideeffect": result.add(ppSideEffect)
|
of "sideeffect": result.add(ppSideEffect)
|
||||||
of "nosideeffect": result.add(ppNoSideEffect)
|
of "nosideeffect": result.add(ppNoSideEffect)
|
||||||
else:
|
else:
|
||||||
|
|
@ -144,8 +148,8 @@ proc checkForSideEffects(n: PNode): TSideEffectAnalysis =
|
||||||
# indirect call without side effects:
|
# indirect call without side effects:
|
||||||
result = seNoSideEffect
|
result = seNoSideEffect
|
||||||
else:
|
else:
|
||||||
# indirect call: we don't know
|
# indirect call: assume side effect:
|
||||||
result = seUnknown
|
return seSideEffect
|
||||||
# we need to check n[0] too: (FwithSideEffectButReturnsProcWithout)(args)
|
# we need to check n[0] too: (FwithSideEffectButReturnsProcWithout)(args)
|
||||||
for i in 0 .. <n.len:
|
for i in 0 .. <n.len:
|
||||||
let ret = checkForSideEffects(n.sons[i])
|
let ret = checkForSideEffects(n.sons[i])
|
||||||
|
|
@ -162,6 +166,53 @@ proc checkForSideEffects(n: PNode): TSideEffectAnalysis =
|
||||||
elif ret == seUnknown and result == seNoSideEffect:
|
elif ret == seUnknown and result == seNoSideEffect:
|
||||||
result = seUnknown
|
result = seUnknown
|
||||||
|
|
||||||
|
type
|
||||||
|
TAssignableResult* = enum
|
||||||
|
arNone, # no l-value and no discriminant
|
||||||
|
arLValue, # is an l-value
|
||||||
|
arLocalLValue, # is an l-value, but local var; must not escape
|
||||||
|
# its stack frame!
|
||||||
|
arDiscriminant # is a discriminant
|
||||||
|
|
||||||
|
proc isAssignable*(owner: PSym, n: PNode): TAssignableResult =
|
||||||
|
## 'owner' can be nil!
|
||||||
|
result = arNone
|
||||||
|
case n.kind
|
||||||
|
of nkSym:
|
||||||
|
# don't list 'skLet' here:
|
||||||
|
if n.sym.kind in {skVar, skResult, skTemp}:
|
||||||
|
if owner != nil and owner.id == n.sym.owner.id and
|
||||||
|
sfGlobal notin n.sym.flags:
|
||||||
|
result = arLocalLValue
|
||||||
|
else:
|
||||||
|
result = arLValue
|
||||||
|
of nkDotExpr:
|
||||||
|
if skipTypes(n.sons[0].typ, abstractInst).kind in {tyVar, tyPtr, tyRef}:
|
||||||
|
result = arLValue
|
||||||
|
else:
|
||||||
|
result = isAssignable(owner, n.sons[0])
|
||||||
|
if result != arNone and sfDiscriminant in n.sons[1].sym.flags:
|
||||||
|
result = arDiscriminant
|
||||||
|
of nkBracketExpr:
|
||||||
|
if skipTypes(n.sons[0].typ, abstractInst).kind in {tyVar, tyPtr, tyRef}:
|
||||||
|
result = arLValue
|
||||||
|
else:
|
||||||
|
result = isAssignable(owner, n.sons[0])
|
||||||
|
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
|
||||||
|
# Object and tuple conversions are still addressable, so we skip them
|
||||||
|
# XXX why is 'tyOpenArray' allowed here?
|
||||||
|
if skipTypes(n.typ, abstractPtrs).kind in {tyOpenArray, tyTuple, tyObject}:
|
||||||
|
result = isAssignable(owner, n.sons[1])
|
||||||
|
elif compareTypes(n.typ, n.sons[1].typ, dcEqIgnoreDistinct):
|
||||||
|
# types that are equal modulo distinction preserve l-value:
|
||||||
|
result = isAssignable(owner, n.sons[1])
|
||||||
|
of nkHiddenDeref, nkDerefExpr:
|
||||||
|
result = arLValue
|
||||||
|
of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
|
||||||
|
result = isAssignable(owner, n.sons[0])
|
||||||
|
else:
|
||||||
|
nil
|
||||||
|
|
||||||
proc matchNodeKinds*(p, n: PNode): bool =
|
proc matchNodeKinds*(p, n: PNode): bool =
|
||||||
# matches the parameter constraint 'p' against the concrete AST 'n'.
|
# matches the parameter constraint 'p' against the concrete AST 'n'.
|
||||||
# Efficiency matters here.
|
# Efficiency matters here.
|
||||||
|
|
@ -199,6 +250,8 @@ proc matchNodeKinds*(p, n: PNode): bool =
|
||||||
let kind = TNodeKind(code[pc+1])
|
let kind = TNodeKind(code[pc+1])
|
||||||
push n.kind == kind
|
push n.kind == kind
|
||||||
inc pc
|
inc pc
|
||||||
|
of ppLValue: push isAssignable(nil, n) in {arLValue, arLocalLValue}
|
||||||
|
of ppLocal: push isAssignable(nil, n) == arLocalLValue
|
||||||
of ppSideEffect: push checkForSideEffects(n) == seSideEffect
|
of ppSideEffect: push checkForSideEffects(n) == seSideEffect
|
||||||
of ppNoSideEffect: push checkForSideEffects(n) != seSideEffect
|
of ppNoSideEffect: push checkForSideEffects(n) != seSideEffect
|
||||||
inc pc
|
inc pc
|
||||||
|
|
|
||||||
|
|
@ -86,24 +86,38 @@ proc bindOrCheck(c: PPatternContext, param: PSym, n: PNode): bool =
|
||||||
IdNodeTablePutLazy(c.mapping, param, n)
|
IdNodeTablePutLazy(c.mapping, param, n)
|
||||||
result = true
|
result = true
|
||||||
|
|
||||||
proc matchNested(c: PPatternContext, p, n: PNode): bool =
|
proc gather(c: PPatternContext, param: PSym, n: PNode) =
|
||||||
# match ``op*param``
|
var pp = IdNodeTableGetLazy(c.mapping, param)
|
||||||
|
if pp != nil and pp.kind == nkArgList:
|
||||||
|
pp.add(n)
|
||||||
|
else:
|
||||||
|
pp = newNodeI(nkArgList, n.info, 1)
|
||||||
|
pp.sons[0] = n
|
||||||
|
IdNodeTablePutLazy(c.mapping, param, pp)
|
||||||
|
|
||||||
proc matchStarAux(c: PPatternContext, op, n, arglist: PNode) =
|
proc matchNested(c: PPatternContext, p, n: PNode, rpn: bool): bool =
|
||||||
if n.kind in nkCallKinds and matches(c, op, n.sons[0]):
|
# match ``op * param`` or ``op *| param``
|
||||||
|
proc matchStarAux(c: PPatternContext, op, n, arglist: PNode,
|
||||||
|
rpn: bool): bool =
|
||||||
|
result = true
|
||||||
|
if n.kind in nkCallKinds and matches(c, op.sons[1], n.sons[0]):
|
||||||
for i in 1..sonsLen(n)-1:
|
for i in 1..sonsLen(n)-1:
|
||||||
matchStarAux(c, op, n[i], arglist)
|
if not matchStarAux(c, op, n[i], arglist, rpn): return false
|
||||||
|
if rpn: arglist.add(n.sons[0])
|
||||||
elif n.kind == nkHiddenStdConv and n.sons[1].kind == nkBracket:
|
elif n.kind == nkHiddenStdConv and n.sons[1].kind == nkBracket:
|
||||||
let n = n.sons[1]
|
let n = n.sons[1]
|
||||||
for i in 0.. <n.len: matchStarAux(c, op, n[i], arglist)
|
for i in 0.. <n.len:
|
||||||
else:
|
if not matchStarAux(c, op, n[i], arglist, rpn): return false
|
||||||
|
elif checkTypes(c, p.sons[2].sym, n):
|
||||||
add(arglist, n)
|
add(arglist, n)
|
||||||
|
else:
|
||||||
|
result = false
|
||||||
|
|
||||||
if n.kind notin nkCallKinds: return false
|
if n.kind notin nkCallKinds: return false
|
||||||
if matches(c, p.sons[1], n.sons[0]):
|
if matches(c, p.sons[1], n.sons[0]):
|
||||||
var arglist = newNodeI(nkArgList, n.info)
|
var arglist = newNodeI(nkArgList, n.info)
|
||||||
matchStarAux(c, p.sons[1], n, arglist)
|
if matchStarAux(c, p, n, arglist, rpn):
|
||||||
result = bindOrCheck(c, p.sons[2].sym, arglist)
|
result = bindOrCheck(c, p.sons[2].sym, arglist)
|
||||||
|
|
||||||
proc matches(c: PPatternContext, p, n: PNode): bool =
|
proc matches(c: PPatternContext, p, n: PNode): bool =
|
||||||
# hidden conversions (?)
|
# hidden conversions (?)
|
||||||
|
|
@ -122,15 +136,21 @@ proc matches(c: PPatternContext, p, n: PNode): bool =
|
||||||
let opr = p.sons[0].ident.s
|
let opr = p.sons[0].ident.s
|
||||||
case opr
|
case opr
|
||||||
of "|": result = matchChoice(c, p, n)
|
of "|": result = matchChoice(c, p, n)
|
||||||
of "*": result = matchNested(c, p, n)
|
of "*": result = matchNested(c, p, n, rpn=false)
|
||||||
|
of "*|": result = matchNested(c, p, n, rpn=true)
|
||||||
of "~": result = not matches(c, p.sons[1], n)
|
of "~": result = not matches(c, p.sons[1], n)
|
||||||
else: InternalError(p.info, "invalid pattern")
|
else: InternalError(p.info, "invalid pattern")
|
||||||
# template {add(a, `&` * b)}(a: string{noalias}, b: varargs[string]) =
|
# template {add(a, `&` * b)}(a: string{noalias}, b: varargs[string]) =
|
||||||
# add(a, b)
|
# add(a, b)
|
||||||
elif p.kind == nkCurlyExpr:
|
elif p.kind == nkCurlyExpr:
|
||||||
assert isPatternParam(c, p.sons[1])
|
if p.sons[1].kind == nkPrefix:
|
||||||
if matches(c, p.sons[0], n):
|
if matches(c, p.sons[0], n):
|
||||||
result = bindOrCheck(c, p.sons[1].sym, n)
|
gather(c, p.sons[1].sons[1].sym, n)
|
||||||
|
result = true
|
||||||
|
else:
|
||||||
|
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
|
||||||
|
|
@ -196,7 +216,17 @@ proc matchStmtList(c: PPatternContext, p, n: PNode): PNode =
|
||||||
elif matches(c, p, n):
|
elif matches(c, p, n):
|
||||||
result = n
|
result = n
|
||||||
|
|
||||||
# writeln(X, a); writeln(X, b); --> writeln(X, a, b)
|
proc aliasAnalysisRequested(params: PNode): bool =
|
||||||
|
if params.len >= 2:
|
||||||
|
for i in 1 .. < params.len:
|
||||||
|
let param = params.sons[i].sym
|
||||||
|
if whichAlias(param) != aqNone: return true
|
||||||
|
|
||||||
|
proc addToArgList(result, n: PNode) =
|
||||||
|
if n.typ != nil and n.typ.kind != tyStmt:
|
||||||
|
if n.kind != nkArgList: result.add(n)
|
||||||
|
else:
|
||||||
|
for i in 0 .. <n.len: result.add(n.sons[i])
|
||||||
|
|
||||||
proc applyRule*(c: PContext, s: PSym, n: PNode): PNode =
|
proc applyRule*(c: PContext, s: PSym, n: PNode): PNode =
|
||||||
## returns a tree to semcheck if the rule triggered; nil otherwise
|
## returns a tree to semcheck if the rule triggered; nil otherwise
|
||||||
|
|
@ -211,36 +241,40 @@ proc applyRule*(c: PContext, s: PSym, n: PNode): PNode =
|
||||||
result = newNodeI(nkCall, n.info)
|
result = newNodeI(nkCall, n.info)
|
||||||
result.add(newSymNode(s, n.info))
|
result.add(newSymNode(s, n.info))
|
||||||
let params = s.typ.n
|
let params = s.typ.n
|
||||||
|
let requiresAA = aliasAnalysisRequested(params)
|
||||||
|
var args: PNode
|
||||||
|
if requiresAA:
|
||||||
|
args = newNodeI(nkArgList, n.info)
|
||||||
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)
|
let x = IdNodeTableGetLazy(ctx.mapping, param)
|
||||||
# couldn't bind parameter:
|
# couldn't bind parameter:
|
||||||
if isNil(x): return nil
|
if isNil(x): return nil
|
||||||
result.add(x)
|
result.add(x)
|
||||||
|
if requiresAA: addToArgList(args, n)
|
||||||
# perform alias analysis here:
|
# perform alias analysis here:
|
||||||
if params.len >= 2:
|
if requiresAA:
|
||||||
for i in 1 .. < params.len:
|
for i in 1 .. < params.len:
|
||||||
|
var rs = result.sons[i]
|
||||||
let param = params.sons[i].sym
|
let param = params.sons[i].sym
|
||||||
case whichAlias(param)
|
case whichAlias(param)
|
||||||
of aqNone: nil
|
of aqNone: nil
|
||||||
of aqShouldAlias:
|
of aqShouldAlias:
|
||||||
# it suffices that it aliases for sure with *some* other param:
|
# it suffices that it aliases for sure with *some* other param:
|
||||||
var ok = false
|
var ok = false
|
||||||
for j in 1 .. < result.len:
|
for arg in items(args):
|
||||||
if j != i and result.sons[j].typ != nil:
|
if arg != rs and aliases.isPartOf(rs, arg) == arYes:
|
||||||
if aliases.isPartOf(result[i], result[j]) == arYes:
|
ok = true
|
||||||
ok = true
|
break
|
||||||
break
|
|
||||||
# constraint not fullfilled:
|
# constraint not fullfilled:
|
||||||
if not ok: return nil
|
if not ok: return nil
|
||||||
of aqNoAlias:
|
of aqNoAlias:
|
||||||
# it MUST not alias with any other param:
|
# it MUST not alias with any other param:
|
||||||
var ok = true
|
var ok = true
|
||||||
for j in 1 .. < result.len:
|
for arg in items(args):
|
||||||
if j != i and result.sons[j].typ != nil:
|
if arg != rs and aliases.isPartOf(rs, arg) != arNo:
|
||||||
if aliases.isPartOf(result[i], result[j]) != arNo:
|
ok = false
|
||||||
ok = false
|
break
|
||||||
break
|
|
||||||
# constraint not fullfilled:
|
# constraint not fullfilled:
|
||||||
if not ok: return nil
|
if not ok: return nil
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -103,55 +103,7 @@ 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 =
|
include hlo, seminst, semcall
|
||||||
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
|
|
||||||
result = n
|
|
||||||
# we apply the last pattern first, so that pattern overriding is possible;
|
|
||||||
# however the resulting AST would better not trigger the old rule then
|
|
||||||
# anymore ;-)
|
|
||||||
for i in countdown(<c.patterns.len, 0):
|
|
||||||
let 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
|
|
||||||
|
|
||||||
proc semAfterMacroCall(c: PContext, n: PNode, s: PSym): PNode =
|
proc semAfterMacroCall(c: PContext, n: PNode, s: PSym): PNode =
|
||||||
inc(evalTemplateCounter)
|
inc(evalTemplateCounter)
|
||||||
|
|
@ -251,6 +203,7 @@ proc SemStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
|
||||||
# a generic has been added to `a`:
|
# a generic has been added to `a`:
|
||||||
if result.kind != nkEmpty: addSon(a, result)
|
if result.kind != nkEmpty: addSon(a, result)
|
||||||
result = a
|
result = a
|
||||||
|
result = hloStmt(c, result)
|
||||||
result = transformStmt(c.module, result)
|
result = transformStmt(c.module, result)
|
||||||
|
|
||||||
proc RecoverContext(c: PContext) =
|
proc RecoverContext(c: PContext) =
|
||||||
|
|
|
||||||
|
|
@ -439,50 +439,8 @@ proc skipObjConv(n: PNode): PNode =
|
||||||
of nkObjUpConv, nkObjDownConv: result = n.sons[0]
|
of nkObjUpConv, nkObjDownConv: result = n.sons[0]
|
||||||
else: result = n
|
else: result = n
|
||||||
|
|
||||||
type
|
|
||||||
TAssignableResult = enum
|
|
||||||
arNone, # no l-value and no discriminant
|
|
||||||
arLValue, # is an l-value
|
|
||||||
arLocalLValue, # is an l-value, but local var; must not escape
|
|
||||||
# its stack frame!
|
|
||||||
arDiscriminant # is a discriminant
|
|
||||||
|
|
||||||
proc isAssignable(c: PContext, n: PNode): TAssignableResult =
|
proc isAssignable(c: PContext, n: PNode): TAssignableResult =
|
||||||
result = arNone
|
result = parampatterns.isAssignable(c.p.owner, n)
|
||||||
case n.kind
|
|
||||||
of nkSym:
|
|
||||||
# don't list 'skLet' here:
|
|
||||||
if n.sym.kind in {skVar, skResult, skTemp}:
|
|
||||||
if c.p.owner.id == n.sym.owner.id and sfGlobal notin n.sym.flags:
|
|
||||||
result = arLocalLValue
|
|
||||||
else:
|
|
||||||
result = arLValue
|
|
||||||
of nkDotExpr:
|
|
||||||
if skipTypes(n.sons[0].typ, abstractInst).kind in {tyVar, tyPtr, tyRef}:
|
|
||||||
result = arLValue
|
|
||||||
else:
|
|
||||||
result = isAssignable(c, n.sons[0])
|
|
||||||
if result != arNone and sfDiscriminant in n.sons[1].sym.flags:
|
|
||||||
result = arDiscriminant
|
|
||||||
of nkBracketExpr:
|
|
||||||
if skipTypes(n.sons[0].typ, abstractInst).kind in {tyVar, tyPtr, tyRef}:
|
|
||||||
result = arLValue
|
|
||||||
else:
|
|
||||||
result = isAssignable(c, n.sons[0])
|
|
||||||
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
|
|
||||||
# Object and tuple conversions are still addressable, so we skip them
|
|
||||||
# XXX why is 'tyOpenArray' allowed here?
|
|
||||||
if skipTypes(n.typ, abstractPtrs).kind in {tyOpenArray, tyTuple, tyObject}:
|
|
||||||
result = isAssignable(c, n.sons[1])
|
|
||||||
elif compareTypes(n.typ, n.sons[1].typ, dcEqIgnoreDistinct):
|
|
||||||
# types that are equal modulo distinction preserve l-value:
|
|
||||||
result = isAssignable(c, n.sons[1])
|
|
||||||
of nkHiddenDeref, nkDerefExpr:
|
|
||||||
result = arLValue
|
|
||||||
of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
|
|
||||||
result = isAssignable(c, n.sons[0])
|
|
||||||
else:
|
|
||||||
nil
|
|
||||||
|
|
||||||
proc newHiddenAddrTaken(c: PContext, n: PNode): PNode =
|
proc newHiddenAddrTaken(c: PContext, n: PNode): PNode =
|
||||||
if n.kind == nkHiddenDeref:
|
if n.kind == nkHiddenDeref:
|
||||||
|
|
@ -1598,4 +1556,3 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||||
LocalError(n.info, errInvalidExpressionX,
|
LocalError(n.info, errInvalidExpressionX,
|
||||||
renderTree(n, {renderNoComments}))
|
renderTree(n, {renderNoComments}))
|
||||||
incl(result.flags, nfSem)
|
incl(result.flags, nfSem)
|
||||||
result = applyPatterns(c, result)
|
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,7 @@ proc instantiateBody(c: PContext, n: PNode, result: PSym) =
|
||||||
addResult(c, result.typ.sons[0], n.info, result.kind)
|
addResult(c, result.typ.sons[0], n.info, result.kind)
|
||||||
addResultNode(c, n)
|
addResultNode(c, n)
|
||||||
var b = semStmtScope(c, n.sons[bodyPos])
|
var b = semStmtScope(c, n.sons[bodyPos])
|
||||||
|
b = hloBody(c, b)
|
||||||
n.sons[bodyPos] = transformBody(c.module, b, result)
|
n.sons[bodyPos] = transformBody(c.module, b, result)
|
||||||
#echo "code instantiated ", result.name.s
|
#echo "code instantiated ", result.name.s
|
||||||
excl(result.flags, sfForward)
|
excl(result.flags, sfForward)
|
||||||
|
|
|
||||||
|
|
@ -714,7 +714,7 @@ proc semLambda(c: PContext, n: PNode): PNode =
|
||||||
LocalError(n.sons[bodyPos].info, errImplOfXNotAllowed, s.name.s)
|
LocalError(n.sons[bodyPos].info, errImplOfXNotAllowed, s.name.s)
|
||||||
pushProcCon(c, s)
|
pushProcCon(c, s)
|
||||||
addResult(c, s.typ.sons[0], n.info, skProc)
|
addResult(c, s.typ.sons[0], n.info, skProc)
|
||||||
let semBody = semStmtScope(c, n.sons[bodyPos])
|
let semBody = hloBody(c, semStmtScope(c, n.sons[bodyPos]))
|
||||||
n.sons[bodyPos] = transformBody(c.module, semBody, s)
|
n.sons[bodyPos] = transformBody(c.module, semBody, s)
|
||||||
addResultNode(c, n)
|
addResultNode(c, n)
|
||||||
popProcCon(c)
|
popProcCon(c)
|
||||||
|
|
@ -772,7 +772,6 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
||||||
rawAddSon(s.typ, nil)
|
rawAddSon(s.typ, nil)
|
||||||
if n.sons[patternPos].kind != nkEmpty:
|
if n.sons[patternPos].kind != nkEmpty:
|
||||||
n.sons[patternPos] = semPattern(c, n.sons[patternPos])
|
n.sons[patternPos] = semPattern(c, n.sons[patternPos])
|
||||||
c.patterns.add(s)
|
|
||||||
|
|
||||||
var proto = SearchForProc(c, s, c.tab.tos-2) # -2 because we have a scope
|
var proto = SearchForProc(c, s, c.tab.tos-2) # -2 because we have a scope
|
||||||
# open for parameters
|
# open for parameters
|
||||||
|
|
@ -821,7 +820,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
||||||
addResult(c, s.typ.sons[0], n.info, kind)
|
addResult(c, s.typ.sons[0], n.info, kind)
|
||||||
if sfImportc notin s.flags:
|
if sfImportc notin s.flags:
|
||||||
# no semantic checking for importc:
|
# no semantic checking for importc:
|
||||||
let semBody = semStmtScope(c, n.sons[bodyPos])
|
let semBody = hloBody(c, semStmtScope(c, n.sons[bodyPos]))
|
||||||
# unfortunately we cannot skip this step when in 'system.compiles'
|
# unfortunately we cannot skip this step when in 'system.compiles'
|
||||||
# context as it may even be evaluated in 'system.compiles':
|
# context as it may even be evaluated in 'system.compiles':
|
||||||
n.sons[bodyPos] = transformBody(c.module, semBody, s)
|
n.sons[bodyPos] = transformBody(c.module, semBody, s)
|
||||||
|
|
@ -844,6 +843,8 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
||||||
sideEffectsCheck(c, s)
|
sideEffectsCheck(c, s)
|
||||||
closeScope(c.tab) # close scope for parameters
|
closeScope(c.tab) # close scope for parameters
|
||||||
popOwner()
|
popOwner()
|
||||||
|
if n.sons[patternPos].kind != nkEmpty:
|
||||||
|
c.patterns.add(s)
|
||||||
|
|
||||||
proc semIterator(c: PContext, n: PNode): PNode =
|
proc semIterator(c: PContext, n: PNode): PNode =
|
||||||
result = semProcAux(c, n, skIterator, iteratorPragmas)
|
result = semProcAux(c, n, skIterator, iteratorPragmas)
|
||||||
|
|
@ -1222,7 +1223,6 @@ proc SemStmt(c: PContext, n: PNode): PNode =
|
||||||
result = emptyNode
|
result = emptyNode
|
||||||
else:
|
else:
|
||||||
incl(result.flags, nfSem)
|
incl(result.flags, nfSem)
|
||||||
result = applyPatterns(c, result)
|
|
||||||
|
|
||||||
proc semStmtScope(c: PContext, n: PNode): PNode =
|
proc semStmtScope(c: PContext, n: PNode): PNode =
|
||||||
openScope(c.tab)
|
openScope(c.tab)
|
||||||
|
|
|
||||||
|
|
@ -392,8 +392,6 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
|
||||||
addSon(s.typ.n, newNodeIT(nkType, n.info, s.typ.sons[0]))
|
addSon(s.typ.n, newNodeIT(nkType, n.info, s.typ.sons[0]))
|
||||||
if n.sons[patternPos].kind != nkEmpty:
|
if n.sons[patternPos].kind != nkEmpty:
|
||||||
n.sons[patternPos] = semPattern(c, n.sons[patternPos])
|
n.sons[patternPos] = semPattern(c, n.sons[patternPos])
|
||||||
c.patterns.add(s)
|
|
||||||
|
|
||||||
var ctx: TemplCtx
|
var ctx: TemplCtx
|
||||||
ctx.toBind = initIntSet()
|
ctx.toBind = initIntSet()
|
||||||
ctx.c = c
|
ctx.c = c
|
||||||
|
|
@ -417,6 +415,8 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
|
||||||
addInterfaceOverloadableSymAt(c, s, curScope)
|
addInterfaceOverloadableSymAt(c, s, curScope)
|
||||||
else:
|
else:
|
||||||
SymTabReplace(c.tab.stack[curScope], proto, s)
|
SymTabReplace(c.tab.stack[curScope], proto, s)
|
||||||
|
if n.sons[patternPos].kind != nkEmpty:
|
||||||
|
c.patterns.add(s)
|
||||||
|
|
||||||
proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
|
proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
|
||||||
template templToExpand(s: expr): expr =
|
template templToExpand(s: expr): expr =
|
||||||
|
|
@ -455,12 +455,22 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
|
||||||
result = semBindStmt(c.c, n, c.toBind)
|
result = semBindStmt(c.c, n, c.toBind)
|
||||||
of nkEmpty, nkSym..nkNilLit: nil
|
of nkEmpty, nkSym..nkNilLit: nil
|
||||||
of nkCurlyExpr:
|
of nkCurlyExpr:
|
||||||
# we support '(pattern){x}' to bind a subpattern to a parameter 'x':
|
# we support '(pattern){x}' to bind a subpattern to a parameter 'x';
|
||||||
if n.len != 2 or n.sons[1].kind != nkIdent:
|
# '(pattern){|x}' does the same but the matches will be gathered in 'x'
|
||||||
|
if n.len != 2:
|
||||||
localError(n.info, errInvalidExpression)
|
localError(n.info, errInvalidExpression)
|
||||||
else:
|
elif n.sons[1].kind == nkIdent:
|
||||||
n.sons[0] = semPatternBody(c, n.sons[0])
|
n.sons[0] = semPatternBody(c, n.sons[0])
|
||||||
n.sons[1] = expectParam(c, n.sons[1])
|
n.sons[1] = expectParam(c, n.sons[1])
|
||||||
|
elif n.sons[1].kind == nkPrefix and n.sons[1].sons[0].kind == nkIdent:
|
||||||
|
let opr = n.sons[1].sons[0]
|
||||||
|
if opr.ident.s == "|":
|
||||||
|
n.sons[0] = semPatternBody(c, n.sons[0])
|
||||||
|
n.sons[1].sons[1] = expectParam(c, n.sons[1].sons[1])
|
||||||
|
else:
|
||||||
|
localError(n.info, errInvalidExpression)
|
||||||
|
else:
|
||||||
|
localError(n.info, errInvalidExpression)
|
||||||
of nkCallKinds:
|
of nkCallKinds:
|
||||||
let s = QualifiedLookUp(c.c, n.sons[0], {})
|
let s = QualifiedLookUp(c.c, n.sons[0], {})
|
||||||
if s != nil:
|
if s != nil:
|
||||||
|
|
@ -473,7 +483,7 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
|
||||||
# we interpret `*` and `|` only as pattern operators if they occur in
|
# we interpret `*` and `|` only as pattern operators if they occur in
|
||||||
# infix notation, so that '`*`(a, b)' can be used for verbatim matching:
|
# infix notation, so that '`*`(a, b)' can be used for verbatim matching:
|
||||||
let opr = n.sons[0]
|
let opr = n.sons[0]
|
||||||
if opr.ident.s == "*":
|
if opr.ident.s == "*" or opr.ident.s == "*|":
|
||||||
result = newNodeI(nkPattern, n.info, n.len)
|
result = newNodeI(nkPattern, n.info, n.len)
|
||||||
result.sons[0] = opr
|
result.sons[0] = opr
|
||||||
result.sons[1] = semPatternBody(c, n.sons[1])
|
result.sons[1] = semPatternBody(c, n.sons[1])
|
||||||
|
|
|
||||||
|
|
@ -554,12 +554,15 @@ proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) =
|
||||||
if kind == skMacro:
|
if kind == skMacro:
|
||||||
# within a macro, every param has the type PNimrodNode!
|
# within a macro, every param has the type PNimrodNode!
|
||||||
# and param.typ.kind in {tyTypeDesc, tyExpr, tyStmt}:
|
# and param.typ.kind in {tyTypeDesc, tyExpr, tyStmt}:
|
||||||
|
# We used to copySym(param) here, but this is not possible for term
|
||||||
|
# rewriting macros; so we simply overwrite param.typ here and hope for
|
||||||
|
# the best ...
|
||||||
let nn = getSysSym"PNimrodNode"
|
let nn = getSysSym"PNimrodNode"
|
||||||
var a = copySym(param)
|
#var a = copySym(param)
|
||||||
a.typ = nn.typ
|
#a.typ = nn.typ
|
||||||
if sfGenSym notin a.flags: addDecl(c, a)
|
param.typ = nn.typ
|
||||||
else:
|
#if sfGenSym notin a.flags: addDecl(c, a)
|
||||||
if sfGenSym notin param.flags: addDecl(c, param)
|
if sfGenSym notin param.flags: addDecl(c, param)
|
||||||
|
|
||||||
proc paramTypeClass(c: PContext, paramType: PType, procKind: TSymKind):
|
proc paramTypeClass(c: PContext, paramType: PType, procKind: TSymKind):
|
||||||
tuple[typ: PType, id: PIdent] =
|
tuple[typ: PType, id: PIdent] =
|
||||||
|
|
|
||||||
|
|
@ -110,8 +110,9 @@ type
|
||||||
proto: TProtocol
|
proto: TProtocol
|
||||||
deleg: PDelegate
|
deleg: PDelegate
|
||||||
|
|
||||||
TInfo = enum
|
TInfo* = enum
|
||||||
SockIdle, SockConnecting, SockConnected, SockListening, SockClosed, SockUDPBound
|
SockIdle, SockConnecting, SockConnected, SockListening, SockClosed,
|
||||||
|
SockUDPBound
|
||||||
|
|
||||||
proc newDelegate*(): PDelegate =
|
proc newDelegate*(): PDelegate =
|
||||||
## Creates a new delegate.
|
## Creates a new delegate.
|
||||||
|
|
|
||||||
|
|
@ -184,7 +184,7 @@ proc register*(d: PDispatcher, s: PAsyncScgiState) =
|
||||||
## Registers ``s`` with dispatcher ``d``.
|
## Registers ``s`` with dispatcher ``d``.
|
||||||
var dele = newDelegate()
|
var dele = newDelegate()
|
||||||
dele.deleVal = s
|
dele.deleVal = s
|
||||||
dele.getSocket = getSocket
|
#dele.getSocket = getSocket
|
||||||
dele.handleAccept = handleAccept
|
dele.handleAccept = handleAccept
|
||||||
d.register(dele)
|
d.register(dele)
|
||||||
|
|
||||||
|
|
|
||||||
26
tests/patterns/tmatrix.nim
Normal file
26
tests/patterns/tmatrix.nim
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
discard """
|
||||||
|
output: "21"
|
||||||
|
"""
|
||||||
|
|
||||||
|
import macros
|
||||||
|
|
||||||
|
type
|
||||||
|
TMat = object
|
||||||
|
dummy: int
|
||||||
|
|
||||||
|
proc `*`(a, b: TMat): TMat = nil
|
||||||
|
proc `+`(a, b: TMat): TMat = nil
|
||||||
|
proc `-`(a, b: TMat): TMat = nil
|
||||||
|
proc `$`(a: TMat): string = result = $a.dummy
|
||||||
|
|
||||||
|
macro optOps{ (`+`|`-`|`*`) *| a }(a: varargs[TMat]): expr =
|
||||||
|
result = newIntLitNode(21)
|
||||||
|
|
||||||
|
#macro optPlus{ `+` * a }(a: varargs[TMat]): expr =
|
||||||
|
# result = newIntLitNode(21)
|
||||||
|
|
||||||
|
var x, y, z: TMat
|
||||||
|
|
||||||
|
echo x + y * z - x
|
||||||
|
|
||||||
|
#echo x + y + z
|
||||||
10
tests/patterns/tnoendlessrec.nim
Normal file
10
tests/patterns/tnoendlessrec.nim
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
discard """
|
||||||
|
output: "4"
|
||||||
|
"""
|
||||||
|
|
||||||
|
# test that an endless recursion is avoided:
|
||||||
|
|
||||||
|
template optLen{len(x)}(x: expr): expr = len(x)
|
||||||
|
|
||||||
|
var s = "lala"
|
||||||
|
echo len(s)
|
||||||
|
|
@ -10,7 +10,7 @@ proc `&&`(s: varargs[string]): string =
|
||||||
for i in 1..len(s)-1: result.add s[i]
|
for i in 1..len(s)-1: result.add s[i]
|
||||||
inc calls
|
inc calls
|
||||||
|
|
||||||
template optConc{ `&&` * a }(a: expr): expr = &&a
|
template optConc{ `&&` * a }(a: varargs[string]): expr = &&a
|
||||||
|
|
||||||
let space = " "
|
let space = " "
|
||||||
echo "my" && (space & "awe" && "some " ) && "concat"
|
echo "my" && (space & "awe" && "some " ) && "concat"
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ discard """
|
||||||
"""
|
"""
|
||||||
|
|
||||||
template optZero{x+x}(x: int): int = x*3
|
template optZero{x+x}(x: int): int = x*3
|
||||||
template andthen{x*3}(x: int): int = x*4
|
template andthen{`*`(x,3)}(x: int): int = x*4
|
||||||
template optSubstr1{x = substr(x, a, b)}(x: string, a, b: int) = setlen(x, b+1)
|
template optSubstr1{x = substr(x, 0, b)}(x: string, b: int) = setlen(x, b+1)
|
||||||
|
|
||||||
template disallowIf{
|
template disallowIf{
|
||||||
if cond: action
|
if cond: action
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
discard """
|
discard """
|
||||||
file: "ttypenoval.nim"
|
file: "ttypenoval.nim"
|
||||||
line: 38
|
line: 38
|
||||||
errormsg: "type mismatch: got (typedesc{int}) but expected 'int'"
|
errormsg: "type mismatch: got (typedesc[int]) but expected 'int'"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# A min-heap.
|
# A min-heap.
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ hel'''
|
||||||
"""
|
"""
|
||||||
|
|
||||||
template optZero{x+x}(x: int): int = x*3
|
template optZero{x+x}(x: int): int = x*3
|
||||||
template andthen{x*3}(x: int): int = x*4
|
template andthen{`*`(x,3)}(x: int): int = x*4
|
||||||
template optSubstr1{x = substr(x, a, b)}(x: string, a, b: int) = setlen(x, b+1)
|
template optSubstr1{x = substr(x, a, b)}(x: string, a, b: int) = setlen(x, b+1)
|
||||||
|
|
||||||
var y = 12
|
var y = 12
|
||||||
|
|
|
||||||
1
todo.txt
1
todo.txt
|
|
@ -2,6 +2,7 @@ version 0.9.0
|
||||||
=============
|
=============
|
||||||
|
|
||||||
- make 'm: stmt' use overloading resolution
|
- make 'm: stmt' use overloading resolution
|
||||||
|
- finish, test and document tmatrix
|
||||||
- document pattern matching
|
- document pattern matching
|
||||||
|
|
||||||
- make 'bind' default for templates and introduce 'mixin'
|
- make 'bind' default for templates and introduce 'mixin'
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue