summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/ccgexprs.nim5
-rw-r--r--compiler/ccgstmts.nim5
-rw-r--r--compiler/parser.nim94
-rw-r--r--compiler/sem.nim5
-rw-r--r--compiler/semexprs.nim79
-rw-r--r--compiler/semfold.nim7
-rw-r--r--compiler/semstmts.nim46
-rw-r--r--todo.txt10
8 files changed, 159 insertions, 92 deletions
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index 6a2ffe752..78173b5e8 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -861,15 +861,14 @@ proc genIfExpr(p: BProc, n: PNode, d: var TLoc) =
   Lend = getLabel(p)
   for i in countup(0, sonsLen(n) - 1):
     it = n.sons[i]
-    case it.kind
-    of nkElifExpr:
+    if it.len == 2:
       initLocExpr(p, it.sons[0], a)
       Lelse = getLabel(p)
       lineF(p, cpsStmts, "if (!$1) goto $2;$n", [rdLoc(a), Lelse])
       expr(p, it.sons[1], tmp)
       lineF(p, cpsStmts, "goto $1;$n", [Lend])
       fixLabel(p, Lelse)
-    of nkElseExpr:
+    elif it.len == 1:
       expr(p, it.sons[0], tmp)
     else: internalError(n.info, "genIfExpr()")
   fixLabel(p, Lend)
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index e11678861..1f0ffd656 100644
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -231,8 +231,7 @@ proc genIfStmt(p: BProc, n: PNode) =
   var Lend = getLabel(p)
   for i in countup(0, sonsLen(n) - 1): 
     var it = n.sons[i]
-    case it.kind
-    of nkElifBranch: 
+    if it.len == 2: 
       initLocExpr(p, it.sons[0], a)
       Lelse = getLabel(p)
       inc(p.labels)
@@ -243,7 +242,7 @@ proc genIfStmt(p: BProc, n: PNode) =
       if sonsLen(n) > 1: 
         lineFF(p, cpsStmts, "goto $1;$n", "br label %$1$n", [Lend])
       fixLabel(p, Lelse)
-    of nkElse:
+    elif it.len == 1:
       genSimpleBlock(p, it.sons[0])
     else: internalError(n.info, "genIfStmt()")
   if sonsLen(n) > 1: fixLabel(p, Lend)
