diff options
Diffstat (limited to 'lib')
-rwxr-xr-x | lib/core/macros.nim | 18 | ||||
-rwxr-xr-x | lib/pure/terminal.nim | 38 | ||||
-rw-r--r-- | lib/pure/unittest.nim | 150 | ||||
-rwxr-xr-x | lib/system.nim | 25 |
4 files changed, 215 insertions, 16 deletions
diff --git a/lib/core/macros.nim b/lib/core/macros.nim index c5afcdf17..825979e27 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 ## @@ -228,11 +230,11 @@ proc parseStmt*(s: string): stmt {.magic: "ParseStmtToAst".} ## Compiles the passed string to its AST representation. ## Expects one or more statements. -proc getAst*(macroOrTemplate: expr): expr {.magic: "ExpandMacroToAst".} +proc getAst*(macroOrTemplate: expr): expr {.magic: "ExpandToAst".} ## 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/terminal.nim b/lib/pure/terminal.nim index 232601640..ab9c31c8f 100755 --- a/lib/pure/terminal.nim +++ b/lib/pure/terminal.nim @@ -14,6 +14,8 @@ ## Changing the style is permanent even after program termination! Use the ## code ``system.addQuitProc(resetAttributes)`` to restore the defaults. +import macros + when defined(windows): import windows, os @@ -210,24 +212,32 @@ type when not defined(windows): var + # XXX: These better be thread-local gFG = 0 gBG = 0 -proc WriteStyled*(txt: string, style: set[TStyle] = {styleBright}) = - ## writes the text `txt` in a given `style`. +proc setStyle*(style: set[TStyle]) = + ## sets the terminal style when defined(windows): var a = 0'i16 if styleBright in style: a = a or int16(FOREGROUND_INTENSITY) if styleBlink in style: a = a or int16(BACKGROUND_INTENSITY) if styleReverse in style: a = a or 0x4000'i16 # COMMON_LVB_REVERSE_VIDEO if styleUnderscore in style: a = a or 0x8000'i16 # COMMON_LVB_UNDERSCORE - var old = getAttributes() discard SetConsoleTextAttribute(conHandle, old or a) - stdout.write(txt) - discard SetConsoleTextAttribute(conHandle, old) else: for s in items(style): stdout.write("\e[" & $ord(s) & 'm') + +proc WriteStyled*(txt: string, style: set[TStyle] = {styleBright}) = + ## writes the text `txt` in a given `style`. + when defined(windows): + var old = getAttributes() + setStyle(style) + stdout.write(txt) + discard SetConsoleTextAttribute(conHandle, old) + else: + setStyle(style) stdout.write(txt) resetAttributes() if gFG != 0: @@ -298,6 +308,24 @@ proc setBackgroundColor*(bg: TBackgroundColor, bright=false) = if bright: inc(gBG, 60) stdout.write("\e[" & $gBG & 'm') +# XXX: +# These should be private, but there is no yet +# facility for binding local symbols within macros +proc styledEchoProcessArg*(s: string) = write stdout, s +proc styledEchoProcessArg*(style: TStyle) = setStyle {style} +proc styledEchoProcessArg*(style: set[TStyle]) = setStyle style +proc styledEchoProcessArg*(color: TForegroundColor) = setForeGroundColor color +proc styledEchoProcessArg*(color: TBackgroundColor) = setBackGroundColor color + +macro styledEcho*(m: stmt): stmt = + result = newNimNode(nnkStmtList) + + for i in countup(1, m.len - 1): + result.add(newCall(!"styledEchoProcessArg", m[i])) + + result.add(newCall(!"write", newIdentNode("stdout"), newStrLitNode("\n"))) + result.add(newCall(!"resetAttributes")) + when isMainModule: system.addQuitProc(resetAttributes) write(stdout, "never mind") diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim new file mode 100644 index 000000000..db3e5a1db --- /dev/null +++ b/lib/pure/unittest.nim @@ -0,0 +1,150 @@ +# +# +# 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, terminal + +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 + +proc printStatus*(s: TestStatus, name: string) = + var color = (if s == OK: fgGreen else: fgRed) + styledEcho styleBright, color, "[", $s, "] ", fgWhite, name, "\n" + +template test*(name: expr, body: stmt): stmt = + if bind shouldRun(name): + bind checkpoints = @[] + var TestStatusIMPL = OK + + try: + TestSetupIMPL() + body + + finally: + TestTeardownIMPL() + printStatus(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() + + result = getAst(rewrite(e, 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], + 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, exp.lineinfo, body)) + diff --git a/lib/system.nim b/lib/system.nim index 226642771..7a7a1c33b 100755 --- a/lib/system.nim +++ b/lib/system.nim @@ -857,7 +857,6 @@ proc insert*[T](x: var seq[T], item: T, i = 0) {.noSideEffect.} = dec(j) x[i] = item - proc repr*[T](x: T): string {.magic: "Repr", noSideEffect.} ## takes any Nimrod variable and returns its string representation. It ## works even for complex data graphs with cycles. This is a great @@ -1922,6 +1921,26 @@ proc `[]`*(s: string, x: TSlice[int]): string {.inline.} = ## slice operation for strings. Negative indexes are supported. result = s.substr(x.a-|s, x.b-|s) +template spliceImpl(x, start, endp, spliced: expr): stmt = + var + count = endp - start + 1 + shift = spliced.len - count + newLen = x.len + shift + totalShifted = x.len - (start + count) + firstShifted = newLen - totalShifted + + if shift > 0: + setLen(x, newLen) + + for i in countdown(newLen - 1, firstShifted): + shallowCopy(x[i], x[i-shift]) + + for c in countup(0, spliced.len - 1): + x[start + c] = spliced[c] + + if shift < 0: + setLen(x, newLen) + proc `[]=`*(s: var string, x: TSlice[int], b: string) = ## slice assignment for strings. Negative indexes are supported. var a = x.a-|s @@ -1929,7 +1948,7 @@ proc `[]=`*(s: var string, x: TSlice[int], b: string) = if L == b.len: for i in 0 .. <L: s[i+a] = b[i] else: - raise newException(EOutOfRange, "differing lengths for slice assignment") + spliceImpl(s, x.a, x.b, b) proc `[]`*[Idx, T](a: array[Idx, T], x: TSlice[int]): seq[T] = ## slice operation for arrays. Negative indexes are NOT supported because @@ -1983,7 +2002,7 @@ proc `[]=`*[T](s: var seq[T], x: TSlice[int], b: openArray[T]) = if L == b.len: for i in 0 .. <L: s[i+a] = b[i] else: - raise newException(EOutOfRange, "differing lengths for slice assignment") + spliceImpl(s, x.a, x.b, b) proc getTypeInfo*[T](x: T): pointer {.magic: "GetTypeInfo".} ## get type information for `x`. Ordinary code should not use this, but |