term rewriting improvements

This commit is contained in:
Araq 2012-09-08 15:43:21 +02:00
commit b64eeeb430
18 changed files with 284 additions and 144 deletions

91
compiler/hlo.nim Normal file
View 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)

View file

@ -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

View file

@ -86,23 +86,37 @@ 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 =
@ -122,12 +136,18 @@ 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:
if p.sons[1].kind == nkPrefix:
if matches(c, p.sons[0], n):
gather(c, p.sons[1].sons[1].sym, n)
result = true
else:
assert isPatternParam(c, p.sons[1]) assert isPatternParam(c, p.sons[1])
if matches(c, p.sons[0], n): if matches(c, p.sons[0], n):
result = bindOrCheck(c, p.sons[1].sym, n) result = bindOrCheck(c, p.sons[1].sym, n)
@ -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,24 +241,29 @@ 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:
@ -236,9 +271,8 @@ proc applyRule*(c: PContext, s: PSym, n: PNode): PNode =
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:

View file

@ -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) =

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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])

View file

@ -554,11 +554,14 @@ 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):

View file

@ -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.

View file

@ -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)

View 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

View 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)

View file

@ -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"

View file

@ -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

View file

@ -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.

View file

@ -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

View file

@ -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'