packages feed

fay 0.4.0.4 → 0.5.0.0

raw patch · 50 files changed

+512/−379 lines, 50 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Language.Fay.Prelude: FayArray :: FayReturnType
- Language.Fay.Prelude: FayBool :: FayReturnType
- Language.Fay.Prelude: FayList :: FayReturnType
- Language.Fay.Prelude: FayNone :: FayReturnType
- Language.Fay.Prelude: FayString :: FayReturnType
- Language.Fay.Prelude: data FayReturnType
- Language.Fay.Types: FayArray :: FayReturnType
- Language.Fay.Types: FayBool :: FayReturnType
- Language.Fay.Types: FayList :: FayReturnType
- Language.Fay.Types: FayNone :: FayReturnType
- Language.Fay.Types: FayString :: FayReturnType
- Language.Fay.Types: data ArgType
- Language.Fay.Types: data FayReturnType
- Language.Fay.Types: instance Eq ArgType
- Language.Fay.Types: instance Eq FayReturnType
- Language.Fay.Types: instance Read FayReturnType
- Language.Fay.Types: instance Show ArgType
- Language.Fay.Types: instance Show FayReturnType
+ Language.Fay: compileGuards :: [GuardedRhs] -> Compile JsExp
+ Language.Fay: makeList :: [JsExp] -> JsExp
+ Language.Fay: typeRep :: FundamentalType -> JsExp
+ Language.Fay.Types: DateType :: FundamentalType
+ Language.Fay.Types: data FundamentalType
+ Language.Fay.Types: instance Eq FundamentalType
+ Language.Fay.Types: instance Show FundamentalType
- Language.Fay: argType :: Type -> ArgType
+ Language.Fay: argType :: Type -> FundamentalType
- Language.Fay: compileFFI :: Name -> String -> FayReturnType -> Type -> Compile [JsStmt]
+ Language.Fay: compileFFI :: Name -> String -> Type -> Compile [JsStmt]
- Language.Fay: formatFFI :: String -> [(JsParam, ArgType)] -> Compile String
+ Language.Fay: formatFFI :: String -> [(JsParam, FundamentalType)] -> Compile String
- Language.Fay: functionTypeArgs :: Type -> [ArgType]
+ Language.Fay: functionTypeArgs :: Type -> [FundamentalType]
- Language.Fay: serialize :: ArgType -> JsExp -> JsExp
+ Language.Fay: serialize :: FundamentalType -> JsExp -> JsExp
- Language.Fay: unserialize :: FayReturnType -> JsExp -> JsExp
+ Language.Fay: unserialize :: FundamentalType -> JsExp -> JsExp
- Language.Fay.FFI: ffi :: Foreign a => String -> FayReturnType -> a
+ Language.Fay.FFI: ffi :: Foreign a => String -> a
- Language.Fay.Types: BoolType :: ArgType
+ Language.Fay.Types: BoolType :: FundamentalType
- Language.Fay.Types: DoubleType :: ArgType
+ Language.Fay.Types: DoubleType :: FundamentalType
- Language.Fay.Types: FunctionType :: ArgType
+ Language.Fay.Types: FunctionType :: [FundamentalType] -> FundamentalType
- Language.Fay.Types: JsType :: ArgType
+ Language.Fay.Types: JsType :: FundamentalType -> FundamentalType
- Language.Fay.Types: ListType :: ArgType
+ Language.Fay.Types: ListType :: FundamentalType -> FundamentalType
- Language.Fay.Types: StringType :: ArgType
+ Language.Fay.Types: StringType :: FundamentalType
- Language.Fay.Types: UnknownType :: ArgType
+ Language.Fay.Types: UnknownType :: FundamentalType

Files

