mirror of
https://github.com/nim-lang/Nim.git
synced 2026-02-13 06:43:52 +00:00
'assert' hides EAssertionFailsure; stdlib makes use of 'tags'
This commit is contained in:
@@ -59,6 +59,7 @@ proc InitDefines*() =
|
||||
DefineSymbol("nimhygiene")
|
||||
DefineSymbol("niminheritable")
|
||||
DefineSymbol("nimmixin")
|
||||
DefineSymbol("nimeffects")
|
||||
|
||||
# add platform specific symbols:
|
||||
case targetCPU
|
||||
|
||||
@@ -102,7 +102,8 @@ proc addEffect(a: PEffects, e: PNode, useLineInfo=true) =
|
||||
throws(a.exc, e)
|
||||
|
||||
proc mergeEffects(a: PEffects, b: PNode, useLineInfo: bool) =
|
||||
for effect in items(b): addEffect(a, effect, useLineInfo)
|
||||
if not b.isNil:
|
||||
for effect in items(b): addEffect(a, effect, useLineInfo)
|
||||
|
||||
proc addTag(a: PEffects, e: PNode, useLineInfo=true) =
|
||||
var aa = a.tags
|
||||
@@ -113,7 +114,8 @@ proc addTag(a: PEffects, e: PNode, useLineInfo=true) =
|
||||
throws(a.tags, e)
|
||||
|
||||
proc mergeTags(a: PEffects, b: PNode, useLineInfo: bool) =
|
||||
for effect in items(b): addTag(a, effect, useLineInfo)
|
||||
if not b.isNil:
|
||||
for effect in items(b): addTag(a, effect, useLineInfo)
|
||||
|
||||
proc listEffects(a: PEffects) =
|
||||
for e in items(a.exc): Message(e.info, hintUser, typeToString(e.typ))
|
||||
@@ -125,7 +127,7 @@ proc catches(tracked: PEffects, e: PType) =
|
||||
var i = tracked.bottom
|
||||
while i < L:
|
||||
# r supertype of e?
|
||||
if inheritanceDiff(tracked.exc[i].excType, e) <= 0:
|
||||
if safeInheritanceDiff(tracked.exc[i].excType, e) <= 0:
|
||||
tracked.exc.sons[i] = tracked.exc.sons[L-1]
|
||||
dec L
|
||||
else:
|
||||
@@ -272,7 +274,7 @@ proc checkRaisesSpec(spec, real: PNode, msg: string, hints: bool) =
|
||||
for r in items(real):
|
||||
block search:
|
||||
for s in 0 .. <spec.len:
|
||||
if inheritanceDiff(r.excType, spec[s].typ) <= 0:
|
||||
if safeInheritanceDiff(r.excType, spec[s].typ) <= 0:
|
||||
used.incl(s)
|
||||
break search
|
||||
# XXX call graph analysis would be nice here!
|
||||
@@ -341,6 +343,6 @@ proc trackProc*(s: PSym, body: PNode) =
|
||||
let tagsSpec = effectSpec(p, wTags)
|
||||
if not isNil(tagsSpec):
|
||||
checkRaisesSpec(tagsSpec, t.tags, "can have an unlisted effect: ",
|
||||
hints=on)
|
||||
hints=off)
|
||||
# after the check, use the formal spec:
|
||||
effects.sons[tagEffects] = tagsSpec
|
||||
|
||||
@@ -1190,16 +1190,23 @@ proc baseOfDistinct*(t: PType): PType =
|
||||
internalAssert parent != nil
|
||||
parent.sons[0] = it.sons[0]
|
||||
|
||||
proc safeInheritanceDiff*(a, b: PType): int =
|
||||
# same as inheritanceDiff but checks for tyError:
|
||||
if a.kind == tyError or b.kind == tyError:
|
||||
result = -1
|
||||
else:
|
||||
result = inheritanceDiff(a, b)
|
||||
|
||||
proc compatibleEffectsAux(se, re: PNode): bool =
|
||||
if re.isNil: return false
|
||||
for r in items(re):
|
||||
block search:
|
||||
for s in items(se):
|
||||
if inheritanceDiff(r.typ, s.typ) <= 0:
|
||||
if safeInheritanceDiff(r.typ, s.typ) <= 0:
|
||||
break search
|
||||
return false
|
||||
result = true
|
||||
|
||||
|
||||
proc compatibleEffects*(formal, actual: PType): bool =
|
||||
# for proc type compatibility checking:
|
||||
assert formal.kind == tyProc and actual.kind == tyProc
|
||||
|
||||
@@ -23,6 +23,13 @@ type
|
||||
## in preventDeadlocks-mode guarantees re-entrancy.
|
||||
TCond* = TSysCond ## Nimrod condition variable
|
||||
|
||||
FLock* = object of TEffect ## effect that denotes that some lock operation
|
||||
## is performed
|
||||
FAquireLock* = object of FLock ## effect that denotes that some lock is
|
||||
## aquired
|
||||
FReleaseLock* = object of FLock ## effect that denotes that some lock is
|
||||
## released
|
||||
|
||||
const
|
||||
noDeadlocks = defined(preventDeadlocks)
|
||||
maxLocksPerThread* = 10 ## max number of locks a thread can hold
|
||||
@@ -51,7 +58,7 @@ proc DeinitLock*(lock: var TLock) {.inline.} =
|
||||
## Frees the resources associated with the lock.
|
||||
DeinitSys(lock)
|
||||
|
||||
proc TryAcquire*(lock: var TLock): bool =
|
||||
proc TryAcquire*(lock: var TLock): bool {.tags: [FAquireLock].} =
|
||||
## Tries to acquire the given lock. Returns `true` on success.
|
||||
result = TryAcquireSys(lock)
|
||||
when noDeadlocks:
|
||||
@@ -83,7 +90,7 @@ proc TryAcquire*(lock: var TLock): bool =
|
||||
inc(locksLen)
|
||||
assert OrderedLocks()
|
||||
|
||||
proc Acquire*(lock: var TLock) =
|
||||
proc Acquire*(lock: var TLock) {.tags: [FAquireLock].} =
|
||||
## Acquires the given lock.
|
||||
when nodeadlocks:
|
||||
var p = addr(lock)
|
||||
@@ -128,7 +135,7 @@ proc Acquire*(lock: var TLock) =
|
||||
else:
|
||||
AcquireSys(lock)
|
||||
|
||||
proc Release*(lock: var TLock) =
|
||||
proc Release*(lock: var TLock) {.tags: [FReleaseLock].} =
|
||||
## Releases the given lock.
|
||||
when nodeadlocks:
|
||||
var p = addr(lock)
|
||||
|
||||
@@ -33,6 +33,10 @@ type
|
||||
EDb* = object of EIO ## exception that is raised if a database error occurs
|
||||
TDbConn* = TMongo ## a database connection; alias for ``TMongo``
|
||||
|
||||
FDb* = object of FIO ## effect that denotes a database operation
|
||||
FReadDb* = object of FReadIO ## effect that denotes a read operation
|
||||
FWriteDb* = object of FWriteIO ## effect that denotes a write operation
|
||||
|
||||
proc dbError*(db: TDbConn, msg: string) {.noreturn.} =
|
||||
## raises an EDb exception with message `msg`.
|
||||
var e: ref EDb
|
||||
@@ -43,12 +47,13 @@ proc dbError*(db: TDbConn, msg: string) {.noreturn.} =
|
||||
e.msg = $db.err & " " & msg
|
||||
raise e
|
||||
|
||||
proc Close*(db: var TDbConn) =
|
||||
proc Close*(db: var TDbConn) {.tags: [FDB].} =
|
||||
## closes the database connection.
|
||||
disconnect(db)
|
||||
destroy(db)
|
||||
|
||||
proc Open*(host: string = defaultHost, port: int = defaultPort): TDbConn =
|
||||
proc Open*(host: string = defaultHost, port: int = defaultPort): TDbConn {.
|
||||
tags: [FDB].} =
|
||||
## opens a database connection. Raises `EDb` if the connection could not
|
||||
## be established.
|
||||
init(result)
|
||||
@@ -108,7 +113,8 @@ proc getId*(obj: var TBSon): TOid =
|
||||
else:
|
||||
raise newException(EInvalidIndex, "_id not in object")
|
||||
|
||||
proc insertID*(db: var TDbConn, namespace: string, data: PJsonNode): TOid =
|
||||
proc insertID*(db: var TDbConn, namespace: string, data: PJsonNode): TOid {.
|
||||
tags: [FWriteDb].} =
|
||||
## converts `data` to BSON format and inserts it in `namespace`. Returns
|
||||
## the generated OID for the ``_id`` field.
|
||||
result = genOid()
|
||||
@@ -116,11 +122,13 @@ proc insertID*(db: var TDbConn, namespace: string, data: PJsonNode): TOid =
|
||||
insert(db, namespace, x)
|
||||
destroy(x)
|
||||
|
||||
proc insert*(db: var TDbConn, namespace: string, data: PJsonNode) =
|
||||
proc insert*(db: var TDbConn, namespace: string, data: PJsonNode) {.
|
||||
tags: [FWriteDb].} =
|
||||
## converts `data` to BSON format and inserts it in `namespace`.
|
||||
discard InsertID(db, namespace, data)
|
||||
|
||||
proc update*(db: var TDbConn, namespace: string, obj: var TBSon) =
|
||||
proc update*(db: var TDbConn, namespace: string, obj: var TBSon) {.
|
||||
tags: [FReadDB, FWriteDb].} =
|
||||
## updates `obj` in `namespace`.
|
||||
var cond: TBson
|
||||
init(cond)
|
||||
@@ -129,13 +137,15 @@ proc update*(db: var TDbConn, namespace: string, obj: var TBSon) =
|
||||
update(db, namespace, cond, obj, ord(UPDATE_UPSERT))
|
||||
destroy(cond)
|
||||
|
||||
proc update*(db: var TDbConn, namespace: string, oid: TOid, obj: PJsonNode) =
|
||||
proc update*(db: var TDbConn, namespace: string, oid: TOid, obj: PJsonNode) {.
|
||||
tags: [FReadDB, FWriteDb].} =
|
||||
## updates the data with `oid` to have the new data `obj`.
|
||||
var a = jsonToBSon(obj, oid)
|
||||
Update(db, namespace, a)
|
||||
destroy(a)
|
||||
|
||||
proc delete*(db: var TDbConn, namespace: string, oid: TOid) =
|
||||
proc delete*(db: var TDbConn, namespace: string, oid: TOid) {.
|
||||
tags: [FWriteDb].} =
|
||||
## Deletes the object belonging to `oid`.
|
||||
var cond: TBson
|
||||
init(cond)
|
||||
@@ -144,11 +154,13 @@ proc delete*(db: var TDbConn, namespace: string, oid: TOid) =
|
||||
discard remove(db, namespace, cond)
|
||||
destroy(cond)
|
||||
|
||||
proc delete*(db: var TDbConn, namespace: string, obj: var TBSon) =
|
||||
proc delete*(db: var TDbConn, namespace: string, obj: var TBSon) {.
|
||||
tags: [FWriteDb].} =
|
||||
## Deletes the object `obj`.
|
||||
delete(db, namespace, getId(obj))
|
||||
|
||||
iterator find*(db: var TDbConn, namespace: string): var TBSon =
|
||||
iterator find*(db: var TDbConn, namespace: string): var TBSon {.
|
||||
tags: [FReadDB].} =
|
||||
## iterates over any object in `namespace`.
|
||||
var cursor: TCursor
|
||||
init(cursor, db, namespace)
|
||||
@@ -157,7 +169,7 @@ iterator find*(db: var TDbConn, namespace: string): var TBSon =
|
||||
destroy(cursor)
|
||||
|
||||
iterator find*(db: var TDbConn, namespace: string,
|
||||
query, fields: var TBSon): var TBSon =
|
||||
query, fields: var TBSon): var TBSon {.tags: [FReadDB].} =
|
||||
## yields the `fields` of any document that suffices `query`.
|
||||
var cursor = find(db, namespace, query, fields, 0'i32, 0'i32, 0'i32)
|
||||
if cursor != nil:
|
||||
@@ -171,7 +183,8 @@ proc setupFieldnames(fields: varargs[string]): TBSon =
|
||||
finish(result)
|
||||
|
||||
iterator find*(db: var TDbConn, namespace: string,
|
||||
query: var TBSon, fields: varargs[string]): var TBSon =
|
||||
query: var TBSon, fields: varargs[string]): var TBSon {.
|
||||
tags: [FReadDB].} =
|
||||
## yields the `fields` of any document that suffices `query`. If `fields`
|
||||
## is ``[]`` the whole document is yielded.
|
||||
var f = setupFieldnames(fields)
|
||||
@@ -188,7 +201,8 @@ proc setupQuery(query: string): TBSon =
|
||||
finish(result)
|
||||
|
||||
iterator find*(db: var TDbConn, namespace: string,
|
||||
query: string, fields: varargs[string]): var TBSon =
|
||||
query: string, fields: varargs[string]): var TBSon {.
|
||||
tags: [FReadDB].} =
|
||||
## yields the `fields` of any document that suffices `query`. If `fields`
|
||||
## is ``[]`` the whole document is yielded.
|
||||
var f = setupFieldnames(fields)
|
||||
|
||||
@@ -18,7 +18,11 @@ type
|
||||
EDb* = object of EIO ## exception that is raised if a database error occurs
|
||||
|
||||
TSqlQuery* = distinct string ## an SQL query string
|
||||
|
||||
|
||||
FDb* = object of FIO ## effect that denotes a database operation
|
||||
FReadDb* = object of FReadIO ## effect that denotes a read operation
|
||||
FWriteDb* = object of FWriteIO ## effect that denotes a write operation
|
||||
|
||||
proc dbError(db: TDbConn) {.noreturn.} =
|
||||
## raises an EDb exception.
|
||||
var e: ref EDb
|
||||
@@ -60,12 +64,18 @@ proc dbFormat(formatstr: TSqlQuery, args: varargs[string]): string =
|
||||
else:
|
||||
add(result, c)
|
||||
|
||||
proc TryExec*(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`]): bool =
|
||||
proc TryExec*(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`]): bool {.
|
||||
tags: [FReadDB, FWriteDb].} =
|
||||
## tries to execute the query and returns true if successful, false otherwise.
|
||||
var q = dbFormat(query, args)
|
||||
return mysql.RealQuery(db, q, q.len) == 0'i32
|
||||
|
||||
proc Exec*(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`]) =
|
||||
proc rawExec(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`]) =
|
||||
var q = dbFormat(query, args)
|
||||
if mysql.RealQuery(db, q, q.len) != 0'i32: dbError(db)
|
||||
|
||||
proc Exec*(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`]) {.
|
||||
tags: [FReadDB, FWriteDb].} =
|
||||
## executes the query and raises EDB if not successful.
|
||||
var q = dbFormat(query, args)
|
||||
if mysql.RealQuery(db, q, q.len) != 0'i32: dbError(db)
|
||||
@@ -80,11 +90,11 @@ proc properFreeResult(sqlres: mysql.PRES, row: cstringArray) =
|
||||
mysql.FreeResult(sqlres)
|
||||
|
||||
iterator FastRows*(db: TDbConn, query: TSqlQuery,
|
||||
args: varargs[string, `$`]): TRow =
|
||||
args: varargs[string, `$`]): TRow {.tags: [FReadDB].} =
|
||||
## executes the query and iterates over the result dataset. This is very
|
||||
## fast, but potenially dangerous: If the for-loop-body executes another
|
||||
## query, the results can be undefined. For MySQL this is the case!.
|
||||
Exec(db, query, args)
|
||||
rawExec(db, query, args)
|
||||
var sqlres = mysql.UseResult(db)
|
||||
if sqlres != nil:
|
||||
var L = int(mysql.NumFields(sqlres))
|
||||
@@ -100,9 +110,9 @@ iterator FastRows*(db: TDbConn, query: TSqlQuery,
|
||||
properFreeResult(sqlres, row)
|
||||
|
||||
proc getRow*(db: TDbConn, query: TSqlQuery,
|
||||
args: varargs[string, `$`]): TRow =
|
||||
args: varargs[string, `$`]): TRow {.tags: [FReadDB].} =
|
||||
## retrieves a single row.
|
||||
Exec(db, query, args)
|
||||
rawExec(db, query, args)
|
||||
var sqlres = mysql.UseResult(db)
|
||||
if sqlres != nil:
|
||||
var L = int(mysql.NumFields(sqlres))
|
||||
@@ -115,10 +125,10 @@ proc getRow*(db: TDbConn, query: TSqlQuery,
|
||||
properFreeResult(sqlres, row)
|
||||
|
||||
proc GetAllRows*(db: TDbConn, query: TSqlQuery,
|
||||
args: varargs[string, `$`]): seq[TRow] =
|
||||
args: varargs[string, `$`]): seq[TRow] {.tags: [FReadDB].} =
|
||||
## executes the query and returns the whole result dataset.
|
||||
result = @[]
|
||||
Exec(db, query, args)
|
||||
rawExec(db, query, args)
|
||||
var sqlres = mysql.UseResult(db)
|
||||
if sqlres != nil:
|
||||
var L = int(mysql.NumFields(sqlres))
|
||||
@@ -134,12 +144,12 @@ proc GetAllRows*(db: TDbConn, query: TSqlQuery,
|
||||
mysql.FreeResult(sqlres)
|
||||
|
||||
iterator Rows*(db: TDbConn, query: TSqlQuery,
|
||||
args: varargs[string, `$`]): TRow =
|
||||
args: varargs[string, `$`]): TRow {.tags: [FReadDB].} =
|
||||
## same as `FastRows`, but slower and safe.
|
||||
for r in items(GetAllRows(db, query, args)): yield r
|
||||
|
||||
proc GetValue*(db: TDbConn, query: TSqlQuery,
|
||||
args: varargs[string, `$`]): string =
|
||||
args: varargs[string, `$`]): string {.tags: [FReadDB].} =
|
||||
## executes the query and returns the result dataset's the first column
|
||||
## of the first row. Returns "" if the dataset contains no rows. This uses
|
||||
## `FastRows`, so it inherits its fragile behaviour.
|
||||
@@ -149,7 +159,7 @@ proc GetValue*(db: TDbConn, query: TSqlQuery,
|
||||
break
|
||||
|
||||
proc TryInsertID*(db: TDbConn, query: TSqlQuery,
|
||||
args: varargs[string, `$`]): int64 =
|
||||
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
|
||||
## executes the query (typically "INSERT") and returns the
|
||||
## generated ID for the row or -1 in case of an error.
|
||||
var q = dbFormat(query, args)
|
||||
@@ -159,24 +169,26 @@ proc TryInsertID*(db: TDbConn, query: TSqlQuery,
|
||||
result = mysql.InsertId(db)
|
||||
|
||||
proc InsertID*(db: TDbConn, query: TSqlQuery,
|
||||
args: varargs[string, `$`]): int64 =
|
||||
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
|
||||
## executes the query (typically "INSERT") and returns the
|
||||
## generated ID for the row.
|
||||
result = TryInsertID(db, query, args)
|
||||
if result < 0: dbError(db)
|
||||
|
||||
proc ExecAffectedRows*(db: TDbConn, query: TSqlQuery,
|
||||
args: varargs[string, `$`]): int64 =
|
||||
args: varargs[string, `$`]): int64 {.
|
||||
tags: [FReadDB, FWriteDb].} =
|
||||
## runs the query (typically "UPDATE") and returns the
|
||||
## number of affected rows
|
||||
Exec(db, query, args)
|
||||
rawExec(db, query, args)
|
||||
result = mysql.AffectedRows(db)
|
||||
|
||||
proc Close*(db: TDbConn) =
|
||||
proc Close*(db: TDbConn) {.tags: [FDb].} =
|
||||
## closes the database connection.
|
||||
if db != nil: mysql.Close(db)
|
||||
|
||||
proc Open*(connection, user, password, database: string): TDbConn =
|
||||
proc Open*(connection, user, password, database: string): TDbConn {.
|
||||
tags: [FDb].} =
|
||||
## opens a database connection. Raises `EDb` if the connection could not
|
||||
## be established.
|
||||
result = mysql.Init(nil)
|
||||
|
||||
@@ -18,6 +18,10 @@ type
|
||||
EDb* = object of EIO ## exception that is raised if a database error occurs
|
||||
|
||||
TSqlQuery* = distinct string ## an SQL query string
|
||||
|
||||
FDb* = object of FIO ## effect that denotes a database operation
|
||||
FReadDb* = object of FReadIO ## effect that denotes a read operation
|
||||
FWriteDb* = object of FWriteIO ## effect that denotes a write operation
|
||||
|
||||
proc sql*(query: string): TSqlQuery {.noSideEffect, inline.} =
|
||||
## constructs a TSqlQuery from the string `query`. This is supposed to be
|
||||
@@ -60,14 +64,15 @@ proc dbFormat(formatstr: TSqlQuery, args: varargs[string]): string =
|
||||
add(result, c)
|
||||
|
||||
proc TryExec*(db: TDbConn, query: TSqlQuery,
|
||||
args: varargs[string, `$`]): bool =
|
||||
args: varargs[string, `$`]): bool {.tags: [FReadDB, FWriteDb].} =
|
||||
## tries to execute the query and returns true if successful, false otherwise.
|
||||
var q = dbFormat(query, args)
|
||||
var res = PQExec(db, q)
|
||||
result = PQresultStatus(res) == PGRES_COMMAND_OK
|
||||
PQclear(res)
|
||||
|
||||
proc Exec*(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`]) =
|
||||
proc Exec*(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`]) {.
|
||||
tags: [FReadDB, FWriteDb].} =
|
||||
## executes the query and raises EDB if not successful.
|
||||
var q = dbFormat(query, args)
|
||||
var res = PQExec(db, q)
|
||||
@@ -91,7 +96,7 @@ proc setRow(res: PPGresult, r: var TRow, line, cols: int32) =
|
||||
add(r[col], x)
|
||||
|
||||
iterator FastRows*(db: TDbConn, query: TSqlQuery,
|
||||
args: varargs[string, `$`]): TRow =
|
||||
args: varargs[string, `$`]): TRow {.tags: [FReadDB].} =
|
||||
## executes the query and iterates over the result dataset. This is very
|
||||
## fast, but potenially dangerous: If the for-loop-body executes another
|
||||
## query, the results can be undefined. For Postgres it is safe though.
|
||||
@@ -104,7 +109,7 @@ iterator FastRows*(db: TDbConn, query: TSqlQuery,
|
||||
PQclear(res)
|
||||
|
||||
proc getRow*(db: TDbConn, query: TSqlQuery,
|
||||
args: varargs[string, `$`]): TRow =
|
||||
args: varargs[string, `$`]): TRow {.tags: [FReadDB].} =
|
||||
## retrieves a single row.
|
||||
var res = setupQuery(db, query, args)
|
||||
var L = PQnfields(res)
|
||||
@@ -113,38 +118,39 @@ proc getRow*(db: TDbConn, query: TSqlQuery,
|
||||
PQclear(res)
|
||||
|
||||
proc GetAllRows*(db: TDbConn, query: TSqlQuery,
|
||||
args: varargs[string, `$`]): seq[TRow] =
|
||||
args: varargs[string, `$`]): seq[TRow] {.tags: [FReadDB].} =
|
||||
## executes the query and returns the whole result dataset.
|
||||
result = @[]
|
||||
for r in FastRows(db, query, args):
|
||||
result.add(r)
|
||||
|
||||
iterator Rows*(db: TDbConn, query: TSqlQuery,
|
||||
args: varargs[string, `$`]): TRow =
|
||||
args: varargs[string, `$`]): TRow {.tags: [FReadDB].} =
|
||||
## same as `FastRows`, but slower and safe.
|
||||
for r in items(GetAllRows(db, query, args)): yield r
|
||||
|
||||
proc GetValue*(db: TDbConn, query: TSqlQuery,
|
||||
args: varargs[string, `$`]): string =
|
||||
args: varargs[string, `$`]): string {.tags: [FReadDB].} =
|
||||
## executes the query and returns the result dataset's the first column
|
||||
## of the first row. Returns "" if the dataset contains no rows.
|
||||
var x = PQgetvalue(setupQuery(db, query, args), 0, 0)
|
||||
result = if isNil(x): "" else: $x
|
||||
|
||||
proc TryInsertID*(db: TDbConn, query: TSqlQuery,
|
||||
args: varargs[string, `$`]): int64 =
|
||||
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].}=
|
||||
## executes the query (typically "INSERT") and returns the
|
||||
## generated ID for the row or -1 in case of an error. For Postgre this adds
|
||||
## ``RETURNING id`` to the query, so it only works if your primary key is
|
||||
## named ``id``.
|
||||
var val = GetValue(db, TSqlQuery(string(query) & " RETURNING id"), args)
|
||||
if val.len > 0:
|
||||
result = ParseBiggestInt(val)
|
||||
var x = PQgetvalue(setupQuery(db, TSqlQuery(string(query) & " RETURNING id"),
|
||||
args), 0, 0)
|
||||
if not isNil(x):
|
||||
result = ParseBiggestInt($x)
|
||||
else:
|
||||
result = -1
|
||||
|
||||
proc InsertID*(db: TDbConn, query: TSqlQuery,
|
||||
args: varargs[string, `$`]): int64 =
|
||||
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
|
||||
## executes the query (typically "INSERT") and returns the
|
||||
## generated ID for the row. For Postgre this adds
|
||||
## ``RETURNING id`` to the query, so it only works if your primary key is
|
||||
@@ -153,7 +159,8 @@ proc InsertID*(db: TDbConn, query: TSqlQuery,
|
||||
if result < 0: dbError(db)
|
||||
|
||||
proc ExecAffectedRows*(db: TDbConn, query: TSqlQuery,
|
||||
args: varargs[string, `$`]): int64 =
|
||||
args: varargs[string, `$`]): int64 {.tags: [
|
||||
FReadDB, FWriteDb].} =
|
||||
## executes the query (typically "UPDATE") and returns the
|
||||
## number of affected rows.
|
||||
var q = dbFormat(query, args)
|
||||
@@ -162,11 +169,12 @@ proc ExecAffectedRows*(db: TDbConn, query: TSqlQuery,
|
||||
result = parseBiggestInt($PQcmdTuples(res))
|
||||
PQclear(res)
|
||||
|
||||
proc Close*(db: TDbConn) =
|
||||
proc Close*(db: TDbConn) {.tags: [FDb].} =
|
||||
## closes the database connection.
|
||||
if db != nil: PQfinish(db)
|
||||
|
||||
proc Open*(connection, user, password, database: string): TDbConn =
|
||||
proc Open*(connection, user, password, database: string): TDbConn {.
|
||||
tags: [FDb].} =
|
||||
## opens a database connection. Raises `EDb` if the connection could not
|
||||
## be established.
|
||||
result = PQsetdbLogin(nil, nil, nil, nil, database, user, password)
|
||||
|
||||
@@ -19,6 +19,10 @@ type
|
||||
|
||||
TSqlQuery* = distinct string ## an SQL query string
|
||||
|
||||
FDb* = object of FIO ## effect that denotes a database operation
|
||||
FReadDb* = object of FReadIO ## effect that denotes a read operation
|
||||
FWriteDb* = object of FWriteIO ## effect that denotes a write operation
|
||||
|
||||
proc sql*(query: string): TSqlQuery {.noSideEffect, inline.} =
|
||||
## constructs a TSqlQuery from the string `query`. This is supposed to be
|
||||
## used as a raw-string-literal modifier:
|
||||
@@ -60,7 +64,7 @@ proc dbFormat(formatstr: TSqlQuery, args: varargs[string]): string =
|
||||
add(result, c)
|
||||
|
||||
proc TryExec*(db: TDbConn, query: TSqlQuery,
|
||||
args: varargs[string, `$`]): bool =
|
||||
args: varargs[string, `$`]): bool {.tags: [FReadDB, FWriteDb].} =
|
||||
## tries to execute the query and returns true if successful, false otherwise.
|
||||
var q = dbFormat(query, args)
|
||||
var stmt: sqlite3.PStmt
|
||||
@@ -68,7 +72,8 @@ proc TryExec*(db: TDbConn, query: TSqlQuery,
|
||||
if step(stmt) == SQLITE_DONE:
|
||||
result = finalize(stmt) == SQLITE_OK
|
||||
|
||||
proc Exec*(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`]) =
|
||||
proc Exec*(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`]) {.
|
||||
tags: [FReadDB, FWriteDb].} =
|
||||
## executes the query and raises EDB if not successful.
|
||||
if not TryExec(db, query, args): dbError(db)
|
||||
|
||||
@@ -89,7 +94,7 @@ proc setRow(stmt: PStmt, r: var TRow, cols: cint) =
|
||||
if not isNil(x): add(r[col], x)
|
||||
|
||||
iterator FastRows*(db: TDbConn, query: TSqlQuery,
|
||||
args: varargs[string, `$`]): TRow =
|
||||
args: varargs[string, `$`]): TRow {.tags: [FReadDB].} =
|
||||
## executes the query and iterates over the result dataset. This is very
|
||||
## fast, but potenially dangerous: If the for-loop-body executes another
|
||||
## query, the results can be undefined. For Sqlite it is safe though.
|
||||
@@ -102,7 +107,7 @@ iterator FastRows*(db: TDbConn, query: TSqlQuery,
|
||||
if finalize(stmt) != SQLITE_OK: dbError(db)
|
||||
|
||||
proc getRow*(db: TDbConn, query: TSqlQuery,
|
||||
args: varargs[string, `$`]): TRow =
|
||||
args: varargs[string, `$`]): TRow {.tags: [FReadDB].} =
|
||||
## retrieves a single row.
|
||||
var stmt = setupQuery(db, query, args)
|
||||
var L = (columnCount(stmt))
|
||||
@@ -112,19 +117,19 @@ proc getRow*(db: TDbConn, query: TSqlQuery,
|
||||
if finalize(stmt) != SQLITE_OK: dbError(db)
|
||||
|
||||
proc GetAllRows*(db: TDbConn, query: TSqlQuery,
|
||||
args: varargs[string, `$`]): seq[TRow] =
|
||||
args: varargs[string, `$`]): seq[TRow] {.tags: [FReadDB].} =
|
||||
## executes the query and returns the whole result dataset.
|
||||
result = @[]
|
||||
for r in FastRows(db, query, args):
|
||||
result.add(r)
|
||||
|
||||
iterator Rows*(db: TDbConn, query: TSqlQuery,
|
||||
args: varargs[string, `$`]): TRow =
|
||||
args: varargs[string, `$`]): TRow {.tags: [FReadDB].} =
|
||||
## same as `FastRows`, but slower and safe.
|
||||
for r in FastRows(db, query, args): yield r
|
||||
|
||||
proc GetValue*(db: TDbConn, query: TSqlQuery,
|
||||
args: varargs[string, `$`]): string =
|
||||
args: varargs[string, `$`]): string {.tags: [FReadDB].} =
|
||||
## executes the query and returns the result dataset's the first column
|
||||
## of the first row. Returns "" if the dataset contains no rows.
|
||||
var stmt = setupQuery(db, query, args)
|
||||
@@ -140,16 +145,19 @@ proc GetValue*(db: TDbConn, query: TSqlQuery,
|
||||
result = ""
|
||||
|
||||
proc TryInsertID*(db: TDbConn, query: TSqlQuery,
|
||||
args: varargs[string, `$`]): int64 =
|
||||
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
|
||||
## executes the query (typically "INSERT") and returns the
|
||||
## generated ID for the row or -1 in case of an error.
|
||||
if tryExec(db, query, args):
|
||||
result = last_insert_rowid(db)
|
||||
else:
|
||||
result = -1
|
||||
var q = dbFormat(query, args)
|
||||
var stmt: sqlite3.PStmt
|
||||
if prepare_v2(db, q, q.len.cint, stmt, nil) == SQLITE_OK:
|
||||
if step(stmt) == SQLITE_DONE:
|
||||
if finalize(stmt) == SQLITE_OK:
|
||||
return last_insert_rowid(db)
|
||||
result = -1
|
||||
|
||||
proc InsertID*(db: TDbConn, query: TSqlQuery,
|
||||
args: varargs[string, `$`]): int64 =
|
||||
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
|
||||
## executes the query (typically "INSERT") and returns the
|
||||
## generated ID for the row. For Postgre this adds
|
||||
## ``RETURNING id`` to the query, so it only works if your primary key is
|
||||
@@ -158,17 +166,19 @@ proc InsertID*(db: TDbConn, query: TSqlQuery,
|
||||
if result < 0: dbError(db)
|
||||
|
||||
proc ExecAffectedRows*(db: TDbConn, query: TSqlQuery,
|
||||
args: varargs[string, `$`]): int64 =
|
||||
args: varargs[string, `$`]): int64 {.
|
||||
tags: [FReadDB, FWriteDb].} =
|
||||
## executes the query (typically "UPDATE") and returns the
|
||||
## number of affected rows.
|
||||
Exec(db, query, args)
|
||||
result = changes(db)
|
||||
|
||||
proc Close*(db: TDbConn) =
|
||||
proc Close*(db: TDbConn) {.tags: [FDB].} =
|
||||
## closes the database connection.
|
||||
if sqlite3.close(db) != SQLITE_OK: dbError(db)
|
||||
|
||||
proc Open*(connection, user, password, database: string): TDbConn =
|
||||
proc Open*(connection, user, password, database: string): TDbConn {.
|
||||
tags: [FDB].} =
|
||||
## opens a database connection. Raises `EDb` if the connection could not
|
||||
## be established. Only the ``connection`` parameter is used for ``sqlite``.
|
||||
var db: TDbConn
|
||||
|
||||
@@ -14,12 +14,14 @@
|
||||
## wanted functionality.
|
||||
|
||||
when defined(Windows):
|
||||
proc ReadLineFromStdin*(prompt: string): TaintedString =
|
||||
proc ReadLineFromStdin*(prompt: string): TaintedString {.
|
||||
tags: [FReadIO, FWriteIO].} =
|
||||
## Reads a line from stdin.
|
||||
stdout.write(prompt)
|
||||
result = readLine(stdin)
|
||||
|
||||
proc ReadLineFromStdin*(prompt: string, line: var TaintedString): bool =
|
||||
proc ReadLineFromStdin*(prompt: string, line: var TaintedString): bool {.
|
||||
tags: [FReadIO, FWriteIO].} =
|
||||
## Reads a `line` from stdin. `line` must not be
|
||||
## ``nil``! May throw an IO exception.
|
||||
## A line of text may be delimited by ``CR``, ``LF`` or
|
||||
@@ -32,7 +34,8 @@ when defined(Windows):
|
||||
else:
|
||||
import readline, history
|
||||
|
||||
proc ReadLineFromStdin*(prompt: string): TaintedString =
|
||||
proc ReadLineFromStdin*(prompt: string): TaintedString {.
|
||||
tags: [FReadIO, FWriteIO].} =
|
||||
var buffer = readline.readLine(prompt)
|
||||
if isNil(buffer): quit(0)
|
||||
result = TaintedString($buffer)
|
||||
@@ -40,7 +43,8 @@ else:
|
||||
add_history(buffer)
|
||||
readline.free(buffer)
|
||||
|
||||
proc ReadLineFromStdin*(prompt: string, line: var TaintedString): bool =
|
||||
proc ReadLineFromStdin*(prompt: string, line: var TaintedString): bool {.
|
||||
tags: [FReadIO, FWriteIO].} =
|
||||
var buffer = readline.readLine(prompt)
|
||||
if isNil(buffer): quit(0)
|
||||
line = TaintedString($buffer)
|
||||
|
||||
@@ -58,5 +58,5 @@ proc URLretrieveString*(url: string): TaintedString =
|
||||
result = stream.data.TaintedString
|
||||
|
||||
when isMainModule:
|
||||
echo URLretrieveString("http://nimrod.ethexor.com/")
|
||||
echo URLretrieveString("http://nimrod-code.org/")
|
||||
|
||||
|
||||
@@ -43,11 +43,12 @@ type
|
||||
proc execProcess*(command: string,
|
||||
options: set[TProcessOption] = {poStdErrToStdOut,
|
||||
poUseShell}): TaintedString {.
|
||||
rtl, extern: "nosp$1".}
|
||||
rtl, extern: "nosp$1",
|
||||
tags: [FExecIO, FReadIO].}
|
||||
## A convenience procedure that executes ``command`` with ``startProcess``
|
||||
## and returns its output as a string.
|
||||
|
||||
proc execCmd*(command: string): int {.rtl, extern: "nosp$1".}
|
||||
proc execCmd*(command: string): int {.rtl, extern: "nosp$1", tags: [FExecIO].}
|
||||
## Executes ``command`` and returns its error code. Standard input, output,
|
||||
## error streams are inherited from the calling process. This operation
|
||||
## is also often called `system`:idx:.
|
||||
@@ -57,7 +58,7 @@ proc startProcess*(command: string,
|
||||
args: openarray[string] = [],
|
||||
env: PStringTable = nil,
|
||||
options: set[TProcessOption] = {poStdErrToStdOut}):
|
||||
PProcess {.rtl, extern: "nosp$1".}
|
||||
PProcess {.rtl, extern: "nosp$1", tags: [FExecIO].}
|
||||
## Starts a process. `Command` is the executable file, `workingDir` is the
|
||||
## process's working directory. If ``workingDir == ""`` the current directory
|
||||
## is used. `args` are the command line arguments that are passed to the
|
||||
@@ -73,7 +74,7 @@ proc startProcess*(command: string,
|
||||
## but ``EOS`` is raised in case of an error.
|
||||
|
||||
proc startCmd*(command: string, options: set[TProcessOption] = {
|
||||
poStdErrToStdOut, poUseShell}): PProcess =
|
||||
poStdErrToStdOut, poUseShell}): PProcess {.tags: [FExecIO].} =
|
||||
## a simpler version of `startProcess` that parses the command line into
|
||||
## program and arguments and then calls `startProcess` with the empty string
|
||||
## for `workingDir` and the nil string table for `env`.
|
||||
@@ -83,38 +84,39 @@ proc startCmd*(command: string, options: set[TProcessOption] = {
|
||||
for i in 1 .. c.len-1: a[i-1] = c[i]
|
||||
result = startProcess(command=c[0], args=a, options=options)
|
||||
|
||||
proc close*(p: PProcess) {.rtl, extern: "nosp$1".}
|
||||
proc close*(p: PProcess) {.rtl, extern: "nosp$1", tags: [].}
|
||||
## When the process has finished executing, cleanup related handles
|
||||
|
||||
proc suspend*(p: PProcess) {.rtl, extern: "nosp$1".}
|
||||
proc suspend*(p: PProcess) {.rtl, extern: "nosp$1", tags: [].}
|
||||
## Suspends the process `p`.
|
||||
|
||||
proc resume*(p: PProcess) {.rtl, extern: "nosp$1".}
|
||||
proc resume*(p: PProcess) {.rtl, extern: "nosp$1", tags: [].}
|
||||
## Resumes the process `p`.
|
||||
|
||||
proc terminate*(p: PProcess) {.rtl, extern: "nosp$1".}
|
||||
proc terminate*(p: PProcess) {.rtl, extern: "nosp$1", tags: [].}
|
||||
## Terminates the process `p`.
|
||||
|
||||
proc running*(p: PProcess): bool {.rtl, extern: "nosp$1".}
|
||||
proc running*(p: PProcess): bool {.rtl, extern: "nosp$1", tags: [].}
|
||||
## Returns true iff the process `p` is still running. Returns immediately.
|
||||
|
||||
proc processID*(p: PProcess): int {.rtl, extern: "nosp$1".} =
|
||||
## returns `p`'s process ID.
|
||||
return p.id
|
||||
|
||||
proc waitForExit*(p: PProcess, timeout: int = -1): int {.rtl, extern: "nosp$1".}
|
||||
proc waitForExit*(p: PProcess, timeout: int = -1): int {.rtl,
|
||||
extern: "nosp$1", tags: [].}
|
||||
## waits for the process to finish and returns `p`'s error code.
|
||||
|
||||
proc peekExitCode*(p: PProcess): int
|
||||
proc peekExitCode*(p: PProcess): int {.tags: [].}
|
||||
## return -1 if the process is still running. Otherwise the process' exit code
|
||||
|
||||
proc inputStream*(p: PProcess): PStream {.rtl, extern: "nosp$1".}
|
||||
proc inputStream*(p: PProcess): PStream {.rtl, extern: "nosp$1", tags: [].}
|
||||
## opens ``p``'s input stream for writing to
|
||||
|
||||
proc outputStream*(p: PProcess): PStream {.rtl, extern: "nosp$1".}
|
||||
proc outputStream*(p: PProcess): PStream {.rtl, extern: "nosp$1", tags: [].}
|
||||
## opens ``p``'s output stream for reading from
|
||||
|
||||
proc errorStream*(p: PProcess): PStream {.rtl, extern: "nosp$1".}
|
||||
proc errorStream*(p: PProcess): PStream {.rtl, extern: "nosp$1", tags: [].}
|
||||
## opens ``p``'s output stream for reading from
|
||||
|
||||
when defined(macosx) or defined(bsd):
|
||||
@@ -156,7 +158,8 @@ proc countProcessors*(): int {.rtl, extern: "nosp$1".} =
|
||||
|
||||
proc execProcesses*(cmds: openArray[string],
|
||||
options = {poStdErrToStdOut, poParentStreams},
|
||||
n = countProcessors()): int {.rtl, extern: "nosp$1".} =
|
||||
n = countProcessors()): int {.rtl, extern: "nosp$1",
|
||||
tags: [FExecIO].} =
|
||||
## executes the commands `cmds` in parallel. Creates `n` processes
|
||||
## that execute in parallel. The highest return value of all processes
|
||||
## is returned.
|
||||
@@ -731,7 +734,7 @@ elif not defined(useNimRtl):
|
||||
proc execCmdEx*(command: string, options: set[TProcessOption] = {
|
||||
poStdErrToStdOut, poUseShell}): tuple[
|
||||
output: TaintedString,
|
||||
exitCode: int] =
|
||||
exitCode: int] {.tags: [FExecIO, FReadIO].} =
|
||||
## a convenience proc that runs the `command`, grabs all its output and
|
||||
## exit code and returns both.
|
||||
var p = startCmd(command, options)
|
||||
|
||||
@@ -23,14 +23,15 @@ type
|
||||
## here shouldn't be used directly. They are
|
||||
## accessible so that a stream implementation
|
||||
## can override them.
|
||||
closeImpl*: proc (s: PStream) {.nimcall.}
|
||||
atEndImpl*: proc (s: PStream): bool {.nimcall.}
|
||||
setPositionImpl*: proc (s: PStream, pos: int) {.nimcall.}
|
||||
getPositionImpl*: proc (s: PStream): int {.nimcall.}
|
||||
closeImpl*: proc (s: PStream) {.nimcall, tags: [].}
|
||||
atEndImpl*: proc (s: PStream): bool {.nimcall, tags: [].}
|
||||
setPositionImpl*: proc (s: PStream, pos: int) {.nimcall, tags: [].}
|
||||
getPositionImpl*: proc (s: PStream): int {.nimcall, tags: [].}
|
||||
readDataImpl*: proc (s: PStream, buffer: pointer,
|
||||
bufLen: int): int {.nimcall.}
|
||||
writeDataImpl*: proc (s: PStream, buffer: pointer, bufLen: int) {.nimcall.}
|
||||
flushImpl*: proc (s: PStream) {.nimcall.}
|
||||
bufLen: int): int {.nimcall, tags: [FReadIO].}
|
||||
writeDataImpl*: proc (s: PStream, buffer: pointer, bufLen: int) {.nimcall,
|
||||
tags: [FWriteIO].}
|
||||
flushImpl*: proc (s: PStream) {.nimcall, tags: [FWriteIO].}
|
||||
|
||||
proc flush*(s: PStream) =
|
||||
## flushes the buffers that the stream `s` might use.
|
||||
|
||||
@@ -1080,21 +1080,21 @@ proc equalMem*(a, b: Pointer, size: int): bool {.
|
||||
## otherwise. Like any procedure dealing with raw memory this is
|
||||
## *unsafe*.
|
||||
|
||||
proc alloc*(size: int): pointer {.noconv, rtl.}
|
||||
proc alloc*(size: int): pointer {.noconv, rtl, tags: [].}
|
||||
## allocates a new memory block with at least ``size`` bytes. The
|
||||
## block has to be freed with ``realloc(block, 0)`` or
|
||||
## ``dealloc(block)``. The block is not initialized, so reading
|
||||
## from it before writing to it is undefined behaviour!
|
||||
## The allocated memory belongs to its allocating thread!
|
||||
## Use `allocShared` to allocate from a shared heap.
|
||||
proc alloc0*(size: int): pointer {.noconv, rtl.}
|
||||
proc alloc0*(size: int): pointer {.noconv, rtl, tags: [].}
|
||||
## allocates a new memory block with at least ``size`` bytes. The
|
||||
## block has to be freed with ``realloc(block, 0)`` or
|
||||
## ``dealloc(block)``. The block is initialized with all bytes
|
||||
## containing zero, so it is somewhat safer than ``alloc``.
|
||||
## The allocated memory belongs to its allocating thread!
|
||||
## Use `allocShared0` to allocate from a shared heap.
|
||||
proc realloc*(p: Pointer, newsize: int): pointer {.noconv, rtl.}
|
||||
proc realloc*(p: Pointer, newsize: int): pointer {.noconv, rtl, tags: [].}
|
||||
## grows or shrinks a given memory block. If p is **nil** then a new
|
||||
## memory block is returned. In either way the block has at least
|
||||
## ``newsize`` bytes. If ``newsize == 0`` and p is not **nil**
|
||||
@@ -1102,7 +1102,7 @@ proc realloc*(p: Pointer, newsize: int): pointer {.noconv, rtl.}
|
||||
## be freed with ``dealloc``.
|
||||
## The allocated memory belongs to its allocating thread!
|
||||
## Use `reallocShared` to reallocate from a shared heap.
|
||||
proc dealloc*(p: Pointer) {.noconv, rtl.}
|
||||
proc dealloc*(p: Pointer) {.noconv, rtl, tags: [].}
|
||||
## frees the memory allocated with ``alloc``, ``alloc0`` or
|
||||
## ``realloc``. This procedure is dangerous! If one forgets to
|
||||
## free the memory a leak occurs; if one tries to access freed
|
||||
@@ -1618,7 +1618,7 @@ var
|
||||
## do when setting this. If ``localRaiseHook`` returns false, the exception
|
||||
## is caught and does not propagate further through the call stack.
|
||||
|
||||
outOfMemHook*: proc () {.nimcall.}
|
||||
outOfMemHook*: proc () {.nimcall, tags: [].}
|
||||
## set this variable to provide a procedure that should be called
|
||||
## in case of an `out of memory`:idx: event. The standard handler
|
||||
## writes an error message and terminates the program. `outOfMemHook` can
|
||||
@@ -1668,7 +1668,7 @@ else:
|
||||
|
||||
proc add*(x: var cstring, y: cstring) {.magic: "AppendStrStr".}
|
||||
|
||||
proc echo*[T](x: varargs[T, `$`]) {.magic: "Echo".}
|
||||
proc echo*[T](x: varargs[T, `$`]) {.magic: "Echo", tags: [FWriteIO].}
|
||||
## special built-in that takes a variable number of arguments. Each argument
|
||||
## is converted to a string via ``$``, so it works for user-defined
|
||||
## types that have an overloaded ``$`` operator.
|
||||
@@ -1677,7 +1677,8 @@ proc echo*[T](x: varargs[T, `$`]) {.magic: "Echo".}
|
||||
## Unlike other IO operations this is guaranteed to be thread-safe as
|
||||
## ``echo`` is very often used for debugging convenience.
|
||||
|
||||
proc debugEcho*[T](x: varargs[T, `$`]) {.magic: "Echo", noSideEffect.}
|
||||
proc debugEcho*[T](x: varargs[T, `$`]) {.magic: "Echo", noSideEffect,
|
||||
tags: [], raises: [].}
|
||||
## Same as ``echo``, but as a special semantic rule, ``debugEcho`` pretends
|
||||
## to be free of side effects, so that it can be used for debugging routines
|
||||
## marked as ``noSideEffect``.
|
||||
@@ -1780,14 +1781,14 @@ when not defined(EcmaScript) and not defined(NimrodVM):
|
||||
## to ``stderr``.
|
||||
|
||||
proc Open*(f: var TFile, filename: string,
|
||||
mode: TFileMode = fmRead, bufSize: int = -1): Bool
|
||||
mode: TFileMode = fmRead, bufSize: int = -1): Bool {.tags: [].}
|
||||
## Opens a file named `filename` with given `mode`.
|
||||
##
|
||||
## Default mode is readonly. Returns true iff the file could be opened.
|
||||
## This throws no exception if the file could not be opened.
|
||||
|
||||
proc Open*(f: var TFile, filehandle: TFileHandle,
|
||||
mode: TFileMode = fmRead): Bool
|
||||
mode: TFileMode = fmRead): Bool {.tags: [].}
|
||||
## Creates a ``TFile`` from a `filehandle` with given `mode`.
|
||||
##
|
||||
## Default mode is readonly. Returns true iff the file could be opened.
|
||||
@@ -1801,56 +1802,57 @@ when not defined(EcmaScript) and not defined(NimrodVM):
|
||||
if not open(result, filename, mode, bufSize):
|
||||
raise newException(EIO, "cannot open: " & filename)
|
||||
|
||||
proc reopen*(f: TFile, filename: string, mode: TFileMode = fmRead): bool
|
||||
proc reopen*(f: TFile, filename: string, mode: TFileMode = fmRead): bool {.
|
||||
tags: [].}
|
||||
## reopens the file `f` with given `filename` and `mode`. This
|
||||
## is often used to redirect the `stdin`, `stdout` or `stderr`
|
||||
## file variables.
|
||||
##
|
||||
## Default mode is readonly. Returns true iff the file could be reopened.
|
||||
|
||||
proc Close*(f: TFile) {.importc: "fclose", nodecl.}
|
||||
proc Close*(f: TFile) {.importc: "fclose", nodecl, tags: [].}
|
||||
## Closes the file.
|
||||
|
||||
proc EndOfFile*(f: TFile): Bool
|
||||
proc EndOfFile*(f: TFile): Bool {.tags: [].}
|
||||
## Returns true iff `f` is at the end.
|
||||
|
||||
proc readChar*(f: TFile): char {.importc: "fgetc", nodecl.}
|
||||
proc readChar*(f: TFile): char {.importc: "fgetc", nodecl, tags: [FReadIO].}
|
||||
## Reads a single character from the stream `f`. If the stream
|
||||
## has no more characters, `EEndOfFile` is raised.
|
||||
proc FlushFile*(f: TFile) {.importc: "fflush", noDecl.}
|
||||
proc FlushFile*(f: TFile) {.importc: "fflush", noDecl, tags: [FWriteIO].}
|
||||
## Flushes `f`'s buffer.
|
||||
|
||||
proc readAll*(file: TFile): TaintedString
|
||||
proc readAll*(file: TFile): TaintedString {.tags: [FReadIO].}
|
||||
## Reads all data from the stream `file`. Raises an IO exception
|
||||
## in case of an error
|
||||
|
||||
proc readFile*(filename: string): TaintedString
|
||||
proc readFile*(filename: string): TaintedString {.tags: [FReadIO].}
|
||||
## Opens a file named `filename` for reading. Then calls `readAll`
|
||||
## and closes the file afterwards. Returns the string.
|
||||
## Raises an IO exception in case of an error.
|
||||
|
||||
proc writeFile*(filename, content: string)
|
||||
proc writeFile*(filename, content: string) {.tags: [FWriteIO].}
|
||||
## Opens a file named `filename` for writing. Then writes the
|
||||
## `content` completely to the file and closes the file afterwards.
|
||||
## Raises an IO exception in case of an error.
|
||||
|
||||
proc write*(f: TFile, r: float)
|
||||
proc write*(f: TFile, i: int)
|
||||
proc write*(f: TFile, i: biggestInt)
|
||||
proc write*(f: TFile, r: biggestFloat)
|
||||
proc write*(f: TFile, s: string)
|
||||
proc write*(f: TFile, b: Bool)
|
||||
proc write*(f: TFile, c: char)
|
||||
proc write*(f: TFile, c: cstring)
|
||||
proc write*(f: TFile, a: varargs[string, `$`])
|
||||
proc write*(f: TFile, r: float) {.tags: [FWriteIO].}
|
||||
proc write*(f: TFile, i: int) {.tags: [FWriteIO].}
|
||||
proc write*(f: TFile, i: biggestInt) {.tags: [FWriteIO].}
|
||||
proc write*(f: TFile, r: biggestFloat) {.tags: [FWriteIO].}
|
||||
proc write*(f: TFile, s: string) {.tags: [FWriteIO].}
|
||||
proc write*(f: TFile, b: Bool) {.tags: [FWriteIO].}
|
||||
proc write*(f: TFile, c: char) {.tags: [FWriteIO].}
|
||||
proc write*(f: TFile, c: cstring) {.tags: [FWriteIO].}
|
||||
proc write*(f: TFile, a: varargs[string, `$`]) {.tags: [FWriteIO].}
|
||||
## Writes a value to the file `f`. May throw an IO exception.
|
||||
|
||||
proc readLine*(f: TFile): TaintedString
|
||||
proc readLine*(f: TFile): TaintedString {.tags: [FReadIO].}
|
||||
## reads a line of text from the file `f`. May throw an IO exception.
|
||||
## A line of text may be delimited by ``CR``, ``LF`` or
|
||||
## ``CRLF``. The newline character(s) are not part of the returned string.
|
||||
|
||||
proc readLine*(f: TFile, line: var TaintedString): bool
|
||||
proc readLine*(f: TFile, line: var TaintedString): bool {.tags: [FReadIO].}
|
||||
## reads a line of text from the file `f` into `line`. `line` must not be
|
||||
## ``nil``! May throw an IO exception.
|
||||
## A line of text may be delimited by ``CR``, ``LF`` or
|
||||
@@ -1858,39 +1860,44 @@ when not defined(EcmaScript) and not defined(NimrodVM):
|
||||
## Returns ``false`` if the end of the file has been reached, ``true``
|
||||
## otherwise. If ``false`` is returned `line` contains no new data.
|
||||
|
||||
proc writeln*[Ty](f: TFile, x: varargs[Ty, `$`]) {.inline.}
|
||||
proc writeln*[Ty](f: TFile, x: varargs[Ty, `$`]) {.inline, tags: [FWriteIO].}
|
||||
## writes the values `x` to `f` and then writes "\n".
|
||||
## May throw an IO exception.
|
||||
|
||||
proc getFileSize*(f: TFile): int64
|
||||
proc getFileSize*(f: TFile): int64 {.tags: [FReadIO].}
|
||||
## retrieves the file size (in bytes) of `f`.
|
||||
|
||||
proc ReadBytes*(f: TFile, a: var openarray[int8], start, len: int): int
|
||||
proc ReadBytes*(f: TFile, a: var openarray[int8], start, len: int): int {.
|
||||
tags: [FReadIO].}
|
||||
## reads `len` bytes into the buffer `a` starting at ``a[start]``. Returns
|
||||
## the actual number of bytes that have been read which may be less than
|
||||
## `len` (if not as many bytes are remaining), but not greater.
|
||||
|
||||
proc ReadChars*(f: TFile, a: var openarray[char], start, len: int): int
|
||||
proc ReadChars*(f: TFile, a: var openarray[char], start, len: int): int {.
|
||||
tags: [FReadIO].}
|
||||
## reads `len` bytes into the buffer `a` starting at ``a[start]``. Returns
|
||||
## the actual number of bytes that have been read which may be less than
|
||||
## `len` (if not as many bytes are remaining), but not greater.
|
||||
|
||||
proc readBuffer*(f: TFile, buffer: pointer, len: int): int
|
||||
proc readBuffer*(f: TFile, buffer: pointer, len: int): int {.tags: [FReadIO].}
|
||||
## reads `len` bytes into the buffer pointed to by `buffer`. Returns
|
||||
## the actual number of bytes that have been read which may be less than
|
||||
## `len` (if not as many bytes are remaining), but not greater.
|
||||
|
||||
proc writeBytes*(f: TFile, a: openarray[int8], start, len: int): int
|
||||
proc writeBytes*(f: TFile, a: openarray[int8], start, len: int): int {.
|
||||
tags: [FWriteIO].}
|
||||
## writes the bytes of ``a[start..start+len-1]`` to the file `f`. Returns
|
||||
## the number of actual written bytes, which may be less than `len` in case
|
||||
## of an error.
|
||||
|
||||
proc writeChars*(f: tFile, a: openarray[char], start, len: int): int
|
||||
proc writeChars*(f: tFile, a: openarray[char], start, len: int): int {.
|
||||
tags: [FWriteIO].}
|
||||
## writes the bytes of ``a[start..start+len-1]`` to the file `f`. Returns
|
||||
## the number of actual written bytes, which may be less than `len` in case
|
||||
## of an error.
|
||||
|
||||
proc writeBuffer*(f: TFile, buffer: pointer, len: int): int
|
||||
proc writeBuffer*(f: TFile, buffer: pointer, len: int): int {.
|
||||
tags: [FWriteIO].}
|
||||
## writes the bytes of buffer pointed to by the parameter `buffer` to the
|
||||
## file `f`. Returns the number of actual written bytes, which may be less
|
||||
## than `len` in case of an error.
|
||||
@@ -1971,7 +1978,7 @@ when not defined(EcmaScript) and not defined(NimrodVM):
|
||||
## allows you to override the behaviour of your application when CTRL+C
|
||||
## is pressed. Only one such hook is supported.
|
||||
|
||||
proc writeStackTrace*()
|
||||
proc writeStackTrace*() {.tags: [FWriteIO].}
|
||||
## writes the current stack trace to ``stderr``. This is only works
|
||||
## for debug builds.
|
||||
when hostOS != "standalone":
|
||||
@@ -2029,7 +2036,7 @@ when not defined(EcmaScript) and not defined(NimrodVM):
|
||||
when hasThreadSupport:
|
||||
include "system/channels"
|
||||
|
||||
iterator lines*(filename: string): TaintedString =
|
||||
iterator lines*(filename: string): TaintedString {.tags: [FReadIO].} =
|
||||
## Iterate over any line in the file named `filename`.
|
||||
## If the file does not exist `EIO` is raised.
|
||||
var f = open(filename)
|
||||
@@ -2037,7 +2044,7 @@ when not defined(EcmaScript) and not defined(NimrodVM):
|
||||
while f.readLine(res): yield res
|
||||
close(f)
|
||||
|
||||
iterator lines*(f: TFile): TaintedString =
|
||||
iterator lines*(f: TFile): TaintedString {.tags: [FReadIO].} =
|
||||
## Iterate over any line in the file `f`.
|
||||
var res = TaintedString(newStringOfCap(80))
|
||||
while f.readLine(res): yield TaintedString(res)
|
||||
@@ -2308,18 +2315,25 @@ proc InstantiationInfo*(index = -1): tuple[filename: string, line: int] {.
|
||||
|
||||
proc raiseAssert*(msg: string) {.noinline.} =
|
||||
raise newException(EAssertionFailed, msg)
|
||||
|
||||
|
||||
when true:
|
||||
proc hiddenRaiseAssert(msg: string) {.raises: [].} =
|
||||
# trick the compiler to not list ``EAssertionFailed`` when called
|
||||
# by ``assert``.
|
||||
type THide = proc (msg: string) {.noinline, raises: [], noSideEffect.}
|
||||
THide(raiseAssert)(msg)
|
||||
|
||||
template assert*(cond: bool, msg = "") =
|
||||
## provides a means to implement `programming by contracts`:idx: in Nimrod.
|
||||
## ``assert`` evaluates expression ``cond`` and if ``cond`` is false, it
|
||||
## raises an ``EAssertionFailure`` exception. However, the compiler may
|
||||
## not generate any code at all for ``assert`` if it is advised to do so.
|
||||
## Use ``assert`` for debugging purposes only.
|
||||
bind InstantiationInfo
|
||||
bind InstantiationInfo, hiddenRaiseAssert
|
||||
when compileOption("assertions"):
|
||||
{.line.}:
|
||||
if not cond:
|
||||
raiseAssert(astToStr(cond) & ' ' & msg)
|
||||
hiddenRaiseAssert(astToStr(cond) & ' ' & msg)
|
||||
|
||||
template doAssert*(cond: bool, msg = "") =
|
||||
## same as `assert` but is always turned on and not affected by the
|
||||
|
||||
@@ -16,23 +16,25 @@
|
||||
# of the standard library!
|
||||
|
||||
|
||||
proc fputs(c: cstring, f: TFile) {.importc: "fputs", noDecl.}
|
||||
proc fgets(c: cstring, n: int, f: TFile): cstring {.importc: "fgets", noDecl.}
|
||||
proc fgetc(stream: TFile): cint {.importc: "fgetc", nodecl.}
|
||||
proc ungetc(c: cint, f: TFile) {.importc: "ungetc", nodecl.}
|
||||
proc putc(c: Char, stream: TFile) {.importc: "putc", nodecl.}
|
||||
proc fprintf(f: TFile, frmt: CString) {.importc: "fprintf", nodecl, varargs.}
|
||||
proc strlen(c: cstring): int {.importc: "strlen", nodecl.}
|
||||
proc fputs(c: cstring, f: TFile) {.importc: "fputs", noDecl, tags: [FWriteIO].}
|
||||
proc fgets(c: cstring, n: int, f: TFile): cstring {.importc: "fgets", noDecl,
|
||||
tags: [FReadIO].}
|
||||
proc fgetc(stream: TFile): cint {.importc: "fgetc", nodecl, tags: [FReadIO].}
|
||||
proc ungetc(c: cint, f: TFile) {.importc: "ungetc", nodecl, tags: [].}
|
||||
proc putc(c: Char, stream: TFile) {.importc: "putc", nodecl, tags: [FWriteIO].}
|
||||
proc fprintf(f: TFile, frmt: CString) {.importc: "fprintf", nodecl, varargs,
|
||||
tags: [FWriteIO].}
|
||||
proc strlen(c: cstring): int {.importc: "strlen", nodecl, tags: [].}
|
||||
|
||||
|
||||
# C routine that is used here:
|
||||
proc fread(buf: Pointer, size, n: int, f: TFile): int {.
|
||||
importc: "fread", noDecl.}
|
||||
importc: "fread", noDecl, tags: [FReadIO].}
|
||||
proc fseek(f: TFile, offset: clong, whence: int): int {.
|
||||
importc: "fseek", noDecl.}
|
||||
proc ftell(f: TFile): int {.importc: "ftell", noDecl.}
|
||||
importc: "fseek", noDecl, tags: [].}
|
||||
proc ftell(f: TFile): int {.importc: "ftell", noDecl, tags: [].}
|
||||
proc setvbuf(stream: TFile, buf: pointer, typ, size: cint): cint {.
|
||||
importc, nodecl.}
|
||||
importc, nodecl, tags: [].}
|
||||
|
||||
proc write(f: TFile, c: cstring) = fputs(c, f)
|
||||
|
||||
|
||||
2
todo.txt
2
todo.txt
@@ -1,7 +1,9 @@
|
||||
version 0.9.2
|
||||
=============
|
||||
|
||||
- make the stdlib aware of tags: sockets, os
|
||||
- test&finish first class iterators:
|
||||
* allow return in first class iterators
|
||||
* nested iterators
|
||||
* arglist as a type?
|
||||
* tyIterator?
|
||||
|
||||
@@ -19,13 +19,6 @@ between threads, so no "stop the world" mechanism is necessary. An unsafe
|
||||
shared memory heap is also provided for the increased efficiency that results
|
||||
from that model.
|
||||
|
||||
..
|
||||
Don't give me that marketing crap. What is Nimrod?
|
||||
--------------------------------------------------
|
||||
|
||||
Nimrod = Mutable value based datatypes + static binding + sugar to make
|
||||
this programming modell as convenient as possible
|
||||
|
||||
|
||||
Why is it named Nimrod?
|
||||
-----------------------
|
||||
@@ -57,10 +50,11 @@ memory heap.
|
||||
How is Nimrod licensed?
|
||||
-----------------------
|
||||
|
||||
The Nimrod compiler is GPL licensed, the runtime library is LGPL licensed.
|
||||
The Nimrod compiler is GPL licensed, the runtime library is LGPL licensed
|
||||
with a special exception that allows for static linking.
|
||||
This means that you can use any license for your own programs developed with
|
||||
Nimrod. If I receive enough requests with good arguments, I may change the
|
||||
license of Nimrod to the BSD license.
|
||||
Nimrod.
|
||||
|
||||
|
||||
How stable is Nimrod?
|
||||
---------------------
|
||||
|
||||
Reference in New Issue
Block a user