{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE QuasiQuotes #-}
module Fay.Runtime where
import Text.Shakespeare.Text
import Data.Text.Lazy as T
import Fay.Config
getRuntimeSource :: Config -> String
getRuntimeSource :: Config -> String
getRuntimeSource Config
cfg =
let ifTs :: T.Text -> T.Text
ifTs :: Text -> Text
ifTs Text
a = if Config -> Bool
configTypeScript Config
cfg then Text
a else Text
""
ifTsJs :: T.Text -> T.Text -> T.Text
ifTsJs :: Text -> Text -> Text
ifTsJs Text
a Text
b = if Config -> Bool
configTypeScript Config
cfg then Text
a else Text
b
in Text -> String
T.unpack [lt|
/*******************************************************************************
* Misc.
*/
#{ifTs "var Fay:{[key:string]: any;} = {};"}
// Workaround for missing functionality in IE 8 and earlier.
if( Object.create === undefined ) {
Object.create = function( o ) {
function F(){}
F.prototype = o;
return new F();
};
}
// Insert properties of b in place into a.
function Fay$$objConcat(a,b){
for (var p in b) if (b.hasOwnProperty(p)){
a[p] = b[p];
}
return a;
}
/*******************************************************************************
* Thunks.
*/
// Force a thunk (if it is a thunk) until WHNF.
function Fay$$_(thunkish,nocache#{ifTs "?: boolean"}){
while (thunkish instanceof Fay$$$) {
thunkish = thunkish.force(nocache);
}
return thunkish;
}
// Apply a function to arguments (see method2 in Fay.hs).
function Fay$$__(){
var f = arguments[0];
for (var i = 1, len = arguments.length; i < len; i++) {
f = (f instanceof Fay$$$? Fay$$_(f) : f)(arguments[i]);
}
return f;
}
// Thunk object.
function Fay$$$(value){
this.forced = false;
this.value = value;
}
// Force the thunk.
Fay$$$.prototype.force = function(nocache) {
return nocache ?
this.value() :
(this.forced ?
this.value :
(this.value = this.value(), this.forced = true, this.value));
};
function Fay$$seq(x) {
return function(y) {
Fay$$_(x,false);
return y;
}
}
function Fay$$seq$36$uncurried(x,y) {
Fay$$_(x,false);
return y;
}
/*******************************************************************************
* Monad.
*/
function Fay$$Monad(value){
this.value = value;
}
// This is used directly from Fay, but can be rebound or shadowed. See primOps in Types.hs.
// >>
function Fay$$then(a){
return function(b){
return Fay$$bind(a)(function(_){
return b;
});
};
}
// This is used directly from Fay, but can be rebound or shadowed. See primOps in Types.hs.
// >>
function Fay$$then$36$uncurried(a,b){
return Fay$$bind$36$uncurried(a,function(_){ return b; });
}
// >>=
// This is used directly from Fay, but can be rebound or shadowed. See primOps in Types.hs.
function Fay$$bind(m){
return function(f){
return new Fay$$$(function(){
var monad = Fay$$_(m,true);
return Fay$$_(f)(monad.value);
});
};
}
// >>=
// This is used directly from Fay, but can be rebound or shadowed. See primOps in Types.hs.
function Fay$$bind$36$uncurried(m,f){
return new Fay$$$(function(){
var monad = Fay$$_(m,true);
return Fay$$_(f)(monad.value);
});
}
// This is used directly from Fay, but can be rebound or shadowed.
function Fay$$$_return(a){
return new Fay$$Monad(a);
}
// Allow the programmer to access thunk forcing directly.
function Fay$$force(thunk){
return function(type){
return new Fay$$$(function(){
Fay$$_(thunk,type);
return new Fay$$Monad(Fay$$unit);
})
}
}
// This is used directly from Fay, but can be rebound or shadowed.
function Fay$$return$36$uncurried(a){
return new Fay$$Monad(a);
}
// Unit: ().
var Fay$$unit = null;
/*******************************************************************************
* Serialization.
* Fay <-> JS. Should be bijective.
*/
// Serialize a Fay object to JS.
function Fay$$fayToJs(type,fayObj){
var base = type[0];
var args = type[1];
var jsObj;
if(base == "action") {
// A nullary monadic action. Should become a nullary JS function.
// Fay () -> function(){ return ... }
return function(){
return Fay$$fayToJs(args[0],Fay$$_(fayObj,true).value);
};
}
else if(base == "function") {
// A proper function.
return function(){
var fayFunc = fayObj;
var return_type = args[args.length-1];
var len = args.length;
// If some arguments.
if (len > 1) {
// Apply to all the arguments.
fayFunc = Fay$$_(fayFunc,true);
// TODO: Perhaps we should throw an error when JS
// passes more arguments than Haskell accepts.
// Unserialize the JS values to Fay for the Fay callback.
if (args == "automatic_function")
{
for (var i = 0; i < arguments.length; i++) {
fayFunc = Fay$$_(fayFunc(Fay$$jsToFay(["automatic"],arguments[i])),true);
}
return Fay$$fayToJs(["automatic"], fayFunc);
}
for (var i = 0, len = len; i < len - 1 && fayFunc instanceof Function; i++) {
fayFunc = Fay$$_(fayFunc(Fay$$jsToFay(args[i],arguments[i])),true);
}
// Finally, serialize the Fay return value back to JS.
var return_base = return_type[0];
var return_args = return_type[1];
// If it's a monadic return value, get the value instead.
if(return_base == "action") {
return Fay$$fayToJs(return_args[0],fayFunc.value);
}
// Otherwise just serialize the value direct.
else {
return Fay$$fayToJs(return_type,fayFunc);
}
} else {
throw new Error("Nullary function?");
}
};
}
else if(base == "string") {
return Fay$$fayToJs_string(fayObj);
}
else if(base == "list") {
// Serialize Fay list to JavaScript array.
var arr = [];
fayObj = Fay$$_(fayObj);
while(fayObj instanceof Fay$$Cons) {
arr.push(Fay$$fayToJs(args[0],fayObj.car));
fayObj = Fay$$_(fayObj.cdr);
}
return arr;
}
else if(base == "tuple") {
// Serialize Fay tuple to JavaScript array.
var arr = [];
fayObj = Fay$$_(fayObj);
var i = 0;
while(fayObj instanceof Fay$$Cons) {
arr.push(Fay$$fayToJs(args[i++],fayObj.car));
fayObj = Fay$$_(fayObj.cdr);
}
return arr;
}
else if(base == "defined") {
fayObj = Fay$$_(fayObj);
return fayObj instanceof Fay.FFI._Undefined
? undefined
: Fay$$fayToJs(args[0],fayObj.slot1);
}
else if(base == "nullable") {
fayObj = Fay$$_(fayObj);
return fayObj instanceof Fay.FFI._Null
? null
: Fay$$fayToJs(args[0],fayObj.slot1);
}
else if(base == "double" || base == "int" || base == "bool") {
// Bools are unboxed.
return Fay$$_(fayObj);
}
else if(base == "ptr")
return fayObj;
else if(base == "unknown")
return Fay$$fayToJs(["automatic"], fayObj);
else if(base == "automatic" && fayObj instanceof Function) {
return Fay$$fayToJs(["function", "automatic_function"], fayObj);
}
else if(base == "automatic" || base == "user") {
fayObj = Fay$$_(fayObj);
if(fayObj instanceof Fay$$Cons || fayObj === null){
// Serialize Fay list to JavaScript array.
var arr = [];
while(fayObj instanceof Fay$$Cons) {
arr.push(Fay$$fayToJs(["automatic"],fayObj.car));
fayObj = Fay$$_(fayObj.cdr);
}
return arr;
} else {
var fayToJsFun = fayObj && fayObj.instance && Fay$$fayToJsHash[fayObj.instance];
return fayToJsFun ? fayToJsFun(type,type[2],fayObj) : fayObj;
}
}
throw new Error("Unhandled Fay->JS translation type: " + base);
}
// Stores the mappings from fay types to js objects.
// This will be populated by compiled modules.
var Fay$$fayToJsHash = {};
// Specialized serializer for string.
function Fay$$fayToJs_string(fayObj){
// Serialize Fay string to JavaScript string.
var str = "";
fayObj = Fay$$_(fayObj);
while(fayObj instanceof Fay$$Cons) {
str += Fay$$_(fayObj.car);
fayObj = Fay$$_(fayObj.cdr);
}
return str;
};
function Fay$$jsToFay_string(x){
return Fay$$list(x)
};
// Special num/bool serializers.
function Fay$$jsToFay_int(x){return x;}
function Fay$$jsToFay_double(x){return x;}
function Fay$$jsToFay_bool(x){return x;}
function Fay$$fayToJs_int(x){return Fay$$_(x);}
function Fay$$fayToJs_double(x){return Fay$$_(x);}
function Fay$$fayToJs_bool(x){return Fay$$_(x);}
// Unserialize an object from JS to Fay.
function Fay$$jsToFay(type,jsObj){
var base = type[0];
var args = type[1];
var fayObj;
if(base == "action") {
// Unserialize a "monadic" JavaScript return value into a monadic value.
return new Fay$$Monad(Fay$$jsToFay(args[0],jsObj));
}
else if(base == "function") {
// Unserialize a function from JavaScript to a function that Fay can call.
// So
//
// var f = function(x,y,z){ … }
//
// becomes something like:
//
// function(x){
// return function(y){
// return function(z){
// return new Fay$$$(function(){
// return Fay$$jsToFay(f(Fay$$fayTojs(x),
// Fay$$fayTojs(y),
// Fay$$fayTojs(z))
// }}}}};
var returnType = args[args.length-1];
var funArgs = args.slice(0,-1);
if (jsObj.length > 0) {
var makePartial = function(args){
return function(arg){
var i = args.length;
var fayArg = Fay$$fayToJs(funArgs[i],arg);
var newArgs = args.concat([fayArg]);
if(newArgs.length == funArgs.length) {
return new Fay$$$(function(){
return Fay$$jsToFay(returnType,jsObj.apply(this,newArgs));
});
} else {
return makePartial(newArgs);
}
};
};
return makePartial([]);
}
else
return function (arg) {
return Fay$$jsToFay(["automatic"], jsObj(Fay$$fayToJs(["automatic"], arg)));
};
}
else if(base == "string") {
// Unserialize a JS string into Fay list (String).
// This is a special case, when String is explicit in the type signature,
// with `Automatic' a string would not be decoded.
return Fay$$list(jsObj);
}
else if(base == "list") {
// Unserialize a JS array into a Fay list ([a]).
var serializedList = [];
for (var i = 0, len = jsObj.length; i < len; i++) {
// Unserialize each JS value into a Fay value, too.
serializedList.push(Fay$$jsToFay(args[0],jsObj[i]));
}
// Pop it all in a Fay list.
return Fay$$list(serializedList);
}
else if(base == "tuple") {
// Unserialize a JS array into a Fay tuple ((a,b,c,...)).
var serializedTuple = [];
for (var i = 0, len = jsObj.length; i < len; i++) {
// Unserialize each JS value into a Fay value, too.
serializedTuple.push(Fay$$jsToFay(args[i],jsObj[i]));
}
// Pop it all in a Fay list.
return Fay$$list(serializedTuple);
}
else if(base == "defined") {
return jsObj === undefined
? new Fay.FFI._Undefined()
: new Fay.FFI._Defined(Fay$$jsToFay(args[0],jsObj));
}
else if(base == "nullable") {
return jsObj === null
? new Fay.FFI._Null()
: new Fay.FFI.Nullable(Fay$$jsToFay(args[0],jsObj));
}
else if(base == "int") {
// Int are unboxed, so there's no forcing to do.
// But we can do validation that the int has no decimal places.
// E.g. Math.round(x)!=x? throw "NOT AN INTEGER, GET OUT!"
fayObj = Math.round(jsObj);
if(fayObj!==jsObj) throw "Argument " + jsObj + " is not an integer!";
return fayObj;
}
else if (base == "double" ||
base == "bool" ||
base == "ptr") {
return jsObj;
}
else if(base == "unknown")
return Fay$$jsToFay(["automatic"], jsObj);
else if(base == "automatic" && jsObj instanceof Function) {
#{ifTsJs "let type: string[][]" "var type"} = [["automatic"]];
for (var i = 0; i < jsObj.length; i++)
type.push(["automatic"]);
return Fay$$jsToFay(["function", type], jsObj);
}
else if(base == "automatic" && jsObj instanceof Array) {
var list = null;
for (var i = jsObj.length - 1; i >= 0; i--) {
list = new Fay$$Cons(Fay$$jsToFay([base], jsObj[i]), list);
}
return list;
}
else if(base == "automatic" || base == "user") {
if (jsObj && jsObj['instance']) {
var jsToFayFun = Fay$$jsToFayHash[jsObj["instance"]];
return jsToFayFun ? jsToFayFun(type,type[2],jsObj) : jsObj;
}
else
return jsObj;
}
throw new Error("Unhandled JS->Fay translation type: " + base);
}
// Stores the mappings from js objects to fay types.
// This will be populated by compiled modules.
var Fay$$jsToFayHash = {};
/*******************************************************************************
* Lists.
*/
// Cons object.
function Fay$$Cons(car,cdr){
this.car = car;
this.cdr = cdr;
}
// Make a list.
function Fay$$list(xs){
var out = null;
for(var i=xs.length-1; i>=0;i--)
out = new Fay$$Cons(xs[i],out);
return out;
}
// Built-in list cons.
function Fay$$cons(x){
return function(y){
return new Fay$$Cons(x,y);
};
}
// List index.
// `list' is already forced by the time it's passed to this function.
// `list' cannot be null and `index' cannot be out of bounds.
function Fay$$index(index,list){
for(var i = 0; i < index; i++) {
list = Fay$$_(list.cdr);
}
return list.car;
}
// List length.
// `list' is already forced by the time it's passed to this function.
function Fay$$listLen(list,max){
for(var i = 0; list !== null && i < max + 1; i++) {
list = Fay$$_(list.cdr);
}
return i == max;
}
/*******************************************************************************
* Numbers.
*/
// Built-in *.
function Fay$$mult(x){
return function(y){
return new Fay$$$(function(){
return Fay$$_(x) * Fay$$_(y);
});
};
}
function Fay$$mult$36$uncurried(x,y){
return new Fay$$$(function(){
return Fay$$_(x) * Fay$$_(y);
});
}
// Built-in +.
function Fay$$add(x){
return function(y){
return new Fay$$$(function(){
return Fay$$_(x) + Fay$$_(y);
});
};
}
// Built-in +.
function Fay$$add$36$uncurried(x,y){
return new Fay$$$(function(){
return Fay$$_(x) + Fay$$_(y);
});
}
// Built-in -.
function Fay$$sub(x){
return function(y){
return new Fay$$$(function(){
return Fay$$_(x) - Fay$$_(y);
});
};
}
// Built-in -.
function Fay$$sub$36$uncurried(x,y){
return new Fay$$$(function(){
return Fay$$_(x) - Fay$$_(y);
});
}
// Built-in /.
function Fay$$divi(x){
return function(y){
return new Fay$$$(function(){
return Fay$$_(x) / Fay$$_(y);
});
};
}
// Built-in /.
function Fay$$divi$36$uncurried(x,y){
return new Fay$$$(function(){
return Fay$$_(x) / Fay$$_(y);
});
}
/*******************************************************************************
* Booleans.
*/
// Are two values equal?
function Fay$$equal(lit1, lit2) {
// Simple case
lit1 = Fay$$_(lit1);
lit2 = Fay$$_(lit2);
if (lit1 === lit2) {
return true;
}
// General case
if (lit1 instanceof Array) {
if (lit1.length != lit2.length) return false;
for (var len = lit1.length, i = 0; i < len; i++) {
if (!Fay$$equal(lit1[i], lit2[i])) return false;
}
return true;
} else if (lit1 instanceof Fay$$Cons && lit2 instanceof Fay$$Cons) {
do {
if (!Fay$$equal(lit1.car,lit2.car))
return false;
lit1 = Fay$$_(lit1.cdr), lit2 = Fay$$_(lit2.cdr);
if (lit1 === null || lit2 === null)
return lit1 === lit2;
} while (true);
} else if (typeof lit1 == 'object' && typeof lit2 == 'object' && lit1 && lit2 &&
lit1.instance === lit2.instance) {
for(var x in lit1) {
if(!Fay$$equal(lit1[x],lit2[x]))
return false;
}
return true;
} else {
return false;
}
}
// Built-in ==.
function Fay$$eq(x){
return function(y){
return new Fay$$$(function(){
return Fay$$equal(x,y);
});
};
}
function Fay$$eq$36$uncurried(x,y){
return new Fay$$$(function(){
return Fay$$equal(x,y);
});
}
// Built-in /=.
function Fay$$neq(x){
return function(y){
return new Fay$$$(function(){
return !(Fay$$equal(x,y));
});
};
}
// Built-in /=.
function Fay$$neq$36$uncurried(x,y){
return new Fay$$$(function(){
return !(Fay$$equal(x,y));
});
}
// Built-in >.
function Fay$$gt(x){
return function(y){
return new Fay$$$(function(){
return Fay$$_(x) > Fay$$_(y);
});
};
}
// Built-in >.
function Fay$$gt$36$uncurried(x,y){
return new Fay$$$(function(){
return Fay$$_(x) > Fay$$_(y);
});
}
// Built-in <.
function Fay$$lt(x){
return function(y){
return new Fay$$$(function(){
return Fay$$_(x) < Fay$$_(y);
});
};
}
// Built-in <.
function Fay$$lt$36$uncurried(x,y){
return new Fay$$$(function(){
return Fay$$_(x) < Fay$$_(y);
});
}
// Built-in >=.
function Fay$$gte(x){
return function(y){
return new Fay$$$(function(){
return Fay$$_(x) >= Fay$$_(y);
});
};
}
// Built-in >=.
function Fay$$gte$36$uncurried(x,y){
return new Fay$$$(function(){
return Fay$$_(x) >= Fay$$_(y);
});
}
// Built-in <=.
function Fay$$lte(x){
return function(y){
return new Fay$$$(function(){
return Fay$$_(x) <= Fay$$_(y);
});
};
}
// Built-in <=.
function Fay$$lte$36$uncurried(x,y){
return new Fay$$$(function(){
return Fay$$_(x) <= Fay$$_(y);
});
}
// Built-in &&.
function Fay$$and(x){
return function(y){
return new Fay$$$(function(){
return Fay$$_(x) && Fay$$_(y);
});
};
}
// Built-in &&.
function Fay$$and$36$uncurried(x,y){
return new Fay$$$(function(){
return Fay$$_(x) && Fay$$_(y);
});
;
}
// Built-in ||.
function Fay$$or(x){
return function(y){
return new Fay$$$(function(){
return Fay$$_(x) || Fay$$_(y);
});
};
}
// Built-in ||.
function Fay$$or$36$uncurried(x,y){
return new Fay$$$(function(){
return Fay$$_(x) || Fay$$_(y);
});
}
/*******************************************************************************
* Mutable references.
*/
// Make a new mutable reference.
function Fay$$Ref(x){
this.value = x;
}
// Write to the ref.
function Fay$$writeRef(ref,x){
ref.value = x;
}
// Get the value from the ref.
function Fay$$readRef(ref){
return ref.value;
}
/*******************************************************************************
* Dates.
*/
function Fay$$date(str){
return Date.parse(str);
}
/*******************************************************************************
* Data.Var
*/
function Fay$$Ref2(val){
this.val = val;
}
function Fay$$Sig(){
this.handlers = [];
}
function Fay$$Var(val){
this.val = val;
this.handlers = [];
}
// Helper used by Fay$$setValue and for merging
function Fay$$broadcastInternal(self, val, force){
var handlers = self.handlers;
var exceptions = [];
for(#{ifTsJs "let" "var"} len = handlers.length, i = 0; i < len; i++) {
try {
force(handlers[i][1](val), true);
} catch (e) {
exceptions.push(e);
}
}
// Rethrow the encountered exceptions.
if (exceptions.length > 0) {
console.error("Encountered " + exceptions.length + " exception(s) while broadcasing a change to ", self);
for(#{ifTsJs "let len: number" "var len"} = exceptions.length, i = 0; i < len; i++) {
(function(exception) {
setTimeout(function() { throw exception; }, 0);
})(exceptions[i]);
}
}
}
function Fay$$setValue(self, val, force){
if (self instanceof Fay$$Ref2) {
self.val = val;
} else if (self instanceof Fay$$Var) {
self.val = val;
Fay$$broadcastInternal(self, val, force);
} else if (self instanceof Fay$$Sig) {
Fay$$broadcastInternal(self, val, force);
} else {
throw "Fay$$setValue given something that's not a Ref2, Var, or Sig"
}
}
function Fay$$subscribe(self, f){
var key = {};
self.handlers.push([key,f]);
var searchStart = self.handlers.length - 1;
return function(_){
for(var i = Math.min(searchStart, self.handlers.length - 1); i >= 0; i--) {
if(self.handlers[i][0] == key) {
self.handlers = self.handlers.slice(0,i).concat(self.handlers.slice(i+1));
return;
}
}
return _; // This variable has to be used, otherwise Closure
// strips it out and Fay serialization breaks.
};
}
/*******************************************************************************
* Application code.
*/
|]