− CHANGELOG
@@ -1,40 +0,0 @@-2012-07-26-0.1.1.0: Lots of documentation updates, some runtime fixes, some more examples, tail-call optimization prototype.--cf6b848 Add analytics to .cabal.-13b8d0e Fix types for ghc.-4a021a3 Use continue (for now) for TCO (refs #19).-b43220c Tail recursive optimization prototype (refs #19).-137a702 Tail recursive example doc.-6ec356b Ref example.-e10023c fromInteger/fromRational in the runtime (closes #20).-7527aaa Add missing operators to Prelude and built-ins list (closes #16).-9ce6915 Throw error about unsupported `where' (closes #17).-406067c Fix silly enumFromTo mistake.-8c37842 A test-case that tail-recursive functions run iteratively.-1791e68 Optimize constant patterns slightly, shaves off some time.-f016d01 Add Fay.Show (preliminary implementation).-66cf298 Add data/show example.-486c4f3 Add note about generating docs.-15fd56c Add note that you need nodejs installed to run the tests.-ae207b7 Include google analytics in docs and generate index.html rather than home.html.-07f7f4a Updated dom example with onload handler.-e702218 Remove max-width to fix firefox.-aa5dc2b Mention minifying slightly in docs.-e53addc Remove language-javascript dependency.-f976ff6 Remove non-ascii character in runtime.js-2abd59d Don't print counts in fay-tests.-c0127c3 Add small note about livescript.-0d35867 Added fork on github for docs.-f6a0d62 Use === instead of == for jseq (closes #6).-7d1a382 Some word break on the pre's.-37e3d53 Rename modules from Main to X (closes #5).-6734221 Ignore more stuff.-4479709 Toggle examples.-3ddf795 Put examples side-by-side (closes #3).-5ae7cca s/strict/proper to avoid confusion.-a482a5b Convert from function expression assignments to function definitions--2012-07-21-0.1.0.0: First version.-
docs/home.hs view
@@ -25,16 +25,6 @@     indentAndHighlight     setupExpanders     setupTableOfContents-    body <- query ".subheadline"-    addClassWith (\i f -> do alert (show i)-                             alert f-                             return f)-                 body-    return ()--addClassWith :: (Double -> String -> Fay String) -> JQuery -> Fay JQuery-addClassWith = ffi "%2.addClass(function(x,i){ return %1(x,Fay$$list(i)); })" FayNone-  -- | Setup highlighting/code re-indenting. indentAndHighlight :: Fay ()
examples/alert.hs view
@@ -9,5 +9,5 @@ main = alert "Hello, World!"  -- | Alert using window.alert.-alert :: Foreign a => a -> Fay ()-alert = ffi "window.alert(%1)" FayNone+alert :: String -> Fay ()+alert = ffi "window.alert(%1)"
examples/canvaswater.hs view
@@ -55,11 +55,11 @@  -- | Add an event listener to an element. addEventListener :: (Foreign a,Eventable a) => a -> String -> Fay () -> Bool -> Fay ()-addEventListener = ffi "%1['addEventListener'](%2,%3,%4)" FayNone+addEventListener = ffi "%1['addEventListener'](%2,%3,%4)"  -- | Get an element by its ID. getElementById :: String -> Fay Element-getElementById = ffi "document['getElementById'](%1)" FayNone+getElementById = ffi "document['getElementById'](%1)"  -------------------------------------------------------------------------------- -- Images@@ -70,11 +70,11 @@  -- | Make a new image. newImage :: Fay Image-newImage = ffi "new Image()" FayNone+newImage = ffi "new Image()"  -- | Make a new image. setSrc :: Image -> String -> Fay ()-setSrc = ffi "%1['src'] = %2" FayNone+setSrc = ffi "%1['src'] = %2"  -------------------------------------------------------------------------------- -- Canvas@@ -85,11 +85,11 @@  -- | Get an element by its ID. getContext :: Element -> String -> Fay Context-getContext = ffi "%1['getContext'](%2)" FayNone+getContext = ffi "%1['getContext'](%2)"  -- | Draw an image onto a canvas rendering context. drawImage :: Context -> Image -> Double -> Double -> Fay ()-drawImage = ffi "%1['drawImage'](%2,%3,%4)" FayNone+drawImage = ffi "%1['drawImage'](%2,%3,%4)"  -- | Draw an image onto a canvas rendering context. --@@ -101,15 +101,15 @@ drawImageSpecific :: Context -> Image                   -> Double -> Double -> Double -> Double -> Double -> Double -> Double -> Double                   -> Fay ()-drawImageSpecific = ffi "%1['drawImage'](%2,%3,%4,%5,%6,%7,%8,%9,%10)" FayNone+drawImageSpecific = ffi "%1['drawImage'](%2,%3,%4,%5,%6,%7,%8,%9,%10)"  -- | Set the fill style. setFillStyle :: Context -> String -> Fay ()-setFillStyle = ffi "%1['fillStyle']=%2" FayNone+setFillStyle = ffi "%1['fillStyle']=%2"  -- | Set the fill style. setFillRect :: Context -> Double -> Double -> Double -> Double -> Fay ()-setFillRect = ffi "%1['fillRect'](%2,%3,%4,%5)" FayNone+setFillRect = ffi "%1['fillRect'](%2,%3,%4,%5)"  -------------------------------------------------------------------------------- -- Ref@@ -120,35 +120,35 @@  -- | Make a new mutable reference. newRef :: Foreign a => a -> Fay (Ref a)-newRef = ffi "new Fay$$Ref(%1)" FayNone+newRef = ffi "new Fay$$Ref(%1)"  -- | Replace the value in the mutable reference. writeRef :: Foreign a => Ref a -> a -> Fay ()-writeRef = ffi "Fay$$writeRef(%1,%2)" FayNone+writeRef = ffi "Fay$$writeRef(%1,%2)"  -- | Get the referred value from the mutable value. readRef :: Foreign a => Ref a -> Fay a-readRef = ffi "Fay$$readRef(%1)" FayNone+readRef = ffi "Fay$$readRef(%1)"  -------------------------------------------------------------------------------- -- Misc  -- | Alert using window.alert. alert :: Foreign a => a -> Fay ()-alert = ffi "window['alert'](%1)" FayNone+alert = ffi "window['alert'](%1)"  -- | Alert using window.alert. print :: Double -> Fay ()-print = ffi "console['log'](%1)" FayNone+print = ffi "console['log'](%1)"  -- | Alert using window.alert. log :: String -> Fay ()-log = ffi "console['log'](%1)" FayNone+log = ffi "console['log'](%1)"  -- | Alert using window.alert. sin :: Double -> Double-sin = ffi "window.Math['sin'](%1)" FayNone+sin = ffi "window.Math['sin'](%1)"  -- | Alert using window.alert. setInterval :: Fay () -> Double -> Fay ()-setInterval = ffi "window['setInterval'](%1,%2)" FayNone+setInterval = ffi "window['setInterval'](%1,%2)"
examples/console.hs view
@@ -8,5 +8,5 @@ main = print "Hello, World!"  -- | Print using console.log.-print :: Foreign a => a -> Fay ()-print = ffi "console.log(%1)" FayNone+print :: String -> Fay ()+print = ffi "console.log(%1)"
examples/data.hs view
@@ -18,4 +18,4 @@ main = print (show (Foo 123 "abc" Bar))  print :: String -> Fay ()-print = ffi "console.log(%1)" FayNone+print = ffi "console.log(%1)"
examples/dom.hs view
@@ -12,16 +12,16 @@ printBody :: Fay () printBody = do   result <- documentGetElements "body"-  print result+  print (show result)  print :: String -> Fay ()-print = ffi "console.log(%1)" FayNone+print = ffi "console.log(%1)"  data Element instance Foreign Element  documentGetElements :: String -> Fay [Element]-documentGetElements = ffi "document.getElementsByTagName(%1)" FayArray+documentGetElements = ffi "document.getElementsByTagName(%1)"  addEventListener :: String -> Fay () -> Bool -> Fay ()-addEventListener = ffi "window['addEventListener'](%1,%2,%3)" FayNone+addEventListener = ffi "window['addEventListener'](%1,%2,%3)"
examples/ref.hs view
@@ -20,13 +20,13 @@ instance Foreign a => Foreign (Ref a)  newRef :: Foreign a => a -> Fay (Ref a)-newRef = ffi "new Fay$$Ref(%1)" FayNone+newRef = ffi "new Fay$$Ref(%1)"  writeRef :: Foreign a => Ref a -> a -> Fay ()-writeRef = ffi "Fay$$writeRef(%1,%2)" FayNone+writeRef = ffi "Fay$$writeRef(%1,%2)"  readRef :: Foreign a => Ref a -> Fay a-readRef = ffi "Fay$$readRef(%1)" FayNone+readRef = ffi "Fay$$readRef(%1)"  print :: String -> Fay ()-print = ffi "console.log(%1)" FayNone+print = ffi "console.log(%1)"
examples/tailrecursive.hs view
@@ -28,10 +28,10 @@ sum n acc = sum (n - 1) (acc + n)  getSeconds :: Fay Double-getSeconds = foreignFay "new Date" FayNone+getSeconds = ffi "new Date()"  printD :: Double -> Fay ()-printD = foreignFay "console.log" FayNone+printD = ffi "console.log(%1)"  printS :: String -> Fay ()-printS = foreignFay "console.log" FayNone+printS = ffi "console.log(%1)"
fay.cabal view
@@ -1,5 +1,5 @@ name:                fay-version:             0.4.0.4+version:             0.5.0.0 synopsis:            A compiler for Fay, a Haskell subset that compiles to JavaScript. description:         Fay is a proper subset of Haskell which can be compiled (type-checked)                      with GHC, and compiled to JavaScript. It is lazy, pure, with a Fay monad,@@ -22,7 +22,7 @@                      .                      /Release Notes/                      .-                     * Added help to the fay compiler executable.+                     * MAJOR FFI RESTRUCTURING. Please see commit d6c346c320245762432928bc83aa8ca98a426138.                      .                      See full history at: <https://github.com/chrisdone/fay/commits> homepage:            http://fay-lang.org/@@ -48,7 +48,8 @@                      tests/23 tests/23.hs tests/24 tests/24.hs tests/25.hs tests/26 tests/26.hs                      tests/2.hs tests/3 tests/3.hs tests/4 tests/4.hs tests/5 tests/5.hs tests/6                      tests/6.hs tests/7 tests/7.hs tests/8 tests/8.hs tests/9 tests/9.hs tests/27-                     tests/27.hs tests/28 tests/28.hs+                     tests/27.hs tests/28 tests/28.hs tests/30.hs tests/29 tests/30 tests/29.hs+                     tests/31.hs tests/31                      -- Documentation files                      docs/beautify.js docs/highlight.pack.js docs/home.css docs/home.js docs/jquery.js                      docs/analytics.js@@ -64,8 +65,6 @@                      docs/snippets/patterns.hs                      docs/snippets/tail.hs                      docs/home.hs-                     -- Misc-                     CHANGELOG  library   hs-source-dirs:    src
hs/stdlib.hs view
@@ -1,19 +1,9 @@-data ArgType-  = DateType-  | FunctionType-  | JsType-  | StringType-  | DoubleType-  | ListType-  | BoolType-  | UnknownType- data Maybe a   = Just a   | Nothing  show :: (Foreign a,Show a) => a -> String-show = ffi "Fay$$encodeShow(%1)" FayString+show = ffi "Fay$$encodeShow(%1)"  -- There is only Double in JS. fromInteger x = x
js/runtime.js view
@@ -2,15 +2,15 @@ var False = false;  /*******************************************************************************-* Thunks.-*/+ * Thunks.+ */  // Force a thunk (if it is a thunk) until WHNF. function _(thunkish,nocache){-  while (thunkish instanceof $) {-    thunkish = thunkish.force(nocache);-  }-  return thunkish;+    while (thunkish instanceof $) {+        thunkish = thunkish.force(nocache);+    }+    return thunkish; }  // Apply a function to arguments (see method2 in Fay.hs).@@ -24,339 +24,410 @@  // Thunk object. function $(value){-  this.forced = false;-  this.value = value;+    this.forced = false;+    this.value = value; }  // Force the thunk. $.prototype.force = function(nocache){-  return nocache-    ? this.value()-    : this.forced-    ? this.value-    : (this.forced = true, this.value = this.value());+    return nocache+        ? this.value()+        : this.forced+        ? this.value+        : (this.forced = true, this.value = this.value()); };  /*******************************************************************************-* Constructors.-*/+ * Constructors.+ */  // A constructor. function Fay$$Constructor(){-  this.name = arguments[0];-  this.fields = Array.prototype.slice.call(arguments,1);+    this.name = arguments[0];+    this.fields = Array.prototype.slice.call(arguments,1); }  // Eval in the context of the Haskell bindings. function Fay$$eval(str){-  return eval(str);+    return eval(str); }  /*******************************************************************************-* Monad.-*/+ * Monad.+ */  function Fay$$Monad(value){-  this.value = value;+    this.value = value; }  // >> function Fay$$then(a){-  return function(b){-    return new $(function(){-      _(a,true);-      return b;-    });-  };+    return function(b){+        return new $(function(){+            _(a,true);+            return b;+        });+    }; }  // >>= function Fay$$bind(m){-  return function(f){-    return new $(function(){-      var monad = _(m,true);-      return f(monad.value);-    });-  };+    return function(f){+        return new $(function(){+            var monad = _(m,true);+            return f(monad.value);+        });+    }; }  var Fay$$unit = null;  // return function Fay$$return(a){-  return new Fay$$Monad(a);+    return new Fay$$Monad(a); }  /*******************************************************************************-* FFI.-*/+ * FFI.+ */  // Serialize a Fay object to JS.-function Fay$$serialize(type,obj){-  type = _(type);-  if(type) type = type[0].name;-  if(type == "JsType"){-    return function(){-      return _(obj,true).value;-    };-  } else {-    obj = _(obj);-    if(type == "StringType" ||-       (obj instanceof Fay$$Cons && typeof obj.car == 'string')){-      var str = "";-      while(obj instanceof Fay$$Cons) {-        str += obj.car;-        obj = _(obj.cdr);-      }-      return str;-    } else if(type == "FunctionType" || typeof obj == 'function'){-      return function(){-        var out = obj;-        for (var len = arguments.length, i = 0; i < len; i++){-          if(typeof out != 'function') {-            throw "Wrong number of arguments for callback: " + arguments.toString();-          }-          out = out(arguments[i]);+function Fay$$serialize(type,fayObj){+    var base = type[0];+    var args = type[1];+    var jsObj;+    switch(base){+    case "action": {+        // A nullary monadic action. Should become a nullary JS function.+        // Fay () -> function(){ return ... }+        jsObj = function(){+            return Fay$$serialize(args[0],_(fayObj,true).value);+        };+        break;+    }+    case "function": {+        // A proper function.+        jsObj = function(){+            var return_type = args[args.length-1];+            var len = args.length;+            // If some arguments.+            if (len > 1) {+                // Apply to all the arguments.+                fayObj = _(fayObj,true);+                for (var i = 0, len = len; i < len - 1; i++) {+                    // Unserialize the JS values to Fay for the Fay callback.+                    fayObj = _(fayObj(Fay$$unserialize(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$$serialize(return_args[0],fayObj.value);+                }+                // Otherwise just serialize the value direct.+                else {+                    return Fay$$serialize(return_type,fayObj);+                }+            } else {+                throw new Error("Nullary function?");+            }+        };+        break;+    }+    case "string": {+        // Serialize Fay string to JavaScript string.+        var str = "";+        fayObj = _(fayObj);+        while(fayObj instanceof Fay$$Cons) {+            str += fayObj.car;+            fayObj = _(fayObj.cdr);         }-        return _(out,true);-      };-    } else if(type == "ListType" || (obj instanceof Fay$$Cons)){-      var arr = [];-      while(obj instanceof Fay$$Cons) {-        arr.push(Fay$$serialize(null,obj.car));-        obj = _(obj.cdr);-      }-      return arr;-    } // else if(type == "BoolType || obj == _(True) || obj == _(False)) {-    //   return obj == _(True);-    // }-    else {-      return obj;+        jsObj = str;+        break;     }-  }+    case "list": {+        // Serialize Fay list to JavaScript array.+        var arr = [];+        fayObj = _(fayObj);+        while(fayObj instanceof Fay$$Cons) {+            arr.push(Fay$$serialize(args[0],fayObj.car));+            fayObj = _(fayObj.cdr);+        }+        jsObj = arr;+        break;+    }+    case "double": {+        // Serialize double, just force the argument. Doubles are unboxed.+        jsObj = _(fayObj);+        break;+    }+    case "unknown": {+        // Just force unknown values to WHNF.+        jsObj = _(fayObj);+        break;+    }+    case "bool": {+        // Bools are unboxed.+        jsObj = fayObj;+        break;+    }+    default: throw new Error("Unhandled serialize type: " + base);+    }+    return jsObj; } +// Unserialize an object from JS to Fay.+function Fay$$unserialize(type,jsObj){+    var base = type[0];+    var args = type[1];+    var fayObj;+    switch(base){+    case "action": {+        // Unserialize a "monadic" JavaScript return value into a monadic value.+        fayObj = Fay$$return(Fay$$unserialize(args[0],jsObj));+        break;+    }+    case "string": {+        // Unserialize a JS string into Fay list (String).+        fayObj = Fay$$list(jsObj);+        break;+    }+    case "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$$unserialize(args[0],jsObj[i]));+        }+        // Pop it all in a Fay list.+        fayObj = Fay$$list(serializedList);+        break;+    }+    case "double": {+        // Doubles are unboxed, so there's nothing to do.+        fayObj = jsObj;+        break;+    }+    case "unknown": {+        // Any unknown values can be left as-is.+        fayObj = jsObj;+        break;+    }+    default: throw new Error("Unhandled unserialize type: " + base);+    }+    return fayObj;+}+ // Encode a value to a Show representation function Fay$$encodeShow(x){-  if (x instanceof $) x = _(x);-  if (x instanceof Array) {-    if (x.length == 0) {-      return "[]";-    } else {-      if (x[0] instanceof Fay$$Constructor) {-        if(x[0].fields.length > 0) {-          var args = x.slice(1);-          var fieldNames = x[0].fields;-          return "(" + x[0].name + " { " + args.map(function(x,i){-            return fieldNames[i] + ' = ' + Fay$$encodeShow(x);-          }).join(", ") + " })";+    if (x instanceof $) x = _(x);+    if (x instanceof Array) {+        if (x.length == 0) {+            return "[]";         } else {-          var args = x.slice(1);-          return "(" + [x[0].name].concat(args.map(Fay$$encodeShow)).join(" ") + ")";+            if (x[0] instanceof Fay$$Constructor) {+                if(x[0].fields.length > 0) {+                    var args = x.slice(1);+                    var fieldNames = x[0].fields;+                    return "(" + x[0].name + " { " + args.map(function(x,i){+                        return fieldNames[i] + ' = ' + Fay$$encodeShow(x);+                    }).join(", ") + " })";+                } else {+                    var args = x.slice(1);+                    return "(" + [x[0].name].concat(args.map(Fay$$encodeShow)).join(" ") + ")";+                }+            } else {+                return "[" + x.map(Fay$$encodeShow).join(",") + "]";+            }         }-      } else {-        return "[" + x.map(Fay$$encodeShow).join(",") + "]";-      }+    } else if (typeof x == 'string') {+        return JSON.stringify(x);+    } else if(x instanceof Fay$$Cons) {+        return Fay$$encodeShow(Fay$$serialize(["list",[["unknown"]]],x));+    } else if(x == null) {+        return '[]';+    } else {+        return x.toString();     }-  } else if (typeof x == 'string') {-    return JSON.stringify(x);-  } else if(x instanceof Fay$$Cons) {-    return Fay$$encodeShow(Fay$$serialize(ListType,x));-  } else if(x == null) {-    return '[]';-  } else {-    return x.toString();-  } } -// Unserialize an object from JS to Fay.-function Fay$$unserialize(typ,obj){-  if(typ == 'string' || typ == 'array' || typ == 'list')-    return Fay$$list(obj);-  else if(typ == 'bool')-    return obj? True : False;-  else if(typ == 'data') {-    alert('Time to unserialize a data record!');-  }-  else return obj;-}- /*******************************************************************************-* Lists.-*/+ * Lists.+ */  // Cons object. function Fay$$Cons(car,cdr){-  this.car = car;-  this.cdr = 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;+    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);-  };+    return function(y){+        return new Fay$$Cons(x,y);+    }; }  // List index. function Fay$$index(index){-  return function(list){-    for(var i = 0; i < index; i++) {-      list = _(list).cdr;-    }-    return list.car;-  };+    return function(list){+        for(var i = 0; i < index; i++) {+            list = _(list).cdr;+        }+        return list.car;+    }; }  /*******************************************************************************-* Numbers.-*/+ * Numbers.+ */  // Built-in *. function Fay$$mult(x){-  return function(y){-    return _(x) * _(y);-  };+    return function(y){+        return _(x) * _(y);+    }; }  // Built-in +. function Fay$$add(x){-  return function(y){-    return _(x) + _(y);-  };+    return function(y){+        return _(x) + _(y);+    }; }  // Built-in -. function Fay$$sub(x){-  return function(y){-    return _(x) - _(y);-  };+    return function(y){+        return _(x) - _(y);+    }; }  // Built-in /. function Fay$$div(x){-  return function(y){-    return _(x) / _(y);-  };+    return function(y){+        return _(x) / _(y);+    }; }  /*******************************************************************************-* Booleans.-*/+ * Booleans.+ */  // Are two values equal? function Fay$$equal(lit1,lit2){-  // Simple case-  lit1 = _(lit1);-  lit2 = _(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;+    // Simple case+    lit1 = _(lit1);+    lit2 = _(lit2);+    if(lit1 === lit2) {+        return true;     }-    return true;-  } else if (lit1 instanceof Fay$$Cons) {-    while(lit1 instanceof Fay$$Cons && lit2 instanceof Fay$$Cons && Fay$$equal(lit1.car,lit2.car))-      lit1 = lit1.cdr, lit2 = lit2.cdr;-    return (lit1 === null && lit2 === null);-  } else return false;+    // 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) {+        while(lit1 instanceof Fay$$Cons && lit2 instanceof Fay$$Cons && Fay$$equal(lit1.car,lit2.car))+            lit1 = lit1.cdr, lit2 = lit2.cdr;+        return (lit1 === null && lit2 === null);+    } else return false; }  // Built-in ==. function Fay$$eq(x){-  return function(y){-    return Fay$$equal(x,y);-  };+    return function(y){+        return Fay$$equal(x,y);+    }; }  // Built-in /=. function Fay$$neq(x){-  return function(y){-    return !(Fay$$equal(x,y));-  };+    return function(y){+        return !(Fay$$equal(x,y));+    }; }  // Built-in >. function Fay$$gt(x){-  return function(y){-    return _(x) > _(y);-  };+    return function(y){+        return _(x) > _(y);+    }; }  // Built-in <. function Fay$$lt(x){-  return function(y){-    return _(x) < _(y);-  };+    return function(y){+        return _(x) < _(y);+    }; }  // Built-in >=. function Fay$$gte(x){-  return function(y){-    return _(x) >= _(y);-  };+    return function(y){+        return _(x) >= _(y);+    }; }  // Built-in <=. function Fay$$lte(x){-  return function(y){-    return _(x) <= _(y);-  };+    return function(y){+        return _(x) <= _(y);+    }; }  // Built-in &&. function Fay$$and(x){-  return function(y){-    return _(x) && _(y);-  };+    return function(y){+        return _(x) && _(y);+    }; }  // Built-in ||. function Fay$$or(x){-  return function(y){-    return _(x) || _(y);-  };+    return function(y){+        return _(x) || _(y);+    }; }  /*******************************************************************************-* Mutable references.-*/+ * Mutable references.+ */  // Make a new mutable reference. function Fay$$Ref(x){-  this.value = x;+    this.value = x; }  // Write to the ref. function Fay$$writeRef(ref,x){-  ref.value = x;+    ref.value = x; }  // Get the value from the ref. function Fay$$readRef(ref,x){-  return ref.value;+    return ref.value; }  /*******************************************************************************-* Dates.-*/+ * Dates.+ */ function Fay$$date(str){-  return window.Date.parse(str);+    return window.Date.parse(str); }  /*******************************************************************************-* Application code.-*/+ * Application code.+ */
src/Language/Fay.hs view
@@ -159,26 +159,22 @@   case pat of     PatBind _ (PVar ident) Nothing (UnGuardedRhs rhs) (BDecls []) ->       case ffiExp rhs of-        Just (formatstr,typ) -> case sig of-          Just sig -> compileFFI ident formatstr typ sig+        Just formatstr -> case sig of+          Just sig -> compileFFI ident formatstr sig           Nothing  -> throwError (FfiNeedsTypeSig pat)         _ -> compileNormalPatBind toplevel ident rhs     _ -> throwError (UnsupportedDeclaration pat) -  where ffiExp (App (App (Var (UnQual (Ident "ffi")))-                         (Lit (String formatstr)))-                    (Con (UnQual (Ident (reads -> [(typ,"")])))))-          = Just (formatstr,typ)+  where ffiExp (App (Var (UnQual (Ident "ffi"))) (Lit (String formatstr))) = Just formatstr         ffiExp _ = Nothing  -- | Compile an FFI call.-compileFFI :: Name             -- ^ Name of the to-be binding.-           -> String           -- ^ The format string.-           -> FayReturnType    -- ^ The return type.-           -> Type             -- ^ Type signature.+compileFFI :: Name   -- ^ Name of the to-be binding.+           -> String -- ^ The format string.+           -> Type   -- ^ Type signature.            -> Compile [JsStmt]-compileFFI name formatstr returnType sig = do-  inner <- formatFFI formatstr (zip params funcArgTypes)+compileFFI name formatstr sig = do+  inner <- formatFFI formatstr (zip params funcFundamentalTypes)   case JS.parse (printJS (wrapReturn inner)) (prettyPrint name) of     Left err -> throwError (FfiFormatInvalidJavaScript inner err)     Right{}  -> fmap return (bindToplevel True (UnQual name) (body inner))@@ -187,19 +183,18 @@         wrapParam name inner = JsFun [name] [] (Just inner)         params = zipWith const uniqueNames [1..typeArity sig]         wrapReturn inner = thunk $-          case lastMay funcArgTypes of-            -- Fay action:-            Just JsType -> monad (unserialize returnType (JsRawExp inner))+          case lastMay funcFundamentalTypes of             -- Returns a “pure” value;             Just{} -> unserialize returnType (JsRawExp inner)             -- Base case:             Nothing -> JsRawExp inner-        funcArgTypes = functionTypeArgs sig+        funcFundamentalTypes = functionTypeArgs sig+        returnType = last funcFundamentalTypes  -- | Format the FFI format string with the given arguments.-formatFFI :: String              -- ^ The format string.-          -> [(JsParam,ArgType)] -- ^ Arguments.-          -> Compile String      -- ^ The JS code.+formatFFI :: String                      -- ^ The format string.+          -> [(JsParam,FundamentalType)] -- ^ Arguments.+          -> Compile String              -- ^ The JS code. formatFFI formatstr args = go formatstr where   go ('%':'*':xs) = do     these <- mapM inject (zipWith const [1..] args)@@ -227,15 +222,30 @@         return (printJS (serialize typ (JsName arg)))  -- | Serialize a value to native JS, if possible.-serialize :: ArgType -> JsExp -> JsExp+serialize :: FundamentalType -> JsExp -> JsExp serialize typ exp =   case typ of --    UnknownType -> force exp     _ -> JsApp (JsName (hjIdent "serialize"))-               [JsName (fromString (show typ)),exp]+               [typeRep typ,exp] +-- | Get a JS-representation of a fundamental type for encoding/decoding.+typeRep :: FundamentalType -> JsExp+typeRep typ =+  case typ of+    FunctionType xs -> JsList [JsLit $ JsStr "function",JsList (map typeRep xs)]+    JsType x        -> JsList [JsLit $ JsStr "action",JsList [typeRep x]]+    ListType x      -> JsList [JsLit $ JsStr "list",JsList [typeRep x]]+    typ             -> JsList [JsLit $ JsStr nom]+      where nom = case typ of+              StringType -> "string"+              DoubleType -> "double"+              BoolType   -> "bool"+              DateType   -> "date"+              _          -> "unknown"+ -- | Get arg types of a function type.-functionTypeArgs :: Type -> [ArgType]+functionTypeArgs :: Type -> [FundamentalType] functionTypeArgs t =   case t of     TyForall _ _ i -> functionTypeArgs i@@ -244,15 +254,15 @@     r              -> [argType r]  -- | Convert a Haskell type to an internal FFI representation.-argType :: Type -> ArgType+argType :: Type -> FundamentalType argType t =   case t of-    TyApp (TyCon "Fay") _ -> JsType+    TyApp (TyCon "Fay") a -> JsType (argType a)     TyCon "String"        -> StringType     TyCon "Double"        -> DoubleType     TyCon "Bool"          -> BoolType-    TyFun{}               -> FunctionType-    TyList _              -> ListType+    TyFun x xs            -> FunctionType (argType x : functionTypeArgs xs)+    TyList x              -> ListType (argType x)     TyParen st            -> argType st     _                     -> UnknownType @@ -392,8 +402,18 @@ -- | Compile a right-hand-side expression. compileRhs :: Rhs -> Compile JsExp compileRhs (UnGuardedRhs exp) = compileExp exp-compileRhs rhs                = throwError (UnsupportedRhs rhs)+compileRhs (GuardedRhss rhss) = compileGuards rhss +-- | Compile guards+compileGuards :: [GuardedRhs] -> Compile JsExp+compileGuards [] = return . JsThrowExp . JsLit . JsStr $ "Non-exhaustive guards"+compileGuards ((GuardedRhs _ (Qualifier (Var (UnQual (Ident "otherwise"))):_) exp):_) = compileExp exp+compileGuards ((GuardedRhs _ (Qualifier guard:_) exp):rest) =+  JsTernaryIf <$> fmap force (compileExp guard)+              <*> compileExp exp+              <*> compileGuards rest+compileGuards rhss = throwError . UnsupportedRhs . GuardedRhss $ rhss+ -- | Compile a pattern match binding. compileFunMatch :: Bool -> Match -> Compile [JsStmt] compileFunMatch toplevel match =@@ -496,8 +516,10 @@ compileList :: [Exp] -> Compile JsExp compileList xs = do   exps <- mapM compileExp xs-  return (JsApp (JsName (hjIdent "list")) [JsList exps])+  return (makeList exps) +makeList exps = (JsApp (JsName (hjIdent "list")) [JsList exps])+ -- | Compile an if. compileIf :: Exp -> Exp -> Exp -> Compile JsExp compileIf cond conseq alt =@@ -773,18 +795,10 @@ stmtsThunk :: [JsStmt] -> JsExp stmtsThunk stmts = JsNew ":thunk" [JsFun [] stmts Nothing] -unserialize :: FayReturnType -> JsExp -> JsExp+unserialize :: FundamentalType -> JsExp -> JsExp unserialize typ exp =   JsApp (JsName (hjIdent "unserialize"))-        [JsLit (JsStr (showReturnType typ)),exp]--  where showReturnType typ =-          case typ of-            FayArray -> "array"-            FayList -> "list"-            FayString -> "string"-            FayBool -> "bool"-            FayNone -> ""+        [typeRep typ,exp]  -- | Force an expression in a thunk. force :: JsExp -> JsExp
src/Language/Fay/Compiler.hs view
@@ -56,6 +56,7 @@                                               ,"this.$list       = Fay$$list;"                                               ,"this.$encodeShow = Fay$$encodeShow;"                                               ,"this.$eval       = Fay$$eval;"+                                              ,"this.$serialize  = Fay$$serialize;"                                               ]                                  else ""                              ,"};"
src/Language/Fay/FFI.hs view
@@ -4,7 +4,7 @@  module Language.Fay.FFI where -import           Language.Fay.Types (Fay, FayReturnType(..))+import           Language.Fay.Types (Fay) import           Prelude            (Bool, Char, Double, String, error)  data JsPtr a@@ -40,6 +40,5 @@ ffi   :: Foreign a   => String        -- ^ The foreign value.-  -> FayReturnType -- ^ JS return type (if any).   -> a             -- ^ Bottom. ffi = error "Language.Fay.FFI.foreignFay: Used foreign function not in a JS engine context."
src/Language/Fay/Prelude.hs view
@@ -2,7 +2,6 @@  module Language.Fay.Prelude   (Fay-  ,FayReturnType(..)   ,Char   ,String   ,Integer@@ -34,7 +33,7 @@   where  import           Language.Fay.Stdlib-import           Language.Fay.Types  (Fay, FayReturnType(..))+import           Language.Fay.Types  (Fay)  import           GHC.Real            (Ratio) import           Prelude             (Bool(..), Char, Double, Integer, Maybe(..), Ord, Read(..),
src/Language/Fay/Types.hs view
@@ -18,8 +18,7 @@   ,Fay   ,CompileConfig(..)   ,CompileState(..)-  ,FayReturnType(..)-  ,ArgType(..))+  ,FundamentalType(..))   where  import           Control.Applicative@@ -158,13 +157,21 @@   | JsBool Bool   deriving (Show,Eq) -data FayReturnType = FayArray | FayList | FayString | FayBool | FayNone-  deriving (Read,Show,Eq)- -- | These are the data types that are serializable directly to native -- JS data types. Strings, floating points and arrays. The others are: -- actiosn in the JS monad, which are thunks that shouldn't be forced -- when serialized but wrapped up as JS zero-arg functions, and -- unknown types can't be converted but should at least be forced.-data ArgType = FunctionType | JsType | StringType | DoubleType | ListType | BoolType | UnknownType-  deriving (Show,Eq)+data FundamentalType+   -- Recursive types.+ = FunctionType [FundamentalType]+ | JsType FundamentalType+ | ListType FundamentalType+ -- Simple types.+ | DateType+ | StringType+ | DoubleType+ | BoolType+ -- | Unknown.+ | UnknownType+   deriving (Show,Eq)
src/Main.hs view
@@ -37,5 +37,8 @@   ,"  fay [OPTIONS] <hs-input-file> ... "   ,""   ,"OPTIONS"-  ,"  -autorun    automatically call main in generated JavaScript"+  ,"  -autorun       automatically call main in generated JavaScript"+  ,"  -inline-force  inline forcing, adds some speed for numbers, blows up code a bit"+  ,"  -flatten-apps  flatten function application, can be more readable,"+  ,"                 no noticeable speed difference"   ]
tests/10.hs view
@@ -9,4 +9,4 @@ append []     ys = ys  print :: String -> Fay ()-print = ffi "console.log(%1)" FayNone+print = ffi "console.log(%1)"
tests/11.hs view
@@ -1,4 +1,4 @@-main = print (show (take 5 (let ns = 1 : map (\x -> x + 1) ns in ns)))+main = print (showList (take 5 (let ns = 1 : map (\x -> x + 1) ns in ns)))  take 0 _      = [] take n (x:xs) = x : take (n - 1) xs@@ -7,4 +7,7 @@ map f (x:xs) = f x : map f xs  print :: String -> Fay ()-print = ffi "console.log(%1)" FayNone+print = ffi "console.log(%1)"++showList :: [Double] -> String+showList = ffi "JSON.stringify(%1)"
tests/12.hs view
@@ -1,4 +1,4 @@-main = print (show (take 5 (let ns = 1 : map (foo 123) ns in ns)))+main = print (showList (take 5 (let ns = 1 : map (foo 123) ns in ns)))  foo x y = x * y / 2 @@ -9,7 +9,7 @@ map f (x:xs) = f x : map f xs  print :: String -> Fay ()-print = ffi "console.log(%1)" FayNone+print = ffi "console.log(%1)" -show :: String -> String-show = ffi "JSON.stringify(%1)" FayString+showList :: [Double] -> String+showList = ffi "JSON.stringify(%1)"
tests/13.hs view
@@ -3,4 +3,4 @@                False -> "Ney!")  print :: String -> Fay ()-print = ffi "console.log(%1)" FayNone+print = ffi "console.log(%1)"
tests/14.hs view
@@ -3,4 +3,4 @@                False -> "Ney!")  print :: String -> Fay ()-print = ffi "console.log(%1)" FayNone+print = ffi "console.log(%1)"
tests/15.hs view
@@ -3,4 +3,4 @@                _    -> "Ney!")  print :: String -> Fay ()-print = ffi "console.log(%1)" FayNone+print = ffi "console.log(%1)"
tests/16.hs view
@@ -4,4 +4,4 @@                 Person "Chris" "Done" 13 -> "Hello!")  print :: String -> Fay ()-print = ffi "console.log(%1)" FayNone+print = ffi "console.log(%1)"
tests/17.hs view
@@ -5,4 +5,4 @@                 _ -> "World!")  print :: String -> Fay ()-print = ffi "console.log(%1)" FayNone+print = ffi "console.log(%1)"
tests/18.hs view
@@ -7,4 +7,4 @@                 _ -> "World!")  print :: String -> Fay ()-print = ffi "console.log(%1)" FayNone+print = ffi "console.log(%1)"
tests/19.hs view
@@ -8,4 +8,4 @@ foo _ = "World!"  print :: String -> Fay ()-print = ffi "console.log(%1)" FayNone+print = ffi "console.log(%1)"
tests/20.hs view
@@ -1,4 +1,4 @@ main = print "Hello," >> print "World!"  print :: String -> Fay ()-print = ffi "console.log(%1)" FayNone+print = ffi "console.log(%1)"
tests/21.hs view
@@ -1,4 +1,4 @@ main = do print "Hello,"; print "World!"  print :: String -> Fay ()-print = ffi "console.log(%1)" FayNone+print = ffi "console.log(%1)"
tests/22.hs view
@@ -3,4 +3,4 @@   print x  print :: String -> Fay ()-print = ffi "console.log(%1)" FayNone+print = ffi "console.log(%1)"
tests/23.hs view
@@ -3,4 +3,4 @@   print "OK."  print :: String -> Fay ()-print = ffi "console.log(%1)" FayNone+print = ffi "console.log(%1)"
tests/24.hs view
@@ -5,4 +5,4 @@   _           -> "Broken.")  print :: String -> Fay ()-print = ffi "console.log(%1)" FayNone+print = ffi "console.log(%1)"
tests/25.hs view
@@ -1,4 +1,4 @@ main = print ((\a 'a' -> "OK.") 0 'b')  print :: String -> Fay ()-print = ffi "console.log(%1)" FayNone+print = ffi "console.log(%1)"
tests/26.hs view
@@ -1,4 +1,4 @@ main = print (5 * 3 / 2)  print :: Double -> Fay ()-print = ffi "console.log(%1)" FayNone+print = ffi "console.log(%1)"
tests/27.hs view
@@ -14,7 +14,7 @@ sum n acc = sum (n - 1) (acc + n)  getSeconds :: Fay Double-getSeconds = foreignFay "new Date" FayNone+getSeconds = foreignFay "new Date"  print :: Double -> Fay ()-print = ffi "console.log(%1)" FayNone+print = ffi "console.log(%1)"
tests/28.hs view
@@ -5,7 +5,7 @@ import           Language.Fay.Prelude  print :: String -> Fay ()-print = ffi "console.log(%1)" FayNone+print = ffi "console.log(%1)"  main :: Fay () main = print $ show $ fromInteger 5
+ tests/29 view
@@ -0,0 +1,3 @@+[[1,2,3],[1,2,3]]+[[1,2,3],1,[2,3]]+[[1,2,3],1,[2,3]]
+ tests/29.hs view
@@ -0,0 +1,28 @@+{-# LANGUAGE NoImplicitPrelude #-}+module Test where++-- | As pattern matches++import Language.Fay.Prelude+import Language.Fay.FFI++matchSame :: [a] -> ([a],[a])+matchSame x@y = (x,y)++matchSplit :: [a] -> ([a],a,[a])+matchSplit x@(y:z) = (x,y,z)++matchNested :: (a, [b]) -> ([b],b,[b])+matchNested (a,b@(x:xs)) = (b,x,xs)++print :: String -> Fay ()+print = ffi "console.log(%1)"++main :: Fay ()+main = do+  print $ show $ matchSame [1,2,3]+  print $ show $ matchSplit [1,2,3]+  print $ show $ matchNested (1, [1,2,3])++show :: Foreign a => a -> String+show = ffi "JSON.stringify(%1)"
tests/3.hs view
@@ -1,4 +1,4 @@ main = print 1  print :: Double -> Fay ()-print = ffi "console.log(%1)" FayNone+print = ffi "console.log(%1)"
+ tests/30 view
@@ -0,0 +1,3 @@+[true,false]+[2,1,0]+[true,false]
+ tests/30.hs view
@@ -0,0 +1,30 @@+{-# LANGUAGE NoImplicitPrelude #-}+module Test where++-- | As pattern matches++import Language.Fay.Prelude+import Language.Fay.FFI++isPositive :: Double -> Bool+isPositive x | x > 0 = True+             | x <= 0 = False++threeConds x | x > 1 = 2+             | x == 1 = 1+             | x < 1 = 0++withOtherwise x | x > 1 = True+                | otherwise = False++-- Not called, throws "non-exhaustive guard"+nonExhaustive x | x > 1 = True++print :: String -> Fay ()+print = ffi "console.log(%1)"++main :: Fay ()+main = do+  print $ show $ (isPositive 1, isPositive 0)+  print $ show $ (threeConds 3, threeConds 1, threeConds 0)+  print $ show $ (withOtherwise 2, withOtherwise 0)
+ tests/31 view
@@ -0,0 +1,1 @@+Hai!
+ tests/31.hs view
@@ -0,0 +1,32 @@+-- | Mutable references.++{-# LANGUAGE EmptyDataDecls    #-}+{-# LANGUAGE NoImplicitPrelude #-}++module Ref where++import           Language.Fay.FFI+import           Language.Fay.Prelude++main :: Fay ()+main = do+  ref <- newRef "Hello, World!"+  x <- readRef ref+  writeRef ref "Hai!"+  readRef ref >>= print++data Ref a+instance Show (Ref a)+instance Foreign a => Foreign (Ref a)++newRef :: Foreign a => a -> Fay (Ref a)+newRef = ffi "new Fay$$Ref(%1)"++writeRef :: Foreign a => Ref a -> a -> Fay ()+writeRef = ffi "Fay$$writeRef(%1,%2)"++readRef :: Foreign a => Ref a -> Fay a+readRef = ffi "Fay$$readRef(%1)"++print :: String -> Fay ()+print = ffi "console.log(%1)"
tests/4.hs view
@@ -1,4 +1,4 @@ main = print "Hello, World!"  print :: String -> Fay ()-print = ffi "console.log(%1)" FayNone+print = ffi "console.log(%1)"
tests/5.hs view
@@ -1,4 +1,4 @@ main = print (2 * 4 / 2)  print :: Double -> Fay ()-print = ffi "console.log(%1)" FayNone+print = ffi "console.log(%1)"
tests/6.hs view
@@ -1,4 +1,4 @@ main = print (10 + (2 * (4 / 2)))  print :: Double -> Fay ()-print = ffi "console.log(%1)" FayNone+print = ffi "console.log(%1)"
tests/7.hs view
@@ -1,4 +1,4 @@ main = print True  print :: Double -> Fay ()-print = ffi "console.log(%1)" FayNone+print = ffi "console.log(%1)"
tests/8.hs view
@@ -3,7 +3,7 @@ main = print (head (fix (\xs -> 123 : xs)))  print :: Double -> Fay ()-print = ffi "console.log(%1)" FayNone+print = ffi "console.log(%1)"  head (x:xs) = x 
tests/9.hs view
@@ -1,7 +1,7 @@ main = print (head (tail (fix (\xs -> 123 : xs))))  print :: Double -> Fay ()-print = ffi "console.log(%1)" FayNone+print = ffi "console.log(%1)"  head (x:xs) = x