New module `annotation` that factorize some common code on users of annotations.
Pull-Request: #596
Reviewed-by: Alexis Laferrière <alexis.laf@xymus.net>
Reviewed-by: Alexandre Terrasa <alexandre@moz-code.org>
# limitations under the License.
# Low-level Sqlite3 features
-module sqlite3 is pkgconfig("sqlite3")
+module native_sqlite3 is pkgconfig("sqlite3")
in "C header" `{
#include <sqlite3.h>
`}
+redef class Sys
+ # Last error raised when calling `Sqlite3::open`
+ var sqlite_open_error: nullable Sqlite3Code = null
+end
+
extern class Sqlite3Code `{int`}
new ok `{ return SQLITE_OK; `} # 0 /* Successful result */
fun is_ok: Bool `{ return recv == SQLITE_OK; `}
end
# A prepared statement
-extern class Statement `{sqlite3_stmt*`}
+extern class NativeStatement `{sqlite3_stmt*`}
# Evaluate the statement
fun step: Sqlite3Code `{
return NativeString_to_s(ret);
`}
+ # Number of bytes in the blob or string at row `i`
fun column_bytes(i: Int) : Int `{
return sqlite3_column_bytes(recv, i);
`}
return sqlite3_column_int(recv, i);
`}
- fun column_text(i: Int) : String import NativeString.to_s `{
- char * ret = (char *) sqlite3_column_text(recv, i);
- if( ret == NULL ){
- ret = "";
- }
- return NativeString_to_s(ret);
+ fun column_text(i: Int): NativeString `{
+ return (char *)sqlite3_column_text(recv, i);
`}
- fun column_type(i: Int) : Int `{
+ # Type of the entry at row `i`
+ fun column_type(i: Int): DataType `{
return sqlite3_column_type(recv, i);
`}
end
# A database connection
-extern class Sqlite3 `{sqlite3 *`}
+extern class NativeSqlite3 `{sqlite3 *`}
# Open a connection to a database in UTF-8
- new open(filename: String) import String.to_cstring `{
+ new open(filename: String) import String.to_cstring, set_sys_sqlite_open_error `{
sqlite3 *self = NULL;
- sqlite3_open(String_to_cstring(filename), &self);
+ int err = sqlite3_open(String_to_cstring(filename), &self);
+ NativeSqlite3_set_sys_sqlite_open_error(self, (void*)(long)err);
+ // The previous cast is a hack, using non pointers in extern classes is not
+ // yet in the spec of the FFI.
return self;
`}
+ # Utility method to set `Sys.sqlite_open_error`
+ private fun set_sys_sqlite_open_error(err: Sqlite3Code) do sys.sqlite_open_error = err
+
# Has this DB been correctly opened?
#
# To know if it has been closed or interrupted, you must check for errors with `error`.
`}
# Prepare a SQL statement
- fun prepare(sql: String): nullable Statement import String.to_cstring, Statement.as nullable `{
+ fun prepare(sql: String): nullable NativeStatement import String.to_cstring, NativeStatement.as nullable `{
sqlite3_stmt *stmt;
int res = sqlite3_prepare_v2(recv, String_to_cstring(sql), -1, &stmt, 0);
if (res == SQLITE_OK)
- return Statement_as_nullable(stmt);
+ return NativeStatement_as_nullable(stmt);
else
- return null_Statement();
+ return null_NativeStatement();
`}
fun last_insert_rowid: Int `{
return sqlite3_errcode(recv);
`}
end
+
+# Sqlite data types
+extern class DataType `{ int `}
+ fun is_integer: Bool `{ return recv == SQLITE_INTEGER; `}
+ fun is_float: Bool `{ return recv == SQLITE_FLOAT; `}
+ fun is_blob: Bool `{ return recv == SQLITE_BLOB; `}
+ fun is_null: Bool `{ return recv == SQLITE_NULL; `}
+ fun is_text: Bool `{ return recv == SQLITE_TEXT; `}
+
+ fun to_i: Int `{ return recv; `}
+end
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 201 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Services to manipulate a Sqlite3 database
+#
+# For more information, refer to the documentation of http://www.sqlite.org/docs.html
+module sqlite3
+
+private import native_sqlite3
+import standard
+
+# A connection to a Sqlite3 database
+class Sqlite3DB
+ private var native_connection: NativeSqlite3
+
+ # Is this connection to the DB open?
+ var is_open = false
+
+ # All `Statement` opened from this connection that must be closed with this connection
+ private var open_statements = new Array[Statement]
+
+ # Open a connection to the database file at `path`
+ init open(path: Text)
+ do
+ native_connection = new NativeSqlite3.open(path.to_s)
+ if native_connection.is_valid then is_open = true
+ end
+
+ # Close this connection to the DB and all open statements
+ fun close
+ do
+ is_open = false
+
+ # close open statements
+ for stmt in open_statements do if stmt.is_open then
+ stmt.close
+ end
+
+ native_connection.close
+ end
+
+ # Prepare and return a `Statement`, return `null` on error
+ fun prepare(sql: Text): nullable Statement
+ do
+ var native_stmt = native_connection.prepare(sql.to_s)
+ if native_stmt == null then return null
+
+ var stmt = new Statement(native_stmt)
+ open_statements.add stmt
+ return stmt
+ end
+
+ # Execute the `sql` statement and return `true` on success
+ fun execute(sql: Text): Bool
+ do
+ var err = native_connection.exec(sql.to_s)
+ return err.is_ok
+ end
+
+ # Create a table on the DB with a statement beginning with "CREATE TABLE ", followed by `rest`
+ #
+ # This method does not escape special characters.
+ fun create_table(rest: Text): Bool do return execute("CREATE TABLE " + rest)
+
+ # Insert in the DB with a statement beginning with "INSERT ", followed by `rest`
+ #
+ # This method does not escape special characters.
+ fun insert(rest: Text): Bool do return execute("INSERT " + rest)
+
+ # Replace in the DB with a statement beginning with "REPLACE", followed by `rest`
+ #
+ # This method does not escape special characters.
+ fun replace(rest: Text): Bool do return execute("REPLACE " + rest)
+
+ # Select from the DB with a statement beginning with "SELECT ", followed by `rest`
+ #
+ # This method does not escape special characters.
+ fun select(rest: Text): nullable Statement do return prepare("SELECT " + rest)
+
+ # TODO add more prefix here as needed
+
+ # The latest error message, or `null` if there is none
+ fun error: nullable String
+ do
+ if not native_connection.is_valid then
+ var err = sys.sqlite_open_error
+ if err == null then return null
+ return err.to_s
+ end
+
+ var err = native_connection.error
+ if err.is_ok then return null
+ return err.to_s
+ end
+end
+
+# A prepared Sqlite3 statement, created from `Sqlite3DB::prepare` or `Sqlite3DB::select`
+class Statement
+ private var native_statement: NativeStatement
+
+ private init(ns: NativeStatement) do self.native_statement = ns
+
+ # Is this statement usable?
+ var is_open = true
+
+ # Close and finalize this statement
+ fun close
+ do
+ is_open = false
+ native_statement.finalize
+ end
+
+ # Reset this statement and return a `StatementIterator` to iterate over the result
+ fun iterator: StatementIterator
+ do
+ native_statement.reset
+ native_statement.step
+ return new StatementIterator(self)
+ end
+end
+
+class StatementRow
+ # Statement linked to `self`
+ var statement: Statement
+
+ private init(s: Statement) do self.statement = s
+
+ # Number of entries in this row
+ #
+ # require: `self.statement.is_open`
+ fun length: Int
+ do
+ assert statement_closed: statement.is_open
+
+ return statement.native_statement.column_count
+ end
+
+ # Returns the `i`th entry on this row
+ fun [](i: Int): StatementEntry do return new StatementEntry(statement, i)
+end
+
+# An entry on a `StatementRow`
+class StatementEntry
+ # Statement linked to `self`
+ var statement: Statement
+
+ private var index: Int
+
+ private init(s: Statement, i: Int)
+ do
+ self.statement = s
+ self.index = i
+ end
+
+ # Name of the column
+ #
+ # require: `self.statement.is_open`
+ fun name: String is cached do
+ assert statement_closed: statement.is_open
+
+ return statement.native_statement.column_name(index)
+ end
+
+ # Get the value of this entry according to its Sqlite type
+ #
+ # require: `self.statement.is_open`
+ fun value: nullable Sqlite3Data
+ do
+ assert statement_closed: statement.is_open
+
+ var data_type = statement.native_statement.column_type(index)
+ if data_type.is_integer then return to_i
+ if data_type.is_float then return to_f
+ if data_type.is_blob then return to_blob
+ if data_type.is_null then return null
+ if data_type.is_text then return to_s
+ abort
+ end
+
+ # Get this entry as `Int`
+ #
+ # If the Sqlite type of this entry is not an integer, it will be `CAST` to
+ # integer. If `null`, returns 0.
+ #
+ # require: `self.statement.is_open`
+ fun to_i: Int
+ do
+ assert statement_closed: statement.is_open
+
+ return statement.native_statement.column_int(index)
+ end
+
+ # Get this entry as `Float`
+ #
+ # If the Sqlite type of this entry is not a floating point, it will be `CAST`
+ # to float. If `null`, returns 0.0.
+ #
+ # require: `self.statement.is_open`
+ fun to_f: Float
+ do
+ assert statement_closed: statement.is_open
+
+ return statement.native_statement.column_double(index)
+ end
+
+ # Get this entry as `String`
+ #
+ # If the Sqlite type of this entry is not text, it will be `CAST` to text.
+ # If null, returns an empty string.
+ #
+ # require: `self.statement.is_open`
+ redef fun to_s
+ do
+ assert statement_closed: statement.is_open
+
+ var native_string = statement.native_statement.column_text(index)
+ if native_string.address_is_null then return ""
+ return native_string.to_s
+ end
+
+ # Get this entry as `Blob`
+ #
+ # If the Sqlite type of this entry is not a blob, it will be `CAST` to text.
+ # If null, returns a NULL pointer.
+ #
+ # require: `self.statement.is_open`
+ fun to_blob: Blob
+ do
+ assert statement_closed: statement.is_open
+
+ # By spec, we must get the pointer before the byte count
+ var pointer = statement.native_statement.column_blob(index)
+ var length = statement.native_statement.column_bytes(index)
+
+ return new Blob(pointer, length)
+ end
+end
+
+# Iterator over the rows of a statement result
+class StatementIterator
+ super Iterator[StatementRow]
+
+ # Statement linked to `self`
+ var statement: Statement
+
+ private init(s: Statement)
+ do
+ self.statement = s
+ self.item = new StatementRow(s)
+ end
+
+ redef var item: StatementRow
+
+ redef var is_ok = true
+
+ # require: `self.statement.is_open`
+ redef fun next
+ do
+ assert statement_closed: statement.is_open
+
+ var err = statement.native_statement.step
+ if err.is_row then
+ is_ok = true
+ else if err.is_done then
+ # Clean complete
+ is_ok = false
+ else
+ # error
+ # FIXME do something with the error?
+ is_ok = false
+ end
+ end
+end
+
+# A data type supported by Sqlite3
+interface Sqlite3Data end
+
+redef universal Int super Sqlite3Data end
+redef universal Float super Sqlite3Data end
+redef class String super Sqlite3Data end
+
+# A Sqlite3 blob
+class Blob
+ super Sqlite3Data
+
+ private init(pointer: Pointer, length: Int)
+ do
+ self.pointer = pointer
+ self.length = length
+ end
+
+ var pointer: Pointer
+ var length: Int
+end
var manifest_application_lines = new Array[String]
# Minimum API level required for the application to run
- var min_sdk: nullable Int = null
+ var min_api: nullable Int = null
# Build target API level
- var target_sdk: nullable Int = null
+ var target_api: nullable Int = null
# Maximum API level on which the application will be allowed to run
- var max_sdk: nullable Int = null
+ var max_api: nullable Int = null
redef fun to_s do return """
name: {{{name or else "null"}}}
annot = priority_annotation_on_modules("java_package", mmodule)
if annot != null then project.java_package = annot.arg_as_string(self)
- var annots = collect_annotations_on_modules("min_sdk_version", mmodule)
- for an in annots do project.min_sdk = an.arg_as_int(self)
+ var annots = collect_annotations_on_modules("min_api_version", mmodule)
+ for an in annots do project.min_api = an.arg_as_int(self)
- annots = collect_annotations_on_modules("max_sdk_version", mmodule)
- for an in annots do project.max_sdk = an.arg_as_int(self)
+ annots = collect_annotations_on_modules("max_api_version", mmodule)
+ for an in annots do project.max_api = an.arg_as_int(self)
- annots = collect_annotations_on_modules("target_sdk_version", mmodule)
- for an in annots do project.target_sdk = an.arg_as_int(self)
+ annots = collect_annotations_on_modules("target_api_version", mmodule)
+ for an in annots do project.target_api = an.arg_as_int(self)
annots = collect_annotations_on_modules("android_manifest", mmodule)
for an in annots do project.manifest_lines.add an.arg_as_string(self) or else ""
var app_version = project.version
if app_version == null then app_version = "1.0"
- var app_min_sdk = project.min_sdk
- if app_min_sdk == null then app_min_sdk = 10
+ var app_min_api = project.min_api
+ if app_min_api == null then app_min_api = 10
- var app_target_sdk = project.target_sdk
- if app_target_sdk == null then app_target_sdk = app_min_sdk
+ var app_target_api = project.target_api
+ if app_target_api == null then app_target_api = app_min_api
- var app_max_sdk = ""
- if project.max_sdk != null then app_max_sdk = "android:maxSdkVersion=\"{app_max_sdk}\""
+ var app_max_api = ""
+ if project.max_api != null then app_max_api = "android:maxSdkVersion=\"{project.max_api.as(not null)}\""
# Clear the previous android project, so there is no "existing project warning"
# or conflict between Java files of different projects
var args = ["android", "-s",
"create", "project",
"--name", short_project_name,
- "--target", "android-{app_target_sdk}",
+ "--target", "android-{app_target_api}",
"--path", android_project_root,
"--package", app_package,
"--activity", short_project_name]
<!-- This is the platform API where NativeActivity was introduced. -->
<uses-sdk
- android:minSdkVersion="{{{app_min_sdk}}}"
- android:targetSdkVersion="{{{app_target_sdk}}}"
- {{{app_max_sdk}}} />
+ android:minSdkVersion="{{{app_min_api}}}"
+ android:targetSdkVersion="{{{app_target_api}}}"
+ {{{app_max_api}}} />
<application
android:label="@string/app_name"
--- /dev/null
+The meta model of Nit programs
+
+These modules define the entities of the Nit meta-model like modules, classes, types and properties
+They also provide an API to build and query models.
+All model classes starts with the `M` letter (`MModule`, `MClass`, etc.)
+
+The model is used by tools that need a higher view than a AST (see `parser`).
+The model represents What the programmer means.
+
+Because of the specification of the Nit language, the model is complex and sometime difficult to understand.
+
+## POSet
+
+There is a lot of classes and relation in the model.
+Most of there relations are based on posets (from the lib `poset`.)
+
+Posets are *partially-ordered sets*; they are used to modelize hierarchies of things (eg. hierarchies of modules)
+
+The poset is an expressive data structure that generalizes most services about hierarchies.
+This avoid the duplication of code and the over-specialization of services.
+The drawback is that a specific request on the model use an abstract vocabulary.
+
+Example. you want the set of modules directly imported by another module.
+There is no specific method in `MModule` for that, the good way is to use services on the some posets
+
+~~~
+var res = mymodule.in_importation.direct_greaters
+~~~
+
+posets are used in two dual ways :
+
+ - by the whole hierarchy, most are in the `Model` and are named `something_somerelation_hierarchy` (eg. `Model::mmodule_importation_hierarchy`).
+ - by the view on en entity in a specific hierarchy, they are in the `MEntity` subclasses and are name `in_somerelation`. (eg. `MModule::in_importation`).
+
+
+## Refinement
+
+The refinement is the cause of the biggest difficulty with the model since the relations between entities are relative to the module considered.
+
+"How many method in this class?" -- It depends, what modules are you considering?
+"What is called by `x.foo` when `x` is dynamically a `Bar`?" -- It depends, in which main module?
+
+This relativity cause most services on model entities to have an additional parameter for the module considered.
# See the License for the specific language governing permissions and
# limitations under the License.
-# Object model of the Nit language
+# Classes, types and properties
#
-# This module define the entities of the Nit meta-model like modules,
-# classes, types and properties
-#
-# It also provide an API to build and query models.
-#
-# All model classes starts with the M letter (`MModule`, `MClass`, etc.)
-#
-# TODO: better doc
+# All three concepts are defined in this same module because these are strongly connected:
+# * types are based on classes
+# * classes contains properties
+# * some properties are types (virtual types)
#
# TODO: liearization, extern stuff
# FIXME: better handling of the types
#
# This characteristic helps the reasoning about classes in a program since a
# single `MClass` object always denote the same class.
-# However, because a `MClass` is global, it does not really have properties nor
-# belong to a hierarchy since the property and the
-# hierarchy of a class depends of a module.
+#
+# The drawback is that classes (`MClass`) contain almost nothing by themselves.
+# These do not really have properties nor belong to a hierarchy since the property and the
+# hierarchy of a class depends of the refinement in the modules.
+#
+# Most services on classes require the precision of a module, and no one can asks what are
+# the super-classes of a class nor what are properties of a class without precising what is
+# the module considered.
+#
+# For instance, during the typing of a source-file, the module considered is the module of the file.
+# eg. the question *is the method `foo` exists in the class `Bar`?* must be reformulated into
+# *is the method `foo` exists in the class `Bar` in the current module?*
+#
+# During some global analysis, the module considered may be the main module of the program.
class MClass
super MEntity
#
# A `MClassDef` is associated with an explicit (or almost) definition of a
# class. Unlike `MClass`, a `MClassDef` is a local definition that belong to
-# a specific module
+# a specific class and a specific module, and contains declarations like super-classes
+# or properties.
+#
+# It is the class definitions that are the backbone of most things in the model:
+# ClassDefs are defined with regard with other classdefs.
+# Refinement and specialization are combined to produce a big poset called the `Model::mclassdef_hierarchy`.
+#
+# Moreover, the extension and the intention of types is defined by looking at the MClassDefs.
class MClassDef
super MEntity
# Replace formals generic types in self with resolved values in `mtype`
# If `cleanup_virtual` is true, then virtual types are also replaced
- # with their bounds
+ # with their bounds.
#
# This function returns self if `need_anchor` is false.
#
#
# The resolution can be done because `E` make sense for the class A (see `can_resolve_for`)
#
- # TODO: Explain the cleanup_virtual
- #
# FIXME: the parameter `cleanup_virtual` is just a bad idea, but having
# two function instead of one seems also to be a bad idea.
#
# It's mean that all refinements of a same class "share" the parameter type,
# but that a generic subclass has its on parameter types.
#
-# However, in the sense of the meta-model, the a parameter type of a class is
-# a valid types in a subclass. The "in the sense of the meta-model" is
+# However, in the sense of the meta-model, a parameter type of a class is
+# a valid type in a subclass. The "in the sense of the meta-model" is
# important because, in the Nit language, the programmer cannot refers
# directly to the parameter types of the super-classes.
#
+++ /dev/null
-This directory contains the nit parser. It is generated from a grammar for sablecc3 ( http://www.sablecc.org ).
-In order to generate nit parser, you need the alternate SableCC3 generator ( http://www.mare.ee/indrek/sablecc/ ).
-
-Contents:
-
- fact_parser.pl: Script used to factorize parser.nit
- Makefile: Update grammar and generate .nit files
- nit.sablecc3xx: Extended sablecc3 grammar (see prescc.pl)
- prescc.pl: Program to transform an extended sablecc3 to a standard one
- parser_nodes.nit: token and nodes classes hierarchy used by the parser and the lexer
- tables.nit, tables_nit.h: Interfaces to access the tables needed by the parser and the lexer
- test_parser.nit:
- xss/*.xss: alternate SableCC3 template files for the Nit language
-
-
-The following are generated but present to avoid the need of sablecc3:
-
- lexer.nit: generated lexer
- parser.nit: generated parser
- parser_prod.nit: All production with generated visit methods
- tables_nit.c: The tables needed by the parser and the lexer
- parser_abs.nit: Raw generated token and nodes classes used to maintain coherence of parser_nodes.nit
-
-
-Other temp files produced by the Makefile:
-
- .nit.sablecc3: Sablecc3 grammar after processing
- .nit.sablecc3.dump: Dump of the grammar to improve sablecc3 multiple runs
- .parser-nofact.nit: The parser generated by SableCC3 before factorization by fact_parser.pl
-
--- /dev/null
+Parser and AST for the Nit language
+
+The parser ans the AST are mostly used by all tools.
+
+The `parser` is the tool that transform source-files into abstract syntax trees (AST) (see `parser_nodes`)
+While the AST is a higher abstraction than blob of text, the AST is still limited and represents only *What the programmer says*.
+
+Classes of nodes of the AST starts with the letter `A` (for most things, eg. `AClassdef`) or `T` (for token eg. `TId`), there is no real reason except historical that might be solved with a new parser.
+
+Variable names of the AST usually starts with `n` (for node). This is also historical but some names with a `a` (to mimic the class name) remains.
+
+## SableCC
+
+Most files in this directory are generated from a grammar for sablecc3 ( http://www.sablecc.org ).
+In order to generate nit parser, you need the alternate SableCC3 generator ( http://www.mare.ee/indrek/sablecc/ ).
+
+## Contents
+
+* fact_parser.pl: Script used to factorize parser.nit
+* Makefile: Update grammar and generate .nit files
+* nit.sablecc3xx: Extended sablecc3 grammar (see prescc.pl)
+* prescc.pl: Program to transform an extended sablecc3 to a standard one
+* parser_nodes.nit: token and nodes classes hierarchy used by the parser and the lexer
+* tables.nit, tables_nit.h: Interfaces to access the tables needed by the parser and the lexer
+* xss/*.xss: alternate SableCC3 template files for the Nit language
+
+
+The following are generated but present to avoid the need of sablecc3:
+
+* lexer.nit: generated lexer
+* parser.nit: generated parser
+* parser_prod.nit: All production with generated visit methods
+* tables_nit.c: The tables needed by the parser and the lexer
+* parser_abs.nit: Raw generated token and nodes classes used to maintain coherence of parser_nodes.nit
+
+
+Other temp files produced by the Makefile:
+
+* .nit.sablecc3: Sablecc3 grammar after processing
+* .nit.sablecc3.dump: Dump of the grammar to improve sablecc3 multiple runs
+* .parser-nofact.nit: The parser generated by SableCC3 before factorization by fact_parser.pl
+
--- /dev/null
+####
+uname: Bob
+pass: zzz
+activated: 1
+perc: 77.7
+####
+uname: Guillaume
+pass: xxx
+activated: 1
+perc: 88.8
+true
+false
--- /dev/null
+Runtime error: Assert failed (alt/test_sqlite3_nity_alt1.nit:27)
+unable to open database file
--- /dev/null
+Runtime error: Assert failed (alt/test_sqlite3_nity_alt2.nit:39)
+SQL logic error or missing database
# See the License for the specific language governing permissions and
# limitations under the License.
-module test_sqlite3
+module test_sqlite3_native
-import sqlite3
+import sqlite3::native_sqlite3
var filename = "test.db"
filename.file_delete
var insert_req_2 = "INSERT INTO users VALUES('Guillaume', 'xxx', 1)"
var select_req = "SELECT * FROM users"
-var db = new Sqlite3.open(filename)
+var db = new NativeSqlite3.open(filename)
assert sqlite_open: db.error.is_ok
db.exec(create_req)
db.close
-db = new Sqlite3.open(filename)
+db = new NativeSqlite3.open(filename)
assert sqlite_reopen: db.error.is_ok
stmt = db.prepare(select_req)
assert sqlite_reselect: db.error.is_ok
assert stmt != null
stmt.step
-assert sqlite_column_0_0_reopened: stmt.column_text(0) == "Bob"
+assert sqlite_column_0_0_reopened: stmt.column_text(0).to_s == "Bob"
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2013 Guillaume Auger <jeho@resist.ca>
+# Copyright 2013-2014 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import sqlite3
+
+var path = "test_nity.db"
+#alt1#path = "/../invalid_path.db"
+if path.file_exists then path.file_delete
+
+var db = new Sqlite3DB.open(path)
+assert db.is_open else print db.error or else "no error?"
+
+assert db.create_table("IF NOT EXISTS users (uname TEXT PRIMARY KEY, pass TEXT NOT NULL, activated INTEGER, perc FLOAT)") else
+ print db.error or else "no error?"
+end
+
+assert db.insert("INTO users VALUES('Bob', 'zzz', 1, 77.7)") else
+ print db.error or else "no error?"
+end
+
+assert db.insert("INTO users VALUES('Guillaume', 'xxx', 1, 88.8)") else
+ print db.error or else "no error?"
+end
+
+#alt2#assert db.insert("INTO notable VALUES('Alexis', 'asdf', 2, 99.9)") else
+#alt2# print db.error or else "no error?"
+#alt2#end
+
+for row in db.select("* FROM users") do
+ print "####"
+
+ printn "{row[0].name}: "
+ print row[0]
+
+ printn "{row[1].name}: "
+ var val = row[1].value
+ assert val isa Text
+ print val.to_s
+
+ printn "{row[2].name}: "
+ print row[2].to_i
+
+ printn "{row[3].name}: "
+ print row[3].to_f
+end
+
+print db.is_open
+db.close
+print db.is_open