summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rwxr-xr-xcompiler/ast.nim2
-rwxr-xr-xcompiler/evals.nim11
-rwxr-xr-xcompiler/msgs.nim6
-rwxr-xr-xcompiler/semstmts.nim14
-rwxr-xr-xlib/core/macros.nim16
-rw-r--r--lib/pure/unittest.nim157
-rwxr-xr-xlib/system.nim21
-rw-r--r--tests/accept/run/tunit.nim47
8 files changed, 258 insertions, 16 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index b33d99554..e60a52fc6 100755
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -369,7 +369,7 @@ type
     mCompileOption, mCompileOptionArg,
     mNLen, mNChild, mNSetChild, mNAdd, mNAddMultiple, mNDel, mNKind, 
     mNIntVal, mNFloatVal, mNSymbol, mNIdent, mNGetType, mNStrVal, mNSetIntVal, 
-    mNSetFloatVal, mNSetSymbol, mNSetIdent, mNSetType, mNSetStrVal, 
+    mNSetFloatVal, mNSetSymbol, mNSetIdent, mNSetType, mNSetStrVal, mNLineInfo,
     mNNewNimNode, mNCopyNimNode, mNCopyNimTree, mStrToIdent, mIdentToStr, 
     mEqIdent, mEqNimrodNode, mNHint, mNWarning, mNError, mGetTypeInfo
 
diff --git a/compiler/evals.nim b/compiler/evals.nim
index b7b3746a5..db15b0370 100755
--- a/compiler/evals.nim
+++ b/compiler/evals.nim
@@ -798,8 +798,9 @@ proc evalMacroCall*(c: PEvalContext, n: PNode, sym: PSym): PNode =
 import
   semdata, sem
   
-proc evalExpandToAst(c: PEvalContext, n: PNode): PNode =
-  var 
+proc evalExpandToAst(c: PEvalContext, original: PNode): PNode =
+  var
+    n = original.copyTree
     macroCall = n.sons[1]
     expandedSym = macroCall.sons[0].sym
     
@@ -854,7 +855,7 @@ proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode =
   of mParseExprToAst: result = evalParseExpr(c, n)
   of mParseStmtToAst: result = evalParseStmt(c, n)
   of mExpandMacroToAst: result = evalExpandToAst(c, n)
-  of mNLen: 
+  of mNLen:
     result = evalAux(c, n.sons[1], {efLValue})
     if isSpecial(result): return 
     var a = result
@@ -1060,6 +1061,10 @@ proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode =
     if (a == b) or
         (b.kind in {nkNilLit, nkEmpty}) and (a.kind in {nkNilLit, nkEmpty}): 
       result.intVal = 1
+  of mNLineInfo:
+    result = evalAux(c, n.sons[1], {})
+    if isSpecial(result): return
+    result = newStrNodeT(result.info.toFileLineCol, n)
   of mAstToYaml:
     var ast = evalAux(c, n.sons[1], {efLValue})
     result = newStrNode(nkStrLit, ast.treeToYaml.ropeToStr)
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index d34c6b410..4f8a21f54 100755
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -456,6 +456,12 @@ proc ToLinenumber*(info: TLineInfo): int {.inline.} =
 proc toColumn*(info: TLineInfo): int {.inline.} = 
   result = info.col
 
+proc toFileLine*(info: TLineInfo): string {.inline.} =
+  result = info.toFilename & ":" & $info.line
+
+proc toFileLineCol*(info: TLineInfo): string {.inline.} =
+  result = info.toFilename & "(" & $info.line & "," & $info.col & ")"
+
 var checkPoints: seq[TLineInfo] = @[]
 
 proc addCheckpoint*(info: TLineInfo) = 
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index c00b68bb5..057c99e94 100755
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -428,7 +428,7 @@ proc semRaise(c: PContext, n: PNode): PNode =
     var typ = n.sons[0].typ
     if typ.kind != tyRef or typ.sons[0].kind != tyObject: 
       localError(n.info, errExprCannotBeRaised)
