summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rwxr-xr-xcompiler/ast.nim5
-rwxr-xr-xcompiler/parser.nim8
-rwxr-xr-xcompiler/semexprs.nim55
-rwxr-xr-xdoc/manual.txt37
-rwxr-xr-xlib/system.nim2
-rw-r--r--tests/reject/tcaseexpr1.nim30
-rwxr-xr-xtests/run/tcasestm.nim24
-rwxr-xr-xweb/news.txt2
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