implemented case expressions

This commit is contained in:
Zahary Karadjov
2012-10-01 23:48:37 +03:00
parent 92f70b08f9
commit 770d4a997e
8 changed files with 137 additions and 24 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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