-  
+
 proc semTry(c: PContext, n: PNode): PNode = 
   result = n
   checkMinSonsLen(n, 2)
@@ -438,15 +438,19 @@ proc semTry(c: PContext, n: PNode): PNode =
     var a = n.sons[i]
     checkMinSonsLen(a, 1)
     var length = sonsLen(a)
-    if a.kind == nkExceptBranch: 
-      for j in countup(0, length - 2): 
+    if a.kind == nkExceptBranch:
+      if length == 2 and a.sons[0].kind == nkBracket:
+        a.sons.splice(0, 1, a.sons[0].sons)
+        length = a.sonsLen
+
+      for j in countup(0, length - 2):
         var typ = semTypeNode(c, a.sons[j], nil)
         if typ.kind == tyRef: typ = typ.sons[0]
-        if typ.kind != tyObject: 
+        if typ.kind != tyObject:
           GlobalError(a.sons[j].info, errExprCannotBeRaised)
         a.sons[j] = newNodeI(nkType, a.sons[j].info)
         a.sons[j].typ = typ
-        if ContainsOrIncl(check, typ.id): 
+        if ContainsOrIncl(check, typ.id):
           localError(a.sons[j].info, errExceptionAlreadyHandled)
     elif a.kind != nkFinally: 
       illFormedAst(n) 
diff --git a/lib/core/macros.nim b/lib/core/macros.nim
index c5afcdf17..e61575f3e 100755
--- a/lib/core/macros.nim
+++ b/lib/core/macros.nim
@@ -36,8 +36,8 @@ type
     nnkMacroDef, nnkTemplateDef, nnkIteratorDef, nnkOfBranch, 

     nnkElifBranch, nnkExceptBranch, nnkElse, nnkMacroStmt, 

     nnkAsmStmt, nnkPragma, nnkIfStmt, nnkWhenStmt, 

-    nnkForStmt, nnkWhileStmt, nnkCaseStmt, 
-    nnkVarSection, nnkLetSection, nnkConstSection, 
+    nnkForStmt, nnkWhileStmt, nnkCaseStmt, 

+    nnkVarSection, nnkLetSection, nnkConstSection, 

     nnkConstDef, nnkTypeSection, nnkTypeDef, 

     nnkYieldStmt, nnkTryStmt, nnkFinally, nnkRaiseStmt, 

     nnkReturnStmt, nnkBreakStmt, nnkContinueStmt, nnkBlockStmt, 

@@ -45,8 +45,8 @@ type
     nnkIncludeStmt, nnkCommentStmt, nnkStmtListExpr, nnkBlockExpr, 

     nnkStmtListType, nnkBlockType, nnkTypeOfExpr, nnkObjectTy, 

     nnkTupleTy, nnkRecList, nnkRecCase, nnkRecWhen, 

-    nnkRefTy, nnkPtrTy, nnkVarTy, 
-    nnkConstTy, nnkMutableTy,
+    nnkRefTy, nnkPtrTy, nnkVarTy, 

+    nnkConstTy, nnkMutableTy,

     nnkDistinctTy, 

     nnkProcTy, nnkEnumTy, nnkEnumFieldDef, nnkReturnToken

   TNimNodeKinds* = set[TNimrodNodeKind]

@@ -187,6 +187,8 @@ proc toStrLit*(n: PNimrodNode): PNimrodNode {.compileTime.} =
   ## in a string literal node

   return newStrLitNode(repr(n))

 

+proc lineinfo*(n: PNimrodNode): string {.magic: "NLineInfo".}

+

 proc toLisp*(n: PNimrodNode): string {.compileTime.} =

   ## Convert the AST `n` to a human-readable string

   ##

@@ -230,9 +232,9 @@ proc parseStmt*(s: string): stmt {.magic: "ParseStmtToAst".}
 

 proc getAst*(macroOrTemplate: expr): expr {.magic: "ExpandMacroToAst".}

   ## Obtains the AST nodes returned from a macro or template invocation.

