diff options
-rwxr-xr-x | compiler/condsyms.nim | 1 | ||||
-rw-r--r-- | compiler/sempass2.nim | 12 | ||||
-rwxr-xr-x | compiler/types.nim | 11 | ||||
-rw-r--r-- | lib/core/locks.nim | 13 | ||||
-rw-r--r-- | lib/impure/db_mongo.nim | 38 | ||||
-rwxr-xr-x | lib/impure/db_mysql.nim | 46 | ||||
-rwxr-xr-x | lib/impure/db_postgres.nim | 38 | ||||
-rwxr-xr-x | lib/impure/db_sqlite.nim | 42 | ||||
-rwxr-xr-x | lib/impure/rdstdin.nim | 12 | ||||
-rwxr-xr-x | lib/impure/web.nim | 2 | ||||
-rwxr-xr-x | lib/pure/osproc.nim | 35 | ||||
-rwxr-xr-x | lib/pure/streams.nim | 15 | ||||
-rwxr-xr-x | lib/system.nim | 98 | ||||
-rwxr-xr-x | lib/system/sysio.nim | 24 | ||||
-rwxr-xr-x | todo.txt | 2 | ||||
-rwxr-xr-x | web/question.txt | 14 |
16 files changed, 242 insertions, 161 deletions
diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index f8f3c9dee..234029ea9 100755 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -59,6 +59,7 @@ proc InitDefines*() = DefineSymbol("nimhygiene") DefineSymbol("niminheritable") DefineSymbol("nimmixin") + DefineSymbol("nimeffects") # add platform specific symbols: case targetCPU diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 59ae26385..c3e2ce8bc 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -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 diff --git a/compiler/types.nim b/compiler/types.nim index c994e0dd8..afc05d773 100755 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -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 diff --git a/lib/core/locks.nim b/lib/core/locks.nim index 083cc18d7..071bde93a 100644 --- a/lib/core/locks.nim +++ b/lib/core/locks.nim @@ -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) diff --git a/lib/impure/db_mongo.nim b/lib/impure/db_mongo.nim index 5195e2a09..b7fb325f9 100644 --- a/lib/impure/db_mongo.nim +++ b/lib/impure/db_mongo.nim @@ -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) diff --git a/lib/impure/db_mysql.nim b/lib/impure/db_mysql.nim index 6284b7995..118ed39bb 100755 --- a/lib/impure/db_mysql.nim +++ b/lib/impure/db_mysql.nim @@ -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) diff --git a/lib/impure/db_postgres.nim b/lib/impure/db_postgres.nim index cb8da75b8..2e2b09bc2 100755 --- a/lib/impure/db_postgres.nim +++ b/lib/impure/db_postgres.nim @@ -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) diff --git a/lib/impure/db_sqlite.nim b/lib/impure/db_sqlite.nim index 4abc2ca97..e9864c599 100755 --- a/lib/impure/db_sqlite.nim +++ b/lib/impure/db_sqlite.nim @@ -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 diff --git a/lib/impure/rdstdin.nim b/lib/impure/rdstdin.nim index cd614fa98..cf076e929 100755 --- a/lib/impure/rdstdin.nim +++ b/lib/impure/rdstdin.nim @@ -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) diff --git a/lib/impure/web.nim b/lib/impure/web.nim index a0aea8421..417fe9746 100755 --- a/lib/impure/web.nim +++ b/lib/impure/web.nim @@ -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/") diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index b9dee638d..46b35cb10 100755 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -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) diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim index 4cd48af31..5db21d76a 100755 --- a/lib/pure/streams.nim +++ b/lib/pure/streams.nim @@ -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. diff --git a/lib/system.nim b/lib/system.nim index 6d7720aa7..d4c7aaf5e 100755 --- a/lib/system.nim +++ b/lib/system.nim @@ -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 diff --git a/lib/system/sysio.nim b/lib/system/sysio.nim index 02a5893d3..9055cca40 100755 --- a/lib/system/sysio.nim +++ b/lib/system/sysio.nim @@ -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) diff --git a/todo.txt b/todo.txt index e17993331..412696681 100755 --- a/todo.txt +++ b/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? diff --git a/web/question.txt b/web/question.txt index 46cbb4f88..8e301b6bd 100755 --- a/web/question.txt +++ b/web/question.txt @@ -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? --------------------- |