'assert' hides EAssertionFailsure; stdlib makes use of 'tags'

This commit is contained in:
Araq
2012-11-18 01:36:20 +01:00
parent 7f6633a06f
commit 3c0a6a8962
16 changed files with 242 additions and 161 deletions

View File

@@ -59,6 +59,7 @@ proc InitDefines*() =
DefineSymbol("nimhygiene")
DefineSymbol("niminheritable")
DefineSymbol("nimmixin")
DefineSymbol("nimeffects")
# add platform specific symbols:
case targetCPU

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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/")

View File

@@ -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)

View File

@@ -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.

View File

@@ -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

View File

@@ -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)

View File

@@ -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?

View File

@@ -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?
---------------------