-  ## Example:
-  ## 
-  ## .. code-block:: nimrod
+  ## Example:

+  ## 

+  ## .. code-block:: nimrod

   ##

   ##   macro FooMacro() = 

   ##     var ast = getAst(BarTemplate())

diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim
new file mode 100644
index 000000000..a5c97ee9b
--- /dev/null
+++ b/lib/pure/unittest.nim
@@ -0,0 +1,157 @@
+#

+#

+#            Nimrod's Runtime Library

+#        (c) Copyright 2011 Nimrod Contributors

+#

+#    See the file "copying.txt", included in this

+#    distribution, for details about the copyright.

+#

+

+## This module implements the standard unit testing facilities such as

+## suites, fixtures and test cases as well as facilities for combinatorial 

+## and randomzied test case generation (not yet available) 

+## and object mocking (not yet available)

+##

+## It is loosely based on C++'s boost.test and Haskell's QuickTest

+##

+## Maintainer: Zahary Karadjov (zah@github)

+##

+

+import

+  macros

+

+type

+  TestStatus* = enum OK, FAILED

+  # ETestFailed* = object of ESynch

+

+var 

+  # XXX: These better be thread-local

+  AbortOnError* = false

+  checkpoints: seq[string] = @[]

+

+template TestSetupIMPL*: stmt = nil

+template TestTeardownIMPL*: stmt = nil

+

+proc shouldRun(testName: string): bool =

+  result = true

+

+template suite*(name: expr, body: stmt): stmt =

+  block:

+    template setup(setupBody: stmt): stmt =

+      template TestSetupIMPL: stmt = setupBody

+

+    template teardown(teardownBody: stmt): stmt =

+      template TestTeardownIMPL: stmt = teardownBody

+

+    body

+

+template test*(name: expr, body: stmt): stmt =

+  if bind shouldRun(name):

+    bind checkpoints = @[]

+    var TestStatusIMPL = OK

+    

+    try:

+      TestSetupIMPL()

+      body

+

+    finally:

+      TestTeardownIMPL()

+      echo "[" & $TestStatusIMPL & "] " & name

+

+proc checkpoint*(msg: string) =

+  checkpoints.add(msg)

+  # TODO: add support for something like SCOPED_TRACE from Google Test

+

+template fail* =

+  for msg in items(bind checkpoints):

+    echo msg

+

+  if AbortOnError: quit(1)

+  

+  TestStatusIMPL = FAILED

+  checkpoints = @[]

+

+macro check*(conditions: stmt): stmt =

+  proc standardRewrite(e: expr): stmt =

+    template rewrite(Exp, lineInfoLit: expr, expLit: string): stmt =

+      if not Exp:

+        checkpoint(lineInfoLit & ": Check failed: " & expLit)

+        fail()

+

+    # XXX: If we don't create a string literal node below, the compiler

+    # will SEGFAULT in a rather strange fashion:

+    #

+    # rewrite(e, e.toStrLit, e.toStrLit) is ok, but

+    #

+    # rewrite(e, e.lineinfo, e.toStrLit) or

+    # rewrite(e, "anything", e.toStrLit) are not

+    #

+    # It may have something to do with the dummyContext hack in 

+    # evals.nim/evalTemplate

+    #

+    result = getAst(rewrite(e, newStrLitNode(e.lineinfo), e.toStrLit))

+  

+  case conditions.kind

+  of nnkCall, nnkCommand, nnkMacroStmt:

+    case conditions[1].kind

+    of nnkInfix:

+      proc rewriteBinaryOp(op: expr): stmt =

+        template rewrite(op, left, right, lineInfoLit: expr, opLit, leftLit, rightLit: string): stmt =

+          block:

+            var 

+              lhs = left

+              rhs = right

+

+            if not `op`(lhs, rhs):

+              checkpoint(lineInfoLit & ": Check failed: " & opLit)

