diff options
author | Andreas Rumpf <rumpf_a@web.de> | 2018-08-16 00:16:49 +0200 |
---|---|---|
committer | Andreas Rumpf <rumpf_a@web.de> | 2018-08-16 00:16:49 +0200 |
commit | 1061e26bc997cd3b74c6b08862ad7145039f2550 (patch) | |
tree | a611040d7fa773de2ea443f62771c4d6c8b8cab8 /compiler | |
parent | da41fc18018058e01ac11132f7ff29651e3fdaa7 (diff) | |
download | Nim-1061e26bc997cd3b74c6b08862ad7145039f2550.tar.gz |
implements 'case statement macros' in order to encourage the development of pattern matching mechanisms that are not terrible to look at
Diffstat (limited to 'compiler')
-rw-r--r-- | compiler/options.nim | 4 | ||||
-rw-r--r-- | compiler/semstmts.nim | 181 |
2 files changed, 110 insertions, 75 deletions
diff --git a/compiler/options.nim b/compiler/options.nim index bc5545488..1873d9d5b 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -119,8 +119,8 @@ type destructor, notnil, dynamicBindSym, - forLoopMacros - #caseStmtMacros + forLoopMacros, + caseStmtMacros SymbolFilesOption* = enum disabledSf, writeOnlySf, readOnlySf, v2Sf diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 7eb915dad..170ac799e 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -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) |