diff options
-rwxr-xr-x | compiler/ast.nim | 5 | ||||
-rwxr-xr-x | compiler/parser.nim | 8 | ||||
-rwxr-xr-x | compiler/semexprs.nim | 55 | ||||
-rwxr-xr-x | doc/manual.txt | 37 | ||||
-rwxr-xr-x | lib/system.nim | 2 | ||||
-rw-r--r-- | tests/reject/tcaseexpr1.nim | 30 | ||||
-rwxr-xr-x | tests/run/tcasestm.nim | 24 | ||||
-rwxr-xr-x | web/news.txt | 2 |
8 files changed, 138 insertions, 25 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index c6e4d8318..aa94644aa 100755 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -781,6 +781,11 @@ proc add*(father, son: PNode) = proc `[]`*(n: PNode, i: int): PNode {.inline.} = result = n.sons[i] +# son access operators with support for negative indices +template `{}`*(n: PNode, i: int): expr = n[i -| n] +template `{}=`*(n: PNode, i: int, s: PNode): stmt = + n.sons[i -| n] = s + var emptyNode* = newNode(nkEmpty) # There is a single empty node that is shared! Do not overwrite it! diff --git a/compiler/parser.nim b/compiler/parser.nim index cdbe42c7e..7612980c5 100755 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -743,7 +743,7 @@ proc isExprStart(p: TParser): bool = case p.tok.tokType of tkSymbol, tkAccent, tkOpr, tkNot, tkNil, tkCast, tkIf, tkProc, tkBind, tkParLe, tkBracketLe, tkCurlyLe, tkIntLit..tkCharLit, tkVar, tkRef, tkPtr, - tkTuple, tkType, tkWhen: + tkTuple, tkType, tkWhen, tkCase: result = true else: result = false @@ -763,9 +763,9 @@ proc parseExpr(p: var TParser): PNode = case p.tok.tokType: of tkIf: result = parseIfExpr(p, nkIfExpr) of tkWhen: result = parseIfExpr(p, nkWhenExpr) + of tkCase: result = parseCase(p) else: result = lowestExpr(p) # XXX needs proper support: - #of tkCase: result = parseCase(p) #of tkTry: result = parseTry(p) proc primary(p: var TParser, skipSuffix = false): PNode = @@ -1044,9 +1044,9 @@ proc parseCase(p: var TParser): PNode = if b.kind == nkElse: break if wasIndented: - eat(p, tkDed) + if p.tok.tokType != tkEof: eat(p, tkDed) popInd(p.lex) - + proc parseTry(p: var TParser): PNode = result = newNodeP(nkTryStmt, p) getTok(p) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 17e6e8048..a658f7f44 100755 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1528,6 +1528,57 @@ proc semMacroStmt(c: PContext, n: PNode, flags: TExprFlags, renderTree(a, {renderNoComments})) result = errorNode(c, n) +proc semCaseExpr(c: PContext, caseStmt: PNode): PNode = + # The case expression is simply rewritten to a StmtListExpr: + # var res {.noInit, genSym.}: type(values) + # + # case E + # of X: res = value1 + # of Y: res = value2 + # + # res + var + info = caseStmt.info + resVar = newSym(skVar, getIdent":res", getCurrOwner(), info) + resNode = newSymNode(resVar, info) + resType: PType + + resVar.flags = { sfGenSym, sfNoInit } + + for i in countup(1, caseStmt.len - 1): + var cs = caseStmt[i] + case cs.kind + of nkOfBranch, nkElifBranch, nkElse: + # the value is always the last son regardless of the branch kind + cs.checkMinSonsLen 1 + var value = cs{-1} + if value.kind == nkStmtList: value.kind = nkStmtListExpr + + value = semExprWithType(c, value) + if resType == nil: + resType = value.typ + elif not sameType(resType, value.typ): + # XXX: semeType is a bit too harsh. + # work on finding a common base type. + # this will be useful for arrays/seq too: + # [ref DerivedA, ref DerivedB, ref Base] + typeMismatch(cs, resType, value.typ) + + cs{-1} = newNode(nkAsgn, cs.info, @[resNode, value]) + else: + IllFormedAst(caseStmt) + + result = newNode(nkStmtListExpr, info, @[ + newNode(nkVarSection, info, @[ + newNode(nkIdentDefs, info, @[ + resNode, + symNodeFromType(c, resType, info), + emptyNode])]), + caseStmt, + resNode]) + + result = semStmtListExpr(c, result) + proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = result = n if gCmd == cmdIdeTools: suggestExpr(c, n) @@ -1707,7 +1758,9 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = of nkTryStmt: result = semTry(c, n) of nkBreakStmt, nkContinueStmt: result = semBreakOrContinue(c, n) of nkForStmt, nkParForStmt: result = semFor(c, n) - of nkCaseStmt: result = semCase(c, n) + of nkCaseStmt: + if efWantStmt in flags: result = semCase(c, n) + else: result = semCaseExpr(c, n) of nkReturnStmt: result = semReturn(c, n) of nkAsmStmt: result = semAsm(c, n) of nkYieldStmt: result = semYield(c, n) diff --git a/doc/manual.txt b/doc/manual.txt index b5f5d9d63..a85c1e753 100755 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -2237,6 +2237,28 @@ An if expression always results in a value, so the ``else`` part is required. ``Elif`` parts are also allowed (but unlikely to be good style). +When expression +~~~~~~~~~~~~~~~ + +Just like an `if expression`, but corresponding to the when statement. + +Case expression +~~~~~~~~~~~~~~~ + +The `case expression` is again very similar to the case statement: + +.. code-block:: nimrod + var favoriteFood = case animal + of "dog": "bones" + of "cat": "mice" + elif animal.endsWith"whale": "plankton" + else: + echo "I'm not sure what to serve, but everybody loves ice cream" + "ice cream" + +As seen in the above example, the case expression can also introduce side +effects. When multiple statements are given for a branch, Nimrod will use +the last expression as the result value, much like in an `expr` template. Table constructor ~~~~~~~~~~~~~~~~~ @@ -3464,15 +3486,14 @@ a type-safe wrapper for the unsafe `printf` function form C: macro safePrintF(formatString: string{lit}, args: vararg[expr]): expr = var i = 0 for c in formatChars(formatString): - const FormatChars = { - 'c': char, - 'd', 'i', 'x', 'X': int, - 'f', 'e', 'E', 'g', 'G': float, - 's': string, - 'p': pointer, - } + var expectedType = case c + of 'c': char + of 'd', 'i', 'x', 'X': int + of 'f', 'e', 'E', 'g', 'G': float + of 's': string + of 'p': pointer + else: EOutOfRange - var expectedType = find(FormatChars, c, EOutOfRange) var actualType = args[i].getType inc i diff --git a/lib/system.nim b/lib/system.nim index 4cfa6ba84..99f1e621a 100755 --- a/lib/system.nim +++ b/lib/system.nim @@ -2116,7 +2116,7 @@ proc `/`*(x, y: int): float {.inline, noSideEffect.} = ## integer division that results in a float. result = toFloat(x) / toFloat(y) -template `-|`(b, s: expr): expr = +template `-|`*(b, s: expr): expr = (if b >= 0: b else: s.len + b) proc `[]`*(s: string, x: TSlice[int]): string {.inline.} = diff --git a/tests/reject/tcaseexpr1.nim b/tests/reject/tcaseexpr1.nim new file mode 100644 index 000000000..0c2e7c452 --- /dev/null +++ b/tests/reject/tcaseexpr1.nim @@ -0,0 +1,30 @@ +discard """ + file: "tcaseexpr1.nim" + + line: 23 + errormsg: "not all cases are covered" + + line: 29 + errormsg: "type mismatch: got (string) but expected 'int'" +""" + +type + E = enum A, B, C + +proc foo(x): auto = + return case x + of 1..9: "digit" + else: "number" + +var r = foo(10) + +var x = C + +var t1 = case x: + of A: "a" + of B: "b" + +var t2 = case x: + of A: 10 + of B, C: "23" + diff --git a/tests/run/tcasestm.nim b/tests/run/tcasestm.nim index cb63e0c51..003ec6e50 100755 --- a/tests/run/tcasestm.nim +++ b/tests/run/tcasestm.nim @@ -1,6 +1,6 @@ discard """ file: "tcasestm.nim" - output: "ayyy" + output: "ayyydd" """ # Test the case statement @@ -22,16 +22,18 @@ of "aa", "bb": write(stdout, "Du bist nicht mein Meister") of "cc", "hash", "when": nil of "will", "it", "finally", "be", "generated": nil -case i -of 1..5, 8, 9: nil -of 6, 7: nil -elif x == "Ha": - nil -elif x == "yyy": - write(stdout, x) -else: - nil - +var z = case i + of 1..5, 8, 9: "aa" + of 6, 7: "bb" + elif x == "Ha": + "cc" + elif x == "yyy": + write(stdout, x) + "dd" + else: + "zz" + +echo z #OUT ayyy diff --git a/web/news.txt b/web/news.txt index d063c3974..a3ba80072 100755 --- a/web/news.txt +++ b/web/news.txt @@ -30,7 +30,9 @@ Compiler Additions Language Additions ------------------ +- ``case expressions`` are now supported. - table constructors now mimic more closely the syntax of case... of... +- Nimrod can now infer the return type of a proc from its body 2012-09-23 Version 0.9.0 released |