+              checkpoint("  " & leftLit & " was " & $lhs)

+              checkpoint("  " & rightLit & " was " & $rhs)

+              fail()

+

+        result = getAst(rewrite(

+          op[0], op[1], op[2],

+          newStrLitNode(op.lineinfo),

+          op.toStrLit,

+          op[1].toStrLit,

+          op[2].toStrLit))

+        

+      result = rewriteBinaryOp(conditions[1])

+  

+    of nnkCall, nnkCommand:

+      # TODO: We can print out the call arguments in case of failure

+      result = standardRewrite(conditions[1])

+

+    of nnkStmtList:

+      result = newNimNode(nnkStmtList)

+      for i in countup(0, conditions[1].len - 1):

+        result.add(newCall(!"check", conditions[1][i]))

+

+    else:

+      result = standardRewrite(conditions[1])

+

+  else:

+    error conditions.lineinfo & ": Malformed check statement"

+

+template require*(conditions: stmt): stmt =

+  block:

+    const AbortOnError = true    

+    check conditions

+

+macro expect*(exp: stmt): stmt =

+  template expectBody(errorTypes, lineInfoLit: expr, body: stmt): stmt =

+    try:

+      body

+      checkpoint(lineInfoLit & ": Expect Failed, no exception was thrown.")

+      fail()

+    except errorTypes:

+      nil

+

+  var expectCall = exp[0]

+  var body = exp[1]

+  

+  var errorTypes = newNimNode(nnkBracket)

+  for i in countup(1, expectCall.len - 1):

+    errorTypes.add(expectCall[i])

+

+  result = getAst(expectBody(errorTypes, newStrLitNode(exp.lineinfo), body))

+

diff --git a/lib/system.nim b/lib/system.nim
index 226642771..b6f5243e5 100755
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -857,6 +857,27 @@ proc insert*[T](x: var seq[T], item: T, i = 0) {.noSideEffect.} =
     dec(j)
   x[i] = item
 
+template spliceImpl(x, start, count, elements: expr): stmt =
+  var 
+    shift = elements.len - count
+    newLen = x.len + shift
+    totalShifted = x.len - (start + count)
+    firstShifted = newLen - totalShifted
+    
+  if shift > 0:
+    setLen(x, newLen)
+
+  for i in countup(firstShifted, newLen - 1):
+    shallowCopy(x[i], x[i-shift])
+
+  for c in countup(0, elements.len - 1):
+    x[start + c] = elements[c]
+
+  if shift < 0:
+    setLen(x, newLen)
+
+proc splice*[T](x: var seq[T], start, count: int, elements: openarray[T] = []) =
+  spliceImpl(x, start, count, elements)
 
 proc repr*[T](x: T): string {.magic: "Repr", noSideEffect.}
   ## takes any Nimrod variable and returns its string representation. It
diff --git a/tests/accept/run/tunit.nim b/tests/accept/run/tunit.nim
new file mode 100644
index 000000000..d0e975119
--- /dev/null
+++ b/tests/accept/run/tunit.nim
@@ -0,0 +1,47 @@
+import

+  unittest, macros

+

+var

+    a = 1

+    b = 22

+    c = 1

+    d = 3

+

+suite "my suite":

+  setup:

+    echo "suite setup"

+    var testVar = "from setup"

+    

+  teardown:

+    echo "suite teardown"

+

+  test "first suite test":

+    testVar = "modified"

+    echo "test var: " & testVar

+    check a > b

+

+  test "second suite test":

+    echo "test var: " & testVar

+

+proc foo: bool =

+  echo "running foo"

+  return true

+

+proc err =

+  raise newException(EArithmetic, "some exception")

+

+test "final test":

+  echo "inside suite-less test"

+

+  check:

+    a == c

+    foo()

+    d > 10

+

+test "arithmetic failure":

+  expect(EArithmetic):

+    err()

+

+  expect(EArithmetic, ESystem):

+    discard foo()

+