From 38d3bc508b3b882e81599bdb0e1d4a2572c23dd0 Mon Sep 17 00:00:00 2001 From: Adam Chlipala Date: Thu, 23 Dec 2010 17:46:40 -0500 Subject: [De]serialization of times in JavaScript; proper integer division in JavaScript; Basis.crypt; Top.mkRead'; more aggressive Mono-level inlining, for values of function-y types --- include/urweb.h | 3 +++ lib/js/urweb.js | 2 ++ lib/ur/basis.urs | 5 +++++ lib/ur/top.ur | 7 ++++++- lib/ur/top.urs | 2 ++ src/c/urweb.c | 34 ++++++++++++++++++++++++++++++++++ src/cjr_print.sml | 4 +++- src/cjrize.sml | 2 +- src/iflow.sml | 4 ++-- src/jscomp.sml | 11 ++++++----- src/mono.sml | 4 +++- src/mono_opt.sml | 2 +- src/mono_print.sml | 10 +++++----- src/mono_reduce.sml | 18 ++++++++++++++---- src/mono_util.sml | 4 ++-- src/monoize.sml | 30 +++++++++++++++--------------- src/settings.sml | 1 + src/urweb.grm | 4 +++- 18 files changed, 108 insertions(+), 39 deletions(-) diff --git a/include/urweb.h b/include/urweb.h index 548e77fe..c52e1c26 100644 --- a/include/urweb.h +++ b/include/urweb.h @@ -169,6 +169,7 @@ char *uw_Basis_ensqlBool(uw_Basis_bool); char *uw_Basis_jsifyString(uw_context, uw_Basis_string); char *uw_Basis_jsifyChar(uw_context, uw_Basis_char); char *uw_Basis_jsifyChannel(uw_context, uw_Basis_channel); +char *uw_Basis_jsifyTime(uw_context, uw_Basis_time); uw_Basis_string uw_Basis_intToString(uw_context, uw_Basis_int); uw_Basis_string uw_Basis_floatToString(uw_context, uw_Basis_float); @@ -301,4 +302,6 @@ uw_Basis_string uw_Basis_timef(uw_context, const char *fmt, uw_Basis_time); uw_Basis_time uw_Basis_stringToTimef(uw_context, const char *fmt, uw_Basis_string); uw_Basis_time uw_Basis_stringToTimef_error(uw_context, const char *fmt, uw_Basis_string); +uw_Basis_string uw_Basis_crypt(uw_context, uw_Basis_string key, uw_Basis_string salt); + #endif diff --git a/lib/js/urweb.js b/lib/js/urweb.js index f98476b7..bba58453 100644 --- a/lib/js/urweb.js +++ b/lib/js/urweb.js @@ -19,7 +19,9 @@ function plus(x, y) { return x + y; } function minus(x, y) { return x - y; } function times(x, y) { return x * y; } function div(x, y) { return x / y; } +function divInt(x, y) { var n = x / y; return n < 0 ? Math.ceil(n) : Math.floor(n); } function mod(x, y) { return x % y; } +function modInt(x, y) { var n = x % y; return n < 0 ? Math.ceil(n) : Math.floor(n); } function lt(x, y) { return x < y; } function le(x, y) { return x <= y; } diff --git a/lib/ur/basis.urs b/lib/ur/basis.urs index 8cf516f8..95deb982 100644 --- a/lib/ur/basis.urs +++ b/lib/ur/basis.urs @@ -146,6 +146,11 @@ val minusSeconds : time -> int -> time val timef : string -> time -> string (* Uses strftime() format string *) +(** * Encryption *) + +val crypt : string -> string -> string + + (** HTTP operations *) con http_cookie :: Type -> Type diff --git a/lib/ur/top.ur b/lib/ur/top.ur index 32d06a43..19259e92 100644 --- a/lib/ur/top.ur +++ b/lib/ur/top.ur @@ -89,7 +89,7 @@ fun read_option [t ::: Type] (_ : read t) = None => None | v => Some v) -fun txt [t] [ctx ::: {Unit}] [use ::: {Type}] (_ : show t) (v : t) = +fun txt [t] [ctx ::: {Unit}] [use ::: {Type}] (_ : show t) (v : t) : xml ctx use [] = cdata (show v) fun map0 [K] [tf :: K -> Type] (f : t :: K -> tf t) [r ::: {K}] (fl : folder r) = @@ -343,3 +343,8 @@ fun eqNullable' [tables ::: {{Type}}] [agg ::: {{Type}}] [exps ::: {Type}] case e2 of None => (SQL {e1} IS NULL) | Some _ => sql_binary sql_eq e1 (sql_inject e2) + +fun mkRead' [t ::: Type] (f : string -> option t) (name : string) : read t = + mkRead (fn s => case f s of + None => error Invalid {txt name}: {txt s} + | Some v => v) f diff --git a/lib/ur/top.urs b/lib/ur/top.urs index a18bf437..74b04ed1 100644 --- a/lib/ur/top.urs +++ b/lib/ur/top.urs @@ -231,3 +231,5 @@ val eqNullable' : tables ::: {{Type}} -> agg ::: {{Type}} -> exps ::: {Type} -> sql_exp tables agg exps (option t) -> option t -> sql_exp tables agg exps bool + +val mkRead' : t ::: Type -> (string -> option t) -> string -> read t diff --git a/src/c/urweb.c b/src/c/urweb.c index a09978cd..efe50591 100644 --- a/src/c/urweb.c +++ b/src/c/urweb.c @@ -13,6 +13,7 @@ #include #include #include +#include #include @@ -2006,6 +2007,27 @@ uw_unit uw_Basis_htmlifyFloat_w(uw_context ctx, uw_Basis_float n) { return uw_unit_v; } +char *uw_Basis_jsifyTime(uw_context ctx, uw_Basis_time n) { + int len; + char *r; + + uw_check_heap(ctx, INTS_MAX); + r = ctx->heap.front; + sprintf(r, "%lld%n", (uw_Basis_int)n, &len); + ctx->heap.front += len+1; + return r; +} + +uw_unit uw_Basis_jsifyInt_w(uw_context ctx, uw_Basis_time n) { + int len; + + uw_check(ctx, INTS_MAX); + sprintf(ctx->page.front, "%lld%n", (uw_Basis_int)n, &len); + ctx->page.front += len; + + return uw_unit_v; +} + char *uw_Basis_htmlifyString(uw_context ctx, uw_Basis_string s) { char *r, *s2; @@ -3568,3 +3590,15 @@ failure_kind uw_runCallback(uw_context ctx, void (*callback)(uw_context)) { return r; } + +uw_Basis_string uw_Basis_crypt(uw_context ctx, uw_Basis_string key, uw_Basis_string salt) { + struct crypt_data *data; + + if ((data = uw_get_global(ctx, "crypt")) == NULL) { + data = malloc(sizeof(struct crypt_data)); + data->initialized = 0; + uw_set_global(ctx, "crypt", data, free); + } + + return uw_strdup(ctx, crypt_r(key, salt, data)); +} diff --git a/src/cjr_print.sml b/src/cjr_print.sml index b4f75eb5..53060ab2 100644 --- a/src/cjr_print.sml +++ b/src/cjr_print.sml @@ -635,7 +635,9 @@ fun unurlify fromClient env (t, loc) = string (Int.toString (size x')), string "] == 0 || request[", string (Int.toString (size x')), - string ("] == '/')) ? __uwc_" ^ ident x' ^ "_" ^ Int.toString n), + string "] == '/')) ? (request += ", + string (Int.toString (size x')), + string (", (*request == '/' ? ++request : NULL), __uwc_" ^ ident x' ^ "_" ^ Int.toString n ^ ")"), space, string ":", space, diff --git a/src/cjrize.sml b/src/cjrize.sml index 9c297fad..2c13e494 100644 --- a/src/cjrize.sml +++ b/src/cjrize.sml @@ -300,7 +300,7 @@ fun cifyExp (eAll as (e, loc), sm) = in ((L'.EUnop (s, e1), loc), sm) end - | L.EBinop (s, e1, e2) => + | L.EBinop (_, s, e1, e2) => let val (e1, sm) = cifyExp (e1, sm) val (e2, sm) = cifyExp (e2, sm) diff --git a/src/iflow.sml b/src/iflow.sml index c0e92cb1..f6e03271 100644 --- a/src/iflow.sml +++ b/src/iflow.sml @@ -1965,7 +1965,7 @@ fun evalExp env (e as (_, loc)) k = | EAbs _ => default () | EUnop (s, e1) => evalExp env e1 (fn e1 => k (Func (Other s, [e1]))) - | EBinop (s, e1, e2) => evalExp env e1 (fn e1 => evalExp env e2 (fn e2 => k (Func (Other s, [e1, e2])))) + | EBinop (_, s, e1, e2) => evalExp env e1 (fn e1 => evalExp env e2 (fn e2 => k (Func (Other s, [e1, e2])))) | ERecord xets => let fun doFields (xes, acc) = @@ -2352,7 +2352,7 @@ fun check file = end | EAbs (x, t1, t2, e) => (EAbs (x, t1, t2, doExp (Unknown :: env) e), loc) | EUnop (uo, e1) => (EUnop (uo, doExp env e1), loc) - | EBinop (bo, e1, e2) => (EBinop (bo, doExp env e1, doExp env e2), loc) + | EBinop (bi, bo, e1, e2) => (EBinop (bi, bo, doExp env e1, doExp env e2), loc) | ERecord xets => (ERecord (map (fn (x, e, t) => (x, doExp env e, t)) xets), loc) | EField (e1, f) => (EField (doExp env e1, f), loc) | ECase (e, pes, ts) => diff --git a/src/jscomp.sml b/src/jscomp.sml index 992a2e30..3b859814 100644 --- a/src/jscomp.sml +++ b/src/jscomp.sml @@ -126,6 +126,7 @@ fun process file = | TFfi ("Basis", "int") => ((EFfiApp ("Basis", "htmlifyInt", [e]), loc), st) | TFfi ("Basis", "float") => ((EFfiApp ("Basis", "htmlifyFloat", [e]), loc), st) | TFfi ("Basis", "channel") => ((EFfiApp ("Basis", "jsifyChannel", [e]), loc), st) + | TFfi ("Basis", "time") => ((EFfiApp ("Basis", "jsifyTime", [e]), loc), st) | TFfi ("Basis", "bool") => ((ECase (e, [((PCon (Enum, PConFfi {mod = "Basis", @@ -701,7 +702,7 @@ fun process file = str ",null)}"], st) end - | EBinop (s, e1, e2) => + | EBinop (bi, s, e1, e2) => let val name = case s of "==" => "eq" @@ -709,8 +710,8 @@ fun process file = | "+" => "plus" | "-" => "minus" | "*" => "times" - | "/" => "div" - | "%" => "mod" + | "/" => (case bi of Int => "divInt" | NotInt => "div") + | "%" => (case bi of Int => "modInt" | NotInt => "mod") | "<" => "lt" | "<=" => "le" | "strcmp" => "strcmp" @@ -1039,12 +1040,12 @@ fun process file = in ((EUnop (s, e), loc), st) end - | EBinop (s, e1, e2) => + | EBinop (bi, s, e1, e2) => let val (e1, st) = exp outer (e1, st) val (e2, st) = exp outer (e2, st) in - ((EBinop (s, e1, e2), loc), st) + ((EBinop (bi, s, e1, e2), loc), st) end | ERecord xets => diff --git a/src/mono.sml b/src/mono.sml index 1d446dda..bf38c0bc 100644 --- a/src/mono.sml +++ b/src/mono.sml @@ -68,6 +68,8 @@ datatype export_kind = datatype Export.export_kind datatype failure_mode = datatype Settings.failure_mode +datatype binop_intness = Int | NotInt + datatype exp' = EPrim of Prim.t | ERel of int @@ -81,7 +83,7 @@ datatype exp' = | EAbs of string * typ * typ * exp | EUnop of string * exp - | EBinop of string * exp * exp + | EBinop of binop_intness * string * exp * exp | ERecord of (string * exp * typ) list | EField of exp * string diff --git a/src/mono_opt.sml b/src/mono_opt.sml index 34f43143..d05e38fd 100644 --- a/src/mono_opt.sml +++ b/src/mono_opt.sml @@ -536,7 +536,7 @@ fun exp e = | EFfiApp ("Basis", "attrifyString_w", [(EFfiApp ("Basis", "str1", [e]), _)]) => EFfiApp ("Basis", "attrifyChar_w", [e]) - | EBinop ("+", (EPrim (Prim.Int n1), _), (EPrim (Prim.Int n2), _)) => EPrim (Prim.Int (Int64.+ (n1, n2))) + | EBinop (_, "+", (EPrim (Prim.Int n1), _), (EPrim (Prim.Int n2), _)) => EPrim (Prim.Int (Int64.+ (n1, n2))) | _ => e diff --git a/src/mono_print.sml b/src/mono_print.sml index 63c98f44..2d296745 100644 --- a/src/mono_print.sml +++ b/src/mono_print.sml @@ -187,11 +187,11 @@ fun p_exp' par env (e, _) = | EUnop (s, e) => parenIf true (box [string s, space, p_exp' true env e]) - | EBinop (s, e1, e2) => parenIf true (box [p_exp' true env e1, - space, - string s, - space, - p_exp' true env e2]) + | EBinop (_, s, e1, e2) => parenIf true (box [p_exp' true env e1, + space, + string s, + space, + p_exp' true env e2]) | ERecord xes => box [string "{", p_list (fn (x, e, _) => diff --git a/src/mono_reduce.sml b/src/mono_reduce.sml index 59ec5a55..f8b209d5 100644 --- a/src/mono_reduce.sml +++ b/src/mono_reduce.sml @@ -92,7 +92,7 @@ fun impure (e, _) = | EApp _ => true | EUnop (_, e) => impure e - | EBinop (_, e1, e2) => impure e1 orelse impure e2 + | EBinop (_, _, e1, e2) => impure e1 orelse impure e2 | ERecord xes => List.exists (fn (_, e, _) => impure e) xes | EField (e, _) => impure e @@ -365,11 +365,21 @@ fun reduce file = val size = U.Exp.fold {typ = fn (_, n) => n, exp = fn (_, n) => n + 1} 0 - fun mayInline (n, e) = + val functionInside' = U.Typ.exists (fn c => case c of + TFun _ => true + | _ => false) + + fun functionInside t = + case #1 t of + TFun (t1, t2) => functionInside' t1 orelse functionInside t2 + | _ => functionInside' t + + fun mayInline (n, e, t) = case IM.find (uses, n) of NONE => false | SOME count => count <= 1 orelse size e <= Settings.getMonoInline () + orelse functionInside t fun summarize d (e, _) = let @@ -426,7 +436,7 @@ fun reduce file = | EAbs _ => [] | EUnop (_, e) => summarize d e - | EBinop (_, e1, e2) => summarize d e1 @ summarize d e2 + | EBinop (_, _, e1, e2) => summarize d e1 @ summarize d e2 | ERecord xets => List.concat (map (summarize d o #2) xets) | EField (e, _) => summarize d e @@ -701,7 +711,7 @@ fun reduce file = let val eo = case eo of NONE => NONE - | SOME e => if mayInline (n, e) then + | SOME e => if mayInline (n, e, t) then SOME e else NONE diff --git a/src/mono_util.sml b/src/mono_util.sml index 56472155..bb09f84d 100644 --- a/src/mono_util.sml +++ b/src/mono_util.sml @@ -200,12 +200,12 @@ fun mapfoldB {typ = fc, exp = fe, bind} = S.map2 (mfe ctx e, fn e' => (EUnop (s, e'), loc)) - | EBinop (s, e1, e2) => + | EBinop (bi, s, e1, e2) => S.bind2 (mfe ctx e1, fn e1' => S.map2 (mfe ctx e2, fn e2' => - (EBinop (s, e1', e2'), loc))) + (EBinop (bi, s, e1', e2'), loc))) | ERecord xes => S.map2 (ListUtil.mapfold (fn (x, e, t) => diff --git a/src/monoize.sml b/src/monoize.sml index 0c0d9d2e..35c6fa83 100644 --- a/src/monoize.sml +++ b/src/monoize.sml @@ -895,42 +895,42 @@ fun monoExp (env, st, fm) (all as (e, loc)) = (L'.TFun ((L'.TFfi ("Basis", "int"), loc), (L'.TFfi ("Basis", "bool"), loc)), loc), (L'.EAbs ("y", (L'.TFfi ("Basis", "int"), loc), (L'.TFfi ("Basis", "bool"), loc), - (L'.EBinop ("==", (L'.ERel 1, loc), (L'.ERel 0, loc)), loc)), loc)), loc), + (L'.EBinop (L'.Int, "==", (L'.ERel 1, loc), (L'.ERel 0, loc)), loc)), loc)), loc), fm) | L.EFfi ("Basis", "eq_float") => ((L'.EAbs ("x", (L'.TFfi ("Basis", "float"), loc), (L'.TFun ((L'.TFfi ("Basis", "float"), loc), (L'.TFfi ("Basis", "bool"), loc)), loc), (L'.EAbs ("y", (L'.TFfi ("Basis", "float"), loc), (L'.TFfi ("Basis", "bool"), loc), - (L'.EBinop ("==", (L'.ERel 1, loc), (L'.ERel 0, loc)), loc)), loc)), loc), + (L'.EBinop (L'.NotInt, "==", (L'.ERel 1, loc), (L'.ERel 0, loc)), loc)), loc)), loc), fm) | L.EFfi ("Basis", "eq_bool") => ((L'.EAbs ("x", (L'.TFfi ("Basis", "bool"), loc), (L'.TFun ((L'.TFfi ("Basis", "bool"), loc), (L'.TFfi ("Basis", "bool"), loc)), loc), (L'.EAbs ("y", (L'.TFfi ("Basis", "bool"), loc), (L'.TFfi ("Basis", "bool"), loc), - (L'.EBinop ("==", (L'.ERel 1, loc), (L'.ERel 0, loc)), loc)), loc)), loc), + (L'.EBinop (L'.NotInt, "==", (L'.ERel 1, loc), (L'.ERel 0, loc)), loc)), loc)), loc), fm) | L.EFfi ("Basis", "eq_string") => ((L'.EAbs ("x", (L'.TFfi ("Basis", "string"), loc), (L'.TFun ((L'.TFfi ("Basis", "string"), loc), (L'.TFfi ("Basis", "bool"), loc)), loc), (L'.EAbs ("y", (L'.TFfi ("Basis", "string"), loc), (L'.TFfi ("Basis", "bool"), loc), - (L'.EBinop ("!strcmp", (L'.ERel 1, loc), (L'.ERel 0, loc)), loc)), loc)), loc), + (L'.EBinop (L'.NotInt, "!strcmp", (L'.ERel 1, loc), (L'.ERel 0, loc)), loc)), loc)), loc), fm) | L.EFfi ("Basis", "eq_char") => ((L'.EAbs ("x", (L'.TFfi ("Basis", "char"), loc), (L'.TFun ((L'.TFfi ("Basis", "char"), loc), (L'.TFfi ("Basis", "bool"), loc)), loc), (L'.EAbs ("y", (L'.TFfi ("Basis", "char"), loc), (L'.TFfi ("Basis", "bool"), loc), - (L'.EBinop ("==", (L'.ERel 1, loc), (L'.ERel 0, loc)), loc)), loc)), loc), + (L'.EBinop (L'.NotInt, "==", (L'.ERel 1, loc), (L'.ERel 0, loc)), loc)), loc)), loc), fm) | L.EFfi ("Basis", "eq_time") => ((L'.EAbs ("x", (L'.TFfi ("Basis", "time"), loc), (L'.TFun ((L'.TFfi ("Basis", "time"), loc), (L'.TFfi ("Basis", "bool"), loc)), loc), (L'.EAbs ("y", (L'.TFfi ("Basis", "time"), loc), (L'.TFfi ("Basis", "bool"), loc), - (L'.EBinop ("==", (L'.ERel 1, loc), (L'.ERel 0, loc)), loc)), loc)), loc), + (L'.EBinop (L'.NotInt, "==", (L'.ERel 1, loc), (L'.ERel 0, loc)), loc)), loc)), loc), fm) | L.ECApp ((L.EFfi ("Basis", "mkEq"), _), t) => @@ -999,7 +999,7 @@ fun monoExp (env, st, fm) (all as (e, loc)) = (L'.TFun ((L'.TFfi ("Basis", "int"), loc), (L'.TFfi ("Basis", "int"), loc)), loc), (L'.EAbs ("y", (L'.TFfi ("Basis", "int"), loc), (L'.TFfi ("Basis", "int"), loc), - (L'.EBinop (s, (L'.ERel 1, loc), (L'.ERel 0, loc)), loc)), loc)), loc) + (L'.EBinop (L'.Int, s, (L'.ERel 1, loc), (L'.ERel 0, loc)), loc)), loc)), loc) in numEx ((L'.TFfi ("Basis", "int"), loc), Prim.Int (Int64.fromInt 0), @@ -1019,7 +1019,7 @@ fun monoExp (env, st, fm) (all as (e, loc)) = (L'.TFun ((L'.TFfi ("Basis", "float"), loc), (L'.TFfi ("Basis", "float"), loc)), loc), (L'.EAbs ("y", (L'.TFfi ("Basis", "float"), loc), (L'.TFfi ("Basis", "float"), loc), - (L'.EBinop (s, (L'.ERel 1, loc), (L'.ERel 0, loc)), loc)), loc)), loc) + (L'.EBinop (L'.NotInt, s, (L'.ERel 1, loc), (L'.ERel 0, loc)), loc)), loc)), loc) in numEx ((L'.TFfi ("Basis", "float"), loc), Prim.Float 0.0, @@ -1086,7 +1086,7 @@ fun monoExp (env, st, fm) (all as (e, loc)) = (L'.TFun ((L'.TFfi ("Basis", "int"), loc), (L'.TFfi ("Basis", "bool"), loc)), loc), (L'.EAbs ("y", (L'.TFfi ("Basis", "int"), loc), (L'.TFfi ("Basis", "bool"), loc), - (L'.EBinop (s, (L'.ERel 1, loc), (L'.ERel 0, loc)), loc)), loc)), loc) + (L'.EBinop (L'.Int, s, (L'.ERel 1, loc), (L'.ERel 0, loc)), loc)), loc)), loc) in ordEx ((L'.TFfi ("Basis", "int"), loc), intBin "<", @@ -1099,7 +1099,7 @@ fun monoExp (env, st, fm) (all as (e, loc)) = (L'.TFun ((L'.TFfi ("Basis", "float"), loc), (L'.TFfi ("Basis", "bool"), loc)), loc), (L'.EAbs ("y", (L'.TFfi ("Basis", "float"), loc), (L'.TFfi ("Basis", "bool"), loc), - (L'.EBinop (s, (L'.ERel 1, loc), (L'.ERel 0, loc)), loc)), loc)), loc) + (L'.EBinop (L'.NotInt, s, (L'.ERel 1, loc), (L'.ERel 0, loc)), loc)), loc)), loc) in ordEx ((L'.TFfi ("Basis", "float"), loc), floatBin "<", @@ -1112,7 +1112,7 @@ fun monoExp (env, st, fm) (all as (e, loc)) = (L'.TFun ((L'.TFfi ("Basis", "bool"), loc), (L'.TFfi ("Basis", "bool"), loc)), loc), (L'.EAbs ("y", (L'.TFfi ("Basis", "bool"), loc), (L'.TFfi ("Basis", "bool"), loc), - (L'.EBinop (s, (L'.ERel 1, loc), (L'.ERel 0, loc)), loc)), loc)), loc) + (L'.EBinop (L'.NotInt, s, (L'.ERel 1, loc), (L'.ERel 0, loc)), loc)), loc)), loc) in ordEx ((L'.TFfi ("Basis", "bool"), loc), boolBin "<", @@ -1125,8 +1125,8 @@ fun monoExp (env, st, fm) (all as (e, loc)) = (L'.TFun ((L'.TFfi ("Basis", "string"), loc), (L'.TFfi ("Basis", "bool"), loc)), loc), (L'.EAbs ("y", (L'.TFfi ("Basis", "string"), loc), (L'.TFfi ("Basis", "bool"), loc), - (L'.EBinop (s, - (L'.EBinop ("strcmp", + (L'.EBinop (L'.NotInt, s, + (L'.EBinop (L'.NotInt, "strcmp", (L'.ERel 1, loc), (L'.ERel 0, loc)), loc), (L'.EPrim (Prim.Int (Int64.fromInt 0)), loc)), loc)), loc)), loc) @@ -1142,7 +1142,7 @@ fun monoExp (env, st, fm) (all as (e, loc)) = (L'.TFun ((L'.TFfi ("Basis", "char"), loc), (L'.TFfi ("Basis", "bool"), loc)), loc), (L'.EAbs ("y", (L'.TFfi ("Basis", "char"), loc), (L'.TFfi ("Basis", "bool"), loc), - (L'.EBinop (s, (L'.ERel 1, loc), (L'.ERel 0, loc)), loc)), loc)), loc) + (L'.EBinop (L'.NotInt, s, (L'.ERel 1, loc), (L'.ERel 0, loc)), loc)), loc)), loc) in ordEx ((L'.TFfi ("Basis", "char"), loc), charBin "<", @@ -1155,7 +1155,7 @@ fun monoExp (env, st, fm) (all as (e, loc)) = (L'.TFun ((L'.TFfi ("Basis", "time"), loc), (L'.TFfi ("Basis", "bool"), loc)), loc), (L'.EAbs ("y", (L'.TFfi ("Basis", "time"), loc), (L'.TFfi ("Basis", "bool"), loc), - (L'.EBinop (s, (L'.ERel 1, loc), (L'.ERel 0, loc)), loc)), loc)), loc) + (L'.EBinop (L'.NotInt, s, (L'.ERel 1, loc), (L'.ERel 0, loc)), loc)), loc)), loc) in ordEx ((L'.TFfi ("Basis", "time"), loc), boolBin "<", diff --git a/src/settings.sml b/src/settings.sml index 4c611336..97c39abf 100644 --- a/src/settings.sml +++ b/src/settings.sml @@ -171,6 +171,7 @@ val jsFuncsBase = basisM [("alert", "alert"), ("stringToInt_error", "pi"), ("urlifyInt", "ts"), ("urlifyFloat", "ts"), + ("urlifyTime", "ts"), ("urlifyString", "uf"), ("urlifyBool", "ub"), ("recv", "rv"), diff --git a/src/urweb.grm b/src/urweb.grm index 21c4a50c..5803f445 100644 --- a/src/urweb.grm +++ b/src/urweb.grm @@ -1232,7 +1232,9 @@ eterm : LPAREN eexp RPAREN (#1 eexp, s (LPARENleft, RPARENright)) val e = (EApp (e, texp), loc) in if length fields <> length sqlexps then - ErrorMsg.errorAt loc "Length mismatch in INSERT field specification" + ErrorMsg.errorAt loc ("Length mismatch in INSERT field specification (" + ^ Int.toString (length fields) + ^ " vs. " ^ Int.toString (length sqlexps) ^ ")") else (); (EApp (e, (ERecord (ListPair.zip (fields, sqlexps)), loc)), loc) -- cgit v1.2.3