Merge pull request #58 from zah/getast-unittests
getAst operational. Unit testing library based on it.
This commit is contained in:
commit
fae8ea0cee
14 changed files with 446 additions and 101 deletions
|
|
@ -327,7 +327,7 @@ type
|
||||||
TMagic* = enum # symbols that require compiler magic:
|
TMagic* = enum # symbols that require compiler magic:
|
||||||
mNone, mDefined, mDefinedInScope, mLow, mHigh, mSizeOf, mIs, mOf,
|
mNone, mDefined, mDefinedInScope, mLow, mHigh, mSizeOf, mIs, mOf,
|
||||||
mEcho, mShallowCopy, mSlurp,
|
mEcho, mShallowCopy, mSlurp,
|
||||||
mAstToYaml, mParseExprToAst, mParseStmtToAst, mExpandMacroToAst,
|
mAstToYaml, mParseExprToAst, mParseStmtToAst, mExpandToAst,
|
||||||
mUnaryLt, mSucc,
|
mUnaryLt, mSucc,
|
||||||
mPred, mInc, mDec, mOrd, mNew, mNewFinalize, mNewSeq, mLengthOpenArray,
|
mPred, mInc, mDec, mOrd, mNew, mNewFinalize, mNewSeq, mLengthOpenArray,
|
||||||
mLengthStr, mLengthArray, mLengthSeq, mIncl, mExcl, mCard, mChr, mGCref,
|
mLengthStr, mLengthArray, mLengthSeq, mIncl, mExcl, mCard, mChr, mGCref,
|
||||||
|
|
@ -369,7 +369,7 @@ type
|
||||||
mCompileOption, mCompileOptionArg,
|
mCompileOption, mCompileOptionArg,
|
||||||
mNLen, mNChild, mNSetChild, mNAdd, mNAddMultiple, mNDel, mNKind,
|
mNLen, mNChild, mNSetChild, mNAdd, mNAddMultiple, mNDel, mNKind,
|
||||||
mNIntVal, mNFloatVal, mNSymbol, mNIdent, mNGetType, mNStrVal, mNSetIntVal,
|
mNIntVal, mNFloatVal, mNSymbol, mNIdent, mNGetType, mNStrVal, mNSetIntVal,
|
||||||
mNSetFloatVal, mNSetSymbol, mNSetIdent, mNSetType, mNSetStrVal,
|
mNSetFloatVal, mNSetSymbol, mNSetIdent, mNSetType, mNSetStrVal, mNLineInfo,
|
||||||
mNNewNimNode, mNCopyNimNode, mNCopyNimTree, mStrToIdent, mIdentToStr,
|
mNNewNimNode, mNCopyNimNode, mNCopyNimTree, mStrToIdent, mIdentToStr,
|
||||||
mEqIdent, mEqNimrodNode, mNHint, mNWarning, mNError, mGetTypeInfo
|
mEqIdent, mEqNimrodNode, mNHint, mNWarning, mNError, mGetTypeInfo
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,7 @@ proc popStackFrame*(c: PEvalContext) {.inline.} =
|
||||||
if (c.tos == nil): InternalError("popStackFrame")
|
if (c.tos == nil): InternalError("popStackFrame")
|
||||||
c.tos = c.tos.next
|
c.tos = c.tos.next
|
||||||
|
|
||||||
|
proc eval*(c: PEvalContext, n: PNode): PNode
|
||||||
proc evalAux(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode
|
proc evalAux(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode
|
||||||
|
|
||||||
proc stackTraceAux(x: PStackFrame) =
|
proc stackTraceAux(x: PStackFrame) =
|
||||||
|
|
@ -764,7 +765,7 @@ proc isEmpty(n: PNode): bool =
|
||||||
proc stringStartingLine(s: PNode): int =
|
proc stringStartingLine(s: PNode): int =
|
||||||
result = s.info.line - countLines(s.strVal)
|
result = s.info.line - countLines(s.strVal)
|
||||||
|
|
||||||
proc evalParseExpr(c: PEvalContext, n: Pnode): Pnode =
|
proc evalParseExpr(c: PEvalContext, n: PNode): PNode =
|
||||||
var code = evalAux(c, n.sons[1], {})
|
var code = evalAux(c, n.sons[1], {})
|
||||||
var ast = parseString(code.getStrValue, code.info.toFilename,
|
var ast = parseString(code.getStrValue, code.info.toFilename,
|
||||||
code.stringStartingLine)
|
code.stringStartingLine)
|
||||||
|
|
@ -773,12 +774,108 @@ proc evalParseExpr(c: PEvalContext, n: Pnode): Pnode =
|
||||||
result = ast.sons[0]
|
result = ast.sons[0]
|
||||||
result.typ = newType(tyExpr, c.module)
|
result.typ = newType(tyExpr, c.module)
|
||||||
|
|
||||||
proc evalParseStmt(c: PEvalContext, n: Pnode): Pnode =
|
proc evalParseStmt(c: PEvalContext, n: PNode): PNode =
|
||||||
var code = evalAux(c, n.sons[1], {})
|
var code = evalAux(c, n.sons[1], {})
|
||||||
result = parseString(code.getStrValue, code.info.toFilename,
|
result = parseString(code.getStrValue, code.info.toFilename,
|
||||||
code.stringStartingLine)
|
code.stringStartingLine)
|
||||||
result.typ = newType(tyStmt, c.module)
|
result.typ = newType(tyStmt, c.module)
|
||||||
|
|
||||||
|
proc evalTemplateAux*(templ, actual: PNode, sym: PSym): PNode =
|
||||||
|
case templ.kind
|
||||||
|
of nkSym:
|
||||||
|
var p = templ.sym
|
||||||
|
if (p.kind == skParam) and (p.owner.id == sym.id):
|
||||||
|
result = copyTree(actual.sons[p.position])
|
||||||
|
else:
|
||||||
|
result = copyNode(templ)
|
||||||
|
of nkNone..nkIdent, nkType..nkNilLit: # atom
|
||||||
|
result = copyNode(templ)
|
||||||
|
else:
|
||||||
|
result = copyNode(templ)
|
||||||
|
newSons(result, sonsLen(templ))
|
||||||
|
for i in countup(0, sonsLen(templ) - 1):
|
||||||
|
result.sons[i] = evalTemplateAux(templ.sons[i], actual, sym)
|
||||||
|
|
||||||
|
proc evalTemplateArgs(n: PNode, s: PSym): PNode =
|
||||||
|
var
|
||||||
|
f, a: int
|
||||||
|
arg: PNode
|
||||||
|
|
||||||
|
f = sonsLen(s.typ)
|
||||||
|
|
||||||
|
# if the template has zero arguments, it can be called without ``()``
|
||||||
|
# `n` is then a nkSym or something similar
|
||||||
|
case n.kind
|
||||||
|
of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit:
|
||||||
|
a = sonsLen(n)
|
||||||
|
else: a = 0
|
||||||
|
|
||||||
|
if a > f: GlobalError(n.info, errWrongNumberOfArguments)
|
||||||
|
|
||||||
|
result = copyNode(n)
|
||||||
|
for i in countup(1, f - 1):
|
||||||
|
if i < a:
|
||||||
|
arg = n.sons[i]
|
||||||
|
else:
|
||||||
|
arg = copyTree(s.typ.n.sons[i].sym.ast)
|
||||||
|
|
||||||
|
addSon(result, arg)
|
||||||
|
|
||||||
|
var evalTemplateCounter = 0
|
||||||
|
# to prevend endless recursion in templates instantation
|
||||||
|
|
||||||
|
proc evalTemplate(n: PNode, sym: PSym): PNode =
|
||||||
|
inc(evalTemplateCounter)
|
||||||
|
if evalTemplateCounter > 100:
|
||||||
|
GlobalError(n.info, errTemplateInstantiationTooNested)
|
||||||
|
|
||||||
|
# replace each param by the corresponding node:
|
||||||
|
var args = evalTemplateArgs(n, sym)
|
||||||
|
result = evalTemplateAux(sym.ast.sons[codePos], args, sym)
|
||||||
|
|
||||||
|
dec(evalTemplateCounter)
|
||||||
|
|
||||||
|
proc evalMacroCall*(c: PEvalContext, n: PNode, sym: PSym): PNode =
|
||||||
|
inc(evalTemplateCounter)
|
||||||
|
if evalTemplateCounter > 100:
|
||||||
|
GlobalError(n.info, errTemplateInstantiationTooNested)
|
||||||
|
|
||||||
|
var s = newStackFrame()
|
||||||
|
s.call = n
|
||||||
|
setlen(s.params, 2)
|
||||||
|
s.params[0] = newNodeIT(nkNilLit, n.info, sym.typ.sons[0])
|
||||||
|
s.params[1] = n
|
||||||
|
pushStackFrame(c, s)
|
||||||
|
discard eval(c, sym.ast.sons[codePos])
|
||||||
|
result = s.params[0]
|
||||||
|
popStackFrame(c)
|
||||||
|
if cyclicTree(result): GlobalError(n.info, errCyclicTree)
|
||||||
|
|
||||||
|
dec(evalTemplateCounter)
|
||||||
|
|
||||||
|
proc evalExpandToAst(c: PEvalContext, original: PNode): PNode =
|
||||||
|
var
|
||||||
|
n = original.copyTree
|
||||||
|
macroCall = n.sons[1]
|
||||||
|
expandedSym = macroCall.sons[0].sym
|
||||||
|
|
||||||
|
for i in countup(1, macroCall.sonsLen - 1):
|
||||||
|
macroCall.sons[i] = evalAux(c, macroCall.sons[i], {})
|
||||||
|
|
||||||
|
case expandedSym.kind
|
||||||
|
of skTemplate:
|
||||||
|
result = evalTemplate(macroCall, expandedSym)
|
||||||
|
of skMacro:
|
||||||
|
# At this point macroCall.sons[0] is nkSym node.
|
||||||
|
# To be completely compatible with normal macro invocation,
|
||||||
|
# we want to replace it with nkIdent node featuring
|
||||||
|
# the original unmangled macro name.
|
||||||
|
macroCall.sons[0] = newIdentNode(expandedSym.name, expandedSym.info)
|
||||||
|
result = evalMacroCall(c, macroCall, expandedSym)
|
||||||
|
else:
|
||||||
|
InternalError(macroCall.info,
|
||||||
|
"ExpandToAst: expanded symbol is no macro or template")
|
||||||
|
|
||||||
proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode =
|
proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode =
|
||||||
var m = getMagic(n)
|
var m = getMagic(n)
|
||||||
case m
|
case m
|
||||||
|
|
@ -805,6 +902,7 @@ proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode =
|
||||||
of mAppendSeqElem: result = evalAppendSeqElem(c, n)
|
of mAppendSeqElem: result = evalAppendSeqElem(c, n)
|
||||||
of mParseExprToAst: result = evalParseExpr(c, n)
|
of mParseExprToAst: result = evalParseExpr(c, n)
|
||||||
of mParseStmtToAst: result = evalParseStmt(c, n)
|
of mParseStmtToAst: result = evalParseStmt(c, n)
|
||||||
|
of mExpandToAst: result = evalExpandToAst(c, n)
|
||||||
of mNLen:
|
of mNLen:
|
||||||
result = evalAux(c, n.sons[1], {efLValue})
|
result = evalAux(c, n.sons[1], {efLValue})
|
||||||
if isSpecial(result): return
|
if isSpecial(result): return
|
||||||
|
|
@ -1011,6 +1109,10 @@ proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode =
|
||||||
if (a == b) or
|
if (a == b) or
|
||||||
(b.kind in {nkNilLit, nkEmpty}) and (a.kind in {nkNilLit, nkEmpty}):
|
(b.kind in {nkNilLit, nkEmpty}) and (a.kind in {nkNilLit, nkEmpty}):
|
||||||
result.intVal = 1
|
result.intVal = 1
|
||||||
|
of mNLineInfo:
|
||||||
|
result = evalAux(c, n.sons[1], {})
|
||||||
|
if isSpecial(result): return
|
||||||
|
result = newStrNodeT(result.info.toFileLineCol, n)
|
||||||
of mAstToYaml:
|
of mAstToYaml:
|
||||||
var ast = evalAux(c, n.sons[1], {efLValue})
|
var ast = evalAux(c, n.sons[1], {efLValue})
|
||||||
result = newStrNode(nkStrLit, ast.treeToYaml.ropeToStr)
|
result = newStrNode(nkStrLit, ast.treeToYaml.ropeToStr)
|
||||||
|
|
|
||||||
|
|
@ -456,6 +456,12 @@ proc ToLinenumber*(info: TLineInfo): int {.inline.} =
|
||||||
proc toColumn*(info: TLineInfo): int {.inline.} =
|
proc toColumn*(info: TLineInfo): int {.inline.} =
|
||||||
result = info.col
|
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] = @[]
|
var checkPoints: seq[TLineInfo] = @[]
|
||||||
|
|
||||||
proc addCheckpoint*(info: TLineInfo) =
|
proc addCheckpoint*(info: TLineInfo) =
|
||||||
|
|
|
||||||
|
|
@ -12,27 +12,13 @@
|
||||||
import
|
import
|
||||||
strutils, hashes, lists, options, lexer, ast, astalgo, trees, treetab,
|
strutils, hashes, lists, options, lexer, ast, astalgo, trees, treetab,
|
||||||
wordrecg, ropes, msgs, os, condsyms, idents, renderer, types, platform, math,
|
wordrecg, ropes, msgs, os, condsyms, idents, renderer, types, platform, math,
|
||||||
magicsys, parser, nversion, nimsets, semdata, evals, semfold, importer,
|
magicsys, parser, nversion, semdata, nimsets, semfold, importer,
|
||||||
procfind, lookups, rodread, pragmas, passes, semtypinst, sigmatch, suggest,
|
procfind, lookups, rodread, pragmas, passes, semtypinst, sigmatch, suggest,
|
||||||
semthreads, intsets, transf
|
semthreads, intsets, transf, evals
|
||||||
|
|
||||||
proc semPass*(): TPass
|
proc semPass*(): TPass
|
||||||
# implementation
|
# implementation
|
||||||
|
|
||||||
proc isTopLevel(c: PContext): bool {.inline.} =
|
|
||||||
result = c.tab.tos <= 2
|
|
||||||
|
|
||||||
proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym =
|
|
||||||
result = newSym(kind, considerAcc(n), getCurrOwner())
|
|
||||||
result.info = n.info
|
|
||||||
|
|
||||||
proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
|
|
||||||
allowed: TSymFlags): PSym
|
|
||||||
# identifier with visability
|
|
||||||
proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode,
|
|
||||||
allowed: TSymFlags): PSym
|
|
||||||
proc semStmtScope(c: PContext, n: PNode): PNode
|
|
||||||
|
|
||||||
type
|
type
|
||||||
TExprFlag = enum
|
TExprFlag = enum
|
||||||
efAllowType, efLValue, efWantIterator, efInTypeof
|
efAllowType, efLValue, efWantIterator, efInTypeof
|
||||||
|
|
@ -50,10 +36,36 @@ proc addResult(c: PContext, t: PType, info: TLineInfo)
|
||||||
proc addResultNode(c: PContext, n: PNode)
|
proc addResultNode(c: PContext, n: PNode)
|
||||||
proc instGenericContainer(c: PContext, n: PNode, header: PType): PType
|
proc instGenericContainer(c: PContext, n: PNode, header: PType): PType
|
||||||
|
|
||||||
|
proc typeMismatch(n: PNode, formal, actual: PType) =
|
||||||
|
GlobalError(n.Info, errGenerated, msgKindToString(errTypeMismatch) &
|
||||||
|
typeToString(actual) & ") " &
|
||||||
|
`%`(msgKindToString(errButExpectedX), [typeToString(formal)]))
|
||||||
|
|
||||||
|
proc fitNode(c: PContext, formal: PType, arg: PNode): PNode =
|
||||||
|
result = IndexTypesMatch(c, formal, arg.typ, arg)
|
||||||
|
if result == nil:
|
||||||
|
typeMismatch(arg, formal, arg.typ)
|
||||||
|
|
||||||
|
proc isTopLevel(c: PContext): bool {.inline.} =
|
||||||
|
result = c.tab.tos <= 2
|
||||||
|
|
||||||
|
proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym =
|
||||||
|
result = newSym(kind, considerAcc(n), getCurrOwner())
|
||||||
|
result.info = n.info
|
||||||
|
|
||||||
|
proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
|
||||||
|
allowed: TSymFlags): PSym
|
||||||
|
# identifier with visability
|
||||||
|
proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode,
|
||||||
|
allowed: TSymFlags): PSym
|
||||||
|
proc semStmtScope(c: PContext, n: PNode): PNode
|
||||||
|
|
||||||
proc ParamsTypeCheck(c: PContext, typ: PType) {.inline.} =
|
proc ParamsTypeCheck(c: PContext, typ: PType) {.inline.} =
|
||||||
if not typeAllowed(typ, skConst):
|
if not typeAllowed(typ, skConst):
|
||||||
GlobalError(typ.n.info, errXisNoType, typeToString(typ))
|
GlobalError(typ.n.info, errXisNoType, typeToString(typ))
|
||||||
|
|
||||||
|
include semtempl
|
||||||
|
|
||||||
proc semConstExpr(c: PContext, n: PNode): PNode =
|
proc semConstExpr(c: PContext, n: PNode): PNode =
|
||||||
var e = semExprWithType(c, n)
|
var e = semExprWithType(c, n)
|
||||||
if e == nil:
|
if e == nil:
|
||||||
|
|
@ -77,16 +89,6 @@ proc semAndEvalConstExpr(c: PContext, n: PNode): PNode =
|
||||||
|
|
||||||
include seminst, semcall
|
include seminst, semcall
|
||||||
|
|
||||||
proc typeMismatch(n: PNode, formal, actual: PType) =
|
|
||||||
GlobalError(n.Info, errGenerated, msgKindToString(errTypeMismatch) &
|
|
||||||
typeToString(actual) & ") " &
|
|
||||||
`%`(msgKindToString(errButExpectedX), [typeToString(formal)]))
|
|
||||||
|
|
||||||
proc fitNode(c: PContext, formal: PType, arg: PNode): PNode =
|
|
||||||
result = IndexTypesMatch(c, formal, arg.typ, arg)
|
|
||||||
if result == nil:
|
|
||||||
typeMismatch(arg, formal, arg.typ)
|
|
||||||
|
|
||||||
proc semAfterMacroCall(c: PContext, n: PNode, s: PSym): PNode =
|
proc semAfterMacroCall(c: PContext, n: PNode, s: PSym): PNode =
|
||||||
result = n
|
result = n
|
||||||
case s.typ.sons[0].kind
|
case s.typ.sons[0].kind
|
||||||
|
|
@ -102,27 +104,12 @@ proc semAfterMacroCall(c: PContext, n: PNode, s: PSym): PNode =
|
||||||
result = fitNode(c, s.typ.sons[0], result)
|
result = fitNode(c, s.typ.sons[0], result)
|
||||||
#GlobalError(s.info, errInvalidParamKindX, typeToString(s.typ.sons[0]))
|
#GlobalError(s.info, errInvalidParamKindX, typeToString(s.typ.sons[0]))
|
||||||
|
|
||||||
include "semtempl.nim"
|
|
||||||
|
|
||||||
proc semMacroExpr(c: PContext, n: PNode, sym: PSym,
|
proc semMacroExpr(c: PContext, n: PNode, sym: PSym,
|
||||||
semCheck: bool = true): PNode =
|
semCheck: bool = true): PNode =
|
||||||
inc(evalTemplateCounter)
|
|
||||||
if evalTemplateCounter > 100:
|
|
||||||
GlobalError(n.info, errTemplateInstantiationTooNested)
|
|
||||||
markUsed(n, sym)
|
markUsed(n, sym)
|
||||||
var p = newEvalContext(c.module, "", false)
|
var p = newEvalContext(c.module, "", false)
|
||||||
var s = newStackFrame()
|
result = evalMacroCall(p, n, sym)
|
||||||
s.call = n
|
|
||||||
setlen(s.params, 2)
|
|
||||||
s.params[0] = newNodeIT(nkNilLit, n.info, sym.typ.sons[0])
|
|
||||||
s.params[1] = n
|
|
||||||
pushStackFrame(p, s)
|
|
||||||
discard eval(p, sym.ast.sons[codePos])
|
|
||||||
result = s.params[0]
|
|
||||||
popStackFrame(p)
|
|
||||||
if cyclicTree(result): GlobalError(n.info, errCyclicTree)
|
|
||||||
if semCheck: result = semAfterMacroCall(c, result, sym)
|
if semCheck: result = semAfterMacroCall(c, result, sym)
|
||||||
dec(evalTemplateCounter)
|
|
||||||
|
|
||||||
proc forceBool(c: PContext, n: PNode): PNode =
|
proc forceBool(c: PContext, n: PNode): PNode =
|
||||||
result = fitNode(c, getSysType(tyBool), n)
|
result = fitNode(c, getSysType(tyBool), n)
|
||||||
|
|
|
||||||
|
|
@ -904,26 +904,37 @@ proc expectStringArg(c: PContext, n: PNode, i: int): PNode =
|
||||||
if result.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit}:
|
if result.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit}:
|
||||||
GlobalError(result.info, errStringLiteralExpected)
|
GlobalError(result.info, errStringLiteralExpected)
|
||||||
|
|
||||||
proc semExpandMacroToAst(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
proc isAstValue(n: PNode): bool =
|
||||||
|
result = n.typ.sym.name.s in [ "expr", "stmt", "PNimrodNode" ]
|
||||||
|
|
||||||
|
proc semExpandToAst(c: PContext, n: PNode, magicSym: PSym, flags: TExprFlags): PNode =
|
||||||
if sonsLen(n) == 2:
|
if sonsLen(n) == 2:
|
||||||
if not isCallExpr(n.sons[1]):
|
if not isCallExpr(n.sons[1]):
|
||||||
GlobalError(n.info, errXisNoMacroOrTemplate, n.renderTree)
|
GlobalError(n.info, errXisNoMacroOrTemplate, n.renderTree)
|
||||||
|
|
||||||
var macroCall = n.sons[1]
|
var macroCall = n.sons[1]
|
||||||
|
|
||||||
var s = qualifiedLookup(c, macroCall.sons[0], {checkUndeclared})
|
var expandedSym = qualifiedLookup(c, macroCall.sons[0], {checkUndeclared})
|
||||||
if s == nil:
|
if expandedSym == nil:
|
||||||
GlobalError(n.info, errUndeclaredIdentifier, macroCall.sons[0].renderTree)
|
GlobalError(n.info, errUndeclaredIdentifier, macroCall.sons[0].renderTree)
|
||||||
|
|
||||||
var expanded : Pnode
|
if not (expandedSym.kind in { skMacro, skTemplate }):
|
||||||
|
GlobalError(n.info, errXisNoMacroOrTemplate, expandedSym.name.s)
|
||||||
|
|
||||||
case s.kind
|
macroCall.sons[0] = newNodeI(nkSym, macroCall.info)
|
||||||
of skMacro: expanded = semMacroExpr(c, macroCall, s, false)
|
macroCall.sons[0].sym = expandedSym
|
||||||
of skTemplate: expanded = semTemplateExpr(c, macroCall, s, false)
|
markUsed(n, expandedSym)
|
||||||
else: GlobalError(n.info, errXisNoMacroOrTemplate, s.name.s)
|
|
||||||
|
|
||||||
var macroRetType = newTypeS(s.typ.sons[0].kind, c)
|
for i in countup(1, macroCall.sonsLen - 1):
|
||||||
result = newMetaNodeIT(expanded, n.info, macroRetType)
|
macroCall.sons[i] = semExprWithType(c, macroCall.sons[i], {efAllowType})
|
||||||
|
|
||||||
|
# Preserve the magic symbol in order to handled in evals.nim
|
||||||
|
n.sons[0] = newNodeI(nkSym, n.info)
|
||||||
|
n.sons[0].sym = magicSym
|
||||||
|
|
||||||
|
n.typ = expandedSym.getReturnType
|
||||||
|
|
||||||
|
result = n
|
||||||
else:
|
else:
|
||||||
result = semDirectOp(c, n, flags)
|
result = semDirectOp(c, n, flags)
|
||||||
|
|
||||||
|
|
@ -963,7 +974,7 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
|
||||||
else:
|
else:
|
||||||
result = semDirectOp(c, n, flags)
|
result = semDirectOp(c, n, flags)
|
||||||
of mSlurp: result = semSlurp(c, n, flags)
|
of mSlurp: result = semSlurp(c, n, flags)
|
||||||
of mExpandMacroToAst: result = semExpandMacroToAst(c, n, flags)
|
of mExpandToAst: result = semExpandToAst(c, n, s, flags)
|
||||||
else: result = semDirectOp(c, n, flags)
|
else: result = semDirectOp(c, n, flags)
|
||||||
|
|
||||||
proc semIfExpr(c: PContext, n: PNode): PNode =
|
proc semIfExpr(c: PContext, n: PNode): PNode =
|
||||||
|
|
|
||||||
|
|
@ -206,7 +206,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
|
||||||
of mNewString, mNewStringOfCap,
|
of mNewString, mNewStringOfCap,
|
||||||
mExit, mInc, ast.mDec, mEcho, mAssert, mSwap, mAppendStrCh,
|
mExit, mInc, ast.mDec, mEcho, mAssert, mSwap, mAppendStrCh,
|
||||||
mAppendStrStr, mAppendSeqElem, mSetLengthStr, mSetLengthSeq,
|
mAppendStrStr, mAppendSeqElem, mSetLengthStr, mSetLengthSeq,
|
||||||
mParseExprToAst, mParseStmtToAst,
|
mParseExprToAst, mParseStmtToAst, mExpandToAst,
|
||||||
mNLen..mNError, mEqRef:
|
mNLen..mNError, mEqRef:
|
||||||
nil
|
nil
|
||||||
else: InternalError(a.info, "evalOp(" & $m & ')')
|
else: InternalError(a.info, "evalOp(" & $m & ')')
|
||||||
|
|
|
||||||
|
|
@ -439,6 +439,10 @@ proc semTry(c: PContext, n: PNode): PNode =
|
||||||
checkMinSonsLen(a, 1)
|
checkMinSonsLen(a, 1)
|
||||||
var length = sonsLen(a)
|
var length = sonsLen(a)
|
||||||
if a.kind == nkExceptBranch:
|
if a.kind == nkExceptBranch:
|
||||||
|
if length == 2 and a.sons[0].kind == nkBracket:
|
||||||
|
a.sons[0..0] = a.sons[0].sons
|
||||||
|
length = a.sonsLen
|
||||||
|
|
||||||
for j in countup(0, length - 2):
|
for j in countup(0, length - 2):
|
||||||
var typ = semTypeNode(c, a.sons[j], nil)
|
var typ = semTypeNode(c, a.sons[j], nil)
|
||||||
if typ.kind == tyRef: typ = typ.sons[0]
|
if typ.kind == tyRef: typ = typ.sons[0]
|
||||||
|
|
|
||||||
|
|
@ -33,22 +33,6 @@ proc isTypeDesc(n: PNode): bool =
|
||||||
result = true
|
result = true
|
||||||
else: result = false
|
else: result = false
|
||||||
|
|
||||||
proc evalTemplateAux(c: PContext, templ, actual: PNode, sym: PSym): PNode =
|
|
||||||
case templ.kind
|
|
||||||
of nkSym:
|
|
||||||
var p = templ.sym
|
|
||||||
if (p.kind == skParam) and (p.owner.id == sym.id):
|
|
||||||
result = copyTree(actual.sons[p.position])
|
|
||||||
else:
|
|
||||||
result = copyNode(templ)
|
|
||||||
of nkNone..nkIdent, nkType..nkNilLit: # atom
|
|
||||||
result = copyNode(templ)
|
|
||||||
else:
|
|
||||||
result = copyNode(templ)
|
|
||||||
newSons(result, sonsLen(templ))
|
|
||||||
for i in countup(0, sonsLen(templ) - 1):
|
|
||||||
result.sons[i] = evalTemplateAux(c, templ.sons[i], actual, sym)
|
|
||||||
|
|
||||||
var evalTemplateCounter: int = 0
|
var evalTemplateCounter: int = 0
|
||||||
# to prevend endless recursion in templates instantation
|
# to prevend endless recursion in templates instantation
|
||||||
|
|
||||||
|
|
@ -77,13 +61,13 @@ proc evalTemplateArgs(c: PContext, n: PNode, s: PSym): PNode =
|
||||||
arg = fitNode(c, s.typ.sons[i], semExprWithType(c, arg))
|
arg = fitNode(c, s.typ.sons[i], semExprWithType(c, arg))
|
||||||
addSon(result, arg)
|
addSon(result, arg)
|
||||||
|
|
||||||
proc evalTemplate(c: PContext, n: PNode, sym: PSym): PNode =
|
proc evalTemplate*(c: PContext, n: PNode, sym: PSym): PNode =
|
||||||
var args: PNode
|
var args: PNode
|
||||||
inc(evalTemplateCounter)
|
inc(evalTemplateCounter)
|
||||||
if evalTemplateCounter <= 100:
|
if evalTemplateCounter <= 100:
|
||||||
# replace each param by the corresponding node:
|
# replace each param by the corresponding node:
|
||||||
args = evalTemplateArgs(c, n, sym)
|
args = evalTemplateArgs(c, n, sym)
|
||||||
result = evalTemplateAux(c, sym.ast.sons[codePos], args, sym)
|
result = evalTemplateAux(sym.ast.sons[codePos], args, sym)
|
||||||
dec(evalTemplateCounter)
|
dec(evalTemplateCounter)
|
||||||
else:
|
else:
|
||||||
GlobalError(n.info, errTemplateInstantiationTooNested)
|
GlobalError(n.info, errTemplateInstantiationTooNested)
|
||||||
|
|
|
||||||
|
|
@ -925,6 +925,11 @@ proc computeSize(typ: PType): biggestInt =
|
||||||
var a: biggestInt = 1
|
var a: biggestInt = 1
|
||||||
result = computeSizeAux(typ, a)
|
result = computeSizeAux(typ, a)
|
||||||
|
|
||||||
|
proc getReturnType*(s: PSym): PType =
|
||||||
|
# Obtains the return type of a iterator/proc/macro/template
|
||||||
|
assert s.kind in { skProc, skTemplate, skMacro, skIterator }
|
||||||
|
result = s.typ.n.sons[0].typ
|
||||||
|
|
||||||
proc getSize(typ: PType): biggestInt =
|
proc getSize(typ: PType): biggestInt =
|
||||||
result = computeSize(typ)
|
result = computeSize(typ)
|
||||||
if result < 0: InternalError("getSize(" & $typ.kind & ')')
|
if result < 0: InternalError("getSize(" & $typ.kind & ')')
|
||||||
|
|
|
||||||
|
|
@ -187,6 +187,8 @@ proc toStrLit*(n: PNimrodNode): PNimrodNode {.compileTime.} =
|
||||||
## in a string literal node
|
## in a string literal node
|
||||||
return newStrLitNode(repr(n))
|
return newStrLitNode(repr(n))
|
||||||
|
|
||||||
|
proc lineinfo*(n: PNimrodNode): string {.magic: "NLineInfo".}
|
||||||
|
|
||||||
proc toLisp*(n: PNimrodNode): string {.compileTime.} =
|
proc toLisp*(n: PNimrodNode): string {.compileTime.} =
|
||||||
## Convert the AST `n` to a human-readable string
|
## Convert the AST `n` to a human-readable string
|
||||||
##
|
##
|
||||||
|
|
@ -228,7 +230,7 @@ proc parseStmt*(s: string): stmt {.magic: "ParseStmtToAst".}
|
||||||
## Compiles the passed string to its AST representation.
|
## Compiles the passed string to its AST representation.
|
||||||
## Expects one or more statements.
|
## 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.
|
## Obtains the AST nodes returned from a macro or template invocation.
|
||||||
## Example:
|
## Example:
|
||||||
##
|
##
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,8 @@
|
||||||
## Changing the style is permanent even after program termination! Use the
|
## Changing the style is permanent even after program termination! Use the
|
||||||
## code ``system.addQuitProc(resetAttributes)`` to restore the defaults.
|
## code ``system.addQuitProc(resetAttributes)`` to restore the defaults.
|
||||||
|
|
||||||
|
import macros
|
||||||
|
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
import windows, os
|
import windows, os
|
||||||
|
|
||||||
|
|
@ -210,24 +212,32 @@ type
|
||||||
|
|
||||||
when not defined(windows):
|
when not defined(windows):
|
||||||
var
|
var
|
||||||
|
# XXX: These better be thread-local
|
||||||
gFG = 0
|
gFG = 0
|
||||||
gBG = 0
|
gBG = 0
|
||||||
|
|
||||||
proc WriteStyled*(txt: string, style: set[TStyle] = {styleBright}) =
|
proc setStyle*(style: set[TStyle]) =
|
||||||
## writes the text `txt` in a given `style`.
|
## sets the terminal style
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
var a = 0'i16
|
var a = 0'i16
|
||||||
if styleBright in style: a = a or int16(FOREGROUND_INTENSITY)
|
if styleBright in style: a = a or int16(FOREGROUND_INTENSITY)
|
||||||
if styleBlink in style: a = a or int16(BACKGROUND_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 styleReverse in style: a = a or 0x4000'i16 # COMMON_LVB_REVERSE_VIDEO
|
||||||
if styleUnderscore in style: a = a or 0x8000'i16 # COMMON_LVB_UNDERSCORE
|
if styleUnderscore in style: a = a or 0x8000'i16 # COMMON_LVB_UNDERSCORE
|
||||||
var old = getAttributes()
|
|
||||||
discard SetConsoleTextAttribute(conHandle, old or a)
|
discard SetConsoleTextAttribute(conHandle, old or a)
|
||||||
stdout.write(txt)
|
|
||||||
discard SetConsoleTextAttribute(conHandle, old)
|
|
||||||
else:
|
else:
|
||||||
for s in items(style):
|
for s in items(style):
|
||||||
stdout.write("\e[" & $ord(s) & 'm')
|
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)
|
stdout.write(txt)
|
||||||
resetAttributes()
|
resetAttributes()
|
||||||
if gFG != 0:
|
if gFG != 0:
|
||||||
|
|
@ -298,6 +308,24 @@ proc setBackgroundColor*(bg: TBackgroundColor, bright=false) =
|
||||||
if bright: inc(gBG, 60)
|
if bright: inc(gBG, 60)
|
||||||
stdout.write("\e[" & $gBG & 'm')
|
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:
|
when isMainModule:
|
||||||
system.addQuitProc(resetAttributes)
|
system.addQuitProc(resetAttributes)
|
||||||
write(stdout, "never mind")
|
write(stdout, "never mind")
|
||||||
|
|
|
||||||
150
lib/pure/unittest.nim
Normal file
150
lib/pure/unittest.nim
Normal file
|
|
@ -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))
|
||||||
|
|
||||||
|
|
@ -857,7 +857,6 @@ proc insert*[T](x: var seq[T], item: T, i = 0) {.noSideEffect.} =
|
||||||
dec(j)
|
dec(j)
|
||||||
x[i] = item
|
x[i] = item
|
||||||
|
|
||||||
|
|
||||||
proc repr*[T](x: T): string {.magic: "Repr", noSideEffect.}
|
proc repr*[T](x: T): string {.magic: "Repr", noSideEffect.}
|
||||||
## takes any Nimrod variable and returns its string representation. It
|
## takes any Nimrod variable and returns its string representation. It
|
||||||
## works even for complex data graphs with cycles. This is a great
|
## 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.
|
## slice operation for strings. Negative indexes are supported.
|
||||||
result = s.substr(x.a-|s, x.b-|s)
|
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) =
|
proc `[]=`*(s: var string, x: TSlice[int], b: string) =
|
||||||
## slice assignment for strings. Negative indexes are supported.
|
## slice assignment for strings. Negative indexes are supported.
|
||||||
var a = x.a-|s
|
var a = x.a-|s
|
||||||
|
|
@ -1929,7 +1948,7 @@ proc `[]=`*(s: var string, x: TSlice[int], b: string) =
|
||||||
if L == b.len:
|
if L == b.len:
|
||||||
for i in 0 .. <L: s[i+a] = b[i]
|
for i in 0 .. <L: s[i+a] = b[i]
|
||||||
else:
|
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] =
|
proc `[]`*[Idx, T](a: array[Idx, T], x: TSlice[int]): seq[T] =
|
||||||
## slice operation for arrays. Negative indexes are NOT supported because
|
## 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:
|
if L == b.len:
|
||||||
for i in 0 .. <L: s[i+a] = b[i]
|
for i in 0 .. <L: s[i+a] = b[i]
|
||||||
else:
|
else:
|
||||||
raise newException(EOutOfRange, "differing lengths for slice assignment")
|
spliceImpl(s, x.a, x.b, b)
|
||||||
|
|
||||||
proc getTypeInfo*[T](x: T): pointer {.magic: "GetTypeInfo".}
|
proc getTypeInfo*[T](x: T): pointer {.magic: "GetTypeInfo".}
|
||||||
## get type information for `x`. Ordinary code should not use this, but
|
## get type information for `x`. Ordinary code should not use this, but
|
||||||
|
|
|
||||||
47
tests/accept/run/tunit.nim
Normal file
47
tests/accept/run/tunit.nim
Normal file
|
|
@ -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()
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue