implements 'case statement macros' in order to encourage the development of pattern matching mechanisms that are not terrible to look at
This commit is contained in:
parent
da41fc1801
commit
1061e26bc9
4 changed files with 172 additions and 79 deletions
|
|
@ -178,71 +178,6 @@ proc semIf(c: PContext, n: PNode): PNode =
|
|||
result.kind = nkIfExpr
|
||||
result.typ = typ
|
||||
|
||||
proc semCase(c: PContext, n: PNode): PNode =
|
||||
result = n
|
||||
checkMinSonsLen(n, 2, c.config)
|
||||
openScope(c)
|
||||
n.sons[0] = semExprWithType(c, n.sons[0])
|
||||
var chckCovered = false
|
||||
var covered: BiggestInt = 0
|
||||
var typ = commonTypeBegin
|
||||
var hasElse = false
|
||||
let caseTyp = skipTypes(n.sons[0].typ, abstractVarRange-{tyTypeDesc})
|
||||
case caseTyp.kind
|
||||
of tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt32, tyBool:
|
||||
chckCovered = true
|
||||
of tyFloat..tyFloat128, tyString, tyError:
|
||||
discard
|
||||
else:
|
||||
localError(c.config, n.info, errSelectorMustBeOfCertainTypes)
|
||||
return
|
||||
for i in countup(1, sonsLen(n) - 1):
|
||||
var x = n.sons[i]
|
||||
when defined(nimsuggest):
|
||||
if c.config.ideCmd == ideSug and exactEquals(c.config.m.trackPos, x.info) and caseTyp.kind == tyEnum:
|
||||
suggestEnum(c, x, caseTyp)
|
||||
case x.kind
|
||||
of nkOfBranch:
|
||||
checkMinSonsLen(x, 2, c.config)
|
||||
semCaseBranch(c, n, x, i, covered)
|
||||
var last = sonsLen(x)-1
|
||||
x.sons[last] = semExprBranchScope(c, x.sons[last])
|
||||
typ = commonType(typ, x.sons[last])
|
||||
of nkElifBranch:
|
||||
chckCovered = false
|
||||
checkSonsLen(x, 2, c.config)
|
||||
openScope(c)
|
||||
x.sons[0] = forceBool(c, semExprWithType(c, x.sons[0]))
|
||||
x.sons[1] = semExprBranch(c, x.sons[1])
|
||||
typ = commonType(typ, x.sons[1])
|
||||
closeScope(c)
|
||||
of nkElse:
|
||||
chckCovered = false
|
||||
checkSonsLen(x, 1, c.config)
|
||||
x.sons[0] = semExprBranchScope(c, x.sons[0])
|
||||
typ = commonType(typ, x.sons[0])
|
||||
hasElse = true
|
||||
else:
|
||||
illFormedAst(x, c.config)
|
||||
if chckCovered:
|
||||
if covered == toCover(c, n.sons[0].typ):
|
||||
hasElse = true
|
||||
else:
|
||||
localError(c.config, n.info, "not all cases are covered")
|
||||
closeScope(c)
|
||||
if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or not hasElse:
|
||||
for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon)
|
||||
# propagate any enforced VoidContext:
|
||||
if typ == c.enforceVoidContext:
|
||||
result.typ = c.enforceVoidContext
|
||||
else:
|
||||
for i in 1..n.len-1:
|
||||
var it = n.sons[i]
|
||||
let j = it.len-1
|
||||
if not endsInNoReturn(it.sons[j]):
|
||||
it.sons[j] = fitNode(c, typ, it.sons[j], it.sons[j].info)
|
||||
result.typ = typ
|
||||
|
||||
proc semTry(c: PContext, n: PNode): PNode =
|
||||
|
||||
var check = initIntSet()
|
||||
|
|
@ -683,29 +618,28 @@ proc isTrivalStmtExpr(n: PNode): bool =
|
|||
return false
|
||||
result = true
|
||||
|
||||
proc handleForLoopMacro(c: PContext; n: PNode): PNode =
|
||||
let iterExpr = n[^2]
|
||||
if iterExpr.kind in nkCallKinds:
|
||||
proc handleStmtMacro(c: PContext; n, selector: PNode; magicType: string): PNode =
|
||||
if selector.kind in nkCallKinds:
|
||||
# we transform
|
||||
# n := for a, b, c in m(x, y, z): Y
|
||||
# to
|
||||
# m(n)
|
||||
let forLoopStmt = magicsys.getCompilerProc(c.graph, "ForLoopStmt")
|
||||
if forLoopStmt == nil: return
|
||||
let maType = magicsys.getCompilerProc(c.graph, magicType)
|
||||
if maType == nil: return
|
||||
|
||||
let headSymbol = iterExpr[0]
|
||||
let headSymbol = selector[0]
|
||||
var o: TOverloadIter
|
||||
var match: PSym = nil
|
||||
var symx = initOverloadIter(o, c, headSymbol)
|
||||
while symx != nil:
|
||||
if symx.kind in {skTemplate, skMacro}:
|
||||
if symx.typ.len == 2 and symx.typ[1] == forLoopStmt.typ:
|
||||
if symx.typ.len == 2 and symx.typ[1] == maType.typ:
|
||||
if match == nil:
|
||||
match = symx
|
||||
else:
|
||||
localError(c.config, n.info, errAmbiguousCallXYZ % [
|
||||
getProcHeader(c.config, match),
|
||||
getProcHeader(c.config, symx), $iterExpr])
|
||||
getProcHeader(c.config, symx), $selector])
|
||||
symx = nextOverloadIter(o, c, headSymbol)
|
||||
|
||||
if match == nil: return
|
||||
|
|
@ -717,6 +651,38 @@ proc handleForLoopMacro(c: PContext; n: PNode): PNode =
|
|||
of skTemplate: result = semTemplateExpr(c, callExpr, match, {})
|
||||
else: result = nil
|
||||
|
||||
proc handleForLoopMacro(c: PContext; n: PNode): PNode =
|
||||
result = handleStmtMacro(c, n, n[^2], "ForLoopStmt")
|
||||
|
||||
proc handleCaseStmtMacro(c: PContext; n: PNode): PNode =
|
||||
# n[0] has been sem'checked and has a type. We use this to resolve
|
||||
# 'match(n[0])' but then we pass 'n' to the 'match' macro. This seems to
|
||||
# be the best solution.
|
||||
var toResolve = newNodeI(nkCall, n.info)
|
||||
toResolve.add newIdentNode(getIdent(c.cache, "match"), n.info)
|
||||
toResolve.add n[0]
|
||||
|
||||
var errors: CandidateErrors
|
||||
var r = resolveOverloads(c, toResolve, toResolve, {skTemplate, skMacro}, {},
|
||||
errors, false)
|
||||
if r.state == csMatch:
|
||||
var match = r.calleeSym
|
||||
markUsed(c.config, n[0].info, match, c.graph.usageSym)
|
||||
styleCheckUse(n[0].info, match)
|
||||
|
||||
# but pass 'n' to the 'match' macro, not 'n[0]':
|
||||
r.call.sons[1] = n
|
||||
let toExpand = semResolvedCall(c, r, r.call, {})
|
||||
case match.kind
|
||||
of skMacro: result = semMacroExpr(c, toExpand, toExpand, match, {})
|
||||
of skTemplate: result = semTemplateExpr(c, toExpand, match, {})
|
||||
else: result = nil
|
||||
# this would be the perfectly consistent solution with 'for loop macros',
|
||||
# but it kinda sucks for pattern matching as the matcher is not attached to
|
||||
# a type then:
|
||||
when false:
|
||||
result = handleStmtMacro(c, n, n[0], "CaseStmt")
|
||||
|
||||
proc semFor(c: PContext, n: PNode): PNode =
|
||||
checkMinSonsLen(n, 3, c.config)
|
||||
var length = sonsLen(n)
|
||||
|
|
@ -758,6 +724,75 @@ proc semFor(c: PContext, n: PNode): PNode =
|
|||
result.typ = c.enforceVoidContext
|
||||
closeScope(c)
|
||||
|
||||
proc semCase(c: PContext, n: PNode): PNode =
|
||||
result = n
|
||||
checkMinSonsLen(n, 2, c.config)
|
||||
openScope(c)
|
||||
n.sons[0] = semExprWithType(c, n.sons[0])
|
||||
var chckCovered = false
|
||||
var covered: BiggestInt = 0
|
||||
var typ = commonTypeBegin
|
||||
var hasElse = false
|
||||
let caseTyp = skipTypes(n.sons[0].typ, abstractVarRange-{tyTypeDesc})
|
||||
case caseTyp.kind
|
||||
of tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt32, tyBool:
|
||||
chckCovered = true
|
||||
of tyFloat..tyFloat128, tyString, tyError:
|
||||
discard
|
||||
else:
|
||||
if caseStmtMacros in c.features:
|
||||
result = handleCaseStmtMacro(c, n)
|
||||
if result != nil: return result
|
||||
|
||||
localError(c.config, n.info, errSelectorMustBeOfCertainTypes)
|
||||
return
|
||||
for i in countup(1, sonsLen(n) - 1):
|
||||
var x = n.sons[i]
|
||||
when defined(nimsuggest):
|
||||
if c.config.ideCmd == ideSug and exactEquals(c.config.m.trackPos, x.info) and caseTyp.kind == tyEnum:
|
||||
suggestEnum(c, x, caseTyp)
|
||||
case x.kind
|
||||
of nkOfBranch:
|
||||
checkMinSonsLen(x, 2, c.config)
|
||||
semCaseBranch(c, n, x, i, covered)
|
||||
var last = sonsLen(x)-1
|
||||
x.sons[last] = semExprBranchScope(c, x.sons[last])
|
||||
typ = commonType(typ, x.sons[last])
|
||||
of nkElifBranch:
|
||||
chckCovered = false
|
||||
checkSonsLen(x, 2, c.config)
|
||||
openScope(c)
|
||||
x.sons[0] = forceBool(c, semExprWithType(c, x.sons[0]))
|
||||
x.sons[1] = semExprBranch(c, x.sons[1])
|
||||
typ = commonType(typ, x.sons[1])
|
||||
closeScope(c)
|
||||
of nkElse:
|
||||
chckCovered = false
|
||||
checkSonsLen(x, 1, c.config)
|
||||
x.sons[0] = semExprBranchScope(c, x.sons[0])
|
||||
typ = commonType(typ, x.sons[0])
|
||||
hasElse = true
|
||||
else:
|
||||
illFormedAst(x, c.config)
|
||||
if chckCovered:
|
||||
if covered == toCover(c, n.sons[0].typ):
|
||||
hasElse = true
|
||||
else:
|
||||
localError(c.config, n.info, "not all cases are covered")
|
||||
closeScope(c)
|
||||
if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or not hasElse:
|
||||
for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon)
|
||||
# propagate any enforced VoidContext:
|
||||
if typ == c.enforceVoidContext:
|
||||
result.typ = c.enforceVoidContext
|
||||
else:
|
||||
for i in 1..n.len-1:
|
||||
var it = n.sons[i]
|
||||
let j = it.len-1
|
||||
if not endsInNoReturn(it.sons[j]):
|
||||
it.sons[j] = fitNode(c, typ, it.sons[j], it.sons[j].info)
|
||||
result.typ = typ
|
||||
|
||||
proc semRaise(c: PContext, n: PNode): PNode =
|
||||
result = n
|
||||
checkSonsLen(n, 1, c.config)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue