123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676 |
- #
- #
- # Doctor Nim
- # (c) Copyright 2020 Andreas Rumpf
- #
- # See the file "copying.txt", included in this
- # distribution, for details about the copyright.
- #
- #[
- - Most important bug:
- while i < x.len and use(s[i]): inc i # is safe
- - We need to map arrays to Z3 and test for something like 'forall(i, (i in 3..4) -> (a[i] > 3))'
- - forall/exists need syntactic sugar as the manual
- - We need teach DrNim what 'inc', 'dec' and 'swap' mean, for example
- 'x in n..m; inc x' implies 'x in n+1..m+1'
- - We need an ``old`` annotation:
- proc f(x: var int; y: var int) {.ensures: x == old(x)+1 and y == old(y)+1 .} =
- inc x
- inc y
- var x = 3
- var y: range[N..M]
- f(x, y)
- {.assume: y in N+1 .. M+1.}
- # --> y in N+1..M+1
- proc myinc(x: var int) {.ensures: x-1 == old(x).} =
- inc x
- facts(x) # x < 3
- myinc x
- facts(x+1)
- We handle state transitions in this way:
- for every f in facts:
- replace 'x' by 'old(x)'
- facts.add ensuresClause
- # then we know: old(x) < 3; x-1 == old(x)
- # we can conclude: x-1 < 3 but leave this task to Z3
- ]#
- import std / [
- parseopt, strutils, os, tables, times
- ]
- import ".." / compiler / [
- ast, types, renderer,
- commands, options, msgs,
- platform,
- idents, lineinfos, cmdlinehelper, modulegraphs, condsyms,
- pathutils, passes, passaux, sem, modules
- ]
- import z3 / z3_api
- when not defined(windows):
- # on UNIX we use static linking because UNIX's lib*.so system is broken
- # beyond repair and the neckbeards don't understand software development.
- {.passL: "dist/z3/build/libz3.a".}
- const
- HelpMessage = "DrNim Version $1 [$2: $3]\n" &
- "Compiled at $4\n" &
- "Copyright (c) 2006-" & copyrightYear & " by Andreas Rumpf\n"
- const
- Usage = """
- drnim [options] [projectfile]
- Options: Same options that the Nim compiler supports.
- """
- proc getCommandLineDesc(conf: ConfigRef): string =
- result = (HelpMessage % [system.NimVersion, platform.OS[conf.target.hostOS].name,
- CPU[conf.target.hostCPU].name, CompileDate]) &
- Usage
- proc helpOnError(conf: ConfigRef) =
- msgWriteln(conf, getCommandLineDesc(conf), {msgStdout})
- msgQuit(0)
- type
- CannotMapToZ3Error = object of ValueError
- Z3Exception = object of ValueError
- DrCon = object
- z3: Z3_context
- graph: ModuleGraph
- mapping: Table[string, Z3_ast]
- canonParameterNames: bool
- proc stableName(result: var string; n: PNode) =
- # we can map full Nim expressions like 'f(a, b, c)' to Z3 variables.
- # We must be carefult to select a unique, stable name for these expressions
- # based on structural equality. 'stableName' helps us with this problem.
- case n.kind
- of nkEmpty, nkNilLit, nkType: discard
- of nkIdent:
- result.add n.ident.s
- of nkSym:
- result.add n.sym.name.s
- result.add '_'
- result.addInt n.sym.id
- of nkCharLit..nkUInt64Lit:
- result.addInt n.intVal
- of nkFloatLit..nkFloat64Lit:
- result.addFloat n.floatVal
- of nkStrLit..nkTripleStrLit:
- result.add strutils.escape n.strVal
- else:
- result.add $n.kind
- result.add '('
- for i in 0..<n.len:
- if i > 0: result.add ", "
- stableName(result, n[i])
- result.add ')'
- proc stableName(n: PNode): string = stableName(result, n)
- proc notImplemented(msg: string) {.noinline.} =
- raise newException(CannotMapToZ3Error, "; cannot map to Z3: " & msg)
- proc translateEnsures(e, x: PNode): PNode =
- if e.kind == nkSym and e.sym.kind == skResult:
- result = x
- else:
- result = shallowCopy(e)
- for i in 0 ..< safeLen(e):
- result[i] = translateEnsures(e[i], x)
- proc typeToZ3(c: DrCon; t: PType): Z3_sort =
- template ctx: untyped = c.z3
- case t.skipTypes(abstractInst+{tyVar}).kind
- of tyEnum, tyInt..tyInt64:
- result = Z3_mk_int_sort(ctx)
- of tyBool:
- result = Z3_mk_bool_sort(ctx)
- of tyFloat..tyFloat128:
- result = Z3_mk_fpa_sort_double(ctx)
- of tyChar, tyUInt..tyUInt64:
- result = Z3_mk_bv_sort(ctx, 64)
- #cuint(getSize(c.graph.config, t) * 8))
- else:
- notImplemented(typeToString(t))
- template binary(op, a, b): untyped =
- var arr = [a, b]
- op(ctx, cuint(2), addr(arr[0]))
- proc nodeToZ3(c: var DrCon; n: PNode; vars: var seq[PNode]): Z3_ast
- template quantorToZ3(fn) {.dirty.} =
- template ctx: untyped = c.z3
- var bound = newSeq[Z3_app](n.len-1)
- for i in 0..n.len-2:
- doAssert n[i].kind == nkSym
- let v = n[i].sym
- let name = Z3_mk_string_symbol(ctx, v.name.s)
- let vz3 = Z3_mk_const(ctx, name, typeToZ3(c, v.typ))
- c.mapping[stableName(n[i])] = vz3
- bound[i] = Z3_to_app(ctx, vz3)
- var dummy: seq[PNode]
- let x = nodeToZ3(c, n[^1], dummy)
- result = fn(ctx, 0, bound.len.cuint, addr(bound[0]), 0, nil, x)
- proc forallToZ3(c: var DrCon; n: PNode): Z3_ast = quantorToZ3(Z3_mk_forall_const)
- proc existsToZ3(c: var DrCon; n: PNode): Z3_ast = quantorToZ3(Z3_mk_exists_const)
- proc paramName(n: PNode): string =
- case n.sym.kind
- of skParam: result = "arg" & $n.sym.position
- of skResult: result = "result"
- else: result = stableName(n)
- proc nodeToZ3(c: var DrCon; n: PNode; vars: var seq[PNode]): Z3_ast =
- template ctx: untyped = c.z3
- template rec(n): untyped = nodeToZ3(c, n, vars)
- case n.kind
- of nkSym:
- let key = if c.canonParameterNames: paramName(n) else: stableName(n)
- result = c.mapping.getOrDefault(key)
- if pointer(result) == nil:
- let name = Z3_mk_string_symbol(ctx, n.sym.name.s)
- result = Z3_mk_const(ctx, name, typeToZ3(c, n.sym.typ))
- c.mapping[key] = result
- vars.add n
- of nkCharLit..nkUInt64Lit:
- if n.typ != nil and n.typ.skipTypes(abstractInst).kind in {tyInt..tyInt64}:
- # optimized for the common case
- result = Z3_mk_int64(ctx, clonglong(n.intval), Z3_mk_int_sort(ctx))
- elif n.typ != nil and n.typ.kind == tyBool:
- result = if n.intval != 0: Z3_mk_true(ctx) else: Z3_mk_false(ctx)
- elif n.typ != nil and isUnsigned(n.typ):
- result = Z3_mk_unsigned_int64(ctx, cast[uint64](n.intVal), typeToZ3(c, n.typ))
- else:
- let zt = if n.typ == nil: Z3_mk_int_sort(ctx) else: typeToZ3(c, n.typ)
- result = Z3_mk_numeral(ctx, $getOrdValue(n), zt)
- of nkFloatLit..nkFloat64Lit:
- result = Z3_mk_fpa_numeral_double(ctx, n.floatVal, Z3_mk_fpa_sort_double(ctx))
- of nkCallKinds:
- assert n.len > 0
- assert n[0].kind == nkSym
- let operator = n[0].sym.magic
- case operator
- of mEqI, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc,
- mEqStr, mEqSet, mEqCString:
- result = Z3_mk_eq(ctx, rec n[1], rec n[2])
- of mLeI, mLeEnum, mLeCh, mLeB, mLePtr, mLeStr:
- result = Z3_mk_le(ctx, rec n[1], rec n[2])
- of mLtI, mLtEnum, mLtCh, mLtB, mLtPtr, mLtStr:
- result = Z3_mk_lt(ctx, rec n[1], rec n[2])
- of mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq:
- # len(x) needs the same logic as 'x' itself
- if n[1].kind == nkSym:
- let key = stableName(n)
- let sym = n[1].sym
- result = c.mapping.getOrDefault(key)
- if pointer(result) == nil:
- let name = Z3_mk_string_symbol(ctx, sym.name.s & ".len")
- result = Z3_mk_const(ctx, name, Z3_mk_int_sort(ctx))
- c.mapping[key] = result
- vars.add n
- else:
- notImplemented(renderTree(n))
- of mAddI, mSucc:
- result = binary(Z3_mk_add, rec n[1], rec n[2])
- of mSubI, mPred:
- result = binary(Z3_mk_sub, rec n[1], rec n[2])
- of mMulI:
- result = binary(Z3_mk_mul, rec n[1], rec n[2])
- of mDivI:
- result = Z3_mk_div(ctx, rec n[1], rec n[2])
- of mModI:
- result = Z3_mk_mod(ctx, rec n[1], rec n[2])
- of mMaxI:
- # max(a, b) <=> ite(a < b, b, a)
- result = Z3_mk_ite(ctx, Z3_mk_lt(ctx, rec n[1], rec n[2]),
- rec n[2], rec n[1])
- of mMinI:
- # min(a, b) <=> ite(a < b, a, b)
- result = Z3_mk_ite(ctx, Z3_mk_lt(ctx, rec n[1], rec n[2]),
- rec n[1], rec n[2])
- of mLeU:
- result = Z3_mk_bvule(ctx, rec n[1], rec n[2])
- of mLtU:
- result = Z3_mk_bvult(ctx, rec n[1], rec n[2])
- of mAnd:
- result = binary(Z3_mk_and, rec n[1], rec n[2])
- of mOr:
- result = binary(Z3_mk_or, rec n[1], rec n[2])
- of mXor:
- result = Z3_mk_xor(ctx, rec n[1], rec n[2])
- of mNot:
- result = Z3_mk_not(ctx, rec n[1])
- of mImplies:
- result = Z3_mk_implies(ctx, rec n[1], rec n[2])
- of mIff:
- result = Z3_mk_iff(ctx, rec n[1], rec n[2])
- of mForall:
- result = forallToZ3(c, n)
- of mExists:
- result = existsToZ3(c, n)
- of mLeF64:
- result = Z3_mk_fpa_leq(ctx, rec n[1], rec n[2])
- of mLtF64:
- result = Z3_mk_fpa_lt(ctx, rec n[1], rec n[2])
- of mAddF64:
- result = Z3_mk_fpa_add(ctx, Z3_mk_fpa_round_nearest_ties_to_even(ctx), rec n[1], rec n[2])
- of mSubF64:
- result = Z3_mk_fpa_sub(ctx, Z3_mk_fpa_round_nearest_ties_to_even(ctx), rec n[1], rec n[2])
- of mMulF64:
- result = Z3_mk_fpa_mul(ctx, Z3_mk_fpa_round_nearest_ties_to_even(ctx), rec n[1], rec n[2])
- of mDivF64:
- result = Z3_mk_fpa_div(ctx, Z3_mk_fpa_round_nearest_ties_to_even(ctx), rec n[1], rec n[2])
- of mShrI:
- # XXX handle conversions from int to uint here somehow
- result = Z3_mk_bvlshr(ctx, rec n[1], rec n[2])
- of mAshrI:
- result = Z3_mk_bvashr(ctx, rec n[1], rec n[2])
- of mShlI:
- result = Z3_mk_bvshl(ctx, rec n[1], rec n[2])
- of mBitandI:
- result = Z3_mk_bvand(ctx, rec n[1], rec n[2])
- of mBitorI:
- result = Z3_mk_bvor(ctx, rec n[1], rec n[2])
- of mBitxorI:
- result = Z3_mk_bvxor(ctx, rec n[1], rec n[2])
- of mOrd, mChr:
- result = rec n[1]
- of mOld:
- let key = (if c.canonParameterNames: paramName(n[1]) else: stableName(n[1])) & ".old"
- result = c.mapping.getOrDefault(key)
- if pointer(result) == nil:
- let name = Z3_mk_string_symbol(ctx, $n)
- result = Z3_mk_const(ctx, name, typeToZ3(c, n.typ))
- c.mapping[key] = result
- # XXX change the logic in `addRangeInfo` for this
- #vars.add n
- else:
- # sempass2 adds some 'fact' like 'x = f(a, b)' (see addAsgnFact)
- # 'f(a, b)' can have an .ensures annotation and we need to make use
- # of this information.
- # we need to map 'f(a, b)' to a Z3 variable of this name
- let op = n[0].typ
- if op != nil and op.n != nil and op.n.len > 0 and op.n[0].kind == nkEffectList and
- ensuresEffects < op.n[0].len:
- let ensures = op.n[0][ensuresEffects]
- if ensures != nil and ensures.kind != nkEmpty:
- let key = stableName(n)
- result = c.mapping.getOrDefault(key)
- if pointer(result) == nil:
- let name = Z3_mk_string_symbol(ctx, $n)
- result = Z3_mk_const(ctx, name, typeToZ3(c, n.typ))
- c.mapping[key] = result
- vars.add n
- if pointer(result) == nil:
- notImplemented(renderTree(n))
- of nkStmtListExpr, nkPar:
- var isTrivial = true
- for i in 0..n.len-2:
- isTrivial = isTrivial and n[i].kind in {nkEmpty, nkCommentStmt}
- if isTrivial:
- result = nodeToZ3(c, n[^1], vars)
- else:
- notImplemented(renderTree(n))
- of nkHiddenDeref:
- result = rec n[0]
- else:
- notImplemented(renderTree(n))
- proc addRangeInfo(c: var DrCon, n: PNode, res: var seq[Z3_ast]) =
- var cmpOp = mLeI
- if n.typ != nil:
- cmpOp =
- case n.typ.skipTypes(abstractInst).kind
- of tyFloat..tyFloat128: mLeF64
- of tyChar, tyUInt..tyUInt64: mLeU
- else: mLeI
- var lowBound, highBound: PNode
- if n.kind == nkSym:
- let v = n.sym
- let t = v.typ.skipTypes(abstractInst - {tyRange})
- case t.kind
- of tyRange:
- lowBound = t.n[0]
- highBound = t.n[1]
- of tyFloat..tyFloat128:
- # no range information for non-range'd floats
- return
- of tyUInt..tyUInt64, tyChar:
- lowBound = newIntNode(nkUInt64Lit, firstOrd(nil, v.typ))
- lowBound.typ = v.typ
- highBound = newIntNode(nkUInt64Lit, lastOrd(nil, v.typ))
- highBound.typ = v.typ
- of tyInt..tyInt64, tyEnum:
- lowBound = newIntNode(nkInt64Lit, firstOrd(nil, v.typ))
- highBound = newIntNode(nkInt64Lit, lastOrd(nil, v.typ))
- else:
- # no range information available:
- return
- elif n.kind in nkCallKinds and n.len == 2 and n[0].kind == nkSym and
- n[0].sym.magic in {mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq}:
- # we know it's a 'len(x)' expression and we seek to teach
- # Z3 that the result is >= 0 and <= high(int).
- doAssert n.kind in nkCallKinds
- doAssert n[0].kind == nkSym
- doAssert n.len == 2
- lowBound = newIntNode(nkInt64Lit, 0)
- if n.typ != nil:
- highBound = newIntNode(nkInt64Lit, lastOrd(nil, n.typ))
- else:
- highBound = newIntNode(nkInt64Lit, high(int64))
- else:
- let op = n[0].typ
- if op != nil and op.n != nil and op.n.len > 0 and op.n[0].kind == nkEffectList and
- ensuresEffects < op.n[0].len:
- let ensures = op.n[0][ensuresEffects]
- if ensures != nil and ensures.kind != nkEmpty:
- var dummy: seq[PNode]
- res.add nodeToZ3(c, translateEnsures(ensures, n), dummy)
- return
- let x = newTree(nkInfix, newSymNode createMagic(c.graph, "<=", cmpOp), lowBound, n)
- let y = newTree(nkInfix, newSymNode createMagic(c.graph, "<=", cmpOp), n, highBound)
- var dummy: seq[PNode]
- res.add nodeToZ3(c, x, dummy)
- res.add nodeToZ3(c, y, dummy)
- proc on_err(ctx: Z3_context, e: Z3_error_code) {.nimcall.} =
- #writeStackTrace()
- let msg = $Z3_get_error_msg(ctx, e)
- raise newException(Z3Exception, msg)
- proc forall(ctx: Z3_context; vars: seq[Z3_ast]; assumption, body: Z3_ast): Z3_ast =
- let x = Z3_mk_implies(ctx, assumption, body)
- if vars.len > 0:
- var bound: seq[Z3_app]
- for v in vars: bound.add Z3_to_app(ctx, v)
- result = Z3_mk_forall_const(ctx, 0, bound.len.cuint, addr(bound[0]), 0, nil, x)
- else:
- result = x
- proc conj(ctx: Z3_context; conds: seq[Z3_ast]): Z3_ast =
- if conds.len > 0:
- result = Z3_mk_and(ctx, cuint(conds.len), unsafeAddr conds[0])
- else:
- result = Z3_mk_true(ctx)
- proc proofEngineAux(c: var DrCon; assumptions: seq[PNode]; toProve: PNode): (bool, string) =
- c.mapping = initTable[string, Z3_ast]()
- let cfg = Z3_mk_config()
- Z3_set_param_value(cfg, "model", "true");
- let ctx = Z3_mk_context(cfg)
- c.z3 = ctx
- Z3_del_config(cfg)
- Z3_set_error_handler(ctx, on_err)
- when false:
- Z3_set_param_value(cfg, "timeout", "1000")
- try:
- #[
- For example, let's have these facts:
- i < 10
- i > 0
- Question:
- i + 3 < 13
- What we need to produce:
- forall(i, (i < 10) & (i > 0) -> (i + 3 < 13))
- ]#
- var collectedVars: seq[PNode]
- let solver = Z3_mk_solver(ctx)
- var lhs: seq[Z3_ast]
- for assumption in assumptions:
- if assumption != nil:
- try:
- let za = nodeToZ3(c, assumption, collectedVars)
- #Z3_solver_assert ctx, solver, za
- lhs.add za
- except CannotMapToZ3Error:
- discard "ignore a fact we cannot map to Z3"
- let z3toProve = nodeToZ3(c, toProve, collectedVars)
- for v in collectedVars:
- addRangeInfo(c, v, lhs)
- # to make Z3 produce nice counterexamples, we try to prove the
- # negation of our conjecture and see if it's Z3_L_FALSE
- let fa = Z3_mk_not(ctx, Z3_mk_implies(ctx, conj(ctx, lhs), z3toProve))
- #Z3_mk_not(ctx, forall(ctx, collectedVars, conj(ctx, lhs), z3toProve))
- #echo "toProve: ", Z3_ast_to_string(ctx, fa), " ", c.graph.config $ toProve.info
- Z3_solver_assert ctx, solver, fa
- let z3res = Z3_solver_check(ctx, solver)
- result[0] = z3res == Z3_L_FALSE
- result[1] = ""
- if not result[0]:
- let counterex = strip($Z3_model_to_string(ctx, Z3_solver_get_model(ctx, solver)))
- if counterex.len > 0:
- result[1].add "; counter example: " & counterex
- except ValueError:
- result[0] = false
- result[1] = getCurrentExceptionMsg()
- finally:
- Z3_del_context(ctx)
- proc proofEngine(graph: ModuleGraph; assumptions: seq[PNode]; toProve: PNode): (bool, string) =
- var c: DrCon
- c.graph = graph
- result = proofEngineAux(c, assumptions, toProve)
- proc translateReq(r, call: PNode): PNode =
- if r.kind == nkSym and r.sym.kind == skParam:
- if r.sym.position+1 < call.len:
- result = call[r.sym.position+1]
- else:
- notImplemented("no argument given for formal parameter: " & r.sym.name.s)
- else:
- result = shallowCopy(r)
- for i in 0 ..< safeLen(r):
- result[i] = translateReq(r[i], call)
- proc requirementsCheck(graph: ModuleGraph; assumptions: seq[PNode];
- call, requirement: PNode): (bool, string) {.nimcall.} =
- try:
- let r = translateReq(requirement, call)
- result = proofEngine(graph, assumptions, r)
- except ValueError:
- result[0] = false
- result[1] = getCurrentExceptionMsg()
- proc compatibleProps(graph: ModuleGraph; formal, actual: PType): bool {.nimcall.} =
- #[
- Thoughts on subtyping rules for 'proc' types:
- proc a(y: int) {.requires: y > 0.} # a is 'weaker' than F
- # 'requires' must be weaker (or equal)
- # 'ensures' must be stronger (or equal)
- # a 'is weaker than' b iff b -> a
- # a 'is stronger than' b iff a -> b
- # --> We can use Z3 to compute whether 'var x: T = q' is valid
- type
- F = proc (y: int) {.requires: y > 5.}
- var
- x: F = a # valid?
- ]#
- proc isEmpty(n: PNode): bool {.inline.} = n == nil or n.safeLen == 0
- result = true
- if formal.n != nil and formal.n.len > 0 and formal.n[0].kind == nkEffectList and
- ensuresEffects < formal.n[0].len:
- let frequires = formal.n[0][requiresEffects]
- let fensures = formal.n[0][ensuresEffects]
- if actual.n != nil and actual.n.len > 0 and actual.n[0].kind == nkEffectList and
- ensuresEffects < actual.n[0].len:
- let arequires = actual.n[0][requiresEffects]
- let aensures = actual.n[0][ensuresEffects]
- var c: DrCon
- c.graph = graph
- c.canonParameterNames = true
- if not frequires.isEmpty:
- result = not arequires.isEmpty and proofEngineAux(c, @[frequires], arequires)[0]
- if result:
- if not fensures.isEmpty:
- result = not aensures.isEmpty and proofEngineAux(c, @[aensures], fensures)[0]
- else:
- # formal has requirements but 'actual' has none, so make it
- # incompatible. XXX What if the requirement only mentions that
- # we already know from the type system?
- result = frequires.isEmpty and fensures.isEmpty
- proc mainCommand(graph: ModuleGraph) =
- let conf = graph.config
- conf.lastCmdTime = epochTime()
- graph.proofEngine = proofEngine
- graph.requirementsCheck = requirementsCheck
- graph.compatibleProps = compatibleProps
- graph.config.errorMax = high(int) # do not stop after first error
- defineSymbol(graph.config.symbols, "nimcheck")
- defineSymbol(graph.config.symbols, "nimDrNim")
- registerPass graph, verbosePass
- registerPass graph, semPass
- compileProject(graph)
- if conf.errorCounter == 0:
- let mem =
- when declared(system.getMaxMem): formatSize(getMaxMem()) & " peakmem"
- else: formatSize(getTotalMem()) & " totmem"
- let loc = $conf.linesCompiled
- let build = if isDefined(conf, "danger"): "Dangerous Release"
- elif isDefined(conf, "release"): "Release"
- else: "Debug"
- let sec = formatFloat(epochTime() - conf.lastCmdTime, ffDecimal, 3)
- let project = if optListFullPaths in conf.globalOptions: $conf.projectFull else: $conf.projectName
- var output = $conf.absOutFile
- if optListFullPaths notin conf.globalOptions: output = output.AbsoluteFile.extractFilename
- rawMessage(conf, hintSuccessX, [
- "loc", loc,
- "sec", sec,
- "mem", mem,
- "build", build,
- "project", project,
- "output", output,
- ])
- proc prependCurDir(f: AbsoluteFile): AbsoluteFile =
- when defined(unix):
- if os.isAbsolute(f.string): result = f
- else: result = AbsoluteFile("./" & f.string)
- else:
- result = f
- proc addCmdPrefix(result: var string, kind: CmdLineKind) =
- # consider moving this to std/parseopt
- case kind
- of cmdLongOption: result.add "--"
- of cmdShortOption: result.add "-"
- of cmdArgument, cmdEnd: discard
- proc processCmdLine(pass: TCmdLinePass, cmd: string; config: ConfigRef) =
- var p = parseopt.initOptParser(cmd)
- var argsCount = 1
- config.commandLine.setLen 0
- config.command = "check"
- config.cmd = cmdCheck
- while true:
- parseopt.next(p)
- case p.kind
- of cmdEnd: break
- of cmdLongOption, cmdShortOption:
- config.commandLine.add " "
- config.commandLine.addCmdPrefix p.kind
- config.commandLine.add p.key.quoteShell # quoteShell to be future proof
- if p.val.len > 0:
- config.commandLine.add ':'
- config.commandLine.add p.val.quoteShell
- if p.key == " ":
- p.key = "-"
- if processArgument(pass, p, argsCount, config): break
- else:
- processSwitch(pass, p, config)
- of cmdArgument:
- config.commandLine.add " "
- config.commandLine.add p.key.quoteShell
- if processArgument(pass, p, argsCount, config): break
- if pass == passCmd2:
- if {optRun, optWasNimscript} * config.globalOptions == {} and
- config.arguments.len > 0 and config.command.normalize notin ["run", "e"]:
- rawMessage(config, errGenerated, errArgsNeedRunOption)
- proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
- let self = NimProg(
- supportsStdinFile: true,
- processCmdLine: processCmdLine,
- mainCommand: mainCommand
- )
- self.initDefinesProg(conf, "drnim")
- if paramCount() == 0:
- helpOnError(conf)
- return
- self.processCmdLineAndProjectPath(conf)
- if not self.loadConfigsAndRunMainCommand(cache, conf): return
- if conf.hasHint(hintGCStats): echo(GC_getStatistics())
- when compileOption("gc", "v2") or compileOption("gc", "refc"):
- # the new correct mark&sweep collector is too slow :-/
- GC_disableMarkAndSweep()
- when not defined(selftest):
- let conf = newConfigRef()
- handleCmdLine(newIdentCache(), conf)
- when declared(GC_setMaxPause):
- echo GC_getStatistics()
- msgQuit(int8(conf.errorCounter > 0))
|