packages feed

fay 0.17.0.0 → 0.18.0.0

raw patch · 78 files changed

+2422/−1888 lines, 78 filesdep +haskell-namesdep +haskell-packagesdep ~basedep ~haskell-src-extsdep ~optparse-applicativePVP ok

version bump matches the API change (PVP)

Dependencies added: haskell-names, haskell-packages

Dependency ranges changed: base, haskell-src-exts, optparse-applicative

API changes (from Hackage documentation)

- Fay.Compiler: compileModule :: FilePath -> String -> Compile [JsStmt]
- Fay.Compiler: compileModuleFromFile :: FilePath -> Compile [JsStmt]
- Fay.Compiler: compileModuleFromName :: ModuleName -> Compile [JsStmt]
- Fay.Compiler: compileToAst :: (Show from, Show to, CompilesTo from to) => FilePath -> CompileReader -> CompileState -> (from -> Compile to) -> String -> IO (Either CompileError (to, CompileState, CompileWriter))
- Fay.Compiler: instance CompilesTo Module [JsStmt]
- Fay.Compiler: runCompile :: CompileReader -> CompileState -> Compile a -> IO (Either CompileError (a, CompileState, CompileWriter))
- Fay.Compiler.Debug: compileTestAst :: (Show from, Show to, CompilesTo from to) => CompileConfig -> (from -> Compile to) -> String -> IO ()
- Fay.Compiler.Debug: debug :: (Show from, Show to, CompilesTo from to) => (from -> Compile to) -> String -> IO ()
- Fay.Compiler.Debug: printCompile :: (Show from, Show to, CompilesTo from to) => CompileConfig -> (from -> Compile to) -> String -> IO ()
- Fay.Compiler.Debug: printTestCompile :: String -> IO ()
- Fay.Types: _stateExports :: CompileState -> Map ModuleName (Set QName)
- Fay.Types: addCurrentExport :: QName -> CompileState -> CompileState
- Fay.Types: addModulePath :: ModulePath -> CompileState -> CompileState
- Fay.Types: addedModulePath :: ModulePath -> CompileState -> Bool
- Fay.Types: class (Parseable from, Printable to) => CompilesTo from to | from -> to
- Fay.Types: compileTo :: CompilesTo from to => from -> Compile to
- Fay.Types: configExportBuiltins :: CompileConfig -> Bool
- Fay.Types: faySourceDir :: IO FilePath
- Fay.Types: getCurrentExports :: CompileState -> Set QName
- Fay.Types: getCurrentExportsWithoutNewtypes :: CompileState -> Set QName
- Fay.Types: getExportsFor :: ModuleName -> CompileState -> Set QName
- Fay.Types: getNonLocalExports :: CompileState -> Set QName
- Fay.Types: instance IsString ModuleName
- Fay.Types: instance IsString Name
- Fay.Types: instance IsString QName
- Fay.Types: stateLocalScope :: CompileState -> Set Name
- Fay.Types: stateModuleScope :: CompileState -> ModuleScope
- Fay.Types: stateModuleScopes :: CompileState -> Map ModuleName ModuleScope
+ Fay: getConfigRuntime :: CompileConfig -> IO String
+ Fay.Compiler: compileWith :: Monoid a => FilePath -> (a -> Module -> Compile a) -> (FilePath -> String -> Compile a) -> String -> Compile (a, CompileState, CompileWriter)
+ Fay.Compiler: runCompileModule :: CompileReader -> CompileState -> Compile a -> CompileModule a
+ Fay.Compiler.Config: shouldExportStrictWrapper :: ModuleName a -> CompileConfig -> Bool
+ Fay.Exts: convertFieldDecl :: FieldDecl a -> ([Name a], BangType a)
+ Fay.Exts: declHeadName :: DeclHead a -> Name a
+ Fay.Exts: fieldDeclNames :: FieldDecl a -> [Name a]
+ Fay.Exts: mkIdent :: String -> Name SrcSpanInfo
+ Fay.Exts: moduleExports :: Module X -> Maybe (ExportSpecList X)
+ Fay.Exts: moduleName :: SrcInfo a => Module a -> ModuleName a
+ Fay.Exts: moduleNameString :: ModuleName t -> String
+ Fay.Exts: noI :: SrcSpanInfo
+ Fay.Exts: type Alt = Alt X
+ Fay.Exts: type BangType = BangType X
+ Fay.Exts: type ClassDecl = ClassDecl X
+ Fay.Exts: type Decl = Decl X
+ Fay.Exts: type DeclHead = DeclHead X
+ Fay.Exts: type Ex = Exp X
+ Fay.Exts: type Exp = Exp X
+ Fay.Exts: type ExportSpec = ExportSpec X
+ Fay.Exts: type FieldDecl = FieldDecl X
+ Fay.Exts: type FieldUpdate = FieldUpdate X
+ Fay.Exts: type GadtDecl = GadtDecl X
+ Fay.Exts: type GuardedAlts = GuardedAlts X
+ Fay.Exts: type GuardedRhs = GuardedRhs X
+ Fay.Exts: type ImportDecl = ImportDecl X
+ Fay.Exts: type ImportSpec = ImportSpec X
+ Fay.Exts: type Literal = Literal X
+ Fay.Exts: type Match = Match X
+ Fay.Exts: type Module = Module X
+ Fay.Exts: type ModuleName = ModuleName X
+ Fay.Exts: type ModulePragma = ModulePragma X
+ Fay.Exts: type Name = Name X
+ Fay.Exts: type Pat = Pat X
+ Fay.Exts: type PatField = PatField X
+ Fay.Exts: type QName = QName X
+ Fay.Exts: type QOp = QOp X
+ Fay.Exts: type QualConDecl = QualConDecl X
+ Fay.Exts: type QualStmt = QualStmt X
+ Fay.Exts: type Rhs = Rhs X
+ Fay.Exts: type SpecialCon = SpecialCon X
+ Fay.Exts: type SrcLoc = SrcLoc
+ Fay.Exts: type Stmt = Stmt X
+ Fay.Exts: type TyVarBind = TyVarBind X
+ Fay.Exts: type Type = Type X
+ Fay.Exts: type X = SrcSpanInfo
+ Fay.Exts.NoAnnotation: instance IsString (ModuleName ())
+ Fay.Exts.NoAnnotation: instance IsString (Name ())
+ Fay.Exts.NoAnnotation: instance IsString (QName ())
+ Fay.Exts.NoAnnotation: type Alt = Alt ()
+ Fay.Exts.NoAnnotation: type BangType = BangType ()
+ Fay.Exts.NoAnnotation: type ClassDecl = ClassDecl ()
+ Fay.Exts.NoAnnotation: type Decl = Decl ()
+ Fay.Exts.NoAnnotation: type DeclHead = DeclHead ()
+ Fay.Exts.NoAnnotation: type Ex = Exp ()
+ Fay.Exts.NoAnnotation: type Exp = Exp ()
+ Fay.Exts.NoAnnotation: type ExportSpec = ExportSpec ()
+ Fay.Exts.NoAnnotation: type FieldDecl = FieldDecl ()
+ Fay.Exts.NoAnnotation: type FieldUpdate = FieldUpdate ()
+ Fay.Exts.NoAnnotation: type GadtDecl = GadtDecl ()
+ Fay.Exts.NoAnnotation: type GuardedAlts = GuardedAlts ()
+ Fay.Exts.NoAnnotation: type GuardedRhs = GuardedRhs ()
+ Fay.Exts.NoAnnotation: type ImportDecl = ImportDecl ()
+ Fay.Exts.NoAnnotation: type ImportSpec = ImportSpec ()
+ Fay.Exts.NoAnnotation: type Literal = Literal ()
+ Fay.Exts.NoAnnotation: type Match = Match ()
+ Fay.Exts.NoAnnotation: type Module = Module ()
+ Fay.Exts.NoAnnotation: type ModuleName = ModuleName ()
+ Fay.Exts.NoAnnotation: type ModulePragma = ModulePragma ()
+ Fay.Exts.NoAnnotation: type Name = Name ()
+ Fay.Exts.NoAnnotation: type Pat = Pat ()
+ Fay.Exts.NoAnnotation: type PatField = PatField ()
+ Fay.Exts.NoAnnotation: type QName = QName ()
+ Fay.Exts.NoAnnotation: type QOp = QOp ()
+ Fay.Exts.NoAnnotation: type QualConDecl = QualConDecl ()
+ Fay.Exts.NoAnnotation: type QualStmt = QualStmt ()
+ Fay.Exts.NoAnnotation: type Rhs = Rhs ()
+ Fay.Exts.NoAnnotation: type SpecialCon = SpecialCon ()
+ Fay.Exts.NoAnnotation: type SrcLoc = SrcLoc
+ Fay.Exts.NoAnnotation: type SrcSpan = SrcSpan
+ Fay.Exts.NoAnnotation: type SrcSpanInfo = SrcSpanInfo
+ Fay.Exts.NoAnnotation: type Stmt = Stmt ()
+ Fay.Exts.NoAnnotation: type TyVarBind = TyVarBind ()
+ Fay.Exts.NoAnnotation: type Type = Type ()
+ Fay.Exts.NoAnnotation: unAnn :: Functor f => f a -> f ()
+ Fay.Exts.Scoped: noI :: Scoped SrcSpanInfo
+ Fay.Exts.Scoped: srcSpanInfo :: Scoped SrcSpanInfo -> SrcSpanInfo
+ Fay.Exts.Scoped: type Alt = Alt X
+ Fay.Exts.Scoped: type BangType = BangType X
+ Fay.Exts.Scoped: type ClassDecl = ClassDecl X
+ Fay.Exts.Scoped: type Decl = Decl X
+ Fay.Exts.Scoped: type DeclHead = DeclHead X
+ Fay.Exts.Scoped: type Ex = Exp X
+ Fay.Exts.Scoped: type Exp = Exp X
+ Fay.Exts.Scoped: type ExportSpec = ExportSpec X
+ Fay.Exts.Scoped: type FieldDecl = FieldDecl X
+ Fay.Exts.Scoped: type FieldUpdate = FieldUpdate X
+ Fay.Exts.Scoped: type GadtDecl = GadtDecl X
+ Fay.Exts.Scoped: type GuardedAlts = GuardedAlts X
+ Fay.Exts.Scoped: type GuardedRhs = GuardedRhs X
+ Fay.Exts.Scoped: type ImportDecl = ImportDecl X
+ Fay.Exts.Scoped: type ImportSpec = ImportSpec X
+ Fay.Exts.Scoped: type Literal = Literal X
+ Fay.Exts.Scoped: type Match = Match X
+ Fay.Exts.Scoped: type Module = Module X
+ Fay.Exts.Scoped: type ModuleName = ModuleName X
+ Fay.Exts.Scoped: type ModulePragma = ModulePragma X
+ Fay.Exts.Scoped: type Name = Name X
+ Fay.Exts.Scoped: type Pat = Pat X
+ Fay.Exts.Scoped: type PatField = PatField X
+ Fay.Exts.Scoped: type QName = QName X
+ Fay.Exts.Scoped: type QOp = QOp X
+ Fay.Exts.Scoped: type QualConDecl = QualConDecl X
+ Fay.Exts.Scoped: type QualStmt = QualStmt X
+ Fay.Exts.Scoped: type Rhs = Rhs X
+ Fay.Exts.Scoped: type SpecialCon = SpecialCon X
+ Fay.Exts.Scoped: type SrcLoc = SrcLoc
+ Fay.Exts.Scoped: type Stmt = Stmt X
+ Fay.Exts.Scoped: type TyVarBind = TyVarBind X
+ Fay.Exts.Scoped: type Type = Type X
+ Fay.Exts.Scoped: type X = Scoped SrcSpanInfo
+ Fay.Types: ShouldBeDesugared :: String -> CompileError
+ Fay.Types: configRuntimePath :: CompileConfig -> Maybe FilePath
+ Fay.Types: configStrict :: CompileConfig -> [String]
+ Fay.Types: configTypecheckOnly :: CompileConfig -> Bool
+ Fay.Types: instance MonadModule Compile
+ Fay.Types: stateInterfaces :: CompileState -> Map ModuleName Symbols
+ Fay.Types: stateTypeSigs :: CompileState -> Map QName Type
+ Fay.Types: type CompileModule a = ModuleT Symbols IO (CompileResult a)
+ Fay.Types: type CompileResult a = Either CompileError (a, CompileState, CompileWriter)
- Fay.Compiler: compileModuleFromAST :: Module -> Compile [JsStmt]
+ Fay.Compiler: compileModuleFromAST :: [JsStmt] -> Module -> Compile [JsStmt]
- Fay.Compiler: compileViaStr :: (Show from, Show to, CompilesTo from to) => FilePath -> CompileConfig -> (from -> Compile to) -> String -> IO (Either CompileError (PrintState, CompileState, CompileWriter))
+ Fay.Compiler: compileViaStr :: FilePath -> CompileConfig -> (Module -> Compile [JsStmt]) -> String -> IO (Either CompileError (PrintState, CompileState, CompileWriter))
- Fay.Types: Compile :: RWST CompileReader CompileWriter CompileState (ErrorT CompileError IO) a -> Compile a
+ Fay.Types: Compile :: (RWST CompileReader CompileWriter CompileState (ErrorT CompileError (ModuleT (ModuleInfo Compile) IO))) a -> Compile a
- Fay.Types: CompileConfig :: Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> [(Maybe String, FilePath)] -> Bool -> Bool -> [FilePath] -> Bool -> Bool -> Maybe FilePath -> Bool -> Bool -> Bool -> Maybe FilePath -> [String] -> Maybe FilePath -> CompileConfig
+ Fay.Types: CompileConfig :: Bool -> Bool -> Bool -> Bool -> Bool -> [(Maybe String, FilePath)] -> Bool -> Bool -> [FilePath] -> Bool -> Bool -> Maybe FilePath -> Bool -> Bool -> Bool -> Maybe FilePath -> [String] -> Maybe FilePath -> [String] -> Bool -> Maybe FilePath -> CompileConfig
- Fay.Types: CompileState :: Map ModuleName (Set QName) -> [(QName, [QName])] -> [(QName, [QName])] -> [(QName, Maybe QName, Type)] -> [(ModuleName, FilePath)] -> Integer -> Set Name -> ModuleScope -> Map ModuleName ModuleScope -> ModuleName -> Set ModulePath -> Bool -> CompileState
+ Fay.Types: CompileState :: Map ModuleName Symbols -> [(QName, [QName])] -> [(QName, [Name])] -> [(QName, Maybe QName, Type)] -> [(ModuleName, FilePath)] -> Integer -> ModuleName -> Set ModulePath -> Bool -> Map QName Type -> CompileState
- Fay.Types: FfiFormatBadChars :: SrcLoc -> String -> CompileError
+ Fay.Types: FfiFormatBadChars :: SrcSpanInfo -> String -> CompileError
- Fay.Types: FfiFormatIncompleteArg :: SrcLoc -> CompileError
+ Fay.Types: FfiFormatIncompleteArg :: SrcSpanInfo -> CompileError
- Fay.Types: FfiFormatInvalidJavaScript :: SrcLoc -> String -> String -> CompileError
+ Fay.Types: FfiFormatInvalidJavaScript :: SrcSpanInfo -> String -> String -> CompileError
- Fay.Types: FfiFormatNoSuchArg :: SrcLoc -> Int -> CompileError
+ Fay.Types: FfiFormatNoSuchArg :: SrcSpanInfo -> Int -> CompileError
- Fay.Types: UnsupportedModuleSyntax :: Module -> CompileError
+ Fay.Types: UnsupportedModuleSyntax :: String -> Module -> CompileError
- Fay.Types: mkModulePath :: ModuleName -> ModulePath
+ Fay.Types: mkModulePath :: ModuleName a -> ModulePath
- Fay.Types: mkModulePathFromQName :: QName -> ModulePath
+ Fay.Types: mkModulePathFromQName :: QName a -> ModulePath
- Fay.Types: mkModulePaths :: ModuleName -> [ModulePath]
+ Fay.Types: mkModulePaths :: ModuleName a -> [ModulePath]
- Fay.Types: stateRecords :: CompileState -> [(QName, [QName])]
+ Fay.Types: stateRecords :: CompileState -> [(QName, [Name])]
- Fay.Types: unCompile :: Compile a -> RWST CompileReader CompileWriter CompileState (ErrorT CompileError IO) a
+ Fay.Types: unCompile :: Compile a -> (RWST CompileReader CompileWriter CompileState (ErrorT CompileError (ModuleT (ModuleInfo Compile) IO))) a

Files

