diff options
-rw-r--r-- | .mailmap | 1 | ||||
-rw-r--r-- | .travis.yml | 3 | ||||
-rw-r--r-- | CHANGELOG | 18 | ||||
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | include/urweb/urweb_cpp.h | 15 | ||||
-rw-r--r-- | lib/js/urweb.js | 68 | ||||
-rw-r--r-- | lib/ur/basis.urs | 15 | ||||
-rw-r--r-- | lib/ur/top.ur | 20 | ||||
-rw-r--r-- | src/c/memmem.c | 16 | ||||
-rw-r--r-- | src/c/openssl.c | 32 | ||||
-rw-r--r-- | src/c/request.c | 4 | ||||
-rw-r--r-- | src/c/urweb.c | 59 | ||||
-rw-r--r-- | src/compiler.sml | 2 | ||||
-rw-r--r-- | src/elisp/urweb-mode.el | 1 | ||||
-rw-r--r-- | src/main.mlton.sml | 10 | ||||
-rw-r--r-- | src/mysql.sml | 4 | ||||
-rw-r--r-- | src/postgres.sml | 12 | ||||
-rw-r--r-- | src/settings.sml | 13 | ||||
-rw-r--r-- | tests/math.ur | 26 | ||||
-rw-r--r-- | tests/normalizeTable.ur | 50 | ||||
-rw-r--r-- | tests/normalizeTable.urp | 1 | ||||
-rw-r--r-- | tests/normalizeTable.urs | 1 | ||||
-rw-r--r-- | tests/timeout.ur | 22 | ||||
-rw-r--r-- | tests/timeout.urp | 7 | ||||
-rw-r--r-- | tests/timeout.urs | 1 |
25 files changed, 355 insertions, 48 deletions
diff --git a/.mailmap b/.mailmap new file mode 100644 index 00000000..abfaa565 --- /dev/null +++ b/.mailmap @@ -0,0 +1 @@ +<bbaren@mit.edu> <bbaren at mit.edu> diff --git a/.travis.yml b/.travis.yml index 0b2b8b90..df4e4abc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,7 +20,8 @@ before_install: - if command -v apt-get &>/dev/null; then sudo apt-get update -qq; fi - if command -v apt-get &>/dev/null; then sudo apt-get install -y mlton; fi - if command -v brew &>/dev/null; then brew update; fi - - if command -v brew &>/dev/null; then brew tap MLton/mlton; fi + - if command -v brew &>/dev/null; then brew uninstall libtool; fi + - if command -v brew &>/dev/null; then brew install libtool; fi - if command -v brew &>/dev/null; then brew install openssl mlton; fi - if command -v brew &>/dev/null; then export CONFIGURE_ARGS="--with-openssl=/usr/local/opt/openssl"; fi @@ -1,4 +1,22 @@ ======== +20160805 +======== + +- Compatibility fixes for C compilers and OpenSSL +- Starting to change SQL functions to return results in most natural order + - Step 1: queryL +- Bug fixes + +======== +20160621 +======== + +- Client-side: detect session timeout and ask the user to reload +- New Basis math functions: abs, acos, asin, atan, atan2, cos, exp, floor, log, pow, sqrt, sin +- Compatibility fixes for newer C and SML compilers +- Bug fixes + +======== 20160515 ======== diff --git a/configure.ac b/configure.ac index 2a09d3b5..196d597c 100644 --- a/configure.ac +++ b/configure.ac @@ -1,4 +1,4 @@ -AC_INIT([urweb], [20160515]) +AC_INIT([urweb], [20160805]) WORKING_VERSION=0 AC_USE_SYSTEM_EXTENSIONS diff --git a/include/urweb/urweb_cpp.h b/include/urweb/urweb_cpp.h index 5b6c6221..0d5f5e0e 100644 --- a/include/urweb/urweb_cpp.h +++ b/include/urweb/urweb_cpp.h @@ -387,6 +387,19 @@ uw_Basis_float uw_Basis_floatFromInt(struct uw_context *, uw_Basis_int); uw_Basis_int uw_Basis_ceil(struct uw_context *, uw_Basis_float); uw_Basis_int uw_Basis_trunc(struct uw_context *, uw_Basis_float); uw_Basis_int uw_Basis_round(struct uw_context *, uw_Basis_float); +uw_Basis_int uw_Basis_floor(struct uw_context *, uw_Basis_float); + +uw_Basis_float uw_Basis_pow(struct uw_context *, uw_Basis_float, uw_Basis_float); +uw_Basis_float uw_Basis_sqrt(struct uw_context *, uw_Basis_float); +uw_Basis_float uw_Basis_sin(struct uw_context *, uw_Basis_float); +uw_Basis_float uw_Basis_cos(struct uw_context *, uw_Basis_float); +uw_Basis_float uw_Basis_log(struct uw_context *, uw_Basis_float); +uw_Basis_float uw_Basis_exp(struct uw_context *, uw_Basis_float); +uw_Basis_float uw_Basis_asin(struct uw_context *, uw_Basis_float); +uw_Basis_float uw_Basis_acos(struct uw_context *, uw_Basis_float); +uw_Basis_float uw_Basis_atan(struct uw_context *, uw_Basis_float); +uw_Basis_float uw_Basis_atan2(struct uw_context *, uw_Basis_float, uw_Basis_float); +uw_Basis_float uw_Basis_abs(struct uw_context *, uw_Basis_float); uw_Basis_string uw_Basis_atom(struct uw_context *, uw_Basis_string); uw_Basis_string uw_Basis_css_url(struct uw_context *, uw_Basis_string); @@ -417,4 +430,6 @@ uw_Sqlcache_Value *uw_Sqlcache_check(struct uw_context *, uw_Sqlcache_Cache *, c void *uw_Sqlcache_store(struct uw_context *, uw_Sqlcache_Cache *, char **, uw_Sqlcache_Value *); void *uw_Sqlcache_flush(struct uw_context *, uw_Sqlcache_Cache *, char **); +int strcmp_nullsafe(const char *, const char *); + #endif diff --git a/lib/js/urweb.js b/lib/js/urweb.js index 410a0e23..68e7979d 100644 --- a/lib/js/urweb.js +++ b/lib/js/urweb.js @@ -116,6 +116,48 @@ function pow(n, m) { return Math.pow(n, m); } +function sqrt(n){ + return Math.sqrt(n); +} + +function sin(n){ + return Math.sin(n); +} + +function cos(n){ + return Math.cos(n); +} + +function log(n){ + return Math.log(n); +} + +function exp(n){ + return Math.exp(n); +} + +function asin(n){ + return Math.asin(n); +} +function acos(n){ + return Math.acos(n); +} + +function atan(n){ + return Math.atan(n); +} + +function atan2(n, m){ + return Math.atan2(n, m); +} + +function floor(n){ + return Math.floor(n); +} + +function abs(n){ + return Math.abs(n); +} // Time, represented as counts of microseconds since the epoch @@ -837,10 +879,12 @@ function normalizeTable(table) { for (script = table.firstChild; script && script != tbody; script = next) { next = script.nextSibling; - if (firstChild) - tbody.insertBefore(script, firstChild); - else - tbody.appendChild(script); + if (script.tagName === "SCRIPT") { + if (firstChild) + tbody.insertBefore(script, firstChild); + else + tbody.appendChild(script); + } } return; @@ -1662,10 +1706,11 @@ function newChannel() { function listener() { var uri = path_join(url_prefix, ".msgs"); var xhr = getXHR(); - var tid, orsc, onTimeout; + var tid, orsc, onTimeout, lastTick; var connect = function () { xhr.onreadystatechange = orsc; + lastTick = new Date().getTime(); tid = window.setTimeout(onTimeout, timeout * 500); requestUri(xhr, uri, false, false); } @@ -1755,8 +1800,19 @@ function listener() { }; onTimeout = function() { + var thisTick = new Date().getTime(); xhrFinished(xhr); - connect(); + + if (thisTick - lastTick > timeout * 1000) { + if (confirm("The session for this page has expired. Please choose \"OK\" to reload.")) { + if (isPost) + history.back(); + else + location.reload(); + } + } else { + connect(); + } }; connect(); diff --git a/lib/ur/basis.urs b/lib/ur/basis.urs index 883cc5b1..1163daed 100644 --- a/lib/ur/basis.urs +++ b/lib/ur/basis.urs @@ -152,7 +152,20 @@ val float : int -> float val ceil : float -> int val trunc : float -> int val round : float -> int - +val floor : float -> int + +(** * Basic Math *) + +val sqrt : float -> float +val sin : float -> float +val cos : float -> float +val log : float -> float +val exp : float -> float +val asin : float -> float +val acos : float -> float +val atan : float -> float +val atan2 : float -> float -> float +val abs: float -> float (** * Time *) diff --git a/lib/ur/top.ur b/lib/ur/top.ur index e831b4f7..6c6c896c 100644 --- a/lib/ur/top.ur +++ b/lib/ur/top.ur @@ -225,15 +225,23 @@ fun query1' [t ::: Name] [fs ::: {Type}] [state ::: Type] (q : sql_query [] [] [ (f : $fs -> state -> state) (i : state) = query q (fn r s => return (f r.t s)) i +val rev = fn [a] => + let + fun rev' acc (ls : list a) = + case ls of + [] => acc + | x :: ls => rev' (x :: acc) ls + in + rev' [] + end + fun queryL [tables] [exps] [tables ~ exps] (q : sql_query [] [] tables exps) = - query q - (fn r ls => return (r :: ls)) - [] + ls <- query q (fn r ls => return (r :: ls)) []; + return (rev ls) fun queryL1 [t ::: Name] [fs ::: {Type}] (q : sql_query [] [] [t = fs] []) = - query q - (fn r ls => return (r.t :: ls)) - [] + ls <- query q (fn r ls => return (r.t :: ls)) []; + return (rev ls) fun queryI [tables ::: {{Type}}] [exps ::: {Type}] [tables ~ exps] (q : sql_query [] [] tables exps) diff --git a/src/c/memmem.c b/src/c/memmem.c index 68526714..f31f4e31 100644 --- a/src/c/memmem.c +++ b/src/c/memmem.c @@ -38,6 +38,8 @@ * POSSIBILITY OF SUCH DAMAGE. */ +// Function renamed by Adam Chlipala in 2016. + #include <sys/cdefs.h> #if defined(LIBC_SCCS) && !defined(lint) __RCSID("$NetBSD$"); @@ -53,13 +55,17 @@ __RCSID("$NetBSD$"); #endif /* - * memmem() returns the location of the first occurence of data + * urweb_memmem() returns the location of the first occurence of data * pattern b2 of size len2 in memory block b1 of size len1 or * NULL if none is found. */ void * -memmem(const void *b1, size_t len1, const void *b2, size_t len2) +urweb_memmem(const void *b1, size_t len1, const void *b2, size_t len2) { + /* Sanity check */ + if(!(b1 != NULL && b2 != NULL && len1 != 0 && len2 != 0)) + return NULL; + /* Initialize search pointer */ char *sp = (char *) b1; @@ -69,16 +75,12 @@ memmem(const void *b1, size_t len1, const void *b2, size_t len2) /* Intialize end of search address space pointer */ char *eos = sp + len1 - len2; - /* Sanity check */ - if(!(b1 && b2 && len1 && len2)) - return NULL; - while (sp <= eos) { if (*sp == *pp) if (memcmp(sp, pp, len2) == 0) return sp; - sp++; + sp++; } return NULL; diff --git a/src/c/openssl.c b/src/c/openssl.c index 15c4de5e..5982b831 100644 --- a/src/c/openssl.c +++ b/src/c/openssl.c @@ -1,6 +1,5 @@ #include "config.h" -#include <assert.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> @@ -8,17 +7,13 @@ #include <fcntl.h> #include <stdio.h> #include <string.h> -#include <pthread.h> -#include <openssl/crypto.h> +#include <openssl/opensslv.h> #include <openssl/sha.h> #include <openssl/rand.h> #define PASSSIZE 4 -// OpenSSL locks array. See threads(3SSL). -static pthread_mutex_t *openssl_locks; - int uw_hash_blocksize = 32; static int password[PASSSIZE]; @@ -33,6 +28,17 @@ static void random_password() { } } +#if OPENSSL_VERSION_NUMBER < 0x10100000L +// We're using OpenSSL <1.1, so we need to specify threading callbacks. See +// threads(3SSL). + +#include <assert.h> +#include <pthread.h> + +#include <openssl/crypto.h> + +static pthread_mutex_t *openssl_locks; + // OpenSSL callbacks #ifdef PTHREAD_T_IS_POINTER static void thread_id(CRYPTO_THREADID *const result) { @@ -60,7 +66,7 @@ static void lock_or_unlock(const int mode, const int type, const char *file, } } -void uw_init_crypto() { +static void init_openssl() { int i; // Set up OpenSSL. assert(openssl_locks == NULL); @@ -74,6 +80,18 @@ void uw_init_crypto() { } CRYPTO_THREADID_set_callback(thread_id); CRYPTO_set_locking_callback(lock_or_unlock); +} + +#else +// We're using OpenSSL >=1.1, which is thread-safe by default. We don't need to +// do anything here. + +static void init_openssl() {} + +#endif // OPENSSL_VERSION_NUMBER < 0x10100000L + +void uw_init_crypto() { + init_openssl(); // Prepare signatures. if (uw_sig_file) { int fd; diff --git a/src/c/request.c b/src/c/request.c index cad84cb2..a7f23851 100644 --- a/src/c/request.c +++ b/src/c/request.c @@ -16,7 +16,7 @@ #define MAX_RETRIES 5 -void *memmem(const void *b1, size_t len1, const void *b2, size_t len2); +void *urweb_memmem(const void *b1, size_t len1, const void *b2, size_t len2); static int try_rollback(uw_context ctx, int will_retry, void *logger_data, uw_logger log_error) { int r = uw_rollback(ctx, will_retry); @@ -418,7 +418,7 @@ request_result uw_request(uw_request_context rc, uw_context ctx, } } - part = memmem(after_sub_headers, body + body_len - after_sub_headers, boundary, boundary_len); + part = urweb_memmem(after_sub_headers, body + body_len - after_sub_headers, boundary, boundary_len); if (!part) { log_error(logger_data, "Missing boundary after multipart payload\n"); return FAILED; diff --git a/src/c/urweb.c b/src/c/urweb.c index c23366fb..afe8457b 100644 --- a/src/c/urweb.c +++ b/src/c/urweb.c @@ -4517,6 +4517,54 @@ uw_Basis_int uw_Basis_round(uw_context ctx, uw_Basis_float n) { return round(n); } +uw_Basis_int uw_Basis_floor(uw_context ctx, uw_Basis_float n) { + return floor(n); +} + +uw_Basis_float uw_Basis_pow(uw_context ctx, uw_Basis_float n, uw_Basis_float m) { + return pow(n,m); +} + +uw_Basis_float uw_Basis_sqrt(uw_context ctx, uw_Basis_float n) { + return sqrt(n); +} + +uw_Basis_float uw_Basis_sin(uw_context ctx, uw_Basis_float n) { + return sin(n); +} + +uw_Basis_float uw_Basis_cos(uw_context ctx, uw_Basis_float n) { + return cos(n); +} + +uw_Basis_float uw_Basis_log(uw_context ctx, uw_Basis_float n) { + return log(n); +} + +uw_Basis_float uw_Basis_exp(uw_context ctx, uw_Basis_float n) { + return exp(n); +} + +uw_Basis_float uw_Basis_asin(uw_context ctx, uw_Basis_float n) { + return asin(n); +} + +uw_Basis_float uw_Basis_acos(uw_context ctx, uw_Basis_float n) { + return acos(n); +} + +uw_Basis_float uw_Basis_atan(uw_context ctx, uw_Basis_float n) { + return atan(n); +} + +uw_Basis_float uw_Basis_atan2(uw_context ctx, uw_Basis_float n, uw_Basis_float m) { + return atan2(n, m); +} + +uw_Basis_float uw_Basis_abs(uw_context ctx, uw_Basis_float n) { + return fabs(n); +} + uw_Basis_string uw_Basis_atom(uw_context ctx, uw_Basis_string s) { char *p; @@ -4713,7 +4761,7 @@ uw_Sqlcache_Value *uw_Sqlcache_check(uw_context ctx, uw_Sqlcache_Cache *cache, c char *key = uw_Sqlcache_allocKeyBuffer(keys, numKeys); char *buf = key; time_t timeInvalid = cache->timeInvalid; - uw_Sqlcache_Entry *entry; + uw_Sqlcache_Entry *entry = NULL; if (numKeys == 0) { entry = cache->table; if (!entry) { @@ -4748,7 +4796,7 @@ static void uw_Sqlcache_storeCommitOne(uw_Sqlcache_Cache *cache, char **keys, uw pthread_rwlock_wrlock(&cache->lockIn); size_t numKeys = cache->numKeys; time_t timeNow = uw_Sqlcache_getTimeNow(cache); - uw_Sqlcache_Entry *entry; + uw_Sqlcache_Entry *entry = NULL; if (numKeys == 0) { entry = cache->table; if (!entry) { @@ -4920,3 +4968,10 @@ void uw_Sqlcache_flush(uw_context ctx, uw_Sqlcache_Cache *cache, char **keys) { } pthread_rwlock_unlock(&cache->lockIn); } + +int strcmp_nullsafe(const char *str1, const char *str2) { + if (str1) + return strcmp(str1, str2); + else + return 1; +} diff --git a/src/compiler.sml b/src/compiler.sml index 76743fad..dccda06d 100644 --- a/src/compiler.sml +++ b/src/compiler.sml @@ -434,7 +434,7 @@ fun parseUrp' accLibs fname = sql = NONE, debug = Settings.getDebug (), profile = false, - timeout = 60, + timeout = 120, ffi = [], link = [], linker = NONE, diff --git a/src/elisp/urweb-mode.el b/src/elisp/urweb-mode.el index bc71a052..d1eec2a1 100644 --- a/src/elisp/urweb-mode.el +++ b/src/elisp/urweb-mode.el @@ -395,7 +395,6 @@ This mode runs `urweb-mode-hook' just before exiting. ;; Treat paragraph-separators in comments as paragraph-separators. (set (make-local-variable 'paragraph-separate) (concat "\\([ \t]*\\*)?\\)?\\(" paragraph-separate "\\)")) - (set (make-local-variable 'require-final-newline) t) ;; forward-sexp-function is an experimental variable in my hacked Emacs. (set (make-local-variable 'forward-sexp-function) 'urweb-user-forward-sexp) ;; For XEmacs diff --git a/src/main.mlton.sml b/src/main.mlton.sml index f595134f..6d368106 100644 --- a/src/main.mlton.sml +++ b/src/main.mlton.sml @@ -246,7 +246,7 @@ fun oneRun args = fun send (sock, s) = let - val n = Socket.sendVec (sock, Word8VectorSlice.full (Vector.map (Word8.fromInt o ord) s)) + val n = Socket.sendVec (sock, Word8VectorSlice.full (MLton.Word8Vector.fromPoly (Vector.map (Word8.fromInt o ord) (MLton.CharVector.toPoly s)))) in if n >= size s then () @@ -272,7 +272,7 @@ val () = case CommandLine.arguments () of val s = if CharVector.exists (fn ch => ch = #"\n") buf then "" else - Vector.map (chr o Word8.toInt) (Socket.recvVec (sock, 1024)) + MLton.CharVector.fromPoly (Vector.map (chr o Word8.toInt) (MLton.Word8Vector.toPoly (Socket.recvVec (sock, 1024)))) val s = buf ^ s val (befor, after) = Substring.splitl (fn ch => ch <> #"\n") (Substring.full s) in @@ -345,12 +345,12 @@ val () = case CommandLine.arguments () of let val v = Socket.recvVec (sock, 1024) in - if Vector.length v = 0 then + if Word8Vector.length v = 0 then OS.Process.failure else let - val s = Vector.map (chr o Word8.toInt) v - val last = Vector.sub (v, Vector.length v - 1) + val s = MLton.CharVector.fromPoly (Vector.map (chr o Word8.toInt) (MLton.Word8Vector.toPoly v)) + val last = Word8Vector.sub (v, Word8Vector.length v - 1) val (rc, s) = if last = Word8.fromInt 1 then (SOME OS.Process.success, String.substring (s, 0, size s - 1)) else if last = Word8.fromInt 2 then diff --git a/src/mysql.sml b/src/mysql.sml index 539428f6..52e4921e 100644 --- a/src/mysql.sml +++ b/src/mysql.sml @@ -867,7 +867,7 @@ fun queryCommon {loc, query, cols, doCols} = newline, string "uw_error(ctx, FATAL, \"", string (ErrorMsg.spanToString loc), - string ": Error reseting statement: %s\\n%s\", ", + string ": Error resetting statement: %s\\n%s\", ", query, string ", mysql_error(conn->conn));", newline], @@ -931,7 +931,7 @@ fun queryCommon {loc, query, cols, doCols} = string "if (mysql_stmt_reset(stmt)) uw_error(ctx, FATAL, \"", string (ErrorMsg.spanToString loc), - string ": Error reseting statement: %s\\n%s\", ", + string ": Error resetting statement: %s\\n%s\", ", query, string ", mysql_error(conn->conn));", newline, diff --git a/src/postgres.sml b/src/postgres.sml index ddfe0ad6..404384d2 100644 --- a/src/postgres.sml +++ b/src/postgres.sml @@ -443,7 +443,7 @@ fun init {dbstring, prepared = ss, tables, views, sequences} = newline, newline, string "if (PQresultStatus(res) != PGRES_COMMAND_OK) {", - box [string "if (!strcmp(PQresultErrorField(res, PG_DIAG_SQLSTATE), \"40001\")) {", + box [string "if (!strcmp_nullsafe(PQresultErrorField(res, PG_DIAG_SQLSTATE), \"40001\")) {", box [newline, string "PQclear(res);", newline, @@ -451,7 +451,7 @@ fun init {dbstring, prepared = ss, tables, views, sequences} = newline], string "}", newline, - string "if (!strcmp(PQresultErrorField(res, PG_DIAG_SQLSTATE), \"40P01\")) {", + string "if (!strcmp_nullsafe(PQresultErrorField(res, PG_DIAG_SQLSTATE), \"40P01\")) {", box [newline, string "PQclear(res);", newline, @@ -629,7 +629,7 @@ fun queryCommon {loc, query, cols, doCols} = string "if (PQresultStatus(res) != PGRES_TUPLES_OK) {", newline, - box [string "if (!strcmp(PQresultErrorField(res, PG_DIAG_SQLSTATE), \"40001\")) {", + box [string "if (!strcmp_nullsafe(PQresultErrorField(res, PG_DIAG_SQLSTATE), \"40001\")) {", box [newline, string "PQclear(res);", newline, @@ -637,7 +637,7 @@ fun queryCommon {loc, query, cols, doCols} = newline], string "}", newline, - string "if (!strcmp(PQresultErrorField(res, PG_DIAG_SQLSTATE), \"40P01\")) {", + string "if (!strcmp_nullsafe(PQresultErrorField(res, PG_DIAG_SQLSTATE), \"40P01\")) {", box [newline, string "PQclear(res);", newline, @@ -800,7 +800,7 @@ fun dmlCommon {loc, dml, mode} = string "if (PQresultStatus(res) != PGRES_COMMAND_OK) {", newline, - box [string "if (!strcmp(PQresultErrorField(res, PG_DIAG_SQLSTATE), \"40001\")) {", + box [string "if (!strcmp_nullsafe(PQresultErrorField(res, PG_DIAG_SQLSTATE), \"40001\")) {", box [newline, string "PQclear(res);", newline, @@ -808,7 +808,7 @@ fun dmlCommon {loc, dml, mode} = newline], string "}", newline, - string "if (!strcmp(PQresultErrorField(res, PG_DIAG_SQLSTATE), \"40P01\")) {", + string "if (!strcmp_nullsafe(PQresultErrorField(res, PG_DIAG_SQLSTATE), \"40P01\")) {", box [newline, string "PQclear(res);", newline, diff --git a/src/settings.sml b/src/settings.sml index 85cab207..b72789df 100644 --- a/src/settings.sml +++ b/src/settings.sml @@ -335,6 +335,19 @@ val jsFuncsBase = basisM [("alert", "alert"), ("ceil", "ceil"), ("trunc", "trunc"), ("round", "round"), + ("floor", "floor"), + + ("pow", "pow"), + ("sqrt", "sqrt"), + ("sin", "sin"), + ("cos", "cos"), + ("log", "log"), + ("exp", "exp"), + ("asin", "asin"), + ("acos", "acos"), + ("atan", "atan"), + ("atan2", "atan2"), + ("abs", "abs"), ("now", "now"), ("timeToString", "showTime"), diff --git a/tests/math.ur b/tests/math.ur new file mode 100644 index 00000000..964b73e6 --- /dev/null +++ b/tests/math.ur @@ -0,0 +1,26 @@ +fun main () = return <xml><body> + <button value="Power 2.0 of 2.0!" onclick={fn _ => alert (show (pow 2.0 2.0))}/> + {[(pow 2.0 2.0)]} + <button value="Square root of 25!" onclick={fn _ => alert (show (sqrt 25.0))}/> + {[(sqrt 25.0)]} + <button value="Sin of 0.1!" onclick={fn _ => alert (show (sin 0.1))}/> + {[(sin 0.1)]} + <button value="Cos of 0.1!" onclick={fn _ => alert (show (cos 0.1))}/> + {[(cos 0.1)]} + <button value="log of 0.1!" onclick={fn _ => alert (show (log 0.1))}/> + {[(log 0.1)]} + <button value="Exp of 0.1!" onclick={fn _ => alert (show (exp 0.1))}/> + {[(exp 0.1)]} + <button value="asin of 0.1!" onclick={fn _ => alert (show (asin 0.1))}/> + {[(asin 0.1)]} + <button value="acos of 0.1!" onclick={fn _ => alert (show (acos 0.1))}/> + {[(acos 0.1)]} + <button value="atan of 0.1!" onclick={fn _ => alert (show (atan 0.1))}/> + {[(atan 0.1)]} + <button value="atan2 of 0.1 and -0.2!" onclick={fn _ => alert (show (atan2 0.1 (-0.2)))}/> + {[(atan2 0.1 (-0.2))]} + <button value="floor of 34.5!" onclick={fn _ => alert (show (floor 34.5))}/> + {[(floor 34.5)]} + <button value="abs of -10.0!" onclick={fn _ => alert (show (abs (-10.0)))}/> + {[(abs (-10.0))]} + </body></xml> diff --git a/tests/normalizeTable.ur b/tests/normalizeTable.ur new file mode 100644 index 00000000..be16d935 --- /dev/null +++ b/tests/normalizeTable.ur @@ -0,0 +1,50 @@ +fun main () = +visible <- source True; +return + <xml> + <body> + <section> + <h1>Static table</h1> + <table border=1> + <thead> + <tr> + <th>Column 0</th> + <th>Column 1</th> + </tr> + </thead> + <tbody> + <tr> + <td>A</td> + <td>B</td> + </tr> + </tbody> + </table> + </section> + + <section> + <h1>Dynamic table</h1> + <table border=1> + <thead> + <tr> + <th>Column 0</th> + <th>Column 1</th> + </tr> + </thead> + <tbody> + <dyn signal={ + visible <- signal visible; + return (if visible then + <xml> + <tr> + <td>A</td> + <td>B</td> + </tr> + </xml> + else + <xml></xml>) + }/> + </tbody> + </table> + </section> + </body> + </xml> diff --git a/tests/normalizeTable.urp b/tests/normalizeTable.urp new file mode 100644 index 00000000..e22cda3b --- /dev/null +++ b/tests/normalizeTable.urp @@ -0,0 +1 @@ +normalizeTable diff --git a/tests/normalizeTable.urs b/tests/normalizeTable.urs new file mode 100644 index 00000000..9e80cf40 --- /dev/null +++ b/tests/normalizeTable.urs @@ -0,0 +1 @@ +val main: unit -> transaction page diff --git a/tests/timeout.ur b/tests/timeout.ur new file mode 100644 index 00000000..d96b42bd --- /dev/null +++ b/tests/timeout.ur @@ -0,0 +1,22 @@ +table listeners : { Ch : channel unit } + +fun ping () = + queryI1 (SELECT * FROM listeners) + (fn r => send r.Ch ()) + +fun main () = + ch <- channel; + dml (INSERT INTO listeners(Ch) VALUES ({[ch]})); + count <- source 0; + return <xml><body onload={let + fun loop () = + _ <- recv ch; + c <- get count; + set count (c + 1); + loop () + in + loop () + end}> + <dyn signal={n <- signal count; return (txt n)}/> + <button onclick={fn _ => rpc (ping ())}>Ping</button> + </body></xml> diff --git a/tests/timeout.urp b/tests/timeout.urp new file mode 100644 index 00000000..6d3ca878 --- /dev/null +++ b/tests/timeout.urp @@ -0,0 +1,7 @@ +timeout 2 +rewrite url Timeout/* +database dbname=test +sql timeout.sql +safeGet main + +timeout diff --git a/tests/timeout.urs b/tests/timeout.urs new file mode 100644 index 00000000..6ac44e0b --- /dev/null +++ b/tests/timeout.urs @@ -0,0 +1 @@ +val main : unit -> transaction page |