summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/urweb/urweb_cpp.h1
-rw-r--r--lib/js/urweb.js21
-rw-r--r--lib/ur/basis.urs4
-rw-r--r--src/c/urweb.c5
-rw-r--r--src/elab.sml8
-rw-r--r--src/elab_env.sml14
-rw-r--r--src/elab_print.sml14
-rw-r--r--src/elab_util.sml8
-rw-r--r--src/elaborate.sml46
-rw-r--r--src/explify.sml2
-rw-r--r--src/monoize.sml39
-rw-r--r--src/settings.sml6
12 files changed, 119 insertions, 49 deletions
diff --git a/include/urweb/urweb_cpp.h b/include/urweb/urweb_cpp.h
index 52e54372..f89c432c 100644
--- a/include/urweb/urweb_cpp.h
+++ b/include/urweb/urweb_cpp.h
@@ -277,6 +277,7 @@ uw_Basis_int uw_Basis_diffInSeconds(struct uw_context *, uw_Basis_time, uw_Basis
uw_Basis_int uw_Basis_toSeconds(struct uw_context *, uw_Basis_time);
uw_Basis_int uw_Basis_diffInMilliseconds(struct uw_context *, uw_Basis_time, uw_Basis_time);
uw_Basis_int uw_Basis_toMilliseconds(struct uw_context *, uw_Basis_time);
+uw_Basis_time uw_Basis_fromMilliseconds(struct uw_context *, uw_Basis_int);
uw_Basis_time uw_Basis_fromDatetime(struct uw_context *, uw_Basis_int, uw_Basis_int, uw_Basis_int, uw_Basis_int, uw_Basis_int, uw_Basis_int);
uw_Basis_int uw_Basis_datetimeYear(struct uw_context *, uw_Basis_time);
uw_Basis_int uw_Basis_datetimeMonth(struct uw_context *, uw_Basis_time);
diff --git a/lib/js/urweb.js b/lib/js/urweb.js
index 335cb525..6cf8a3f3 100644
--- a/lib/js/urweb.js
+++ b/lib/js/urweb.js
@@ -149,6 +149,10 @@ function toMilliseconds(tm) {
return Math.round(tm / 1000);
}
+function fromMilliseconds(tm) {
+ return tm * 1000;
+}
+
function addSeconds(tm, n) {
return tm + n * 1000000;
}
@@ -468,8 +472,11 @@ function onConnectFail(f) {
connectHandlers = cons(flift(f), connectHandlers);
}
-function conn() {
- runHandlers("Connect", connectHandlers, null);
+function conn(msg) {
+ var rx = /(.*)<body>((.|\n|\r)*)<\/body>(.*)/g;
+ var arr = rx.exec(msg);
+ msg = (arr && arr.length >= 3) ? arr[2] : msg;
+ runHandlers("RPC failure", connectHandlers, msg);
}
var serverHandlers = null;
@@ -1468,6 +1475,14 @@ function strcmp(str1, str2) {
return ((str1 == str2) ? 0 : ((str1 > str2) ? 1 : -1));
}
+function chr(n) {
+ return String.fromCharCode(n);
+}
+
+function htmlifySpecialChar(ch) {
+ return "&#" + ch.charCodeAt(0) + ";";
+}
+
// Remote calls
@@ -1591,7 +1606,7 @@ function rc(prefix, uri, parse, k, needsSig, isN) {
}
} else {
if (isN == null)
- conn();
+ conn(xhr.responseText);
else
k(null);
}
diff --git a/lib/ur/basis.urs b/lib/ur/basis.urs
index ec6ef599..a4872c32 100644
--- a/lib/ur/basis.urs
+++ b/lib/ur/basis.urs
@@ -163,6 +163,7 @@ val toSeconds : time -> int
val diffInSeconds : time -> time -> int
(* Earlier time first *)
val toMilliseconds : time -> int
+val fromMilliseconds : int -> time
val diffInMilliseconds : time -> time -> int
val timef : string -> time -> string (* Uses strftime() format string *)
val readUtc : string -> option time
@@ -553,6 +554,9 @@ val sql_div : t ::: Type -> sql_arith t -> sql_binary t t t
val sql_mod : sql_binary int int int
val sql_eq : t ::: Type -> sql_binary t t bool
+(* Note that the semantics of this operator on nullable types are different than for standard SQL!
+ * Instead, we do it the sane way, where [NULL = NULL]. *)
+
val sql_ne : t ::: Type -> sql_binary t t bool
val sql_lt : t ::: Type -> sql_binary t t bool
val sql_le : t ::: Type -> sql_binary t t bool
diff --git a/src/c/urweb.c b/src/c/urweb.c
index 09d04f1c..30619314 100644
--- a/src/c/urweb.c
+++ b/src/c/urweb.c
@@ -4087,6 +4087,11 @@ uw_Basis_int uw_Basis_toMilliseconds(uw_context ctx, uw_Basis_time tm) {
return tm.seconds * 1000 + tm.microseconds / 1000;
}
+uw_Basis_time uw_Basis_fromMilliseconds(uw_context ctx, uw_Basis_int n) {
+ uw_Basis_time tm = {n / 1000, n % 1000 * 1000};
+ return tm;
+}
+
uw_Basis_int uw_Basis_diffInMilliseconds(uw_context ctx, uw_Basis_time tm1, uw_Basis_time tm2) {
return uw_Basis_toMilliseconds(ctx, tm2) - uw_Basis_toMilliseconds(ctx, tm1);
}
diff --git a/src/elab.sml b/src/elab.sml
index 249531f1..209d3307 100644
--- a/src/elab.sml
+++ b/src/elab.sml
@@ -138,13 +138,19 @@ and edecl' =
withtype exp = exp' located
and edecl = edecl' located
+(* We have to be careful about crawling automatically generated signatures recursively,
+ * importing all type-class instances that we find.
+ * The reason is that selfification will add signatures of anonymous structures,
+ * and it's counterintuitive for instances to escape anonymous structures! *)
+datatype import_mode = Import | Skip
+
datatype sgn_item' =
SgiConAbs of string * int * kind
| SgiCon of string * int * kind * con
| SgiDatatype of (string * int * string list * (string * int * con option) list) list
| SgiDatatypeImp of string * int * int * string list * string * string list * (string * int * con option) list
| SgiVal of string * int * con
- | SgiStr of string * int * sgn
+ | SgiStr of import_mode * string * int * sgn
| SgiSgn of string * int * sgn
| SgiConstraint of con * con
| SgiClassAbs of string * int * kind
diff --git a/src/elab_env.sml b/src/elab_env.sml
index 9fbe7bd7..9c9cd14f 100644
--- a/src/elab_env.sml
+++ b/src/elab_env.sml
@@ -990,7 +990,7 @@ fun sgiSeek (sgi, (sgns, strs, cons)) =
| SgiDatatypeImp (x, n, _, _, _, _, _) => (sgns, strs, IM.insert (cons, n, x))
| SgiVal _ => (sgns, strs, cons)
| SgiSgn (x, n, _) => (IM.insert (sgns, n, x), strs, cons)
- | SgiStr (x, n, _) => (sgns, IM.insert (strs, n, x), cons)
+ | SgiStr (_, x, n, _) => (sgns, IM.insert (strs, n, x), cons)
| SgiConstraint _ => (sgns, strs, cons)
| SgiClassAbs (x, n, _) => (sgns, strs, IM.insert (cons, n, x))
| SgiClass (x, n, _, _) => (sgns, strs, IM.insert (cons, n, x))
@@ -1143,13 +1143,13 @@ and hnormSgn env (all as (sgn, loc)) =
else
traverse (ms, sgi :: pre, rest)
- | (sgi as (SgiStr (x', n, sgn'), loc)) :: rest =>
+ | (sgi as (SgiStr (im, x', n, sgn'), loc)) :: rest =>
(case ms of
[] => traverse (ms, sgi :: pre, rest)
| x :: ms' =>
if x = x' then
List.revAppend (pre,
- (SgiStr (x', n,
+ (SgiStr (im, x', n,
rewrite (sgn', ms')), loc) :: rest)
else
traverse (ms, sgi :: pre, rest))
@@ -1186,7 +1186,7 @@ fun enrichClasses env classes (m1, ms) sgn =
fun default () = (classes, newClasses, sgiSeek (#1 sgi, fmap), env)
in
case #1 sgi of
- SgiStr (x, _, sgn) =>
+ SgiStr (Import, x, _, sgn) =>
let
val str = manifest (m1, ms, #2 sgi)
val sgn' = sgnSubSgn (str, fmap) sgn
@@ -1360,7 +1360,7 @@ fun sgiBinds env (sgi, loc) =
env xncs
end
| SgiVal (x, n, t) => pushENamedAs env x n t
- | SgiStr (x, n, sgn) => pushStrNamedAs env x n sgn
+ | SgiStr (_, x, n, sgn) => pushStrNamedAs env x n sgn
| SgiSgn (x, n, sgn) => pushSgnNamedAs env x n sgn
| SgiConstraint _ => env
@@ -1374,7 +1374,7 @@ fun sgnSubCon x =
fun projectStr env {sgn, str, field} =
case #1 (hnormSgn env sgn) of
SgnConst sgis =>
- (case sgnSeek (fn SgiStr (x, _, sgn) => if x = field then SOME sgn else NONE | _ => NONE) sgis of
+ (case sgnSeek (fn SgiStr (_, x, _, sgn) => if x = field then SOME sgn else NONE | _ => NONE) sgis of
NONE => NONE
| SOME (sgn, subs) => SOME (sgnSubSgn (str, subs) sgn))
| SgnError => SOME (SgnError, ErrorMsg.dummySpan)
@@ -1544,7 +1544,7 @@ fun sgnSeekConstraints (str, sgis) =
| SgiDatatypeImp (x, n, _, _, _, _, _) => seek (sgis, sgns, strs, IM.insert (cons, n, x), acc)
| SgiVal _ => seek (sgis, sgns, strs, cons, acc)
| SgiSgn (x, n, _) => seek (sgis, IM.insert (sgns, n, x), strs, cons, acc)
- | SgiStr (x, n, _) => seek (sgis, sgns, IM.insert (strs, n, x), cons, acc)
+ | SgiStr (_, x, n, _) => seek (sgis, sgns, IM.insert (strs, n, x), cons, acc)
| SgiClassAbs (x, n, _) => seek (sgis, sgns, strs, IM.insert (cons, n, x), acc)
| SgiClass (x, n, _, _) => seek (sgis, sgns, strs, IM.insert (cons, n, x), acc)
in
diff --git a/src/elab_print.sml b/src/elab_print.sml
index 957d4646..5a41883f 100644
--- a/src/elab_print.sml
+++ b/src/elab_print.sml
@@ -611,13 +611,13 @@ fun p_sgn_item env (sgiAll as (sgi, _)) =
string ":",
space,
p_con env c]
- | SgiStr (x, n, sgn) => box [string "structure",
- space,
- p_named x n,
- space,
- string ":",
- space,
- p_sgn env sgn]
+ | SgiStr (_, x, n, sgn) => box [string "structure",
+ space,
+ p_named x n,
+ space,
+ string ":",
+ space,
+ p_sgn env sgn]
| SgiSgn (x, n, sgn) => box [string "signature",
space,
p_named x n,
diff --git a/src/elab_util.sml b/src/elab_util.sml
index fef55852..acc696dd 100644
--- a/src/elab_util.sml
+++ b/src/elab_util.sml
@@ -688,10 +688,10 @@ fun mapfoldB {kind, con, sgn_item, sgn, bind} =
S.map2 (con ctx c,
fn c' =>
(SgiVal (x, n, c'), loc))
- | SgiStr (x, n, s) =>
+ | SgiStr (im, x, n, s) =>
S.map2 (sg ctx s,
fn s' =>
- (SgiStr (x, n, s'), loc))
+ (SgiStr (im, x, n, s'), loc))
| SgiSgn (x, n, s) =>
S.map2 (sg ctx s,
fn s' =>
@@ -738,7 +738,7 @@ fun mapfoldB {kind, con, sgn_item, sgn, bind} =
bind (ctx, NamedC (x, n, (KType, loc),
SOME (CModProj (m1, ms, s), loc)))
| SgiVal _ => ctx
- | SgiStr (x, n, sgn) =>
+ | SgiStr (_, x, n, sgn) =>
bind (ctx, Str (x, n, sgn))
| SgiSgn (x, n, sgn) =>
bind (ctx, Sgn (x, n, sgn))
@@ -1270,7 +1270,7 @@ and maxNameSgi (sgi, _) =
foldl (fn ((_, n', _), m) => Int.max (n', m))
(Int.max (n1, n2)) ns
| SgiVal (_, n, _) => n
- | SgiStr (_, n, sgn) => Int.max (n, maxNameSgn sgn)
+ | SgiStr (_, _, n, sgn) => Int.max (n, maxNameSgn sgn)
| SgiSgn (_, n, sgn) => Int.max (n, maxNameSgn sgn)
| SgiConstraint _ => 0
| SgiClassAbs (_, n, _) => n
diff --git a/src/elaborate.sml b/src/elaborate.sml
index 1885345d..c3e81b65 100644
--- a/src/elaborate.sml
+++ b/src/elaborate.sml
@@ -2481,7 +2481,7 @@ fun dopenConstraints (loc, env, denv) {str, strs} =
L'.SgnConst sgis =>
foldl (fn (sgi, cs) =>
case #1 sgi of
- L'.SgiStr (x, _, _) =>
+ L'.SgiStr (L'.Import, x, _, _) =>
(case E.projectStr env {sgn = sgn, str = st, field = x} of
NONE => raise Fail "Elaborate: projectStr in collect"
| SOME sgn' =>
@@ -2494,6 +2494,18 @@ fun dopenConstraints (loc, env, denv) {str, strs} =
D.assert env denv (c1, c2)) denv (collect true (st, sgn))
end
+fun tcdump env =
+ Print.preface("Instances", p_list_sep Print.PD.newline
+ (fn (cl, ls) =>
+ box [p_con env cl,
+ box [Print.PD.string "{",
+ p_list (fn (t, e) =>
+ box [p_exp env e,
+ Print.PD.string " : ",
+ p_con env t]) ls,
+ Print.PD.string "}"]])
+ (E.listClasses env))
+
fun elabSgn_item ((sgi, loc), (env, denv, gs)) =
((*Print.preface ("elabSgi", SourcePrint.p_sgn_item (sgi, loc));*)
case sgi of
@@ -2695,7 +2707,7 @@ fun elabSgn_item ((sgi, loc), (env, denv, gs)) =
val (env', n) = E.pushStrNamed env x sgn'
val denv' = dopenConstraints (loc, env', denv) {str = x, strs = []}
in
- ([(L'.SgiStr (x, n, sgn'), loc)], (env', denv', gs' @ gs))
+ ([(L'.SgiStr (L'.Import, x, n, sgn'), loc)], (env', denv', gs' @ gs))
end
| L.SgiSgn (x, sgn) =>
@@ -2814,7 +2826,7 @@ and elabSgn (env, denv) (sgn, loc) =
else
();
(cons, vals, SS.add (sgns, x), strs))
- | L'.SgiStr (x, _, _) =>
+ | L'.SgiStr (_, x, _, _) =>
(if SS.member (strs, x) then
sgnError env (DuplicateStr (loc, x))
else
@@ -2865,7 +2877,7 @@ and elabSgn (env, denv) (sgn, loc) =
(unifyKinds env k ck
handle KUnify x => sgnError env (WhereWrongKind x);
true)
- | (L'.SgiStr (x', _, sgn''), _) =>
+ | (L'.SgiStr (_, x', _, sgn''), _) =>
(case ms of
[] => false
| m :: ms' =>
@@ -2914,8 +2926,8 @@ and selfify env {str, strs, sgn} =
map (fn (x, n, xs, xncs) => (L'.SgiDatatypeImp (x, n, str, strs, x, xs, xncs), loc)) dts
| (L'.SgiClassAbs (x, n, k), loc) =>
[(L'.SgiClass (x, n, k, (L'.CModProj (str, strs, x), loc)), loc)]
- | (L'.SgiStr (x, n, sgn), loc) =>
- [(L'.SgiStr (x, n, selfify env {str = str, strs = strs @ [x], sgn = sgn}), loc)]
+ | (L'.SgiStr (im, x, n, sgn), loc) =>
+ [(L'.SgiStr (im, x, n, selfify env {str = str, strs = strs @ [x], sgn = sgn}), loc)]
| x => [x],
E.sgiBinds env sgi)) env sgis)), #2 sgn)
| L'.SgnFun _ => sgn
@@ -2987,7 +2999,7 @@ and dopen env {str, strs, sgn} =
[(L'.DVal (x, n, t, (L'.EModProj (str, strs, x), loc)), loc)]
else
[]
- | L'.SgiStr (x, n, sgn) =>
+ | L'.SgiStr (_, x, n, sgn) =>
if isVisible x then
[(L'.DStr (x, n, sgn, (L'.StrProj (m, x), loc)), loc)]
else
@@ -3033,8 +3045,8 @@ and sgiOfDecl (d, loc) =
| L'.DVal (x, n, t, _) => [(L'.SgiVal (x, n, t), loc)]
| L'.DValRec vis => map (fn (x, n, t, _) => (L'.SgiVal (x, n, t), loc)) vis
| L'.DSgn (x, n, sgn) => [(L'.SgiSgn (x, n, sgn), loc)]
- | L'.DStr (x, n, sgn, _) => [(L'.SgiStr (x, n, sgn), loc)]
- | L'.DFfiStr (x, n, sgn) => [(L'.SgiStr (x, n, sgn), loc)]
+ | L'.DStr (x, n, sgn, _) => [(L'.SgiStr (L'.Import, x, n, sgn), loc)]
+ | L'.DFfiStr (x, n, sgn) => [(L'.SgiStr (L'.Import, x, n, sgn), loc)]
| L'.DConstraint cs => [(L'.SgiConstraint cs, loc)]
| L'.DExport _ => []
| L'.DTable (tn, x, n, c, _, pc, _, cc) =>
@@ -3344,10 +3356,10 @@ and subSgn' counterparts env strLoc sgn1 (sgn2 as (_, loc2)) =
NONE
| _ => NONE)
- | L'.SgiStr (x, n2, sgn2) =>
+ | L'.SgiStr (_, x, n2, sgn2) =>
seek (fn (env, sgi1All as (sgi1, loc)) =>
case sgi1 of
- L'.SgiStr (x', n1, sgn1) =>
+ L'.SgiStr (_, x', n1, sgn1) =>
if x = x' then
let
(* Don't forget to save & restore the
@@ -3749,7 +3761,7 @@ and wildifyStr env (str, sgn) =
else
nd
end
- | L'.SgiStr (x, _, s) =>
+ | L'.SgiStr (_, x, _, s) =>
(case #1 (hnormSgn env' s) of
L'.SgnConst sgis' => naddMod (nd, x, (env', buildNeeded env' sgis'))
| _ => nd)
@@ -4496,7 +4508,7 @@ and elabStr (env, denv) (str, loc) =
((L'.SgiSgn (x, n, sgn), loc) :: sgis, cons, vals, sgns, strs)
end
- | L'.SgiStr (x, n, sgn) =>
+ | L'.SgiStr (im, x, n, sgn) =>
let
val (strs, x) =
if SS.member (strs, x) then
@@ -4504,7 +4516,7 @@ and elabStr (env, denv) (str, loc) =
else
(SS.add (strs, x), x)
in
- ((L'.SgiStr (x, n, sgn), loc) :: sgis, cons, vals, sgns, strs)
+ ((L'.SgiStr (im, x, n, sgn), loc) :: sgis, cons, vals, sgns, strs)
end
| L'.SgiConstraint _ => ((sgi, loc) :: sgis, cons, vals, sgns, strs)
| L'.SgiClassAbs (x, n, k) =>
@@ -4610,7 +4622,7 @@ and elabStr (env, denv) (str, loc) =
* question-mark identifiers generated previously by this
* very code fragment. *)
fun mungeName m =
- if List.exists (fn (L'.SgiStr (x, _, _), _) => x = m
+ if List.exists (fn (L'.SgiStr (_, x, _, _), _) => x = m
| _ => false) sgis then
mungeName ("?" ^ m)
else
@@ -4619,7 +4631,7 @@ and elabStr (env, denv) (str, loc) =
val m = mungeName m
in
((L'.StrApp (str1', str2'), loc),
- (L'.SgnConst ((L'.SgiStr (m, n, selfifyAt env {str = str2', sgn = sgn2}), loc) :: sgis), loc),
+ (L'.SgnConst ((L'.SgiStr (L'.Skip, m, n, selfifyAt env {str = str2', sgn = sgn2}), loc) :: sgis), loc),
gs1 @ gs2)
end
| _ => raise Fail "Unable to hnormSgn in functor application")
@@ -5001,7 +5013,7 @@ fun elabFile basis basis_tm topStr topSgn top_tm env file =
();
(*Print.preface("File", ElabPrint.p_file env file);*)
-
+
(L'.DFfiStr ("Basis", basis_n, sgn), ErrorMsg.dummySpan)
:: ds
@ (L'.DStr ("Top", top_n, topSgn, topStr), ErrorMsg.dummySpan)
diff --git a/src/explify.sml b/src/explify.sml
index fd0f3277..f38151d2 100644
--- a/src/explify.sml
+++ b/src/explify.sml
@@ -150,7 +150,7 @@ fun explifySgi (sgi, loc) =
SOME (L'.SgiDatatypeImp (x, n, m1, ms, s, xs, map (fn (x, n, co) =>
(x, n, Option.map explifyCon co)) xncs), loc)
| L.SgiVal (x, n, c) => SOME (L'.SgiVal (x, n, explifyCon c), loc)
- | L.SgiStr (x, n, sgn) => SOME (L'.SgiStr (x, n, explifySgn sgn), loc)
+ | L.SgiStr (_, x, n, sgn) => SOME (L'.SgiStr (x, n, explifySgn sgn), loc)
| L.SgiSgn (x, n, sgn) => SOME (L'.SgiSgn (x, n, explifySgn sgn), loc)
| L.SgiConstraint _ => NONE
| L.SgiClassAbs (x, n, k) => SOME (L'.SgiConAbs (x, n, (L'.KArrow (explifyKind k, (L'.KType, loc)), loc)), loc)
diff --git a/src/monoize.sml b/src/monoize.sml
index bdd8f5c3..75851a48 100644
--- a/src/monoize.sml
+++ b/src/monoize.sml
@@ -2306,22 +2306,45 @@ fun monoExp (env, st, fm) (all as (e, loc)) =
_), _),
_), _),
_), _),
- _), _),
+ arg1), _),
_), _),
_) =>
let
val s = (L'.TFfi ("Basis", "string"), loc)
+
+ val default = strcat [str "(",
+ (L'.ERel 1, loc),
+ str " ",
+ (L'.ERel 2, loc),
+ str " ",
+ (L'.ERel 0, loc),
+ str ")"]
+
+ val body = case #1 arg1 of
+ L.CApp ((L.CFfi ("Basis", "option"), _), _) =>
+ (L'.ECase ((L'.ERel 2, loc),
+ [((L'.PPrim (Prim.String (Prim.Normal, "=")), loc),
+ strcat [str "((",
+ (L'.ERel 1, loc),
+ str " ",
+ (L'.ERel 2, loc),
+ str " ",
+ (L'.ERel 0, loc),
+ str ") OR ((",
+ (L'.ERel 1, loc),
+ str ") IS NULL AND (",
+ (L'.ERel 0, loc),
+ str ") IS NULL))"]),
+ ((L'.PWild, loc),
+ default)],
+ {disc = s,
+ result = s}), loc)
+ | _ => default
in
((L'.EAbs ("c", s, (L'.TFun (s, (L'.TFun (s, s), loc)), loc),
(L'.EAbs ("e1", s, (L'.TFun (s, s), loc),
(L'.EAbs ("e2", s, s,
- strcat [str "(",
- (L'.ERel 1, loc),
- str " ",
- (L'.ERel 2, loc),
- str " ",
- (L'.ERel 0, loc),
- str ")"]), loc)), loc)), loc),
+ body), loc)), loc)), loc),
fm)
end
| L.EFfi ("Basis", "sql_and") => (str "AND", fm)
diff --git a/src/settings.sml b/src/settings.sml
index 3694e1f3..f9125c64 100644
--- a/src/settings.sml
+++ b/src/settings.sml
@@ -343,6 +343,7 @@ val jsFuncsBase = basisM [("alert", "alert"),
("addSeconds", "addSeconds"),
("diffInSeconds", "diffInSeconds"),
("toMilliseconds", "toMilliseconds"),
+ ("fromMilliseconds", "fromMilliseconds"),
("diffInMilliseconds", "diffInMilliseconds"),
("fromDatetime", "fromDatetime"),
@@ -376,7 +377,10 @@ val jsFuncsBase = basisM [("alert", "alert"),
("atom", "atom"),
("css_url", "css_url"),
("property", "property"),
- ("giveFocus", "giveFocus")]
+ ("giveFocus", "giveFocus"),
+
+ ("htmlifySpecialChar", "htmlifySpecialChar"),
+ ("chr", "chr")]
val jsFuncs = ref jsFuncsBase
fun setJsFuncs ls = jsFuncs := foldl (fn ((k, v), m) => M.insert (m, k, v)) jsFuncsBase ls
fun jsFunc x = M.find (!jsFuncs, x)