fay.cabal view
@@ -1,5 +1,5 @@ name:                fay-version:             0.17.0.0+version:             0.18.0.0 synopsis:            A compiler for Fay, a Haskell subset that compiles to JavaScript. description:         Fay is a proper subset of Haskell which is type-checked                      with GHC, and compiled to JavaScript. It is lazy, pure, has a Fay monad,@@ -44,6 +44,8 @@   -- Test cases   tests/asPatternMatch.hs   tests/asPatternMatch.res+  tests/automatic.hs+  tests/automatic.res   tests/AutomaticList.hs   tests/AutomaticList.res   tests/baseFixities.hs@@ -67,6 +69,8 @@   tests/Compile/CPPTypecheck.hs   tests/Compile/ImportRecords.hs   tests/Compile/Records.hs+  tests/Compile/StrictWrapper.hs+  tests/Compile/StrictWrapper.res   tests/CPP.hs   tests/CPP.res   tests/curry.hs@@ -136,6 +140,8 @@   tests/Floating.res   tests/fromInteger.hs   tests/fromInteger.res+  tests/fromIntegral.hs+  tests/fromIntegral.res   tests/FromString/Dep.hs   tests/FromString/Dep.res   tests/FromString/DepDep.hs@@ -253,6 +259,12 @@   tests/patternMatchFail.hs   tests/patternMatchingTuples.hs   tests/patternMatchingTuples.res+  tests/QualifiedImport/X.hs+  tests/QualifiedImport/X.res+  tests/QualifiedImport/Y.hs+  tests/QualifiedImport/Y.res+  tests/QualifiedImport.hs+  tests/QualifiedImport.res   tests/Ratio.hs   tests/Ratio.res   tests/RealFrac.hs@@ -291,6 +303,12 @@   tests/ReExport2.res   tests/ReExport3.hs   tests/ReExport3.res+  tests/ReExportGlobally/A.hs+  tests/ReExportGlobally/A.res+  tests/ReExportGlobally.hs+  tests/ReExportGlobally.res+  tests/ReExportGloballyExplicit.hs+  tests/ReExportGloballyExplicit.res   tests/reservedWords.hs   tests/reservedWords.res   tests/sections.hs@@ -352,28 +370,33 @@   exposed-modules:     Fay                      , Fay.Compiler                      , Fay.Compiler.Config-                     , Fay.Compiler.Debug                      , Fay.Control.Monad.Extra                      , Fay.Control.Monad.IO                      , Fay.Convert                      , Fay.Data.List.Extra+                     , Fay.Exts+                     , Fay.Exts.Scoped+                     , Fay.Exts.NoAnnotation                      , Fay.FFI                      , Fay.System.Directory.Extra                      , Fay.System.Process.Extra                      , Fay.Types   other-modules:       Fay.Compiler.Decl                      , Fay.Compiler.Defaults+                     , Fay.Compiler.Desugar                      , Fay.Compiler.Exp                      , Fay.Compiler.FFI                      , Fay.Compiler.GADT+                     , Fay.Compiler.Import                      , Fay.Compiler.InitialPass                      , Fay.Compiler.Misc-                     , Fay.Compiler.ModuleScope                      , Fay.Compiler.Optimizer                      , Fay.Compiler.Packages                      , Fay.Compiler.Pattern+                     , Fay.Compiler.PrimOp                      , Fay.Compiler.Print                      , Fay.Compiler.QName+                     , Fay.Compiler.State                      , Fay.Compiler.Typecheck                      , Paths_fay   ghc-options:       -O2 -Wall -fno-warn-name-shadowing@@ -389,6 +412,8 @@                    , directory                    , filepath                    , ghc-paths+                   , haskell-packages+                   , haskell-names >= 0.3 && < 0.4                    , haskell-src-exts >= 1.14                    , language-ecmascript >= 0.15 && < 1.0                    , mtl
js/runtime.js view
@@ -165,8 +165,17 @@         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$$fayToJs(["automatic"], Fay$$_(fayFunc(Fay$$jsToFay(["automatic"],arguments[i])),true));+          }+          return fayFunc;+        }+         for (var i = 0, len = len; i < len - 1 && fayFunc instanceof Function; i++) {-          // Unserialize the JS values to Fay for the Fay callback.           fayFunc = Fay$$_(fayFunc(Fay$$jsToFay(args[i],arguments[i])),true);         }         // Finally, serialize the Fay return value back to JS.@@ -240,7 +249,9 @@      fayObj = Fay$$_(fayObj); -    if(fayObj instanceof Fay$$Cons || fayObj === null){+    if(fayObj instanceof Function) {+      jsObj = Fay$$fayToJs(["function", "automatic_function"], fayObj);+    } else if(fayObj instanceof Fay$$Cons || fayObj === null){       // Serialize Fay list to JavaScript array.       var arr = [];       while(fayObj instanceof Fay$$Cons) {@@ -249,7 +260,7 @@       }       jsObj = arr;     } else {-      var fayToJsFun = Fay$$fayToJsHash[fayObj.constructor.name];+      var fayToJsFun = fayObj && fayObj.constructor && Fay$$fayToJsHash[fayObj.constructor.name];       jsObj = fayToJsFun ? fayToJsFun(type,type[2],fayObj) : fayObj;     }   }@@ -313,21 +324,31 @@     //    }}}}};     var returnType = args[args.length-1];     var funArgs = args.slice(0,-1);-    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);-        }++    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([]);+      fayObj = makePartial([]);+    }+    else {+      fayObj =+        function (arg)+        {+           return Fay$$jsToFay(["automatic"], jsObj(Fay$$fayToJs(["automatic"], arg)));+        };+    }   }   else if(base == "string") {     // Unserialize a JS string into Fay list (String).@@ -396,6 +417,12 @@         list = new Fay$$Cons(Fay$$jsToFay([base], jsObj[i]), list);       }       fayObj = list;+    }+    else if (jsObj instanceof Function) {+      var type = [["automatic"]];+      for (var i = 0; i < jsObj.length; i++)+        type.push(["automatic"]);+      return Fay$$jsToFay(["function", type], jsObj);     }     else       fayObj = jsObj;
src/Fay.hs view
@@ -15,32 +15,43 @@   ,compileFromToAndGenerateHtml   ,toJsName   ,showCompileError-  ,getRuntime)-   where+  ,getConfigRuntime+  ,getRuntime+  ) where  import           Fay.Compiler-import           Fay.Compiler.Misc   (printSrcLoc)+import           Fay.Compiler.Misc                      (ioWarn,+                                                         printSrcSpanInfo) import           Fay.Compiler.Packages+import           Fay.Compiler.Typecheck+import qualified Fay.Exts                               as F import           Fay.Types  import           Control.Applicative import           Control.Monad import           Data.List-import           Language.Haskell.Exts        (prettyPrint)-import           Language.Haskell.Exts.Syntax+import           Language.Haskell.Exts.Annotated        (prettyPrint)+import           Language.Haskell.Exts.Annotated.Syntax+import           Language.Haskell.Exts.SrcLoc import           Paths_fay import           System.FilePath  -- | Compile the given file and write the output to the given path, or -- if nothing given, stdout. compileFromTo :: CompileConfig -> FilePath -> Maybe FilePath -> IO ()-compileFromTo config filein fileout = do-  result <- maybe (compileFile config filein)-                  (compileFromToAndGenerateHtml config filein)-                  fileout-  case result of-    Right out -> maybe (putStrLn out) (`writeFile` out) fileout-    Left err -> error $ showCompileError err+compileFromTo cfg filein fileout =+  if configTypecheckOnly cfg+  then do+    cfg' <- resolvePackages cfg+    res <- typecheck cfg' filein+    either (error . showCompileError) (ioWarn cfg') res+  else do+    result <- maybe (compileFile cfg filein)+                      (compileFromToAndGenerateHtml cfg filein)+                      fileout+    case result of+      Right out -> maybe (putStrLn out) (`writeFile` out) fileout+      Left err -> error $ showCompileError err  -- | Compile the given file and write to the output, also generate any HTML. compileFromToAndGenerateHtml :: CompileConfig -> FilePath -> FilePath -> IO (Either CompileError String)@@ -74,16 +85,15 @@ -- | Compile a file returning the state. compileFileWithState :: CompileConfig -> FilePath -> IO (Either CompileError (String,CompileState)) compileFileWithState config filein = do-  runtime <- getRuntime+  runtime <- getConfigRuntime config   hscode <- readFile filein   raw <- readFile runtime   config' <- resolvePackages config   compileToModule filein config' raw (compileToplevelModule filein) hscode  -- | Compile the given module to a runnable module.-compileToModule :: (Show from,Show to,CompilesTo from to)-                => FilePath-                -> CompileConfig -> String -> (from -> Compile to) -> String+compileToModule :: FilePath+                -> CompileConfig -> String -> (F.Module -> Compile [JsStmt]) -> String                 -> IO (Either CompileError (String,CompileState)) compileToModule filepath config raw with hscode = do   result <- compileViaStr filepath config with hscode@@ -95,7 +105,7 @@             , state             )   where-    generateWrapped jscode (ModuleName modulename) =+    generateWrapped jscode (ModuleName _ modulename) =       unlines $ filter (not . null)       [if configExportRuntime config then raw else ""       ,jscode@@ -115,38 +125,47 @@ -- | Print a compile error for human consumption. showCompileError :: CompileError -> String showCompileError e = case e of-  ParseError pos err -> err ++ " at line: " ++ show (srcLine pos) ++ " column: " ++ show (srcColumn pos)-  UnsupportedDeclaration d -> "unsupported declaration: " ++ prettyPrint d-  UnsupportedExportSpec es -> "unsupported export specification: " ++ prettyPrint es-  UnsupportedExpression expr -> "unsupported expression syntax: " ++ prettyPrint expr-  UnsupportedFieldPattern p -> "unsupported field pattern: " ++ prettyPrint p-  UnsupportedImport i -> "unsupported import syntax, we're too lazy: " ++ prettyPrint i-  UnsupportedLet -> "let not supported here"-  UnsupportedLetBinding d -> "unsupported let binding: " ++ prettyPrint d-  UnsupportedLiteral lit -> "unsupported literal syntax: " ++ prettyPrint lit-  UnsupportedModuleSyntax m -> "unsupported module syntax" ++ prettyPrint m-  UnsupportedPattern pat -> "unsupported pattern syntax: " ++ prettyPrint pat-  UnsupportedQualStmt stmt -> "unsupported list qualifier: " ++ prettyPrint stmt-  UnsupportedRecursiveDo -> "recursive `do' isn't supported"-  UnsupportedRhs rhs -> "unsupported right-hand side syntax: " ++ prettyPrint rhs-  UnsupportedWhereInAlt alt -> "`where' not supported here: " ++ prettyPrint alt-  UnsupportedWhereInMatch m -> "unsupported `where' syntax: " ++ prettyPrint m-  EmptyDoBlock -> "empty `do' block"-  InvalidDoBlock -> "invalid `do' block"-  FfiNeedsTypeSig d -> "your FFI declaration needs a type signature: " ++ prettyPrint d-  FfiFormatBadChars      srcloc cs -> printSrcLoc srcloc ++ ": invalid characters for FFI format string: " ++ show cs-  FfiFormatNoSuchArg     srcloc i  -> printSrcLoc srcloc ++ ": no such argument in FFI format string: " ++ show i-  FfiFormatIncompleteArg srcloc    -> printSrcLoc srcloc ++ ": incomplete `%' syntax in FFI format string"-  FfiFormatInvalidJavaScript srcloc code err ->-    printSrcLoc srcloc ++ ":" ++-    "\ninvalid JavaScript code in FFI format string:\n"-                                         ++ err ++ "\nin " ++ code-  Couldn'tFindImport i places ->+  Couldn'tFindImport i places      ->     "could not find an import in the path: " ++ prettyPrint i ++ ", \n" ++     "searched in these places: " ++ intercalate ", " places-  UnableResolveQualified qname -> "unable to resolve qualified names " ++ prettyPrint qname-  GHCError s -> "ghc: " ++ s+  EmptyDoBlock -> "empty `do' block"+  FfiFormatBadChars srcloc cs      -> printSrcSpanInfo srcloc ++ ": invalid characters for FFI format string: " ++ show cs+  FfiFormatIncompleteArg srcloc    -> printSrcSpanInfo srcloc ++ ": incomplete `%' syntax in FFI format string"+  FfiFormatInvalidJavaScript l c m ->+    printSrcSpanInfo l ++ ":" +++    "\ninvalid JavaScript code in FFI format string:\n" ++ m ++ "\nin " ++ c+  FfiFormatNoSuchArg srcloc i      ->+    printSrcSpanInfo srcloc ++ ":" +++    "\nno such argument in FFI format string: " ++ show i+  FfiNeedsTypeSig d                -> "your FFI declaration needs a type signature: " ++ prettyPrint d+  GHCError s                       -> "ghc: " ++ s+  InvalidDoBlock                   -> "invalid `do' block"+  ParseError pos err               ->+    err ++ " at line: " ++ show (srcLine pos) ++ " column:" +++    "\n" ++ show (srcColumn pos)+  ShouldBeDesugared s              -> "Expected this to be desugared (this is a bug): " ++ s+  UnableResolveQualified qname     -> "unable to resolve qualified names " ++ prettyPrint qname+  UnsupportedDeclaration d         -> "unsupported declaration: " ++ prettyPrint d+  UnsupportedExportSpec es         -> "unsupported export specification: " ++ prettyPrint es+  UnsupportedExpression expr       -> "unsupported expression syntax: " ++ prettyPrint expr+  UnsupportedFieldPattern p        -> "unsupported field pattern: " ++ prettyPrint p+  UnsupportedImport i              -> "unsupported import syntax, we're too lazy: " ++ prettyPrint i+  UnsupportedLet                   -> "let not supported here"+  UnsupportedLetBinding d          -> "unsupported let binding: " ++ prettyPrint d+  UnsupportedLiteral lit           -> "unsupported literal syntax: " ++ prettyPrint lit+  UnsupportedModuleSyntax s m      -> "unsupported module syntax in " ++ s ++ ": " ++ prettyPrint m+  UnsupportedPattern pat           -> "unsupported pattern syntax: " ++ prettyPrint pat+  UnsupportedQualStmt stmt         -> "unsupported list qualifier: " ++ prettyPrint stmt+  UnsupportedRecursiveDo           -> "recursive `do' isn't supported"+  UnsupportedRhs rhs               -> "unsupported right-hand side syntax: " ++ prettyPrint rhs+  UnsupportedWhereInAlt alt        -> "`where' not supported here: " ++ prettyPrint alt+  UnsupportedWhereInMatch m        -> "unsupported `where' syntax: " ++ prettyPrint m  -- | Get the JS runtime source.+-- This will return the user supplied runtime if it exists.+getConfigRuntime :: CompileConfig -> IO String+getConfigRuntime cfg = maybe getRuntime return $ configRuntimePath cfg++-- | Get the default JS runtime source. getRuntime :: IO String getRuntime = getDataFileName "js/runtime.js"
src/Fay/Compiler.hs view
@@ -1,23 +1,20 @@-{-# OPTIONS -Wall -fno-warn-name-shadowing -fno-warn-orphans #-} {-# LANGUAGE FlexibleInstances     #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings     #-} {-# LANGUAGE RecordWildCards       #-}+{-# LANGUAGE ScopedTypeVariables   #-} {-# LANGUAGE ViewPatterns          #-}  -- | The Haskell→Javascript compiler.  module Fay.Compiler-  (runCompile+  (runCompileModule   ,compileViaStr-  ,compileToAst+  ,compileWith   ,compileExp   ,compileDecl   ,compileToplevelModule-  ,compileModuleFromFile   ,compileModuleFromContents-  ,compileModuleFromName-  ,compileModule   ,compileModuleFromAST   ,parseFay)   where@@ -25,119 +22,83 @@ import           Fay.Compiler.Config import           Fay.Compiler.Decl import           Fay.Compiler.Defaults+import           Fay.Compiler.Desugar import           Fay.Compiler.Exp import           Fay.Compiler.FFI-import           Fay.Compiler.InitialPass (initialPass)+import           Fay.Compiler.Import+import           Fay.Compiler.InitialPass        (initialPass) import           Fay.Compiler.Misc-import           Fay.Compiler.ModuleScope (findPrimOp) import           Fay.Compiler.Optimizer+import           Fay.Compiler.PrimOp             (findPrimOp) import           Fay.Compiler.QName+import           Fay.Compiler.State import           Fay.Compiler.Typecheck import           Fay.Control.Monad.IO+import qualified Fay.Exts                        as F+import           Fay.Exts.NoAnnotation           (unAnn)+import qualified Fay.Exts.NoAnnotation           as N import           Fay.Types  import           Control.Applicative import           Control.Monad.Error-import           Control.Monad.State import           Control.Monad.RWS+import           Control.Monad.State import           Data.Default                    (def)-import qualified Data.Map                        as M import           Data.Maybe import qualified Data.Set                        as S-import           Language.Haskell.Exts+import           Language.Haskell.Exts.Annotated hiding (name)+import           Language.Haskell.Names+import           Prelude                         hiding (mod)  -------------------------------------------------------------------------------- -- Top level entry points  -- | Compile a Haskell source string to a JavaScript source string.-compileViaStr :: (Show from,Show to,CompilesTo from to)-              => FilePath-              -> CompileConfig-              -> (from -> Compile to)-              -> String-              -> IO (Either CompileError (PrintState,CompileState,CompileWriter))-compileViaStr filepath config with from = do-  cs <- defaultCompileState-  rs <- defaultCompileReader config-  runCompile rs-             cs+compileViaStr+  :: FilePath+  -> CompileConfig+  -> (F.Module -> Compile [JsStmt])+  -> String+  -> IO (Either CompileError (PrintState,CompileState,CompileWriter))+compileViaStr filepath cfg with from = do+  rs <- defaultCompileReader cfg+  runTopCompile rs+             defaultCompileState              (parseResult (throwError . uncurry ParseError)                           (fmap (\x -> execState (runPrinter (printJS x)) printConfig) . with)                           (parseFay filepath from)) -  where printConfig = def { psPretty = configPrettyPrint config }---- | Compile a Haskell source string to a JavaScript source string.-compileToAst :: (Show from,Show to,CompilesTo from to)-              => FilePath-              -> CompileReader-              -> CompileState-              -> (from -> Compile to)-              -> String-              -> IO (Either CompileError (to,CompileState,CompileWriter))-compileToAst filepath reader state with from =-  runCompile reader-             state-             (parseResult (throwError . uncurry ParseError)-                          with-                          (parseFay filepath from))+  where printConfig = def { psPretty = configPrettyPrint cfg }  -- | Compile the top-level Fay module.-compileToplevelModule :: FilePath -> Module -> Compile [JsStmt]-compileToplevelModule filein mod@(Module _ (ModuleName modulename) _ _ _ _ _)  = do+compileToplevelModule :: FilePath -> F.Module -> Compile [JsStmt]+compileToplevelModule filein mod@Module{}  = do   cfg <- config id-  when (configTypecheck cfg) $-    typecheck (configPackageConf cfg) (configWall cfg) $-      fromMaybe modulename $ configFilePath cfg-  initialPass mod-  cs <- io defaultCompileState-  modify $ \s -> s { stateImported = stateImported cs }-  fmap fst . listen $ compileModuleFromFile filein+  when (configTypecheck cfg) $ do+    res <- io $ typecheck cfg $+             fromMaybe (F.moduleNameString (F.moduleName mod)) $+               configFilePath cfg+    either throwError warn res+  initialPass filein+  -- Reset imports after initialPass so the modules can be imported during code generation.+  startCompile compileFileWithSource filein+compileToplevelModule _ m = throwError $ UnsupportedModuleSyntax "compileToplevelModule" m  -------------------------------------------------------------------------------- -- Compilers --- | Read a file and compile.-compileModuleFromFile :: FilePath -> Compile [JsStmt]-compileModuleFromFile fp = io (readFile fp) >>= compileModule fp- -- | Compile a source string. compileModuleFromContents :: String -> Compile [JsStmt]-compileModuleFromContents = compileModule "<interactive>"---- | Lookup a module from include directories and compile.-compileModuleFromName :: ModuleName -> Compile [JsStmt]-compileModuleFromName name =-  unlessImported name compileModule-    where-      unlessImported :: ModuleName-                     -> (FilePath -> String -> Compile [JsStmt])-                     -> Compile [JsStmt]-      unlessImported "Fay.Types" _ = return []-      unlessImported name importIt = do-        imported <- gets stateImported-        case lookup name imported of-          Just _  -> return []-          Nothing -> do-            dirs <- configDirectoryIncludePaths <$> config id-            (filepath,contents) <- findImport dirs name-            modify $ \s -> s { stateImported = (name,filepath) : imported }-            importIt filepath contents+compileModuleFromContents = compileFileWithSource "<interactive>"  -- | Compile given the location and source string.-compileModule :: FilePath -> String -> Compile [JsStmt]-compileModule filepath contents = do-  state <- get-  reader <- ask-  result <- io $ compileToAst filepath reader state compileModuleFromAST contents-  case result of-    Right (stmts,state,writer) -> do-      modify $ \s -> s { stateImported      = stateImported state-                       , stateLocalScope    = S.empty-                       , stateJsModulePaths = stateJsModulePaths state-                       }-      maybeOptimize $ stmts ++ writerCons writer ++ makeTranscoding writer-    Left err -> throwError err+compileFileWithSource :: FilePath -> String -> Compile [JsStmt]+compileFileWithSource filepath contents = do+  (stmts,st,wr) <- compileWith filepath compileModuleFromAST compileFileWithSource contents+  modify $ \s -> s { stateImported      = stateImported      st+                   , stateJsModulePaths = stateJsModulePaths st+                   }+  maybeOptimize $ stmts ++ writerCons wr ++ makeTranscoding wr   where     makeTranscoding :: CompileWriter -> [JsStmt]     makeTranscoding CompileWriter{..} =@@ -152,52 +113,62 @@         else stmts  -- | Compile a parse HSE module.-compileModuleFromAST :: Module -> Compile [JsStmt]-compileModuleFromAST (Module _ modulename pragmas Nothing _exports imports decls) =-  withModuleScope $ do-    imported <- fmap concat (mapM compileImport imports)-    modify $ \s -> s { stateModuleName = modulename-                     , stateModuleScope = fromMaybe (error $ "Could not find stateModuleScope for " ++ show modulename) $ M.lookup modulename $ stateModuleScopes s-                     , stateUseFromString = hasLanguagePragmas ["OverloadedStrings", "RebindableSyntax"] pragmas-                     }-    current <- compileDecls True decls+compileModuleFromAST :: [JsStmt] -> F.Module -> Compile [JsStmt]+compileModuleFromAST imported mod''@(Module _ _ pragmas _ _) = do+  res <- io $ desugar mod''+  case res of+    Left err -> throwError err+    Right mod' -> do+      mod@(Module _ _ _ _ decls) <- annotateModule Haskell2010 [] $ mod'+      let modName = unAnn $ F.moduleName mod+      modify $ \s -> s { stateUseFromString = hasLanguagePragmas ["OverloadedStrings", "RebindableSyntax"] pragmas+                       }+      current <- compileDecls True decls -    exportStdlib     <- config configExportStdlib-    exportStdlibOnly <- config configExportStdlibOnly-    modulePaths      <- createModulePath modulename-    extExports       <- generateExports-    let stmts = imported ++ modulePaths ++ current ++ extExports-    return $ if exportStdlibOnly-      then if anStdlibModule modulename-              then stmts-              else []-      else if not exportStdlib && anStdlibModule modulename-              then []-              else stmts-compileModuleFromAST mod = throwError (UnsupportedModuleSyntax mod)+      exportStdlib     <- config configExportStdlib+      exportStdlibOnly <- config configExportStdlibOnly+      modulePaths      <- createModulePath modName+      extExports       <- generateExports+      strictExports    <- generateStrictExports+      let stmts = imported ++ modulePaths ++ current ++ extExports ++ strictExports+      return $ if exportStdlibOnly+        then if anStdlibModule modName+                then stmts+                else []+        else if not exportStdlib && anStdlibModule modName+                then []+                else stmts+compileModuleFromAST _ mod = throwError $ UnsupportedModuleSyntax "compileModuleFromAST" mod -hasLanguagePragmas :: [String] -> [ModulePragma] -> Bool++--------------------------------------------------------------------------------+-- Misc compilation++-- | Check if the given language pragmas are all present.+hasLanguagePragmas :: [String] -> [F.ModulePragma] -> Bool hasLanguagePragmas pragmas modulePragmas = (== length pragmas) . length . filter (`elem` pragmas) $ flattenPragmas modulePragmas   where-    flattenPragmas :: [ModulePragma] -> [String]+    flattenPragmas :: [F.ModulePragma] -> [String]     flattenPragmas ps = concat $ map pragmaName ps     pragmaName (LanguagePragma _ q) = map unname q     pragmaName _ = [] --instance CompilesTo Module [JsStmt] where compileTo = compileModuleFromAST-- -- | For a module A.B, generate -- | var A = {}; -- | A.B = {};-createModulePath :: ModuleName -> Compile [JsStmt]-createModulePath =-  liftM concat . mapM modPath . mkModulePaths+createModulePath :: ModuleName a -> Compile [JsStmt]+createModulePath (unAnn -> m) = do+  cfg <- config id+  reg <- liftM concat . mapM modPath . mkModulePaths $ m+  strict <-+    if shouldExportStrictWrapper m cfg+      then liftM concat . mapM modPath . mkModulePaths $ (\(ModuleName i n) -> ModuleName i ("Strict." ++ n)) m+       else return []+  return $ reg ++ strict   where     modPath :: ModulePath -> Compile [JsStmt]     modPath mp = whenImportNotGenerated mp $ \(unModulePath -> l) -> case l of-     [n] -> [JsVar (JsNameVar . UnQual $ Ident n) (JsObj [])]+     [n] -> [JsVar (JsNameVar . UnQual () $ Ident () n) (JsObj [])]      _   -> [JsSetModule mp (JsObj [])]      whenImportNotGenerated :: ModulePath -> (ModulePath -> [JsStmt]) -> Compile [JsStmt]@@ -212,22 +183,38 @@ -- | Generate exports for non local names, local exports have already been added to the module. generateExports :: Compile [JsStmt] generateExports = do-  m <- gets stateModuleName-  map (exportExp m) . S.toList . getNonLocalExports <$> gets id+  modName <- gets stateModuleName+  maybe [] (map (exportExp modName) . S.toList) <$> gets (getNonLocalExportsWithoutNewtypes modName)   where-    exportExp :: ModuleName -> QName -> JsStmt+    exportExp :: N.ModuleName -> N.QName -> JsStmt     exportExp m v = JsSetQName (changeModule m v) $ case findPrimOp v of-      Just p  -> JsName $ JsNameVar p+      Just p  -> JsName $ JsNameVar p -- TODO add test case for this case, is it needed at all?       Nothing -> JsName $ JsNameVar v +-- | Generate strict wrappers for the exports of the module.+generateStrictExports :: Compile [JsStmt]+generateStrictExports = do+  cfg <- config id+  modName <- gets stateModuleName+  if shouldExportStrictWrapper modName cfg+    then do+      locals <- gets (getLocalExportsWithoutNewtypes modName)+      nonLocals <- gets (getNonLocalExportsWithoutNewtypes modName)+      let int = maybe [] (map exportExp' . S.toList) locals+      let ext = maybe [] (map (exportExp modName)  . S.toList) nonLocals+      return $ int ++ ext+    else return []+  where+    exportExp :: N.ModuleName -> N.QName -> JsStmt+    exportExp m v = JsSetQName (changeModule' ("Strict." ++) $ changeModule m v) $ JsName $ JsNameVar $ changeModule' ("Strict." ++) v++    exportExp' :: N.QName -> JsStmt+    exportExp' name = JsSetQName (changeModule' ("Strict." ++) name) $ serialize (JsName (JsNameVar name))++    serialize :: JsExp -> JsExp+    serialize n = JsApp (JsRawExp "Fay$$fayToJs") [JsRawExp "['automatic']", n]+ -- | Is the module a standard module, i.e., one that we'd rather not -- output code for if we're compiling separate files.-anStdlibModule :: ModuleName -> Bool-anStdlibModule (ModuleName name) = name `elem` ["Prelude","FFI","Fay.FFI","Data.Data"]---- | Compile the given import.-compileImport :: ImportDecl -> Compile [JsStmt]--- Package imports are ignored since they are used for some trickery in fay-base.-compileImport (ImportDecl _ _    _     _ Just{}  _       _) = return []-compileImport (ImportDecl _ name False _ Nothing Nothing _) = compileModuleFromName name-compileImport i = throwError $ UnsupportedImport i+anStdlibModule :: ModuleName a -> Bool+anStdlibModule (ModuleName _ name) = name `elem` ["Prelude","FFI","Fay.FFI","Data.Data"]
src/Fay/Compiler/Config.hs view
@@ -4,10 +4,12 @@  module Fay.Compiler.Config where -import Data.Default-import Data.Maybe-import Fay.Types+import           Fay.Types +import           Data.Default+import           Data.Maybe+import           Language.Haskell.Exts.Annotated (ModuleName (..))+ -- | Get all include directories without the package mapping. configDirectoryIncludePaths :: CompileConfig -> [FilePath] configDirectoryIncludePaths = map snd . configDirectoryIncludes@@ -36,13 +38,15 @@ addConfigPackages :: [String] -> CompileConfig -> CompileConfig addConfigPackages fps cfg = foldl (flip addConfigPackage) cfg fps +shouldExportStrictWrapper :: ModuleName a -> CompileConfig -> Bool+shouldExportStrictWrapper (ModuleName _ m) cs = m `elem` configStrict cs+ -- | Default configuration. instance Default CompileConfig where   def = addConfigPackage "fay-base"     CompileConfig     { configOptimize           = False     , configFlattenApps        = False-    , configExportBuiltins     = True     , configExportRuntime      = True     , configExportStdlib       = True     , configExportStdlibOnly   = False@@ -59,4 +63,7 @@     , configPackageConf        = Nothing     , configPackages           = []     , configBasePath           = Nothing+    , configStrict             = []+    , configTypecheckOnly      = False+    , configRuntimePath        = Nothing     }
− src/Fay/Compiler/Debug.hs
@@ -1,60 +0,0 @@-{-# LANGUAGE RecordWildCards #-}---- | Some useful debug functions.--module Fay.Compiler.Debug where--import Fay.Compiler.Defaults-import Fay.Compiler-import Fay.Compiler.Misc (parseResult)-import Fay.Types--import Control.Monad.Error-import Data.Default---- | Compile a String of Fay and print it as beautified JavaScript.-printTestCompile :: String -> IO ()-printTestCompile = printCompile def { configWarn = False } compileModuleFromAST---- | Compile a Haskell source string to a JavaScript source string.-compileTestAst :: (Show from,Show to,CompilesTo from to)-             => CompileConfig-             -> (from -> Compile to)-             -> String-             -> IO ()-compileTestAst cfg with from = do-  state <- defaultCompileState-  reader <- defaultCompileReader cfg-  out <- runCompile reader-             state-             (parseResult (throwError . uncurry ParseError)-                          with-                          (parseFay "<interactive>" from))-  case out of-    Left err -> error $ show err-    Right (ok,_,_) -> print ok---- | Print a useful debug output of a compilation.-debug :: (Show from,Show to,CompilesTo from to) => (from -> Compile to) -> String -> IO ()-debug compile string = do-  putStrLn "AST:\n"-  compileTestAst c compile string-  putStrLn ""-  putStrLn "JS (unoptimized):\n"-  printCompile def { configTypecheck = False } compile string-  putStrLn "JS (optimized):\n"-  printCompile c compile string--  where c = def { configOptimize = True, configTypecheck = False }---- | Compile the given input and print the output out prettily.-printCompile :: (Show from,Show to,CompilesTo from to)-              => CompileConfig-              -> (from -> Compile to)-              -> String-              -> IO ()-printCompile config with from = do-  result <- compileViaStr "<interactive>" config { configPrettyPrint = True } with from-  case result of-    Left err -> print err-    Right (PrintState{..},_,_) -> putStrLn . concat . reverse $ psOutput
src/Fay/Compiler/Decl.hs view
@@ -1,131 +1,129 @@-{-# OPTIONS -fno-warn-orphans -fno-warn-name-shadowing #-}+{-# OPTIONS -fno-warn-name-shadowing #-}+{-# LANGUAGE FlexibleInstances     #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings     #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE ViewPatterns #-}+{-# LANGUAGE ViewPatterns          #-}  -- | Compile declarations.  module Fay.Compiler.Decl where -import Fay.Compiler.Exp-import Fay.Compiler.FFI-import Fay.Compiler.GADT-import Fay.Compiler.Misc-import Fay.Compiler.Pattern-import Fay.Data.List.Extra-import Fay.Types+import           Fay.Compiler.Exp+import           Fay.Compiler.FFI+import           Fay.Compiler.GADT+import           Fay.Compiler.Misc+import           Fay.Compiler.Pattern+import           Fay.Compiler.State+import           Fay.Data.List.Extra+import           Fay.Exts                        (convertFieldDecl,+                                                  fieldDeclNames)+import           Fay.Exts.NoAnnotation           (unAnn)+import qualified Fay.Exts.Scoped                 as S+import           Fay.Types -import Control.Applicative-import Control.Monad.Error-import Control.Monad.RWS-import Language.Haskell.Exts+import           Control.Applicative+import           Control.Monad.Error+import           Control.Monad.RWS+import           Language.Haskell.Exts.Annotated  -- | Compile Haskell declaration.-compileDecls :: Bool -> [Decl] -> Compile [JsStmt]-compileDecls toplevel decls =-  case decls of-    [] -> return []-    (TypeSig _ _ sig:bind@PatBind{}:decls) -> appendM (scoped (compilePatBind toplevel (Just sig) bind))-                                                      (compileDecls toplevel decls)-    (decl:decls) -> appendM (scoped (compileDecl toplevel decl))-                            (compileDecls toplevel decls)+compileDecls :: Bool -> [S.Decl] -> Compile [JsStmt]+compileDecls toplevel decls = case decls of+  [] -> return []+  (TypeSig _ _ sig:bind@PatBind{}:decls) -> appendM (compilePatBind toplevel (Just sig) bind)+                                                    (compileDecls toplevel decls)+  (decl:decls) -> appendM (compileDecl toplevel decl)+                          (compileDecls toplevel decls) -  where appendM m n = do x <- m-                         xs <- n-                         return (x ++ xs)-        scoped = if toplevel then withScope else id+  where+    appendM m n = do x <- m+                     xs <- n+                     return (x ++ xs)  -- | Compile a declaration.-compileDecl :: Bool -> Decl -> Compile [JsStmt]-compileDecl toplevel decl =-  case decl of-    pat@PatBind{} -> compilePatBind toplevel Nothing pat-    FunBind matches -> compileFunCase toplevel matches-    DataDecl _ DataType _ _ tyvars constructors _ -> compileDataDecl toplevel tyvars constructors-    GDataDecl _ DataType _l _i tyvars _n decls _ -> compileDataDecl toplevel tyvars (map convertGADT decls)-    DataDecl _ NewType  _ _ _ _ _ -> return []-    -- Just ignore type aliases and signatures.-    TypeDecl {} -> return []-    TypeSig  {} -> return []-    InfixDecl{} -> return []-    ClassDecl{} -> return []-    InstDecl {} -> return [] -- FIXME: Ignore.-    DerivDecl{} -> return []-    _ -> throwError (UnsupportedDeclaration decl)+compileDecl :: Bool -> S.Decl -> Compile [JsStmt]+compileDecl toplevel decl = case decl of+  pat@PatBind{} -> compilePatBind toplevel Nothing pat+  FunBind _ matches -> compileFunCase toplevel matches+  DataDecl _ (DataType _ ) _ head' constructors _ -> compileDataDecl toplevel (mkTyVars head') constructors+  GDataDecl _ (DataType _) _l (mkTyVars -> tyvars) _n decls _ -> compileDataDecl toplevel tyvars (map convertGADT decls)+  DataDecl _ (NewType _)  _ _ _ _ -> return []+  -- Just ignore type aliases and signatures.+  TypeDecl {} -> return []+  TypeSig  {} -> return []+  InfixDecl{} -> return []+  ClassDecl{} -> return []+  InstDecl {} -> return [] -- FIXME: Ignore.+  DerivDecl{} -> return []+  _ -> throwError (UnsupportedDeclaration decl) --- | Convenient instance.-instance CompilesTo Decl [JsStmt] where compileTo = compileDecl True +mkTyVars :: S.DeclHead -> [S.TyVarBind]+mkTyVars (DHead _ _ binds) = binds+mkTyVars (DHInfix _ t1 _ t2) = [t1, t2]+mkTyVars (DHParen _ dh) = mkTyVars dh+ -- | Compile a top-level pattern bind.-compilePatBind :: Bool -> Maybe Type -> Decl -> Compile [JsStmt]-compilePatBind toplevel sig pat =-  case pat of-    PatBind srcloc (PVar ident) Nothing (UnGuardedRhs rhs) (BDecls []) ->-      case ffiExp rhs of-        Just formatstr -> case sig of-          Just sig -> compileFFI srcloc ident formatstr sig-          Nothing  -> throwError (FfiNeedsTypeSig pat)-        _ -> compileUnguardedRhs srcloc toplevel ident rhs-    PatBind srcloc (PVar ident) Nothing (UnGuardedRhs rhs) bdecls ->-      compileUnguardedRhs srcloc toplevel ident (Let bdecls rhs)-    PatBind srcloc pat Nothing (UnGuardedRhs rhs) _bdecls -> do-      exp <- compileExp rhs-      name <- withScopedTmpJsName return-      [JsIf t b1 []] <- compilePat (JsName name) pat []-      let err = [throw (prettyPrint srcloc ++ "Irrefutable pattern failed for pattern: " ++ prettyPrint pat) (JsList [])]-      return [JsVar name exp, JsIf t b1 err]-    _ -> throwError (UnsupportedDeclaration pat)+compilePatBind :: Bool -> Maybe S.Type -> S.Decl -> Compile [JsStmt]+compilePatBind toplevel sig pat = case pat of+  PatBind _ (PVar _ ident) Nothing (UnGuardedRhs _ rhs) Nothing ->+    case ffiExp rhs of+      Just formatstr -> case sig of+        Just sig -> compileFFI ident formatstr sig+        Nothing  -> throwError (FfiNeedsTypeSig pat)+      _ -> compileUnguardedRhs toplevel ident rhs+  PatBind _ (PVar _ ident) Nothing (UnGuardedRhs _ rhs) (Just bdecls) ->+    compileUnguardedRhs toplevel ident (Let S.noI bdecls rhs)+  PatBind _ pat Nothing (UnGuardedRhs _ rhs) _bdecls -> do+    exp <- compileExp rhs+    name <- withScopedTmpJsName return+    [JsIf t b1 []] <- compilePat (JsName name) pat []+    let err = [throw ("Irrefutable pattern failed for pattern: " ++ prettyPrint pat) (JsList [])]+    return [JsVar name exp, JsIf t b1 err]+  _ -> throwError (UnsupportedDeclaration pat)  -- | Compile a normal simple pattern binding.-compileUnguardedRhs :: SrcLoc -> Bool -> Name -> Exp -> Compile [JsStmt]-compileUnguardedRhs _srcloc toplevel ident rhs = do-  unless toplevel $ bindVar ident-  withScope $ do-    body <- compileExp rhs-    bind <- bindToplevel toplevel ident (thunk body)-    return [bind]+compileUnguardedRhs :: Bool -> S.Name -> S.Exp -> Compile [JsStmt]+compileUnguardedRhs toplevel ident rhs = do+  body <- compileExp rhs+  bind <- bindToplevel toplevel ident (thunk body)+  return [bind]  -- | Compile a data declaration (or a GADT, latter is converted to former).-compileDataDecl :: Bool -> [TyVarBind] -> [QualConDecl] -> Compile [JsStmt]+compileDataDecl :: Bool -> [S.TyVarBind] -> [S.QualConDecl] -> Compile [JsStmt] compileDataDecl toplevel tyvars constructors =   fmap concat $-    forM constructors $ \(QualConDecl srcloc _ _ condecl) ->+    forM constructors $ \(QualConDecl _ _ _ condecl) ->       case condecl of-        ConDecl name types  -> do-          let fields =  map (Ident . ("slot"++) . show . fst) . zip [1 :: Integer ..] $ types-              fields' = zip (map return fields) types-          cons <- makeConstructor name fields-          func <- makeFunc name fields-          emitFayToJs name tyvars fields'-          emitJsToFay name tyvars fields'-          emitCons cons-          return [func]-        InfixConDecl t1 name t2 -> do-          let slots = ["slot1","slot2"]+        ConDecl _ name types -> do+          let slots =  map (Ident () . ("slot"++) . show . fst) $ zip [1 :: Int ..] types+              fields = zip (map return slots) types+          cons <- makeConstructor name slots+          func <- makeFunc name slots+          emitFayToJs name tyvars fields+          emitJsToFay name tyvars fields+          return [cons, func]+        InfixConDecl _ t1 name t2 -> do+          let slots = [Ident () "slot1",Ident () "slot2"]               fields = zip (map return slots) [t1, t2]           cons <- makeConstructor name slots           func <- makeFunc name slots           emitFayToJs name tyvars fields           emitJsToFay name tyvars fields-          emitCons cons-          return [func]-        RecDecl name fields' -> do-          let fields = concatMap fst fields'+          return [cons, func]+        RecDecl _ name fields' -> do+          let fields = concatMap fieldDeclNames fields'           cons <- makeConstructor name fields           func <- makeFunc name fields-          funs <- makeAccessors srcloc fields-          emitFayToJs name tyvars fields'-          emitJsToFay name tyvars fields'-          emitCons cons-          return (func : funs)+          funs <- makeAccessors fields+          emitFayToJs name tyvars (map convertFieldDecl fields')+          emitJsToFay name tyvars (map convertFieldDecl fields')+          return (cons : func : funs)    where-    emitCons cons = tell (mempty { writerCons = [cons] })-     -- Creates a constructor _RecConstr for a Record-    makeConstructor :: Name -> [Name] -> Compile JsStmt-    makeConstructor name (map (JsNameVar . UnQual) -> fields) = do+    makeConstructor :: Name a -> [Name b] -> Compile JsStmt+    makeConstructor (unAnn -> name) (map (JsNameVar . UnQual () . unAnn) -> fields) = do       qname <- qualify name       return $         JsSetConstructor qname $@@ -135,8 +133,8 @@                 Nothing      -- Creates a function to initialize the record by regular application-    makeFunc :: Name -> [Name] -> Compile JsStmt-    makeFunc name (map (JsNameVar . UnQual) -> fields) = do+    makeFunc :: Name a -> [Name b] -> Compile JsStmt+    makeFunc (unAnn -> name) (map (JsNameVar . UnQual () . unAnn) -> fields) = do       let fieldExps = map JsName fields       qname <- qualify name       let mp = mkModulePathFromQName qname@@ -152,69 +150,72 @@           return $ JsSetQName qname func      -- Creates getters for a RecDecl's values-    makeAccessors :: SrcLoc -> [Name] -> Compile [JsStmt]-    makeAccessors _srcloc fields =-      forM fields $ \name ->+    makeAccessors :: [S.Name] -> Compile [JsStmt]+    makeAccessors fields =+      forM fields $ \(unAnn -> name) ->            bindToplevel toplevel                         name                         (JsFun Nothing                                [JsNameVar "x"]                                []                                (Just (thunk (JsGetProp (force (JsName (JsNameVar "x")))-                                                       (JsNameVar (UnQual name))))))+                                                       (JsNameVar (UnQual () name))))))   -- | Compile a function which pattern matches (causing a case analysis).-compileFunCase :: Bool -> [Match] -> Compile [JsStmt]+compileFunCase :: Bool -> [S.Match] -> Compile [JsStmt] compileFunCase _toplevel [] = return []-compileFunCase toplevel matches@(Match _ name argslen _ _ _:_) = do+compileFunCase toplevel (InfixMatch l pat name pats rhs binds : rest) =+  compileFunCase toplevel (Match l name (pat:pats) rhs binds : rest)+compileFunCase toplevel matches@(Match _ name argslen _ _:_) = do   pats <- fmap optimizePatConditions (mapM compileCase matches)-  bindVar name   bind <- bindToplevel toplevel                        name                        (foldr (\arg inner -> JsFun Nothing [arg] [] (Just inner))                               (stmtsThunk (concat pats ++ basecase))                               args)   return [bind]-  where args = zipWith const uniqueNames argslen+  where+    args = zipWith const uniqueNames argslen -        isWildCardMatch (Match _ _ pats _ _ _) = all isWildCardPat pats+    isWildCardMatch (Match _ _ pats          _ _) = all isWildCardPat pats+    isWildCardMatch (InfixMatch _ pat _ pats _ _) = all isWildCardPat (pat:pats) -        compileCase :: Match -> Compile [JsStmt]-        compileCase match@(Match _ _ pats _ rhs _) =-          withScope $ do-            whereDecls' <- whereDecls match-            generateScope $ zipWithM (\arg pat -> compilePat (JsName arg) pat []) args pats-            generateScope $ mapM compileLetDecl whereDecls'-            rhsform <- compileRhs rhs-            body <- if null whereDecls'-                      then return [either id JsEarlyReturn rhsform]-                      else do-                          binds <- mapM compileLetDecl whereDecls'-                          case rhsform of-                            Right exp ->-                              return [JsEarlyReturn $ JsApp (JsFun Nothing [] (concat binds) (Just exp)) []]-                            Left stmt ->-                              withScopedTmpJsName $ \n -> return-                                [ JsVar n (JsApp (JsFun Nothing [] (concat binds ++ [stmt]) Nothing) [])-                                , JsIf (JsNeq JsUndefined (JsName n)) [JsEarlyReturn (JsName n)] []-                                ]-            foldM (\inner (arg,pat) ->-                    compilePat (JsName arg) pat inner)-                  body-                  (zip args pats)+    compileCase :: S.Match -> Compile [JsStmt]+    compileCase (InfixMatch l pat name pats rhs binds) =+      compileCase $ Match l name (pat:pats) rhs binds+    compileCase match@(Match _ _ pats rhs _) = do+      whereDecls' <- whereDecls match+      rhsform <- compileRhs rhs+      body <- if null whereDecls'+                then return [either id JsEarlyReturn rhsform]+                else do+                    binds <- mapM compileLetDecl whereDecls'+                    case rhsform of+                      Right exp ->+                        return [JsEarlyReturn $ JsApp (JsFun Nothing [] (concat binds) (Just exp)) []]+                      Left stmt ->+                        withScopedTmpJsName $ \n -> return+                          [ JsVar n (JsApp (JsFun Nothing [] (concat binds ++ [stmt]) Nothing) [])+                          , JsIf (JsNeq JsUndefined (JsName n)) [JsEarlyReturn (JsName n)] []+                          ]+      foldM (\inner (arg,pat) ->+              compilePat (JsName arg) pat inner)+            body+            (zip args pats) -        whereDecls :: Match -> Compile [Decl]-        whereDecls (Match _ _ _ _ _ (BDecls decls)) = return decls-        whereDecls match = throwError (UnsupportedWhereInMatch match)+    whereDecls :: S.Match -> Compile [S.Decl]+    whereDecls (Match _ _ _ _ (Just (BDecls _ decls))) = return decls+    whereDecls (Match _ _ _ _ Nothing) = return []+    whereDecls match = throwError (UnsupportedWhereInMatch match) -        basecase :: [JsStmt]-        basecase = if any isWildCardMatch matches-                      then []-                      else [throw ("unhandled case in " ++ prettyPrint name)-                                  (JsList (map JsName args))]+    basecase :: [JsStmt]+    basecase = if any isWildCardMatch matches+                  then []+                  else [throw ("unhandled case in " ++ prettyPrint name)+                              (JsList (map JsName args))]  -- | Compile a right-hand-side expression.-compileRhs :: Rhs -> Compile (Either JsStmt JsExp)-compileRhs (UnGuardedRhs exp) = Right <$> compileExp exp-compileRhs (GuardedRhss rhss) = Left <$> compileGuards rhss+compileRhs :: S.Rhs -> Compile (Either JsStmt JsExp)+compileRhs (UnGuardedRhs _ exp) = Right <$> compileExp exp+compileRhs (GuardedRhss _ rhss) = Left <$> compileGuards rhss
src/Fay/Compiler/Defaults.hs view
@@ -4,17 +4,19 @@  module Fay.Compiler.Defaults where -import Fay.Compiler.Config-import Fay.Compiler.Decl (compileDecls)-import Fay.Compiler.Exp (compileLit)-import Fay.Types+import           Fay.Compiler.Config+import           Fay.Compiler.Decl   (compileDecls)+import           Fay.Compiler.Exp    (compileLit)+import           Fay.Types+import           Paths_fay -import Data.Default-import Data.Map as M-import Data.Set as S-import Language.Haskell.Exts.Syntax-import Paths_fay+import           Data.Map            as M+import           Data.Set            as S +-- | The data-files source directory.+faySourceDir :: IO FilePath+faySourceDir = getDataFileName "src/"+ -- | The default compiler reader value. defaultCompileReader :: CompileConfig -> IO CompileReader defaultCompileReader config = do@@ -26,20 +28,16 @@     }  -- | The default compiler state.-defaultCompileState :: IO CompileState-defaultCompileState = do-  types <- getDataFileName "src/Language/Fay/Types.hs"-  return CompileState-    {  _stateExports = M.empty-    , stateModuleName = ModuleName "Main"-    , stateRecordTypes = []-    , stateRecords = []-    , stateNewtypes = []-    , stateImported = [("Fay.Types",types)]-    , stateNameDepth = 1-    , stateLocalScope = S.empty-    , stateModuleScope = def-    , stateModuleScopes = M.empty-    , stateJsModulePaths = S.empty-    , stateUseFromString = False-    }+defaultCompileState :: CompileState+defaultCompileState = CompileState+  { stateInterfaces    = M.empty+  , stateModuleName    = "Main"+  , stateRecordTypes   = []+  , stateRecords       = []+  , stateNewtypes      = []+  , stateImported      = []+  , stateNameDepth     = 1+  , stateJsModulePaths = S.empty+  , stateUseFromString = False+  , stateTypeSigs      = M.empty+  }
+ src/Fay/Compiler/Desugar.hs view
@@ -0,0 +1,269 @@+{-# LANGUAGE DeriveFunctor              #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE TypeFamilies               #-}++module Fay.Compiler.Desugar+  (desugar+  ) where++import           Fay.Types                       (CompileError (..))++import           Control.Applicative+import           Control.Monad.Error+import           Control.Monad.Reader+import           Data.Maybe+import           Language.Haskell.Exts.Annotated hiding (binds, loc)+import           Prelude                         hiding (exp)+++-- Types++data DesugarReader = DesugarReader { readerNameDepth :: Int }++newtype Desugar a = Desugar+  { unDesugar :: (ReaderT DesugarReader+                       (ErrorT CompileError IO))+                       a+  } deriving ( MonadReader DesugarReader+             , MonadError CompileError+             , MonadIO+             , Monad+             , Functor+             , Applicative+             )++runDesugar :: Desugar a -> IO (Either CompileError a)+runDesugar m = runErrorT (runReaderT (unDesugar m) (DesugarReader 0))++-- | Generate a temporary, SCOPED name for testing conditions and+-- such. We don't have name tracking yet, so instead we use this.+withScopedTmpName :: l -> (Name l -> Desugar a) -> Desugar a+withScopedTmpName l f = do+  n <- asks readerNameDepth+  local (\r -> DesugarReader $ readerNameDepth r + 1) $+   f $ Ident l $ "$gen" ++ show n+++-- | Top level, desugar a whole module possibly returning errors+desugar :: Module l -> IO (Either CompileError (Module l))+desugar md = runDesugar (desugarModule md)++-- | Desugaring++desugarModule :: Module l -> Desugar (Module l)+desugarModule m = case m of+  Module l h ps is decls -> Module l h ps is <$> mapM desugarDecl decls+  _ -> return $ m++desugarDecl :: Decl l -> Desugar (Decl l)+desugarDecl d = case d of+  FunBind l ms -> FunBind l <$> mapM desugarMatch ms+  PatBind l p mt rhs mbs -> PatBind l <$> desugarPat p <*> return mt <*> desugarRhs rhs <*> mmap desugarBinds mbs++  _ -> return d++mmap :: (Applicative f) => (t -> f a) -> Maybe t -> f (Maybe a)+mmap f mbs' = case mbs' of Just b -> return <$> f b; Nothing -> pure Nothing++desugarBinds :: Binds l -> Desugar (Binds l)+desugarBinds bs = case bs of+  BDecls l ds -> BDecls l <$> mapM desugarDecl ds+  _ -> return bs++desugarMatch :: Match l -> Desugar (Match l)+desugarMatch m = case m of+  Match l n ps rhs mb -> Match l (desugarName n) <$> mapM desugarPat ps <*> desugarRhs rhs <*> mmap desugarBinds mb+  InfixMatch l p n ps r mb -> InfixMatch l <$> desugarPat p <*> return (desugarName n) <*> mapM desugarPat ps <*> desugarRhs r <*> mmap desugarBinds mb++desugarRhs :: Rhs l -> Desugar (Rhs l)+desugarRhs r = case r of+  UnGuardedRhs l e -> UnGuardedRhs l <$> desugarExp e+  GuardedRhss l gs -> GuardedRhss l <$> mapM desugarGuardedRhs gs++desugarGuardedRhs :: GuardedRhs l -> Desugar (GuardedRhs l)+desugarGuardedRhs g = case g of+  GuardedRhs l stmts exp -> GuardedRhs l <$> mapM desugarStmt stmts <*> desugarExp exp++desugarExp :: Exp l -> Desugar (Exp l)+desugarExp ex = case ex of+  -- (a `f`) => (\b -> f a b)+  LeftSection l e q -> desugarExp =<<+    (withScopedTmpName l $ \v ->+      return $ Lambda l [PVar l v] (InfixApp l e q (Var l (UnQual l v))))+  -- (`f` b) => (\a -> f a b)+  RightSection l q e -> desugarExp =<<+    (withScopedTmpName l $ \tmp ->+      return (Lambda l [PVar l tmp] (InfixApp l (Var l (UnQual l tmp)) q e)))++  -- Check for TupleCon+  Var _ q -> return $ desugarVar ex q+  Con _ q -> return $ desugarVar ex q++  IPVar{} -> return ex+  Lit{} -> return ex+  InfixApp l e1 qop e2 -> InfixApp l <$> desugarExp e1 <*> return (desugarQOp qop) <*> desugarExp e2+  App l e1 e2 -> App l <$> desugarExp e1 <*> desugarExp e2+  NegApp l e -> NegApp l <$> desugarExp e+  Lambda l ps e -> Lambda l <$> mapM desugarPat ps <*> desugarExp e+  Let l b e -> Let l <$> desugarBinds b <*> desugarExp e+  If l e1 e2 e3 -> If l <$> desugarExp e1 <*> desugarExp e2 <*> desugarExp e3+  Case l e as -> Case l <$> desugarExp e <*> mapM desugarAlt as+  Do _ stmts -> maybe (throwError EmptyDoBlock) return =<< (mmap desugarExp $ foldl desugarStmt' Nothing (reverse stmts))+  MDo l ss -> MDo l <$> mapM desugarStmt ss+  Tuple l b es -> Tuple l b <$> mapM desugarExp es+  TupleSection l b mes -> TupleSection l b <$> mapM (mmap desugarExp) mes+  List l es -> List l <$> mapM desugarExp es+  Paren l e -> Paren l <$> desugarExp e+  RecConstr l q f -> RecConstr l (desugarQName q) <$> mapM desugarFieldUpdate f+  RecUpdate l e f -> RecUpdate l <$> desugarExp e <*> mapM desugarFieldUpdate f+  EnumFrom l e -> EnumFrom l <$> desugarExp e+  EnumFromTo l e1 e2 -> EnumFromTo l <$> desugarExp e1 <*> desugarExp e2+  EnumFromThen l e1 e2 -> EnumFromThen l <$> desugarExp e1 <*> desugarExp e2+  EnumFromThenTo l e1 e2 e3 -> EnumFromThenTo l <$> desugarExp e1 <*> desugarExp e2 <*> desugarExp e3+  ListComp l e qs -> ListComp l <$> desugarExp e <*> mapM desugarQualStmt qs+  ParComp l e qqs -> ParComp l <$> desugarExp e <*> mapM (mapM desugarQualStmt) qqs+  ExpTypeSig l e t -> ExpTypeSig l <$> desugarExp e <*> return (desugarType t)+  VarQuote l q -> return $ VarQuote l (desugarQName q)+  TypQuote l q -> return $ TypQuote l (desugarQName q)+  BracketExp l b -> return $ BracketExp l (desugarBracket b)+  SpliceExp l s -> return $ SpliceExp l (desugarSplice s)+  QuasiQuote{} -> return ex+  XTag{} -> return ex+  XETag{} -> return ex+  XPcdata{} -> return ex+  XExpTag{} -> return ex+  XChildTag{} -> return ex+  GenPragma{} -> return ex+  Proc l p e -> Proc l <$> desugarPat p <*> desugarExp e+  LeftArrApp{} -> return ex+  RightArrApp{} -> return ex+  LeftArrHighApp{} -> return ex+  RightArrHighApp{} -> return ex+  CorePragma{} -> return ex+  SCCPragma{} -> return ex++-- | Convert do notation into binds and thens.+desugarStmt' :: Maybe (Exp l) -> (Stmt l) -> Maybe (Exp l)+desugarStmt' inner stmt =+  maybe initStmt subsequentStmt inner+  where+    initStmt = case stmt of+      Qualifier _ exp -> Just exp+      LetStmt{}     -> error "UnsupportedLet"+      _             -> error "InvalidDoBlock"++    subsequentStmt inner' = case stmt of+      Generator loc pat exp -> desugarGenerator loc pat inner' exp+      Qualifier s exp -> Just $ InfixApp s exp+                                         (QVarOp s $ UnQual s $ Symbol s ">>")+                                         inner'+      LetStmt _ (BDecls s binds) -> Just $ Let s (BDecls s binds) inner'+      LetStmt _ _ -> error "UnsupportedLet"+      RecStmt{} -> error "UnsupportedRecursiveDo"++    desugarGenerator :: l -> Pat l -> Exp l -> Exp l -> Maybe (Exp l)+    desugarGenerator s pat inner' exp =+      Just $ InfixApp s+                      exp+                      (QVarOp s $ UnQual s $ Symbol s ">>=")+                      (Lambda s [pat] (inner'))+++desugarPat :: Pat l -> Desugar (Pat l)+desugarPat pt = case pt of+  -- (p) => p+  PParen _ p -> desugarPat p++  PVar l n -> return $ PVar l (desugarName n)+  PLit {} -> return pt+  PNeg l p -> PNeg l <$> desugarPat p+  PNPlusK{} -> return pt+  PInfixApp l p1 q p2 -> PInfixApp l <$> desugarPat p1 <*> return (desugarQName q) <*> desugarPat p2+  PApp l q ps -> PApp l (desugarQName q) <$> mapM desugarPat ps+  PTuple l b ps -> PTuple l b <$> mapM desugarPat ps+  PList l ps -> PList l <$> mapM desugarPat ps+  PRec l q pfs -> PRec l (desugarQName q) <$> mapM desugarPatField pfs+  PAsPat l n p -> PAsPat l (desugarName n) <$> desugarPat p+  PWildCard{} -> return pt+  PIrrPat l p -> PIrrPat l <$> desugarPat p+  PatTypeSig l p t -> PatTypeSig l <$> desugarPat p <*> return (desugarType t)+  PViewPat l e p -> PViewPat l <$> desugarExp e <*> desugarPat p+  PBangPat l p -> PBangPat l <$> desugarPat p+  _ -> return pt++desugarPatField :: PatField l -> Desugar (PatField l)+desugarPatField pf = case pf of+  -- {a} => {a=a} for R{a}+  PFieldPun l n -> let dn = desugarName n in desugarPatField $ PFieldPat l (UnQual l dn) (PVar l dn)++  PFieldPat l q p -> PFieldPat l (desugarQName q) <$> desugarPat p+  PFieldWildcard l -> return $ PFieldWildcard l++desugarGuardedAlts :: GuardedAlts l -> Desugar (GuardedAlts l)+desugarGuardedAlts g = case g of+  UnGuardedAlt l e -> UnGuardedAlt l <$> desugarExp e+  GuardedAlts l gas -> GuardedAlts l <$> mapM desugarGuardedAlt gas++desugarQOp :: QOp l -> QOp l+desugarQOp = id++desugarType :: Type l -> Type l+desugarType = id++desugarQName :: QName l -> QName l+desugarQName = id++desugarQualStmt :: QualStmt l -> Desugar (QualStmt l)+desugarQualStmt q = case q of+  QualStmt l s -> QualStmt l <$> desugarStmt s+  ThenTrans l e -> ThenTrans l <$> desugarExp e+  ThenBy l e1 e2 -> ThenBy l <$> desugarExp e1 <*> desugarExp e2+  GroupBy l e -> GroupBy l <$> desugarExp e+  GroupUsing l e -> GroupUsing l <$> desugarExp e+  GroupByUsing l e1 e2 -> GroupByUsing l <$> desugarExp e1 <*> desugarExp e2++desugarAlt :: Alt l -> Desugar (Alt l)+desugarAlt (Alt l p ga mb) = Alt l <$> desugarPat p <*> desugarGuardedAlts ga <*> mmap desugarBinds mb++desugarFieldUpdate :: FieldUpdate l -> Desugar (FieldUpdate l)+desugarFieldUpdate f = case f of+  FieldUpdate l q e -> FieldUpdate l (desugarQName q) <$> desugarExp e+  FieldPun l n -> let dn = UnQual l (desugarName n)+                  in desugarFieldUpdate $ FieldUpdate l dn (Var l dn)+  FieldWildcard{} -> return f++desugarBracket :: Bracket l -> Bracket l+desugarBracket = id++desugarSplice :: Splice l -> Splice l+desugarSplice = id++desugarGuardedAlt :: GuardedAlt l -> Desugar (GuardedAlt l)+desugarGuardedAlt (GuardedAlt l ss e) = GuardedAlt l <$> mapM desugarStmt ss <*> desugarExp e++desugarStmt :: Stmt l -> Desugar (Stmt l)+desugarStmt s = case s of+  Generator l p e -> Generator l <$> desugarPat p <*> desugarExp e+  Qualifier l e -> Qualifier l <$> desugarExp e+  LetStmt l b -> LetStmt l <$> desugarBinds b+  RecStmt l ss -> RecStmt l <$> mapM desugarStmt ss++desugarName :: Name a -> Name a+desugarName = id++desugarVar :: Exp l -> QName l -> Exp l+desugarVar e q = case q of+  Special _ t@TupleCon{} -> fromMaybe e $ desugarTupleCon t+  _ -> e++-- | (,) => \x y -> (x,y)+desugarTupleCon :: SpecialCon l -> Maybe (Exp l)+desugarTupleCon s = case s of+  TupleCon l b n -> Just $ Lambda l params body+    where+      -- It doesn't matter if these variable names shadow anything since+      -- this lambda won't have inner scopes.+      names  = take n $ map (Ident l . ("$gen" ++) . show) [(1::Int)..]+      params = PVar l <$> names+      body   = Tuple l b (Var l . UnQual l <$> names)+  _ -> Nothing
src/Fay/Compiler/Exp.hs view
@@ -1,107 +1,104 @@-{-# OPTIONS -fno-warn-name-shadowing -fno-warn-orphans #-}-{-# LANGUAGE OverloadedStrings #-}+{-# OPTIONS -fno-warn-name-shadowing #-}+{-# LANGUAGE FlexibleInstances     #-} {-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings     #-}+{-# LANGUAGE TypeSynonymInstances  #-}+{-# LANGUAGE ViewPatterns          #-}  -- | Compile expressions. -module Fay.Compiler.Exp where+module Fay.Compiler.Exp+  (compileExp+  ,compileGuards+  ,compileLetDecl+  ,compileLit+  ) where -import Fay.Compiler.Misc-import Fay.Compiler.Pattern-import Fay.Compiler.Print-import Fay.Compiler.FFI             (compileFFIExp)-import Fay.Types+import           Fay.Compiler.FFI                (compileFFIExp)+import           Fay.Compiler.Misc+import           Fay.Compiler.Pattern+import           Fay.Compiler.Print+import           Fay.Compiler.QName+import           Fay.Data.List.Extra+import           Fay.Exts.NoAnnotation           (unAnn)+import           Fay.Exts.Scoped                 (noI)+import qualified Fay.Exts.Scoped                 as S+import           Fay.Types -import Control.Applicative-import Control.Monad.Error-import Control.Monad.RWS-import Data.Maybe-import Language.Haskell.Exts+import           Control.Applicative+import           Control.Monad.Error+import           Control.Monad.RWS+import           Language.Haskell.Exts.Annotated+import           Language.Haskell.Names  -- | Compile Haskell expression.-compileExp :: Exp -> Compile JsExp-compileExp exp =-  case exp of-    Paren exp                     -> compileExp exp-    Var qname                     -> compileVar qname-    Lit lit                       -> compileLit lit-    App exp1 exp2                 -> compileApp exp1 exp2-    NegApp exp                    -> compileNegApp exp-    InfixApp exp1 op exp2         -> compileInfixApp exp1 op exp2-    Let (BDecls decls) exp        -> compileLet decls exp-    List []                       -> return JsNull-    List xs                       -> compileList xs-    Tuple _boxed xs               -> compileList xs-    If cond conseq alt            -> compileIf cond conseq alt-    Case exp alts                 -> compileCase exp alts-    Con (UnQual (Ident "True"))   -> return (JsLit (JsBool True))-    Con (UnQual (Ident "False"))  -> return (JsLit (JsBool False))-    Con qname                     -> compileVar qname-    Do stmts                      -> compileDoBlock stmts-    Lambda _ pats exp             -> compileLambda pats exp-    LeftSection e o               -> compileExp =<< desugarLeftSection e o-    RightSection o e              -> compileExp =<< desugarRightSection o e-    EnumFrom i                    -> compileEnumFrom i-    EnumFromTo i i'               -> compileEnumFromTo i i'-    EnumFromThen a b              -> compileEnumFromThen a b-    EnumFromThenTo a b z          -> compileEnumFromThenTo a b z-    RecConstr name fieldUpdates   -> compileRecConstr name fieldUpdates-    RecUpdate rec  fieldUpdates   -> compileRecUpdate rec fieldUpdates-    ListComp exp stmts            -> compileExp =<< desugarListComp exp stmts-    ExpTypeSig srcloc exp sig     ->-      case ffiExp exp of-        Nothing -> compileExp exp-        Just formatstr -> compileFFIExp srcloc Nothing formatstr sig--    exp -> throwError (UnsupportedExpression exp)---- | Compiling instance.-instance CompilesTo Exp JsExp where compileTo = compileExp+compileExp :: S.Exp -> Compile JsExp+compileExp exp = case exp of+  Paren _ exp                        -> compileExp exp+  Var _ qname                        -> compileVar qname+  Lit _ lit                          -> compileLit lit+  App _ exp1 exp2                    -> compileApp exp1 exp2+  NegApp _ exp                       -> compileNegApp exp+  InfixApp _ exp1 op exp2            -> compileInfixApp exp1 op exp2+  Let _ (BDecls _ decls) exp         -> compileLet decls exp+  List _ []                          -> return JsNull+  List _ xs                          -> compileList xs+  Tuple _ _boxed xs                  -> compileList xs+  If _ cond conseq alt               -> compileIf cond conseq alt+  Case _ exp alts                    -> compileCase exp alts+  Con _ (UnQual _ (Ident _ "True"))  -> return (JsLit (JsBool True))+  Con _ (UnQual _ (Ident _ "False")) -> return (JsLit (JsBool False))+  Con _ qname                        -> compileVar qname+  Lambda _ pats exp                  -> compileLambda pats exp+  EnumFrom _ i                       -> compileEnumFrom i+  EnumFromTo _ i i'                  -> compileEnumFromTo i i'+  EnumFromThen _ a b                 -> compileEnumFromThen a b+  EnumFromThenTo _ a b z             -> compileEnumFromThenTo a b z+  RecConstr _ name fieldUpdates      -> compileRecConstr name fieldUpdates+  RecUpdate _ rec  fieldUpdates      -> compileRecUpdate rec fieldUpdates+  ListComp _ exp stmts               -> compileExp =<< desugarListComp exp stmts+  Do {}                              -> shouldBeDesugared exp+  LeftSection {}                     -> shouldBeDesugared exp+  RightSection {}                    -> shouldBeDesugared exp+  ExpTypeSig _ exp sig               ->+    case ffiExp exp of+      Nothing -> compileExp exp+      Just formatstr -> compileFFIExp (S.srcSpanInfo $ ann exp) Nothing formatstr sig --- | Turn a tuple constructor into a normal lambda expression.-tupleConToFunction :: Boxed -> Int -> Exp-tupleConToFunction b n = Lambda noLoc params body-  where names  = take n (Ident . pure <$> ['a'..])-        params = PVar <$> names-        body   = Tuple b (Var . UnQual <$> names)-        noLoc  = error "no source location for SpecialCon"+  exp -> throwError (UnsupportedExpression exp)  -- | Compile variable.-compileVar :: QName -> Compile JsExp-compileVar qname = do-  case qname of-    Special (TupleCon b n) -> compileExp (tupleConToFunction b n)-    _ -> do-      qname <- unsafeResolveName qname-      return (JsName (JsNameVar qname))+compileVar :: S.QName -> Compile JsExp+compileVar qname = case qname of+  Special _ t@TupleCon{} -> shouldBeDesugared t+  _ -> do+    qname <- unsafeResolveName qname+    return (JsName (JsNameVar qname))  -- | Compile Haskell literal.-compileLit :: Literal -> Compile JsExp-compileLit lit =-  case lit of-    Char ch       -> return (JsLit (JsChar ch))-    Int integer   -> return (JsLit (JsInt (fromIntegral integer))) -- FIXME:-    Frac rational -> return (JsLit (JsFloating (fromRational rational)))-    -- TODO: Use real JS strings instead of array, probably it will-    -- lead to the same result.-    String string -> do-      fromString <- gets stateUseFromString-      if fromString-        then return (JsLit (JsStr string))-        else return (JsApp (JsName (JsBuiltIn "list")) [JsLit (JsStr string)])-    lit           -> throwError (UnsupportedLiteral lit)+compileLit :: S.Literal -> Compile JsExp+compileLit lit = case lit of+  Char _ ch _      -> return (JsLit (JsChar ch))+  Int _ integer _   -> return (JsLit (JsInt (fromIntegral integer))) -- FIXME:+  Frac _ rational _ -> return (JsLit (JsFloating (fromRational rational)))+  String _ string _ -> do+    fromString <- gets stateUseFromString+    if fromString+      then return (JsLit (JsStr string))+      else return (JsApp (JsName (JsBuiltIn "list")) [JsLit (JsStr string)])+  lit           -> throwError (UnsupportedLiteral lit)  -- | Compile simple application.-compileApp :: Exp -> Exp -> Compile JsExp-compileApp exp1@(Con q) exp2 =+compileApp :: S.Exp -> S.Exp -> Compile JsExp+compileApp exp1@(Con _ q) exp2 =   maybe (compileApp' exp1 exp2) (const $ compileExp exp2) =<< lookupNewtypeConst q-compileApp exp1@(Var q) exp2 =+compileApp exp1@(Var _ q) exp2 =   maybe (compileApp' exp1 exp2) (const $ compileExp exp2) =<< lookupNewtypeDest q compileApp exp1 exp2 =   compileApp' exp1 exp2  -- | Helper for compileApp.-compileApp' :: Exp -> Exp -> Compile JsExp+compileApp' :: S.Exp -> S.Exp -> Compile JsExp compileApp' exp1 exp2 = do   flattenApps <- config configFlattenApps   jsexp1 <- compileExp exp1@@ -111,7 +108,7 @@     -- In this approach code ends up looking like this:     -- a(a(a(a(a(a(a(a(a(a(L)(c))(b))(0))(0))(y))(t))(a(a(F)(3*a(a(d)+a(a(f)/20))))*a(a(f)/2)))(140+a(f)))(y))(t)})     -- Which might be OK for speed, but increases the JS stack a fair bit.-    method1 :: JsExp -> Exp -> Compile JsExp+    method1 :: JsExp -> S.Exp -> Compile JsExp     method1 exp1 exp2 =       JsApp <$> (forceFlatName <$> return exp1)             <*> fmap return (compileExp exp2)@@ -122,7 +119,7 @@     -- In this approach code ends up looking like this:     -- d(O,a,b,0,0,B,w,e(d(I,3*e(e(c)+e(e(g)/20))))*e(e(g)/2),140+e(g),B,w)}),d(K,g,e(c)+0.05))     -- Which should be much better for the stack and readability, but probably not great for speed.-    method2 :: JsExp -> Exp -> Compile JsExp+    method2 :: JsExp -> S.Exp -> Compile JsExp     method2 exp1 exp2 = fmap flatten $       JsApp <$> return exp1             <*> fmap return (compileExp exp2)@@ -134,28 +131,25 @@         flatten x = x  -- | Compile a negate application-compileNegApp :: Exp -> Compile JsExp+compileNegApp :: S.Exp -> Compile JsExp compileNegApp e = JsNegApp . force <$> compileExp e  -- | Compile an infix application, optimizing the JS cases.-compileInfixApp :: Exp -> QOp -> Exp -> Compile JsExp-compileInfixApp exp1 ap exp2 = compileExp (App (App (Var op) exp1) exp2)-+compileInfixApp :: S.Exp -> S.QOp -> S.Exp -> Compile JsExp+compileInfixApp exp1 ap exp2 = compileExp (App noI (App noI (Var noI op) exp1) exp2)   where op = getOp ap-        getOp (QVarOp op) = op-        getOp (QConOp op) = op+        getOp (QVarOp _ op) = op+        getOp (QConOp _ op) = op  -- | Compile a let expression.-compileLet :: [Decl] -> Exp -> Compile JsExp-compileLet decls exp =-  withScope $ do-    generateScope $ mapM compileLetDecl decls-    binds <- mapM compileLetDecl decls-    body <- compileExp exp-    return (JsApp (JsFun Nothing [] [] (Just $ stmtsThunk $ concat binds ++ [JsEarlyReturn body])) [])+compileLet :: [S.Decl] -> S.Exp -> Compile JsExp+compileLet decls exp = do+  binds <- mapM compileLetDecl decls+  body <- compileExp exp+  return (JsApp (JsFun Nothing [] [] (Just $ stmtsThunk $ concat binds ++ [JsEarlyReturn body])) [])  -- | Compile let declaration.-compileLetDecl :: Decl -> Compile [JsStmt]+compileLetDecl :: S.Decl -> Compile [JsStmt] compileLetDecl decl = do   compileDecls <- asks readerCompileDecls   case decl of@@ -165,20 +159,20 @@     _              -> throwError (UnsupportedLetBinding decl)  -- | Compile a list expression.-compileList :: [Exp] -> Compile JsExp+compileList :: [S.Exp] -> Compile JsExp compileList xs = do   exps <- mapM compileExp xs   return (makeList exps)  -- | Compile an if.-compileIf :: Exp -> Exp -> Exp -> Compile JsExp+compileIf :: S.Exp -> S.Exp -> S.Exp -> Compile JsExp compileIf cond conseq alt =   JsTernaryIf <$> fmap force (compileExp cond)               <*> compileExp conseq               <*> compileExp alt  -- | Compile case expressions.-compileCase :: Exp -> [Alt] -> Compile JsExp+compileCase :: S.Exp -> [S.Alt] -> Compile JsExp compileCase exp alts = do   exp <- compileExp exp   withScopedTmpJsName $ \tmpName -> do@@ -193,29 +187,28 @@             [exp]  -- | Compile the given pattern against the given expression.-compilePatAlt :: JsExp -> Alt -> Compile [JsStmt]+compilePatAlt :: JsExp -> S.Alt -> Compile [JsStmt] compilePatAlt exp alt@(Alt _ pat rhs wheres) = case wheres of-  BDecls (_ : _) -> throwError (UnsupportedWhereInAlt alt)-  IPBinds (_ : _) -> throwError (UnsupportedWhereInAlt alt)-  _ -> withScope $ do-    generateScope $ compilePat exp pat []+  Just (BDecls _ (_ : _)) -> throwError (UnsupportedWhereInAlt alt)+  Just (IPBinds _ (_ : _)) -> throwError (UnsupportedWhereInAlt alt)+  _ -> do     alt <- compileGuardedAlt rhs     compilePat exp pat [alt]  -- | Compile a guarded alt.-compileGuardedAlt :: GuardedAlts -> Compile JsStmt+compileGuardedAlt :: S.GuardedAlts -> Compile JsStmt compileGuardedAlt alt =   case alt of-    UnGuardedAlt exp -> JsEarlyReturn <$> compileExp exp-    GuardedAlts alts -> compileGuards (map altToRhs alts)+    UnGuardedAlt _ exp -> JsEarlyReturn <$> compileExp exp+    GuardedAlts _ alts -> compileGuards (map altToRhs alts)    where     altToRhs (GuardedAlt l s e) = GuardedRhs l s e  -- | Compile guards-compileGuards :: [GuardedRhs] -> Compile JsStmt-compileGuards ((GuardedRhs _ (Qualifier (Var (UnQual (Ident "otherwise"))):_) exp):_) =+compileGuards :: [S.GuardedRhs] -> Compile JsStmt+compileGuards ((GuardedRhs _ (Qualifier _ (Var _ (UnQual _ (Ident _ "otherwise"))):_) exp):_) =   (\e -> JsIf (JsLit $ JsBool True) [JsEarlyReturn e] []) <$> compileExp exp-compileGuards (GuardedRhs _ (Qualifier guard:_) exp : rest) =+compileGuards (GuardedRhs _ (Qualifier _ guard:_) exp : rest) =   makeIf <$> fmap force (compileExp guard)          <*> compileExp exp          <*> if null rest then return [] else do@@ -223,24 +216,16 @@            return [gs']     where makeIf gs e gss = JsIf gs [JsEarlyReturn e] gss -compileGuards rhss = throwError . UnsupportedRhs . GuardedRhss $ rhss---- | Compile a do block.-compileDoBlock :: [Stmt] -> Compile JsExp-compileDoBlock stmts = do-  doblock <- foldM compileStmt Nothing (reverse stmts)-  maybe (throwError EmptyDoBlock) compileExp doblock+compileGuards rhss = throwError . UnsupportedRhs . GuardedRhss noI $ rhss  -- | Compile a lambda.-compileLambda :: [Pat] -> Exp -> Compile JsExp-compileLambda pats exp =-  withScope $ do-    generateScope $ generateStatements JsNull-    exp   <- compileExp exp-    stmts <- generateStatements exp-    case stmts of-      [JsEarlyReturn fun@JsFun{}] -> return fun-      _ -> error "Unexpected statements in compileLambda"+compileLambda :: [S.Pat] -> S.Exp -> Compile JsExp+compileLambda pats exp = do+  exp   <- compileExp exp+  stmts <- generateStatements exp+  case stmts of+    [JsEarlyReturn fun@JsFun{}] -> return fun+    _ -> error "Unexpected statements in compileLambda"    where unhandledcase = throw "unhandled case" . JsName         allfree = all isWildCardPat pats@@ -251,151 +236,106 @@                 [JsEarlyReturn exp]                 (reverse (zip uniqueNames pats)) --- | Desugar left sections to lambdas.-desugarLeftSection :: Exp -> QOp -> Compile Exp-desugarLeftSection e o = withScopedTmpName $ \tmp ->-    return (Lambda undefined [PVar tmp] (InfixApp e o (Var (UnQual tmp))))---- | Desugar left sections to lambdas.-desugarRightSection :: QOp -> Exp -> Compile Exp-desugarRightSection o e = withScopedTmpName $ \tmp ->-    return (Lambda undefined [PVar tmp] (InfixApp (Var (UnQual tmp)) o e))- -- | Compile [e1..] arithmetic sequences.-compileEnumFrom :: Exp -> Compile JsExp+compileEnumFrom :: S.Exp -> Compile JsExp compileEnumFrom i = do   e <- compileExp i-  name <- unsafeResolveName "enumFrom"-  return (JsApp (JsName (JsNameVar name)) [e])+  return (JsApp (JsName (JsNameVar (Qual () "Prelude" "enumFrom"))) [e])  -- | Compile [e1..e3] arithmetic sequences.-compileEnumFromTo :: Exp -> Exp -> Compile JsExp+compileEnumFromTo :: S.Exp -> S.Exp -> Compile JsExp compileEnumFromTo i i' = do   f <- compileExp i   t <- compileExp i'-  name <- unsafeResolveName "enumFromTo"   cfg <- config id   return $ case optEnumFromTo cfg f t of     Just s -> s-    _ -> JsApp (JsApp (JsName (JsNameVar name)) [f]) [t]+    _ -> JsApp (JsApp (JsName (JsNameVar (Qual () "Prelude" "enumFromTo"))) [f]) [t]  -- | Compile [e1,e2..] arithmetic sequences.-compileEnumFromThen :: Exp -> Exp -> Compile JsExp+compileEnumFromThen :: S.Exp -> S.Exp -> Compile JsExp compileEnumFromThen a b = do   fr <- compileExp a   th <- compileExp b-  name <- unsafeResolveName "enumFromThen"-  return (JsApp (JsApp (JsName (JsNameVar name)) [fr]) [th])+  return (JsApp (JsApp (JsName (JsNameVar (Qual () "Prelude" "enumFromThen"))) [fr]) [th])  -- | Compile [e1,e2..e3] arithmetic sequences.-compileEnumFromThenTo :: Exp -> Exp -> Exp -> Compile JsExp+compileEnumFromThenTo :: S.Exp -> S.Exp -> S.Exp -> Compile JsExp compileEnumFromThenTo a b z = do   fr <- compileExp a   th <- compileExp b   to <- compileExp z-  name <- unsafeResolveName "enumFromThenTo"   cfg <- config id   return $ case optEnumFromThenTo cfg fr th to of     Just s -> s-    _ -> JsApp (JsApp (JsApp (JsName (JsNameVar name)) [fr]) [th]) [to]+    _ -> JsApp (JsApp (JsApp (JsName (JsNameVar (Qual () "Prelude" "enumFromThenTo"))) [fr]) [th]) [to]  -- | Compile a record construction with named fields -- | GHC will warn on uninitialized fields, they will be undefined in JS.-compileRecConstr :: QName -> [FieldUpdate] -> Compile JsExp+compileRecConstr :: S.QName -> [S.FieldUpdate] -> Compile JsExp compileRecConstr name fieldUpdates = do-    -- var obj = new $_Type()-    qname <- unsafeResolveName name-    let record = JsVar (JsNameVar name) (JsNew (JsConstructor qname) [])-    setFields <- liftM concat (forM fieldUpdates (updateStmt name))-    return $ JsApp (JsFun Nothing [] (record:setFields) (Just (JsName (JsNameVar name)))) []-  where updateStmt :: QName -> FieldUpdate -> Compile [JsStmt]-        updateStmt o (FieldUpdate field value) = do-          exp <- compileExp value-          return [JsSetProp (JsNameVar o) (JsNameVar field) exp]-        updateStmt name FieldWildcard = do-          records <- liftM stateRecords get-          let fields = fromJust (lookup name records)-          return (map (\fieldName -> JsSetProp (JsNameVar name)-                                               (JsNameVar fieldName)-                                               (JsName (JsNameVar fieldName)))-                      fields)-        -- TODO: FieldPun-        -- I couldn't find a code that generates (FieldUpdate (FieldPun ..))-        updateStmt _ u = error ("updateStmt: " ++ show u)+  -- var obj = new $_Type()+  let unQualName = unQualify $ unAnn name+  qname <- unsafeResolveName name+  let record = JsVar (JsNameVar unQualName) (JsNew (JsConstructor qname) [])+  setFields <- liftM concat (forM fieldUpdates (updateStmt name))+  return $ JsApp (JsFun Nothing [] (record:setFields) (Just $ JsName $ JsNameVar $ unQualify $ unAnn name)) []+  where+    -- updateStmt :: QName a -> S.FieldUpdate -> Compile [JsStmt]+    updateStmt (unAnn -> o) (FieldUpdate _ (unAnn -> field) value) = do+      exp <- compileExp value+      return [JsSetProp (JsNameVar $ unQualify o) (JsNameVar $ unQualify field) exp]+    updateStmt name (FieldWildcard (wildcardFields -> fields)) = do+      return $ for fields $ \fieldName -> JsSetProp (JsNameVar $ unAnn name)+                                                    (JsNameVar fieldName)+                                                    (JsName $ JsNameVar fieldName)+    -- I couldn't find a code that generates (FieldUpdate (FieldPun ..))+    updateStmt _ u = error ("updateStmt: " ++ show u) +    wildcardFields l = case l of+      Scoped (RecExpWildcard es) _ -> map (unQualify . gname2Qname . origGName) . map fst $ es+      _ -> []++ -- | Compile a record update.-compileRecUpdate :: Exp -> [FieldUpdate] -> Compile JsExp+compileRecUpdate :: S.Exp -> [S.FieldUpdate] -> Compile JsExp compileRecUpdate rec fieldUpdates = do-    record <- force <$> compileExp rec-    let copyName = UnQual (Ident "$_record_to_update")-        copy = JsVar (JsNameVar copyName)-                     (JsRawExp ("Object.create(" ++ printJSString record ++ ")"))-    setFields <- forM fieldUpdates (updateExp copyName)-    return $ JsApp (JsFun Nothing [] (copy:setFields) (Just (JsName (JsNameVar copyName)))) []-  where updateExp :: QName -> FieldUpdate -> Compile JsStmt-        updateExp copyName (FieldUpdate field value) =-          JsSetProp (JsNameVar copyName) (JsNameVar field) <$> compileExp value-        updateExp copyName (FieldPun name) =-          -- let a = 1 in C {a}-          return $ JsSetProp (JsNameVar copyName)-                             (JsNameVar (UnQual name))-                             (JsName (JsNameVar (UnQual name)))-        -- TODO: FieldWildcard-        -- I also couldn't find a code that generates (FieldUpdate FieldWildCard)-        updateExp _ FieldWildcard = error "unsupported update: FieldWildcard"+  record <- force <$> compileExp rec+  let copyName = UnQual () $ Ident () "$_record_to_update"+      copy = JsVar (JsNameVar copyName)+                   (JsRawExp ("Object.create(" ++ printJSString record ++ ")"))+  setFields <- forM fieldUpdates (updateExp copyName)+  return $ JsApp (JsFun Nothing [] (copy:setFields) (Just $ JsName $ JsNameVar copyName)) []+  where+    updateExp :: QName a -> S.FieldUpdate -> Compile JsStmt+    updateExp (unAnn -> copyName) (FieldUpdate _ (unQualify . unAnn -> field) value) =+      JsSetProp (JsNameVar copyName) (JsNameVar field) <$> compileExp value+    updateExp _ f@FieldPun{} = shouldBeDesugared f+    -- I also couldn't find a code that generates (FieldUpdate FieldWildCard)+    updateExp _ FieldWildcard{} = error "unsupported update: FieldWildcard"  -- | Desugar list comprehensions.-desugarListComp :: Exp -> [QualStmt] -> Compile Exp-desugarListComp e [] =-    return (List [ e ])-desugarListComp e (QualStmt (Generator loc p e2) : stmts) = do-    nested <- desugarListComp e stmts-    withScopedTmpName $ \f ->-      return (Let (BDecls [ FunBind [-          Match loc f [ p         ] Nothing (UnGuardedRhs nested)    (BDecls []),-          Match loc f [ PWildCard ] Nothing (UnGuardedRhs (List [])) (BDecls [])-          ]]) (App (App (Var (UnQual (Ident "concatMap"))) (Var (UnQual f))) e2))-desugarListComp e (QualStmt (Qualifier e2)       : stmts) = do-    nested <- desugarListComp e stmts-    return (If e2 nested (List []))-desugarListComp e (QualStmt (LetStmt bs)         : stmts) = do-    nested <- desugarListComp e stmts-    return (Let bs nested)-desugarListComp _ (s                             : _    ) =-    throwError (UnsupportedQualStmt s)+desugarListComp :: S.Exp -> [S.QualStmt] -> Compile S.Exp+desugarListComp e [] = return (List noI [ e ])+desugarListComp e (QualStmt _ (Generator _ p e2) : stmts) = do+  nested <- desugarListComp e stmts+  withScopedTmpName $ \f ->+    return (Let noI (BDecls noI [ FunBind noI [+        Match noI f [ p             ] (UnGuardedRhs noI nested) Nothing+      , Match noI f [ PWildCard noI ] (UnGuardedRhs noI (List noI [])) Nothing+      ]]) (App noI (App noI (Var noI (Qual noI (ModuleName noI "$Prelude") (Ident noI "concatMap"))) (Var noI (UnQual noI f))) e2))+desugarListComp e (QualStmt _ (Qualifier _ e2) : stmts) = do+  nested <- desugarListComp e stmts+  return (If noI e2 nested (List noI []))+desugarListComp e (QualStmt _ (LetStmt _ bs) : stmts) = do+  nested <- desugarListComp e stmts+  return (Let noI bs nested)+desugarListComp _ (s : _ ) =+  throwError (UnsupportedQualStmt s)  -- | Make a Fay list. makeList :: [JsExp] -> JsExp makeList exps = JsApp (JsName $ JsBuiltIn "list") [JsList exps]---- | Compile a statement of a do block.-compileStmt :: Maybe Exp -> Stmt -> Compile (Maybe Exp)-compileStmt inner stmt =-  case inner of-    Nothing -> initStmt-    Just inner -> subsequentStmt inner--  where initStmt =-          case stmt of-            Qualifier exp -> return (Just exp)-            LetStmt{}     -> throwError UnsupportedLet-            _             -> throwError InvalidDoBlock--        subsequentStmt inner =-          case stmt of-            Generator loc pat exp -> compileGenerator loc pat inner exp-            Qualifier exp -> return (Just (InfixApp exp-                                                    (QVarOp (UnQual (Symbol ">>")))-                                                    inner))-            LetStmt (BDecls binds) -> return (Just (Let (BDecls binds) inner))-            LetStmt _ -> throwError UnsupportedLet-            RecStmt{} -> throwError UnsupportedRecursiveDo--        compileGenerator srcloc pat inner exp = do-          let body = Lambda srcloc [pat] inner-          return (Just (InfixApp exp-                                 (QVarOp (UnQual (Symbol ">>=")))-                                 body))  -- | Optimize short literal [e1..e3] arithmetic sequences. optEnumFromTo :: CompileConfig -> JsExp -> JsExp -> Maybe JsExp
src/Fay/Compiler/FFI.hs view
@@ -12,123 +12,131 @@   ,compileFFIExp   ,jsToFayHash   ,fayToJsHash+  ,typeArity   ) where -import Fay.Compiler.Misc-import Fay.Compiler.Print           (printJSString)-import Fay.Compiler.QName-import Fay.Types+import           Fay.Compiler.Misc+import           Fay.Compiler.Print                     (printJSString)+import           Fay.Compiler.QName+import           Fay.Exts.NoAnnotation                  (unAnn)+import qualified Fay.Exts.NoAnnotation                  as N+import qualified Fay.Exts.Scoped                        as S+import           Fay.Types -import Control.Monad.Error-import Control.Monad.Writer-import Control.Applicative ((<$>), (<*>))-import Data.Char-import Data.Generics.Schemes-import Data.List-import Data.Maybe-import Data.String-import Language.ECMAScript3.Parser  as JS-import Language.ECMAScript3.Syntax-import Language.Haskell.Exts        (prettyPrint)-import Language.Haskell.Exts.Syntax-import Prelude                      hiding (exp, mod)-import Safe+import           Control.Applicative                    ((<$>), (<*>))+import           Control.Arrow                          ((***))+import           Control.Monad.Error+import           Control.Monad.Writer+import           Data.Char+import           Data.Generics.Schemes+import           Data.List+import           Data.Maybe+import           Data.String+import           Language.ECMAScript3.Parser            as JS+import           Language.ECMAScript3.Syntax+import           Language.Haskell.Exts.Annotated        (SrcSpanInfo,+                                                         prettyPrint)+import           Language.Haskell.Exts.Annotated.Syntax+import           Prelude                                hiding (exp, mod)+import           Safe  -- | Compile an FFI call.-compileFFI :: SrcLoc -- ^ Location of the original FFI decl.-           -> Name  -- ^ Name of the to-be binding.+compileFFI :: S.Name  -- ^ Name of the to-be binding.            -> String -- ^ The format string.-           -> Type   -- ^ Type signature.+           -> S.Type   -- ^ Type signature.            -> Compile [JsStmt]-compileFFI srcloc name formatstr sig =+compileFFI name' formatstr sig =   -- substitute newtypes with their child types before calling   -- real compileFFI   compileFFI' =<< rmNewtys sig -  where rmNewtys :: Type -> Compile Type-        rmNewtys (TyForall b c t) = TyForall b c <$> rmNewtys t-        rmNewtys (TyFun t1 t2)    = TyFun <$> rmNewtys t1 <*> rmNewtys t2-        rmNewtys (TyTuple b tl)   = TyTuple b <$> mapM rmNewtys tl-        rmNewtys (TyList t)       = TyList <$> rmNewtys t-        rmNewtys (TyApp t1 t2)    = TyApp <$> rmNewtys t1 <*> rmNewtys t2-        rmNewtys t@TyVar{}        = return t-        rmNewtys (TyCon qname)    = do-          newty <- lookupNewtypeConst qname-          return $ case newty of-                     Nothing     -> TyCon qname-                     Just (_,ty) -> ty-        rmNewtys (TyParen t)      = TyParen <$> rmNewtys t-        rmNewtys (TyInfix t1 q t2)= flip TyInfix q <$> rmNewtys t1 <*> rmNewtys t2-        rmNewtys (TyKind t k)     = flip TyKind k <$> rmNewtys t+  where+    rmNewtys :: S.Type -> Compile N.Type+    rmNewtys (TyForall _ b c t) = TyForall () (fmap (map unAnn) b) (fmap unAnn c) <$> rmNewtys t+    rmNewtys (TyFun _ t1 t2)    = TyFun () <$> rmNewtys t1 <*> rmNewtys t2+    rmNewtys (TyTuple _ b tl)   = TyTuple () b <$> mapM rmNewtys tl+    rmNewtys (TyList _ t)       = TyList () <$> rmNewtys t+    rmNewtys (TyApp _ t1 t2)    = TyApp () <$> rmNewtys t1 <*> rmNewtys t2+    rmNewtys t@TyVar{}          = return (unAnn t)+    rmNewtys (TyCon _ qname)    = do+      newty <- lookupNewtypeConst qname+      return $ case newty of+                 Nothing     -> TyCon () (unAnn qname)+                 Just (_,ty) -> ty+    rmNewtys (TyParen _ t)      = TyParen () <$> rmNewtys t+    rmNewtys (TyInfix _ t1 q t2)= flip (TyInfix ()) (unAnn q) <$> rmNewtys t1 <*> rmNewtys t2+    rmNewtys (TyKind _ t k)     = flip (TyKind ()) (unAnn k) <$> rmNewtys t -        compileFFI' :: Type -> Compile [JsStmt]-        compileFFI' sig' = do-          fun <- compileFFIExp srcloc (Just name) formatstr sig'-          stmt <- bindToplevel True name fun-          return [stmt]+    compileFFI' :: N.Type -> Compile [JsStmt]+    compileFFI' sig' = do+      fun <- compileFFIExp loc (Just name) formatstr sig'+      stmt <- bindToplevel True name fun+      return [stmt] +    name = unAnn name'+    loc = S.srcSpanInfo $ ann name'+ -- | Compile an FFI expression (also used when compiling top level definitions).-compileFFIExp :: SrcLoc -> Maybe Name -> String -> Type -> Compile JsExp-compileFFIExp srcloc nameopt formatstr sig = do+compileFFIExp :: SrcSpanInfo -> Maybe (Name a) -> String -> (Type a) -> Compile JsExp+compileFFIExp loc (fmap unAnn -> nameopt) formatstr (unAnn -> sig) = do   let name = fromMaybe "<exp>" nameopt-  inner <- formatFFI srcloc formatstr (zip params funcFundamentalTypes)+  inner <- formatFFI loc formatstr (zip params funcFundamentalTypes)   case JS.parse JS.expression (prettyPrint name) (printJSString (wrapReturn inner)) of-    Left err -> throwError (FfiFormatInvalidJavaScript srcloc inner (show err))+    Left err -> throwError (FfiFormatInvalidJavaScript loc inner (show err))     Right exp  -> do       config' <- config id-      when (configGClosure config') $ warnDotUses srcloc inner exp+      when (configGClosure config') $ warnDotUses loc inner exp       return (body inner) -  where body inner = foldr wrapParam (wrapReturn inner) params-        wrapParam pname inner = JsFun Nothing [pname] [] (Just inner)-        params = zipWith const uniqueNames [1..typeArity sig]-        wrapReturn inner = thunk $-          case lastMay funcFundamentalTypes of-            -- Returns a “pure” value;-            Just{} -> jsToFay SerializeAnywhere returnType (JsRawExp inner)-            -- Base case:-            Nothing -> JsRawExp inner-        funcFundamentalTypes = functionTypeArgs sig-        returnType = last funcFundamentalTypes+  where+    body inner = foldr wrapParam (wrapReturn inner) params+    wrapParam pname inner = JsFun Nothing [pname] [] (Just inner)+    params = zipWith const uniqueNames [1..typeArity sig]+    wrapReturn :: String -> JsExp+    wrapReturn inner = thunk $+      case lastMay funcFundamentalTypes of+        -- Returns a “pure” value;+        Just{} -> jsToFay SerializeAnywhere returnType (JsRawExp inner)+        -- Base case:+        Nothing -> JsRawExp inner+    funcFundamentalTypes = functionTypeArgs sig+    returnType = last funcFundamentalTypes  -- | Warn about uses of naked x.y which will not play nicely with Google Closure.-warnDotUses :: SrcLoc -> String -> Expression SourcePos -> Compile ()-warnDotUses srcloc string expr =+warnDotUses :: SrcSpanInfo -> String -> Expression SourcePos -> Compile ()+warnDotUses srcSpanInfo string expr =   when anyrefs $-    warn $ printSrcLoc srcloc ++ ":\nDot ref syntax used in FFI JS code: " ++ string+    warn $ printSrcSpanInfo srcSpanInfo ++ ":\nDot ref syntax used in FFI JS code: " ++ string -  where anyrefs = not (null (listify dotref expr)) ||+  where+    anyrefs = not (null (listify dotref expr)) ||                   not (null (listify ldot expr)) -        dotref :: Expression SourcePos -> Bool-        dotref x = case x of-          DotRef _ (VarRef _ (Id _ name)) _-             | name `elem` globalNames -> False-          DotRef{}                     -> True-          _                            -> False+    dotref :: Expression SourcePos -> Bool+    dotref x = case x of+      DotRef _ (VarRef _ (Id _ name)) _+         | name `elem` globalNames -> False+      DotRef{}                     -> True+      _                            -> False -        ldot :: LValue SourcePos -> Bool-        ldot x =-          case x of-            LDot _ (VarRef _ (Id _ name)) _-             | name `elem` globalNames -> False-            LDot{}                     -> True-            _                          -> False+    ldot :: LValue SourcePos -> Bool+    ldot x =+      case x of+        LDot _ (VarRef _ (Id _ name)) _+         | name `elem` globalNames -> False+        LDot{}                     -> True+        _                          -> False -        globalNames = ["Math","console","JSON"]+    globalNames = ["Math","console","JSON"]  -- | Make a Fay→JS encoder.-emitFayToJs :: Name -> [TyVarBind] -> [([Name],BangType)] -> Compile ()-emitFayToJs name tyvars (explodeFields -> fieldTypes) = do+emitFayToJs :: Name a -> [TyVarBind b] -> [([Name c], BangType d)] -> Compile ()+emitFayToJs (unAnn -> name) (map unAnn -> tyvars) (explodeFields -> fieldTypes) = do   qname <- qualify name-  let ctrName = printJSString $ unqualName qname+  let ctrName = printJSString $ unQual qname   tell $ mempty { writerFayToJs = [(ctrName, translator)] }    where-    unqualName :: QName -> Name-    unqualName (UnQual n) = n-    unqualName (Qual _ n) = n-    unqualName Special{}  = error "unqualName: Special{}"     translator =       JsFun Nothing             [JsNameVar "type", argTypes, transcodingObjForced]@@ -137,9 +145,9 @@      obj :: JsStmt     obj = JsVar obj_ $-      JsObj [("instance",JsLit (JsStr (printJSString name)))]+      JsObj [("instance",JsLit (JsStr (printJSString (unAnn name))))] -    fieldStmts :: [(Int,(Name,BangType))] -> [JsStmt]+    fieldStmts :: [(Int,(N.Name,N.BangType))] -> [JsStmt]     fieldStmts [] = []     fieldStmts ((i,fieldType):fts) =       JsVar obj_v field :@@ -148,20 +156,20 @@           [] :         fieldStmts fts       where-        obj_v = JsNameVar (UnQual (Ident $ "obj_" ++ d))-        decl = JsNameVar (UnQual (Ident d))+        obj_v = JsNameVar $ UnQual () (Ident () $ "obj_" ++ d)+        decl = JsNameVar $ UnQual () (Ident () d)         (d, field) = declField i fieldType -    obj_ = JsNameVar (UnQual (Ident "obj_"))+    obj_ = JsNameVar "obj_"      -- Declare/encode Fay→JS field-    declField :: Int -> (Name,BangType) -> (String,JsExp)+    declField :: Int -> (N.Name,N.BangType) -> (String,JsExp)     declField i (fname,typ) =       (prettyPrint fname       ,fayToJs (SerializeUserArg i)                (argType (bangType typ))                (JsGetProp (JsName transcodingObjForced)-                          (JsNameVar (UnQual fname))))+                          (JsNameVar (UnQual () fname))))  -- | A name used for transcoding. transcodingObj :: JsName@@ -172,54 +180,53 @@ transcodingObjForced = JsNameVar "_obj"  -- | Get arg types of a function type.-functionTypeArgs :: Type -> [FundamentalType]-functionTypeArgs t =-  case t of-    TyForall _ _ i -> functionTypeArgs i-    TyFun a b      -> argType a : functionTypeArgs b-    TyParen st     -> functionTypeArgs st-    r              -> [argType r]+functionTypeArgs :: N.Type -> [FundamentalType]+functionTypeArgs t = case t of+  TyForall _ _ _ i -> functionTypeArgs i+  TyFun _ a b      -> argType a : functionTypeArgs b+  TyParen _ st     -> functionTypeArgs st+  r                -> [argType r]  -- | Convert a Haskell type to an internal FFI representation.-argType :: Type -> FundamentalType+argType :: N.Type -> FundamentalType argType t = case t of-  TyCon "String"              -> StringType-  TyCon "Double"              -> DoubleType-  TyCon "Int"                 -> IntType-  TyCon "Bool"                -> BoolType-  TyApp (TyCon "Ptr") _       -> PtrType-  TyApp (TyCon "Automatic") _ -> Automatic-  TyApp (TyCon "Defined") a   -> Defined (argType a)-  TyApp (TyCon "Nullable") a  -> Nullable (argType a)-  TyApp (TyCon "Fay") a       -> JsType (argType a)-  TyFun x xs                  -> FunctionType (argType x : functionTypeArgs xs)-  TyList x                    -> ListType (argType x)-  TyTuple _ xs                -> TupleType (map argType xs)-  TyParen st                  -> argType st-  TyApp op arg                -> userDefined (reverse (arg : expandApp op))+  TyCon _ (UnQual _ (Ident _ "String"))                -> StringType+  TyCon _ (UnQual _ (Ident _ "Double"))                -> DoubleType+  TyCon _ (UnQual _ (Ident _ "Int"))                   -> IntType+  TyCon _ (UnQual _ (Ident _ "Bool"))                  -> BoolType+  TyApp _ (TyCon _ (UnQual _ (Ident _ "Ptr"))) _       -> PtrType+  TyApp _ (TyCon _ (UnQual _ (Ident _ "Automatic"))) _ -> Automatic+  TyApp _ (TyCon _ (UnQual _ (Ident _ "Defined"))) a   -> Defined (argType a)+  TyApp _ (TyCon _ (UnQual _ (Ident _ "Nullable"))) a  -> Nullable (argType a)+  TyApp _ (TyCon _ (UnQual _ (Ident _ "Fay"))) a       -> JsType (argType a)+  TyFun _ x xs                  -> FunctionType (argType x : functionTypeArgs xs)+  TyList _ x                    -> ListType (argType x)+  TyTuple _ _ xs                -> TupleType (map argType xs)+  TyParen _ st                  -> argType st+  TyApp _ op arg                -> userDefined (reverse (arg : expandApp op))   _                     ->     -- No semantic point to this, merely to avoid GHC's broken     -- warning.     case t of-      TyCon (UnQual user)   -> UserDefined user []+      TyCon _ (UnQual _ user)   -> UserDefined user []       _ -> UnknownType  -- | Extract the type.-bangType :: BangType -> Type+bangType :: N.BangType -> N.Type bangType typ = case typ of-  BangedTy ty   -> ty-  UnBangedTy ty -> ty-  UnpackedTy ty -> ty+  BangedTy _ ty   -> ty+  UnBangedTy _ ty -> ty+  UnpackedTy _ ty -> ty  -- | Expand a type application.-expandApp :: Type -> [Type]-expandApp (TyParen t) = expandApp t-expandApp (TyApp op arg) = arg : expandApp op+expandApp :: N.Type -> [N.Type]+expandApp (TyParen _ t) = expandApp t+expandApp (TyApp _ op arg) = arg : expandApp op expandApp x = [x]  -- | Generate a user-defined type.-userDefined :: [Type] -> FundamentalType-userDefined (TyCon (UnQual name):typs) = UserDefined name (map argType typs)+userDefined :: [N.Type] -> FundamentalType+userDefined (TyCon _ (UnQual _ name):typs) = UserDefined name (map argType typs) userDefined _ = UnknownType  -- | Translate: JS → Fay.@@ -245,10 +252,10 @@   _ -> recursive    where flat specialize =-          JsApp (JsName (JsBuiltIn (fromString (method ++ "_" ++ specialize))))+          JsApp (JsName (JsBuiltIn (Ident () (method ++ "_" ++ specialize))))                 [exp]         recursive =-          JsApp (JsName (JsBuiltIn (fromString method)))+          JsApp (JsName (JsBuiltIn (Ident () method)))                 [typeRep context typ                 ,exp]         js ty' =@@ -295,19 +302,19 @@                                         (ret "unknown"))  -- | Get the arity of a type.-typeArity :: Type -> Int+typeArity :: Type a -> Int typeArity t = case t of-  TyForall _ _ i -> typeArity i-  TyFun _ b      -> 1 + typeArity b-  TyParen st     -> typeArity st+  TyForall _ _ _ i -> typeArity i+  TyFun _ _ b      -> 1 + typeArity b+  TyParen _ st     -> typeArity st   _              -> 0 --- | Format the FFI format string with the given arguments.-formatFFI :: SrcLoc                     -- ^ Location of the original FFI decl.+-- | Format the FFI  format string with the given arguments.+formatFFI :: SrcSpanInfo                -- ^ Source Location.           -> String                     -- ^ The format string.           -> [(JsName,FundamentalType)] -- ^ Arguments.           -> Compile String             -- ^ The JS code.-formatFFI srcloc formatstr args = go formatstr where+formatFFI loc formatstr args = go formatstr where   go ('%':'*':xs) = do     these <- mapM inject (zipWith const [1..] args)     rest <- go xs@@ -315,10 +322,10 @@   go ('%':'%':xs) = do     rest <- go xs     return ('%' : rest)-  go ['%'] = throwError (FfiFormatIncompleteArg srcloc)+  go ['%'] = throwError (FfiFormatIncompleteArg loc)   go ('%':(span isDigit -> (op,xs))) =     case readMay op of-     Nothing -> throwError (FfiFormatBadChars srcloc op)+     Nothing -> throwError (FfiFormatBadChars loc op)      Just n -> do        this <- inject n        rest <- go xs@@ -329,7 +336,7 @@    inject n =     case listToMaybe (drop (n-1) args) of-      Nothing -> throwError (FfiFormatNoSuchArg srcloc n)+      Nothing -> throwError (FfiFormatNoSuchArg loc n)       Just (arg,typ) ->         return (printJSString (fayToJs SerializeAnywhere typ (JsName arg))) @@ -346,10 +353,10 @@ jsToFayHash cases = [JsExpStmt $ JsApp (JsName $ JsBuiltIn "objConcat") [JsName $ JsBuiltIn "jsToFayHash", JsObj cases]]  -- | Make a JS→Fay decoder.-emitJsToFay ::  Name -> [TyVarBind] -> [([Name], BangType)] -> Compile ()-emitJsToFay name tyvars (explodeFields -> fieldTypes) = do+emitJsToFay :: Name a -> [TyVarBind b] -> [([Name c],BangType d)] -> Compile ()+emitJsToFay (unAnn -> name) (map unAnn -> tyvars) (map (unAnn *** unAnn) . explodeFields -> fieldTypes) = do   qname <- qualify name-  tell (mempty { writerJsToFay = [(printJSString name, translator qname)] })+  tell (mempty { writerJsToFay = [(printJSString (unAnn name), translator qname)] })    where     translator qname =@@ -357,7 +364,7 @@             (Just $ JsNew (JsConstructor qname)                           (map (decodeField . getIndex name tyvars) fieldTypes))     -- Decode JS→Fay field-    decodeField :: (Int,(Name,BangType)) -> JsExp+    decodeField :: (Int,(N.Name,N.BangType)) -> JsExp     decodeField (i,(fname,typ)) =       jsToFay (SerializeUserArg i)               (argType (bangType typ))@@ -369,17 +376,17 @@ argTypes = JsNameVar "argTypes"  -- | Get the index of a name from the set of type variables bindings.-getIndex :: Name -> [TyVarBind] -> (Name,BangType) -> (Int,(Name,BangType))-getIndex name tyvars (sname,ty) =+getIndex :: Name a -> [TyVarBind b] -> (Name c,BangType d) -> (Int,(N.Name,N.BangType))+getIndex (unAnn -> name) (map unAnn -> tyvars) (unAnn -> sname,unAnn -> ty) =   case bangType ty of-    TyVar tyname -> case elemIndex tyname (map tyvar tyvars) of+    TyVar _ tyname -> case elemIndex tyname (map tyvar tyvars) of       Nothing -> error $ "unknown type variable " ++ prettyPrint tyname ++                          " for " ++ prettyPrint name ++ "." ++ prettyPrint sname ++ "," ++-                         " vars were: " ++ unwords (map prettyPrint tyvars)+                         " vars were: " ++ unwords (map prettyPrint tyvars) ++ ", rest: " ++ show tyvars       Just i -> (i,(sname,ty))     _ -> (0,(sname,ty))  -- | Extract the name from a possibly-kinded tyvar.-tyvar :: TyVarBind -> Name-tyvar (UnkindedVar v) = v-tyvar (KindedVar v _) = v+tyvar :: N.TyVarBind -> N.Name+tyvar (UnkindedVar _ v) = v+tyvar (KindedVar _ v _) = v
src/Fay/Compiler/GADT.hs view
@@ -4,17 +4,18 @@   (convertGADT   ) where -import           Language.Haskell.Exts hiding (name, binds)+import           Language.Haskell.Exts.Annotated hiding (binds, name)  -- | Convert a GADT to a normal data type.-convertGADT :: GadtDecl -> QualConDecl-convertGADT d =-  case d of-    GadtDecl srcloc name typ -> QualConDecl srcloc tyvars context-                                            (ConDecl name (convertFunc typ))-  where tyvars = []-        context = []-        convertFunc (TyCon _) = []-        convertFunc (TyFun x xs) = UnBangedTy x : convertFunc xs-        convertFunc (TyParen x) = convertFunc x-        convertFunc _ = []+convertGADT :: GadtDecl a -> QualConDecl a+convertGADT d = case d of+  GadtDecl s name typ -> QualConDecl s tyvars context+                           (ConDecl s name (convertFunc typ))+  where+    tyvars = Nothing+    context = Nothing+    convertFunc :: Type a -> [BangType a]+    convertFunc (TyCon _ _) = []+    convertFunc (TyFun s x xs) = UnBangedTy s x : convertFunc xs+    convertFunc (TyParen _ x) = convertFunc x+    convertFunc _ = []
+ src/Fay/Compiler/Import.hs view
@@ -0,0 +1,119 @@+{-# LANGUAGE TupleSections #-}+{-# LANGUAGE ViewPatterns  #-}++-- | Handles finding imports and compiling them recursively.+-- This is done for each full AST traversal the copmiler does+-- which at this point is InitialPass's preprocessing+-- and Compiler's code generation+module Fay.Compiler.Import+  (startCompile+  ,compileWith+  ) where++import           Fay.Compiler.Config+import           Fay.Compiler.Misc+import           Fay.Control.Monad.IO+import qualified Fay.Exts                        as F+import           Fay.Exts.NoAnnotation           (unAnn)+import           Fay.Types++import           Control.Applicative+import           Control.Monad.Error+import           Control.Monad.RWS+import           Language.Haskell.Exts.Annotated hiding (name, var)+import           Prelude                         hiding (mod, read)+import           System.Directory+import           System.FilePath++-- | Start the compilation process using `compileModule` to compile a file.+startCompile :: (FilePath -> String -> Compile a) -> FilePath -> Compile a+startCompile compileModule filein = do+  modify $ \s -> s { stateImported = [] }+  fmap fst . listen $ compileModuleFromFile compileModule filein++-- | Compile a module+compileWith+  :: Monoid a+  => FilePath+  -> (a -> F.Module -> Compile a)+  -> (FilePath -> String -> Compile a)+  -> String+  -> Compile (a, CompileState, CompileWriter)+compileWith filepath with compileModule from = do+  rd <- ask+  st <- get+  res <- Compile . lift . lift $+    runCompileModule+      rd+      st+      (parseResult (throwError . uncurry ParseError)+                   (\mod@(Module _ _ _ imports _) -> do+                     res <- foldr (<>) mempty <$> mapM (compileImport compileModule) imports+                     modify $ \s -> s { stateModuleName = unAnn $ F.moduleName mod }+                     with res mod+                   )+                   (parseFay filepath from))+  either throwError return res++-- | Compile a module given its file path+compileModuleFromFile+  :: (FilePath -> String -> Compile a)+  -> FilePath+  -> Compile a+compileModuleFromFile compileModule fp = io (readFile fp) >>= compileModule fp++-- | Lookup a module from include directories and compile.+compileModuleFromName+  :: Monoid a+  => (FilePath -> String -> Compile a)+  -> F.ModuleName+  -> Compile a+compileModuleFromName compileModule nm =+  unlessImported nm compileModule+    where+      unlessImported+        :: Monoid a+        => ModuleName l+        -> (FilePath -> String -> Compile a)+        -> Compile a+      unlessImported (ModuleName _ "Fay.Types") _ = return mempty+      unlessImported (unAnn -> name) importIt = do+        imported <- gets stateImported+        case lookup name imported of+          Just _  -> return mempty+          Nothing -> do+            dirs <- configDirectoryIncludePaths <$> config id+            (filepath,contents) <- findImport dirs name+            modify $ \s -> s { stateImported = (name,filepath) : imported }+            importIt filepath contents++-- | Compile an import.+compileImport+  :: Monoid a+  => (FilePath -> String -> Compile a)+  -> F.ImportDecl+  -> Compile a+compileImport compileModule i = case i of+  -- Package imports are ignored since they are used for some trickery in fay-base.+  ImportDecl _ _    _ _ Just{}  _ _ -> return mempty+  ImportDecl _ name _ _ Nothing _ _ -> compileModuleFromName compileModule name++-- | Find an import's filepath and contents from its module name.+findImport :: [FilePath] -> ModuleName a -> Compile (FilePath,String)+findImport alldirs (unAnn -> mname) = go alldirs mname where+  go :: [FilePath] -> ModuleName a -> Compile (FilePath,String)+  go _ (ModuleName _ "Fay.Types") = return ("Fay/Types.hs", "newtype Fay a = Fay (Identity a)\n\nnewtype Identity a = Identity a")+  go (dir:dirs) name = do+    exists <- io (doesFileExist path)+    if exists+      then (path,) . stdlibHack <$> io (readFile path)+      else go dirs name+    where+      path = dir </> replace '.' '/' (prettyPrint name) ++ ".hs"+      replace c r = map (\x -> if x == c then r else x)+  go [] name =+    throwError $ Couldn'tFindImport (unAnn name) alldirs++  stdlibHack = case mname of+    ModuleName _ "Fay.FFI" -> const "module Fay.FFI where\n\ndata Nullable a = Nullable a | Null\n\ndata Defined a = Defined a | Undefined"+    _ -> id
src/Fay/Compiler/InitialPass.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ViewPatterns      #-}  -- | Preprocessing collecting names, data types, newtypes, imports, and exports -- for all modules recursively.@@ -6,104 +7,83 @@   (initialPass   ) where -import           Fay.Compiler.Config+import           Fay.Compiler.Desugar import           Fay.Compiler.GADT+import           Fay.Compiler.Import import           Fay.Compiler.Misc-import           Fay.Compiler.ModuleScope-import           Fay.Control.Monad.Extra import           Fay.Control.Monad.IO import           Fay.Data.List.Extra+import qualified Fay.Exts                        as F+import           Fay.Exts.NoAnnotation           (unAnn)+import qualified Fay.Exts.NoAnnotation           as N import           Fay.Types  import           Control.Applicative import           Control.Monad.Error import           Control.Monad.RWS-import qualified Data.Set as S-import qualified Data.Map as M-import           Language.Haskell.Exts.Parser-import           Language.Haskell.Exts.Syntax-import           Prelude hiding (mod, read)+import qualified Data.Map                        as M+import           Language.Haskell.Exts.Annotated hiding (name, var)+import qualified Language.Haskell.Names          as HN+import           Prelude                         hiding (mod, read)  -- | Preprocess and collect all information needed during code generation.-initialPass :: Module -> Compile ()-initialPass (Module _ mod _ Nothing exports imports decls) =-  withModuleScope $ do-    modify $ \s -> s { stateModuleName = mod-                     , stateModuleScope = findTopLevelNames mod decls-                     }-    forM_ imports compileImport-    forM_ decls scanRecordDecls-    forM_ decls scanNewtypeDecls-    case exports of-      Just exps -> mapM_ emitExport exps-      Nothing -> do-        exps <- moduleLocals mod <$> gets stateModuleScope-        modify $ flip (foldr addCurrentExport) exps-    modify $ \s -> s { stateModuleScopes = M.insert mod (stateModuleScope s) (stateModuleScopes s) }-initialPass m = throwError (UnsupportedModuleSyntax m)+initialPass :: FilePath -> Compile ()+initialPass = startCompile preprocessFileWithSource -compileImport :: ImportDecl -> Compile ()-compileImport (ImportDecl _ _ _ _ Just{} _ _) = return ()-compileImport (ImportDecl _ name False _ Nothing Nothing Nothing) =-  compileImportWithFilter name (const $ return True)-compileImport (ImportDecl _ name False _ Nothing Nothing (Just (True, specs))) =-  compileImportWithFilter name (fmap not . imported specs)-compileImport (ImportDecl _ name False _ Nothing Nothing (Just (False, specs))) =-  compileImportWithFilter name (imported specs)-compileImport i =-  throwError $ UnsupportedImport i+-- | Preprocess a module given its filepath and content.+preprocessFileWithSource :: FilePath -> String -> Compile ()+preprocessFileWithSource filepath contents = do+  (_,st,_) <- compileWith filepath preprocessAST preprocessFileWithSource contents+  -- This is the state we want to keep+  modify $ \s -> s { stateRecords     = stateRecords     st+                   , stateRecordTypes = stateRecordTypes st+                   , stateImported    = stateImported    st+                   , stateNewtypes    = stateNewtypes    st+                   , stateInterfaces  = stateInterfaces  st+                   , stateTypeSigs    = stateTypeSigs    st+                     -- TODO This needs to be added otherwise the+                     -- "executable" generation in Fay.hs gets the+                     -- wrong name. Not sure why it works to do it+                     -- here!+                   , stateModuleName  = stateModuleName  st+                   } -compileWith :: (Show from,Parseable from)-            => FilePath-            -> CompileReader-            -> CompileState-            -> (from -> Compile ())-            -> String-            -> Compile (Either CompileError ((),CompileState,CompileWriter))-compileWith filepath r st with from =-  io $ runCompile r-                  st-                  (parseResult (throwError . uncurry ParseError)-                  with-                  (parseFay filepath from))+-- | Preprocess from an AST+preprocessAST :: () -> F.Module -> Compile ()+preprocessAST () mod'@Module{} = do+  res <- io $ desugar mod'+  case res of+    Left err -> throwError err+    Right dmod -> do+      let (Module _ _ _ _ decls) = dmod+      -- This can only return one element since we only compile one module.+      ([exports],_) <- HN.getInterfaces Haskell2010 [] [dmod]+      modify $ \s -> s { stateInterfaces = M.insert (stateModuleName s) exports $ stateInterfaces s }+      forM_ decls scanTypeSigs+      forM_ decls scanRecordDecls+      forM_ decls scanNewtypeDecls+preprocessAST () mod = throwError $ UnsupportedModuleSyntax "preprocessAST" mod --- | Don't re-import the same modules.-unlessImported :: ModuleName-               -> (QName -> Compile Bool)-               -> (FilePath -> String -> Compile ())-               -> Compile ()-unlessImported "Fay.Types" _ _ = return ()-unlessImported name importFilter importIt = do-  isImported <- lookup name <$> gets stateImported-  case isImported of-    Just _ -> return ()-    Nothing -> do-      dirs <- configDirectoryIncludePaths <$> config id-      (filepath,contents) <- findImport dirs name-      modify $ \s -> s { stateImported = (name,filepath) : stateImported s }-      importIt filepath contents-  exports <- gets $ getExportsFor name-  imports <- filterM importFilter $ S.toList exports-  modify $ \s -> s { stateModuleScope = bindAsLocals imports (stateModuleScope s) }+--------------------------------------------------------------------------------+-- | Preprocessing  -- | Find newtype declarations-scanNewtypeDecls :: Decl -> Compile ()-scanNewtypeDecls (DataDecl _ NewType _ _ _ constructors _) = compileNewtypeDecl constructors+scanNewtypeDecls :: F.Decl -> Compile ()+scanNewtypeDecls (DataDecl _ NewType{} _ _ constructors _) = compileNewtypeDecl constructors scanNewtypeDecls _ = return ()  -- | Add new types to the state-compileNewtypeDecl :: [QualConDecl] -> Compile ()-compileNewtypeDecl [QualConDecl _ _ _ condecl] =-  case condecl of-      -- newtype declaration without destructor-    ConDecl name  [ty]            -> addNewtype name Nothing ty-    RecDecl cname [([dname], ty)] -> addNewtype cname (Just dname) ty-    x -> error $ "compileNewtypeDecl case: Should be impossible (this is a bug). Got: " ++ show x+compileNewtypeDecl :: [F.QualConDecl] -> Compile ()+compileNewtypeDecl [QualConDecl _ _ _ condecl] = case condecl of+    -- newtype declaration without destructor+  ConDecl _ name  [ty]            -> addNewtype name Nothing ty+  RecDecl _ cname [FieldDecl _ [dname] ty] -> addNewtype cname (Just dname) ty+  x -> error $ "compileNewtypeDecl case: Should be impossible (this is a bug). Got: " ++ show x   where-    getBangTy :: BangType -> Type-    getBangTy (BangedTy t)   = t-    getBangTy (UnBangedTy t) = t-    getBangTy (UnpackedTy t) = t+    getBangTy :: F.BangType -> N.Type+    getBangTy (BangedTy _ t)   = unAnn t+    getBangTy (UnBangedTy _ t) = unAnn t+    getBangTy (UnpackedTy _ t) = unAnn t      addNewtype cname dname ty = do       qcname <- qualify cname@@ -115,91 +95,57 @@ compileNewtypeDecl q = error $ "compileNewtypeDecl: Should be impossible (this is a bug). Got: " ++ show q  -- | Add record declarations to the state-scanRecordDecls :: Decl -> Compile ()+scanRecordDecls :: F.Decl -> Compile () scanRecordDecls decl = do   case decl of-    DataDecl _loc DataType _ctx name _tyvarb qualcondecls _deriv -> do+    DataDecl _loc DataType{} _ctx (F.declHeadName -> name) qualcondecls _deriv -> do       let ns = for qualcondecls (\(QualConDecl _loc' _tyvarbinds _ctx' condecl) -> conDeclName condecl)       addRecordTypeState name ns     _ -> return ()    case decl of-    DataDecl _ DataType _ _ _ constructors _ -> dataDecl constructors-    GDataDecl _ DataType _l _i _v _n decls _ -> dataDecl (map convertGADT decls)+    DataDecl _ DataType{} _ _ constructors _ -> dataDecl constructors+    GDataDecl _ DataType{} _ _ _ decls _ -> dataDecl (map convertGADT decls)     _ -> return ()    where-    addRecordTypeState name cons = modify $ \s -> s-      { stateRecordTypes = (UnQual name, map UnQual cons) : stateRecordTypes s }+    addRecordTypeState (unAnn -> name') (map unAnn -> cons') = do+      name <- qualify name'+      cons <- mapM qualify cons'+      modify $ \s -> s { stateRecordTypes = (name, cons) : stateRecordTypes s } -    conDeclName (ConDecl n _) = n-    conDeclName (InfixConDecl _ n _) = n-    conDeclName (RecDecl n _) = n+    conDeclName (ConDecl _ n _) = n+    conDeclName (InfixConDecl _ _ n _) = n+    conDeclName (RecDecl _ n _) = n      -- | Collect record definitions and store record name and field names.     -- A ConDecl will have fields named slot1..slotN-    dataDecl :: [QualConDecl] -> Compile ()+    dataDecl :: [F.QualConDecl] -> Compile ()     dataDecl constructors = do       forM_ constructors $ \(QualConDecl _ _ _ condecl) ->         case condecl of-          ConDecl name types -> do-            let fields =  map (Ident . ("slot"++) . show . fst) . zip [1 :: Integer ..] $ types+          ConDecl _ name types -> do+            let fields =  map (Ident () . ("slot"++) . show . fst) . zip [1 :: Integer ..] $ types             addRecordState name fields-          InfixConDecl _t1 name _t2 ->-            addRecordState name ["slot1", "slot2"]-          RecDecl name fields' -> do-            let fields = concatMap fst fields'+          InfixConDecl _ _t1 name _t2 ->+            addRecordState name [F.mkIdent "slot1", F.mkIdent "slot2"]+          RecDecl _ name fields' -> do+            let fields = concatMap F.fieldDeclNames fields'             addRecordState name fields        where-        addRecordState :: Name -> [Name] -> Compile ()-        addRecordState name fields = modify $ \s -> s-          { stateRecords = (UnQual name,map UnQual fields) : stateRecords s }+        addRecordState :: Name a -> [Name b] -> Compile ()+        addRecordState name' fields = do+          name <- qualify name'+          modify $ \s -> s+            { stateRecords = (name,map unAnn fields) : stateRecords s } --- | Is this name imported from anywhere?-imported :: [ImportSpec] -> QName -> Compile Bool-imported is qn = anyM (matching qn) is+scanTypeSigs :: F.Decl -> Compile ()+scanTypeSigs decl = case decl of+  TypeSig _ names typ -> mapM_ (`addTypeSig` typ) names+  _ -> return ()   where-    matching :: QName -> ImportSpec -> Compile Bool-    matching (Qual _ name) (IAbs typ) = return (name == typ)-    matching (Qual _ name) (IVar var) = return $ name == var-    matching (Qual _ name) (IThingAll typ) = do-      recs <- typeToRecs $ UnQual typ-      if UnQual name `elem` recs-        then return True-        else do-          fields <- typeToFields $ UnQual typ-          return $ UnQual name `elem` fields-    matching (Qual _ name) (IThingWith typ cns) =-      flip anyM cns $ \cn -> case cn of-        ConName _ -> do-          recs <- typeToRecs $ UnQual typ-          return $ UnQual name `elem` recs-        VarName _ -> do-          fields <- typeToFields $ UnQual typ-          return $ UnQual name `elem` fields-    matching q is' = error $ "compileImport: Unsupported QName ImportSpec combination " ++ show (q, is') ++ ", this is a bug!"---- | Compile an import filtering the exports based on the current module's imports-compileImportWithFilter :: ModuleName -> (QName -> Compile Bool) -> Compile ()-compileImportWithFilter name importFilter =-  unlessImported name importFilter $ \filepath contents -> do-    read <- ask-    stat <- get-    result <- compileWith filepath read stat initialPass contents-    case result of-      Right ((),st,_) -> do-        imports <- filterM importFilter $ S.toList $ getCurrentExports st-        -- Merges the state gotten from passing through an imported-        -- module with the current state. We can assume no duplicate-        -- records exist since GHC would pick that up.-        modify $ \s -> s { stateRecords      = stateRecords st-                         , stateLocalScope   = S.empty-                         , stateRecordTypes  = stateRecordTypes st-                         , stateImported     = stateImported st-                         , stateNewtypes     = stateNewtypes st-                         , stateModuleScope  = bindAsLocals imports (stateModuleScope s)-                         , _stateExports     = _stateExports st-                         , stateModuleScopes = stateModuleScopes st-                         }-      Left err -> throwError err+    addTypeSig :: F.Name -> F.Type -> Compile ()+    addTypeSig (unAnn -> n') (unAnn -> t) = do+      n <- qualify n'+      modify $ \s -> s { stateTypeSigs = M.insert n t (stateTypeSigs s) }
src/Fay/Compiler/Misc.hs view
@@ -1,40 +1,36 @@-{-# LANGUAGE TupleSections #-}-{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE FlexibleContexts  #-} {-# LANGUAGE OverloadedStrings #-}-{-# OPTIONS -Wall -fno-warn-orphans  #-}+{-# LANGUAGE RecordWildCards   #-}+{-# LANGUAGE ViewPatterns      #-}  -- | Miscellaneous functions used throughout the compiler.  module Fay.Compiler.Misc where +import           Fay.Compiler.PrimOp import           Fay.Control.Monad.IO-import qualified Fay.Compiler.ModuleScope        as ModuleScope+import qualified Fay.Exts                          as F+import           Fay.Exts.NoAnnotation             (unAnn)+import qualified Fay.Exts.NoAnnotation             as N+import qualified Fay.Exts.Scoped                   as S import           Fay.Types  import           Control.Applicative import           Control.Monad.Error import           Control.Monad.RWS+import           Data.Char                         (isAlpha) import           Data.List+import qualified Data.Map                          as M import           Data.Maybe-import qualified Data.Map                        as M-import qualified Data.Set                        as S import           Data.String-import           Data.Version                    (parseVersion)-import           Language.Haskell.Exts.Extension-import           Language.Haskell.Exts.Fixity-import           Language.Haskell.Exts.Parser-import           Language.Haskell.Exts.Pretty-import           Language.Haskell.Exts.Syntax-import           Prelude                         hiding (exp, mod)-import           System.Directory-import           System.FilePath+import           Data.Version                      (parseVersion)+import           Distribution.HaskellSuite.Modules+import           Language.Haskell.Exts.Annotated   hiding (name)+import           Language.Haskell.Names+import           Prelude                           hiding (exp, mod) import           System.IO-import           System.Process                  (readProcess)-import           Text.ParserCombinators.ReadP    (readP_to_S)---- | Make an identifier from the built-in HJ module.-fayBuiltin :: String -> QName-fayBuiltin = Qual (ModuleName "Fay$") . Ident+import           System.Process                    (readProcess)+import           Text.ParserCombinators.ReadP      (readP_to_S)  -- | Wrap an expression in a thunk. thunk :: JsExp -> JsExp@@ -59,26 +55,43 @@ uniqueNames = map JsParam [1::Integer ..]  -- | Resolve a given maybe-qualified name to a fully qualifed name.-tryResolveName :: QName -> Compile (Maybe QName)-tryResolveName special@Special{} = return (Just special)-tryResolveName q@Qual{} =-  ModuleScope.resolveName q <$> gets stateModuleScope-tryResolveName u@(UnQual name) = do-  names <- gets stateLocalScope-  env <- gets stateModuleScope-  if S.member name names-    then return $ Just (UnQual name)-    else maybe (Just <$> qualify name) (return . Just) $ ModuleScope.resolveName u env+tryResolveName :: Show l => QName (Scoped l) -> Maybe N.QName+tryResolveName s@Special{}                                      = Just $ unAnn s+tryResolveName s@(UnQual _ (Ident _ n)) | "$gen" `isPrefixOf` n = Just $ unAnn s+tryResolveName (unAnn -> Qual () (ModuleName () "$Prelude") n)  = Just $ Qual () (ModuleName () "Prelude") n+tryResolveName q@(Qual _ (ModuleName _ "Fay$") _)               = Just $ unAnn q+tryResolveName (Qual (Scoped ni _) _ _)                         = case ni of+    GlobalValue n -> replaceWithBuiltIns . gname2Qname . origGName $ origName n+    _             -> Nothing+    -- TODO should LocalValue just return the name for qualified imports?+tryResolveName q@(UnQual (Scoped ni _) (unAnn -> name))         = case ni of+    GlobalValue n -> replaceWithBuiltIns . gname2Qname . origGName $ origName n+    LocalValue _  -> Just $ UnQual () name+    ScopeError _  -> resolvePrimOp q+    _             -> Nothing +gname2Qname :: GName -> N.QName+gname2Qname g = case g of+  GName "" s -> UnQual () $ mkName s+  GName m  s -> Qual () (ModuleName () m) $ mkName s+  where+    mkName s@(x:_)+      | isAlpha x || x == '_' = Ident () s+      | otherwise = Symbol () s+    mkName "" = error "mkName \"\""++replaceWithBuiltIns :: N.QName -> Maybe N.QName+replaceWithBuiltIns n = findPrimOp n <|> return n+ -- | Resolve a given maybe-qualified name to a fully qualifed name. -- Use this when a resolution failure is a bug.-unsafeResolveName :: QName -> Compile QName-unsafeResolveName q = maybe (throwError $ UnableResolveQualified q) return =<< tryResolveName q+unsafeResolveName :: S.QName -> Compile N.QName+unsafeResolveName q = maybe (throwError $ UnableResolveQualified (unAnn q)) return $ tryResolveName q  -- | Resolve a newtype constructor.-lookupNewtypeConst :: QName -> Compile (Maybe (Maybe QName,Type))+lookupNewtypeConst :: S.QName -> Compile (Maybe (Maybe N.QName,N.Type)) lookupNewtypeConst n = do-  mName <- tryResolveName n+  let mName = tryResolveName n   case mName of     Nothing -> return Nothing     Just name -> do@@ -88,94 +101,36 @@         Just (_,dname,ty) -> return $ Just (dname,ty)  -- | Resolve a newtype destructor.-lookupNewtypeDest :: QName -> Compile (Maybe (QName,Type))+lookupNewtypeDest :: S.QName -> Compile (Maybe (N.QName,N.Type)) lookupNewtypeDest n = do-  mName <- tryResolveName n+  let mName = tryResolveName n   newtypes <- gets stateNewtypes   case find (\(_,dname,_) -> dname == mName) newtypes of     Nothing -> return Nothing     Just (cname,_,ty) -> return $ Just (cname,ty)  -- | Qualify a name for the current module.-qualify :: Name -> Compile QName-qualify name = do+qualify :: Name a -> Compile (N.QName)+qualify (Ident _ name) = do   modulename <- gets stateModuleName-  return (Qual modulename name)+  return (Qual () modulename (Ident () name))+qualify (Symbol _ name) = do+  modulename <- gets stateModuleName+  return (Qual () modulename (Symbol () name))  -- | Qualify a QName for the current module if unqualified.-qualifyQName :: QName -> Compile QName-qualifyQName (UnQual name) = qualify name-qualifyQName n             = return n+qualifyQName :: QName a -> Compile N.QName+qualifyQName (UnQual _ name) = qualify name+qualifyQName (unAnn -> n)    = return n  -- | Make a top-level binding.-bindToplevel :: Bool -> Name -> JsExp -> Compile JsStmt-bindToplevel toplevel name expr =+bindToplevel :: Bool -> Name a -> JsExp -> Compile JsStmt+bindToplevel toplevel (unAnn -> name) expr =   if toplevel     then do       mod <- gets stateModuleName-      return $ JsSetQName (Qual mod name) expr-    else return $ JsVar (JsNameVar $ UnQual name) expr----- | Create a temporary environment and discard it after the given computation.-withModuleScope :: Compile a -> Compile a-withModuleScope m = do-  scope <- gets stateModuleScope-  value <- m-  modify $ \s -> s { stateModuleScope = scope }-  return value---- | Create a temporary scope and discard it after the given computation.-withScope :: Compile a -> Compile a-withScope m = do-  scope <- gets stateLocalScope-  value <- m-  modify $ \s -> s { stateLocalScope = scope }-  return value---- | Run a compiler and just get the scope information.-generateScope :: Compile a -> Compile ()-generateScope m = do-  st <- get-  _ <- m-  scope <- gets stateLocalScope-  put st { stateLocalScope = scope }---- | Bind a variable in the current scope.-bindVar :: Name -> Compile ()-bindVar name =-  modify $ \s -> s { stateLocalScope = S.insert name (stateLocalScope s) }---- | Emit exported names.-emitExport :: ExportSpec -> Compile ()-emitExport spec = case spec of-  EVar (UnQual n) -> emitVar n-  EVar q@Qual{} -> modify $ addCurrentExport q-  EThingAll (UnQual name) -> do-    cons <- typeToRecs (UnQual name)-    fields <- typeToFields (UnQual name)-    mapM_ (emitVar . unQName) $ cons ++ fields-  EThingWith (UnQual _) ns -> mapM_ emitCName ns-  EAbs _ -> return () -- Type only, skip-  EModuleContents mod -> do-    known_exports <- gets _stateExports-    current_scope <- gets stateModuleScope-    let names = case M.lookup mod known_exports of-          Just exports -> S.toList exports                      -- in case we're exporting other module take it's export list-          Nothing -> ModuleScope.moduleLocals mod current_scope -- in case we're exporting outselves export local names-    mapM_ (emitExport . EVar) names--  -- Skip qualified exports for type exports in fay-base since-  -- qualified imports are not supported yet an error will be thrown-  -- on the import so hopefully this won't be confusing.-  EThingAll (Qual _ _) -> return ()-  e -> throwError $ UnsupportedExportSpec e- where-   emitVar = return . UnQual >=> unsafeResolveName >=> emitExport . EVar-   emitCName (VarName n) = emitVar n-   emitCName (ConName n) = emitVar n-   unQName (UnQual u) = u-   unQName _ = error "unQName Qual or Special -- should never happen"+      return $ JsSetQName (Qual () mod name) expr+    else return $ JsVar (JsNameVar $ UnQual () name) expr  -- | Force an expression in a thunk. force :: JsExp -> JsExp@@ -189,7 +144,7 @@ isConstant _       = False  -- | Deconstruct a parse result (a la maybe, foldr, either).-parseResult :: ((SrcLoc,String) -> b) -> (a -> b) -> ParseResult a -> b+parseResult :: ((F.SrcLoc,String) -> b) -> (a -> b) -> ParseResult a -> b parseResult die ok result = case result of   ParseOk a -> ok a   ParseFailed srcloc msg -> die (srcloc,msg)@@ -218,18 +173,18 @@ throwExp msg expr = JsThrowExp (JsList [JsLit (JsStr msg),expr])  -- | Is an alt a wildcard?-isWildCardAlt :: Alt -> Bool+isWildCardAlt :: S.Alt -> Bool isWildCardAlt (Alt _ pat _ _) = isWildCardPat pat  -- | Is a pattern a wildcard?-isWildCardPat :: Pat -> Bool+isWildCardPat :: S.Pat -> Bool isWildCardPat PWildCard{} = True isWildCardPat PVar{}      = True isWildCardPat _           = False  -- | Return formatter string if expression is a FFI call.-ffiExp :: Exp -> Maybe String-ffiExp (App (Var (UnQual (Ident "ffi"))) (Lit (String formatstr))) = Just formatstr+ffiExp :: Exp a -> Maybe String+ffiExp (App _ (Var _ (UnQual _ (Ident _ "ffi"))) (Lit _ (String _ formatstr _))) = Just formatstr ffiExp _ = Nothing  -- | Generate a temporary, SCOPED name for testing conditions and@@ -244,32 +199,49 @@  -- | Generate a temporary, SCOPED name for testing conditions and -- such. We don't have name tracking yet, so instead we use this.-withScopedTmpName :: (Name -> Compile a) -> Compile a+withScopedTmpName :: (S.Name -> Compile a) -> Compile a withScopedTmpName withName = do   depth <- gets stateNameDepth   modify $ \s -> s { stateNameDepth = depth + 1 }-  ret <- withName $ Ident $ "$gen" ++ show depth+  ret <- withName $ Ident S.noI $ "$gen" ++ show depth   modify $ \s -> s { stateNameDepth = depth }   return ret  -- | Print out a compiler warning. warn :: String -> Compile () warn "" = return ()-warn w = do-  shouldWarn <- config configWarn-  when shouldWarn . io . hPutStrLn stderr $ "Warning: " ++ w+warn w = config id >>= io . (`ioWarn` w) +ioWarn :: CompileConfig -> String -> IO ()+ioWarn _ "" = return ()+ioWarn cfg w =+  when (configWall cfg) $+    hPutStrLn stderr $ "Warning: " ++ w+ -- | Pretty print a source location.-printSrcLoc :: SrcLoc -> String+printSrcLoc :: S.SrcLoc -> String printSrcLoc SrcLoc{..} = srcFilename ++ ":" ++ show srcLine ++ ":" ++ show srcColumn +printSrcSpanInfo :: SrcSpanInfo -> String+printSrcSpanInfo (SrcSpanInfo a b) = concat $ printSrcSpan a : map printSrcSpan b++printSrcSpan :: SrcSpan -> String+printSrcSpan SrcSpan{..} = srcSpanFilename ++ ": (" ++ show srcSpanStartLine ++ "," ++ show srcSpanStartColumn ++ ")-(" ++ show srcSpanEndLine ++ "," ++ show srcSpanEndColumn ++ ")"++ -- | Lookup the record for a given type name.-typeToRecs :: QName -> Compile [QName]-typeToRecs typ = fromMaybe [] . lookup typ <$> gets stateRecordTypes+typeToRecs :: QName a -> Compile [N.QName]+typeToRecs (unAnn -> typ) = fromMaybe [] . lookup typ <$> gets stateRecordTypes +recToFields :: S.QName -> Compile [N.Name]+recToFields con = do+  case tryResolveName con of+    Nothing -> return []+    Just c -> fromMaybe [] . lookup c <$> gets stateRecords+ -- | Get the fields for a given type.-typeToFields :: QName -> Compile [QName]-typeToFields typ = do+typeToFields :: QName a -> Compile [N.Name]+typeToFields (unAnn -> typ) = do   allrecs <- gets stateRecords   typerecs <- typeToRecs typ   return . concatMap snd . filter ((`elem` typerecs) . fst) $ allrecs@@ -287,29 +259,17 @@   where     readVersion = listToMaybe . filter (null . snd) . readP_to_S parseVersion --- | Find an import's filepath and contents from its module name.-findImport :: [FilePath] -> ModuleName -> Compile (FilePath,String)-findImport alldirs mname = go alldirs mname where-  go (dir:dirs) name = do-    exists <- io (doesFileExist path)-    if exists-      then (path,) . stdlibHack <$> io (readFile path)-      else go dirs name-    where-      path = dir </> replace '.' '/' (prettyPrint name) ++ ".hs"-      replace c r = map (\x -> if x == c then r else x)-  go [] name =-    throwError $ Couldn'tFindImport name alldirs--  stdlibHack-    | mname == ModuleName "Fay.FFI" = const "module Fay.FFI where\n\ndata Nullable a = Nullable a | Null\n\ndata Defined a = Defined a | Undefined"-    | otherwise = id+-- | Run the top level compilation for all modules.+runTopCompile+  :: CompileReader+  -> CompileState+  -> Compile a+  -> IO (Either CompileError (a,CompileState,CompileWriter))+runTopCompile reader' state' m = fst <$> runModuleT (runErrorT (runRWST (unCompile m) reader' state')) [] "fay" (\_fp -> return undefined) M.empty --- | Run the compiler.-runCompile :: CompileReader -> CompileState-           -> Compile a-           -> IO (Either CompileError (a,CompileState,CompileWriter))-runCompile reader' state' m = runErrorT (runRWST (unCompile m) reader' state')+-- | Runs compilation for a single module.+runCompileModule :: CompileReader -> CompileState -> Compile a -> CompileModule a+runCompileModule reader' state' m = runErrorT (runRWST (unCompile m) reader' state')  -- | Parse some Fay code. parseFay :: Parseable ast => FilePath -> String -> ParseResult ast@@ -364,3 +324,6 @@                    ,KindSignatures]   , fixities = Just (preludeFixities ++ baseFixities)   }++shouldBeDesugared :: (Functor f, Show (f ())) => f l -> Compile a+shouldBeDesugared = throwError . ShouldBeDesugared . show . unAnn
− src/Fay/Compiler/ModuleScope.hs
@@ -1,149 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}---- | Handles variable bindings on the module level and also keeps track of--- primitive operations that we want to treat specially.--module Fay.Compiler.ModuleScope-  (ModuleScope-  ,bindAsLocals-  ,findTopLevelNames-  ,resolveName-  ,moduleLocals-  ,findPrimOp-  ) where--import           Fay.Compiler.GADT--import           Control.Arrow-import           Control.Monad.Reader-import           Control.Monad.Writer-import           Data.Default-import           Data.Map (Map)-import qualified Data.Map as M-import           Language.Haskell.Exts hiding (name, binds)-import           Prelude hiding (mod)---- | Maps names bound in the module to their real names--- The keys are unqualified for locals and imports,--- the values are always fully qualified--- Example contents:---   [ (UnQUal     "main"      , Qual "Main"     "main")---   , (UnQual     "take"      , Qual "Prelude"  "take")---   , (  Qual "M" "insertWith", Qual "Data.Map" "insertWith") ]-newtype ModuleScope = ModuleScope (Map QName QName)-  deriving Show--instance Monoid ModuleScope where-  mempty                                  = ModuleScope M.empty-  mappend (ModuleScope a) (ModuleScope b) = ModuleScope $ a `M.union` b--instance Default ModuleScope where-  def = mempty---- | Find the path of a locally bound name--- Returns special values in the "Fay$" module for primOps-resolveName :: QName -> ModuleScope -> Maybe QName-resolveName q (ModuleScope binds) = case M.lookup q binds of -- lookup in the module environment.--  -- something pointing to prelude, is it a primop?-  Just q'@(Qual (ModuleName "Prelude") n) -> case M.lookup n envPrimOpsMap of-    Just x  -> Just x  -- A primop which looks like it's imported from prelude.-    Nothing -> Just q' -- Regular prelude import, leave it as is.--  -- No matches in the current environment, so it may be a primop if it's unqualified.-  -- If Nothing is returned from either of the branches it means that there is-  -- no primop and nothing in env scope so GHC would have given an error.-  Nothing -> case q of-    UnQual n -> M.lookup n envPrimOpsMap-    _        -> Nothing-  j -> j -- Non-prelude import that was found in the env---- | Bind a list of names into the local scope--- Right now all bindings are made unqualified-bindAsLocals :: [QName] -> ModuleScope -> ModuleScope-bindAsLocals qs (ModuleScope binds) =-  -- This needs to be changed to not use unqual to support qualified imports.-  ModuleScope $ binds `M.union` M.fromList (map (unqual &&& id) qs)-    where unqual (Qual _ n) = UnQual n-          unqual u@UnQual{} = u-          unqual Special{}  = error "fay: ModuleScope.bindAsLocals: Special"---- | Find all names that are bound locally in this module, which excludes imports.-moduleLocals :: ModuleName -> ModuleScope -> [QName]-moduleLocals mod (ModuleScope binds) = filter isLocal . M.elems $ binds-  where-    isLocal (Qual m _) = mod == m-    isLocal _ = False------------------------------------------------------------------------------------- Primitive Operations---- | The built-in operations that aren't actually compiled from--- anywhere, they come from runtime.js.------ They're in the names list so that they can be overriden by the user--- in e.g. let a * b = a - b in 1 * 2.------ So we resolve them to Fay$, i.e. the prefix used for the runtime--- support. $ is not allowed in Haskell module names, so there will be--- no conflicts if a user decicdes to use a module named Fay.------ So e.g. will compile to (*) Fay$$mult, which is in runtime.js.-envPrimOpsMap :: Map Name QName-envPrimOpsMap = M.fromList-  [ (Symbol ">>",    Qual (ModuleName "Fay$") (Ident "then"))-  , (Symbol ">>=",   Qual (ModuleName "Fay$") (Ident "bind"))-  , (Ident "return", Qual (ModuleName "Fay$") (Ident "return"))-  , (Ident "force",  Qual (ModuleName "Fay$") (Ident "force"))-  , (Ident "seq",    Qual (ModuleName "Fay$") (Ident "seq"))-  , (Symbol "*",     Qual (ModuleName "Fay$") (Ident "mult"))-  , (Symbol "+",     Qual (ModuleName "Fay$") (Ident "add"))-  , (Symbol "-",     Qual (ModuleName "Fay$") (Ident "sub"))-  , (Symbol "/",     Qual (ModuleName "Fay$") (Ident "divi"))-  , (Symbol "==",    Qual (ModuleName "Fay$") (Ident "eq"))-  , (Symbol "/=",    Qual (ModuleName "Fay$") (Ident "neq"))-  , (Symbol ">",     Qual (ModuleName "Fay$") (Ident "gt"))-  , (Symbol "<",     Qual (ModuleName "Fay$") (Ident "lt"))-  , (Symbol ">=",    Qual (ModuleName "Fay$") (Ident "gte"))-  , (Symbol "<=",    Qual (ModuleName "Fay$") (Ident "lte"))-  , (Symbol "&&",    Qual (ModuleName "Fay$") (Ident "and"))-  , (Symbol "||",    Qual (ModuleName "Fay$") (Ident "or"))-  ]---- | Lookup a primop that was resolved to a Prelude definition.-findPrimOp :: QName -> Maybe QName-findPrimOp (Qual (ModuleName "Prelude") s) = M.lookup s envPrimOpsMap-findPrimOp _ = Nothing------------------------------------------------------------------------------------- AST--type ModuleScopeSt = ReaderT ModuleName (Writer ModuleScope) ()---- | Get module level names from a haskell module AST.-findTopLevelNames :: ModuleName -> [Decl] -> ModuleScope-findTopLevelNames mod decls = snd . runWriter $ runReaderT (mapM_ d_decl decls) mod--bindName :: Name -> ModuleScopeSt-bindName k = ask >>= \mod -> tell (ModuleScope $ M.singleton (UnQual k) (Qual mod k))--d_decl :: Decl -> ModuleScopeSt-d_decl d = case d of-  DataDecl _ _ _ _ _ dd _           -> mapM_ d_qualCon dd-  GDataDecl _ DataType _ _ _ _ ds _ -> mapM_ (d_qualCon . convertGADT) ds-  PatBind _ (PVar n) _ _ _          -> bindName n-  FunBind (Match _ n _ _ _ _ : _)   -> bindName n-  ClassDecl _ _ _ _ _ cds           -> mapM_ d_classDecl cds-  TypeSig _ ns _                    -> mapM_ bindName ns-  _                                 -> return ()--d_classDecl :: ClassDecl -> ModuleScopeSt-d_classDecl cd = case cd of-  ClsDecl d -> d_decl d-  _         -> return ()--d_qualCon :: QualConDecl -> ModuleScopeSt-d_qualCon (QualConDecl _ _ _ cd) = case cd of-  ConDecl n _        -> bindName n-  InfixConDecl _ n _ -> bindName n-  RecDecl n ns       -> bindName n >> mapM_ bindName (concatMap fst ns)
src/Fay/Compiler/Optimizer.hs view
@@ -1,31 +1,31 @@-{-# OPTIONS -fno-warn-orphans #-}-{-# LANGUAGE TupleSections #-}-{-# LANGUAGE PatternGuards #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE PatternGuards     #-}+{-# LANGUAGE TupleSections     #-}  -- | Optimizing the outputted JavaScript(-ish) AST.  module Fay.Compiler.Optimizer where -import Fay.Compiler.Misc+import           Fay.Compiler.Misc+import           Fay.Types -import Control.Applicative-import Control.Arrow (first)-import Control.Monad.Error-import Control.Monad.Writer-import Control.Monad.State-import Data.List-import Data.Maybe-import Fay.Types-import Language.Haskell.Exts (QName(..),ModuleName(..),Name(..))+import           Control.Applicative+import           Control.Arrow                   (first)+import           Control.Monad.Error+import           Control.Monad.State+import           Control.Monad.Writer+import           Data.List+import           Data.Maybe+import qualified Fay.Exts.NoAnnotation           as N+import           Language.Haskell.Exts.Annotated hiding (app, name, op) -import Prelude hiding (exp)+import           Prelude                         hiding (exp)  -- | The arity of a function. Arity here is defined to be the number -- of arguments that can be directly uncurried from a curried lambda -- abstraction. So \x y z -> if x then (\a -> a) else (\a -> a) has an -- arity of 3, not 4.-type FuncArity = (QName,Int)+type FuncArity = (N.QName,Int)  -- | Optimize monad. type Optimize = State OptState@@ -33,7 +33,7 @@ -- | State. data OptState = OptState   { optStmts   :: [JsStmt]-  , optUncurry :: [QName]+  , optUncurry :: [N.QName]   }  -- | Run an optimizer, which may output additional statements.@@ -46,50 +46,48 @@ -- | Inline x >> y to x;y in the JS output. inlineMonad :: [JsStmt] -> [JsStmt] inlineMonad = map go where-  go stmt =-    case stmt of-      JsVar name exp          -> JsVar name (inline exp)-      JsMappedVar a name exp  -> JsMappedVar a name (inline exp)-      JsIf exp stmts stmts'   -> JsIf (inline exp) (map go stmts) (map go stmts')-      JsEarlyReturn exp       -> JsEarlyReturn (inline exp)-      JsThrow exp             -> JsThrow (inline exp)-      JsWhile exp stmts       -> JsWhile (inline exp) (map go stmts)-      JsUpdate name exp       -> JsUpdate name (inline exp)-      JsSetProp a b exp       -> JsSetProp a b (inline exp)-      JsSetQName a exp        -> JsSetQName a (inline exp)-      JsSetModule a exp       -> JsSetModule a (inline exp)-      JsSetConstructor a exp  -> JsSetConstructor a (inline exp)-      JsSetPropExtern a b exp -> JsSetPropExtern a b (inline exp)-      JsContinue              -> JsContinue-      JsBlock stmts           -> JsBlock (map go stmts)-      JsExpStmt exp           -> JsExpStmt (inline exp)+  go stmt = case stmt of+    JsVar name exp          -> JsVar name (inline exp)+    JsMappedVar a name exp  -> JsMappedVar a name (inline exp)+    JsIf exp stmts stmts'   -> JsIf (inline exp) (map go stmts) (map go stmts')+    JsEarlyReturn exp       -> JsEarlyReturn (inline exp)+    JsThrow exp             -> JsThrow (inline exp)+    JsWhile exp stmts       -> JsWhile (inline exp) (map go stmts)+    JsUpdate name exp       -> JsUpdate name (inline exp)+    JsSetProp a b exp       -> JsSetProp a b (inline exp)+    JsSetQName a exp        -> JsSetQName a (inline exp)+    JsSetModule a exp       -> JsSetModule a (inline exp)+    JsSetConstructor a exp  -> JsSetConstructor a (inline exp)+    JsSetPropExtern a b exp -> JsSetPropExtern a b (inline exp)+    JsContinue              -> JsContinue+    JsBlock stmts           -> JsBlock (map go stmts)+    JsExpStmt exp           -> JsExpStmt (inline exp) -  inline expr =-    case expr of-      -- Optimizations-      JsApp op args -> fromMaybe (JsApp (inline op) $ map inline args) (flatten expr)+  inline expr = case expr of+    -- Optimizations+    JsApp op args -> fromMaybe (JsApp (inline op) $ map inline args) (flatten expr) -      -- Plumbing-      JsFun nm names stmts mexp        -> JsFun nm names (map go stmts) (fmap inline mexp)+    -- Plumbing+    JsFun nm names stmts mexp        -> JsFun nm names (map go stmts) (fmap inline mexp) -      JsNegApp exp                     -> JsNegApp (inline exp)-      JsTernaryIf exp1 exp2 exp3       -> JsTernaryIf (inline exp1) (inline exp2) (inline exp3)-      JsParen exp                      -> JsParen (inline exp)-      JsGetProp exp name               -> JsGetProp (inline exp) name-      JsLookup exp exp2                -> JsLookup (inline exp) (inline exp2)-      JsUpdateProp exp name exp2       -> JsUpdateProp (inline exp) name (inline exp2)-      JsGetPropExtern exp string       -> JsGetPropExtern (inline exp) string-      JsUpdatePropExtern exp name exp2 -> JsUpdatePropExtern (inline exp) name (inline exp2)-      JsList exps                      -> JsList (map inline exps)-      JsNew name exps                  -> JsNew name (map inline exps)-      JsThrowExp exp                   -> JsThrowExp (inline exp)-      JsInstanceOf exp name            -> JsInstanceOf (inline exp) name-      JsIndex i exp                    -> JsIndex i (inline exp)-      JsEq exp exp2                    -> JsEq (inline exp) (inline exp2)-      JsNeq exp exp2                   -> JsNeq (inline exp) (inline exp2)-      JsInfix string exp exp2          -> JsInfix string (inline exp) (inline exp2)-      JsObj keyvals                    -> JsObj keyvals-      rest                             -> rest+    JsNegApp exp                     -> JsNegApp (inline exp)+    JsTernaryIf exp1 exp2 exp3       -> JsTernaryIf (inline exp1) (inline exp2) (inline exp3)+    JsParen exp                      -> JsParen (inline exp)+    JsGetProp exp name               -> JsGetProp (inline exp) name+    JsLookup exp exp2                -> JsLookup (inline exp) (inline exp2)+    JsUpdateProp exp name exp2       -> JsUpdateProp (inline exp) name (inline exp2)+    JsGetPropExtern exp string       -> JsGetPropExtern (inline exp) string+    JsUpdatePropExtern exp name exp2 -> JsUpdatePropExtern (inline exp) name (inline exp2)+    JsList exps                      -> JsList (map inline exps)+    JsNew name exps                  -> JsNew name (map inline exps)+    JsThrowExp exp                   -> JsThrowExp (inline exp)+    JsInstanceOf exp name            -> JsInstanceOf (inline exp) name+    JsIndex i exp                    -> JsIndex i (inline exp)+    JsEq exp exp2                    -> JsEq (inline exp) (inline exp2)+    JsNeq exp exp2                   -> JsNeq (inline exp) (inline exp2)+    JsInfix string exp exp2          -> JsInfix string (inline exp) (inline exp2)+    JsObj keyvals                    -> JsObj keyvals+    rest                             -> rest  -- | Flatten a a>>(b>>c) to [a,b,c]. flatten :: JsExp -> Maybe JsExp@@ -100,17 +98,19 @@  -- | Try to collect nested a>>(b>>c). collect :: JsExp -> Maybe [JsExp]-collect exp =-  case exp of-    JsApp op args | isThen op ->-      case args of-        [rest,x] -> (x :) <$> collect rest-        [x]  -> return [x]-        _ -> Nothing-    _ -> return [exp]+collect exp = case exp of+  JsApp op args | isThen op ->+    case args of+      [rest,x] -> (x :) <$> collect rest+      [x]  -> return [x]+      _ -> Nothing+  _ -> return [exp] -  where isThen = (== JsName (JsNameVar (Qual (ModuleName "Fay$") (Ident "then$uncurried"))))+  where+    isThen (JsName (JsNameVar (Qual _ (ModuleName _ m) (Ident _ n)))) = m == "Fay$" && n == "then$uncurried"+    isThen _ = False + -- | Perform any top-level cross-module optimizations and GO DEEP to -- optimize further. optimizeToplevel :: [JsStmt] -> Optimize [JsStmt]@@ -169,8 +169,8 @@     JsApp a b                      -> do       result <- walkAndStripForces arities exp       case result of-        Just strippedExp             -> go strippedExp-        Nothing                      -> JsApp <$> go a <*> mapM go b+        Just strippedExp           -> go strippedExp+        Nothing                    -> JsApp <$> go a <*> mapM go b     JsNegApp e                     -> JsNegApp <$> go e     JsTernaryIf a b c              -> JsTernaryIf <$> go a <*> go b <*> go c     JsParen e                      -> JsParen <$> go e@@ -190,12 +190,14 @@ walkAndStripForces :: [FuncArity] -> JsExp -> Optimize (Maybe JsExp) walkAndStripForces arities = go True [] where   go frst args app = case app of-    JsApp (JsName JsForce) [e] -> if frst-                                     then do result <- go False args e-                                             case result of-                                               Nothing -> return Nothing-                                               Just ex -> return (Just (JsApp (JsName JsForce) [ex]))-                                     else go False args e+    JsApp (JsName JsForce) [e] ->+      if frst+        then do+          result <- go False args e+          case result of+            Nothing -> return Nothing+            Just ex -> return (Just (JsApp (JsName JsForce) [ex]))+        else go False args e     JsApp op [arg] -> go False (arg:args) op     JsName (JsNameVar f)       | Just arity <- lookup f arities, length args == arity -> do@@ -229,9 +231,9 @@   collectFunc (JsVar (JsNameVar name) exp) | arity > 0 = [(name,arity)]     where arity = expArity exp   collectFunc _ = []-  prim = map (first (Qual (ModuleName "Fay$"))) (unary ++ binary)-  unary = map (,1) [Ident "return"]-  binary = map ((,2) . Ident)+  prim = map (first (Qual () (ModuleName () "Fay$"))) (unary ++ binary)+  unary = map (,1) [Ident () "return"]+  binary = map ((,2) . Ident ())                ["then","bind","mult","mult","add","sub","div"                ,"eq","neq","gt","lt","gte","lte","and","or"] @@ -241,7 +243,7 @@ expArity _ = 0  -- | Change foo(x)(y) to foo$uncurried(x,y).-uncurryBinding :: [JsStmt] -> QName -> Maybe JsStmt+uncurryBinding :: [JsStmt] -> N.QName -> Maybe JsStmt uncurryBinding stmts qname = listToMaybe (mapMaybe funBinding stmts)   where     funBinding stmt = case stmt of@@ -257,13 +259,13 @@         inner -> JsFun Nothing (reverse args) [] (Just inner)  -- | Rename an uncurried copy of a curried function.-renameUncurried :: QName -> QName+renameUncurried :: N.QName -> N.QName renameUncurried q = case q of-  Qual m n -> Qual m (renameUnQual n)-  UnQual n -> UnQual (renameUnQual n)+  Qual _ m n -> Qual () m (renameUnQual n)+  UnQual _ n -> UnQual () (renameUnQual n)   s -> s   where     renameUnQual n = case n of-      Ident nom -> Ident (nom ++ postfix)-      Symbol nom -> Symbol (nom ++ postfix)+      Ident _ nom -> Ident () (nom ++ postfix)+      Symbol _ nom -> Symbol () (nom ++ postfix)     postfix = "$uncurried"
src/Fay/Compiler/Packages.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE TupleSections              #-}+{-# LANGUAGE TupleSections #-}  -- | Dealing with Cabal packages in Fay's own special way. 
src/Fay/Compiler/Pattern.hs view
@@ -1,83 +1,83 @@ {-# OPTIONS -fno-warn-name-shadowing #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ViewPatterns      #-}  -- | Compile pattern matches.  module Fay.Compiler.Pattern where -import Fay.Compiler.Misc-import Fay.Compiler.QName-import Fay.Types+import           Fay.Compiler.Misc+import           Fay.Compiler.QName+import           Fay.Exts.NoAnnotation           (unAnn)+import qualified Fay.Exts.NoAnnotation           as N+import qualified Fay.Exts.Scoped                 as S+import           Fay.Types -import Control.Monad.Error-import Control.Monad.State-import Control.Monad.Reader-import Data.List-import Data.Maybe-import Language.Haskell.Exts+import           Control.Applicative+import           Control.Monad.Error+import           Control.Monad.Reader+import           Language.Haskell.Exts.Annotated+import           Language.Haskell.Names  -- | Compile the given pattern against the given expression.-compilePat :: JsExp -> Pat -> [JsStmt] -> Compile [JsStmt]-compilePat exp pat body =-  case pat of-    PVar name       -> compilePVar name exp body-    PApp cons pats  -> do-      newty <- lookupNewtypeConst cons-      case newty of-        Nothing -> compilePApp cons pats exp body-        Just _  -> compileNewtypePat pats exp body-    PLit literal    -> compilePLit exp literal body-    PParen pat      -> compilePat exp pat body-    PWildCard       -> return body-    pat@PInfixApp{} -> compileInfixPat exp pat body-    PList pats      -> compilePList pats body exp-    PTuple _bx pats -> compilePList pats body exp-    PAsPat name pat -> compilePAsPat exp name pat body-    PRec name pats  -> compilePatFields exp name pats body-    pat             -> throwError (UnsupportedPattern pat)+compilePat :: JsExp -> S.Pat -> [JsStmt] -> Compile [JsStmt]+compilePat exp pat body = case pat of+  PVar _ name       -> compilePVar name exp body+  PApp _ cons pats  -> do+    newty <- lookupNewtypeConst cons+    case newty of+      Nothing -> compilePApp cons pats exp body+      Just _  -> compileNewtypePat pats exp body+  PLit _ literal    -> compilePLit exp literal body+  PParen{}          -> shouldBeDesugared pat+  PWildCard _       -> return body+  pat@PInfixApp{}   -> compileInfixPat exp pat body+  PList _ pats      -> compilePList pats body exp+  PTuple _ _bx pats -> compilePList pats body exp+  PAsPat _ name pat -> compilePAsPat exp name pat body+  PRec _ name pats  -> compilePatFields exp name pats body+  pat               -> throwError (UnsupportedPattern pat)  -- | Compile a pattern variable e.g. x.-compilePVar :: Name -> JsExp -> [JsStmt] -> Compile [JsStmt]-compilePVar name exp body = do-  bindVar name-  return $ JsVar (JsNameVar (UnQual name)) exp : body+compilePVar :: S.Name -> JsExp -> [JsStmt] -> Compile [JsStmt]+compilePVar (unAnn -> name) exp body =+  return $ JsVar (JsNameVar (UnQual () name)) exp : body  -- | Compile a record field pattern.-compilePatFields :: JsExp -> QName -> [PatField] -> [JsStmt] -> Compile [JsStmt]+compilePatFields :: JsExp -> S.QName -> [S.PatField] -> [JsStmt] -> Compile [JsStmt] compilePatFields exp name pats body = do-    c <- liftM (++ body) (compilePats' [] pats)-    qname <- unsafeResolveName name-    return [JsIf (force exp `JsInstanceOf` JsConstructor qname) c []]-  where -- compilePats' collects field names that had already been matched so that-        -- wildcard generates code for the rest of the fields.-        compilePats' :: [QName] -> [PatField] -> Compile [JsStmt]-        compilePats' names (PFieldPun name:xs) =-          compilePats' names (PFieldPat (UnQual name) (PVar name):xs)+  c <- liftM (++ body) (compilePats' [] pats)+  qname <- unsafeResolveName name+  return [JsIf (force exp `JsInstanceOf` JsConstructor qname) c []]+  where+    -- compilePats' collects field names that had already been matched so that+      -- wildcard generates code for the rest of the fields.+      compilePats' :: [S.QName] -> [S.PatField] -> Compile [JsStmt]+      compilePats' _ (p@PFieldPun{}:_) = shouldBeDesugared p+      compilePats' names (PFieldPat _ fieldname (PVar _ (unAnn -> varName)):xs) = do+        r <- compilePats' (fieldname : names) xs+        return $ JsVar (JsNameVar (UnQual () varName))+                       (JsGetProp (force exp) (JsNameVar (unQualify $ unAnn fieldname)))+                 : r -- TODO: think about this force call -        compilePats' names (PFieldPat fieldname (PVar varName):xs) = do-          r <- compilePats' (fieldname : names) xs-          bindVar varName-          return $ JsVar (JsNameVar (UnQual varName))-                         (JsGetProp (force exp) (JsNameVar fieldname))-                   : r -- TODO: think about this force call+      compilePats' names (PFieldWildcard (wildcardFields -> fields):xs) = do+        f <- forM fields $ \fieldName ->+          return $ JsVar (JsNameVar fieldName)+                         (JsGetProp (force exp) (JsNameVar fieldName))+        r <- compilePats' names xs+        return $ f ++ r -        compilePats' names (PFieldWildcard:xs) = do-          records <- liftM stateRecords get-          let fields = fromJust (lookup name records)-              fields' = fields \\ names-          f <- mapM (\fieldName -> do bindVar (unQual fieldName)-                                      return (JsVar (JsNameVar fieldName)-                                             (JsGetProp (force exp) (JsNameVar fieldName))))-                   fields'-          r <- compilePats' names xs-          return $ f ++ r+      compilePats' _ [] = return [] -        compilePats' _ [] = return []+      compilePats' _ (pat:_) = throwError (UnsupportedFieldPattern pat) -        compilePats' _ (pat:_) = throwError (UnsupportedFieldPattern pat)+      wildcardFields :: S.X -> [N.QName]+      wildcardFields l = case l of+        Scoped (RecPatWildcard es) _ -> map (unQualify . gname2Qname . origGName) es+        _ -> []  -- | Compile a literal value from a pattern match.-compilePLit :: JsExp -> Literal -> [JsStmt] -> Compile [JsStmt]+compilePLit :: JsExp -> S.Literal -> [JsStmt] -> Compile [JsStmt] compilePLit exp literal body = do   c <- ask   lit <- readerCompileLit c literal@@ -85,56 +85,55 @@                body                []] -  where -- Equality test for two expressions, with some optimizations.-        equalExps :: JsExp -> JsExp -> JsExp-        equalExps a b-          | isConstant a && isConstant b = JsEq a b-          | isConstant a = JsEq a (force b)-          | isConstant b = JsEq (force a) b-          | otherwise =-             JsApp (JsName (JsBuiltIn "equal")) [a,b]+  where+    -- Equality test for two expressions, with some optimizations.+    equalExps :: JsExp -> JsExp -> JsExp+    equalExps a b+      | isConstant a && isConstant b = JsEq a b+      | isConstant a = JsEq a (force b)+      | isConstant b = JsEq (force a) b+      | otherwise =+         JsApp (JsName (JsBuiltIn "equal")) [a,b]  -- | Compile as binding in pattern match-compilePAsPat :: JsExp -> Name -> Pat -> [JsStmt] -> Compile [JsStmt]-compilePAsPat exp name pat body = do-  bindVar name+compilePAsPat :: JsExp -> S.Name -> S.Pat -> [JsStmt] -> Compile [JsStmt]+compilePAsPat exp (unAnn -> name) pat body = do   p <- compilePat exp pat body-  return $ JsVar (JsNameVar $ UnQual name) exp : p+  return $ JsVar (JsNameVar $ UnQual () name) exp : p  -- | Compile a pattern match on a newtype.-compileNewtypePat :: [Pat] -> JsExp -> [JsStmt] -> Compile [JsStmt]+compileNewtypePat :: [S.Pat] -> JsExp -> [JsStmt] -> Compile [JsStmt] compileNewtypePat [pat] exp body = compilePat exp pat body compileNewtypePat ps _ _ = error $ "compileNewtypePat: Should be impossible (this is a bug). Got: " ++ show ps  -- | Compile a pattern application.-compilePApp :: QName -> [Pat] -> JsExp -> [JsStmt] -> Compile [JsStmt]+compilePApp :: S.QName -> [S.Pat] -> JsExp -> [JsStmt] -> Compile [JsStmt] compilePApp cons pats exp body = do   let forcedExp = force exp   let boolIf b = return [JsIf (JsEq forcedExp (JsLit (JsBool b))) body []]   case cons of     -- Special-casing on the booleans.-    Special UnitCon -> return (JsExpStmt forcedExp : body)-    UnQual "True"   -> boolIf True-    UnQual "False"  -> boolIf False+    Special _ (UnitCon _) -> return (JsExpStmt forcedExp : body)+    UnQual _ (Ident _ "True")   -> boolIf True+    UnQual _ (Ident _ "False")  -> boolIf False     -- Everything else, generic:-    _ -> do-      rf <- fmap (lookup cons) (gets stateRecords)-      let recordFields =-            fromMaybe-              (error $ "Constructor '" ++ prettyPrint cons ++-                       "' was not found in stateRecords, did you try running this through GHC first?")-              rf-      substmts <- foldM (\body (field,pat) ->-                             compilePat (JsGetProp forcedExp (JsNameVar field)) pat body)-                  body-                  (reverse (zip recordFields pats))-      qcons <- unsafeResolveName cons-      return [JsIf (forcedExp `JsInstanceOf` JsConstructor qcons)-                   substmts-                   []]+    n -> do+      let n' = tryResolveName n+      case n' of+        Nothing -> error $ "Constructor '" ++ prettyPrint n ++ "' could not be resolved"+        Just _ -> do+          recordFields <- map (UnQual ()) <$> recToFields n+          substmts <- foldM (\body (field,pat) ->+                                 compilePat (JsGetProp forcedExp (JsNameVar field)) pat body)+                      body+                      (reverse (zip recordFields pats))+          qcons <- unsafeResolveName cons+          return [JsIf (forcedExp `JsInstanceOf` JsConstructor qcons)+                       substmts+                       []]  -- | Compile a pattern list.-compilePList :: [Pat] -> [JsStmt] -> JsExp -> Compile [JsStmt]+compilePList :: [S.Pat] -> [JsStmt] -> JsExp -> Compile [JsStmt] compilePList [] body exp =   return [JsIf (JsEq (force exp) JsNull) body []] compilePList pats body exp = do@@ -151,19 +150,18 @@                []]  -- | Compile an infix pattern (e.g. cons and tuples.)-compileInfixPat :: JsExp -> Pat -> [JsStmt] -> Compile [JsStmt]-compileInfixPat exp pat@(PInfixApp left (Special cons) right) body =-  case cons of-    Cons ->-      withScopedTmpJsName $ \tmpName -> do-        let forcedExp = JsName tmpName-            x = JsGetProp forcedExp (JsNameVar "car")-            xs = JsGetProp forcedExp (JsNameVar "cdr")-        rightMatch <- compilePat xs right body-        leftMatch <- compilePat x left rightMatch-        return [JsVar tmpName (force exp)-               ,JsIf (JsInstanceOf forcedExp (JsBuiltIn "Cons"))-                     leftMatch-                     []]-    _ -> throwError (UnsupportedPattern pat)+compileInfixPat :: JsExp -> S.Pat -> [JsStmt] -> Compile [JsStmt]+compileInfixPat exp pat@(PInfixApp _ left (Special _ cons) right) body = case cons of+  Cons _ ->+    withScopedTmpJsName $ \tmpName -> do+      let forcedExp = JsName tmpName+          x = JsGetProp forcedExp (JsNameVar "car")+          xs = JsGetProp forcedExp (JsNameVar "cdr")+      rightMatch <- compilePat xs right body+      leftMatch <- compilePat x left rightMatch+      return [JsVar tmpName (force exp)+             ,JsIf (JsInstanceOf forcedExp (JsBuiltIn "Cons"))+                   leftMatch+                   []]+  _ -> throwError (UnsupportedPattern pat) compileInfixPat _ pat _ = throwError (UnsupportedPattern pat)
+ src/Fay/Compiler/PrimOp.hs view
@@ -0,0 +1,69 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ViewPatterns      #-}++--------------------------------------------------------------------------------+-- | Primitive Operations+-- Built-in operations that aren't actually compiled from+-- anywhere, they come from runtime.js.+--+-- They're in the names list so that they can be overriden by the user+-- in e.g. let a * b = a - b in 1 * 2.+--+-- So we resolve them to Fay$, i.e. the prefix used for the runtime+-- support. $ is not allowed in Haskell module names, so there will be+-- no conflicts if a user decicdes to use a module named Fay.+--+-- So e.g. will compile to (*) Fay$$mult, which is in runtime.js.++module Fay.Compiler.PrimOp+  ( fayBuiltin+  , findPrimOp+  , resolvePrimOp+  ) where++import           Fay.Exts.NoAnnotation           (unAnn)+import qualified Fay.Exts.NoAnnotation           as N++import           Data.Map                        (Map)+import qualified Data.Map                        as M+import           Language.Haskell.Exts.Annotated hiding (binds, name)+import           Prelude                         hiding (mod)++-- | Make an identifier from the built-in HJ module.+fayBuiltin :: a -> String -> QName a+fayBuiltin a = Qual a (ModuleName a "Fay$") . Ident a++-- | Mapping from unqualified names to qualified primitive names.+primOpsMap :: Map N.Name N.QName+primOpsMap = M.fromList+  [ (Symbol () ">>",     fayBuiltin () "then")+  , (Symbol () ">>=",    fayBuiltin () "bind")+  , (Ident  () "return", fayBuiltin () "return")+  , (Ident  () "force",  fayBuiltin () "force")+  , (Ident  () "seq",    fayBuiltin () "seq")+  , (Symbol ()  "*",     fayBuiltin () "mult")+  , (Symbol ()  "+",     fayBuiltin () "add")+  , (Symbol ()  "-",     fayBuiltin () "sub")+  , (Symbol ()  "/",     fayBuiltin () "divi")+  , (Symbol ()  "==",    fayBuiltin () "eq")+  , (Symbol ()  "/=",    fayBuiltin () "neq")+  , (Symbol ()  ">",     fayBuiltin () "gt")+  , (Symbol ()  "<",     fayBuiltin () "lt")+  , (Symbol ()  ">=",    fayBuiltin () "gte")+  , (Symbol ()  "<=",    fayBuiltin () "lte")+  , (Symbol ()  "&&",    fayBuiltin () "and")+  , (Symbol ()  "||",    fayBuiltin () "or")+  ]++-- | Lookup a primop that was resolved to a Prelude definition.+findPrimOp :: N.QName -> Maybe N.QName+findPrimOp (Qual _ (ModuleName _ "Prelude") s) = M.lookup s primOpsMap+findPrimOp _ = Nothing++-- | If this is resolved to a Prelude identifier or if it's unqualified,+-- check if it's a primop+resolvePrimOp :: QName a -> Maybe N.QName+resolvePrimOp (unAnn -> q) = case q of+  (Qual _ (ModuleName _ "Prelude") _) -> findPrimOp q+  (UnQual _ n) -> findPrimOp $ Qual () (ModuleName () "Prelude") n+  _ -> Nothing
src/Fay/Compiler/Print.hs view
@@ -1,9 +1,9 @@ {-# OPTIONS -fno-warn-orphans        #-} {-# OPTIONS -fno-warn-unused-do-bind #-}-{-# LANGUAGE FlexibleInstances       #-}-{-# LANGUAGE RecordWildCards         #-}-{-# LANGUAGE TypeSynonymInstances    #-}-{-# LANGUAGE ViewPatterns            #-}+{-# LANGUAGE FlexibleInstances    #-}+{-# LANGUAGE RecordWildCards      #-}+{-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE ViewPatterns         #-}  -- | Simple code (non-pretty) printing. --@@ -16,17 +16,19 @@  module Fay.Compiler.Print where +import           Fay.Compiler.PrimOp+import qualified Fay.Exts.NoAnnotation                  as N import           Fay.Types  import           Control.Monad import           Control.Monad.State import           Data.Aeson.Encode-import qualified Data.ByteString.Lazy.UTF8    as UTF8+import qualified Data.ByteString.Lazy.UTF8              as UTF8 import           Data.Default import           Data.List import           Data.String-import           Language.Haskell.Exts.Syntax-import           Prelude                      hiding (exp)+import           Language.Haskell.Exts.Annotated.Syntax+import           Prelude                                hiding (exp)  -------------------------------------------------------------------------------- -- Printing@@ -52,39 +54,39 @@       (JsBool b)       -> if b then "true" else "false"  -- | Print (and properly encode to JS) a qualified name.-instance Printable QName where+instance Printable N.QName where   printJS qname =     case qname of-      Qual (ModuleName "Fay$") name -> "Fay$$" +> name-      Qual moduleName name -> moduleName +> "." +> name-      UnQual name -> printJS name-      Special con -> printJS con+      Qual _ (ModuleName _ "Fay$") name -> "Fay$$" +> name+      Qual _ moduleName name -> moduleName +> "." +> name+      UnQual _ name -> printJS name+      Special _ con -> printJS con  -- | Print module name.-instance Printable ModuleName where-  printJS (ModuleName "Fay$") =+instance Printable N.ModuleName where+  printJS (ModuleName _ "Fay$") =     write "Fay$"-  printJS (ModuleName moduleName) = write $ go moduleName+  printJS (ModuleName _ moduleName) = write $ go moduleName      where go ('.':xs) = '.' : go xs           go (x:xs) = normalizeName [x] ++ go xs           go [] = []  -- | Print special constructors (tuples, list, etc.)-instance Printable SpecialCon where+instance Printable N.SpecialCon where   printJS specialCon =-    printJS $ (Qual (ModuleName "Fay$") . Ident) $+    printJS $ fayBuiltin () $       case specialCon of-        UnitCon -> "unit"-        Cons    -> "cons"+        UnitCon _ -> "unit"+        Cons    _ -> "cons"         _       -> error $ "Special constructor not supported: " ++ show specialCon  -- | Print (and properly encode) a name.-instance Printable Name where+instance Printable N.Name where   printJS name = write $     case name of-      Ident  idn -> encodeName idn-      Symbol sym -> encodeName sym+      Ident  _ idn -> encodeName idn+      Symbol _ sym -> encodeName sym  -- | Print a list of statements. instance Printable [JsStmt] where@@ -180,7 +182,7 @@   printJS (JsLitObj assoc) =     "{" +> (intercalateM "," (map cons assoc)) +> "}"       where-        cons :: (Name, JsExp) -> Printer ()+        cons :: (N.Name, JsExp) -> Printer ()         cons (key,value) = "\"" +> key +> "\": " +> value   printJS (JsFun nm params stmts ret) =        "function"@@ -209,8 +211,8 @@ -- | Unqualify a JsName. ident :: JsName -> JsName ident n = case n of-  JsConstructor (Qual _ s) -> JsNameVar $ UnQual s-  a -> a+  JsConstructor (Qual _ _ s) -> JsNameVar $ UnQual () s+  a                          -> a  -- | Print one of the kinds of names. instance Printable JsName where@@ -226,16 +228,16 @@       JsConstructor qname -> printCons qname       JsBuiltIn qname     -> "Fay$$" +> printJS qname       JsParametrizedType  -> write "type"-      JsModuleName (ModuleName m) -> write m+      JsModuleName (ModuleName _ m) -> write m  -- | Print a constructor name given a QName.-printCons :: QName -> Printer ()-printCons (UnQual n) = printConsName n-printCons (Qual (ModuleName m) n) = printJS m +> "." +> printConsName n-printCons (Special _) = error "qname2String Special"+printCons :: N.QName -> Printer ()+printCons (UnQual _ n) = printConsName n+printCons (Qual _ (ModuleName _ m) n) = printJS m +> "." +> printConsName n+printCons (Special {}) = error "qname2String Special"  -- | Print a constructor name given a Name. Helper for printCons.-printConsName :: Name -> Printer ()+printConsName :: N.Name -> Printer () printConsName n = write "_" >> printJS n  -- | Just write out strings.@@ -287,6 +289,7 @@     allowed = ['a'..'z'] ++ ['A'..'Z'] ++ ['0'..'9'] ++ "_"     escapeChar c = "$" ++ charId c ++ "$"     charId c = show (fromEnum c)+  -------------------------------------------------------------------------------- -- Printing
src/Fay/Compiler/QName.hs view
@@ -2,26 +2,35 @@  module Fay.Compiler.QName where -import Language.Haskell.Exts.Syntax+import           Language.Haskell.Exts.Annotated  -- | Extract the module name from a qualified name.-qModName :: QName -> Maybe ModuleName-qModName (Qual m _) = Just m+qModName :: QName a -> Maybe (ModuleName a)+qModName (Qual _ m _) = Just m qModName _          = Nothing  -- | Extract the name from a QName.-unQual :: QName -> Name-unQual (Qual _ n) = n-unQual (UnQual n) = n+unQual :: QName a -> Name a+unQual (Qual _ _ n) = n+unQual (UnQual _ n) = n unQual Special{} = error "unQual Special{}" +unQualify :: QName a -> QName a+unQualify (Qual a _ n) = UnQual a n+unQualify u@UnQual{} = u+unQualify Special{}  = error "unQualify: Special{}"+ -- | Change or add the ModuleName of a QName.-changeModule :: ModuleName -> QName -> QName-changeModule m (Qual _ n) = Qual m n-changeModule m (UnQual n) = Qual m n+changeModule :: ModuleName a -> QName a -> QName a+changeModule m (Qual a _ n) = Qual a m n+changeModule m (UnQual a n) = Qual a m n changeModule _ Special{}  = error "changeModule Special{}" +changeModule' :: (String -> String) -> QName a -> QName a+changeModule' f (Qual l (ModuleName ml mn) n) = Qual l (ModuleName ml $ f mn) n+changeModule' _ x = x+ -- | Extract the string from a Name.-unname :: Name -> String-unname (Ident s) = s-unname (Symbol s) = s+unname :: Name a -> String+unname (Ident _ s) = s+unname (Symbol _ s) = s
+ src/Fay/Compiler/State.hs view
@@ -0,0 +1,66 @@+{-# LANGUAGE NamedFieldPuns #-}++-- | Pure functions for working with CompileState++module Fay.Compiler.State where++import           Fay.Compiler.Misc+import           Fay.Compiler.QName+import qualified Fay.Exts.NoAnnotation  as N+import           Fay.Types++import qualified Data.Map               as M+import           Data.Set               (Set)+import qualified Data.Set               as S+import           Language.Haskell.Names++-- | Get all non local identifiers that should be exported in the JS module scope.+getNonLocalExportsWithoutNewtypes :: N.ModuleName -> CompileState -> Maybe (Set N.QName)+getNonLocalExportsWithoutNewtypes modName cs =+  fmap ( S.filter (not . isLocal)+       . S.map (gname2Qname . origGName . sv_origName)+       . S.filter (not . (`isNewtype` cs))+       . (\(Symbols exports _) -> exports)+       )+       . M.lookup modName . stateInterfaces $ cs+  where+   isLocal = (Just modName ==) . qModName++getLocalExportsWithoutNewtypes :: N.ModuleName -> CompileState -> Maybe (Set N.QName)+getLocalExportsWithoutNewtypes modName cs =+  fmap ( S.filter isLocal+       . S.map (gname2Qname . origGName . sv_origName)+       . S.filter (not . (`isNewtype` cs))+       . (\(Symbols exports _) -> exports)+       )+       . M.lookup modName . stateInterfaces $ cs+  where+   isLocal = (Just modName ==) . qModName++-- | Is this *resolved* name a new type constructor or destructor?+isNewtype :: SymValueInfo OrigName -> CompileState -> Bool+isNewtype s cs = case s of+  SymValue{}                     -> False+  SymMethod{}                    -> False+  SymSelector    { sv_typeName } -> not . (`isNewtypeDest` cs) . gname2Qname . origGName $ sv_typeName+  SymConstructor { sv_typeName } -> not . (`isNewtypeCons` cs) . gname2Qname . origGName $ sv_typeName++-- | Is this *resolved* name a new type destructor?+isNewtypeDest :: N.QName -> CompileState -> Bool+isNewtypeDest o = any (\(_,mdest,_) -> mdest == Just o) . stateNewtypes++-- | Is this *resolved* name a new type constructor?+isNewtypeCons :: N.QName -> CompileState -> Bool+isNewtypeCons o = any (\(cons,_,_) -> cons  == o) . stateNewtypes++-- | Add a ModulePath to CompileState, meaning it has been printed.+addModulePath :: ModulePath -> CompileState -> CompileState+addModulePath mp cs = cs { stateJsModulePaths = mp `S.insert` stateJsModulePaths cs }++-- | Has this ModulePath been added/printed?+addedModulePath :: ModulePath -> CompileState -> Bool+addedModulePath mp CompileState { stateJsModulePaths } = mp `S.member` stateJsModulePaths+++findTypeSig :: N.QName -> CompileState -> Maybe N.Type+findTypeSig n  = M.lookup n . stateTypeSigs
src/Fay/Compiler/Typecheck.hs view
@@ -2,21 +2,19 @@  module Fay.Compiler.Typecheck where +import           Fay.Compiler.Defaults import           Fay.Compiler.Misc-import           Fay.Control.Monad.IO import           Fay.System.Process.Extra import           Fay.Types -import           Control.Monad.Error import           Data.List import           Data.Maybe-import qualified GHC.Paths                  as GHCPaths+import qualified GHC.Paths                as GHCPaths  -- | Call out to GHC to type-check the file.-typecheck :: Maybe FilePath -> Bool -> String -> Compile ()-typecheck packageConf wall fp = do-  cfg <- config id-  faydir <- io faySourceDir+typecheck :: CompileConfig -> FilePath -> IO (Either CompileError String)+typecheck cfg fp = do+  faydir <- faySourceDir   let includes = configDirectoryIncludes cfg    -- Remove the fay source dir from includeDirs to prevent errors on FFI instance declarations.@@ -24,10 +22,10 @@   let packages = nub . map (fromJust . fst) . filter (isJust . fst) $ includes    ghcPackageDbArgs <--    case packageConf of+    case configPackageConf cfg of       Nothing -> return []       Just pk -> do-        flag <- io getGhcPackageDbFlag+        flag <- getGhcPackageDbFlag         return [flag ++ '=' : pk]   let flags =           [ "-fno-code"@@ -38,8 +36,8 @@           , "Language.Fay.DummyMain"           , "-i" ++ intercalate ":" includeDirs           , fp ] ++ ghcPackageDbArgs ++ wallF ++ map ("-package " ++) packages-  res <- io $ readAllFromProcess GHCPaths.ghc flags ""-  either (throwError . GHCError . fst) (warn . fst) res+  res <- readAllFromProcess GHCPaths.ghc flags ""+  either (return . Left . GHCError . fst) (return . Right . fst) res    where-    wallF | wall = ["-Wall"]+    wallF | configWall cfg = ["-Wall"]           | otherwise = []
src/Fay/Convert.hs view
@@ -1,7 +1,7 @@ {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE OverloadedStrings  #-}-{-# LANGUAGE TupleSections      #-} {-# LANGUAGE PatternGuards      #-}+{-# LANGUAGE TupleSections      #-} {-# OPTIONS -fno-warn-type-defaults #-}  -- | Convert a Haskell value to a (JSON representation of a) Fay value.@@ -184,49 +184,42 @@  -- | Parse a number. parseNumber :: Value -> Maybe Number-parseNumber value =-  case value of-    Number n -> return n-    _ -> mzero+parseNumber value = case value of+  Number n -> return n+  _ -> mzero  -- | Parse a bool. parseBool :: Value -> Maybe Bool-parseBool value =-  case value of-    Bool n -> return n-    _ -> mzero+parseBool value = case value of+  Bool n -> return n+  _ -> mzero  -- | Parse a string. parseString :: Value -> Maybe String-parseString value =-  case value of-    String s -> return (Text.unpack s)-    _ -> mzero+parseString value = case value of+  String s -> return (Text.unpack s)+  _ -> mzero  -- | Parse a char. parseChar :: Value -> Maybe Char-parseChar value =-  case value of-    String s | Just (c,_) <- Text.uncons s -> return c-    _ -> mzero+parseChar value = case value of+  String s | Just (c,_) <- Text.uncons s -> return c+  _ -> mzero  -- | Parse a Text. parseText :: Value -> Maybe Text-parseText value =-  case value of-    String s -> return s-    _ -> mzero+parseText value = case value of+  String s -> return s+  _ -> mzero  -- | Parse an array. parseArray :: Data a => Value -> Maybe [a]-parseArray value =-  case value of-    Array xs -> mapM readFromFay (Vector.toList xs)-    _ -> mzero+parseArray value = case value of+  Array xs -> mapM readFromFay (Vector.toList xs)+  _ -> mzero  -- | Parse unit. parseUnit :: Value -> Maybe ()-parseUnit value =-  case value of-    Null -> return ()-    _ -> mzero+parseUnit value = case value of+  Null -> return ()+  _ -> mzero
src/Fay/Data/List/Extra.hs view
@@ -2,8 +2,8 @@  module Fay.Data.List.Extra where -import Data.List hiding (map)-import Prelude hiding (map)+import           Data.List hiding (map)+import           Prelude   hiding (map)  -- | Get the union of a list of lists. unionOf :: (Eq a) => [[a]] -> [a]
+ src/Fay/Exts.hs view
@@ -0,0 +1,70 @@+module Fay.Exts where++import qualified Language.Haskell.Exts.Annotated as A++type X = A.SrcSpanInfo++type Alt = A.Alt X+type BangType = A.BangType X+type ClassDecl = A.ClassDecl X+type Decl = A.Decl X+type DeclHead = A.DeclHead X+type Ex = A.Exp X+type Exp = A.Exp X+type ExportSpec = A.ExportSpec X+type FieldDecl = A.FieldDecl X+type FieldUpdate = A.FieldUpdate X+type GadtDecl = A.GadtDecl X+type GuardedAlts = A.GuardedAlts X+type GuardedRhs = A.GuardedRhs X+type ImportDecl = A.ImportDecl X+type ImportSpec = A.ImportSpec X+type Literal = A.Literal X+type Match = A.Match X+type Module = A.Module X+type ModuleName = A.ModuleName X+type ModulePragma = A.ModulePragma X+type Name = A.Name X+type Pat = A.Pat X+type PatField = A.PatField X+type QName = A.QName X+type QOp = A.QOp X+type QualConDecl = A.QualConDecl X+type QualStmt = A.QualStmt X+type Rhs = A.Rhs X+type SpecialCon = A.SpecialCon X+type SrcLoc = A.SrcLoc+type Stmt = A.Stmt X+type TyVarBind = A.TyVarBind X+type Type = A.Type X++moduleName :: A.SrcInfo a => A.Module a -> A.ModuleName a+moduleName (A.Module _ (Just (A.ModuleHead _ n _ _)) _ _ _) = n+moduleName (A.Module a Nothing                     _ _ _) = A.ModuleName a "Main"+moduleName m = error $ "moduleName: " ++ A.prettyPrint m++moduleExports :: A.Module X -> Maybe (A.ExportSpecList X)+moduleExports (A.Module _ (Just (A.ModuleHead _ _ _ e)) _ _ _) = e+moduleExports (A.Module _ Nothing                     _ _ _) = Nothing+moduleExports m = error $ ("moduleExports: " ++ A.prettyPrint m)++moduleNameString :: A.ModuleName t -> String+moduleNameString (A.ModuleName _ n) = n++mkIdent :: String -> A.Name A.SrcSpanInfo+mkIdent = A.Ident noI++noI :: A.SrcSpanInfo+noI = A.noInfoSpan (A.mkSrcSpan A.noLoc A.noLoc)++convertFieldDecl :: A.FieldDecl a -> ([A.Name a], A.BangType a)+convertFieldDecl (A.FieldDecl _ ns b) = (ns, b)++fieldDeclNames :: A.FieldDecl a -> [A.Name a]+fieldDeclNames (A.FieldDecl _ ns _) = ns++declHeadName :: A.DeclHead a -> A.Name a+declHeadName d = case d of+  A.DHead _ n _ -> n+  A.DHInfix _ _ n _ -> n+  A.DHParen _ h -> declHeadName h
+ src/Fay/Exts/NoAnnotation.hs view
@@ -0,0 +1,67 @@+{-# OPTIONS -fno-warn-orphans  #-}+{-# LANGUAGE FlexibleInstances #-}+module Fay.Exts.NoAnnotation where+++import           Data.Char                       (isAlpha)+import           Data.List                       (intercalate)+import           Data.List.Split                 (splitOn)+import           Data.String+import qualified Language.Haskell.Exts.Annotated as A++type Alt = A.Alt ()+type BangType = A.BangType ()+type ClassDecl = A.ClassDecl ()+type Decl = A.Decl ()+type DeclHead = A.DeclHead ()+type Ex = A.Exp ()+type Exp = A.Exp ()+type ExportSpec = A.ExportSpec ()+type FieldDecl = A.FieldDecl ()+type FieldUpdate = A.FieldUpdate ()+type GadtDecl = A.GadtDecl ()+type GuardedAlts = A.GuardedAlts ()+type GuardedRhs = A.GuardedRhs ()+type ImportDecl = A.ImportDecl ()+type ImportSpec = A.ImportSpec ()+type Literal = A.Literal ()+type Match = A.Match ()+type Module = A.Module ()+type ModuleName = A.ModuleName ()+type ModulePragma = A.ModulePragma ()+type Name = A.Name ()+type Pat = A.Pat ()+type PatField = A.PatField ()+type QName = A.QName ()+type QOp = A.QOp ()+type QualConDecl = A.QualConDecl ()+type QualStmt = A.QualStmt ()+type Rhs = A.Rhs ()+type SpecialCon = A.SpecialCon ()+type SrcLoc = A.SrcLoc+type SrcSpan = A.SrcSpan+type SrcSpanInfo = A.SrcSpanInfo+type Stmt = A.Stmt ()+type TyVarBind = A.TyVarBind ()+type Type = A.Type ()++unAnn :: Functor f => f a -> f ()+unAnn = fmap (const ())++-- | Helpful for some things.+instance IsString (A.Name ()) where+  fromString n@(c:_)+    | isAlpha c || c == '_' = A.Ident () n+    | otherwise             = A.Symbol () n+  fromString [] = error "Name fromString: empty string"++-- | Helpful for some things.+instance IsString (A.QName ()) where+  fromString s = case splitOn "." s of+    []  -> error "QName fromString: empty string"+    [x] -> A.UnQual () $ fromString x+    xs  -> A.Qual () (fromString $ intercalate "." $ init xs) $ fromString (last xs)++-- | Helpful for writing qualified symbols (Fay.*).+instance IsString (A.ModuleName ()) where+   fromString = A.ModuleName ()
+ src/Fay/Exts/Scoped.hs view
@@ -0,0 +1,49 @@+module Fay.Exts.Scoped where++import qualified Fay.Exts                        as F++import qualified Language.Haskell.Exts.Annotated as A+import qualified Language.Haskell.Names          as HN+++type X = HN.Scoped A.SrcSpanInfo++type Alt = A.Alt X+type BangType = A.BangType X+type ClassDecl = A.ClassDecl X+type Decl = A.Decl X+type DeclHead = A.DeclHead X+type Ex = A.Exp X+type Exp = A.Exp X+type ExportSpec = A.ExportSpec X+type FieldDecl = A.FieldDecl X+type FieldUpdate = A.FieldUpdate X+type GadtDecl = A.GadtDecl X+type GuardedAlts = A.GuardedAlts X+type GuardedRhs = A.GuardedRhs X+type ImportDecl = A.ImportDecl X+type ImportSpec = A.ImportSpec X+type Literal = A.Literal X+type Match = A.Match X+type Module = A.Module X+type ModuleName = A.ModuleName X+type ModulePragma = A.ModulePragma X+type Name = A.Name X+type Pat = A.Pat X+type PatField = A.PatField X+type QName = A.QName X+type QOp = A.QOp X+type QualConDecl = A.QualConDecl X+type QualStmt = A.QualStmt X+type Rhs = A.Rhs X+type SpecialCon = A.SpecialCon X+type SrcLoc = A.SrcLoc+type Stmt = A.Stmt X+type TyVarBind = A.TyVarBind X+type Type = A.Type X++noI :: HN.Scoped A.SrcSpanInfo+noI = HN.Scoped HN.None F.noI++srcSpanInfo :: HN.Scoped A.SrcSpanInfo -> A.SrcSpanInfo+srcSpanInfo (HN.Scoped _ l) = l
src/Fay/FFI.hs view
@@ -14,7 +14,7 @@  import           Data.String (IsString) import           Fay.Types-import           Prelude      (Bool, Char, Double, Int, Maybe, String, error)+import           Prelude     (Bool, Char, Double, Int, Maybe, String, error)  -- | Values that may be null --  Nullable x decodes to x, Null decodes to null.
src/Fay/System/Directory/Extra.hs view
@@ -1,9 +1,9 @@ -- | Extra directory functions. module Fay.System.Directory.Extra where -import           Control.Monad (forM)+import           Control.Monad    (forM) import           System.Directory (doesDirectoryExist, getDirectoryContents)-import           System.FilePath ((</>))+import           System.FilePath  ((</>))  -- | Get all files in a folder and its subdirectories. -- Taken from Real World Haskell
src/Fay/Types.hs view
@@ -1,10 +1,5 @@-{-# OPTIONS -fno-warn-orphans #-}-{-# LANGUAGE DeriveDataTypeable         #-}-{-# LANGUAGE FunctionalDependencies     #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE MultiParamTypeClasses      #-}-{-# LANGUAGE OverloadedStrings          #-}-{-# LANGUAGE RecordWildCards            #-}+{-# LANGUAGE TypeFamilies               #-}  -- | All Fay types and instances. @@ -15,19 +10,14 @@   ,JsName(..)   ,CompileError(..)   ,Compile(..)-  ,CompilesTo(..)+  ,CompileResult+  ,CompileModule   ,Printable(..)   ,Fay   ,CompileReader(..)   ,CompileWriter(..)   ,CompileConfig(..)   ,CompileState(..)-  ,addCurrentExport-  ,getCurrentExports-  ,getNonLocalExports-  ,getCurrentExportsWithoutNewtypes-  ,getExportsFor-  ,faySourceDir   ,FundamentalType(..)   ,PrintState(..)   ,Printer(..)@@ -37,54 +27,54 @@   ,mkModulePath   ,mkModulePaths   ,mkModulePathFromQName-  ,addModulePath-  ,addedModulePath   ) where +import           Fay.Compiler.QName+import qualified Fay.Exts                          as F+import qualified Fay.Exts.NoAnnotation             as N+import qualified Fay.Exts.Scoped                   as S+ import           Control.Applicative-import           Control.Monad.Error    (Error, ErrorT, MonadError)-import           Control.Monad.Identity (Identity)-import           Control.Monad.State+import           Control.Monad.Error               (Error, ErrorT, MonadError)+import           Control.Monad.Identity            (Identity) import           Control.Monad.RWS+import           Control.Monad.State import           Data.Default import           Data.List import           Data.List.Split-import           Data.Maybe-import           Data.Map              (Map)-import qualified Data.Map              as M-import           Data.Set              (Set)-import qualified Data.Set              as S+import           Data.Map                          (Map)+import           Data.Set                          (Set) import           Data.String-import           Language.Haskell.Exts--import           Fay.Compiler.ModuleScope (ModuleScope)-import           Fay.Compiler.QName-import           Paths_fay+import           Distribution.HaskellSuite.Modules+import           Language.Haskell.Exts.Annotated+import           Language.Haskell.Names            (Symbols)  -------------------------------------------------------------------------------- -- Compiler types  -- | Configuration of the compiler. data CompileConfig = CompileConfig-  { configOptimize           :: Bool                       -- ^ Run optimizations-  , configFlattenApps        :: Bool                       -- ^ Flatten function application?-  , configExportBuiltins     :: Bool                       -- ^ Export built-in functions?-  , configExportRuntime      :: Bool                       -- ^ Export the runtime?-  , configExportStdlib       :: Bool                       -- ^ Export the stdlib?-  , configExportStdlibOnly   :: Bool                       -- ^ Export /only/ the stdlib?+  { configOptimize          :: Bool                        -- ^ Run optimizations+  , configFlattenApps       :: Bool                        -- ^ Flatten function application?+  , configExportRuntime     :: Bool                        -- ^ Export the runtime?+  , configExportStdlib      :: Bool                        -- ^ Export the stdlib?+  , configExportStdlibOnly  :: Bool                        -- ^ Export /only/ the stdlib?   , configDirectoryIncludes :: [(Maybe String, FilePath)]  -- ^ Possibly a fay package name, and a include directory.-  , configPrettyPrint        :: Bool                       -- ^ Pretty print the JS output?-  , configHtmlWrapper        :: Bool                       -- ^ Output a HTML file including the produced JS.-  , configHtmlJSLibs         :: [FilePath]                 -- ^ Any JS files to link to in the HTML.-  , configLibrary            :: Bool                       -- ^ Don't invoke main in the produced JS.-  , configWarn               :: Bool                       -- ^ Warn on dubious stuff, not related to typechecking.-  , configFilePath           :: Maybe FilePath             -- ^ File path to output to.-  , configTypecheck          :: Bool                       -- ^ Typecheck with GHC.-  , configWall               :: Bool                       -- ^ Typecheck with -Wall.-  , configGClosure           :: Bool                       -- ^ Run Google Closure on the produced JS.-  , configPackageConf        :: Maybe FilePath             -- ^ The package config e.g. packages-6.12.3.-  , configPackages           :: [String]                   -- ^ Included Fay packages.-  , configBasePath           :: Maybe FilePath             -- ^ Custom source location for fay-base+  , configPrettyPrint       :: Bool                        -- ^ Pretty print the JS output?+  , configHtmlWrapper       :: Bool                        -- ^ Output a HTML file including the produced JS.+  , configHtmlJSLibs        :: [FilePath]                  -- ^ Any JS files to link to in the HTML.+  , configLibrary           :: Bool                        -- ^ Don't invoke main in the produced JS.+  , configWarn              :: Bool                        -- ^ Warn on dubious stuff, not related to typechecking.+  , configFilePath          :: Maybe FilePath              -- ^ File path to output to.+  , configTypecheck         :: Bool                        -- ^ Typecheck with GHC.+  , configWall              :: Bool                        -- ^ Typecheck with -Wall.+  , configGClosure          :: Bool                        -- ^ Run Google Closure on the produced JS.+  , configPackageConf       :: Maybe FilePath              -- ^ The package config e.g. packages-6.12.3.+  , configPackages          :: [String]                    -- ^ Included Fay packages.+  , configBasePath          :: Maybe FilePath              -- ^ Custom source location for fay-base+  , configStrict            :: [String]                    -- ^ Produce strict and uncurried wrappers for all functions with type signatures in the given module+  , configTypecheckOnly     :: Bool                        -- ^ Only invoke GHC for typechecking, don't produce any output+  , configRuntimePath       :: Maybe FilePath   } deriving (Show)  -- | The name of a module split into a list for code generation.@@ -92,41 +82,40 @@   deriving (Eq, Ord, Show)  -- | Construct the complete ModulePath from a ModuleName.-mkModulePath :: ModuleName -> ModulePath-mkModulePath (ModuleName m) = ModulePath . splitOn "." $ m+mkModulePath :: ModuleName a -> ModulePath+mkModulePath (ModuleName _ m) = ModulePath . splitOn "." $ m  -- | Construct intermediate module paths from a ModuleName. -- mkModulePaths "A.B" => [["A"], ["A","B"]]-mkModulePaths :: ModuleName -> [ModulePath]-mkModulePaths (ModuleName m) = map ModulePath . tail . inits . splitOn "." $ m+mkModulePaths :: ModuleName a -> [ModulePath]+mkModulePaths (ModuleName _ m) = map ModulePath . tail . inits . splitOn "." $ m  -- | Converting a QName to a ModulePath is only relevant for constructors since -- they can conflict with module names.-mkModulePathFromQName :: QName -> ModulePath-mkModulePathFromQName (Qual (ModuleName m) n) = mkModulePath $ ModuleName $ m ++ "." ++ unname n+mkModulePathFromQName :: QName a -> ModulePath+mkModulePathFromQName (Qual _ (ModuleName _ m) n) = mkModulePath $ ModuleName F.noI $ m ++ "." ++ unname n mkModulePathFromQName _ = error "mkModulePathFromQName: Not a qualified name"  -- | State of the compiler. data CompileState = CompileState-  { _stateExports      :: Map ModuleName (Set QName) -- ^ Collects exports from modules-  , stateRecordTypes   :: [(QName,[QName])]          -- ^ Map types to constructors-  , stateRecords       :: [(QName,[QName])]          -- ^ Map constructors to fields-  , stateNewtypes      :: [(QName, Maybe QName, Type)] -- ^ Newtype constructor, destructor, wrapped type tuple-  , stateImported      :: [(ModuleName,FilePath)]    -- ^ Map of all imported modules and their source locations.-  , stateNameDepth     :: Integer                    -- ^ Depth of the current lexical scope.-  , stateLocalScope    :: Set Name                   -- ^ Names in the current lexical scope.-  , stateModuleScope   :: ModuleScope                -- ^ Names in the module scope.-  , stateModuleScopes  :: Map ModuleName ModuleScope-  , stateModuleName    :: ModuleName                 -- ^ Name of the module currently being compiled.-  , stateJsModulePaths :: Set ModulePath-  , stateUseFromString :: Bool+  -- TODO Change N.QName to GName? They can never be special so it would simplify.+  { stateInterfaces    :: Map N.ModuleName Symbols           -- ^ Exported identifiers for all modules+  , stateRecordTypes   :: [(N.QName,[N.QName])]              -- ^ Map types to constructors+  , stateRecords       :: [(N.QName,[N.Name])]               -- ^ Map constructors to fields+  , stateNewtypes      :: [(N.QName, Maybe N.QName, N.Type)] -- ^ Newtype constructor, destructor, wrapped type tuple+  , stateImported      :: [(N.ModuleName,FilePath)]          -- ^ Map of all imported modules and their source locations.+  , stateNameDepth     :: Integer                            -- ^ Depth of the current lexical scope, used for creating unshadowing variables.+  , stateModuleName    :: N.ModuleName                       -- ^ Name of the module currently being compiled.+  , stateJsModulePaths :: Set ModulePath                     -- ^ Module paths that have code generated for them.+  , stateUseFromString :: Bool                               -- ^ Use JS Strings instead of [Char] for string literals?+  , stateTypeSigs      :: Map N.QName N.Type                 -- ^ Module level declarations having explicit type signatures   } deriving (Show)  -- | Things written out by the compiler. data CompileWriter = CompileWriter-  { writerCons     :: [JsStmt] -- ^ Constructors.-  , writerFayToJs  :: [(String,JsExp)] -- ^ Fay to JS dispatchers.-  , writerJsToFay  :: [(String,JsExp)] -- ^ JS to Fay dispatchers.+  { writerCons    :: [JsStmt]         -- ^ Constructors.+  , writerFayToJs :: [(String,JsExp)] -- ^ Fay to JS dispatchers.+  , writerJsToFay :: [(String,JsExp)] -- ^ JS to Fay dispatchers.   }   deriving (Show) @@ -139,57 +128,17 @@ -- | Configuration and globals for the compiler. data CompileReader = CompileReader   { readerConfig       :: CompileConfig -- ^ The compilation configuration.-  , readerCompileLit   :: Literal -> Compile JsExp-  , readerCompileDecls :: Bool -> [Decl] -> Compile [JsStmt]+  , readerCompileLit   :: S.Literal -> Compile JsExp+  , readerCompileDecls :: Bool -> [S.Decl] -> Compile [JsStmt]   } --- | The data-files source directory.-faySourceDir :: IO FilePath-faySourceDir = getDataFileName "src/"---- | Add a ModulePath to CompileState, meaning it has been printed.-addModulePath :: ModulePath -> CompileState -> CompileState-addModulePath mp cs = cs { stateJsModulePaths = mp `S.insert` stateJsModulePaths cs }---- | Has this ModulePath been added/printed?-addedModulePath :: ModulePath -> CompileState -> Bool-addedModulePath mp CompileState{..} = mp `S.member` stateJsModulePaths---- | Adds a new export to '_stateExports' for the module specified by--- 'stateModuleName'.-addCurrentExport :: QName -> CompileState -> CompileState-addCurrentExport q cs =-    cs { _stateExports = M.insert (stateModuleName cs) qnames $ _stateExports cs}-  where-    qnames = maybe (S.singleton q) (S.insert q)-           $ M.lookup (stateModuleName cs) (_stateExports cs)---- | Get all exports for the current module.-getCurrentExports :: CompileState -> Set QName-getCurrentExports cs = getExportsFor (stateModuleName cs) cs---- | Get exports from the current module originating from other modules.-getNonLocalExports :: CompileState -> Set QName-getNonLocalExports st = S.filter ((/= Just (stateModuleName st)) . qModName) . getCurrentExportsWithoutNewtypes $ st---- | Get all exports from the current module except newtypes.-getCurrentExportsWithoutNewtypes :: CompileState -> Set QName-getCurrentExportsWithoutNewtypes cs = excludeNewtypes cs $ getCurrentExports cs-  where-    excludeNewtypes :: CompileState -> Set QName -> Set QName-    excludeNewtypes cs' names =-      let newtypes = stateNewtypes cs'-          constrs = map (\(c, _, _) -> c) newtypes-          destrs  = map (\(_, d, _) -> fromJust d) . filter (\(_, d, _) -> isJust d) $ newtypes-       in names `S.difference` (S.fromList constrs `S.union` S.fromList destrs)---- | Get all of the exported identifiers for the given module.-getExportsFor :: ModuleName -> CompileState -> Set QName-getExportsFor mn cs = fromMaybe S.empty $ M.lookup mn (_stateExports cs)- -- | Compile monad. newtype Compile a = Compile-  { unCompile :: RWST CompileReader CompileWriter CompileState (ErrorT CompileError IO) a -- ^ Run the compiler.+  { unCompile :: (RWST CompileReader+                      CompileWriter+                      CompileState+                      (ErrorT CompileError (ModuleT (ModuleInfo Compile) IO)))+                   a -- ^ Uns the compiler   }   deriving (MonadState CompileState            ,MonadError CompileError@@ -198,13 +147,28 @@            ,MonadIO            ,Monad            ,Functor-           ,Applicative)+           ,Applicative+           ) --- | Just a convenience class to generalize the parsing/printing of--- various types of syntax.-class (Parseable from,Printable to) => CompilesTo from to | from -> to where-  compileTo :: from -> Compile to+type CompileResult a+  = Either CompileError+           (a, CompileState, CompileWriter) +type CompileModule a+  = ModuleT Symbols+            IO+            (CompileResult a)++instance MonadModule Compile where+  type ModuleInfo Compile = Symbols+  lookupInCache        = liftModuleT . lookupInCache+  insertInCache n m    = liftModuleT $ insertInCache n m+  getPackages          = liftModuleT $ getPackages+  readModuleInfo fps n = liftModuleT $ readModuleInfo fps n++liftModuleT :: ModuleT Symbols IO a -> Compile a+liftModuleT = Compile . lift . lift+ -- | A source mapping. data Mapping = Mapping   { mappingName :: String -- ^ The name of the mapping.@@ -214,13 +178,13 @@  -- | The state of the pretty printer. data PrintState = PrintState-  { psPretty       :: Bool      -- ^ Are we to pretty print?-  , psLine         :: Int       -- ^ The current line.-  , psColumn       :: Int       -- ^ Current column.-  , psMapping      :: [Mapping] -- ^ Source mappings.-  , psIndentLevel  :: Int       -- ^ Current indentation level.-  , psOutput       :: [String]  -- ^ The current output. TODO: Make more efficient.-  , psNewline      :: Bool      -- ^ Just outputted a newline?+  { psPretty      :: Bool      -- ^ Are we to pretty print?+  , psLine        :: Int       -- ^ The current line.+  , psColumn      :: Int       -- ^ Current column.+  , psMapping     :: [Mapping] -- ^ Source mappings.+  , psIndentLevel :: Int       -- ^ Current indentation level.+  , psOutput      :: [String]  -- ^ The current output. TODO: Make more efficient.+  , psNewline     :: Bool      -- ^ Just outputted a newline?   }  -- | Default state.@@ -237,32 +201,33 @@  -- | Error type. data CompileError-  = ParseError SrcLoc String-  | UnsupportedDeclaration Decl-  | UnsupportedExportSpec ExportSpec-  | UnsupportedExpression Exp-  | UnsupportedFieldPattern PatField-  | UnsupportedImport ImportDecl-  | UnsupportedLet-  | UnsupportedLetBinding Decl-  | UnsupportedLiteral Literal-  | UnsupportedModuleSyntax Module-  | UnsupportedPattern Pat-  | UnsupportedQualStmt QualStmt-  | UnsupportedRecursiveDo-  | UnsupportedRhs Rhs-  | UnsupportedWhereInAlt Alt-  | UnsupportedWhereInMatch Match+  = Couldn'tFindImport N.ModuleName [FilePath]   | EmptyDoBlock-  | InvalidDoBlock-  | Couldn'tFindImport ModuleName [FilePath]-  | FfiNeedsTypeSig Decl-  | FfiFormatBadChars SrcLoc String-  | FfiFormatNoSuchArg SrcLoc Int-  | FfiFormatIncompleteArg SrcLoc-  | FfiFormatInvalidJavaScript SrcLoc String String-  | UnableResolveQualified QName+  | FfiFormatBadChars SrcSpanInfo String+  | FfiFormatIncompleteArg SrcSpanInfo+  | FfiFormatInvalidJavaScript SrcSpanInfo String String+  | FfiFormatNoSuchArg SrcSpanInfo Int+  | FfiNeedsTypeSig S.Decl   | GHCError String+  | InvalidDoBlock+  | ParseError S.SrcLoc String+  | ShouldBeDesugared String+  | UnableResolveQualified N.QName+  | UnsupportedDeclaration S.Decl+  | UnsupportedExportSpec N.ExportSpec+  | UnsupportedExpression S.Exp+  | UnsupportedFieldPattern S.PatField+  | UnsupportedImport F.ImportDecl+  | UnsupportedLet+  | UnsupportedLetBinding S.Decl+  | UnsupportedLiteral S.Literal+  | UnsupportedModuleSyntax String F.Module+  | UnsupportedPattern S.Pat+  | UnsupportedQualStmt S.QualStmt+  | UnsupportedRecursiveDo+  | UnsupportedRhs S.Rhs+  | UnsupportedWhereInAlt S.Alt+  | UnsupportedWhereInMatch S.Match   deriving (Show) instance Error CompileError @@ -283,9 +248,9 @@   | JsWhile JsExp [JsStmt]   | JsUpdate JsName JsExp   | JsSetProp JsName JsName JsExp-  | JsSetQName QName JsExp+  | JsSetQName N.QName JsExp   | JsSetModule ModulePath JsExp-  | JsSetConstructor QName JsExp+  | JsSetConstructor N.QName JsExp   | JsSetPropExtern JsName JsName JsExp   | JsContinue   | JsBlock [JsStmt]@@ -318,7 +283,7 @@   | JsNeq JsExp JsExp   | JsInfix String JsExp JsExp -- Used to optimize *, /, +, etc   | JsObj [(String,JsExp)]-  | JsLitObj [(Name,JsExp)]+  | JsLitObj [(N.Name,JsExp)]   | JsUndefined   | JsAnd JsExp JsExp   | JsOr  JsExp JsExp@@ -326,7 +291,7 @@  -- | A name of some kind. data JsName-  = JsNameVar QName+  = JsNameVar N.QName   | JsThis   | JsParametrizedType   | JsThunk@@ -334,9 +299,9 @@   | JsApply   | JsParam Integer   | JsTmp Integer-  | JsConstructor QName-  | JsBuiltIn Name-  | JsModuleName ModuleName+  | JsConstructor N.QName+  | JsBuiltIn N.Name+  | JsModuleName N.ModuleName   deriving (Eq,Show)  -- | Literal value type.@@ -362,7 +327,7 @@  | JsType FundamentalType  | ListType FundamentalType  | TupleType [FundamentalType]- | UserDefined Name [FundamentalType]+ | UserDefined N.Name [FundamentalType]  | Defined FundamentalType  | Nullable FundamentalType  -- Simple types.@@ -377,18 +342,6 @@  -- Unknown.  | UnknownType    deriving (Show)---- | Helpful for some things.-instance IsString Name where-  fromString = Ident---- | Helpful for some things.-instance IsString QName where-  fromString = UnQual . Ident---- | Helpful for writing qualified symbols (Fay.*).-instance IsString ModuleName where-  fromString = ModuleName  -- | The serialization context indicates whether we're currently -- serializing some value or a particular field in a user-defined data
src/main/Main.hs view
@@ -1,51 +1,48 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE RecordWildCards  #-}-{-# OPTIONS -fno-warn-orphans #-} -- | Main compiler executable.  module Main where  import           Fay-import           Fay.Compiler import           Fay.Compiler.Config-import           Fay.Compiler.Debug-import           Fay.Control.Monad.IO+import           Paths_fay           (version) -import qualified Control.Exception        as E+import qualified Control.Exception   as E import           Control.Monad import           Data.Default-import           Data.List.Split          (wordsBy)+import           Data.List.Split     (wordsBy) import           Data.Maybe-import           Data.Version             (showVersion)+import           Data.Version        (showVersion) import           Options.Applicative-import           Paths_fay                (version)-import           System.Console.Haskeline import           System.Environment  -- | Options and help. data FayCompilerOptions = FayCompilerOptions-  { optLibrary      :: Bool-  , optFlattenApps  :: Bool-  , optHTMLWrapper  :: Bool-  , optHTMLJSLibs   :: [String]-  , optInclude      :: [String]-  , optPackages     :: [String]-  , optWall         :: Bool-  , optNoGHC        :: Bool-  , optStdout       :: Bool-  , optVersion      :: Bool-  , optOutput       :: Maybe String-  , optPretty       :: Bool-  , optFiles        :: [String]-  , optOptimize     :: Bool-  , optGClosure     :: Bool-  , optPackageConf  :: Maybe String-  , optNoRTS        :: Bool-  , optNoStdlib     :: Bool-  , optPrintRuntime :: Bool-  , optStdlibOnly   :: Bool-  , optNoBuiltins   :: Bool-  , optBasePath     :: Maybe FilePath+  { optLibrary       :: Bool+  , optFlattenApps   :: Bool+  , optHTMLWrapper   :: Bool+  , optHTMLJSLibs    :: [String]+  , optInclude       :: [String]+  , optPackages      :: [String]+  , optWall          :: Bool+  , optNoGHC         :: Bool+  , optStdout        :: Bool+  , optVersion       :: Bool+  , optOutput        :: Maybe String+  , optPretty        :: Bool+  , optFiles         :: [String]+  , optOptimize      :: Bool+  , optGClosure      :: Bool+  , optPackageConf   :: Maybe String+  , optNoRTS         :: Bool+  , optNoStdlib      :: Bool+  , optPrintRuntime  :: Bool+  , optStdlibOnly    :: Bool+  , optBasePath      :: Maybe FilePath+  , optStrict        :: [String]+  , optTypecheckOnly :: Bool+  , optRuntimePath   :: Maybe FilePath   }  -- | Main entry point.@@ -53,35 +50,36 @@ main = do   packageConf <- fmap (lookup "HASKELL_PACKAGE_SANDBOX") getEnvironment   opts <- execParser parser+  let config = addConfigDirectoryIncludePaths ("." : optInclude opts) $+        addConfigPackages (optPackages opts) $ def+          { configOptimize         = optOptimize opts+          , configFlattenApps      = optFlattenApps opts+          , configPrettyPrint      = optPretty opts+          , configLibrary          = optLibrary opts+          , configHtmlWrapper      = optHTMLWrapper opts+          , configHtmlJSLibs       = optHTMLJSLibs opts+          , configTypecheck        = not $ optNoGHC opts+          , configWall             = optWall opts+          , configGClosure         = optGClosure opts+          , configPackageConf      = optPackageConf opts <|> packageConf+          , configExportRuntime    = not (optNoRTS opts)+          , configExportStdlib     = not (optNoStdlib opts)+          , configExportStdlibOnly = optStdlibOnly opts+          , configBasePath         = optBasePath opts+          , configStrict           = optStrict opts+          , configTypecheckOnly    = optTypecheckOnly opts+          , configRuntimePath      = optRuntimePath opts+          }   if optVersion opts     then runCommandVersion     else if optPrintRuntime opts-      then getRuntime >>= readFile >>= putStr+      then getConfigRuntime config >>= readFile >>= putStr       else do-        let config = addConfigDirectoryIncludePaths ("." : optInclude opts) $-              addConfigPackages (optPackages opts) $ def-                { configOptimize         = optOptimize opts-                , configFlattenApps      = optFlattenApps opts-                , configExportBuiltins   = not (optNoBuiltins opts)-                , configPrettyPrint      = optPretty opts-                , configLibrary          = optLibrary opts-                , configHtmlWrapper      = optHTMLWrapper opts-                , configHtmlJSLibs       = optHTMLJSLibs opts-                , configTypecheck        = not $ optNoGHC opts-                , configWall             = optWall opts-                , configGClosure         = optGClosure opts-                , configPackageConf      = optPackageConf opts <|> packageConf-                , configExportRuntime    = not (optNoRTS opts)-                , configExportStdlib     = not (optNoStdlib opts)-                , configExportStdlibOnly = optStdlibOnly opts-                , configBasePath         = optBasePath opts-                }         void $ incompatible htmlAndStdout opts "Html wrapping and stdout are incompatible"         case optFiles opts of-             ["-"] -> getContents >>= printCompile config compileModuleFromAST-             []    -> runInteractive-             files -> forM_ files $ \file ->-               compileFromTo config file (if optStdout opts then Nothing else Just (outPutFile opts file))+          []    -> putStrLn $ helpTxt ++ "\n  More information: fay --help"+          files -> forM_ files $ \file ->+            compileFromTo config file (if optStdout opts then Nothing else Just (outPutFile opts file))    where     parser = info (helper <*> options) (fullDesc <> header helpTxt)@@ -107,7 +105,7 @@   <*> switch (long "version" <> help "Output version number")   <*> optional (strOption (long "output" <> short 'o' <> metavar "file" <> help "Output to specified file"))   <*> switch (long "pretty" <> short 'p' <> help "Pretty print the output")-  <*> arguments Just (metavar "- | <hs-file>...")+  <*> arguments Just (metavar "<hs-file>...")   <*> switch (long "optimize" <> short 'O' <> help "Apply optimizations to generated code")   <*> switch (long "closure" <> help "Provide help with Google Closure")   <*> optional (strOption (long "package-conf" <> help "Specify the Cabal package config file"))@@ -115,8 +113,11 @@   <*> switch (long "no-stdlib" <> help "Don't generate code for the Prelude/FFI")   <*> switch (long "print-runtime" <> help "Print the runtime JS source to stdout")   <*> switch (long "stdlib" <> help "Only output the stdlib")-  <*> switch (long "no-builtins" <> help "Don't export no-builtins")-  <*> optional (strOption (long "base-path" <> help "If fay can't find the sources of fay-base you can use this to provide the path. Use --base-path ~/example instead of --base-path=~/example to make sure ~ is expanded properly"))+  <*> optional (strOption $ long "base-path" <> help "If fay can't find the sources of fay-base you can use this to provide the path. Use --base-path ~/example instead of --base-path=~/example to make sure ~ is expanded properly")+  <*> strsOption (long "strict" <> metavar "modulename[, ..]"+      <> help "Generate strict and uncurried exports for the supplied modules. Simplifies calling Fay from JS")+  <*> switch (long "typecheck-only" <> help "Only invoke GHC for typechecking, don't produce any output")+  <*> optional (strOption $ long "runtime-path" <> help "Custom path to the runtime so you don't have to reinstall fay when modifying it")    where strsOption m =           nullOption (m <> reader (Right . wordsBy (== ',')) <> value [])@@ -134,9 +135,6 @@ helpTxt :: String helpTxt = concat   ["fay -- The fay compiler from (a proper subset of) Haskell to Javascript\n\n"-  ,"SYNOPSIS\n"-  ,"  fay [OPTIONS] [- | <hs-file>...]\n"-  ,"  fay - takes input on stdin and prints to stdout. Pretty prints\n"   ,"  fay <hs-file>... processes each .hs file"   ] @@ -147,27 +145,3 @@ -- | Incompatible options. htmlAndStdout :: FayCompilerOptions -> Bool htmlAndStdout opts = optHTMLWrapper opts && optStdout opts---- | Run interactively.-runInteractive :: IO ()-runInteractive = runInputT defaultSettings loop where-  loop = do-    minput <- getInputLine "> "-    case minput of-      Nothing -> return ()-      Just "" -> loop-      Just input -> do-        result <- io $ compileViaStr "<interactive>" config compileExp input-        case result of-          Left err -> do-            -- an error occured, maybe input was not an expression,-            -- but a declaration, try compiling the input as a declaration-            outputStrLn $ "can't parse input as expression: " ++ show err-            result' <- io $ compileViaStr "<interactive>" config (compileDecl True) input-            case result' of-              Right (PrintState{..},_,_) -> outputStr (concat (reverse psOutput))-              Left err' ->-                outputStrLn $ "can't parse input as declaration: " ++ show err'-          Right (PrintState{..},_,_) -> outputStr (concat (reverse psOutput))-        loop-  config = def { configPrettyPrint = True }
src/tests/Test/CommandLine.hs view
@@ -2,7 +2,7 @@  module Test.CommandLine (tests) where -import Fay.System.Process.Extra+import           Fay.System.Process.Extra  import           Control.Applicative import           Data.Maybe
src/tests/Test/Compile.hs view
@@ -1,20 +1,26 @@-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards   #-}+{-# LANGUAGE TemplateHaskell   #-}  module Test.Compile (tests) where -import Fay-import Fay.Compiler.Config+import           Fay+import           Fay.Compiler.Config+import           Fay.System.Process.Extra+import           Fay.Types                       () -import Data.Default-import Data.Maybe-import Language.Haskell.Exts.Syntax-import System.Environment-import Test.Framework-import Test.Framework.Providers.HUnit-import Test.Framework.TH-import Test.HUnit                     (Assertion, assertBool, assertEqual, assertFailure)-import Test.Util+import           Control.Applicative+import           Control.Monad+import           Data.Default+import           Data.Maybe+import           Language.Haskell.Exts.Annotated+import           System.Environment+import           Test.Framework+import           Test.Framework.Providers.HUnit+import           Test.Framework.TH+import           Test.HUnit                      (Assertion, assertBool,+                                                  assertEqual, assertFailure)+import           Test.Util  tests :: Test tests = $testGroupGenerator@@ -32,7 +38,7 @@   case res of     Left err -> error (show err)     Right (_,r) -> assertBool "RecordImport_Export was not added to stateImported" .-                     isJust . lookup (ModuleName "RecordImport_Export") $ stateImported r+                     isJust . lookup (ModuleName () "RecordImport_Export") $ stateImported r  fp :: FilePath fp = "tests/RecordImport_Import.hs"@@ -46,8 +52,8 @@     Right (_,r) ->       -- TODO order should not matter       assertEqual "stateRecordTypes mismatch"-        [ (UnQual (Ident "T"),[UnQual (Symbol ":+")])-        , (UnQual (Ident "R"),[UnQual (Ident "R"), UnQual (Ident "S")])+        [ ("Compile.Records.T", ["Compile.Records.:+"])+        , ("Compile.Records.R", ["Compile.Records.R","Compile.Records.S"])         ]         (stateRecordTypes r) @@ -60,8 +66,8 @@     Right (_,r) ->       -- TODO order should not matter       assertEqual "stateRecordTypes mismatch"-        [ (UnQual (Ident "T"),[UnQual (Symbol ":+")])-        , (UnQual (Ident "R"),[UnQual (Ident "R"), UnQual (Ident "S")])+        [ ("Compile.Records.T",["Compile.Records.:+"])+        , ("Compile.Records.R",["Compile.Records.R", "Compile.Records.S"])         ]         (stateRecordTypes r) @@ -76,6 +82,17 @@   whatAGreatFramework <- fmap (lookup "HASKELL_PACKAGE_SANDBOX") getEnvironment   res <- compileFile defConf { configPackageConf = whatAGreatFramework, configTypecheck = True, configFilePath = Just "tests/Compile/CPPMultiLineStrings.hs" } "tests/Compile/CPPMultiLineStrings.hs"   either (assertFailure . show) (const $ return ()) res++case_strictWrapper :: Assertion+case_strictWrapper = do+  whatAGreatFramework <- fmap (lookup "HASKELL_PACKAGE_SANDBOX") getEnvironment+  res <- compileFile defConf { configPackageConf = whatAGreatFramework, configTypecheck = True, configFilePath = Just "tests/Compile/StrictWrapper.hs", configStrict = ["StrictWrapper"] } "tests/Compile/StrictWrapper.hs"+  (\a b -> either a b res) (assertFailure . show) $ \js -> do+    writeFile "tests/Compile/StrictWrapper.js" js+    (err, out) <- either id id <$> readAllFromProcess "node" ["tests/Compile/StrictWrapper.js"] ""+    when (err /= "") $ assertFailure err+    expected <- readFile "tests/Compile/StrictWrapper.res"+    assertEqual "strictWrapper node stdout" expected out  defConf :: CompileConfig defConf = addConfigDirectoryIncludePaths ["tests/"]
src/tests/Test/Util.hs view
@@ -7,8 +7,8 @@ import           Fay.System.Process.Extra (readAllFromProcess)  import           Control.Applicative-import           System.Directory import           Prelude                  hiding (pred)+import           System.Directory  -- Path to the fay executable, looks in cabal-dev, dist, PATH in that order. fayPath :: IO (Maybe FilePath)
src/tests/Tests.hs view
@@ -19,8 +19,8 @@ import           System.Directory import           System.Environment import           System.FilePath-import qualified Test.Compile                   as Compile import qualified Test.CommandLine               as Cmd+import qualified Test.Compile                   as Compile import qualified Test.Convert                   as C import           Test.Framework import           Test.Framework.Providers.HUnit@@ -43,7 +43,7 @@ -- | Make the case-by-case unit tests. makeCompilerTests :: Maybe FilePath -> Maybe FilePath -> IO Test makeCompilerTests packageConf basePath = do-  files <- sort . filter (not . isInfixOf "/Compile/") . filter (isSuffixOf ".hs") <$> getRecursiveContents "tests"+  files <- sort . filter (\v -> not (isInfixOf "/Compile/" v) && not (isInfixOf "/regressions/" v)) . filter (isSuffixOf ".hs") <$> getRecursiveContents "tests"   return $ testGroup "Tests" $ flip map files $ \file -> testCase file $ do     testFile packageConf basePath False file     testFile packageConf basePath True file@@ -61,7 +61,7 @@               , configBasePath = basePath               }   resExists <- doesFileExist resf-  let partialName = root ++ "_partial"+  let partialName = root ++ "_partial.res"   partialExists <- doesFileExist partialName   compileFromTo config file (Just out)   result <- runJavaScriptFile out
+ tests/Compile/StrictWrapper.hs view
@@ -0,0 +1,21 @@+module StrictWrapper (f,g,h) where++import           FFI+import           Prelude++f :: Int -> Int -> Int+f x y = x + y++data R = R { i :: Int }++g :: R -> Int+g R{i=i} = i++h :: R -> R+h (R i) = R (i + 1)++main :: Fay ()+main = do+  ffi "console.log(Strict.StrictWrapper.f(1,2))" :: Fay ()+  ffi "console.log(Strict.StrictWrapper.g({instance:'R',i:1}))" :: Fay ()+  ffi "console.log(Strict.StrictWrapper.h({instance:'R',i:1}))" :: Fay ()
+ tests/Compile/StrictWrapper.res view
@@ -0,0 +1,3 @@+3+1+{ instance: 'R', i: 2 }
tests/ExportQualified_Export.hs view
@@ -1,9 +1,9 @@ {-# LANGUAGE PackageImports #-} module ExportQualified_Export (main, X.X) where -import Prelude+import           Prelude -import "foo" X+import           "foo" X  main :: Fay () main = return ()
tests/FromString/FayText.hs view
@@ -3,7 +3,7 @@ -- | Module to be shared between server and client. -- -- This module must be valid for both GHC and Fay.-module FayText where+module FromString.FayText where  import           Prelude #ifdef FAY@@ -40,4 +40,3 @@  fromString :: String -> Text fromString = pack-
tests/LazyOperators.hs view
@@ -1,6 +1,8 @@+module LazyOperators where+ import           Prelude  main :: Fay () main = print testFn -testFn = let f a b = snd (a/b,10::Double) in f 1 0 -- undefined undefined+testFn = let f a b = snd (a/b,10::Double) in f (1::Double) (0::Double)
tests/List.hs view
@@ -1,11 +1,15 @@ import           FFI import           Prelude hiding (take) +main :: Fay () main = putStrLn (showList (take 5 (let ns = 1 : map' (\x -> x + 1) ns in ns))) +take :: Int -> [a] -> [a] take 0 _      = [] take n (x:xs) = x : take (n - 1) xs ++map' :: (a -> b) -> [a] -> [b] map' f []     = [] map' f (x:xs) = f x : map' f xs 
tests/List2.hs view
@@ -3,11 +3,15 @@  main = putStrLn (showList (take 5 (let ns = 1 : map' (foo 123) ns in ns))) +foo :: Double -> Double -> Double foo x y = x * y / 2 +take :: Int -> [a] -> [a] take 0 _      = [] take n (x:xs) = x : take (n - 1) xs ++map' :: (a -> b) -> [a] -> [b] map' f []     = [] map' f (x:xs) = f x : map' f xs 
tests/Monad2.hs view
@@ -1,6 +1,6 @@-{-# LANGUAGE EmptyDataDecls    #-}+{-# LANGUAGE EmptyDataDecls #-} -{-# LANGUAGE RankNTypes        #-}+{-# LANGUAGE RankNTypes     #-}  -- | Monads test. 
tests/Nullable.hs view
@@ -7,7 +7,7 @@ main = do   printD $ Nullable (1 :: Double)   printNS $ Nullable "Hello, World!"-  printSS $ Nullable ["Hello,","World!"]+  printSS $ Defined ["Hello,","World!"]   printD $ (Null :: Nullable Double)   print' $ R (Nullable 1)   print' $ R Null
tests/Num.hs view
@@ -1,9 +1,11 @@-import Prelude+module Num where +import           Prelude+ main = do-  print (1 + 2)-  print (4 - 1)-  print (3 * 1)-  print (negate (1 - 4))-  print (abs (1 - 4))-  print ((-3) * signum (-10))+  print (1 + 2::Int)+  print (4 - 1::Int)+  print (3 * 1::Int)+  print (negate (1 - 4::Int))+  print (abs (1 - 4::Int))+  print ((-3) * signum (-10::Int))
tests/Ord.hs view
@@ -1,21 +1,23 @@-import Prelude+module Ord where +import           Prelude+ main = do-  when (1 < 2) $ putStrLn "Expected <"-  when (1 < 1) $ putStrLn "Unexpected < (1)"-  when (2 < 1) $ putStrLn "Unexpected < (2)"-  when (1 >= 2) $ putStrLn "Unexpected >="-  when (1 >= 1) $ putStrLn "Expected >= (1)"-  when (2 >= 1) $ putStrLn "Expected >= (2)"-  when (1 > 2) $ putStrLn "Unexpected > (1)"-  when (1 > 1) $ putStrLn "Unexpected > (2)"-  when (2 > 1) $ putStrLn "Expected >"-  when (1 <= 2) $ putStrLn "Expected <= (1)"-  when (1 <= 1) $ putStrLn "Expected <= (2)"-  when (2 <= 1) $ putStrLn "Unexpected <="-  print $ max 1 2-  print $ min 1 2-  case compare 1 2 of+  when ((1::Int) < 2) $ putStrLn "Expected <"+  when ((1::Int) < 1) $ putStrLn "Unexpected < (1)"+  when ((2::Int) < 1) $ putStrLn "Unexpected < (2)"+  when ((1::Int) >= 2) $ putStrLn "Unexpected >="+  when ((1::Int) >= 1) $ putStrLn "Expected >= (1)"+  when ((2::Int) >= 1) $ putStrLn "Expected >= (2)"+  when ((1::Int) > 2) $ putStrLn "Unexpected > (1)"+  when ((1::Int) > 1) $ putStrLn "Unexpected > (2)"+  when ((2::Int) > 1) $ putStrLn "Expected >"+  when ((1::Int) <= 2) $ putStrLn "Expected <= (1)"+  when ((1::Int) <= 1) $ putStrLn "Expected <= (2)"+  when ((2::Int) <= 1) $ putStrLn "Unexpected <="+  print $ max 1 (2::Int)+  print $ min 1 (2::Int)+  case compare 1 (2::Int) of     EQ -> putStrLn "FAIL (EQ)"     LT -> putStrLn "WIN (LT)"     GT -> putStrLn "FAIL (GT)"
+ tests/QualifiedImport.hs view
@@ -0,0 +1,18 @@+module QualifiedImport where++import           FFI+import           Prelude++import qualified QualifiedImport.X+import qualified QualifiedImport.X as X+import qualified QualifiedImport.Y as Y++main :: Fay ()+main = do+  print QualifiedImport.X.x+  print Y.y+  print X.X3 { X.x4 = 1 }+  print $ X.X3 2+  print (X.X3 3) { X.x4 = 4 }+  case X.X3 4 of+    X.X3 { X.x4 = n } -> print n
+ tests/QualifiedImport.res view
@@ -0,0 +1,6 @@+1+2+{ instance: 'X3', x4: 1 }+{ instance: 'X3', x4: 2 }+{ instance: 'X3', x4: 4 }+4
+ tests/QualifiedImport/X.hs view
@@ -0,0 +1,8 @@+module QualifiedImport.X where++import           Prelude++x :: Double+x = 1++data X2 = X3 { x4 :: Double }
+ tests/QualifiedImport/X.res view
+ tests/QualifiedImport/Y.hs view
@@ -0,0 +1,6 @@+module QualifiedImport.Y where++import           Prelude++y :: Double+y = 2
+ tests/QualifiedImport/Y.res view
+ tests/ReExportGlobally.hs view
@@ -0,0 +1,14 @@+module ReExportGlobally (main, x) where++import           FFI+import           Prelude++import           ReExportGlobally.A (x)++main :: Fay ()+main = do+  ffi "console.log(ReExportGlobally.x)" :: Fay () -- Re-export to JS+  ffi "console.log('NewTy' in ReExportGlobally.A)" :: Fay () -- Don't add exports for new types+  ffi "console.log('NewTy' in ReExportGlobally)" :: Fay () -- Don't add exports for new types+  ffi "console.log('unNewTy' in ReExportGlobally.A)" :: Fay () -- Don't add exports for new types+  ffi "console.log('unNewTy' in ReExportGlobally)" :: Fay () -- Don't add exports for new types
+ tests/ReExportGlobally.res view
@@ -0,0 +1,5 @@+1+false+false+false+false
+ tests/ReExportGlobally/A.hs view
@@ -0,0 +1,8 @@+module ReExportGlobally.A (x, NewTy(..)) where++import           Prelude++x :: Double+x = 1++newtype NewTy = NewTy { unNewTy :: Double }
+ tests/ReExportGlobally/A.res view
+ tests/ReExportGloballyExplicit.hs view
@@ -0,0 +1,9 @@+module Foo (main, x) where++import           FFI+import           Prelude++import           ReExportGlobally.A (x)++main :: Fay ()+main = ffi "console.log(Foo.x)"
+ tests/ReExportGloballyExplicit.res view
@@ -0,0 +1,1 @@+1
tests/T190_B.hs view
@@ -1,6 +1,7 @@ module T190_B where -import T190_A+import           Prelude+import           T190_A  main :: Fay () main = return ()
+ tests/automatic.hs view
@@ -0,0 +1,22 @@+import Prelude+import FFI++func :: Bool -> Int -> Int -> Int+func a b c = if a then b else c++semiAutomatic :: Ptr (Bool -> Int -> Int -> Int) -> Bool -> Int+semiAutomatic = ffi "(function () { return Fay$$fayToJs(['function', 'automatic_function'], %1)(%2, 1, 2); })()"++automatic :: Ptr (Bool -> Int -> Int -> Int) -> Bool -> Int+automatic = ffi "(function () { return Fay$$fayToJs(['automatic'], %1)(%2, 1, 2); })()"++print' :: Ptr a -> Fay ()+print' = ffi "console.log(Fay$$_(%1))"++main :: Fay ()+main = do+  print' (semiAutomatic func True)+  print' (semiAutomatic func False)+  print' (automatic func True)+  print' (automatic func False)+
+ tests/automatic.res view
@@ -0,0 +1,4 @@+1+2+1+2
tests/circular.hs view
@@ -1,7 +1,8 @@-import Prelude+module Circular where +import           Prelude+ main :: Fay ()-main = let y = x + 1+main = let y = x + 1::Double            x = y + 1        in print y-
tests/ffimunging.hs view
@@ -2,8 +2,8 @@  module Maybe where -import FFI-import Prelude+import           FFI+import           Prelude  data Munge a b = Fudge a b @@ -19,7 +19,7 @@   case munge (Foo (Fudge ["a","b"] [1,2])) of     Foo (Fudge xs is) -> do printS xs                             printI is-  case sponge Fudge ["a","b"] [1,2] of+  case sponge (Fudge ["a","b"] [1,2]) of     Fudge xs is -> do printS xs                       printI is 
+ tests/fromIntegral.hs view
@@ -0,0 +1,4 @@+import           Prelude++main :: Fay ()+main = putStrLn $ show $ fromIntegral 5
+ tests/fromIntegral.res view
@@ -0,0 +1,1 @@+5
tests/guards.hs view
@@ -1,9 +1,11 @@-import Prelude+import           Prelude -f n | n <= 0 = 0+f :: Int -> Int+f n | n <=  0 = 0     | n >= 10 = 11 f n          = n + 1 +g :: Int -> Int g n = case n of   n | n <= 0 -> 0     | n >= 10 -> 11@@ -17,4 +19,3 @@   print $ g (-1)   print $ g 12   print $ g 1-
tests/listComprehensions.hs view
@@ -1,4 +1,4 @@-import Prelude+import           Prelude  main :: Fay ()-main = putStrLn $ show $ sum [ x*x | x <- [1, 2, 3, 4, 5], let y = x + 4, y < 8]+main = putStrLn $ show $ sum [ x*x | x <- [1::Int, 2, 3, 4, 5], let y = x + 4, y < 8]
tests/negation.hs view
@@ -1,9 +1,11 @@-import Prelude+import           Prelude -main = do print $ (-7/2)-          print $ (-7)/2-          print $ -f x/y+print' :: Double -> Fay ()+print' = print++main = do print' $ (-7/2)+          print' $ (-7)/2+          print' $ -f x/y      where f n = n * n            x = 5            y = 2-
tests/pats.hs view
@@ -1,9 +1,10 @@-import Prelude+import           Prelude  main :: Fay ()-main =-      case [1,2] of-        []    -> putStrLn "got []"-        [a]   -> putStrLn "got one value."-        [a,b] -> putStrLn "got two values."-+main = do+  case [1,2] of+    []    -> putStrLn "got []"+    [a]   -> putStrLn "got one value."+    [a,b] -> putStrLn "got two values."+  case [1,2] of+    (([1,2])) -> putStrLn "parens"
tests/pats.res view
@@ -1,1 +1,2 @@ got two values.+parens
tests/patternGuards.hs view
@@ -2,21 +2,24 @@  -- | As pattern matches -import           Prelude import           FFI+import           Prelude  isPositive :: Double -> Bool isPositive x | x > 0 = True              | x <= 0 = False +threeConds :: Double -> Double threeConds x | x > 1 = 2              | x == 1 = 1              | x < 1 = 0 +withOtherwise :: Double -> Bool withOtherwise x | x > 1 = True                 | otherwise = False  -- Not called, throws "non-exhaustive guard"+nonExhaustive :: Double -> Bool nonExhaustive x | x > 1 = True  main :: Fay ()
tests/recordWildCards.hs view
@@ -8,22 +8,32 @@  data X = X { foo :: Int } | Y { foo :: Int } -f :: C -> Int-f (C {a, ..}) = a + d+partialMatch :: C -> Int+partialMatch C{a=x, ..} = x + d -test_fun :: C-test_fun = let {a=10; b=20; c=30; d=40} in C{..}+con :: C+con = let {a=10; b=20; c=30; d=40} in C{..} -test2 :: X -> Int-test2 X{..} = foo+match :: X -> Int+match X{..} = foo +partialCon :: C+partialCon = let a = 11; b = 2; c = 3; d = 4 in C { a = 1, ..}++partialMatch2 c =+  let a = 100+  in case c of+       C{a=x,..} -> a+ main = do-    let r = C{a=1, b=2, c=3, d=4}-    print (f r)-    print test_fun+  print con+  print partialCon -    let x = X{foo=9}-    print (test2 x)+  print $ match X{foo=9}+  print $ partialMatch C{a=1, b=2, c=3, d=4}+  print $ partialMatch2 $ C 1 2 3 4 -    let y = Y{foo=6}-    print (test2 y)++  -- non exhaustive pattern match in `match`+  let y = Y{foo=6}+  print (match y)
tests/recordWildCards_partial.res view
@@ -1,3 +1,5 @@-5 { instance: 'C', a: 10, b: 20, c: 30, d: 40 }+{ instance: 'C', a: 1, b: 2, c: 3, d: 4 } 9+5+100
tests/sections.hs view
@@ -1,10 +1,12 @@-import Prelude+module Sections where +import           Prelude+ withTwo :: (Int -> Int) -> Int withTwo f = f 2  main :: Fay () main = do-  print $ (* 3) 2+  print $ (* 3) (2::Int)   print $ (7 `div`) 2   print $ withTwo (4*)
tests/whereBind.hs view
@@ -2,7 +2,6 @@  main :: Fay () main =-    let x = 10-    in putStrLn $ show (x + y)+    let x = 10 :: Int+    in print $ x + y   where y = 20-
tests/whereBind2.hs view
@@ -1,5 +1,6 @@ import           Prelude +someFun :: Int -> String someFun x = fun x   where fun x | x < 50 = "ok"               | otherwise = "nop"@@ -8,4 +9,3 @@ main = do     putStrLn (someFun 30)     putStrLn (someFun 100)-