fay 0.18.1.3 → 0.24.2.0
raw patch · 269 files changed
Files
- CHANGELOG.md +332/−5
- Setup.hs +2/−16
- examples/D3TreeSample.hs +96/−0
- examples/D3TreeSample.html +18/−0
- examples/calc.hs +3/−3
- examples/calc.html +1/−1
- examples/json.hs +3/−3
- examples/oscillator.hs +0/−11
- fay.cabal +154/−101
- js/runtime.js +0/−750
- src/Fay.hs +86/−63
- src/Fay/Compiler.hs +53/−60
- src/Fay/Compiler/Config.hs +0/−70
- src/Fay/Compiler/Decl.hs +61/−39
- src/Fay/Compiler/Defaults.hs +6/−6
- src/Fay/Compiler/Desugar.hs +213/−75
- src/Fay/Compiler/Desugar/Name.hs +29/−0
- src/Fay/Compiler/Desugar/Types.hs +43/−0
- src/Fay/Compiler/Exp.hs +103/−105
- src/Fay/Compiler/FFI.hs +68/−92
- src/Fay/Compiler/GADT.hs +7/−7
- src/Fay/Compiler/Import.hs +21/−17
- src/Fay/Compiler/InitialPass.hs +41/−34
- src/Fay/Compiler/Misc.hs +46/−87
- src/Fay/Compiler/ModuleT.hs +134/−0
- src/Fay/Compiler/Optimizer.hs +13/−12
- src/Fay/Compiler/Packages.hs +30/−20
- src/Fay/Compiler/Parse.hs +69/−0
- src/Fay/Compiler/Pattern.hs +38/−41
- src/Fay/Compiler/Prelude.hs +67/−0
- src/Fay/Compiler/PrimOp.hs +4/−2
- src/Fay/Compiler/Print.hs +156/−238
- src/Fay/Compiler/QName.hs +1/−1
- src/Fay/Compiler/State.hs +6/−6
- src/Fay/Compiler/Typecheck.hs +24/−8
- src/Fay/Config.hs +165/−0
- src/Fay/Control/Monad/Extra.hs +0/−31
- src/Fay/Control/Monad/IO.hs +0/−9
- src/Fay/Convert.hs +186/−168
- src/Fay/Data/List/Extra.hs +0/−14
- src/Fay/Exts.hs +6/−6
- src/Fay/Exts/NoAnnotation.hs +4/−5
- src/Fay/Exts/Scoped.hs +3/−3
- src/Fay/FFI.hs +1/−1
- src/Fay/Runtime.hs +842/−0
- src/Fay/System/Directory/Extra.hs +0/−21
- src/Fay/System/Process/Extra.hs +0/−15
- src/Fay/Types.hs +84/−285
- src/Fay/Types/CompileError.hs +40/−0
- src/Fay/Types/CompileResult.hs +9/−0
- src/Fay/Types/FFI.hs +40/−0
- src/Fay/Types/Js.hs +92/−0
- src/Fay/Types/ModulePath.hs +32/−0
- src/Fay/Types/Printer.hs +140/−0
- src/haskell-names/LICENSE +3/−0
- src/haskell-names/Language/Haskell/Names.hs +17/−0
- src/haskell-names/Language/Haskell/Names/Annotated.hs +100/−0
- src/haskell-names/Language/Haskell/Names/Exports.hs +144/−0
- src/haskell-names/Language/Haskell/Names/GetBound.hs +157/−0
- src/haskell-names/Language/Haskell/Names/GlobalSymbolTable.hs +114/−0
- src/haskell-names/Language/Haskell/Names/GlobalSymbolTable.hs-boot +10/−0
- src/haskell-names/Language/Haskell/Names/Imports.hs +274/−0
- src/haskell-names/Language/Haskell/Names/LocalSymbolTable.hs +34/−0
- src/haskell-names/Language/Haskell/Names/ModuleSymbols.hs +161/−0
- src/haskell-names/Language/Haskell/Names/Open/Base.hs +152/−0
- src/haskell-names/Language/Haskell/Names/Open/Derived.hs +88/−0
- src/haskell-names/Language/Haskell/Names/Open/Instances.hs +361/−0
- src/haskell-names/Language/Haskell/Names/RecordWildcards.hs +145/−0
- src/haskell-names/Language/Haskell/Names/Recursive.hs +132/−0
- src/haskell-names/Language/Haskell/Names/ScopeUtils.hs +87/−0
- src/haskell-names/Language/Haskell/Names/SyntaxUtils.hs +136/−0
- src/haskell-names/Language/Haskell/Names/Types.hs +293/−0
- src/main/Main.hs +82/−64
- src/tests/Test/CommandLine.hs +9/−10
- src/tests/Test/Compile.hs +84/−41
- src/tests/Test/Convert.hs +9/−9
- src/tests/Test/Desugar.hs +167/−0
- src/tests/Test/Util.hs +22/−15
- src/tests/Tests.hs +113/−42
- tests/AllBaseModules.hs +28/−0
- tests/AllBaseModules.res +1/−0
- tests/AutomaticList.hs +0/−1
- tests/Bool.hs +0/−2
- tests/CPP.hs +3/−1
- tests/Char.hs +29/−1
- tests/Char.res +26/−0
- tests/Compile/StrictWrapper.hs +59/−1
- tests/Compile/StrictWrapper.res +11/−0
- tests/Compile/pretty.hs +9/−0
- tests/Compile/pretty.res +7/−0
- tests/Compile/prettyOperators.hs +9/−0
- tests/Compile/prettyOperators.res +7/−0
- tests/Compile/prettyThunks.hs +9/−0
- tests/Compile/prettyThunks.res +7/−0
- tests/Defined.hs +0/−1
- tests/DesugarFFI.hs +28/−0
- tests/DesugarFFI.res +1/−0
- tests/DoLet2.hs +0/−1
- tests/DoLet3.hs +0/−1
- tests/Double.hs +2/−1
- tests/Double2.hs +2/−1
- tests/Double3.hs +2/−1
- tests/Double4.hs +2/−2
- tests/Either.hs +1/−1
- tests/EmptyDataDeclArray.hs +20/−0
- tests/EmptyDataDeclArray.res +3/−0
- tests/Eq.hs +1/−1
- tests/ExportEThingAll.hs +0/−1
- tests/ExportList.hs +1/−3
- tests/ExportList_A.hs +0/−1
- tests/ExportList_B.hs +0/−1
- tests/ExportQualified_Export.hs +1/−2
- tests/ExportType.hs +0/−1
- tests/Floating.hs +1/−1
- tests/FromString.hs +1/−2
- tests/GADTs_without_records.hs +2/−4
- tests/GuardWhere.hs +1/−1
- tests/HidePreludeImport_Import.hs +0/−2
- tests/HierarchicalImport.hs +2/−2
- tests/ImplicitPrelude.hs +7/−0
- tests/ImplicitPrelude.res +1/−0
- tests/ImportHiding.hs +1/−1
- tests/ImportIThingAll.hs +2/−1
- tests/ImportList.hs +0/−1
- tests/ImportListType.hs +2/−0
- tests/ImportType.hs +1/−2
- tests/ImportType2.hs +1/−0
- tests/ImportType2I/A.hs +0/−0
- tests/ImportType2I/B.hs +0/−0
- tests/Integer.hs +10/−0
- tests/Integer.res +5/−0
- tests/Integral.hs +1/−1
- tests/Issue215A.hs +1/−3
- tests/Js2FayFunc.hs +3/−1
- tests/JsFunctionPassing.hs +2/−1
- tests/LambdaCase.hs +11/−0
- tests/LambdaCase.res +1/−0
- tests/LazyOperators.hs +0/−2
- tests/List.hs +2/−0
- tests/List2.hs +3/−0
- tests/ListEq.hs +7/−0
- tests/ListEq.res +3/−0
- tests/MainThunk.hs +10/−0
- tests/MainThunk.res +3/−0
- tests/ModuleReExports.hs +1/−0
- tests/ModuleRecordClash.hs +0/−1
- tests/ModuleRecordClash2.hs +2/−4
- tests/ModuleRecordClash2_Hello.hs +0/−2
- tests/Monad.hs +2/−6
- tests/Monad2.hs +1/−4
- tests/MultiWayIf.hs +13/−0
- tests/MultiWayIf.res +3/−0
- tests/NestedImporting.hs +0/−2
- tests/NestedImporting2.hs +0/−1
- tests/NewtypeImport_Export.hs +0/−1
- tests/NewtypeImport_Import.hs +14/−13
- tests/Nullable.hs +3/−2
- tests/Num.hs +1/−2
- tests/Ord.hs +1/−2
- tests/PrefixOpPat.hs +7/−0
- tests/PrefixOpPat.res +1/−0
- tests/QualifiedImport.hs +0/−1
- tests/Ratio.hs +0/−1
- tests/ReExport3.hs +0/−1
- tests/ReExportGlobally.hs +0/−1
- tests/ReExportGloballyExplicit.hs +0/−1
- tests/RealFrac.hs +2/−1
- tests/RecCon.hs +2/−1
- tests/RecDecl.hs +2/−1
- tests/RecordImport2_Export1.hs +1/−1
- tests/RecordImport2_Export2.hs +1/−1
- tests/RecordImport2_Import.hs +0/−1
- tests/RecordImport_Export.hs +0/−2
- tests/RecordImport_Import.hs +2/−1
- tests/Sink.hs +2/−4
- tests/SkipLetTypes.hs +1/−2
- tests/SkipWhereTypes.hs +1/−2
- tests/String.hs +0/−4
- tests/String.res +0/−1
- tests/StringForcing.hs +0/−1
- tests/Strings.hs +3/−0
- tests/Strings.res +1/−0
- tests/T190.hs +0/−2
- tests/T190_A.hs +0/−2
- tests/T190_B.hs +0/−1
- tests/T190_C.hs +0/−2
- tests/TextOrd.hs +15/−0
- tests/TextOrd.res +4/−0
- tests/Trace.hs +11/−0
- tests/Trace.res +13/−0
- tests/TupleCalls.hs +3/−2
- tests/TyVarSerialization.hs +0/−1
- tests/Var.hs +24/−0
- tests/Var.res +5/−0
- tests/VarPtr.hs +11/−0
- tests/VarPtr.res +2/−0
- tests/WhenUnlessRecursion.hs +22/−0
- tests/WhenUnlessRecursion.res +3/−0
- tests/asPatternMatch.hs +1/−1
- tests/automatic.hs +2/−2
- tests/baseFixities.hs +1/−1
- tests/basicFunctions.hs +1/−2
- tests/case.hs +1/−2
- tests/case2.hs +1/−2
- tests/case3.hs +11/−0
- tests/case3.res +3/−0
- tests/caseList.hs +1/−2
- tests/caseWildcard.hs +1/−2
- tests/circular.hs +0/−2
- tests/curry.hs +1/−1
- tests/cycle.hs +2/−1
- tests/do.hs +2/−2
- tests/doAssingPatternMatch.hs +2/−2
- tests/doBindAssign.hs +2/−2
- tests/doLet.hs +9/−1
- tests/doLet.res +3/−0
- tests/emptyMain.hs +2/−1
- tests/enumFrom.hs +1/−1
- tests/error.hs +1/−1
- tests/ffiExpr.hs +1/−3
- tests/ffimunging.hs +1/−4
- tests/fix.hs +2/−1
- tests/fromInteger.hs +1/−1
- tests/fromIntegral.hs +5/−2
- tests/guards.hs +1/−1
- tests/infixDataConst.hs +1/−1
- tests/ints.hs +3/−3
- tests/linesAndWords.hs +2/−1
- tests/listComprehensions.hs +1/−1
- tests/listlen.hs +0/−1
- tests/mutableReference.hs +2/−4
- tests/nameGen.hs +1/−2
- tests/namedFieldPuns.hs +2/−2
- tests/negation.hs +2/−1
- tests/newtype.hs +21/−3
- tests/newtype.res +6/−0
- tests/newtypeIndirectApp.hs +1/−3
- tests/numTheory.hs +1/−1
- tests/nums.hs +1/−2
- tests/pats.hs +1/−1
- tests/patternGuards.hs +1/−2
- tests/patternMatchFail.hs +2/−2
- tests/patternMatchLet.hs +10/−0
- tests/patternMatchLet.res +1/−0
- tests/patternMatchingTuples.hs +1/−5
- tests/recordFunctionPatternMatch.hs +2/−2
- tests/recordPatternMatch.hs +1/−2
- tests/recordPatternMatch2.hs +1/−2
- tests/recordUseBeforeDefine.hs +1/−2
- tests/recordWildCards.hs +2/−1
- tests/records.hs +1/−2
- tests/recursive.hs +1/−3
- tests/reservedWords.hs +1/−2
- tests/sections.hs +0/−2
- tests/seq-fake.hs +1/−1
- tests/seq.hs +1/−1
- tests/serialization.hs +4/−3
- tests/serialization.res +4/−5
- tests/succPred.hs +1/−1
- tests/tailRecursion.hs +1/−1
- tests/then.hs +1/−2
- tests/tupleCon.hs +1/−1
- tests/tupleSec.hs +1/−1
- tests/unit.hs +2/−1
- tests/utf8.hs +1/−4
- tests/where.hs +1/−2
- tests/whereBind.hs +1/−1
- tests/whereBind2.hs +1/−1
- tests/whereBind3.hs +1/−2
CHANGELOG.md view
@@ -2,18 +2,345 @@ See full history at: <https://github.com/faylang/fay/commits> +### 0.24.2.0 (2021-01-10)++* Drop GHC 8.0 support.+* Cabal upgraded to 2.0 spec-version.+* Travis CI refactored to allow cabal-install 3.2.0.0 builds.++### 0.24.1.1 (2021-01-08)++* Relax boundaries for aeson, tasty, base-compat. Allow builds for Gentoo (#469).++### 0.24.1.0 (2020-03-26)++* GHC 8.10.1 support.++#### 0.24.0.5 (2020-03-10)++* GHC 8.8 support.++#### 0.24.0.4 (2019-12-03)++* Fixes stack nix integration broken.++#### 0.24.0.3 (2019-04-29)++* Dependency updates including GHC-8.6 support.+* Added stack compatibility.++#### 0.24.0.2++* Fix dependent compilation fail when building project with stack (#457).++#### 0.24.0.1++* Dependency updates incl GHC 8.4 support++## 0.24.0.0++* Add the option to generate typescript output, thanks to Junji Hashimoto.++### 0.23.2.0++* GHC 8.2 support+* Use traverse-with-class 1.0.*, and as a result drop support for GHC < 8.++#### 0.23.1.16++* Fix build on GHC 7.4++#### 0.23.1.15++* Allow optparse-applicative 0.13.*.++#### 0.23.1.14++* Fix a compilation error introduced in 0.23.1.13.++#### 0.23.1.13++* Add support for haskell-src-exts 1.18.1.++#### 0.23.1.12++* Fix compilation on GHC < 7.8.++#### 0.23.1.11++* Tighten some impossible lower bounds we can't support.++#### 0.23.1.10++* Don't compile with `-fprof-auto` by default.++#### 0.23.1.9++* Allow and require `haskell-src-exts 1.17.*`++#### 0.23.1.8++* Allow `vector 0.11.*`++#### 0.23.1.7++* Fix panic when compiling irrefutable pattern matches on lists (thanks Christopher Parks)++#### 0.23.1.6++* Allow `syb 0.5.*`++#### 0.23.1.5++* Allow `aeson 0.9.*`++#### 0.23.1.4++* Fix compilation on at least GHC 7.6 (maybe older versions as well...)++#### 0.23.1.3++* Inline parts of `haskell-names 0.4.1` and `haskell-packages` to drop transitive dependencies on Cabal and other packages.++#### 0.23.1.2++* Allow filepath 1.4.*++#### 0.23.1.1++* Allow do let bindings of newtypes.++### 0.23.1.0++* Add `--show-ghc-calls` and `configShowGhcCalls` to print invocations to GHC.++#### 0.23.0.1++* Allow `mtl-compat 0.2.*` and `transformers-compat 0.4.*"`.++## 0.23.0.0++New features:++* GHC 7.10 support+* Add a `--pretty-operators` flag to replace the escaped operator names with their actual names - By Michal Seweryn+* Add a `--pretty-all` flag that enables `--pretty`, `--pretty-operators`, and `--pretty-thunks` - By Michal Seweryn+* De/serialize type variables as 'automatic'. You no longer have to annotate FFI functions with Automatic, this is now the default. If you need the old behavior, use Ptr. For most existing cases this change shouldn't change anything. - By Zachary Mason++Example output with `--pretty-all`:+```javascript+Main.main = new $(function(){+ return _(_(Prelude["$"])(Prelude.print))(_(_(Prelude["++"])(Main.g))(Fay$$list("b")));+});+```+++API changes - By Michal Seweryn:++* Export CompileResult from Fay.Types+* Export PrintReader and PrintWriter, and reducing number of PrintState fields (these are moved to PrinterReader and PrintWriter)+* In the meanwhile PrintWriter field type changed - output is now stored as ShowS - tests run faster now+* Replace monadic functions (askP, getP, ...) with higher level ones: indented, askIf, newline, write, mapping. askIf is pretty much like previous askP, but works like if-then-else.+* Internally we are now using `ExceptT` instead of `ErrorT`. This fixes deprecation warnings when using `transformers 0.4.*`.++Bug fixes:+* Defer automatic_function fayToJs call until after argument application - by Zachary Mason++Dependency bumps:+* Allow `utf8-string 1.0.*`+* Allow `time == 1.5.*`++## 0.22.0.0++* Add a `--pretty-thunks` flag and compiler option that replaces `Fay$$_` and `Fay$$$` with `_` and `$` respectively. Consider this a development flag since it may clash with JS libraries (notably jQuery and underscore)+* Allow `language-ecmascript 0.17.*`++#### 0.21.2.1 (2014-11-10)++* Hide all packages by default when typechecking. This avoids conflicting with packages that haven't been specified on the command-line with `--package`, e.g. the `text` package when you import `Data.Text`.++### 0.21.2 (2014-10-21)++* Previously all package imports were ignored, now we only ignore `"base"` package imports.++### 0.21.1 (2014-10-21)++* Lots of additions to in `fay-base` adding the following modules:+ * Data.Var - Mutable variables, Reactive variables, and reactive signals+ * Unsafe.Coerce+ * Data.Text (fay-text will be updated to reuse this module)+ * Data.Time+ * Data.Ord, Data.Function, Data.Maybe, Data.List, Data.Either+ * Data.Defined and Data.Nullable+ * Data.Mutex - Simple mutexes+ * Control.Exception+ * Data.LocalStorage+ * Data.MutMap - Mutable maps++The introduction of `Data.Var` required some additions to fay's runtime.++#### 0.21.0.2 (2014-10-19)++* Fallback to ghc and ghc-pkg in PATH if not available from GHC.Paths++#### 0.21.0.1 (2014-10-12)++* Update to `optparse-applicative == 0.11.*`++## 0.21.0.0 (2014-10-11)++* Errors are now properly thrown from `encodeFay`. Changes the type signature to `encodeFay :: (GenericQ Value -> GenericQ Value) -> GenericQ Value`+* Update to `haskell-src-exts == 1.16.*`, This changes the type signature of `readerCompileLit` to `:: S.Sign -> S.Literal -> Compile JsExp`+* Fixes `ghc-pkg describe` stdout errors not always being printed++### 0.20.2.0 (2014-09-14)++* Config option to disable optimizations of newtypes, treating them as+ normal data types. This can be triggered by setting+ `configOptimizeNewtypes = False` or passing+ `--no-optimized-newtypes`.++#### 0.20.1.4 (2014-09-04)++* Update to `optparse-applicative == 0.10.*`++#### 0.20.1.3 (2014-08-29)++* Test suite is no longer built by default, cabal install with `-ftest` to enable.++#### 0.20.1.2 (2014-08-18)++* Updated homepage URLs, fay-lang.org was 301'd++#### 0.20.1.1 (2014-06-17)++* Don't cache the `main` thunk in the generated `main` call.++### 0.20.1.0 (2014-06-14)++* Add default case for UTCTime in Fay.Convert using the aeson instances. Note that this serializes to a json string so you won't be able to deserialize it as a separate type (such as Date) when using `Automatic` in Fay.++* Added `Fay.Config.defaultConfigWithSandbox` that reads the `HASKELL_PACKAGE_SANDBOX` environment variable. Client libraries can use this instead of manually reading from `getEnvironment`.++#### 0.20.0.4 (2014-05-23)++* Allow `optparse-applicative 0.9.*`++#### 0.20.0.3 (2014-05-09)++* Allow `mtl 2.2.*`++#### 0.20.0.2 (2014-05-08)++* Allow `haskell-names 0.4.*`++#### 0.20.0.1 (2014-05-08)++* Allow `transformers >= 0.4.1 && < 0.5`++## 0.20.0.0 (2014-04-29)++* Adds support for LambdaCase and MultiWayIf++* Modules have moved around a lot and several modules have been un-exposed. From now on you will probably only need to deal with at most `Fay` (which re-exports a lot of things), `Fay.Config`, `Fay.Types.CompileError`, `Fay.Convert`, and `Fay.Types.CompileResult`. Please let us know if you would like us to expose more things++* Config:+ * `CompileConfig` has been renamed to `Config` and is now located in `Fay.Config`.+ * `CompileConfig` has become a temporary type alias for `Config`.+ * `Fay.Compiler.Config` is deprecated, import `Fay` or `Fay.Config` instead.+ * The `data-default` instance for `Config` is deprecated, use `defaultConfig` instead.++* compiling+ * `compileFileWithState` is deprecated, use `compileFileWithResult` which returns a `Fay.Types.CompileResult` instead. As a consequence `CompileState` is also deprecated from public consumption.+ * `compileFile`, `compileFromToAndGenerateHtml` no longer return a triple with the sourcemap, use `compileFileWithResult` if you want access to this.++* Importing `Fay.Types` has been deprecated, import `Fay` instead.++* `readFromFay` has been rewritten using `syb` instead of `pretty-show` (Thanks to Michael Sloan and Chris Done)+ * This introduces the following breaking changes:+ * `readFromFay` has a `Data` constraint instead of `Show`.+ * Drops support for Rational and Integer (see below for migration steps). The reason is that neither was serialized in a way that would roundtrip for all values. Also, for similar reasons, fromRational is potentially divergent for aeson's new use of the Scientific type.+ * And adds the following features:+ * You can now write custom `Show` instances targeting GHC for types shared with Fay.+ * Better performance.+ * Allows the serialization and deserialization to be customized on a per-type basis, via encodeFay and decodeFay.+ * To migrate code using Rational or Integer, use encodeFay and pass an argument e.g. `(\f x -> maybe (f x) myIntegerToValueConversion (cast x))` and likewise to decodeFay.++Bugfixes:++* Mltiple guards on a pattern in a case expression skipped everything but the first guard. To fix this an optimization we had on pattern conditions was disabled.++Dependency bumps:++* Allow Cabal 1.20 and 1.21++Internal:++* Test cases are now using `tasty` instead of `test-framework`. To run cases in parallel use `fay-tests --num-threads=N` (see `fay-tests --help` for more info).+* Added a test group for desugaring.++#### 0.19.2.1 (2014-04-14)++* Allow `haskell-src-exts 1.15.*`++### 0.19.2 (2014-04-10)++* Fixes a bug where arrays used with empty data decls would be deserialized into a Fay list instead of kept as is.++#### 0.19.1.2 (2014-04-07)++* Fix optimizations that were not applied and add codegen test cases.++#### 0.19.1.1 (2014-03-17)++* Allow `optparse-applicative 0.8.*`++### 0.19.1 (2014-03-13)++* Added Data.Char to fay-base++Dependency bumps:+* Allow `Cabal 1.19.*`+* Allow `process 1.2.*`++#### 0.19.0.2 (2014-01-30)++Bugfixes:+* Don't export transcoding information for fay-base packages when compiling with --no-stdlib+* Better error messages when forgetting the type signature in an FFI declaration++#### 0.19.0.1 (2014-01-15)++Dependency bumps:+* Allow `aeson 0.7.*`++## 0.19 (2014-01-14)++* Made import Prelude is implicit, but note that RebindableSyntax implies NoImplicitPrelude.+* Allow FFI declarations in let and where statements: `let f :: X; f = ffi "..."` and `where f :: X; f = ffi "..."`++Bugfixes:+* Removed extra </script> tag that was generated by --html-wrapper+* FFI expressions in top level declarations now produce identical code to a normal top level FFI declaration+* Don't export Data.Ratio and Debug.Trace when using --no-stdlib++Dependency bumps:+* Allow text 1.1+* Allow attoparsec 0.11++ **Note: 0.18.0.1 added source mappings returned by `Fay:compileFile` and friends meaning it should have been a major bump. Sorry about this!** -### 0.18.1.3 (2013-12-14)+#### 0.18.1.3 (2013-12-14) * Add parsing of Integer to Fay.Convert (note that the runtime doesn't have arbitrary precision Integers) * Allow text 1.0.* -### 0.18.1.2 (2013-11-26)+#### 0.18.1.2 (2013-11-26) * Add support for indirect application of newtypes (such as `p = NewType; foo = p x` and `bar = NewType $ y`) -### 0.18.1.1 (2013-11-22)+#### 0.18.1.1 (2013-11-22) * Fix a bug where records with the same name as top level modules wouldn't be initialized correctly. * Fail when using enum syntax on unsupported literal types (for instance ['a'..'z'])@@ -66,8 +393,8 @@ New features: * Support for qualified imports. Note: You still can't have multiple constructors with the same name in the FFI since the `instance` field in the serialization is still unqualified.-* `Automatic` transcoding now works for functions. See [[Calling Fay From JavaScript]]-* `--strict modulename[, ..]` generates strict and transcoding wrappers for a module's exports. See [[Calling Fay From JavaScript]]+* `Automatic` transcoding now works for functions. See [Calling Fay From JavaScript](https://github.com/faylang/fay/wiki/Calling-Fay-from-JavaScript)+* `--strict modulename[, ..]` generates strict and transcoding wrappers for a module's exports. See [Calling Fay From JavaScript](https://github.com/faylang/fay/wiki/Calling-Fay-from-JavaScript) * `--typecheck-only` just runs the GHC type checker with the appropriate Fay flags. * `--runtime-path FILEPATH` allows you to supply a custom runtime. Probably only useful for debugging.
Setup.hs view
@@ -7,24 +7,10 @@ import Distribution.PackageDescription import Distribution.Simple -main = do+main = defaultMainWithHooks simpleUserHooks- { preConf = \_args _flags -> do putStrLn reminder- return emptyHookedBuildInfo- , postInst = (\_ _ _ _ -> putStrLn fayBaseReminder)+ { postInst = \_ _ _ _ -> putStrLn fayBaseReminder }--reminder =- " \n\- \- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n\- \ \n\- \ REMEMBER: This compiler is in flux, supercalifragelistic style. You should \n\- \ read the CHANGELOG for this release as the changes probably \n\- \ affect you. \n\- \ \n\- \ \n\- \ \n\- \- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n" fayBaseReminder = " \n\
+ examples/D3TreeSample.hs view
@@ -0,0 +1,96 @@+module D3TreeSample where++import FFI+import Data.Char++data Event+data D3SvgCanvas+data D3SvgDiagonal+data D3SvgLink+data D3SvgNode+data D3LayoutTree+data D3LayoutTreeNodes+data D3LayoutTreeLinks++--------- D3: A JavaScript visualization library for HTML and SVG. http://d3js.org+--------- Usage example with Fay++--------- JSON FEED++data JsonTree = JsonNode { name :: String, children :: JsonForest }+type JsonForest = [JsonTree]++unfoldTree :: (Int -> (String, [Int])) -> Int -> JsonTree+unfoldTree f b = let (a, bs) = f b in JsonNode a (unfoldForest f bs)++unfoldForest :: (Int -> (String, [Int])) -> [Int] -> JsonForest+unfoldForest f = map (unfoldTree f)++createJson :: Int -> JsonTree+createJson = unfoldTree (\n -> ([chr (n + ord 'a')], replicate n (n-1)))++--------- FFI++addWindowEvent :: String -> (Event -> Fay ()) -> Fay ()+addWindowEvent = ffi "window.addEventListener(%1, %2)"++createSvgCanvas :: Int -> Int -> Fay D3SvgCanvas+createSvgCanvas = ffi "d3.select('#viz')\+ \.append('svg:svg').attr('width', %1).attr('height', %2)\+ \.append('svg:g').attr('transform', 'translate(40, 40)')"++createTree :: Int -> Int -> Fay D3LayoutTree+createTree = ffi "d3.layout.tree().size([%1,%2])"++createDiagonal :: Fay D3SvgDiagonal+createDiagonal = ffi "d3.svg.diagonal().projection(function(d) { return [d.x, d.y]; })"++createNodes :: D3LayoutTree -> JsonTree -> Fay D3LayoutTreeNodes+createNodes = ffi "%1.nodes(%2)"++createLinks :: D3LayoutTree -> D3LayoutTreeNodes -> Fay D3LayoutTreeLinks+createLinks = ffi "%1.links(%2)"++displayLinks :: D3SvgCanvas -> D3LayoutTreeLinks -> D3SvgDiagonal -> Fay D3SvgLink+displayLinks = ffi "%1.selectAll('pathlink').data(%2)\+ \.enter().append('svg:path')\+ \.attr('class', 'link')\+ \.attr('d', %3)"++displayNodes :: D3SvgCanvas -> D3LayoutTreeNodes -> Fay D3SvgNode+displayNodes = ffi "%1.selectAll('g.node').data(%2)\+ \.enter().append('svg:g')\+ \.attr('transform', \+ \function(d) { return 'translate(' + d.x + ',' + d.y + ')'; })"++addDotToNodes :: D3SvgNode -> Double -> Fay ()+addDotToNodes = ffi "%1.append('svg:circle').attr('r', %2)"++addTextToNodes :: D3SvgNode -> Fay()+addTextToNodes = ffi "%1.append('svg:text')\+ \.attr('dx', function(d) { return d.children ? -8 : 8; })\+ \.attr('dy', 3)\+ \.attr('text-anchor', function(d) { return d.children ? 'end' : 'start'; })\+ \.text(function(d) { return d.name; })"++--------- START++d3TreeSample :: Event -> Fay()+d3TreeSample event = do+ svgCanvas <- createSvgCanvas 800 600+ d3Tree <- createTree 600 300+ diagonal <- createDiagonal++ nodes <- createNodes d3Tree (createJson 4)+ links <- createLinks d3Tree nodes++ svgLink <- displayLinks svgCanvas links diagonal+ svgNode <- displayNodes svgCanvas nodes++ addDotToNodes svgNode 2.5+ addTextToNodes svgNode+++main :: Fay ()+main = do+ addWindowEvent "load" d3TreeSample
+ examples/D3TreeSample.html view
@@ -0,0 +1,18 @@+<!doctype html>+<html>+ <head>+ <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>+ <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js" charset="utf-8"></script>+ <script type="text/javascript" src="D3TreeSample.js"></script>+ <style>+ .link {+ fill: none;+ stroke: #ccc;+ stroke-width: 2.5px;+ }+ </style>+ </head>+ <body>+ <div id="viz"></div>+ </body>+</html>
examples/calc.hs view
@@ -32,7 +32,7 @@ return () else do setVal (show n) display writeRef appendMore True)- getInput = getVal display >>= (return . parseDouble 10)+ getInput = getVal display >>= (return . parseDouble) operator op = do calculate num <- getInput@@ -104,8 +104,8 @@ -------------------------------------------------------------------------------- -- Utilities -parseDouble :: Int -> String -> Double-parseDouble = ffi "parseFloat(%2,%1) || 0"+parseDouble :: String -> Double+parseDouble = ffi "parseFloat(%1) || 0" -------------------------------------------------------------------------------- -- Refs
examples/calc.html view
@@ -10,7 +10,7 @@ <body> <h1>Calc</h1> <p>A clone of <a href="http://trelford.com/PitCalculatorApp.htm">Pit's calculator app</a></p>- <p>JS generated by <a href="http://fay-lang.org/">Fay</a>, a Haskell+ <p>JS generated by <a href="https://github.com/faylang/fay/wiki">Fay</a>, a Haskell subset. <a href="https://github.com/faylang/fay/blob/master/examples/calc.hs">Source code</a></p> </body> </html>
examples/json.hs view
@@ -1,5 +1,3 @@-- module Console where import FFI@@ -11,10 +9,12 @@ myData :: MyData myData = MyData { xVar = pack "asdfasd", yVar = 9 } - main = do jsonSerialized <- toJSON myData jsonDeserialized <- toMyData jsonSerialized+ -- The "instance" field below is required for reliable parsing.+ -- If your JSON source doesn't have an "instance" field, you can+ -- add one like this: ffi "%1['instance']='MyData',%1" :: Json -> MyData fromStringData <- toMyData "{\"xVar\":3,\"yVar\":-1,\"instance\":\"MyData\"}" print' jsonSerialized
examples/oscillator.hs view
@@ -1,12 +1,9 @@ {-# LANGUAGE EmptyDataDecls #-}-{-# LANGUAGE NoImplicitPrelude #-} module RingOscillator (main) where import FFI-import Prelude - -- System parameters. -- data Params = Params { alpha :: Double@@ -419,14 +416,6 @@ zipWith5 z (a:as) (b:bs) (c:cs) (d:ds) (e:es) = z a b c d e : zipWith5 z as bs cs ds es zipWith5 _ _ _ _ _ _ = []--mapM :: (a -> Fay b) -> [a] -> Fay [b]-mapM m (x:xs) = m x >>= (\mx -> mapM m xs >>= (\mxs -> return (mx:mxs)))-mapM _ [] = return []--forM :: [a] -> (a -> Fay b) -> Fay [b]-forM (x:xs) m = m x >>= (\mx -> mapM m xs >>= (\mxs -> return (mx:mxs)))-forM [] _ = return [] replicateM :: Int -> Fay a -> Fay [a] replicateM n x = sequence (replicate n x)
fay.cabal view
@@ -1,5 +1,6 @@+cabal-version: 2.0 name: fay-version: 0.18.1.3+version: 0.24.2.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,@@ -7,18 +8,13 @@ . /Documentation/ .- See <http://fay-lang.org/>+ See <https://github.com/faylang/fay/wiki> . /Examples/ . See the examples directory and <https://github.com/faylang/fay/wiki#fay-in-the-wild> .- /Release Notes/- .- See <https://github.com/faylang/fay/blob/master/CHANGELOG.md>- .- See full history at: <https://github.com/faylang/fay/commits>-homepage: http://fay-lang.org/+homepage: https://github.com/faylang/fay/wiki bug-reports: https://github.com/faylang/fay/issues license: BSD3 license-file: LICENSE@@ -27,13 +23,14 @@ copyright: 2012 Chris Done, Adam Bergmark category: Development, Web, Fay build-type: Custom-cabal-version: >=1.8-data-files: js/runtime.js- src/Fay/FFI.hs+Tested-with: GHC ==8.4.4 || ==8.6.5 || ==8.8.3+data-files:+ src/Fay/FFI.hs extra-source-files:+ CHANGELOG.md LICENSE README.md- CHANGELOG.md+ src/haskell-names/LICENSE -- Examples examples/*.hs examples/*.html@@ -70,102 +67,158 @@ type: git location: https://github.com/faylang/fay.git +flag test+ description: Build the fay-tests executable+ manual: True+ default: False++custom-setup+ setup-depends:+ base+ , Cabal+ library- hs-source-dirs: src- exposed-modules: Fay- , Fay.Compiler- , Fay.Compiler.Config- , 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.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- build-depends: base >= 4 && < 5- , Cabal < 1.19- , aeson < 0.7- , attoparsec < 0.11- , bytestring < 0.11- , containers < 0.6- , cpphs < 1.19- , data-default < 0.6- , directory < 1.3- , filepath < 1.4- , ghc-paths < 0.2- , haskell-names >= 0.3.1 && < 0.4- , haskell-packages >= 0.2.3.1 && < 0.3- , haskell-src-exts >= 1.14 && < 1.15- , language-ecmascript >= 0.15 && < 1.0- , mtl < 2.2- , pretty-show >= 1.6 && < 1.7- , process < 1.2- , safe < 0.4- , split < 0.3- , syb < 0.5- , text < 1.1- , time < 1.5- , uniplate >= 1.6.11 && < 1.7- , unordered-containers < 0.3- , utf8-string < 0.4- , vector < 0.11- , sourcemap < 0.2+ default-language: Haskell2010 + ghc-options: -O2 -Wall+ hs-source-dirs:+ src+ src/haskell-names+ autogen-modules:+ Paths_fay+ exposed-modules:+ Fay+ Fay.Compiler+ Fay.Compiler.Desugar+ Fay.Compiler.Parse+ Fay.Compiler.Prelude+ Fay.Config+ Fay.Convert+ Fay.FFI+ Fay.Types+ Fay.Types.CompileError+ Fay.Types.CompileResult+ other-modules:+ Fay.Compiler.Decl+ Fay.Compiler.Defaults+ Fay.Compiler.Desugar.Name+ Fay.Compiler.Desugar.Types+ Fay.Compiler.Exp+ Fay.Compiler.FFI+ Fay.Compiler.GADT+ Fay.Compiler.Import+ Fay.Compiler.InitialPass+ Fay.Compiler.Misc+ Fay.Compiler.ModuleT+ Fay.Compiler.Optimizer+ Fay.Compiler.Packages+ Fay.Compiler.Pattern+ Fay.Compiler.PrimOp+ Fay.Compiler.Print+ Fay.Compiler.QName+ Fay.Compiler.State+ Fay.Compiler.Typecheck+ Fay.Exts+ Fay.Exts.NoAnnotation+ Fay.Exts.Scoped+ Fay.Runtime+ Fay.Types.FFI+ Fay.Types.Js+ Fay.Types.ModulePath+ Fay.Types.Printer+ Language.Haskell.Names+ Language.Haskell.Names.Annotated+ Language.Haskell.Names.Exports+ Language.Haskell.Names.GetBound+ Language.Haskell.Names.GlobalSymbolTable+ Language.Haskell.Names.Imports+ Language.Haskell.Names.LocalSymbolTable+ Language.Haskell.Names.ModuleSymbols+ Language.Haskell.Names.Open.Base+ Language.Haskell.Names.Open.Derived+ Language.Haskell.Names.Open.Instances+ Language.Haskell.Names.RecordWildcards+ Language.Haskell.Names.Recursive+ Language.Haskell.Names.ScopeUtils+ Language.Haskell.Names.SyntaxUtils+ Language.Haskell.Names.Types+ Paths_fay+ build-depends:+ base >= 4.9 && < 4.15+ , base-compat >= 0.10 && < 0.12+ , aeson > 0.6 && < 1.6+ , bytestring >= 0.9 && < 0.11+ , containers >= 0.4 && < 0.7+ , data-default >= 0.2 && < 0.8+ , data-lens-light == 0.1.*+ , directory >= 1.1 && < 1.4+ , filepath >= 1.3 && < 1.5+ , ghc-paths == 0.1.*+ , haskell-src-exts >= 1.20+ , language-ecmascript >= 0.15 && < 0.20+ , mtl >= 2.1 && < 2.3+ , mtl-compat >= 0.1 && < 0.3+ , process >= 1.1 && < 1.7+ , safe >= 0.2 && < 0.4+ , sourcemap == 0.1.*+ , split >= 0.1 && < 0.3+ , spoon >= 0.1 && < 0.4+ , syb >= 0.3 && < 0.8+ , text >= 0.11 && < 1.3+ , time >= 1.4 && < 1.10+ , transformers >= 0.3 && < 0.6+ , transformers-compat >= 0.3 && < 0.7+ , traverse-with-class >= 1.0 && < 1.1+ , uniplate >= 1.6.11 && < 1.7+ , unordered-containers == 0.2.*+ , utf8-string >= 0.1 && < 1.1+ , vector < 0.13+ , shakespeare < 2.1 + if impl(ghc < 7.8)+ build-depends: tagged+ executable fay+ default-language: Haskell2010 hs-source-dirs: src/main ghc-options: -O2 -Wall- ghc-prof-options: -fprof-auto main-is: Main.hs- build-depends: base- , fay- , data-default- , optparse-applicative >= 0.6 && < 0.8- , split+ build-depends:+ base+ , fay+ , mtl+ , optparse-applicative >= 0.11 && < 0.16+ , split+ other-modules:+ Paths_fay executable fay-tests- hs-source-dirs: src/tests+ default-language: Haskell2010 ghc-options: -O2 -Wall -threaded -with-rtsopts=-N- ghc-prof-options: -fprof-auto+ hs-source-dirs: src/tests main-is: Tests.hs- other-modules: Test.CommandLine- Test.Compile- Test.Convert- Test.Util- build-depends: base- , fay- , HUnit < 1.3- , aeson- , attoparsec- , bytestring- , data-default- , directory- , filepath- , haskell-src-exts- , test-framework < 0.9- , test-framework-hunit < 0.4- , test-framework-th < 0.3- , text- , utf8-string+ if flag(test)+ other-modules:+ Test.CommandLine+ Test.Compile+ Test.Convert+ Test.Desugar+ Test.Util+ Paths_fay+ build-depends:+ base+ , aeson+ , attoparsec+ , bytestring+ , containers+ , directory+ , fay+ , filepath+ , haskell-src-exts+ , random >= 1.0 && < 1.3+ , tasty >= 0.9 && < 1.5+ , tasty-hunit >= 0.8 && < 0.11+ , tasty-th == 0.1.*+ , text+ , utf8-string+ else+ buildable: False
− js/runtime.js
@@ -1,750 +0,0 @@-/*******************************************************************************- * Misc.- */--// Workaround for missing functionality in IE 8 and earlier.-if( Object.create === undefined ) {- Object.create = function( o ) {- function F(){}- F.prototype = o;- return new F();- };-}--// Insert properties of b in place into a.-function Fay$$objConcat(a,b){- for (var p in b) if (b.hasOwnProperty(p)){- a[p] = b[p];- }- return a;-}--/*******************************************************************************- * Thunks.- */--// Force a thunk (if it is a thunk) until WHNF.-function Fay$$_(thunkish,nocache){- while (thunkish instanceof Fay$$$) {- thunkish = thunkish.force(nocache);- }- return thunkish;-}--// Apply a function to arguments (see method2 in Fay.hs).-function Fay$$__(){- var f = arguments[0];- for (var i = 1, len = arguments.length; i < len; i++) {- f = (f instanceof Fay$$$? Fay$$_(f) : f)(arguments[i]);- }- return f;-}--// Thunk object.-function Fay$$$(value){- this.forced = false;- this.value = value;-}--// Force the thunk.-Fay$$$.prototype.force = function(nocache) {- return nocache ?- this.value() :- (this.forced ?- this.value :- (this.value = this.value(), this.forced = true, this.value));-};---function Fay$$seq(x) {- return function(y) {- Fay$$_(x,false);- return y;- }-}--function Fay$$seq$36$uncurried(x,y) {- Fay$$_(x,false);- return y;-}--/*******************************************************************************- * Monad.- */--function Fay$$Monad(value){- this.value = value;-}--// This is used directly from Fay, but can be rebound or shadowed. See primOps in Types.hs.-// >>-function Fay$$then(a){- return function(b){- return Fay$$bind(a)(function(_){- return b;- });- };-}--// This is used directly from Fay, but can be rebound or shadowed. See primOps in Types.hs.-// >>-function Fay$$then$36$uncurried(a,b){- return Fay$$bind$36$uncurried(a,function(_){ return b; });-}--// >>=-// This is used directly from Fay, but can be rebound or shadowed. See primOps in Types.hs.-function Fay$$bind(m){- return function(f){- return new Fay$$$(function(){- var monad = Fay$$_(m,true);- return Fay$$_(f)(monad.value);- });- };-}--// >>=-// This is used directly from Fay, but can be rebound or shadowed. See primOps in Types.hs.-function Fay$$bind$36$uncurried(m,f){- return new Fay$$$(function(){- var monad = Fay$$_(m,true);- return Fay$$_(f)(monad.value);- });-}--// This is used directly from Fay, but can be rebound or shadowed.-function Fay$$$_return(a){- return new Fay$$Monad(a);-}--// Allow the programmer to access thunk forcing directly.-function Fay$$force(thunk){- return function(type){- return new Fay$$$(function(){- Fay$$_(thunk,type);- return new Fay$$Monad(Fay$$unit);- })- }-}--// This is used directly from Fay, but can be rebound or shadowed.-function Fay$$return$36$uncurried(a){- return new Fay$$Monad(a);-}--// Unit: ().-var Fay$$unit = null;--/*******************************************************************************- * Serialization.- * Fay <-> JS. Should be bijective.- */--// Serialize a Fay object to JS.-function Fay$$fayToJs(type,fayObj){- var base = type[0];- var args = type[1];- var jsObj;- if(base == "action") {- // A nullary monadic action. Should become a nullary JS function.- // Fay () -> function(){ return ... }- return function(){- return Fay$$fayToJs(args[0],Fay$$_(fayObj,true).value);- };-- }- else if(base == "function") {- // A proper function.- return function(){- var fayFunc = fayObj;- var return_type = args[args.length-1];- var len = args.length;- // If some arguments.- if (len > 1) {- // Apply to all the arguments.- fayFunc = Fay$$_(fayFunc,true);- // TODO: Perhaps we should throw an error when JS- // passes more arguments than Haskell accepts.-- // Unserialize the JS values to Fay for the Fay callback.- if (args == "automatic_function")- {- for (var i = 0; i < arguments.length; i++) {- fayFunc = Fay$$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++) {- fayFunc = Fay$$_(fayFunc(Fay$$jsToFay(args[i],arguments[i])),true);- }- // Finally, serialize the Fay return value back to JS.- var return_base = return_type[0];- var return_args = return_type[1];- // If it's a monadic return value, get the value instead.- if(return_base == "action") {- return Fay$$fayToJs(return_args[0],fayFunc.value);- }- // Otherwise just serialize the value direct.- else {- return Fay$$fayToJs(return_type,fayFunc);- }- } else {- throw new Error("Nullary function?");- }- };-- }- else if(base == "string") {- return Fay$$fayToJs_string(fayObj);- }- else if(base == "list") {- // Serialize Fay list to JavaScript array.- var arr = [];- fayObj = Fay$$_(fayObj);- while(fayObj instanceof Fay$$Cons) {- arr.push(Fay$$fayToJs(args[0],fayObj.car));- fayObj = Fay$$_(fayObj.cdr);- }- return arr;- }- else if(base == "tuple") {- // Serialize Fay tuple to JavaScript array.- var arr = [];- fayObj = Fay$$_(fayObj);- var i = 0;- while(fayObj instanceof Fay$$Cons) {- arr.push(Fay$$fayToJs(args[i++],fayObj.car));- fayObj = Fay$$_(fayObj.cdr);- }- return arr;- }- else if(base == "defined") {- fayObj = Fay$$_(fayObj);- return fayObj instanceof Fay.FFI._Undefined- ? undefined- : Fay$$fayToJs(args[0],fayObj.slot1);- }- else if(base == "nullable") {- fayObj = Fay$$_(fayObj);- return fayObj instanceof Fay.FFI._Null- ? null- : Fay$$fayToJs(args[0],fayObj.slot1);- }- else if(base == "double" || base == "int" || base == "bool") {- // Bools are unboxed.- return Fay$$_(fayObj);- }- else if(base == "ptr" || base == "unknown")- return fayObj;- else if(base == "automatic" && fayObj instanceof Function) {- return Fay$$fayToJs(["function", "automatic_function"], fayObj);- }- else if(base == "automatic" || base == "user") {- fayObj = Fay$$_(fayObj);-- if(fayObj instanceof Fay$$Cons || fayObj === null){- // Serialize Fay list to JavaScript array.- var arr = [];- while(fayObj instanceof Fay$$Cons) {- arr.push(Fay$$fayToJs(["automatic"],fayObj.car));- fayObj = Fay$$_(fayObj.cdr);- }- return arr;- } else {- var fayToJsFun = fayObj && fayObj.instance && Fay$$fayToJsHash[fayObj.instance];- return fayToJsFun ? fayToJsFun(type,type[2],fayObj) : fayObj;- }- }-- throw new Error("Unhandled Fay->JS translation type: " + base);-}--// Stores the mappings from fay types to js objects.-// This will be populated by compiled modules.-var Fay$$fayToJsHash = {};--// Specialized serializer for string.-function Fay$$fayToJs_string(fayObj){- // Serialize Fay string to JavaScript string.- var str = "";- fayObj = Fay$$_(fayObj);- while(fayObj instanceof Fay$$Cons) {- str += Fay$$_(fayObj.car);- fayObj = Fay$$_(fayObj.cdr);- }- return str;-};-function Fay$$jsToFay_string(x){- return Fay$$list(x)-};--// Special num/bool serializers.-function Fay$$jsToFay_int(x){return x;}-function Fay$$jsToFay_double(x){return x;}-function Fay$$jsToFay_bool(x){return x;}--function Fay$$fayToJs_int(x){return Fay$$_(x);}-function Fay$$fayToJs_double(x){return Fay$$_(x);}-function Fay$$fayToJs_bool(x){return Fay$$_(x);}--// Unserialize an object from JS to Fay.-function Fay$$jsToFay(type,jsObj){- var base = type[0];- var args = type[1];- var fayObj;- if(base == "action") {- // Unserialize a "monadic" JavaScript return value into a monadic value.- return new Fay$$Monad(Fay$$jsToFay(args[0],jsObj));- }- else if(base == "function") {- // Unserialize a function from JavaScript to a function that Fay can call.- // So- //- // var f = function(x,y,z){ … }- //- // becomes something like:- //- // function(x){- // return function(y){- // return function(z){- // return new Fay$$$(function(){- // return Fay$$jsToFay(f(Fay$$fayTojs(x),- // Fay$$fayTojs(y),- // Fay$$fayTojs(z))- // }}}}};- var returnType = args[args.length-1];- var funArgs = args.slice(0,-1);-- if (jsObj.length > 0) {- var makePartial = function(args){- return function(arg){- var i = args.length;- var fayArg = Fay$$fayToJs(funArgs[i],arg);- var newArgs = args.concat([fayArg]);- if(newArgs.length == funArgs.length) {- return new Fay$$$(function(){- return Fay$$jsToFay(returnType,jsObj.apply(this,newArgs));- });- } else {- return makePartial(newArgs);- }- };- };- return makePartial([]);- }- else- return function (arg) {- return Fay$$jsToFay(["automatic"], jsObj(Fay$$fayToJs(["automatic"], arg)));- };- }- else if(base == "string") {- // Unserialize a JS string into Fay list (String).- // This is a special case, when String is explicit in the type signature,- // with `Automatic' a string would not be decoded.- return Fay$$list(jsObj);- }- else if(base == "list") {- // Unserialize a JS array into a Fay list ([a]).- var serializedList = [];- for (var i = 0, len = jsObj.length; i < len; i++) {- // Unserialize each JS value into a Fay value, too.- serializedList.push(Fay$$jsToFay(args[0],jsObj[i]));- }- // Pop it all in a Fay list.- return Fay$$list(serializedList);- }- else if(base == "tuple") {- // Unserialize a JS array into a Fay tuple ((a,b,c,...)).- var serializedTuple = [];- for (var i = 0, len = jsObj.length; i < len; i++) {- // Unserialize each JS value into a Fay value, too.- serializedTuple.push(Fay$$jsToFay(args[i],jsObj[i]));- }- // Pop it all in a Fay list.- return Fay$$list(serializedTuple);- }- else if(base == "defined") {- return jsObj === undefined- ? new Fay.FFI._Undefined()- : new Fay.FFI._Defined(Fay$$jsToFay(args[0],jsObj));- }- else if(base == "nullable") {- return jsObj === null- ? new Fay.FFI._Null()- : new Fay.FFI.Nullable(Fay$$jsToFay(args[0],jsObj));- }- else if(base == "int") {- // Int are unboxed, so there's no forcing to do.- // But we can do validation that the int has no decimal places.- // E.g. Math.round(x)!=x? throw "NOT AN INTEGER, GET OUT!"- fayObj = Math.round(jsObj);- if(fayObj!==jsObj) throw "Argument " + jsObj + " is not an integer!";- return fayObj;- }- else if (base == "double" ||- base == "bool" ||- base == "ptr" ||- base == "unknown") {- return jsObj;- }- else if(base == "automatic" && jsObj instanceof Function) {- var type = [["automatic"]];- for (var i = 0; i < jsObj.length; i++)- type.push(["automatic"]);- return Fay$$jsToFay(["function", type], jsObj);- }- else if(base == "automatic" || base == "user") {- if (jsObj && jsObj['instance']) {- var jsToFayFun = Fay$$jsToFayHash[jsObj["instance"]];- return jsToFayFun ? jsToFayFun(type,type[2],jsObj) : jsObj;- }- else if (jsObj instanceof Array) {- var list = null;- for (var i = jsObj.length - 1; i >= 0; i--) {- list = new Fay$$Cons(Fay$$jsToFay([base], jsObj[i]), list);- }- return list;- }- else- return jsObj;- }-- throw new Error("Unhandled JS->Fay translation type: " + base);-}--// Stores the mappings from js objects to fay types.-// This will be populated by compiled modules.-var Fay$$jsToFayHash = {};--/*******************************************************************************- * Lists.- */--// Cons object.-function Fay$$Cons(car,cdr){- this.car = car;- this.cdr = cdr;-}--// Make a list.-function Fay$$list(xs){- var out = null;- for(var i=xs.length-1; i>=0;i--)- out = new Fay$$Cons(xs[i],out);- return out;-}--// Built-in list cons.-function Fay$$cons(x){- return function(y){- return new Fay$$Cons(x,y);- };-}--// List index.-// `list' is already forced by the time it's passed to this function.-// `list' cannot be null and `index' cannot be out of bounds.-function Fay$$index(index,list){- for(var i = 0; i < index; i++) {- list = Fay$$_(list.cdr);- }- return list.car;-}--// List length.-// `list' is already forced by the time it's passed to this function.-function Fay$$listLen(list,max){- for(var i = 0; list !== null && i < max + 1; i++) {- list = Fay$$_(list.cdr);- }- return i == max;-}--/*******************************************************************************- * Numbers.- */--// Built-in *.-function Fay$$mult(x){- return function(y){- return new Fay$$$(function(){- return Fay$$_(x) * Fay$$_(y);- });- };-}--function Fay$$mult$36$uncurried(x,y){-- return new Fay$$$(function(){- return Fay$$_(x) * Fay$$_(y);- });--}--// Built-in +.-function Fay$$add(x){- return function(y){- return new Fay$$$(function(){- return Fay$$_(x) + Fay$$_(y);- });- };-}--// Built-in +.-function Fay$$add$36$uncurried(x,y){-- return new Fay$$$(function(){- return Fay$$_(x) + Fay$$_(y);- });--}--// Built-in -.-function Fay$$sub(x){- return function(y){- return new Fay$$$(function(){- return Fay$$_(x) - Fay$$_(y);- });- };-}-// Built-in -.-function Fay$$sub$36$uncurried(x,y){-- return new Fay$$$(function(){- return Fay$$_(x) - Fay$$_(y);- });--}--// Built-in /.-function Fay$$divi(x){- return function(y){- return new Fay$$$(function(){- return Fay$$_(x) / Fay$$_(y);- });- };-}--// Built-in /.-function Fay$$divi$36$uncurried(x,y){-- return new Fay$$$(function(){- return Fay$$_(x) / Fay$$_(y);- });--}--/*******************************************************************************- * Booleans.- */--// Are two values equal?-function Fay$$equal(lit1, lit2) {- // Simple case- lit1 = Fay$$_(lit1);- lit2 = Fay$$_(lit2);- if (lit1 === lit2) {- return true;- }- // General case- if (lit1 instanceof Array) {- if (lit1.length != lit2.length) return false;- for (var len = lit1.length, i = 0; i < len; i++) {- if (!Fay$$equal(lit1[i], lit2[i])) return false;- }- return true;- } else if (lit1 instanceof Fay$$Cons && lit2 instanceof Fay$$Cons) {- do {- if (!Fay$$equal(lit1.car,lit2.car))- return false;- lit1 = Fay$$_(lit1.cdr), lit2 = Fay$$_(lit2.cdr);- if (lit1 === null || lit2 === null)- return lit1 === lit2;- } while (true);- } else if (typeof lit1 == 'object' && typeof lit2 == 'object' && lit1 && lit2 &&- lit1.instance === lit2.instance) {- for(var x in lit1) {- if(!Fay$$equal(lit1[x],lit2[x]))- return false;- }- return true;- } else {- return false;- }-}--// Built-in ==.-function Fay$$eq(x){- return function(y){- return new Fay$$$(function(){- return Fay$$equal(x,y);- });- };-}--function Fay$$eq$36$uncurried(x,y){-- return new Fay$$$(function(){- return Fay$$equal(x,y);- });--}--// Built-in /=.-function Fay$$neq(x){- return function(y){- return new Fay$$$(function(){- return !(Fay$$equal(x,y));- });- };-}--// Built-in /=.-function Fay$$neq$36$uncurried(x,y){-- return new Fay$$$(function(){- return !(Fay$$equal(x,y));- });--}--// Built-in >.-function Fay$$gt(x){- return function(y){- return new Fay$$$(function(){- return Fay$$_(x) > Fay$$_(y);- });- };-}--// Built-in >.-function Fay$$gt$36$uncurried(x,y){-- return new Fay$$$(function(){- return Fay$$_(x) > Fay$$_(y);- });--}--// Built-in <.-function Fay$$lt(x){- return function(y){- return new Fay$$$(function(){- return Fay$$_(x) < Fay$$_(y);- });- };-}---// Built-in <.-function Fay$$lt$36$uncurried(x,y){-- return new Fay$$$(function(){- return Fay$$_(x) < Fay$$_(y);- });--}---// Built-in >=.-function Fay$$gte(x){- return function(y){- return new Fay$$$(function(){- return Fay$$_(x) >= Fay$$_(y);- });- };-}--// Built-in >=.-function Fay$$gte$36$uncurried(x,y){-- return new Fay$$$(function(){- return Fay$$_(x) >= Fay$$_(y);- });--}--// Built-in <=.-function Fay$$lte(x){- return function(y){- return new Fay$$$(function(){- return Fay$$_(x) <= Fay$$_(y);- });- };-}--// Built-in <=.-function Fay$$lte$36$uncurried(x,y){-- return new Fay$$$(function(){- return Fay$$_(x) <= Fay$$_(y);- });--}--// Built-in &&.-function Fay$$and(x){- return function(y){- return new Fay$$$(function(){- return Fay$$_(x) && Fay$$_(y);- });- };-}--// Built-in &&.-function Fay$$and$36$uncurried(x,y){-- return new Fay$$$(function(){- return Fay$$_(x) && Fay$$_(y);- });- ;-}--// Built-in ||.-function Fay$$or(x){- return function(y){- return new Fay$$$(function(){- return Fay$$_(x) || Fay$$_(y);- });- };-}--// Built-in ||.-function Fay$$or$36$uncurried(x,y){-- return new Fay$$$(function(){- return Fay$$_(x) || Fay$$_(y);- });--}--/*******************************************************************************- * Mutable references.- */--// Make a new mutable reference.-function Fay$$Ref(x){- this.value = x;-}--// Write to the ref.-function Fay$$writeRef(ref,x){- ref.value = x;-}--// Get the value from the ref.-function Fay$$readRef(ref,x){- return ref.value;-}--/*******************************************************************************- * Dates.- */-function Fay$$date(str){- return window.Date.parse(str);-}--/*******************************************************************************- * Application code.- */
src/Fay.hs view
@@ -1,48 +1,51 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE TypeSynonymInstances #-} {-# LANGUAGE ViewPatterns #-} -- | Main library entry point. module Fay- (module Fay.Types+ (module Fay.Config+ ,CompileError (..)+ ,CompileState (..)+ ,CompileResult (..) ,compileFile ,compileFileWithState+ ,compileFileWithResult ,compileFromTo ,compileFromToAndGenerateHtml ,toJsName+ ,toTsName ,showCompileError- ,getConfigRuntime- ,getRuntime+ ,readConfigRuntime ) where +import Fay.Compiler.Prelude+ import Fay.Compiler import Fay.Compiler.Misc (ioWarn, printSrcSpanInfo) import Fay.Compiler.Packages import Fay.Compiler.Typecheck+import Fay.Config+import Fay.Runtime import qualified Fay.Exts as F import Fay.Types -import Control.Applicative-import Control.Monad import Data.Aeson (encode) import qualified Data.ByteString.Lazy as L-import Data.Default-import Data.List-import Language.Haskell.Exts.Annotated (prettyPrint)-import Language.Haskell.Exts.Annotated.Syntax+import Language.Haskell.Exts (prettyPrint)+import Language.Haskell.Exts.Syntax import Language.Haskell.Exts.SrcLoc-import Paths_fay import SourceMap (generate) import SourceMap.Types 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 -> FilePath -> Maybe FilePath -> IO () compileFromTo cfg filein fileout = if configTypecheckOnly cfg then do@@ -54,15 +57,15 @@ (compileFromToAndGenerateHtml cfg filein) fileout case result of- Right (out,_) -> maybe (putStrLn out) (`writeFile` out) fileout+ 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,[Mapping]))+-- | Compile the given file and write to the output, also generates HTML and sourcemap files if configured.+compileFromToAndGenerateHtml :: Config -> FilePath -> FilePath -> IO (Either CompileError String) compileFromToAndGenerateHtml config filein fileout = do- result <- compileFile config { configFilePath = Just filein } filein- case result of- Right (out,mappings) -> do+ mres <- compileFileWithResult config { configFilePath = Just filein } filein+ case mres of+ Right res -> do when (configHtmlWrapper config) $ writeFile (replaceExtension fileout "html") $ unlines [ "<!doctype html>"@@ -71,22 +74,23 @@ ," <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>" , unlines . map ((" "++) . makeScriptTagSrc) $ configHtmlJSLibs config , " " ++ makeScriptTagSrc relativeJsPath- , " </script>" , " </head>" , " <body>" , " </body>" , "</html>"] - when (configSourceMap config) $ do- L.writeFile (replaceExtension fileout "map") $- encode $- generate SourceMapping- { smFile = fileout- , smSourceRoot = Nothing- , smMappings = mappings- }+ case (configSourceMap config, resSourceMappings res) of+ (True, Just mappings) ->+ L.writeFile (replaceExtension fileout "map") $+ encode $+ generate SourceMapping+ { smFile = fileout+ , smSourceRoot = Nothing+ , smMappings = mappings+ }+ _ -> return () - return (Right (if configSourceMap config then sourceMapHeader ++ out else out,mappings))+ return $ Right (if configSourceMap config then sourceMapHeader ++ resOutput res else resOutput res) where relativeJsPath = makeRelative (dropFileName fileout) fileout makeScriptTagSrc :: FilePath -> String makeScriptTagSrc s = "<script type=\"text/javascript\" src=\"" ++ s ++ "\"></script>"@@ -94,46 +98,60 @@ Left err -> return (Left err) -- | Compile the given file.-compileFile :: CompileConfig -> FilePath -> IO (Either CompileError (String,[Mapping]))-compileFile config filein = fmap (\(src,maps,_) -> (src,maps)) <$> compileFileWithState config filein+compileFile :: Config -> FilePath -> IO (Either CompileError String)+compileFile config filein = fmap (\(src,_,_) -> src) <$> compileFileWithState config filein --- | Compile a file returning the state.-compileFileWithState :: CompileConfig -> FilePath -> IO (Either CompileError (String,[Mapping],CompileState))+-- | Compile a file returning additional generated metadata.+compileFileWithResult :: Config -> FilePath -> IO (Either CompileError CompileResult)+compileFileWithResult config filein = do+ res <- compileFileWithState config filein+ return $ do+ (s,m,st) <- res+ return CompileResult+ { resOutput = s+ , resImported = map (first F.moduleNameString) $ stateImported st+ , resSourceMappings = m+ }++-- | Compile a file returning the resulting internal state of the compiler.+-- Don't use this directly, it's only exposed for the test suite.+compileFileWithState :: Config -> FilePath -> IO (Either CompileError (String,Maybe [Mapping],CompileState)) compileFileWithState config filein = do- runtime <- getConfigRuntime config+ raw <- readConfigRuntime 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 :: FilePath- -> CompileConfig -> String -> (F.Module -> Compile [JsStmt]) -> String- -> IO (Either CompileError (String,[Mapping],CompileState))+ -> Config -> String -> (F.Module -> Compile [JsStmt]) -> String+ -> IO (Either CompileError (String,Maybe [Mapping],CompileState)) compileToModule filepath config raw with hscode = do- result <- compileViaStr filepath config printState with hscode+ result <- compileViaStr filepath config with hscode return $ case result of Left err -> Left err- Right (PrintState{..},state,_) ->- Right ( generateWrapped (concat $ reverse psOutput)- (stateModuleName state)- , psMappings+ Right (printer,state@CompileState{ stateModuleName = (ModuleName _ modulename) },_) ->+ Right ( pwOutputString pw+ , if null (pwMappings pw) then Nothing else Just (pwMappings pw) , state )- where- generateWrapped jscode (ModuleName _ modulename) =- unlines $ filter (not . null)- [if configExportRuntime config then raw else ""- ,jscode- ,if not (configLibrary config)- then unlines [";"- ,"Fay$$_(" ++ modulename ++ ".main);"- ]- else ""- ]- printState = def { psPretty = configPrettyPrint config- , psLine = length (lines raw) + 3- }+ where+ pw = execPrinter (runtime <> aliases <> printer <> main) pr+ runtime = if configExportRuntime config then write raw else mempty+ aliases = if configPrettyThunks config+ then write . unlines $ [ "var $ = Fay$$$;"+ , "var _ = Fay$$_;"+ , "var __ = Fay$$__;"+ ]+ else mempty+ main = if not (configLibrary config)+ then write $ "Fay$$_(" ++ modulename ++ ".main, true);\n"+ else mempty+ pr = defaultPrintReader+ { prPrettyThunks = configPrettyThunks config+ , prPretty = configPrettyPrint config+ , prPrettyOperators = configPrettyOperators config+ } -- | Convert a Haskell filename to a JS filename. toJsName :: String -> String@@ -141,6 +159,12 @@ ('s':'h':'.': (reverse -> file)) -> file ++ ".js" _ -> x +-- | Convert a Haskell filename to a TypeScript filename.+toTsName :: String -> String+toTsName x = case reverse x of+ ('s':'h':'.': (reverse -> file)) -> file ++ ".ts"+ _ -> x+ -- | Print a compile error for human consumption. showCompileError :: CompileError -> String showCompileError e = case e of@@ -163,13 +187,13 @@ 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+ UnableResolveQualified qname -> "unable to resolve qualified names (this might be a bug):" ++ prettyPrint qname UnsupportedDeclaration d -> "unsupported declaration: " ++ prettyPrint d UnsupportedEnum{} -> "only Int is allowed in enum expressions" 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+ UnsupportedImport i -> "unsupported import syntax: " ++ prettyPrint i UnsupportedLet -> "let not supported here" UnsupportedLetBinding d -> "unsupported let binding: " ++ prettyPrint d UnsupportedLiteral lit -> "unsupported literal syntax: " ++ prettyPrint lit@@ -183,9 +207,8 @@ -- | 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"+readConfigRuntime :: Config -> IO String+readConfigRuntime cfg =+ case configRuntimePath cfg of+ Just path -> readFile path+ Nothing -> return $ getRuntimeSource cfg
src/Fay/Compiler.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-}@@ -19,7 +20,8 @@ ,parseFay) where -import Fay.Compiler.Config+import Fay.Compiler.Prelude+ import Fay.Compiler.Decl import Fay.Compiler.Defaults import Fay.Compiler.Desugar@@ -29,26 +31,23 @@ import Fay.Compiler.InitialPass (initialPass) import Fay.Compiler.Misc import Fay.Compiler.Optimizer+import Fay.Compiler.Parse import Fay.Compiler.PrimOp (findPrimOp) import Fay.Compiler.QName import Fay.Compiler.State import Fay.Compiler.Typecheck-import Fay.Control.Monad.IO+import Fay.Config 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 Control.Monad.State+import Control.Monad.Except (throwError)+import Control.Monad.RWS (gets, modify) -import Data.Maybe import qualified Data.Set as S-import Language.Haskell.Exts.Annotated hiding (name)-import Language.Haskell.Names-import Prelude hiding (mod)+import Language.Haskell.Exts hiding (name)+import Language.Haskell.Names (annotateModule) -------------------------------------------------------------------------------- -- Top level entry points@@ -57,17 +56,16 @@ compileViaStr :: FilePath- -> CompileConfig- -> PrintState+ -> Config -> (F.Module -> Compile [JsStmt]) -> String- -> IO (Either CompileError (PrintState,CompileState,CompileWriter))-compileViaStr filepath cfg printState with from = do+ -> IO (Either CompileError (Printer,CompileState,CompileWriter))+compileViaStr filepath cfg with from = do rs <- defaultCompileReader cfg runTopCompile rs defaultCompileState (parseResult (throwError . uncurry ParseError)- (fmap (\x -> execState (runPrinter (printJS x)) printState) . with)+ (fmap (mconcat . map printJS) . with) (parseFay filepath from)) -- | Compile the top-level Fay module.@@ -95,18 +93,23 @@ -- | Compile given the location and source string. compileFileWithSource :: FilePath -> String -> Compile ([JsStmt], [JsStmt]) compileFileWithSource filepath contents = do- ((hstmts,fstmts),st,wr) <- compileWith filepath compileModuleFromAST compileFileWithSource contents+ exportStdlib <- config configExportStdlib+ ((hstmts,fstmts),st,wr) <- compileWith filepath compileModuleFromAST compileFileWithSource desugar contents modify $ \s -> s { stateImported = stateImported st , stateJsModulePaths = stateJsModulePaths st }- hstmts' <- maybeOptimize $ hstmts ++ writerCons wr ++ makeTranscoding wr+ hstmts' <- maybeOptimize $ hstmts ++ writerCons wr ++ makeTranscoding exportStdlib (stateModuleName st) wr fstmts' <- maybeOptimize fstmts return (hstmts', fstmts') where- makeTranscoding :: CompileWriter -> [JsStmt]- makeTranscoding CompileWriter{..} =- let fay2js = if null writerFayToJs then [] else fayToJsHash writerFayToJs- js2fay = if null writerJsToFay then [] else jsToFayHash writerJsToFay+ makeTranscoding :: Bool -> ModuleName a -> CompileWriter -> [JsStmt]+ makeTranscoding exportStdlib moduleName CompileWriter{..} =+ let fay2js = if null writerFayToJs || (anStdlibModule moduleName && not exportStdlib)+ then []+ else fayToJsHash writerFayToJs+ js2fay = if null writerJsToFay || (anStdlibModule moduleName && not exportStdlib)+ then []+ else jsToFayHash writerJsToFay in fay2js ++ js2fay maybeOptimize :: [JsStmt] -> Compile [JsStmt] maybeOptimize stmts = do@@ -117,62 +120,52 @@ -- | Compile a parse HSE module. compileModuleFromAST :: ([JsStmt], [JsStmt]) -> F.Module -> Compile ([JsStmt], [JsStmt])-compileModuleFromAST (hstmts0, fstmts0) mod''@(Module _ _ pragmas _ _) = do- res <- io $ desugar mod''- case res of- Left err -> throwError err- Right mod' -> do- mod@(Module _ _ _ _ decls) <- annotateModule Haskell2010 defaultExtensions $ mod'- let modName = unAnn $ F.moduleName mod- modify $ \s -> s { stateUseFromString = hasLanguagePragmas ["OverloadedStrings", "RebindableSyntax"] pragmas- }- current <- compileDecls True decls+compileModuleFromAST (hstmts0, fstmts0) mod'@Module{} = do+ ~mod@(Module _ _ pragmas _ decls) <- annotateModule Haskell2010 defaultExtensions 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 modName- extExports <- generateExports- strictExports <- generateStrictExports- let hstmts = hstmts0 ++ modulePaths ++ current ++ extExports- fstmts = fstmts0 ++ strictExports- return $ if exportStdlibOnly- then if anStdlibModule modName- then (hstmts, fstmts)- else ([], [])- else if not exportStdlib && anStdlibModule modName- then ([], [])- else (hstmts, fstmts)+ exportStdlib <- config configExportStdlib+ exportStdlibOnly <- config configExportStdlibOnly+ modulePaths <- createModulePath modName+ extExports <- generateExports+ strictExports <- generateStrictExports+ let hstmts = hstmts0 ++ modulePaths ++ current ++ extExports+ fstmts = fstmts0 ++ strictExports+ return $ if exportStdlibOnly+ then if anStdlibModule modName+ then (hstmts, fstmts)+ else ([], [])+ else if not exportStdlib && anStdlibModule modName+ then ([], [])+ else (hstmts, fstmts) compileModuleFromAST _ mod = throwError $ UnsupportedModuleSyntax "compileModuleFromAST" mod -------------------------------------------------------------------------------- -- 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 :: [F.ModulePragma] -> [String]- flattenPragmas ps = concat $ map pragmaName ps- pragmaName (LanguagePragma _ q) = map unname q- pragmaName _ = []- -- | For a module A.B, generate -- | var A = {}; -- | A.B = {}; createModulePath :: ModuleName a -> Compile [JsStmt] createModulePath (unAnn -> m) = do cfg <- config id- reg <- liftM concat . mapM modPath . mkModulePaths $ m+ let isTs = configTypeScript cfg+ reg <- fmap concat . mapM (modPath isTs) . mkModulePaths $ m strict <- if shouldExportStrictWrapper m cfg- then liftM concat . mapM modPath . mkModulePaths $ (\(ModuleName i n) -> ModuleName i ("Strict." ++ n)) m+ then fmap concat . mapM (modPath isTs) . 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 [])]+ modPath :: Bool -> ModulePath -> Compile [JsStmt]+ modPath isTs mp = whenImportNotGenerated mp $ \(unModulePath -> l) -> case l of+ [n] -> if isTs+ then [JsMapVar (JsNameVar . UnQual () $ Ident () n) (JsObj [])]+ else [JsVar (JsNameVar . UnQual () $ Ident () n) (JsObj [])] _ -> [JsSetModule mp (JsObj [])] whenImportNotGenerated :: ModulePath -> (ModulePath -> [JsStmt]) -> Compile [JsStmt]@@ -221,4 +214,4 @@ -- | 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 a -> Bool-anStdlibModule (ModuleName _ name) = name `elem` ["Prelude","FFI","Fay.FFI","Data.Data"]+anStdlibModule (ModuleName _ name) = name `elem` ["Prelude","FFI","Fay.FFI","Data.Data","Data.Ratio","Debug.Trace","Data.Char"]
− src/Fay/Compiler/Config.hs
@@ -1,70 +0,0 @@-{-# OPTIONS -fno-warn-orphans #-}---- | Configuration functions.--module Fay.Compiler.Config where--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---- | Get all include directories not included through packages.-nonPackageConfigDirectoryIncludePaths :: CompileConfig -> [FilePath]-nonPackageConfigDirectoryIncludePaths = map snd . filter (isJust . fst) . configDirectoryIncludes---- | Add a mapping from (maybe) a package to a source directory-addConfigDirectoryInclude :: Maybe String -> FilePath -> CompileConfig -> CompileConfig-addConfigDirectoryInclude pkg fp cfg = cfg { configDirectoryIncludes = (pkg, fp) : configDirectoryIncludes cfg }---- | Add several include directories.-addConfigDirectoryIncludes :: [(Maybe String,FilePath)] -> CompileConfig -> CompileConfig-addConfigDirectoryIncludes pkgFps cfg = foldl (\c (pkg,fp) -> addConfigDirectoryInclude pkg fp c) cfg pkgFps---- | Add several include directories without package references.-addConfigDirectoryIncludePaths :: [FilePath] -> CompileConfig -> CompileConfig-addConfigDirectoryIncludePaths fps cfg = foldl (flip (addConfigDirectoryInclude Nothing)) cfg fps---- | Add a package to compilation-addConfigPackage :: String -> CompileConfig -> CompileConfig-addConfigPackage pkg cfg = cfg { configPackages = pkg : configPackages cfg }---- | Add several packages to compilation-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- , configExportRuntime = True- , configExportStdlib = True- , configExportStdlibOnly = False- , configDirectoryIncludes = []- , configPrettyPrint = False- , configHtmlWrapper = False- , configHtmlJSLibs = []- , configLibrary = False- , configWarn = True- , configFilePath = Nothing- , configTypecheck = True- , configWall = False- , configGClosure = False- , configPackageConf = Nothing- , configPackages = []- , configBasePath = Nothing- , configStrict = []- , configTypecheckOnly = False- , configRuntimePath = Nothing- , configSourceMap = False- }
src/Fay/Compiler/Decl.hs view
@@ -1,6 +1,6 @@-{-# OPTIONS -fno-warn-name-shadowing #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ViewPatterns #-} @@ -8,45 +8,39 @@ module Fay.Compiler.Decl where +import Fay.Compiler.Prelude+ 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.Annotated+import Control.Monad.Except (throwError)+import Control.Monad.RWS (gets, modify)+import Language.Haskell.Exts hiding (binds, loc, name) +{-# ANN module ("HLint: ignore Reduce duplication"::String) #-}+ -- | Compile Haskell declaration. 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)+compileDecls toplevel = fmap concat . mapM (compileDecl toplevel) -- | Compile a declaration. compileDecl :: Bool -> S.Decl -> Compile [JsStmt] compileDecl toplevel decl = case decl of- pat@PatBind{} -> compilePatBind toplevel Nothing pat+ pat@PatBind{} -> compilePatBind toplevel pat FunBind _ matches -> compileFunCase toplevel matches DataDecl _ (DataType _ ) _ (mkTyVars -> tyvars) constructors _ -> compileDataDecl toplevel tyvars constructors GDataDecl _ (DataType _) _l (mkTyVars -> tyvars) _n decls _ -> compileDataDecl toplevel tyvars (map convertGADT decls)- DataDecl _ (NewType _) _ _ _ _ -> return []+ DataDecl _ (NewType _) _ head' constructors _ ->+ ifOptimizeNewtypes (return [])+ (compileDataDecl toplevel (mkTyVars head') constructors) -- Just ignore type aliases and signatures. TypeDecl {} -> return [] TypeSig {} -> return []@@ -68,23 +62,34 @@ mkTyVars :: S.DeclHead -> [S.TyVarBind]-mkTyVars (DHead _ _ binds) = binds-mkTyVars (DHInfix _ t1 _ t2) = [t1, t2]-mkTyVars (DHParen _ dh) = mkTyVars dh+mkTyVars x = go x []+ where+ go (DHead _ _) = id+ go (DHInfix _ r _) = (r:)+ go (DHParen _ dh) = go dh+ go (DHApp _ dh r) = go dh . (r:) -- | Compile a top-level pattern bind.-compilePatBind :: Bool -> Maybe S.Type -> S.Decl -> Compile [JsStmt]-compilePatBind toplevel sig patDecl = case patDecl of- PatBind srcloc (PVar _ ident) Nothing (UnGuardedRhs _ rhs) Nothing ->- case ffiExp rhs of- Just formatstr -> case sig of- Just sig -> compileFFI ident formatstr sig- Nothing -> throwError $ FfiNeedsTypeSig patDecl- _ -> compileUnguardedRhs toplevel srcloc ident rhs+compilePatBind :: Bool -> S.Decl -> Compile [JsStmt]+compilePatBind toplevel patDecl = case patDecl of+ PatBind _ (PVar _ name')+ (UnGuardedRhs _+ (ExpTypeSig _+ (App _ (Var _ (UnQual _ (Ident _ "ffi")))+ (Lit _ (String _ formatstr _)))+ sig)) Nothing ->+ let name = unAnn name'+ loc = S.srcSpanInfo $ ann name'+ in do+ fun <- compileFFIExp loc (Just name) formatstr sig+ stmt <- bindToplevel toplevel (Just (srcInfoSpan loc)) name fun+ return [stmt]+ PatBind srcloc (PVar _ ident) (UnGuardedRhs _ rhs) Nothing ->+ compileUnguardedRhs toplevel srcloc ident rhs -- TODO: Generalize to all patterns- PatBind srcloc (PVar _ ident) Nothing (UnGuardedRhs _ rhs) (Just bdecls) ->+ PatBind srcloc (PVar _ ident) (UnGuardedRhs _ rhs) (Just bdecls) -> compileUnguardedRhs toplevel srcloc ident (Let S.noI bdecls rhs)- PatBind _ pat Nothing (UnGuardedRhs _ rhs) _bdecls -> case pat of+ PatBind _ pat (UnGuardedRhs _ rhs) _bdecls -> case pat of PList {} -> compilePatBind' pat rhs PTuple{} -> compilePatBind' pat rhs PApp {} -> compilePatBind' pat rhs@@ -95,10 +100,22 @@ compilePatBind' pat rhs = 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]+ m <- compilePat (JsName name) pat []+ m2 <- interleavePatternMatchFailures m pat m+ return (JsVar name exp : m2) + interleavePatternMatchFailures :: [JsStmt] -> S.Pat -> [JsStmt] -> Compile [JsStmt]+ interleavePatternMatchFailures original pat = walk+ where+ walk m = case m of+ [JsIf t b1 []] -> do+ b2 <- walk b1+ return [JsIf t b2 err]+ [JsVar n exp2] -> return [JsVar n exp2]+ stmt:stmts -> (stmt:) <$> walk stmts+ [] -> error $ "Fay bug! Can't compile pat bind for pattern: " ++ show original+ err = [throw ("Irrefutable pattern failed for pattern: " ++ prettyPrint pat) (JsList [])]+ -- | Compile a normal simple pattern binding. compileUnguardedRhs :: Bool -> S.X -> S.Name -> S.Exp -> Compile [JsStmt] compileUnguardedRhs toplevel srcloc ident rhs = do@@ -146,7 +163,7 @@ JsSetConstructor qname $ JsFun (Just $ JsConstructor qname) fields- (for fields $ \field -> JsSetProp JsThis field (JsName field))+ (flip fmap fields $ \field -> JsSetProp JsThis field (JsName field)) Nothing -- Creates a function to initialize the record by regular application@@ -191,18 +208,23 @@ (Just (srcInfoSpan (S.srcSpanInfo srcloc))) name (foldr (\arg inner -> JsFun Nothing [arg] [] (Just inner))- (stmtsThunk (concat pats ++ basecase))+ (stmtsThunk $ deleteAfterReturn (concat pats ++ basecase)) args) return [bind] where+ deleteAfterReturn :: [JsStmt] -> [JsStmt]+ deleteAfterReturn [] = []+ deleteAfterReturn (x@(JsEarlyReturn _):_) = [x]+ deleteAfterReturn (x:xs) = x:deleteAfterReturn xs+ args = zipWith const uniqueNames argslen isWildCardMatch (Match _ _ pats _ _) = all isWildCardPat pats isWildCardMatch (InfixMatch _ pat _ pats _ _) = all isWildCardPat (pat:pats) compileCase :: S.Match -> Compile [JsStmt]- compileCase (InfixMatch l pat name pats rhs binds) =- compileCase $ Match l name (pat:pats) rhs binds+ compileCase (InfixMatch l pat nm pats rhs binds) =+ compileCase $ Match l nm (pat:pats) rhs binds compileCase match@(Match _ _ pats rhs _) = do whereDecls' <- whereDecls match rhsform <- compileRhs rhs
src/Fay/Compiler/Defaults.hs view
@@ -4,21 +4,21 @@ module Fay.Compiler.Defaults where -import Fay.Compiler.Config-import Fay.Compiler.Decl (compileDecls)-import Fay.Compiler.Exp (compileLit)+import Fay.Compiler.Decl (compileDecls)+import Fay.Compiler.Exp (compileLit)+import Fay.Config import Fay.Types import Paths_fay -import Data.Map as M-import Data.Set as S+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 -> IO CompileReader defaultCompileReader config = do srcdir <- faySourceDir return CompileReader
src/Fay/Compiler/Desugar.hs view
@@ -1,70 +1,59 @@-{-# LANGUAGE DeriveFunctor #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE MultiParamTypeClasses #-}+-- | Desugars a reasonable amount of syntax to reduce duplication in code generation.+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE MonoLocalBinds #-} module Fay.Compiler.Desugar- (desugar+ ( desugar+ , desugar'+ , desugarExpParen+ , desugarPatParen ) where +import Fay.Compiler.Prelude++import Fay.Compiler.Desugar.Name+import Fay.Compiler.Desugar.Types+import Fay.Compiler.Misc (ffiExp, hasLanguagePragma)+import Fay.Compiler.QName (unQual, unname) import Fay.Exts.NoAnnotation (unAnn) import Fay.Types (CompileError (..)) -import Control.Applicative-import Control.Monad.Error-import Control.Monad.Reader-import Data.Data (Data)+import Control.Monad.Except (throwError)+import Control.Monad.Reader (asks) import qualified Data.Generics.Uniplate.Data as U-import Data.Maybe-import Data.Typeable (Typeable)-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 :: (Data l, Typeable l) => 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--+import Language.Haskell.Exts hiding (binds, loc, name) -- | Top level, desugar a whole module possibly returning errors-desugar :: (Data l, Typeable l) => Module l -> IO (Either CompileError (Module l))-desugar md = runDesugar $+desugar :: (Data l, Typeable l) => l -> Module l -> IO (Either CompileError (Module l))+desugar = desugar' "$gen"++-- | Desugar with the option to specify a prefix for generated names.+-- Useful if you want to provide valid haskell names that HSE can print.+desugar' :: (Data l, Typeable l) => String -> l -> Module l -> IO (Either CompileError (Module l))+desugar' prefix emptyAnnotation md = runDesugar prefix emptyAnnotation $ checkEnum md >> desugarSection md >>= desugarListComp- >>= return . desugarTupleCon+ >>= desugarTupleCon >>= return . desugarPatParen >>= return . desugarFieldPun >>= return . desugarPatFieldPun >>= desugarDo >>= desugarTupleSection---- | Desugaring+ >>= desugarImplicitPrelude+ >>= desugarFFITypeSigs+ >>= desugarLCase+ >>= return . desugarMultiIf+ >>= return . desugarInfixOp+ >>= return . desugarInfixPat+ >>= return . desugarExpParen+{-# ANN desugar' "HLint: ignore Use <$>" #-} -desugarSection :: (Data l, Typeable l) => Module l -> Desugar (Module l)+-- | (a `f`) => \b -> a `f` b+-- (`f` b) => \a -> a `f` b+desugarSection :: (Data l, Typeable l) => Module l -> Desugar l (Module l) desugarSection = transformBiM $ \ex -> case ex of LeftSection l e q -> withScopedTmpName l $ \tmp -> return $ Lambda l [PVar l tmp] (InfixApp l e q (Var l (UnQual l tmp)))@@ -72,13 +61,13 @@ return $ Lambda l [PVar l tmp] (InfixApp l (Var l (UnQual l tmp)) q e) _ -> return ex -desugarDo :: (Data l, Typeable l) => Module l -> Desugar (Module l)+-- | Convert do notation into binds and thens.+desugarDo :: (Data l, Typeable l) => Module l -> Desugar l (Module l) desugarDo = transformBiM $ \ex -> case ex of Do _ stmts -> maybe (throwError EmptyDoBlock) return $ foldl desugarStmt' Nothing (reverse stmts) _ -> return ex --- | Convert do notation into binds and thens.-desugarStmt' :: Maybe (Exp l) -> (Stmt l) -> Maybe (Exp l)+desugarStmt' :: Maybe (Exp l) -> Stmt l -> Maybe (Exp l) desugarStmt' inner stmt = maybe initStmt subsequentStmt inner where@@ -101,39 +90,55 @@ Just $ InfixApp s exp (QVarOp s $ UnQual s $ Symbol s ">>=")- (Lambda s [pat] (inner'))+ (Lambda s [pat] inner') -- | (,) => \x y -> (x,y) -- (,,) => \x y z -> (x,y,z) -- etc-desugarTupleCon :: (Data l, Typeable l) => Module l -> Module l-desugarTupleCon = transformBi $ \ex -> case ex of- Var _ (Special _ t@TupleCon{}) -> fromTupleCon ex t- Con _ (Special _ t@TupleCon{}) -> fromTupleCon ex t- _ -> ex+desugarTupleCon :: (Data l, Typeable l) => Module l -> Desugar l (Module l)+desugarTupleCon md = do+ prefix <- asks readerTmpNamePrefix+ return $ flip transformBi md $ \ex -> case ex of+ Var _ (Special _ t@TupleCon{}) -> fromTupleCon prefix ex t+ Con _ (Special _ t@TupleCon{}) -> fromTupleCon prefix ex t+ _ -> ex where- fromTupleCon :: Exp l -> SpecialCon l -> Exp l- fromTupleCon e s = fromMaybe e $ case s of+ fromTupleCon :: String -> Exp l -> SpecialCon l -> Exp l+ fromTupleCon prefix e s = fromMaybe e $ 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)..]+ names = take n $ unscopedTmpNames l prefix params = PVar l <$> names body = Tuple l b (Var l . UnQual l <$> names) _ -> Nothing -desugarTupleSection :: (Data l, Typeable l) => Module l -> Desugar (Module l)-desugarTupleSection = transformBiM $ \ex -> case ex of- TupleSection l _ mes -> do- (names, lst) <- genSlotNames l mes (varNames l)- return $ Lambda l (map (PVar l) names) (Tuple l Unboxed lst)+-- | \case { ... } => \foo -> case foo of { ... }+desugarLCase :: (Data l, Typeable l) => Module l -> Desugar l (Module l)+desugarLCase = transformBiM $ \ex -> case ex of+ LCase l alts -> withScopedTmpName l $ \n -> return $ Lambda l [PVar l n] (Case l (Var l (UnQual l n)) alts) _ -> return ex++-- | if | p -> x | q -> y => case () of _ | p -> x | q -> y+desugarMultiIf :: (Data l, Typeable l) => Module l -> Module l+desugarMultiIf = transformBi $ \ex -> case ex of+ MultiIf l alts -> Case l (Con l (Special l (UnitCon l)))+ [Alt l (PWildCard l) (GuardedRhss l alts) Nothing]+ _ -> ex++-- | (a,) => \b -> (a,b)+desugarTupleSection :: (Data l, Typeable l) => Module l -> Desugar l (Module l)+desugarTupleSection md = do+ prefix <- asks readerTmpNamePrefix+ flip transformBiM md $ \ex -> case ex of+ TupleSection l _ mes -> do+ (names, lst) <- genSlotNames l mes (unscopedTmpNames l prefix)+ return $ Lambda l (map (PVar l) names) (Tuple l Boxed lst)+ _ -> return ex where- varNames :: l -> [Name l]- varNames l = map (\i -> Ident l ("$gen_" ++ show i)) [0::Int ..] - genSlotNames :: l -> [Maybe (Exp l)] -> [Name l] -> Desugar ([Name l], [Exp l])+ genSlotNames :: l -> [Maybe (Exp l)] -> [Name l] -> Desugar l ([Name l], [Exp l]) genSlotNames _ [] _ = return ([], []) genSlotNames l (Nothing : rest) ns = do -- it's safe to use head/tail here because ns is an infinite list@@ -149,19 +154,20 @@ PParen _ p -> p _ -> pt +-- | {a} => {a=a} for R{a} expressions desugarFieldPun :: (Data l, Typeable l) => Module l -> Module l desugarFieldPun = transformBi $ \f -> case f of- FieldPun l n -> let dn = UnQual l n in FieldUpdate l dn (Var l dn)+ FieldPun l n -> FieldUpdate l n (Var l n) _ -> f +-- | {a} => {a=a} for R{a} patterns desugarPatFieldPun :: (Data l, Typeable l) => Module l -> Module l desugarPatFieldPun = transformBi $ \pf -> case pf of- -- {a} => {a=a} for R{a}- PFieldPun l n -> PFieldPat l (UnQual l n) (PVar l n)+ PFieldPun l n -> PFieldPat l n (PVar l (unQual n)) _ -> pf -- | Desugar list comprehensions.-desugarListComp :: (Data l, Typeable l) => Module l -> Desugar (Module l)+desugarListComp :: (Data l, Typeable l) => Module l -> Desugar l (Module l) desugarListComp = transformBiM $ \ex -> case ex of ListComp l exp stmts -> desugarListComp' l exp stmts _ -> return ex@@ -187,7 +193,7 @@ -- syntax to GHC.Base.Enum instead of using our Enum class so we check -- for obviously incorrect usages and throw an error on them. This can -- only checks literals, but it helps a bit.-checkEnum :: (Data l, Typeable l) => Module l -> Desugar ()+checkEnum :: (Data l, Typeable l) => Module l -> Desugar l () checkEnum = mapM_ f . universeBi where f ex = case ex of@@ -197,8 +203,8 @@ e@(EnumFromThenTo _ e1 e2 e3) -> checkIntOrUnknown e [e1,e2,e3] _ -> return () - checkIntOrUnknown :: Exp l -> [Exp l] -> Desugar ()- checkIntOrUnknown exp es = when (not $ any isIntOrUnknown es) (throwError . UnsupportedEnum $ unAnn exp)+ checkIntOrUnknown :: Exp l -> [Exp l] -> Desugar l ()+ checkIntOrUnknown exp es = unless (any isIntOrUnknown es) (throwError . UnsupportedEnum $ unAnn exp) isIntOrUnknown :: Exp l -> Bool isIntOrUnknown e = case e of Con {} -> False@@ -211,6 +217,138 @@ EnumFromThen {} -> False EnumFromThenTo {} -> False _ -> True++-- | Adds an explicit import Prelude statement when appropriate.+desugarImplicitPrelude :: (Data l, Typeable l) => Module l -> Desugar l (Module l)+desugarImplicitPrelude m =+ if preludeNotNeeded+ then return m+ else addPrelude m+ where+ preludeNotNeeded = hasExplicitPrelude m ||+ hasLanguagePragma "NoImplicitPrelude" (getPragmas m)++ getPragmas :: (Data l, Typeable l) => Module l -> [ModulePragma l]+ getPragmas = universeBi++ getImportDecls :: Module l -> [ImportDecl l]+ getImportDecls (Module _ _ _ decls _) = decls+ getImportDecls _ = []++ setImportDecls :: [ImportDecl l] -> Module l -> Module l+ setImportDecls decls (Module a b c _ d) = Module a b c decls d+ setImportDecls _ mod = mod++ hasExplicitPrelude :: Module l -> Bool+ hasExplicitPrelude = any isPrelude . getImportDecls++ isPrelude :: ImportDecl l -> Bool+ isPrelude decl = case importModule decl of+ ModuleName _ name -> name == "Prelude"++ addPrelude :: Module l -> Desugar l (Module l)+ addPrelude mod = do+ let decls = getImportDecls mod+ prelude <- getPrelude+ return $ setImportDecls (prelude : decls) mod++ getPrelude :: Desugar l (ImportDecl l)+ getPrelude = do+ noInfo <- asks readerNoInfo+ return $ ImportDecl noInfo (ModuleName noInfo "Prelude") False False False Nothing Nothing Nothing++desugarFFITypeSigs :: (Data l, Typeable l) => Module l -> Desugar l (Module l)+desugarFFITypeSigs = desugarToplevelFFITypeSigs >=> desugarBindsTypeSigs++-- | For each toplevel FFI pattern binding, search the module for the relevant+-- type declaration; if found, add a type signature to the ffi expression.+-- e.g.+-- foo :: Int+-- foo = ffi "3"+-- becomes+-- foo :: Int+-- foo = ffi "3" :: Int+desugarToplevelFFITypeSigs :: (Data l, Typeable l) => Module l -> Desugar l (Module l)+desugarToplevelFFITypeSigs m = case m of+ Module a b c d decls -> do+ decls' <- addFFIExpTypeSigs decls+ return $ Module a b c d decls'+ _ -> return m++desugarBindsTypeSigs :: (Data l, Typeable l) => Module l -> Desugar l (Module l)+desugarBindsTypeSigs = transformBiM $ \(BDecls srcInfo decls) -> do+ decls' <- addFFIExpTypeSigs decls+ return $ BDecls srcInfo decls'++addFFIExpTypeSigs :: (Data l, Typeable l) => [Decl l] -> Desugar l [Decl l]+addFFIExpTypeSigs decls = do+ let typeSigs = getTypeSigs decls+ sequence $ go typeSigs decls+ where+ -- | Create a lookup list mapping names to types, for all the types declared+ -- through standalone (ie: not in an expression) type signatures at this+ -- scope level.+ getTypeSigs ds = [ (unname n, typ) | TypeSig _ names typ <- ds, n <- names ]++ go typeSigs = map (addTypeSig typeSigs)++ addTypeSig typeSigs decl = case decl of+ (PatBind loc pat rhs binds) ->+ case getUnguardedRhs rhs of+ Just (srcInfo, rhExp) ->+ if isFFI rhExp+ then do+ rhExp' <- addSigToExp typeSigs decl rhExp+ return $ PatBind loc pat (UnGuardedRhs srcInfo rhExp') binds+ else return decl+ _ -> return decl+ _ -> return decl++ getUnguardedRhs rhs = case rhs of+ (UnGuardedRhs srcInfo exp) -> Just (srcInfo, exp)+ _ -> Nothing++ isFFI = isJust . ffiExp++ -- | Adds an explicit type signature to an expression (which is assumed to+ -- be the RHS of a declaration). This should only need to be called for FFI+ -- function declarations.+ -- Arguments:+ -- sigs: List of toplevel type signatures+ -- decl: The declaration, which should be a PatBind.+ -- rhExp: Expression comprising the RHS of the declaration+ addSigToExp typeSigs decl rhExp = case getTypeFor typeSigs decl of+ Just typ -> do+ noInfo <- asks readerNoInfo+ return $ ExpTypeSig noInfo rhExp typ+ Nothing -> return rhExp++ getTypeFor typeSigs decl = case decl of+ (PatBind _ (PVar _ name) _ _) -> lookup (unname name) typeSigs+ _ -> Nothing++-- | a `op` b => op a b+-- a + b => (+) a b+-- for expressions+desugarInfixOp :: (Data l, Typeable l) => Module l -> Module l+desugarInfixOp = transformBi $ \ex -> case ex of+ InfixApp l e1 oper e2 -> App l (App l (getOp oper) e1) e2+ where+ getOp (QVarOp l' o) = Var l' o+ getOp (QConOp l' o) = Con l' o+ _ -> ex++-- | a : b => (:) a b for patterns+desugarInfixPat :: (Data l, Typeable l) => Module l -> Module l+desugarInfixPat = transformBi $ \pt -> case pt of+ PInfixApp l p1 iop p2 -> PApp l iop [p1, p2]+ _ -> pt++-- | (a) => a for patterns+desugarExpParen :: (Data l, Typeable l) => Module l -> Module l+desugarExpParen = transformBi $ \ex -> case ex of+ Paren _ e -> e+ _ -> ex transformBi :: U.Biplate (from a) (to a) => (to a -> to a) -> from a -> from a transformBi = U.transformBi
+ src/Fay/Compiler/Desugar/Name.hs view
@@ -0,0 +1,29 @@+-- | Generate names while desugaring.+module Fay.Compiler.Desugar.Name+ ( withScopedTmpName+ , unscopedTmpNames+ ) where++import Fay.Compiler.Prelude++import Fay.Compiler.Desugar.Types++import Control.Monad.Reader (asks, local)+import Language.Haskell.Exts (Name (..))++-- | Generate a temporary, SCOPED name for testing conditions and+-- such. We don't have name tracking yet, so instead we use this.+withScopedTmpName :: (Data l, Typeable l) => l -> (Name l -> Desugar l a) -> Desugar l a+withScopedTmpName l f = do+ prefix <- asks readerTmpNamePrefix+ n <- asks readerNameDepth+ local (\r -> r { readerNameDepth = n + 1 }) $+ f $ tmpName l prefix n++-- | Generates temporary names where the scope doesn't matter.+unscopedTmpNames :: l -> String -> [Name l]+unscopedTmpNames l prefix = map (tmpName l prefix) [0..]++-- | Don't call this directly, use withScopedTmpName or unscopedTmpNames instead.+tmpName :: l -> String -> Int -> Name l+tmpName l prefix n = Ident l $ prefix ++ show n
+ src/Fay/Compiler/Desugar/Types.hs view
@@ -0,0 +1,43 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE MultiParamTypeClasses #-}++-- | The transformer stack used during desugaring.++module Fay.Compiler.Desugar.Types+ ( DesugarReader (..)+ , Desugar+ , runDesugar+ ) where++import Fay.Types (CompileError (..))++#if __GLASGOW_HASKELL__ < 710+import Control.Applicative+#endif+import Control.Monad.Except+import Control.Monad.Reader++data DesugarReader l = DesugarReader+ { readerNameDepth :: Int+ , readerNoInfo :: l+ , readerTmpNamePrefix :: String+ }++newtype Desugar l a = Desugar+ { unDesugar :: (ReaderT (DesugarReader l)+ (ExceptT CompileError IO))+ a+ } deriving ( MonadReader (DesugarReader l)+ , MonadError CompileError+ , MonadIO+ , Monad+ , Functor+ , Applicative+ )++runDesugar :: String -> l -> Desugar l a -> IO (Either CompileError a)+runDesugar tmpNamePrefix emptyAnnotation m =+ runExceptT (runReaderT (unDesugar m) (DesugarReader 0 emptyAnnotation tmpNamePrefix))
src/Fay/Compiler/Exp.hs view
@@ -1,6 +1,6 @@-{-# OPTIONS -fno-warn-name-shadowing #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeSynonymInstances #-} {-# LANGUAGE ViewPatterns #-}@@ -14,60 +14,60 @@ ,compileLit ) where +import Fay.Compiler.Prelude+ 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.Config 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 Control.Monad.Except (throwError)+import Control.Monad.RWS (asks, gets) import qualified Data.Char as Char-import Language.Haskell.Exts.Annotated-import Language.Haskell.Names+import Language.Haskell.Exts hiding (alt, binds, name, op)+import Language.Haskell.Names (NameInfo (RecExpWildcard), Scoped (Scoped)) -- | Compile Haskell expression. compileExp :: S.Exp -> Compile JsExp-compileExp exp = case exp of- Paren _ exp -> compileExp exp+compileExp e = case e of Var _ qname -> compileVar qname- Lit _ lit -> compileLit lit+ Lit s lit -> compileLit (Signless s) lit+ App _ (Var _ (UnQual _ (Ident _ "ffi"))) _ -> throwError $ FfiNeedsTypeSig e 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 _ (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 {} -> shouldBeDesugared exp- Do {} -> shouldBeDesugared exp- LeftSection {} -> shouldBeDesugared exp- RightSection {} -> shouldBeDesugared exp- TupleSection {} -> shouldBeDesugared exp- ExpTypeSig _ exp sig ->- case ffiExp exp of- Nothing -> compileExp exp- Just formatstr -> compileFFIExp (S.srcSpanInfo $ ann exp) Nothing formatstr sig-- exp -> throwError (UnsupportedExpression exp)+ RecConstr _ name fieldUpdates -> compileRecConstr e name fieldUpdates+ RecUpdate _ rec fieldUpdates -> compileRecUpdate e rec fieldUpdates+ ExpTypeSig _ exp sig -> case ffiExp exp of+ Nothing -> compileExp exp+ Just formatstr -> compileFFIExp (S.srcSpanInfo $ ann exp) Nothing formatstr sig+ ListComp {} -> shouldBeDesugared e+ Do {} -> shouldBeDesugared e+ LeftSection {} -> shouldBeDesugared e+ RightSection {} -> shouldBeDesugared e+ TupleSection {} -> shouldBeDesugared e+ Paren {} -> shouldBeDesugared e+ InfixApp {} -> shouldBeDesugared e+ exp -> throwError $ UnsupportedExpression exp -- | Compile variable. compileVar :: S.QName -> Compile JsExp@@ -75,35 +75,42 @@ compileVar qname = do nc <- lookupNewtypeConst qname nd <- lookupNewtypeDest qname- if (nc /= Nothing || nd /= Nothing)+ if isJust nc || isJust nd then -- variable is either a newtype constructor or newtype destructor, -- replace it with identity function return idFun- else do- qname <- unsafeResolveName qname- return (JsName (JsNameVar qname))+ else JsName . JsNameVar <$> unsafeResolveName qname where idFun = JsFun Nothing [JsTmp 1] [] (Just (JsName $ JsTmp 1)) -- | Compile Haskell literal.-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)))+compileLit :: S.Sign -> S.Literal -> Compile JsExp+compileLit sign lit = case lit of+ Char _ ch _ -> return (JsLit (JsChar ch))+ Int _ integer _ -> return (JsLit (JsInt (fromIntegral (applySign integer)))) -- FIXME:+ Frac _ rational _ -> return (JsLit (JsFloating (fromRational (applySign 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)+ return $ if fromString+ then JsLit (JsStr string)+ else JsApp (JsName (JsBuiltIn "list")) [JsLit (JsStr string)]+ _ -> throwError $ UnsupportedLiteral lit+ where+ applySign :: Num a => a -> a+ applySign = case sign of+ Signless _ -> id+ Negative _ -> negate -- | Compile simple application. compileApp :: S.Exp -> S.Exp -> Compile JsExp compileApp exp1@(Con _ q) exp2 =- maybe (compileApp' exp1 exp2) (const $ compileExp exp2) =<< lookupNewtypeConst q+ ifOptimizeNewtypes+ (maybe (compileApp' exp1 exp2) (const $ compileExp exp2) =<< lookupNewtypeConst q)+ (compileApp' exp1 exp2) compileApp exp1@(Var _ q) exp2 =- maybe (compileApp' exp1 exp2) (const $ compileExp exp2) =<< lookupNewtypeDest q+ ifOptimizeNewtypes+ (maybe (compileApp' exp1 exp2) (const $ compileExp exp2) =<< lookupNewtypeDest q)+ (compileApp' exp1 exp2) compileApp exp1 exp2 = compileApp' exp1 exp2 @@ -119,9 +126,9 @@ -- 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 -> S.Exp -> Compile JsExp- method1 exp1 exp2 =- JsApp <$> (forceFlatName <$> return exp1)- <*> fmap return (compileExp exp2)+ method1 e1 e2 =+ JsApp <$> (forceFlatName <$> return e1)+ <*> fmap return (compileExp e2) where forceFlatName name = JsApp (JsName JsForce) [name] @@ -130,9 +137,9 @@ -- 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 -> S.Exp -> Compile JsExp- method2 exp1 exp2 = fmap flatten $- JsApp <$> return exp1- <*> fmap return (compileExp exp2)+ method2 e1 e2 = fmap flatten $+ JsApp <$> return e1+ <*> fmap return (compileExp e2) where flatten (JsApp op args) = case op of@@ -144,21 +151,6 @@ compileNegApp :: S.Exp -> Compile JsExp compileNegApp e = JsNegApp . force <$> compileExp e --- | Compile an infix application, optimizing the JS cases.-compileInfixApp :: S.Exp -> S.QOp -> S.Exp -> Compile JsExp-compileInfixApp exp1 ap exp2 = case exp1 of- Con _ q -> do- newtypeConst <- lookupNewtypeConst q- case newtypeConst of- Just _ -> compileExp exp2- Nothing -> normalApp- _ -> normalApp- where- normalApp = compileExp (App noI (App noI (Var noI op) exp1) exp2)- op = getOp ap- getOp (QVarOp _ op) = op- getOp (QConOp _ op) = op- -- | Compile a let expression. compileLet :: [S.Decl] -> S.Exp -> Compile JsExp compileLet decls exp = do@@ -171,10 +163,10 @@ compileLetDecl decl = do compileDecls <- asks readerCompileDecls case decl of- decl@PatBind{} -> compileDecls False [decl]- decl@FunBind{} -> compileDecls False [decl]- TypeSig{} -> return []- _ -> throwError (UnsupportedLetBinding decl)+ PatBind{} -> compileDecls False [decl]+ FunBind{} -> compileDecls False [decl]+ TypeSig{} -> return []+ _ -> throwError $ UnsupportedLetBinding decl -- | Compile a list expression. compileList :: [S.Exp] -> Compile JsExp@@ -191,55 +183,57 @@ -- | Compile case expressions. compileCase :: S.Exp -> [S.Alt] -> Compile JsExp-compileCase exp alts = do- exp <- compileExp exp+compileCase e alts = do+ exp <- compileExp e withScopedTmpJsName $ \tmpName -> do- pats <- fmap optimizePatConditions $ mapM (compilePatAlt (JsName tmpName)) alts+ pats <- optimizePatConditions <$> mapM (compilePatAlt (JsName tmpName)) alts+ let (xx,flag) = deleteAfterReturn (concat pats) return $ JsApp (JsFun Nothing [tmpName]- (concat pats)- (if any isWildCardAlt alts+ xx+ (if (flag || any isWildCardAlt alts) then Nothing else Just (throwExp "unhandled case" (JsName tmpName)))) [exp]+ where+ deleteAfterReturn :: [JsStmt] -> ([JsStmt],Bool)+ deleteAfterReturn [] = ([],False)+ deleteAfterReturn (x@(JsEarlyReturn _):_) = ([x],True)+ deleteAfterReturn (x:xs) = ((x:xx),flag)+ where (xx,flag) = deleteAfterReturn xs -- | Compile the given pattern against the given expression. compilePatAlt :: JsExp -> S.Alt -> Compile [JsStmt]-compilePatAlt exp alt@(Alt _ pat rhs wheres) = case wheres of- Just (BDecls _ (_ : _)) -> throwError (UnsupportedWhereInAlt alt)- Just (IPBinds _ (_ : _)) -> throwError (UnsupportedWhereInAlt alt)- _ -> do+compilePatAlt exp a@(Alt _ pat rhs wheres) = case wheres of+ Just (BDecls _ (_ : _)) -> throwError $ UnsupportedWhereInAlt a+ Just (IPBinds _ (_ : _)) -> throwError $ UnsupportedWhereInAlt a+ _ -> do alt <- compileGuardedAlt rhs compilePat exp pat [alt] -- | Compile a guarded alt.-compileGuardedAlt :: S.GuardedAlts -> Compile JsStmt+compileGuardedAlt :: S.Rhs -> Compile JsStmt compileGuardedAlt alt = case alt of- UnGuardedAlt _ exp -> JsEarlyReturn <$> compileExp exp- GuardedAlts _ alts -> compileGuards (map altToRhs alts)- where- altToRhs (GuardedAlt l s e) = GuardedRhs l s e+ UnGuardedRhs _ exp -> JsEarlyReturn <$> compileExp exp+ GuardedRhss _ alts -> compileGuards alts -- | Compile guards 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) = makeIf <$> fmap force (compileExp guard) <*> compileExp exp <*> if null rest then return [] else do gs' <- compileGuards rest return [gs']- where makeIf gs e gss = JsIf gs [JsEarlyReturn e] gss+ where makeIf gs e = JsIf gs [JsEarlyReturn e] compileGuards rhss = throwError . UnsupportedRhs . GuardedRhss noI $ rhss -- | Compile a lambda. compileLambda :: [S.Pat] -> S.Exp -> Compile JsExp-compileLambda pats exp = do- exp <- compileExp exp+compileLambda pats = compileExp >=> \exp -> do stmts <- generateStatements exp case stmts of [JsEarlyReturn fun@JsFun{}] -> return fun@@ -250,9 +244,13 @@ generateStatements exp = foldM (\inner (param,pat) -> do stmts <- compilePat (JsName param) pat inner- return [JsEarlyReturn (JsFun Nothing [param] (stmts ++ [unhandledcase param | not allfree]) Nothing)])+ return [JsEarlyReturn (JsFun Nothing [param] (deleteAfterReturn $ stmts ++ [unhandledcase param | not allfree]) Nothing)]) [JsEarlyReturn exp] (reverse (zip uniqueNames pats))+ deleteAfterReturn :: [JsStmt] -> [JsStmt]+ deleteAfterReturn [] = []+ deleteAfterReturn (x@(JsEarlyReturn _):_) = [x]+ deleteAfterReturn (x:xs) = x:deleteAfterReturn xs -- | Compile [e1..] arithmetic sequences. compileEnumFrom :: S.Exp -> Compile JsExp@@ -290,36 +288,36 @@ -- | Compile a record construction with named fields -- | GHC will warn on uninitialized fields, they will be undefined in JS.-compileRecConstr :: S.QName -> [S.FieldUpdate] -> Compile JsExp-compileRecConstr name fieldUpdates = do+compileRecConstr :: S.Exp -> S.QName -> [S.FieldUpdate] -> Compile JsExp+compileRecConstr origExp name fieldUpdates = do -- var obj = new $_Type() let unQualName = withIdent lowerFirst . unQualify $ unAnn name qname <- unsafeResolveName name let record = JsVar (JsNameVar unQualName) (JsNew (JsConstructor qname) [])- setFields <- liftM concat (forM fieldUpdates (updateStmt name))+ setFields <- concat <$> forM fieldUpdates (updateStmt name) return $ JsApp (JsFun Nothing [] (record:setFields) (Just . JsName . JsNameVar . withIdent lowerFirst . 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 $ withIdent lowerFirst $ unQualify o) (JsNameVar $ unQualify field) exp]- updateStmt o (FieldWildcard (wildcardFields -> fields)) = do- return $ for fields $ \fieldName -> JsSetProp (JsNameVar . withIdent lowerFirst . unQualify . unAnn $ o)+ updateStmt o (FieldWildcard (wildcardFields -> fields)) =+ return $ flip fmap fields $ \fieldName -> JsSetProp (JsNameVar . withIdent lowerFirst . unQualify . unAnn $ o) (JsNameVar fieldName) (JsName $ JsNameVar fieldName) -- I couldn't find a code that generates (FieldUpdate (FieldPun ..))- updateStmt _ u = error ("updateStmt: " ++ show u)+ updateStmt _ _ = throwError $ UnsupportedExpression origExp wildcardFields l = case l of- Scoped (RecExpWildcard es) _ -> map (unQualify . origName2QName) . map fst $ es+ Scoped (RecExpWildcard es) _ -> map (unQualify . origName2QName . fst) es _ -> [] lowerFirst :: String -> String lowerFirst "" = "" lowerFirst (x:xs) = '_' : Char.toLower x : xs -- | Compile a record update.-compileRecUpdate :: S.Exp -> [S.FieldUpdate] -> Compile JsExp-compileRecUpdate rec fieldUpdates = do+compileRecUpdate :: S.Exp -> S.Exp -> [S.FieldUpdate] -> Compile JsExp+compileRecUpdate origExp rec fieldUpdates = do record <- force <$> compileExp rec let copyName = UnQual () $ Ident () "$_record_to_update" copy = JsVar (JsNameVar copyName)@@ -332,14 +330,14 @@ 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"+ updateExp _ FieldWildcard{} = throwError $ UnsupportedExpression origExp -- | Make a Fay list. makeList :: [JsExp] -> JsExp makeList exps = JsApp (JsName $ JsBuiltIn "list") [JsList exps] -- | Optimize short literal [e1..e3] arithmetic sequences.-optEnumFromTo :: CompileConfig -> JsExp -> JsExp -> Maybe JsExp+optEnumFromTo :: Config -> JsExp -> JsExp -> Maybe JsExp optEnumFromTo cfg (JsLit f) (JsLit t) = if configOptimize cfg then case (f,t) of@@ -348,14 +346,14 @@ _ -> Nothing else Nothing where strict :: (Enum a, Ord a, Num a) => (a -> JsLit) -> a -> a -> Maybe JsExp- strict litfn f t =- if fromEnum t - fromEnum f < maxStrictASLen- then Just . makeList . map (JsLit . litfn) $ enumFromTo f t+ strict litfn fr to =+ if fromEnum to - fromEnum fr < maxStrictASLen+ then Just . makeList . map (JsLit . litfn) $ enumFromTo fr to else Nothing optEnumFromTo _ _ _ = Nothing -- | Optimize short literal [e1,e2..e3] arithmetic sequences.-optEnumFromThenTo :: CompileConfig -> JsExp -> JsExp -> JsExp -> Maybe JsExp+optEnumFromThenTo :: Config -> JsExp -> JsExp -> JsExp -> Maybe JsExp optEnumFromThenTo cfg (JsLit fr) (JsLit th) (JsLit to) = if configOptimize cfg then case (fr,th,to) of@@ -364,10 +362,10 @@ _ -> Nothing else Nothing where strict :: (Enum a, Ord a, Num a) => (a -> JsLit) -> a -> a -> a -> Maybe JsExp- strict litfn fr th to =- if (fromEnum to - fromEnum fr) `div`- (fromEnum th - fromEnum fr) + 1 < maxStrictASLen- then Just . makeList . map (JsLit . litfn) $ enumFromThenTo fr th to+ strict litfn fr' th' to' =+ if (fromEnum to' - fromEnum fr') `div`+ (fromEnum th' - fromEnum fr') + 1 < maxStrictASLen+ then Just . makeList . map (JsLit . litfn) $ enumFromThenTo fr' th' to' else Nothing optEnumFromThenTo _ _ _ _ = Nothing
src/Fay/Compiler/FFI.hs view
@@ -1,106 +1,89 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE ViewPatterns #-}-{-# OPTIONS -Wall #-} -- | Compile FFI definitions. module Fay.Compiler.FFI (emitFayToJs ,emitJsToFay- ,compileFFI ,compileFFIExp ,jsToFayHash ,fayToJsHash ,typeArity ) where +import Fay.Compiler.Prelude+ import Fay.Compiler.Misc-import Fay.Compiler.Print (printJSString)+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.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.Arrow ((***))-import Control.Monad.Error-import Control.Monad.Writer-import Data.Char+import Control.Monad.Except (throwError)+import Control.Monad.Writer (tell) import Data.Generics.Schemes-import Data.List-import Data.Maybe-import Data.String-import Language.ECMAScript3.Parser as JS+import Language.ECMAScript3.Parser as JS import Language.ECMAScript3.Syntax-import Language.Haskell.Exts.Annotated (SrcSpanInfo,- prettyPrint,srcInfoSpan)-import Language.Haskell.Exts.Annotated.Syntax-import Prelude hiding (exp, mod)-import Safe+import Language.Haskell.Exts (SrcSpanInfo, prettyPrint)+import Language.Haskell.Exts.Syntax --- | Compile an FFI call.-compileFFI :: S.Name -- ^ Name of the to-be binding.- -> String -- ^ The format string.- -> S.Type -- ^ Type signature.- -> Compile [JsStmt]-compileFFI name' formatstr sig =+-- | Compile an FFI expression (also used when compiling top level definitions).+compileFFIExp :: SrcSpanInfo -> Maybe (Name a) -> String -> S.Type -> Compile JsExp+compileFFIExp loc (fmap unAnn -> nameopt) formatstr sig' = -- substitute newtypes with their child types before calling -- real compileFFI- compileFFI' =<< rmNewtys sig-+ compileFFI' . unAnn =<< rmNewtys sig' 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' :: N.Type -> Compile [JsStmt]- compileFFI' sig' = do- fun <- compileFFIExp loc (Just name) formatstr sig'- stmt <- bindToplevel True (Just (srcInfoSpan loc)) name fun- return [stmt]-- name = unAnn name'- loc = S.srcSpanInfo $ ann name'---- | Compile an FFI expression (also used when compiling top level definitions).-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 loc formatstr (zip params funcFundamentalTypes)- case JS.parse JS.expression (prettyPrint name) (printJSString (wrapReturn inner)) of- Left err -> throwError (FfiFormatInvalidJavaScript loc inner (show err))- Right exp -> do- config' <- config id- when (configGClosure config') $ warnDotUses loc inner exp- return (body inner)+ rmNewtys typ = case typ of+ TyForall _ b c t -> TyForall () (fmap (map unAnn) b) (fmap unAnn c) <$> rmNewtys t+ TyFun _ t1 t2 -> TyFun () <$> rmNewtys t1 <*> rmNewtys t2+ TyTuple _ b tl -> TyTuple () b <$> mapM rmNewtys tl+ TyList _ t -> TyList () <$> rmNewtys t+ TyApp _ t1 t2 -> TyApp () <$> rmNewtys t1 <*> rmNewtys t2+ t@TyVar{} -> return $ unAnn t+ TyCon _ qname -> maybe (TyCon () (unAnn qname)) snd <$> lookupNewtypeConst qname+ TyParen _ t -> TyParen () <$> rmNewtys t+ TyInfix _ t1 q t2 -> flip (TyInfix ()) (unAnn q) <$> rmNewtys t1 <*> rmNewtys t2+ TyKind _ t k -> flip (TyKind ()) (unAnn k) <$> rmNewtys t+ TyPromoted {} -> return $ unAnn typ+ TyParArray _ t -> TyParArray () <$> rmNewtys t+ TyEquals _ t1 t2 -> TyEquals () <$> rmNewtys t1 <*> rmNewtys t2+ TySplice {} -> return $ unAnn typ+ TyBang _ bt unp t -> TyBang () (unAnn bt) (unAnn unp) <$> rmNewtys t+ TyWildCard {} -> error "TyWildCard not supported"+ TyQuasiQuote {} -> error "TyQuasiQuote not supported"+ TyUnboxedSum {} -> error "TyUnboxedSum not supported" - 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+ compileFFI' :: N.Type -> Compile JsExp+ compileFFI' sig = do+ let name = fromMaybe "<exp>" nameopt+ inner <- formatFFI loc formatstr (zip params funcFundamentalTypes)+ case JS.parse JS.expression (prettyPrint name) (printJSString (wrapReturn inner)) of+ Left err -> throwError (FfiFormatInvalidJavaScript loc inner (show err))+ Right exp -> do+ config' <- config id+ 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 :: 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 :: SrcSpanInfo -> String -> Expression SourcePos -> Compile ()@@ -130,7 +113,7 @@ globalNames = ["Math","console","JSON"] -- | Make a Fay→JS encoder.-emitFayToJs :: Name a -> [TyVarBind b] -> [([Name c], BangType d)] -> Compile ()+emitFayToJs :: Name a -> [TyVarBind b] -> [([Name c], Type d)] -> Compile () emitFayToJs (unAnn -> name) (map unAnn -> tyvars) (explodeFields -> fieldTypes) = do qname <- qualify name let ctrName = printJSString $ unQual qname@@ -147,7 +130,7 @@ obj = JsVar obj_ $ JsObj [("instance",JsLit (JsStr (printJSString (unAnn name))))] - fieldStmts :: [(Int,(N.Name,N.BangType))] -> [JsStmt]+ fieldStmts :: [(Int,(N.Name,N.Type))] -> [JsStmt] fieldStmts [] = [] fieldStmts ((i,fieldType):fts) = JsVar obj_v field :@@ -163,11 +146,11 @@ obj_ = JsNameVar "obj_" -- Declare/encode Fay→JS field- declField :: Int -> (N.Name,N.BangType) -> (String,JsExp)+ declField :: Int -> (N.Name,N.Type) -> (String,JsExp) declField i (fname,typ) = (prettyPrint fname ,fayToJs (SerializeUserArg i)- (argType (bangType typ))+ (argType typ) (JsGetProp (JsName transcodingObjForced) (JsNameVar (UnQual () fname)))) @@ -211,13 +194,6 @@ TyCon _ (UnQual _ user) -> UserDefined user [] _ -> UnknownType --- | Extract the type.-bangType :: N.BangType -> N.Type-bangType typ = case typ of- BangedTy _ ty -> ty- UnBangedTy _ ty -> ty- UnpackedTy _ ty -> ty- -- | Expand a type application. expandApp :: N.Type -> [N.Type] expandApp (TyParen _ t) = expandApp t@@ -353,7 +329,7 @@ jsToFayHash cases = [JsExpStmt $ JsApp (JsName $ JsBuiltIn "objConcat") [JsName $ JsBuiltIn "jsToFayHash", JsObj cases]] -- | Make a JS→Fay decoder.-emitJsToFay :: Name a -> [TyVarBind b] -> [([Name c],BangType d)] -> Compile ()+emitJsToFay :: Name a -> [TyVarBind b] -> [([Name c],Type d)] -> Compile () emitJsToFay (unAnn -> name) (map unAnn -> tyvars) (map (unAnn *** unAnn) . explodeFields -> fieldTypes) = do qname <- qualify name tell (mempty { writerJsToFay = [(printJSString (unAnn name), translator qname)] })@@ -364,10 +340,10 @@ (Just $ JsNew (JsConstructor qname) (map (decodeField . getIndex name tyvars) fieldTypes)) -- Decode JS→Fay field- decodeField :: (Int,(N.Name,N.BangType)) -> JsExp+ decodeField :: (Int,(N.Name,N.Type)) -> JsExp decodeField (i,(fname,typ)) = jsToFay (SerializeUserArg i)- (argType (bangType typ))+ (argType typ) (JsGetPropExtern (JsName transcodingObj) (prettyPrint fname)) @@ -376,9 +352,9 @@ argTypes = JsNameVar "argTypes" -- | Get the index of a name from the set of type variables bindings.-getIndex :: Name a -> [TyVarBind b] -> (Name c,BangType d) -> (Int,(N.Name,N.BangType))+getIndex :: Name a -> [TyVarBind b] -> (Name c,Type d) -> (Int,(N.Name,N.Type)) getIndex (unAnn -> name) (map unAnn -> tyvars) (unAnn -> sname,unAnn -> ty) =- case bangType ty of+ case ty of TyVar _ tyname -> case elemIndex tyname (map tyvar tyvars) of Nothing -> error $ "unknown type variable " ++ prettyPrint tyname ++ " for " ++ prettyPrint name ++ "." ++ prettyPrint sname ++ "," ++
src/Fay/Compiler/GADT.hs view
@@ -4,18 +4,18 @@ (convertGADT ) where -import Language.Haskell.Exts.Annotated hiding (binds, name)+import Language.Haskell.Exts hiding (name) -- | Convert a GADT to a normal data type. convertGADT :: GadtDecl a -> QualConDecl a convertGADT d = case d of- GadtDecl s name typ -> QualConDecl s tyvars context- (ConDecl s name (convertFunc typ))+ GadtDecl s name tyvars context Nothing typ ->+ QualConDecl s tyvars context (ConDecl s name (convertFunc typ))+ GadtDecl s name tyvars context (Just fs) _typ ->+ QualConDecl s tyvars context (RecDecl s name fs) where- tyvars = Nothing- context = Nothing- convertFunc :: Type a -> [BangType a]+ convertFunc :: Type a -> [Type a] convertFunc (TyCon _ _) = []- convertFunc (TyFun s x xs) = UnBangedTy s x : convertFunc xs+ convertFunc (TyFun _ x xs) = x : convertFunc xs convertFunc (TyParen _ x) = convertFunc x convertFunc _ = []
src/Fay/Compiler/Import.hs view
@@ -1,27 +1,28 @@-{-# LANGUAGE TupleSections #-}-{-# LANGUAGE ViewPatterns #-}+{-# LANGUAGE NoImplicitPrelude #-}+{-# 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+ ( startCompile+ , compileWith ) where -import Fay.Compiler.Config+import Fay.Compiler.Prelude+ import Fay.Compiler.Misc-import Fay.Control.Monad.IO+import Fay.Compiler.Parse+import Fay.Config 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 Control.Monad.Except (throwError)+import Control.Monad.RWS (ask, get, gets, lift, listen, modify)+import Language.Haskell.Exts hiding (name) import System.Directory import System.FilePath @@ -33,13 +34,14 @@ -- | Compile a module compileWith- :: Monoid a+ :: (Monoid a, Semigroup a) => FilePath -> (a -> F.Module -> Compile a) -> (FilePath -> String -> Compile a)+ -> (F.X -> F.Module -> IO (Either CompileError F.Module)) -> String -> Compile (a, CompileState, CompileWriter)-compileWith filepath with compileModule from = do+compileWith filepath with compileModule before from = do rd <- ask st <- get res <- Compile . lift . lift $@@ -47,7 +49,9 @@ rd st (parseResult (throwError . uncurry ParseError)- (\mod@(Module _ _ _ imports _) -> do+ (\mod' -> do+ ~mod@(Module _ _ _ imports _) <-+ either throwError return =<< io (before F.noI mod') res <- foldr (<>) mempty <$> mapM (compileImport compileModule) imports modify $ \s -> s { stateModuleName = unAnn $ F.moduleName mod } with res mod@@ -94,9 +98,9 @@ -> 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+ -- Trickery in fay-base needs this special case+ ImportDecl _ _ _ _ _ (Just "base") _ _ -> return mempty+ ImportDecl _ name _ _ _ _ _ _ -> compileModuleFromName compileModule name -- | Find an import's filepath and contents from its module name. findImport :: [FilePath] -> ModuleName a -> Compile (FilePath,String)
src/Fay/Compiler/InitialPass.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ViewPatterns #-} @@ -7,24 +8,22 @@ (initialPass ) where +import Fay.Compiler.Prelude+ import Fay.Compiler.Desugar import Fay.Compiler.GADT import Fay.Compiler.Import import Fay.Compiler.Misc-import Fay.Control.Monad.IO-import Fay.Data.List.Extra+import Fay.Compiler.Parse 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 Control.Monad.Except (throwError)+import Control.Monad.RWS (modify) 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)+import Language.Haskell.Exts hiding (name)+import qualified Language.Haskell.Names as HN (getInterfaces) -- | Preprocess and collect all information needed during code generation. initialPass :: FilePath -> Compile ()@@ -33,7 +32,7 @@ -- | Preprocess a module given its filepath and content. preprocessFileWithSource :: FilePath -> String -> Compile () preprocessFileWithSource filepath contents = do- (_,st,_) <- compileWith filepath preprocessAST preprocessFileWithSource contents+ (_,st,_) <- compileWith filepath preprocessAST preprocessFileWithSource desugar contents -- This is the state we want to keep modify $ \s -> s { stateRecords = stateRecords st , stateRecordTypes = stateRecordTypes st@@ -50,18 +49,15 @@ -- | 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 defaultExtensions [dmod]- modify $ \s -> s { stateInterfaces = M.insert (stateModuleName s) exports $ stateInterfaces s }- forM_ decls scanTypeSigs- forM_ decls scanRecordDecls- forM_ decls scanNewtypeDecls+preprocessAST () mod@(Module _ _ _ _ decls) = do+ -- This can only return one element since we only compile one module.+ ~([exports],_) <- HN.getInterfaces Haskell2010 defaultExtensions [mod]+ modify $ \s -> s { stateInterfaces = M.insert (stateModuleName s) exports $ stateInterfaces s }+ forM_ decls scanTypeSigs+ forM_ decls scanRecordDecls+ ifOptimizeNewtypes+ (forM_ decls scanNewtypeDecls)+ (return ()) preprocessAST () mod = throwError $ UnsupportedModuleSyntax "preprocessAST" mod --------------------------------------------------------------------------------@@ -80,32 +76,43 @@ 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 :: 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 qdname <- case dname of Nothing -> return Nothing Just n -> Just <$> qualify n modify (\cs@CompileState{stateNewtypes=nts} ->- cs{stateNewtypes=(qcname,qdname,getBangTy ty):nts})+ cs{stateNewtypes=(qcname,qdname,unAnn ty):nts}) compileNewtypeDecl q = error $ "compileNewtypeDecl: Should be impossible (this is a bug). Got: " ++ show q +{-# ANN scanRecordDecls ("HLint: ignore Redundant flip" :: String) #-} -- | Add record declarations to the state scanRecordDecls :: F.Decl -> Compile () scanRecordDecls decl = do case decl of- DataDecl _loc DataType{} _ctx (F.declHeadName -> name) qualcondecls _deriv -> do- let ns = for qualcondecls (\(QualConDecl _loc' _tyvarbinds _ctx' condecl) -> conDeclName condecl)- addRecordTypeState name ns+ DataDecl _loc ty _ctx (F.declHeadName -> name) qualcondecls _deriv -> do+ let addIt = let ns = flip fmap qualcondecls (\(QualConDecl _loc' _tyvarbinds _ctx' condecl) -> conDeclName condecl)+ in addRecordTypeState name ns+ case ty of+ DataType{} -> addIt+ NewType{} -> ifOptimizeNewtypes+ (return ())+ addIt _ -> return () case decl of- DataDecl _ DataType{} _ _ constructors _ -> dataDecl constructors- GDataDecl _ DataType{} _ _ _ decls _ -> dataDecl (map convertGADT decls)+ DataDecl _ ty _ _ constructors _ ->+ case ty of+ DataType{} -> dataDecl constructors+ NewType{} -> ifOptimizeNewtypes+ (return ())+ (dataDecl constructors)+ GDataDecl _ ty _ _ _ decls _ ->+ case ty of+ DataType{} -> dataDecl (map convertGADT decls)+ NewType{} -> ifOptimizeNewtypes+ (return ())+ (dataDecl (map convertGADT decls)) _ -> return () where@@ -121,7 +128,7 @@ -- | Collect record definitions and store record name and field names. -- A ConDecl will have fields named slot1..slotN dataDecl :: [F.QualConDecl] -> Compile ()- dataDecl constructors = do+ dataDecl constructors = forM_ constructors $ \(QualConDecl _ _ _ condecl) -> case condecl of ConDecl _ name types -> do
src/Fay/Compiler/Misc.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ViewPatterns #-}@@ -7,30 +8,27 @@ module Fay.Compiler.Misc where +import Fay.Compiler.Prelude++import Fay.Compiler.ModuleT (runModuleT) import Fay.Compiler.PrimOp-import Fay.Control.Monad.IO-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.Compiler.QName (unname)+import Fay.Config+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 Data.String-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 Control.Monad.Except (runExceptT, throwError)+import Control.Monad.RWS (asks, gets, modify, runRWST)+import Data.Version (parseVersion)+import Language.Haskell.Exts hiding (name)+import Language.Haskell.Names (GName (GName), NameInfo (GlobalValue, LocalValue, ScopeError),+ OrigName, Scoped (Scoped), origGName, origName) import System.IO-import System.Process (readProcess)-import Text.ParserCombinators.ReadP (readP_to_S)+import System.Process (readProcess)+import Text.ParserCombinators.ReadP (readP_to_S) -- | Wrap an expression in a thunk. thunk :: JsExp -> JsExp@@ -113,7 +111,7 @@ Just (cname,_,ty) -> return $ Just (cname,ty) -- | Qualify a name for the current module.-qualify :: Name a -> Compile (N.QName)+qualify :: Name a -> Compile N.QName qualify (Ident _ name) = do modulename <- gets stateModuleName return (Qual () modulename (Ident () name))@@ -153,19 +151,21 @@ ParseFailed srcloc msg -> die (srcloc,msg) -- | Get a config option.-config :: (CompileConfig -> a) -> Compile a+config :: (Config -> a) -> Compile a config f = asks (f . readerConfig) -- | Optimize pattern matching conditions by merging conditions in common.+-- TODO This is buggy and no longer used. Fails on tests/case3 optimizePatConditions :: [[JsStmt]] -> [[JsStmt]]-optimizePatConditions = concatMap merge . groupBy sameIf where+optimizePatConditions = id+ {- concatMap merge . groupBy sameIf where sameIf [JsIf cond1 _ _] [JsIf cond2 _ _] = cond1 == cond2 sameIf _ _ = False merge xs@([JsIf cond _ _]:_) = [[JsIf cond (concat (optimizePatConditions (map getIfConsequent xs))) []]] merge noifs = noifs getIfConsequent [JsIf _ cons _] = cons- getIfConsequent other = other+ getIfConsequent other = other -} -- | Throw a JS exception. throw :: String -> JsExp -> JsStmt@@ -215,7 +215,7 @@ warn "" = return () warn w = config id >>= io . (`ioWarn` w) -ioWarn :: CompileConfig -> String -> IO ()+ioWarn :: Config -> String -> IO () ioWarn _ "" = return () ioWarn cfg w = when (configWall cfg) $@@ -237,7 +237,7 @@ typeToRecs (unAnn -> typ) = fromMaybe [] . lookup typ <$> gets stateRecordTypes recToFields :: S.QName -> Compile [N.Name]-recToFields con = do+recToFields con = case tryResolveName con of Nothing -> return [] Just c -> fromMaybe [] . lookup c <$> gets stateRecords@@ -268,72 +268,31 @@ -> 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+runTopCompile reader' state' m = fst <$> runModuleT (runExceptT (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')+runCompileModule reader' state' m = runExceptT (runRWST (unCompile m) reader' state') --- | Parse some Fay code.-parseFay :: Parseable ast => FilePath -> String -> ParseResult ast-parseFay filepath = parseWithMode parseMode { parseFilename = filepath } . applyCPP+shouldBeDesugared :: (Functor f, Show (f ())) => f l -> Compile a+shouldBeDesugared = throwError . ShouldBeDesugared . show . unAnn --- | Apply incredibly simplistic CPP handling. It only recognizes the following:------ > #if FAY--- > #ifdef FAY--- > #ifndef FAY--- > #else--- > #endif------ Note that this implementation replaces all removed lines with blanks, so--- that line numbers remain accurate.-applyCPP :: String -> String-applyCPP =- unlines . loop NoCPP . lines+-- | Check if the given language pragmas are all present.+hasLanguagePragmas :: [String] -> [ModulePragma l] -> Bool+hasLanguagePragmas pragmas modulePragmas = (== length pragmas) . length . filter (`elem` pragmas) $ flattenPragmas modulePragmas where- loop _ [] = []- loop state' ("#if FAY":rest) = "" : loop (CPPIf True state') rest- loop state' ("#ifdef FAY":rest) = "" : loop (CPPIf True state') rest- loop state' ("#ifndef FAY":rest) = "" : loop (CPPIf False state') rest- loop (CPPIf b oldState') ("#else":rest) = "" : loop (CPPElse (not b) oldState') rest- loop (CPPIf _ oldState') ("#endif":rest) = "" : loop oldState' rest- loop (CPPElse _ oldState') ("#endif":rest) = "" : loop oldState' rest- loop state' (x:rest) = (if toInclude state' then x else "") : loop state' rest-- toInclude NoCPP = True- toInclude (CPPIf x state') = x && toInclude state'- toInclude (CPPElse x state') = x && toInclude state'---- | The CPP's parsing state.-data CPPState = NoCPP- | CPPIf Bool CPPState- | CPPElse Bool CPPState---- | The parse mode for Fay.-parseMode :: ParseMode-parseMode = defaultParseMode- { extensions = defaultExtensions- , fixities = Just (preludeFixities ++ baseFixities)- }+ flattenPragmas :: [ModulePragma l] -> [String]+ flattenPragmas = concatMap pragmaName+ pragmaName (LanguagePragma _ q) = map unname q+ pragmaName _ = [] -shouldBeDesugared :: (Functor f, Show (f ())) => f l -> Compile a-shouldBeDesugared = throwError . ShouldBeDesugared . show . unAnn+hasLanguagePragma :: String -> [ModulePragma l] -> Bool+hasLanguagePragma pr = hasLanguagePragmas [pr] -defaultExtensions :: [Extension]-defaultExtensions = map EnableExtension- [EmptyDataDecls- ,ExistentialQuantification- ,FlexibleContexts- ,FlexibleInstances- ,GADTs- ,KindSignatures- ,NamedFieldPuns- ,PackageImports- ,RecordWildCards- ,StandaloneDeriving- ,TupleSections- ,TypeFamilies- ,TypeOperators- ] ++ map DisableExtension- [ImplicitPrelude]+-- | if then else for when 'configOptimizeNewtypes'.+ifOptimizeNewtypes :: Compile a -> Compile a -> Compile a+ifOptimizeNewtypes then' else' = do+ optimize <- config configOptimizeNewtypes+ if optimize+ then then'+ else else'
+ src/Fay/Compiler/ModuleT.hs view
@@ -0,0 +1,134 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeSynonymInstances #-}+module Fay.Compiler.ModuleT+ (+ -- * Module monad+ -- | When you need to resolve modules, you work in a 'ModuleT' monad (or+ -- another monad that is an instance of 'MonadModule') and use the+ -- 'getModuleInfo' function.+ --+ -- It finds an installed module by its name and reads (and caches) its+ -- info from the info file. Then you run a 'ModuleT' monadic action+ -- using 'evalModuleT' or 'runModuleT'.+ --+ -- To run a 'ModuleT' action you'll also need to provide the set of+ -- packages (represented by their 'InstalledPackageInfo') in which to+ -- search for modules. You can get such a set from either+ -- 'getInstalledPackages' or 'readPackagesInfo', depending on your use+ -- case.+ ModuleT+ , getModuleInfo+ , runModuleT+ , MonadModule (..)+ -- * Module names+ , ModName (..)+ ) where++import Fay.Compiler.Prelude++import Control.Monad.Reader+import Control.Monad.State+import qualified Data.Char as Char (isAlphaNum, isUpper)+import qualified Data.Map as Map++-- ModuleName extracted from Cabal, (c) 2008 Duncan Coutts, Licensed as BSD3+newtype ModuleName = ModuleName [String]+ deriving (Eq, Ord, Show)++fromString :: String -> ModuleName+fromString string+ | all validModuleComponent components' = ModuleName components'+ | otherwise = error $ "ModuleName.fromString: invalid module name " ++ show string++ where+ components' = split string++ split cs = case break (=='.') cs of+ (chunk,[]) -> [chunk]+ (chunk,_:rest) -> chunk : split rest++ validModuleComponent :: String -> Bool+ validModuleComponent [] = False+ validModuleComponent (c:cs) = Char.isUpper c+ && all validModuleChar cs++ validModuleChar :: Char -> Bool+ validModuleChar c = Char.isAlphaNum c || c == '_' || c == '\''+++-- | This class defines the interface that is used by 'getModuleInfo', so+-- that you can use it in monads other than 'ModuleT'.+--+-- You don't typically have to define your own instances of this class, but+-- here are a couple of cases when you might:+--+-- * A pure (non-'MonadIO') mockup module monad for testing purposes+--+-- * A transformer over 'ModuleT'+--+-- * You need a more complex way to retrieve the module info+class Monad m => MonadModule m where+ -- | The type of module info+ type ModuleInfo m+ lookupInCache :: ModName n => n -> m (Maybe (ModuleInfo m))+ insertInCache :: ModName n => n -> ModuleInfo m -> m ()++ -- | Read the module info, given a list of search paths and the module+ -- name+ readModuleInfo :: ModName n => [FilePath] -> n -> m (ModuleInfo m)++-- | Different libraries (Cabal, haskell-src-exts, ...) use different types+-- to represent module names. Hence this class.+class ModName n where+ modToString :: n -> String++instance ModName String where+ modToString = id++-- | Convert module name from arbitrary representation to Cabal's one+convertModuleName :: ModName n => n -> ModuleName+convertModuleName = fromString . modToString++-- | Tries to find the module in the current set of packages, then find the+-- module's info file, and reads and caches its contents.+--+-- Returns 'Nothing' if the module could not be found in the current set of+-- packages. If the module is found, but something else goes wrong (e.g.+-- there's no info file for it), an exception is thrown.+getModuleInfo :: (MonadModule m, ModName n) => n -> m (Maybe (ModuleInfo m))+getModuleInfo = lookupInCache++-- | A standard module monad transformer.+--+-- @i@ is the type of module info, @m@ is the underlying monad.+newtype ModuleT i m a =+ ModuleT+ (StateT (Map.Map ModuleName i)+ (ReaderT ([FilePath] -> ModuleName -> m i) m) a)+ deriving (Functor, Applicative, Monad)++instance MonadTrans (ModuleT i) where+ lift = ModuleT . lift . lift++instance MonadIO m => MonadIO (ModuleT i m) where+ liftIO = ModuleT . liftIO++instance (Functor m, Monad m) => MonadModule (ModuleT i m) where+ type ModuleInfo (ModuleT i m) = i+ lookupInCache n = ModuleT $ Map.lookup (convertModuleName n) <$> get+ insertInCache n i = ModuleT $ modify $ Map.insert (convertModuleName n) i+ readModuleInfo dirs mod' =+ lift =<< ModuleT ask <*> pure dirs <*> pure (convertModuleName mod')++-- | Run a 'ModuleT' action+runModuleT+ :: (Monad m, Monoid i)+ => ModuleT i m a -- ^ the monadic action to run+ -> m (a, Map.Map ModuleName i)+ -- ^ return value, plus all cached module infos (that is, the initial set+ -- plus all infos that have been read by the action itself)+runModuleT (ModuleT a) =+ runReaderT (runStateT a Map.empty) (\_ _ -> return mempty)
src/Fay/Compiler/Optimizer.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternGuards #-} {-# LANGUAGE TupleSections #-}@@ -6,20 +8,15 @@ module Fay.Compiler.Optimizer where +import Fay.Compiler.Prelude+ import Fay.Compiler.Misc import Fay.Types -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 Control.Monad.State (State, modify, runState)+import Control.Monad.Writer (runWriter, tell) import qualified Fay.Exts.NoAnnotation as N-import Language.Haskell.Exts.Annotated hiding (app, name, op)--import Prelude hiding (exp)+import Language.Haskell.Exts hiding (app, name, op) -- | The arity of a function. Arity here is defined to be the number -- of arguments that can be directly uncurried from a curried lambda@@ -120,6 +117,7 @@ tco = map inStmt where inStmt stmt = case stmt of JsVar name exp -> JsVar name (inject name exp)+ JsSetQName l name exp -> JsSetQName l name (inject (JsNameVar name) exp) e -> e inject name exp = case exp of JsFun nm params [] (Just (JsNew JsThunk [JsFun _ [] stmts ret])) ->@@ -211,10 +209,11 @@ -- | Apply the given function to the top-level expressions in the -- given statement. applyToExpsInStmt :: [FuncArity] -> ([FuncArity] -> JsExp -> Optimize JsExp) -> JsStmt -> Optimize JsStmt-applyToExpsInStmt funcs f stmts = uncurryInStmt stmts where+applyToExpsInStmt funcs f = uncurryInStmt where transform = f funcs uncurryInStmt stmt = case stmt of JsVar name exp -> JsVar name <$> transform exp+ JsSetQName l name exp -> JsSetQName l name <$> transform exp JsEarlyReturn exp -> JsEarlyReturn <$> transform exp JsIf op ithen ielse -> JsIf <$> transform op <*> mapM uncurryInStmt ithen@@ -224,7 +223,7 @@ -- | Collect functions and their arity from the whole codeset. collectFuncs :: [JsStmt] -> [FuncArity] collectFuncs = (++ prim) . concatMap collectFunc where- collectFunc (JsVar (JsNameVar name) exp) | arity > 0 = [(name,arity)]+ collectFunc (JsSetQName _ name exp) | arity > 0 = [(name,arity)] where arity = expArity exp collectFunc _ = [] prim = map (first (Qual () (ModuleName () "Fay$"))) (unary ++ binary)@@ -245,6 +244,8 @@ funBinding stmt = case stmt of JsVar (JsNameVar name) body | name == qname -> JsVar (JsNameVar (renameUncurried name)) <$> uncurryIt body+ JsSetQName l name body+ | name == qname -> JsSetQName l (renameUncurried name) <$> uncurryIt body _ -> Nothing uncurryIt = Just . go [] where
src/Fay/Compiler/Packages.hs view
@@ -1,32 +1,27 @@-{-# LANGUAGE TupleSections #-}-+{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE TupleSections #-} -- | Dealing with Cabal packages in Fay's own special way.- module Fay.Compiler.Packages where -import Fay.Compiler.Config-import Fay.Control.Monad.Extra-import Fay.System.Process.Extra-import Fay.Types+import Fay.Compiler.Prelude++import Fay.Config import Paths_fay -import Control.Applicative-import Control.Monad-import Data.List-import Data.Maybe import Data.Version import GHC.Paths import System.Directory import System.FilePath+import System.Environment -- | Given a configuration, resolve any packages specified to their -- data file directories for importing the *.hs sources.-resolvePackages :: CompileConfig -> IO CompileConfig+resolvePackages :: Config -> IO Config resolvePackages config = foldM resolvePackage config (configPackages config) -- | Resolve package.-resolvePackage :: CompileConfig -> String -> IO CompileConfig+resolvePackage :: Config -> String -> IO Config resolvePackage config name = do desc <- describePackage (configPackageConf config) name case packageVersion desc of@@ -40,9 +35,12 @@ exists <- mapM doesSourceDirExist includes if or exists then return (addConfigDirectoryIncludes (map (Just nameVer,) includes) config)- else error $ "unable to find (existing) package's share dir: " ++ name ++ "\n" ++- "tried: " ++ unlines includes ++ "\n" ++- "but none of them seem to have Haskell files in them."+ else error $ concat+ [ "unable to find (existing) package's share dir: ", name, "\n"+ , "tried: ", unlines includes, "\n"+ , "but none of them seem to have Haskell files in them.\n"+ , "If you are using a sandbox you need to specify the HASKELL_PACKAGE_SANDBOX environment variable or use --package-conf."+ ] -- | Does a directory exist and does it contain any Haskell sources? doesSourceDirExist :: FilePath -> IO Bool@@ -57,12 +55,24 @@ -- | Describe the given package. describePackage :: Maybe FilePath -> String -> IO String describePackage db name = do- result <- readAllFromProcess ghc_pkg args ""+ exists <- doesFileExist ghc_pkg+ stackInNixShell <- fmap isJust (lookupEnv "STACK_IN_NIX_SHELL")+ let command = if exists+ then if (isInfixOf ".stack" ghc_pkg || stackInNixShell)+ then "stack"+ else ghc_pkg+ else "ghc-pkg"+ extraArgs = case command of+ "stack" -> ["exec","--","ghc-pkg"]+ _ -> []+ args = extraArgs ++ ["describe",name] ++ ["--expand-env-vars", "-v2"]+ ++ ["--package-db=" ++ db' | Just db' <- [db]]+ when stackInNixShell (unsetEnv "STACK_IN_NIX_SHELL")+ result <- readAllFromProcess command args ""+ when stackInNixShell (setEnv "STACK_IN_NIX_SHELL" "1") case result of- Left (err,_out) -> error $ "ghc-pkg describe error:\n" ++ err+ Left (err,out) -> error $ "ghc-pkg describe error:\n" ++ err ++ "\n" ++ out Right (_err,out) -> return out-- where args = ["describe",name] ++ ["-f" ++ db' | Just db' <- [db]] -- | Get the package version from the package description. packageVersion :: String -> Maybe String
+ src/Fay/Compiler/Parse.hs view
@@ -0,0 +1,69 @@+module Fay.Compiler.Parse+ ( parseFay+ , defaultExtensions+ ) where++import Language.Haskell.Exts++-- | Parse some Fay code.+parseFay :: Parseable ast => FilePath -> String -> ParseResult ast+parseFay filepath = parseWithMode parseMode { parseFilename = filepath } . applyCPP++-- | Apply incredibly simplistic CPP handling. It only recognizes the following:+--+-- > #if FAY+-- > #ifdef FAY+-- > #ifndef FAY+-- > #else+-- > #endif+--+-- Note that this implementation replaces all removed lines with blanks, so+-- that line numbers remain accurate.+applyCPP :: String -> String+applyCPP =+ unlines . loop NoCPP . lines+ where+ loop _ [] = []+ loop state' ("#if FAY":rest) = "" : loop (CPPIf True state') rest+ loop state' ("#ifdef FAY":rest) = "" : loop (CPPIf True state') rest+ loop state' ("#ifndef FAY":rest) = "" : loop (CPPIf False state') rest+ loop (CPPIf b oldState') ("#else":rest) = "" : loop (CPPElse (not b) oldState') rest+ loop (CPPIf _ oldState') ("#endif":rest) = "" : loop oldState' rest+ loop (CPPElse _ oldState') ("#endif":rest) = "" : loop oldState' rest+ loop state' (x:rest) = (if toInclude state' then x else "") : loop state' rest++ toInclude NoCPP = True+ toInclude (CPPIf x state') = x && toInclude state'+ toInclude (CPPElse x state') = x && toInclude state'++-- | The CPP's parsing state.+data CPPState = NoCPP+ | CPPIf Bool CPPState+ | CPPElse Bool CPPState++-- | The parse mode for Fay.+parseMode :: ParseMode+parseMode = defaultParseMode+ { extensions = defaultExtensions+ , fixities = Just (preludeFixities ++ baseFixities)+ }++defaultExtensions :: [Extension]+defaultExtensions = map EnableExtension+ [EmptyDataDecls+ ,ExistentialQuantification+ ,FlexibleContexts+ ,FlexibleInstances+ ,GADTs+ ,ImplicitPrelude+ ,KindSignatures+ ,LambdaCase+ ,MultiWayIf+ ,NamedFieldPuns+ ,PackageImports+ ,RecordWildCards+ ,StandaloneDeriving+ ,TupleSections+ ,TypeFamilies+ ,TypeOperators+ ]
src/Fay/Compiler/Pattern.hs view
@@ -1,4 +1,4 @@-{-# OPTIONS -fno-warn-name-shadowing #-}+{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ViewPatterns #-} @@ -6,6 +6,8 @@ module Fay.Compiler.Pattern where +import Fay.Compiler.Prelude+ import Fay.Compiler.Misc import Fay.Compiler.QName import Fay.Exts.NoAnnotation (unAnn)@@ -13,11 +15,10 @@ import qualified Fay.Exts.Scoped as S import Fay.Types -import Control.Applicative-import Control.Monad.Error-import Control.Monad.Reader-import Language.Haskell.Exts.Annotated-import Language.Haskell.Names+import Control.Monad.Except (throwError)+import Control.Monad.Reader (ask)+import Language.Haskell.Exts hiding (name)+import Language.Haskell.Names (NameInfo (RecPatWildcard), Scoped (Scoped)) -- | Compile the given pattern against the given expression. compilePat :: JsExp -> S.Pat -> [JsStmt] -> Compile [JsStmt]@@ -26,17 +27,17 @@ PApp _ cons pats -> do newty <- lookupNewtypeConst cons case newty of- Nothing -> compilePApp cons pats exp body+ Nothing -> compilePApp pat cons pats exp body Just _ -> compileNewtypePat pats exp body- PLit _ literal -> compilePLit exp literal body- PParen{} -> shouldBeDesugared pat+ PLit _ sign lit -> compilePLit exp sign lit 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+ PAsPat _ name pt -> compilePAsPat exp name pt body PRec _ name pats -> compilePatFields exp name pats body- pat -> throwError (UnsupportedPattern pat)+ PParen{} -> shouldBeDesugared pat+ PInfixApp{} -> shouldBeDesugared pat+ _ -> throwError (UnsupportedPattern pat) -- | Compile a pattern variable e.g. x. compilePVar :: S.Name -> JsExp -> [JsStmt] -> Compile [JsStmt]@@ -46,7 +47,7 @@ -- | Compile a record field pattern. compilePatFields :: JsExp -> S.QName -> [S.PatField] -> [JsStmt] -> Compile [JsStmt] compilePatFields exp name pats body = do- c <- liftM (++ body) (compilePats' [] pats)+ c <- (++ body) <$> compilePats' [] pats qname <- unsafeResolveName name return [JsIf (force exp `JsInstanceOf` JsConstructor qname) c []] where@@ -77,10 +78,10 @@ _ -> [] -- | Compile a literal value from a pattern match.-compilePLit :: JsExp -> S.Literal -> [JsStmt] -> Compile [JsStmt]-compilePLit exp literal body = do+compilePLit :: JsExp -> S.Sign -> S.Literal -> [JsStmt] -> Compile [JsStmt]+compilePLit exp sign literal body = do c <- ask- lit <- readerCompileLit c literal+ lit <- readerCompileLit c sign literal return [JsIf (equalExps exp lit) body []]@@ -107,13 +108,26 @@ compileNewtypePat ps _ _ = error $ "compileNewtypePat: Should be impossible (this is a bug). Got: " ++ show ps -- | Compile a pattern application.-compilePApp :: S.QName -> [S.Pat] -> JsExp -> [JsStmt] -> Compile [JsStmt]-compilePApp cons pats exp body = do+compilePApp :: S.Pat -> S.QName -> [S.Pat] -> JsExp -> [JsStmt] -> Compile [JsStmt]+compilePApp origPat 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)+ Special _ Cons{} -> case pats of+ [left, right] ->+ withScopedTmpJsName $ \tmpName -> do+ let forcedList = JsName tmpName+ x = JsGetProp forcedList (JsNameVar "car")+ xs = JsGetProp forcedList (JsNameVar "cdr")+ rightMatch <- compilePat xs right body+ leftMatch <- compilePat x left rightMatch+ return [JsVar tmpName (force exp)+ ,JsIf (JsInstanceOf forcedList (JsBuiltIn "Cons"))+ leftMatch+ []]+ _ -> throwError $ UnsupportedPattern origPat UnQual _ (Ident _ "True") -> boolIf True UnQual _ (Ident _ "False") -> boolIf False -- Everything else, generic:@@ -123,8 +137,8 @@ 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)+ substmts <- foldM (\bd (field,pat) ->+ compilePat (JsGetProp forcedExp (JsNameVar field)) pat bd) body (reverse (zip recordFields pats)) qcons <- unsafeResolveName cons@@ -138,30 +152,13 @@ return [JsIf (JsEq (force exp) JsNull) body []] compilePList pats body exp = do let forcedExp = force exp- stmts <- foldM (\body (i,pat) -> compilePat (JsApp (JsName (JsBuiltIn "index"))- [JsLit (JsInt i),forcedExp])- pat- body)+ stmts <- foldM (\bd (i,pat) -> compilePat (JsApp (JsName (JsBuiltIn "index"))+ [JsLit (JsInt i),forcedExp])+ pat+ bd) body (reverse (zip [0..] pats)) let patsLen = JsLit (JsInt (length pats)) return [JsIf (JsApp (JsName (JsBuiltIn "listLen")) [forcedExp,patsLen]) stmts []]---- | Compile an infix pattern (e.g. cons and tuples.)-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/Prelude.hs view
@@ -0,0 +1,67 @@+-- | Re-exports of base functionality. Note that this module is just+-- used inside the compiler. It's not compiled to JavaScript.+-- Based on the base-extended package (c) 2013 Simon Meier, licensed as BSD3.+{-# LANGUAGE NoImplicitPrelude #-}+module Fay.Compiler.Prelude+ ( module Prelude.Compat -- Partial++ -- * Control modules+ , module Control.Applicative+ , module Control.Arrow -- Partial+ , module Control.Monad.Compat++ -- * Data modules+ , module Data.Char -- Partial+ , module Data.Data -- Partial+ , module Data.Either+ , module Data.Function+ , module Data.List.Compat -- Partial+ , module Data.Maybe+ , module Data.Monoid -- Partial+ , module Data.Ord+ , module Data.Traversable++ -- * Safe+ , module Safe++ -- * Additions+ , anyM+ , io+ , readAllFromProcess+ ) where++import Control.Applicative+import Control.Arrow (first, second, (&&&), (***), (+++), (|||))+import Control.Monad.Compat hiding (guard)+import Data.Char hiding (GeneralCategory (..))+import Data.Data (Data (..), Typeable)+import Data.Either+import Data.Function (on)+import Data.List.Compat+import Data.Maybe+import Data.Monoid (Monoid (..))+import Data.Ord+import Data.Traversable+import Prelude.Compat hiding (exp, mod)+import Safe++import Control.Monad.Except hiding (filterM)+import System.Exit+import System.Process++-- | Alias of liftIO, I hate typing it. Hate reading it.+io :: MonadIO m => IO a -> m a+io = liftIO++-- | Do any of the (monadic) predicates match?+anyM :: (Functor m, Applicative m, Monad m) => (a -> m Bool) -> [a] -> m Bool+anyM p l = not . null <$> filterM p l++-- | Read from a process returning both std err and out.+readAllFromProcess :: FilePath -> [String] -> String -> IO (Either (String,String) (String,String))+readAllFromProcess program flags input = do+ (code,out,err) <- readProcessWithExitCode program flags input+ return $ case code of+ ExitFailure 127 -> Left ("cannot find executable " ++ program, "")+ ExitFailure _ -> Left (err, out)+ ExitSuccess -> Right (err, out)
src/Fay/Compiler/PrimOp.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ViewPatterns #-} @@ -21,13 +22,14 @@ , resolvePrimOp ) where +import Fay.Compiler.Prelude+ 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)+import Language.Haskell.Exts -- | Make an identifier from the built-in HJ module. fayBuiltin :: a -> String -> QName a
src/Fay/Compiler/Print.hs view
@@ -1,214 +1,200 @@-{-# OPTIONS -fno-warn-orphans #-}-{-# OPTIONS -fno-warn-unused-do-bind #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE TypeSynonymInstances #-}-{-# LANGUAGE ViewPatterns #-}---- | Simple code (non-pretty) printing.------ No clever printing is done here. If you want pretty printing, use a--- JS pretty printer. The output should be passed directly to a JS--- compressor, anyway.+{-# OPTIONS -fno-warn-orphans #-}+{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE OverloadedStrings #-}+-- | Code printers. Can be used to produce both pretty and not+-- pretty output. -- -- Special constructors and symbols in Haskell are encoded to -- JavaScript appropriately.- module Fay.Compiler.Print where +import Fay.Compiler.Prelude+ 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 Data.Default-import Data.List-import Data.String-import Language.Haskell.Exts.Annotated-import Prelude hiding (exp)-import SourceMap.Types+import Data.Aeson+import qualified Data.ByteString.Lazy.UTF8 as UTF8+import Language.Haskell.Exts hiding (alt, name, op, sym) -------------------------------------------------------------------------------- -- Printing -- | Print the JS to a flat string. printJSString :: Printable a => a -> String-printJSString x = concat $ reverse $ psOutput $ execState (runPrinter (printJS x)) def+printJSString x = pwOutputString (execPrinter (printJS x) defaultPrintReader) -- | Print the JS to a pretty string. printJSPretty :: Printable a => a -> String-printJSPretty x = concat $ reverse $ psOutput $ execState (runPrinter (printJS x)) def { psPretty = True }+printJSPretty x = pwOutputString (execPrinter (printJS x) defaultPrintReader{ prPretty = True }) --- | Print literals. These need some special encoding for--- JS-format literals. Could use the Text.JSON library.+-- | Encode String to JS-format lterals. Could use the+-- Text.JSON library.+toJsStringLit :: String -> String+toJsStringLit = UTF8.toString . encode++-- | Print literals. instance Printable JsLit where- printJS typ = write $- let u8 = UTF8.toString . encode . UTF8.fromString- in case typ of- (JsChar char) -> u8 [char]- (JsStr str) -> u8 str+ printJS typ = write $ case typ of+ (JsChar char) -> toJsStringLit [char]+ (JsStr str) -> toJsStringLit str (JsInt int) -> show int (JsFloating rat) -> show rat (JsBool b) -> if b then "true" else "false" -- | Print (and properly encode to JS) a qualified name.-instance Printable N.QName where+instance Printable (QName l) where printJS qname = case qname of- Qual _ (ModuleName _ "Fay$") name -> "Fay$$" +> name- Qual _ moduleName name -> moduleName +> "." +> name+ Qual _ (ModuleName _ "Fay$") name -> "Fay$$" <> printJS name+ Qual _ moduleName name -> printJS moduleName <> printProp name UnQual _ name -> printJS name Special _ con -> printJS con +-- | Prints pretty operators.+-- prPrettyOperator flag determines the way of accessing operators (e.g. `($)`) and+-- identifiers with apostrophes (e.g. `length'`). If prPrettyOperators is set true,+-- then these will be accessed with square brackets (e.g. Prelude["$"] or+-- Prelude["length'"]). Otherwise special characters will be escaped and accessed+-- with dot (e.g. Prelude.$36$ or Prelude.length$39$). Alphanumeric_ identifiers are+-- always accessed with dot operator (e.g. Prelude.length)+printProp :: Name l -> Printer+printProp name = askIf prPrettyOperators pretty ugly+ where pretty = if all (`elem` allowedNameChars) nameString then dot else brackets+ ugly = dot+ dot = "." <> printJS name+ brackets = "[" <> write (toJsStringLit nameString) <> "]"+ nameString = case name of+ Ident _ s -> s+ Symbol _ s -> s+ -- | Print module name.-instance Printable N.ModuleName where- printJS (ModuleName _ "Fay$") =- write "Fay$"+instance Printable (ModuleName l) where+ printJS (ModuleName _ "Fay$") = "Fay$" printJS (ModuleName _ moduleName) = write $ go moduleName- where go ('.':xs) = '.' : go xs go (x:xs) = normalizeName [x] ++ go xs go [] = [] +-- | Print (and properly encode) a name.+instance Printable (Name l) where+ printJS = write . encodeName++ -- | Print special constructors (tuples, list, etc.)-instance Printable N.SpecialCon where+instance Printable (SpecialCon l) where printJS specialCon = printJS $ fayBuiltin () $ case specialCon of UnitCon _ -> "unit" Cons _ -> "cons"- _ -> error $ "Special constructor not supported: " ++ show specialCon+ _ -> error $ "Special constructor not supported: " +++ show (void specialCon) --- | Print (and properly encode) a name.-instance Printable N.Name where- printJS name = write $- case name of- Ident _ idn -> encodeName idn- Symbol _ sym -> encodeName sym -- | Print a list of statements.-instance Printable [JsStmt] where- printJS = mapM_ printJS+printStmts :: [JsStmt] -> Printer+printStmts = mconcat . map printJS -- | Print a single statement. instance Printable JsStmt where printJS (JsExpStmt e) =- printJS e +> ";" +> newline+ printJS e <> ";" <> newline printJS (JsBlock stmts) =- "{ " +> stmts +> "}"+ "{ " <> printStmts stmts <> "}"+ printJS (JsMapVar name expr) =+ "var " <> printJS name <> " : {[key: string]: any;} = " <> printJS expr <> ";" <> newline printJS (JsVar name expr) =- "var " +> name +> " = " +> expr +> ";" +> newline+ "var " <> printJS name <> " = " <> printJS expr <> ";" <> newline printJS (JsUpdate name expr) =- name +> " = " +> expr +> ";" +> newline+ printJS name <> " = " <> printJS expr <> ";" <> newline printJS (JsSetProp name prop expr) =- name +> "." +> prop +> " = " +> expr +> ";" +> newline- printJS (JsSetQName msrcloc name expr) = do- maybe (return ()) mapping msrcloc- name +> " = " +> expr +> ";" +> newline+ printJS name <> "." <> printJS prop <> " = " <> printJS expr <> ";" <> newline+ printJS (JsSetQName msrcloc name expr) =+ maybe mempty mapping msrcloc <> printJS name <> " = " <> printJS expr <> ";" <> newline printJS (JsSetConstructor name expr) =- printCons name +> " = " +> expr +> ";" +> newline +>+ printCons name <> " = " <> printJS expr <> ";" <> newline <> -- The unqualifiedness here is bad.- printCons name +> ".prototype.instance = \"" +> printConsUnQual name +> "\";" +> newline+ printCons name <> ".prototype.instance = \"" <> printConsUnQual name <> "\";" <> newline printJS (JsSetModule mp expr) =- mp +> " = " +> expr +> ";" +> newline+ printJS mp <> " = " <> printJS expr <> ";" <> newline printJS (JsSetPropExtern name prop expr) =- name +> "['" +> prop +> "'] = " +> expr +> ";" +> newline- printJS (JsIf exp thens elses) =- "if (" +> exp +> ") {" +> newline +>- indented (printJS thens) +>- "}" +>- (when (length elses > 0) $ " else {" +>- indented (printJS elses) +>- "}") +> newline- printJS (JsEarlyReturn exp) =- "return " +> exp +> ";" +> newline- printJS (JsThrow exp) = do- "throw " +> exp +> ";" +> newline+ printJS name <> "['" <> printJS prop <> "'] = " <> printJS expr <> ";" <> newline+ printJS (JsIf expr thens elses) =+ "if (" <> printJS expr <> ") {" <> newline <>+ indented (printStmts thens) <>+ "}" <>+ (if null elses+ then mempty+ else " else {" <> newline <>+ indented (printStmts elses) <>+ "}") <> newline+ printJS (JsEarlyReturn expr) =+ "return " <> printJS expr <> ";" <> newline+ printJS (JsThrow expr) =+ "throw " <> printJS expr <> ";" <> newline printJS (JsWhile cond stmts) =- "while (" +> cond +> ") {" +> newline +>- indented (printJS stmts) +>- "}" +> newline- printJS JsContinue =- printJS "continue;" +> newline+ "while (" <> printJS cond <> ") {" <> newline <>+ indented (printStmts stmts) <>+ "}" <> newline+ printJS JsContinue = "continue;" <> newline -- | Print a module path. instance Printable ModulePath where- printJS (unModulePath -> l) = write $ intercalate "." l+ printJS = write . intercalate "." . unModulePath -- | Print an expression. instance Printable JsExp where- printJS (JsSeq es) = "(" +> intercalateM "," (map printJS es) +> ")"+ printJS (JsSeq es) = "(" <> mintercalate "," (map printJS es) <> ")" printJS (JsRawExp e) = write e printJS (JsName name) = printJS name- printJS (JsThrowExp exp) =- "(function(){ throw (" +> exp +> "); })()"- printJS JsNull =- printJS "null"- printJS JsUndefined =- printJS "undefined"- printJS (JsLit lit) =- printJS lit- printJS (JsParen exp) =- "(" +> exp +> ")"- printJS (JsList exps) =- "[" +> intercalateM "," (map printJS exps) +> printJS "]"- printJS (JsNew name args) =- "new " +> (JsApp (JsName name) args)- printJS (JsIndex i exp) =- "(" +> exp +> ")[" +> show i +> "]"- printJS (JsEq exp1 exp2) =- exp1 +> " === " +> exp2- printJS (JsNeq exp1 exp2) =- exp1 +> " !== " +> exp2- printJS (JsGetProp exp prop) = exp +> "." +> prop- printJS (JsLookup exp1 exp2) =- exp1 +> "[" +> exp2 +> "]"+ printJS (JsThrowExp expr) = "(function(){ throw (" <> printJS expr <> "); })()"+ printJS JsNull = "null"+ printJS JsUndefined = "undefined"+ printJS (JsLit lit) = printJS lit+ printJS (JsParen expr) = "(" <> printJS expr <> ")"+ printJS (JsList exprs) = "[" <> mintercalate "," (map printJS exprs) <> "]"+ printJS (JsNew name args) = "new " <> printJS (JsApp (JsName name) args)+ printJS (JsIndex i expr) = "(" <> printJS expr <> ")[" <> write (show i) <> "]"+ printJS (JsEq expr1 expr2) = printJS expr1 <> " === " <> printJS expr2+ printJS (JsNeq expr1 expr2) = printJS expr1 <> " !== " <> printJS expr2+ printJS (JsGetProp expr prop) = printJS expr <> "." <> printJS prop+ printJS (JsLookup expr1 expr2) = printJS expr1 <> "[" <> printJS expr2 <> "]" printJS (JsUpdateProp name prop expr) =- "(" +> name +> "." +> prop +> " = " +> expr +> ")"- printJS (JsInfix op x y) =- x +> " " +> op +> " " +> y- printJS (JsGetPropExtern exp prop) =- exp +> "[" +> (JsLit . JsStr) prop +> "]"+ "(" <> printJS name <> "." <> printJS prop <> " = " <> printJS expr <> ")"+ printJS (JsInfix op x y) = printJS x <> " " <> write op <> " " <> printJS y+ printJS (JsGetPropExtern expr prop) =+ printJS expr <> "[" <> write (toJsStringLit prop) <> "]" printJS (JsUpdatePropExtern name prop expr) =- "(" +> name +> "['" +> prop +> "'] = " +> expr +> ")"+ "(" <> printJS name <> "['" <> printJS prop <> "'] = " <> printJS expr <> ")" printJS (JsTernaryIf cond conseq alt) =- cond +> " ? " +> conseq +> " : " +> alt- printJS (JsInstanceOf exp classname) =- exp +> " instanceof " +> classname+ printJS cond <> " ? " <> printJS conseq <> " : " <> printJS alt+ printJS (JsInstanceOf expr classname) =+ printJS expr <> " instanceof " <> printJS classname printJS (JsObj assoc) =- "{" +> (intercalateM "," (map cons assoc)) +> "}"- where cons (key,value) = "\"" +> key +> "\": " +> value- printJS (JsLitObj assoc) =- "{" +> (intercalateM "," (map cons assoc)) +> "}"- where- cons :: (N.Name, JsExp) -> Printer ()- cons (key,value) = "\"" +> key +> "\": " +> value+ "{" <> mintercalate "," (map cons assoc) <> "}"+ where cons (key,value) = write (toJsStringLit key) <> ": " <> printJS value+ printJS (JsLitObj assoc) = "{" <> mintercalate "," (map cons assoc) <> "}"+ where cons (key,value) = "\"" <> printJS key <> ": " <> printJS value printJS (JsFun nm params stmts ret) = "function"- +> maybe (return ()) ((" " +>) . printJS . ident) nm- +> "("- +> (intercalateM "," (map printJS params))- +> "){" +> newline- +> indented (stmts +>+ <> maybe mempty ((" " <>) . printJS . ident) nm+ <> "("+ <> mintercalate "," (map printJS params)+ <> "){" <> newline+ <> indented (printStmts stmts <> case ret of- Just ret' -> "return " +> ret' +> ";" +> newline- Nothing -> return ())- +> "}"+ Just ret' -> "return " <> printJS ret' <> ";" <> newline+ Nothing -> mempty)+ <> "}" printJS (JsApp op args) =- (if isFunc op then JsParen op else op)- +> "("- +> (intercalateM "," (map printJS args))- +> ")"- where isFunc JsFun{..} = True; isFunc _ = False- printJS (JsNegApp args) =- "(-(" +> printJS args +> "))"- printJS (JsAnd a b) =- printJS a +> "&&" +> printJS b- printJS (JsOr a b) =- printJS a +> "||" +> printJS b+ printJS (case op of JsFun {} -> JsParen op; _ -> op)+ <> "("+ <> mintercalate "," (map printJS args)+ <> ")"+ printJS (JsNegApp args) = "(-(" <> printJS args <> "))"+ printJS (JsAnd a b) = printJS a <> "&&" <> printJS b+ printJS (JsOr a b) = printJS a <> "||" <> printJS b -- | Unqualify a JsName. ident :: JsName -> JsName@@ -221,40 +207,32 @@ printJS name = case name of JsNameVar qname -> printJS qname- JsThis -> write "this"- JsThunk -> write "Fay$$$"- JsForce -> write "Fay$$_"- JsApply -> write "Fay$$__"- JsParam i -> write ("$p" ++ show i)- JsTmp i -> write ("$tmp" ++ show i)+ JsThis -> "this"+ JsThunk -> askIf prPrettyThunks "$" "Fay$$$"+ JsForce -> askIf prPrettyThunks "_" "Fay$$_"+ JsApply -> askIf prPrettyThunks "__" "Fay$$__"+ JsParam i -> "$p" <> write (show i)+ JsTmp i -> "$tmp" <> write (show i) JsConstructor qname -> printCons qname- JsBuiltIn qname -> "Fay$$" +> printJS qname- JsParametrizedType -> write "type"+ JsBuiltIn qname -> "Fay$$" <> printJS qname+ JsParametrizedType -> "type" JsModuleName (ModuleName _ m) -> write m -- | Print a constructor name given a QName.-printCons :: N.QName -> Printer ()+printCons :: QName l -> Printer printCons (UnQual _ n) = printConsName n-printCons (Qual _ (ModuleName _ m) n) = printJS m +> "." +> printConsName n-printCons (Special {}) = error "qname2String Special"+printCons (Qual _ (ModuleName _ m) n) = write m <> "." <> printConsName n+printCons Special {} = error "qname2String Special" -- | Print an unqualified name.-printConsUnQual :: N.QName -> Printer ()+printConsUnQual :: QName l -> Printer printConsUnQual (UnQual _ x) = printJS x printConsUnQual (Qual _ _ n) = printJS n-printConsUnQual (Special {}) = error "printConsUnqual Special"+printConsUnQual Special {} = error "printConsUnqual Special" -- | Print a constructor name given a Name. Helper for printCons.-printConsName :: N.Name -> Printer ()-printConsName n = write "_" >> printJS n---- | Just write out strings.-instance Printable String where- printJS = write---- | A printer is a printable.-instance Printable (Printer ()) where- printJS = id+printConsName :: Name l -> Printer+printConsName = ("_" <>) . printJS -------------------------------------------------------------------------------- -- Name encoding@@ -276,91 +254,31 @@ -- The problem only occurs if there is a module A.B and a constructor B in module A. ++ ["__defineGetter__", "__defineSetter__", "__lookupGetter__", "__lookupSetter__", "constructor", "force", "forced", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable", "toLocaleString", "toString", "value", "valueOf"] +allowedNameChars :: String+allowedNameChars = ['a'..'z'] ++ ['A'..'Z'] ++ ['0'..'9'] ++ "_" -- | Encode a Haskell name to JavaScript.-encodeName :: String -> String+encodeName :: Name l -> String+ -- | This is a hack for names generated in the Haskell AST. Should be -- removed once it's no longer needed.-encodeName ('$':'g':'e':'n':name) = "$gen_" ++ normalizeName name-encodeName name- | name `elem` reservedWords = "$_" ++ normalizeName name- | otherwise = normalizeName name+encodeName n = case n of+ (Ident _ idn) -> encodeString idn+ (Symbol _ sym) -> encodeString sym+ where encodeString ('$':'g':'e':'n':name) = "$gen_" ++ normalizeName name+ encodeString name+ | name `elem` reservedWords = "$_" ++ normalizeName name+ | otherwise = normalizeName name -- | Normalize the given name to JavaScript-valid names. normalizeName :: String -> String-normalizeName name =- concatMap encodeChar name-+normalizeName = concatMap encodeChar where- encodeChar c | c `elem` allowed = [c]- | otherwise = escapeChar c- allowed = ['a'..'z'] ++ ['A'..'Z'] ++ ['0'..'9'] ++ "_"+ encodeChar c | c `elem` allowedNameChars = [c]+ | otherwise = escapeChar c escapeChar c = "$" ++ charId c ++ "$" charId c = show (fromEnum c) ------------------------------------------------------------------------------------- Printing----- | Print the given printer indented.-indented :: Printer a -> Printer ()-indented p = do- PrintState{..} <- get- if psPretty- then do modify $ \s -> s { psIndentLevel = psIndentLevel + 1 }- p- modify $ \s -> s { psIndentLevel = psIndentLevel }- else p >> return ()---- | Output a newline.-newline :: Printer ()-newline = do- PrintState{..} <- get- when psPretty $ do- write "\n"- modify $ \s -> s { psNewline = True }---- | Write out a string, updating the current position information.-write :: String -> Printer ()-write x = do- PrintState{..} <- get- let out = if psNewline then replicate (2*psIndentLevel) ' ' ++ x else x- modify $ \s -> s { psOutput = out : psOutput- , psLine = psLine + additionalLines- , psColumn = if additionalLines > 0- then length (concat (take 1 (reverse srclines)))- else psColumn + length x- , psNewline = False- }- return (error "Nothing to return for writer string.")-- where srclines = lines x- additionalLines = length (filter (=='\n') x)---- | Generate a mapping from the Haskell location to the current point in the output.-mapping :: SrcSpan -> Printer ()-mapping SrcSpan{..} = do- modify $ \s -> s { psMappings = m s : psMappings s }- return ()-- where m ps = Mapping { mapGenerated = Pos (fromIntegral (psLine ps))- (fromIntegral (psColumn ps))- , mapOriginal = Just (Pos (fromIntegral srcSpanStartLine)- (fromIntegral srcSpanStartColumn - 1))- , mapSourceFile = Just srcSpanFilename- , mapName = Nothing- }---- | Intercalate monadic action.-intercalateM :: String -> [Printer a] -> Printer ()-intercalateM _ [] = return ()-intercalateM _ [x] = x >> return ()-intercalateM str (x:xs) = do- x- write str- intercalateM str xs---- | Concatenate two printables.-(+>) :: (Printable a, Printable b) => a -> b -> Printer ()-pa +> pb = printJS pa >> printJS pb+-- | Intercalate monoids.+mintercalate :: String -> [Printer] -> Printer+mintercalate str xs = mconcat $ intersperse (write str) xs
src/Fay/Compiler/QName.hs view
@@ -2,7 +2,7 @@ module Fay.Compiler.QName where -import Language.Haskell.Exts.Annotated+import Language.Haskell.Exts -- | Extract the module name from a qualified name. qModName :: QName a -> Maybe (ModuleName a)
src/Fay/Compiler/State.hs view
@@ -12,7 +12,7 @@ import qualified Data.Map as M import Data.Set (Set) import qualified Data.Set as S-import Language.Haskell.Names+import Language.Haskell.Names (sv_origName, Symbols (Symbols), SymValueInfo (SymValue, SymMethod, SymSelector, SymConstructor), OrigName, sv_typeName) -- | Get all non local identifiers that should be exported in the JS module scope. getNonLocalExportsWithoutNewtypes :: N.ModuleName -> CompileState -> Maybe (Set N.QName)@@ -40,10 +40,10 @@ -- | 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) . origName2QName $ sv_typeName- SymConstructor { sv_typeName } -> not . (`isNewtypeCons` cs) . origName2QName $ sv_typeName+ SymValue{} -> False+ SymMethod{} -> False+ SymSelector { sv_typeName = tn } -> not . (`isNewtypeDest` cs) . origName2QName $ tn+ SymConstructor { sv_typeName = tn } -> not . (`isNewtypeCons` cs) . origName2QName $ tn -- | Is this *resolved* name a new type destructor? isNewtypeDest :: N.QName -> CompileState -> Bool@@ -61,6 +61,6 @@ addedModulePath :: ModulePath -> CompileState -> Bool addedModulePath mp CompileState { stateJsModulePaths } = mp `S.member` stateJsModulePaths -+-- | Find the type signature of a top level name findTypeSig :: N.QName -> CompileState -> Maybe N.Type findTypeSig n = M.lookup n . stateTypeSigs
src/Fay/Compiler/Typecheck.hs view
@@ -2,17 +2,20 @@ module Fay.Compiler.Typecheck where +import Fay.Compiler.Prelude+ import Fay.Compiler.Defaults import Fay.Compiler.Misc-import Fay.System.Process.Extra+import Fay.Config import Fay.Types -import Data.List-import Data.Maybe-import qualified GHC.Paths as GHCPaths+import qualified GHC.Paths as GHCPaths +import System.Directory+import System.Environment+ -- | Call out to GHC to type-check the file.-typecheck :: CompileConfig -> FilePath -> IO (Either CompileError String)+typecheck :: Config -> FilePath -> IO (Either CompileError String) typecheck cfg fp = do faydir <- faySourceDir let includes = configDirectoryIncludes cfg@@ -29,8 +32,7 @@ return [flag ++ '=' : pk] let flags = [ "-fno-code"- , "-XNoImplicitPrelude"- , "-hide-package base"+ , "-hide-all-packages" , "-cpp", "-pgmPcpphs", "-optP--cpp" , "-optP-C" -- Don't let hse-cpp remove //-style comments. , "-DFAY=1"@@ -38,7 +40,21 @@ , "Language.Fay.DummyMain" , "-i" ++ intercalate ":" includeDirs , fp ] ++ ghcPackageDbArgs ++ wallF ++ map ("-package " ++) packages- res <- readAllFromProcess GHCPaths.ghc flags ""+ exists <- doesFileExist GHCPaths.ghc+ stackInNixShell <- fmap isJust (lookupEnv "STACK_IN_NIX_SHELL")+ let ghcPath = if exists+ then if (isInfixOf ".stack" GHCPaths.ghc || stackInNixShell)+ then "stack"+ else GHCPaths.ghc+ else "ghc"+ extraFlags = case ghcPath of+ "stack" -> ["exec","--","ghc"]+ _ -> []+ when (configShowGhcCalls cfg) $+ putStrLn . unwords $ ghcPath : (extraFlags ++ flags)+ when stackInNixShell (unsetEnv "STACK_IN_NIX_SHELL")+ res <- readAllFromProcess ghcPath (extraFlags ++ flags) ""+ when stackInNixShell (setEnv "STACK_IN_NIX_SHELL" "1") either (return . Left . GHCError . fst) (return . Right . fst) res where wallF | configWall cfg = ["-Wall"]
+ src/Fay/Config.hs view
@@ -0,0 +1,165 @@+{-# LANGUAGE NoImplicitPrelude #-}+-- | Configuring the compiler+module Fay.Config+ ( Config+ ( configOptimize+ , configFlattenApps+ , configExportRuntime+ , configExportStdlib+ , configExportStdlibOnly+ , configPrettyPrint+ , configHtmlWrapper+ , configSourceMap+ , configHtmlJSLibs+ , configLibrary+ , configWarn+ , configFilePath+ , configTypecheck+ , configWall+ , configGClosure+ , configPackageConf+ , configBasePath+ , configStrict+ , configTypecheckOnly+ , configRuntimePath+ , configOptimizeNewtypes+ , configPrettyThunks+ , configPrettyOperators+ , configShowGhcCalls+ , configTypeScript+ )+ , defaultConfig+ , defaultConfigWithSandbox+ , configDirectoryIncludes+ , configDirectoryIncludePaths+ , nonPackageConfigDirectoryIncludePaths+ , addConfigDirectoryInclude+ , addConfigDirectoryIncludes+ , addConfigDirectoryIncludePaths+ , configPackages+ , addConfigPackage+ , addConfigPackages+ , shouldExportStrictWrapper+ ) where++import Fay.Compiler.Prelude++import Data.Default+import Data.Maybe ()+import Language.Haskell.Exts (ModuleName (..))+import System.Environment++-- | Configuration of the compiler.+-- The fields with a leading underscore+data Config = Config+ { 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.+ , configSourceMap :: Bool -- ^ Output a source map file as outfile.map.+ , 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 JavaScript callable wrappers for all+ -- exported functions with type signatures in the given module+ , configTypecheckOnly :: Bool -- ^ Only invoke GHC for typechecking, don't produce any output+ , configRuntimePath :: Maybe FilePath+ , configOptimizeNewtypes :: Bool -- ^ Optimize away newtype constructors?+ , configPrettyThunks :: Bool -- ^ Use pretty thunk names?+ , configPrettyOperators :: Bool -- ^ Use pretty operators?+ , configShowGhcCalls :: Bool -- ^ Print commands sent to GHC?+ , configTypeScript :: Bool -- ^ Output a TypeScript file.+ } deriving (Show)++defaultConfig :: Config+defaultConfig = addConfigPackage "fay-base"+ Config+ { configOptimize = False+ , configFlattenApps = False+ , configExportRuntime = True+ , configExportStdlib = True+ , configExportStdlibOnly = False+ , _configDirectoryIncludes = []+ , configPrettyPrint = False+ , configHtmlWrapper = False+ , configHtmlJSLibs = []+ , configLibrary = False+ , configWarn = True+ , configFilePath = Nothing+ , configTypecheck = True+ , configWall = False+ , configGClosure = False+ , configPackageConf = Nothing+ , _configPackages = []+ , configBasePath = Nothing+ , configStrict = []+ , configTypecheckOnly = False+ , configRuntimePath = Nothing+ , configSourceMap = False+ , configOptimizeNewtypes = True+ , configPrettyThunks = False+ , configPrettyOperators = False+ , configShowGhcCalls = False+ , configTypeScript = False+ }++defaultConfigWithSandbox :: IO Config+defaultConfigWithSandbox = do+ packageConf <- fmap (lookup "HASKELL_PACKAGE_SANDBOX") getEnvironment+ return defaultConfig { configPackageConf = packageConf }++-- | Default configuration.+instance Default Config where+ def = defaultConfig++-- | Reading _configDirectoryIncludes is safe to do.+configDirectoryIncludes :: Config -> [(Maybe String, FilePath)]+configDirectoryIncludes = _configDirectoryIncludes++-- | Get all include directories without the package mapping.+configDirectoryIncludePaths :: Config -> [FilePath]+configDirectoryIncludePaths = map snd . _configDirectoryIncludes++-- | Get all include directories not included through packages.+nonPackageConfigDirectoryIncludePaths :: Config -> [FilePath]+nonPackageConfigDirectoryIncludePaths = map snd . filter (isJust . fst) . _configDirectoryIncludes++-- | Add a mapping from (maybe) a package to a source directory+addConfigDirectoryInclude :: Maybe String -> FilePath -> Config -> Config+addConfigDirectoryInclude pkg fp cfg = cfg { _configDirectoryIncludes = (pkg, fp) : _configDirectoryIncludes cfg }++-- | Add several include directories.+addConfigDirectoryIncludes :: [(Maybe String,FilePath)] -> Config -> Config+addConfigDirectoryIncludes pkgFps cfg = foldl (\c (pkg,fp) -> addConfigDirectoryInclude pkg fp c) cfg pkgFps++-- | Add several include directories without package references.+addConfigDirectoryIncludePaths :: [FilePath] -> Config -> Config+addConfigDirectoryIncludePaths fps cfg = foldl (flip (addConfigDirectoryInclude Nothing)) cfg fps++-- | Reading _configPackages is safe to do.+configPackages :: Config -> [String]+configPackages = _configPackages++-- | Add a package to compilation+addConfigPackage :: String -> Config -> Config+addConfigPackage pkg cfg = cfg { _configPackages = pkg : _configPackages cfg }++-- | Add several packages to compilation+addConfigPackages :: [String] -> Config -> Config+addConfigPackages fps cfg = foldl (flip addConfigPackage) cfg fps+++-- | Should a strict wrapper be generated for this module?+shouldExportStrictWrapper :: ModuleName a -> Config -> Bool+shouldExportStrictWrapper (ModuleName _ m) cs = m `elem` configStrict cs
− src/Fay/Control/Monad/Extra.hs
@@ -1,31 +0,0 @@--- | Extra monadic functions.--module Fay.Control.Monad.Extra where--import Control.Monad-import Data.Maybe---- | Word version of flip (>>=).-bind :: (Monad m) => (a -> m b) -> m a -> m b-bind = flip (>>=)---- | When the value is Just.-whenJust :: Monad m => Maybe a -> (a -> m ()) -> m ()-whenJust (Just a) m = m a-whenJust Nothing _ = return ()---- | Wrap up a form in a Maybe.-just :: Functor m => m a -> m (Maybe a)-just = fmap Just---- | Flip of mapMaybe.-forMaybe :: [a] -> (a -> Maybe b) -> [b]-forMaybe = flip mapMaybe---- | Monadic version of maybe.-maybeM :: (Monad m) => a -> (a1 -> m a) -> Maybe a1 -> m a-maybeM nil cons a = maybe (return nil) cons a---- | Do any of the (monadic) predicates match?-anyM :: Monad m => (a -> m Bool) -> [a] -> m Bool-anyM p l = return . not . null =<< filterM p l
− src/Fay/Control/Monad/IO.hs
@@ -1,9 +0,0 @@--- | Alias of MonadIO.--module Fay.Control.Monad.IO where--import Control.Monad.Trans---- | Alias of liftIO, I hate typing it. Hate reading it.-io :: MonadIO m => IO a -> m a-io = liftIO
src/Fay/Convert.hs view
@@ -1,234 +1,252 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE PatternGuards #-}-{-# LANGUAGE TupleSections #-}-{-# OPTIONS -fno-warn-type-defaults #-}+{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE PatternGuards #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE ViewPatterns #-} -- | Convert a Haskell value to a (JSON representation of a) Fay value. module Fay.Convert (showToFay- ,readFromFay)+ ,readFromFay+ ,readFromFay'+ ,encodeFay+ ,decodeFay) where -import Control.Applicative-import Control.Monad-import Control.Monad.State+import Fay.Compiler.Prelude++import Control.Monad.State (evalStateT, get, lift, put)+import Control.Spoon import Data.Aeson-import Data.Attoparsec.Number-import Data.Char+import Data.Aeson.Types (parseEither) import Data.Data-import Data.Function import Data.Generics.Aliases-import Data.HashMap.Strict (HashMap)-import qualified Data.HashMap.Strict as Map-import Data.Maybe-import Data.Text (Text)-import qualified Data.Text as Text-import Data.Vector (Vector)-import qualified Data.Vector as Vector-import Numeric-import Safe-import qualified Text.Show.Pretty as Show+import Data.HashMap.Strict (HashMap)+import qualified Data.HashMap.Strict as Map+import Data.Text (Text)+import qualified Data.Text as Text+import Data.Time.Clock (UTCTime)+import Data.Vector (Vector)+import qualified Data.Vector as Vector -------------------------------------------------------------------------------- -- The conversion functions. --- | Convert a Haskell value to a value representing a Fay value.-showToFay :: Show a => a -> Maybe Value-showToFay = Show.reify >=> convert where- convert value = case value of- -- Special cases- Show.Con "True" _ -> return (Bool True)- Show.Con "False" _ -> return (Bool False)-- -- Objects/records- Show.Con name values -> fmap (Object . Map.fromList . (("instance",string name) :))- (slots values)- Show.Rec name fields -> fmap (Object . Map.fromList . (("instance",string name) :))- (mapM (uncurry keyval) fields)- Show.InfixCons _ _ -> Nothing -- TODO https://github.com/faylang/fay/issues/316-- -- ()- Show.Tuple [] -> return Null-- -- List types- Show.Tuple values -> fmap (Array . Vector.fromList) (mapM convert values)- Show.List values -> fmap (Array . Vector.fromList) (mapM convert values)-- -- Text types- Show.String chars -> fmap string (readMay chars)- Show.Char char -> fmap (string.return) (readMay char)+-- | Convert a Haskell value to a Fay json value. This can fail when primitive+-- values aren't handled by explicit cases. 'encodeFay' can be used to+-- resolve this issue.+showToFay :: Data a => a -> Maybe Value+showToFay = spoon . encodeFay id - -- Numeric types (everything treated as a double)- Show.Neg{} -> double <|> int- Show.Integer{} -> int- Show.Float{} -> double- Show.Ratio{} -> double- where double = convertDouble value- int = convertInt value+-- | Convert a Haskell value to a Fay json value. This can fail when primitive+-- values aren't handled by explicit cases. When this happens, you can add+-- additional cases via the first parameter.+--+-- The first parameter is a function that can be used to override the+-- conversion. This usually looks like using 'extQ' to additional type-+-- specific cases.+encodeFay :: (GenericQ Value -> GenericQ Value) -> GenericQ Value+encodeFay specialCases = specialCases $+ encodeGeneric rec+ `extQ` unit+ `extQ` Bool+ `extQ` (toJSON :: Int -> Value)+ `extQ` (toJSON :: Float -> Value)+ `extQ` (toJSON :: Double -> Value)+ `extQ` (toJSON :: UTCTime -> Value)+ `ext1Q` list+ `extQ` string+ `extQ` char+ `extQ` text+ where+ rec :: GenericQ Value+ rec = encodeFay specialCases+ unit () = Null+ list :: Data a => [a] -> Value+ list = Array . Vector.fromList . map rec+ string = String . Text.pack+ char = String . Text.pack . (:[])+ text = String - -- Number converters- convertDouble = fmap (Number . D) . pDouble- convertInt = fmap (Number . I) . pInt+encodeGeneric :: GenericQ Value -> GenericQ Value+encodeGeneric rec x =+ case constrName of+ '(':(dropWhile (==',') -> ")") ->+ Array $ Vector.fromList $ gmapQ rec x+ _ -> Object $ Map.fromList $ map (first Text.pack) fields+ where+ fields =+ ("instance", String $ Text.pack constrName) :+ zip labels (gmapQ rec x)+ constrName = showConstr constr+ constr = toConstr x+ -- Note: constrFields can throw errors for non-algebraic datatypes. These+ -- ought to be taken care of in the other cases of encodeFay.+ labels = case constrFields constr of+ [] -> map (("slot"++).show) [1::Int ..]+ ls -> ls - -- Number parsers- pDouble :: Show.Value -> Maybe Double- pDouble value = case value of- Show.Float str -> getDouble str- Show.Ratio x y -> liftM2 (on (/) fromIntegral) (pInt x) (pInt y)- Show.Neg str -> fmap (* (-1)) (pDouble str)- _ -> Nothing- pInt value = case value of- Show.Integer str -> getInt str- Show.Neg str -> fmap (* (-1)) (pInt str)- _ -> Nothing+-- | Convert a Fay json value to a Haskell value.+readFromFay :: Data a => Value -> Maybe a+readFromFay = either (const Nothing) Just . decodeFay (const id) - -- Number readers- getDouble :: String -> Maybe Double- getDouble = fmap fst . listToMaybe . readFloat- getInt :: String -> Maybe Integer- getInt = fmap fst . listToMaybe . readInt 10 isDigit charToInt- where charToInt c = fromEnum c - fromEnum '0'+-- | Convert a Fay json value to a Haskell value. This is like readFromFay,+-- except it yields helpful error messages on failure.+readFromFay' :: Data a => Value -> Either String a+readFromFay' = decodeFay (const id) - -- Utilities- string = String . Text.pack- slots = zipWithM keyval (map (("slot"++).show) [1::Int ..])- keyval key val = fmap (Text.pack key,) (convert val)+-- | Convert a Fay json value to a Haskell value.+--+-- The first parameter is a function that can be used to override the+-- conversion. This usually looks like using 'extR' to additional type-+-- specific cases.+decodeFay :: Data b+ => (forall a. Data a => Value -> Either String a -> Either String a)+ -> Value+ -> Either String b+decodeFay specialCases value = specialCases value $+ parseDataOrTuple rec value+ `extR` parseUnit value+ `extR` parseBool value+ `extR` parseInt value+ `extR` parseFloat value+ `extR` parseDouble value+ `ext1R` parseArray rec value+ `extR` parseUTCTime value+ `extR` parseString value+ `extR` parseChar value+ `extR` parseText value+ where+ rec :: GenericParser+ rec = decodeFay specialCases --- | Convert a value representing a Fay value to a Haskell value.-readFromFay :: Data a => Value -> Maybe a-readFromFay value =- parseDataOrTuple value- `ext1R` parseArray value- `extR` parseDouble value- `extR` parseInt value- `extR` parseInteger value- `extR` parseBool value- `extR` parseString value- `extR` parseChar value- `extR` parseText value- `extR` parseUnit value+type GenericParser = forall a. Data a => Value -> Either String a -- | Parse a data type or record or tuple.-parseDataOrTuple :: Data a => Value -> Maybe a-parseDataOrTuple value = result where+parseDataOrTuple :: forall a. Data a => GenericParser -> Value -> Either String a+parseDataOrTuple rec value = result where result = getAndParse value- typ = dataTypeOf (fromJust result)+ typ = dataTypeOf (undefined :: a) getAndParse x = case x of- Object obj -> parseObject typ obj- Array tuple -> parseTuple typ tuple- _ -> mzero+ Object obj -> parseObject rec typ obj+ Array tuple -> parseTuple rec typ tuple+ _ -> badData value -- | Parse a tuple.-parseTuple :: Data a => DataType -> Vector Value -> Maybe a-parseTuple typ arr =+parseTuple :: Data a => GenericParser -> DataType -> Vector Value -> Either String a+parseTuple rec typ arr = case dataTypeConstrs typ of- [cons] -> evalStateT (fromConstrM (do i:next <- get+ [cons] -> evalStateT (fromConstrM (do ~(i:next) <- get put next value <- lift (Vector.indexM arr i)- lift (readFromFay value))+ lift (rec value)) cons) [0..]- _ -> Nothing+ _ -> badData (Array arr) -- | Parse a data constructor from an object.-parseObject :: Data a => DataType -> HashMap Text Value -> Maybe a-parseObject typ obj = listToMaybe (catMaybes choices) where- choices = map makeConstructor constructors- constructors = dataTypeConstrs typ- makeConstructor cons = do- name <- Map.lookup (Text.pack "instance") obj >>= parseString- guard (showConstr cons == name)- if null fields- then makeSimple obj cons- else makeRecord obj cons fields-- where fields = constrFields cons+parseObject :: Data a => GenericParser -> DataType -> HashMap Text Value -> Either String a+parseObject rec typ obj =+ case Map.lookup (Text.pack "instance") obj of+ Just (parseString -> Right name) ->+ case filter (\con -> showConstr con == name) (dataTypeConstrs typ) of+ [con] ->+ let fields = constrFields con+ in if null fields+ then makeSimple rec obj con+ else makeRecord rec obj con fields+ _ -> badData (Object obj)+ _ -> badData (Object obj) -- | Make a simple ADT constructor from an object: { "slot1": 1, "slot2": 2} -> Foo 1 2-makeSimple :: Data a => HashMap Text Value -> Constr -> Maybe a-makeSimple obj cons =- evalStateT (fromConstrM (do i:next <- get+makeSimple :: Data a => GenericParser -> HashMap Text Value -> Constr -> Either String a+makeSimple rec obj cons =+ evalStateT (fromConstrM (do ~(i:next) <- get put next- value <- lift (Map.lookup (Text.pack ("slot" ++ show i)) obj)- lift (readFromFay value))+ value <- lift (lookupField obj (Text.pack ("slot" ++ show i)))+ lift (rec value)) cons)- [1..]+ [(1::Integer)..] -- | Make a record from a key-value: { "x": 1 } -> Foo { x = 1 }-makeRecord :: Data a => HashMap Text Value -> Constr -> [String] -> Maybe a-makeRecord obj cons fields =- evalStateT (fromConstrM (do key:next <- get- put next- value <- lift (Map.lookup (Text.pack key) obj)- lift (readFromFay value))- cons)- fields+makeRecord :: Data a => GenericParser -> HashMap Text Value -> Constr -> [String] -> Either String a+makeRecord rec obj cons =+ evalStateT $+ fromConstrM+ (do ~(key:next) <- get+ put next+ value <- lift (lookupField obj (Text.pack key))+ lift $ rec value)+ cons --- | Parse a double.-parseDouble :: Value -> Maybe Double-parseDouble value = do- number <- parseNumber value- case number of- I n -> return (fromIntegral n)- D n -> return n+lookupField :: HashMap Text Value -> Text -> Either String Value+lookupField obj key =+ justRight ("Missing field " ++ Text.unpack key ++ " in " ++ show (Object obj)) $+ Map.lookup key obj --- | Parse an int.-parseInt :: Value -> Maybe Int-parseInt value = do- number <- parseNumber value- case number of- I n -> return (fromIntegral n)- _ -> mzero+-- | Parse a float.+parseFloat :: Value -> Either String Float+parseFloat = parseEither parseJSON --- | Parse an integer.-parseInteger :: Value -> Maybe Integer-parseInteger value = do- number <- parseNumber value- case number of- I n -> return n- _ -> mzero+-- | Parse a double.+parseDouble :: Value -> Either String Double+parseDouble = parseEither parseJSON --- | Parse a number.-parseNumber :: Value -> Maybe Number-parseNumber value = case value of- Number n -> return n- _ -> mzero+-- | Parse an int.+parseInt :: Value -> Either String Int+parseInt = parseEither parseJSON -- | Parse a bool.-parseBool :: Value -> Maybe Bool+parseBool :: Value -> Either String Bool parseBool value = case value of Bool n -> return n- _ -> mzero+ _ -> badData value -- | Parse a string.-parseString :: Value -> Maybe String+parseString :: Value -> Either String String parseString value = case value of String s -> return (Text.unpack s)- _ -> mzero+ _ -> badData value +parseUTCTime :: Value -> Either String UTCTime+parseUTCTime value = case fromJSON value of+ Success t -> Right t+ Error _ -> badData value+ -- | Parse a char.-parseChar :: Value -> Maybe Char+parseChar :: Value -> Either String Char parseChar value = case value of String s | Just (c,_) <- Text.uncons s -> return c- _ -> mzero+ _ -> badData value -- | Parse a Text.-parseText :: Value -> Maybe Text+parseText :: Value -> Either String Text parseText value = case value of String s -> return s- _ -> mzero+ _ -> badData value -- | Parse an array.-parseArray :: Data a => Value -> Maybe [a]-parseArray value = case value of- Array xs -> mapM readFromFay (Vector.toList xs)- _ -> mzero+parseArray :: Data a => GenericParser -> Value -> Either String [a]+parseArray rec value = case value of+ Array xs -> mapM rec (Vector.toList xs)+ _ -> badData value -- | Parse unit.-parseUnit :: Value -> Maybe ()+parseUnit :: Value -> Either String () parseUnit value = case value of Null -> return ()- _ -> mzero+ _ -> badData value++badData :: forall a. Data a => Value -> Either String a+badData value = Left $+ "Bad data in decodeFay - expected valid " +++ show (typeOf (undefined :: a)) +++ ", but got:\n" +++ show value++justRight :: b -> Maybe a -> Either b a+justRight x Nothing = Left x+justRight _ (Just y) = Right y
− src/Fay/Data/List/Extra.hs
@@ -1,14 +0,0 @@--- | Extra list functions.--module Fay.Data.List.Extra where--import Data.List hiding (map)-import Prelude hiding (map)---- | Get the union of a list of lists.-unionOf :: (Eq a) => [[a]] -> [a]-unionOf = foldr union []---- | Flip of map.-for :: (Functor f) => f a -> (a -> b) -> f b-for = flip fmap
src/Fay/Exts.hs view
@@ -1,6 +1,6 @@ module Fay.Exts where -import qualified Language.Haskell.Exts.Annotated as A+import qualified Language.Haskell.Exts as A type X = A.SrcSpanInfo @@ -15,7 +15,6 @@ 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@@ -46,7 +45,7 @@ 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)+moduleExports m = error $ "moduleExports: " ++ A.prettyPrint m moduleNameString :: A.ModuleName t -> String moduleNameString (A.ModuleName _ n) = n@@ -57,7 +56,7 @@ 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 a -> ([A.Name a], A.Type a) convertFieldDecl (A.FieldDecl _ ns b) = (ns, b) fieldDeclNames :: A.FieldDecl a -> [A.Name a]@@ -65,6 +64,7 @@ declHeadName :: A.DeclHead a -> A.Name a declHeadName d = case d of- A.DHead _ n _ -> n- A.DHInfix _ _ n _ -> n+ A.DHead _ n -> n+ A.DHInfix _ _ n -> n A.DHParen _ h -> declHeadName h+ A.DHApp _ h _ -> declHeadName h
src/Fay/Exts/NoAnnotation.hs view
@@ -2,12 +2,11 @@ {-# LANGUAGE FlexibleInstances #-} module Fay.Exts.NoAnnotation where +import Fay.Compiler.Prelude -import Data.Char (isAlpha)-import Data.List (intercalate) import Data.List.Split (splitOn) import Data.String-import qualified Language.Haskell.Exts.Annotated as A+import qualified Language.Haskell.Exts as A type Alt = A.Alt () type BangType = A.BangType ()@@ -20,7 +19,6 @@ 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 ()@@ -37,6 +35,7 @@ type QualConDecl = A.QualConDecl () type QualStmt = A.QualStmt () type Rhs = A.Rhs ()+type Sign = A.Sign () type SpecialCon = A.SpecialCon () type SrcLoc = A.SrcLoc type SrcSpan = A.SrcSpan@@ -46,7 +45,7 @@ type Type = A.Type () unAnn :: Functor f => f a -> f ()-unAnn = fmap (const ())+unAnn = void -- | Helpful for some things. instance IsString (A.Name ()) where
src/Fay/Exts/Scoped.hs view
@@ -2,8 +2,8 @@ import qualified Fay.Exts as F -import qualified Language.Haskell.Exts.Annotated as A-import qualified Language.Haskell.Names as HN+import qualified Language.Haskell.Exts as A+import qualified Language.Haskell.Names as HN (Scoped (Scoped), NameInfo (None)) type X = HN.Scoped A.SrcSpanInfo@@ -19,7 +19,6 @@ 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@@ -36,6 +35,7 @@ type QualConDecl = A.QualConDecl X type QualStmt = A.QualStmt X type Rhs = A.Rhs X+type Sign = A.Sign X type SpecialCon = A.SpecialCon X type SrcLoc = A.SrcLoc type Stmt = A.Stmt X
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 (error) -- | Values that may be null -- Nullable x decodes to x, Null decodes to null.
+ src/Fay/Runtime.hs view
@@ -0,0 +1,842 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE QuasiQuotes #-}++module Fay.Runtime where++import Text.Shakespeare.Text+import Data.Text.Lazy as T+import Fay.Config++-- | Get the default runtime source.+getRuntimeSource :: Config -> String+getRuntimeSource cfg =+ let ifTs :: T.Text -> T.Text+ ifTs a = if configTypeScript cfg then a else ""+ ifTsJs :: T.Text -> T.Text -> T.Text+ ifTsJs a b = if configTypeScript cfg then a else b+ in T.unpack [lt|+/*******************************************************************************+ * Misc.+ */+#{ifTs "var Fay:{[key:string]: any;} = {};"}++// Workaround for missing functionality in IE 8 and earlier.+if( Object.create === undefined ) {+ Object.create = function( o ) {+ function F(){}+ F.prototype = o;+ return new F();+ };+}++// Insert properties of b in place into a.+function Fay$$objConcat(a,b){+ for (var p in b) if (b.hasOwnProperty(p)){+ a[p] = b[p];+ }+ return a;+}++/*******************************************************************************+ * Thunks.+ */++// Force a thunk (if it is a thunk) until WHNF.+function Fay$$_(thunkish,nocache#{ifTs "?: boolean"}){+ while (thunkish instanceof Fay$$$) {+ thunkish = thunkish.force(nocache);+ }+ return thunkish;+}++// Apply a function to arguments (see method2 in Fay.hs).+function Fay$$__(){+ var f = arguments[0];+ for (var i = 1, len = arguments.length; i < len; i++) {+ f = (f instanceof Fay$$$? Fay$$_(f) : f)(arguments[i]);+ }+ return f;+}++// Thunk object.+function Fay$$$(value){+ this.forced = false;+ this.value = value;+}++// Force the thunk.+Fay$$$.prototype.force = function(nocache) {+ return nocache ?+ this.value() :+ (this.forced ?+ this.value :+ (this.value = this.value(), this.forced = true, this.value));+};+++function Fay$$seq(x) {+ return function(y) {+ Fay$$_(x,false);+ return y;+ }+}++function Fay$$seq$36$uncurried(x,y) {+ Fay$$_(x,false);+ return y;+}++/*******************************************************************************+ * Monad.+ */++function Fay$$Monad(value){+ this.value = value;+}++// This is used directly from Fay, but can be rebound or shadowed. See primOps in Types.hs.+// >>+function Fay$$then(a){+ return function(b){+ return Fay$$bind(a)(function(_){+ return b;+ });+ };+}++// This is used directly from Fay, but can be rebound or shadowed. See primOps in Types.hs.+// >>+function Fay$$then$36$uncurried(a,b){+ return Fay$$bind$36$uncurried(a,function(_){ return b; });+}++// >>=+// This is used directly from Fay, but can be rebound or shadowed. See primOps in Types.hs.+function Fay$$bind(m){+ return function(f){+ return new Fay$$$(function(){+ var monad = Fay$$_(m,true);+ return Fay$$_(f)(monad.value);+ });+ };+}++// >>=+// This is used directly from Fay, but can be rebound or shadowed. See primOps in Types.hs.+function Fay$$bind$36$uncurried(m,f){+ return new Fay$$$(function(){+ var monad = Fay$$_(m,true);+ return Fay$$_(f)(monad.value);+ });+}++// This is used directly from Fay, but can be rebound or shadowed.+function Fay$$$_return(a){+ return new Fay$$Monad(a);+}++// Allow the programmer to access thunk forcing directly.+function Fay$$force(thunk){+ return function(type){+ return new Fay$$$(function(){+ Fay$$_(thunk,type);+ return new Fay$$Monad(Fay$$unit);+ })+ }+}++// This is used directly from Fay, but can be rebound or shadowed.+function Fay$$return$36$uncurried(a){+ return new Fay$$Monad(a);+}++// Unit: ().+var Fay$$unit = null;++/*******************************************************************************+ * Serialization.+ * Fay <-> JS. Should be bijective.+ */++// Serialize a Fay object to JS.+function Fay$$fayToJs(type,fayObj){+ var base = type[0];+ var args = type[1];+ var jsObj;+ if(base == "action") {+ // A nullary monadic action. Should become a nullary JS function.+ // Fay () -> function(){ return ... }+ return function(){+ return Fay$$fayToJs(args[0],Fay$$_(fayObj,true).value);+ };++ }+ else if(base == "function") {+ // A proper function.+ return function(){+ var fayFunc = fayObj;+ var return_type = args[args.length-1];+ var len = args.length;+ // If some arguments.+ if (len > 1) {+ // Apply to all the arguments.+ fayFunc = Fay$$_(fayFunc,true);+ // TODO: Perhaps we should throw an error when JS+ // passes more arguments than Haskell accepts.++ // Unserialize the JS values to Fay for the Fay callback.+ if (args == "automatic_function")+ {+ for (var i = 0; i < arguments.length; i++) {+ fayFunc = Fay$$_(fayFunc(Fay$$jsToFay(["automatic"],arguments[i])),true);+ }+ return Fay$$fayToJs(["automatic"], fayFunc);+ }++ for (var i = 0, len = len; i < len - 1 && fayFunc instanceof Function; i++) {+ fayFunc = Fay$$_(fayFunc(Fay$$jsToFay(args[i],arguments[i])),true);+ }+ // Finally, serialize the Fay return value back to JS.+ var return_base = return_type[0];+ var return_args = return_type[1];+ // If it's a monadic return value, get the value instead.+ if(return_base == "action") {+ return Fay$$fayToJs(return_args[0],fayFunc.value);+ }+ // Otherwise just serialize the value direct.+ else {+ return Fay$$fayToJs(return_type,fayFunc);+ }+ } else {+ throw new Error("Nullary function?");+ }+ };++ }+ else if(base == "string") {+ return Fay$$fayToJs_string(fayObj);+ }+ else if(base == "list") {+ // Serialize Fay list to JavaScript array.+ var arr = [];+ fayObj = Fay$$_(fayObj);+ while(fayObj instanceof Fay$$Cons) {+ arr.push(Fay$$fayToJs(args[0],fayObj.car));+ fayObj = Fay$$_(fayObj.cdr);+ }+ return arr;+ }+ else if(base == "tuple") {+ // Serialize Fay tuple to JavaScript array.+ var arr = [];+ fayObj = Fay$$_(fayObj);+ var i = 0;+ while(fayObj instanceof Fay$$Cons) {+ arr.push(Fay$$fayToJs(args[i++],fayObj.car));+ fayObj = Fay$$_(fayObj.cdr);+ }+ return arr;+ }+ else if(base == "defined") {+ fayObj = Fay$$_(fayObj);+ return fayObj instanceof Fay.FFI._Undefined+ ? undefined+ : Fay$$fayToJs(args[0],fayObj.slot1);+ }+ else if(base == "nullable") {+ fayObj = Fay$$_(fayObj);+ return fayObj instanceof Fay.FFI._Null+ ? null+ : Fay$$fayToJs(args[0],fayObj.slot1);+ }+ else if(base == "double" || base == "int" || base == "bool") {+ // Bools are unboxed.+ return Fay$$_(fayObj);+ }+ else if(base == "ptr")+ return fayObj;+ else if(base == "unknown")+ return Fay$$fayToJs(["automatic"], fayObj);+ else if(base == "automatic" && fayObj instanceof Function) {+ return Fay$$fayToJs(["function", "automatic_function"], fayObj);+ }+ else if(base == "automatic" || base == "user") {+ fayObj = Fay$$_(fayObj);++ if(fayObj instanceof Fay$$Cons || fayObj === null){+ // Serialize Fay list to JavaScript array.+ var arr = [];+ while(fayObj instanceof Fay$$Cons) {+ arr.push(Fay$$fayToJs(["automatic"],fayObj.car));+ fayObj = Fay$$_(fayObj.cdr);+ }+ return arr;+ } else {+ var fayToJsFun = fayObj && fayObj.instance && Fay$$fayToJsHash[fayObj.instance];+ return fayToJsFun ? fayToJsFun(type,type[2],fayObj) : fayObj;+ }+ }++ throw new Error("Unhandled Fay->JS translation type: " + base);+}++// Stores the mappings from fay types to js objects.+// This will be populated by compiled modules.+var Fay$$fayToJsHash = {};++// Specialized serializer for string.+function Fay$$fayToJs_string(fayObj){+ // Serialize Fay string to JavaScript string.+ var str = "";+ fayObj = Fay$$_(fayObj);+ while(fayObj instanceof Fay$$Cons) {+ str += Fay$$_(fayObj.car);+ fayObj = Fay$$_(fayObj.cdr);+ }+ return str;+};+function Fay$$jsToFay_string(x){+ return Fay$$list(x)+};++// Special num/bool serializers.+function Fay$$jsToFay_int(x){return x;}+function Fay$$jsToFay_double(x){return x;}+function Fay$$jsToFay_bool(x){return x;}++function Fay$$fayToJs_int(x){return Fay$$_(x);}+function Fay$$fayToJs_double(x){return Fay$$_(x);}+function Fay$$fayToJs_bool(x){return Fay$$_(x);}++// Unserialize an object from JS to Fay.+function Fay$$jsToFay(type,jsObj){+ var base = type[0];+ var args = type[1];+ var fayObj;+ if(base == "action") {+ // Unserialize a "monadic" JavaScript return value into a monadic value.+ return new Fay$$Monad(Fay$$jsToFay(args[0],jsObj));+ }+ else if(base == "function") {+ // Unserialize a function from JavaScript to a function that Fay can call.+ // So+ //+ // var f = function(x,y,z){ … }+ //+ // becomes something like:+ //+ // function(x){+ // return function(y){+ // return function(z){+ // return new Fay$$$(function(){+ // return Fay$$jsToFay(f(Fay$$fayTojs(x),+ // Fay$$fayTojs(y),+ // Fay$$fayTojs(z))+ // }}}}};+ var returnType = args[args.length-1];+ var funArgs = args.slice(0,-1);++ if (jsObj.length > 0) {+ var makePartial = function(args){+ return function(arg){+ var i = args.length;+ var fayArg = Fay$$fayToJs(funArgs[i],arg);+ var newArgs = args.concat([fayArg]);+ if(newArgs.length == funArgs.length) {+ return new Fay$$$(function(){+ return Fay$$jsToFay(returnType,jsObj.apply(this,newArgs));+ });+ } else {+ return makePartial(newArgs);+ }+ };+ };+ return makePartial([]);+ }+ else+ return function (arg) {+ return Fay$$jsToFay(["automatic"], jsObj(Fay$$fayToJs(["automatic"], arg)));+ };+ }+ else if(base == "string") {+ // Unserialize a JS string into Fay list (String).+ // This is a special case, when String is explicit in the type signature,+ // with `Automatic' a string would not be decoded.+ return Fay$$list(jsObj);+ }+ else if(base == "list") {+ // Unserialize a JS array into a Fay list ([a]).+ var serializedList = [];+ for (var i = 0, len = jsObj.length; i < len; i++) {+ // Unserialize each JS value into a Fay value, too.+ serializedList.push(Fay$$jsToFay(args[0],jsObj[i]));+ }+ // Pop it all in a Fay list.+ return Fay$$list(serializedList);+ }+ else if(base == "tuple") {+ // Unserialize a JS array into a Fay tuple ((a,b,c,...)).+ var serializedTuple = [];+ for (var i = 0, len = jsObj.length; i < len; i++) {+ // Unserialize each JS value into a Fay value, too.+ serializedTuple.push(Fay$$jsToFay(args[i],jsObj[i]));+ }+ // Pop it all in a Fay list.+ return Fay$$list(serializedTuple);+ }+ else if(base == "defined") {+ return jsObj === undefined+ ? new Fay.FFI._Undefined()+ : new Fay.FFI._Defined(Fay$$jsToFay(args[0],jsObj));+ }+ else if(base == "nullable") {+ return jsObj === null+ ? new Fay.FFI._Null()+ : new Fay.FFI.Nullable(Fay$$jsToFay(args[0],jsObj));+ }+ else if(base == "int") {+ // Int are unboxed, so there's no forcing to do.+ // But we can do validation that the int has no decimal places.+ // E.g. Math.round(x)!=x? throw "NOT AN INTEGER, GET OUT!"+ fayObj = Math.round(jsObj);+ if(fayObj!==jsObj) throw "Argument " + jsObj + " is not an integer!";+ return fayObj;+ }+ else if (base == "double" ||+ base == "bool" ||+ base == "ptr") {+ return jsObj;+ }+ else if(base == "unknown")+ return Fay$$jsToFay(["automatic"], jsObj);+ else if(base == "automatic" && jsObj instanceof Function) {+ #{ifTsJs "let type: string[][]" "var type"} = [["automatic"]];+ for (var i = 0; i < jsObj.length; i++)+ type.push(["automatic"]);+ return Fay$$jsToFay(["function", type], jsObj);+ }+ else if(base == "automatic" && jsObj instanceof Array) {+ var list = null;+ for (var i = jsObj.length - 1; i >= 0; i--) {+ list = new Fay$$Cons(Fay$$jsToFay([base], jsObj[i]), list);+ }+ return list;+ }+ else if(base == "automatic" || base == "user") {+ if (jsObj && jsObj['instance']) {+ var jsToFayFun = Fay$$jsToFayHash[jsObj["instance"]];+ return jsToFayFun ? jsToFayFun(type,type[2],jsObj) : jsObj;+ }+ else+ return jsObj;+ }++ throw new Error("Unhandled JS->Fay translation type: " + base);+}++// Stores the mappings from js objects to fay types.+// This will be populated by compiled modules.+var Fay$$jsToFayHash = {};++/*******************************************************************************+ * Lists.+ */++// Cons object.+function Fay$$Cons(car,cdr){+ this.car = car;+ this.cdr = cdr;+}++// Make a list.+function Fay$$list(xs){+ var out = null;+ for(var i=xs.length-1; i>=0;i--)+ out = new Fay$$Cons(xs[i],out);+ return out;+}++// Built-in list cons.+function Fay$$cons(x){+ return function(y){+ return new Fay$$Cons(x,y);+ };+}++// List index.+// `list' is already forced by the time it's passed to this function.+// `list' cannot be null and `index' cannot be out of bounds.+function Fay$$index(index,list){+ for(var i = 0; i < index; i++) {+ list = Fay$$_(list.cdr);+ }+ return list.car;+}++// List length.+// `list' is already forced by the time it's passed to this function.+function Fay$$listLen(list,max){+ for(var i = 0; list !== null && i < max + 1; i++) {+ list = Fay$$_(list.cdr);+ }+ return i == max;+}++/*******************************************************************************+ * Numbers.+ */++// Built-in *.+function Fay$$mult(x){+ return function(y){+ return new Fay$$$(function(){+ return Fay$$_(x) * Fay$$_(y);+ });+ };+}++function Fay$$mult$36$uncurried(x,y){++ return new Fay$$$(function(){+ return Fay$$_(x) * Fay$$_(y);+ });++}++// Built-in +.+function Fay$$add(x){+ return function(y){+ return new Fay$$$(function(){+ return Fay$$_(x) + Fay$$_(y);+ });+ };+}++// Built-in +.+function Fay$$add$36$uncurried(x,y){++ return new Fay$$$(function(){+ return Fay$$_(x) + Fay$$_(y);+ });++}++// Built-in -.+function Fay$$sub(x){+ return function(y){+ return new Fay$$$(function(){+ return Fay$$_(x) - Fay$$_(y);+ });+ };+}+// Built-in -.+function Fay$$sub$36$uncurried(x,y){++ return new Fay$$$(function(){+ return Fay$$_(x) - Fay$$_(y);+ });++}++// Built-in /.+function Fay$$divi(x){+ return function(y){+ return new Fay$$$(function(){+ return Fay$$_(x) / Fay$$_(y);+ });+ };+}++// Built-in /.+function Fay$$divi$36$uncurried(x,y){++ return new Fay$$$(function(){+ return Fay$$_(x) / Fay$$_(y);+ });++}++/*******************************************************************************+ * Booleans.+ */++// Are two values equal?+function Fay$$equal(lit1, lit2) {+ // Simple case+ lit1 = Fay$$_(lit1);+ lit2 = Fay$$_(lit2);+ if (lit1 === lit2) {+ return true;+ }+ // General case+ if (lit1 instanceof Array) {+ if (lit1.length != lit2.length) return false;+ for (var len = lit1.length, i = 0; i < len; i++) {+ if (!Fay$$equal(lit1[i], lit2[i])) return false;+ }+ return true;+ } else if (lit1 instanceof Fay$$Cons && lit2 instanceof Fay$$Cons) {+ do {+ if (!Fay$$equal(lit1.car,lit2.car))+ return false;+ lit1 = Fay$$_(lit1.cdr), lit2 = Fay$$_(lit2.cdr);+ if (lit1 === null || lit2 === null)+ return lit1 === lit2;+ } while (true);+ } else if (typeof lit1 == 'object' && typeof lit2 == 'object' && lit1 && lit2 &&+ lit1.instance === lit2.instance) {+ for(var x in lit1) {+ if(!Fay$$equal(lit1[x],lit2[x]))+ return false;+ }+ return true;+ } else {+ return false;+ }+}++// Built-in ==.+function Fay$$eq(x){+ return function(y){+ return new Fay$$$(function(){+ return Fay$$equal(x,y);+ });+ };+}++function Fay$$eq$36$uncurried(x,y){++ return new Fay$$$(function(){+ return Fay$$equal(x,y);+ });++}++// Built-in /=.+function Fay$$neq(x){+ return function(y){+ return new Fay$$$(function(){+ return !(Fay$$equal(x,y));+ });+ };+}++// Built-in /=.+function Fay$$neq$36$uncurried(x,y){++ return new Fay$$$(function(){+ return !(Fay$$equal(x,y));+ });++}++// Built-in >.+function Fay$$gt(x){+ return function(y){+ return new Fay$$$(function(){+ return Fay$$_(x) > Fay$$_(y);+ });+ };+}++// Built-in >.+function Fay$$gt$36$uncurried(x,y){++ return new Fay$$$(function(){+ return Fay$$_(x) > Fay$$_(y);+ });++}++// Built-in <.+function Fay$$lt(x){+ return function(y){+ return new Fay$$$(function(){+ return Fay$$_(x) < Fay$$_(y);+ });+ };+}+++// Built-in <.+function Fay$$lt$36$uncurried(x,y){++ return new Fay$$$(function(){+ return Fay$$_(x) < Fay$$_(y);+ });++}+++// Built-in >=.+function Fay$$gte(x){+ return function(y){+ return new Fay$$$(function(){+ return Fay$$_(x) >= Fay$$_(y);+ });+ };+}++// Built-in >=.+function Fay$$gte$36$uncurried(x,y){++ return new Fay$$$(function(){+ return Fay$$_(x) >= Fay$$_(y);+ });++}++// Built-in <=.+function Fay$$lte(x){+ return function(y){+ return new Fay$$$(function(){+ return Fay$$_(x) <= Fay$$_(y);+ });+ };+}++// Built-in <=.+function Fay$$lte$36$uncurried(x,y){++ return new Fay$$$(function(){+ return Fay$$_(x) <= Fay$$_(y);+ });++}++// Built-in &&.+function Fay$$and(x){+ return function(y){+ return new Fay$$$(function(){+ return Fay$$_(x) && Fay$$_(y);+ });+ };+}++// Built-in &&.+function Fay$$and$36$uncurried(x,y){++ return new Fay$$$(function(){+ return Fay$$_(x) && Fay$$_(y);+ });+ ;+}++// Built-in ||.+function Fay$$or(x){+ return function(y){+ return new Fay$$$(function(){+ return Fay$$_(x) || Fay$$_(y);+ });+ };+}++// Built-in ||.+function Fay$$or$36$uncurried(x,y){++ return new Fay$$$(function(){+ return Fay$$_(x) || Fay$$_(y);+ });++}++/*******************************************************************************+ * Mutable references.+ */++// Make a new mutable reference.+function Fay$$Ref(x){+ this.value = x;+}++// Write to the ref.+function Fay$$writeRef(ref,x){+ ref.value = x;+}++// Get the value from the ref.+function Fay$$readRef(ref){+ return ref.value;+}++/*******************************************************************************+ * Dates.+ */+function Fay$$date(str){+ return Date.parse(str);+}++/*******************************************************************************+ * Data.Var+ */++function Fay$$Ref2(val){+ this.val = val;+}++function Fay$$Sig(){+ this.handlers = [];+}++function Fay$$Var(val){+ this.val = val;+ this.handlers = [];+}++// Helper used by Fay$$setValue and for merging+function Fay$$broadcastInternal(self, val, force){+ var handlers = self.handlers;+ var exceptions = [];+ for(#{ifTsJs "let" "var"} len = handlers.length, i = 0; i < len; i++) {+ try {+ force(handlers[i][1](val), true);+ } catch (e) {+ exceptions.push(e);+ }+ }+ // Rethrow the encountered exceptions.+ if (exceptions.length > 0) {+ console.error("Encountered " + exceptions.length + " exception(s) while broadcasing a change to ", self);+ for(#{ifTsJs "let len: number" "var len"} = exceptions.length, i = 0; i < len; i++) {+ (function(exception) {+ setTimeout(function() { throw exception; }, 0);+ })(exceptions[i]);+ }+ }+}++function Fay$$setValue(self, val, force){+ if (self instanceof Fay$$Ref2) {+ self.val = val;+ } else if (self instanceof Fay$$Var) {+ self.val = val;+ Fay$$broadcastInternal(self, val, force);+ } else if (self instanceof Fay$$Sig) {+ Fay$$broadcastInternal(self, val, force);+ } else {+ throw "Fay$$setValue given something that's not a Ref2, Var, or Sig"+ }+}++function Fay$$subscribe(self, f){+ var key = {};+ self.handlers.push([key,f]);+ var searchStart = self.handlers.length - 1;+ return function(_){+ for(var i = Math.min(searchStart, self.handlers.length - 1); i >= 0; i--) {+ if(self.handlers[i][0] == key) {+ self.handlers = self.handlers.slice(0,i).concat(self.handlers.slice(i+1));+ return;+ }+ }+ return _; // This variable has to be used, otherwise Closure+ // strips it out and Fay serialization breaks.+ };+}++/*******************************************************************************+ * Application code.+ */++|]
− src/Fay/System/Directory/Extra.hs
@@ -1,21 +0,0 @@--- | Extra directory functions.-module Fay.System.Directory.Extra where--import Control.Monad (forM)-import System.Directory (doesDirectoryExist, getDirectoryContents)-import System.FilePath ((</>))---- | Get all files in a folder and its subdirectories.--- Taken from Real World Haskell--- http://book.realworldhaskell.org/read/io-case-study-a-library-for-searching-the-filesystem.html-getRecursiveContents :: FilePath -> IO [FilePath]-getRecursiveContents topdir = do- names <- getDirectoryContents topdir- let properNames = filter (`notElem` [".", ".."]) names- paths <- forM properNames $ \name -> do- let path = topdir </> name- isDirectory <- doesDirectoryExist path- if isDirectory- then getRecursiveContents path- else return [path]- return (concat paths)
− src/Fay/System/Process/Extra.hs
@@ -1,15 +0,0 @@--- | Extra process functions.--module Fay.System.Process.Extra where--import System.Exit-import System.Process---- | Read from a process returning both std err and out.-readAllFromProcess :: FilePath -> [String] -> String -> IO (Either (String,String) (String,String))-readAllFromProcess program flags input = do- (code,out,err) <- readProcessWithExitCode program flags input- return $ case code of- ExitFailure 127 -> Left ("cannot find executable " ++ program, "")- ExitFailure _ -> Left (err, out)- ExitSuccess -> Right (err, out)
src/Fay/Types.hs view
@@ -1,102 +1,73 @@+{-# LANGUAGE CPP #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE TypeFamilies #-} -- | All Fay types and instances. module Fay.Types- (JsStmt(..)- ,JsExp(..)- ,JsLit(..)- ,JsName(..)- ,CompileError(..)- ,Compile(..)- ,CompileResult- ,CompileModule- ,Printable(..)- ,Fay- ,CompileReader(..)- ,CompileWriter(..)- ,CompileConfig(..)- ,CompileState(..)- ,FundamentalType(..)- ,PrintState(..)- ,Printer(..)- ,SerializeContext(..)- ,ModulePath (unModulePath)- ,mkModulePath- ,mkModulePaths- ,mkModulePathFromQName+ ( JsStmt(..)+ , JsExp(..)+ , JsLit(..)+ , JsName(..)+ , CompileError(..)+ , Compile(..)+ , CompileModule+ , Printable(..)+ , Fay+ , CompileReader(..)+ , CompileResult(..)+ , CompileWriter(..)+ , Config(..)+ , CompileState(..)+ , FundamentalType(..)+ , PrintState(..)+ , defaultPrintState+ , PrintReader(..)+ , defaultPrintReader+ , PrintWriter(..)+ , pwOutputString+ , Printer(..)+ , execPrinter+ , indented+ , askIf+ , newline+ , write+ , mapping+ , SerializeContext(..)+ , ModulePath (unModulePath)+ , mkModulePath+ , mkModulePaths+ , mkModulePathFromQName ) 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 Fay.Compiler.Prelude +import Fay.Compiler.ModuleT+import Fay.Config+import qualified Fay.Exts.NoAnnotation as N+import qualified Fay.Exts.Scoped as S+import Fay.Types.CompileError+import Fay.Types.CompileResult+import Fay.Types.FFI+import Fay.Types.Js+import Fay.Types.ModulePath+import Fay.Types.Printer++#if __GLASGOW_HASKELL__ < 710 import Control.Applicative-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.Map (Map)-import Data.Set (Set)-import Data.String-import Distribution.HaskellSuite.Modules-import Language.Haskell.Exts.Annotated-import Language.Haskell.Names (Symbols)-import SourceMap.Types+#endif+import Control.Monad.Except (ExceptT, MonadError)+import Control.Monad.Identity (Identity)+import Control.Monad.RWS (MonadIO, MonadReader, MonadState, MonadWriter, RWST, lift)+import Data.Map (Map)+import Data.Set (Set)+import Language.Haskell.Names (Symbols)+import Data.Semigroup (Semigroup) -------------------------------------------------------------------------------- -- Compiler types --- | Configuration of the compiler.-data CompileConfig = CompileConfig- { 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.- , configSourceMap :: Bool -- ^ Output a source map file as outfile.map.- , 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.-newtype ModulePath = ModulePath { unModulePath :: [String] }- deriving (Eq, Ord, Show)---- | Construct the complete ModulePath from a ModuleName.-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 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 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 -- TODO Change N.QName to GName? They can never be special so it would simplify.@@ -117,228 +88,56 @@ { writerCons :: [JsStmt] -- ^ Constructors. , writerFayToJs :: [(String,JsExp)] -- ^ Fay to JS dispatchers. , writerJsToFay :: [(String,JsExp)] -- ^ JS to Fay dispatchers.- }- deriving (Show)+ } deriving (Show) -- | Simple concatenating instance.+instance Semigroup CompileWriter where+ (CompileWriter a b c) <> (CompileWriter x y z) =+ CompileWriter (a++x) (b++y) (c++z)++-- | Simple concatenating instance. instance Monoid CompileWriter where mempty = CompileWriter [] [] []- mappend (CompileWriter a b c) (CompileWriter x y z) =- CompileWriter (a++x) (b++y) (c++z)+ mappend = (<>) -- | Configuration and globals for the compiler. data CompileReader = CompileReader- { readerConfig :: CompileConfig -- ^ The compilation configuration.- , readerCompileLit :: S.Literal -> Compile JsExp+ { readerConfig :: Config -- ^ The compilation configuration.+ , readerCompileLit :: S.Sign -> S.Literal -> Compile JsExp , readerCompileDecls :: Bool -> [S.Decl] -> Compile [JsStmt] } -- | Compile monad. newtype Compile a = Compile- { unCompile :: (RWST CompileReader- CompileWriter- CompileState- (ErrorT CompileError (ModuleT (ModuleInfo Compile) IO)))- a -- ^ Uns the compiler- }- deriving (MonadState CompileState- ,MonadError CompileError- ,MonadReader CompileReader- ,MonadWriter CompileWriter- ,MonadIO- ,Monad- ,Functor- ,Applicative- )--type CompileResult a- = Either CompileError- (a, CompileState, CompileWriter)+ { unCompile :: RWST CompileReader CompileWriter CompileState+ (ExceptT CompileError (ModuleT (ModuleInfo Compile) IO))+ a -- ^ Uns the compiler+ } deriving+ ( Applicative+ , Functor+ , Monad+ , MonadError CompileError+ , MonadIO+ , MonadReader CompileReader+ , MonadState CompileState+ , MonadWriter CompileWriter+ ) -type CompileModule a- = ModuleT Symbols- IO- (CompileResult a)+type CompileModule a = ModuleT Symbols IO (Either CompileError (a, CompileState, CompileWriter)) 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 --- | 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.- , psMappings :: [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.-instance Default PrintState where- def = PrintState False 0 0 [] 0 [] False---- | The printer monad.-newtype Printer a = Printer { runPrinter :: State PrintState a }- deriving (Monad,Functor,MonadState PrintState)---- | Print some value.-class Printable a where- printJS :: a -> Printer ()---- | Error type.-data CompileError- = Couldn'tFindImport N.ModuleName [FilePath]- | EmptyDoBlock- | 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- | UnsupportedEnum N.Exp- | 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- -- | The JavaScript FFI interfacing monad. newtype Fay a = Fay (Identity a)- deriving Monad------------------------------------------------------------------------------------- JS AST types---- | Statement type.-data JsStmt- = JsVar JsName JsExp- | JsIf JsExp [JsStmt] [JsStmt]- | JsEarlyReturn JsExp- | JsThrow JsExp- | JsWhile JsExp [JsStmt]- | JsUpdate JsName JsExp- | JsSetProp JsName JsName JsExp- | JsSetQName (Maybe SrcSpan) N.QName JsExp- | JsSetModule ModulePath JsExp- | JsSetConstructor N.QName JsExp- | JsSetPropExtern JsName JsName JsExp- | JsContinue- | JsBlock [JsStmt]- | JsExpStmt JsExp- deriving (Show,Eq)---- | Expression type.-data JsExp- = JsName JsName- | JsRawExp String- | JsSeq [JsExp]- | JsFun (Maybe JsName) [JsName] [JsStmt] (Maybe JsExp)- | JsLit JsLit- | JsApp JsExp [JsExp]- | JsNegApp JsExp- | JsTernaryIf JsExp JsExp JsExp- | JsNull- | JsParen JsExp- | JsGetProp JsExp JsName- | JsLookup JsExp JsExp- | JsUpdateProp JsExp JsName JsExp- | JsGetPropExtern JsExp String- | JsUpdatePropExtern JsExp JsName JsExp- | JsList [JsExp]- | JsNew JsName [JsExp]- | JsThrowExp JsExp- | JsInstanceOf JsExp JsName- | JsIndex Int JsExp- | JsEq JsExp JsExp- | JsNeq JsExp JsExp- | JsInfix String JsExp JsExp -- Used to optimize *, /, +, etc- | JsObj [(String,JsExp)]- | JsLitObj [(N.Name,JsExp)]- | JsUndefined- | JsAnd JsExp JsExp- | JsOr JsExp JsExp- deriving (Show,Eq)---- | A name of some kind.-data JsName- = JsNameVar N.QName- | JsThis- | JsParametrizedType- | JsThunk- | JsForce- | JsApply- | JsParam Integer- | JsTmp Integer- | JsConstructor N.QName- | JsBuiltIn N.Name- | JsModuleName N.ModuleName- deriving (Eq,Show)---- | Literal value type.-data JsLit- = JsChar Char- | JsStr String- | JsInt Int- | JsFloating Double- | JsBool Bool- deriving (Show,Eq)---- | Just handy to have.-instance IsString JsLit where fromString = JsStr---- | These are the data types that are serializable directly to native--- JS data types. Strings, floating points and arrays. The others are:--- actions in the JS monad, which are thunks that shouldn't be forced--- when serialized but wrapped up as JS zero-arg functions, and--- unknown types can't be converted but should at least be forced.-data FundamentalType- -- Recursive types.- = FunctionType [FundamentalType]- | JsType FundamentalType- | ListType FundamentalType- | TupleType [FundamentalType]- | UserDefined N.Name [FundamentalType]- | Defined FundamentalType- | Nullable FundamentalType- -- Simple types.- | DateType- | StringType- | DoubleType- | IntType- | BoolType- | PtrType- -- Automatically serialize this type.- | Automatic- -- Unknown.- | UnknownType- deriving (Show)---- | The serialization context indicates whether we're currently--- serializing some value or a particular field in a user-defined data--- type.-data SerializeContext = SerializeAnywhere | SerializeUserArg Int- deriving (Read,Show,Eq)+ deriving+ ( Applicative+ , Functor+ , Monad+ )
+ src/Fay/Types/CompileError.hs view
@@ -0,0 +1,40 @@+module Fay.Types.CompileError (CompileError (..)) where++import qualified Fay.Exts as F+import qualified Fay.Exts.NoAnnotation as N+import qualified Fay.Exts.Scoped as S++import Language.Haskell.Exts++-- | Error type.+data CompileError+ = Couldn'tFindImport N.ModuleName [FilePath]+ | EmptyDoBlock+ | FfiFormatBadChars SrcSpanInfo String+ | FfiFormatIncompleteArg SrcSpanInfo+ | FfiFormatInvalidJavaScript SrcSpanInfo String String+ | FfiFormatNoSuchArg SrcSpanInfo Int+ | FfiNeedsTypeSig S.Exp+ | GHCError String+ | InvalidDoBlock+ | ParseError S.SrcLoc String+ | ShouldBeDesugared String+ | UnableResolveQualified N.QName+ | UnsupportedDeclaration S.Decl+ | UnsupportedEnum N.Exp+ | 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)+{-# ANN module "HLint: ignore Use camelCase" #-}
+ src/Fay/Types/CompileResult.hs view
@@ -0,0 +1,9 @@+module Fay.Types.CompileResult (CompileResult (..)) where++import SourceMap.Types++data CompileResult = CompileResult+ { resOutput :: String+ , resImported :: [(String, FilePath)]+ , resSourceMappings :: Maybe [Mapping]+ } deriving Show
+ src/Fay/Types/FFI.hs view
@@ -0,0 +1,40 @@++module Fay.Types.FFI+ ( FundamentalType (..)+ , SerializeContext (..)+ ) where++import qualified Fay.Exts.NoAnnotation as N++-- | These are the data types that are serializable directly to native+-- JS data types. Strings, floating points and arrays. The others are:+-- actions in the JS monad, which are thunks that shouldn't be forced+-- when serialized but wrapped up as JS zero-arg functions, and+-- unknown types can't be converted but should at least be forced.+data FundamentalType+ -- Recursive types.+ = FunctionType [FundamentalType]+ | JsType FundamentalType+ | ListType FundamentalType+ | TupleType [FundamentalType]+ | UserDefined N.Name [FundamentalType]+ | Defined FundamentalType+ | Nullable FundamentalType+ -- Simple types.+ | DateType+ | StringType+ | DoubleType+ | IntType+ | BoolType+ | PtrType+ -- Automatically serialize this type.+ | Automatic+ -- Unknown.+ | UnknownType+ deriving (Show)++-- | The serialization context indicates whether we're currently+-- serializing some value or a particular field in a user-defined data+-- type.+data SerializeContext = SerializeAnywhere | SerializeUserArg Int+ deriving (Eq, Read, Show)
+ src/Fay/Types/Js.hs view
@@ -0,0 +1,92 @@+-- | JS AST types++module Fay.Types.Js+ ( JsStmt (..)+ , JsExp (..)+ , JsLit (..)+ , JsName (..)+ ) where++import qualified Fay.Exts.NoAnnotation as N+import Fay.Types.ModulePath++import Data.String+import Language.Haskell.Exts++-- | Statement type.+data JsStmt+ = JsVar JsName JsExp+ | JsMapVar JsName JsExp+ | JsIf JsExp [JsStmt] [JsStmt]+ | JsEarlyReturn JsExp+ | JsThrow JsExp+ | JsWhile JsExp [JsStmt]+ | JsUpdate JsName JsExp+ | JsSetProp JsName JsName JsExp+ | JsSetQName (Maybe SrcSpan) N.QName JsExp+ | JsSetModule ModulePath JsExp+ | JsSetConstructor N.QName JsExp+ | JsSetPropExtern JsName JsName JsExp+ | JsContinue+ | JsBlock [JsStmt]+ | JsExpStmt JsExp+ deriving (Show,Eq)++-- | Expression type.+data JsExp+ = JsName JsName+ | JsRawExp String+ | JsSeq [JsExp]+ | JsFun (Maybe JsName) [JsName] [JsStmt] (Maybe JsExp)+ | JsLit JsLit+ | JsApp JsExp [JsExp]+ | JsNegApp JsExp+ | JsTernaryIf JsExp JsExp JsExp+ | JsNull+ | JsParen JsExp+ | JsGetProp JsExp JsName+ | JsLookup JsExp JsExp+ | JsUpdateProp JsExp JsName JsExp+ | JsGetPropExtern JsExp String+ | JsUpdatePropExtern JsExp JsName JsExp+ | JsList [JsExp]+ | JsNew JsName [JsExp]+ | JsThrowExp JsExp+ | JsInstanceOf JsExp JsName+ | JsIndex Int JsExp+ | JsEq JsExp JsExp+ | JsNeq JsExp JsExp+ | JsInfix String JsExp JsExp -- Used to optimize *, /, +, etc+ | JsObj [(String,JsExp)]+ | JsLitObj [(N.Name,JsExp)]+ | JsUndefined+ | JsAnd JsExp JsExp+ | JsOr JsExp JsExp+ deriving (Eq, Show)++-- | A name of some kind.+data JsName+ = JsNameVar N.QName+ | JsThis+ | JsParametrizedType+ | JsThunk+ | JsForce+ | JsApply+ | JsParam Integer+ | JsTmp Integer+ | JsConstructor N.QName+ | JsBuiltIn N.Name+ | JsModuleName N.ModuleName+ deriving (Eq, Show)++-- | Literal value type.+data JsLit+ = JsChar Char+ | JsStr String+ | JsInt Int+ | JsFloating Double+ | JsBool Bool+ deriving (Eq, Show)++-- | Just handy to have.+instance IsString JsLit where fromString = JsStr
+ src/Fay/Types/ModulePath.hs view
@@ -0,0 +1,32 @@+module Fay.Types.ModulePath+ ( ModulePath (..)+ , mkModulePath+ , mkModulePaths+ , mkModulePathFromQName+ ) where++import Fay.Compiler.QName+import qualified Fay.Exts as F++import Data.List+import Data.List.Split+import Language.Haskell.Exts++-- | The name of a module split into a list for code generation.+newtype ModulePath = ModulePath { unModulePath :: [String] }+ deriving (Eq, Ord, Show)++-- | Construct the complete ModulePath from a ModuleName.+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 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 a -> ModulePath+mkModulePathFromQName (Qual _ (ModuleName _ m) n) = mkModulePath $ ModuleName F.noI $ m ++ "." ++ unname n+mkModulePathFromQName _ = error "mkModulePathFromQName: Not a qualified name"
+ src/Fay/Types/Printer.hs view
@@ -0,0 +1,140 @@+{-# LANGUAGE FlexibleContexts #-}+module Fay.Types.Printer+ ( PrintReader(..)+ , defaultPrintReader+ , PrintWriter(..)+ , pwOutputString+ , PrintState(..)+ , defaultPrintState+ , Printer(..)+ , Printable(..)+ , execPrinter+ , indented+ , newline+ , write+ , askIf+ , mapping+ ) where++import Fay.Compiler.Prelude++import Control.Monad.RWS (RWS, asks, execRWS, get, modify, put, tell)+import Data.List (elemIndex)+import Data.Maybe (fromMaybe)+import Data.String+import Language.Haskell.Exts+import SourceMap.Types+import qualified Data.Semigroup as SG++-- | Global options of the printer+data PrintReader = PrintReader+ { prPretty :: Bool -- ^ Are we to pretty print?+ , prPrettyThunks :: Bool -- ^ Use pretty thunk names?+ , prPrettyOperators :: Bool -- ^ Use pretty operators?+ }++-- | default printer options (non-pretty printing)+defaultPrintReader :: PrintReader+defaultPrintReader = PrintReader False False False++-- | Output of printer+data PrintWriter = PrintWriter+ { pwMappings :: [Mapping] -- ^ Source mappings.+ , pwOutput :: ShowS -- ^ The current output.+ }++pwOutputString :: PrintWriter -> String+pwOutputString (PrintWriter _ out) = out ""++instance SG.Semigroup PrintWriter where+ (PrintWriter a b) <> (PrintWriter x y) = PrintWriter (a ++ x) (b . y)++-- | Output concatenation+instance Monoid PrintWriter where+ mempty = PrintWriter [] id+ mappend = (<>)++-- | The state of the pretty printer.+data PrintState = PrintState+ { psLine :: Int -- ^ The current line.+ , psColumn :: Int -- ^ Current column.+ , psIndentLevel :: Int -- ^ Current indentation level.+ , psNewline :: Bool -- ^ Just outputted a newline?+ }++-- | Default state.+defaultPrintState :: PrintState+defaultPrintState = PrintState 0 0 0 False++-- | The printer.+newtype Printer = Printer+ { runPrinter :: RWS PrintReader PrintWriter PrintState () }++execPrinter :: Printer -> PrintReader -> PrintWriter+execPrinter (Printer p) r = snd $ execRWS p r defaultPrintState++instance SG.Semigroup Printer where+ (Printer p) <> (Printer q) = Printer (p >> q)++instance Monoid Printer where+ mempty = Printer $ return ()+ mappend = (<>)++-- | Print some value.+class Printable a where+ printJS :: a -> Printer++-- | Print the given printer indented.+indented :: Printer -> Printer+indented (Printer p) = Printer $ asks prPretty >>= \pretty ->+ when pretty (addToIndentLevel 1) >> p >> when pretty (addToIndentLevel (-1))+ where addToIndentLevel d = modify (\ps -> ps { psIndentLevel = psIndentLevel ps + d })++-- | Output a newline and makes next line indented when prPretty is True.+-- Does nothing when prPretty is False+newline :: Printer+newline = Printer $ asks prPretty >>= flip when writeNewline+ where writeNewline = writeRWS "\n" >> modify (\s -> s { psNewline = True })++-- | Write out a raw string, respecting the indentation+-- Note: if you pass a string with newline characters, it will print them+-- out even if prPretty is set to False. Also next line won't be indented.+-- If you want write a smart newline (that is the one which will be written+-- out only if prPretty is true, and after which the line will be indented)+-- use `newline`)+write :: String -> Printer+write = Printer . writeRWS++writeRWS :: String -> RWS PrintReader PrintWriter PrintState ()+writeRWS x = do+ ps <- get+ let out = if psNewline ps+ then replicate (2 * psIndentLevel ps) ' ' ++ x+ else x+ tell mempty { pwOutput = (out++) }++ let newLines = length (filter (== '\n') x)+ put ps { psLine = psLine ps + newLines+ , psColumn = fromMaybe (psColumn ps + length x) . elemIndex '\n' $ reverse x+ , psNewline = False+ }++-- | Write out a string, updating the current position information.+instance IsString Printer where+ fromString = write++-- | exec one of Printers depending on PrintReader property.+askIf :: (PrintReader -> Bool) -> Printer -> Printer -> Printer+askIf f (Printer p) (Printer q) = Printer $ asks f >>= (\b -> if b then p else q)++-- | Generate a mapping from the Haskell location to the current point in the output.+mapping :: SrcSpan -> Printer+mapping srcSpan = Printer $ get >>= \ps ->+ let m = Mapping { mapGenerated = Pos (fromIntegral (psLine ps))+ (fromIntegral (psColumn ps))+ , mapOriginal = Just (Pos (fromIntegral (srcSpanStartLine srcSpan))+ (fromIntegral (srcSpanStartColumn srcSpan) - 1))+ , mapSourceFile = Just (srcSpanFilename srcSpan)+ , mapName = Nothing+ }+ in tell $ mempty { pwMappings = [m] }
+ src/haskell-names/LICENSE view
@@ -0,0 +1,3 @@+Forked from haskell-names+Licensed as BSD3+Authors: Roman Cheplyaka, Lennart Augustsson
+ src/haskell-names/Language/Haskell/Names.hs view
@@ -0,0 +1,17 @@+module Language.Haskell.Names+ (+ -- * Core functions+ annotateModule+ , getInterfaces+ -- * Types+ , SymValueInfo(..)+ , Symbols(..)+ , Scoped(..)+ , NameInfo(..)+ , GName(..)+ , OrigName(..)+ , HasOrigName(..)+ ) where++import Language.Haskell.Names.Types (Symbols (..), SymValueInfo (..), Scoped (..), NameInfo (..), GName (..), OrigName (..), HasOrigName (..))+import Language.Haskell.Names.Recursive (getInterfaces, annotateModule)
+ src/haskell-names/Language/Haskell/Names/Annotated.hs view
@@ -0,0 +1,100 @@+-- This module uses the open recursion interface+-- ("Language.Haskell.Names.Open") to annotate the AST with binding+-- information.+{-# OPTIONS -fno-warn-name-shadowing #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE ImplicitParams #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE PatternGuards #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-}+module Language.Haskell.Names.Annotated+ ( Scoped (..)+ , NameInfo (..)+ , annotate+ ) where++import Fay.Compiler.Prelude+import qualified Language.Haskell.Names.GlobalSymbolTable as Global+import qualified Language.Haskell.Names.LocalSymbolTable as Local+import Language.Haskell.Names.Open.Base+import Language.Haskell.Names.Open.Instances ()+import Language.Haskell.Names.RecordWildcards+import Language.Haskell.Names.Types++import Data.Lens.Light+import Data.Proxy+import Language.Haskell.Exts+import Data.Typeable ( eqT, (:~:)(Refl) )++annotate+ :: forall a l .+ (Resolvable (a (Scoped l)), Functor a, Typeable l)+ => Scope -> a l -> a (Scoped l)+annotate sc = annotateRec (Proxy :: Proxy l) sc . fmap (Scoped None)++annotateRec+ :: forall a l .+ (Typeable l, Resolvable a)+ => Proxy l -> Scope -> a -> a+annotateRec _ sc a = go sc a where+ go :: forall a . Resolvable a => Scope -> a -> a+ go sc a+ | ReferenceV <- getL nameCtx sc+ , Just (Refl :: QName (Scoped l) :~: a) <- eqT+ = lookupValue (fmap sLoc a) sc <$ a+ | ReferenceT <- getL nameCtx sc+ , Just (Refl :: QName (Scoped l) :~: a) <- eqT+ = lookupType (fmap sLoc a) sc <$ a+ | BindingV <- getL nameCtx sc+ , Just (Refl :: Name (Scoped l) :~: a) <- eqT+ = Scoped ValueBinder (sLoc . ann $ a) <$ a+ | BindingT <- getL nameCtx sc+ , Just (Refl :: Name (Scoped l) :~: a) <- eqT+ = Scoped TypeBinder (sLoc . ann $ a) <$ a+ | Just (Refl :: FieldUpdate (Scoped l) :~: a) <- eqT+ = case a of+ FieldPun l n -> FieldPun l (lookupValue (sLoc <$> n) sc <$ n)+ FieldWildcard l ->+ let+ namesUnres = sc ^. wcNames+ resolve n =+ let Scoped info _ = lookupValue (sLoc l <$ UnQual () n) sc+ in info+ namesRes =+ map+ (\f -> (wcFieldOrigName f, resolve $ wcFieldName f))+ namesUnres+ in FieldWildcard $ Scoped (RecExpWildcard namesRes) (sLoc l)+ _ -> rmap go sc a+ | Just (Refl :: PatField (Scoped l) :~: a) <- eqT+ , PFieldWildcard l <- a+ = PFieldWildcard $+ Scoped+ (RecPatWildcard $ map wcFieldOrigName $ sc ^. wcNames)+ (sLoc l)+ | otherwise+ = rmap go sc a++lookupValue :: QName l -> Scope -> Scoped l+lookupValue qn sc = Scoped nameInfo (ann qn)+ where+ nameInfo =+ case Local.lookupValue qn $ getL lTable sc of+ Right r -> LocalValue r+ _ ->+ case Global.lookupValue qn $ getL gTable sc of+ Global.Result r -> GlobalValue r+ Global.Error e -> ScopeError e+ Global.Special -> None++lookupType :: QName l -> Scope -> Scoped l+lookupType qn sc = Scoped nameInfo (ann qn)+ where+ nameInfo =+ case Global.lookupType qn $ getL gTable sc of+ Global.Result r -> GlobalType r+ Global.Error e -> ScopeError e+ Global.Special -> None
+ src/haskell-names/Language/Haskell/Names/Exports.hs view
@@ -0,0 +1,144 @@+{-# OPTIONS -fno-warn-name-shadowing #-}+{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE NoMonoLocalBinds #-}+{-# LANGUAGE TypeFamilies #-}+module Language.Haskell.Names.Exports+ ( processExports+ ) where++import Fay.Compiler.Prelude++import Fay.Compiler.ModuleT+import Language.Haskell.Names.GlobalSymbolTable as Global+import Language.Haskell.Names.ModuleSymbols+import Language.Haskell.Names.ScopeUtils+import Language.Haskell.Names.SyntaxUtils+import Language.Haskell.Names.Types (Error (..), GName (..), ModuleNameS, NameInfo (..),+ Scoped (..), Symbols (..), mkTy, mkVal, st_origName)++import Control.Monad.Writer (WriterT (WriterT), runWriterT)+import qualified Data.Map as Map+import qualified Data.Set as Set+import Language.Haskell.Exts++processExports+ :: (MonadModule m, ModuleInfo m ~ Symbols, Data l, Eq l)+ => Global.Table+ -> Module l+ -> m (Maybe (ExportSpecList (Scoped l)), Symbols)+processExports tbl m =+ case getExportSpecList m of+ Nothing ->+ return (Nothing, moduleSymbols tbl m)+ Just exp ->+ liftM (first Just) $ resolveExportSpecList tbl exp++resolveExportSpecList+ :: (MonadModule m, ModuleInfo m ~ Symbols)+ => Global.Table+ -> ExportSpecList l+ -> m (ExportSpecList (Scoped l), Symbols)+resolveExportSpecList tbl (ExportSpecList l specs) =+ liftM (first $ ExportSpecList $ none l) $+ runWriterT $+ mapM (WriterT . resolveExportSpec tbl) specs++resolveExportSpec+ :: (MonadModule m, ModuleInfo m ~ Symbols)+ => Global.Table+ -> ExportSpec l+ -> m (ExportSpec (Scoped l), Symbols)+resolveExportSpec tbl exp =+ case exp of+ EVar l qn -> return $+ case Global.lookupValue qn tbl of+ Global.Error err ->+ (scopeError err exp, mempty)+ Global.Result i ->+ let s = mkVal i+ in+ (EVar (Scoped (Export s) l)+ (Scoped (GlobalValue i) <$> qn), s)+ Global.Special {} -> error "Global.Special in export list?"+ EAbs l ns qn -> return $+ case Global.lookupType qn tbl of+ Global.Error err ->+ (scopeError err exp, mempty)+ Global.Result i ->+ let s = mkTy i+ in+ (EAbs (Scoped (Export s) l) (noScope ns)+ (Scoped (GlobalType i) <$> qn), s)+ Global.Special {} -> error "Global.Special in export list?"+ EThingWith l (EWildcard wcl wcn) qn [] -> return $+ case Global.lookupType qn tbl of+ Global.Error err ->+ (scopeError err exp, mempty)+ Global.Result i ->+ let+ subs = mconcat+ [ mkVal info+ | info <- allValueInfos+ , Just n' <- return $ sv_parent info+ , n' == st_origName i ]+ s = mkTy i <> subs+ in+ ( EThingWith (Scoped (Export s) l)+ (EWildcard (Scoped (Export s) wcl) wcn)+ (Scoped (GlobalType i) <$> qn)+ []+ , s+ )+ Global.Special {} -> error "Global.Special in export list?"+ EThingWith _ (EWildcard _ _) _qn _cns -> error "Name resolution: CNames are not supported in wildcard exports"+ EThingWith l (NoWildcard wcl) qn cns -> return $+ case Global.lookupType qn tbl of+ Global.Error err ->+ (scopeError err exp, mempty)+ Global.Result i ->+ let+ (cns', subs) =+ resolveCNames+ (Global.toSymbols tbl)+ (st_origName i)+ (\cn -> ENotInScope (UnQual (ann cn) (unCName cn))) -- FIXME better error+ cns+ s = mkTy i <> subs+ in+ ( EThingWith (Scoped (Export s) l)+ (NoWildcard (Scoped (Export s) wcl))+ (Scoped (GlobalType i) <$> qn)+ cns'+ , s+ )+ Global.Special {} -> error "Global.Special in export list?"+ EModuleContents _ (ModuleName _ mod) ->+ -- FIXME ambiguity check+ let+ filterByPrefix+ :: Ord i+ => ModuleNameS+ -> Map.Map GName (Set.Set i)+ -> Set.Set i+ filterByPrefix prefix m =+ Set.unions+ [ i | (GName { gModule = prefix' }, i) <- Map.toList m, prefix' == prefix ]++ filterEntities+ :: Ord i+ => Map.Map GName (Set.Set i)+ -> Set.Set i+ filterEntities ents =+ Set.intersection+ (filterByPrefix mod ents)+ (filterByPrefix "" ents)++ eVals = filterEntities $ Global.values tbl+ eTyps = filterEntities $ Global.types tbl++ s = Symbols eVals eTyps+ in+ return (Scoped (Export s) <$> exp, s)+ where+ allValueInfos =+ Set.toList $ Map.foldl' Set.union Set.empty $ Global.values tbl
+ src/haskell-names/Language/Haskell/Names/GetBound.hs view
@@ -0,0 +1,157 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE UndecidableInstances #-}+module Language.Haskell.Names.GetBound+ ( GetBound(..)+ ) where++import Fay.Compiler.Prelude+import qualified Language.Haskell.Names.GlobalSymbolTable as Global+import Language.Haskell.Names.RecordWildcards+import Language.Haskell.Names.SyntaxUtils++import Data.Generics.Uniplate.Data+import Language.Haskell.Exts+++-- | Get bound value identifiers.+class GetBound a l | a -> l where+ -- | For record wildcards we need to know which fields the given+ -- constructor has. So we pass the global table for that.+ getBound :: Global.Table -> a -> [Name l]++-- XXX account for shadowing?+instance (GetBound a l) => GetBound [a] l where+ getBound ctx xs = concatMap (getBound ctx) xs++instance (GetBound a l) => GetBound (Maybe a) l where+ getBound ctx = maybe [] (getBound ctx)++instance (GetBound a l, GetBound b l) => GetBound (a, b) l where+ getBound ctx (a, b) = getBound ctx a ++ getBound ctx b++instance (Data l) => GetBound (Binds l) l where+ getBound ctx e = case e of+ BDecls _ ds -> getBound ctx ds+ IPBinds _ _ -> [] -- XXX doesn't bind regular identifiers++instance (Data l) => GetBound (Decl l) l where+ getBound ctx e = case e of+ TypeDecl{} -> []+ TypeFamDecl{} -> []+ DataDecl _ _ _ _ ds _ -> getBound ctx ds+ GDataDecl _ _ _ _ _ ds _ -> getBound ctx ds+ DataFamDecl{} -> []+ TypeInsDecl{} -> []+ DataInsDecl _ _ _ ds _ -> getBound ctx ds+ GDataInsDecl _ _ _ _ ds _ -> getBound ctx ds+ ClassDecl _ _ _ _ mds -> getBound ctx mds+ InstDecl{} -> []+ DerivDecl{} -> []+ InfixDecl{} -> []+ DefaultDecl{} -> []+ SpliceDecl{} -> []+ TypeSig{} -> []+ FunBind _ [] -> error "getBound: FunBind []"+ FunBind _ (Match _ n _ _ _ : _) -> [n]+ FunBind _ (InfixMatch _ _ n _ _ _ : _) -> [n]+ PatBind _ p _ _ -> getBound ctx p+ ForImp _ _ _ _ n _ -> [n]+ ForExp _ _ _ n _ -> [n]+ RulePragmaDecl{} -> []+ DeprPragmaDecl{} -> []+ WarnPragmaDecl{} -> []+ InlineSig{} -> []+ SpecSig{} -> []+ SpecInlineSig{} -> []+ InstSig{} -> []+ AnnPragma{} -> []+ InlineConlikeSig{} -> []+ ClosedTypeFamDecl{} -> []+ MinimalPragma{} -> []+ _ -> error "Unsupported syntax"++instance (Data l) => GetBound (QualConDecl l) l where+ getBound ctx (QualConDecl _ _ _ d) = getBound ctx d++instance (Data l) => GetBound (GadtDecl l) l where+ getBound _ctx (GadtDecl _l conName _tyvarBinds _context mbFieldDecls _ty) =+ -- GADT constructor name+ [conName] +++ -- GADT selector names+ [ fieldName+ | Just fieldDecls <- return mbFieldDecls+ , FieldDecl _l' fieldNames _fieldTy <- fieldDecls+ , fieldName <- fieldNames+ ]++instance (Data l) => GetBound (ConDecl l) l where+ getBound ctx e = case e of+ ConDecl _ n _ -> [n]+ InfixConDecl _ _ n _ -> [n]+ RecDecl _ n fs -> n : getBound ctx fs++instance (Data l) => GetBound (FieldDecl l) l where+ getBound _ctx (FieldDecl _ ns _) = ns++instance (Data l) => GetBound (ClassDecl l) l where+ getBound _ctx e = case e of+ ClsDecl _ d -> getBoundSign d+ ClsDataFam{} -> []+ ClsTyFam{} -> []+ ClsTyDef{} -> []+ ClsDefSig{} -> []++instance (Data l) => GetBound (Match l) l where+ getBound _ctx e = case e of+ Match _ n _ _ _ -> [n]+ InfixMatch _ _ n _ _ _ -> [n]++instance (Data l) => GetBound (Stmt l) l where+ getBound ctx e =+ case e of+ Generator _ pat _ -> getBound ctx pat+ LetStmt _ bnds -> getBound ctx bnds+ RecStmt _ stmts -> getBound ctx stmts+ Qualifier {} -> []++instance (Data l) => GetBound (QualStmt l) l where+ getBound ctx e =+ case e of+ QualStmt _ stmt -> getBound ctx stmt+ _ -> []++instance (Data l) => GetBound (Pat l) l where+ getBound gt p =+ [ n | p' <- universe $ transform dropExp p, n <- varp p' ]++ where++ varp (PVar _ n) = [n]+ varp (PAsPat _ n _) = [n]+ varp (PNPlusK _ n _) = [n]+ varp (PRec _ con fs) =+ [ n+ | -- (lazily) compute elided fields for the case when 'f' below is a wildcard+ let elidedFields = map wcFieldName $ patWcNames gt con fs+ , f <- fs+ , n <- getRecVars elidedFields f+ ]+ varp _ = []++ -- must remove nested Exp so universe doesn't descend into them+ dropExp (PViewPat _ _ x) = x+ dropExp x = x++ getRecVars :: [Name ()] -> PatField l -> [Name l]+ getRecVars _ PFieldPat {} = [] -- this is already found by the generic algorithm+ getRecVars _ (PFieldPun _ qn) = [qNameToName qn]+ getRecVars elidedFields (PFieldWildcard l) = map (l <$) elidedFields++getBoundSign :: Decl l -> [Name l]+getBoundSign (TypeSig _ ns _) = ns+getBoundSign _ = []
+ src/haskell-names/Language/Haskell/Names/GlobalSymbolTable.hs view
@@ -0,0 +1,114 @@+{-# OPTIONS -fno-warn-name-shadowing #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE NoImplicitPrelude #-}+-- | This module is designed to be imported qualified.+module Language.Haskell.Names.GlobalSymbolTable+ ( Table+ , empty+ , Result(..)+ , lookupValue+ , lookupType+ , fromLists+ , types+ , values+ , toSymbols+ ) where++import Fay.Compiler.Prelude hiding (empty)+import Language.Haskell.Names.SyntaxUtils+import Language.Haskell.Names.Types++import Data.Lens.Light+import qualified Data.Map as Map+import qualified Data.Set as Set+import Language.Haskell.Exts as HSE+import Data.Semigroup (Semigroup)++-- | Global symbol table — contains global names+data Table =+ Table+ (Map.Map GName (Set.Set (SymValueInfo OrigName)))+ (Map.Map GName (Set.Set (SymTypeInfo OrigName)))+ deriving (Eq, Ord, Show, Data, Typeable)++valLens :: Lens Table (Map.Map GName (Set.Set (SymValueInfo OrigName)))+valLens = lens (\(Table vs _) -> vs) (\vs (Table _ ts) -> Table vs ts)++tyLens :: Lens Table (Map.Map GName (Set.Set (SymTypeInfo OrigName)))+tyLens = lens (\(Table _ ts) -> ts) (\ts (Table vs _) -> Table vs ts)++instance Semigroup Table where+ (Table vs1 ts1) <> (Table vs2 ts2) =+ Table (j vs1 vs2) (j ts1 ts2)+ where+ j :: (Ord i, Ord k)+ => Map.Map k (Set.Set i)+ -> Map.Map k (Set.Set i)+ -> Map.Map k (Set.Set i)+ j = Map.unionWith Set.union+instance Monoid Table where+ mempty = empty+ mappend = (<>)++toGName :: QName l -> GName+toGName (UnQual _ n) = GName "" (nameToString n)+toGName (Qual _ (ModuleName _ m) n) = GName m (nameToString n)+toGName (HSE.Special _ _) = error "toGName: Special"++empty :: Table+empty = Table Map.empty Map.empty++lookupL+ :: HasOrigName i+ => Lens Table (Map.Map GName (Set.Set (i OrigName)))+ -> QName l+ -> Table+ -> Result l (i OrigName)+lookupL _ (HSE.Special {}) _ =+ Language.Haskell.Names.GlobalSymbolTable.Special+lookupL lens qn tbl =+ case Set.toList <$> (Map.lookup (toGName qn) $ getL lens tbl) of+ Nothing -> Error $ ENotInScope qn+ Just [] -> Error $ ENotInScope qn+ Just [i] -> Result i+ Just is -> Error $ EAmbiguous qn (map origName is)++data Result l a+ = Result a+ | Error (Error l)+ | Special++lookupValue :: QName l -> Table -> Result l (SymValueInfo OrigName)+lookupValue = lookupL valLens++lookupType :: QName l -> Table -> Result l (SymTypeInfo OrigName)+lookupType = lookupL tyLens++fromMaps+ :: Map.Map GName (Set.Set (SymValueInfo OrigName))+ -> Map.Map GName (Set.Set (SymTypeInfo OrigName))+ -> Table+fromMaps = Table++fromLists+ :: ([(GName, SymValueInfo OrigName)],+ [(GName, SymTypeInfo OrigName)])+ -> Table+fromLists (vs, ts) =+ fromMaps+ (Map.fromListWith Set.union $ map (second Set.singleton) vs)+ (Map.fromListWith Set.union $ map (second Set.singleton) ts)++values :: Table -> Map.Map GName (Set.Set (SymValueInfo OrigName))+types :: Table -> Map.Map GName (Set.Set (SymTypeInfo OrigName))+values = getL valLens+types = getL tyLens++toSymbols :: Table -> Symbols+toSymbols tbl =+ Symbols+ (gather $ values tbl)+ (gather $ types tbl)+ where+ gather :: Ord a => Map.Map k (Set.Set a) -> Set.Set a+ gather = Map.foldl' Set.union Set.empty
+ src/haskell-names/Language/Haskell/Names/GlobalSymbolTable.hs-boot view
@@ -0,0 +1,10 @@+module Language.Haskell.Names.GlobalSymbolTable where++import Data.Data++data Table deriving Typeable+instance Eq Table+instance Ord Table+instance Show Table+instance Data Table+
+ src/haskell-names/Language/Haskell/Names/Imports.hs view
@@ -0,0 +1,274 @@+{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE PatternGuards #-}+{-# LANGUAGE TypeFamilies #-}+{-# OPTIONS -fno-warn-name-shadowing #-}+{-# OPTIONS -fno-warn-orphans #-} -- ModName (ModuleName l)+module Language.Haskell.Names.Imports (processImports) where++import Fay.Compiler.Prelude++import Fay.Compiler.ModuleT+import qualified Language.Haskell.Names.GlobalSymbolTable as Global+import Language.Haskell.Names.ScopeUtils+import Language.Haskell.Names.SyntaxUtils+import Language.Haskell.Names.Types++import Control.Monad.Writer (WriterT (WriterT), runWriterT)+import Data.Foldable (fold)+import Data.Lens.Light+import qualified Data.Map as Map+import qualified Data.Set as Set+import Language.Haskell.Exts++instance ModName (ModuleName l) where+ modToString (ModuleName _ s) = s++preludeName :: String+preludeName = "Prelude"++processImports+ :: (MonadModule m, ModuleInfo m ~ Symbols)+ => ExtensionSet+ -> [ImportDecl l]+ -> m ([ImportDecl (Scoped l)], Global.Table)+processImports exts importDecls = do++ (annotated, tbl) <- runWriterT $ mapM (WriterT . processImport) importDecls++ let+ isPreludeImported = not . null $+ [ () | ImportDecl { importModule = ModuleName _ modName } <- importDecls+ , modName == preludeName ]++ importPrelude =+ ImplicitPrelude `Set.member` exts &&+ not isPreludeImported++ tbl' <-+ if not importPrelude+ then return tbl+ else do+ -- FIXME currently we don't have a way to signal an error when+ -- Prelude cannot be found+ syms <- fold `liftM` getModuleInfo preludeName+ return $ tbl <>+ computeSymbolTable+ False -- not qualified+ (ModuleName () preludeName)+ syms++ return (annotated, tbl')++processImport+ :: (MonadModule m, ModuleInfo m ~ Symbols)+ => ImportDecl l+ -> m (ImportDecl (Scoped l), Global.Table)+processImport imp = do+ mbi <- getModuleInfo (importModule imp)+ case mbi of+ Nothing ->+ let e = EModNotFound (importModule imp)+ in return (scopeError e imp, Global.empty)+ Just syms -> return $ resolveImportDecl syms imp++resolveImportDecl+ :: Symbols+ -> ImportDecl l+ -> (ImportDecl (Scoped l), Global.Table)+resolveImportDecl syms (ImportDecl l mod qual src impSafe pkg mbAs mbSpecList) =+ let+ (mbSpecList', impSyms) =+ (fmap fst &&& maybe syms snd) $+ resolveImportSpecList mod syms <$> mbSpecList+ tbl = computeSymbolTable qual (fromMaybe mod mbAs) impSyms+ info =+ case mbSpecList' of+ Just sl | Scoped (ScopeError e) _ <- ann sl ->+ ScopeError e+ _ -> Import tbl+ in+ (ImportDecl+ (Scoped info l)+ (Scoped (ImportPart syms) <$> mod)+ qual+ src+ impSafe+ pkg+ (fmap noScope mbAs)+ mbSpecList'+ , tbl)++resolveImportSpecList+ :: ModuleName l+ -> Symbols+ -> ImportSpecList l+ -> (ImportSpecList (Scoped l), Symbols)+resolveImportSpecList mod allSyms (ImportSpecList l isHiding specs) =+ let specs' = map (resolveImportSpec mod isHiding allSyms) specs+ mentionedSyms = mconcat $ rights $ map ann2syms specs'+ importedSyms = computeImportedSymbols isHiding allSyms mentionedSyms+ newAnn = Scoped (ImportPart importedSyms) l+ in+ (ImportSpecList newAnn isHiding specs', importedSyms)++-- | This function takes care of the possible 'hiding' clause+computeImportedSymbols+ :: Bool+ -> Symbols -- ^ all symbols+ -> Symbols -- ^ mentioned symbols+ -> Symbols -- ^ imported symbols+computeImportedSymbols isHiding (Symbols vs ts) mentionedSyms =+ case isHiding of+ False -> mentionedSyms+ True ->+ let+ Symbols hvs hts = mentionedSyms+ allTys = symbolMap st_origName ts+ hidTys = symbolMap st_origName hts+ allVls = symbolMap sv_origName vs+ hidVls = symbolMap sv_origName hvs+ in+ Symbols+ (Set.fromList $ Map.elems $ allVls Map.\\ hidVls)+ (Set.fromList $ Map.elems $ allTys Map.\\ hidTys)++symbolMap+ :: Ord s+ => (a -> s)+ -> Set.Set a+ -> Map.Map s a+symbolMap f is = Map.fromList [(f i, i) | i <- Set.toList is]++resolveImportSpec+ :: ModuleName l+ -> Bool+ -> Symbols+ -> ImportSpec l+ -> ImportSpec (Scoped l)+-- NB: this can be made more efficient+resolveImportSpec mod isHiding syms spec =+ case spec of+ IVar _ n ->+ let+ matches = mconcat $+ -- Strictly speaking, the isConstructor check is unnecessary+ -- because constructors are lexically different from anything+ -- else.+ [ mkVal info+ | info <- vs+ , not (isConstructor info)+ , sv_origName info ~~ n]+ in+ checkUnique+ (ENotExported Nothing n mod)+ matches+ spec+ -- FIXME think about data families etc.+ IAbs _ _ n+ | isHiding ->+ -- This is a bit special. 'C' may match both types/classes and+ -- data constructors.+ -- FIXME Still check for uniqueness?+ let+ Symbols vlMatches tyMatches =+ mconcat [ mkVal info | info <- vs, sv_origName info ~~ n]+ <>+ mconcat [ mkTy info | info <- ts, st_origName info ~~ n]+ in+ if Set.null tyMatches && Set.null vlMatches+ then+ scopeError (ENotExported Nothing n mod) spec+ else+ Scoped (ImportPart (Symbols vlMatches tyMatches)) <$> spec+ | otherwise ->+ let+ matches = mconcat+ [mkTy info | info <- ts, st_origName info ~~ n]+ in+ checkUnique+ (ENotExported Nothing n mod)+ matches+ spec+ -- FIXME+ -- What about things like:+ -- head(..)+ -- String(..)+ -- ?+ IThingAll l n ->+ let+ matches = [ info | info <- ts, st_origName info ~~ n]+ subs = mconcat+ [ mkVal info+ | n <- matches+ , info <- vs+ , Just n' <- return $ sv_parent info+ , n' == st_origName n ]+ n' =+ checkUnique+ (ENotExported Nothing n mod)+ (foldMap mkTy matches)+ n+ in+ case ann n' of+ e@(Scoped ScopeError{} _) -> IThingAll e n'+ _ ->+ IThingAll+ (Scoped+ (ImportPart (subs <> foldMap mkTy matches))+ l+ )+ n'++ IThingWith l n cns ->+ let+ matches = [info | info <- ts, st_origName info ~~ n]+ n' =+ checkUnique+ (ENotExported Nothing n mod)+ (foldMap mkTy matches)+ n+ typeName = st_origName $ head matches -- should be safe+ (cns', cnSyms) =+ resolveCNames+ syms+ typeName+ (\cn -> ENotExported (Just n) (unCName cn) mod)+ cns+ in+ IThingWith+ (Scoped+ (ImportPart (cnSyms <> foldMap mkTy matches))+ l+ )+ n'+ cns'+ where+ (~~) :: OrigName -> Name l -> Bool+ OrigName { origGName = GName { gName = n } } ~~ n' = n == nameToString n'++ isConstructor :: SymValueInfo n -> Bool+ isConstructor SymConstructor {} = True+ isConstructor _ = False++ vs = Set.toList $ syms^.valSyms+ ts = Set.toList $ syms^.tySyms++ann2syms :: Annotated a => a (Scoped l) -> Either (Error l) (Symbols)+ann2syms a =+ case ann a of+ Scoped (ScopeError e) _ -> Left e+ Scoped (ImportPart syms) _ -> Right syms+ _ -> Left $ EInternal "ann2syms"++checkUnique+ :: Functor f =>+ Error l ->+ Symbols ->+ f l ->+ f (Scoped l)+checkUnique notFound syms@(Symbols vs ts) f =+ case Set.size vs + Set.size ts of+ 0 -> scopeError notFound f+ 1 -> Scoped (ImportPart syms) <$> f+ -- there should be no clashes, and it should be checked elsewhere+ _ -> scopeError (EInternal "ambiguous import") f
+ src/haskell-names/Language/Haskell/Names/LocalSymbolTable.hs view
@@ -0,0 +1,34 @@+-- | This module is designed to be imported qualified.+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE NoImplicitPrelude #-}+module Language.Haskell.Names.LocalSymbolTable+ ( Table+ , empty+ , lookupValue+ , addValue+ ) where++import Fay.Compiler.Prelude hiding (empty)+import Language.Haskell.Names.SyntaxUtils+import Language.Haskell.Names.Types++import qualified Data.Map as Map+import Language.Haskell.Exts+import Data.Semigroup ()++-- | Local symbol table — contains locally bound names+newtype Table = Table (Map.Map NameS SrcLoc)+ deriving Semigroup++addValue :: SrcInfo l => Name l -> Table -> Table+addValue n (Table vs) =+ Table (Map.insert (nameToString n) (getPointLoc $ ann n) vs)++lookupValue :: QName l -> Table -> Either (Error l) SrcLoc+lookupValue qn@(UnQual _ n) (Table vs) =+ maybe (Left $ ENotInScope qn) Right $+ Map.lookup (nameToString n) vs+lookupValue qn _ = Left $ ENotInScope qn++empty :: Table+empty = Table Map.empty
+ src/haskell-names/Language/Haskell/Names/ModuleSymbols.hs view
@@ -0,0 +1,161 @@+{-# OPTIONS -fno-warn-name-shadowing #-}+{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE ViewPatterns #-}+module Language.Haskell.Names.ModuleSymbols+ ( moduleSymbols+ , moduleTable+ )+ where++import Fay.Compiler.Prelude++import Language.Haskell.Exts+import Language.Haskell.Names.GetBound+import qualified Language.Haskell.Names.GlobalSymbolTable as Global+import Language.Haskell.Names.ScopeUtils+import Language.Haskell.Names.SyntaxUtils+import Language.Haskell.Names.Types++import Data.Lens.Light+import qualified Data.Map as Map+import qualified Data.Set as Set++-- | Compute module's global table. It contains both the imported entities+-- and the global entities defined in this module.+moduleTable+ :: (Eq l, Data l)+ => Global.Table -- ^ the import table for this module+ -> Module l+ -> Global.Table+moduleTable impTbl m =+ impTbl <>+ computeSymbolTable False (getModuleName m) (moduleSymbols impTbl m)++-- | Compute the symbols that are defined in the given module.+--+-- The import table is needed to resolve possible top-level record+-- wildcard bindings, such as+--+-- >A {..} = foo+moduleSymbols+ :: (Eq l, Data l)+ => Global.Table -- ^ the import table for this module+ -> Module l+ -> Symbols+moduleSymbols impTbl m =+ let (vs,ts) =+ partitionEithers $+ concatMap+ (getTopDeclSymbols impTbl $ getModuleName m)+ (getModuleDecls m)+ in+ setL valSyms (Set.fromList vs) $+ setL tySyms (Set.fromList ts) mempty++type TypeName = GName+type ConName = Name ()+type SelectorName = Name ()+type Constructors = [(ConName, [SelectorName])]++-- Extract names that get bound by a top level declaration.+getTopDeclSymbols+ :: forall l . (Eq l, Data l)+ => Global.Table -- ^ the import table for this module+ -> ModuleName l+ -> Decl l+ -> [Either (SymValueInfo OrigName) (SymTypeInfo OrigName)]+getTopDeclSymbols impTbl mdl d =+ map (either (Left . fmap OrigName) (Right . fmap OrigName)) $+ case d of+ TypeDecl _ dh _ ->+ let tn = hname dh+ in [ Right (SymType { st_origName = tn, st_fixity = Nothing })]++ TypeFamDecl _loc dh _mrs _mk ->+ let tn = hname dh+ in [ Right (SymTypeFam { st_origName = tn, st_fixity = Nothing })]++ DataDecl _ dataOrNew _ dh qualConDecls _ ->+ let+ cons :: Constructors+ cons = do -- list monad+ QualConDecl _ _ _ conDecl <- qualConDecls+ case conDecl of+ ConDecl _ n _ -> return (void n, [])+ InfixConDecl _ _ n _ -> return (void n, [])+ RecDecl _ n fields ->+ return (void n , [void f | FieldDecl _ fNames _ <- fields, f <- fNames])++ dq = hname dh++ infos = constructorsToInfos dq cons++ in+ Right (dataOrNewCon dataOrNew dq Nothing) : map Left infos++ GDataDecl _ dataOrNew _ dh _ gadtDecls _ ->+ -- FIXME: We shouldn't create selectors for fields with existential type variables!+ let+ dq = hname dh++ cons :: Constructors+ cons = do -- list monad+ GadtDecl _ cn _tyvarBinds _context (fromMaybe [] -> fields) _ty <- gadtDecls+ return (void cn , [void f | FieldDecl _ fNames _ <- fields, f <- fNames])++ infos = constructorsToInfos dq cons+ in+ Right (dataOrNewCon dataOrNew dq Nothing) : map Left infos++ DataFamDecl _ _ dh _ ->+ let tn = hname dh+ in [Right (SymDataFam { st_origName = tn, st_fixity = Nothing })]++ ClassDecl _ _ dh _ mds ->+ let+ ms = getBound impTbl d+ cq = hname dh+ cdecls = fromMaybe [] mds+ in+ Right (SymClass { st_origName = cq, st_fixity = Nothing }) :+ [ Right (SymTypeFam { st_origName = hname dh, st_fixity = Nothing }) | ClsTyFam _ dh _ _ <- cdecls ] +++ [ Right (SymDataFam { st_origName = hname dh, st_fixity = Nothing }) | ClsDataFam _ _ dh _ <- cdecls ] +++ [ Left (SymMethod { sv_origName = qname mn, sv_fixity = Nothing, sv_className = cq }) | mn <- ms ]++ FunBind _ ms ->+ let vn : _ = getBound impTbl ms+ in [ Left (SymValue { sv_origName = qname vn, sv_fixity = Nothing }) ]++ PatBind _ p _ _ ->+ [ Left (SymValue { sv_origName = qname vn, sv_fixity = Nothing }) | vn <- getBound impTbl p ]++ ForImp _ _ _ _ fn _ ->+ [ Left (SymValue { sv_origName = qname fn, sv_fixity = Nothing }) ]++ _ -> []+ where+ ModuleName _ smdl = mdl+ qname = GName smdl . nameToString+ hname :: DeclHead l -> GName+ hname = qname . getDeclHeadName+ dataOrNewCon dataOrNew = case dataOrNew of DataType {} -> SymData; NewType {} -> SymNewType++ constructorsToInfos :: TypeName -> Constructors -> [SymValueInfo GName]+ constructorsToInfos ty cons = conInfos ++ selInfos+ where+ conInfos =+ [ SymConstructor { sv_origName = qname con, sv_fixity = Nothing, sv_typeName = ty }+ | (con, _) <- cons+ ]++ selectorsMap :: Map.Map SelectorName [ConName]+ selectorsMap =+ Map.unionsWith (++) . flip map cons $ \(c, fs) ->+ Map.unionsWith (++) . flip map fs $ \f ->+ Map.singleton f [c]++ selInfos =+ [ (SymSelector { sv_origName = qname f, sv_fixity = Nothing, sv_typeName = ty, sv_constructors = map qname fCons })+ | (f, fCons) <- Map.toList selectorsMap+ ]
+ src/haskell-names/Language/Haskell/Names/Open/Base.hs view
@@ -0,0 +1,152 @@+-- | This module provides a more flexible way to process Haskell code —+-- using an open-recursive traversal.+--+-- You can look at "Language.Haskell.Exts" source as an example+-- of how to use this module.+{-# OPTIONS -fno-warn-name-shadowing #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DefaultSignatures #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE ImplicitParams #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE MonoLocalBinds #-}++module Language.Haskell.Names.Open.Base+ ( Resolvable (..)+ , intro+ , mergeLocalScopes+ , alg+ , Scope (..)+ , setWcNames+ , gTable+ , exprV+ , exprT+ , rmap+ , wcNames+ , nameCtx+ , NameContext (..)+ , initialScope+ , binderV+ , Alg (..)+ , binderT+ , defaultRtraverse+ , lTable+ ) where++import Fay.Compiler.Prelude+import Language.Haskell.Names.GetBound+import qualified Language.Haskell.Names.GlobalSymbolTable as Global+import qualified Language.Haskell.Names.LocalSymbolTable as Local+import Language.Haskell.Names.RecordWildcards++import Control.Monad.Identity+import Data.Generics.Traversable+import Data.Lens.Light+import GHC.Exts (Constraint)+import Language.Haskell.Exts++-- | Describes how we should treat names in the current context+data NameContext+ = BindingT+ | BindingV+ | ReferenceT+ | ReferenceV+ | Other++-- | Contains information about the node's enclosing scope. Can be+-- accessed through the lenses: 'gTable', 'lTable', 'nameCtx', 'wcNames'.+data Scope = Scope+ { _gTable :: Global.Table+ , _lTable :: Local.Table+ , _nameCtx :: NameContext+ , _wcNames :: WcNames+ }++makeLens ''Scope++-- | Create an initial scope+initialScope :: Global.Table -> Scope+initialScope tbl = Scope tbl Local.empty Other []++-- | Merge local tables of two scopes. The other fields of the scopes are+-- assumed to be the same.+mergeLocalScopes :: Scope -> Scope -> Scope+mergeLocalScopes sc1 sc2 =+ modL lTable (<> sc2 ^. lTable) sc1++-- | The algebra for 'rtraverse'. It's newtype-wrapped because an implicit+-- parameter cannot be polymorphic.+newtype Alg w = Alg+ { runAlg :: forall d . Resolvable d => d -> Scope -> w d }++alg :: (?alg :: Alg w, Resolvable d) => d -> Scope -> w d+alg = runAlg ?alg++data ConstraintProxy (p :: * -> Constraint) = ConstraintProxy++defaultRtraverse+ :: (GTraversable Resolvable a, Applicative f, ?alg :: Alg f)+ => a -> Scope -> f a+defaultRtraverse a sc =+ let ?c = ConstraintProxy :: ConstraintProxy Resolvable+ in gtraverse @Resolvable (\a -> alg a sc) a++-- | A type that implements 'Resolvable' provides a way to perform+-- a shallow scope-aware traversal.++-- There is a generic implementation, 'defaultRtraverse', which is based on+-- 'GTraversable'. It can be used when there the scope of all the immediate+-- children is the same as the scope of the current node.+--+-- We use 'Typeable' here rather than a class-based approach.+-- Otherwise, hand-written instances would carry extremely long lists of+-- constraints, saying that the subterms satisfy the user-supplied class.+class Typeable a => Resolvable a where+ rtraverse+ :: (Applicative f, ?alg :: Alg f)+ => a -> Scope -> f a++instance (Typeable a, GTraversable Resolvable a) => Resolvable a where+ rtraverse = defaultRtraverse++-- | Analogous to 'gmap', but for 'Resolvable'+rmap+ :: Resolvable a+ => (forall b. Resolvable b => Scope -> b -> b)+ -> Scope -> a -> a+rmap f sc =+ let ?alg = Alg $ \a sc -> Identity (f sc a)+ in runIdentity . flip rtraverse sc++intro :: (SrcInfo l, GetBound a l) => a -> Scope -> Scope+intro node sc =+ modL lTable+ (\tbl -> foldl' (flip Local.addValue) tbl $+ getBound (sc ^. gTable) node)+ sc++setNameCtx :: NameContext -> Scope -> Scope+setNameCtx = setL nameCtx++setWcNames :: WcNames -> Scope -> Scope+setWcNames = setL wcNames++binderV :: Scope -> Scope+binderV = setNameCtx BindingV++binderT :: Scope -> Scope+binderT = setNameCtx BindingT++exprV :: Scope -> Scope+exprV = setNameCtx ReferenceV++exprT :: Scope -> Scope+exprT = setNameCtx ReferenceT
+ src/haskell-names/Language/Haskell/Names/Open/Derived.hs view
@@ -0,0 +1,88 @@+{-# OPTIONS_GHC -fno-warn-orphans -fno-warn-unused-matches #-}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE UndecidableInstances #-}+module Language.Haskell.Names.Open.Derived where++import Data.Generics.Traversable.TH+import Language.Haskell.Exts++deriveGTraversable ''ModuleName+deriveGTraversable ''SpecialCon+deriveGTraversable ''QName+deriveGTraversable ''Name+deriveGTraversable ''Boxed+deriveGTraversable ''IPName+deriveGTraversable ''QOp+deriveGTraversable ''Op+deriveGTraversable ''CName+deriveGTraversable ''Module+deriveGTraversable ''ModuleHead+deriveGTraversable ''ExportSpecList+deriveGTraversable ''ExportSpec+deriveGTraversable ''ImportDecl+deriveGTraversable ''ImportSpecList+deriveGTraversable ''ImportSpec+deriveGTraversable ''Assoc+deriveGTraversable ''Decl+deriveGTraversable ''Annotation+deriveGTraversable ''DataOrNew+deriveGTraversable ''DeclHead+deriveGTraversable ''InstHead+deriveGTraversable ''InstRule+deriveGTraversable ''Deriving+deriveGTraversable ''Binds+deriveGTraversable ''IPBind+deriveGTraversable ''Match+deriveGTraversable ''QualConDecl+deriveGTraversable ''ConDecl+deriveGTraversable ''FieldDecl+deriveGTraversable ''GadtDecl+deriveGTraversable ''ClassDecl+deriveGTraversable ''InstDecl+deriveGTraversable ''BangType+deriveGTraversable ''Rhs+deriveGTraversable ''GuardedRhs+deriveGTraversable ''Type+deriveGTraversable ''TyVarBind+deriveGTraversable ''FunDep+deriveGTraversable ''Context+deriveGTraversable ''Asst+deriveGTraversable ''Literal+deriveGTraversable ''Exp+deriveGTraversable ''XName+deriveGTraversable ''XAttr+deriveGTraversable ''Bracket+deriveGTraversable ''Splice+deriveGTraversable ''Safety+deriveGTraversable ''CallConv+deriveGTraversable ''ModulePragma+deriveGTraversable ''Tool+deriveGTraversable ''Activation+deriveGTraversable ''Rule+deriveGTraversable ''RuleVar+deriveGTraversable ''WarningText+deriveGTraversable ''Pat+deriveGTraversable ''PXAttr+deriveGTraversable ''RPatOp+deriveGTraversable ''RPat+deriveGTraversable ''PatField+deriveGTraversable ''Stmt+deriveGTraversable ''QualStmt+deriveGTraversable ''FieldUpdate+deriveGTraversable ''Alt+deriveGTraversable ''Promoted+deriveGTraversable ''BooleanFormula+deriveGTraversable ''TypeEqn+deriveGTraversable ''Overlap+deriveGTraversable ''Sign+deriveGTraversable ''Namespace+deriveGTraversable ''Role+deriveGTraversable ''PatternSynDirection+deriveGTraversable ''Unpackedness+deriveGTraversable ''ResultSig+deriveGTraversable ''InjectivityInfo+deriveGTraversable ''DerivStrategy+deriveGTraversable ''MaybePromotedName
+ src/haskell-names/Language/Haskell/Names/Open/Instances.hs view
@@ -0,0 +1,361 @@+{-# OPTIONS -fno-warn-name-shadowing #-}+{-# OPTIONS -fno-warn-orphans #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE ImplicitParams #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE UndecidableInstances #-}++-- MonoLocalBinds extension prevents premature generalization, which+-- results in the "default" instance being picked.+{-# LANGUAGE MonoLocalBinds #-}+module Language.Haskell.Names.Open.Instances () where++import Fay.Compiler.Prelude+import Language.Haskell.Names.GetBound+import Language.Haskell.Names.Open.Base+import Language.Haskell.Names.Open.Derived ()+import Language.Haskell.Names.RecordWildcards+import Language.Haskell.Names.Types++import Data.Lens.Light+import qualified Data.Traversable as T+import Language.Haskell.Exts++c :: Applicative w => c -> w c+c = pure++(<|)+ :: (Applicative w, Resolvable b, ?alg :: Alg w)+ => w (b -> c) -> (b, Scope) -> w c+(<|) k (b, sc) = k <*> alg b sc+infixl 4 <|++(-:) :: Scope -> a -> (a, Scope)+sc -: b = (b, sc)+infix 5 -:++instance {-# OVERLAPPING #-} (Resolvable l, SrcInfo l, Data l) => Resolvable (Decl l) where+ rtraverse e sc =+ case e of+ -- N.B. We do not add pat to the local scope.+ --+ -- If this is a top-level binding, then we shouldn't do so, lest+ -- global values are marked as local.+ -- (see https://github.com/haskell-suite/haskell-names/issues/35)+ --+ -- If this is a local binding, then we have already introduced these+ -- variables when processing the enclosing Binds.+ PatBind l pat rhs mbWhere ->+ let+ scWithWhere = intro mbWhere sc+ in+ c PatBind+ <| sc -: l+ <| sc -: pat+ <| exprV scWithWhere -: rhs+ <| sc -: mbWhere+ -- FunBind consists of Matches, which we handle below anyway.+ TypeSig l names ty ->+ c TypeSig+ <| sc -: l+ <| exprV sc -: names+ <| sc -: ty+ _ -> defaultRtraverse e sc++instance {-# OVERLAPPING #-} (Resolvable l, SrcInfo l, Data l) => Resolvable (Type l) where+ rtraverse e sc = defaultRtraverse e (exprT sc)++instance {-# OVERLAPPING #-} (Resolvable l, SrcInfo l, Data l) => Resolvable (DeclHead l) where+ rtraverse e sc =+ case e of+ DHead l name ->+ c DHead+ <| sc -: l+ <| binderT sc -: name+ DHInfix l v1 name ->+ c DHInfix+ <| sc -: l+ <| sc -: v1+ <| binderT sc -: name+ _ -> defaultRtraverse e sc++instance {-# OVERLAPPING #-} (Resolvable l, SrcInfo l, Data l) => Resolvable (ConDecl l) where+ rtraverse e sc =+ case e of+ ConDecl l name tys ->+ c ConDecl+ <| sc -: l+ <| binderV sc -: name+ <| sc -: tys+ InfixConDecl l t1 name t2 ->+ c InfixConDecl+ <| sc -: l+ <| sc -: t1+ <| binderV sc -: name+ <| sc -: t2+ RecDecl l name fields ->+ c RecDecl+ <| sc -: l+ <| binderV sc -: name+ <| sc -: fields+++instance {-# OVERLAPPING #-} (Resolvable l, SrcInfo l, Data l) => Resolvable (FieldDecl l) where+ rtraverse e sc =+ case e of+ FieldDecl l name tys ->+ c FieldDecl+ <| sc -: l+ <| binderV sc -: name+ <| sc -: tys++instance {-# OVERLAPPING #-} (Resolvable l, SrcInfo l, Data l) => Resolvable (Pat l) where+ rtraverse e sc =+ case e of+ PVar l name ->+ c PVar+ <| sc -: l+ <| binderV sc -: name+ PNPlusK l name i ->+ c PNPlusK+ <| sc -: l+ <| binderV sc -: name+ <| sc -: i+ PInfixApp l pat1 name pat2 ->+ c PInfixApp+ <| sc -: l+ <| sc -: pat1+ <| exprV sc -: name+ <| sc -: pat2+ PApp l qn pat ->+ c PApp+ <| sc -: l+ <| exprV sc -: qn+ <| sc -: pat+ PRec l qn pfs ->+ let+ scWc =+ setWcNames (patWcNames (sc ^. gTable) qn pfs) sc+ in+ c PRec+ <| sc -: l+ <| exprV sc -: qn+ <| scWc -: pfs+ PAsPat l n pat ->+ c PAsPat+ <| sc -: l+ <| binderV sc -: n+ <| sc -: pat+ PViewPat l exp pat ->+ c PViewPat+ <| sc -: l+ <| exprV sc -: exp+ <| sc -: pat+ _ -> defaultRtraverse e sc++instance {-# OVERLAPPING #-} (Resolvable l, SrcInfo l, Data l) => Resolvable (PatField l) where+ rtraverse e sc =+ case e of+ PFieldPat l qn pat ->+ c PFieldPat+ <| sc -: l+ <| exprV sc -: qn+ <| sc -: pat+ PFieldPun l qn ->+ c PFieldPun+ <| sc -: l+ <| exprV sc -: qn+ -- In future we might want to annotate PFieldWildcard with the names+ -- it introduces.+ PFieldWildcard {} -> defaultRtraverse e sc++-- | Chain a sequence of nodes where every node may introduce some+-- variables into scope for the subsequent nodes. Examples: patterns (see+-- note [Nested pattern scopes]), statements.+chain+ :: ( Resolvable (a l)+ , GetBound (a l) l+ , Applicative w+ , SrcInfo l+ , Data l+ , ?alg :: Alg w)+ => [a l] -> Scope -> (w [a l], Scope)+chain pats sc =+ case pats of+ [] -> (pure [], sc)+ p:ps ->+ let+ sc' = intro p sc+ p' = alg p sc+ (ps', sc'') = chain ps sc'+ in ((:) <$> p' <*> ps', sc'')++instance {-# OVERLAPPING #-} (Resolvable l, SrcInfo l, Data l) => Resolvable (Match l) where+ rtraverse e sc =+ case e of+ Match l name pats rhs mbWhere ->+ -- f x y z = ...+ -- where ...+ let+ (pats', scWithPats) = chain pats sc+ scWithWhere = intro mbWhere scWithPats+ in+ c Match+ <| sc -: l+ <| binderV sc -: name+ <*> pats' -- has been already traversed+ <| exprV scWithWhere -: rhs+ <| scWithPats -: mbWhere+ InfixMatch l pat1 name patsRest rhs mbWhere ->+ let+ equivalentMatch = Match l name (pat1:patsRest) rhs mbWhere+ back (Match l name (pat1:patsRest) rhs mbWhere) =+ InfixMatch l pat1 name patsRest rhs mbWhere+ back _ = error "InfixMatch"+ in back <$> rtraverse equivalentMatch sc++-- NB: there is an inefficiency here (and in similar places), because we+-- call intro on the same subtree several times. Maybe tackle it later.+instance {-# OVERLAPPING #-} (Resolvable l, SrcInfo l, Data l) => Resolvable (Binds l) where+ rtraverse e sc =+ case e of+ BDecls l decls ->+ let scWithBinds = intro decls sc+ in+ c BDecls+ <| sc -: l+ <| scWithBinds -: decls+ _ -> defaultRtraverse e sc++instance {-# OVERLAPPING #-} (Resolvable l, SrcInfo l, Data l) => Resolvable (Exp l) where+ rtraverse e sc =+ case e of+ Let l bnds body ->+ let scWithBinds = intro bnds sc+ in+ c Let+ <| sc -: l+ <| scWithBinds -: bnds+ <| scWithBinds -: body++ Lambda l pats body ->+ let (pats', scWithPats) = chain pats sc+ in+ c Lambda+ <| sc -: l+ <*> pats'+ <| scWithPats -: body++ ListComp l e stmts ->+ let (stmts', scWithStmts) = chain stmts sc+ in+ c ListComp+ <| sc -: l+ <| scWithStmts -: e+ <*> stmts'++ ParComp l e stmtss ->+ let+ (stmtss', scsWithStmts) =+ unzip $ map (\stmts -> chain stmts sc) stmtss+ scWithAllStmtss = foldl1' mergeLocalScopes scsWithStmts+ in+ c ParComp+ <| sc -: l+ <| scWithAllStmtss -: e+ <*> T.sequenceA stmtss'++ Proc l pat e ->+ let scWithPat = intro pat sc+ in+ c Proc+ <| sc -: l+ <| sc -: pat+ <| scWithPat -: e++ RecConstr l qn fields ->+ let+ scWc =+ setWcNames+ (expWcNames+ (sc ^. gTable)+ (sc ^. lTable)+ qn+ fields)+ sc+ in+ c RecConstr+ <| sc -: l+ <| sc -: qn+ <| scWc -: fields++ _ -> defaultRtraverse e sc++instance {-# OVERLAPPING #-} (Resolvable l, SrcInfo l, Data l) => Resolvable (Alt l) where+ rtraverse e sc =+ case e of+ Alt l pat guardedAlts mbWhere ->+ let+ scWithPat = intro pat sc+ scWithBinds = intro mbWhere scWithPat+ in+ c Alt+ <| sc -: l+ <| sc -: pat+ <| scWithBinds -: guardedAlts+ <| scWithBinds -: mbWhere++instance {-# OVERLAPPING #-} (Resolvable l, SrcInfo l, Data l) => Resolvable (GuardedRhs l) where+ rtraverse e sc =+ case e of+ GuardedRhs l stmts exp ->+ let (stmts', scWithStmts) = chain stmts sc+ in+ c GuardedRhs+ <| sc -: l+ <*> stmts'+ <| scWithStmts -: exp++instance {-# OVERLAPPING #-} (Resolvable l, SrcInfo l, Data l) => Resolvable [Stmt l] where+ rtraverse e sc =+ fst $ chain e sc++instance {-# OVERLAPPING #-} (Resolvable l, SrcInfo l, Data l) => Resolvable (QualStmt l) where+ rtraverse e sc =+ case e of+ QualStmt {} -> defaultRtraverse e sc+ _ -> error "haskell-names: TransformListComp is not supported yet"++{-+Note [Nested pattern scopes]+~~~~~~~~~~~~~~~~~~~~~~++When we resolve a group of patterns, their scopes nest.++Most of the time, this is not important, but there are two exceptions:+1. ScopedTypeVariables++Example: f (x :: a) (y :: a) = ...++The first 'a' is a binder, the second — a reference.++2. View patterns++An expression inside a view pattern may reference the variables bound+earlier.++Example: f x (find (< x) -> Just y) = ...+-}++-- Some road-block Resolvable instances+instance {-# OVERLAPPING #-} Typeable a => Resolvable (Scoped a) where+ rtraverse = flip $ const pure+instance {-# OVERLAPPING #-} Resolvable SrcSpan where+ rtraverse = flip $ const pure+instance {-# OVERLAPPING #-} Resolvable SrcSpanInfo where+ rtraverse = flip $ const pure++
+ src/haskell-names/Language/Haskell/Names/RecordWildcards.hs view
@@ -0,0 +1,145 @@+-- Wildcards are tricky, they deserve a module of their own+{-# OPTIONS -fno-warn-name-shadowing #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE PatternGuards #-}+{-# LANGUAGE TupleSections #-}+module Language.Haskell.Names.RecordWildcards+ ( patWcNames+ , wcFieldName+ , WcNames+ , expWcNames+ , wcFieldOrigName+ ) where++import qualified Language.Haskell.Names.GlobalSymbolTable as Global+import qualified Language.Haskell.Names.LocalSymbolTable as Local+import Language.Haskell.Names.SyntaxUtils+import Language.Haskell.Names.Types++import Control.Monad+import qualified Data.Map as Map+import Data.Maybe+import qualified Data.Set as Set+import Language.Haskell.Exts++-- | Information about the names being introduced by a record wildcard+--+-- During resolving traversal, we always (lazily) construct this list when+-- we process PRec or RecConstr, even if it doesn't contain a wildcard.+--+-- Then, if the pattern or construction actually contains a wildcard, we use the computed value.+type WcNames = [WcField]++-- | Information about a field in the wildcard+data WcField = WcField+ { wcFieldName :: Name ()+ -- ^ the field's simple name+ , wcFieldOrigName :: OrigName+ -- ^ the field's original name+ , wcExistsGlobalValue :: Bool+ -- ^ whether there is a global value in scope with the same name as+ -- the field but different from the field selector+ }++getElidedFields+ :: Global.Table+ -> QName l+ -> [Name l] -- mentioned field names+ -> WcNames+getElidedFields gt con fields =+ let+ givenFieldNames :: Map.Map (Name ()) ()+ givenFieldNames =+ Map.fromList . map ((, ()) . void) $ fields++ -- FIXME must report error when the constructor cannot be+ -- resolved+ (mbConOrigName, mbTypeOrigName) =+ case Global.lookupValue con gt of+ Global.Result info@SymConstructor{} ->+ (Just $ sv_origName info, Just $ sv_typeName info)+ _ -> (Nothing, Nothing)++ allValueInfos :: Set.Set (SymValueInfo OrigName)+ allValueInfos = Set.unions $ Map.elems $ Global.values gt++ ourFieldInfos :: Set.Set (SymValueInfo OrigName)+ ourFieldInfos =+ case mbConOrigName of+ Nothing -> Set.empty+ Just conOrigName ->+ flip Set.filter allValueInfos $ \v ->+ case v of+ SymSelector { sv_constructors }+ | conOrigName `elem` sv_constructors -> True+ _ -> False++ existsGlobalValue :: Name () -> Bool+ existsGlobalValue name =+ case Global.lookupValue (UnQual () name) gt of+ Global.Result info+ | Just typeOrigName <- mbTypeOrigName+ , SymSelector {} <- info+ , sv_typeName info == typeOrigName+ -> False -- this is the field selector+ | otherwise -> True -- exists, but not this field's selector+ _ -> False -- doesn't exist or ambiguous++ ourFieldNames :: Map.Map (Name ()) WcField+ ourFieldNames =+ Map.fromList $+ map+ (+ (\orig ->+ let name = stringToName . gName . origGName $ orig in+ (name, ) $+ WcField+ { wcFieldName = name+ , wcFieldOrigName = orig+ , wcExistsGlobalValue = existsGlobalValue name+ }+ ) . sv_origName+ )+ $ Set.toList ourFieldInfos++ in Map.elems $ ourFieldNames `Map.difference` givenFieldNames++nameOfPatField :: PatField l -> Maybe (Name l)+nameOfPatField pf =+ case pf of+ PFieldPat _ qn _ -> Just $ qNameToName qn+ PFieldPun _ qn -> Just $ qNameToName qn+ PFieldWildcard {} -> Nothing++nameOfUpdField :: FieldUpdate l -> Maybe (Name l)+nameOfUpdField pf =+ case pf of+ FieldUpdate _ qn _ -> Just $ qNameToName qn+ FieldPun _ qn -> Just $ qNameToName qn+ FieldWildcard {} -> Nothing++patWcNames+ :: Global.Table+ -> QName l+ -> [PatField l]+ -> WcNames+patWcNames gt con patfs =+ getElidedFields gt con $+ mapMaybe nameOfPatField patfs++expWcNames+ :: Global.Table+ -> Local.Table+ -> QName l+ -> [FieldUpdate l]+ -> WcNames+expWcNames gt lt con patfs =+ filter isInScope $+ getElidedFields gt con $+ mapMaybe nameOfUpdField patfs+ where+ isInScope field+ | Right {} <- Local.lookupValue qn lt = True+ | otherwise = wcExistsGlobalValue field+ where+ qn = UnQual () $ wcFieldName field
+ src/haskell-names/Language/Haskell/Names/Recursive.hs view
@@ -0,0 +1,132 @@+{-# OPTIONS -fno-warn-name-shadowing #-}+{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+module Language.Haskell.Names.Recursive+ ( computeInterfaces+ , getInterfaces+ , annotateModule+ ) where++import Fay.Compiler.Prelude++import Fay.Compiler.ModuleT+import Language.Haskell.Names.Annotated+import Language.Haskell.Names.Exports+import Language.Haskell.Names.Imports+import Language.Haskell.Names.ModuleSymbols+import Language.Haskell.Names.Open.Base+import Language.Haskell.Names.ScopeUtils+import Language.Haskell.Names.SyntaxUtils+import Language.Haskell.Names.Types++import Data.Data (Data)+import Data.Foldable+import Data.Graph (flattenSCC, stronglyConnComp)+import qualified Data.Set as Set+import Language.Haskell.Exts+++-- | Take a set of modules and return a list of sets, where each sets for+-- a strongly connected component in the import graph.+-- The boolean determines if imports using @SOURCE@ are taken into account.+groupModules :: forall l . [Module l] -> [[Module l]]+groupModules modules =+ map flattenSCC $ stronglyConnComp $ map mkNode modules+ where+ mkNode :: Module l -> (Module l, ModuleName (), [ModuleName ()])+ mkNode m =+ ( m+ , dropAnn $ getModuleName m+ , map (dropAnn . importModule) $ getImports m+ )++-- | Annotate a module with scoping information. This assumes that all+-- module dependencies have been resolved and cached — usually you need+-- to run 'computeInterfaces' first, unless you have one module in+-- isolation.+annotateModule+ :: (MonadModule m, ModuleInfo m ~ Symbols, Data l, SrcInfo l, Eq l)+ => Language -- ^ base language+ -> [Extension] -- ^ global extensions (e.g. specified on the command line)+ -> Module l -- ^ input module+ -> m (Module (Scoped l)) -- ^ output (annotated) module+annotateModule lang exts mod@(Module lm mh os is ds) = do+ let extSet = moduleExtensions lang exts mod+ (imp, impTbl) <- processImports extSet is+ let tbl = moduleTable impTbl mod+ (exp, _syms) <- processExports tbl mod++ let+ lm' = none lm+ os' = fmap noScope os+ is' = imp+ ds' = annotate (initialScope tbl) `map` ds++ mh' = flip fmap mh $ \(ModuleHead lh n mw _me) ->+ let+ lh' = none lh+ n' = noScope n+ mw' = fmap noScope mw+ me' = exp+ in ModuleHead lh' n' mw' me'++ return $ Module lm' mh' os' is' ds'++annotateModule _ _ _ = error "annotateModule: non-standard modules are not supported"++-- | Compute interfaces for a set of mutually recursive modules and write+-- the results to the cache. Return the set of import/export errors.+findFixPoint+ :: (Ord l, Data l, MonadModule m, ModuleInfo m ~ Symbols)+ => [(Module l, ExtensionSet)]+ -- ^ module and all extensions with which it is to be compiled.+ -- Use 'moduleExtensions' to build this list.+ -> m (Set.Set (Error l))+findFixPoint mods = go mods (map (const mempty) mods) where+ go mods syms = do+ forM_ (zip syms mods) $ \(s,(m, _)) -> insertInCache (getModuleName m) s+ (syms', errors) <- liftM unzip $ forM mods $ \(m, extSet) -> do+ (imp, impTbl) <- processImports extSet $ getImports m+ let tbl = moduleTable impTbl m+ (exp, syms) <- processExports tbl m+ return (syms, foldMap getErrors imp <> foldMap getErrors exp)+ if syms' == syms+ then return $ mconcat errors+ else go mods syms'++-- | 'computeInterfaces' takes a list of possibly recursive modules and+-- computes the interface of each module. The computed interfaces are+-- written into the @m@'s cache and are available to further computations+-- in this monad.+--+-- Returns the set of import/export errors. Note that the interfaces are+-- registered in the cache regardless of whether there are any errors, but+-- if there are errors, the interfaces may be incomplete.+computeInterfaces+ :: (MonadModule m, ModuleInfo m ~ Symbols, Data l, SrcInfo l, Ord l)+ => Language -- ^ base language+ -> [Extension] -- ^ global extensions (e.g. specified on the command line)+ -> [Module l] -- ^ input modules+ -> m (Set.Set (Error l)) -- ^ errors in export or import lists+computeInterfaces lang exts =+ liftM fold . mapM findFixPoint . map supplyExtensions . groupModules+ where+ supplyExtensions = map $ \m -> (m, moduleExtensions lang exts m)++-- | Like 'computeInterfaces', but also returns a list of interfaces, one+-- per module and in the same order+getInterfaces+ :: (MonadModule m, ModuleInfo m ~ Symbols, Data l, SrcInfo l, Ord l)+ => Language -- ^ base language+ -> [Extension] -- ^ global extensions (e.g. specified on the command line)+ -> [Module l] -- ^ input modules+ -> m ([Symbols], Set.Set (Error l)) -- ^ output modules, and errors in export or import lists+getInterfaces lang exts mods = do+ errs <- computeInterfaces lang exts mods+ ifaces <- forM mods $ \mod ->+ let modName = getModuleName mod in+ fromMaybe (error $ msg modName) `liftM` lookupInCache modName+ return (ifaces, errs)+ where+ msg modName = "getInterfaces: module " ++ modToString modName ++ " is not in the cache"
+ src/haskell-names/Language/Haskell/Names/ScopeUtils.hs view
@@ -0,0 +1,87 @@+{-# OPTIONS -fno-warn-name-shadowing #-}+module Language.Haskell.Names.ScopeUtils+ ( computeSymbolTable+ , noScope+ , none+ , resolveCNames+ , scopeError+ , sv_parent+ ) where++import Fay.Compiler.Prelude+import qualified Language.Haskell.Names.GlobalSymbolTable as Global+import Language.Haskell.Names.SyntaxUtils+import Language.Haskell.Names.Types++import Data.Lens.Light+import qualified Data.Set as Set+import Language.Haskell.Exts++scopeError :: Functor f => Error l -> f l -> f (Scoped l)+scopeError e f = Scoped (ScopeError e) <$> f++none :: l -> Scoped l+none = Scoped None++noScope :: (Annotated a) => a l -> a (Scoped l)+noScope = fmap none++sv_parent :: SymValueInfo n -> Maybe n+sv_parent (SymSelector { sv_typeName = n }) = Just n+sv_parent (SymConstructor { sv_typeName = n }) = Just n+sv_parent (SymMethod { sv_className = n }) = Just n+sv_parent _ = Nothing++computeSymbolTable+ :: Bool+ -- ^ If 'True' (\"qualified\"), then only the qualified names are+ -- inserted.+ --+ -- If 'False', then both qualified and unqualified names are insterted.+ -> ModuleName l+ -> Symbols+ -> Global.Table+computeSymbolTable qual (ModuleName _ mod) syms =+ Global.fromLists $+ if qual+ then renamed+ else renamed <> unqualified+ where+ vs = Set.toList $ syms^.valSyms+ ts = Set.toList $ syms^.tySyms+ renamed = renameSyms mod+ unqualified = renameSyms ""+ renameSyms mod = (map (rename mod) vs, map (rename mod) ts)+ rename :: HasOrigName i => ModuleNameS -> i OrigName -> (GName, i OrigName)+ rename m v = ((origGName . origName $ v) { gModule = m }, v)++resolveCName+ :: Symbols+ -> OrigName+ -> (CName l -> Error l) -- ^ error for "not found" condition+ -> CName l+ -> (CName (Scoped l), Symbols)+resolveCName syms parent notFound cn =+ let+ vs =+ [ info+ | info <- Set.toList $ syms^.valSyms+ , let name = gName . origGName $ sv_origName info+ , nameToString (unCName cn) == name+ , Just p <- return $ sv_parent info+ , p == parent+ ]+ in+ case vs of+ [] -> (scopeError (notFound cn) cn, mempty)+ [i] -> (Scoped (GlobalValue i) <$> cn, mkVal i)+ _ -> (scopeError (EInternal "resolveCName") cn, mempty)++resolveCNames+ :: Symbols+ -> OrigName+ -> (CName l -> Error l) -- ^ error for "not found" condition+ -> [CName l]+ -> ([CName (Scoped l)], Symbols)+resolveCNames syms orig notFound =+ second mconcat . unzip . map (resolveCName syms orig notFound)
+ src/haskell-names/Language/Haskell/Names/SyntaxUtils.hs view
@@ -0,0 +1,136 @@+{-# OPTIONS -fno-warn-name-shadowing #-}+module Language.Haskell.Names.SyntaxUtils+ ( dropAnn+ , getModuleName+ , getImports+ , getExportSpecList+ , getDeclHeadName+ , getModuleDecls+ , nameToString+ , stringToName+ , qNameToName+ , unCName+ , getErrors+ , moduleExtensions+ ) where++import Language.Haskell.Names.Types++import Data.Char+import Data.Either+import Data.Foldable+import Data.Maybe+import qualified Data.Set as Set+import Language.Haskell.Exts++dropAnn :: (Functor a) => a l -> a ()+dropAnn = fmap (const ())++getModuleName :: Module l -> ModuleName l+getModuleName (Module _ (Just (ModuleHead _ mn _ _)) _ _ _) = mn+getModuleName (XmlPage _ mn _ _ _ _ _) = mn+getModuleName (XmlHybrid _ (Just (ModuleHead _ mn _ _)) _ _ _ _ _ _ _) = mn+getModuleName m = main_mod (ann m)++getImports :: Module l -> [ImportDecl l]+getImports (Module _ _ _ is _) = is+getImports (XmlPage _ _ _ _ _ _ _) = []+getImports (XmlHybrid _ _ _ is _ _ _ _ _) = is++getModuleDecls :: Module l -> [Decl l]+getModuleDecls (Module _ _ _ _ ds) = ds+getModuleDecls (XmlPage _ _ _ _ _ _ _) = []+getModuleDecls (XmlHybrid _ _ _ _ ds _ _ _ _) = ds++getExportSpecList :: Module l -> Maybe (ExportSpecList l)+getExportSpecList m = me where ModuleHead _ _ _ me = getModuleHead m++getModuleHead :: Module l -> ModuleHead l+getModuleHead (Module _ (Just mh) _ _ _) = mh+getModuleHead (XmlHybrid _ (Just mh) _ _ _ _ _ _ _) = mh+getModuleHead m = ModuleHead l (main_mod l) Nothing (Just (ExportSpecList l [EVar l (UnQual l (Ident l "main"))]))+ where l = ann m++qNameToName :: QName l -> Name l+qNameToName (UnQual _ n) = n+qNameToName (Qual _ _ n) = n+qNameToName (Special l s) = Ident l (specialConToString s)++getDeclHeadName :: DeclHead l -> Name l+getDeclHeadName dh =+ case dh of+ DHead _ n -> n+ DHInfix _ _ n -> n+ DHParen _ dh' -> getDeclHeadName dh'+ DHApp _ dh' _ -> getDeclHeadName dh'++----------------------------------------------------++nameToString :: Name l -> String+nameToString (Ident _ s) = s+nameToString (Symbol _ s) = s++stringToName :: String -> Name ()+stringToName s@(c:_) | isSymbol c = Symbol () s+stringToName s = Ident () s++specialConToString :: SpecialCon l -> String+specialConToString (UnitCon _) = "()"+specialConToString (ListCon _) = "[]"+specialConToString (FunCon _) = "->"+specialConToString (TupleCon _ Boxed n) = replicate (n-1) ','+specialConToString (TupleCon _ Unboxed n) = '#':replicate (n-1) ','+specialConToString (Cons _) = ":"+specialConToString (UnboxedSingleCon _) = "#"+specialConToString (ExprHole _) = "_"++unCName :: CName l -> Name l+unCName (VarName _ n) = n+unCName (ConName _ n) = n++getErrors :: (Ord l, Foldable a) => a (Scoped l) -> Set.Set (Error l)+getErrors = foldl' f Set.empty+ where+ f errors (Scoped (ScopeError e) _) = Set.insert e errors+ f errors _ = errors++-- | Compute the extension set for the given module, based on the global+-- preferences (e.g. specified on the command line) and module's LANGUAGE+-- pragmas.+moduleExtensions+ :: Language -- ^ base language+ -> [Extension] -- ^ global extensions+ -> Module l+ -> ExtensionSet+moduleExtensions globalLang globalExts mod =+ let+ (mbModLang, modExts) = getModuleExtensions mod+ lang = fromMaybe globalLang mbModLang+ kexts = toExtensionList lang (globalExts ++ modExts)+ in Set.fromList kexts++getModuleExtensions :: Module l -> (Maybe Language, [Extension])+getModuleExtensions mod =+ let+ names =+ [ name+ | let+ pragmas =+ case mod of+ Module _ _ pragmas _ _ -> pragmas+ XmlPage _ _ pragmas _ _ _ _ -> pragmas+ XmlHybrid _ _ pragmas _ _ _ _ _ _ -> pragmas+ , LanguagePragma _ names <- pragmas+ , Ident _ name <- names+ ]++ classified :: [Either Language Extension]+ classified =+ flip map names $ \name ->+ case (parseExtension name, classifyLanguage name) of+ (e, UnknownLanguage {}) -> Right e+ (_, l) -> Left l++ (langs, exts) = partitionEithers classified+ in+ (if null langs then Nothing else Just $ last langs, exts)
+ src/haskell-names/Language/Haskell/Names/Types.hs view
@@ -0,0 +1,293 @@+{-# OPTIONS -fno-warn-name-shadowing #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveFoldable #-}+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE DeriveTraversable #-}+{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE StandaloneDeriving #-}+module Language.Haskell.Names.Types+ ( Error (..)+ , ExtensionSet+ , GName (..)+ , HasOrigName (..)+ , ModuleNameS+ , NameInfo (..)+ , NameS+ , OrigName (..)+ , Scoped (..)+ , SymTypeInfo (..)+ , SymValueInfo (..)+ , Symbols (..)+ , mkTy+ , mkVal+ , ppError+ , ppGName+ , ppOrigName+ , sLoc+ , tySyms+ , valSyms+ ) where++import {-# SOURCE #-} qualified Language.Haskell.Names.GlobalSymbolTable as Global+import Fay.Compiler.Prelude++import Data.Foldable as F+import Data.Lens.Light+import qualified Data.Set as Set+import Language.Haskell.Exts+import Text.Printf+import qualified Data.Semigroup as SG++type ExtensionSet = Set.Set KnownExtension++-- | Repesents the symbol's fixity+type SymFixity = (Assoc (), Int)++-- | Information about a value-level entitity+data SymValueInfo name+ = SymValue+ { sv_origName :: name+ , sv_fixity :: Maybe SymFixity+ }+ -- ^ value or function+ | SymMethod+ { sv_origName :: name+ , sv_fixity :: Maybe SymFixity+ , sv_className :: name+ }+ -- ^ class method+ | SymSelector+ { sv_origName :: name+ , sv_fixity :: Maybe SymFixity+ , sv_typeName :: name+ , sv_constructors :: [name]+ }+ -- ^ record field selector+ | SymConstructor+ { sv_origName :: name+ , sv_fixity :: Maybe SymFixity+ , sv_typeName :: name+ }+ -- ^ data constructor+ deriving (Eq, Ord, Show, Data, Typeable, Functor, Foldable, Traversable)++-- | Information about a type-level entitity+data SymTypeInfo name+ = SymType+ { st_origName :: name+ , st_fixity :: Maybe SymFixity+ }+ -- ^ type synonym+ | SymData+ { st_origName :: name+ , st_fixity :: Maybe SymFixity+ }+ -- ^ data type+ | SymNewType+ { st_origName :: name+ , st_fixity :: Maybe SymFixity+ }+ -- ^ newtype+ | SymTypeFam+ { st_origName :: name+ , st_fixity :: Maybe SymFixity+ }+ -- ^ type family+ | SymDataFam+ { st_origName :: name+ , st_fixity :: Maybe SymFixity+ }+ -- ^ data family+ | SymClass+ { st_origName :: name+ , st_fixity :: Maybe SymFixity+ }+ -- ^ type class+ deriving (Eq, Ord, Show, Data, Typeable, Functor, Foldable, Traversable)++class HasOrigName i where+ origName :: i n -> n++instance HasOrigName SymValueInfo where+ origName = sv_origName++instance HasOrigName SymTypeInfo where+ origName = st_origName++-- | The set of symbols (entities) exported by a single module. Contains+-- the sets of value-level and type-level entities.+data Symbols = Symbols (Set.Set (SymValueInfo OrigName)) (Set.Set (SymTypeInfo OrigName))+ deriving (Eq, Ord, Show, Data, Typeable)++instance SG.Semigroup Symbols where+ (Symbols s1 t1) <> (Symbols s2 t2) =+ Symbols (s1 <> s2) (t1 <> t2)++instance Monoid Symbols where+ mempty = Symbols mempty mempty+ mappend = (<>)++valSyms :: Lens Symbols (Set.Set (SymValueInfo OrigName))+valSyms = lens (\(Symbols vs _) -> vs) (\vs (Symbols _ ts) -> Symbols vs ts)++tySyms :: Lens Symbols (Set.Set (SymTypeInfo OrigName))+tySyms = lens (\(Symbols _ ts) -> ts) (\ts (Symbols vs _) -> Symbols vs ts)++mkVal :: SymValueInfo OrigName -> Symbols+mkVal i = Symbols (Set.singleton i) mempty++mkTy :: SymTypeInfo OrigName -> Symbols+mkTy i = Symbols mempty (Set.singleton i)++-- | String representing an unqualified entity name+type NameS = String+-- | String representing a module name+type ModuleNameS = String++-- | Possibly qualified name. If the name is not qualified,+-- 'ModuleNameS' is the empty string.+data GName = GName+ { gModule :: ModuleNameS+ , gName :: NameS+ }+ deriving (Eq, Ord, Show, Data, Typeable)++-- | Display a 'GName'+ppGName :: GName -> String+ppGName (GName mod name) = printf "%s.%s" mod name++-- # if !MIN_VERSION_Cabal(1,17,0)+-- deriving instance Typeable PackageIdentifier+-- deriving instance Data PackageIdentifier+-- deriving instance Typeable PackageName+-- deriving instance Data PackageName+-- deriving instance Data Version+-- # endif++-- | Qualified name, where 'ModuleNameS' points to the module where the+-- name was originally defined. The module part is never empty.+--+-- Also contains name and version of the package where it was defined. If+-- it's 'Nothing', then the entity is defined in the \"current\" package.+data OrigName = OrigName+ { origGName :: GName+ }+ deriving (Eq, Ord, Show, Data, Typeable)++-- | Display an 'OrigName'+ppOrigName :: OrigName -> String+ppOrigName (OrigName gname) = ppGName gname++-- | A pair of the name information and original annotation. Used as an+-- annotation type for AST.+data Scoped l = Scoped (NameInfo l) l+ deriving (Functor, Foldable, Traversable, Show, Typeable, Data, Eq, Ord)++data NameInfo l+ = GlobalValue (SymValueInfo OrigName) -- ^ global value+ | GlobalType (SymTypeInfo OrigName) -- ^ global type+ | LocalValue SrcLoc -- ^ local value, and location where it is bound+ | TypeVar SrcLoc -- ^ type variable, and location where it is bound+ | ValueBinder -- ^ here the value name is bound+ | TypeBinder -- ^ here the type name is defined+ | Import Global.Table+ -- ^ @import@ declaration, and the table of symbols that it+ -- introduces+ | ImportPart Symbols+ -- ^ part of an @import@ declaration+ | Export Symbols+ -- ^ @export@ declaration, and the symbols it exports+ | RecPatWildcard [OrigName]+ -- ^ wildcard in a record pattern. The list contains resolved names+ -- of the fields that are brought in scope by this pattern.+ | RecExpWildcard [(OrigName, NameInfo l)]+ -- ^ wildcard in a record construction expression. The list contains+ -- resolved names of the fields and information about values+ -- assigned to those fields.+ | None+ -- ^ no annotation+ | ScopeError (Error l)+ -- ^ scope error+ deriving (Functor, Foldable, Traversable, Show, Typeable, Data, Eq, Ord)++data Error l+ = ENotInScope (QName l) -- FIXME annotate with namespace (types/values)+ -- ^ name is not in scope+ | EAmbiguous (QName l) [OrigName]+ -- ^ name is ambiguous+ | ETypeAsClass (QName l)+ -- ^ type is used where a type class is expected+ | EClassAsType (QName l)+ -- ^ type class is used where a type is expected+ | ENotExported+ (Maybe (Name l)) --+ (Name l) --+ (ModuleName l)+ -- ^ Attempt to explicitly import a name which is not exported (or,+ -- possibly, does not even exist). For example:+ --+ -- >import Prelude(Bool(Right))+ --+ -- The fields are:+ --+ -- 1. optional parent in the import list, e.g. @Bool@ in @Bool(Right)@+ --+ -- 2. the name which is not exported+ --+ -- 3. the module which does not export the name+ | EModNotFound (ModuleName l)+ -- ^ module not found+ | EInternal String+ -- ^ internal error+ deriving (Data, Typeable, Show, Functor, Foldable, Traversable, Eq, Ord)++-- | Display an error.+--+-- Note: can span multiple lines; the trailing newline is included.+ppError :: SrcInfo l => Error l -> String+ppError e =+ case e of+ ENotInScope qn -> printf "%s: not in scope: %s\n"+ (ppLoc qn)+ (prettyPrint qn)+ EAmbiguous qn names ->+ printf "%s: ambiguous name %s\nIt may refer to:\n"+ (ppLoc qn)+ (prettyPrint qn)+ +++ F.concat (map (printf " %s\n" . ppOrigName) names)+ ETypeAsClass qn ->+ printf "%s: type %s is used where a class is expected\n"+ (ppLoc qn)+ (prettyPrint qn)+ EClassAsType qn ->+ printf "%s: class %s is used where a type is expected\n"+ (ppLoc qn)+ (prettyPrint qn)+ ENotExported _mbParent name mod ->+ printf "%s: %s does not export %s\n"+ (ppLoc name)+ (prettyPrint mod)+ (prettyPrint name)+ -- FIXME: make use of mbParent+ EModNotFound mod ->+ printf "%s: module not found: %s\n"+ (ppLoc mod)+ (prettyPrint mod)+ EInternal s -> printf "Internal error: %s\n" s++ where+ ppLoc :: (Annotated a, SrcInfo l) => a l -> String+ ppLoc = prettyPrint . getPointLoc . ann++instance (SrcInfo l) => SrcInfo (Scoped l) where+ toSrcInfo l1 ss l2 = Scoped None $ toSrcInfo l1 ss l2+ fromSrcInfo = Scoped None . fromSrcInfo+ getPointLoc = getPointLoc . sLoc+ fileName = fileName . sLoc+ startLine = startLine . sLoc+ startColumn = startColumn . sLoc++sLoc :: Scoped l -> l+sLoc (Scoped _ l) = l
src/main/Main.hs view
@@ -1,69 +1,75 @@-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE RecordWildCards #-} -- | Main compiler executable. module Main where import Fay-import Fay.Compiler.Config-import Paths_fay (version)+import Paths_fay (version) -import qualified Control.Exception as E+import Control.Applicative ((<|>), (<$>), (<*>))+import qualified Control.Exception as E import Control.Monad-import Data.Default-import Data.List.Split (wordsBy)+import Control.Monad.Reader+import Data.List.Split (wordsBy) import Data.Maybe-import Data.Version (showVersion)-import Options.Applicative-import Options.Applicative.Types-import System.Environment+import Data.Monoid ((<>))+import Data.Version (showVersion)+import Options.Applicative (Mod, OptionFields, Parser, argument, execParser, fullDesc, header, help,+ helper, info, long, many, metavar, option, optional, short, strOption,+ switch, value)+import Options.Applicative.Types (ReadM (ReadM)) -- | 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- , 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- , optSourceMap :: Bool- , optFiles :: [String]+ { optBasePath :: Maybe FilePath+ , optGClosure :: Bool+ , optFlattenApps :: Bool+ , optHTMLJSLibs :: [String]+ , optHTMLWrapper :: Bool+ , optInclude :: [String]+ , optLibrary :: Bool+ , optNoGHC :: Bool+ , optNoOptimizeNewtypes :: Bool+ , optNoRTS :: Bool+ , optNoStdlib :: Bool+ , optOptimize :: Bool+ , optOutput :: Maybe String+ , optPackages :: [String]+ , optPackageConf :: Maybe String+ , optPrettyAll :: Bool+ , optPretty :: Bool+ , optPrettyOperators :: Bool+ , optPrettyThunks :: Bool+ , optPrintRuntime :: Bool+ , optRuntimePath :: Maybe FilePath+ , optSourceMap :: Bool+ , optStdlibOnly :: Bool+ , optStdout :: Bool+ , optStrict :: [String]+ , optTypecheckOnly :: Bool+ , optVersion :: Bool+ , optWall :: Bool+ , optShowGhcCalls :: Bool+ , optTypeScript :: Bool+ , optFiles :: [String] } -- | Main entry point. main :: IO () main = do- packageConf <- fmap (lookup "HASKELL_PACKAGE_SANDBOX") getEnvironment- opts <- execParser parser+ config' <- defaultConfigWithSandbox+ opts <- execParser parser let config = addConfigDirectoryIncludePaths ("." : optInclude opts) $- addConfigPackages (optPackages opts) $ def+ addConfigPackages (optPackages opts) $ config' { configOptimize = optOptimize opts , configFlattenApps = optFlattenApps opts- , configPrettyPrint = optPretty opts+ , configPrettyPrint = optPretty opts || optPrettyAll opts , configLibrary = optLibrary opts , configHtmlWrapper = optHTMLWrapper opts , configHtmlJSLibs = optHTMLJSLibs opts , configTypecheck = not $ optNoGHC opts , configWall = optWall opts , configGClosure = optGClosure opts- , configPackageConf = optPackageConf opts <|> packageConf+ , configPackageConf = optPackageConf opts <|> configPackageConf config' , configExportRuntime = not (optNoRTS opts) , configExportStdlib = not (optNoStdlib opts) , configExportStdlibOnly = optStdlibOnly opts@@ -72,11 +78,16 @@ , configTypecheckOnly = optTypecheckOnly opts , configRuntimePath = optRuntimePath opts , configSourceMap = optSourceMap opts+ , configOptimizeNewtypes = not $ optNoOptimizeNewtypes opts+ , configPrettyThunks = optPrettyThunks opts || optPrettyAll opts+ , configPrettyOperators = optPrettyOperators opts || optPrettyAll opts+ , configShowGhcCalls = optShowGhcCalls opts+ , configTypeScript = optTypeScript opts } if optVersion opts then runCommandVersion else if optPrintRuntime opts- then getConfigRuntime config >>= readFile >>= putStr+ then readConfigRuntime config >>= putStr else do void $ incompatible htmlAndStdout opts "Html wrapping and stdout are incompatible" case optFiles opts of@@ -88,41 +99,49 @@ parser = info (helper <*> options) (fullDesc <> header helpTxt) outPutFile :: FayCompilerOptions -> String -> FilePath- outPutFile opts file = fromMaybe (toJsName file) $ optOutput opts+ outPutFile opts file = fromMaybe (if optTypeScript opts then toTsName file else toJsName file) $ optOutput opts -- | All Fay's command-line options. options :: Parser FayCompilerOptions options = FayCompilerOptions- <$> switch (long "library" <> help "Don't automatically call main in generated JavaScript")+ <$> 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")+ <*> switch (long "closure" <> help "Provide help with Google Closure") <*> switch (long "flatten-apps" <> help "flatten function applicaton")- <*> switch (long "html-wrapper" <> help "Create an html file that loads the javascript") <*> strsOption (long "html-js-lib" <> metavar "file1[, ..]" <> help "javascript files to add to <head> if using option html-wrapper")+ <*> switch (long "html-wrapper" <> help "Create an html file that loads the javascript") <*> strsOption (long "include" <> metavar "dir1[, ..]" <> help "additional directories for include")- <*> strsOption (long "package" <> metavar "package[, ..]"- <> help "packages to use for compilation")- <*> switch (long "Wall" <> help "Typecheck with -Wall")+ <*> switch (long "library" <> help "Don't automatically call main in generated JavaScript") <*> switch (long "no-ghc" <> help "Don't typecheck, specify when not working with files")- <*> switch (long "stdout" <> short 's' <> help "Output to stdout")- <*> 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")- <*> 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"))+ <*> switch (long "no-optimized-newtypes" <> help "Remove optimizations for newtypes, treating them as normal data types") <*> switch (long "no-rts" <> short 'r' <> help "Don't export the RTS") <*> switch (long "no-stdlib" <> help "Don't generate code for the Prelude/FFI")+ <*> switch (long "optimize" <> short 'O' <> help "Apply optimizations to generated code")+ <*> optional (strOption (long "output" <> short 'o' <> metavar "file" <> help "Output to specified file"))+ <*> strsOption (long "package" <> metavar "package[, ..]"+ <> help "packages to use for compilation")+ <*> optional (strOption (long "package-conf" <> help "Specify the Cabal package config file"))+ <*> switch (long "pretty-all" <> help "Pretty print, pretty operators and pretty thunks")+ <*> switch (long "pretty" <> short 'p' <> help "Pretty print the output")+ <*> switch (long "pretty-operators" <> help "Use pretty operators")+ <*> switch (long "pretty-thunks" <> help "Use pretty thunk names") <*> switch (long "print-runtime" <> help "Print the runtime JS source to stdout")+ <*> optional (strOption $ long "runtime-path" <> help "Custom path to the runtime so you don't have to reinstall fay when modifying it")+ <*> switch (long "sourcemap" <> help "Produce a source map in <outfile>.map") <*> switch (long "stdlib" <> help "Only output the stdlib")- <*> 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")+ <*> switch (long "stdout" <> short 's' <> help "Output to stdout") <*> 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")- <*> switch (long "sourcemap" <> help "Produce a source map in <outfile>.map")- <*> arguments Just (metavar "<hs-file>...")- where strsOption m = nullOption (m <> reader (ReadM . Right . wordsBy (== ',')) <> value [])+ <*> switch (long "version" <> help "Output version number")+ <*> switch (long "Wall" <> help "Typecheck with -Wall")+ <*> switch (long "show-ghc-calls" <> help "Print commands sent to ghc")+ <*> switch (long "ts" <> help "Output TypeScript instead of JavaScript")+ <*> many (argument (ReadM ask) (metavar "<hs-file>..."))+ where+ strsOption :: Mod OptionFields [String] -> Parser [String]+ strsOption m = option (ReadM . fmap (wordsBy (== ',')) $ ask) (m <> value []) -- | Make incompatible options. incompatible :: Monad m@@ -134,10 +153,9 @@ -- | The basic help text. helpTxt :: String-helpTxt = concat- ["fay -- The fay compiler from (a proper subset of) Haskell to Javascript\n\n"- ," fay <hs-file>... processes each .hs file"- ]+helpTxt+ = "fay -- The fay compiler from (a proper subset of) Haskell to Javascript\n\n"+ ++ " fay <hs-file>... processes each .hs file" -- | Print the command version. runCommandVersion :: IO ()
src/tests/Test/CommandLine.hs view
@@ -2,20 +2,17 @@ module Test.CommandLine (tests) where -import Fay.System.Process.Extra+import Fay.Compiler.Prelude+import Test.Util -import Control.Applicative-import Data.Maybe import System.Directory import System.Environment import System.FilePath-import Test.Framework-import Test.Framework.Providers.HUnit-import Test.Framework.TH-import Test.HUnit (Assertion, assertBool)-import Test.Util+import Test.Tasty+import Test.Tasty.HUnit+import Test.Tasty.TH -tests :: Test+tests :: TestTree tests = $testGroupGenerator compileFile :: [String] -> IO (Either String String)@@ -42,4 +39,6 @@ whatAGreatFramework <- fmap (lookup "HASKELL_PACKAGE_SANDBOX") getEnvironment res <- compileFile (["--include=tests", "tests/RecordImport_Import.hs","--no-ghc"] ++ ["--package-conf=" ++ packageConf | Just packageConf <- [whatAGreatFramework] ])- assertBool (fromLeft res) (isRight res)+ case res of+ Left e -> assertFailure $ "Compilation failed: " ++ e+ Right _ -> assertBool "impossible" True
src/tests/Test/Compile.hs view
@@ -1,39 +1,31 @@+{-# LANGUAGE CPP #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE TemplateHaskell #-} -module Test.Compile (tests) where+module Test.Compile (tests,runScriptFile) where import Fay-import Fay.Compiler.Config-import Fay.System.Process.Extra-import Fay.Types ()+import Fay.Compiler.Prelude -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+import Language.Haskell.Exts+import Test.Tasty+import Test.Tasty.HUnit+import Test.Tasty.TH -tests :: Test+tests :: TestTree tests = $testGroupGenerator case_imports :: Assertion case_imports = do- whatAGreatFramework <- fmap (lookup "HASKELL_PACKAGE_SANDBOX") getEnvironment- res <- compileFile defConf { configPackageConf = whatAGreatFramework } fp+ cfg <- defConf+ res <- compileFile cfg fp assertBool "Could not compile file with imports" (isRight res) case_importedList :: Assertion case_importedList = do- whatAGreatFramework <- fmap (lookup "HASKELL_PACKAGE_SANDBOX") getEnvironment- res <- compileFileWithState defConf { configPackageConf = whatAGreatFramework } fp+ cfg <- defConf+ res <- compileFileWithState cfg fp case res of Left err -> error (show err) Right (_,_,r) -> assertBool "RecordImport_Export was not added to stateImported" .@@ -44,8 +36,8 @@ case_stateRecordTypes :: Assertion case_stateRecordTypes = do- whatAGreatFramework <- fmap (lookup "HASKELL_PACKAGE_SANDBOX") getEnvironment- res <- compileFileWithState defConf { configPackageConf = whatAGreatFramework } "tests/Compile/Records.hs"+ cfg <- defConf+ res <- compileFileWithState cfg "tests/Compile/Records.hs" case res of Left err -> error (show err) Right (_,_,r) ->@@ -54,12 +46,12 @@ [ ("Compile.Records.T", ["Compile.Records.:+"]) , ("Compile.Records.R", ["Compile.Records.R","Compile.Records.S"]) ]- (stateRecordTypes r)+ (filter (isFromMod "Compile.Records") $ stateRecordTypes r) case_importStateRecordTypes :: Assertion case_importStateRecordTypes = do- whatAGreatFramework <- fmap (lookup "HASKELL_PACKAGE_SANDBOX") getEnvironment- res <- compileFileWithState defConf { configPackageConf = whatAGreatFramework } "tests/Compile/ImportRecords.hs"+ cfg <- defConf+ res <- compileFileWithState cfg "tests/Compile/ImportRecords.hs" case res of Left err -> error (show err) Right (_,_,r) ->@@ -68,40 +60,91 @@ [ ("Compile.Records.T",["Compile.Records.:+"]) , ("Compile.Records.R",["Compile.Records.R", "Compile.Records.S"]) ]- (stateRecordTypes r)+ (filter (isFromMod "Compile.Records") $ stateRecordTypes r) +isFromMod :: String -> (QName (),[QName ()]) -> Bool+isFromMod modName = (==) modName . getModuleName . fst+ where+ getModuleName (Qual _ (ModuleName _ n) _) = n+ getModuleName x = error $ "getModuleName: expected qualified name: " ++ show x+ case_typecheckCPP :: Assertion case_typecheckCPP = do- whatAGreatFramework <- fmap (lookup "HASKELL_PACKAGE_SANDBOX") getEnvironment- res <- compileFile defConf { configPackageConf = whatAGreatFramework, configTypecheck = True, configFilePath = Just "tests/Compile/CPPTypecheck.hs" } "tests/Compile/CPPTypecheck.hs"+ cfg <- defConf+ res <- compileFile cfg { configTypecheck = True, configFilePath = Just "tests/Compile/CPPTypecheck.hs" } "tests/Compile/CPPTypecheck.hs" either (assertFailure . show) (const $ return ()) res case_cppMultiLineStrings :: Assertion case_cppMultiLineStrings = do- whatAGreatFramework <- fmap (lookup "HASKELL_PACKAGE_SANDBOX") getEnvironment- res <- compileFile defConf { configPackageConf = whatAGreatFramework, configTypecheck = True, configFilePath = Just "tests/Compile/CPPMultiLineStrings.hs" } "tests/Compile/CPPMultiLineStrings.hs"+ cfg <- defConf+ res <- compileFile cfg { 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"] ""+ cfg <- defConf+ res <- compileFile cfg { configTypecheck = True, configFilePath = Just "tests/Compile/StrictWrapper.hs", configStrict = ["StrictWrapper"] } "tests/Compile/StrictWrapper.hs"+ let isTs = configTypeScript cfg+ suffix = if isTs then ".ts" else ".js"+ (\a b -> either a b res) (assertFailure . show) $ \js -> do+ writeFile ("tests/Compile/StrictWrapper" ++ suffix) js+ (err, out) <- either id id <$> runScriptFile isTs ("tests/Compile/StrictWrapper" ++ suffix) when (err /= "") $ assertFailure err expected <- readFile "tests/Compile/StrictWrapper.res" assertEqual "strictWrapper node stdout" expected out -defConf :: CompileConfig-defConf = addConfigDirectoryIncludePaths ["tests/"]- $ def { configTypecheck = False }+assertPretty :: Config -> String -> Assertion+assertPretty cfg flagName = do+ let isTs = configTypeScript cfg+ suffix = if isTs then ".ts" else ".js"+ res <- compileFile cfg $ "tests/Compile/" ++ flagName ++ ".hs"+ case res of+ Left l -> assertFailure $ "Should compile, but failed with: " ++ show l+ Right js -> do+ writeFile ("tests/Compile/" ++ flagName ++ suffix) js+ (err, out) <- either id id <$> runScriptFile isTs+ ("tests/Compile/" ++ flagName ++ suffix)+ when (err /= "") $ assertFailure err+ expected <- readFile $ "tests/Compile/" ++ flagName ++ ".res"+ assertEqual (flagName ++ " node stdout") expected out +case_pretty :: Assertion+case_pretty = do+ cfg <- defConf+ assertPretty cfg { configPrettyPrint = True } "pretty"++case_prettyThunks :: Assertion+case_prettyThunks = do+ cfg <- defConf+ assertPretty cfg { configPrettyThunks = True } "prettyThunks"++case_prettyOperators :: Assertion+case_prettyOperators = do+ cfg <- defConf+ assertPretty cfg { configPrettyOperators = True } "prettyOperators"+ case_charEnum :: Assertion case_charEnum = do- whatAGreatFramework <- fmap (lookup "HASKELL_PACKAGE_SANDBOX") getEnvironment- res <- compileFile defConf { configPackageConf = whatAGreatFramework, configTypecheck = True, configFilePath = Just "tests/Compile/EnumChar.hs" } "tests/Compile/EnumChar.hs"+ cfg <- defConf+ res <- compileFile cfg { configTypecheck = True, configFilePath = Just "tests/Compile/EnumChar.hs" } "tests/Compile/EnumChar.hs" case res of Left UnsupportedEnum{} -> return () Left l -> assertFailure $ "Should have failed with UnsupportedEnum, but failed with: " ++ show l- Right _ -> assertFailure $ "Should have failed with UnsupportedEnum, but compiled"+ Right _ -> assertFailure "Should have failed with UnsupportedEnum, but compiled"++defConf :: IO Config+defConf = do+ cfg <- defaultConfigWithSandbox+ return $ addConfigDirectoryIncludePaths ["tests/"] cfg { configTypecheck = False }++-- | Run a JS or TS file.+runScriptFile :: Bool -- ^ If a file-format is TypeScript, this is True.+ -> String -- ^ A name of script file+ -> IO (Either (String,String) (String,String))+runScriptFile True file = do+ tsc_ret <- readAllFromProcess "tsc" [file] ""+ case tsc_ret of+ Left _ -> return tsc_ret+ Right _ -> readAllFromProcess "node" [(reverse (drop 3 (reverse file))) ++ ".js" ] ""++runScriptFile False file = readAllFromProcess "node" [file] ""
src/tests/Test/Convert.hs view
@@ -3,19 +3,18 @@ module Test.Convert (tests) where +import Fay.Convert+ import qualified Data.Aeson.Parser as Aeson import Data.Attoparsec.ByteString import qualified Data.ByteString as Bytes import qualified Data.ByteString.UTF8 as UTF8 import Data.Data-import Data.Ratio import Data.Text (Text, pack)-import Fay.Convert-import Test.Framework-import Test.Framework.Providers.HUnit-import Test.HUnit (assertEqual)+import Test.Tasty+import Test.Tasty.HUnit -tests :: Test+tests :: TestTree tests = testGroup "Test.Convert" [reading, showing] where reading = testGroup "reading" $ flip map readTests $ \(ReadTest value) ->@@ -35,7 +34,7 @@ -- Test cases -- | A test.-data Testcase = forall x. Show x => Testcase x Bytes.ByteString+data Testcase = forall x. (Show x,Data x) => Testcase x Bytes.ByteString -- | A read test. data ReadTest = forall x. (Data x,Show x,Eq x,Read x) => ReadTest x@@ -69,9 +68,10 @@ showTests = -- Fundamental data types [(1 :: Int) → "1"+ ,(1 :: Float) → "1.0"+ ,(1/2 :: Float) → "0.5" ,(1 :: Double) → "1.0" ,(1/2 :: Double) → "0.5"- ,(1%2 :: Rational) → "0.5" ,([1,2] :: [Int]) → "[1,2]" ,((1,2) :: (Int,Int)) → "[1,2]" ,"abc" → "\"abc\""@@ -122,7 +122,7 @@ -- | Labelled record. data LabelledRecord = LabelledRecord { barInt :: Int, barDouble :: Double }- | LabelledRecord2 { bar :: Int, bob :: Double }+ | LabelledRecord2 { bar :: Int, bob :: Float } deriving (Show,Data,Typeable,Read,Eq) -- | Order matters in unlabelled constructors.
+ src/tests/Test/Desugar.hs view
@@ -0,0 +1,167 @@+{-# LANGUAGE ViewPatterns #-}+module Test.Desugar+ ( tests+ , devTest+ , parseE+ , parseM+ , des+ ) where++import Fay.Compiler.Prelude++import Fay.Compiler.Desugar+import Fay.Compiler.Parse (parseFay)+import Fay.Types.CompileError (CompileError (..))++import Language.Haskell.Exts hiding (name)+import Test.Tasty+import Test.Tasty.HUnit+-- import Text.Groom++tests :: TestTree+tests = testGroup "desugar" $ map (\(T k a b) -> testCase k $ doDesugar k a b) testDeclarations++data DesugarTest = T String String String++testDeclarations :: [DesugarTest]+testDeclarations =+ [T "LambdaCase"+ "import Prelude; f = \\gen0 -> case gen0 of { _ -> 2 }"+ "import Prelude; f = \\case { _ -> 2 }"+ ,T "MultiWayIf"+ "import Prelude; f = case () of { _ | True -> 1 | False -> 2 }"+ "import Prelude; f = if | True -> 1 | False -> 2"+ ,T "TupleCon"+ "import Prelude; f = \\gen0 gen1 gen2 -> (gen0, gen1, gen2)"+ "import Prelude; f = (,,)"+ ,T "Do"+ "import Prelude; f = (>>=) x (\\gen0 -> (>>) y z)"+ "import Prelude; f = do { gen0 <- x; y; z }"+ ,T "TupleSection"+ "import Prelude; f = \\gen0 gen1 -> (gen0,2,gen1)"+ "import Prelude; f = (,2,)"+ ,T "ImplicitPrelude1" -- Add missing Prelude import+ "import Prelude"+ ""+ ,T "ImplicitPrelude2" -- Keep existing Prelude import+ "import Prelude ()"+ "import Prelude ()"+ ,T "ImplicitPrelude3" -- Keep existing qualified import+ "import qualified Prelude"+ "import qualified Prelude"+ ,T "NoImplicitPrelude"+ "{-# LANGUAGE NoImplicitPrelude #-}"+ "{-# LANGUAGE NoImplicitPrelude #-}"+ ,T "OperatorSectionRight"+ "import Prelude; f = \\gen0 -> g gen0 x"+ "import Prelude; f = (`g` x)"+ ,T "OperatorSectionLeft"+ "import Prelude; f = \\gen0 -> g x gen0"+ "import Prelude; f = (x `g`)"+ ,T "InfixOpOp"+ "import Prelude; f = (+) x y"+ "import Prelude; f = x + y"+ ,T "InfixOpFun"+ "import Prelude; f = g x y"+ "import Prelude; f = x `g` y"+ ,T "InfixOpCons"+ "import Prelude; f = (:) x y"+ "import Prelude; f = x : y"+ ,T "ExpParen"+ "import Prelude; f = x y"+ "import Prelude; f = (x (y))"+ ,T "PatParen"+ "import Prelude; f x = y"+ "import Prelude; f (x) = y"+ ,T "PatInfixOp"+ "import Prelude; f ((:) x y) = z"+ "import Prelude; f (x : y) = z"+ ,T "PatFieldPun"+ "import Prelude; f R { x = x } = y"+ "import Prelude; f R { x } = y"+ ,T "PatField"+ "import Prelude; f = R { x = x }"+ "import Prelude; f = R { x }"+ ,T "FFITopLevel"+ "import Prelude; f :: Int; f = ffi \"1\" :: Int"+ "import Prelude; f :: Int; f = ffi \"1\""+ ,T "FFIWhere"+ "import Prelude; f = () where x :: Int; x = ffi \"1\" :: Int"+ "import Prelude; f = () where x :: Int; x = ffi \"1\""+ ]++parseAndDesugar :: String -> String -> IO (Module SrcLoc, Either CompileError (Module SrcLoc))+parseAndDesugar name s =+ case parseFay "test" s :: ParseResult (Module SrcSpanInfo) of+ ParseFailed a b -> error $ show (name, a, b)+ ParseOk (fmap srcSpanInfoToSrcLoc -> m) -> do+ d <- desugar' "gen" noLoc m+ return (m, d)+ where+ srcSpanInfoToSrcLoc :: SrcSpanInfo -> SrcLoc+ srcSpanInfoToSrcLoc = (\ss -> SrcLoc { srcFilename = srcSpanFilename ss+ , srcLine = srcSpanStartLine ss+ , srcColumn = srcSpanStartColumn ss+ }) . srcInfoSpan+doDesugar :: String -> String -> String -> Assertion+doDesugar testName a b = do+ (originalExpected, desugaredExpected, desugared) <- parseAndDesugarAll testName a b+ assertEqual "identity" (unAnn originalExpected) (unAnn desugaredExpected)+ assertEqual "desugared" (unAnn originalExpected) (unAnn desugared )+ assertEqual "both" (unAnn desugared ) (unAnn desugaredExpected)++parseAndDesugarAll :: String -> String -> String -> IO (Module SrcLoc, Module SrcLoc, Module SrcLoc)+parseAndDesugarAll testName a b = do+ (originalExpected',Right desugaredExpected) <- parseAndDesugar (testName ++ " expected") a+ (_ ,Right desugared ) <- parseAndDesugar (testName ++ " input") b+ -- We need to desugar parens in the original module since we+ -- strip it away in desugaring but there isn't alawys a way to construct+ -- this paren directly from a source string+ let originalExpected = desugarPatParen . desugarExpParen $ originalExpected'+ return (originalExpected, desugaredExpected, desugared)++-- When developing:++devTest :: String -> IO ()+devTest nam = do+ let (T _ a b) = fromJust (find (\(T n _ _) -> n == nam) testDeclarations)+ (originalExpected, desugaredExpected, desugared) <- parseAndDesugarAll "" a b+ if unAnn desugared == unAnn desugaredExpected && unAnn desugared == unAnn originalExpected+ then putStrLn "OK"+ else do+ putStrLn "--- originalExpected"+ g $ unAnn originalExpected+ putStrLn "--- desugaredExpected"+ g $ unAnn desugaredExpected+ putStrLn "--- desugared"+ g $ unAnn desugared+ putStrLn "--- originalExpected"+ pretty originalExpected+ putStrLn "--- desugaredExpected"+ pretty desugaredExpected+ putStrLn "--- desugared"+ pretty desugared+ when (unAnn originalExpected /= unAnn desugaredExpected) $ putStrLn "originalExpected /= desugaredExpected"+ when (unAnn desugared /= unAnn desugaredExpected) $ putStrLn "desugared /= desugaredExpected"+ when (unAnn desugared /= unAnn originalExpected ) $ putStrLn "desugared /= originalExpected"+ when (unAnn desugaredExpected /= unAnn originalExpected ) $ putStrLn "desugaredExpected /= undesugared"++g :: Show a => a -> IO ()+g = print -- putStrLn . groom++unAnn :: Functor f => f a -> f ()+unAnn = void++pretty :: Module SrcLoc -> IO ()+pretty = putStrLn . prettyPrint++parseM :: String -> Module ()+parseM s = let ParseOk m = parseFay "module" s :: ParseResult (Module SrcSpanInfo) in unAnn m++parseE :: String -> Exp ()+parseE s = let ParseOk m = parseFay "exp" s :: ParseResult (Exp SrcSpanInfo) in unAnn m++des :: String -> IO ()+des s = do+ Right r <- desugar' "gen" () $ parseM s+ g r
src/tests/Test/Util.hs view
@@ -1,14 +1,14 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE NoImplicitPrelude #-} module Test.Util ( fayPath- , isRight- , fromLeft+ , getRecursiveContents ) where -import Fay.System.Process.Extra (readAllFromProcess)+import Fay.Compiler.Prelude -import Control.Applicative-import Prelude hiding (pred) import System.Directory+import System.FilePath -- Path to the fay executable, looks in cabal-dev, dist, PATH in that order. fayPath :: IO (Maybe FilePath)@@ -20,20 +20,27 @@ usingWhich = fmap (concat . lines . snd) . hush <$> readAllFromProcess "which" ["fay"] "" firstWhereM :: (Monad m) => (a -> m Bool) -> [a] -> m (Maybe a)-firstWhereM pred ins = case ins of+firstWhereM p ins = case ins of [] -> return Nothing- a:as -> pred a >>= \b ->+ a:as -> p a >>= \b -> if b then return (Just a)- else firstWhereM pred as+ else firstWhereM p as -- from the package `errors` hush :: Either a b -> Maybe b hush = either (const Nothing) Just -isRight :: Either a b -> Bool-isRight (Right _) = True-isRight (Left _) = False--fromLeft :: Either a b -> a-fromLeft (Left a) = a-fromLeft (Right _) = error "fromLeft got Right"+-- | Get all files in a folder and its subdirectories.+-- Taken from Real World Haskell+-- http://book.realworldhaskell.org/read/io-case-study-a-library-for-searching-the-filesystem.html+getRecursiveContents :: FilePath -> IO [FilePath]+getRecursiveContents topdir = do+ names <- getDirectoryContents topdir+ let properNames = filter (`notElem` [".", ".."]) names+ paths <- forM properNames $ \name -> do+ let path = topdir </> name+ isDirectory <- doesDirectoryExist path+ if isDirectory+ then getRecursiveContents path+ else return [path]+ return (concat paths)
src/tests/Tests.hs view
@@ -1,4 +1,5 @@-{-# LANGUAGE ViewPatterns #-}+{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE ViewPatterns #-} -- | Generate the web site/documentation for the Fay project. --@@ -7,66 +8,113 @@ module Main where +import Fay.Compiler.Prelude+ import Fay-import Fay.Compiler.Config-import Fay.System.Directory.Extra-import Fay.System.Process.Extra+import qualified Test.CommandLine as Cmd+import qualified Test.Compile as Compile+import qualified Test.Convert as Convert+import qualified Test.Desugar as Desugar+import Test.Util -import Control.Applicative-import Data.Char-import Data.Default-import Data.List-import Data.Maybe-import Data.Ord+import Data.Set (Set)+import qualified Data.Set as S import System.Directory import System.Environment import System.FilePath-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-import Test.HUnit (assertEqual, assertFailure)+import System.Random+import Test.Tasty+import Test.Tasty.HUnit -- | Main test runner. main :: IO () main = do sandbox <- fmap (lookup "HASKELL_PACKAGE_SANDBOX") getEnvironment- (packageConf,args) <- fmap (prefixed (=="-package-conf")) getArgs- let (basePath,args') = prefixed (=="-base-path") args- compiler <- makeCompilerTests (packageConf <|> sandbox) basePath- defaultMainWithArgs [Compile.tests, Cmd.tests, compiler, C.tests]- args'+ (packageConf,args) <- prefixed (== "-package-conf") <$> getArgs+ let (basePath,args') = prefixed (== "-base-path" ) args+ let (testCount, args'') = first (readMay =<<) $ prefixed (== "-random") args'+ let (isTs, args''') = case args'' of+ ("-ts":xs) -> (True,xs)+ _ -> (False,args'')+ (runtime,codegen) <- makeCompilerTests (packageConf <|> sandbox) basePath testCount isTs+ withArgs args''' $ defaultMain $ testGroup "Fay"+ [ Desugar.tests+ , Convert.tests+ , codegen+ , Cmd.tests+ , Compile.tests+ , runtime+ ] -- | Extract the element prefixed by the given element in the list. prefixed :: (a -> Bool) -> [a] -> (Maybe a,[a]) prefixed f (break f -> (x,y)) = (listToMaybe (drop 1 y),x ++ drop 2 y) -- | Make the case-by-case unit tests.-makeCompilerTests :: Maybe FilePath -> Maybe FilePath -> IO Test-makeCompilerTests packageConf basePath = do- files <- sortBy (comparing (map toLower)) . 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+makeCompilerTests :: Maybe FilePath -> Maybe FilePath -> Maybe Int -> Bool -> IO (TestTree,TestTree)+makeCompilerTests packageConf basePath rand isTs = do+ runtimeFiles' <- runtimeTestFiles+ runtimeFiles <- maybe (return runtimeFiles') (randomize runtimeFiles') rand+ codegenFiles <- codegenTestFiles+ return+ ( makeTestGroup "Runtime tests"+ runtimeFiles+ (\file -> do testFile packageConf basePath False isTs file+ testFile packageConf basePath True isTs file)+ , makeTestGroup "Codegen tests"+ codegenFiles+ (testCodegen packageConf basePath isTs))+ where+ makeTestGroup title files inner =+ testGroup title $ flip map files $ \file ->+ testCase file $ inner file+ runtimeTestFiles =+ filter (not . nonRuntime) <$> testFiles+ codegenTestFiles =+ filter (isInfixOf "/codegen/") <$> testFiles+ testFiles =+ sortBy (comparing (map toLower)) . filter (isSuffixOf ".hs") <$>+ getRecursiveContents "tests"+ nonRuntime x =+ any (`isInfixOf` x) ["/Compile/","/regressions/","/codegen/"]+ randomize :: [String] -> Int -> IO [String]+ randomize l i = do+ is <- randomizeAux S.empty i (0,length l - 1)+ return . map (l !!) $ S.toList is+ randomizeAux :: Set Int -> Int -> (Int,Int) -> IO (Set Int)+ randomizeAux s count b = do+ i <- randomRIO b+ let s' = S.insert i s+ if S.size s' == count+ then return s'+ else randomizeAux (S.insert i s) count b -testFile :: Maybe FilePath -> Maybe FilePath -> Bool -> String -> IO ()-testFile packageConf basePath opt file = do- let root = (reverse . drop 1 . dropWhile (/='.') . reverse) file- out = toJsName file- resf = root <.> "res"+fns :: Bool -> String -> (String, String, FilePath)+fns isTs file =+ ( root+ , if isTs then toTsName file else toJsName file+ , root <.> "res"+ )+ where+ root = reverse . drop 1 . dropWhile (/='.') . reverse $ file++testFile :: Maybe FilePath -> Maybe FilePath -> Bool -> Bool -> String -> IO ()+testFile packageConf basePath opt isTs file = do+ let (root, out, resf) = fns isTs file config =- addConfigDirectoryIncludePaths ["tests/"] $- def { configOptimize = opt- , configTypecheck = False- , configPackageConf = packageConf- , configBasePath = basePath- }+ addConfigDirectoryIncludePaths ["tests/"]+ defaultConfig+ { configOptimize = opt+ , configTypecheck = False+ , configPackageConf = packageConf+ , configBasePath = basePath+ , configTypeScript = isTs+ } resExists <- doesFileExist resf let partialName = root ++ "_partial.res" partialExists <- doesFileExist partialName compileFromTo config file (Just out)- result <- runJavaScriptFile out+ result <- Compile.runScriptFile isTs out if resExists then do output <- readFile resf assertEqual file output (either show snd result)@@ -79,6 +127,29 @@ Right (err,res) -> assertFailure $ "Did not fail:\n stdout: " ++ res ++ "\n\nstderr: " ++ err else assertEqual (file ++ ": Expected program to fail") True (either (const True) (const False) result) --- | Run a JS file.-runJavaScriptFile :: String -> IO (Either (String,String) (String,String))-runJavaScriptFile file = readAllFromProcess "node" [file] ""+-- | Test the generated code output for the given file with+-- optimizations turned on. This disables runtime generation and+-- things like that; it's only concerned with the core of the program.+testCodegen :: Maybe FilePath -> Maybe FilePath -> Bool -> String -> IO ()+testCodegen packageConf basePath isTs file = do+ let (_, out, resf) = fns isTs file+ config =+ addConfigDirectoryIncludePaths ["tests/codegen/"]+ defaultConfig+ { configOptimize = True+ , configTypecheck = False+ , configPackageConf = packageConf+ , configBasePath = basePath+ , configExportStdlib = False+ , configPrettyPrint = True+ , configLibrary = True+ , configExportRuntime = False+ , configTypeScript = isTs+ }+ compileFromTo config file (Just out)+ actual <- readStripped out+ expected <- readStripped $ resf ++ (if isTs then "_ts" else "")+ assertEqual file expected actual+ where readStripped =+ fmap (unlines . filter (not . null) . lines) . readFile+
+ tests/AllBaseModules.hs view
@@ -0,0 +1,28 @@+module AllBaseModules where++import Control.Exception ()+import Data.Char ()+import Data.Data ()+import Data.Defined ()+import Data.Either ()+import Data.Function ()+import Data.List ()+import Data.LocalStorage ()+import Data.Maybe ()+import Data.MutMap ()+import Data.MutMap.Internal ()+import Data.Mutex ()+import Data.Nullable ()+import Data.Ord ()+import Data.Ratio ()+import Data.Text ()+import Data.Time ()+import Data.Var ()+import Debug.Trace ()+import FFI+import Fay.Unsafe ()+import Prelude+import Unsafe.Coerce ()++main :: Fay ()+main = putStrLn "ok"
+ tests/AllBaseModules.res view
@@ -0,0 +1,1 @@+ok
tests/AutomaticList.hs view
@@ -1,6 +1,5 @@ module AutomaticList where -import Prelude import FFI main :: Fay ()
tests/Bool.hs view
@@ -1,4 +1,2 @@-import Prelude- main :: Fay () main = print True
tests/CPP.hs view
@@ -1,4 +1,6 @@-{-# LANGUAGE CPP #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE NoImplicitPrelude #-}+ #ifdef FAY import Prelude #else
tests/Char.hs view
@@ -1,10 +1,38 @@ module Char where -import Prelude import FFI +import Data.Char+ main :: Fay () main = do print 'a' putStrLn ('a' : "bc") print (head "abc")+ print (ord 'a')+ print (chr 97)+ print (chr (ord 'a'))+ print (ord (chr 97))+ print (isAscii 'a')+ print (isAscii (chr 128))+ print (isLatin1 (chr 255))+ print (isLatin1 (chr 256))+ print (toUpper 'a')+ print (toLower 'A')+ print (toLower (toUpper 'a'))+ print (isAsciiLower 'a')+ print (isAsciiLower 'A')+ print (isAsciiUpper 'Z')+ print (isAsciiUpper 'z')+ print (isAsciiUpper (toUpper 'a'))+ print (isAsciiUpper (toLower 'A'))+ print (isAsciiLower (toLower 'Z'))+ print (isAsciiLower (toUpper 'z'))+ print (all isDigit "0123456789")+ print (isDigit 'a')+ print (all isOctDigit "01234567")+ print (any isOctDigit ['8', '9'])+ print (all isHexDigit "0123456789ABCDEFabcdef")+ print (any isHexDigit "ghijklmnopqrstuvwxyzGHIJKLMNOPQRSTUVWXYZ")+ print (all isSpace [' ', '\t', '\n', '\r', '\f', '\v', '\xa0', '\x3000'])+
tests/Char.res view
@@ -1,3 +1,29 @@ a abc a+97+a+a+97+true+false+true+false+A+a+a+true+false+true+false+true+false+true+false+true+false+true+false+true+false+true
tests/Compile/StrictWrapper.hs view
@@ -1,4 +1,8 @@-module StrictWrapper (f,g,h,r,clog) where+module StrictWrapper (+ f, g, h, r, clog, logInlineOnly, logSeparateOnly, logBoth,+ sumInt, sumIntWrapped, zipWithPlus, zipWithPlusWrapped,+ sumPair, sumPairWrapped, zipPairs, zipPairsWrapped+) where import FFI import Prelude@@ -20,6 +24,45 @@ clog :: a -> Fay () clog = ffi "console.log(%1)" +-- FFI Expressions+logInlineOnly = ffi "console.log(%1)" :: a -> Fay ()++logSeparateOnly :: a -> Fay ()+logSeparateOnly = ffi "console.log(%1)"++logBoth :: a -> Fay ()+logBoth = ffi "console.log(%1)" :: a -> Fay ()++-- lists+sumInt :: [Int] -> Int+sumInt xs = sum xs++zipWithPlus :: [Int] -> [Int] -> [Int]+zipWithPlus xs ys = zipWith (+) xs ys++data IntList = IntList { list :: [Int] }++sumIntWrapped :: IntList -> Int+sumIntWrapped (IntList xs) = sumInt xs++zipWithPlusWrapped :: IntList -> IntList -> IntList+zipWithPlusWrapped (IntList x) (IntList y) = IntList $ zipWithPlus x y++-- tuples+sumPair :: (Int, Int) -> Int+sumPair (x,y) = x + y++zipPairs :: (Int, Int) -> (Int, Int) -> (Int, Int)+zipPairs (x1,y1) (x2,y2) = (x1 + x2, y1 + y2)++data IntPair = IntPair { pair :: (Int, Int) }++sumPairWrapped :: IntPair -> Int+sumPairWrapped (IntPair x) = sumPair x++zipPairsWrapped :: IntPair -> IntPair -> IntPair+zipPairsWrapped (IntPair x) (IntPair y) = IntPair $ zipPairs x y+ -- You should probably not use the strict wrapper from Fay, this is just for the sake of the test. main :: Fay () main = do@@ -28,3 +71,18 @@ ffi "console.log(Strict.StrictWrapper.h({instance:'R',i:1}))" :: Fay () ffi "console.log(Strict.StrictWrapper.r)" :: Fay () ffi "Strict.StrictWrapper.clog(123)" :: Fay ()+ ffi "Strict.StrictWrapper.logInlineOnly('inlineOnly')" :: Fay ()+ ffi "Strict.StrictWrapper.logSeparateOnly('separateOnly')" :: Fay ()+ ffi "Strict.StrictWrapper.logBoth('both')" :: Fay ()+ + -- lists+ (ffi "console.log(Strict.StrictWrapper.sumInt(%1))" :: [Int] -> Fay ()) [1, 2, 3]+ (ffi "console.log(Strict.StrictWrapper.sumIntWrapped(%1))" :: IntList -> Fay ()) (IntList [1, 2, 3])+ (ffi "console.log(Strict.StrictWrapper.zipWithPlus(%1, %2))" :: [Int] -> [Int] -> Fay ()) [1, 2, 3] [1, 2, 3]+ (ffi "console.log(Strict.StrictWrapper.zipWithPlusWrapped(%1, %2))" :: IntList -> IntList -> Fay ()) (IntList [1, 2, 3]) (IntList [1, 2, 3])+ + -- tuples+ (ffi "console.log(Strict.StrictWrapper.sumPair(%1))" :: (Int, Int) -> Fay ()) (1, 3)+ (ffi "console.log(Strict.StrictWrapper.sumPairWrapped(%1))" :: IntPair -> Fay ()) (IntPair (1, 3))+ (ffi "console.log(Strict.StrictWrapper.zipPairs(%1, %2))" :: (Int, Int) -> (Int, Int) -> Fay ()) (1, 3) (1, 3)+ (ffi "console.log(Strict.StrictWrapper.zipPairsWrapped(%1, %2))" :: IntPair -> IntPair -> Fay ()) (IntPair (1, 3)) (IntPair (1, 3))
tests/Compile/StrictWrapper.res view
@@ -3,3 +3,14 @@ { instance: 'R', i: 2 } { instance: 'R', i: 2 } 123+inlineOnly+separateOnly+both+6+6+[ 2, 4, 6 ]+{ instance: 'IntList', list: [ 2, 4, 6 ] }+4+4+[ 2, 6 ]+{ instance: 'IntPair', pair: [ 2, 6 ] }
+ tests/Compile/pretty.hs view
@@ -0,0 +1,9 @@+module Pretty where++import Prelude++main = do+ let n = 3::Int+ print . length' 0 . takeWhile (<=n) $ [0..]+ putStrLn $ if (n + 5) ^ 2 == 64 && n*8 == 26 then "T" else "F"+ forM [1..5] $ \k -> putStrLn $ if odd k then "odd" else "even"
+ tests/Compile/pretty.res view
@@ -0,0 +1,7 @@+4+F+odd+even+odd+even+odd
+ tests/Compile/prettyOperators.hs view
@@ -0,0 +1,9 @@+module Pretty where++import Prelude++main = do+ let n = 3::Int+ print . length' 0 . takeWhile (<=n) $ [0..]+ putStrLn $ if (n + 5) ^ 2 == 64 && n*8 == 26 then "T" else "F"+ forM [1..5] $ \k -> putStrLn $ if odd k then "odd" else "even"
+ tests/Compile/prettyOperators.res view
@@ -0,0 +1,7 @@+4+F+odd+even+odd+even+odd
+ tests/Compile/prettyThunks.hs view
@@ -0,0 +1,9 @@+module Pretty where++import Prelude++main = do+ let n = 3::Int+ print . length' 0 . takeWhile (<=n) $ [0..]+ putStrLn $ if (n + 5) ^ 2 == 64 && n*8 == 26 then "T" else "F"+ forM [1..5] $ \k -> putStrLn $ if odd k then "odd" else "even"
+ tests/Compile/prettyThunks.res view
@@ -0,0 +1,7 @@+4+F+odd+even+odd+even+odd
tests/Defined.hs view
@@ -1,5 +1,4 @@ import FFI-import Prelude data R = R (Defined Double)
+ tests/DesugarFFI.hs view
@@ -0,0 +1,28 @@+module DesugarFFI where++import Prelude+import FFI++-- top-level FFI call with multi type signature+addOne, addTwo :: Int -> Int++addOne = ffi "%1 + 1"+addTwo = ffi "%1 + 2"++-- FFI call in a let binding+addThree :: Int -> Int+addThree x =+ let go :: Int -> Int+ go = ffi "%1 + 3"+ in go x++-- FFI call in a where binding+addFour :: Int -> Int+addFour x = go x+ where+ go :: Int -> Int+ go = ffi "%1 + 4"++main = do+ let result = addOne . addTwo . addThree . addFour $ 0+ putStrLn $ show result
+ tests/DesugarFFI.res view
@@ -0,0 +1,1 @@+10
tests/DoLet2.hs view
@@ -1,6 +1,5 @@ module DoLet2 where -import Prelude import FFI main = do
tests/DoLet3.hs view
@@ -1,6 +1,5 @@ module DoLet3 where -import Prelude import FFI data R = R Int
tests/Double.hs view
@@ -1,3 +1,4 @@-import Prelude+module Double where +main :: Fay () main = print ((2 * 4 / 2) :: Double)
tests/Double2.hs view
@@ -1,3 +1,4 @@-import Prelude+module Double2 where +main :: Fay () main = print ((10 + (2 * (4 / 2))) :: Double)
tests/Double3.hs view
@@ -1,3 +1,4 @@-import Prelude+module Double3 where +main :: Fay () main = print ((5 * 3 / 2) :: Double)
tests/Double4.hs view
@@ -1,4 +1,4 @@-import Prelude+module Double4 where +main :: Fay () main = print 1-
tests/Either.hs view
@@ -1,4 +1,4 @@-import Prelude+module Either where raw :: Either Int Int -> Int raw x = case x of Left a -> a + 1
+ tests/EmptyDataDeclArray.hs view
@@ -0,0 +1,20 @@+import FFI++data Array a++empty :: Fay (Array a)+empty = ffi "[]"++push:: a -> Array a -> Fay ()+push = ffi "%2.push(%1)"++len :: Array a -> Fay Int+len = ffi "%1['length']"++main :: Fay ()+main = do+ arr <- empty+ print arr+ push (5::Int) arr+ print =<< len arr+ print arr
+ tests/EmptyDataDeclArray.res view
@@ -0,0 +1,3 @@+[]+1+[ 5 ]
tests/Eq.hs view
@@ -1,4 +1,4 @@-import Prelude+module Eq where main = do when (1 == 1) $ putStrLn "Expected =="
tests/ExportEThingAll.hs view
@@ -1,6 +1,5 @@ module ExportEThingAll where -import Prelude import ExportEThingAll_Export main = print Barbles
tests/ExportList.hs view
@@ -1,6 +1,5 @@-module Main (main) where+module ExportList (main) where -import Prelude import ExportList_A import FFI @@ -15,4 +14,3 @@ print $ b1 b print c print d-
tests/ExportList_A.hs view
@@ -1,7 +1,6 @@ module ExportList_A (x, A (..), B (B, b1), module ExportList_B) where import ExportList_B-import Prelude x :: Double x = 1
tests/ExportList_B.hs view
@@ -2,7 +2,6 @@ import ExportList_C import ExportList_D-import Prelude y :: Double y = 2
tests/ExportQualified_Export.hs view
@@ -2,8 +2,7 @@ module ExportQualified_Export (main, X.X) where import Prelude--import "foo" X+import "base" X main :: Fay () main = return ()
tests/ExportType.hs view
@@ -8,7 +8,6 @@ ) where import FFI-import Prelude data X = X
tests/Floating.hs view
@@ -1,4 +1,4 @@-import Prelude+module Floating where main = do print $ exp 0
tests/FromString.hs view
@@ -1,9 +1,9 @@ {-# LANGUAGE OverloadedStrings, RebindableSyntax #-} module FromString where -import Prelude import FromString.FayText import FromString.Dep (myString, depTest)+import Prelude main :: Fay () main = do@@ -12,4 +12,3 @@ putStrLn myString print myString depTest-
tests/GADTs_without_records.hs view
@@ -2,14 +2,12 @@ module GADTs_without_records where -import Prelude- data Expr a where I :: Int -> Expr Int Plus :: Expr Int -> Expr Int -> Expr Int B :: Bool -> Expr Bool IfThenElse :: Expr Bool -> Expr a -> Expr a -> Expr a- + false :: Expr Bool false = B False @@ -23,7 +21,7 @@ eval (IfThenElse p e1 e2) = case eval p of True -> eval e1 False -> eval e2- + n5 = I 5 n2 = I 2
tests/GuardWhere.hs view
@@ -1,4 +1,4 @@-import Prelude+module GuardWhere where poseL :: Bool -> String poseL y | y == True = "Not OK"
tests/HidePreludeImport_Import.hs view
@@ -1,6 +1,4 @@ module HidePreludeImport_Import where -import Prelude- last :: Double last = 1
tests/HierarchicalImport.hs view
@@ -1,6 +1,6 @@-import Prelude+module HierarchicalImport where+ import Hierarchical.Export main :: Fay () main = putStrLn exported-
+ tests/ImplicitPrelude.hs view
@@ -0,0 +1,7 @@+module ImplicitPrelude where++result :: Int+result = 3 + 4++main :: Fay ()+main = putStrLn (show result)
+ tests/ImplicitPrelude.res view
@@ -0,0 +1,1 @@+7
tests/ImportHiding.hs view
@@ -1,4 +1,4 @@-import Prelude+module ImportHiding where import ImportList1.A hiding (y) import ImportList1.B hiding (x)
tests/ImportIThingAll.hs view
@@ -1,4 +1,5 @@-import Prelude+module ImportIThingAll where+ import FFI import ImportList1.C (A (..))
tests/ImportList.hs view
@@ -1,6 +1,5 @@ module ImportList where -import Prelude import FFI import ImportList1.A (x)
tests/ImportListType.hs view
@@ -1,3 +1,5 @@+module ImportListType where+ import Prelude (Fay, putStrLn) main :: Fay ()
tests/ImportType.hs view
@@ -1,4 +1,4 @@-import Prelude+module ImportType where import ExportType @@ -14,4 +14,3 @@ print w' print (V 1 2) print (v1 (V 1 2))-
tests/ImportType2.hs view
@@ -3,4 +3,5 @@ import ImportType2I.A (foo) import ImportType2I.B (Foo) +main :: Fay () main = foo
tests/ImportType2I/A.hs view
tests/ImportType2I/B.hs view
+ tests/Integer.hs view
@@ -0,0 +1,10 @@+module Integer where++main :: Fay ()+main = do+ -- Integer is Ord, Eq, and Show, but not Num.+ print $ (1::Integer) < 1000+ print $ (1::Integer) > 1000+ print $ (1::Integer) == 1000+ print $ (3::Integer) == 3+ print $ 1024
+ tests/Integer.res view
@@ -0,0 +1,5 @@+true+false+false+true+1024
tests/Integral.hs view
@@ -1,4 +1,4 @@-import Prelude+module Integral where printPair :: (Int,Int) -> Fay () printPair (x,y) = print x >> print y
tests/Issue215A.hs view
@@ -1,7 +1,5 @@ module Issue215A where -import Prelude import Issue215.B -main = do- print $ f 2+main = print $ f 2
tests/Js2FayFunc.hs view
@@ -1,6 +1,8 @@+module Js2FayFunc where+ import FFI-import Prelude +main :: Fay () main = do f <- getF g <- getG
tests/JsFunctionPassing.hs view
@@ -1,7 +1,8 @@ {-# LANGUAGE EmptyDataDecls #-} +module JsFunctionPassing where+ import FFI-import Prelude data Func
+ tests/LambdaCase.hs view
@@ -0,0 +1,11 @@+{-# LANGUAGE LambdaCase #-}+module LambdaCase where++f :: Int -> Bool+f = \case+ 2 -> True+ _ -> False++main :: Fay ()+main = do+ print (f 2)
+ tests/LambdaCase.res view
@@ -0,0 +1,1 @@+true
tests/LazyOperators.hs view
@@ -1,7 +1,5 @@ module LazyOperators where -import Prelude- main :: Fay () main = print testFn
tests/List.hs view
@@ -1,3 +1,5 @@+module List where+ import FFI import Prelude hiding (take)
tests/List2.hs view
@@ -1,6 +1,9 @@+module List2 where+ import FFI import Prelude hiding (take) +main :: Fay () main = putStrLn (showList (take 5 (let ns = 1 : map' (foo 123) ns in ns))) foo :: Double -> Double -> Double
+ tests/ListEq.hs view
@@ -0,0 +1,7 @@+module ListEq where++main :: Fay ()+main = do+ print $ [] == []+ print $ [1,2,3] == [1,2,3]+ print $ ["a"] == ["b"]
+ tests/ListEq.res view
@@ -0,0 +1,3 @@+true+true+false
+ tests/MainThunk.hs view
@@ -0,0 +1,10 @@+import FFI++main = do putStrLn "Hey ho!"+ setTimeout 500 doThing+ doThing++doThing = putStrLn "Hello, World!"++setTimeout :: Int -> (Fay ()) -> Fay ()+setTimeout = ffi "(function (f,i) { var id = setTimeout(function () { f(id); }, i); return id; })(%2,%1)"
+ tests/MainThunk.res view
@@ -0,0 +1,3 @@+Hey ho!+Hello, World!+Hello, World!
tests/ModuleReExports.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE NoImplicitPrelude #-} module ModuleReExports where
tests/ModuleRecordClash.hs view
@@ -1,6 +1,5 @@ module ModuleRecordClash (main) where -import Prelude import ModuleRecordClash.R data R = R
tests/ModuleRecordClash2.hs view
@@ -1,11 +1,9 @@-module Main where+module ModuleRecordClash2 where import FFI import ModuleRecordClash2_Hello-import Prelude alert :: String -> Fay () alert = ffi "console.log(%1)" -main = do- alert (greeting defaultHello)+main = alert (greeting defaultHello)
tests/ModuleRecordClash2_Hello.hs view
@@ -1,8 +1,6 @@ -- This module needs to be top level to do the intended test. module ModuleRecordClash2_Hello where -import Prelude- defaultHello :: ModuleRecordClash2_Hello defaultHello = ModuleRecordClash2_Hello { greeting = "Hello, world!" }
tests/Monad.hs view
@@ -1,8 +1,5 @@-{-# LANGUAGE EmptyDataDecls #-}---- | Monads test.--import Prelude+{-# LANGUAGE EmptyDataDecls #-}+module Monad where main :: Fay () main = do@@ -13,4 +10,3 @@ y <- return 101112 print x print y-
tests/Monad2.hs view
@@ -1,10 +1,7 @@ {-# LANGUAGE EmptyDataDecls #-}- {-# LANGUAGE RankNTypes #-} --- | Monads test.--import Prelude+module Monad2 where main :: Fay () main = do
+ tests/MultiWayIf.hs view
@@ -0,0 +1,13 @@+{-# LANGUAGE MultiWayIf #-}+module MultiWayIf where++f :: Int -> Char+f x = if | x == 1 -> 'a'+ | x == 2 -> 'b'+ | otherwise -> 'c'++main :: Fay ()+main = do+ print (f 1)+ print (f 2)+ print (f 3)
+ tests/MultiWayIf.res view
@@ -0,0 +1,3 @@+a+b+c
tests/NestedImporting.hs view
@@ -1,6 +1,4 @@ module NestedImporting where -import Prelude- r :: Double r = 1
tests/NestedImporting2.hs view
@@ -1,6 +1,5 @@ module NestedImporting2 where -import Prelude import NestedImporting2.A main :: Fay ()
tests/NewtypeImport_Export.hs view
@@ -1,7 +1,6 @@ module NewtypeImport_Export where import FFI-import Prelude newtype MyInteger = MyInteger Int
tests/NewtypeImport_Import.hs view
@@ -1,25 +1,26 @@-import Prelude+module NewtypeImport_Import where+ import NewtypeImport_Export x = case MyInteger undefined of- MyInteger _ -> 1+ MyInteger _ -> 1 y = case undefined of- MyInteger _ -> 1+ MyInteger _ -> 1 int :: Int int = undefined yInt = case int of- _ -> 1+ _ -> 1 main = do- print x- print y- print yInt- print (Baz (Bar 1))- baz <- getBaz- print baz- case baz of- Baz (Bar i) -> print i- print (bar $ unwrapBaz baz)+ print x+ print y+ print yInt+ print (Baz (Bar 1))+ baz <- getBaz+ print baz+ case baz of+ Baz (Bar i) -> print i+ print (bar $ unwrapBaz baz)
tests/Nullable.hs view
@@ -1,5 +1,6 @@+module Nullable where+ import FFI-import Prelude data R = R (Nullable Double) @@ -39,4 +40,4 @@ r2 = ffi "{ instance : 'R', slot1 : null }" parseInt :: String -> Nullable Int-parseInt = ffi "(function () { var n = global.parseInt(%1, 10); if (isNaN(n)) return null; return n; })()"+parseInt = ffi "(function () { var n = parseInt(%1, 10); if (isNaN(n)) return null; return n; })()"
tests/Num.hs view
@@ -1,7 +1,6 @@ module Num where -import Prelude-+main :: Fay () main = do print (1 + 2::Int) print (4 - 1::Int)
tests/Ord.hs view
@@ -1,7 +1,6 @@ module Ord where -import Prelude-+main :: Fay () main = do when ((1::Int) < 2) $ putStrLn "Expected <" when ((1::Int) < 1) $ putStrLn "Unexpected < (1)"
+ tests/PrefixOpPat.hs view
@@ -0,0 +1,7 @@+module PrefixOpPat where++f ((:) x y) = x++main :: Fay ()+main = do+ print $ f [1,2]
+ tests/PrefixOpPat.res view
@@ -0,0 +1,1 @@+1
tests/QualifiedImport.hs view
@@ -1,7 +1,6 @@ module QualifiedImport where import FFI-import Prelude import qualified QualifiedImport.X import qualified QualifiedImport.X as X
tests/Ratio.hs view
@@ -1,6 +1,5 @@ module Ratio where -import Prelude import Data.Ratio main :: Fay ()
tests/ReExport3.hs view
@@ -1,4 +1,3 @@-import Prelude import ReExport2 main :: Fay ()
tests/ReExportGlobally.hs view
@@ -1,7 +1,6 @@ module ReExportGlobally (main, x) where import FFI-import Prelude import ReExportGlobally.A (x)
tests/ReExportGloballyExplicit.hs view
@@ -1,7 +1,6 @@ module Foo (main, x) where import FFI-import Prelude import ReExportGlobally.A (x)
tests/RealFrac.hs view
@@ -1,5 +1,6 @@-import Prelude+module RealFrac where +main :: Fay () main = do print $ fst $ properFraction 1.5 print $ snd $ properFraction 1.5
tests/RecCon.hs view
@@ -1,7 +1,8 @@-import Prelude+module RecCon where data Bool = True | False +main :: Fay () main = print (head (fix (\xs -> 123 : xs))) fix f = let x = f x in x
tests/RecDecl.hs view
@@ -1,5 +1,6 @@+module RecDecl where+ import FFI-import Prelude data R = R { i :: Double, c :: Char } data S = S Double Char
tests/RecordImport2_Export1.hs view
@@ -1,5 +1,5 @@ module RecordImport2_Export1 (main) where-import Prelude+ data R = R { wrong :: Double } main :: Fay ()
tests/RecordImport2_Export2.hs view
@@ -1,3 +1,3 @@ module RecordImport2_Export2 where-import Prelude+ data R = R { correct :: Double }
tests/RecordImport2_Import.hs view
@@ -1,6 +1,5 @@ module RecordExport where -import Prelude import FFI import RecordImport2_Export1 import RecordImport2_Export2
tests/RecordImport_Export.hs view
@@ -2,7 +2,5 @@ module RecordImport_Export where -import Prelude- data R = R Integer data Fields = Fields { fieldFoo :: Integer, fieldBar :: Integer }
tests/RecordImport_Import.hs view
@@ -1,6 +1,7 @@ {- NOTE: This file is also used in the Compile tests. -} -import Prelude+module RecordImport_Import where+ import RecordImport_Export f :: R -> R
tests/Sink.hs view
@@ -1,12 +1,10 @@ -- https://github.com/faylang/fay/issues/285-module Main where+module Sink where -import Prelude import FFI run :: Fay ()-run = do- runSink (Sink putStrLn) "hello"+run = runSink (Sink putStrLn) "hello" newtype Sink a = Sink { runSink :: a -> Fay () }
tests/SkipLetTypes.hs view
@@ -1,6 +1,5 @@-import Prelude+module SkipLetType where main = let t :: Bool t = True in print t-
tests/SkipWhereTypes.hs view
@@ -1,7 +1,6 @@-import Prelude+module SkipWhereTypes where main = print t where t :: Bool t = True-
− tests/String.hs
@@ -1,4 +0,0 @@-import Prelude--main = putStrLn "Hello, World!"-
− tests/String.res
@@ -1,1 +0,0 @@-Hello, World!
tests/StringForcing.hs view
@@ -1,6 +1,5 @@ module StringForcing where -import Prelude import FFI main = do
+ tests/Strings.hs view
@@ -0,0 +1,3 @@+module Strings where++main = putStrLn "Hello, World!"
+ tests/Strings.res view
@@ -0,0 +1,1 @@+Hello, World!
tests/T190.hs view
@@ -1,7 +1,5 @@ module T190 where -import Prelude- import T190_B import T190_C
tests/T190_A.hs view
@@ -1,6 +1,4 @@ module T190_A where -import Prelude- foo :: Int -> Fay () foo _ = putStrLn "WRONG!"
tests/T190_B.hs view
@@ -1,6 +1,5 @@ module T190_B where -import Prelude import T190_A main :: Fay ()
tests/T190_C.hs view
@@ -1,6 +1,4 @@ module T190_C where -import Prelude- foo :: String -> Fay () foo x = putStrLn x
+ tests/TextOrd.hs view
@@ -0,0 +1,15 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE PackageImports #-}+{-# LANGUAGE RebindableSyntax #-}+module TextOrd where++import Prelude++import Data.Text++main :: Fay ()+main = do+ print ("abc" < ("c"::Text))+ print ("a" < ("bcd"::Text))+ print ("bcd" > ("a"::Text))+ print ("bcd" < ("a"::Text))
+ tests/TextOrd.res view
@@ -0,0 +1,4 @@+true+true+true+false
+ tests/Trace.hs view
@@ -0,0 +1,11 @@+module Trace where++import FFI+import Debug.Trace++fac :: Int -> Int+fac 0 = trace "fac" (traceShow 0 1)+fac n = trace "fac" (traceShow n (n * fac (n - 1)))++main :: Fay ()+main = print $ fac 5
+ tests/Trace.res view
@@ -0,0 +1,13 @@+fac+5+fac+4+fac+3+fac+2+fac+1+fac+0+120
tests/TupleCalls.hs view
@@ -1,5 +1,6 @@+module TupleCalls where+ import FFI-import Prelude f :: (Int,Double) -> Double f = ffi "%1[0]+%1[1]"@@ -10,10 +11,10 @@ h :: (String,Int) -> (Int,String) h = ffi "[%1[0]['length'],'#'+%1[1]['toString']()+'#']" +main :: Fay () main = do print $ f (1,2.3) print $ fst (g 8.76) print $ snd (g 8.76) print $ fst (h ("abc",12)) putStrLn $ snd (h ("abc",12))-
tests/TyVarSerialization.hs view
@@ -1,6 +1,5 @@ module TyVarSerialization where -import Prelude import FFI main :: Fay ()
+ tests/Var.hs view
@@ -0,0 +1,24 @@+-- | Tests for the var types.++module Var where++import Data.Var++main =+ do v <- newVar (0 :: Int)+ subscribe v+ (\v ->+ putStrLn ("v changed: " ++ show v))+ set v 4+ modify v (+ 5)+ s <- newSig+ subscribe s+ (\v ->+ putStrLn ("s signalled: " ++ show v))+ set s (567 :: Int)+ r <- newRef (123 :: Int)+ v <- get r+ putStrLn ("ref: " ++ show v)+ set r (666 :: Int)+ v <- get r+ putStrLn ("ref(2): " ++ show v)
+ tests/Var.res view
@@ -0,0 +1,5 @@+v changed: 4+v changed: 9+s signalled: 567+ref: 123+ref(2): 666
+ tests/VarPtr.hs view
@@ -0,0 +1,11 @@+module VarPtr where++import Data.Var++data Record = Record Int++main = do + v <- newVar $ Record 5+ subscribeAndRead v $ \y -> case y of+ Record a -> putStrLn . show $ a+ set v $ Record 10
+ tests/VarPtr.res view
@@ -0,0 +1,2 @@+5+10
+ tests/WhenUnlessRecursion.hs view
@@ -0,0 +1,22 @@+module WhenUnlessRecursion where++import FFI++getStackSize :: Fay Int+getStackSize = ffi "(Error[\"stackTraceLimit\"] = Infinity, new Error().stack.split('\\n').length)"++checkGrowth :: Maybe Int -> Int -> Fay ()+checkGrowth Nothing _ = return ()+checkGrowth (Just old) new = if new == old then return () else (error $ "Call stack growth: " ++ show old ++ " to " ++ show new)++main = do+ putStrLn "loopIf" >> loopIf Nothing 0+ putStrLn "loopWhen" >> loopWhen Nothing 0+ putStrLn "loopUnless" >> loopUnless Nothing 0+ where+ pred = (< 5)+ step = (+ 1)+ action f s n = getStackSize >>= \s' -> checkGrowth s s' >> f (Just s') (step n)+ loopIf s n = if (pred n) then (action loopIf s n) else return ()+ loopWhen s n = when (pred n) (action loopWhen s n)+ loopUnless s n = unless (not (pred n)) (action loopUnless s n)
+ tests/WhenUnlessRecursion.res view
@@ -0,0 +1,3 @@+loopIf+loopWhen+loopUnless
tests/asPatternMatch.hs view
@@ -1,4 +1,4 @@-import Prelude+module AsPatternMatch where matchSame :: [a] -> ([a],[a]) matchSame x@y = (x,y)
tests/automatic.hs view
@@ -1,4 +1,5 @@-import Prelude+module Automatic where+ import FFI func :: Bool -> Int -> Int -> Int@@ -19,4 +20,3 @@ print' (semiAutomatic func False) print' (automatic func True) print' (automatic func False)-
tests/baseFixities.hs view
@@ -1,4 +1,4 @@-import Prelude+module BaseFixities where fmap :: (a -> b) -> Fay a -> Fay b fmap f m = m >>= (return . f)
tests/basicFunctions.hs view
@@ -1,4 +1,4 @@-import Prelude+module BasicFunctions where main = putStrLn (concat' ["Hello, ","World!"]) @@ -9,4 +9,3 @@ append (x:xs) ys = x : append xs ys append [] ys = ys-
tests/case.hs view
@@ -1,6 +1,5 @@-import Prelude+module Case where main = putStrLn (case True of True -> "Hello!" False -> "Ney!")-
tests/case2.hs view
@@ -1,6 +1,5 @@-import Prelude+module Case2 where main = putStrLn (case False of True -> "Hello!" False -> "Ney!")-
+ tests/case3.hs view
@@ -0,0 +1,11 @@+module Case3 where++f x = case () of+ _ | x == 1 -> 1+ | x == 2 -> 2+ | True -> 3++main = do+ print (f 1)+ print (f 2)+ print (f 3)
+ tests/case3.res view
@@ -0,0 +1,3 @@+1+2+3
tests/caseList.hs view
@@ -1,8 +1,7 @@-import Prelude+module CaseList where main = putStrLn (case [1,2,3,4,5] of [1,2,3,4,6] -> "6!" [1,2,4,2,4] -> "a!" [1,2,3,4,5] -> "OK." _ -> "Broken.")-
tests/caseWildcard.hs view
@@ -1,6 +1,5 @@-import Prelude+module CaseWildcard where main = putStrLn (case False of True -> "Hello!" _ -> "Ney!")-
tests/circular.hs view
@@ -1,7 +1,5 @@ module Circular where -import Prelude- main :: Fay () main = let y = x + 1::Double x = y + 1
tests/curry.hs view
@@ -1,4 +1,4 @@-import Prelude+module Curry where f :: Int -> Int -> Int f x y = x + y
tests/cycle.hs view
@@ -1,3 +1,4 @@-import Prelude+module Cycle where +main :: Fay () main = mapM_ putStrLn (take 5 (cycle ["a", "b", "c"]))
tests/do.hs view
@@ -1,4 +1,4 @@-import Prelude+module Do where +main :: Fay () main = do putStrLn "Hello,"; putStrLn "World!"-
tests/doAssingPatternMatch.hs view
@@ -1,6 +1,6 @@-import Prelude+module DoAssingPatternMatch where +main :: Fay () main = do [1,2] <- return [1,2] putStrLn "OK."-
tests/doBindAssign.hs view
@@ -1,6 +1,6 @@-import Prelude+module DoBindAssign where +main :: Fay () main = do x <- return "Hello, World!" >>= return putStrLn x-
tests/doLet.hs view
@@ -1,4 +1,4 @@-import Prelude+module DoLet where main = do first@@ -7,6 +7,7 @@ fourth fifth sixth+ newtyp first = do let x = 123@@ -44,3 +45,10 @@ print y print x +newtype I = I Int+newtyp = do+ putStrLn "newtype"+ I i <- return (I 1)+ print i+ let I j = I 2+ print j
tests/doLet.res view
@@ -11,3 +11,6 @@ 10 777 123+newtype+1+2
tests/emptyMain.hs view
@@ -1,3 +1,4 @@-import Prelude+module EmptyMain where +main :: Fay () main = return ()
tests/enumFrom.hs view
@@ -1,4 +1,4 @@-import Prelude+module EnumFrom where main :: Fay () main = do
tests/error.hs view
@@ -1,3 +1,3 @@-import Prelude+module Error where main = error "This is an error"
tests/ffiExpr.hs view
@@ -1,7 +1,5 @@-{-# LANGUAGE NoImplicitPrelude #-}-module Main where+module FfiExpr where -import Prelude import FFI main :: Fay ()
tests/ffimunging.hs view
@@ -1,9 +1,6 @@-{-# LANGUAGE NoImplicitPrelude #-}--module Maybe where+module FfiMunging where import FFI-import Prelude data Munge a b = Fudge a b
tests/fix.hs view
@@ -1,5 +1,6 @@-import Prelude+module Fix where +main :: Fay () main = print (head (tail (fix (\xs -> 123 : xs)))) fix f = let x = f x in x
tests/fromInteger.hs view
@@ -1,4 +1,4 @@-import Prelude+module FromInteger where main :: Fay () main = putStrLn $ show $ fromInteger 5
tests/fromIntegral.hs view
@@ -1,4 +1,7 @@-import Prelude+module FromIntegral where main :: Fay ()-main = putStrLn $ show $ fromIntegral 5+main = print $ fromIntegral' 5++fromIntegral' :: Int -> Double+fromIntegral' = fromIntegral
tests/guards.hs view
@@ -1,4 +1,4 @@-import Prelude+module Guards where f :: Int -> Int f n | n <= 0 = 0
tests/infixDataConst.hs view
@@ -1,4 +1,4 @@-import Prelude+module InfixDataConst where data Ty1 = Integer `InfixConst1` Integer
tests/ints.hs view
@@ -1,5 +1,5 @@-import Prelude+module Ints where +main :: Fay () main = do- print 123-+ print (123 :: Int)
tests/linesAndWords.hs view
@@ -1,7 +1,8 @@-import Prelude+module LinesAndWords where quote s = "\"" ++ s ++ "\"" +main :: Fay () main = do mapM_ (putStrLn . quote) $ words " this is\ta\n\r\ftest " putStrLn $ quote $ unwords ["this", "is", "too"]
tests/listComprehensions.hs view
@@ -1,4 +1,4 @@-import Prelude+module ListComprehensions where main :: Fay () main = putStrLn $ show $ sum [ x*x | x <- [1::Int, 2, 3, 4, 5], let y = x + 4, y < 8]
tests/listlen.hs view
@@ -1,7 +1,6 @@ module Listlen (main) where import FFI-import Prelude -- Test provided by ticket go :: [Double] -> [Double]
tests/mutableReference.hs view
@@ -1,9 +1,7 @@--- | Mutable references.--{-# LANGUAGE EmptyDataDecls #-}+{-# LANGUAGE EmptyDataDecls #-}+module MutableReference where import FFI-import Prelude main :: Fay () main = do
tests/nameGen.hs view
@@ -1,4 +1,4 @@-import Prelude+module NameGen where data SomeRec = SomeRec { a :: Integer, b :: Integer } | Y | X @@ -12,4 +12,3 @@ putStrLn $ case t of SomeRec _ _ -> "Bad" Y -> "OK."-
tests/namedFieldPuns.hs view
@@ -1,9 +1,9 @@ {-# LANGUAGE NamedFieldPuns #-}+module NamedFieldPuns where import FFI-import Prelude -data SomeRec = SomeRec { a :: Integer, b :: Integer } | Y | X+data SomeRec = SomeRec { a :: Int, b :: Int } | Y | X fun :: SomeRec -> SomeRec fun SomeRec{a} = SomeRec{a=a+1, b=10}
tests/negation.hs view
@@ -1,8 +1,9 @@-import Prelude+module Negation where print' :: Double -> Fay () print' = print +main :: Fay () main = do print' $ (-7/2) print' $ (-7)/2 print' $ -f x/y
tests/newtype.hs view
@@ -1,5 +1,6 @@+module Newtype where+ import FFI-import Prelude newtype MyInteger = MyInteger Int @@ -21,13 +22,30 @@ getBaz :: Fay Baz getBaz = ffi "{ instance: 'Bar', bar: 1 }" +getBazExpr = ffi "{ instance: 'Bar', bar : 2 }" :: Fay Baz++getBazExpr' = f (Baz (Bar 2)) (ffi "{ instance: 'Bar', bar : 3 }" :: Baz)+f :: Baz -> Baz -> Baz+f (Baz x) (Baz y) = Baz (Bar (bar x + bar y))++ main = do print x print y print yInt print (Baz (Bar 1))+ baz <- getBaz print baz- case baz of- Baz (Bar i) -> print i+ case baz of Baz (Bar i) -> print i print (bar $ unwrapBaz baz)++ bazExpr <- getBazExpr+ print bazExpr+ case bazExpr of Baz (Bar i) -> print i+ print (bar $ unwrapBaz bazExpr)++ let bazExpr' = getBazExpr'+ print bazExpr'+ case bazExpr' of Baz (Bar i) -> print i+ print (bar $ unwrapBaz bazExpr')
tests/newtype.res view
@@ -5,3 +5,9 @@ { instance: 'Bar', bar: 1 } 1 1+{ instance: 'Bar', bar: 2 }+2+2+{ instance: 'Bar', bar: 5 }+5+5
tests/newtypeIndirectApp.hs view
@@ -1,6 +1,4 @@-module Main where--import Prelude+module NewtypeIndirectApp where newtype Parser a = Parser { runParser :: Char -> Either Char (a, Char) }
tests/numTheory.hs view
@@ -1,4 +1,4 @@-import Prelude+module NumTheory where main = do print $ (subtract 3 5 :: Int)
tests/nums.hs view
@@ -1,5 +1,4 @@-import Prelude+module Nums where main :: Fay () main = print (-10 :: Double)-
tests/pats.hs view
@@ -1,4 +1,4 @@-import Prelude+module Pats where main :: Fay () main = do
tests/patternGuards.hs view
@@ -1,9 +1,8 @@ {-# LANGUAGE FlexibleInstances #-} --- | As pattern matches+module PatternGuards where import FFI-import Prelude isPositive :: Double -> Bool isPositive x | x > 0 = True
tests/patternMatchFail.hs view
@@ -1,4 +1,4 @@-import Prelude+module PatternMatchFail where +main :: Fay () main = putStrLn ((\a 'a' -> "OK.") 0 'b')-
+ tests/patternMatchLet.hs view
@@ -0,0 +1,10 @@+module PatternMatchLet where++first3 :: String -> Fay ()+first3 cs = do+ let (a:b:c:_) = cs+ putStrLn [a, b, c]++main :: Fay ()+main = first3 "abcd"+
+ tests/patternMatchLet.res view
@@ -0,0 +1,1 @@+abc
tests/patternMatchingTuples.hs view
@@ -1,8 +1,4 @@--- compile with fay --html-wrapper--- error thrown as soon as HTML page is loaded:--- Uncaught TypeError: Cannot read property 'car' of null--import Prelude+module PatternMatchingTuples where main :: Fay () main = putStrLn doTest
tests/recordFunctionPatternMatch.hs view
@@ -1,11 +1,11 @@-import Prelude+module RecordFunctionPatternMatch where data Person = Person String String Int +main :: Fay () main = putStrLn (foo (Person "Chris" "Done" 14)) foo (Person "Chris" "Done" 13) = "Foo!" foo (Person "Chris" "Barf" 14) = "Bar!" foo (Person "Chris" "Done" 14) = "Hello!" foo _ = "World!"-
tests/recordPatternMatch.hs view
@@ -1,8 +1,7 @@-import Prelude+module RecordPatternMatch where data Person = Person String String Int main = putStrLn (case Person "Chris" "Done" 14 of Person "Chris" "Done" 13 -> "Hello!" _ -> "World!")-
tests/recordPatternMatch2.hs view
@@ -1,4 +1,4 @@-import Prelude+module RecordPatternMatch2 where data Person = Person String String Int @@ -7,4 +7,3 @@ Person "Chris" "Barf" 14 -> "Bar!" Person "Chris" "Done" 14 -> "Hello!" _ -> "World!")-
tests/recordUseBeforeDefine.hs view
@@ -1,4 +1,4 @@-import Prelude+module RecordUseBeforeDefine where import Hierarchical.RecordDefined @@ -10,4 +10,3 @@ print $ g (Callback 1) data R = R Double-
tests/recordWildCards.hs view
@@ -1,8 +1,9 @@ {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE RecordWildCards #-} +module RecordWildCards where+ import FFI-import Prelude data C = C { a :: Int, b :: Int, c :: Int, d :: Int }
tests/records.hs view
@@ -1,4 +1,4 @@-import Prelude+module Records where data Person1 = Person1 String String Int data Person2 = Person2 { fname :: String, sname :: String, age :: Int }@@ -14,4 +14,3 @@ putStrLn (case p2 of Person2 "Chris" "Done" 13 -> "Hello!") putStrLn (case p2a of Person2 "Chris" "Done" 13 -> "Hello!") putStrLn (case p3 of Person3 "Chris" "Done" 13 -> "Hello!")-
tests/recursive.hs view
@@ -1,6 +1,4 @@-module Main where--import Prelude+module Recursive where ones :: [Int] ones = 1 : ones
tests/reservedWords.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE EmptyDataDecls #-} -import Prelude+module ReservedWords where main = do -- All reserved words@@ -43,4 +43,3 @@ putStrLn "" -- Stdlib functions that need to be encoded putStrLn $ const "stdconst" 2-
tests/sections.hs view
@@ -1,7 +1,5 @@ module Sections where -import Prelude- withTwo :: (Int -> Int) -> Int withTwo f = f 2
tests/seq-fake.hs view
@@ -1,4 +1,4 @@-import Prelude+module SeqFake where fakeSeq :: a -> b -> b fakeSeq x y = y
tests/seq.hs view
@@ -1,3 +1,3 @@-import Prelude+module Seq where main = error "You shall not pass!" `seq` return ()
tests/serialization.hs view
@@ -1,4 +1,5 @@-import Prelude+module Serialization where+ import FFI data Parametric a = Parametric a@@ -10,8 +11,8 @@ printMaybeConcrete (Just (ConcreteRecord 42)) printMaybeAutomatic (Just (ConcreteRecord 42)) printMaybe (Just (ConcreteRecord 42))- printUnknown (error "do not want")- printUnknownField (Just (error "do not want"))+ printUnknown 42+ printUnknownField (Just 42) printAutomatic (Just (ConcreteRecord 42)) printParametricButConcreteType :: Parametric ConcreteRecord -> Fay ()
tests/serialization.res view
@@ -3,15 +3,14 @@ { instance: 'Parametric', slot1: { instance: 'ConcreteRecord', concreteField: 123 } } { instance: 'Parametric',- slot1: { forced: false, value: [Function] } }+ slot1: { instance: 'ConcreteRecord', concreteField: 123 } } { instance: 'Just', slot1: { instance: 'ConcreteRecord', concreteField: 42 } } { instance: 'Just', slot1: { instance: 'ConcreteRecord', concreteField: 42 } } { instance: 'Just',- slot1: { forced: false, value: [Function] } }-{ forced: false, value: [Function] }-{ instance: 'Just',- slot1: { forced: false, value: [Function] } }+ slot1: { instance: 'ConcreteRecord', concreteField: 42 } }+42+{ instance: 'Just', slot1: 42 } { instance: 'Just', slot1: { instance: 'ConcreteRecord', concreteField: 42 } }
tests/succPred.hs view
@@ -1,4 +1,4 @@-import Prelude+module SuccPred where main = do print (succ 1 :: Int)
tests/tailRecursion.hs view
@@ -1,5 +1,5 @@ -- | This is to test tail-recursive calls are iterative.-import Prelude+module TailRecursion where main = do print (sumTo 1000 0 :: Double)
tests/then.hs view
@@ -1,4 +1,3 @@-import Prelude+module Then where main = putStrLn "Hello," >> putStrLn "World!"-
tests/tupleCon.hs view
@@ -1,4 +1,4 @@-import Prelude+module TupleCon where main = do print $ (,) 1 2
tests/tupleSec.hs view
@@ -1,5 +1,5 @@ {-# LANGUAGE TupleSections #-}-import Prelude+module TupleSec where main = do print $ (,2) 1
tests/unit.hs view
@@ -1,4 +1,5 @@-import Prelude+module Unit where+ main = case (f ()) of () -> print 123
tests/utf8.hs view
@@ -1,8 +1,6 @@ {-# LANGUAGE EmptyDataDecls #-} --- | Unicode test.--import Prelude+module Utf8 where main :: Fay () main = do@@ -131,4 +129,3 @@ putStrLn "ﹰ ﹱ ﹲ ﹴ ﹶ ﹷ ﹸ ﹹ ﹺ ﹻ ﹼ ﹽ ﹾ ﹿ ﺀ ﺁ ﺂ ﺃ ﺄ ﺅ ﺆ ﺇ ﺈ ﺉ ﺊ ﺋ ﺌ ﺍ ﺎ ﺏ ﺐ ﺑ ﺒ ﺓ ﺔ ﺕ ﺖ ﺗ ﺘ ﺙ ﺚ ﺛ ﺜ ﺝ ﺞ ﺟ ﺠ ﺡ ﺢ ﺣ ﺤ ﺥ ﺦ ﺧ ﺨ ﺩ ﺪ ﺫ ﺬ ﺭ ﺮ ﺯ ﺰ ﺱ ﺲ ﺳ ﺴ ﺵ ﺶ ﺷ ﺸ ﺹ ﺺ ﺻ ﺼ ﺽ ﺾ ﺿ ﻀ ﻁ ﻂ ﻃ ﻄ ﻅ ﻆ ﻇ ﻈ ﻉ ﻊ ﻋ ﻌ ﻍ ﻎ ﻏ ﻐ ﻑ ﻒ ﻓ ﻔ ﻕ ﻖ ﻗ ﻘ ﻙ ﻚ ﻛ ﻜ ﻝ ﻞ ﻟ ﻠ ﻡ ﻢ ﻣ ﻤ ﻥ ﻦ ﻧ ﻨ ﻩ ﻪ ﻫ ﻬ ﻭ ﻮ ﻯ ﻰ ﻱ ..." -- Halfwidth and Fullwidth Forms putStrLn "! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~ 。 「 」 、 ・ ヲ ァ ィ ゥ ェ ォ ャ ュ ョ ッ ー ア イ ウ エ オ カ キ ク ケ コ サ シ ス セ ソ タ チ ツ ..."-
tests/where.hs view
@@ -1,6 +1,5 @@-import Prelude+module Where where main = putStrLn $ "Hello " ++ friends ++ family where friends = "my friends" family = " and family"-
tests/whereBind.hs view
@@ -1,4 +1,4 @@-import Prelude+module WhereBind where main :: Fay () main =
tests/whereBind2.hs view
@@ -1,4 +1,4 @@-import Prelude+module WhereBind2 where someFun :: Int -> String someFun x = fun x
tests/whereBind3.hs view
@@ -1,4 +1,4 @@-import Prelude+module WhereBind3 where f :: String -> String f x = friends ++ family@@ -6,4 +6,3 @@ family = " and family" main = putStrLn (f "my friends")-