diff --git a/compiler/parser.nim b/compiler/parser.nim
index 517297a87..f9f7f7fad 100644
--- a/compiler/parser.nim
+++ b/compiler/parser.nim
@@ -315,9 +315,7 @@ proc indexExprList(p: var TParser, first: PNode, k: TNodeKind,
   optPar(p)
   eat(p, endToken)
 
-proc exprColonEqExpr(p: var TParser): PNode =
-  #| exprColonEqExpr = expr (':'|'=' expr)?
-  var a = parseExpr(p)
+proc colonOrEquals(p: var TParser, a: PNode): PNode =
   if p.tok.tokType == tkColon:
     result = newNodeP(nkExprColonExpr, p)
     getTok(p)
@@ -333,6 +331,11 @@ proc exprColonEqExpr(p: var TParser): PNode =
   else:
     result = a
 
+proc exprColonEqExpr(p: var TParser): PNode =
+  #| exprColonEqExpr = expr (':'|'=' expr)?
+  var a = parseExpr(p)
+  result = colonOrEquals(p, a)
+
 proc exprList(p: var TParser, endTok: TTokType, result: PNode) = 
   #| exprList = expr ^+ comma
   getTok(p)
@@ -443,9 +446,76 @@ proc parseGStrLit(p: var TParser, a: PNode): PNode =
     getTok(p)
   else:
     result = a
-  
-proc identOrLiteral(p: var TParser): PNode = 
-  #| generalizedLit ::= GENERALIZED_STR_LIT | GENERALIZED_TRIPLESTR_LIT
+
+type
+  TPrimaryMode = enum pmNormal, pmTypeDesc, pmTypeDef, pmSkipSuffix
+
+proc complexOrSimpleStmt(p: var TParser): PNode
+proc simpleExpr(p: var TParser, mode = pmNormal): PNode
+
+proc semiStmtList(p: var TParser, result: PNode) =
+  if p.tok.tokType == tkSemicolon:
+    # '(;' enforces 'stmt' context:
+    getTok(p)
+    optInd(p, result)
+  result.add(complexOrSimpleStmt(p))
+  while p.tok.tokType == tkSemicolon:
+    getTok(p)
+    optInd(p, result)
+    result.add(complexOrSimpleStmt(p))
+  result.kind = nkStmtListExpr
+
+proc parsePar(p: var TParser): PNode =
+  #| parKeyw = 'discard' | 'include' | 'if' | 'while' | 'case' | 'try'
+  #|         | 'finally' | 'except' | 'for' | 'block' | 'const' | 'let'
+  #|         | 'when' | 'var' | 'bind' | 'mixin'
+  #| par = '(' optInd (&parKeyw complexOrSimpleStmt ^+ ';' 
+  #|                  | simpleExpr ('=' expr (';' complexOrSimpleStmt ^+ ';' )? )?
+  #|                             | (':' expr)? (',' (exprColonEqExpr comma?)*)?  )?
+  #|         optPar ')'
+  #
+  # unfortunately it's ambiguous: (expr: expr) vs (exprStmt); however a 
+  # leading ';' could be used to enforce a 'stmt' context ...
+  result = newNodeP(nkPar, p)
+  getTok(p)
+  optInd(p, result)
+  if p.tok.tokType in {tkDiscard, tkInclude, tkIf, tkWhile, tkCase, 
+                       tkTry, tkFinally, tkExcept, tkFor, tkBlock, 
+                       tkConst, tkLet, tkWhen, tkVar, tkBind, 
+                       tkMixin, tkSemicolon}:
+    semiStmtList(p, result)
+  elif p.tok.tokType != tkParRi:
+    var a = simpleExpr(p)
+    if p.tok.tokType == tkEquals:
+      # special case: allow assignments
+      getTok(p)
+      optInd(p, result)
+      let b = parseExpr(p)
+      let asgn = newNodeI(nkAsgn, a.info, 2)
+      asgn.sons[0] = a
+      asgn.sons[1] = b
+      result.add(asgn)
+    elif p.tok.tokType == tkSemicolon:
+      # stmt context:
+      result.add(a)
+      semiStmtList(p, result)
+    else:
+      a = colonOrEquals(p, a)
+      result.add(a)
+      if p.tok.tokType == tkComma:
+        getTok(p)
+        skipComment(p, a)
+        while p.tok.tokType != tkParRi and p.tok.tokType != tkEof:
+          var a = exprColonEqExpr(p)
+          addSon(result, a)
+          if p.tok.tokType != tkComma: break 
+          getTok(p)
+          skipComment(p, a)
+  optPar(p)
+  eat(p, tkParRi)
+
+proc identOrLiteral(p: var TParser, mode: TPrimaryMode): PNode = 
+  #| generalizedLit = GENERALIZED_STR_LIT | GENERALIZED_TRIPLESTR_LIT
   #| identOrLiteral = generalizedLit | symbol 
   #|                | INT_LIT | INT8_LIT | INT16_LIT | INT32_LIT | INT64_LIT
   #|                | UINT_LIT | UINT8_LIT | UINT16_LIT | UINT32_LIT | UINT64_LIT
@@ -453,7 +523,7 @@ proc identOrLiteral(p: var TParser): PNode =
   #|                | STR_LIT | RSTR_LIT | TRIPLESTR_LIT
   #|                | CHAR_LIT
   #|                | NIL
-  #|                | tupleConstr | arrayConstr | setOrTableConstr
+  #|                | par | arrayConstr | setOrTableConstr
   #|                | castExpr
   #| tupleConstr = '(' optInd (exprColonEqExpr comma?)* optPar ')'
   #| arrayConstr = '[' optInd (exprColonEqExpr comma?)* optPar ']'
@@ -537,7 +607,10 @@ proc identOrLiteral(p: var TParser): PNode =
     getTok(p)
   of tkParLe:
     # () constructor
-    result = exprColonEqExprList(p, nkPar, tkParRi)
+    if mode in {pmTypeDesc, pmTypeDef}:
+      result = exprColonEqExprList(p, nkPar, tkParRi)
+    else:
+      result = parsePar(p)
   of tkCurlyLe:
     # {} constructor
     result = setOrTableConstr(p)
@@ -583,9 +656,6 @@ proc primarySuffix(p: var TParser, r: PNode): PNode =
       result = indexExprList(p, result, nkCurlyExpr, tkCurlyRi)
     else: break
 
-type
-  TPrimaryMode = enum pmNormal, pmTypeDesc, pmTypeDef, pmSkipSuffix
-
 proc primary(p: var TParser, mode: TPrimaryMode): PNode
 
 proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode =
@@ -926,7 +996,7 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
     optInd(p, result)
     addSon(result, primary(p, pmNormal))
   else:
-    result = identOrLiteral(p)
+    result = identOrLiteral(p, mode)
     if mode != pmSkipSuffix:
       result = primarySuffix(p, result)
 
diff --git a/compiler/sem.nim b/compiler/sem.nim
index 88249fedb..0e4b65d9f 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -58,10 +58,13 @@ proc fitNode(c: PContext, formal: PType, arg: PNode): PNode =
     result = copyNode(arg)
     result.typ = formal
 
+var CommonTypeBegin = PType(kind: tyExpr)
+
 proc commonType*(x, y: PType): PType =
   # new type relation that is used for array constructors,
   # if expressions, etc.:
-  if x == nil: return y
+  if x == nil: return x
+  if y == nil: return y
   var a = skipTypes(x, {tyGenericInst})
   var b = skipTypes(y, {tyGenericInst})
   result = x
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 8b1e5aea1..9b7318e62 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -1458,26 +1458,73 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
   of mQuoteAst: result = semQuoteAst(c, n)
   else: result = semDirectOp(c, n, flags)
 
-proc semIfExpr(c: PContext, n: PNode): PNode = 
-  result = n
-  checkMinSonsLen(n, 2)
-  var typ: PType = nil
+proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
+  # If semCheck is set to false, ``when`` will return the verbatim AST of
+  # the correct branch. Otherwise the AST will be passed through semStmt.
+  result = nil
+  
+  template setResult(e: expr) =
+    if semCheck: result = semStmt(c, e) # do not open a new scope!
+    else: result = e
+
   for i in countup(0, sonsLen(n) - 1): 
     var it = n.sons[i]
     case it.kind
-    of nkElifExpr: 
+    of nkElifBranch, nkElifExpr: 
       checkSonsLen(it, 2)
-      it.sons[0] = forceBool(c, semExprWithType(c, it.sons[0]))
-      it.sons[1] = semExprWithType(c, it.sons[1])
-      if typ == nil: typ = it.sons[1].typ
-      else: it.sons[1] = fitNode(c, typ, it.sons[1])
-    of nkElseExpr: 
+      var e = semConstExpr(c, it.sons[0])
+      if e.kind != nkIntLit: InternalError(n.info, "semWhen")
+      elif e.intVal != 0 and result == nil:
+        setResult(it.sons[1]) 
+    of nkElse, nkElseExpr:
       checkSonsLen(it, 1)
-      it.sons[0] = semExprWithType(c, it.sons[0])
-      if typ != nil: it.sons[0] = fitNode(c, typ, it.sons[0])
-      else: InternalError(it.info, "semIfExpr")
+      if result == nil: 
+        setResult(it.sons[0])
     else: illFormedAst(n)
-  result.typ = typ
+  if result == nil: 
+    result = newNodeI(nkNilLit, n.info) 
+  # The ``when`` statement implements the mechanism for platform dependent
+  # code. Thus we try to ensure here consistent ID allocation after the
+  # ``when`` statement.
+  IDsynchronizationPoint(200)
+
+
+proc semExprBranch(c: PContext, n: PNode): PNode =
+  result = semExpr(c, n)
+  if result.typ != nil:
+    # XXX tyGenericInst here?
+    semProcvarCheck(c, result)
+    if result.typ.kind == tyVar: result = newDeref(result)
+    semDestructorCheck(c, result, {})
+
+proc semIf(c: PContext, n: PNode): PNode = 
+  result = n
+  var typ = CommonTypeBegin
+  var hasElse = false
+  for i in countup(0, sonsLen(n) - 1): 
+    var it = n.sons[i]
+    if it.len == 2:
+      it.sons[0] = forceBool(c, semExprWithType(c, it.sons[0]))
+      openScope(c.tab)
+      it.sons[1] = semExprBranch(c, it.sons[1])
+      typ = commonType(typ, it.sons[1].typ)
+      closeScope(c.tab)
+    elif it.len == 1:
+      hasElse = true
+      openScope(c.tab)
+      it.sons[0] = semExprBranch(c, it.sons[0])
+      typ = commonType(typ, it.sons[0].typ)
+      closeScope(c.tab)
+    else: illFormedAst(it)
+  if isEmptyType(typ) or not hasElse:
+    for it in n: discardCheck(it.lastSon)
+    result.kind = nkIfStmt
+  else:
+    for it in n:
+      let j = it.len-1
+      it.sons[j] = fitNode(c, typ, it.sons[j])
+    result.kind = nkIfExpr
+    result.typ = typ
 
 proc semSetConstr(c: PContext, n: PNode): PNode = 
   result = newNodeI(nkCurly, n.info)
@@ -1739,7 +1786,6 @@ proc fixImmediateParams(n: PNode): PNode =
   # the planned overload resolution reforms
   for i in 1 .. <safeLen(n):
     if n[i].kind == nkDo: n.sons[i] = n[i][bodyPos]
-  
   result = n
 
 proc semExport(c: PContext, n: PNode): PNode =
@@ -1910,7 +1956,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
     checkSonsLen(n, 1)
     n.sons[0] = semExpr(c, n.sons[0], flags)
   of nkCast: result = semCast(c, n)
-  of nkIfExpr: result = semIfExpr(c, n)
+  of nkIfExpr, nkIfStmt: result = semIf(c, n)
   of nkStmtListExpr: result = semStmtListExpr(c, n)
   of nkBlockExpr: result = semBlockExpr(c, n)
   of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkHiddenCallConv: 
@@ -1936,7 +1982,6 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   of nkLetSection: result = semVarOrLet(c, n, skLet)
   of nkConstSection: result = semConst(c, n)
   of nkTypeSection: result = SemTypeSection(c, n)
-  of nkIfStmt: result = SemIf(c, n)
   of nkDiscardStmt: result = semDiscard(c, n)
   of nkWhileStmt: result = semWhile(c, n)
   of nkTryStmt: result = semTry(c, n)
diff --git a/compiler/semfold.nim b/compiler/semfold.nim
index 8142b5e03..cc1f4b5ee 100644
--- a/compiler/semfold.nim
+++ b/compiler/semfold.nim
@@ -378,18 +378,17 @@ proc getConstIfExpr(c: PSym, n: PNode): PNode =
   result = nil
   for i in countup(0, sonsLen(n) - 1): 
     var it = n.sons[i]
-    case it.kind
-    of nkElifExpr: 
+    if it.len == 2:
       var e = getConstExpr(c, it.sons[0])
       if e == nil: return nil
       if getOrdValue(e) != 0: 
         if result == nil: 
           result = getConstExpr(c, it.sons[1])
           if result == nil: return 
-    of nkElseExpr: 
+    elif it.len == 1:
       if result == nil: result = getConstExpr(c, it.sons[0])
     else: internalError(it.info, "getConstIfExpr()")
-  
+
 proc partialAndExpr(c: PSym, n: PNode): PNode = 
   # partial evaluation
   result = n
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 9e9de7260..e1d8ed5a8 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -12,52 +12,6 @@
 
 proc semCommand(c: PContext, n: PNode): PNode =
   result = semExprNoType(c, n)
-
-proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
-  # If semCheck is set to false, ``when`` will return the verbatim AST of
-  # the correct branch. Otherwise the AST will be passed through semStmt.
-  result = nil
-  
-  template setResult(e: expr) =
-    if semCheck: result = semStmt(c, e) # do not open a new scope!
-    else: result = e
-
-  for i in countup(0, sonsLen(n) - 1): 
-    var it = n.sons[i]
-    case it.kind
-    of nkElifBranch, nkElifExpr: 
-      checkSonsLen(it, 2)
-      var e = semConstExpr(c, it.sons[0])
-      if e.kind != nkIntLit: InternalError(n.info, "semWhen")
-      elif e.intVal != 0 and result == nil:
-        setResult(it.sons[1]) 
-    of nkElse, nkElseExpr:
-      checkSonsLen(it, 1)
-      if result == nil: 
-        setResult(it.sons[0])
-    else: illFormedAst(n)
-  if result == nil: 
-    result = newNodeI(nkNilLit, n.info) 
-  # The ``when`` statement implements the mechanism for platform dependent
-  # code. Thus we try to ensure here consistent ID allocation after the
-  # ``when`` statement.
-  IDsynchronizationPoint(200)
-
-proc semIf(c: PContext, n: PNode): PNode = 
-  result = n
-  for i in countup(0, sonsLen(n) - 1): 
-    var it = n.sons[i]
-    case it.kind
-    of nkElifBranch: 
-      checkSonsLen(it, 2)
-      it.sons[0] = forceBool(c, semExprWithType(c, it.sons[0]))
-      openScope(c.tab)
-      it.sons[1] = semStmt(c, it.sons[1])
-      closeScope(c.tab)
-    of nkElse: 
-      if sonsLen(it) == 1: it.sons[0] = semStmtScope(c, it.sons[0])
-      else: illFormedAst(it)
-    else: illFormedAst(n)
   
 proc semDiscard(c: PContext, n: PNode): PNode = 
   result = n
diff --git a/todo.txt b/todo.txt
index 8b1ecd358..b827b56cb 100644
--- a/todo.txt
+++ b/todo.txt
@@ -10,18 +10,16 @@ version 0.9.2
 - parser/grammar:
   * check that of branches can only receive even simpler expressions, don't
     allow 'of (var x = 23; nkIdent)'
-  * allow (var x = 12; for i in ... ; x) construct
-  * try except as an expression
+  * document (var x = 12; for i in ... ; x) construct
 - make use of commonType relation in expressions
 - further expr/stmt unification:
-  - nkIfStmt vs nkIfExpr
-  - start with JS backend and support exprs everywhere
-  - then enhance C backend
-  - OR: do the temp stuff in transf
+  - rewrite nkCaseExpr handling
+  - try except as an expression
 
 Bugs
 ====
 
+- new parser breaks docgen
 - docgen: sometimes effects are listed twice
 - 'result' is not properly cleaned for NRVO
 - instantiated generics are listed in error messages