Compare commits
25 commits
Author | SHA1 | Date | |
---|---|---|---|
Jeremy Penner | 3691299adf | ||
Jeremy Penner | 6984389fbf | ||
Jeremy Penner | 6df7ec8c33 | ||
Jeremy Penner | 5914e83437 | ||
Jeremy Penner | 64312e57fa | ||
Jeremy Penner | 9f2da61b88 | ||
Jeremy Penner | 1673c7322e | ||
Jeremy Penner | f199a9238d | ||
Jeremy Penner | c0ae50fba0 | ||
Jeremy Penner | 0914090ed4 | ||
Jeremy Penner | 35d9fc10e7 | ||
Jeremy Penner | f33b0cd695 | ||
Jeremy Penner | 926d808a92 | ||
Jeremy Penner | 078e667c44 | ||
Jeremy Penner | 0f31f8ea4c | ||
Jeremy Penner | fd3fcbd978 | ||
Jeremy Penner | 837ee0299a | ||
Jeremy Penner | 0cdcd865c5 | ||
Jeremy Penner | ea5da24813 | ||
Jeremy Penner | 82d04e0649 | ||
Jeremy Penner | f0156c576a | ||
Jeremy Penner | 6d46d0f638 | ||
Jeremy Penner | e1e0e168b6 | ||
Jeremy Penner | 881943ed17 | ||
Jeremy Penner | c25cb5d292 |
280
diet-sqlite/codes.lua
Normal file
280
diet-sqlite/codes.lua
Normal file
|
@ -0,0 +1,280 @@
|
||||||
|
-- works with luajit ffi or https://github.com/q66/cffi-lua
|
||||||
|
local ffi = require "diet-sqlite.ffi"
|
||||||
|
|
||||||
|
return {
|
||||||
|
SQLITE_TRANSIENT = ffi.cast('void(*)(void*)', -1);
|
||||||
|
SQLITE_STATIC = ffi.cast('void(*)(void*)', 0);
|
||||||
|
|
||||||
|
log_callback_type = 'void(*)(void*,int,const char*)';
|
||||||
|
|
||||||
|
-- error codes
|
||||||
|
SQLITE_EMPTY = 16;
|
||||||
|
SQLITE_CANTOPEN = 14;
|
||||||
|
SQLITE_FULL = 13;
|
||||||
|
SQLITE_BUSY = 5;
|
||||||
|
SQLITE_FORMAT = 24;
|
||||||
|
SQLITE_READONLY = 8;
|
||||||
|
SQLITE_LOCKED = 6;
|
||||||
|
SQLITE_INTERNAL = 2;
|
||||||
|
SQLITE_ROW = 100;
|
||||||
|
SQLITE_PERM = 3;
|
||||||
|
SQLITE_CORRUPT = 11;
|
||||||
|
SQLITE_ABORT = 4;
|
||||||
|
SQLITE_SCHEMA = 17;
|
||||||
|
SQLITE_DONE = 101;
|
||||||
|
SQLITE_MISMATCH = 20;
|
||||||
|
SQLITE_OK = 0;
|
||||||
|
SQLITE_TOOBIG = 18;
|
||||||
|
SQLITE_AUTH = 23;
|
||||||
|
SQLITE_NOLFS = 22;
|
||||||
|
SQLITE_PROTOCOL = 15;
|
||||||
|
SQLITE_IOERR = 10;
|
||||||
|
SQLITE_CONSTRAINT = 19;
|
||||||
|
SQLITE_NOTADB = 26;
|
||||||
|
SQLITE_MISUSE = 21;
|
||||||
|
SQLITE_RANGE = 25;
|
||||||
|
SQLITE_NOMEM = 7;
|
||||||
|
SQLITE_ERROR = 1;
|
||||||
|
SQLITE_INTERRUPT = 9;
|
||||||
|
SQLITE_NOTFOUND = 12;
|
||||||
|
-- Codes
|
||||||
|
SQLITE_IOERR_BLOCKED = 2826;
|
||||||
|
SQLITE_LIMIT_EXPR_DEPTH = 3;
|
||||||
|
SQLITE_IOERR_SHMMAP = 5386;
|
||||||
|
SQLITE_ACCESS_READ = 2;
|
||||||
|
SQLITE_IOCAP_SAFE_APPEND = 512;
|
||||||
|
SQLITE_READONLY_CANTLOCK = 520;
|
||||||
|
SQLITE_CREATE_INDEX = 1;
|
||||||
|
SQLITE_STATUS_SCRATCH_SIZE = 8;
|
||||||
|
SQLITE_OPEN_MAIN_JOURNAL = 2048;
|
||||||
|
SQLITE_CREATE_VIEW = 8;
|
||||||
|
SQLITE_SET_LOCKPROXYFILE = 3;
|
||||||
|
SQLITE_CANTOPEN_NOTEMPDIR = 270;
|
||||||
|
SQLITE_NULL = 5;
|
||||||
|
SQLITE_IOCAP_ATOMIC1K = 4;
|
||||||
|
SQLITE_STATUS_PAGECACHE_OVERFLOW = 2;
|
||||||
|
SQLITE_SHM_SHARED = 4;
|
||||||
|
SQLITE_ANALYZE = 28;
|
||||||
|
SQLITE_FCNTL_WIN32_AV_RETRY = 9;
|
||||||
|
SQLITE_LOCKED_SHAREDCACHE = 262;
|
||||||
|
SQLITE_OPEN_TRANSIENT_DB = 1024;
|
||||||
|
SQLITE_TESTCTRL_ALWAYS = 13;
|
||||||
|
SQLITE_DBCONFIG_LOOKASIDE = 1001;
|
||||||
|
SQLITE_CREATE_TEMP_TRIGGER = 5;
|
||||||
|
SQLITE_IOERR_TRUNCATE = 1546;
|
||||||
|
SQLITE_IOERR_DELETE = 2570;
|
||||||
|
SQLITE_GET_LOCKPROXYFILE = 2;
|
||||||
|
SQLITE_FCNTL_SIZE_HINT = 5;
|
||||||
|
SQLITE_STATUS_SCRATCH_OVERFLOW = 4;
|
||||||
|
SQLITE_OPEN_MAIN_DB = 256;
|
||||||
|
SQLITE_STATUS_PARSER_STACK = 6;
|
||||||
|
SQLITE_OPEN_PRIVATECACHE = 262144;
|
||||||
|
SQLITE_FUNCTION = 31;
|
||||||
|
SQLITE_LIMIT_LIKE_PATTERN_LENGTH = 8;
|
||||||
|
SQLITE_IOERR_SEEK = 5642;
|
||||||
|
SQLITE_FCNTL_CHUNK_SIZE = 6;
|
||||||
|
SQLITE_DROP_TEMP_VIEW = 15;
|
||||||
|
SQLITE_DBSTATUS_LOOKASIDE_USED = 0;
|
||||||
|
SQLITE_IOCAP_POWERSAFE_OVERWRITE = 4096;
|
||||||
|
SQLITE_CONFIG_URI = 17;
|
||||||
|
SQLITE_IOERR_READ = 266;
|
||||||
|
SQLITE_ABORT = 4;
|
||||||
|
SQLITE_DBSTATUS_MAX = 9;
|
||||||
|
SQLITE_OPEN_FULLMUTEX = 65536;
|
||||||
|
SQLITE_IOCAP_ATOMIC64K = 256;
|
||||||
|
SQLITE_CREATE_TEMP_VIEW = 6;
|
||||||
|
SQLITE_ATTACH = 24;
|
||||||
|
SQLITE_CREATE_TRIGGER = 7;
|
||||||
|
SQLITE_MUTEX_FAST = 0;
|
||||||
|
SQLITE_REPLACE = 5;
|
||||||
|
SQLITE_TESTCTRL_PENDING_BYTE = 11;
|
||||||
|
SQLITE_DELETE = 9;
|
||||||
|
SQLITE_IOERR_DIR_FSYNC = 1290;
|
||||||
|
SQLITE_FCNTL_PRAGMA = 14;
|
||||||
|
SQLITE_FAIL = 3;
|
||||||
|
SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE = 5;
|
||||||
|
SQLITE_STMTSTATUS_SORT = 2;
|
||||||
|
SQLITE_IOERR_LOCK = 3850;
|
||||||
|
SQLITE_VTAB_CONSTRAINT_SUPPORT = 1;
|
||||||
|
SQLITE_CHECKPOINT_RESTART = 2;
|
||||||
|
SQLITE_OPEN_READONLY = 1;
|
||||||
|
SQLITE_CONFIG_MUTEX = 10;
|
||||||
|
SQLITE_IOCAP_ATOMIC512 = 2;
|
||||||
|
SQLITE_FCNTL_VFSNAME = 12;
|
||||||
|
SQLITE_DBSTATUS_CACHE_HIT = 7;
|
||||||
|
SQLITE_IOERR_NOMEM = 3082;
|
||||||
|
SQLITE_CHECKPOINT_PASSIVE = 0;
|
||||||
|
SQLITE_IOERR_CHECKRESERVEDLOCK = 3594;
|
||||||
|
SQLITE_IOERR_RDLOCK = 2314;
|
||||||
|
SQLITE_STMTSTATUS_AUTOINDEX = 3;
|
||||||
|
SQLITE_ROLLBACK = 1;
|
||||||
|
SQLITE_UTF16BE = 3;
|
||||||
|
SQLITE_INDEX_CONSTRAINT_GE = 32;
|
||||||
|
SQLITE_IOERR_SHMOPEN = 4618;
|
||||||
|
SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN = 2048;
|
||||||
|
SQLITE_DBSTATUS_CACHE_WRITE = 9;
|
||||||
|
SQLITE_IOCAP_ATOMIC4K = 16;
|
||||||
|
SQLITE_LIMIT_VARIABLE_NUMBER = 9;
|
||||||
|
SQLITE_DBSTATUS_CACHE_MISS = 8;
|
||||||
|
SQLITE_CHECKPOINT_FULL = 1;
|
||||||
|
SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL = 6;
|
||||||
|
SQLITE_DBSTATUS_LOOKASIDE_HIT = 4;
|
||||||
|
SQLITE_STATUS_PAGECACHE_SIZE = 7;
|
||||||
|
SQLITE_SYNC_NORMAL = 2;
|
||||||
|
SQLITE_DBSTATUS_SCHEMA_USED = 2;
|
||||||
|
SQLITE_CONFIG_SINGLETHREAD = 1;
|
||||||
|
SQLITE_LIMIT_SQL_LENGTH = 1;
|
||||||
|
SQLITE_FCNTL_FILE_POINTER = 7;
|
||||||
|
SQLITE_CREATE_TEMP_INDEX = 3;
|
||||||
|
SQLITE_READ = 20;
|
||||||
|
SQLITE_STATUS_MALLOC_COUNT = 9;
|
||||||
|
SQLITE_DBSTATUS_STMT_USED = 3;
|
||||||
|
SQLITE_SHM_EXCLUSIVE = 8;
|
||||||
|
SQLITE_INDEX_CONSTRAINT_LE = 8;
|
||||||
|
SQLITE_BUSY_RECOVERY = 261;
|
||||||
|
SQLITE_DROP_VIEW = 17;
|
||||||
|
SQLITE_TESTCTRL_PRNG_SAVE = 5;
|
||||||
|
SQLITE_OPEN_MASTER_JOURNAL = 16384;
|
||||||
|
SQLITE_PRAGMA = 19;
|
||||||
|
SQLITE_CONFIG_MALLOC = 4;
|
||||||
|
SQLITE_UTF16 = 4;
|
||||||
|
SQLITE_STATUS_MEMORY_USED = 0;
|
||||||
|
SQLITE_TESTCTRL_LAST = 19;
|
||||||
|
SQLITE_CONFIG_SERIALIZED = 3;
|
||||||
|
SQLITE_FCNTL_LOCKSTATE = 1;
|
||||||
|
SQLITE_OPEN_TEMP_DB = 512;
|
||||||
|
SQLITE_TESTCTRL_EXPLAIN_STMT = 19;
|
||||||
|
SQLITE_MUTEX_STATIC_PMEM = 7;
|
||||||
|
SQLITE_OPEN_SUBJOURNAL = 8192;
|
||||||
|
SQLITE_TESTCTRL_BITVEC_TEST = 8;
|
||||||
|
SQLITE_DROP_TRIGGER = 16;
|
||||||
|
SQLITE_ALTER_TABLE = 26;
|
||||||
|
SQLITE_IOERR_UNLOCK = 2058;
|
||||||
|
SQLITE_CONFIG_GETMALLOC = 5;
|
||||||
|
SQLITE_SHM_LOCK = 2;
|
||||||
|
SQLITE_DROP_TEMP_TABLE = 13;
|
||||||
|
SQLITE_FLOAT = 2;
|
||||||
|
SQLITE_OPEN_CREATE = 4;
|
||||||
|
SQLITE_TESTCTRL_RESERVE = 14;
|
||||||
|
SQLITE_TESTCTRL_ASSERT = 12;
|
||||||
|
SQLITE_FCNTL_OVERWRITE = 11;
|
||||||
|
SQLITE_STATUS_MALLOC_SIZE = 5;
|
||||||
|
SQLITE_OPEN_SHAREDCACHE = 131072;
|
||||||
|
SQLITE_OPEN_URI = 64;
|
||||||
|
SQLITE_TESTCTRL_SCRATCHMALLOC = 17;
|
||||||
|
SQLITE_DROP_TABLE = 11;
|
||||||
|
SQLITE_TESTCTRL_PRNG_RESET = 7;
|
||||||
|
SQLITE_CREATE_TEMP_TABLE = 4;
|
||||||
|
SQLITE_TESTCTRL_PRNG_RESTORE = 6;
|
||||||
|
SQLITE_STATUS_SCRATCH_USED = 3;
|
||||||
|
SQLITE_LIMIT_TRIGGER_DEPTH = 10;
|
||||||
|
SQLITE_MUTEX_STATIC_LRU2 = 7;
|
||||||
|
SQLITE_TESTCTRL_FIRST = 5;
|
||||||
|
SQLITE_OPEN_AUTOPROXY = 32;
|
||||||
|
SQLITE_SYNC_FULL = 3;
|
||||||
|
SQLITE_TESTCTRL_LOCALTIME_FAULT = 18;
|
||||||
|
SQLITE_TESTCTRL_FAULT_INSTALL = 9;
|
||||||
|
SQLITE_MUTEX_STATIC_PRNG = 5;
|
||||||
|
SQLITE_MUTEX_STATIC_OPEN = 4;
|
||||||
|
SQLITE_MUTEX_STATIC_MEM2 = 4;
|
||||||
|
SQLITE_TRANSACTION = 22;
|
||||||
|
SQLITE_MUTEX_STATIC_MEM = 3;
|
||||||
|
SQLITE_IOERR_DIR_CLOSE = 4362;
|
||||||
|
SQLITE_MUTEX_STATIC_MASTER = 2;
|
||||||
|
SQLITE_MUTEX_RECURSIVE = 1;
|
||||||
|
SQLITE_CREATE_VTABLE = 29;
|
||||||
|
SQLITE_CONFIG_SCRATCH = 6;
|
||||||
|
SQLITE_STMTSTATUS_FULLSCAN_STEP = 1;
|
||||||
|
SQLITE_INDEX_CONSTRAINT_LT = 16;
|
||||||
|
SQLITE_MUTEX_STATIC_LRU = 6;
|
||||||
|
SQLITE_DBCONFIG_ENABLE_TRIGGER = 1003;
|
||||||
|
SQLITE_CONFIG_MEMSTATUS = 9;
|
||||||
|
SQLITE_BLOB = 4;
|
||||||
|
SQLITE_UTF16_ALIGNED = 8;
|
||||||
|
SQLITE_DETACH = 25;
|
||||||
|
SQLITE_STATUS_PAGECACHE_USED = 1;
|
||||||
|
SQLITE_FCNTL_PERSIST_WAL = 10;
|
||||||
|
SQLITE_IOERR_SHORT_READ = 522;
|
||||||
|
SQLITE_UTF16LE = 2;
|
||||||
|
SQLITE_UTF8 = 1;
|
||||||
|
SQLITE_TEXT = 3;
|
||||||
|
SQLITE_INDEX_CONSTRAINT_EQ = 2;
|
||||||
|
SQLITE_IOERR_SHMSIZE = 4874;
|
||||||
|
SQLITE_INSERT = 18;
|
||||||
|
SQLITE_LOCK_RESERVED = 2;
|
||||||
|
SQLITE_LIMIT_ATTACHED = 7;
|
||||||
|
SQLITE_LIMIT_COLUMN = 2;
|
||||||
|
SQLITE_IOERR_FSTAT = 1802;
|
||||||
|
SQLITE_CONFIG_LOOKASIDE = 13;
|
||||||
|
SQLITE_CONFIG_GETPCACHE2 = 19;
|
||||||
|
SQLITE_READONLY_RECOVERY = 264;
|
||||||
|
SQLITE_CONFIG_GETMUTEX = 11;
|
||||||
|
SQLITE_IOCAP_ATOMIC16K = 64;
|
||||||
|
SQLITE_LIMIT_FUNCTION_ARG = 6;
|
||||||
|
SQLITE_REINDEX = 27;
|
||||||
|
SQLITE_OPEN_READWRITE = 2;
|
||||||
|
SQLITE_COPY = 0;
|
||||||
|
SQLITE_SAVEPOINT = 32;
|
||||||
|
SQLITE_DROP_VTABLE = 30;
|
||||||
|
SQLITE_IOERR_CLOSE = 4106;
|
||||||
|
SQLITE_INDEX_CONSTRAINT_MATCH = 64;
|
||||||
|
SQLITE_ANY = 5;
|
||||||
|
SQLITE_UPDATE = 23;
|
||||||
|
SQLITE_SELECT = 21;
|
||||||
|
SQLITE_DROP_INDEX = 10;
|
||||||
|
SQLITE_INDEX_CONSTRAINT_GT = 4;
|
||||||
|
SQLITE_IOCAP_SEQUENTIAL = 1024;
|
||||||
|
SQLITE_CONFIG_PAGECACHE = 7;
|
||||||
|
SQLITE_TESTCTRL_OPTIMIZATIONS = 15;
|
||||||
|
SQLITE_IOERR_ACCESS = 3338;
|
||||||
|
SQLITE_IOCAP_ATOMIC = 1;
|
||||||
|
SQLITE_TESTCTRL_ISKEYWORD = 16;
|
||||||
|
SQLITE_IOCAP_ATOMIC32K = 128;
|
||||||
|
SQLITE_DROP_TEMP_TRIGGER = 14;
|
||||||
|
SQLITE_OPEN_WAL = 524288;
|
||||||
|
SQLITE_OPEN_EXCLUSIVE = 16;
|
||||||
|
SQLITE_CORRUPT_VTAB = 267;
|
||||||
|
SQLITE_OPEN_TEMP_JOURNAL = 4096;
|
||||||
|
SQLITE_CANTOPEN_ISDIR = 526;
|
||||||
|
SQLITE_OPEN_DELETEONCLOSE = 8;
|
||||||
|
SQLITE_CONFIG_HEAP = 8;
|
||||||
|
SQLITE_INTEGER = 1;
|
||||||
|
SQLITE_ACCESS_EXISTS = 0;
|
||||||
|
SQLITE_FCNTL_POWERSAFE_OVERWRITE = 13;
|
||||||
|
SQLITE_LIMIT_VDBE_OP = 5;
|
||||||
|
SQLITE_IOERR_FSYNC = 1034;
|
||||||
|
SQLITE_LOCK_SHARED = 1;
|
||||||
|
SQLITE_IOCAP_ATOMIC2K = 8;
|
||||||
|
SQLITE_LOCK_EXCLUSIVE = 4;
|
||||||
|
SQLITE_DBSTATUS_CACHE_USED = 1;
|
||||||
|
SQLITE_LOCK_PENDING = 3;
|
||||||
|
SQLITE_SYNC_DATAONLY = 16;
|
||||||
|
SQLITE_DENY = 1;
|
||||||
|
SQLITE_DROP_TEMP_INDEX = 12;
|
||||||
|
SQLITE_LAST_ERRNO = 4;
|
||||||
|
SQLITE_FCNTL_SYNC_OMITTED = 8;
|
||||||
|
SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS = 10;
|
||||||
|
SQLITE_CONFIG_LOG = 16;
|
||||||
|
SQLITE_OPEN_NOMUTEX = 32768;
|
||||||
|
SQLITE_DBCONFIG_ENABLE_FKEY = 1002;
|
||||||
|
SQLITE_IGNORE = 2;
|
||||||
|
SQLITE_IOERR_WRITE = 778;
|
||||||
|
SQLITE_IOERR_SHMLOCK = 5130;
|
||||||
|
SQLITE_CONFIG_MULTITHREAD = 2;
|
||||||
|
SQLITE_ABORT_ROLLBACK = 516;
|
||||||
|
SQLITE_SHM_NLOCK = 8;
|
||||||
|
SQLITE_CONFIG_PCACHE = 14;
|
||||||
|
SQLITE_LIMIT_COMPOUND_SELECT = 4;
|
||||||
|
SQLITE_ACCESS_READWRITE = 1;
|
||||||
|
SQLITE_CONFIG_PCACHE2 = 18;
|
||||||
|
SQLITE_IOCAP_ATOMIC8K = 32;
|
||||||
|
SQLITE_LOCK_NONE = 0;
|
||||||
|
SQLITE_CREATE_TABLE = 2;
|
||||||
|
SQLITE_LIMIT_LENGTH = 0;
|
||||||
|
SQLITE_CONFIG_GETPCACHE = 15;
|
||||||
|
SQLITE_SHM_UNLOCK = 1;
|
||||||
|
SQLITE_PREPARE_PERSISTENT = 0x01;
|
||||||
|
SQLITE_PREPARE_NORMALIZE = 0x02;
|
||||||
|
SQLITE_PREPARE_NO_VTAB = 0x04;
|
||||||
|
}
|
||||||
|
|
5
diet-sqlite/ffi.lua
Normal file
5
diet-sqlite/ffi.lua
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
-- works with luajit ffi or https://github.com/q66/cffi-lua
|
||||||
|
local ok, ffi = pcall(require, "ffi")
|
||||||
|
if not ok then ffi = require "cffi" end
|
||||||
|
|
||||||
|
return ffi
|
67
diet-sqlite/init.fnl
Normal file
67
diet-sqlite/init.fnl
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
; This small library is based on https://github.com/Wiladams/LJIT2SQLite
|
||||||
|
; It is meant to be an _extremely_ thin wrapper around the SQLite API.
|
||||||
|
|
||||||
|
(local sqlffi (require :diet-sqlite.sqlite3_ffi))
|
||||||
|
(local ffi (require :diet-sqlite.ffi))
|
||||||
|
(local util (require :lib.util))
|
||||||
|
(local {: SQLITE_OK : SQLITE_INTEGER : SQLITE_FLOAT : SQLITE_NULL : SQLITE_BLOB : SQLITE_TEXT : SQLITE_TRANSIENT
|
||||||
|
: SQLITE_CONFIG_LOG : log_callback_type} (require :diet-sqlite.codes))
|
||||||
|
|
||||||
|
(local sql {:step sqlffi.sqlite3_step
|
||||||
|
:reset sqlffi.sqlite3_reset
|
||||||
|
:shutdown sqlffi.sqlite3_shutdown})
|
||||||
|
|
||||||
|
(fn sql.open [dbname]
|
||||||
|
(let [lpdb (ffi.new "sqlite3*[1]")
|
||||||
|
rc (sqlffi.sqlite3_open dbname lpdb)
|
||||||
|
db (. lpdb 0)]
|
||||||
|
; (when (not= db nil) (ffi.gc db sqlffi.sqlite3_close_v2))
|
||||||
|
(values rc (. lpdb 0))))
|
||||||
|
|
||||||
|
(fn sql.close [db]
|
||||||
|
(ffi.gc db nil)
|
||||||
|
(sqlffi.sqlite3_close_v2 db))
|
||||||
|
|
||||||
|
(fn sql.prepare [db sql ?flags]
|
||||||
|
(let [ppStmt (ffi.new "sqlite3_stmt *[1]")
|
||||||
|
rc (sqlffi.sqlite3_prepare_v3 db sql -1 (or ?flags 0) ppStmt nil)
|
||||||
|
stmt (. ppStmt 0)]
|
||||||
|
(when (not= stmt nil) (ffi.gc stmt sqlffi.sqlite3_finalize))
|
||||||
|
(values rc stmt)))
|
||||||
|
|
||||||
|
; this exists just to have a unique object identity you can shove into a table
|
||||||
|
(set sql.null {})
|
||||||
|
|
||||||
|
(fn sql.bind [stmt i ?val]
|
||||||
|
(match (type i)
|
||||||
|
:table (each [ival val (ipairs i)] (sql.assert (sql.bind stmt ival val)))
|
||||||
|
:number
|
||||||
|
(match (type ?val)
|
||||||
|
:string (sqlffi.sqlite3_bind_text stmt i ?val (length ?val) SQLITE_TRANSIENT)
|
||||||
|
:number (sqlffi.sqlite3_bind_double stmt i ?val)
|
||||||
|
:nil (sqlffi.sqlite3_bind_null stmt i)
|
||||||
|
(where :table (= ?val sql.null)) (sqlffi.sqlite3_bind_null stmt i)
|
||||||
|
_ (error (.. "Don't know how to bind value " (fv ?val))))))
|
||||||
|
|
||||||
|
(fn sql.column [stmt icol]
|
||||||
|
(let [i (- icol 1)] ; column indexes are 0-based
|
||||||
|
(match (sqlffi.sqlite3_column_type stmt i)
|
||||||
|
SQLITE_INTEGER (sqlffi.sqlite3_column_double stmt i)
|
||||||
|
SQLITE_FLOAT (sqlffi.sqlite3_column_double stmt i)
|
||||||
|
SQLITE_NULL nil
|
||||||
|
SQLITE_TEXT (ffi.string (sqlffi.sqlite3_column_text stmt i))
|
||||||
|
SQLITE_BLOB (error "no blob support right now")
|
||||||
|
?unknown (error (.. "unrecognized type " ?unknown)))))
|
||||||
|
|
||||||
|
(fn sql.columns [stmt]
|
||||||
|
(icollect [i (util.countiter (sqlffi.sqlite3_column_count stmt))] (or (sql.column stmt i) sql.null)))
|
||||||
|
|
||||||
|
(fn sql.assert-rc [rc-expect rc ...]
|
||||||
|
(if (= rc-expect rc) ... (error (.. "Expected " rc-expect ", got " rc))))
|
||||||
|
(fn sql.assert [...] (sql.assert-rc SQLITE_OK ...))
|
||||||
|
|
||||||
|
(fn sql.register-logger [logfn]
|
||||||
|
(let [callback (ffi.cast log_callback_type (fn [pArg iErrCode zMsg] (logfn (ffi.string zMsg) iErrCode)))]
|
||||||
|
(sql.assert (sqlffi.sqlite3_config SQLITE_CONFIG_LOG callback (ffi.cast "void *" nil)))))
|
||||||
|
|
||||||
|
sql
|
866
diet-sqlite/sqlite3_ffi.lua
Normal file
866
diet-sqlite/sqlite3_ffi.lua
Normal file
|
@ -0,0 +1,866 @@
|
||||||
|
--[[
|
||||||
|
** 2001 September 15
|
||||||
|
**
|
||||||
|
** The author disclaims copyright to this source code. In place of
|
||||||
|
** a legal notice, here is a blessing:
|
||||||
|
**
|
||||||
|
** May you do good and not evil.
|
||||||
|
** May you find forgiveness for yourself and forgive others.
|
||||||
|
** May you share freely, never taking more than you give.
|
||||||
|
**
|
||||||
|
--]]
|
||||||
|
|
||||||
|
local ffi = require "diet-sqlite.ffi"
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Created from this version:
|
||||||
|
|
||||||
|
#define SQLITE_VERSION "3.7.12.1"
|
||||||
|
#define SQLITE_VERSION_NUMBER 3007012
|
||||||
|
#define SQLITE_SOURCE_ID "2012-05-22 02:45:53 6d326d44fd1d626aae0e8456e5fa2049f1ce0789"
|
||||||
|
--]]
|
||||||
|
|
||||||
|
|
||||||
|
ffi.cdef[[
|
||||||
|
|
||||||
|
const char sqlite3_version[];
|
||||||
|
const char *sqlite3_libversion(void);
|
||||||
|
const char *sqlite3_sourceid(void);
|
||||||
|
int sqlite3_libversion_number(void);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int sqlite3_threadsafe(void);
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct sqlite3 sqlite3;
|
||||||
|
|
||||||
|
|
||||||
|
typedef int64_t sqlite3_int64;
|
||||||
|
typedef uint64_t sqlite3_uint64;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int sqlite3_close(sqlite3 *);
|
||||||
|
int sqlite3_close_v2(sqlite3 *);
|
||||||
|
|
||||||
|
|
||||||
|
int sqlite3_exec(
|
||||||
|
sqlite3*, /* An open database */
|
||||||
|
const char *sql, /* SQL to be evaluated */
|
||||||
|
int (*callback)(void*,int,char**,char**), /* Callback function */
|
||||||
|
void *, /* 1st argument to callback */
|
||||||
|
char **errmsg /* Error msg written here */
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct sqlite3_file sqlite3_file;
|
||||||
|
struct sqlite3_file {
|
||||||
|
const struct sqlite3_io_methods *pMethods;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct sqlite3_io_methods sqlite3_io_methods;
|
||||||
|
struct sqlite3_io_methods {
|
||||||
|
int iVersion;
|
||||||
|
int (*xClose)(sqlite3_file*);
|
||||||
|
int (*xRead)(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
|
||||||
|
int (*xWrite)(sqlite3_file*, const void*, int iAmt, sqlite3_int64 iOfst);
|
||||||
|
int (*xTruncate)(sqlite3_file*, sqlite3_int64 size);
|
||||||
|
int (*xSync)(sqlite3_file*, int flags);
|
||||||
|
int (*xFileSize)(sqlite3_file*, sqlite3_int64 *pSize);
|
||||||
|
int (*xLock)(sqlite3_file*, int);
|
||||||
|
int (*xUnlock)(sqlite3_file*, int);
|
||||||
|
int (*xCheckReservedLock)(sqlite3_file*, int *pResOut);
|
||||||
|
int (*xFileControl)(sqlite3_file*, int op, void *pArg);
|
||||||
|
int (*xSectorSize)(sqlite3_file*);
|
||||||
|
int (*xDeviceCharacteristics)(sqlite3_file*);
|
||||||
|
int (*xShmMap)(sqlite3_file*, int iPg, int pgsz, int, void volatile**);
|
||||||
|
int (*xShmLock)(sqlite3_file*, int offset, int n, int flags);
|
||||||
|
void (*xShmBarrier)(sqlite3_file*);
|
||||||
|
int (*xShmUnmap)(sqlite3_file*, int deleteFlag);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct sqlite3_mutex sqlite3_mutex;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct sqlite3_vfs sqlite3_vfs;
|
||||||
|
typedef void (*sqlite3_syscall_ptr)(void);
|
||||||
|
struct sqlite3_vfs {
|
||||||
|
int iVersion;
|
||||||
|
int szOsFile;
|
||||||
|
int mxPathname;
|
||||||
|
sqlite3_vfs *pNext;
|
||||||
|
const char *zName;
|
||||||
|
void *pAppData;
|
||||||
|
int (*xOpen)(sqlite3_vfs*, const char *zName, sqlite3_file*,
|
||||||
|
int flags, int *pOutFlags);
|
||||||
|
int (*xDelete)(sqlite3_vfs*, const char *zName, int syncDir);
|
||||||
|
int (*xAccess)(sqlite3_vfs*, const char *zName, int flags, int *pResOut);
|
||||||
|
int (*xFullPathname)(sqlite3_vfs*, const char *zName, int nOut, char *zOut);
|
||||||
|
void *(*xDlOpen)(sqlite3_vfs*, const char *zFilename);
|
||||||
|
void (*xDlError)(sqlite3_vfs*, int nByte, char *zErrMsg);
|
||||||
|
void (*(*xDlSym)(sqlite3_vfs*,void*, const char *zSymbol))(void);
|
||||||
|
void (*xDlClose)(sqlite3_vfs*, void*);
|
||||||
|
int (*xRandomness)(sqlite3_vfs*, int nByte, char *zOut);
|
||||||
|
int (*xSleep)(sqlite3_vfs*, int microseconds);
|
||||||
|
int (*xCurrentTime)(sqlite3_vfs*, double*);
|
||||||
|
int (*xGetLastError)(sqlite3_vfs*, int, char *);
|
||||||
|
|
||||||
|
int (*xCurrentTimeInt64)(sqlite3_vfs*, sqlite3_int64*);
|
||||||
|
|
||||||
|
int (*xSetSystemCall)(sqlite3_vfs*, const char *zName, sqlite3_syscall_ptr);
|
||||||
|
sqlite3_syscall_ptr (*xGetSystemCall)(sqlite3_vfs*, const char *zName);
|
||||||
|
const char *(*xNextSystemCall)(sqlite3_vfs*, const char *zName);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int sqlite3_initialize(void);
|
||||||
|
int sqlite3_shutdown(void);
|
||||||
|
int sqlite3_os_init(void);
|
||||||
|
int sqlite3_os_end(void);
|
||||||
|
|
||||||
|
|
||||||
|
int sqlite3_config(int, ...);
|
||||||
|
|
||||||
|
|
||||||
|
int sqlite3_db_config(sqlite3*, int op, ...);
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct sqlite3_mem_methods sqlite3_mem_methods;
|
||||||
|
struct sqlite3_mem_methods {
|
||||||
|
void *(*xMalloc)(int); /* Memory allocation function */
|
||||||
|
void (*xFree)(void*); /* Free a prior allocation */
|
||||||
|
void *(*xRealloc)(void*,int); /* Resize an allocation */
|
||||||
|
int (*xSize)(void*); /* Return the size of an allocation */
|
||||||
|
int (*xRoundup)(int); /* Round up request size to allocation size */
|
||||||
|
int (*xInit)(void*); /* Initialize the memory allocator */
|
||||||
|
void (*xShutdown)(void*); /* Deinitialize the memory allocator */
|
||||||
|
void *pAppData; /* Argument to xInit() and xShutdown() */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int sqlite3_extended_result_codes(sqlite3*, int onoff);
|
||||||
|
|
||||||
|
|
||||||
|
sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*);
|
||||||
|
|
||||||
|
|
||||||
|
int sqlite3_changes(sqlite3*);
|
||||||
|
|
||||||
|
|
||||||
|
int sqlite3_total_changes(sqlite3*);
|
||||||
|
|
||||||
|
|
||||||
|
void sqlite3_interrupt(sqlite3*);
|
||||||
|
|
||||||
|
|
||||||
|
int sqlite3_complete(const char *sql);
|
||||||
|
int sqlite3_complete16(const void *sql);
|
||||||
|
|
||||||
|
|
||||||
|
int sqlite3_busy_handler(sqlite3*, int(*)(void*,int), void*);
|
||||||
|
|
||||||
|
|
||||||
|
int sqlite3_busy_timeout(sqlite3*, int ms);
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Legacy, don't want to use this in new code
|
||||||
|
int sqlite3_get_table(
|
||||||
|
sqlite3 *db, // An open database
|
||||||
|
const char *zSql, // SQL to be evaluated
|
||||||
|
char ***pazResult, // Results of the query
|
||||||
|
int *pnRow, // Number of result rows written here
|
||||||
|
int *pnColumn, // Number of result columns written here
|
||||||
|
char **pzErrmsg // Error msg written here
|
||||||
|
);
|
||||||
|
void sqlite3_free_table(char **result);
|
||||||
|
*/
|
||||||
|
|
||||||
|
char *sqlite3_mprintf(const char*,...);
|
||||||
|
char *sqlite3_vmprintf(const char*, va_list);
|
||||||
|
char *sqlite3_snprintf(int,char*,const char*, ...);
|
||||||
|
char *sqlite3_vsnprintf(int,char*,const char*, va_list);
|
||||||
|
|
||||||
|
|
||||||
|
void *sqlite3_malloc(int);
|
||||||
|
void *sqlite3_realloc(void*, int);
|
||||||
|
void sqlite3_free(void*);
|
||||||
|
|
||||||
|
|
||||||
|
sqlite3_int64 sqlite3_memory_used(void);
|
||||||
|
sqlite3_int64 sqlite3_memory_highwater(int resetFlag);
|
||||||
|
|
||||||
|
|
||||||
|
void sqlite3_randomness(int N, void *P);
|
||||||
|
|
||||||
|
|
||||||
|
int sqlite3_set_authorizer(sqlite3*,
|
||||||
|
int (*xAuth)(void*,int,const char*,const char*,const char*,const char*),
|
||||||
|
void *pUserData
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void *sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*);
|
||||||
|
void *sqlite3_profile(sqlite3*,
|
||||||
|
void(*xProfile)(void*,const char*,sqlite3_uint64), void*);
|
||||||
|
|
||||||
|
|
||||||
|
void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
|
||||||
|
|
||||||
|
int sqlite3_open(
|
||||||
|
const char *filename, /* Database filename (UTF-8) */
|
||||||
|
sqlite3 **ppDb /* OUT: SQLite db handle */
|
||||||
|
);
|
||||||
|
int sqlite3_open16(
|
||||||
|
const void *filename, /* Database filename (UTF-16) */
|
||||||
|
sqlite3 **ppDb /* OUT: SQLite db handle */
|
||||||
|
);
|
||||||
|
int sqlite3_open_v2(
|
||||||
|
const char *filename, /* Database filename (UTF-8) */
|
||||||
|
sqlite3 **ppDb, /* OUT: SQLite db handle */
|
||||||
|
int flags, /* Flags */
|
||||||
|
const char *zVfs /* Name of VFS module to use */
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam);
|
||||||
|
int sqlite3_uri_boolean(const char *zFile, const char *zParam, int bDefault);
|
||||||
|
sqlite3_int64 sqlite3_uri_int64(const char*, const char*, sqlite3_int64);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int sqlite3_errcode(sqlite3 *db);
|
||||||
|
int sqlite3_extended_errcode(sqlite3 *db);
|
||||||
|
const char *sqlite3_errmsg(sqlite3*);
|
||||||
|
const void *sqlite3_errmsg16(sqlite3*);
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct sqlite3_stmt sqlite3_stmt;
|
||||||
|
|
||||||
|
|
||||||
|
int sqlite3_limit(sqlite3*, int id, int newVal);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int sqlite3_prepare(
|
||||||
|
sqlite3 *db, /* Database handle */
|
||||||
|
const char *zSql, /* SQL statement, UTF-8 encoded */
|
||||||
|
int nByte, /* Maximum length of zSql in bytes. */
|
||||||
|
sqlite3_stmt **ppStmt, /* OUT: Statement handle */
|
||||||
|
const char **pzTail /* OUT: Pointer to unused portion of zSql */
|
||||||
|
);
|
||||||
|
int sqlite3_prepare_v2(
|
||||||
|
sqlite3 *db, /* Database handle */
|
||||||
|
const char *zSql, /* SQL statement, UTF-8 encoded */
|
||||||
|
int nByte, /* Maximum length of zSql in bytes. */
|
||||||
|
sqlite3_stmt **ppStmt, /* OUT: Statement handle */
|
||||||
|
const char **pzTail /* OUT: Pointer to unused portion of zSql */
|
||||||
|
);
|
||||||
|
int sqlite3_prepare_v3(
|
||||||
|
sqlite3 *db, /* Database handle */
|
||||||
|
const char *zSql, /* SQL statement, UTF-8 encoded */
|
||||||
|
int nByte, /* Maximum length of zSql in bytes. */
|
||||||
|
unsigned int prepFlags, /* Zero or more SQLITE_PREPARE_ flags */
|
||||||
|
sqlite3_stmt **ppStmt, /* OUT: Statement handle */
|
||||||
|
const char **pzTail /* OUT: Pointer to unused portion of zSql */
|
||||||
|
);
|
||||||
|
int sqlite3_prepare16(
|
||||||
|
sqlite3 *db, /* Database handle */
|
||||||
|
const void *zSql, /* SQL statement, UTF-16 encoded */
|
||||||
|
int nByte, /* Maximum length of zSql in bytes. */
|
||||||
|
sqlite3_stmt **ppStmt, /* OUT: Statement handle */
|
||||||
|
const void **pzTail /* OUT: Pointer to unused portion of zSql */
|
||||||
|
);
|
||||||
|
int sqlite3_prepare16_v2(
|
||||||
|
sqlite3 *db, /* Database handle */
|
||||||
|
const void *zSql, /* SQL statement, UTF-16 encoded */
|
||||||
|
int nByte, /* Maximum length of zSql in bytes. */
|
||||||
|
sqlite3_stmt **ppStmt, /* OUT: Statement handle */
|
||||||
|
const void **pzTail /* OUT: Pointer to unused portion of zSql */
|
||||||
|
);
|
||||||
|
int sqlite3_prepare16_v3(
|
||||||
|
sqlite3 *db, /* Database handle */
|
||||||
|
const void *zSql, /* SQL statement, UTF-16 encoded */
|
||||||
|
int nByte, /* Maximum length of zSql in bytes. */
|
||||||
|
unsigned int prepFlags, /* Zero or more SQLITE_PREPARE_ flags */
|
||||||
|
sqlite3_stmt **ppStmt, /* OUT: Statement handle */
|
||||||
|
const void **pzTail /* OUT: Pointer to unused portion of zSql */
|
||||||
|
);
|
||||||
|
|
||||||
|
const char *sqlite3_sql(sqlite3_stmt *pStmt);
|
||||||
|
int sqlite3_stmt_readonly(sqlite3_stmt *pStmt);
|
||||||
|
int sqlite3_stmt_busy(sqlite3_stmt*);
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct Mem sqlite3_value;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct sqlite3_context sqlite3_context;
|
||||||
|
|
||||||
|
int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*));
|
||||||
|
int sqlite3_bind_double(sqlite3_stmt*, int, double);
|
||||||
|
int sqlite3_bind_int(sqlite3_stmt*, int, int);
|
||||||
|
int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64);
|
||||||
|
int sqlite3_bind_null(sqlite3_stmt*, int);
|
||||||
|
int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int n, void(*)(void*));
|
||||||
|
int sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int, void(*)(void*));
|
||||||
|
int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*);
|
||||||
|
int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n);
|
||||||
|
int sqlite3_bind_parameter_count(sqlite3_stmt*);
|
||||||
|
const char *sqlite3_bind_parameter_name(sqlite3_stmt*, int);
|
||||||
|
int sqlite3_bind_parameter_index(sqlite3_stmt*, const char *zName);
|
||||||
|
int sqlite3_clear_bindings(sqlite3_stmt*);
|
||||||
|
int sqlite3_column_count(sqlite3_stmt *pStmt);
|
||||||
|
const char *sqlite3_column_name(sqlite3_stmt*, int N);
|
||||||
|
const void *sqlite3_column_name16(sqlite3_stmt*, int N);
|
||||||
|
const char *sqlite3_column_database_name(sqlite3_stmt*,int);
|
||||||
|
const void *sqlite3_column_database_name16(sqlite3_stmt*,int);
|
||||||
|
const char *sqlite3_column_table_name(sqlite3_stmt*,int);
|
||||||
|
const void *sqlite3_column_table_name16(sqlite3_stmt*,int);
|
||||||
|
const char *sqlite3_column_origin_name(sqlite3_stmt*,int);
|
||||||
|
const void *sqlite3_column_origin_name16(sqlite3_stmt*,int);
|
||||||
|
const char *sqlite3_column_decltype(sqlite3_stmt*,int);
|
||||||
|
const void *sqlite3_column_decltype16(sqlite3_stmt*,int);
|
||||||
|
int sqlite3_step(sqlite3_stmt*);
|
||||||
|
int sqlite3_data_count(sqlite3_stmt *pStmt);
|
||||||
|
const void *sqlite3_column_blob(sqlite3_stmt*, int iCol);
|
||||||
|
int sqlite3_column_bytes(sqlite3_stmt*, int iCol);
|
||||||
|
int sqlite3_column_bytes16(sqlite3_stmt*, int iCol);
|
||||||
|
double sqlite3_column_double(sqlite3_stmt*, int iCol);
|
||||||
|
int sqlite3_column_int(sqlite3_stmt*, int iCol);
|
||||||
|
sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol);
|
||||||
|
const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol);
|
||||||
|
const void *sqlite3_column_text16(sqlite3_stmt*, int iCol);
|
||||||
|
int sqlite3_column_type(sqlite3_stmt*, int iCol);
|
||||||
|
sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol);
|
||||||
|
int sqlite3_finalize(sqlite3_stmt *pStmt);
|
||||||
|
int sqlite3_reset(sqlite3_stmt *pStmt);
|
||||||
|
|
||||||
|
|
||||||
|
int sqlite3_create_function(
|
||||||
|
sqlite3 *db,
|
||||||
|
const char *zFunctionName,
|
||||||
|
int nArg,
|
||||||
|
int eTextRep,
|
||||||
|
void *pApp,
|
||||||
|
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
|
||||||
|
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
||||||
|
void (*xFinal)(sqlite3_context*)
|
||||||
|
);
|
||||||
|
int sqlite3_create_function16(
|
||||||
|
sqlite3 *db,
|
||||||
|
const void *zFunctionName,
|
||||||
|
int nArg,
|
||||||
|
int eTextRep,
|
||||||
|
void *pApp,
|
||||||
|
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
|
||||||
|
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
||||||
|
void (*xFinal)(sqlite3_context*)
|
||||||
|
);
|
||||||
|
int sqlite3_create_function_v2(
|
||||||
|
sqlite3 *db,
|
||||||
|
|
||||||
|
const char *zFunctionName,
|
||||||
|
int nArg,
|
||||||
|
int eTextRep,
|
||||||
|
void *pApp,
|
||||||
|
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
|
||||||
|
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
||||||
|
void (*xFinal)(sqlite3_context*),
|
||||||
|
void(*xDestroy)(void*)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const void *sqlite3_value_blob(sqlite3_value*);
|
||||||
|
int sqlite3_value_bytes(sqlite3_value*);
|
||||||
|
int sqlite3_value_bytes16(sqlite3_value*);
|
||||||
|
double sqlite3_value_double(sqlite3_value*);
|
||||||
|
int sqlite3_value_int(sqlite3_value*);
|
||||||
|
sqlite3_int64 sqlite3_value_int64(sqlite3_value*);
|
||||||
|
const unsigned char *sqlite3_value_text(sqlite3_value*);
|
||||||
|
const void *sqlite3_value_text16(sqlite3_value*);
|
||||||
|
const void *sqlite3_value_text16le(sqlite3_value*);
|
||||||
|
const void *sqlite3_value_text16be(sqlite3_value*);
|
||||||
|
int sqlite3_value_type(sqlite3_value*);
|
||||||
|
int sqlite3_value_numeric_type(sqlite3_value*);
|
||||||
|
|
||||||
|
|
||||||
|
void *sqlite3_aggregate_context(sqlite3_context*, int nBytes);
|
||||||
|
|
||||||
|
|
||||||
|
void *sqlite3_user_data(sqlite3_context*);
|
||||||
|
|
||||||
|
|
||||||
|
sqlite3 *sqlite3_context_db_handle(sqlite3_context*);
|
||||||
|
|
||||||
|
|
||||||
|
void *sqlite3_get_auxdata(sqlite3_context*, int N);
|
||||||
|
void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void sqlite3_result_blob(sqlite3_context*, const void*, int, void(*)(void*));
|
||||||
|
void sqlite3_result_double(sqlite3_context*, double);
|
||||||
|
void sqlite3_result_error(sqlite3_context*, const char*, int);
|
||||||
|
void sqlite3_result_error16(sqlite3_context*, const void*, int);
|
||||||
|
void sqlite3_result_error_toobig(sqlite3_context*);
|
||||||
|
void sqlite3_result_error_nomem(sqlite3_context*);
|
||||||
|
void sqlite3_result_error_code(sqlite3_context*, int);
|
||||||
|
void sqlite3_result_int(sqlite3_context*, int);
|
||||||
|
void sqlite3_result_int64(sqlite3_context*, sqlite3_int64);
|
||||||
|
void sqlite3_result_null(sqlite3_context*);
|
||||||
|
void sqlite3_result_text(sqlite3_context*, const char*, int, void(*)(void*));
|
||||||
|
void sqlite3_result_text16(sqlite3_context*, const void*, int, void(*)(void*));
|
||||||
|
void sqlite3_result_text16le(sqlite3_context*, const void*, int,void(*)(void*));
|
||||||
|
void sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*));
|
||||||
|
void sqlite3_result_value(sqlite3_context*, sqlite3_value*);
|
||||||
|
void sqlite3_result_zeroblob(sqlite3_context*, int n);
|
||||||
|
|
||||||
|
|
||||||
|
int sqlite3_create_collation(
|
||||||
|
sqlite3*,
|
||||||
|
const char *zName,
|
||||||
|
int eTextRep,
|
||||||
|
void *pArg,
|
||||||
|
int(*xCompare)(void*,int,const void*,int,const void*)
|
||||||
|
);
|
||||||
|
int sqlite3_create_collation_v2(
|
||||||
|
sqlite3*,
|
||||||
|
const char *zName,
|
||||||
|
int eTextRep,
|
||||||
|
void *pArg,
|
||||||
|
int(*xCompare)(void*,int,const void*,int,const void*),
|
||||||
|
void(*xDestroy)(void*)
|
||||||
|
);
|
||||||
|
int sqlite3_create_collation16(
|
||||||
|
sqlite3*,
|
||||||
|
const void *zName,
|
||||||
|
int eTextRep,
|
||||||
|
void *pArg,
|
||||||
|
int(*xCompare)(void*,int,const void*,int,const void*)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
int sqlite3_collation_needed(
|
||||||
|
sqlite3*,
|
||||||
|
void*,
|
||||||
|
void(*)(void*,sqlite3*,int eTextRep,const char*)
|
||||||
|
);
|
||||||
|
int sqlite3_collation_needed16(
|
||||||
|
sqlite3*,
|
||||||
|
void*,
|
||||||
|
void(*)(void*,sqlite3*,int eTextRep,const void*)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int sqlite3_sleep(int);
|
||||||
|
|
||||||
|
|
||||||
|
char *sqlite3_temp_directory;
|
||||||
|
|
||||||
|
|
||||||
|
int sqlite3_get_autocommit(sqlite3*);
|
||||||
|
|
||||||
|
|
||||||
|
sqlite3 *sqlite3_db_handle(sqlite3_stmt*);
|
||||||
|
|
||||||
|
|
||||||
|
const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName);
|
||||||
|
|
||||||
|
int sqlite3_db_readonly(sqlite3 *db, const char *zDbName);
|
||||||
|
|
||||||
|
|
||||||
|
sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt);
|
||||||
|
|
||||||
|
|
||||||
|
void *sqlite3_commit_hook(sqlite3*, int(*)(void*), void*);
|
||||||
|
void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*);
|
||||||
|
|
||||||
|
|
||||||
|
void *sqlite3_update_hook(
|
||||||
|
sqlite3*,
|
||||||
|
void(*)(void *,int ,char const *,char const *,sqlite3_int64),
|
||||||
|
void*
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
int sqlite3_enable_shared_cache(int);
|
||||||
|
|
||||||
|
|
||||||
|
int sqlite3_release_memory(int);
|
||||||
|
|
||||||
|
|
||||||
|
int sqlite3_db_release_memory(sqlite3*);
|
||||||
|
|
||||||
|
|
||||||
|
sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 N);
|
||||||
|
|
||||||
|
|
||||||
|
int sqlite3_table_column_metadata(
|
||||||
|
sqlite3 *db, /* Connection handle */
|
||||||
|
const char *zDbName, /* Database name or NULL */
|
||||||
|
const char *zTableName, /* Table name */
|
||||||
|
const char *zColumnName, /* Column name */
|
||||||
|
char const **pzDataType, /* OUTPUT: Declared data type */
|
||||||
|
char const **pzCollSeq, /* OUTPUT: Collation sequence name */
|
||||||
|
int *pNotNull, /* OUTPUT: True if NOT NULL constraint exists */
|
||||||
|
int *pPrimaryKey, /* OUTPUT: True if column part of PK */
|
||||||
|
int *pAutoinc /* OUTPUT: True if column is auto-increment */
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
int sqlite3_load_extension(
|
||||||
|
sqlite3 *db, /* Load the extension into this database connection */
|
||||||
|
const char *zFile, /* Name of the shared library containing extension */
|
||||||
|
const char *zProc, /* Entry point. Derived from zFile if 0 */
|
||||||
|
char **pzErrMsg /* Put error message here if not 0 */
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
int sqlite3_enable_load_extension(sqlite3 *db, int onoff);
|
||||||
|
|
||||||
|
|
||||||
|
int sqlite3_auto_extension(void (*xEntryPoint)(void));
|
||||||
|
|
||||||
|
|
||||||
|
void sqlite3_reset_auto_extension(void);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct sqlite3_vtab sqlite3_vtab;
|
||||||
|
typedef struct sqlite3_index_info sqlite3_index_info;
|
||||||
|
typedef struct sqlite3_vtab_cursor sqlite3_vtab_cursor;
|
||||||
|
typedef struct sqlite3_module sqlite3_module;
|
||||||
|
|
||||||
|
|
||||||
|
struct sqlite3_module {
|
||||||
|
int iVersion;
|
||||||
|
int (*xCreate)(sqlite3*, void *pAux,
|
||||||
|
int argc, const char *const*argv,
|
||||||
|
sqlite3_vtab **ppVTab, char**);
|
||||||
|
int (*xConnect)(sqlite3*, void *pAux,
|
||||||
|
int argc, const char *const*argv,
|
||||||
|
sqlite3_vtab **ppVTab, char**);
|
||||||
|
int (*xBestIndex)(sqlite3_vtab *pVTab, sqlite3_index_info*);
|
||||||
|
int (*xDisconnect)(sqlite3_vtab *pVTab);
|
||||||
|
int (*xDestroy)(sqlite3_vtab *pVTab);
|
||||||
|
int (*xOpen)(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor);
|
||||||
|
int (*xClose)(sqlite3_vtab_cursor*);
|
||||||
|
int (*xFilter)(sqlite3_vtab_cursor*, int idxNum, const char *idxStr,
|
||||||
|
int argc, sqlite3_value **argv);
|
||||||
|
int (*xNext)(sqlite3_vtab_cursor*);
|
||||||
|
int (*xEof)(sqlite3_vtab_cursor*);
|
||||||
|
int (*xColumn)(sqlite3_vtab_cursor*, sqlite3_context*, int);
|
||||||
|
int (*xRowid)(sqlite3_vtab_cursor*, sqlite3_int64 *pRowid);
|
||||||
|
int (*xUpdate)(sqlite3_vtab *, int, sqlite3_value **, sqlite3_int64 *);
|
||||||
|
int (*xBegin)(sqlite3_vtab *pVTab);
|
||||||
|
int (*xSync)(sqlite3_vtab *pVTab);
|
||||||
|
int (*xCommit)(sqlite3_vtab *pVTab);
|
||||||
|
int (*xRollback)(sqlite3_vtab *pVTab);
|
||||||
|
int (*xFindFunction)(sqlite3_vtab *pVtab, int nArg, const char *zName,
|
||||||
|
void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),
|
||||||
|
void **ppArg);
|
||||||
|
int (*xRename)(sqlite3_vtab *pVtab, const char *zNew);
|
||||||
|
/* The methods above are in version 1 of the sqlite_module object. Those
|
||||||
|
** below are for version 2 and greater. */
|
||||||
|
int (*xSavepoint)(sqlite3_vtab *pVTab, int);
|
||||||
|
int (*xRelease)(sqlite3_vtab *pVTab, int);
|
||||||
|
int (*xRollbackTo)(sqlite3_vtab *pVTab, int);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct sqlite3_index_info {
|
||||||
|
/* Inputs */
|
||||||
|
int nConstraint; /* Number of entries in aConstraint */
|
||||||
|
struct sqlite3_index_constraint {
|
||||||
|
int iColumn; /* Column on left-hand side of constraint */
|
||||||
|
unsigned char op; /* Constraint operator */
|
||||||
|
unsigned char usable; /* True if this constraint is usable */
|
||||||
|
int iTermOffset; /* Used internally - xBestIndex should ignore */
|
||||||
|
} *aConstraint; /* Table of WHERE clause constraints */
|
||||||
|
int nOrderBy; /* Number of terms in the ORDER BY clause */
|
||||||
|
struct sqlite3_index_orderby {
|
||||||
|
int iColumn; /* Column number */
|
||||||
|
unsigned char desc; /* True for DESC. False for ASC. */
|
||||||
|
} *aOrderBy; /* The ORDER BY clause */
|
||||||
|
/* Outputs */
|
||||||
|
struct sqlite3_index_constraint_usage {
|
||||||
|
int argvIndex; /* if >0, constraint is part of argv to xFilter */
|
||||||
|
unsigned char omit; /* Do not code a test for this constraint */
|
||||||
|
} *aConstraintUsage;
|
||||||
|
int idxNum; /* Number used to identify the index */
|
||||||
|
char *idxStr; /* String, possibly obtained from sqlite3_malloc */
|
||||||
|
int needToFreeIdxStr; /* Free idxStr using sqlite3_free() if true */
|
||||||
|
int orderByConsumed; /* True if output is already ordered */
|
||||||
|
double estimatedCost; /* Estimated cost of using this index */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int sqlite3_create_module(
|
||||||
|
sqlite3 *db, /* SQLite connection to register module with */
|
||||||
|
const char *zName, /* Name of the module */
|
||||||
|
const sqlite3_module *p, /* Methods for the module */
|
||||||
|
void *pClientData /* Client data for xCreate/xConnect */
|
||||||
|
);
|
||||||
|
int sqlite3_create_module_v2(
|
||||||
|
sqlite3 *db, /* SQLite connection to register module with */
|
||||||
|
const char *zName, /* Name of the module */
|
||||||
|
const sqlite3_module *p, /* Methods for the module */
|
||||||
|
void *pClientData, /* Client data for xCreate/xConnect */
|
||||||
|
void(*xDestroy)(void*) /* Module destructor function */
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
struct sqlite3_vtab {
|
||||||
|
const sqlite3_module *pModule; /* The module for this virtual table */
|
||||||
|
int nRef; /* NO LONGER USED */
|
||||||
|
char *zErrMsg; /* Error message from sqlite3_mprintf() */
|
||||||
|
/* Virtual table implementations will typically add additional fields */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct sqlite3_vtab_cursor {
|
||||||
|
sqlite3_vtab *pVtab; /* Virtual table of this cursor */
|
||||||
|
/* Virtual table implementations will typically add additional fields */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
int sqlite3_declare_vtab(sqlite3*, const char *zSQL);
|
||||||
|
|
||||||
|
|
||||||
|
int sqlite3_overload_function(sqlite3*, const char *zFuncName, int nArg);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct sqlite3_blob sqlite3_blob;
|
||||||
|
|
||||||
|
|
||||||
|
int sqlite3_blob_open(
|
||||||
|
sqlite3*,
|
||||||
|
const char *zDb,
|
||||||
|
const char *zTable,
|
||||||
|
const char *zColumn,
|
||||||
|
sqlite3_int64 iRow,
|
||||||
|
int flags,
|
||||||
|
sqlite3_blob **ppBlob
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
int sqlite3_blob_reopen(sqlite3_blob *, sqlite3_int64);
|
||||||
|
|
||||||
|
|
||||||
|
int sqlite3_blob_close(sqlite3_blob *);
|
||||||
|
|
||||||
|
|
||||||
|
int sqlite3_blob_bytes(sqlite3_blob *);
|
||||||
|
|
||||||
|
|
||||||
|
int sqlite3_blob_read(sqlite3_blob *, void *Z, int N, int iOffset);
|
||||||
|
|
||||||
|
|
||||||
|
int sqlite3_blob_write(sqlite3_blob *, const void *z, int n, int iOffset);
|
||||||
|
|
||||||
|
|
||||||
|
sqlite3_vfs *sqlite3_vfs_find(const char *zVfsName);
|
||||||
|
int sqlite3_vfs_register(sqlite3_vfs*, int makeDflt);
|
||||||
|
int sqlite3_vfs_unregister(sqlite3_vfs*);
|
||||||
|
|
||||||
|
|
||||||
|
sqlite3_mutex *sqlite3_mutex_alloc(int);
|
||||||
|
void sqlite3_mutex_free(sqlite3_mutex*);
|
||||||
|
void sqlite3_mutex_enter(sqlite3_mutex*);
|
||||||
|
int sqlite3_mutex_try(sqlite3_mutex*);
|
||||||
|
void sqlite3_mutex_leave(sqlite3_mutex*);
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct sqlite3_mutex_methods sqlite3_mutex_methods;
|
||||||
|
struct sqlite3_mutex_methods {
|
||||||
|
int (*xMutexInit)(void);
|
||||||
|
int (*xMutexEnd)(void);
|
||||||
|
sqlite3_mutex *(*xMutexAlloc)(int);
|
||||||
|
void (*xMutexFree)(sqlite3_mutex *);
|
||||||
|
void (*xMutexEnter)(sqlite3_mutex *);
|
||||||
|
int (*xMutexTry)(sqlite3_mutex *);
|
||||||
|
void (*xMutexLeave)(sqlite3_mutex *);
|
||||||
|
int (*xMutexHeld)(sqlite3_mutex *);
|
||||||
|
int (*xMutexNotheld)(sqlite3_mutex *);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
sqlite3_mutex *sqlite3_db_mutex(sqlite3*);
|
||||||
|
|
||||||
|
int sqlite3_file_control(sqlite3*, const char *zDbName, int op, void*);
|
||||||
|
|
||||||
|
|
||||||
|
int sqlite3_test_control(int op, ...);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag);
|
||||||
|
|
||||||
|
|
||||||
|
int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct sqlite3_pcache sqlite3_pcache;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct sqlite3_pcache_page sqlite3_pcache_page;
|
||||||
|
struct sqlite3_pcache_page {
|
||||||
|
void *pBuf; /* The content of the page */
|
||||||
|
void *pExtra; /* Extra information associated with the page */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct sqlite3_pcache_methods2 sqlite3_pcache_methods2;
|
||||||
|
struct sqlite3_pcache_methods2 {
|
||||||
|
int iVersion;
|
||||||
|
void *pArg;
|
||||||
|
int (*xInit)(void*);
|
||||||
|
void (*xShutdown)(void*);
|
||||||
|
sqlite3_pcache *(*xCreate)(int szPage, int szExtra, int bPurgeable);
|
||||||
|
void (*xCachesize)(sqlite3_pcache*, int nCachesize);
|
||||||
|
int (*xPagecount)(sqlite3_pcache*);
|
||||||
|
sqlite3_pcache_page *(*xFetch)(sqlite3_pcache*, unsigned key, int createFlag);
|
||||||
|
void (*xUnpin)(sqlite3_pcache*, sqlite3_pcache_page*, int discard);
|
||||||
|
void (*xRekey)(sqlite3_pcache*, sqlite3_pcache_page*,
|
||||||
|
unsigned oldKey, unsigned newKey);
|
||||||
|
void (*xTruncate)(sqlite3_pcache*, unsigned iLimit);
|
||||||
|
void (*xDestroy)(sqlite3_pcache*);
|
||||||
|
void (*xShrink)(sqlite3_pcache*);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct sqlite3_backup sqlite3_backup;
|
||||||
|
|
||||||
|
|
||||||
|
sqlite3_backup *sqlite3_backup_init(
|
||||||
|
sqlite3 *pDest, /* Destination database handle */
|
||||||
|
const char *zDestName, /* Destination database name */
|
||||||
|
sqlite3 *pSource, /* Source database handle */
|
||||||
|
const char *zSourceName /* Source database name */
|
||||||
|
);
|
||||||
|
int sqlite3_backup_step(sqlite3_backup *p, int nPage);
|
||||||
|
int sqlite3_backup_finish(sqlite3_backup *p);
|
||||||
|
int sqlite3_backup_remaining(sqlite3_backup *p);
|
||||||
|
int sqlite3_backup_pagecount(sqlite3_backup *p);
|
||||||
|
|
||||||
|
|
||||||
|
int sqlite3_unlock_notify(
|
||||||
|
sqlite3 *pBlocked, /* Waiting connection */
|
||||||
|
void (*xNotify)(void **apArg, int nArg), /* Callback function to invoke */
|
||||||
|
void *pNotifyArg /* Argument to pass to xNotify */
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int sqlite3_stricmp(const char *, const char *);
|
||||||
|
int sqlite3_strnicmp(const char *, const char *, int);
|
||||||
|
|
||||||
|
|
||||||
|
void sqlite3_log(int iErrCode, const char *zFormat, ...);
|
||||||
|
|
||||||
|
|
||||||
|
void *sqlite3_wal_hook(
|
||||||
|
sqlite3*,
|
||||||
|
int(*)(void *,sqlite3*,const char*,int),
|
||||||
|
void*
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
int sqlite3_wal_autocheckpoint(sqlite3 *db, int N);
|
||||||
|
|
||||||
|
|
||||||
|
int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb);
|
||||||
|
|
||||||
|
|
||||||
|
int sqlite3_wal_checkpoint_v2(
|
||||||
|
sqlite3 *db, /* Database handle */
|
||||||
|
const char *zDb, /* Name of attached database (or NULL) */
|
||||||
|
int eMode, /* SQLITE_CHECKPOINT_* value */
|
||||||
|
int *pnLog, /* OUT: Size of WAL log in frames */
|
||||||
|
int *pnCkpt /* OUT: Total number of frames checkpointed */
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
int sqlite3_vtab_config(sqlite3*, int op, ...);
|
||||||
|
|
||||||
|
int sqlite3_vtab_on_conflict(sqlite3 *);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// RTree Geometry Queries
|
||||||
|
typedef struct sqlite3_rtree_geometry sqlite3_rtree_geometry;
|
||||||
|
|
||||||
|
|
||||||
|
int sqlite3_rtree_geometry_callback(
|
||||||
|
sqlite3 *db,
|
||||||
|
const char *zGeom,
|
||||||
|
int (*xGeom)(sqlite3_rtree_geometry*, int n, double *a, int *pRes),
|
||||||
|
void *pContext
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
struct sqlite3_rtree_geometry {
|
||||||
|
void *pContext;
|
||||||
|
int nParam;
|
||||||
|
double *aParam;
|
||||||
|
void *pUser;
|
||||||
|
void (*xDelUser)(void *);
|
||||||
|
};
|
||||||
|
]]
|
||||||
|
|
||||||
|
local Lib = ffi.load(ffi.os == "Windows" and "bin/sqlite3" or "sqlite3")
|
||||||
|
|
||||||
|
return Lib;
|
||||||
|
|
115
editor/init.fnl
115
editor/init.fnl
|
@ -1,16 +1,56 @@
|
||||||
(local util (require :lib.util))
|
(local util (require :lib.util))
|
||||||
|
(local lume (require :lib.lume))
|
||||||
(local core (require :core))
|
(local core (require :core))
|
||||||
(local command (require :core.command))
|
(local command (require :core.command))
|
||||||
(local keymap (require :core.keymap))
|
(local keymap (require :core.keymap))
|
||||||
(local common (require :core.common))
|
(local common (require :core.common))
|
||||||
|
|
||||||
(fn inline-eval [eval]
|
(fn place-to-line [selection]
|
||||||
|
(let [selection (lume.clone selection)]
|
||||||
|
(when (selection:place?)
|
||||||
|
(set selection.acol 1)
|
||||||
|
(set selection.bcol math.huge))
|
||||||
|
selection))
|
||||||
|
|
||||||
|
(fn get-selection []
|
||||||
(let [ldoc core.active_view.doc
|
(let [ldoc core.active_view.doc
|
||||||
(aline acol bline bcol) (ldoc:get_selection)
|
(aline acol bline bcol) (ldoc:get_selection)]
|
||||||
inject #(ldoc:insert bline bcol (eval $1))]
|
{: ldoc : aline : acol : bline : bcol
|
||||||
(if (and (= aline bline) (= acol bcol))
|
:place? (fn [self] (and (= self.aline self.bline) (= self.acol self.bcol)))
|
||||||
(inject (ldoc:get_text aline 1 aline 10000000))
|
:rtl? (fn [self] (or (and (= self.aline self.bline) (< self.bcol self.acol)) (< self.bline self.aline)))
|
||||||
(inject (ldoc:get_text aline acol bline bcol)))))
|
:get-text (fn [self] (self.ldoc:get_text self.aline self.acol self.bline self.bcol))
|
||||||
|
:replace-text (fn [self text]
|
||||||
|
(self.ldoc:set_selection self.aline self.acol self.bline self.bcol)
|
||||||
|
(self.ldoc:text_input text))}))
|
||||||
|
|
||||||
|
(fn to-place [selection ?beginning]
|
||||||
|
(let [selection (lume.clone selection)]
|
||||||
|
(if (or (and ?beginning (not (selection:rtl?))) (and (not ?beginning) (selection:rtl?)))
|
||||||
|
(set (selection.bline selection.bcol) (values selection.aline selection.acol))
|
||||||
|
(set (selection.aline selection.acol) (values selection.bline selection.bcol)))
|
||||||
|
selection))
|
||||||
|
|
||||||
|
(fn selected-form [] (: (place-to-line (get-selection)) :get-text))
|
||||||
|
|
||||||
|
(fn find-closest [s pattern i-target]
|
||||||
|
(var (start end) nil)
|
||||||
|
(set (start end) (s:find pattern))
|
||||||
|
(while (and start (< end (- i-target 1)))
|
||||||
|
(set (start end) (s:find pattern (+ end 1))))
|
||||||
|
(if (and start (<= start i-target)) (values start (+ end 1))
|
||||||
|
(values i-target i-target)))
|
||||||
|
|
||||||
|
(local symbol-pattern "[a-zA-Z%!%@%#%$%%%^%&%*%<%>%?%/%~%-%_%=%+][a-zA-Z%!%@%#%$%%%^%&%*%<%>%?%/%~%-%_%=%+0-9%.%:]*")
|
||||||
|
(fn place-to-symbol [selection]
|
||||||
|
(let [selection (lume.clone selection)]
|
||||||
|
(when (selection:place?)
|
||||||
|
(let [line (: (place-to-line selection) :get-text)]
|
||||||
|
(set (selection.acol selection.bcol) (find-closest line symbol-pattern selection.acol))))
|
||||||
|
selection))
|
||||||
|
|
||||||
|
(fn selected-symbol [] (: (place-to-symbol (get-selection)) :get-text))
|
||||||
|
|
||||||
|
(fn inline-eval [eval] (: (to-place (get-selection)) :replace-text (eval (selected-form))))
|
||||||
|
|
||||||
(require :editor.editmode)
|
(require :editor.editmode)
|
||||||
|
|
||||||
|
@ -18,16 +58,59 @@
|
||||||
"repl:submit" #(core.active_view:submit)
|
"repl:submit" #(core.active_view:submit)
|
||||||
})
|
})
|
||||||
|
|
||||||
(local ReplView (require :editor.replview))
|
(local {: show : submit} (util.require :inspector.debug))
|
||||||
(local repl (require :editor.repl))
|
(local replsession (require :editor.replsession))
|
||||||
(command.add nil {
|
(command.add nil {
|
||||||
"repl:create" (fn []
|
"repl:create" #(show)
|
||||||
(local node (core.root_view:get_active_node))
|
|
||||||
(node:add_view (ReplView (repl.new)))
|
|
||||||
)
|
|
||||||
})
|
|
||||||
(keymap.add {
|
|
||||||
:return "repl:submit"
|
|
||||||
})
|
})
|
||||||
|
|
||||||
{: inline-eval}
|
(fn go-to-definition [symbol]
|
||||||
|
(fn jump-to-find-result [result]
|
||||||
|
(when (not (match result
|
||||||
|
{:vals [loc]}
|
||||||
|
(let [(filename line) (when loc (loc:match "(.*):([0-9]+)"))
|
||||||
|
filename (or filename "")
|
||||||
|
filename (if (filename:find "^%.%.%.") "" (or (filename:match "^%./(.*)") filename))
|
||||||
|
line (tonumber (or line 0))
|
||||||
|
ldoc (when (> (length filename) 0) (core.open_doc filename))]
|
||||||
|
(when ldoc
|
||||||
|
(core.root_view:open_doc ldoc)
|
||||||
|
(ldoc:set_selection line 1 line 1)
|
||||||
|
true))))
|
||||||
|
(core.log (.. "Unable to find symbol " symbol))))
|
||||||
|
(: (replsession.session) :submit (.. ",find " symbol) jump-to-find-result) true)
|
||||||
|
|
||||||
|
(fn replace-selected-symbol [text] (: (place-to-symbol (get-selection)) :replace-text text))
|
||||||
|
|
||||||
|
(fn autocomplete-results [text]
|
||||||
|
(var symbols [])
|
||||||
|
(: (replsession.session) :submit (.. ",complete " text) #(set symbols (or $1.vals [])) true)
|
||||||
|
(icollect [_ symbol (ipairs symbols)]
|
||||||
|
(let [item {:text symbol}]
|
||||||
|
(: (replsession.session) :submit (.. ",doc " symbol) #(when $1.vals (set item.info (. $1.vals 1))))
|
||||||
|
item)))
|
||||||
|
|
||||||
|
(fn autocomplete-symbol [callback ?starting-text]
|
||||||
|
(fn fixup-result [text item] (callback (or (and item item.text) text)))
|
||||||
|
(core.command_view:enter "Symbol" fixup-result autocomplete-results)
|
||||||
|
(print "start-text" ?starting-text)
|
||||||
|
(when ?starting-text
|
||||||
|
(core.command_view:set_text ?starting-text)
|
||||||
|
(core.command_view:update_suggestions)))
|
||||||
|
|
||||||
|
(command.add :core.docview {
|
||||||
|
"fennel:eval" #(submit (selected-form))
|
||||||
|
"fennel:go-to-definition-under-cursor" #(go-to-definition (selected-symbol))
|
||||||
|
"fennel:go-to-definition" #(autocomplete-symbol #(go-to-definition $1) (selected-symbol))
|
||||||
|
"fennel:autocomplete" #(autocomplete-symbol #(replace-selected-symbol $1) (selected-symbol))
|
||||||
|
})
|
||||||
|
|
||||||
|
(keymap.add {
|
||||||
|
:return "repl:submit"
|
||||||
|
"alt+e" "fennel:eval"
|
||||||
|
"alt+d" "fennel:go-to-definition-under-cursor"
|
||||||
|
"ctrl+space" "fennel:autocomplete"
|
||||||
|
})
|
||||||
|
|
||||||
|
{: inline-eval : symbol-pattern}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
(local lume (require :lib.lume))
|
(local lume (require :lib.lume))
|
||||||
(local {: textbutton : under : group-wrapper} (util.require :editor.imgui))
|
(local {: textbutton : under : group-wrapper} (util.require :editor.imgui))
|
||||||
(local {: inspect} (util.require :inspector))
|
(local {: inspect} (util.require :inspector))
|
||||||
|
(local replsession (require :editor.replsession))
|
||||||
|
|
||||||
(local repl (util.hot-table ...))
|
(local repl (util.hot-table ...))
|
||||||
|
|
||||||
(fn repl.inspector [{: w &as form} {: vals : states}]
|
(fn repl.inspector [{: w &as form} {: vals : states}]
|
||||||
|
@ -19,30 +21,20 @@
|
||||||
(fn repl.mk-result [vals]
|
(fn repl.mk-result [vals]
|
||||||
{:draw repl.inspector : vals :states (icollect [_ (ipairs vals)] {})})
|
{:draw repl.inspector : vals :states (icollect [_ (ipairs vals)] {})})
|
||||||
|
|
||||||
(fn repl.run [{: listeners}]
|
|
||||||
(fennel.repl {:readChunk coroutine.yield
|
|
||||||
:onValues #(repl.notify listeners (repl.mk-result $1))
|
|
||||||
:onError (fn [errType err luaSource] (repl.notify listeners (repl.mk-result [err])))
|
|
||||||
:pp #$1
|
|
||||||
:env (lume.clone _G)}))
|
|
||||||
|
|
||||||
(fn repl.listen [{: listeners} listener]
|
(fn repl.listen [{: listeners} listener]
|
||||||
(table.insert listeners listener))
|
(table.insert listeners listener))
|
||||||
|
|
||||||
(fn repl.unlisten [{: listeners} listener]
|
(fn repl.unlisten [{: listeners} listener]
|
||||||
(lume.remove listeners listener))
|
(lume.remove listeners listener))
|
||||||
|
|
||||||
(fn repl.submit [{: coro} chunk]
|
(fn repl.submit [{: session : listeners} chunk]
|
||||||
(coroutine.resume coro (.. chunk "\n")))
|
(session:submit chunk (fn [{: vals : err : traceback}] (repl.notify listeners (repl.mk-result (or vals [err traceback]))))))
|
||||||
|
|
||||||
(fn repl.new []
|
(fn repl.new [?id]
|
||||||
(local result
|
|
||||||
{:listeners []
|
{:listeners []
|
||||||
:listen #(repl.listen $...)
|
:listen #(repl.listen $...)
|
||||||
:unlisten #(repl.unlisten $...)
|
:unlisten #(repl.unlisten $...)
|
||||||
:submit #(repl.submit $...)
|
:submit #(repl.submit $...)
|
||||||
:coro (coroutine.create repl.run)})
|
:session (replsession.session ?id)})
|
||||||
(coroutine.resume result.coro result)
|
|
||||||
result)
|
|
||||||
|
|
||||||
repl.hot
|
repl.hot
|
||||||
|
|
49
editor/replsession.fnl
Normal file
49
editor/replsession.fnl
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
(local util (require :lib.util))
|
||||||
|
(local fennel (require :lib.fennel))
|
||||||
|
(local core (require :core))
|
||||||
|
|
||||||
|
(local replsession (util.hot-table ...))
|
||||||
|
|
||||||
|
(set replsession.sessions {})
|
||||||
|
|
||||||
|
(fn replsession.session-run [session]
|
||||||
|
(fennel.repl {:readChunk coroutine.yield
|
||||||
|
; todo: log errors?
|
||||||
|
:onValues #(pcall session.callback {:vals $1})
|
||||||
|
:onError #(pcall session.callback {:errType $1 :err $2 :luaSource $3 :traceback (fennel.traceback)})
|
||||||
|
:pp #$1
|
||||||
|
:env (lume.clone _G)}))
|
||||||
|
|
||||||
|
(fn replsession.restart-session [session]
|
||||||
|
(set session.coro (coroutine.create replsession.session-run))
|
||||||
|
(coroutine.resume session.coro session)
|
||||||
|
session)
|
||||||
|
|
||||||
|
(fn replsession.new-session [] (replsession.restart-session {:submit replsession.submit}))
|
||||||
|
|
||||||
|
(fn replsession.current-session-id []
|
||||||
|
(var session-id nil)
|
||||||
|
(let [coro (coroutine.running)]
|
||||||
|
(each [id session (pairs replsession.sessions)]
|
||||||
|
(when (= session.coro coro) (set session-id id)))
|
||||||
|
session-id))
|
||||||
|
|
||||||
|
(fn replsession.submit [session chunk callback ?suppress-crash]
|
||||||
|
(assert (= session.callback nil))
|
||||||
|
(set session.callback callback)
|
||||||
|
(match (pcall coroutine.resume session.coro (.. chunk "\n"))
|
||||||
|
(false err) (do (when (not ?suppress-crash) (core.log (.. "REPL crashed: " err)))
|
||||||
|
(replsession.restart-session session)))
|
||||||
|
(assert (= session.callback callback))
|
||||||
|
(set session.callback nil))
|
||||||
|
|
||||||
|
(fn replsession.activate [id] (set replsession.active-session-id id))
|
||||||
|
(fn replsession.session-id [?id] (or ?id replsession.active-session-id :REPL))
|
||||||
|
(fn replsession.session [?id]
|
||||||
|
(let [id (replsession.session-id ?id)
|
||||||
|
session (or (. replsession.sessions id) (replsession.new-session))]
|
||||||
|
(tset replsession.sessions id session)
|
||||||
|
session))
|
||||||
|
|
||||||
|
replsession.hot
|
||||||
|
|
99
gneiss/cpu.fnl
Normal file
99
gneiss/cpu.fnl
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
(local Sqlog (require :sqlog))
|
||||||
|
(import-macros {: $ : specify : query} :sqlog.macros)
|
||||||
|
|
||||||
|
(fn nextval [varname] ($ (+ (coalesce (max ,[:var varname]) 0) 1)))
|
||||||
|
|
||||||
|
(local Cpu {})
|
||||||
|
(fn Cpu.new [self ?filename]
|
||||||
|
(set self.sqlog (Sqlog ?filename))
|
||||||
|
(set self.instructions [])
|
||||||
|
(self:init-new-db))
|
||||||
|
|
||||||
|
(fn Cpu.init-new-db [self]
|
||||||
|
"Initialize a fresh database with the necessary tables and rules."
|
||||||
|
(specify self.sqlog
|
||||||
|
; cpu layout:
|
||||||
|
(table cpu.running process-id)
|
||||||
|
[cpu.running 0] ; 0 means nothing
|
||||||
|
(table cpu.fp process-id frame-id)
|
||||||
|
(table cpu.frame frame-id var-id value)
|
||||||
|
(table cpu.program program-id instruction-id instruction)
|
||||||
|
; special frame variables:
|
||||||
|
; $result - the anonymous result of the most recent computation (useful?)
|
||||||
|
; $fp-return - the frame-id to return to when finished
|
||||||
|
; $program - the id of the program that the frame is referencing
|
||||||
|
; $ip - the id of the instruction within the program that the process is executing
|
||||||
|
|
||||||
|
; function calls create a new frame by incrementing cpu.next-fp and populating cpu.frame.
|
||||||
|
; returning from a function call sets the $result of $fp-return to the $result of the current frame,
|
||||||
|
; sets cpu.fp for the current process to $fp-return, and removes all data from cpu.frame with the current frame ID.
|
||||||
|
(table compiler.sourcemap program-id instruction-id filename position)
|
||||||
|
|
||||||
|
(* [cpu.next-process ,(nextval :process-id)] [cpu.fp process-id _])
|
||||||
|
(* [cpu.next-fp ,(nextval :frame-id)] [cpu.frame frame-id _ _])
|
||||||
|
(* [cpu.next-program-id ,(nextval :program-id)] [cpu.program program-id _ _ _])
|
||||||
|
(* [cpu.next-instruction-id program-id ,(nextval :instruction-id)] [cpu.program program-id instruction-id _ _])
|
||||||
|
(* [cpu.next-instruction-id next-program-id 1] [cpu.next-program-id next-program-id])
|
||||||
|
(* [cpu.frame-var var-id value] [cpu.running process-id] [cpu.fp process-id frame-id] [cpu.frame frame-id var-id value])
|
||||||
|
(* [cpu.my-fp fp] [cpu.running pid] [cpu.fp pid fp])
|
||||||
|
(* [cpu.ip program-id ip] [cpu.frame-var :$ip ip] [cpu.frame-var :$program program-id])
|
||||||
|
(* [cpu.instruction instruction] [cpu.ip program-id ip] [cpu.program program-id ip instruction])
|
||||||
|
(* [cpu.return-fp fp] [cpu.frame-var :$fp-return fp])
|
||||||
|
|
||||||
|
; frame 1 destroys all processes that enter it (program 1 will implement this logic)
|
||||||
|
[cpu.frame 1 :$ip 1]
|
||||||
|
[cpu.frame 1 :$program 1])
|
||||||
|
|
||||||
|
; define program 1 for frame 1
|
||||||
|
(self:insert-program ($
|
||||||
|
(!- [cpu.fp _ 1])
|
||||||
|
(!= [cpu.frame _ _ 0] [cpu.frame 1 :$ip _]))) ; this shouldn't be needed
|
||||||
|
|
||||||
|
(set self.ret
|
||||||
|
(self:def-instruction
|
||||||
|
($ (!= [cpu.fp _ fp-return] [cpu.fp pid _] [cpu.running pid] [cpu.return-fp fp-return])))))
|
||||||
|
|
||||||
|
(fn Cpu.def-instruction [self ...]
|
||||||
|
"Creates a new reusable instruction and returns its ID"
|
||||||
|
(let [analysis (self.sqlog:compile-action [:do ...])]
|
||||||
|
(table.insert self.instructions analysis)
|
||||||
|
(length self.instructions)))
|
||||||
|
|
||||||
|
(fn Cpu.insert-program [self ...]
|
||||||
|
"Creates a new program with the specified instructions"
|
||||||
|
(let [[{: program-id}] (query self.sqlog [cpu.next-program-id program-id])]
|
||||||
|
(each [ip action (ipairs [...])]
|
||||||
|
(let [instruction (if (not= (type action) :table) action (self:def-instruction action))]
|
||||||
|
(specify self.sqlog [cpu.program #program-id #ip #instruction])))
|
||||||
|
program-id))
|
||||||
|
|
||||||
|
(fn Cpu.initialize-process [self program-id]
|
||||||
|
"Creates a new process, starting at the beginning of the specified program"
|
||||||
|
(let [[{: process-id : fp}] (query self.sqlog [cpu.next-process process-id] [cpu.next-fp fp])]
|
||||||
|
(specify self.sqlog
|
||||||
|
(!+ [cpu.fp #process-id #fp])
|
||||||
|
(!+ [cpu.frame #fp :$program #program-id])
|
||||||
|
(!+ [cpu.frame #fp :$ip 1])
|
||||||
|
(!+ [cpu.frame #fp :$fp-return 1]))))
|
||||||
|
|
||||||
|
(fn Cpu.process-round [self]
|
||||||
|
"Executes each currently-active process until it hits a suspend instruction or is removed."
|
||||||
|
(let [process-ids (query self.sqlog [cpu.fp process-id _])]
|
||||||
|
(each [_ {: process-id} (ipairs process-ids)]
|
||||||
|
(specify self.sqlog (!= [cpu.running #process-id] [cpu.running _]))
|
||||||
|
(var done false)
|
||||||
|
(while (not done)
|
||||||
|
(let [[instruction-result] (query self.sqlog [cpu.instruction instruction])
|
||||||
|
{: instruction} (or instruction-result {})
|
||||||
|
action (and instruction (. self.instructions instruction))]
|
||||||
|
(if (= action nil) (set done true)
|
||||||
|
(= (type action) :function) (action self)
|
||||||
|
(self.sqlog:execute action))
|
||||||
|
(specify self.sqlog (!= [cpu.frame _ _ (+ ip 1)] [cpu.frame fp :$ip ip] [cpu.fp #process-id fp])))))
|
||||||
|
(specify self.sqlog (!= [cpu.running 0] [cpu.running _]))
|
||||||
|
process-ids))
|
||||||
|
|
||||||
|
(fn Cpu.run [self] (while (> (length (self:process-round)) 0)))
|
||||||
|
|
||||||
|
(setmetatable Cpu {:__call (fn [cls ...] (doto (setmetatable {} {:__index cls}) (: :new ...)))})
|
||||||
|
|
34
gneiss/example.fnl
Normal file
34
gneiss/example.fnl
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
(local Cpu (require :gneiss.cpu))
|
||||||
|
(local {: show} (require :inspector.debug))
|
||||||
|
(local sql (require :diet-sqlite))
|
||||||
|
(import-macros {: $ : query : specify} :sqlog.macros)
|
||||||
|
|
||||||
|
(sql.shutdown)
|
||||||
|
; (sql.register-logger #(print "SQL:" $2 $1))
|
||||||
|
|
||||||
|
(local cpu (Cpu))
|
||||||
|
|
||||||
|
(macro divisible-by [val denominator]
|
||||||
|
`($ (= (% ,val ,denominator) 0)))
|
||||||
|
|
||||||
|
(pp (divisible-by i 3))
|
||||||
|
|
||||||
|
(specify cpu.sqlog
|
||||||
|
(table fizzbuzz i fizzbuzz))
|
||||||
|
|
||||||
|
(let [pgid (cpu:insert-program ($
|
||||||
|
(do ; 1
|
||||||
|
(!+ [cpu.frame fp :i 1] [cpu.my-fp fp])) ; ideally: (!+ [cpu.frame-var :i 1])
|
||||||
|
(do ;2
|
||||||
|
(!+ [fizzbuzz i (case (when (and ,(divisible-by i 3) ,(divisible-by i 5)) :fizzbuzz)
|
||||||
|
(when ,(divisible-by i 3) :fizz)
|
||||||
|
(when ,(divisible-by i 5) :buzz)
|
||||||
|
(else i))] [cpu.frame-var :i i])
|
||||||
|
(!= [cpu.frame _ _ (+ i 1)] [cpu.frame fp :i i] [cpu.my-fp fp]) ; ideally: (!= [cpu.frame-var :i 1])?
|
||||||
|
(!= [cpu.frame _ _ 1] [cpu.frame fp :$ip _] [cpu.my-fp fp] [cpu.frame-var :i i] (< i 100))) ; if i < 100 goto 2
|
||||||
|
,cpu.ret))
|
||||||
|
pid (cpu:initialize-process pgid)]
|
||||||
|
(cpu:run)
|
||||||
|
(show (query cpu.sqlog [fizzbuzz i fizzbuzz])))
|
||||||
|
|
||||||
|
{}
|
|
@ -3,6 +3,7 @@
|
||||||
(local util (require :lib.util))
|
(local util (require :lib.util))
|
||||||
(local repl (require :editor.repl))
|
(local repl (require :editor.repl))
|
||||||
(local ReplView (require :editor.replview))
|
(local ReplView (require :editor.replview))
|
||||||
|
(local replsession (require :editor.replsession))
|
||||||
|
|
||||||
(local module (util.hot-table ...))
|
(local module (util.hot-table ...))
|
||||||
|
|
||||||
|
@ -15,16 +16,21 @@
|
||||||
|
|
||||||
(fn create-inspector-window [name ?value]
|
(fn create-inspector-window [name ?value]
|
||||||
(let [node (core.root_view:get_active_node)
|
(let [node (core.root_view:get_active_node)
|
||||||
conn (repl.new)
|
conn (repl.new name)
|
||||||
view (ReplView conn)]
|
view (ReplView conn)]
|
||||||
(set view.inspector-name name)
|
(set view.inspector-name name)
|
||||||
(set view.title name)
|
(set view.title name)
|
||||||
(view:append {:draw (fn [_ _ x y] (renderer.draw_text style.font name x y style.text) (+ (style.font:get_height) style.padding.y))})
|
(view:append {:draw (fn [_ _ x y] (renderer.draw_text style.font name x y style.text) (+ (style.font:get_height) style.padding.y))})
|
||||||
(view:append (repl.mk-result [?value]))
|
(when ?value
|
||||||
(node:add_view view)))
|
(view:append (repl.mk-result [?value])))
|
||||||
|
(node:split :right view)
|
||||||
|
view))
|
||||||
|
|
||||||
(lambda module.show [name ?value]
|
(lambda module.show [?value ?name]
|
||||||
(when (= (find-existing-inspector-window name) nil)
|
(let [name (replsession.session-id ?name)]
|
||||||
(create-inspector-window name ?value)))
|
(or (find-existing-inspector-window name) (create-inspector-window name ?value))))
|
||||||
|
|
||||||
|
(lambda module.submit [chunk ?name]
|
||||||
|
(: (module.show ?name) :submit chunk))
|
||||||
|
|
||||||
module.hot
|
module.hot
|
||||||
|
|
1807
lib/fennel.lua
1807
lib/fennel.lua
File diff suppressed because it is too large
Load diff
12
main.lua
12
main.lua
|
@ -7,12 +7,14 @@ pp = function(x) print(fv(x)) end
|
||||||
lume = require("lib.lume")
|
lume = require("lib.lume")
|
||||||
|
|
||||||
_coroutine_resume = coroutine.resume
|
_coroutine_resume = coroutine.resume
|
||||||
function coroutine.resume(...)
|
function handle_error(ok, ...)
|
||||||
local state,result = _coroutine_resume(...)
|
if not ok then
|
||||||
if not state then
|
error(...)
|
||||||
error( tostring(result), 2 ) -- Output error message
|
|
||||||
end
|
end
|
||||||
return state,result
|
return ok, ...
|
||||||
|
end
|
||||||
|
function coroutine.resume(...)
|
||||||
|
return handle_error(_coroutine_resume(...))
|
||||||
end
|
end
|
||||||
|
|
||||||
require("vendor.lite.main")
|
require("vendor.lite.main")
|
||||||
|
|
289
sqlog/compiler.fnl
Normal file
289
sqlog/compiler.fnl
Normal file
|
@ -0,0 +1,289 @@
|
||||||
|
; sqlog, a datalog-like system built on sqlite
|
||||||
|
; may not actually have the full power of datalog OR the full power of sqlite
|
||||||
|
|
||||||
|
; datalog has a database of facts, and rules. facts are represented in sqlite directly as tables.
|
||||||
|
; rules are used by the sqlog engine when generating queries; typically they take the form
|
||||||
|
; of subqueries used in the WITH RECURSIVE clause.
|
||||||
|
; It would be possible to store them as views, but the view would need to be regenerated from
|
||||||
|
; its base rules anytime there is a change, so sqlog needs to know about them no matter what.
|
||||||
|
; Making sqlog manage them in-memory also allows us to use it to query arbitrary sqlite databases.
|
||||||
|
|
||||||
|
; Ideally we would persist rules as JSON in a special table; maybe sqlog_rules?
|
||||||
|
|
||||||
|
(local Compiler {})
|
||||||
|
|
||||||
|
; Generating SQL from Datalog should not be too complex, but it pays to start with the simplest
|
||||||
|
; case and build up from there.
|
||||||
|
|
||||||
|
; simple queries:
|
||||||
|
; [p x y] -> SELECT p.c1 AS x, p.c2 AS y FROM p
|
||||||
|
; [p 1 y] -> SELECT p.c2 AS y FROM p WHERE p.c1 = 1
|
||||||
|
; [q x] [p x 1] -> SELECT q.c1 AS x FROM q JOIN p WHERE p.c1 = q.c1 AND p.c2 = 1
|
||||||
|
; [p 1 2] -> SELECT true FROM p WHERE p.c1 = 1 AND p.c2 = 2
|
||||||
|
; [p 1 x] [p x 2] -> SELECT t1.c2 AS x FROM p AS t1 JOIN p AS t2 WHERE t1.c1 = 1 AND t1.c2 = t2.c1 AND t2.c2 = 2
|
||||||
|
|
||||||
|
; queries using rules:
|
||||||
|
; ([ancestor x y] [parent x y]) -> SELECT p.c1 AS x, p.c2 AS y FROM parent AS p
|
||||||
|
; ([ancestor x y] [parent x z] [ancestor z y]) -> SELECT p.c1 AS x, a.y AS y FROM parent AS p JOIN ancestor AS a WHERE p.c1 = a.x AND p.c2 = a.y
|
||||||
|
; [ancestor x :john] -> WITH RECURSIVE ancestor(x, y) AS (SELECT ... UNION SELECT ...) SELECT a.c1 AS x FROM ancestor AS a WHERE a.y = 'john'
|
||||||
|
; ([ancestor :bob x] [ancestor x :john]) -> SELECT 'bob' AS c1, a.c1 AS c2 FROM ancestor AS a WHERE a.c2 = 'john'
|
||||||
|
|
||||||
|
; queries with arithmetic operations (function calls?)
|
||||||
|
; [p x y] [q (+ x 1)] -> SELECT p.c1 AS x, p.c2 AS y FROM p JOIN q WHERE q.c1 = p.c1 + 1
|
||||||
|
; [p (+ x 1) y] [q x] -> SELECT q.c1 AS x, p.c2 AS y FROM p JOIN q WHERE p.c1 = q.c1 + 1
|
||||||
|
; [p (+ x 1) x] -> SELECT p.c2 AS x FROM p WHERE p.c1 = p.c2 + 1
|
||||||
|
|
||||||
|
; queries with comparisons
|
||||||
|
; [p x y] (< x 5) -> SELECT p.c1 AS x, p.c2 AS y FROM p WHERE p.c1 < 5
|
||||||
|
; [p x y] (= x y) -> unnecessary but supported, can be written (p x x)
|
||||||
|
; [p x y] (= x (+ y 1)) -> unnecessary but supported, can be written (p (+ x 1) x)?
|
||||||
|
|
||||||
|
; confusing expressions we probably won't support:
|
||||||
|
; [p (+ x 1) (* x 2)] -> SELECT p.c1 - 1 AS x FROM p WHERE p.c1 + 1 = p.c2 * 2??
|
||||||
|
; no, that's not right - this says x+1 = c1 AND x*2 = c2
|
||||||
|
; [p (+ x 1) (* x 2)] -> SELECT p.c1 - 1 AS x FROM p WHERE p.c2 = (p.c1 - 1) * 2
|
||||||
|
; [p (+ x 1) y] -> meaningless? or...
|
||||||
|
; -> SELECT p.c1 - 1 AS x, p.c2 AS y FROM p
|
||||||
|
; is there a way to trick sql into generating x = p.c1 - 1 from p.c1 = x + 1?
|
||||||
|
; [p z y] (= z (+ x 1))
|
||||||
|
|
||||||
|
; unsupported: inline comparisons
|
||||||
|
; [p (< x 5) y] -> SELECT p.c1 AS x, p.c2 AS y FROM p WHERE p.c1 < 5 -- does this make sense? seems hard to read, hard to parse
|
||||||
|
|
||||||
|
; insert:
|
||||||
|
; [p 1 2] -> INSERT INTO p (c1, c2) VALUES (1, 2)
|
||||||
|
; [p 1 (+ 2 3)] -> INSERT INTO p (c1, c2) VALUES (1, 2 + 3)
|
||||||
|
; (!+ [p (+ x 1) 3] [q x]) -> INSERT INTO p (c1, c2) SELECT q.c1 + 1 AS c1, 3 AS c2 FROM q
|
||||||
|
|
||||||
|
; delete:
|
||||||
|
; (!- [p _ _]) -> DELETE FROM p
|
||||||
|
; (!- [p 1 _]) -> DELETE FROM p WHERE p.c1 = 1
|
||||||
|
; augh sqlite doesn't support joins on delete! The most generic solution:
|
||||||
|
; (!- [p 1 x] [q x]) -> DELETE FROM p WHERE p._rowid_ IN (SELECT p._rowid_ FROM p JOIN q WHERE p.c1 = 1 AND p.c2 = q.c1)
|
||||||
|
|
||||||
|
; update:
|
||||||
|
; (!= [q (+ x 1)] [q x]) -> UPDATE q SET c1 = q.c1 + 1
|
||||||
|
; (!= [p (+ x 1) _] [p x 3]) -> UPDATE p SET c1 = p.c1 + 1 WHERE p.c2 = 3
|
||||||
|
; invalid update:
|
||||||
|
; (!= [p (+ x 1) _] [q x]) -> UPDATE p SET c1 = q.c1 + 1 FROM q -- this is valid sql, but it's madness - enforce the first query clause matches table
|
||||||
|
|
||||||
|
(fn cat [list sep ?f]
|
||||||
|
"Join all the elements in list with the string sep. If ?f is supplied, it is called to transform each value in the list into a new value first."
|
||||||
|
(table.concat (icollect [i v (ipairs list)] ((or ?f #$1) v i)) sep))
|
||||||
|
|
||||||
|
(fn any [list pred]
|
||||||
|
"Returns true if the supplied predicate function returns true for any values in list."
|
||||||
|
(var found false)
|
||||||
|
(each [_ v (ipairs list) :until found] (set found (pred v)))
|
||||||
|
found)
|
||||||
|
|
||||||
|
(fn append-if-missing [list value]
|
||||||
|
"Adds value to the end of list if it is not currently included."
|
||||||
|
(when (not (any list #(= $1 value)))
|
||||||
|
(table.insert list value)))
|
||||||
|
|
||||||
|
(fn countiter [max]
|
||||||
|
"An iterator that counts from 1 to max, inclusive."
|
||||||
|
(fn [_ iprev]
|
||||||
|
(let [i (if iprev (+ iprev 1) 1)]
|
||||||
|
(when (<= i max) i))))
|
||||||
|
|
||||||
|
(fn quoteid [name] (.. "\"" (name:gsub "\"" "\"\"") "\""))
|
||||||
|
|
||||||
|
(fn Compiler.new [self]
|
||||||
|
(set self.tables {})
|
||||||
|
(set self.rules {}))
|
||||||
|
|
||||||
|
(fn Compiler.deftable [self name ...]
|
||||||
|
"Defines the column names of a table and their expected ordering"
|
||||||
|
(when (. name self.rules) (error "tables and rules must not overlap"))
|
||||||
|
(tset self.tables name [...])
|
||||||
|
{:query (.. "CREATE TABLE " (quoteid name) "(" (cat [...] ", " quoteid)
|
||||||
|
", PRIMARY KEY (" (cat [...] ", " quoteid) "))")
|
||||||
|
:constants []
|
||||||
|
:selection []})
|
||||||
|
|
||||||
|
(fn Compiler.defrule [self head ...]
|
||||||
|
"Defines a new rule or expands the definition of an existing rule."
|
||||||
|
(match head
|
||||||
|
[:literal name] (let [rulelist (or (. self.rules name) [])]
|
||||||
|
(table.insert rulelist [head ...])
|
||||||
|
(tset self.rules name rulelist))
|
||||||
|
_ (error "Expected literal for head, got " (fv head))))
|
||||||
|
|
||||||
|
(fn new-analysis [?parent]
|
||||||
|
"Creates a new empty analysis object. If ?parent is supplied, share the list of constants and referenced rules, as these are shared
|
||||||
|
with the full SQL expression."
|
||||||
|
{:variables [] ; list of all variable names referenced; used to determine which values to return from a query
|
||||||
|
:variable-mapping {} ; a mapping from variable name to a SQL expr to which it is equal
|
||||||
|
:selection [] ; list of all columns being selected, generally of the form [:as expr name]
|
||||||
|
:clauses [] ; list of WHERE clauses, to be joined together by AND
|
||||||
|
:tables [] ; list of tables or rules queried. (. tables 1) is generally aliased to _t1. All tables are inner joined.
|
||||||
|
:constants (or (?. ?parent :constants) []) ; list of constant values to be bound to the prepared SQL expression
|
||||||
|
:referenced-rules (or (?. ?parent :referenced-rules) [])}) ; list of all rules referenced by this expression, so they can be included
|
||||||
|
|
||||||
|
(fn add-clause [analysis clause] (table.insert analysis.clauses clause))
|
||||||
|
|
||||||
|
(fn Compiler.reference-name [self analysis name]
|
||||||
|
"Called when a query references a new table. Appends the table or rule name to the end of analysis.tables
|
||||||
|
and returns the index of the new value. Also updates analysis.referenced-rules as needed."
|
||||||
|
(if (or (. self.rules name) (. self.tables name))
|
||||||
|
(do (table.insert analysis.tables name)
|
||||||
|
(when (. self.rules name)
|
||||||
|
(append-if-missing analysis.referenced-rules name))
|
||||||
|
(length analysis.tables))
|
||||||
|
(error (.. "Unknown table / rule " name))))
|
||||||
|
|
||||||
|
(fn Compiler.reference-variable [self analysis varname expr]
|
||||||
|
"Called when a query expression (expr) is known to be equal to the value of varname - usually because
|
||||||
|
the variable appears in the position of a particular rule or column. Defines a mapping from variable
|
||||||
|
to expression if none exists, otherwise adds a new clause testing that the original mapping is equal
|
||||||
|
to this new mapping, implementing unification."
|
||||||
|
(when (not= varname :_)
|
||||||
|
(match (. analysis.variable-mapping varname)
|
||||||
|
mapping (add-clause analysis [:= mapping expr])
|
||||||
|
nil (do (tset analysis.variable-mapping varname expr)
|
||||||
|
(table.insert analysis.variables varname)))))
|
||||||
|
|
||||||
|
; These are SQL functions that return boolean values and can be used as a standalone clause.
|
||||||
|
(local comparitors (collect [_ op (ipairs [:< :> :<= :>= := :and :or])] op true))
|
||||||
|
(fn Compiler.analyze-literal [self analysis literal]
|
||||||
|
"Adds a literal of the form [table expr expr expr...] or a comparison expression of the form (op expr expr) to
|
||||||
|
the analysis object. Typically this implies a join and some number of WHERE clauses."
|
||||||
|
(match literal
|
||||||
|
[:literal name params] (let [itable (self:reference-name analysis name)]
|
||||||
|
(each [icolumn value (ipairs params)]
|
||||||
|
(match value
|
||||||
|
[:var varname] (self:reference-variable analysis varname [:column itable icolumn])
|
||||||
|
[:const val] (add-clause analysis [:= [:column itable icolumn] [:const val]])
|
||||||
|
(where [op] (= (. comparitors op) nil)) (add-clause analysis [:= [:column itable icolumn] value])
|
||||||
|
_ (error (.. "expected var, const, or function, got " (fv value))))))
|
||||||
|
(where [op] (. comparitors op)) (add-clause analysis literal)
|
||||||
|
_ (error (.. "Expected literal or comparison but got " (fv literal)))))
|
||||||
|
|
||||||
|
(local infix-ops (collect [_ op (ipairs [:+ :- :* :/ :< :> :<= :>= := :|| :and :or :%])] op true))
|
||||||
|
(fn Compiler.gen-expr [self analysis expr]
|
||||||
|
"Generates SQL code for a given expression tree."
|
||||||
|
(match expr
|
||||||
|
[:const val] (do (table.insert analysis.constants val) "?")
|
||||||
|
[:column itable icolumn] (.. "_t" itable "."
|
||||||
|
(match (. self.tables (. analysis.tables itable))
|
||||||
|
colnames (quoteid (. colnames icolumn))
|
||||||
|
_ (.. "c" icolumn)))
|
||||||
|
[:rowid itable] (.. "_t" itable "._rowid_")
|
||||||
|
[:as subexpr name] (.. (self:gen-expr analysis subexpr) " AS " (quoteid name))
|
||||||
|
[:set column subexpr] (.. (quoteid column) " = " (self:gen-expr analysis subexpr))
|
||||||
|
[:case & clauses] (.. "CASE " (cat clauses " " #(self:gen-expr analysis $1)) " END")
|
||||||
|
[:when cmp result] (.. "WHEN " (self:gen-expr analysis cmp) " THEN " (self:gen-expr analysis result))
|
||||||
|
[:else result] (.. "ELSE " (self:gen-expr analysis result))
|
||||||
|
(where [:var name] (. analysis.variable-mapping name)) (self:gen-expr analysis (. analysis.variable-mapping name))
|
||||||
|
(where [op lhs rhs] (. infix-ops op)) (.. "(" (self:gen-expr analysis lhs) " " op " " (self:gen-expr analysis rhs) ")")
|
||||||
|
[funcname & args] (.. funcname "(" (cat args ", " #(self:gen-expr analysis $1)) ")")
|
||||||
|
_ (error (.. "Unrecognized expression " (fv expr)))))
|
||||||
|
|
||||||
|
(fn Compiler.gen-rule-clause [self analysis-parent [head & literals]]
|
||||||
|
"Generates a SELECT statement for a given rule (potentially in a family of rules with the same name to be unioned together)"
|
||||||
|
(let [analysis (new-analysis analysis-parent)]
|
||||||
|
(each [_ literal (ipairs literals)] (self:analyze-literal analysis literal))
|
||||||
|
(match head
|
||||||
|
[:literal name params]
|
||||||
|
(set analysis.selection (icollect [icolumn param (ipairs params)] [:as param (.. :c icolumn)]))
|
||||||
|
_ (error (.. "Expected literal, got " (fv head))))
|
||||||
|
(self:gen-select analysis)))
|
||||||
|
|
||||||
|
(fn Compiler.gen-rule [self analysis name]
|
||||||
|
"Generates a full expression for a rule family with the given name, to be placed in a WITH RECURSIVE block."
|
||||||
|
(let [rule (. self.rules name)
|
||||||
|
column-count (match rule
|
||||||
|
[[[:literal _ clauses]]] (length clauses)
|
||||||
|
_ (error (.. "Rule should be list of list of literals, was " (fv rule))))]
|
||||||
|
(.. (quoteid name) "(" (table.concat (icollect [i (countiter column-count)] (.. "c" i)) ", ") ") AS NOT MATERIALIZED ("
|
||||||
|
(cat rule " UNION " #(self:gen-rule-clause analysis $1)) ")")))
|
||||||
|
|
||||||
|
(fn Compiler.gen-with-rules [self analysis]
|
||||||
|
"Generates a WITH block containing all of the referenced rules found during analysis. Will append to the referenced-rules list
|
||||||
|
as needed if rules depend on other rules, and only return when all requirements are satisfied."
|
||||||
|
(let [rulequeries []]
|
||||||
|
; ipairs will iterate over all referenced-rules even if gen-rule causes more to be appended
|
||||||
|
(each [_ name (ipairs analysis.referenced-rules)]
|
||||||
|
(table.insert rulequeries (self:gen-rule analysis name)))
|
||||||
|
(if (> (length rulequeries) 0)
|
||||||
|
(.. "WITH RECURSIVE " (cat rulequeries ", ") " ")
|
||||||
|
"")))
|
||||||
|
|
||||||
|
(fn Compiler.gen-select [self analysis]
|
||||||
|
"Generates a SELECT statement for the query defined by analysis."
|
||||||
|
(.. "SELECT "
|
||||||
|
(if (> (length analysis.selection) 0)
|
||||||
|
(cat analysis.selection ", " #(self:gen-expr analysis $1))
|
||||||
|
"true")
|
||||||
|
(if (> (length analysis.tables) 0)
|
||||||
|
(.. " FROM " (cat analysis.tables " JOIN " #(.. (quoteid $1) " AS _t" $2)))
|
||||||
|
"")
|
||||||
|
(if (> (length analysis.clauses) 0)
|
||||||
|
(.. " WHERE " (cat analysis.clauses " AND " #(self:gen-expr analysis $1)))
|
||||||
|
"")))
|
||||||
|
|
||||||
|
(fn tail [[ _ & result]] result)
|
||||||
|
(fn Compiler.gen-update [self analysis]
|
||||||
|
"Generates an UPDATE statement for the query defined by analysis."
|
||||||
|
(.. "UPDATE " (quoteid (. analysis.tables 1)) " AS _t1 SET "
|
||||||
|
(cat analysis.selection ", " #(self:gen-expr analysis $1))
|
||||||
|
(if (>= (length analysis.tables) 2)
|
||||||
|
(.. " FROM " (cat (tail analysis.tables) " JOIN " #(.. (quoteid $1) " AS _t" (+ $2 1))))
|
||||||
|
"")
|
||||||
|
(if (> (length analysis.clauses) 0)
|
||||||
|
(.. " WHERE " (cat analysis.clauses " AND " #(self:gen-expr analysis $1)))
|
||||||
|
"")))
|
||||||
|
|
||||||
|
(fn Compiler.query [self ...]
|
||||||
|
"Analyzes the given literals and generates an appropriate SELECT statement."
|
||||||
|
(let [analysis (new-analysis)]
|
||||||
|
(each [_ literal (ipairs [...])] (self:analyze-literal analysis literal))
|
||||||
|
(set analysis.selection (icollect [_ varname (ipairs analysis.variables)] [:as (. analysis.variable-mapping varname) varname]))
|
||||||
|
(set analysis.query (.. (self:gen-with-rules analysis) (self:gen-select analysis)))
|
||||||
|
analysis))
|
||||||
|
|
||||||
|
(fn Compiler.insert [self head ...]
|
||||||
|
"Analyzes the given literals and generates an appropriate INSERT statement."
|
||||||
|
(match head
|
||||||
|
[:literal name params]
|
||||||
|
(let [analysis (new-analysis)
|
||||||
|
columns (. self.tables name)]
|
||||||
|
(each [_ literal (ipairs [...])] (self:analyze-literal analysis literal))
|
||||||
|
(set analysis.selection (icollect [icolumn param (ipairs params)]
|
||||||
|
(match param
|
||||||
|
[:var :_] nil
|
||||||
|
expr [:as expr (. columns icolumn)])))
|
||||||
|
(set analysis.query (.. (self:gen-with-rules analysis)
|
||||||
|
"INSERT INTO " (quoteid name) " (" (cat analysis.selection ", " #(match $1 [:as _ column] (quoteid column))) ") "
|
||||||
|
(self:gen-select analysis)))
|
||||||
|
analysis)
|
||||||
|
_ (error (.. "Expected literal, got " (fv head)))))
|
||||||
|
|
||||||
|
(fn Compiler.delete [self head ...]
|
||||||
|
"Analyzes the given literals and generates an appropriate DELETE statement."
|
||||||
|
(match head
|
||||||
|
[:literal name params]
|
||||||
|
(let [analysis (new-analysis)]
|
||||||
|
(each [_ literal (ipairs [head ...])] (self:analyze-literal analysis literal))
|
||||||
|
(set analysis.selection [[:as [:rowid 1] :_rowid_]])
|
||||||
|
(set analysis.query (.. (self:gen-with-rules analysis) "DELETE FROM " (quoteid name) " WHERE _rowid_ IN (" (self:gen-select analysis) ")"))
|
||||||
|
analysis)
|
||||||
|
_ (error (.. "Expected literal, got " (fv head)))))
|
||||||
|
|
||||||
|
(fn Compiler.update [self newhead head ...]
|
||||||
|
"Analyzes the given literals and generates an appropriate UPDATE statement."
|
||||||
|
(match [newhead head]
|
||||||
|
[[:literal name params] [:literal name]]
|
||||||
|
(let [analysis (new-analysis)
|
||||||
|
columns (. self.tables name)]
|
||||||
|
(each [_ literal (ipairs [head ...])] (self:analyze-literal analysis literal))
|
||||||
|
(set analysis.selection (icollect [icolumn param (ipairs params)]
|
||||||
|
(match param
|
||||||
|
[:var :_] nil
|
||||||
|
expr [:set (. columns icolumn) expr])))
|
||||||
|
(set analysis.query (.. (self:gen-with-rules analysis) (self:gen-update analysis)))
|
||||||
|
analysis)))
|
||||||
|
|
||||||
|
(setmetatable Compiler {:__call (fn [cls ...] (doto (setmetatable {} {:__index cls} ) (: :new ...)))})
|
63
sqlog/init.fnl
Normal file
63
sqlog/init.fnl
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
(local Compiler (require :sqlog.compiler))
|
||||||
|
(local sqlite (require :diet-sqlite))
|
||||||
|
(local {: SQLITE_ROW} (require :diet-sqlite.codes))
|
||||||
|
|
||||||
|
(local Sqlog {})
|
||||||
|
|
||||||
|
(fn Sqlog.new [self ?dbname]
|
||||||
|
(set self.compiler (Compiler))
|
||||||
|
(set self.db (sqlite.assert (sqlite.open (or ?dbname ":memory:")))))
|
||||||
|
|
||||||
|
(fn Sqlog.deftable [self name ...] (self:execute (self.compiler:deftable name ...)))
|
||||||
|
(fn Sqlog.defrule [self head ...] (self.compiler:defrule head ...))
|
||||||
|
|
||||||
|
(fn multi? [analysis] (and analysis (> (length analysis) 0)))
|
||||||
|
(fn Sqlog.compile-sql [self analysis]
|
||||||
|
(if (multi? analysis) (each [_ a (ipairs analysis)] (self:compile-sql a))
|
||||||
|
(when (= analysis.stmt nil)
|
||||||
|
(let [stmt (sqlite.assert (sqlite.prepare self.db analysis.query))]
|
||||||
|
(sqlite.bind stmt analysis.constants)
|
||||||
|
(set analysis.stmt stmt))))
|
||||||
|
analysis)
|
||||||
|
|
||||||
|
(fn Sqlog.execute [self analysis ?collect-results]
|
||||||
|
(if (multi? analysis) (icollect [_ a (ipairs analysis)] (self:execute a ?collect-results))
|
||||||
|
(do (print "running:" analysis.query (fv analysis.constants))
|
||||||
|
(self:compile-sql analysis)
|
||||||
|
(sqlite.reset analysis.stmt)
|
||||||
|
(if ?collect-results
|
||||||
|
(let [result []]
|
||||||
|
(while (= SQLITE_ROW (sqlite.step analysis.stmt))
|
||||||
|
(table.insert result (collect [icol expr (ipairs analysis.selection)]
|
||||||
|
(match expr
|
||||||
|
[:as _ name] name
|
||||||
|
_ icol)
|
||||||
|
(sqlite.column analysis.stmt icol))))
|
||||||
|
result)
|
||||||
|
(sqlite.step analysis.stmt)))))
|
||||||
|
|
||||||
|
(fn Sqlog.compile-action [self action]
|
||||||
|
(self:compile-sql
|
||||||
|
(match action
|
||||||
|
[:do & actions] (icollect [_ act (ipairs actions)] (self:compile-action act))
|
||||||
|
[:!+ & insert] (self.compiler:insert (table.unpack insert))
|
||||||
|
[:literal] (self.compiler:insert action)
|
||||||
|
[:!- & delete] (self.compiler:delete (table.unpack delete))
|
||||||
|
[:!= & update] (self.compiler:update (table.unpack update))
|
||||||
|
_ (error (.. "No such action " (fv action))))))
|
||||||
|
|
||||||
|
(fn Sqlog.compile-query [self ...] (self:compile-sql (self.compiler:query ...)))
|
||||||
|
|
||||||
|
(fn Sqlog.specify [self action ...]
|
||||||
|
(when (not= action nil)
|
||||||
|
(match action
|
||||||
|
[:* & rule] (self:defrule (table.unpack rule))
|
||||||
|
[:table & params] (self:deftable (table.unpack (icollect [_ param (ipairs params)]
|
||||||
|
(match param [_ name] name))))
|
||||||
|
[:sql query & constants] (self:execute {: query : constants})
|
||||||
|
_ (self:execute (self:compile-action action)))
|
||||||
|
(self:specify ...)))
|
||||||
|
|
||||||
|
(fn Sqlog.query [self ...] (self:execute (self:compile-query ...) true))
|
||||||
|
|
||||||
|
(setmetatable Sqlog {:__call (fn [cls ...] (doto (setmetatable {} {:__index cls} ) (: :new ...)))})
|
31
sqlog/macros.fnl
Normal file
31
sqlog/macros.fnl
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
; [q X] -> q(X) -> [:literal :q [:var :X]]
|
||||||
|
; [q :x] -> q(x) -> [:literal :q [:const :x]]
|
||||||
|
; [q #(+ 1 2)] -> q(3) -> [:literal q [:const (+ 1 2)]]
|
||||||
|
|
||||||
|
(fn clause [c]
|
||||||
|
(match c
|
||||||
|
(where [escape expr] (list? c) (= (tostring escape) :unquote)) expr
|
||||||
|
(where [escape expr] (list? c) (= (tostring escape) :hashfn)) `[:const ,expr]
|
||||||
|
(where [name & params] (sequence? c)) `[:literal ,(tostring name) ,(icollect [_ param (ipairs params)] (clause param))]
|
||||||
|
(where [head & args] (list? c) (sym? head)) (icollect [_ expr (ipairs args) :into [(tostring head)]] (clause expr))
|
||||||
|
(where c (list? c)) (icollect [_ val (ipairs c)] (clause val))
|
||||||
|
(where v (sym? c)) `[:var ,(tostring v)]
|
||||||
|
_ `[:const ,c]))
|
||||||
|
|
||||||
|
(fn clauses [...]
|
||||||
|
(icollect [_ c (ipairs [...]) :into `(values)] (clause c)))
|
||||||
|
|
||||||
|
(fn defrule [s ...] `(: ,s :defrule ,(clauses ...)))
|
||||||
|
|
||||||
|
(fn defrules [s ...]
|
||||||
|
(icollect [_ rule (ipairs [...]) :into `(do)]
|
||||||
|
(defrule s (table.unpack rule))))
|
||||||
|
|
||||||
|
(fn query [s ...] `(: ,s :query ,(clauses ...)))
|
||||||
|
(fn insert [s ...] `(: ,s :insert ,(clauses ...)))
|
||||||
|
(fn delete [s ...] `(: ,s :delete ,(clauses ...)))
|
||||||
|
(fn update [s ...] `(: ,s :update ,(clauses ...)))
|
||||||
|
(fn specify [s ...] `(: ,s :specify ,(clauses ...)))
|
||||||
|
|
||||||
|
{: clause : clauses :$ clauses : defrule : defrules : query : insert : delete : update : specify}
|
||||||
|
|
18
sqlog/sqltest.fnl
Normal file
18
sqlog/sqltest.fnl
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
(local Sqlog (require :sqlog))
|
||||||
|
(local {: show} (require :inspector.debug))
|
||||||
|
(import-macros {: $ : query : specify} :sqlog.macros)
|
||||||
|
|
||||||
|
(local s (Sqlog))
|
||||||
|
(specify s
|
||||||
|
(table parent parent child)
|
||||||
|
(* [generation name (|| name " jr") 2] [parent name (|| name " jr")])
|
||||||
|
(* [generation name (|| name " iii") 3] [ancestor name (|| name " iii")])
|
||||||
|
(* [ancestor x y 2] [parent x y])
|
||||||
|
(* [ancestor x y (+ gen 1)] [parent x z] [ancestor z y gen])
|
||||||
|
[parent :bob "bob jr"]
|
||||||
|
[parent "bob jr" "bob iii"]
|
||||||
|
[parent :bob #(.. :fred :dy)]
|
||||||
|
[parent :fred :jim]
|
||||||
|
[parent :fred :betty])
|
||||||
|
(show (query s [ancestor (case descendant (when "bob jr" :bob) (else :jim)) descendant gen]))
|
||||||
|
|
|
@ -15,8 +15,8 @@ syntax.add {
|
||||||
{ pattern = "0x[%da-fA-F]+", type = "number" },
|
{ pattern = "0x[%da-fA-F]+", type = "number" },
|
||||||
{ pattern = "-?%d+[%d%.]*", type = "number" },
|
{ pattern = "-?%d+[%d%.]*", type = "number" },
|
||||||
{ pattern = "-?%.?%d+", type = "number" },
|
{ pattern = "-?%.?%d+", type = "number" },
|
||||||
{ pattern = "%f[^(][^()'%s\"]+", type = "function" },
|
{ pattern = "%f[^(%[%{][^()'%s\"]+", type = "function" },
|
||||||
{ pattern = "[^()'%s\"]+", type = "symbol" },
|
{ pattern = "[^()%s\"]+", type = "symbol" },
|
||||||
},
|
},
|
||||||
|
|
||||||
symbols = {
|
symbols = {
|
||||||
|
|
17
wrap.fnl
17
wrap.fnl
|
@ -7,22 +7,29 @@
|
||||||
(local keymap (require :core.keymap))
|
(local keymap (require :core.keymap))
|
||||||
(local translate (require :core.doc.translate))
|
(local translate (require :core.doc.translate))
|
||||||
|
|
||||||
(command.add "core.docview" {
|
(fn get-modname []
|
||||||
"fennel:eval" #(editor.inline-eval #(fv (fennel.eval $1 {:env _G :compiler-env _G}) {}))
|
|
||||||
"lume:hotswap" (fn []
|
|
||||||
(local modname
|
|
||||||
(-> core.active_view.doc.filename
|
(-> core.active_view.doc.filename
|
||||||
(: :gsub "%.%a+$" "")
|
(: :gsub "%.%a+$" "")
|
||||||
(: :gsub "/" ".")
|
(: :gsub "/" ".")
|
||||||
(: :gsub "^data%." "")
|
(: :gsub "^data%." "")
|
||||||
(: :gsub "%.init$" "")))
|
(: :gsub "%.init$" "")))
|
||||||
|
|
||||||
|
(command.add "core.docview" {
|
||||||
|
"lume:hotswap" (fn []
|
||||||
|
(local modname (get-modname))
|
||||||
(core.log (.. "Hotswapping " modname))
|
(core.log (.. "Hotswapping " modname))
|
||||||
(local (mod err) (util.hotswap modname))
|
(local (mod err) (util.hotswap modname))
|
||||||
(when (not= err nil) (print err) (error err)))
|
(when (not= err nil) (print err) (error err)))
|
||||||
|
"fennel:unload-macro" (fn []
|
||||||
|
(let [modname (get-modname)]
|
||||||
|
(if (. fennel.macro-loaded modname)
|
||||||
|
(do (core.log (.. "Unloading macro module " modname))
|
||||||
|
(tset fennel.macro-loaded modname nil))
|
||||||
|
(core.log (.. modname " was not a loaded macro module")))))
|
||||||
})
|
})
|
||||||
(keymap.add {
|
(keymap.add {
|
||||||
"alt+e" "fennel:eval"
|
|
||||||
"alt+r" "lume:hotswap"
|
"alt+r" "lume:hotswap"
|
||||||
|
"alt+u" "fennel:unload-macro"
|
||||||
})
|
})
|
||||||
|
|
||||||
{}
|
{}
|
||||||
|
|
Loading…
Reference in a new issue