git.iko.soy/rowtype-yoga/purescript-native
on c8778d3 help sign in
125 files changed+5593−2716
since splitsnapshots
unifiedsplit
.gitignore−15 .travis.yml+3 CONTRIBUTING.md+14 ChangeLog.md−3 LICENSE+16−26 README-go.md−48 README.md+723−28 Setup.hs−2 Setup.lhs+6 app/Main.hs−250 default.nix−23 examples/.gitignore+1 examples/failing/Arrays.purs+1 examples/failing/Import.purs+1 examples/failing/KindError.purs+1 examples/failing/LocalVars.purs+3 examples/failing/PatternScope.purs+7 examples/failing/Rank2Types.purs+3 examples/failing/ReadShow.purs+5 examples/failing/SkolemEscape.purs+3 examples/failing/TypeError.purs+3 examples/passing/Arrays.purs+20 examples/passing/Assign.purs+7 examples/passing/Auto.purs+9 examples/passing/Church.purs+12 examples/passing/Conditional.purs+5 examples/passing/Cons.purs+7 examples/passing/Console.purs+23 examples/passing/ExternData.purs+13 examples/passing/FFI.purs+5 examples/passing/Foreach.purs+7 examples/passing/Functions.purs+11 examples/passing/Guards.purs+8 examples/passing/IfThenElse.purs+34 examples/passing/Import.purs+12 examples/passing/List.purs+22 examples/passing/Match.purs+3 examples/passing/Maybe.purs+10 examples/passing/MethodCall.purs+6 examples/passing/Module.purs+22 examples/passing/Monad.purs+26 examples/passing/NameResolution.purs+15 examples/passing/NamedPatterns.purs+3 examples/passing/Nested.purs+3 examples/passing/ObjectUpdate.purs+14 examples/passing/Objects.purs+12 examples/passing/Operators.purs+42 examples/passing/Patterns.purs+23 examples/passing/Person.purs+7 examples/passing/Rank2Data.purs+23 examples/passing/Rank2Types.purs+17 examples/passing/Recursion.purs+4 examples/passing/TypeDecl.purs+14 examples/passing/TypeSynonyms.purs+31 examples/passing/iota.purs+5 examples/passing/s.purs+1 examples/shelltest/Console.js+19 examples/shelltest/Foreach.js+1 examples/shelltest/List.js+7 examples/shelltest/Maybe.js+5 examples/shelltest/runAll.test+23 examples/shelltest/runMain.js+1 libraries/.gitignore+1 libraries/jquery/Makefile+4 libraries/jquery/example.purs+6 libraries/jquery/jquery.js+7 libraries/jquery/jquery.purs+23 libraries/prelude/Makefile+3 libraries/prelude/prelude.js+65 libraries/prelude/prelude.purs+273 package.yaml−86 psgo.cabal−145 purescript.cabal+59 release.md+31 src/CodeGen/IL.hs−321 src/CodeGen/IL/Common.hs−196 src/CodeGen/IL/Optimizer.hs−81 src/CodeGen/IL/Optimizer/Common.hs−18 src/CodeGen/IL/Optimizer/Inliner.hs−435 src/CodeGen/IL/Optimizer/MagicDo.hs−146 src/CodeGen/IL/Optimizer/TCO.hs−141 src/CodeGen/IL/Printer.hs−527 src/Data/Generics/Extras.hs+24 src/Language/PureScript.hs+38 src/Language/PureScript/CodeGen.hs+18 src/Language/PureScript/CodeGen/Externs.hs+41 src/Language/PureScript/CodeGen/JS.hs+173 src/Language/PureScript/CodeGen/JS/AST.hs+49 src/Language/PureScript/CodeGen/Monad.hs+31 src/Language/PureScript/Declarations.hs+43 src/Language/PureScript/Kinds.hs+26 src/Language/PureScript/Names.hs+55 src/Language/PureScript/Operators.hs+128 src/Language/PureScript/Optimize.hs+95 src/Language/PureScript/Parser.hs+22 src/Language/PureScript/Parser/Common.hs+253 src/Language/PureScript/Parser/Declarations.hs+113 src/Language/PureScript/Parser/Kinds.hs+41 src/Language/PureScript/Parser/State.hs+22 src/Language/PureScript/Parser/Types.hs+104 src/Language/PureScript/Parser/Values.hs+268 src/Language/PureScript/Pretty.hs+23 src/Language/PureScript/Pretty/Common.hs+79 src/Language/PureScript/Pretty/JS.hs+224 src/Language/PureScript/Pretty/Kinds.hs+47 src/Language/PureScript/Pretty/Types.hs+88 src/Language/PureScript/Pretty/Values.hs+209 src/Language/PureScript/TypeChecker.hs+138 src/Language/PureScript/TypeChecker/Kinds.hs+125 src/Language/PureScript/TypeChecker/Monad.hs+182 src/Language/PureScript/TypeChecker/Synonyms.hs+48 src/Language/PureScript/TypeChecker/Types.hs+753 src/Language/PureScript/Types.hs+73 src/Language/PureScript/Unknown.hs+24 src/Language/PureScript/Values.hs+101 src/Main.hs+70 src/Tests.hs−141 stack.nix−23 stack.yaml−24 support/ffi-loader.go−30 support/go.mod.ffi-loader−2 support/go.mod.output−2 support/go.mod.working−1 test/Spec.hs−2 tests/Main.hs+71
.gitignore−15
@@ -1,23 +1,8 @@
11dist
22cabal-dev
3−.cabal-sandbox
4−cabal.sandbox.config
53*.o
64*.hi
75*.chi
86*.chs.h
97*.lksh*
108.virthualenv
11−.psci_modules/
12−.test_modules/
13−bower_components/
14−node_modules
15−tmp/
16−.stack-work/
17−stack.yaml.lock
18−output
19−examples/docs/docs/
20−core-tests/full-core-docs.md
21−.psc-ide-port
22−.vscode/
23−result
.travis.ymlnew file+3
@@ -0,0 +1,3 @@
1+language: haskell
2+notifications:
3+ email: false
CONTRIBUTING.mdnew file+14
@@ -0,0 +1,14 @@
1+Pull requests are encouraged.
2+
3+Please follow the following guidelines:
4+
5+- Add at least a test to `examples/passing/` and possibly to `examples/failing`
6+- Run `cabal test`
7+- Consider adding a full test with Javascript to `examples/shelltest`
8+- Please run `hlint`.
9+
10+If you would like to contribute, please consider the issues in the current milestone first.
11+
12+Send me (`paf31`) a message if you have any implementation questions, I'm more than happy to talk about the design, and I'm more than willing to redesign parts if necessary.
13+
14+Finally, if you have made code changes and would like to be included in the copyright notice in the cabal file, please include that change in your pull request.
ChangeLog.mddeleted−3
@@ -1,3 +0,0 @@
1−# Changelog for pscpp
2−
3−## Unreleased changes
LICENSE+16−26
@@ -1,30 +1,20 @@
1−Copyright Andy Arvanitis (c) 2018
1+The MIT License (MIT)
22
3−All rights reserved.
3+Copyright (c) 2013 Phil Freeman
44
5−Redistribution and use in source and binary forms, with or without
6−modification, are permitted provided that the following conditions are met:
5+Permission is hereby granted, free of charge, to any person obtaining a copy of
6+this software and associated documentation files (the "Software"), to deal in
7+the Software without restriction, including without limitation the rights to
8+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9+the Software, and to permit persons to whom the Software is furnished to do so,
10+subject to the following conditions:
711
8− * Redistributions of source code must retain the above copyright
9− notice, this list of conditions and the following disclaimer.
12+The above copyright notice and this permission notice shall be included in all
13+copies or substantial portions of the Software.
1014
11− * Redistributions in binary form must reproduce the above
12− copyright notice, this list of conditions and the following
13− disclaimer in the documentation and/or other materials provided
14− with the distribution.
15−
16− * Neither the name of Author name here nor the names of other
17− contributors may be used to endorse or promote products derived
18− from this software without specific prior written permission.
19−
20−THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21−"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22−LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23−A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24−OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25−SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26−LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27−DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28−THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29−(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30−OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
15+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
README-go.mddeleted−48
@@ -1,48 +0,0 @@
1−
2−### psgo
3−
4−#### Performance
5−
6−* No runtime system beyond some support types and functions
7−* Uses PureScript's normal tail call optimization techniques for generated Go code
8−
9−#### Differences from PureScript:
10−
11−* Foreign imports/exports are Go instead of JavaScript – see [standard library foreign implementations](https://github.com/andyarvanitis/purescript-native-go-ffi)
12−* No Go-specific REPL
13−
14−#### Other notes:
15−
16−* The provided `Any` type is just an alias for `interface{}`
17−* PureScript types are represented in Go with:
18−
19− | PureScript | Go |
20− |------------|----|
21− |`Array` | `[]Any`|
22− |`String` | `string`|
23− |`Number` | `float64`|
24− |`Int` | `int`|
25− |`Char` | `string` (single UTF-8 entity)|
26− |`Boolean` | `bool`|
27− |Records | `map[string]Any`|
28−
29−#### Future ideas:
30−
31−* Nice facilities (modules) for concurrency/parallelism, using goroutines
32−
33−
34−#### Getting Started
35−This assumes you are running macOS (OS X) or a Unix-like system (Linux, *BSD, etc.).
36−
37−1. Choose a working directory wherever you like. Then create a `src` subdirectory under it, which will be where you will place your own PureScript source files.
38−
39−1. Initialize your project with `spago init`, and install any dependencies with `spago install` (please see their instructions if you haven't used it before).
40−
41−1. Add the `backend = "psgo"` option to your `spago.dhall` file. That way `spago` will use the psgo backend when compiling.
42−
43−1. You should now be ready to build a PureScript program:
44− * As stated above, place your source file(s) in the working directory's `src` subdirectory and execute `spago build`.
45−
46− * This will generate the Go source tree for your program and then build an executable binary. The resulting executable will be in your working directory and will be named `Main`. The generated source files will be under `output/`.
47−
48−---
README.md+723−28
@@ -1,41 +1,736 @@
1−[![PureScript](https://raw.githubusercontent.com/purescript/purescript/master/logo.png)](http://purescript.org)
1+purescript
2+==========
23
3−This is a platform-native compiler backend for [PureScript](https://github.com/purescript/purescript). It attempts to generate "sane", debuggable, and portable [C++11](https://isocpp.org/wiki/faq/cpp11) (or later) or [Go](https://golang.org/) code as an intermediate language, which is then compiled to a native executable binary. This also enables easy interoperability with existing C/C++ or Go frameworks and libraries on a number of platforms.
4+[![Build Status](https://secure.travis-ci.org/paf31/purescript.png?branch=master)](http://travis-ci.org/paf31/purescript)
45
5−There are two utilities in the purescript-native "suite": `pscpp` and `psgo`. The source code in this branch is for `psgo`, the purescript-to-Go transpiler. For the source code of the `pscpp` purescript-to-C++ transpiler, please see the [cpp branch](https://github.com/andyarvanitis/purescript-native/tree/cpp).
6+A small strongly, statically typed compile-to-JS language with basic extensible records and type-safe blocks
67
7−#### Platforms
8−* Although purescript-native should work on any platform supporting PureScript and modern C++ or Go, the generated code and resulting binary executables have been tested on:
9− * macOS Mojave 10.14.5 – full test suite
10− * For C++, Xcode 10.2.1's `clang` was used
11− * For Go, `go` version 1.12.7 was used
12− * Windows 10 x64 – full test suite
13− * C++: Visual Studio 2017 and `clang`
14− * Go: `go` version 1.12.7
15− * Linux Debian 9.5 amd64 – full test suite (C++)
16− * Default versions of `clang` and `gcc`
17− * Linux Debian 10 amd64 – full test suite (Go)
18− * Default version of `go`
19− * Raspberry Pi 3 B+ Raspbian official build (ARM), default versions of `clang` and `gcc`
8+## Current Features
209
10+- Full Type Inference
11+- Higher Kinded Polymorphism
12+- Support for basic Javascript types
13+- Extensible records
14+- Type-safe blocks with for/while/assignment etc.
15+- Functions with multiple arguments
16+- Pattern matching
17+- Simple FFI
18+
19+## Experimental Features
2120
22−#### Requirements for building `pscpp` and `psgo`
21+- Modules
22+- Rank N Types
2323
24−* [Haskell Stack](https://docs.haskellstack.org/en/stable/README/) (if you're running macOS 10.14.5+, you can use pre-built binaries from [here](https://github.com/andyarvanitis/purescript-native/releases/))
24+## Try It!
2525
26−#### Requirements for using PureScript + purescript-native
26+Try PureScript in the browser [here](http://tryps.functorial.com/).
2727
28−* A recent version (0.13+) of [purescript](https://github.com/purescript/purescript/releases).
28+## License
2929
30−* For `pscpp`, a C++11-capable toolchain, e.g. recent versions of clang, gcc, Microsoft Visual Studio
31− * GNU Make + `psc-package` is the default supported build tool, but you should be able to use your favorite package manager, C++ build system, tools, debuggers, etc.
32−* For `psgo`, the [Go toolchain](https://golang.org) for your system
33− * You can use your favorite PureScript package manager and build tools – but for simplicity, [`spago`](https://github.com/spacchetti/spago) is recommended.
30+See the `LICENSE` file
3431
32+## Contributing
3533
36−#### For more information and a Getting Started guide, please see
37−* [`pscpp`](https://github.com/andyarvanitis/purescript-native/blob/cpp/README-cpp.md)
38−* [`psgo`](https://github.com/andyarvanitis/purescript-native/blob/golang/README-go.md)
34+Contributions are always encouraged! Please see `CONTRIBUTING.md` for guidelines.
35+
36+## Installation
37+
38+Install the Haskell Plaform, then `cabal install`
39+
40+## Usage
41+
42+The `psc` executable takes a list of PureScript source files as arguments and by default writes out its errors or output to the console.
43+
44+The following options are supported:
45+
46+ -o --output
47+ Write the generated Javascript to the specified file
48+
49+ -e --foreign imports
50+ Write a list of foreign imports declarations to the specified file in addition to generating Javascript output
51+
52+## Motivation
53+
54+I was looking for a simple functional language which would compile to JavaScript and have the following characteristics:
55+
56+- Generate simple readable Javascript
57+- Provide the ability to compile to tight loops if necessary
58+- Reasonable type system
59+- Ideally, written in a programming language I would enjoy working in
60+- Provides a simple interface to existing Javascript code
61+
62+I didn't find exactly what I was looking for, so I wrote PureScript. It doesn't have everything right now, but it should serve as a simple core on which to develop new ideas.
63+
64+PureScript is *not* designed to be a general-purpose programming language. The primary use case is as a generator for purely-functional core libraries, with the main application code written in another language.
65+
66+PureScript can also be seen as a trade-off between a theoretically ideal language and one which generates reasonably high performance code.
67+
68+## Hello, PureScript!
69+
70+As an introductory example, here is the usual "Hello World" written in PureScript:
71+
72+```haskell
73+foreign import console :: { log :: String -> {} }
74+
75+main = \() -> console.log "Hello, World!"
76+```
77+
78+which compiles to the following Javascript:
79+
80+```javascript
81+function main() {
82+ console.log("Hello, World!");
83+}
84+```
85+
86+## Another Example
87+
88+The following code defines a `Person` data type and a function to generate a string representation for a `Person`:
89+
90+```haskell
91+data Person = Person { name :: String, age :: Number }
92+
93+foreign import numberToString :: Number -> String
94+
95+showPerson :: Person -> String
96+showPerson = \p -> case p of
97+ Person o -> o.name ++ ", aged " ++ numberToString(o.age)
98+```
99+
100+Line by line, this reads as follows:
101+
102+- `Person` is a data type with one constructor, also called `Person`
103+ - The `Person` constructor takes an object with two properties, `name` which is a `String`, and `age` which is a `Number`
104+- The `numberToString` function is written in Javascript, and converts a `Number` to its `String` representation
105+- The `showPerson` function takes a `Person` and returns a `String`
106+- `showPerson` works by case analysis on its argument `p`, first matching the constructor `Person` and then using string concatenation and object accessors to return its result.
107+
108+The generated Javascript looks like this:
109+
110+```javascript
111+var Person = function (value) {
112+ return { ctor: 'Person', value: value };
113+};
114+
115+function showPerson(p) {
116+ if (p.ctor === "Person") {
117+ return p.value.name + ", aged " + itoa(p.value.age)
118+ };
119+ throw "Failed pattern match"
120+}
121+
122+```
123+
124+## Type System
125+
126+The type system defines the following types:
127+
128+- Primitive Types
129+ - Number
130+ - String
131+ - Boolean
132+- Arrays
133+ - E.g. `[String]`, `[[Number]]`
134+- Records
135+ - E.g. `{ foo :: String, bar :: Number }`
136+- Tagged Unions
137+ - E.g. `data Foo a = Foo | Bar String`
138+- Functions
139+ - E.g. `Number -> String`
140+ - E.g. `(Number, Number) -> Number`
141+ - Functions can have zero or more arguments
142+- Polymorphic types (for top level declarations only)
143+ - E.g. `forall a. a -> a`
144+
145+## Primitive Types
146+
147+The three primitive types `String`, `Number` and `Boolean` correspond to their Javascript equivalents at runtime.
148+
149+PureScript supports the same binary and unary operations on primitive types as Javascript, with the following exceptions:
150+
151+- String concatenation is denoted `++` to differentiate it from numeric addition, `+`
152+- PureScript's `==` and `!=` correspond to Javascript's strong equality tests `===` and `!==`
153+
154+Examples:
155+
156+```haskell
157+num = 1 + 2 * 3
158+str = "Hello, " ++ "World!"
159+bool = 1 > 2 || true
160+```
161+
162+## Arrays
163+
164+PureScript arrays correspond to Javascript arrays at runtime, but all elements must have the same type.
165+
166+Array literals look like Javascript array literals: `[1, 2, 3]`
167+
168+Array elements can be read using array index notation `arr !! index`
169+
170+## Records
171+
172+PureScript records correspond to Javascript objects.
173+
174+Record literals look like Javascript object literals: `{ foo: "Foo" }`
175+
176+Properties can be read by using dot notation: `o.foo`
177+
178+## Tagged Unions
179+
180+Tagged unions consist of one or more constructors, each of which takes zero or one arguments.
181+
182+Tagged unions can only be created using their constructors, and deconstructed through pattern matching (see later).
183+
184+For example:
185+
186+```haskell
187+data Foo a = Foo | Bar String
188+
189+runFoo = \foo -> case foo of
190+Foo -> "It's a Foo"
191+Bar s -> "It's a Bar. The string is " ++ s
192+
193+runFoo Foo ++ runFoo (Bar "Test")
194+```
195+
196+In the example, Foo is a tagged union type which has two constructors. It's first constructor `Foo` takes no argument, and it's second `Bar` takes one, which must be a String.
197+
198+`runFoo` is an example of pattern matching on a tagged union type to discover its constructor, and the last line shows how `Foo`s are constructed.
199+
200+## Functions
201+
202+Functions in PureScript can have zero or more arguments in general, just like in Javascript.
203+
204+Functions are introduced by using a backslash followed by a list of argument names:
205+
206+```haskell
207+test1 = \a b -> a + b
208+```
209+
210+which would correspond to the Javascript
211+
212+```javascript
213+function test1(a) {
214+ return function (b) {
215+ return a + b;
216+ }
217+}
218+```
219+
220+Multiple argument functions can be introduced by wrapping the arguments in parentheses, and separating them with commas:
221+
222+```haskell
223+test1 = \(a, b) -> a + b
224+```
225+
226+which generates
227+
228+```javascript
229+function test1(a, b) {
230+ return a + b;
231+}
232+```
233+
234+In the case of a function with no arguments, the parentheses may be omitted, as follows:
235+
236+```haskell
237+test2 = \ -> 100
238+```
239+
240+which would correspond to the Javascript `function test2() { return 100; }`
241+
242+Multiple-argument and single-argument syntax can be mixed, as follows:
243+
244+```haskell
245+test3 = \a (b, c) d -> a + b + c + d
246+```
247+
248+which generates
249+
250+```javascript
251+function test3(a) {
252+ return function (b, c) {
253+ return function (d) {
254+ return a + b + c + d;
255+ }
256+ }
257+}
258+```
259+
260+Functions are applied by providing their arguments inside parentheses:
261+
262+```haskell
263+test1(1, 2, 3)
264+test2()
265+```
266+
267+A special case is made in the case of functions with one argument. These functions can be applied without parentheses, and function application associates to the left:
268+
269+```haskell
270+-- has type Number -> Number -> Number -> Number
271+addThree = \a b c -> a + b + c
272+
273+-- has type Number -> Number -> Number
274+addThree 1
275+
276+-- has type Number -> Number
277+addThree 1 2
278+
279+-- has type Number
280+addThree 1 2 3
281+```
282+
283+## Polymorphic Types
284+
285+Expressions defined at the top level may have polymorphic types.
286+
287+Here is an example:
288+
289+```haskell
290+identity = \x -> x
291+```
292+
293+`identity` is inferred to have (polymorphic) type `forall t0. t0 -> t0`. This means that for any type `t0`, `identity` can be given a value of type `t0` and will give back a value of the same type.
294+
295+A type annotation can also be provided:
296+
297+```haskell
298+identity :: forall a. a -> a
299+identity = \x -> x
300+```
301+
302+Functions may also be polymorphic in row types or type variables with other kinds (see "Kind System"):
303+
304+```haskell
305+addProps = \o -> o.foo + o.bar
306+```
307+
308+Here, `addProps` is inferred to have type `forall r. { foo :: Number, bar :: Number | r } -> Number`. That is, it can take any type which has properties `Foo` and `Bar`, and *any other record properties*.
309+
310+So, the following compiles:
311+
312+```haskell
313+addProps { foo: 1, bar: 2, baz: 3 }
314+```
315+
316+but the following does not:
317+
318+```haskell
319+addProps { foo: 1 }
320+```
321+
322+since the `bar` property is missing.
323+
324+Again, a type annotation can be provided if necessary.
325+
326+## Rank N Types (Experimental Feature)
327+
328+It is also possible for the `forall` quantifier to appear on the left of a function arrow, inside types record fields and data constructors, and in type synonyms.
329+
330+In most cases, a type annotation is necessary when using this feature.
331+
332+As an example, we can pass a polymorphic function as an argument to another function:
333+
334+```haskell
335+poly :: (forall a. a -> a) -> Boolean
336+poly = \f -> (f 0 < 1) == f true
337+```
338+
339+Notice that the polymorphic function's type argument is instantiated to both `Number` and `Boolean`.
340+
341+An argument to `poly` must indeed be polymorphic. For example, the following fails:
342+
343+```haskell
344+test = poly (\n -> n + 1)
345+```
346+
347+since the skolemized type variable `a` does not unify with `Number`.
348+
349+## Type Inference
350+
351+All types can be inferred, but annotations can optionally be provided.
352+
353+## Kind System
354+
355+There are two primitive kinds, the kind `*` of types and the kind `#` of rows. Higher kinded types are also supported. That is, a type variable can refer to not only a type or a row, but a type constructor, or row constructor etc.
356+
357+## Whitespace
358+
359+Syntax is whitespace sensitive. The general rule of thumb is that declarations which span multiple lines should be indented past the column on which they were first defined on their subsequent lines.
360+
361+That is, the following is valid:
362+
363+```haskell
364+foo = bar(x) +
365+ baz(x)
366+```
367+
368+But this is not:
369+
370+```haskell
371+foo = bar(x) +
372+baz(x)
373+```
374+
375+## Do Notation
376+
377+There is a keyword `do`, which does not have the same purpose as the `do` keyword in Haskell.
378+
379+`do` introduces a block. Blocks consist of statements, and must return a value of the same type on every branch of execution.
380+
381+The following types of statement are supported:
382+
383+- Variable introduction
384+- Variable assignment
385+- For loops
386+- For-each loops
387+- While loops
388+- If-Then-Else statements
389+
390+Here is an example of a power function defined using a block:
391+
392+```haskell
393+pow = \n p -> do
394+ var m = n
395+ for i <- 0 until p:
396+ m = m * n
397+ return m
398+```
399+
400+Blocks enable local mutation of their variables, but mutation is not allowed in general. The type system prevents mutable variables from escaping their scope.
401+
402+That is, while the example above is valid, the following does not compile:
403+
404+```haskell
405+incr = \n -> do
406+ n = n + 1
407+ return n
408+```
409+
410+The variable `n` is not mutable, and so the assignment in the first line of the `do` block is not allowed.
411+
412+This function can be rewritten as follows:
413+
414+```haskell
415+incr = \n -> do
416+ var m = n
417+ m = m + 1
418+ return m
419+```
420+
421+## For Loops
422+
423+For loops look like this:
424+
425+```haskell
426+total = do
427+ var n = 0
428+ for i <- 0 until 10:
429+ n = n + i
430+ return n
431+```
432+
433+The bounds `0` and `10` are inclusive and exclusive respectively.
434+
435+## For-Each Loops
436+
437+For each loops loop over the elements in an array using the `Object.forEach` method. A polyfill may be required for some browsers:
438+
439+```haskell
440+total = \arr -> do
441+ var n = 0
442+ foreach i in arr:
443+ n = n + i
444+ return n
445+```
446+
447+## While Loops
448+
449+The syntax of a while loop is similar to a foreach loop:
450+
451+```haskell
452+log2 = \n -> do
453+ var count = 0
454+ var m = n
455+ while m > 1:
456+ m = m / 2
457+ count = count + 1
458+ return count
459+```
460+
461+## If-Then-Else Statements
462+
463+Else branches are optional, and may contain further `if` statements, just as in Javascript:
464+
465+```haskell
466+collatz = \n -> do
467+ var count = 0
468+ var m = n
469+ while m > 1:
470+ if m % 2 == 0:
471+ m = m / 2
472+ else:
473+ m = m * 3 + 1
474+ count = count + 1
475+ return count
476+```
477+
478+## If-Then-Else Expressions
479+
480+The `if`, `then` and `else` keywords can also be used to create conditional expressions. In this case, the `else` block is always required.
481+
482+For example,
483+
484+```haskell
485+conditional = if 2 > 1 then "ok" else "oops"
486+```
487+
488+## Pattern Matching
489+
490+Pattern matching deconstructs a value to bring zero or more expressions into scope. Pattern matches are introduced with the `case` keyword.
491+
492+Pattern matches have the following general form
493+
494+```haskell
495+case value of
496+ pattern -> result
497+ -- ...
498+ pattern -> result
499+```
500+
501+The following pattern types are supported:
502+
503+- Wildcard pattern
504+- Literal patterns
505+- Variable pattern
506+- Array patterns
507+- Record patterns
508+- Named patterns
509+- Guards
510+
511+Patterns need not be exhaustive. A pattern match failed exception will be thrown at runtime if no pattern matches the input.
512+
513+## Wildcard Patterns
514+
515+The wilcard `_` matches any input and brings nothing into scope:
516+
517+```haskell
518+case x of
519+ _ -> 0
520+```
521+
522+## Literal Patterns
523+
524+Literal patterns are provided to match on primitives:
525+
526+```haskell
527+case booleanValue of
528+ true -> 0
529+ false -> 1
530+
531+case stringValue of
532+ "Foo" -> 0
533+ _ -> 1
534+
535+case numericValue of
536+ 0 -> 0
537+ _ -> 1
538+```
539+
540+## Variable Pattern
541+
542+A variable pattern matches any input and binds that input to its name:
543+
544+```haskell
545+case foo(x) of
546+ result -> bar(result)
547+```
548+
549+## Array Patterns
550+
551+Array patterns match an input which is an array, and bring its elements into scope.
552+
553+For example:
554+
555+```haskell
556+f = \arr -> case arr of
557+ [x] -> x
558+ [x, y : xs] -> x * y + f(xs)
559+ _ -> 0
560+```
561+
562+Here, the first pattern only matches arrays of length one, and brings the first element of the array into scope.
563+
564+The second pattern matches arrays with two or more elements, and brings the first and second elements into scope, along with the remainder of the array as the variable `xs`.
565+
566+## Record Patterns
567+
568+Record patterns match an input which is a record, and bring its properties into scope.
569+
570+```haskell
571+f = \o -> case o of
572+ { foo = "Foo" } -> o.bar
573+ _ -> 0
574+```
575+
576+## Nested Patterns
577+
578+The patterns above can be combined to create larger patterns.
579+
580+For example:
581+
582+```haskell
583+f = \o -> case o of
584+ { arr = [x:_], take = "car" } -> x
585+ { arr = [_,x:_], take = "cadr" } -> x
586+ _ -> 0
587+```
588+
589+## Named Patterns
590+
591+Named patterns bring additional names into scope when using nested patterns. Any pattern can be named by using the `@` symbol:
592+
593+```haskell
594+f = \arr -> case arr of
595+ a@[_,_:_] -> a
596+ a -> a
597+```
598+
599+Here, in the first pattern, any array with two or more elements will be matched and bound to the variable `a`.
600+
601+## Guards
602+
603+Guards are used to impose additional constraints inside a pattern using boolean-valued expressions, and are introduced with a pipe after the pattern:
604+
605+```haskell
606+evens = \arr -> case arr of
607+ [] -> 0
608+ [x:xs] | x % 2 == 0 -> 1 + evens xs
609+ [_:xs] -> evens xs
610+```
611+
612+## Type Synonyms
613+
614+For convenience, it is possible to declare a synonym for a type using the `type` keyword. Type synonyms can include type arguments.
615+
616+For example:
617+
618+```haskell
619+type Foo = { foo :: Number, bar Number }
620+
621+addFoo :: Foo -> Number
622+addFoo = \o -> o.foo + o.bar
623+```
624+## Record Updates
625+
626+Properties on records can be updated using the following syntax: `o { key = value, ..., key = value }`
627+
628+For example, the following function increments the `foo` property on its argument:
629+
630+```haskell
631+incr = \o -> o { foo = o.foo + 1 }
632+```
633+
634+The generated Javascript assumes the existence of a method called `Object.extend` such that `Object.extend(o, p)` takes an object `o` and generates a shallow copy of `o` including the properties of `p`. A simple JQuery implementation of this specification is
635+
636+```haskell
637+Object.prototype.extend = function(o, p) {
638+ return $.extend({}, o, p);
639+};
640+```
641+
642+## Operators
643+
644+In addition to the standard operators, user-defined infix operators can be created by enclosing names in parentheses:
645+
646+E.g. to create a synonym for string concatenation:
647+
648+```haskell
649+(<>) = \s1 s2 -> s1 ++ s2
650+
651+greeting = "Hello" <> "World!"
652+```
653+
654+Regular functions can be used as operators by enclosing their names in backticks:
655+
656+```haskell
657+foo = \x y -> x * y + y
658+
659+test = 10 `foo` 20
660+```
661+
662+Fixity declarations can associate a precedence level, which is a natural number, to a user-defined operator, and specify which way it associates:
663+
664+```haskell
665+infixl 5 <>
666+infixr 7 %%
667+```
668+
669+## Modules
670+
671+Simple modules are supported by the `module` keyword.
672+
673+Introduce a module as follows, with a list of indented declarations:
674+
675+```haskell
676+module A where
677+
678+ foo = \x -> x
679+```
680+
681+Modules may be nested:
682+
683+```haskell
684+module A where
685+
686+ foo = \x -> x
687+
688+ module B where
689+
690+ bar = \y -> y
691+```
692+
693+Names may be qualified by using a dot:
694+
695+```
696+foo = A.foo
697+bar = A.B.bar
698+```
699+
700+All the names in a module can be aliased using the `import` declaration:
701+
702+```haskell
703+import A
704+```
705+
706+You can also limit which names are going to be aliased:
707+
708+```haskell
709+import A.B (bar)
710+```
711+
712+## Foreign Function Interface
713+
714+The `foreign import` keyword declares a value which is defined in Javascript, and its type:
715+
716+```haskell
717+foreign import pow :: (Number, Number) -> Number
718+```
719+
720+To declare a new type with no constructors, use `foreign import data` and provide the kind:
721+
722+```haskell
723+foreign import data IO :: * -> *
724+
725+foreign import console :: {
726+ log :: String -> IO {}
727+}
728+```
729+
730+To alias a name of a field defined on a Javascript type to a PureScript function, use `foreign import member`. For example, to define a function `length` which accesses the `length` member of an array:
731+
732+```haskell
733+foreign import member "length" length :: forall a. [a] -> Number
734+```
39735
40736
41−---
Setup.hsdeleted−2
@@ -1,2 +0,0 @@
1−import Distribution.Simple
2−main = defaultMain
Setup.lhsnew file+6
@@ -0,0 +1,6 @@
1+#!/usr/bin/runhaskell
2+> module Main where
3+> import Distribution.Simple
4+> main :: IO ()
5+> main = defaultMain
6+
app/Main.hsdeleted−250
@@ -1,250 +0,0 @@
1−{-# LANGUAGE OverloadedStrings #-}
2−{-# LANGUAGE RankNTypes #-}
3−{-# LANGUAGE TemplateHaskell #-}
4−
5−module Main where
6−
7−import Control.Monad (when)
8−import qualified Control.Monad.Parallel as Par
9−import Data.Aeson
10−import Data.Aeson.Types
11−import Data.Char
12−import Data.FileEmbed (embedFile)
13−import Data.List (delete, intercalate, isPrefixOf, nub, partition, (\\))
14−import Data.Maybe
15−import Data.Monoid ((<>))
16−import Data.Version
17−import Control.Monad.Supply
18−import Control.Monad.Supply.Class
19−import Text.Printf
20−
21−import System.Environment
22−import System.Directory (createDirectoryIfMissing, doesDirectoryExist, doesFileExist, getCurrentDirectory, getModificationTime)
23−import System.FilePath ((</>), joinPath, splitDirectories, takeDirectory)
24−import System.FilePath.Find
25−import System.Process
26−
27−import Data.Text (Text)
28−import qualified Data.Text as T
29−import qualified Data.Text.Encoding as T
30−import qualified Data.Text.Lazy as L
31−import qualified Data.Text.Lazy.Encoding as L
32−import qualified Data.ByteString as B
33−
34−import Development.GitRev
35−
36−import Language.PureScript.AST.Literals
37−import Language.PureScript.CoreFn
38−import Language.PureScript.CoreFn.FromJSON
39−
40−import CodeGen.IL
41−import CodeGen.IL.Common
42−import CodeGen.IL.Printer
43−
44−import Tests
45−
46−data Command = Build | Run (Maybe String)
47−
48−parseJson :: Text -> Value
49−parseJson text
50− | Just fileJson <- decode . L.encodeUtf8 $ L.fromStrict text = fileJson
51− | otherwise = error "Bad json"
52−
53−jsonToModule :: Value -> Module Ann
54−jsonToModule value =
55− case parse moduleFromJSON value of
56− Success (_, r) -> r
57− _ -> error "Failed parsing corefn. Psgo currently supports purescript <= 0.13.8"
58−
59−splitArgs :: [String] -> ([String], Maybe String, [String])
60−splitArgs args =
61− let (opts, files) = partition (isPrefixOf "--") args
62− opts' = (map . map) toLower opts
63− (moduleName, files') =
64− if null files
65− then (Nothing, files)
66− else
67− if "--run" `elem` opts' && length (files) >= 1
68− then do
69− let file0 = head files
70− files' = tail files
71− case file0 of
72− "Main.main" -> (Just "Main", files')
73− "Test.Main.main" -> (Just "Test.Main", files')
74− _ -> (Nothing, files)
75− else (Nothing, files)
76− in (opts, moduleName, files')
77−
78−main :: IO ()
79−main = do
80− args <- getArgs
81− -- let (opts, files) = partition (isPrefixOf "--") args
82− let (opts, moduleName, files) = splitArgs args
83− opts' = (map . map) toLower opts
84− if "--tests" `elem` opts'
85− then runTests
86− else do
87− if "--help" `elem` opts' || "--version" `elem` opts'
88− then do
89− when ("--help" `elem` opts') $ do
90− putStrLn help
91− when ("--version" `elem` opts') $ do
92− let branch = $(gitBranch)
93− details | branch == "golang" = "master, commit " ++ $(gitHash)
94− | otherwise = branch
95− putStrLn $ details ++ if $(gitDirty) then " (DIRTY)" else ""
96− else do
97− if null files
98− then do
99− currentDir <- getCurrentDirectory
100− processFiles opts' [currentDir]
101− else
102− processFiles opts' files
103− if "--run" `elem` opts
104− then runBuildTool $ Run moduleName
105− else when ("--no-build" `notElem` opts) $
106− runBuildTool Build
107− return ()
108− where
109− processFiles :: [String] -> [FilePath] -> IO ()
110− processFiles opts [file] = do
111− isDir <- doesDirectoryExist file
112− if isDir then do
113− files <- find always (fileName ==? corefn) file
114− if null files
115− then errorNoCorefnFiles
116− else generateFiles opts files
117− else generateFiles opts [file]
118− processFiles opts files = do
119− generateFiles opts files
120− basePath :: [FilePath] -> FilePath
121− basePath files =
122− let filepath = takeDirectory (head files) in
123− joinPath $ (init $ splitDirectories filepath)
124− generateFiles :: [String] -> [FilePath] -> IO ()
125− generateFiles opts files = do
126− let baseOutpath = basePath files
127− writeSupportFiles baseOutpath
128− Par.mapM (generateCode opts baseOutpath) files
129− return ()
130− runBuildTool :: Command -> IO ()
131− runBuildTool cmd = do
132− project <- projectEnv
133− case cmd of
134− Build -> do
135− callProcess "go" ["build", T.unpack project </> modPrefix' </> "Main"]
136− Run moduleName -> do
137− callProcess "go" ["run", T.unpack project </> modPrefix' </> fromMaybe "Main" moduleName]
138− return ()
139−
140−generateCode :: [String] -> FilePath -> FilePath -> IO ()
141−generateCode opts baseOutpath jsonFile = do
142− jsonModTime <- getModificationTime jsonFile
143− let filepath = takeDirectory jsonFile
144− dirparts = splitDirectories $ filepath
145− mname = last dirparts
146− basedir = joinPath $ init dirparts
147− mname' = T.pack mname
148− possibleFileName = basedir </> (T.unpack mname') </> implFileName mname'
149− exists <- doesFileExist possibleFileName
150− if exists
151− then do
152− modTime <- getModificationTime possibleFileName
153− when (modTime < jsonModTime) $
154− transpile opts baseOutpath jsonFile
155− else transpile opts baseOutpath jsonFile
156−
157−transpile :: [String] -> FilePath -> FilePath -> IO ()
158−transpile opts baseOutpath jsonFile = do
159− jsonText <- T.decodeUtf8 <$> B.readFile jsonFile
160− project <- projectEnv
161− let module' = jsonToModule $ parseJson jsonText
162− ((_, foreigns, asts, implHeader, implFooter), _) <- runSupplyT 5 (moduleToIL module' project)
163− let mn = moduleNameToIL' $ moduleName module'
164− implementation = prettyPrintIL asts
165− outpath = joinPath [baseOutpath, T.unpack mn]
166− implPath = outpath </> implFileName mn
167− createDirectoryIfMissing True outpath
168− putStrLn implPath
169− B.writeFile implPath $ T.encodeUtf8 (implHeader <> implementation <> implFooter)
170−
171−writeSupportFiles :: FilePath -> IO ()
172−writeSupportFiles baseOutpath = do
173− currentDir <- getCurrentDirectory
174− let outputdir = T.pack $ baseOutpath \\ currentDir
175− ffiOutpath = currentDir </> subdir
176− createDirectoryIfMissing True baseOutpath
177− createDirectoryIfMissing True ffiOutpath
178− writeModuleFile outputdir currentDir $(embedFile "support/go.mod.working")
179− writeModuleFile outputdir baseOutpath $(embedFile "support/go.mod.output")
180− writeModuleFile outputdir ffiOutpath $(embedFile "support/go.mod.ffi-loader")
181− writeLoaderFile ffiOutpath $(embedFile "support/ffi-loader.go")
182− where
183− writeModuleFile :: Text -> FilePath -> B.ByteString -> IO ()
184− writeModuleFile outputdir path modText = do
185− let goModSource = path </> "go.mod"
186− goModSourceExists <- doesFileExist goModSource
187− when (not goModSourceExists) $ do
188− project <- projectEnv
189− let project' = T.unpack project
190− outputdir' = tail $ T.unpack outputdir
191− modText' = T.replace "$PROJECT" project $ T.decodeUtf8 modText
192− replaceLoader = project' </> ffiLoader' <> "=." </> subdir
193− replaceOutput = project' </> modPrefix' <> "=." </> outputdir'
194− B.writeFile goModSource $ T.encodeUtf8 modText'
195− callProcess "go" ["mod", "edit", "-replace", replaceLoader]
196− callProcess "go" ["mod", "edit", "-replace", replaceOutput]
197− callProcess "go" ["clean", "-modcache"]
198− writeLoaderFile :: FilePath -> B.ByteString -> IO ()
199− writeLoaderFile ffiOutpath loaderText = do
200− let loaderSource = ffiOutpath </> "ffi_loader.go"
201− loaderSourceExists <- doesFileExist loaderSource
202− when (not loaderSourceExists) $ do
203− B.writeFile loaderSource loaderText
204−
205−implFileName :: Text -> FilePath
206−implFileName mn = ((\c -> if c == '.' then '_' else c) <$> T.unpack mn) <> ".go"
207−
208−projectEnv :: IO Text
209−projectEnv = do
210− T.pack . fromMaybe defaultProject <$> lookupEnv goproject
211−
212−help :: String
213−help = "Usage: psgo OPTIONS COREFN-FILES\n\
214− \ PureScript to native (via go) compiler\n\n\
215− \Available options:\n\
216− \ --help Show this help text\n\n\
217− \ --version Show the version number\n\n\
218− \ --run Run the generated go code directly, without building an\n\
219− \ executable\n\
220− \ --no-build Generate go source files, but do not build an executable\n\
221− \ --tests Run test cases (under construction)\n\n\
222− \See also:\n\
223− \ purs compile --help\n"
224−
225−corefn :: String
226−corefn = "corefn.json"
227−
228−goSrc :: String
229−goSrc = ".go"
230−
231−goproject :: String
232−goproject = "GOPROJECT"
233−
234−defaultProject :: String
235−defaultProject = "project.localhost"
236−
237−modPrefix' :: String
238−modPrefix' = T.unpack modPrefix
239−
240−ffiLoader' :: String
241−ffiLoader' = T.unpack ffiLoader
242−
243−subdir :: String
244−subdir = T.unpack modLabel
245−
246−errorNoCorefnFiles :: IO ()
247−errorNoCorefnFiles = do
248− ioError . userError $ "no compiled purescript '" <> corefn <> "' files found –\n" <>
249− " make sure to use the '--codegen corefn' option with your purs\n" <>
250− " project build tool"
default.nixdeleted−23
@@ -1,23 +0,0 @@
1−let
2− haskellNix = import (builtins.fetchTarball https://github.com/input-output-hk/haskell.nix/archive/master.tar.gz) {};
3− nixpkgsSrc = haskellNix.sources.nixpkgs-1903; # has hsc2hs-0.68.4
4−
5− hsc2hsOverlay = self: super: {
6− buildPackages = super.buildPackages // {
7− hsc2hs = super.haskellPackages.hsc2hs;
8− };
9− };
10−
11− nixpkgsArgs = haskellNix.nixpkgsArgs // {
12− overlays = haskellNix.nixpkgsArgs.overlays ++ [ hsc2hsOverlay ];
13− };
14−in
15−{ pkgs ? import nixpkgsSrc nixpkgsArgs
16−}:
17−
18−pkgs.haskell-nix.stackProject {
19− src = pkgs.haskell-nix.haskellLib.cleanGit {
20− name = "purescript-native";
21− src = ./.;
22− };
23−}
examples/.gitignorenew file+1
@@ -0,0 +1 @@
1+shelltest/tests
examples/failing/Arrays.pursnew file+1
@@ -0,0 +1 @@
1+test = \arr -> arr !! (0 !! 0)
examples/failing/Import.pursnew file+1
@@ -0,0 +1 @@
1+import UndefinedModule
examples/failing/KindError.pursnew file+1
@@ -0,0 +1 @@
1+data KindError f a = One f | Two f a
examples/failing/LocalVars.pursnew file+3
@@ -0,0 +1,3 @@
1+bad = \x -> do
2+ x = x + 1
3+ return x
examples/failing/PatternScope.pursnew file+7
@@ -0,0 +1,7 @@
1+-- Pattern scope doesn't leak
2+foobar = \x -> do
3+ var y = case x of
4+ z | z % 2 == 0 -> 1
5+ _ -> 2
6+ return z
7+
examples/failing/Rank2Types.pursnew file+3
@@ -0,0 +1,3 @@
1+foreign import test :: (forall a. a -> a) -> Number
2+
3+test1 = test (\n -> n + 1)
examples/failing/ReadShow.pursnew file+5
@@ -0,0 +1,5 @@
1+foreign import read :: forall a. String -> a
2+
3+foreign import show :: forall a. a -> String
4+
5+test = \x -> show (read x)
examples/failing/SkolemEscape.pursnew file+3
@@ -0,0 +1,3 @@
1+foreign import foo :: (forall a. a -> a) -> Number
2+
3+test = \x -> foo x
examples/failing/TypeError.pursnew file+3
@@ -0,0 +1,3 @@
1+test = do
2+ var x = 0
3+ return x ++ "A"
examples/passing/Arrays.pursnew file+20
@@ -0,0 +1,20 @@
1+test1 = \arr -> arr !! 0 + arr !! 1
2+
3+test2 = \arr -> case arr of
4+ [x, y] -> x + y
5+ [x] -> x
6+ [] -> 0
7+ (x : y : _) -> x + y
8+
9+data Tree = One Number | Some [Tree]
10+
11+test3 = \(tree, sum) -> case tree of
12+ One n -> n
13+ Some (n1 : n2 : rest) -> test3(n1, sum) * 10 + test3(n2, sum) * 5 + sum(rest)
14+
15+test4 = \arr -> case arr of
16+ [] -> 0
17+ [_] -> 0
18+ x : y : xs -> x * y + test4 xs
19+
20+
examples/passing/Assign.pursnew file+7
@@ -0,0 +1,7 @@
1+sum = \n -> {
2+ var x = 0;
3+ for (i <- 0 until n) {
4+ x = x + i;
5+ }
6+ return x;
7+ }
examples/passing/Auto.pursnew file+9
@@ -0,0 +1,9 @@
1+data Auto s i o = Auto { state :: s, step :: s -> i -> o }
2+
3+type SomeAuto i o = forall r. (forall s. Auto s i o -> r) -> r
4+
5+exists :: forall s i o. s -> (s -> i -> o) -> SomeAuto i o
6+exists = \state step f -> f (Auto { state: state, step: step })
7+
8+run :: forall i o. SomeAuto i o -> i -> o
9+run = \s i -> s (\a -> case a of Auto a -> a.step a.state i)
examples/passing/Church.pursnew file+12
@@ -0,0 +1,12 @@
1+type List a = forall r. r -> (a -> r -> r) -> r
2+
3+empty :: forall a. List a
4+empty = \r f -> r
5+
6+cons :: forall a. a -> List a -> List a
7+cons = \a l r f -> f a (l r f)
8+
9+append :: forall a. List a -> List a -> List a
10+append = \l1 l2 r f -> l2 (l1 r f) f
11+
12+test = append (cons 1 empty) (cons 2 empty)
examples/passing/Conditional.pursnew file+5
@@ -0,0 +1,5 @@
1+fns = \f -> if f true then f else \x -> x
2+
3+bin = \x -> if x && !x then x else x
4+
5+not = \x -> if x then false else true
examples/passing/Cons.pursnew file+7
@@ -0,0 +1,7 @@
1+infixr 6 :
2+
3+foreign import (:) :: forall a. a -> [a] -> [a]
4+
5+test1 = \xs -> 1 : xs
6+
7+test2 = 1 : 2 : 3 : []
examples/passing/Console.pursnew file+23
@@ -0,0 +1,23 @@
1+foreign import data IO :: * -> *
2+
3+foreign import ret :: forall a. a -> IO a
4+
5+foreign import (>>=) :: forall a b. IO a -> (a -> IO b) -> IO b
6+
7+(*>) = \a -> \b -> a >>= \x -> b
8+
9+foreign import putStrLn :: String -> IO {}
10+
11+replicateM_ :: forall a. Number -> IO a -> IO {}
12+replicateM_ = \n -> \x ->
13+ {
14+ var io = ret {};
15+ for (i <- 0 until n)
16+ {
17+ io = x *> io;
18+ }
19+ return io;
20+ }
21+
22+main = replicateM_ 10 (putStrLn "Hello World!")
23+
examples/passing/ExternData.pursnew file+13
@@ -0,0 +1,13 @@
1+foreign import data IO :: * -> *
2+
3+foreign import fmap :: forall a b. IO a -> (a -> b) -> IO b
4+
5+foreign import ret :: forall a. a -> IO a
6+
7+foreign import bind :: forall a b. IO a -> (a -> IO b) -> IO b
8+
9+foreign import showMessage :: String -> IO { }
10+
11+foreign import prompt :: IO String
12+
13+main = prompt `bind` \s -> showMessage s
examples/passing/FFI.pursnew file+5
@@ -0,0 +1,5 @@
1+foreign import foo :: String -> String
2+
3+bar :: String -> String
4+
5+bar = \x -> foo (x ++ "bar")
examples/passing/Foreach.pursnew file+7
@@ -0,0 +1,7 @@
1+sum = \ns -> {
2+ var total = 0;
3+ foreach (n in ns) {
4+ total = total + n;
5+ }
6+ return total;
7+ }
examples/passing/Functions.pursnew file+11
@@ -0,0 +1,11 @@
1+test1 = \ -> 0
2+
3+test2 = \() -> 0
4+
5+test3 = \a (b, c) d -> a + b + c + d
6+
7+test4 = \(a) -> a
8+
9+test5 = \(%%) -> 1 %% 2
10+
11+test6 = \((+++), (***)) -> 1 +++ 2 *** 3
examples/passing/Guards.pursnew file+8
@@ -0,0 +1,8 @@
1+collatz = \x -> case x of
2+ y | y % 2 == 0 -> y / 2
3+ y -> y * 3 + 1
4+
5+-- Guards have access to current scope
6+collatz2 = \x y -> case x of
7+ z | y > 0 -> z / 2
8+ z -> z * 3 + 1
examples/passing/IfThenElse.pursnew file+34
@@ -0,0 +1,34 @@
1+testIf =
2+ {
3+ var x = 0;
4+ if (x == 0) {
5+ x = x + 1;
6+ }
7+ return x;
8+ }
9+
10+testIfElse =
11+ {
12+ var x = 0;
13+ if (x == 0) {
14+ x = x + 1;
15+ } else {
16+ x = x - 1;
17+ }
18+ return x;
19+ }
20+
21+testIfElseIf = \x ->
22+ {
23+ var y = x;
24+ while (y > 1) {
25+ if (y % 3 == 0) {
26+ y = y / 3;
27+ } else if (y % 3 == 1) {
28+ y = (y - 1) % 3;
29+ } else {
30+ y = y * 4 + 1;
31+ }
32+ }
33+ return y;
34+ }
examples/passing/Import.pursnew file+12
@@ -0,0 +1,12 @@
1+module Hello where
2+ id :: forall a. a -> a
3+ id = \x -> x
4+
5+ module Nested where
6+ import Hello (id)
7+
8+ foo = id
9+
10+import Hello.Nested
11+
12+main = \() -> foo 42
examples/passing/List.pursnew file+22
@@ -0,0 +1,22 @@
1+data List a
2+ = Nil
3+ | Cons
4+ { head :: a
5+ , tail :: List a
6+ }
7+
8+isNull = \l -> case l of
9+ Nil -> true
10+ _ -> false
11+
12+concat = \l1 -> \l2 -> case l1 of
13+ Nil -> l2
14+ Cons x -> Cons
15+ { head: x.head
16+ , tail: concat x.tail l2
17+ }
18+
19+flatten = \xss -> case xss of
20+ Nil -> Nil
21+ Cons cons -> concat cons.head (flatten cons.tail)
22+
examples/passing/Match.pursnew file+3
@@ -0,0 +1,3 @@
1+data Foo a = Foo
2+
3+foo = \f -> case f of Foo -> "foo"
examples/passing/Maybe.pursnew file+10
@@ -0,0 +1,10 @@
1+data Maybe a = Nothing | Just a
2+
3+maybe :: forall a b. b -> (a -> b) -> Maybe a -> b
4+maybe = \b -> \f -> \m -> case m of
5+ Nothing -> b
6+ Just a -> f a
7+
8+fmap = \m -> \f -> maybe Nothing (\a -> Just (f a)) m
9+
10+bind = \m -> \f -> maybe Nothing f m
examples/passing/MethodCall.pursnew file+6
@@ -0,0 +1,6 @@
1+foreign import foo :: () -> {}
2+
3+test = {
4+ foo();
5+ return 0;
6+ }
examples/passing/Module.pursnew file+22
@@ -0,0 +1,22 @@
1+module Test where
2+
3+ data Foo = Foo String
4+
5+ foo :: Test.Foo -> String
6+ foo = \f -> case f of Foo s -> s ++ "foo"
7+
8+ bar :: Foo -> String
9+ bar = foo
10+
11+ module Inner where
12+
13+ inner = "Inner"
14+
15+baz :: Test.Foo -> String
16+baz = Test.foo
17+
18+innerCopy :: String
19+innerCopy = Test.Inner.inner
20+
21+match :: Test.Foo -> String
22+match = \f -> case f of Test.Foo s -> s ++ "foo"
examples/passing/Monad.pursnew file+26
@@ -0,0 +1,26 @@
1+type Monad m = { ret :: forall a. a -> m a
2+ , bind :: forall a b. m a -> (a -> m b) -> m b }
3+
4+data Id a = Id a
5+
6+id :: Monad Id
7+id = { ret : Id
8+ , bind : \ma f -> case ma of Id a -> f a }
9+
10+data Maybe a = Nothing | Just a
11+
12+maybe :: Monad Maybe
13+maybe = { ret : Just
14+ , bind : \ma f -> case ma of
15+ Nothing -> Nothing
16+ Just a -> f a
17+ }
18+
19+test :: forall m. Monad m -> m Number
20+test = \m -> m.bind (m.ret 1) (\n1 ->
21+ m.bind (m.ret "Test") (\n2 ->
22+ m.ret n1))
23+
24+test1 = test id
25+
26+test2 = test maybe
examples/passing/NameResolution.pursnew file+15
@@ -0,0 +1,15 @@
1+foo = \x -> x
2+
3+module A where
4+ bar = foo
5+
6+ module B where
7+
8+ abc = foo
9+ xyz = bar
10+
11+ foo = \x y -> x
12+
13+ baz = foo
14+
15+ abc = A.B.abc
examples/passing/NamedPatterns.pursnew file+3
@@ -0,0 +1,3 @@
1+foo = \x -> case x of
2+ y@{ foo = "Foo" } -> y
3+ y -> y
examples/passing/Nested.pursnew file+3
@@ -0,0 +1,3 @@
1+data Extend r a = Extend { prev :: r a, next :: a }
2+
3+data Matrix r a = Square (r (r a)) | Bigger (Matrix (Extend r) a)
examples/passing/ObjectUpdate.pursnew file+14
@@ -0,0 +1,14 @@
1+update1 = \o -> o { foo = "Foo" }
2+
3+update2 :: forall r. { foo :: String | r } -> { foo :: String | r }
4+update2 = \o -> o { foo = "Foo" }
5+
6+replace = \o -> case o of
7+ { foo = "Foo" } -> o { foo = "Bar" }
8+ { foo = "Bar" } -> o { bar = "Baz" }
9+ o -> o
10+
11+polyUpdate :: forall a r. { foo :: a | r } -> { foo :: String | r }
12+polyUpdate = \o -> o { foo = "Foo" }
13+
14+inferPolyUpdate = \o -> o { foo = "Foo" }
examples/passing/Objects.pursnew file+12
@@ -0,0 +1,12 @@
1+test = \x -> x.foo + x.bar
2+
3+append = \o -> { foo: o.foo, bar: 1 }
4+
5+apTest = append({foo : "Foo", baz: "Baz"})
6+
7+f = (\a -> a.b.c) { b: { c: 1, d: "Hello" }, e: "World" }
8+
9+g = (\a -> a.f { x: 1, y: "y" }) { f: \o -> o.x + 1 }
10+
11+typed :: { foo :: Number }
12+typed = { foo: 0 }
examples/passing/Operators.pursnew file+42
@@ -0,0 +1,42 @@
1+test1 = \(x, y, z) -> x * y + z(x)(y)
2+
3+test2 = (\x -> x.foo false) { foo : \_ -> 1 }
4+
5+test3 = (\(x, y) -> x)(1 + 2 * (1 + 2), true && (false || false))
6+
7+k = \x -> \y -> x
8+
9+test4 = 1 `k` 2
10+
11+infixl 5 %%
12+
13+(%%) = \x -> \y -> x * y + y
14+
15+test5 = 1 %% 2 %% 3
16+
17+test6 = ((\x -> x) `k` 2) 3
18+
19+(<>) = \s1 -> \s2 -> s1 ++ s2
20+
21+test7 = "Hello" <> "World!"
22+
23+module Ops where
24+
25+ (@@) :: forall a b. (a -> b) -> a -> b
26+ (@@) = \f x -> f x
27+
28+ foo :: String -> String
29+ foo = \s -> s
30+
31+ test8 = foo @@ "Hello World"
32+
33+ bar = \s1 s2 -> s1 ++ s2
34+
35+test9 = Ops.foo Ops.@@ "Hello World"
36+
37+test10 = "Hello" `Ops.bar` "World"
38+
39+(...) :: forall a. [a] -> [a] -> [a]
40+(...) = \as -> \bs -> as
41+
42+test11 = [1, 2, 3] ... [4, 5, 6]
examples/passing/Patterns.pursnew file+23
@@ -0,0 +1,23 @@
1+test = \x -> case x of
2+ { str = "Foo", bool = true } -> true
3+ { str = "Bar", bool = b } -> b
4+ _ -> false
5+
6+f = \o -> case o of
7+ { foo = "Foo" } -> o.bar
8+ _ -> 0
9+
10+g = \o -> case o of
11+ { arr = [x : xs], take = "car" } -> x
12+ { arr = [_, x : xs], take = "cadr" } -> x
13+ _ -> 0
14+
15+
16+h = \o -> case o of
17+ a@[_,_,_] -> a
18+ _ -> []
19+
20+isDesc = \o -> case o of
21+ [x, y] | x > y -> true
22+ _ -> false
23+
examples/passing/Person.pursnew file+7
@@ -0,0 +1,7 @@
1+data Person = Person { name :: String, age :: Number }
2+
3+foreign import itoa :: Number -> String
4+
5+showPerson :: Person -> String
6+showPerson = \p -> case p of
7+ Person o -> o.name ++ ", aged " ++ itoa(o.age)
examples/passing/Rank2Data.pursnew file+23
@@ -0,0 +1,23 @@
1+data Id = Id forall a. a -> a
2+
3+runId = \id a -> case id of
4+ Id f -> f a
5+
6+data Nat = Nat forall r. r -> (r -> r) -> r
7+
8+runNat = \nat -> case nat of
9+ Nat f -> f 0 (\n -> n + 1)
10+
11+zero = Nat (\zero _ -> zero)
12+
13+succ = \n -> case n of
14+ Nat f -> Nat (\zero succ -> succ (f zero succ))
15+
16+add = \n m -> case n of
17+ Nat f -> case m of
18+ Nat g -> Nat (\zero succ -> g (f zero succ) succ)
19+
20+one = succ zero
21+two = succ zero
22+four = add two two
23+fourNumber = runNat four
examples/passing/Rank2Types.pursnew file+17
@@ -0,0 +1,17 @@
1+test1 :: (forall a. (a -> a)) -> Number
2+test1 = \f -> f 0
3+
4+foreign import data ST :: * -> * -> *
5+
6+foreign import runST :: forall a. (forall s. ST s a) -> a
7+
8+foreign import exampleST :: forall s. ST s Number
9+
10+testST = runST exampleST
11+
12+foreign import push :: forall el. el -> [el] -> [el]
13+
14+replicateM :: forall m a. (forall a. a -> m a) -> (forall a b. m a -> (a -> m b) -> m b) -> Number -> m a -> m [a]
15+replicateM = \ret bind n m -> case n of
16+ 0 -> ret []
17+ n -> bind m (\x -> bind (replicateM ret bind (n - 1) m) (\xs -> ret (push x xs)))
examples/passing/Recursion.pursnew file+4
@@ -0,0 +1,4 @@
1+fib = \n -> case n of
2+ 0 -> 1
3+ 1 -> 1
4+ n -> fib (n - 1) + fib (n - 2)
examples/passing/TypeDecl.pursnew file+14
@@ -0,0 +1,14 @@
1+id :: forall a. (a) -> a
2+id = \x -> x
3+
4+k :: String -> Number -> String
5+k = \x -> \y -> x
6+
7+iterate :: forall a. Number -> (a -> a) -> a -> a
8+iterate = \n -> \f -> \a -> {
9+ var result = a;
10+ for (i <- 0 until n) {
11+ result = f result;
12+ }
13+ return result;
14+ }
examples/passing/TypeSynonyms.pursnew file+31
@@ -0,0 +1,31 @@
1+type Lens a b =
2+ { get :: a -> b
3+ , set :: a -> b -> a
4+ }
5+
6+composeLenses :: forall a b c. Lens a b -> Lens b c -> Lens a c
7+composeLenses = \l1 -> \l2 ->
8+ { get: \a -> l2.get (l1.get a)
9+ , set: \a -> \c ->
10+ {
11+ var b = l1.get a;
12+ var b1 = l2.set b c;
13+ return l1.set a b1;
14+ }
15+ }
16+
17+type Pair a b = { fst :: a, snd :: b }
18+
19+fst :: forall a b. Lens (Pair a b) a
20+fst =
21+ { get: \p -> p.fst
22+ , set: \p -> \a -> { fst: a, snd: p.snd }
23+ }
24+
25+test1 :: forall a b c. Lens (Pair (Pair a b) c) a
26+test1 = composeLenses fst fst
27+
28+foreign import head :: forall a. Lens [a] a
29+
30+test2 :: forall a b c. Lens (Pair [Pair a b] c) a
31+test2 = composeLenses fst (composeLenses head fst)
examples/passing/iota.pursnew file+5
@@ -0,0 +1,5 @@
1+s = \x -> \y -> \z -> x z (y z)
2+
3+k = \x -> \y -> x
4+
5+iota = \x -> x s k
examples/passing/s.pursnew file+1
@@ -0,0 +1 @@
1+s = \x y z -> x z (y z)
examples/shelltest/Console.jsnew file+19
@@ -0,0 +1,19 @@
1+// data IO a = \ -> a
2+
3+// ret :: forall a. a -> IO a
4+var ret = function(a) {
5+ return function() { return a; };
6+}
7+
8+// (>>=) :: forall a b. IO a -> (a -> IO b) -> IO b
9+var $62$62$61 = function(first) {
10+ return function (f) {
11+ return function () {
12+ var a = first();
13+ return f(a)();
14+ };
15+ };
16+};
17+
18+// putStrLn :: String -> IO {}
19+var putStrLn = function(str) { return function() { console.log(str); return {}; }; };
examples/shelltest/Foreach.jsnew file+1
@@ -0,0 +1 @@
1+console.log(sum([1,2,3,4,5]));
examples/shelltest/List.jsnew file+7
@@ -0,0 +1,7 @@
1+var l1 = Cons({ head: 1, tail: Nil });
2+var l2 = Cons({ head: 2, tail: Nil });
3+var l3 = Cons({ head: l1, tail: Cons({ head: l2, tail: Nil }) });
4+var flat = flatten(l3);
5+var f1 = flat.value.head;
6+var f2 = flat.value.tail.value.head
7+console.log([f1, f2]);
examples/shelltest/Maybe.jsnew file+5
@@ -0,0 +1,5 @@
1+console.log(bind(Just("a"))(function(a) {
2+ return bind(Just("b"))(function(b) {
3+ return Just(a + b);
4+ });
5+}));
examples/shelltest/runAll.testnew file+23
@@ -0,0 +1,23 @@
1+# Maybe
2+mkdir -p tests && ../../dist/build/psc/psc ../passing/Maybe.purs -o tests/Maybe.js && cat tests/Maybe.js Maybe.js | nodejs
3+<<<
4+>>>
5+{ ctor: 'Just', value: 'ab' }
6+>>>2
7+>>>= 0
8+
9+# List
10+mkdir -p tests && ../../dist/build/psc/psc ../passing/List.purs -o tests/List.js && cat tests/List.js List.js | nodejs
11+<<<
12+>>>
13+[ 1, 2 ]
14+>>>2
15+>>>= 0
16+
17+# Foreach
18+mkdir -p tests && ../../dist/build/psc/psc ../passing/Foreach.purs -o tests/Foreach.js && cat tests/Foreach.js Foreach.js | nodejs
19+<<<
20+>>>
21+15
22+>>>2
23+>>>= 0
examples/shelltest/runMain.jsnew file+1
@@ -0,0 +1 @@
1+main();
libraries/.gitignorenew file+1
@@ -0,0 +1 @@
1+*/all.js
libraries/jquery/Makefilenew file+4
@@ -0,0 +1,4 @@
1+all:
2+ ../../dist/build/psc/psc jquery.purs example.purs -o all.js
3+ cat jquery.js >> all.js
4+ echo "main()" >> all.js
libraries/jquery/example.pursnew file+6
@@ -0,0 +1,6 @@
1+main = {
2+ var div = create "<div>";
3+ div = div `appendText` "Hello World!" `css` { color: "#FF0000" };
4+ div = div `appendTo` select "body";
5+ return { };
6+ }
libraries/jquery/jquery.jsnew file+7
@@ -0,0 +1,7 @@
1+function select(s) { return $(s); }
2+function create(s) { return $(s); }
3+function attr(j, a) { return j.attr(o); }
4+function css(j, a) { return j.css(o); }
5+function append(j1, j2) { return j1.append(j2); }
6+function appendTo(j1, j2) { return j1.appendTo(j2); }
7+function appendText(j1, s) { return j1.append(s); }
libraries/jquery/jquery.pursnew file+23
@@ -0,0 +1,23 @@
1+-- The jQuery wrapper type
2+foreign import data JQuery :: *
3+
4+-- Wrapper function for jQuery selection $('..')
5+foreign import select :: String -> JQuery
6+
7+-- Wrapper function for jQuery creation e.g. $('<div>')
8+foreign import create :: String -> JQuery
9+
10+-- .attr({ ... })
11+foreign import attr :: forall attr. JQuery -> { | attr } -> JQuery
12+
13+-- .css({ ... })
14+foreign import css :: forall css. JQuery -> { | css } -> JQuery
15+
16+-- .append(...)
17+foreign import append :: JQuery -> JQuery -> JQuery
18+
19+-- .appendTo(...)
20+foreign import appendTo :: JQuery -> JQuery -> JQuery
21+
22+-- .append(...)
23+foreign import appendText :: JQuery -> String -> JQuery
libraries/prelude/Makefilenew file+3
@@ -0,0 +1,3 @@
1+all:
2+ ../../dist/build/psc/psc prelude.purs -o all.js
3+ cat prelude.js >> all.js
libraries/prelude/prelude.jsnew file+65
@@ -0,0 +1,65 @@
1+// Arrays
2+
3+function indexOf(l) { return function (e) { return l.indexOf(e); }; }
4+function lastIndexOf(l) { return function (e) { return l.lastIndexOf(e); }; }
5+function concat(l1) { return function (l2) { return l1.concat(l2); }; }
6+function join(l) { return l.join(); }
7+function joinWith(l) { return function (s) { return l.join(s); }; }
8+function push(l) { return function (e) { var l1 = l.slice(); l1.push(e); return l1; }; }
9+function reverse(l) { var l1 = l.slice(); l1.reverse(); return l1; }
10+function shift(l) { var l1 = l.slice(); l1.shift(); return l1; }
11+function slice(s) { return function(e) { return function (l) { return l.slice(s, e); }; }; }
12+function sort(l) { var l1 = l.slice(); l.sort(); return l1; }
13+function splice(s) { return function(e) { return function(l1) { return function(l2) { return l2.splice(s, e, l1); }; }; }; }
14+
15+function charAt(i) { return function(s) { return s.charAt(i); }; }
16+function indexOfS(s1) { return function(s2) { return s2.indexOf(s2); }; }
17+function lastIndexOfS(s1) { return function(s2) { return s2.lastIndexOf(s2); }; }
18+function localeCompare(s1) { return function(s2) { return s1.localeCompare(s2); }; }
19+function replace(s1) { return function(s2) { return function(s3) { return s3.replace(s1, s2) }; }; }
20+function sliceS(st) { return function(e) { return function(s) { return s.slice(st, e); }; }; }
21+function split(sep) { return function(s) { return s.split(s); }; }
22+function substr(n1) { return function(n2) { return function(s) { return s.substr(n1, n2); }; }; }
23+function substring(n1) { return function(n2) { return function(s) { return s.substring(n1, n2); }; }; }
24+function toLower(s) { return s.toLower(); }
25+function toUpper(s) { return s.toUpper(); }
26+function trim(s) { return s.trim(); }
27+
28+function regex(s1) { return function(s2) { return new Regex(s1, s2); }; };
29+function test(r) { return function (s) { return r.test(s); }; };
30+function match(r) { return function (s) { return s.match(r); }; };
31+function replaceR(r) { return function(s1) { return function(s2) { return s2.replace(r, s1) }; }; }
32+function search(r) { return function (s) { return s.search(r); }; };
33+
34+var nan = NaN;
35+var infinity = Infinity;
36+function toExponential(n) { return n.toExponential(); }
37+function toFixed(d) { return function(n) { return n.toFixed(d); }; }
38+function toPrecision(d) { return function(n) { return n.toPrecision(d); }; }
39+function numberToString(n) { return n.toString(); }
40+
41+var math = Math;
42+
43+function map(f) {
44+ return function(xs) {
45+ var ys = [];
46+ for (var i = 0; i < xs.length; i++) {
47+ ys[i] = f(xs[i]);
48+ }
49+ return ys;
50+ };
51+}
52+
53+function filter(xs) {
54+ return function (p) {
55+ var ys = [];
56+ var j = 0;
57+ for (var i = 0; i < xs.length; i++) {
58+ var x = xs[i];
59+ if (p(x)) {
60+ ys[j++] = x;
61+ }
62+ }
63+ return ys;
64+ };
65+}
libraries/prelude/prelude.pursnew file+273
@@ -0,0 +1,273 @@
1+id :: forall a. a -> a
2+id = \x -> x
3+
4+flip :: forall a b c. (a -> b -> c) -> b -> a -> c
5+flip = \f -> \b -> \a -> f a b
6+
7+konst :: forall a b. a -> b -> a
8+konst = \a -> \b -> a
9+
10+(|>) :: forall a b c. (a -> b) -> (b -> c) -> a -> c
11+(|>) = \f -> \g -> \a -> g (f a)
12+
13+(<|) :: forall a b c. (b -> c) -> (a -> b) -> a -> c
14+(<|) = flip (|>)
15+
16+-- Maybe
17+
18+data Maybe a = Nothing | Just a
19+
20+maybe :: forall a b. b -> (a -> b) -> Maybe a -> b
21+maybe = \b -> \f -> \m -> case m of
22+ Nothing -> b
23+ Just a -> f a
24+
25+fromMaybe :: forall a. a -> Maybe a -> a
26+fromMaybe = \a -> maybe a id
27+
28+bindMaybe :: forall a b. Maybe a -> (a -> Maybe b) -> Maybe b
29+bindMaybe = \m -> \f -> maybe Nothing f m
30+
31+-- Either
32+
33+data Either a b = Left a | Right b
34+
35+either :: forall a b c. (a -> c) -> (b -> c) -> Either a b -> c
36+either = \f -> \g -> \e -> case e of
37+ Left a -> f a
38+ Right b -> g b
39+
40+bindEither :: forall e a b. Either e a -> (a -> Either e b) -> Either e b
41+bindEither = either (\e -> \_ -> Left e) (\a -> \f -> f a)
42+
43+-- Arrays
44+
45+head :: forall a. [a] -> a
46+head = \xs -> case xs of
47+ x : _ -> x
48+
49+headSafe :: forall a. [a] -> Maybe a
50+headSafe = \xs -> case xs of
51+ x : _ -> Just x
52+ _ -> Nothing
53+
54+tail :: forall a. [a] -> [a]
55+tail = \xs -> case xs of
56+ _ : xs -> xs
57+
58+tailSafe :: forall a. [a] -> Maybe [a]
59+tailSafe = \xs -> case xs of
60+ _ : xs -> Just xs
61+ _ -> Nothing
62+
63+foreign import map :: forall a b. (a -> b) -> [a] -> [b]
64+
65+foldr :: forall a b. (a -> b -> a) -> a -> [b] -> a
66+foldr = \f -> \a -> \bs -> case bs of
67+ b : bs -> f (foldr f a bs) b
68+ [] -> a
69+
70+foldl :: forall a b. (a -> b -> b) -> b -> [a] -> b
71+foldl = \f -> \b -> \as -> {
72+ var result = b;
73+ foreach (a in as) {
74+ result = f a result;
75+ }
76+ return result;
77+ }
78+
79+foreign import member "length" length :: forall a. [a] -> Number
80+
81+foreign import indexOf :: forall a. [a] -> a -> Number
82+
83+foreign import lastIndexOf :: forall a. [a] -> a -> Number
84+
85+foreign import concat :: forall a. [a] -> [a] -> [a]
86+
87+foreign import join :: [String] -> String
88+
89+foreign import joinWith :: [String] -> String -> String
90+
91+foreign import push :: forall a. [a] -> a -> [a]
92+
93+foreign import reverse :: forall a. [a] -> [a]
94+
95+foreign import shift :: forall a. [a] -> [a]
96+
97+foreign import slice :: forall a. Number -> Number -> [a] -> [a]
98+
99+foreign import sort :: forall a. [a] -> [a]
100+
101+foreign import splice :: forall a. Number -> Number -> [a] -> [a] -> [a]
102+
103+cons :: forall a. a -> [a] -> [a]
104+cons = \a -> concat([a])
105+
106+concatMap :: forall a b. [a] -> (a -> [b]) -> [b]
107+concatMap = \as -> \f -> {
108+ var result = [];
109+ foreach (a in as) {
110+ result = result `concat` f a;
111+ }
112+ return result;
113+ }
114+
115+foreign import filter :: forall a. [a] -> (a -> Boolean) -> [a]
116+
117+empty :: forall a. [a] -> Boolean
118+empty = \as -> case as of
119+ [] -> true
120+ _ -> false
121+
122+range :: Number -> Number -> [Number]
123+range = \lo -> \hi -> {
124+ var ns = [];
125+ for (n <- lo until hi) {
126+ ns = push ns n;
127+ }
128+ return ns;
129+ }
130+
131+zipWith :: forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
132+zipWith = \f -> \as -> \bs -> case { as: as, bs: bs } of
133+ { as = a : as1, bs = b : bs1 } -> cons (f a b) (zipWith f as1 bs1)
134+ _ -> []
135+
136+any :: forall a. (a -> Boolean) -> [a] -> Boolean
137+any = \p -> \as -> {
138+ for (i <- 0 until length as) {
139+ if (p (as !! i)) {
140+ return true;
141+ }
142+ }
143+ return false;
144+ }
145+
146+all :: forall a. (a -> Boolean) -> [a] -> Boolean
147+all = \p -> \as -> {
148+ for (i <- 0 until length as) {
149+ if (!(p (as !! i))) {
150+ return false;
151+ }
152+ }
153+ return true;
154+ }
155+
156+-- Pairs
157+
158+type Tuple a b = { fst :: a, snd :: b }
159+
160+curry :: forall a b c. (Tuple a b -> c) -> a -> b -> c
161+curry = \f -> \a -> \b -> f { fst: a, snd: b }
162+
163+uncurry :: forall a b c. (a -> b -> c) -> Tuple a b -> c
164+uncurry = \f -> \t -> f t.fst t.snd
165+
166+tuple :: forall a b. a -> b -> Tuple a b
167+tuple = curry id
168+
169+zip :: forall a b. [a] -> [b] -> [Tuple a b]
170+zip = zipWith tuple
171+
172+unzip :: forall a b. [Tuple a b] -> Tuple [a] [b]
173+unzip = \ts -> case ts of
174+ t : ts1 -> case unzip ts1 of
175+ { fst = as, snd = bs } -> tuple (cons t.fst as) (cons t.snd bs)
176+ [] -> tuple [] []
177+
178+-- Strings
179+
180+foreign import member "length" lengthS :: String -> Number
181+
182+foreign import charAt :: Number -> String -> String
183+
184+foreign import indexOfS :: String -> String -> Number
185+
186+foreign import lastIndexOfS :: String -> String -> Number
187+
188+foreign import localeCompare :: String -> String -> Number
189+
190+foreign import replace :: String -> String -> String -> String
191+
192+foreign import sliceS :: Number -> Number -> String -> String
193+
194+foreign import split :: String -> String -> [String]
195+
196+foreign import substr :: Number -> Number -> String -> String
197+
198+foreign import substring :: Number -> Number -> String -> String
199+
200+foreign import toLower :: String -> String
201+
202+foreign import toUpper :: String -> String
203+
204+foreign import trim :: String -> String
205+
206+-- Regex
207+
208+foreign import data Regex :: *
209+
210+foreign import regex :: String -> String -> Regex
211+
212+foreign import test :: Regex -> String -> Boolean
213+
214+foreign import match :: Regex -> String -> [String]
215+
216+foreign import replaceR :: Regex -> String -> String -> String
217+
218+foreign import search :: Regex -> String -> Number
219+
220+-- Globals
221+
222+foreign import nan :: Number
223+
224+foreign import infinity :: Number
225+
226+foreign import toExponential :: Number -> String
227+
228+foreign import toFixed :: Number -> Number -> String
229+
230+foreign import toPrecision :: Number -> Number -> String
231+
232+foreign import numberToString :: Number -> String
233+
234+foreign import isNaN :: Number -> Boolean
235+
236+foreign import isFinite :: Number -> Boolean
237+
238+foreign import parseFloat :: String -> Number
239+
240+foreign import parseInt :: String -> Number
241+
242+foreign import encodeURIComponent :: String -> String
243+
244+foreign import decodeURIComponent :: String -> String
245+
246+foreign import encodeURI :: String -> String
247+
248+foreign import decodeURI :: String -> String
249+
250+-- Math
251+
252+type Math =
253+ { abs :: Number -> Number
254+ , acos :: Number -> Number
255+ , asin :: Number -> Number
256+ , atan :: Number -> Number
257+ , atan2 :: (Number, Number) -> Number
258+ , aceil :: Number -> Number
259+ , cos :: Number -> Number
260+ , exp :: Number -> Number
261+ , floor :: Number -> Number
262+ , log :: Number -> Number
263+ , max :: (Number, Number) -> Number
264+ , pow :: (Number, Number) -> Number
265+ , random :: () -> Number
266+ , round :: Number -> Number
267+ , sin :: Number -> Number
268+ , sqrt :: Number -> Number
269+ , tan :: Number -> Number
270+ }
271+
272+foreign import math :: Math
273+
package.yamldeleted−86
@@ -1,86 +0,0 @@
1−name: psgo
2−version: 0.1.0.0
3−github: "andyarvanitis/purescript-native"
4−license: BSD3
5−author: "Andy Arvanitis"
6−maintainer: "example@example.com"
7−copyright: "2018 Andy Arvanitis"
8−
9−extra-source-files:
10−- README.md
11−- ChangeLog.md
12−- stack.yaml
13−- support/*
14−
15−# Metadata used when publishing your package
16−# synopsis: Short description of your package
17−# category: Web
18−
19−# To avoid duplicated efforts in documentation and dealing with the
20−# complications of embedding Haddock markup inside cabal files, it is
21−# common to point users to the README.md file.
22−description: Please see the README on Github at <https://github.com/andyarvanitis/purescript-native#readme>
23−
24−dependencies:
25−- base >= 4.7 && < 5
26−- bytestring >= 0.10.8.2 && < 0.11
27−- purescript
28−- base-compat >=0.6.0
29−- protolude >=0.1.6
30−- text
31−- containers
32−- filepath
33−- monad-parallel >= 0.7.2.2 && <0.8
34−- pattern-arrows >=0.0.2 && <0.1
35−- safe >=0.3.9 && <0.4
36−- transformers >=0.3.0 && <0.6
37−- mtl >=2.1.0 && <2.3.0
38−- aeson >=1.0 && <1.5
39−- directory
40−- process
41−- file-embed
42−- filemanip
43−- gitrev
44−
45−library:
46− source-dirs: src
47− default-extensions:
48− - ConstraintKinds
49− - DataKinds
50− - DeriveFunctor
51− - EmptyDataDecls
52− - FlexibleContexts
53− - KindSignatures
54− - LambdaCase
55− - MultiParamTypeClasses
56− - NoImplicitPrelude
57− - PatternGuards
58− - PatternSynonyms
59− - RankNTypes
60− - RecordWildCards
61− - OverloadedStrings
62− - ScopedTypeVariables
63− - TupleSections
64− - ViewPatterns
65−
66−executables:
67− psgo:
68− main: Main.hs
69− source-dirs: app
70− ghc-options:
71− - -threaded
72− - -rtsopts
73− - -with-rtsopts=-N
74− dependencies:
75− - psgo
76−
77−tests:
78− psgo-test:
79− main: Spec.hs
80− source-dirs: test
81− ghc-options:
82− - -threaded
83− - -rtsopts
84− - -with-rtsopts=-N
85− dependencies:
86− - psgo
psgo.cabaldeleted−145
@@ -1,145 +0,0 @@
1−cabal-version: 1.12
2−
3−-- This file has been generated from package.yaml by hpack version 0.35.0.
4−--
5−-- see: https://github.com/sol/hpack
6−
7−name: psgo
8−version: 0.1.0.0
9−description: Please see the README on Github at <https://github.com/andyarvanitis/purescript-native#readme>
10−homepage: https://github.com/andyarvanitis/purescript-native#readme
11−bug-reports: https://github.com/andyarvanitis/purescript-native/issues
12−author: Andy Arvanitis
13−maintainer: example@example.com
14−copyright: 2018 Andy Arvanitis
15−license: BSD3
16−license-file: LICENSE
17−build-type: Simple
18−extra-source-files:
19− README.md
20− ChangeLog.md
21− stack.yaml
22− support/ffi-loader.go
23− support/go.mod.ffi-loader
24− support/go.mod.output
25− support/go.mod.working
26−
27−source-repository head
28− type: git
29− location: https://github.com/andyarvanitis/purescript-native
30−
31−library
32− exposed-modules:
33− CodeGen.IL
34− CodeGen.IL.Common
35− CodeGen.IL.Optimizer
36− CodeGen.IL.Optimizer.Common
37− CodeGen.IL.Optimizer.Inliner
38− CodeGen.IL.Optimizer.MagicDo
39− CodeGen.IL.Optimizer.TCO
40− CodeGen.IL.Printer
41− Tests
42− other-modules:
43− Paths_psgo
44− hs-source-dirs:
45− src
46− default-extensions:
47− ConstraintKinds
48− DataKinds
49− DeriveFunctor
50− EmptyDataDecls
51− FlexibleContexts
52− KindSignatures
53− LambdaCase
54− MultiParamTypeClasses
55− NoImplicitPrelude
56− PatternGuards
57− PatternSynonyms
58− RankNTypes
59− RecordWildCards
60− OverloadedStrings
61− ScopedTypeVariables
62− TupleSections
63− ViewPatterns
64− build-depends:
65− aeson >=1.0 && <1.5
66− , base >=4.7 && <5
67− , base-compat >=0.6.0
68− , bytestring >=0.10.8.2 && <0.11
69− , containers
70− , directory
71− , file-embed
72− , filemanip
73− , filepath
74− , gitrev
75− , monad-parallel >=0.7.2.2 && <0.8
76− , mtl >=2.1.0 && <2.3.0
77− , pattern-arrows >=0.0.2 && <0.1
78− , process
79− , protolude >=0.1.6
80− , purescript
81− , safe >=0.3.9 && <0.4
82− , text
83− , transformers >=0.3.0 && <0.6
84− default-language: Haskell2010
85−
86−executable psgo
87− main-is: Main.hs
88− other-modules:
89− Paths_psgo
90− hs-source-dirs:
91− app
92− ghc-options: -threaded -rtsopts -with-rtsopts=-N
93− build-depends:
94− aeson >=1.0 && <1.5
95− , base >=4.7 && <5
96− , base-compat >=0.6.0
97− , bytestring >=0.10.8.2 && <0.11
98− , containers
99− , directory
100− , file-embed
101− , filemanip
102− , filepath
103− , gitrev
104− , monad-parallel >=0.7.2.2 && <0.8
105− , mtl >=2.1.0 && <2.3.0
106− , pattern-arrows >=0.0.2 && <0.1
107− , process
108− , protolude >=0.1.6
109− , psgo
110− , purescript
111− , safe >=0.3.9 && <0.4
112− , text
113− , transformers >=0.3.0 && <0.6
114− default-language: Haskell2010
115−
116−test-suite psgo-test
117− type: exitcode-stdio-1.0
118− main-is: Spec.hs
119− other-modules:
120− Paths_psgo
121− hs-source-dirs:
122− test
123− ghc-options: -threaded -rtsopts -with-rtsopts=-N
124− build-depends:
125− aeson >=1.0 && <1.5
126− , base >=4.7 && <5
127− , base-compat >=0.6.0
128− , bytestring >=0.10.8.2 && <0.11
129− , containers
130− , directory
131− , file-embed
132− , filemanip
133− , filepath
134− , gitrev
135− , monad-parallel >=0.7.2.2 && <0.8
136− , mtl >=2.1.0 && <2.3.0
137− , pattern-arrows >=0.0.2 && <0.1
138− , process
139− , protolude >=0.1.6
140− , psgo
141− , purescript
142− , safe >=0.3.9 && <0.4
143− , text
144− , transformers >=0.3.0 && <0.6
145− default-language: Haskell2010
purescript.cabalnew file+59
@@ -0,0 +1,59 @@
1+name: purescript
2+version: 0.1.19
3+cabal-version: >=1.8
4+build-type: Simple
5+license: MIT
6+license-file: LICENSE
7+copyright: (c) Phil Freeman 2013
8+maintainer: Phil Freeman <paf31@cantab.net>
9+stability: experimental
10+synopsis: PureScript Programming Language Compiler
11+description: A small compile-to-JS language with extensible records and type-safe blocks
12+category: Language
13+author: Phil Freeman <paf31@cantab.net>
14+data-dir: ""
15+
16+library
17+ build-depends: base >=4 && <5, cmdtheline -any, containers -any,
18+ directory -any, filepath -any, mtl -any, parsec -any,
19+ syb -any, transformers -any, utf8-string -any
20+ exposed-modules: Data.Generics.Extras Language.PureScript
21+ Language.PureScript.CodeGen Language.PureScript.CodeGen.Externs
22+ Language.PureScript.CodeGen.JS Language.PureScript.CodeGen.JS.AST
23+ Language.PureScript.CodeGen.Monad Language.PureScript.Declarations
24+ Language.PureScript.Kinds Language.PureScript.Names
25+ Language.PureScript.Operators Language.PureScript.Optimize
26+ Language.PureScript.Parser Language.PureScript.Parser.Common
27+ Language.PureScript.Parser.Declarations
28+ Language.PureScript.Parser.Kinds Language.PureScript.Parser.State
29+ Language.PureScript.Parser.Types Language.PureScript.Parser.Values
30+ Language.PureScript.Pretty Language.PureScript.Pretty.Common
31+ Language.PureScript.Pretty.JS Language.PureScript.Pretty.Kinds
32+ Language.PureScript.Pretty.Types Language.PureScript.Pretty.Values
33+ Language.PureScript.TypeChecker
34+ Language.PureScript.TypeChecker.Kinds
35+ Language.PureScript.TypeChecker.Monad
36+ Language.PureScript.TypeChecker.Synonyms
37+ Language.PureScript.TypeChecker.Types Language.PureScript.Types
38+ Language.PureScript.Unknown Language.PureScript.Values Main
39+ exposed: True
40+ buildable: True
41+ hs-source-dirs: src
42+
43+executable psc
44+ build-depends: base >=4 && <5, cmdtheline -any, containers -any,
45+ directory -any, filepath -any, mtl -any, parsec -any,
46+ purescript -any, syb -any, transformers -any, utf8-string -any
47+ main-is: Main.hs
48+ buildable: True
49+ hs-source-dirs: src
50+ ghc-options: -Wall -O2 -fno-warn-unused-do-bind
51+
52+test-suite tests
53+ build-depends: base >=4 && <5, cmdtheline -any, containers -any,
54+ directory -any, filepath -any, mtl -any, parsec -any,
55+ purescript -any, syb -any, transformers -any, utf8-string -any
56+ type: exitcode-stdio-1.0
57+ main-is: Main.hs
58+ buildable: True
59+ hs-source-dirs: tests
release.mdnew file+31
@@ -0,0 +1,31 @@
1+## 0.2
2+
3+### Closed Issues
4+
5+- Avoid 'do' keyword for blocks
6+- Allow method calls in blocks
7+- Polymorphic Object Update
8+- Possibility of overlapping imports
9+- Avoid reassigning vars when importing modules
10+- Add basic module imports (#66)
11+- Need to be able to access names in the global module
12+- Generate formatted Javascript
13+- Modules Documentation
14+- Module imports
15+- User-defined operators and modules don't work together
16+- Optional parentheses in function declaration
17+- Hello world example
18+- Allow guards access to current scope
19+- Array indexing syntax is ambiguous
20+- Make FFI syntax match Haskell
21+- Member extern syntax
22+- Perform some basic optimization on the generated Javascript.
23+- Syntactic sugar for introducing curried functions
24+- Array pattern match syntax is confusing
25+- Make record declaration syntax match Haskell
26+- Split code generation into AST -> JS AST and JS AST -> String
27+- Use a different file extension
28+- Prelude
29+- Syntax for creating an array from a list of heads and a tail
30+- RankNTypes
31+- Modules
src/CodeGen/IL.hsdeleted−321
@@ -1,321 +0,0 @@
1−-- | This module generates code in the core imperative representation from
2−-- elaborated PureScript code.
3−module CodeGen.IL
4− ( module AST
5− , module Common
6− , moduleToIL
7− ) where
8−
9−import Prelude.Compat
10−import Protolude (ordNub)
11−
12−import Control.Arrow ((&&&))
13−import Control.Monad (forM, liftM, replicateM, void)
14−import Control.Monad.Supply.Class
15−
16−import Data.List ((\\), delete, intersect, nub)
17−import qualified Data.Foldable as F
18−import qualified Data.Map as M
19−import Data.Maybe (fromMaybe, isNothing, mapMaybe)
20−import Data.Monoid ((<>))
21−import Data.String (fromString)
22−import Data.Text (Text)
23−import qualified Data.Text as T
24−
25−import Language.PureScript.AST.SourcePos
26−import Language.PureScript.CoreImp.AST (AST)
27−import qualified Language.PureScript.CoreImp.AST as AST
28−import Language.PureScript.CoreFn
29−import Language.PureScript.Crash
30−import Language.PureScript.Names
31−import Language.PureScript.Options
32−import Language.PureScript.PSString (PSString, mkString)
33−import Language.PureScript.Traversals (sndM)
34−import qualified Language.PureScript.Constants as C
35−
36−import System.FilePath.Posix ((</>))
37−
38−import CodeGen.IL.Common as Common
39−import CodeGen.IL.Optimizer
40−import CodeGen.IL.Printer
41−
42−data DeclType = ModuleDecl | LetDecl | RecLetDecl deriving (Eq)
43−
44−-- | Generate code in the simplified intermediate representation for all declarations in a
45−-- module.
46−moduleToIL
47− :: forall m
48− -- . (Monad m, MonadReader Options m, MonadSupply m, MonadError MultipleErrors m)
49− . (Monad m, MonadSupply m)
50− => Module Ann
51− -> Text
52− -> m (Text, [Text], [AST], Text, Text)
53−moduleToIL (Module _ coms mn _ imps _ foreigns decls) project =
54− do
55− ilDecls <- mapM (bindToIL ModuleDecl) decls
56− optimized <- traverse (traverse (optimize modName')) ilDecls
57− let optimized' = concat optimized
58− values = annotValue <$> optimized'
59− foreigns' = moduleIdentToIL <$> foreigns
60− interface = interfaceSource modName values foreigns
61− imports = nub . concat $ importToIL <$> optimized'
62− implHeader = implHeaderSource modName imports project
63− implFooter = implFooterSource (runModuleName mn) foreigns
64− return $ (interface, foreigns', optimized', implHeader, implFooter)
65− where
66− modName = moduleNameToIL mn
67− modName' = AST.Var Nothing modName
68−
69− annotValue :: AST -> (Text, Bool)
70− annotValue (AST.Function _ (Just name) [] (AST.Block _ [AST.Return _ ret]))
71− | isLiteral ret = (name, True)
72− annotValue (AST.Function _ (Just name) [] _) = (name, False)
73− annotValue _ = error "Unexpected top-level value form"
74−
75− -- | Extracts all declaration names from a binding group.
76− getNames :: Bind Ann -> [Ident]
77− getNames (NonRec _ ident _) = [ident]
78− getNames (Rec vals) = map (snd . fst) vals
79−
80− -- | Generates IL code for a module import
81− --
82− importToIL :: AST -> [Text]
83− importToIL = AST.everything (++) modRef
84− where
85− modRef (AST.Indexer _ (AST.Var _ _) (AST.Var _ mname))
86− | not $ T.null mname = [extract mname]
87− modRef _ = []
88− extract :: Text -> Text
89− extract = T.replace "_" "." . T.dropWhileEnd (=='_')
90−
91− -- |
92− -- Generate code in the simplified intermediate representation for a declaration
93− --
94− bindToIL :: DeclType -> Bind Ann -> m [AST]
95− bindToIL dt (NonRec ann ident val) = return <$> nonRecToIL dt ann ident val
96− bindToIL dt (Rec vals) = forM vals (uncurry . uncurry $ nonRecToIL dt')
97− where
98− dt' = if dt == LetDecl then RecLetDecl else dt
99−
100− -- | Generate code in the simplified intermediate representation for a single non-recursive
101− -- declaration.
102− --
103− -- The main purpose of this function is to handle code generation for comments.
104− nonRecToIL :: DeclType -> Ann -> Ident -> Expr Ann -> m AST
105− nonRecToIL dt a i e@(extractAnn -> (_, com, _, _)) | not (null com) = do
106− -- withoutComment <- asks optionsNoComments
107− let withoutComment = False
108− if withoutComment
109− then nonRecToIL dt a i (modifyAnn removeComments e)
110− else AST.Comment Nothing com <$> nonRecToIL dt a i (modifyAnn removeComments e)
111− nonRecToIL ModuleDecl (ss, _, _, _) ident val = do
112− il <- valueToIL val
113− pure $ AST.Function Nothing (Just $ moduleIdentToIL ident) [] (AST.Block Nothing [AST.Return Nothing il])
114− nonRecToIL RecLetDecl (ss, _, _, _) ident val = do
115− ilExpr <- valueToIL val
116− pure $ AST.Assignment Nothing (AST.Var Nothing $ identToIL ident) ilExpr
117− nonRecToIL _ (ss, _, _, _) ident val = do
118− il <- valueToIL val
119− pure $ AST.VariableIntroduction Nothing (identToIL ident) (Just il)
120−
121− -- | Generate code in the simplified intermediate representation for a variable based on a
122− -- PureScript identifier.
123− var :: Ident -> AST
124− var = AST.Var Nothing . identToIL
125−
126− accessorString :: PSString -> AST -> AST
127− accessorString prop = AST.Indexer Nothing (AST.StringLiteral Nothing prop)
128−
129− -- | Generate code in the simplified intermediate representation for a value or expression.
130−
131− valueToIL :: Expr Ann -> m AST
132− valueToIL (Literal (pos, _, _, _) l) = literalToValueIL pos l
133− valueToIL (Accessor _ prop val) =
134− accessorString prop <$> valueToIL val
135− valueToIL (ObjectUpdate _ o ps) = do
136− obj <- valueToIL o
137− updates <- mapM (sndM valueToIL) ps
138− obj' <- freshName'
139− let objVar = AST.Var Nothing obj'
140− copy = AST.VariableIntroduction Nothing obj' (Just $ copyDict obj)
141− assign (k, v) = AST.Assignment Nothing (accessorString k objVar) v
142− sts = copy : (assign <$> updates) ++ [AST.Return Nothing objVar]
143− return $ AST.App Nothing (AST.Function Nothing Nothing [] (AST.Block Nothing sts)) []
144− valueToIL (Abs _ arg val) = do
145− ret <- valueToIL val
146− let ilArg = case arg of
147− UnusedIdent -> []
148− _ -> [identToIL arg]
149− return $ AST.Function Nothing Nothing ilArg (AST.Block Nothing [AST.Return Nothing ret])
150− valueToIL e@App{} = do
151− let (f, args) = unApp e []
152− args' <- mapM valueToIL args
153− fn <- valueToIL f
154− case f of
155− Var (_, _, _, Just IsNewtype) _ -> return (head args')
156− _ -> flip (foldl (\fn a -> AST.App Nothing fn [a])) args' <$> valueToIL f
157− where
158− unApp :: Expr Ann -> [Expr Ann] -> (Expr Ann, [Expr Ann])
159− unApp (App _ val arg) args = unApp val (arg : args)
160− unApp other args = (other, args)
161− valueToIL (Var _ ident) = return $ varToIL ident
162− valueToIL (Case (ss, _, _, _) values binders) = do
163− vals <- mapM valueToIL values
164− bindersToIL ss binders vals
165− valueToIL (Let _ ds val) = do
166− let recurs = concatMap getNames $ filter isRec ds
167− recurDs = (\v -> AST.VariableIntroduction Nothing (identToIL v) Nothing) <$> recurs
168− ds' <- concat <$> mapM (bindToIL LetDecl) ds
169− ret <- valueToIL val
170− return $ AST.App Nothing (AST.Function Nothing Nothing [] (AST.Block Nothing (recurDs ++ ds' ++ [AST.Return Nothing ret]))) []
171− where
172− isRec :: Bind Ann -> Bool
173− isRec (Rec _) = True
174− isRec _ = False
175− valueToIL (Constructor (_, _, _, Just IsNewtype) _ (ProperName ctor) _) = error "IsNewtype"
176− valueToIL (Constructor _ _ (ProperName ctor) fields) =
177− let body = AST.ObjectLiteral Nothing $ (mkString ctor, AST.BooleanLiteral Nothing True) :
178− ((\field -> (mkString (identToIL field), var field)) `map` fields)
179− fn = foldr (\f inner -> AST.Function Nothing Nothing [identToIL f] (AST.Block Nothing [AST.Return Nothing inner])) body fields
180− in return fn
181−
182−
183− iife :: Text -> [AST] -> AST
184− iife v exprs = AST.App Nothing (AST.Function Nothing Nothing [] (AST.Block Nothing $ exprs ++ [AST.Return Nothing $ AST.Var Nothing v])) []
185−
186− literalToValueIL :: SourceSpan -> Literal (Expr Ann) -> m AST
187− literalToValueIL ss (NumericLiteral (Left i)) = return $ AST.NumericLiteral (Just ss) (Left i)
188− literalToValueIL ss (NumericLiteral (Right n)) = return $ AST.NumericLiteral (Just ss) (Right n)
189− literalToValueIL ss (StringLiteral s) = return $ AST.StringLiteral (Just ss) s
190− literalToValueIL ss (CharLiteral c) = return $ AST.StringLiteral (Just ss) (fromString [c])
191− literalToValueIL ss (BooleanLiteral b) = return $ AST.BooleanLiteral (Just ss) b
192− literalToValueIL ss (ArrayLiteral xs) = AST.ArrayLiteral (Just ss) <$> mapM valueToIL xs
193− literalToValueIL ss (ObjectLiteral ps) = AST.ObjectLiteral (Just ss) <$> mapM (sndM valueToIL) ps
194−
195− -- | Generate code in the simplified intermediate representation for a reference to a
196− -- variable.
197− varToIL :: Qualified Ident -> AST
198− varToIL (Qualified Nothing ident) = var ident
199− varToIL qual = qualifiedToIL id qual
200−
201− -- | Generate code in the simplified intermediate representation for a reference to a
202− -- variable that may have a qualified name.
203− qualifiedToIL :: (a -> Ident) -> Qualified a -> AST
204− qualifiedToIL f (Qualified (Just (ModuleName mn')) a) | mn' == C.prim = AST.Var Nothing . moduleIdentToIL $ f a
205− qualifiedToIL f (Qualified (Just mn') a) | mn /= mn' = AST.Indexer Nothing (AST.Var Nothing . moduleIdentToIL $ f a) (AST.Var Nothing (moduleNameToIL mn'))
206− qualifiedToIL f (Qualified _ a) = AST.Indexer Nothing (AST.Var Nothing . moduleIdentToIL $ f a) (AST.Var Nothing "")
207−
208− -- foreignIdent :: Ident -> AST
209− -- foreignIdent ident = accessorString (mkString $ runIdent ident) (AST.Var Nothing "$foreign")
210−
211− -- | Generate code in the simplified intermediate representation for pattern match binders
212− -- and guards.
213− bindersToIL :: SourceSpan -> [CaseAlternative Ann] -> [AST] -> m AST
214− bindersToIL ss binders vals = do
215− valNames <- replicateM (length vals) freshName'
216− let assignments = zipWith (AST.VariableIntroduction Nothing) valNames (map Just vals)
217− ils <- forM binders $ \(CaseAlternative bs result) -> do
218− ret <- guardsToIL result
219− go valNames ret bs
220− return $ AST.App Nothing (AST.Function Nothing Nothing [] (AST.Block Nothing (assignments ++ concat ils ++ [AST.Throw Nothing (AST.StringLiteral Nothing $ mkString failedPatternMessage)])))
221− []
222− where
223− go :: [Text] -> [AST] -> [Binder Ann] -> m [AST]
224− go _ done [] = return done
225− go (v:vs) done' (b:bs) = do
226− done'' <- go vs done' bs
227− binderToIL v done'' b
228− go _ _ _ = internalError "Invalid arguments to bindersToIL"
229−
230− failedPatternMessage :: Text
231− failedPatternMessage = "Failed pattern match at " <> runModuleName mn <> " " <> displayStartEndPos ss
232−
233− guardsToIL :: Either [(Guard Ann, Expr Ann)] (Expr Ann) -> m [AST]
234− guardsToIL (Left gs) = traverse genGuard gs where
235− genGuard (cond, val) = do
236− cond' <- valueToIL cond
237− val' <- valueToIL val
238− return
239− (AST.IfElse Nothing (AST.Binary Nothing AST.EqualTo cond' (AST.BooleanLiteral Nothing True))
240− (AST.Block Nothing [AST.Return Nothing val']) Nothing)
241−
242− guardsToIL (Right v) = return . AST.Return Nothing <$> valueToIL v
243−
244− -- | Generate code in the simplified intermediate representation for a pattern match
245− -- binder.
246− binderToIL :: Text -> [AST] -> Binder Ann -> m [AST]
247− binderToIL _ done NullBinder{} = return done
248− binderToIL varName done (LiteralBinder _ l) =
249− literalToBinderIL varName done l
250− binderToIL varName done (VarBinder _ ident) =
251− return (AST.VariableIntroduction Nothing (identToIL ident) (Just (AST.Var Nothing varName)) : done)
252− binderToIL varName done (ConstructorBinder (_, _, _, Just IsNewtype) _ _ [b]) =
253− binderToIL varName done b
254− binderToIL varName done (ConstructorBinder (_, _, _, Just (IsConstructor ctorType fields)) _ (Qualified _ (ProperName ctor)) bs) = do
255− il <- go (zip fields bs) done
256− return $ case ctorType of
257− ProductType -> il
258− SumType ->
259− [AST.IfElse Nothing (AST.InstanceOf Nothing (AST.Var Nothing varName) (AST.StringLiteral Nothing (mkString ctor)))
260− (AST.Block Nothing il)
261− Nothing]
262− where
263− go :: [(Ident, Binder Ann)] -> [AST] -> m [AST]
264− go [] done' = return done'
265− go ((field, binder) : remain) done' = do
266− argVar <- freshName'
267− done'' <- go remain done'
268− il <- binderToIL argVar done'' binder
269− return (AST.VariableIntroduction Nothing argVar (Just $ accessorString (mkString $ identToIL field) $ AST.Var Nothing varName) : il)
270−
271− binderToIL _ _ ConstructorBinder{} =
272− internalError "binderToIL: Invalid ConstructorBinder in binderToIL"
273− binderToIL varName done (NamedBinder _ ident binder) = do
274− il <- binderToIL varName done binder
275− return (AST.VariableIntroduction Nothing (identToIL ident) (Just (AST.Var Nothing varName)) : il)
276−
277− literalToBinderIL :: Text -> [AST] -> Literal (Binder Ann) -> m [AST]
278− literalToBinderIL varName done (NumericLiteral num) =
279− return [AST.IfElse Nothing (AST.Binary Nothing AST.EqualTo (AST.Var Nothing varName) (AST.NumericLiteral Nothing num)) (AST.Block Nothing done) Nothing]
280− literalToBinderIL varName done (CharLiteral c) =
281− return [AST.IfElse Nothing (AST.Binary Nothing AST.EqualTo (AST.Var Nothing varName) (AST.StringLiteral Nothing (fromString [c]))) (AST.Block Nothing done) Nothing]
282− literalToBinderIL varName done (StringLiteral str) =
283− return [AST.IfElse Nothing (AST.Binary Nothing AST.EqualTo (AST.Var Nothing varName) (AST.StringLiteral Nothing str)) (AST.Block Nothing done) Nothing]
284− literalToBinderIL varName done (BooleanLiteral True) =
285− return [AST.IfElse Nothing (AST.Binary Nothing AST.EqualTo (AST.Var Nothing varName) (AST.BooleanLiteral Nothing True)) (AST.Block Nothing done) Nothing]
286− literalToBinderIL varName done (BooleanLiteral False) =
287− return [AST.IfElse Nothing (AST.Binary Nothing AST.EqualTo (AST.Var Nothing varName) (AST.BooleanLiteral Nothing False)) (AST.Block Nothing done) Nothing]
288− -- return [AST.IfElse Nothing (AST.Unary Nothing AST.Not (AST.Var Nothing varName)) (AST.Block Nothing done) Nothing]
289− literalToBinderIL varName done (ObjectLiteral bs) = go done bs
290− where
291− go :: [AST] -> [(PSString, Binder Ann)] -> m [AST]
292− go done' [] = return done'
293− go done' ((prop, binder):bs') = do
294− propVar <- freshName'
295− done'' <- go done' bs'
296− il <- binderToIL propVar done'' binder
297− return (AST.VariableIntroduction Nothing propVar (Just (accessorString prop (AST.Var Nothing varName))) : il)
298− literalToBinderIL varName done (ArrayLiteral bs) = do
299− il <- go done 0 bs
300− return [AST.IfElse Nothing (AST.Binary Nothing AST.EqualTo
301− (arrayLength $ AST.Var Nothing varName)
302− (AST.NumericLiteral Nothing (Left (fromIntegral $ length bs))))
303− (AST.Block Nothing il) Nothing]
304− where
305− go :: [AST] -> Integer -> [Binder Ann] -> m [AST]
306− go done' _ [] = return done'
307− go done' index (binder:bs') = do
308− elVar <- freshName'
309− done'' <- go done' (index + 1) bs'
310− il <- binderToIL elVar done'' binder
311− return (AST.VariableIntroduction Nothing elVar (Just (AST.Indexer Nothing (AST.NumericLiteral Nothing (Left index)) (AST.Var Nothing varName))) : il)
312−
313−emptyAnn :: Ann
314−emptyAnn = (SourceSpan "" (SourcePos 0 0) (SourcePos 0 0), [], Nothing, Nothing)
315−
316−arrayLength :: AST -> AST
317−arrayLength a = AST.App Nothing (AST.Var Nothing arrayLengthFn) [a]
318−-- arrayLength a = AST.Var Nothing (arrayLengthFn <> "(" <> prettyPrintIL1 a <> ")")
319−
320−copyDict :: AST -> AST
321−copyDict a = AST.App Nothing (AST.Var Nothing copyDictFn) [a]
src/CodeGen/IL/Common.hsdeleted−196
@@ -1,196 +0,0 @@
1−-- | Common code generation utility functions
2−module CodeGen.IL.Common where
3−
4−import Prelude.Compat
5−
6−import Control.Monad.Supply.Class (MonadSupply, freshName)
7−import Data.Char
8−import Data.Maybe (maybe)
9−import Data.Monoid ((<>))
10−import Data.Text (Text)
11−import qualified Data.Text as T
12−
13−import Language.PureScript.Crash
14−import Language.PureScript.Names
15−
16−import qualified Language.PureScript.Constants as C
17−
18−moduleNameToIL :: ModuleName -> Text
19−moduleNameToIL (ModuleName mn) =
20− let name = T.replace "." "_" mn
21− in if nameIsILBuiltIn name then (name <> moduleRenamerMarker) else name
22−
23−moduleNameToIL' :: ModuleName -> Text
24−moduleNameToIL' (ModuleName mn) = T.replace "_" "." mn
25−
26−-- | Convert an 'Ident' into a valid IL identifier:
27−--
28−-- * Alphanumeric characters are kept unmodified.
29−--
30−-- * Reserved IL identifiers are wrapped with '_'.
31−--
32−identToIL :: Ident -> Text
33−identToIL UnusedIdent = unusedName
34−identToIL (Ident "$__unused") = unusedName
35−identToIL (Ident name) | name == C.undefined = undefinedName -- Note: done in Printer.hs too
36−identToIL (Ident name) = properToIL name
37−identToIL (GenIdent _ _) = internalError "GenIdent in identToIL"
38−
39−moduleIdentToIL :: Ident -> Text
40−moduleIdentToIL UnusedIdent = unusedName
41−moduleIdentToIL (Ident name) | name == C.undefined = undefinedName
42−moduleIdentToIL (Ident name) = moduleProperToIL name
43−moduleIdentToIL (GenIdent _ _) = internalError "GenIdent in identToIL"
44−
45−undefinedName :: Text
46−undefinedName = "Undefined"
47−
48−unusedName :: Text
49−unusedName = "_"
50−
51−properToIL :: Text -> Text
52−properToIL name
53− | nameIsILReserved name = "ˉ" <> name
54− | otherwise = T.concatMap identCharToText name
55−
56−-- | Attempts to find a human-readable name for a symbol, if none has been specified returns the
57−-- ordinal value.
58−identCharToText :: Char -> Text
59−identCharToText 'ṩ' = "_ṩ"
60−identCharToText c | isAlphaNum c = T.singleton c
61−identCharToText '_' = "_"
62−identCharToText '\'' = "ꞌ" -- lowercase saltillo
63−identCharToText c = error (show (ord c)) -- TODO: should never occur now(?)
64−
65−moduleProperToIL :: Text -> Text
66−moduleProperToIL = T.concatMap identCharToText
67−
68−-- | Checks whether an identifier name is reserved in IL.
69−nameIsILReserved :: Text -> Bool
70−nameIsILReserved name =
71− name `elem` ilAnyReserved
72−
73−-- | Checks whether a name matches a built-in value in IL.
74−nameIsILBuiltIn :: Text -> Bool
75−nameIsILBuiltIn name =
76− name `elem`
77− [ "Any"
78− , "Apply"
79− , "Contains"
80− , "CopyDict"
81− , "Dict"
82− , "EffFn"
83− , "Fn"
84− , "Foreign"
85− , "Get"
86− , "Index"
87− , "Is"
88− , "Length"
89− , "Once"
90− , "Run"
91− , "Undefined"
92− ]
93−
94−ilAnyReserved :: [Text]
95−ilAnyReserved =
96− concat
97− [ ilKeywords
98− , ilLiterals
99− ]
100−
101−ilKeywords :: [Text]
102−ilKeywords =
103− [ "break"
104− , "case"
105− , "chan"
106− , "const"
107− , "continue"
108− , "default"
109− , "defer"
110− , "else"
111− , "fallthrough"
112− , "for"
113− , "func"
114− , "go"
115− , "goto"
116− , "if"
117− , "import"
118− , "interface"
119− , "map"
120− , "package"
121− , "range"
122− , "return"
123− , "select"
124− , "struct"
125− , "switch"
126− , "type"
127− , "var"
128− ]
129−
130−ilLiterals :: [Text]
131−ilLiterals =
132− [ "init"
133− , "nil"
134− , "int"
135− , "int8"
136− , "int16"
137− , "int32"
138− , "int64"
139− , "uint"
140− , "uint8"
141− , "uint16"
142− , "uint32"
143− , "uint64"
144− , "uintptr"
145− , "float32"
146− , "float64"
147− , "complex64"
148− , "complex128"
149− , "byte"
150− , "rune"
151− , "string"
152− ]
153−
154−withPrefix :: Text -> Text
155−withPrefix s = "Ꞌ" <> s -- uppercase saltillo
156−
157−anyType :: Text
158−anyType = "Any"
159−
160−dictType :: Text
161−dictType = "Dict"
162−
163−arrayType :: Text
164−arrayType = "[]" <> anyType
165−
166−uncurriedFnType :: Int -> Text
167−uncurriedFnType i = "Fn" <> (T.pack . show $ i)
168−
169−int :: Text
170−int = "int"
171−
172−float :: Text
173−float = "float64"
174−
175−bool :: Text
176−bool = "bool"
177−
178−string :: Text
179−string = "string"
180−
181−arrayLengthFn :: Text
182−arrayLengthFn = "Length"
183−
184−indexFn :: Text
185−indexFn = "Index"
186−
187−copyDictFn :: Text
188−copyDictFn = "CopyDict"
189−
190−freshName' :: MonadSupply m => m Text
191−freshName' = do
192− name <- freshName
193− return $ T.replace "$" "ṩ" name
194−
195−moduleRenamerMarker :: Text
196−moduleRenamerMarker = "__"
src/CodeGen/IL/Optimizer.hsdeleted−81
@@ -1,81 +0,0 @@
1−-- | This module optimizes code in the intermediate representation.
2−module CodeGen.IL.Optimizer (optimize) where
3−
4−import Prelude.Compat
5−
6−import Control.Monad.Supply.Class (MonadSupply)
7−import Data.Text (Text)
8−import Language.PureScript.CoreImp.AST
9−import Language.PureScript.CoreImp.Optimizer.Blocks
10−import Language.PureScript.CoreImp.Optimizer.Common hiding (isDict)
11−import Language.PureScript.CoreImp.Optimizer.Inliner (etaConvert, evaluateIifes, inlineVariables, unThunk)
12−import Language.PureScript.CoreImp.Optimizer.Unused
13−
14−import CodeGen.IL.Common (unusedName)
15−import CodeGen.IL.Optimizer.Inliner
16−import CodeGen.IL.Optimizer.MagicDo
17−import CodeGen.IL.Optimizer.TCO
18−
19−import qualified Language.PureScript.Constants as C
20−
21−-- | Apply a series of optimizer passes to simplified IL code
22−optimize :: MonadSupply m => AST -> AST -> m AST
23−optimize mn il = do
24− il' <- untilFixedPoint (inlineFnComposition . inlineUnsafeCoerce . inlineUnsafePartial . tidyUp) il
25− il'' <- untilFixedPoint (return . ignoreUnusedResults . inlineCommonValues . inlineCommonOperators . tidyUp) . tco mn . inlineST
26− =<< untilFixedPoint (return . magicDoST)
27− =<< untilFixedPoint (return . magicDoEff)
28− =<< untilFixedPoint (return . magicDoEffect) il'
29− untilFixedPoint (return . collapseVarDeclarations) il''
30− where
31− tidyUp :: AST -> AST
32− tidyUp = applyAll
33− [ collapseNestedBlocks
34− , collapseNestedIfs
35− , collapseIfChecks
36− , removeCodeAfterReturnStatements
37− , unThunk
38− , etaConvert
39− , evaluateIifes
40− , inlineVariables
41− ]
42−
43−untilFixedPoint :: (Monad m, Eq a) => (a -> m a) -> a -> m a
44−untilFixedPoint f = go
45− where
46− go a = do
47− a' <- f a
48− if a' == a then return a' else go a'
49−
50−collapseIfChecks :: AST -> AST
51−collapseIfChecks = everywhere collapse where
52− collapse :: AST -> AST
53− collapse (IfElse _ (Binary _ EqualTo (BooleanLiteral _ True) (BooleanLiteral _ True)) (Block _ [exprs]) _) = exprs
54− collapse (IfElse _ (Binary _ EqualTo (Indexer _ (Var _ prop) (Var _ val)) (BooleanLiteral _ True)) (Block _ [exprs]) _)
55− | prop == "otherwise" && val == "Data_Boolean" = exprs
56− collapse exprs = exprs
57−
58−collapseVarDeclarations :: AST -> AST
59−collapseVarDeclarations = everywhere collapse where
60− collapse :: AST -> AST
61− collapse (Block ss sts) = Block ss (go Nothing sts)
62− collapse s = s
63−
64− go :: Maybe Text -> [AST] -> [AST]
65− go _ [] = []
66− go Nothing (decl@(VariableIntroduction _ var (Just _)) : sts) = decl : go (Just var) (go Nothing sts)
67− go (Just var') ((VariableIntroduction ss var (Just val)) : sts)
68− | var == var' = (Assignment ss (Var Nothing var) val) : go (Just var') sts
69− go var' (s:sts) = s : go var' sts
70−
71−ignoreUnusedResults :: AST -> AST
72−ignoreUnusedResults = everywhere $ removeFromBlock go
73− where
74− go :: [AST] -> [AST]
75− go [] = []
76− go (VariableIntroduction ss var (Just s) : sts)
77− | not $ any (everything (||) (isUsed var)) sts = sts'
78− where
79− sts' | App{} <- s = s : (go sts)
80− | otherwise = go sts
81− go (s:sts) = s : go sts
src/CodeGen/IL/Optimizer/Common.hsdeleted−18
@@ -1,18 +0,0 @@
1−-- | Common functions used by the various optimizer phases
2−module CodeGen.IL.Optimizer.Common where
3−
4−import Prelude.Compat
5−
6−import Data.Text (Text)
7−
8−import Language.PureScript.CoreImp.AST
9−import Language.PureScript.PSString (PSString, decodeString)
10−
11−isDict :: (Text, PSString) -> AST -> Bool
12−isDict (moduleName, dictName) (Indexer _ (Var _ x) (Var _ y)) =
13− Just x == decodeString dictName && y == moduleName
14−isDict _ _ = False
15−
16−isDict' :: [(Text, PSString)] -> AST -> Bool
17−isDict' xs il = any (`isDict` il) xs
18−
src/CodeGen/IL/Optimizer/Inliner.hsdeleted−435
@@ -1,435 +0,0 @@
1−-- | This module performs basic inlining of known functions
2−module CodeGen.IL.Optimizer.Inliner
3− ( inlineCommonValues
4− , inlineCommonOperators
5− , inlineFnComposition
6− , inlineUnsafeCoerce
7− , inlineUnsafePartial
8− ) where
9−
10−import Prelude.Compat
11−
12−import Control.Monad.Supply.Class (MonadSupply)
13−
14−import Data.Either (rights)
15−import Data.Maybe (fromMaybe)
16−import Data.String (IsString, fromString)
17−import Data.Text (Text)
18−import qualified Data.Text as T
19−
20−import Language.PureScript.PSString (PSString, decodeString, mkString)
21−import Language.PureScript.CoreImp.AST
22−import Language.PureScript.CoreImp.Optimizer.Common hiding (isDict, isDict')
23−import Language.PureScript.AST (SourceSpan(..))
24−import qualified Language.PureScript.Constants as C
25−
26−import CodeGen.IL.Common
27−import CodeGen.IL.Optimizer.Common
28−
29−-- TODO: Potential bug:
30−-- Shouldn't just inline this case: { var x = 0; x.toFixed(10); }
31−-- Needs to be: { 0..toFixed(10); }
32−-- Probably needs to be fixed in pretty-printer instead.
33−shouldInline :: AST -> Bool
34−shouldInline (Var _ _) = True
35−shouldInline (NumericLiteral _ _) = True
36−shouldInline (StringLiteral _ _) = True
37−shouldInline (BooleanLiteral _ _) = True
38−shouldInline (Indexer _ index val) = shouldInline index && shouldInline val
39−shouldInline _ = False
40−
41−inlineCommonValues :: AST -> AST
42−inlineCommonValues = everywhere convert
43− where
44− convert :: AST -> AST
45− convert (App ss fn [dict])
46− | isDict' [semiringNumber, semiringInt] dict && isDict fnZero fn = NumericLiteral ss (Left 0)
47− | isDict' [semiringNumber, semiringInt] dict && isDict fnOne fn = NumericLiteral ss (Left 1)
48− | isDict boundedBoolean dict && isDict fnBottom fn = BooleanLiteral ss False
49− | isDict boundedBoolean dict && isDict fnTop fn = BooleanLiteral ss True
50− convert (App ss (App _ fn [dict]) [x])
51− | isDict ringInt dict && isDict fnNegate fn = Unary ss Negate (unbox x $ snd ringInt)
52− convert (App ss (App _ (App _ fn [dict]) [x]) [y])
53− | isDict semiringInt dict && isDict fnAdd fn = intOp ss Add semiringInt x y
54− | isDict semiringInt dict && isDict fnMultiply fn = intOp ss Multiply semiringInt x y
55− | isDict ringInt dict && isDict fnSubtract fn = intOp ss Subtract ringInt x y
56− convert other = other
57− fnZero = (C.dataSemiring, C.zero)
58− fnOne = (C.dataSemiring, C.one)
59− fnBottom = (C.dataBounded, C.bottom)
60− fnTop = (C.dataBounded, C.top)
61− fnAdd = (C.dataSemiring, C.add)
62− fnMultiply = (C.dataSemiring, C.mul)
63− fnSubtract = (C.dataRing, C.sub)
64− fnNegate = (C.dataRing, C.negate)
65− intOp ss op (_, d) x y = Binary ss op (unbox x d) (unbox y d)
66−
67−inlineCommonOperators :: AST -> AST
68−inlineCommonOperators = everywhereTopDown $ applyAll $
69− [ binary semiringNumber opAdd Add
70− , binary semiringNumber opMul Multiply
71−
72− , binary ringNumber opSub Subtract
73− , unary ringNumber opNegate Negate
74−
75− , binary euclideanRingNumber opDiv Divide
76−
77− -- , binary eqNumber opEq EqualTo
78− -- , binary eqNumber opNotEq NotEqualTo
79− , binary eqInt opEq EqualTo
80− , binary eqInt opNotEq NotEqualTo
81− , binary eqString opEq EqualTo
82− , binary eqString opNotEq NotEqualTo
83− , binary eqChar opEq EqualTo
84− , binary eqChar opNotEq NotEqualTo
85− , binary eqBoolean opEq EqualTo
86− , binary eqBoolean opNotEq NotEqualTo
87−
88− , binary ordBoolean opLessThan LessThan
89− , binary ordBoolean opLessThanOrEq LessThanOrEqualTo
90− , binary ordBoolean opGreaterThan GreaterThan
91− , binary ordBoolean opGreaterThanOrEq GreaterThanOrEqualTo
92− , binary ordChar opLessThan LessThan
93− , binary ordChar opLessThanOrEq LessThanOrEqualTo
94− , binary ordChar opGreaterThan GreaterThan
95− , binary ordChar opGreaterThanOrEq GreaterThanOrEqualTo
96− , binary ordInt opLessThan LessThan
97− , binary ordInt opLessThanOrEq LessThanOrEqualTo
98− , binary ordInt opGreaterThan GreaterThan
99− , binary ordInt opGreaterThanOrEq GreaterThanOrEqualTo
100− , binary ordNumber opLessThan LessThan
101− , binary ordNumber opLessThanOrEq LessThanOrEqualTo
102− , binary ordNumber opGreaterThan GreaterThan
103− , binary ordNumber opGreaterThanOrEq GreaterThanOrEqualTo
104− , binary ordString opLessThan LessThan
105− , binary ordString opLessThanOrEq LessThanOrEqualTo
106− , binary ordString opGreaterThan GreaterThan
107− , binary ordString opGreaterThanOrEq GreaterThanOrEqualTo
108−
109− , binary semigroupString opAppend Add
110−
111− , binary heytingAlgebraBoolean opConj And
112− , binary heytingAlgebraBoolean opDisj Or
113− , unary heytingAlgebraBoolean opNot Not
114−
115− , binary' C.dataIntBits C.or BitwiseOr
116− , binary' C.dataIntBits C.and BitwiseAnd
117− , binary' C.dataIntBits C.xor BitwiseXor
118− , binary' C.dataIntBits C.shl ShiftLeft
119− , binary' C.dataIntBits C.shr ShiftRight
120− , unary' C.dataIntBits C.complement BitwiseNot
121−
122− , inlineNonClassFunction (isModFn (C.dataFunction, C.apply)) $ \f x -> App Nothing f [x]
123− , inlineNonClassFunction (isModFn (C.dataFunction, C.applyFlipped)) $ \x f -> App Nothing f [x]
124− , inlineUnsafeIndex (isModFnWithDict (C.dataArray, C.unsafeIndex)) $ flip (Indexer Nothing)
125− ] ++
126− [ fn | i <- [0..10], fn <- [ mkFn i, runFn i ] ] ++
127− [ fn | i <- [0..10], fn <- [ mkEffFn C.controlMonadEffUncurried C.mkEffFn i, runEffFn C.controlMonadEffUncurried C.runEffFn i ] ] ++
128− [ fn | i <- [0..10], fn <- [ mkEffFn C.effectUncurried C.mkEffectFn i, runEffFn C.effectUncurried C.runEffectFn i ] ]
129− where
130− binary :: (Text, PSString) -> (Text, PSString) -> BinaryOperator -> AST -> AST
131− binary dict@(_, d) fns op = convert where
132− convert :: AST -> AST
133− convert (App ss (App _ (App _ fn [dict']) [x]) [y])
134− | isDict dict dict' && isDict fns fn = Binary ss op (unbox x d) (unbox y d)
135− convert other = other
136− binary' :: Text -> PSString -> BinaryOperator -> AST -> AST
137− binary' moduleName opString op = convert where
138− convert :: AST -> AST
139− convert (App ss (App _ fn [x]) [y]) | isDict (moduleName, opString) fn = Binary ss op (unbox x mn) (unbox y mn)
140− convert other = other
141− mn :: PSString
142− mn = mkString moduleName
143− unary :: (Text, PSString) -> (Text, PSString) -> UnaryOperator -> AST -> AST
144− unary dicts@(_, d) fns op = convert where
145− convert :: AST -> AST
146− convert (App ss (App _ fn [dict']) [x]) | isDict dicts dict' && isDict fns fn = Unary ss op (unbox x d)
147− convert other = other
148− unary' :: Text -> PSString -> UnaryOperator -> AST -> AST
149− unary' moduleName fnName op = convert where
150− convert :: AST -> AST
151− convert (App ss fn [x]) | isDict (moduleName, fnName) fn = Unary ss op (unbox x fnName)
152− convert other = other
153−
154− mkFn :: Int -> AST -> AST
155− mkFn = mkFn' C.dataFunctionUncurried C.mkFn $ \ss1 ss2 ss3 args il ->
156− Function ss1 Nothing args (Block ss2 [Return ss3 il])
157−
158− mkEffFn :: Text -> Text -> Int -> AST -> AST
159− mkEffFn modName fnName = mkFn' modName fnName $ \ss1 ss2 ss3 args il ->
160− Function ss1 Nothing args (Block ss2 [Return ss3 (App ss3 il [])])
161−
162− mkFn' :: Text -> Text -> (Maybe SourceSpan -> Maybe SourceSpan -> Maybe SourceSpan -> [Text] -> AST -> AST) -> Int -> AST -> AST
163− mkFn' modName fnName res 0 = convert where
164− convert :: AST -> AST
165− convert (App _ mkFnN [Function s1 Nothing [_] (Block s2 [Return s3 il])]) | isNFn modName fnName 0 mkFnN =
166− res s1 s2 s3 [] il
167− convert other = other
168− mkFn' modName fnName res n = convert where
169− convert :: AST -> AST
170− convert orig@(App ss mkFnN [fn]) | isNFn modName fnName n mkFnN =
171− case collectArgs n [] fn of
172− Just (args, [Return ss' ret]) -> res ss ss ss' args ret
173− _ -> orig
174− convert other = other
175− collectArgs :: Int -> [Text] -> AST -> Maybe ([Text], [AST])
176− collectArgs 1 acc (Function _ Nothing [oneArg] (Block _ il)) | length acc == n - 1 = Just (reverse (oneArg : acc), il)
177− collectArgs m acc (Function _ Nothing [oneArg] (Block _ [Return _ ret])) = collectArgs (m - 1) (oneArg : acc) ret
178− collectArgs _ _ _ = Nothing
179−
180− isNFn :: Text -> Text -> Int -> AST -> Bool
181− isNFn expectMod prefix n (Indexer _ (Var _ name) (Var _ modName)) | modName == expectMod =
182− name == fromString (T.unpack prefix <> show n)
183− isNFn _ _ _ _ = False
184−
185− runFn :: Int -> AST -> AST
186− runFn = runFn' C.dataFunctionUncurried C.runFn (\ss f args ->
187− let len = length args
188− typ = mkString $ uncurriedFnType len in
189− if len > 0
190− then App ss (App Nothing (StringLiteral Nothing typ) [f]) args
191− else App ss f args)
192− runEffFn :: Text -> Text -> Int -> AST -> AST
193− runEffFn modName fnName = runFn' modName fnName $ \ss fn acc ->
194− let typ = mkString . uncurriedFnType $ length acc in
195− Function ss Nothing [] (Block ss [Return ss (App ss (App Nothing (StringLiteral Nothing typ) [fn]) acc)])
196−
197− runFn' :: Text -> Text -> (Maybe SourceSpan -> AST -> [AST] -> AST) -> Int -> AST -> AST
198− runFn' modName runFnName res n = convert where
199− convert :: AST -> AST
200− convert il = fromMaybe il $ go n [] il
201−
202− go :: Int -> [AST] -> AST -> Maybe AST
203− go 0 acc (App ss runFnN [fn]) | isNFn modName runFnName n runFnN && length acc == n =
204− Just $ res ss fn acc
205− go m acc (App _ lhs [arg]) = go (m - 1) (arg : acc) lhs
206− go _ _ _ = Nothing
207−
208− inlineNonClassFunction :: (AST -> Bool) -> (AST -> AST -> AST) -> AST -> AST
209− inlineNonClassFunction p f = convert where
210− convert :: AST -> AST
211− convert (App _ (App _ op' [x]) [y]) | p op' = f x y
212− convert other = other
213−
214− inlineUnsafeIndex :: (AST -> Bool) -> (AST -> AST -> AST) -> AST -> AST
215− inlineUnsafeIndex p _ = convert where
216− convert :: AST -> AST
217− convert (App _ (App ss op' [x]) [y@NumericLiteral{}])
218− | p op' = Indexer ss y x
219− convert (App _ (App ss op' [x]) [y])
220− | p op' = App ss (Var Nothing indexFn) [x, y]
221− convert other = other
222−
223− isModFn :: (Text, PSString) -> AST -> Bool
224− isModFn (m, op) (Indexer _ (Var _ op') (Var _ m')) =
225− m == m' && decodeString op == Just op'
226− isModFn _ _ = False
227−
228− isModFnWithDict :: (Text, PSString) -> AST -> Bool
229− isModFnWithDict (m, op) (App _ (Indexer _ (Var _ op') (Var _ m')) [Var _ _]) =
230− m == m' && decodeString op == Just op'
231− isModFnWithDict _ _ = False
232−
233−-- (f <<< g $ x) = f (g x)
234−-- (f <<< g) = \x -> f (g x)
235−inlineFnComposition :: forall m. MonadSupply m => AST -> m AST
236−inlineFnComposition = everywhereTopDownM convert where
237− convert :: AST -> m AST
238− convert (App s1 (App s2 (App _ (App _ fn [dict']) [x]) [y]) [z])
239− | isFnCompose dict' fn = return $ App s1 x [App s2 y [z]]
240− | isFnComposeFlipped dict' fn = return $ App s2 y [App s1 x [z]]
241− convert app@(App ss (App _ (App _ fn [dict']) _) _)
242− | isFnCompose dict' fn || isFnComposeFlipped dict' fn = mkApps ss <$> goApps app <*> freshName'
243− convert other = return other
244−
245− mkApps :: Maybe SourceSpan -> [Either AST (Text, AST)] -> Text -> AST
246− mkApps ss fns a = App ss (Function ss Nothing [] (Block ss $ vars <> [Return Nothing comp])) []
247− where
248− vars = uncurry (VariableIntroduction ss) . fmap Just <$> rights fns
249− comp = Function ss Nothing [a] (Block ss [Return Nothing apps])
250− apps = foldr (\fn acc -> App ss (mkApp fn) [acc]) (Var ss a) fns
251−
252− mkApp :: Either AST (Text, AST) -> AST
253− mkApp = either id $ \(name, arg) -> Var (getSourceSpan arg) name
254−
255− goApps :: AST -> m [Either AST (Text, AST)]
256− goApps (App _ (App _ (App _ fn [dict']) [x]) [y])
257− | isFnCompose dict' fn = mappend <$> goApps x <*> goApps y
258− | isFnComposeFlipped dict' fn = mappend <$> goApps y <*> goApps x
259− goApps app@(App {}) = pure . Right . (,app) <$> freshName'
260− goApps other = pure [Left other]
261−
262− isFnCompose :: AST -> AST -> Bool
263− isFnCompose dict' fn = isDict semigroupoidFn dict' && isDict fnCompose fn
264−
265− isFnComposeFlipped :: AST -> AST -> Bool
266− isFnComposeFlipped dict' fn = isDict semigroupoidFn dict' && isDict fnComposeFlipped fn
267−
268− fnCompose :: forall a b. (IsString a, IsString b) => (a, b)
269− fnCompose = (C.controlSemigroupoid, C.compose)
270−
271− fnComposeFlipped :: forall a b. (IsString a, IsString b) => (a, b)
272− fnComposeFlipped = (C.controlSemigroupoid, C.composeFlipped)
273−
274−inlineUnsafeCoerce :: AST -> AST
275−inlineUnsafeCoerce = everywhereTopDown convert where
276− convert (App _ (Indexer _ (Var _ unsafeCoerceFn) (Var _ unsafeCoerce)) [ comp ])
277− | unsafeCoerceFn == C.unsafeCoerceFn && unsafeCoerce == C.unsafeCoerce
278− = comp
279− convert other = other
280−
281−inlineUnsafePartial :: AST -> AST
282−inlineUnsafePartial = everywhereTopDown convert where
283− convert (App ss fn [ comp ])
284− | isDict fnUnsafePartial fn
285− = App ss comp []
286− where
287− fnUnsafePartial :: forall a b. (IsString a, IsString b) => (a, b)
288− fnUnsafePartial = (C.partialUnsafe, C.unsafePartial)
289− convert other = other
290−
291−semiringNumber :: forall a b. (IsString a, IsString b) => (a, b)
292−semiringNumber = (C.dataSemiring, C.semiringNumber)
293−
294−semiringInt :: forall a b. (IsString a, IsString b) => (a, b)
295−semiringInt = (C.dataSemiring, C.semiringInt)
296−
297−ringNumber :: forall a b. (IsString a, IsString b) => (a, b)
298−ringNumber = (C.dataRing, C.ringNumber)
299−
300−ringInt :: forall a b. (IsString a, IsString b) => (a, b)
301−ringInt = (C.dataRing, C.ringInt)
302−
303−euclideanRingNumber :: forall a b. (IsString a, IsString b) => (a, b)
304−euclideanRingNumber = (C.dataEuclideanRing, C.euclideanRingNumber)
305−
306−-- eqNumber :: forall a b. (IsString a, IsString b) => (a, b)
307−-- eqNumber = (C.dataEq, C.eqNumber)
308−
309−eqInt :: forall a b. (IsString a, IsString b) => (a, b)
310−eqInt = (C.dataEq, C.eqInt)
311−
312−eqString :: forall a b. (IsString a, IsString b) => (a, b)
313−eqString = (C.dataEq, C.eqString)
314−
315−eqChar :: forall a b. (IsString a, IsString b) => (a, b)
316−eqChar = (C.dataEq, C.eqChar)
317−
318−eqBoolean :: forall a b. (IsString a, IsString b) => (a, b)
319−eqBoolean = (C.dataEq, C.eqBoolean)
320−
321−ordBoolean :: forall a b. (IsString a, IsString b) => (a, b)
322−ordBoolean = (C.dataOrd, C.ordBoolean)
323−
324−ordNumber :: forall a b. (IsString a, IsString b) => (a, b)
325−ordNumber = (C.dataOrd, C.ordNumber)
326−
327−ordInt :: forall a b. (IsString a, IsString b) => (a, b)
328−ordInt = (C.dataOrd, C.ordInt)
329−
330−ordString :: forall a b. (IsString a, IsString b) => (a, b)
331−ordString = (C.dataOrd, C.ordString)
332−
333−ordChar :: forall a b. (IsString a, IsString b) => (a, b)
334−ordChar = (C.dataOrd, C.ordChar)
335−
336−semigroupString :: forall a b. (IsString a, IsString b) => (a, b)
337−semigroupString = (C.dataSemigroup, C.semigroupString)
338−
339−boundedBoolean :: forall a b. (IsString a, IsString b) => (a, b)
340−boundedBoolean = (C.dataBounded, C.boundedBoolean)
341−
342−heytingAlgebraBoolean :: forall a b. (IsString a, IsString b) => (a, b)
343−heytingAlgebraBoolean = (C.dataHeytingAlgebra, C.heytingAlgebraBoolean)
344−
345−semigroupoidFn :: forall a b. (IsString a, IsString b) => (a, b)
346−semigroupoidFn = (C.controlSemigroupoid, C.semigroupoidFn)
347−
348−opAdd :: forall a b. (IsString a, IsString b) => (a, b)
349−opAdd = (C.dataSemiring, C.add)
350−
351−opMul :: forall a b. (IsString a, IsString b) => (a, b)
352−opMul = (C.dataSemiring, C.mul)
353−
354−opEq :: forall a b. (IsString a, IsString b) => (a, b)
355−opEq = (C.dataEq, C.eq)
356−
357−opNotEq :: forall a b. (IsString a, IsString b) => (a, b)
358−opNotEq = (C.dataEq, C.notEq)
359−
360−opLessThan :: forall a b. (IsString a, IsString b) => (a, b)
361−opLessThan = (C.dataOrd, C.lessThan)
362−
363−opLessThanOrEq :: forall a b. (IsString a, IsString b) => (a, b)
364−opLessThanOrEq = (C.dataOrd, C.lessThanOrEq)
365−
366−opGreaterThan :: forall a b. (IsString a, IsString b) => (a, b)
367−opGreaterThan = (C.dataOrd, C.greaterThan)
368−
369−opGreaterThanOrEq :: forall a b. (IsString a, IsString b) => (a, b)
370−opGreaterThanOrEq = (C.dataOrd, C.greaterThanOrEq)
371−
372−opAppend :: forall a b. (IsString a, IsString b) => (a, b)
373−opAppend = (C.dataSemigroup, C.append)
374−
375−opSub :: forall a b. (IsString a, IsString b) => (a, b)
376−opSub = (C.dataRing, C.sub)
377−
378−opNegate :: forall a b. (IsString a, IsString b) => (a, b)
379−opNegate = (C.dataRing, C.negate)
380−
381−opDiv :: forall a b. (IsString a, IsString b) => (a, b)
382−opDiv = (C.dataEuclideanRing, C.div)
383−
384−opConj :: forall a b. (IsString a, IsString b) => (a, b)
385−opConj = (C.dataHeytingAlgebra, C.conj)
386−
387−opDisj :: forall a b. (IsString a, IsString b) => (a, b)
388−opDisj = (C.dataHeytingAlgebra, C.disj)
389−
390−opNot :: forall a b. (IsString a, IsString b) => (a, b)
391−opNot = (C.dataHeytingAlgebra, C.not)
392−
393−unbox :: AST -> PSString -> AST
394−unbox e d = select . inlineCommonValues $ inlineCommonOperators e
395− where
396− select :: AST -> AST
397− select e' =
398− case e' of
399− Var{} -> unbox' e'
400− App{} -> unbox' e'
401− Indexer{} -> unbox' e'
402− _ -> e'
403− unbox' :: AST -> AST
404− unbox' e' = maybe e' (\t -> App Nothing (StringLiteral Nothing t) [e']) $ unboxType d
405−
406−unboxType :: PSString -> Maybe PSString
407−unboxType s = do
408− t <- decodeString s
409− t' <- unboxType' t
410− return $ mkString t'
411− where
412− unboxType' :: Text -> Maybe Text
413− unboxType' t
414− | t == C.eqInt ||
415− t == C.semiringInt ||
416− t == C.ringInt ||
417− t == C.ordInt ||
418− t == C.dataIntBits
419− = Just int
420− | t == C.semiringNumber ||
421− t == C.ringNumber ||
422− t == C.ordNumber ||
423− t == C.euclideanRingNumber
424− = Just float
425− | t == C.eqBoolean ||
426− t == C.ordBoolean ||
427− t == C.heytingAlgebraBoolean
428− = Just bool
429− | t == C.eqChar ||
430− t == C.eqString ||
431− t == C.ordChar ||
432− t == C.ordString ||
433− t == C.semigroupString
434− = Just string
435− | otherwise = Nothing
src/CodeGen/IL/Optimizer/MagicDo.hsdeleted−146
@@ -1,146 +0,0 @@
1−-- | This module implements the "Magic Do" optimization, which inlines calls to return
2−-- and bind for the Eff monad, as well as some of its actions.
3−module CodeGen.IL.Optimizer.MagicDo (magicDoEffect, magicDoEff, magicDoST, inlineST) where
4−
5−import Prelude.Compat
6−import Protolude (ordNub)
7−
8−import Data.Maybe (fromJust, isJust)
9−import Data.Text (Text)
10−
11−import Language.PureScript.CoreImp.AST
12−import Language.PureScript.CoreImp.Optimizer.Common hiding (isDict)
13−import Language.PureScript.PSString (PSString, decodeString, mkString)
14−import qualified Language.PureScript.Constants as C
15−
16−import CodeGen.IL.Common (undefinedName, unusedName)
17−import CodeGen.IL.Optimizer.Common
18−
19−-- | Inline type class dictionaries for >>= and return for the Eff monad
20−--
21−-- E.g.
22−--
23−-- Prelude[">>="](dict)(m1)(function(x) {
24−-- return ...;
25−-- })
26−--
27−-- becomes
28−--
29−-- function __do {
30−-- var x = m1();
31−-- ...
32−-- }
33−magicDoEff :: AST -> AST
34−magicDoEff = magicDo C.eff C.effDictionaries
35−
36−magicDoEffect :: AST -> AST
37−magicDoEffect = magicDo C.effect C.effectDictionaries
38−
39−magicDoST :: AST -> AST
40−magicDoST = magicDo C.st C.stDictionaries
41−
42−magicDo :: Text -> C.EffectDictionaries -> AST -> AST
43−magicDo effectModule C.EffectDictionaries{..} = everywhereTopDown convert
44− where
45− -- Desugar monomorphic calls to >>= and return for the Eff monad
46− convert :: AST -> AST
47− -- Desugar pure
48− convert (App _ (App _ pure' [val]) []) | isPure pure' = val
49− -- Desugar discard
50− convert (App _ (App _ bind [m]) [Function s1 Nothing [unused] (Block s2 il)]) | isDiscard bind && unused == unusedName =
51− Function s1 Nothing [] $ Block s2 (App s2 m [] : map applyReturns il )
52− -- Desugar bind
53− convert (App _ (App _ bind [m]) [Function s1 Nothing [arg] (Block s2 il)]) | isBind bind =
54− Function s1 Nothing [] $ Block s2 (VariableIntroduction s2 arg (Just (App s2 m [])) : map applyReturns il)
55− -- Desugar untilE
56− convert (App s1 (App _ f [arg]) []) | isEffFunc edUntil f =
57− App s1 (Function s1 Nothing [] (Block s1 [ While s1 (Unary s1 Not (App s1 arg [])) (Block s1 []), Return s1 $ ObjectLiteral s1 []])) []
58− -- Desugar whileE
59− convert (App _ (App _ (App s1 f [arg1]) [arg2]) []) | isEffFunc edWhile f =
60− App s1 (Function s1 Nothing [] (Block s1 [ While s1 (App s1 arg1 []) (Block s1 [ App s1 arg2 [] ]), Return s1 $ ObjectLiteral s1 []])) []
61− -- -- Inline __do returns
62− -- convert (Return _ (App _ (Function _ (Just ident) [] body) [])) | ident == fnName = body
63− -- Inline double applications
64− convert (App _ (App s1 (Function s2 Nothing [] (Block ss body)) []) []) =
65− App s1 (Function s2 Nothing [] (Block ss (applyReturns `fmap` body))) []
66− convert other = other
67− -- Check if an expression represents a monomorphic call to >>= for the Eff monad
68− isBind (App _ fn [dict]) | isDict (effectModule, edBindDict) dict && isBindPoly fn = True
69− isBind _ = False
70− -- Check if an expression represents a call to @discard@
71− isDiscard (App _ (App _ fn [dict1]) [dict2])
72− | isDict (C.controlBind, C.discardUnitDictionary) dict1 &&
73− isDict (effectModule, edBindDict) dict2 &&
74− isDiscardPoly fn = True
75− isDiscard _ = False
76−
77− -- Check if an expression represents a monomorphic call to pure or return for the Eff applicative
78− isPure (App _ fn [dict]) | isDict (effectModule, edApplicativeDict) dict && isPurePoly fn = True
79− isPure _ = False
80− -- Check if an expression represents the polymorphic >>= function
81− isBindPoly = isDict (C.controlBind, C.bind)
82− -- Check if an expression represents the polymorphic pure function
83− isPurePoly = isDict (C.controlApplicative, C.pure')
84− -- Check if an expression represents the polymorphic discard function
85− isDiscardPoly = isDict (C.controlBind, C.discard)
86− -- Check if an expression represents a function in the Effect module
87− isEffFunc name (Indexer _ (StringLiteral _ name') (Var _ eff)) = eff == effectModule && name == name'
88− isEffFunc _ _ = False
89−
90− applyReturns :: AST -> AST
91− applyReturns (Return ss ret) = Return ss (App ss ret [])
92− applyReturns (Block ss ils) = Block ss (map applyReturns ils)
93− applyReturns (While ss cond il) = While ss cond (applyReturns il)
94− applyReturns (For ss v lo hi il) = For ss v lo hi (applyReturns il)
95− applyReturns (ForIn ss v xs il) = ForIn ss v xs (applyReturns il)
96− applyReturns (IfElse ss cond t f) = IfElse ss cond (applyReturns t) (applyReturns `fmap` f)
97− applyReturns other = other
98−
99−-- | Inline functions in the ST module
100−inlineST :: AST -> AST
101−inlineST = everywhere convertBlock
102− where
103− -- Look for runST blocks and inline the STRefs there.
104− -- If all STRefs are used in the scope of the same runST, only using { read, write, modify }STRef then
105− -- we can be more aggressive about inlining, and actually turn STRefs into local variables.
106− convertBlock (App s1 f [arg]) | isSTFunc C.runST f =
107− let refs = ordNub . findSTRefsIn $ arg
108− usages = findAllSTUsagesIn arg
109− allUsagesAreLocalVars = all (\u -> let v = toVar u in isJust v && fromJust v `elem` refs) usages
110− localVarsDoNotEscape = all (\r -> length (r `appearingIn` arg) == length (filter (\u -> let v = toVar u in v == Just r) usages)) refs
111− in App s1 (everywhere (convert (allUsagesAreLocalVars && localVarsDoNotEscape)) arg) []
112− convertBlock other = other
113− -- Convert a block in a safe way, preserving object wrappers of references,
114− -- or in a more aggressive way, turning wrappers into local variables depending on the
115− -- agg(ressive) parameter.
116− convert agg (App s1 f [arg]) | isSTFunc C.newSTRef f =
117− Function s1 Nothing [] (Block s1 [Return s1 $ if agg then arg else ObjectLiteral s1 [(mkString C.stRefValue, arg)]])
118− convert agg (App _ (App s1 f [ref]) []) | isSTFunc C.readSTRef f =
119− if agg then ref else Indexer s1 (StringLiteral s1 C.stRefValue) ref
120− convert agg (App _ (App _ (App s1 f [arg]) [ref]) []) | isSTFunc C.writeSTRef f =
121− if agg then Assignment s1 ref arg else Assignment s1 (Indexer s1 (StringLiteral s1 C.stRefValue) ref) arg
122− convert agg (App _ (App _ (App s1 f [func]) [ref]) []) | isSTFunc C.modifySTRef f =
123− if agg then Assignment s1 ref (App s1 func [ref]) else Assignment s1 (Indexer s1 (StringLiteral s1 C.stRefValue) ref) (App s1 func [Indexer s1 (StringLiteral s1 C.stRefValue) ref])
124− convert _ other = other
125− -- Check if an expression represents a function in the ST module
126− isSTFunc name (Indexer _ (StringLiteral _ name') (Var _ st)) = st == C.st && name == name'
127− isSTFunc _ _ = False
128− -- Find all ST Refs initialized in this block
129− findSTRefsIn = everything (++) isSTRef
130− where
131− isSTRef (VariableIntroduction _ ident (Just (App _ (App _ f [_]) []))) | isSTFunc C.newSTRef f = [ident]
132− isSTRef _ = []
133− -- Find all STRefs used as arguments to readSTRef, writeSTRef, modifySTRef
134− findAllSTUsagesIn = everything (++) isSTUsage
135− where
136− isSTUsage (App _ (App _ f [ref]) []) | isSTFunc C.readSTRef f = [ref]
137− isSTUsage (App _ (App _ (App _ f [_]) [ref]) []) | isSTFunc C.writeSTRef f || isSTFunc C.modifySTRef f = [ref]
138− isSTUsage _ = []
139− -- Find all uses of a variable
140− appearingIn ref = everything (++) isVar
141− where
142− isVar e@(Var _ v) | v == ref = [e]
143− isVar _ = []
144− -- Convert a AST value to a String if it is a Var
145− toVar (Var _ v) = Just v
146− toVar _ = Nothing
src/CodeGen/IL/Optimizer/TCO.hsdeleted−141
@@ -1,141 +0,0 @@
1−module CodeGen.IL.Optimizer.TCO (tco, tcoLoop) where
2−
3−import Prelude.Compat
4−
5−import Data.Text (Text)
6−import Data.Monoid ((<>))
7−import Language.PureScript.CoreImp.AST
8−import Language.PureScript.AST.SourcePos (SourceSpan)
9−import Safe (headDef, tailSafe)
10−import CodeGen.IL.Common
11−
12−import qualified Language.PureScript.Constants as C
13−
14−tcoLoop :: Text
15−tcoLoop = "ṩtco_loop"
16−
17−tco :: AST -> AST -> AST
18−tco mn = everywhere convert where
19− tcoVar :: Text -> Text
20− tcoVar arg = "ṩtco_var_" <> arg
21−
22− copyVar :: Text -> Text
23− copyVar arg = "ṩcopy_" <> arg
24−
25− tcoDone :: Text
26− tcoDone = "ṩtco_done"
27−
28− tcoResult :: Text
29− tcoResult = "ṩtco_result"
30−
31− convert :: AST -> AST
32− convert fn@(Function ss (Just name) args _)
33− | isTailRecursive name body'
34− = replace (toLoop name outerArgs innerArgs body')
35− where
36− innerArgs = headDef [] argss
37− outerArgs = concat . reverse $ tailSafe argss
38− (argss, body', replace) = collectAllFunctionArgs [] id fn
39− convert (Assignment ss (Var _ name) fn@(Function _ Nothing _ _))
40− | isTailRecursive name body'
41− = Assignment ss (Var ss name) (replace (toLoop name outerArgs innerArgs body'))
42− where
43− innerArgs = headDef [] argss
44− outerArgs = concat . reverse $ tailSafe argss
45− (argss, body', replace) = collectAllFunctionArgs [] id fn
46− convert js = js
47−
48− collectAllFunctionArgs :: [[Text]] -> (AST -> AST) -> AST -> ([[Text]], AST, AST -> AST)
49− collectAllFunctionArgs allArgs f (Function s1 ident args (Block s2 (body@(Return _ _):_))) =
50− collectAllFunctionArgs (args : allArgs) (\b -> f (Function s1 ident (map copyVar args) (Block s2 [b]))) body
51− collectAllFunctionArgs allArgs f (Function ss ident args body@(Block _ _)) =
52− (args : allArgs, body, f . Function ss ident (map copyVar args))
53− collectAllFunctionArgs allArgs f (Return s1 (Function s2 ident args (Block s3 [body]))) =
54− collectAllFunctionArgs (args : allArgs) (\b -> f (Return s1 (Function s2 ident (map copyVar args) (Block s3 [b])))) body
55− collectAllFunctionArgs allArgs f (Return s1 (Function s2 ident args body@(Block _ _))) =
56− (args : allArgs, body, f . Return s1 . Function s2 ident (map copyVar args))
57− collectAllFunctionArgs allArgs f body = (allArgs, body, f)
58−
59− isTailRecursive :: Text -> AST -> Bool
60− isTailRecursive ident js = countSelfReferences js > 0 && allInTailPosition js where
61− countSelfReferences = everything (+) match where
62− match :: AST -> Int
63− match (Var _ ident') | ident == ident' = 1
64− match _ = 0
65−
66− allInTailPosition (Return _ expr)
67− | isSelfCall ident expr = countSelfReferences expr == 1
68− | otherwise = countSelfReferences expr == 0
69− allInTailPosition (While _ js1 body)
70− = countSelfReferences js1 == 0 && allInTailPosition body
71− allInTailPosition (For _ _ js1 js2 body)
72− = countSelfReferences js1 == 0 && countSelfReferences js2 == 0 && allInTailPosition body
73− allInTailPosition (ForIn _ _ js1 body)
74− = countSelfReferences js1 == 0 && allInTailPosition body
75− allInTailPosition (IfElse _ js1 body el)
76− = countSelfReferences js1 == 0 && allInTailPosition body && all allInTailPosition el
77− allInTailPosition (Block _ body)
78− = all allInTailPosition body
79− allInTailPosition (Throw _ js1)
80− = countSelfReferences js1 == 0
81− allInTailPosition (ReturnNoResult _)
82− = True
83− allInTailPosition (VariableIntroduction _ _ js1)
84− = all ((== 0) . countSelfReferences) js1
85− allInTailPosition (Assignment _ _ js1)
86− = countSelfReferences js1 == 0
87− allInTailPosition (Comment _ _ js1)
88− = allInTailPosition js1
89− allInTailPosition _
90− = False
91−
92− toLoop :: Text -> [Text] -> [Text] -> AST -> AST
93− toLoop ident outerArgs innerArgs js =
94− Block rootSS $
95− concatMap (\arg -> [ VariableIntroduction rootSS (tcoVar arg) . Just $
96− Var rootSS (copyVar arg) ]) (outerArgs ++ innerArgs) ++
97− [ Assignment rootSS (Var rootSS $ "var " <> tcoDone) $ BooleanLiteral Nothing False
98− , VariableIntroduction rootSS tcoResult Nothing
99− , Assignment rootSS (Var rootSS $ "var " <> tcoLoop) (Function rootSS (Just tcoLoop) (outerArgs ++ innerArgs) (Block rootSS [loopify js]))
100− , While rootSS (Unary Nothing Not (Var rootSS tcoDone))
101− (Block rootSS
102− [(Assignment rootSS (Var rootSS tcoResult) (App rootSS (Var rootSS tcoLoop) ((map (Var rootSS . tcoVar) outerArgs) ++ (map (Var rootSS . copyVar) innerArgs))))])
103− , Return rootSS (Var rootSS tcoResult)
104− ]
105− where
106− rootSS = Nothing
107−
108− loopify :: AST -> AST
109− loopify (Return ss ret)
110− | isSelfCall ident ret =
111− let
112− allArgumentValues = concat $ collectArgs [] ret
113− in
114− Block ss $
115− zipWith (\val arg ->
116− Assignment ss (Var ss (tcoVar arg)) val) allArgumentValues outerArgs
117− ++ zipWith (\val arg ->
118− Assignment ss (Var ss (copyVar arg)) val) (drop (length outerArgs) allArgumentValues) innerArgs
119− ++ [ ReturnNoResult ss ]
120− | otherwise = Block ss [ markDone ss, Return ss ret ]
121− loopify (ReturnNoResult ss) = Block ss [ markDone ss, ReturnNoResult ss ]
122− loopify (While ss cond body) = While ss cond (loopify body)
123− loopify (For ss i js1 js2 body) = For ss i js1 js2 (loopify body)
124− loopify (ForIn ss i js1 body) = ForIn ss i js1 (loopify body)
125− loopify (IfElse ss cond body el) = IfElse ss cond (loopify body) (fmap loopify el)
126− loopify (Block ss body) = Block ss (map loopify body)
127− loopify other = other
128−
129− markDone :: Maybe SourceSpan -> AST
130− markDone ss = Assignment ss (Var ss tcoDone) (BooleanLiteral ss True)
131−
132− collectArgs :: [[AST]] -> AST -> [[AST]]
133− collectArgs acc (App _ fn args') = collectArgs (args' : acc) fn
134− collectArgs acc _ = acc
135−
136− isSelfCall :: Text -> AST -> Bool
137− isSelfCall ident (App _ (Var _ ident') _) = ident == ident'
138− isSelfCall ident (App _ (Indexer _ (Var _ ident') (Var _ mn')) _) = mn' == "" && ident == ident'
139− isSelfCall ident (App _ (Indexer _ (Var _ ident') mn') _) = mn' == mn && ident == ident'
140− isSelfCall ident (App _ fn _) = isSelfCall ident fn
141− isSelfCall _ _ = False
src/CodeGen/IL/Printer.hsdeleted−527
@@ -1,527 +0,0 @@
1−-- | Pretty printer for the AST
2−module CodeGen.IL.Printer
3− ( prettyPrintIL
4− , prettyPrintIL1
5− , prettyPrintILWithSourceMaps
6− , interfaceSource
7− , implHeaderSource
8− , implFooterSource
9− , isLiteral
10− , modLabel
11− , modPrefix
12− , ffiLoader
13− ) where
14−
15−import Prelude.Compat
16−
17−import Control.Arrow ((<+>))
18−import Control.Monad (forM, mzero)
19−import Control.Monad.State (StateT, evalStateT)
20−import Control.PatternArrows
21−import Data.List (filter, (\\))
22−import qualified Control.Arrow as A
23−
24−import Data.Maybe (fromMaybe)
25−import Data.Monoid ((<>))
26−import Data.Text (Text)
27−import qualified Data.Text as T
28−
29−import Language.PureScript.AST (SourceSpan(..))
30−import Language.PureScript.CoreImp.AST
31−import Language.PureScript.Comments
32−import Language.PureScript.Crash
33−import Language.PureScript.Names (Ident, runIdent)
34−import Language.PureScript.Pretty.Common
35−import Language.PureScript.PSString (PSString, decodeString, mkString)
36−
37−import CodeGen.IL.Common
38−import CodeGen.IL.Optimizer.TCO (tcoLoop)
39−
40−import qualified Language.PureScript.Constants as C
41−
42−-- TODO (Christoph): Get rid of T.unpack / pack
43−
44−literals :: (Emit gen) => Pattern PrinterState AST gen
45−literals = mkPattern' match'
46− where
47− match' :: (Emit gen) => AST -> StateT PrinterState Maybe gen
48− match' il = (addMapping' (getSourceSpan il) <>) <$> match il
49−
50− match :: (Emit gen) => AST -> StateT PrinterState Maybe gen
51− match (NumericLiteral _ n) = return $ emit $ T.pack $ either show show n
52− match (StringLiteral _ s) = return $ emit $ stringLiteral s
53− match (BooleanLiteral _ True) = return $ emit "true"
54− match (BooleanLiteral _ False) = return $ emit "false"
55− match (ArrayLiteral _ xs) = mconcat <$> sequence
56− [ return . emit $ arrayType <> "{"
57− , intercalate (emit ", ") <$> forM xs prettyPrintIL'
58− , return $ emit "}"
59− ]
60− -- match (ObjectLiteral _ []) = return $ emit "std::initializer_list<std::pair<const string, boxed>>{}"
61− match (ObjectLiteral _ ps) = mconcat <$> sequence
62− [ return . emit $ dictType <> "{"
63− , withIndent $ do
64− ils <- forM ps $ \(key, value) -> do
65− value' <- prettyPrintIL' value
66− return $ objectPropertyToString key <> value'
67− return $ intercalate (emit " ") $ map (<> emit ",") ils
68− , return $ emit "}"
69− ]
70− where
71− objectPropertyToString :: (Emit gen) => PSString -> gen
72− objectPropertyToString s = emit $ stringLiteral s <> ": "
73− match (Block _ sts) = mconcat <$> sequence
74− [ return $ emit "{\n"
75− , withIndent $ prettyStatements sts
76− , return $ emit "\n"
77− , currentIndent
78− , return $ emit "}"
79− ]
80− match (Var _ ident) | ident == C.undefined = return $ emit undefinedName
81− match (Var _ ident) = return $ emit ident
82− match (VariableIntroduction _ ident value) = mconcat <$> sequence
83− [ return . emit $ varDecl <> " " <> ident <> " " <> anyType
84− , maybe (return mempty) (fmap (emit " = " <>) . prettyPrintIL') value
85− ]
86− match (Assignment _ target value) = mconcat <$> sequence
87− [ prettyPrintIL' target
88− , return $ emit " = "
89− , prettyPrintIL' value
90− ]
91− match (App _ val []) = mconcat <$> sequence
92− [ return $ emit "Run("
93− , prettyPrintIL' val
94− , return $ emit ")"
95− ]
96− match (App _ (StringLiteral _ u) [arg])
97− | Just ty <- decodeString u = mconcat <$> sequence
98− [ prettyPrintIL' arg
99− , return $ emit ".("
100− , return $ emit ty
101− , return $ emit ")"
102− ]
103− match (App _ (Var _ fn) [arg]) | fn == arrayLengthFn = mconcat <$> sequence
104− [ return $ emit fn
105− , return $ emit "("
106− , prettyPrintIL' arg
107− , return $ emit ")"
108− ]
109− match (App _ (Var _ fn) args) | fn == indexFn = mconcat <$> sequence
110− [ return $ emit fn
111− , return $ emit "("
112− , intercalate (emit ", ") <$> forM args prettyPrintIL'
113− , return $ emit ")"
114− ]
115− match (App _ (Var _ fn) args) | fn == tcoLoop = mconcat <$> sequence
116− [ return $ emit fn
117− , return $ emit "("
118− , intercalate (emit ", ") <$> forM args prettyPrintIL'
119− , return $ emit ")"
120− ]
121− match (App _ (App Nothing (StringLiteral Nothing fnx) [fn@Function{}]) args)
122− | Just ftype <- decodeString fnx
123− , "Fn" `T.isPrefixOf` ftype = mconcat <$> sequence
124− [ prettyPrintIL' fn
125− , return $ emit "("
126− , intercalate (emit ", ") <$> forM args prettyPrintIL'
127− , return $ emit ")"
128− ]
129− match (App _ (App Nothing (StringLiteral Nothing fnx) [fn]) args)
130− | Just ftype <- decodeString fnx
131− , "Fn" `T.isPrefixOf` ftype = mconcat <$> sequence
132− [ prettyPrintIL' fn
133− , return $ emit ".("
134− , return $ emit ftype
135− , return $ emit ")("
136− , intercalate (emit ", ") <$> forM args prettyPrintIL'
137− , return $ emit ")"
138− ]
139− match app@(App{})
140− | (val, args) <- unApp app []
141− , length args > 1
142− = mconcat <$> sequence
143− [ return $ emit "Apply("
144− , prettyPrintIL' val
145− , return $ emit ", "
146− , intercalate (emit ", ") <$> forM args prettyPrintIL'
147− , return $ emit ")"
148− ]
149− match (App _ val args) = mconcat <$> sequence
150− [ return $ emit "Apply("
151− , prettyPrintIL' val
152− , return $ emit ", "
153− , intercalate (emit ", ") <$> forM args prettyPrintIL'
154− , return $ emit ")"
155− ]
156− match (Function _ (Just name) [] (Block _ [Return _ ret]))
157− | isComplexLiteral ret = mconcat <$> sequence
158− [ return $ emit "\n"
159− , return . emit $ varDecl <> " " <> initName name <> " Once\n"
160− , prettyPrintIL' $ VariableIntroduction Nothing (valueName name) Nothing
161− , return $ emit "\n\n"
162− , return $ emit "func "
163− , return . emit $ withPrefix name
164− , return . emit $ "() " <> anyType <> " "
165− , return $ emit "{\n"
166− , withIndent $ do
167− indentString <- currentIndent
168− return $ indentString <> (emit $ initName name <> ".Do(func() {\n") <>
169− indentString <> indentString <> (emit $ valueName name <> " = ")
170− , withIndent $ do
171− case ret of
172− Function _ Nothing args body -> mconcat <$> sequence
173− [ return $ emit "func("
174− , return . emit $ intercalate ", " (renderArg anyType <$> args)
175− , return . emit $ ") " <> anyType <> " "
176− , prettyPrintIL' body
177− ]
178− _ -> do
179− prettyPrintIL' ret
180− , return $ emit "\n"
181− , withIndent $ do
182− indentString <- currentIndent
183− return $ indentString <> (emit $ "})\n" <> "return " <> valueName name <>"\n")
184− , return $ emit "}\n\n"
185− ]
186− match (Function _ (Just name) [] ret) = mconcat <$> sequence
187− [ return $ emit "func "
188− , return . emit $ withPrefix name
189− , return . emit $ "() " <> anyType <> " "
190− , prettyPrintIL' ret
191− ]
192− match (Function _ _ args ret) = mconcat <$> sequence
193− [ return $ emit "func("
194− , return . emit $ intercalate ", " (renderArg anyType <$> args)
195− , return . emit $ ") " <> anyType <> " "
196− , prettyPrintIL' ret
197− ]
198− match (Indexer _ (Var _ name) (Var _ "")) = mconcat <$> sequence
199− [ prettyPrintIL' (Var Nothing $ withPrefix name)
200− , return $ emit "()"
201− ]
202− match (Indexer _ (Var _ name) val) = mconcat <$> sequence
203− [ prettyPrintIL' val
204− , return $ emit "."
205− , prettyPrintIL' (Var Nothing $ withPrefix name)
206− , return $ emit "()"
207− ]
208− match (Indexer _ prop@StringLiteral{} val@ObjectLiteral{}) = mconcat <$> sequence
209− [ prettyPrintIL' val
210− , return $ emit "["
211− , prettyPrintIL' prop
212− , return $ emit "]"
213− ]
214− match (Indexer _ prop@StringLiteral{} val) = mconcat <$> sequence
215− [ prettyPrintIL' val
216− , return $ emit ".("
217− , return $ emit dictType
218− , return $ emit ")["
219− , prettyPrintIL' prop
220− , return $ emit "]"
221− ]
222− match (Indexer _ index val@ArrayLiteral{}) = mconcat <$> sequence
223− [ prettyPrintIL' val
224− , return $ emit "["
225− , prettyPrintIL' index
226− , return $ emit "]"
227− ]
228− match (Indexer _ index val) = mconcat <$> sequence
229− [ prettyPrintIL' val
230− , return $ emit ".("
231− , return $ emit arrayType
232− , return $ emit ")["
233− , prettyPrintIL' index
234− , return $ emit "]"
235− ]
236− match (InstanceOf _ val ty) = mconcat <$> sequence
237− [ return $ emit "Is("
238− , prettyPrintIL' val
239− , return $ emit ", "
240− , prettyPrintIL' ty
241− , return $ emit ")"
242− ]
243− match (While _ cond sts) = mconcat <$> sequence
244− [ return $ emit "for "
245− , prettyPrintIL' cond
246− , return $ emit " "
247− , prettyPrintIL' sts
248− ]
249− match (For _ ident start end sts) = mconcat <$> sequence
250− [ return $ emit $ "for var " <> ident <> " = "
251− , prettyPrintIL' start
252− , return $ emit $ "; " <> ident <> " < "
253− , prettyPrintIL' end
254− , return $ emit $ "; " <> ident <> "++ "
255− , prettyPrintIL' sts
256− ]
257− match (ForIn _ ident obj sts) = mconcat <$> sequence
258− [ return $ emit $ "for var " <> ident <> " in "
259− , prettyPrintIL' obj
260− , return $ emit " "
261− , prettyPrintIL' sts
262− ]
263− match (IfElse _ (Binary _ EqualTo cond@Binary{} (BooleanLiteral Nothing True)) thens elses) = mconcat <$> sequence
264− [ return $ emit "if "
265− , prettyPrintIL' cond
266− , return $ emit " "
267− , prettyPrintIL' thens
268− , maybe (return mempty) (fmap (emit " else " <>) . prettyPrintIL') elses
269− ]
270− match (IfElse _ (Binary _ EqualTo cond@Binary{} (BooleanLiteral Nothing False)) thens elses) = mconcat <$> sequence
271− [ return $ emit "if !("
272− , prettyPrintIL' cond
273− , return $ emit ") "
274− , prettyPrintIL' thens
275− , maybe (return mempty) (fmap (emit " else " <>) . prettyPrintIL') elses
276− ]
277− match (IfElse _ cond thens elses) = mconcat <$> sequence
278− [ return $ emit "if "
279− , prettyPrintIL' cond
280− , return $ emit " "
281− , prettyPrintIL' thens
282− , maybe (return mempty) (fmap (emit " else " <>) . prettyPrintIL') elses
283− ]
284− match (Return _ value) = mconcat <$> sequence
285− [ return $ emit "return "
286− , prettyPrintIL' value
287− ]
288− match (ReturnNoResult _) = return . emit $ "return " <> undefinedName
289− -- match (Throw _ _) = return mempty
290− match (Throw _ value) = mconcat <$> sequence
291− [ return $ emit "panic("
292− , prettyPrintIL' value
293− , return $ emit ")"
294− ]
295− match (Comment _ com il) = mconcat <$> sequence
296− [ return $ emit "\n"
297− , mconcat <$> forM com comment
298− , prettyPrintIL' il
299− ]
300− match _ = mzero
301−
302− comment :: (Emit gen) => Comment -> StateT PrinterState Maybe gen
303− comment (LineComment com) = fmap mconcat $ sequence $
304− [ currentIndent
305− , return $ emit "//" <> emit com <> emit "\n"
306− ]
307− comment (BlockComment com) = fmap mconcat $ sequence $
308− [ currentIndent
309− , return $ emit "/**\n"
310− ] ++
311− map asLine (T.lines com) ++
312− [ currentIndent
313− , return $ emit " */\n"
314− , currentIndent
315− ]
316− where
317− asLine :: (Emit gen) => Text -> StateT PrinterState Maybe gen
318− asLine s = do
319− i <- currentIndent
320− return $ i <> emit " * " <> (emit . removeComments) s <> emit "\n"
321−
322− removeComments :: Text -> Text
323− removeComments t =
324− case T.stripPrefix "*/" t of
325− Just rest -> removeComments rest
326− Nothing -> case T.uncons t of
327− Just (x, xs) -> x `T.cons` removeComments xs
328− Nothing -> ""
329−
330−unary' :: (Emit gen) => UnaryOperator -> (AST -> Text) -> Operator PrinterState AST gen
331−unary' op mkStr = Wrap match (<>)
332− where
333− match :: (Emit gen) => Pattern PrinterState AST (gen, AST)
334− match = mkPattern match'
335− where
336− match' (Unary _ op' val) | op' == op = Just (emit $ mkStr val, val)
337− match' _ = Nothing
338−
339−unary :: (Emit gen) => UnaryOperator -> Text -> Operator PrinterState AST gen
340−unary op str = unary' op (const str)
341−
342−negateOperator :: (Emit gen) => Operator PrinterState AST gen
343−negateOperator = unary' Negate (\v -> if isNegate v then "- " else "-")
344− where
345− isNegate (Unary _ Negate _) = True
346− isNegate _ = False
347−
348−binary :: (Emit gen) => BinaryOperator -> Text -> Operator PrinterState AST gen
349−binary op str = AssocL match (\v1 v2 -> v1 <> emit (" " <> str <> " ") <> v2)
350− where
351− match :: Pattern PrinterState AST (AST, AST)
352− match = mkPattern match'
353− where
354− match' (Binary _ op' v1 v2) | op' == op = Just (v1, v2)
355− match' _ = Nothing
356−
357−isLiteral :: AST -> Bool
358−isLiteral Function{} = True
359−isLiteral NumericLiteral{} = True
360−isLiteral StringLiteral{} = True
361−isLiteral BooleanLiteral{} = True
362−isLiteral ObjectLiteral{} = True
363−isLiteral ArrayLiteral{} = True
364−isLiteral _ = False
365−
366−isComplexLiteral :: AST -> Bool
367−-- isComplexLiteral Function{} = True
368−isComplexLiteral ObjectLiteral{} = True
369−isComplexLiteral ArrayLiteral{} = True
370−isComplexLiteral _ = False
371−
372−unApp :: AST -> [AST] -> (AST, [AST])
373−unApp (App _ val [arg]) args = unApp val (arg : args)
374−unApp other args = (other, args)
375−
376−prettyStatements :: (Emit gen) => [AST] -> StateT PrinterState Maybe gen
377−prettyStatements sts = do
378− ils <- forM sts prettyPrintIL'
379− indentString <- currentIndent
380− return $ intercalate (emit "\n") $ map (indentString <>) ils
381−
382−-- | Generate a pretty-printed string representing a collection of C++ expressions at the same indentation level
383−prettyPrintILWithSourceMaps :: [AST] -> (Text, [SMap])
384−prettyPrintILWithSourceMaps il =
385− let StrPos (_, s, mp) = (fromMaybe (internalError "Incomplete pattern") . flip evalStateT (PrinterState 0) . prettyStatements) il
386− in (s, mp)
387−
388−prettyPrintIL :: [AST] -> Text
389−prettyPrintIL = maybe (internalError "Incomplete pattern") runPlainString . flip evalStateT (PrinterState 0) . prettyStatements
390−
391−-- | Generate an indented, pretty-printed string representing a C++ expression
392−prettyPrintIL' :: (Emit gen) => AST -> StateT PrinterState Maybe gen
393−prettyPrintIL' = A.runKleisli $ runPattern matchValue
394− where
395− matchValue :: (Emit gen) => Pattern PrinterState AST gen
396− matchValue = buildPrettyPrinter operators (literals <+> fmap parensPos matchValue)
397− operators :: (Emit gen) => OperatorTable PrinterState AST gen
398− operators =
399− OperatorTable [ [ unary New "?" ]
400− , [ unary Not "!"
401− , unary BitwiseNot "~"
402− , unary Positive "+"
403− , negateOperator ]
404− , [ binary Multiply "*"
405− , binary Divide "/"
406− , binary Modulus "%" ]
407− , [ binary Add "+"
408− , binary Subtract "-" ]
409− , [ binary ShiftLeft "<<"
410− , binary ShiftRight ">>" ]
411− , [ binary LessThan "<"
412− , binary LessThanOrEqualTo "<="
413− , binary GreaterThan ">"
414− , binary GreaterThanOrEqualTo ">=" ]
415− , [ binary EqualTo "=="
416− , binary NotEqualTo "!=" ]
417− , [ binary BitwiseAnd "&" ]
418− , [ binary BitwiseXor "^" ]
419− , [ binary BitwiseOr "|" ]
420− , [ binary And "&&" ]
421− , [ binary Or "||" ]
422− ]
423−
424−prettyPrintIL1 :: AST -> Text
425−prettyPrintIL1 = maybe (internalError "Incomplete pattern") runPlainString . flip evalStateT (PrinterState 0) . prettyPrintIL'
426−
427−stringLiteral :: PSString -> Text
428−stringLiteral pss | Just s <- decodeString pss = stringLiteral' s
429− where
430− stringLiteral' :: Text -> Text
431− stringLiteral' s = "\"" <> T.concatMap encodeChar s <> "\""
432− encodeChar :: Char -> Text
433− encodeChar '\0' = "\\x00"
434− encodeChar '\b' = "\\b"
435− encodeChar '\t' = "\\t"
436− encodeChar '\n' = "\\n"
437− encodeChar '\v' = "\\v"
438− encodeChar '\f' = "\\f"
439− encodeChar '\r' = "\\r"
440− encodeChar '"' = "\\\""
441− encodeChar '\\' = "\\\\"
442− encodeChar c = T.singleton $ c
443−stringLiteral _ = "\"\\uFFFD\""
444−
445−interfaceSource :: Text -> [(Text,Bool)] -> [Ident] -> Text
446−interfaceSource _ _ _ = ""
447−
448−implHeaderSource :: Text -> [Text] -> Text -> Text
449−implHeaderSource mn imports otherPrefix =
450− "// Code generated by psgo. DO NOT EDIT.\n\n" <>
451− "package " <> (if mn == "Main" || mn == "Test_Main" then "main" else mn) <> "\n\n" <>
452− "import . \"" <> runtime <> "\"\n" <>
453− (if mn == "Main" || mn == "Test_Main"
454− then "import _ \"" <> otherPrefix <> "/" <> ffiLoader <> "\"\n"
455− else "\n") <>
456− "import (\n" <>
457− (T.concat $ formatImport <$> imports) <> ")\n\n" <>
458− "type _ = Any\n\n"
459− where
460− formatImport :: Text -> Text
461− formatImport s =
462− "\t\"" <> otherPrefix <> "/" <> modPrefix <> "/" <> moduleRenamer s <> "\"\n"
463− moduleRenamer :: Text -> Text
464− moduleRenamer s
465− | (s', sfx) <- T.breakOn moduleRenamerMarker s,
466− not $ T.null sfx = s'
467− moduleRenamer s = s
468−
469−implFooterSource :: Text -> [Ident] -> Text
470−implFooterSource mn foreigns =
471− "\n\n\n" <>
472− (if null foreigns
473− then ""
474− else ("// Foreign values\n\n" <>
475− "var " <> foreignDict <> " = "<> foreignMod <> "(\"" <> mn <> "\")\n\n" <>
476− (T.concat $ (\foreign' ->
477− let name = moduleIdentToIL foreign' in
478− varDecl <> " " <> initName name <> " Once\n" <>
479− varDecl <> " " <> valueName name <> " " <> anyType <> "\n\n" <>
480− "func " <>
481− withPrefix name <>
482− "() Any { \n" <>
483− " " <> initName name <> ".Do(func() {\n" <>
484− " " <> valueName name <> " = " <>
485− "Get(" <> foreignDict <> ", " <>
486− (stringLiteral . mkString $ runIdent foreign') <> ")\n" <>
487− " })\n" <>
488− " return " <> valueName name <> "\n" <>
489− "}\n\n") <$> foreigns))) <>
490− if mn == "Main" || mn == "Test.Main" then mainSource else "\n"
491− where
492− mainSource :: Text
493− mainSource = "\
494− \func main() {\n\
495− \ Run(Ꞌmain())\n\
496− \}\n\n\
497− \"
498−
499−varDecl :: Text
500−varDecl = "var"
501−
502−renderArg :: Text -> Text -> Text
503−renderArg decl arg = arg <> " " <> decl
504−
505−foreignMod :: Text
506−foreignMod = "Foreign"
507−
508−foreignDict :: Text
509−foreignDict = "foreign"
510−
511−modLabel :: Text
512−modLabel = "purescript-native"
513−
514−modPrefix :: Text
515−modPrefix = modLabel <> "/output"
516−
517−runtime :: Text
518−runtime = "github.com/" <> modLabel <> "/go-runtime"
519−
520−ffiLoader :: Text
521−ffiLoader = modLabel <> "/ffi-loader"
522−
523−initName :: Text -> Text
524−initName s = "ᛌ" <> s <> "_once"
525−
526−valueName :: Text -> Text
527−valueName s = "ᛧ" <> s
src/Data/Generics/Extras.hsnew file+24
@@ -0,0 +1,24 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Data.Generics.Extras
4+-- Copyright : (c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+{-# LANGUAGE Rank2Types #-}
16+
17+module Data.Generics.Extras where
18+
19+import Data.Data
20+
21+everywhereM' :: (Monad m, Data d) => (forall d1. (Data d1) => d1 -> m d1) -> d -> m d
22+everywhereM' f x = do
23+ y <- f x
24+ gmapM (everywhereM' f) y
src/Language/PureScript.hsnew file+38
@@ -0,0 +1,38 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.PureScript
4+-- Copyright : (c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+module Language.PureScript (module P, compile) where
16+
17+import Language.PureScript.Values as P
18+import Language.PureScript.Types as P
19+import Language.PureScript.Kinds as P
20+import Language.PureScript.Declarations as P
21+import Language.PureScript.Names as P
22+import Language.PureScript.Parser as P
23+import Language.PureScript.CodeGen as P
24+import Language.PureScript.TypeChecker as P
25+import Language.PureScript.Pretty as P
26+import Language.PureScript.Optimize as P
27+import Language.PureScript.Operators as P
28+
29+import Data.List (intercalate)
30+import Data.Maybe (mapMaybe)
31+
32+compile :: [Declaration] -> Either String (String, String, Environment)
33+compile decls = do
34+ bracketted <- rebracket decls
35+ (_, env) <- runCheck (typeCheckAll bracketted)
36+ let js = prettyPrintJS . map optimize . concat . mapMaybe (\decl -> declToJs Nothing global decl env) $ bracketted
37+ let exts = intercalate "\n" . mapMaybe (externToPs 0 global env) $ bracketted
38+ return (js, exts, env)
src/Language/PureScript/CodeGen.hsnew file+18
@@ -0,0 +1,18 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.PureScript.CodeGen
4+-- Copyright : (c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+module Language.PureScript.CodeGen (module C) where
16+
17+import Language.PureScript.CodeGen.JS as C
18+import Language.PureScript.CodeGen.Externs as C
src/Language/PureScript/CodeGen/Externs.hsnew file+41
@@ -0,0 +1,41 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.PureScript.CodeGen.Externs
4+-- Copyright : (c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+module Language.PureScript.CodeGen.Externs (
16+ externToPs
17+) where
18+
19+import Data.Maybe (mapMaybe)
20+import qualified Data.Map as M
21+import Language.PureScript.Declarations
22+import Language.PureScript.TypeChecker.Monad
23+import Language.PureScript.Pretty
24+import Language.PureScript.Names
25+
26+externToPs :: Int -> ModulePath -> Environment -> Declaration -> Maybe String
27+externToPs indent path env (ValueDeclaration name _) = do
28+ (ty, _) <- M.lookup (path, name) $ names env
29+ return $ replicate indent ' ' ++ "foreign import " ++ show name ++ " :: " ++ prettyPrintType ty
30+externToPs indent path env (DataDeclaration name _ _) = do
31+ (kind, _) <- M.lookup (path, name) $ types env
32+ return $ replicate indent ' ' ++ "foreign import data " ++ show name ++ " :: " ++ prettyPrintKind kind
33+externToPs indent _ _ (ExternMemberDeclaration member name ty) =
34+ return $ replicate indent ' ' ++ "foreign import member " ++ show member ++ " " ++ show name ++ " :: " ++ prettyPrintType ty
35+externToPs indent _ _ (ExternDataDeclaration name kind) =
36+ return $ replicate indent ' ' ++ "foreign import data " ++ show name ++ " :: " ++ prettyPrintKind kind
37+externToPs indent _ _ (TypeSynonymDeclaration name args ty) =
38+ return $ replicate indent ' ' ++ "type " ++ show name ++ " " ++ unwords args ++ " = " ++ prettyPrintType ty
39+externToPs indent path env (ModuleDeclaration name decls) =
40+ return $ replicate indent ' ' ++ "module " ++ show name ++ " where\n" ++ unlines (mapMaybe (externToPs (indent + 2) (subModule path name) env) decls)
41+externToPs _ _ _ _ = Nothing
src/Language/PureScript/CodeGen/JS.hsnew file+173
@@ -0,0 +1,173 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.PureScript.CodeGen.JS
4+-- Copyright : (c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+module Language.PureScript.CodeGen.JS (
16+ module AST,
17+ declToJs
18+) where
19+
20+import Data.Maybe (mapMaybe)
21+import qualified Data.Map as M
22+import Control.Arrow (second)
23+import Control.Monad (forM)
24+
25+import Language.PureScript.TypeChecker (Environment, names)
26+import Language.PureScript.Values
27+import Language.PureScript.Names
28+import Language.PureScript.Declarations
29+import Language.PureScript.Pretty.Common
30+import Language.PureScript.CodeGen.Monad
31+import Language.PureScript.CodeGen.JS.AST as AST
32+import Language.PureScript.TypeChecker.Monad (NameKind(..))
33+
34+declToJs :: Maybe Ident -> ModulePath -> Declaration -> Environment -> Maybe [JS]
35+declToJs curMod mp (ValueDeclaration ident (Abs args ret)) e =
36+ Just $ JSFunction (Just ident) args (JSBlock [JSReturn (valueToJs mp e ret)]) :
37+ maybe [] (return . setProperty (identToJs ident) (JSVar ident)) curMod
38+declToJs curMod mp (ValueDeclaration ident val) e =
39+ Just $ JSVariableIntroduction ident (Just (valueToJs mp e val)) :
40+ maybe [] (return . setProperty (identToJs ident) (JSVar ident)) curMod
41+declToJs curMod _ (ExternMemberDeclaration member ident _) _ =
42+ Just $ JSFunction (Just ident) [Ident "value"] (JSBlock [JSReturn (JSAccessor member (JSVar (Ident "value")))]) :
43+ maybe [] (return . setProperty (show ident) (JSVar ident)) curMod
44+declToJs curMod mp (DataDeclaration _ _ ctors) _ =
45+ Just $ flip concatMap ctors $ \(pn@(ProperName ctor), maybeTy) ->
46+ let
47+ ctorJs =
48+ case maybeTy of
49+ Nothing -> JSVariableIntroduction (Ident ctor) (Just (JSObjectLiteral [ ("ctor", JSStringLiteral (show (Qualified mp pn))) ]))
50+ Just _ -> JSFunction (Just (Ident ctor)) [Ident "value"]
51+ (JSBlock [JSReturn
52+ (JSObjectLiteral [ ("ctor", JSStringLiteral (show (Qualified mp pn)))
53+ , ("value", JSVar (Ident "value")) ])])
54+ in ctorJs : maybe [] (return . setProperty ctor (JSVar (Ident ctor))) curMod
55+declToJs curMod mp (ModuleDeclaration pn@(ProperName name) decls) env =
56+ Just $ [ JSVariableIntroduction (Ident name) Nothing
57+ , JSApp (JSFunction Nothing [Ident name]
58+ (JSBlock (concat $ mapMaybe (\decl -> declToJs (Just (Ident name)) (subModule mp pn) decl env) decls)))
59+ [JSAssignment (JSAssignVariable (Ident name))
60+ (JSBinary Or (JSVar (Ident name)) (JSObjectLiteral []))]] ++
61+ maybe [] (return . setProperty name (JSVar (Ident name))) curMod
62+declToJs _ _ _ _ = Nothing
63+
64+setProperty :: String -> JS -> Ident -> JS
65+setProperty prop val curMod = JSAssignment (JSAssignProperty prop (JSAssignVariable curMod)) val
66+
67+valueToJs :: ModulePath -> Environment -> Value -> JS
68+valueToJs _ _ (NumericLiteral n) = JSNumericLiteral n
69+valueToJs _ _ (StringLiteral s) = JSStringLiteral s
70+valueToJs _ _ (BooleanLiteral b) = JSBooleanLiteral b
71+valueToJs m e (ArrayLiteral xs) = JSArrayLiteral (map (valueToJs m e) xs)
72+valueToJs m e (ObjectLiteral ps) = JSObjectLiteral (map (second (valueToJs m e)) ps)
73+valueToJs m e (ObjectUpdate o ps) = JSApp (JSAccessor "extend" (JSVar (Ident "Object"))) [ valueToJs m e o, JSObjectLiteral (map (second (valueToJs m e)) ps)]
74+valueToJs _ _ (Constructor name) = qualifiedToJS runProperName name
75+valueToJs m e (Block sts) = JSApp (JSFunction Nothing [] (JSBlock (map (statementToJs m e) sts))) []
76+valueToJs m e (Case value binders) = runGen (bindersToJs m e binders (valueToJs m e value))
77+valueToJs m e (IfThenElse cond th el) = JSConditional (valueToJs m e cond) (valueToJs m e th) (valueToJs m e el)
78+valueToJs m e (Accessor prop val) = JSAccessor prop (valueToJs m e val)
79+valueToJs m e (Indexer index val) = JSIndexer (valueToJs m e index) (valueToJs m e val)
80+valueToJs m e (App val args) = JSApp (valueToJs m e val) (map (valueToJs m e) args)
81+valueToJs m e (Abs args val) = JSFunction Nothing args (JSBlock [JSReturn (valueToJs m e val)])
82+valueToJs m e (Unary op val) = JSUnary op (valueToJs m e val)
83+valueToJs m e (Binary op v1 v2) = JSBinary op (valueToJs m e v1) (valueToJs m e v2)
84+valueToJs m e (Var ident) = case M.lookup (qualify m ident) (names e) of
85+ Just (_, Alias aliasModule aliasIdent) -> qualifiedToJS identToJs (Qualified aliasModule aliasIdent)
86+ _ -> qualifiedToJS identToJs ident
87+valueToJs m e (TypedValue val _) = valueToJs m e val
88+valueToJs _ _ _ = error "Invalid argument to valueToJs"
89+
90+qualifiedToJS :: (a -> String) -> Qualified a -> JS
91+qualifiedToJS f (Qualified (ModulePath parts) a) =
92+ delimited (f a : reverse (map show parts))
93+ where delimited [part] = JSVar (Ident (part))
94+ delimited (part:parts') = JSAccessor part (delimited parts')
95+ delimited _ = error "Invalid argument to delimited"
96+
97+bindersToJs :: ModulePath -> Environment -> [(Binder, Value)] -> JS -> Gen JS
98+bindersToJs m e binders val = do
99+ valName <- fresh
100+ jss <- forM binders $ \(binder, result) -> binderToJs m e valName [JSReturn (valueToJs m e result)] binder
101+ return $ JSApp (JSFunction Nothing [Ident valName] (JSBlock (concat jss ++ [JSThrow (JSStringLiteral "Failed pattern match")])))
102+ [val]
103+
104+binderToJs :: ModulePath -> Environment -> String -> [JS] -> Binder -> Gen [JS]
105+binderToJs _ _ _ done NullBinder = return done
106+binderToJs _ _ varName done (StringBinder str) =
107+ return [JSIfElse (JSBinary EqualTo (JSVar (Ident varName)) (JSStringLiteral str)) (JSBlock done) Nothing]
108+binderToJs _ _ varName done (NumberBinder num) =
109+ return [JSIfElse (JSBinary EqualTo (JSVar (Ident varName)) (JSNumericLiteral num)) (JSBlock done) Nothing]
110+binderToJs _ _ varName done (BooleanBinder True) =
111+ return [JSIfElse (JSVar (Ident varName)) (JSBlock done) Nothing]
112+binderToJs _ _ varName done (BooleanBinder False) =
113+ return [JSIfElse (JSUnary Not (JSVar (Ident varName))) (JSBlock done) Nothing]
114+binderToJs _ _ varName done (VarBinder ident) =
115+ return (JSVariableIntroduction ident (Just (JSVar (Ident varName))) : done)
116+binderToJs m _ varName done (NullaryBinder ctor) =
117+ return [JSIfElse (JSBinary EqualTo (JSAccessor "ctor" (JSVar (Ident varName))) (JSStringLiteral (show (uncurry Qualified $ qualify m ctor)))) (JSBlock done) Nothing]
118+binderToJs m e varName done (UnaryBinder ctor b) = do
119+ value <- fresh
120+ js <- binderToJs m e value done b
121+ return [JSIfElse (JSBinary EqualTo (JSAccessor "ctor" (JSVar (Ident varName))) (JSStringLiteral (show (uncurry Qualified $ qualify m ctor)))) (JSBlock (JSVariableIntroduction (Ident value) (Just (JSAccessor "value" (JSVar (Ident varName)))) : js)) Nothing]
122+binderToJs m e varName done (ObjectBinder bs) = go done bs
123+ where
124+ go :: [JS] -> [(String, Binder)] -> Gen [JS]
125+ go done' [] = return done'
126+ go done' ((prop, binder):bs') = do
127+ propVar <- fresh
128+ done'' <- go done' bs'
129+ js <- binderToJs m e propVar done'' binder
130+ return (JSVariableIntroduction (Ident propVar) (Just (JSAccessor prop (JSVar (Ident varName)))) : js)
131+binderToJs m e varName done (ArrayBinder bs) = do
132+ js <- go done 0 bs
133+ return [JSIfElse (JSBinary EqualTo (JSAccessor "length" (JSVar (Ident varName))) (JSNumericLiteral (Left (fromIntegral $ length bs)))) (JSBlock js) Nothing]
134+ where
135+ go :: [JS] -> Integer -> [Binder] -> Gen [JS]
136+ go done' _ [] = return done'
137+ go done' index (binder:bs') = do
138+ elVar <- fresh
139+ done'' <- go done' (index + 1) bs'
140+ js <- binderToJs m e elVar done'' binder
141+ return (JSVariableIntroduction (Ident elVar) (Just (JSIndexer (JSNumericLiteral (Left index)) (JSVar (Ident varName)))) : js)
142+binderToJs m e varName done (ConsBinder headBinder tailBinder) = do
143+ headVar <- fresh
144+ tailVar <- fresh
145+ js1 <- binderToJs m e headVar done headBinder
146+ js2 <- binderToJs m e tailVar js1 tailBinder
147+ return [JSIfElse (JSBinary GreaterThan (JSAccessor "length" (JSVar (Ident varName))) (JSNumericLiteral (Left 0))) (JSBlock
148+ ( JSVariableIntroduction (Ident headVar) (Just (JSIndexer (JSNumericLiteral (Left 0)) (JSVar (Ident varName)))) :
149+ JSVariableIntroduction (Ident tailVar) (Just (JSApp (JSAccessor "slice" (JSVar (Ident varName))) [JSNumericLiteral (Left 1)])) :
150+ js2
151+ )) Nothing]
152+binderToJs m e varName done (NamedBinder ident binder) = do
153+ js <- binderToJs m e varName done binder
154+ return (JSVariableIntroduction ident (Just (JSVar (Ident varName))) : js)
155+binderToJs m e varName done (GuardedBinder cond binder) = binderToJs m e varName done' binder
156+ where
157+ done' = [JSIfElse (valueToJs m e cond) (JSBlock done) Nothing]
158+
159+statementToJs :: ModulePath -> Environment -> Statement -> JS
160+statementToJs m e (VariableIntroduction ident value) = JSVariableIntroduction ident (Just (valueToJs m e value))
161+statementToJs m e (Assignment target value) = JSAssignment (JSAssignVariable target) (valueToJs m e value)
162+statementToJs m e (While cond sts) = JSWhile (valueToJs m e cond) (JSBlock (map (statementToJs m e) sts))
163+statementToJs m e (For ident start end sts) = JSFor ident (valueToJs m e start) (valueToJs m e end) (JSBlock (map (statementToJs m e) sts))
164+statementToJs m e (ForEach ident arr sts) = JSApp (JSAccessor "forEach" (valueToJs m e arr)) [JSFunction Nothing [ident] (JSBlock (map (statementToJs m e) sts))]
165+statementToJs m e (If ifst) = ifToJs ifst
166+ where
167+ ifToJs :: IfStatement -> JS
168+ ifToJs (IfStatement cond thens elses) = JSIfElse (valueToJs m e cond) (JSBlock (map (statementToJs m e) thens)) (fmap elseToJs elses)
169+ elseToJs :: ElseStatement -> JS
170+ elseToJs (Else sts) = JSBlock (map (statementToJs m e) sts)
171+ elseToJs (ElseIf elif) = ifToJs elif
172+statementToJs m e (ValueStatement val) = valueToJs m e val
173+statementToJs m e (Return value) = JSReturn (valueToJs m e value)
src/Language/PureScript/CodeGen/JS/AST.hsnew file+49
@@ -0,0 +1,49 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.PureScript.CodeGen.JS.AST
4+-- Copyright : (c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+{-# LANGUAGE DeriveDataTypeable #-}
16+
17+module Language.PureScript.CodeGen.JS.AST where
18+
19+import Language.PureScript.Names
20+import Language.PureScript.Values
21+
22+import Data.Data
23+
24+data JS
25+ = JSNumericLiteral (Either Integer Double)
26+ | JSStringLiteral String
27+ | JSBooleanLiteral Bool
28+ | JSUnary UnaryOperator JS
29+ | JSBinary BinaryOperator JS JS
30+ | JSArrayLiteral [JS]
31+ | JSIndexer JS JS
32+ | JSObjectLiteral [(String, JS)]
33+ | JSAccessor String JS
34+ | JSFunction (Maybe Ident) [Ident] JS
35+ | JSApp JS [JS]
36+ | JSVar Ident
37+ | JSConditional JS JS JS
38+ | JSBlock [JS]
39+ | JSVariableIntroduction Ident (Maybe JS)
40+ | JSAssignment JSAssignment JS
41+ | JSWhile JS JS
42+ | JSFor Ident JS JS JS
43+ | JSIfElse JS JS (Maybe JS)
44+ | JSReturn JS
45+ | JSThrow JS deriving (Show, Data, Typeable)
46+
47+data JSAssignment
48+ = JSAssignVariable Ident
49+ | JSAssignProperty String JSAssignment deriving (Show, Data, Typeable)
src/Language/PureScript/CodeGen/Monad.hsnew file+31
@@ -0,0 +1,31 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.PureScript.CodeGen.Monad
4+-- Copyright : (c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
16+
17+module Language.PureScript.CodeGen.Monad where
18+
19+import Control.Monad.State
20+import Control.Applicative
21+
22+newtype Gen a = Gen { unGen :: State Int a } deriving (Functor, Applicative, Monad, MonadState Int)
23+
24+runGen :: Gen a -> a
25+runGen = flip evalState 0 . unGen
26+
27+fresh :: Gen String
28+fresh = do
29+ n <- get
30+ modify (+ 1)
31+ return $ '_' : show n
src/Language/PureScript/Declarations.hsnew file+43
@@ -0,0 +1,43 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.PureScript.Declarations
4+-- Copyright : (c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+{-# LANGUAGE DeriveDataTypeable #-}
16+
17+module Language.PureScript.Declarations where
18+
19+import Language.PureScript.Values
20+import Language.PureScript.Types
21+import Language.PureScript.Names
22+import Language.PureScript.Kinds
23+
24+import qualified Data.Data as D
25+
26+type Precedence = Integer
27+
28+data Associativity = Infixl | Infixr deriving (Show, D.Data, D.Typeable)
29+
30+data Fixity = Fixity Associativity Precedence deriving (Show, D.Data, D.Typeable)
31+
32+data Declaration
33+ = DataDeclaration ProperName [String] [(ProperName, Maybe PolyType)]
34+ | TypeSynonymDeclaration ProperName [String] PolyType
35+ | TypeDeclaration Ident PolyType
36+ | ValueDeclaration Ident Value
37+ | ExternDeclaration Ident PolyType
38+ | ExternMemberDeclaration String Ident PolyType
39+ | ExternDataDeclaration ProperName Kind
40+ | FixityDeclaration Fixity String
41+ | ModuleDeclaration ProperName [Declaration]
42+ | ImportDeclaration ModulePath (Maybe [Ident])
43+ deriving (Show, D.Data, D.Typeable)
src/Language/PureScript/Kinds.hsnew file+26
@@ -0,0 +1,26 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.PureScript.Kinds
4+-- Copyright : (c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+{-# LANGUAGE DeriveDataTypeable #-}
16+
17+module Language.PureScript.Kinds where
18+
19+import Data.Data
20+import Language.PureScript.Unknown
21+
22+data Kind
23+ = KUnknown (Unknown Kind)
24+ | Star
25+ | Row
26+ | FunKind Kind Kind deriving (Show, Eq, Data, Typeable)
src/Language/PureScript/Names.hsnew file+55
@@ -0,0 +1,55 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.PureScript.Names
4+-- Copyright : (c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+{-# LANGUAGE DeriveDataTypeable #-}
16+
17+module Language.PureScript.Names where
18+
19+import Data.Data
20+import Data.List (inits, intercalate)
21+
22+data Ident = Ident String | Op String deriving (Eq, Ord, Data, Typeable)
23+
24+instance Show Ident where
25+ show (Ident s) = s
26+ show (Op op) = '(':op ++ ")"
27+
28+newtype ProperName = ProperName { runProperName :: String } deriving (Eq, Ord, Data, Typeable)
29+
30+instance Show ProperName where
31+ show = runProperName
32+
33+data ModulePath = ModulePath [ProperName] deriving (Eq, Ord, Data, Typeable)
34+
35+instance Show ModulePath where
36+ show (ModulePath segments) = intercalate "." $ map show segments
37+
38+subModule :: ModulePath -> ProperName -> ModulePath
39+subModule (ModulePath mp) name = ModulePath (mp ++ [name])
40+
41+global :: ModulePath
42+global = ModulePath []
43+
44+data Qualified a = Qualified ModulePath a deriving (Eq, Ord, Data, Typeable)
45+
46+instance (Show a) => Show (Qualified a) where
47+ show (Qualified (ModulePath names) a) = intercalate "." (map show names ++ [show a])
48+
49+qualify :: ModulePath -> Qualified a -> (ModulePath, a)
50+qualify mp (Qualified (ModulePath []) a) = (mp, a)
51+qualify _ (Qualified mp a) = (mp, a)
52+
53+nameResolution :: ModulePath -> Qualified a -> [(ModulePath, a)]
54+nameResolution (ModulePath mp) (Qualified (ModulePath []) a) = [ (ModulePath mp', a) | mp' <- reverse $ inits mp ]
55+nameResolution _ (Qualified mp a) = [(mp, a)]
src/Language/PureScript/Operators.hsnew file+128
@@ -0,0 +1,128 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.PureScript.Operators
4+-- Copyright : (c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+{-# LANGUAGE Rank2Types #-}
16+
17+module Language.PureScript.Operators (
18+ rebracket
19+) where
20+
21+import Language.PureScript.Names
22+import Language.PureScript.Declarations
23+import Language.PureScript.Values
24+
25+import Data.Function (on)
26+import Data.List (groupBy, sortBy)
27+import qualified Data.Map as M
28+import qualified Data.Generics as G
29+import qualified Data.Generics.Extras as G
30+import Control.Monad.State
31+import Control.Applicative
32+import qualified Text.Parsec as P
33+import qualified Text.Parsec.Pos as P
34+import qualified Text.Parsec.Expr as P
35+
36+rebracket :: [Declaration] -> Either String [Declaration]
37+rebracket ds = do
38+ m <- collectFixities ds
39+ let opTable = customOperatorTable m
40+ ds' <- G.everywhereM' (G.mkM (matchOperators opTable)) ds
41+ return $ G.everywhere (G.mkT removeParens) ds'
42+
43+removeParens :: Value -> Value
44+removeParens (Parens val) = val
45+removeParens val = val
46+
47+customOperatorTable :: M.Map (Qualified Ident) Fixity -> [[(Qualified Ident, Value -> Value -> Value, Associativity)]]
48+customOperatorTable fixities =
49+ let
50+ applyUserOp name t1 t2 = App (App (Var name) [t1]) [t2]
51+ userOps = map (\(name, Fixity a p) -> (name, applyUserOp name, p, a)) . M.toList $ fixities
52+ sorted = sortBy (compare `on` (\(_, _, p, _) -> p)) (userOps ++ builtIns)
53+ groups = groupBy ((==) `on` (\(_, _, p, _) -> p)) sorted
54+ in
55+ map (map (\(name, f, _, a) -> (name, f, a))) groups
56+
57+type Chain = [Either Value (Qualified Ident)]
58+
59+matchOperators :: [[(Qualified Ident, Value -> Value -> Value, Associativity)]] -> Value -> Either String Value
60+matchOperators ops val = G.everywhereM' (G.mkM parseChains) val
61+ where
62+ parseChains :: Value -> Either String Value
63+ parseChains b@(BinaryNoParens _ _ _) = bracketChain (extendChain b)
64+ parseChains other = return other
65+ extendChain :: Value -> Chain
66+ extendChain (BinaryNoParens name l r) = Left l : Right name : extendChain r
67+ extendChain other = [Left other]
68+ bracketChain :: Chain -> Either String Value
69+ bracketChain = either (Left . show) Right . P.parse (P.buildExpressionParser opTable parseValue <* P.eof) "operator expression"
70+ opTable = map (map (\(name, f, a) -> P.Infix (P.try (matchOp name) >> return f) (toAssoc a))) ops
71+ ++ [[P.Infix (P.try (parseOp >>= \ident -> return (\t1 t2 -> App (App (Var ident) [t1]) [t2]))) P.AssocLeft]]
72+
73+toAssoc :: Associativity -> P.Assoc
74+toAssoc Infixl = P.AssocLeft
75+toAssoc Infixr = P.AssocRight
76+
77+parseValue :: P.Parsec Chain () Value
78+parseValue = P.token show (const (P.initialPos "")) (either Just (const Nothing)) P.<?> "expression"
79+
80+parseOp :: P.Parsec Chain () (Qualified Ident)
81+parseOp = P.token show (const (P.initialPos "")) (either (const Nothing) Just) P.<?> "operator"
82+
83+matchOp :: Qualified Ident -> P.Parsec Chain () ()
84+matchOp op = do
85+ ident <- parseOp
86+ guard (ident == op)
87+
88+collectFixities :: [Declaration] -> Either String (M.Map (Qualified Ident) Fixity)
89+collectFixities = go M.empty global
90+ where
91+ go :: M.Map (Qualified Ident) Fixity -> ModulePath -> [Declaration] -> Either String (M.Map (Qualified Ident) Fixity)
92+ go m _ [] = return m
93+ go m p (FixityDeclaration fixity name : rest) = do
94+ let qual = Qualified p (Op name)
95+ when (qual `M.member` m) (Left $ "redefined fixity for " ++ show name)
96+ go (M.insert qual fixity m) p rest
97+ go m p (ModuleDeclaration name decls : rest) = do
98+ m' <- go m (subModule p name) decls
99+ go m' p rest
100+ go m p (_:ds) = go m p ds
101+
102+globalOp :: String -> Qualified Ident
103+globalOp = Qualified global . Op
104+
105+builtIns :: [(Qualified Ident, Value -> Value -> Value, Precedence, Associativity)]
106+builtIns = [ (globalOp "<", Binary LessThan, 3, Infixl)
107+ , (globalOp "<=", Binary LessThanOrEqualTo, 3, Infixl)
108+ , (globalOp ">", Binary GreaterThan, 3, Infixl)
109+ , (globalOp ">=", Binary GreaterThanOrEqualTo, 3, Infixl)
110+ , (globalOp "!!", flip Indexer, 4, Infixl)
111+ , (globalOp "*", Binary Multiply, 5, Infixl)
112+ , (globalOp "/", Binary Divide, 5, Infixl)
113+ , (globalOp "%", Binary Modulus, 5, Infixl)
114+ , (globalOp "++", Binary Concat, 6, Infixr)
115+ , (globalOp "+", Binary Add, 7, Infixl)
116+ , (globalOp "-", Binary Subtract, 7, Infixl)
117+ , (globalOp "<<", Binary ShiftLeft, 8, Infixl)
118+ , (globalOp ">>", Binary ShiftRight, 8, Infixl)
119+ , (globalOp ">>>", Binary ZeroFillShiftRight, 8, Infixl)
120+ , (globalOp "==", Binary EqualTo, 9, Infixl)
121+ , (globalOp "!=", Binary NotEqualTo, 9, Infixl)
122+ , (globalOp "&", Binary BitwiseAnd, 10, Infixl)
123+ , (globalOp "^", Binary BitwiseXor, 10, Infixl)
124+ , (globalOp "|", Binary BitwiseOr, 10, Infixl)
125+ , (globalOp "&&", Binary And, 11, Infixr)
126+ , (globalOp "||", Binary Or, 11, Infixr)
127+ ]
128+
src/Language/PureScript/Optimize.hsnew file+95
@@ -0,0 +1,95 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.PureScript.Optimize
4+-- Copyright : (c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+module Language.PureScript.Optimize (
16+ optimize
17+) where
18+
19+import Data.Data
20+import Data.Generics
21+
22+import Language.PureScript.Names
23+import Language.PureScript.CodeGen.JS.AST
24+
25+optimize :: JS -> JS
26+optimize = removeUnusedVariables . unThunk . etaConvert . inlineVariables
27+
28+replaceIdent :: (Data d) => Ident -> JS -> d -> d
29+replaceIdent var1 js = everywhere (mkT replace)
30+ where
31+ replace (JSVar var2) | var1 == var2 = js
32+ replace other = other
33+
34+isReassigned :: (Data d) => Ident -> d -> Bool
35+isReassigned var1 = everything (||) (mkQ False check)
36+ where
37+ check :: JS -> Bool
38+ check (JSAssignment (JSAssignVariable var2) _) | var1 == var2 = True
39+ check _ = False
40+
41+isUsed :: (Data d) => Ident -> d -> Bool
42+isUsed var1 = everything (||) (mkQ False check)
43+ where
44+ check :: JS -> Bool
45+ check (JSVar var2) | var1 == var2 = True
46+ check (JSAssignment target _) | var1 == targetVariable target = True
47+ check _ = False
48+ targetVariable :: JSAssignment -> Ident
49+ targetVariable (JSAssignVariable var) = var
50+ targetVariable (JSAssignProperty _ tgt) = targetVariable tgt
51+
52+shouldInline :: JS -> Bool
53+shouldInline (JSVar _) = True
54+shouldInline (JSNumericLiteral _) = True
55+shouldInline (JSStringLiteral _) = True
56+shouldInline (JSBooleanLiteral _) = True
57+shouldInline (JSAccessor _ val) = shouldInline val
58+shouldInline (JSIndexer index val) = shouldInline index && shouldInline val
59+shouldInline _ = False
60+
61+inlineVariables :: JS -> JS
62+inlineVariables = everywhere (mkT removeFromBlock)
63+ where
64+ removeFromBlock :: JS -> JS
65+ removeFromBlock (JSBlock sts) = JSBlock (go sts)
66+ removeFromBlock js = js
67+ go :: [JS] -> [JS]
68+ go [] = []
69+ go (JSVariableIntroduction var (Just js) : sts) | shouldInline js && not (isReassigned var sts) = go (replaceIdent var js sts)
70+ go (s:sts) = s : go sts
71+
72+removeUnusedVariables :: JS -> JS
73+removeUnusedVariables = everywhere (mkT removeFromBlock)
74+ where
75+ removeFromBlock :: JS -> JS
76+ removeFromBlock (JSBlock sts) = JSBlock (go sts)
77+ removeFromBlock js = js
78+ go :: [JS] -> [JS]
79+ go [] = []
80+ go (JSVariableIntroduction var _ : sts) | not (isUsed var sts) = go sts
81+ go (s:sts) = s : go sts
82+
83+etaConvert :: JS -> JS
84+etaConvert = everywhere (mkT convert)
85+ where
86+ convert :: JS -> JS
87+ convert (JSBlock [JSReturn (JSApp (JSFunction Nothing [ident] (JSBlock body)) [arg])]) | shouldInline arg = JSBlock (replaceIdent ident arg body)
88+ convert js = js
89+
90+unThunk :: JS -> JS
91+unThunk = everywhere (mkT convert)
92+ where
93+ convert :: JS -> JS
94+ convert (JSBlock [JSReturn (JSApp (JSFunction Nothing [] (JSBlock body)) [])]) = JSBlock body
95+ convert js = js
src/Language/PureScript/Parser.hsnew file+22
@@ -0,0 +1,22 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.PureScript.Parser
4+-- Copyright : (c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+module Language.PureScript.Parser (module P) where
16+
17+import Language.PureScript.Parser.Common as P
18+import Language.PureScript.Parser.Types as P
19+import Language.PureScript.Parser.Values as P
20+import Language.PureScript.Parser.State as P
21+import Language.PureScript.Parser.Kinds as P
22+import Language.PureScript.Parser.Declarations as P
src/Language/PureScript/Parser/Common.hsnew file+253
@@ -0,0 +1,253 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.PureScript.Parser.Common
4+-- Copyright : (c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+{-# LANGUAGE FlexibleContexts #-}
16+
17+module Language.PureScript.Parser.Common where
18+
19+import Data.Functor.Identity
20+import Control.Applicative
21+import Control.Monad
22+import Language.PureScript.Parser.State
23+import qualified Text.Parsec as P
24+import qualified Text.Parsec.Token as PT
25+
26+import Language.PureScript.Names
27+
28+reservedNames :: [String]
29+reservedNames = [ "case"
30+ , "of"
31+ , "data"
32+ , "type"
33+ , "var"
34+ , "val"
35+ , "while"
36+ , "for"
37+ , "foreach"
38+ , "if"
39+ , "then"
40+ , "else"
41+ , "return"
42+ , "true"
43+ , "false"
44+ , "foreign"
45+ , "import"
46+ , "member"
47+ , "forall"
48+ , "do"
49+ , "until"
50+ , "in"
51+ , "break"
52+ , "catch"
53+ , "continue"
54+ , "debugger"
55+ , "default"
56+ , "delete"
57+ , "finally"
58+ , "function"
59+ , "instanceof"
60+ , "new"
61+ , "switch"
62+ , "this"
63+ , "throw"
64+ , "try"
65+ , "typeof"
66+ , "void"
67+ , "with"
68+ , "Number"
69+ , "String"
70+ , "Boolean"
71+ , "infixl"
72+ , "infixr"
73+ , "module" ]
74+
75+builtInOperators :: [String]
76+builtInOperators = [ "~", "-", "<=", ">=", "<", ">", "*", "/", "%", "++", "+", "<<", ">>>", ">>"
77+ , "==", "!=", "&&", "||", "&", "^", "|", "!!", "!", "." ]
78+
79+reservedOpNames :: [String]
80+reservedOpNames = builtInOperators ++ [ "->" ]
81+
82+identStart :: P.Parsec String u Char
83+identStart = P.lower <|> P.oneOf "_$"
84+
85+properNameStart :: P.Parsec String u Char
86+properNameStart = P.upper
87+
88+identLetter :: P.Parsec String u Char
89+identLetter = P.alphaNum <|> P.oneOf "_'"
90+
91+opStart :: P.Parsec String u Char
92+opStart = P.oneOf ":.!#%&*+/<=>?@^|~"
93+
94+opLetter :: P.Parsec String u Char
95+opLetter = P.oneOf ":.#$%&*+./<=>?@^|"
96+
97+langDef :: PT.GenLanguageDef String u Identity
98+langDef = PT.LanguageDef
99+ { PT.reservedNames = reservedNames
100+ , PT.reservedOpNames = reservedOpNames
101+ , PT.commentStart = "{-"
102+ , PT.commentEnd = "-}"
103+ , PT.commentLine = "--"
104+ , PT.nestedComments = True
105+ , PT.identStart = identStart
106+ , PT.identLetter = identLetter
107+ , PT.opStart = opStart
108+ , PT.opLetter = opLetter
109+ , PT.caseSensitive = True
110+ }
111+
112+tokenParser :: PT.GenTokenParser String u Identity
113+tokenParser = PT.makeTokenParser langDef
114+
115+lexeme :: P.Parsec String u a -> P.Parsec String u a
116+lexeme = PT.lexeme tokenParser
117+
118+identifier :: P.Parsec String u String
119+identifier = PT.identifier tokenParser
120+
121+reserved :: String -> P.Parsec String u ()
122+reserved = PT.reserved tokenParser
123+
124+reservedOp :: String -> P.Parsec String u ()
125+reservedOp = PT.reservedOp tokenParser
126+
127+operator :: P.Parsec String u String
128+operator = PT.operator tokenParser
129+
130+stringLiteral :: P.Parsec String u String
131+stringLiteral = PT.stringLiteral tokenParser
132+
133+whiteSpace :: P.Parsec String u ()
134+whiteSpace = PT.whiteSpace tokenParser
135+
136+semi :: P.Parsec String u String
137+semi = PT.semi tokenParser
138+
139+colon :: P.Parsec String u String
140+colon = PT.colon tokenParser
141+
142+dot :: P.Parsec String u String
143+dot = PT.dot tokenParser
144+
145+comma :: P.Parsec String u String
146+comma = PT.comma tokenParser
147+
148+tick :: P.Parsec String u Char
149+tick = lexeme $ P.char '`'
150+
151+pipe :: P.Parsec String u Char
152+pipe = lexeme $ P.char '|'
153+
154+natural :: P.Parsec String u Integer
155+natural = PT.natural tokenParser
156+
157+squares :: P.Parsec String ParseState a -> P.Parsec String ParseState a
158+squares = P.between (lexeme $ P.char '[') (lexeme $ indented *> P.char ']') . (indented *>)
159+
160+parens :: P.Parsec String ParseState a -> P.Parsec String ParseState a
161+parens = P.between (lexeme $ P.char '(') (lexeme $ indented *> P.char ')') . (indented *>)
162+
163+braces :: P.Parsec String ParseState a -> P.Parsec String ParseState a
164+braces = P.between (lexeme $ P.char '{') (lexeme $ indented *> P.char '}') . (indented *>)
165+
166+angles :: P.Parsec String ParseState a -> P.Parsec String ParseState a
167+angles = P.between (lexeme $ P.char '<') (lexeme $ indented *> P.char '>') . (indented *>)
168+
169+sepBy :: P.Parsec String ParseState a -> P.Parsec String ParseState sep -> P.Parsec String ParseState [a]
170+sepBy p s = P.sepBy (indented *> p) (indented *> s)
171+
172+sepBy1 :: P.Parsec String ParseState a -> P.Parsec String ParseState sep -> P.Parsec String ParseState [a]
173+sepBy1 p s = P.sepBy1 (indented *> p) (indented *> s)
174+
175+semiSep :: P.Parsec String ParseState a -> P.Parsec String ParseState [a]
176+semiSep = flip sepBy semi
177+
178+semiSep1 :: P.Parsec String ParseState a -> P.Parsec String ParseState [a]
179+semiSep1 = flip sepBy1 semi
180+
181+commaSep :: P.Parsec String ParseState a -> P.Parsec String ParseState [a]
182+commaSep = flip sepBy comma
183+
184+commaSep1 :: P.Parsec String ParseState a -> P.Parsec String ParseState [a]
185+commaSep1 = flip sepBy1 comma
186+
187+properName :: P.Parsec String u ProperName
188+properName = lexeme $ ProperName <$> P.try ((:) <$> P.upper <*> many (PT.identLetter langDef) P.<?> "name")
189+
190+parseQualified :: P.Parsec String ParseState a -> P.Parsec String ParseState (Qualified a)
191+parseQualified parser = part global
192+ where
193+ part path = (do name <- P.try (properName <* delimiter)
194+ part (subModule path name))
195+ <|> (Qualified path <$> P.try parser)
196+ delimiter = indented *> dot
197+
198+integerOrFloat :: P.Parsec String u (Either Integer Double)
199+integerOrFloat = (Left <$> P.try (PT.natural tokenParser) <|>
200+ Right <$> P.try (PT.float tokenParser)) P.<?> "number"
201+
202+augment :: P.Stream s m t => P.ParsecT s u m a -> P.ParsecT s u m b -> (a -> b -> a) -> P.ParsecT s u m a
203+augment p q f = flip (maybe id $ flip f) <$> p <*> P.optionMaybe q
204+
205+fold :: P.Stream s m t => P.ParsecT s u m a -> P.ParsecT s u m b -> (a -> b -> a) -> P.ParsecT s u m a
206+fold first more combine = do
207+ a <- first
208+ bs <- P.many more
209+ return $ foldl combine a bs
210+
211+buildPostfixParser :: P.Stream s m t => [a -> P.ParsecT s u m a] -> P.ParsecT s u m a -> P.ParsecT s u m a
212+buildPostfixParser fs first = do
213+ a <- first
214+ go a
215+ where
216+ go a = do
217+ maybeA <- P.optionMaybe $ P.choice (map ($ a) fs)
218+ case maybeA of
219+ Nothing -> return a
220+ Just a' -> go a'
221+
222+operatorOrBuiltIn :: P.Parsec String u String
223+operatorOrBuiltIn = P.try operator <|> P.choice (map (\s -> P.try (reservedOp s) >> return s) builtInOperators)
224+
225+parseIdent :: P.Parsec String ParseState Ident
226+parseIdent = (Ident <$> identifier) <|> (Op <$> parens operatorOrBuiltIn)
227+
228+parseIdentInfix :: P.Parsec String ParseState (Qualified Ident)
229+parseIdentInfix = (P.between tick tick (parseQualified (Ident <$> identifier))) <|> parseQualified (Op <$> operatorOrBuiltIn)
230+
231+mark :: P.Parsec String ParseState a -> P.Parsec String ParseState a
232+mark p = do
233+ current <- indentationLevel <$> P.getState
234+ pos <- P.sourceColumn <$> P.getPosition
235+ P.modifyState $ \st -> st { indentationLevel = pos }
236+ a <- p
237+ P.modifyState $ \st -> st { indentationLevel = current }
238+ return a
239+
240+checkIndentation :: (P.Column -> P.Column -> Bool) -> P.Parsec String ParseState ()
241+checkIndentation rel = do
242+ col <- P.sourceColumn <$> P.getPosition
243+ current <- indentationLevel <$> P.getState
244+ guard (col `rel` current)
245+
246+indented :: P.Parsec String ParseState ()
247+indented = checkIndentation (>) P.<?> "indentation"
248+
249+same :: P.Parsec String ParseState ()
250+same = checkIndentation (==) P.<?> "no indentation"
251+
252+runIndentParser :: P.Parsec String ParseState a -> String -> Either P.ParseError a
253+runIndentParser p = P.runParser p (ParseState 0) ""
src/Language/PureScript/Parser/Declarations.hsnew file+113
@@ -0,0 +1,113 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.PureScript.Parser.Declarations
4+-- Copyright : (c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+module Language.PureScript.Parser.Declarations (
16+ parseDeclaration,
17+ parseDeclarations
18+) where
19+
20+import Control.Applicative
21+import qualified Text.Parsec as P
22+
23+import Language.PureScript.Names
24+import Language.PureScript.Parser.State
25+import Language.PureScript.Parser.Common
26+import Language.PureScript.Declarations
27+import Language.PureScript.Parser.Values
28+import Language.PureScript.Parser.Types
29+import Language.PureScript.Parser.Kinds
30+
31+parseDataDeclaration :: P.Parsec String ParseState Declaration
32+parseDataDeclaration = do
33+ reserved "data"
34+ name <- indented *> properName
35+ tyArgs <- many (indented *> identifier)
36+ lexeme $ indented *> P.char '='
37+ ctors <- sepBy1 ((,) <$> properName <*> P.optionMaybe (indented *> parsePolyType)) pipe
38+ return $ DataDeclaration name tyArgs ctors
39+
40+parseTypeDeclaration :: P.Parsec String ParseState Declaration
41+parseTypeDeclaration =
42+ TypeDeclaration <$> P.try (parseIdent <* lexeme (indented *> P.string "::"))
43+ <*> parsePolyType
44+
45+parseTypeSynonymDeclaration :: P.Parsec String ParseState Declaration
46+parseTypeSynonymDeclaration =
47+ TypeSynonymDeclaration <$> (P.try (reserved "type") *> indented *> properName)
48+ <*> many (indented *> identifier)
49+ <*> (lexeme (indented *> P.char '=') *> parsePolyType)
50+
51+parseValueDeclaration :: P.Parsec String ParseState Declaration
52+parseValueDeclaration =
53+ ValueDeclaration <$> P.try (parseIdent <* lexeme (indented *> P.char '='))
54+ <*> parseValue
55+
56+parseExternDeclaration :: P.Parsec String ParseState Declaration
57+parseExternDeclaration = P.try (reserved "foreign") *> indented *> (reserved "import") *> indented *>
58+ (ExternDataDeclaration <$> (P.try (reserved "data") *> indented *> properName)
59+ <*> (lexeme (indented *> P.string "::") *> parseKind)
60+ <|> ExternDeclaration <$> parseIdent
61+ <*> (lexeme (indented *> P.string "::") *> parsePolyType)
62+ <|> ExternMemberDeclaration <$> (P.try (reserved "member") *> indented *> stringLiteral)
63+ <*> (indented *> parseIdent)
64+ <*> (lexeme (indented *> P.string "::") *> parsePolyType))
65+
66+parseAssociativity :: P.Parsec String ParseState Associativity
67+parseAssociativity =
68+ (P.try (reserved "infixl") >> return Infixl) <|>
69+ (P.try (reserved "infixr") >> return Infixr)
70+
71+parseFixity :: P.Parsec String ParseState Fixity
72+parseFixity = Fixity <$> parseAssociativity <*> (indented *> natural)
73+
74+parseFixityDeclaration :: P.Parsec String ParseState Declaration
75+parseFixityDeclaration = do
76+ fixity <- parseFixity
77+ indented
78+ name <- operator
79+ return $ FixityDeclaration fixity name
80+
81+parseModuleDeclaration :: P.Parsec String ParseState Declaration
82+parseModuleDeclaration = do
83+ reserved "module"
84+ indented
85+ name <- properName
86+ lexeme $ P.string "where"
87+ decls <- mark (P.many (same *> parseDeclaration))
88+ return $ ModuleDeclaration name decls
89+
90+parseModulePath :: P.Parsec String ParseState ModulePath
91+parseModulePath = ModulePath <$> properName `sepBy1` dot
92+
93+parseImportDeclaration :: P.Parsec String ParseState Declaration
94+parseImportDeclaration = do
95+ reserved "import"
96+ indented
97+ modulePath <- parseModulePath
98+ idents <- P.optionMaybe $ parens $ commaSep1 parseIdent
99+ return $ ImportDeclaration modulePath idents
100+
101+parseDeclaration :: P.Parsec String ParseState Declaration
102+parseDeclaration = P.choice
103+ [ parseDataDeclaration
104+ , parseTypeDeclaration
105+ , parseTypeSynonymDeclaration
106+ , parseValueDeclaration
107+ , parseExternDeclaration
108+ , parseFixityDeclaration
109+ , parseModuleDeclaration
110+ , parseImportDeclaration ] P.<?> "declaration"
111+
112+parseDeclarations :: P.Parsec String ParseState [Declaration]
113+parseDeclarations = whiteSpace *> mark (P.many (same *> parseDeclaration)) <* P.eof
src/Language/PureScript/Parser/Kinds.hsnew file+41
@@ -0,0 +1,41 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.PureScript.Parser.Kinds
4+-- Copyright : (c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+module Language.PureScript.Parser.Kinds (
16+ parseKind
17+) where
18+
19+import Language.PureScript.Kinds
20+import Language.PureScript.Parser.State
21+import Language.PureScript.Parser.Common
22+import Control.Applicative
23+import qualified Text.Parsec as P
24+import qualified Text.Parsec.Expr as P
25+
26+parseStar :: P.Parsec String ParseState Kind
27+parseStar = const Star <$> lexeme (P.char '*')
28+
29+parseRow :: P.Parsec String ParseState Kind
30+parseRow = const Row <$> lexeme (P.char '#')
31+
32+parseTypeAtom :: P.Parsec String ParseState Kind
33+parseTypeAtom = indented *> P.choice (map P.try
34+ [ parseStar
35+ , parseRow
36+ , parens parseKind ])
37+
38+parseKind :: P.Parsec String ParseState Kind
39+parseKind = P.buildExpressionParser operators parseTypeAtom P.<?> "kind"
40+ where
41+ operators = [ [ P.Infix (lexeme (P.try (P.string "->")) >> return FunKind) P.AssocRight ] ]
src/Language/PureScript/Parser/State.hsnew file+22
@@ -0,0 +1,22 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.PureScript.Parser.State
4+-- Copyright : (c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+module Language.PureScript.Parser.State where
16+
17+import qualified Text.Parsec as P
18+
19+data ParseState = ParseState
20+ { indentationLevel :: P.Column } deriving Show
21+
22+
src/Language/PureScript/Parser/Types.hsnew file+104
@@ -0,0 +1,104 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.PureScript.Parser.Types
4+-- Copyright : (c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+module Language.PureScript.Parser.Types (
16+ parseType,
17+ parsePolyType,
18+ parseRow
19+) where
20+
21+import Language.PureScript.Types
22+import Language.PureScript.Parser.State
23+import Language.PureScript.Parser.Common
24+import Control.Applicative
25+import qualified Text.Parsec as P
26+import qualified Text.Parsec.Expr as P
27+import Control.Monad (unless)
28+
29+parseNumber :: P.Parsec String ParseState Type
30+parseNumber = const Number <$> reserved "Number"
31+
32+parseString :: P.Parsec String ParseState Type
33+parseString = const String <$> reserved "String"
34+
35+parseBoolean :: P.Parsec String ParseState Type
36+parseBoolean = const Boolean <$> reserved "Boolean"
37+
38+parseArray :: P.Parsec String ParseState Type
39+parseArray = squares $ Array <$> parseType
40+
41+parseObject :: P.Parsec String ParseState Type
42+parseObject = braces $ Object <$> parseRow
43+
44+parseFunction :: P.Parsec String ParseState Type
45+parseFunction = do
46+ args <- lexeme $ parens $ commaSep parsePolyType
47+ lexeme $ P.string "->"
48+ resultType <- parseType
49+ return $ Function args resultType
50+
51+parseTypeVariable :: P.Parsec String ParseState Type
52+parseTypeVariable = TypeVar <$> identifier
53+
54+parseTypeConstructor :: P.Parsec String ParseState Type
55+parseTypeConstructor = TypeConstructor <$> parseQualified properName
56+
57+parseForAll :: P.Parsec String ParseState Type
58+parseForAll = (mkForAll <$> (P.try (reserved "forall") *> P.many1 (indented *> identifier) <* indented <* dot)
59+ <*> parseType)
60+
61+parseTypeAtom :: P.Parsec String ParseState Type
62+parseTypeAtom = indented *> P.choice (map P.try
63+ [ parseNumber
64+ , parseString
65+ , parseBoolean
66+ , parseArray
67+ , parseObject
68+ , parseFunction
69+ , parseTypeVariable
70+ , parseTypeConstructor
71+ , parseForAll
72+ , parens parseType ])
73+
74+parseAnyType :: P.Parsec String ParseState Type
75+parseAnyType = (P.buildExpressionParser operators . buildPostfixParser postfixTable $ parseTypeAtom) P.<?> "type"
76+ where
77+ postfixTable :: [Type -> P.Parsec String ParseState Type]
78+ postfixTable = [ \x -> TypeApp x <$> P.try (indented *> parseTypeAtom) ]
79+ operators = [ [ P.Infix (lexeme (P.try (P.string "->")) >> return (\t1 t2 -> Function [t1] t2)) P.AssocRight ] ]
80+
81+parseType :: P.Parsec String ParseState Type
82+parseType = do
83+ ty <- parseAnyType
84+ unless (isMonoType ty) $ P.unexpected "polymorphic type"
85+ return ty
86+
87+parsePolyType :: P.Parsec String ParseState PolyType
88+parsePolyType = do
89+ ty <- parseAnyType
90+ unless (isPolyType ty) $ P.unexpected "polymorphic type"
91+ return ty
92+
93+parseNameAndType :: P.Parsec String ParseState (String, Type)
94+parseNameAndType = (,) <$> (indented *> identifier <* indented <* lexeme (P.string "::")) <*> parsePolyType
95+
96+parseRowEnding :: P.Parsec String ParseState Row
97+parseRowEnding = P.option REmpty (RowVar <$> (lexeme (indented *> P.char '|') *> indented *> identifier))
98+
99+parseRow :: P.Parsec String ParseState Row
100+parseRow = (fromList <$> (commaSep parseNameAndType) <*> parseRowEnding) P.<?> "row"
101+ where
102+ fromList :: [(String, Type)] -> Row -> Row
103+ fromList [] r = r
104+ fromList ((name, t):ts) r = RCons name t (fromList ts r)
src/Language/PureScript/Parser/Values.hsnew file+268
@@ -0,0 +1,268 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.PureScript.Parser.Values
4+-- Copyright : (c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+module Language.PureScript.Parser.Values (
16+ parseValue,
17+ parseBinder
18+) where
19+
20+import Language.PureScript.Values
21+import Language.PureScript.Parser.State
22+import qualified Language.PureScript.Parser.Common as C
23+import Control.Applicative
24+import qualified Text.Parsec as P
25+import Text.Parsec.Expr
26+import Language.PureScript.Parser.Types
27+
28+booleanLiteral :: P.Parsec String ParseState Bool
29+booleanLiteral = (C.reserved "true" >> return True) P.<|> (C.reserved "false" >> return False)
30+
31+parseNumericLiteral :: P.Parsec String ParseState Value
32+parseNumericLiteral = NumericLiteral <$> C.integerOrFloat
33+
34+parseStringLiteral :: P.Parsec String ParseState Value
35+parseStringLiteral = StringLiteral <$> C.stringLiteral
36+
37+parseBooleanLiteral :: P.Parsec String ParseState Value
38+parseBooleanLiteral = BooleanLiteral <$> booleanLiteral
39+
40+parseArrayLiteral :: P.Parsec String ParseState Value
41+parseArrayLiteral = ArrayLiteral <$> C.squares (C.commaSep parseValue)
42+
43+parseObjectLiteral :: P.Parsec String ParseState Value
44+parseObjectLiteral = ObjectLiteral <$> C.braces (C.commaSep parseIdentifierAndValue)
45+
46+parseIdentifierAndValue :: P.Parsec String ParseState (String, Value)
47+parseIdentifierAndValue = (,) <$> (C.indented *> C.identifier <* C.indented <* C.colon)
48+ <*> (C.indented *> parseValue)
49+
50+parseAbs :: P.Parsec String ParseState Value
51+parseAbs = do
52+ C.lexeme $ P.char '\\'
53+ args <- P.many (C.indented *> (P.try singleArg <|> manyArgs))
54+ C.lexeme $ C.indented *> P.string "->"
55+ value <- parseValue
56+ return $ toFunction args value
57+ where
58+ manyArgs :: P.Parsec String ParseState (Value -> Value)
59+ manyArgs = do
60+ args <- C.parens (C.commaSep C.parseIdent)
61+ return $ Abs args
62+ singleArg :: P.Parsec String ParseState (Value -> Value)
63+ singleArg = Abs . return <$> C.parseIdent
64+ toFunction :: [Value -> Value] -> Value -> Value
65+ toFunction [] value = Abs [] value
66+ toFunction args value = foldr (($)) value args
67+
68+parseVar :: P.Parsec String ParseState Value
69+parseVar = Var <$> C.parseQualified C.parseIdent
70+
71+parseConstructor :: P.Parsec String ParseState Value
72+parseConstructor = Constructor <$> C.parseQualified C.properName
73+
74+parseCase :: P.Parsec String ParseState Value
75+parseCase = Case <$> P.between (P.try (C.reserved "case")) (C.indented *> C.reserved "of") parseValue
76+ <*> (C.indented *> C.mark (P.many (C.same *> C.mark parseCaseAlternative)))
77+
78+parseCaseAlternative :: P.Parsec String ParseState (Binder, Value)
79+parseCaseAlternative = (,) <$> (parseGuardedBinder <* C.lexeme (P.string "->"))
80+ <*> parseValue
81+ P.<?> "case alternative"
82+
83+parseIfThenElse :: P.Parsec String ParseState Value
84+parseIfThenElse = IfThenElse <$> (P.try (C.reserved "if") *> C.indented *> parseValue)
85+ <*> (C.indented *> C.reserved "then" *> C.indented *> parseValue)
86+ <*> (C.indented *> C.reserved "else" *> C.indented *> parseValue)
87+
88+parseBlock :: P.Parsec String ParseState Value
89+parseBlock = Block <$> parseManyStatements
90+
91+parseManyStatements :: P.Parsec String ParseState [Statement]
92+parseManyStatements = (do
93+ C.lexeme $ P.char '{'
94+ C.indented
95+ sts <- C.mark (P.many (C.same *> C.mark parseStatement))
96+ C.lexeme (P.char '}')
97+ return sts) P.<?> "block"
98+
99+parseValueAtom :: P.Parsec String ParseState Value
100+parseValueAtom = P.choice
101+ [ P.try parseNumericLiteral
102+ , P.try parseStringLiteral
103+ , P.try parseBooleanLiteral
104+ , parseArrayLiteral
105+ , P.try parseObjectLiteral
106+ , parseAbs
107+ , P.try parseConstructor
108+ , P.try parseVar
109+ , parseBlock
110+ , parseCase
111+ , parseIfThenElse
112+ , Parens <$> C.parens parseValue ]
113+
114+parsePropertyUpdate :: P.Parsec String ParseState (String, Value)
115+parsePropertyUpdate = do
116+ name <- C.lexeme C.identifier
117+ C.lexeme $ C.indented *> P.char '='
118+ value <- C.indented *> parseValue
119+ return (name, value)
120+
121+parseAccessor :: Value -> P.Parsec String ParseState Value
122+parseAccessor (Constructor _) = P.unexpected "constructor"
123+parseAccessor obj = P.try $ Accessor <$> (C.indented *> C.dot *> P.notFollowedBy C.opLetter *> C.indented *> C.identifier) <*> pure obj
124+
125+parseValue :: P.Parsec String ParseState Value
126+parseValue =
127+ (buildExpressionParser operators
128+ . C.buildPostfixParser postfixTable2
129+ $ indexersAndAccessors) P.<?> "expression"
130+ where
131+ indexersAndAccessors = C.buildPostfixParser postfixTable1 parseValueAtom
132+ postfixTable1 = [ parseAccessor
133+ , \v -> P.try $ flip ObjectUpdate <$> (C.indented *> C.braces (C.commaSep1 (C.indented *> parsePropertyUpdate))) <*> pure v ]
134+ postfixTable2 = [ \v -> P.try (C.indented *> indexersAndAccessors >>= \t2 -> return (\t1 -> App t1 [t2])) <*> pure v
135+ , \v -> P.try $ flip App <$> (C.indented *> C.parens (C.commaSep parseValue)) <*> pure v
136+ , \v -> flip TypedValue <$> (P.try (C.lexeme (C.indented *> P.string "::")) *> parsePolyType) <*> pure v ]
137+ operators = [ [ Prefix $ C.lexeme (P.try $ C.indented *> C.reservedOp "!") >> return (Unary Not)
138+ , Prefix $ C.lexeme (P.try $ C.indented *> C.reservedOp "~") >> return (Unary BitwiseNot)
139+ , Prefix $ C.lexeme (P.try $ C.indented *> C.reservedOp "-") >> return (Unary Negate)
140+ , Prefix $ C.lexeme (P.try $ C.indented *> C.reservedOp "+") >> return id ]
141+ , [ Infix (C.lexeme (P.try (C.indented *> C.parseIdentInfix P.<?> "operator") >>= \ident ->
142+ return (BinaryNoParens ident))) AssocRight ]
143+ ]
144+
145+parseVariableIntroduction :: P.Parsec String ParseState Statement
146+parseVariableIntroduction = do
147+ C.reserved "var"
148+ name <- C.indented *> C.parseIdent
149+ C.lexeme $ C.indented *> P.char '='
150+ value <- parseValue
151+ C.indented *> C.semi
152+ return $ VariableIntroduction name value
153+
154+parseAssignment :: P.Parsec String ParseState Statement
155+parseAssignment = do
156+ tgt <- P.try $ do
157+ tgt <- C.parseIdent
158+ C.lexeme $ C.indented *> P.char '='
159+ return tgt
160+ value <- parseValue
161+ C.indented *> C.semi
162+ return $ Assignment tgt value
163+
164+parseWhile :: P.Parsec String ParseState Statement
165+parseWhile = While <$> (C.reserved "while" *> C.indented *> C.parens parseValue)
166+ <*> (C.indented *> parseManyStatements)
167+
168+parseFor :: P.Parsec String ParseState Statement
169+parseFor = For <$> (C.reserved "for" *> C.indented *> C.lexeme (P.char '(') *> C.indented *> C.parseIdent)
170+ <*> (C.indented *> C.lexeme (P.string "<-") *> parseValue)
171+ <*> (C.indented *> C.reserved "until" *> parseValue <* C.indented <* C.lexeme (P.char ')'))
172+ <*> parseManyStatements
173+
174+parseForEach :: P.Parsec String ParseState Statement
175+parseForEach = ForEach <$> (C.reserved "foreach" *> C.indented *> C.lexeme (P.char '(') *> C.indented *> C.parseIdent)
176+ <*> (C.indented *> C.reserved "in" *> parseValue <* C.lexeme (P.char ')'))
177+ <*> parseManyStatements
178+
179+parseIf :: P.Parsec String ParseState Statement
180+parseIf = If <$> parseIfStatement
181+
182+parseIfStatement :: P.Parsec String ParseState IfStatement
183+parseIfStatement =
184+ IfStatement <$> (C.reserved "if" *> C.indented *> C.parens parseValue)
185+ <*> parseManyStatements
186+ <*> P.optionMaybe parseElseStatement
187+
188+parseElseStatement :: P.Parsec String ParseState ElseStatement
189+parseElseStatement = C.reserved "else" >> (ElseIf <$> parseIfStatement
190+ <|> Else <$> parseManyStatements)
191+
192+parseValueStatement :: P.Parsec String ParseState Statement
193+parseValueStatement = ValueStatement <$> (parseValue <* C.semi)
194+
195+parseReturn :: P.Parsec String ParseState Statement
196+parseReturn = Return <$> (C.reserved "return" *> parseValue <* C.indented <* C.semi)
197+
198+parseStatement :: P.Parsec String ParseState Statement
199+parseStatement = P.choice
200+ [ parseVariableIntroduction
201+ , parseAssignment
202+ , parseWhile
203+ , parseFor
204+ , parseForEach
205+ , parseIf
206+ , parseValueStatement
207+ , parseReturn ] P.<?> "statement"
208+
209+parseStringBinder :: P.Parsec String ParseState Binder
210+parseStringBinder = StringBinder <$> C.stringLiteral
211+
212+parseBooleanBinder :: P.Parsec String ParseState Binder
213+parseBooleanBinder = BooleanBinder <$> booleanLiteral
214+
215+parseNumberBinder :: P.Parsec String ParseState Binder
216+parseNumberBinder = NumberBinder <$> C.integerOrFloat
217+
218+parseVarBinder :: P.Parsec String ParseState Binder
219+parseVarBinder = VarBinder <$> C.parseIdent
220+
221+parseNullaryBinder :: P.Parsec String ParseState Binder
222+parseNullaryBinder = NullaryBinder <$> C.lexeme (C.parseQualified C.properName)
223+
224+parseUnaryBinder :: P.Parsec String ParseState Binder
225+parseUnaryBinder = UnaryBinder <$> C.lexeme (C.parseQualified C.properName) <*> (C.indented *> parseBinder)
226+
227+parseObjectBinder :: P.Parsec String ParseState Binder
228+parseObjectBinder = ObjectBinder <$> C.braces (C.commaSep (C.indented *> parseIdentifierAndBinder))
229+
230+parseArrayBinder :: P.Parsec String ParseState Binder
231+parseArrayBinder = C.squares $ ArrayBinder <$> (C.commaSep (C.indented *> parseBinder))
232+
233+parseNamedBinder :: P.Parsec String ParseState Binder
234+parseNamedBinder = NamedBinder <$> (C.parseIdent <* C.indented <* C.lexeme (P.char '@'))
235+ <*> (C.indented *> parseBinder)
236+
237+parseNullBinder :: P.Parsec String ParseState Binder
238+parseNullBinder = C.lexeme (P.char '_') *> P.notFollowedBy C.identLetter *> return NullBinder
239+
240+parseIdentifierAndBinder :: P.Parsec String ParseState (String, Binder)
241+parseIdentifierAndBinder = do
242+ name <- C.lexeme C.identifier
243+ C.lexeme $ C.indented *> P.char '='
244+ binder <- C.indented *> parseBinder
245+ return (name, binder)
246+
247+parseBinderAtom :: P.Parsec String ParseState Binder
248+parseBinderAtom = P.choice (map P.try
249+ [ parseNullBinder
250+ , parseStringBinder
251+ , parseBooleanBinder
252+ , parseNumberBinder
253+ , parseNamedBinder
254+ , parseVarBinder
255+ , parseUnaryBinder
256+ , parseNullaryBinder
257+ , parseObjectBinder
258+ , parseArrayBinder
259+ , C.parens parseBinder ]) P.<?> "binder"
260+
261+parseBinder :: P.Parsec String ParseState Binder
262+parseBinder = (buildExpressionParser operators parseBinderAtom) P.<?> "expression"
263+ where
264+ operators = [ [ Infix ( C.lexeme (P.try $ C.indented *> C.reservedOp ":") >> return ConsBinder) AssocRight ] ]
265+
266+parseGuardedBinder :: P.Parsec String ParseState Binder
267+parseGuardedBinder = flip ($) <$> parseBinder <*> P.option id (GuardedBinder <$> (C.indented *> C.lexeme (P.char '|') *> C.indented *> parseValue))
268+
src/Language/PureScript/Pretty.hsnew file+23
@@ -0,0 +1,23 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.PureScript.Pretty
4+-- Copyright : (c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+module Language.PureScript.Pretty (module P) where
16+
17+import Language.PureScript.Pretty.Kinds as P
18+import Language.PureScript.Pretty.Values as P
19+import Language.PureScript.Pretty.Types as P
20+import Language.PureScript.Pretty.JS as P
21+
22+
23+
src/Language/PureScript/Pretty/Common.hsnew file+79
@@ -0,0 +1,79 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.PureScript.Pretty.Common
4+-- Copyright : (c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+{-# LANGUAGE GeneralizedNewtypeDeriving, GADTs #-}
16+
17+module Language.PureScript.Pretty.Common where
18+
19+import Data.Char
20+import Control.Monad.State
21+import qualified Control.Category as C
22+import Control.Category ((>>>))
23+import qualified Control.Arrow as A
24+import Control.Arrow ((***), (<+>))
25+
26+import Language.PureScript.Names
27+
28+identToJs :: Ident -> String
29+identToJs (Ident name) = name
30+identToJs (Op op) = concatMap opCharToString op
31+ where
32+ opCharToString :: Char -> String
33+ opCharToString = (:) '$'. show . ord
34+
35+newtype Pattern u a b = Pattern { runPattern :: A.Kleisli (StateT u Maybe) a b } deriving (C.Category, A.Arrow, A.ArrowZero, A.ArrowPlus)
36+
37+instance Functor (Pattern u a) where
38+ fmap f (Pattern p) = Pattern $ A.Kleisli $ fmap f . A.runKleisli p
39+
40+pattern :: Pattern u a b -> u -> a -> Maybe b
41+pattern p u = flip evalStateT u . A.runKleisli (runPattern p)
42+
43+mkPattern :: (a -> Maybe b) -> Pattern u a b
44+mkPattern f = Pattern $ A.Kleisli (lift . f)
45+
46+mkPattern' :: (a -> StateT u Maybe b) -> Pattern u a b
47+mkPattern' = Pattern . A.Kleisli
48+
49+parens :: String -> String
50+parens s = ('(':s) ++ ")"
51+
52+chainl :: Pattern u a (a, a) -> (r -> r -> r) -> Pattern u a r -> Pattern u a r
53+chainl g f p = fix $ \c -> g >>> ((c <+> p) *** p) >>> A.arr (uncurry f)
54+
55+chainr :: Pattern u a (a, a) -> (r -> r -> r) -> Pattern u a r -> Pattern u a r
56+chainr g f p = fix $ \c -> g >>> (p *** (c <+> p)) >>> A.arr (uncurry f)
57+
58+wrap :: Pattern u a (s, a) -> (s -> r -> r) -> Pattern u a r -> Pattern u a r
59+wrap g f p = fix $ \c -> g >>> (C.id *** (c <+> p)) >>> A.arr (uncurry f)
60+
61+split :: Pattern u a (s, t) -> (s -> t -> r) -> Pattern u a r
62+split s f = s >>> A.arr (uncurry f)
63+
64+data OperatorTable u a r = OperatorTable { runOperatorTable :: [ [Operator u a r] ] }
65+
66+data Operator u a r where
67+ AssocL :: Pattern u a (a, a) -> (r -> r -> r) -> Operator u a r
68+ AssocR :: Pattern u a (a, a) -> (r -> r -> r) -> Operator u a r
69+ Wrap :: Pattern u a (s, a) -> (s -> r -> r) -> Operator u a r
70+ Split :: Pattern u a (s, t) -> (s -> t -> r) -> Operator u a r
71+
72+buildPrettyPrinter :: OperatorTable u a r -> Pattern u a r -> Pattern u a r
73+buildPrettyPrinter table p = foldl (\p' ops -> foldl1 (<+>) (flip map ops $ \op ->
74+ case op of
75+ AssocL pat g -> chainl pat g p'
76+ AssocR pat g -> chainr pat g p'
77+ Wrap pat g -> wrap pat g p'
78+ Split pat g -> split pat g
79+ ) <+> p') p $ runOperatorTable table
src/Language/PureScript/Pretty/JS.hsnew file+224
@@ -0,0 +1,224 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.PureScript.Pretty.JS
4+-- Copyright : (c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+module Language.PureScript.Pretty.JS (
16+ prettyPrintJS
17+) where
18+
19+import Language.PureScript.Names
20+import Language.PureScript.Values
21+import Language.PureScript.Pretty.Common
22+import Language.PureScript.CodeGen.JS.AST
23+
24+import Data.List
25+import Data.Maybe (fromMaybe)
26+import qualified Control.Arrow as A
27+import Control.Arrow ((<+>))
28+import Control.Applicative
29+import Control.Monad.State
30+
31+newtype PrinterState = PrinterState { indent :: Int } deriving (Show, Eq, Ord)
32+
33+blockIndent :: Int
34+blockIndent = 4
35+
36+withIndent :: StateT PrinterState Maybe String -> StateT PrinterState Maybe String
37+withIndent action = do
38+ modify $ \st -> st { indent = indent st + blockIndent }
39+ result <- action
40+ modify $ \st -> st { indent = indent st - blockIndent }
41+ return result
42+
43+currentIndent :: StateT PrinterState Maybe String
44+currentIndent = do
45+ current <- get
46+ return $ replicate (indent current) ' '
47+
48+literals :: Pattern PrinterState JS String
49+literals = mkPattern' match
50+ where
51+ match :: JS -> StateT PrinterState Maybe String
52+ match (JSNumericLiteral n) = return $ either show show n
53+ match (JSStringLiteral s) = return $ show s
54+ match (JSBooleanLiteral True) = return "true"
55+ match (JSBooleanLiteral False) = return "false"
56+ match (JSArrayLiteral xs) = fmap concat $ sequence
57+ [ return "[ "
58+ , fmap (intercalate ", ") $ forM xs prettyPrintJS'
59+ , return " ]"
60+ ]
61+ match (JSObjectLiteral ps) = fmap concat $ sequence
62+ [ return "{\n"
63+ , withIndent $ do
64+ jss <- forM ps $ \(key, value) -> fmap ((key ++ ": ") ++) . prettyPrintJS' $ value
65+ indentString <- currentIndent
66+ return $ intercalate ", \n" $ map (indentString ++) jss
67+ , return "\n"
68+ , currentIndent
69+ , return "}"
70+ ]
71+ match (JSBlock sts) = fmap concat $ sequence
72+ [ return "{\n"
73+ , withIndent $ do
74+ jss <- forM sts prettyPrintJS'
75+ indentString <- currentIndent
76+ return $ intercalate "\n" $ map (++ "; ") $ map (indentString ++) jss
77+ , return "\n"
78+ , currentIndent
79+ , return "}"
80+ ]
81+ match (JSVar ident) = return (identToJs ident)
82+ match (JSVariableIntroduction ident value) = fmap concat $ sequence
83+ [ return "var "
84+ , return $ identToJs ident
85+ , maybe (return "") (fmap (" = " ++) . prettyPrintJS') value
86+ ]
87+ match (JSAssignment target value) = fmap concat $ sequence
88+ [ return $ targetToJs target
89+ , return " = "
90+ , prettyPrintJS' value
91+ ]
92+ match (JSWhile cond sts) = fmap concat $ sequence
93+ [ return "while ("
94+ , prettyPrintJS' cond
95+ , return ") "
96+ , prettyPrintJS' sts
97+ ]
98+ match (JSFor ident start end sts) = fmap concat $ sequence
99+ [ return $ "for (" ++ identToJs ident ++ " = "
100+ , prettyPrintJS' start
101+ , return $ "; " ++ identToJs ident ++ " < "
102+ , prettyPrintJS' end
103+ , return $ "; " ++ identToJs ident ++ "++) "
104+ , prettyPrintJS' sts
105+ ]
106+ match (JSIfElse cond thens elses) = fmap concat $ sequence
107+ [ return "if ("
108+ , prettyPrintJS' cond
109+ , return ") "
110+ , prettyPrintJS' thens
111+ , maybe (return "") (fmap (" else " ++) . prettyPrintJS') elses
112+ ]
113+ match (JSReturn value) = fmap concat $ sequence
114+ [ return "return "
115+ , prettyPrintJS' value
116+ ]
117+ match (JSThrow value) = fmap concat $ sequence
118+ [ return "throw "
119+ , prettyPrintJS' value
120+ ]
121+ match _ = mzero
122+
123+targetToJs :: JSAssignment -> String
124+targetToJs (JSAssignVariable ident) = identToJs ident
125+targetToJs (JSAssignProperty prop target) = targetToJs target ++ "." ++ prop
126+
127+conditional :: Pattern PrinterState JS ((JS, JS), JS)
128+conditional = mkPattern match
129+ where
130+ match (JSConditional cond th el) = Just ((th, el), cond)
131+ match _ = Nothing
132+
133+accessor :: Pattern PrinterState JS (String, JS)
134+accessor = mkPattern match
135+ where
136+ match (JSAccessor prop val) = Just (prop, val)
137+ match _ = Nothing
138+
139+indexer :: Pattern PrinterState JS (String, JS)
140+indexer = mkPattern' match
141+ where
142+ match (JSIndexer index val) = (,) <$> prettyPrintJS' index <*> pure val
143+ match _ = mzero
144+
145+lam :: Pattern PrinterState JS ((Maybe Ident, [Ident]), JS)
146+lam = mkPattern match
147+ where
148+ match (JSFunction name args ret) = Just ((name, args), ret)
149+ match _ = Nothing
150+
151+app :: Pattern PrinterState JS (String, JS)
152+app = mkPattern' match
153+ where
154+ match (JSApp val args) = do
155+ jss <- mapM prettyPrintJS' args
156+ return (intercalate ", " jss, val)
157+ match _ = mzero
158+
159+unary :: UnaryOperator -> String -> Operator PrinterState JS String
160+unary op str = Wrap match (++)
161+ where
162+ match :: Pattern PrinterState JS (String, JS)
163+ match = mkPattern match'
164+ where
165+ match' (JSUnary op' val) | op' == op = Just (str, val)
166+ match' _ = Nothing
167+
168+binary :: BinaryOperator -> String -> Operator PrinterState JS String
169+binary op str = AssocR match (\v1 v2 -> v1 ++ " " ++ str ++ " " ++ v2)
170+ where
171+ match :: Pattern PrinterState JS (JS, JS)
172+ match = mkPattern match'
173+ where
174+ match' (JSBinary op' v1 v2) | op' == op = Just (v1, v2)
175+ match' _ = Nothing
176+
177+prettyPrintJS1 :: JS -> String
178+prettyPrintJS1 = fromMaybe (error "Incomplete pattern") . flip evalStateT (PrinterState 0) . prettyPrintJS'
179+
180+prettyPrintJS :: [JS] -> String
181+prettyPrintJS sts = fromMaybe (error "Incomplete pattern") . flip evalStateT (PrinterState 0) $ do
182+ jss <- forM sts prettyPrintJS'
183+ indentString <- currentIndent
184+ return $ intercalate "\n" $ map (++ "; ") $ map (indentString ++) jss
185+
186+prettyPrintJS' :: JS -> StateT PrinterState Maybe String
187+prettyPrintJS' = A.runKleisli $ runPattern matchValue
188+ where
189+ matchValue :: Pattern PrinterState JS String
190+ matchValue = buildPrettyPrinter operators (literals <+> fmap parens matchValue)
191+ operators :: OperatorTable PrinterState JS String
192+ operators =
193+ OperatorTable [ [ Wrap accessor $ \prop val -> val ++ "." ++ prop ]
194+ , [ Wrap indexer $ \index val -> val ++ "[" ++ index ++ "]" ]
195+ , [ Wrap app $ \args val -> val ++ "(" ++ args ++ ")" ]
196+ , [ Wrap lam $ \(name, args) ret -> "function "
197+ ++ maybe "" identToJs name
198+ ++ "(" ++ intercalate ", " (map identToJs args) ++ ") "
199+ ++ ret ]
200+ , [ Wrap conditional $ \(th, el) cond -> cond ++ " ? " ++ prettyPrintJS1 th ++ " : " ++ prettyPrintJS1 el ]
201+ , [ binary LessThan "<" ]
202+ , [ binary LessThanOrEqualTo "<=" ]
203+ , [ binary GreaterThan ">" ]
204+ , [ binary GreaterThanOrEqualTo ">=" ]
205+ , [ unary Not "!" ]
206+ , [ unary BitwiseNot "~" ]
207+ , [ unary Negate "-" ]
208+ , [ binary Multiply "*" ]
209+ , [ binary Divide "/" ]
210+ , [ binary Modulus "%" ]
211+ , [ binary Concat "+" ]
212+ , [ binary Add "+" ]
213+ , [ binary Subtract "-" ]
214+ , [ binary ShiftLeft "<<" ]
215+ , [ binary ShiftRight ">>" ]
216+ , [ binary ZeroFillShiftRight ">>>" ]
217+ , [ binary EqualTo "===" ]
218+ , [ binary NotEqualTo "!==" ]
219+ , [ binary BitwiseAnd "&" ]
220+ , [ binary BitwiseXor "^" ]
221+ , [ binary BitwiseOr "|" ]
222+ , [ binary And "&&" ]
223+ , [ binary Or "||" ]
224+ ]
src/Language/PureScript/Pretty/Kinds.hsnew file+47
@@ -0,0 +1,47 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.PureScript.Pretty.Kinds
4+-- Copyright : (c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+module Language.PureScript.Pretty.Kinds (
16+ prettyPrintKind
17+) where
18+
19+import Data.Maybe (fromMaybe)
20+
21+import Language.PureScript.Kinds
22+import Language.PureScript.Pretty.Common
23+import Language.PureScript.Unknown
24+import Control.Arrow (ArrowPlus(..))
25+
26+typeLiterals :: Pattern () Kind String
27+typeLiterals = mkPattern match
28+ where
29+ match Star = Just "*"
30+ match Row = Just "#"
31+ match (KUnknown (Unknown u)) = Just $ 'u' : show u
32+ match _ = Nothing
33+
34+funKind :: Pattern () Kind (Kind, Kind)
35+funKind = mkPattern match
36+ where
37+ match (FunKind arg ret) = Just (arg, ret)
38+ match _ = Nothing
39+
40+prettyPrintKind :: Kind -> String
41+prettyPrintKind = fromMaybe (error "Incomplete pattern") . pattern matchKind ()
42+ where
43+ matchKind :: Pattern () Kind String
44+ matchKind = buildPrettyPrinter operators (typeLiterals <+> fmap parens matchKind)
45+ operators :: OperatorTable () Kind String
46+ operators =
47+ OperatorTable [ [ AssocR funKind $ \arg ret -> arg ++ " -> " ++ ret ] ]
src/Language/PureScript/Pretty/Types.hsnew file+88
@@ -0,0 +1,88 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.PureScript.Pretty.Types
4+-- Copyright : (c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+module Language.PureScript.Pretty.Types (
16+ prettyPrintType,
17+ prettyPrintRow
18+) where
19+
20+import Data.Maybe (fromMaybe)
21+import Data.List (intercalate)
22+import Control.Arrow ((<+>))
23+
24+import Language.PureScript.Types
25+import Language.PureScript.Pretty.Common
26+import Language.PureScript.Unknown
27+
28+typeLiterals :: Pattern () Type String
29+typeLiterals = mkPattern match
30+ where
31+ match Number = Just "Number"
32+ match String = Just "String"
33+ match Boolean = Just "Boolean"
34+ match (Array ty) = Just $ "[" ++ prettyPrintType ty ++ "]"
35+ match (Object row) = Just $ "{ " ++ prettyPrintRow row ++ " }"
36+ match (TypeVar var) = Just var
37+ match (TypeConstructor ctor) = Just $ show ctor
38+ match (TUnknown (Unknown u)) = Just $ 'u' : show u
39+ match (Skolem s) = Just $ 's' : show s
40+ match (SaturatedTypeSynonym name args) = Just $ show name ++ "<" ++ intercalate "," (map prettyPrintType args) ++ ">"
41+ match (ForAll ident ty) = Just $ "forall " ++ ident ++ ". " ++ prettyPrintType ty
42+ match _ = Nothing
43+
44+prettyPrintRow :: Row -> String
45+prettyPrintRow = (\(tys, rest) -> intercalate ", " (map (uncurry nameAndTypeToPs) tys) ++ tailToPs rest) . toList []
46+ where
47+ nameAndTypeToPs :: String -> Type -> String
48+ nameAndTypeToPs name ty = name ++ " :: " ++ prettyPrintType ty
49+ tailToPs :: Row -> String
50+ tailToPs REmpty = ""
51+ tailToPs (RUnknown (Unknown u)) = " | u" ++ show u
52+ tailToPs (RowVar var) = " | " ++ var
53+ tailToPs (RSkolem s) = " | s" ++ show s
54+ tailToPs _ = error "Invalid row tail"
55+ toList :: [(String, Type)] -> Row -> ([(String, Type)], Row)
56+ toList tys (RCons name ty row) = toList ((name, ty):tys) row
57+ toList tys r = (tys, r)
58+
59+typeApp :: Pattern () Type (Type, Type)
60+typeApp = mkPattern match
61+ where
62+ match (TypeApp f x) = Just (f, x)
63+ match _ = Nothing
64+
65+singleArgumentFunction :: Pattern () Type (Type, Type)
66+singleArgumentFunction = mkPattern match
67+ where
68+ match (Function [arg] ret) = Just (arg, ret)
69+ match _ = Nothing
70+
71+function :: Pattern () Type ([Type], Type)
72+function = mkPattern match
73+ where
74+ match (Function args ret) = Just (args, ret)
75+ match _ = Nothing
76+
77+prettyPrintType :: Type -> String
78+prettyPrintType = fromMaybe (error "Incomplete pattern") . pattern matchType ()
79+ where
80+ matchType :: Pattern () Type String
81+ matchType = buildPrettyPrinter operators (typeLiterals <+> fmap parens matchType)
82+ operators :: OperatorTable () Type String
83+ operators =
84+ OperatorTable [ [ AssocL typeApp $ \f x -> f ++ " " ++ x ]
85+ , [ AssocR singleArgumentFunction $ \arg ret -> arg ++ " -> " ++ ret
86+ , Wrap function $ \args ret -> "(" ++ intercalate ", " (map prettyPrintType args) ++ ") -> " ++ ret
87+ ]
88+ ]
src/Language/PureScript/Pretty/Values.hsnew file+209
@@ -0,0 +1,209 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.PureScript.Pretty.Values
4+-- Copyright : Kinds.hs(c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+module Language.PureScript.Pretty.Values (
16+ prettyPrintValue,
17+ prettyPrintBinder
18+) where
19+
20+import Data.Maybe (fromMaybe)
21+import Data.List (intercalate)
22+import Control.Arrow ((<+>))
23+
24+import Language.PureScript.Types
25+import Language.PureScript.Values
26+import Language.PureScript.Pretty.Common
27+import Language.PureScript.Pretty.Types
28+
29+literals :: Pattern () Value String
30+literals = mkPattern match
31+ where
32+ match (NumericLiteral n) = Just $ either show show n
33+ match (StringLiteral s) = Just $ show s
34+ match (BooleanLiteral True) = Just "true"
35+ match (BooleanLiteral False) = Just "false"
36+ match (ArrayLiteral xs) = Just $ "[" ++ intercalate ", " (map prettyPrintValue xs) ++ "]"
37+ match (ObjectLiteral ps) = Just $ "{" ++ intercalate ", " (map (uncurry prettyPrintObjectProperty) ps) ++ "}"
38+ match (Constructor name) = Just $ show name
39+ match (Block sts) = Just $ "do { " ++ intercalate " ; " (map prettyPrintStatement sts) ++ " }"
40+ match (Case value binders) = Just $ "case " ++ prettyPrintValue value ++ " of { " ++ intercalate " ; " (map (uncurry prettyPrintCaseAlternative) binders) ++ " }"
41+ match (Var ident) = Just $ show ident
42+ match _ = Nothing
43+
44+prettyPrintCaseAlternative :: Binder -> Value -> String
45+prettyPrintCaseAlternative binder val = prettyPrintBinder binder ++ " -> " ++ prettyPrintValue val
46+
47+ifThenElse :: Pattern () Value ((Value, Value), Value)
48+ifThenElse = mkPattern match
49+ where
50+ match (IfThenElse cond th el) = Just ((th, el), cond)
51+ match _ = Nothing
52+
53+accessor :: Pattern () Value (String, Value)
54+accessor = mkPattern match
55+ where
56+ match (Accessor prop val) = Just (prop, val)
57+ match _ = Nothing
58+
59+indexer :: Pattern () Value (Value, Value)
60+indexer = mkPattern match
61+ where
62+ match (Indexer index val) = Just (index, val)
63+ match _ = Nothing
64+
65+objectUpdate :: Pattern () Value ([String], Value)
66+objectUpdate = mkPattern match
67+ where
68+ match (ObjectUpdate o ps) = Just (flip map ps $ \(key, val) -> key ++ " = " ++ prettyPrintValue val, o)
69+ match _ = Nothing
70+
71+app :: Pattern () Value (String, Value)
72+app = mkPattern match
73+ where
74+ match (App val args) = Just (intercalate "," (map prettyPrintValue args), val)
75+ match _ = Nothing
76+
77+lam :: Pattern () Value ([String], Value)
78+lam = mkPattern match
79+ where
80+ match (Abs args val) = Just (map show args, val)
81+ match _ = Nothing
82+
83+typed :: Pattern () Value (PolyType, Value)
84+typed = mkPattern match
85+ where
86+ match (TypedValue val ty) = Just (ty, val)
87+ match _ = Nothing
88+
89+unary :: UnaryOperator -> String -> Operator () Value String
90+unary op str = Wrap match (++)
91+ where
92+ match :: Pattern () Value (String, Value)
93+ match = mkPattern match'
94+ where
95+ match' (Unary op' val) | op' == op = Just (str, val)
96+ match' _ = Nothing
97+
98+binary :: BinaryOperator -> String -> Operator () Value String
99+binary op str = AssocR match (\v1 v2 -> v1 ++ " " ++ str ++ " " ++ v2)
100+ where
101+ match :: Pattern () Value (Value, Value)
102+ match = mkPattern match'
103+ where
104+ match' (Binary op' v1 v2) | op' == op = Just (v1, v2)
105+ match' _ = Nothing
106+
107+prettyPrintValue :: Value -> String
108+prettyPrintValue = fromMaybe (error "Incomplete pattern") . pattern matchValue ()
109+ where
110+ matchValue :: Pattern () Value String
111+ matchValue = buildPrettyPrinter operators (literals <+> fmap parens matchValue)
112+ operators :: OperatorTable () Value String
113+ operators =
114+ OperatorTable [ [ Wrap accessor $ \prop val -> val ++ "." ++ prop ]
115+ , [ Wrap objectUpdate $ \ps val -> val ++ "{ " ++ intercalate ", " ps ++ " }" ]
116+ , [ Wrap app $ \args val -> val ++ "(" ++ args ++ ")" ]
117+ , [ Split lam $ \args val -> "\\" ++ intercalate ", " args ++ " -> " ++ prettyPrintValue val ]
118+ , [ Wrap ifThenElse $ \(th, el) cond -> cond ++ " ? " ++ prettyPrintValue th ++ " : " ++ prettyPrintValue el ]
119+ , [ Wrap typed $ \ty val -> val ++ " :: " ++ prettyPrintType ty ]
120+ , [ AssocR indexer (\index val -> val ++ " !! " ++ index) ]
121+ , [ binary LessThan "<" ]
122+ , [ binary LessThanOrEqualTo "<=" ]
123+ , [ binary GreaterThan ">" ]
124+ , [ binary GreaterThanOrEqualTo ">=" ]
125+ , [ unary Not "!" ]
126+ , [ unary BitwiseNot "~" ]
127+ , [ unary Negate "-" ]
128+ , [ binary Multiply "*" ]
129+ , [ binary Divide "/" ]
130+ , [ binary Modulus "%" ]
131+ , [ binary Concat "++" ]
132+ , [ binary Add "+" ]
133+ , [ binary Subtract "-" ]
134+ , [ binary ShiftLeft "<<" ]
135+ , [ binary ShiftRight ">>" ]
136+ , [ binary ZeroFillShiftRight ">>>" ]
137+ , [ binary EqualTo "==" ]
138+ , [ binary NotEqualTo "!=" ]
139+ , [ binary BitwiseAnd "&" ]
140+ , [ binary BitwiseXor "^" ]
141+ , [ binary BitwiseOr "|" ]
142+ , [ binary And "&&" ]
143+ , [ binary Or "||" ]
144+ ]
145+
146+prettyPrintBinderAtom :: Pattern () Binder String
147+prettyPrintBinderAtom = mkPattern match
148+ where
149+ match :: Binder -> Maybe String
150+ match NullBinder = Just "_"
151+ match (StringBinder str) = Just $ show str
152+ match (NumberBinder num) = Just $ either show show num
153+ match (BooleanBinder True) = Just "true"
154+ match (BooleanBinder False) = Just "false"
155+ match (VarBinder ident) = Just $ show ident
156+ match (NullaryBinder ctor) = Just $ show ctor
157+ match (UnaryBinder ctor b) = Just $ show ctor ++ " " ++ prettyPrintBinder b
158+ match (ObjectBinder bs) = Just $ "{ " ++ intercalate ", " (map (uncurry prettyPrintObjectPropertyBinder) bs) ++ " }"
159+ match (ArrayBinder bs) = Just $ "[ " ++ intercalate ", " (map prettyPrintBinder bs) ++ " ]"
160+ match (NamedBinder ident binder) = Just $ show ident ++ "@" ++ prettyPrintBinder binder
161+ match (GuardedBinder cond binder) = Just $ prettyPrintBinder binder ++ " | " ++ prettyPrintValue cond
162+ match _ = Nothing
163+
164+prettyPrintBinder :: Binder -> String
165+prettyPrintBinder = fromMaybe (error "Incomplete pattern") . pattern matchBinder ()
166+ where
167+ matchBinder :: Pattern () Binder String
168+ matchBinder = buildPrettyPrinter operators (prettyPrintBinderAtom <+> fmap parens matchBinder)
169+ operators :: OperatorTable () Binder String
170+ operators =
171+ OperatorTable [ [ AssocR matchConsBinder (\b1 b2 -> b1 ++ " : " ++ b2) ] ]
172+
173+matchConsBinder :: Pattern () Binder (Binder, Binder)
174+matchConsBinder = mkPattern match'
175+ where
176+ match' (ConsBinder b1 b2) = Just (b1, b2)
177+ match' _ = Nothing
178+
179+prettyPrintObjectPropertyBinder :: String -> Binder -> String
180+prettyPrintObjectPropertyBinder key binder = key ++ ": " ++ prettyPrintBinder binder
181+
182+prettyPrintObjectProperty :: String -> Value -> String
183+prettyPrintObjectProperty key value = key ++ ": " ++ prettyPrintValue value
184+
185+prettyPrintStatement :: Statement -> String
186+prettyPrintStatement (VariableIntroduction ident value) = "var " ++ show ident ++ " = " ++ prettyPrintValue value
187+prettyPrintStatement (Assignment target value) = show target ++ " = " ++ prettyPrintValue value
188+prettyPrintStatement (While cond sts) = "while " ++ prettyPrintValue cond ++ ": {" ++ intercalate ";" (map prettyPrintStatement sts) ++ " }"
189+prettyPrintStatement (For ident start end sts) = "for " ++ show ident
190+ ++ " <- " ++ prettyPrintValue start
191+ ++ " until " ++ prettyPrintValue end ++ ": {"
192+ ++ intercalate "; " (map prettyPrintStatement sts) ++ " }"
193+prettyPrintStatement (ForEach ident arr sts) = "foreach " ++ show ident
194+ ++ " in " ++ prettyPrintValue arr ++ ": {"
195+ ++ intercalate "; " (map prettyPrintStatement sts) ++ " }"
196+prettyPrintStatement (If ifst) = prettyPrintIfStatement ifst
197+prettyPrintStatement (ValueStatement val) = prettyPrintValue val
198+prettyPrintStatement (Return value) = "return " ++ prettyPrintValue value
199+
200+prettyPrintIfStatement :: IfStatement -> String
201+prettyPrintIfStatement (IfStatement cond thens elst) =
202+ "if "
203+ ++ prettyPrintValue cond ++ ": {"
204+ ++ intercalate "; " (map prettyPrintStatement thens) ++ " }"
205+ ++ maybe "" prettyPrintElseStatement elst
206+
207+prettyPrintElseStatement :: ElseStatement -> String
208+prettyPrintElseStatement (Else sts) = "else: {" ++ intercalate "; " (map prettyPrintStatement sts) ++ " }"
209+prettyPrintElseStatement (ElseIf ifst) = "else " ++ prettyPrintIfStatement ifst
src/Language/PureScript/TypeChecker.hsnew file+138
@@ -0,0 +1,138 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.PureScript.TypeChecker
4+-- Copyright : (c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+{-# LANGUAGE FlexibleInstances #-}
16+
17+module Language.PureScript.TypeChecker (
18+ module T,
19+ typeCheckAll
20+) where
21+
22+import Language.PureScript.TypeChecker.Monad as T
23+import Language.PureScript.TypeChecker.Kinds as T
24+import Language.PureScript.TypeChecker.Types as T
25+import Language.PureScript.TypeChecker.Synonyms as T
26+
27+import Data.Maybe
28+import qualified Data.Map as M
29+
30+import Language.PureScript.Values
31+import Language.PureScript.Types
32+import Language.PureScript.Names
33+import Language.PureScript.Kinds
34+import Language.PureScript.Declarations
35+
36+import Control.Monad.State
37+import Control.Monad.Error
38+
39+typeCheckAll :: [Declaration] -> Check ()
40+typeCheckAll [] = return ()
41+typeCheckAll (DataDeclaration name args dctors : rest) = do
42+ rethrow (("Error in type constructor " ++ show name ++ ":\n") ++) $ do
43+ env <- getEnv
44+ modulePath <- checkModulePath `fmap` get
45+ guardWith (show name ++ " is already defined") $ not $ M.member (modulePath, name) (types env)
46+ ctorKind <- kindsOf (Just name) args (mapMaybe snd dctors)
47+ putEnv $ env { types = M.insert (modulePath, name) (ctorKind, Data) (types env) }
48+ forM_ dctors $ \(dctor, maybeTy) ->
49+ rethrow (("Error in data constructor " ++ show name ++ ":\n") ++) $ do
50+ env' <- getEnv
51+ guardWith (show dctor ++ " is already defined") $ not $ M.member (modulePath, dctor) (dataConstructors env')
52+ let retTy = foldl TypeApp (TypeConstructor (Qualified modulePath name)) (map TypeVar args)
53+ let dctorTy = maybe retTy (\ty -> Function [ty] retTy) maybeTy
54+ let polyType = mkForAll args dctorTy
55+ putEnv $ env' { dataConstructors = M.insert (modulePath, dctor) polyType (dataConstructors env') }
56+ typeCheckAll rest
57+typeCheckAll (TypeSynonymDeclaration name args ty : rest) = do
58+ rethrow (("Error in type synonym " ++ show name ++ ":\n") ++) $ do
59+ env <- getEnv
60+ modulePath <- checkModulePath `fmap` get
61+ guardWith (show name ++ " is already defined") $ not $ M.member (modulePath, name) (types env)
62+ kind <- kindsOf (Just name) args [ty]
63+ putEnv $ env { types = M.insert (modulePath, name) (kind, TypeSynonym) (types env)
64+ , typeSynonyms = M.insert (modulePath, name) (args, ty) (typeSynonyms env) }
65+ typeCheckAll rest
66+typeCheckAll (TypeDeclaration name ty : ValueDeclaration name' val : rest) | name == name' =
67+ typeCheckAll (ValueDeclaration name (TypedValue val ty) : rest)
68+typeCheckAll (TypeDeclaration name _ : _) = throwError $ "Orphan type declaration for " ++ show name
69+typeCheckAll (ValueDeclaration name val : rest) = do
70+ rethrow (("Error in declaration " ++ show name ++ ":\n") ++) $ do
71+ env <- getEnv
72+ modulePath <- checkModulePath `fmap` get
73+ case M.lookup (modulePath, name) (names env) of
74+ Just _ -> throwError $ show name ++ " is already defined"
75+ Nothing -> do
76+ ty <- typeOf (Just name) val
77+ putEnv (env { names = M.insert (modulePath, name) (ty, Value) (names env) })
78+ typeCheckAll rest
79+typeCheckAll (ExternDataDeclaration name kind : rest) = do
80+ env <- getEnv
81+ modulePath <- checkModulePath `fmap` get
82+ guardWith (show name ++ " is already defined") $ not $ M.member (modulePath, name) (types env)
83+ putEnv $ env { types = M.insert (modulePath, name) (kind, TypeSynonym) (types env) }
84+ typeCheckAll rest
85+typeCheckAll (ExternMemberDeclaration member name ty : rest) = do
86+ rethrow (("Error in foreign import member declaration " ++ show name ++ ":\n") ++) $ do
87+ env <- getEnv
88+ modulePath <- checkModulePath `fmap` get
89+ kind <- kindOf ty
90+ guardWith "Expected kind *" $ kind == Star
91+ case M.lookup (modulePath, name) (names env) of
92+ Just _ -> throwError $ show name ++ " is already defined"
93+ Nothing -> case ty of
94+ _ | isSingleArgumentFunction ty -> do
95+ putEnv (env { names = M.insert (modulePath, name) (ty, Extern) (names env)
96+ , members = M.insert (modulePath, name) member (members env) })
97+ | otherwise -> throwError "Foreign member declarations must have function types, with an single argument."
98+ typeCheckAll rest
99+ where
100+ isSingleArgumentFunction (Function [_] _) = True
101+ isSingleArgumentFunction (ForAll _ t) = isSingleArgumentFunction t
102+ isSingleArgumentFunction _ = False
103+typeCheckAll (ExternDeclaration name ty : rest) = do
104+ rethrow (("Error in foreign import declaration " ++ show name ++ ":\n") ++) $ do
105+ env <- getEnv
106+ modulePath <- checkModulePath `fmap` get
107+ kind <- kindOf ty
108+ guardWith "Expected kind *" $ kind == Star
109+ case M.lookup (modulePath, name) (names env) of
110+ Just _ -> throwError $ show name ++ " is already defined"
111+ Nothing -> putEnv (env { names = M.insert (modulePath, name) (ty, Extern) (names env) })
112+ typeCheckAll rest
113+typeCheckAll (FixityDeclaration _ name : rest) = do
114+ typeCheckAll rest
115+ env <- getEnv
116+ modulePath <- checkModulePath `fmap` get
117+ guardWith ("Fixity declaration with no binding: " ++ name) $ M.member (modulePath, Op name) $ names env
118+typeCheckAll (ModuleDeclaration name decls : rest) = do
119+ withModule name $ typeCheckAll decls
120+ typeCheckAll rest
121+typeCheckAll (ImportDeclaration modulePath idents : rest) = do
122+ env <- getEnv
123+ currentModule <- checkModulePath `fmap` get
124+ rethrow errorMessage $ do
125+ guardWith ("Module " ++ show modulePath ++ " does not exist") $ moduleExists env
126+ case idents of
127+ Nothing -> bindIdents (map snd $ filterModule env) currentModule env
128+ Just idents' -> bindIdents idents' currentModule env
129+ typeCheckAll rest
130+ where errorMessage = (("Error in import declaration " ++ show modulePath ++ ":\n") ++)
131+ filterModule = filter ((== modulePath) . fst) . M.keys . names
132+ moduleExists env = not $ null $ filterModule env
133+ bindIdents idents' currentModule env =
134+ forM_ idents' $ \ident -> do
135+ guardWith (show currentModule ++ "." ++ show ident ++ " is already defined") $ (currentModule, ident) `M.notMember` names env
136+ case (modulePath, ident) `M.lookup` names env of
137+ Just (pt, _) -> modifyEnv (\e -> e { names = M.insert (currentModule, ident) (pt, Alias modulePath ident) (names e) })
138+ Nothing -> throwError (show modulePath ++ "." ++ show ident ++ " is undefined")
src/Language/PureScript/TypeChecker/Kinds.hsnew file+125
@@ -0,0 +1,125 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.PureScript.TypeChecker.Kinds
4+-- Copyright : (c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+{-# LANGUAGE DeriveDataTypeable #-}
16+
17+module Language.PureScript.TypeChecker.Kinds (
18+ kindsOf,
19+ kindOf
20+) where
21+
22+import Language.PureScript.Types
23+import Language.PureScript.Kinds
24+import Language.PureScript.Names
25+import Language.PureScript.TypeChecker.Monad
26+import Language.PureScript.Pretty
27+import Language.PureScript.Unknown
28+
29+import Control.Monad.State
30+import Control.Monad.Error
31+
32+import Control.Applicative
33+
34+import qualified Data.Map as M
35+
36+instance Unifiable Kind where
37+ unknown = KUnknown
38+ isUnknown (KUnknown u) = Just u
39+ isUnknown _ = Nothing
40+ KUnknown u1 ~~ KUnknown u2 | u1 == u2 = return ()
41+ KUnknown u ~~ k = replace u k
42+ k ~~ KUnknown u = replace u k
43+ Star ~~ Star = return ()
44+ Row ~~ Row = return ()
45+ FunKind k1 k2 ~~ FunKind k3 k4 = do
46+ k1 ~~ k3
47+ k2 ~~ k4
48+ k1 ~~ k2 = throwError $ "Cannot unify " ++ prettyPrintKind k1 ++ " with " ++ prettyPrintKind k2 ++ "."
49+ apply s (KUnknown u) = runSubstitution s u
50+ apply s (FunKind k1 k2) = FunKind (apply s k1) (apply s k2)
51+ apply _ k = k
52+ unknowns (KUnknown (Unknown u)) = [u]
53+ unknowns (FunKind k1 k2) = unknowns k1 ++ unknowns k2
54+ unknowns _ = []
55+
56+kindOf :: Type -> Check Kind
57+kindOf ty = fmap (\(k, _, _) -> k) . runSubst $ starIfUnknown <$> infer Nothing M.empty ty
58+
59+kindsOf :: Maybe ProperName -> [String] -> [Type] -> Check Kind
60+kindsOf name args ts = fmap (starIfUnknown . (\(k, _, _) -> k)) . runSubst $ do
61+ tyCon <- fresh
62+ kargs <- replicateM (length args) fresh
63+ ks <- inferAll (fmap (\pn -> (pn, tyCon)) name) (M.fromList (zip args kargs)) ts
64+ tyCon ~~ foldr FunKind Star kargs
65+ forM_ ks $ \k -> k ~~ Star
66+ return tyCon
67+
68+starIfUnknown :: Kind -> Kind
69+starIfUnknown (KUnknown _) = Star
70+starIfUnknown (FunKind k1 k2) = FunKind (starIfUnknown k1) (starIfUnknown k2)
71+starIfUnknown k = k
72+
73+inferAll :: Maybe (ProperName, Kind) -> M.Map String Kind -> [Type] -> Subst [Kind]
74+inferAll name m = mapM (infer name m)
75+
76+infer :: Maybe (ProperName, Kind) -> M.Map String Kind -> Type -> Subst Kind
77+infer name m (Array t) = do
78+ k <- infer name m t
79+ k ~~ Star
80+ return Star
81+infer name m (Object row) = do
82+ k <- inferRow name m row
83+ k ~~ Row
84+ return Star
85+infer name m (Function args ret) = do
86+ ks <- inferAll name m args
87+ k <- infer name m ret
88+ k ~~ Star
89+ forM ks (~~ Star)
90+ return Star
91+infer _ m (TypeVar v) =
92+ case M.lookup v m of
93+ Just k -> return k
94+ Nothing -> throwError $ "Unbound type variable " ++ v
95+infer (Just (name, k)) _ (TypeConstructor (Qualified (ModulePath []) pn)) | name == pn = return k
96+infer _ _ (TypeConstructor v) = do
97+ env <- liftCheck getEnv
98+ modulePath <- checkModulePath `fmap` get
99+ case M.lookup (qualify modulePath v) (types env) of
100+ Nothing -> throwError $ "Unknown type constructor '" ++ show v ++ "'"
101+ Just (kind, _) -> return kind
102+infer name m (TypeApp t1 t2) = do
103+ k0 <- fresh
104+ k1 <- infer name m t1
105+ k2 <- infer name m t2
106+ k1 ~~ FunKind k2 k0
107+ return k0
108+infer name m (ForAll ident ty) = do
109+ k <- fresh
110+ infer name (M.insert ident k m) ty
111+infer _ _ _ = return Star
112+
113+inferRow :: Maybe (ProperName, Kind) -> M.Map String Kind -> Row -> Subst Kind
114+inferRow _ m (RowVar v) = do
115+ case M.lookup v m of
116+ Just k -> return k
117+ Nothing -> throwError $ "Unbound row variable " ++ v
118+inferRow _ _ REmpty = return Row
119+inferRow name m (RCons _ ty row) = do
120+ k1 <- infer name m ty
121+ k2 <- inferRow name m row
122+ k1 ~~ Star
123+ k2 ~~ Row
124+ return Row
125+inferRow _ _ _ = error "Invalid row in inferRow"
src/Language/PureScript/TypeChecker/Monad.hsnew file+182
@@ -0,0 +1,182 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.PureScript.TypeChecker.Monad
4+-- Copyright : (c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+{-# LANGUAGE GeneralizedNewtypeDeriving, FlexibleInstances, RankNTypes, DeriveDataTypeable,
16+ GADTs, StandaloneDeriving, MultiParamTypeClasses, FlexibleContexts #-}
17+
18+module Language.PureScript.TypeChecker.Monad where
19+
20+import Language.PureScript.Types
21+import Language.PureScript.Kinds
22+import Language.PureScript.Names
23+import Language.PureScript.Unknown
24+
25+import Data.Data
26+import Data.Maybe
27+import Data.Monoid
28+import Control.Applicative
29+import Control.Monad.State
30+import Control.Monad.Error
31+
32+import qualified Data.Map as M
33+
34+data NameKind = Value | Extern | Alias ModulePath Ident | LocalVariable deriving Show
35+
36+data TypeDeclarationKind = Data | ExternData | TypeSynonym deriving Show
37+
38+data Environment = Environment
39+ { names :: M.Map (ModulePath, Ident) (Type, NameKind)
40+ , types :: M.Map (ModulePath, ProperName) (Kind, TypeDeclarationKind)
41+ , dataConstructors :: M.Map (ModulePath, ProperName) Type
42+ , typeSynonyms :: M.Map (ModulePath, ProperName) ([String], Type)
43+ , members :: M.Map (ModulePath, Ident) String
44+ } deriving (Show)
45+
46+emptyEnvironment :: Environment
47+emptyEnvironment = Environment M.empty M.empty M.empty M.empty M.empty
48+
49+bindNames :: (MonadState CheckState m) => M.Map (ModulePath, Ident) (Type, NameKind) -> m a -> m a
50+bindNames newNames action = do
51+ orig <- get
52+ modify $ \st -> st { checkEnv = (checkEnv st) { names = newNames `M.union` (names . checkEnv $ st) } }
53+ a <- action
54+ modify $ \st -> st { checkEnv = (checkEnv st) { names = names . checkEnv $ orig } }
55+ return a
56+
57+bindLocalVariables :: (Functor m, MonadState CheckState m) => [(Ident, Type)] -> m a -> m a
58+bindLocalVariables bindings action = do
59+ modulePath <- checkModulePath `fmap` get
60+ bindNames (M.fromList $ flip map bindings $ \(name, ty) -> ((modulePath, name), (ty, LocalVariable))) action
61+
62+lookupVariable :: (Functor m, MonadState CheckState m, MonadError String m) => Qualified Ident -> m Type
63+lookupVariable var = do
64+ env <- getEnv
65+ modulePath <- checkModulePath <$> get
66+ let tries = map (First . flip M.lookup (names env)) (nameResolution modulePath var)
67+ case getFirst (mconcat tries) of
68+ Nothing -> throwError $ show var ++ " is undefined"
69+ Just (ty, _) -> return ty
70+
71+data AnyUnifiable where
72+ AnyUnifiable :: forall t. (Unifiable t) => t -> AnyUnifiable
73+
74+data CheckState = CheckState { checkEnv :: Environment
75+ , checkNextVar :: Int
76+ , checkModulePath :: ModulePath
77+ }
78+
79+newtype Check a = Check { unCheck :: StateT CheckState (Either String) a }
80+ deriving (Functor, Monad, Applicative, MonadPlus, MonadState CheckState, MonadError String)
81+
82+getEnv :: (Functor m, MonadState CheckState m) => m Environment
83+getEnv = checkEnv <$> get
84+
85+putEnv :: (MonadState CheckState m) => Environment -> m ()
86+putEnv env = modify (\s -> s { checkEnv = env })
87+
88+modifyEnv :: (MonadState CheckState m) => (Environment -> Environment) -> m ()
89+modifyEnv f = modify (\s -> s { checkEnv = f (checkEnv s) })
90+
91+runCheck :: Check a -> Either String (a, Environment)
92+runCheck c = do
93+ (a, s) <- flip runStateT (CheckState emptyEnvironment 0 global) $ unCheck c
94+ return (a, checkEnv s)
95+
96+guardWith :: (MonadError e m) => e -> Bool -> m ()
97+guardWith _ True = return ()
98+guardWith e False = throwError e
99+
100+rethrow :: (MonadError e m) => (e -> e) -> m a -> m a
101+rethrow f = flip catchError $ \e -> throwError (f e)
102+
103+withModule :: ProperName -> Check a -> Check a
104+withModule name act = do
105+ original <- checkModulePath `fmap` get
106+ modify $ \s -> s { checkModulePath = subModule (checkModulePath s) name }
107+ a <- act
108+ modify $ \s -> s { checkModulePath = original }
109+ return a
110+
111+newtype Substitution = Substitution { runSubstitution :: forall t. (Unifiable t) => Unknown t -> t }
112+
113+instance Monoid Substitution where
114+ mempty = Substitution unknown
115+ s1 `mappend` s2 = Substitution $ \u -> apply s1 (apply s2 (unknown u))
116+
117+data SubstState = SubstState { substSubst :: Substitution
118+ , substFutureEscapeChecks :: [AnyUnifiable] }
119+
120+newtype Subst a = Subst { unSubst :: StateT SubstState Check a }
121+ deriving (Functor, Monad, Applicative, MonadPlus)
122+
123+instance MonadState CheckState Subst where
124+ get = Subst . lift $ get
125+ put = Subst . lift . put
126+
127+deriving instance MonadError String Subst
128+
129+liftCheck :: Check a -> Subst a
130+liftCheck = Subst . lift
131+
132+runSubst :: (Unifiable a) => Subst a -> Check (a, Substitution, [AnyUnifiable])
133+runSubst subst = do
134+ (a, s) <- flip runStateT (SubstState mempty []) . unSubst $ subst
135+ return (apply (substSubst s) a, substSubst s, substFutureEscapeChecks s)
136+
137+substituteWith :: (Typeable t) => (Unknown t -> t) -> Substitution
138+substituteWith f = Substitution $ \u -> fromMaybe (unknown u) $ do
139+ u1 <- cast u
140+ cast (f u1)
141+
142+substituteOne :: (Unifiable t) => Unknown t -> t -> Substitution
143+substituteOne u t = substituteWith $ \u1 ->
144+ case u1 of
145+ u2 | u2 == u -> t
146+ | otherwise -> unknown u2
147+
148+replace :: (Unifiable t) => Unknown t -> t -> Subst ()
149+replace u t' = do
150+ sub <- substSubst <$> Subst get
151+ let t = apply sub t'
152+ occursCheck u t
153+ let current = apply sub $ unknown u
154+ case isUnknown current of
155+ Just u1 | u1 == u -> return ()
156+ _ -> current ~~ t
157+ Subst . modify $ \s -> s { substSubst = substituteOne u t <> substSubst s }
158+
159+class (Typeable t, Data t, Show t) => Unifiable t where
160+ unknown :: Unknown t -> t
161+ (~~) :: t -> t -> Subst ()
162+ isUnknown :: t -> Maybe (Unknown t)
163+ apply :: Substitution -> t -> t
164+ unknowns :: t -> [Int]
165+
166+occursCheck :: (Unifiable t) => Unknown s -> t -> Subst ()
167+occursCheck (Unknown u) t =
168+ case isUnknown t of
169+ Nothing -> guardWith "Occurs check fails" (u `notElem` unknowns t)
170+ _ -> return ()
171+
172+fresh' :: Subst Int
173+fresh' = do
174+ n <- checkNextVar <$> get
175+ modify $ \s -> s { checkNextVar = succ (checkNextVar s) }
176+ return n
177+
178+fresh :: (Unifiable t) => Subst t
179+fresh = unknown . Unknown <$> fresh'
180+
181+escapeCheckLater :: (Unifiable t) => t -> Subst ()
182+escapeCheckLater t = Subst . modify $ \s -> s { substFutureEscapeChecks = AnyUnifiable t : substFutureEscapeChecks s }
src/Language/PureScript/TypeChecker/Synonyms.hsnew file+48
@@ -0,0 +1,48 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.PureScript.TypeChecker.Synonyms
4+-- Copyright : (c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+module Language.PureScript.TypeChecker.Synonyms (
16+ saturateTypeSynonym,
17+ saturateAllTypeSynonyms
18+) where
19+
20+import Language.PureScript.Types
21+import Language.PureScript.Names
22+
23+import Data.Maybe (fromMaybe)
24+import Data.Data
25+import Data.Generics
26+import Data.Generics.Extras
27+import Control.Monad.Writer
28+import Control.Monad.Error
29+
30+buildTypeSubstitution :: Qualified ProperName -> Int -> Type -> Either String (Maybe Type)
31+buildTypeSubstitution name n = go n []
32+ where
33+ go :: Int -> [Type] -> Type -> Either String (Maybe Type)
34+ go 0 args (TypeConstructor ctor) | name == ctor = return (Just $ SaturatedTypeSynonym ctor args)
35+ go m _ (TypeConstructor ctor) | m > 0 && name == ctor = throwError $ "Partially applied type synonym " ++ show name
36+ go m args (TypeApp f arg) = go (m - 1) (arg:args) f
37+ go _ _ _ = return Nothing
38+
39+saturateTypeSynonym :: (Data d) => Qualified ProperName -> Int -> d -> Either String d
40+saturateTypeSynonym name n = everywhereM' (mkM replace)
41+ where
42+ replace t = fmap (fromMaybe t) $ buildTypeSubstitution name n t
43+
44+saturateAllTypeSynonyms :: (Data d) => [(Qualified ProperName, Int)] -> d -> Either String d
45+saturateAllTypeSynonyms syns d = foldM (\result (name, n) -> saturateTypeSynonym name n result) d syns
46+
47+
48+
src/Language/PureScript/TypeChecker/Types.hsnew file+753
@@ -0,0 +1,753 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.PureScript.TypeChecker.Types
4+-- Copyright : (c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts #-}
16+
17+module Language.PureScript.TypeChecker.Types (
18+ typeOf
19+) where
20+
21+import Data.List
22+import Data.Maybe (fromMaybe)
23+import qualified Data.Data as D
24+import Data.Generics
25+ (mkT, something, everywhere, everywhereBut, mkQ, extQ)
26+
27+import Language.PureScript.Values
28+import Language.PureScript.Types
29+import Language.PureScript.Kinds
30+import Language.PureScript.Names
31+import Language.PureScript.TypeChecker.Monad
32+import Language.PureScript.TypeChecker.Kinds
33+import Language.PureScript.TypeChecker.Synonyms
34+import Language.PureScript.Pretty
35+import Language.PureScript.Unknown
36+
37+import Control.Monad.State
38+import Control.Monad.Error
39+
40+import Control.Applicative
41+import Control.Arrow (Arrow(..))
42+
43+import qualified Data.Map as M
44+
45+instance Unifiable Type where
46+ unknown = TUnknown
47+ isUnknown (TUnknown u) = Just u
48+ isUnknown _ = Nothing
49+ (~~) = unifyTypes
50+ apply s (TUnknown u) = runSubstitution s u
51+ apply s (SaturatedTypeSynonym name tys) = SaturatedTypeSynonym name $ map (apply s) tys
52+ apply s (ForAll idents ty) = ForAll idents $ apply s ty
53+ apply s (Array t) = Array (apply s t)
54+ apply s (Object r) = Object (apply s r)
55+ apply s (Function args ret) = Function (map (apply s) args) (apply s ret)
56+ apply s (TypeApp t1 t2) = TypeApp (apply s t1) (apply s t2)
57+ apply _ t = t
58+ unknowns (TUnknown (Unknown u)) = [u]
59+ unknowns (SaturatedTypeSynonym _ tys) = concatMap unknowns tys
60+ unknowns (ForAll _ ty) = unknowns ty
61+ unknowns (Array t) = unknowns t
62+ unknowns (Object r) = unknowns r
63+ unknowns (Function args ret) = concatMap unknowns args ++ unknowns ret
64+ unknowns (TypeApp t1 t2) = unknowns t1 ++ unknowns t2
65+ unknowns _ = []
66+
67+instance Unifiable Row where
68+ unknown = RUnknown
69+ isUnknown (RUnknown u) = Just u
70+ isUnknown _ = Nothing
71+ r1 ~~ r2 =
72+ let
73+ (s1, r1') = rowToList r1
74+ (s2, r2') = rowToList r2
75+ int = [ (t1, t2) | (name, t1) <- s1, (name', t2) <- s2, name == name' ]
76+ sd1 = [ (name, t1) | (name, t1) <- s1, name `notElem` map fst s2 ]
77+ sd2 = [ (name, t2) | (name, t2) <- s2, name `notElem` map fst s1 ]
78+ in do
79+ forM_ int (uncurry (~~))
80+ unifyRows sd1 r1' sd2 r2'
81+ where
82+ unifyRows :: [(String, Type)] -> Row -> [(String, Type)] -> Row -> Subst ()
83+ unifyRows [] (RUnknown u) sd r = replace u (rowFromList (sd, r))
84+ unifyRows sd r [] (RUnknown u) = replace u (rowFromList (sd, r))
85+ unifyRows ((name, ty):row) r others u@(RUnknown un) = do
86+ occursCheck un ty
87+ forM row $ \(_, t) -> occursCheck un t
88+ u' <- fresh
89+ u ~~ RCons name ty u'
90+ unifyRows row r others u'
91+ unifyRows [] REmpty [] REmpty = return ()
92+ unifyRows [] (RowVar v1) [] (RowVar v2) | v1 == v2 = return ()
93+ unifyRows [] (RSkolem s1) [] (RSkolem s2) | s1 == s2 = return ()
94+ unifyRows sd3 r3 sd4 r4 = throwError $ "Cannot unify " ++ prettyPrintRow (rowFromList (sd3, r3)) ++ " with " ++ prettyPrintRow (rowFromList (sd4, r4)) ++ "."
95+ apply s (RUnknown u) = runSubstitution s u
96+ apply s (RCons name ty r) = RCons name (apply s ty) (apply s r)
97+ apply _ r = r
98+ unknowns (RUnknown (Unknown u)) = [u]
99+ unknowns (RCons _ ty r) = unknowns ty ++ unknowns r
100+ unknowns _ = []
101+
102+unifyTypes :: Type -> Type -> Subst ()
103+unifyTypes t1 t2 = rethrow (\e -> "Error unifying type " ++ prettyPrintType t1 ++ " with type " ++ prettyPrintType t2 ++ ":\n" ++ e) $ do
104+ unifyTypes' t1 t2
105+ where
106+ unifyTypes' (TUnknown u1) (TUnknown u2) | u1 == u2 = return ()
107+ unifyTypes' (TUnknown u) t = replace u t
108+ unifyTypes' t (TUnknown u) = replace u t
109+ unifyTypes' (SaturatedTypeSynonym name1 args1) (SaturatedTypeSynonym name2 args2)
110+ | name1 == name2 = zipWithM_ unifyTypes args1 args2
111+ unifyTypes' (SaturatedTypeSynonym name args) ty = do
112+ ty1 <- expandTypeSynonym name args
113+ ty1 `unifyTypes` ty
114+ unifyTypes' ty s@(SaturatedTypeSynonym _ _) = s `unifyTypes` ty
115+ unifyTypes' (ForAll ident1 ty1) (ForAll ident2 ty2) = do
116+ sk <- skolemize ident1 ty1
117+ replaced <- replaceVarWithUnknown ident2 ty2
118+ sk `unifyTypes` replaced
119+ unifyTypes' (ForAll ident ty1) ty2 = do
120+ sk <- skolemize ident ty1
121+ sk `unifyTypes` ty2
122+ unifyTypes' ty f@(ForAll _ _) = f `unifyTypes` ty
123+ unifyTypes' Number Number = return ()
124+ unifyTypes' String String = return ()
125+ unifyTypes' Boolean Boolean = return ()
126+ unifyTypes' (Array s) (Array t) = s `unifyTypes` t
127+ unifyTypes' (Object row1) (Object row2) = row1 ~~ row2
128+ unifyTypes' (Function args1 ret1) (Function args2 ret2) = do
129+ guardWith "Function applied to incorrect number of args" $ length args1 == length args2
130+ zipWithM_ unifyTypes args1 args2
131+ ret1 `unifyTypes` ret2
132+ unifyTypes' (TypeVar v1) (TypeVar v2) | v1 == v2 = return ()
133+ unifyTypes' (TypeConstructor c1) (TypeConstructor c2) = do
134+ modulePath <- checkModulePath `fmap` get
135+ guardWith ("Cannot unify " ++ show c1 ++ " with " ++ show c2 ++ ".") (qualify modulePath c1 == qualify modulePath c2)
136+ unifyTypes' (TypeApp t3 t4) (TypeApp t5 t6) = do
137+ t3 `unifyTypes` t5
138+ t4 `unifyTypes` t6
139+ unifyTypes' (Skolem s1) (Skolem s2) | s1 == s2 = return ()
140+ unifyTypes' t3 t4 = throwError $ "Cannot unify " ++ prettyPrintType t3 ++ " with " ++ prettyPrintType t4 ++ "."
141+
142+isFunction :: Value -> Bool
143+isFunction (Abs _ _) = True
144+isFunction (TypedValue untyped _) = isFunction untyped
145+isFunction _ = False
146+
147+typeOf :: Maybe Ident -> Value -> Check Type
148+typeOf name val = do
149+ (ty, sub, checks) <- runSubst $ case name of
150+ Just ident | isFunction val ->
151+ case val of
152+ TypedValue value ty -> do
153+ kind <- liftCheck $ kindOf ty
154+ guardWith ("Expected type of kind *, was " ++ prettyPrintKind kind) $ kind == Star
155+ ty' <- replaceAllTypeSynonyms ty
156+ modulePath <- checkModulePath <$> get
157+ bindNames (M.singleton (modulePath, ident) (ty, LocalVariable)) $ check value ty'
158+ return ty'
159+ _ -> do
160+ me <- fresh
161+ modulePath <- checkModulePath <$> get
162+ ty <- bindNames (M.singleton (modulePath, ident) (me, LocalVariable)) $ infer val
163+ ty ~~ me
164+ return ty
165+ _ -> infer val
166+ escapeCheck checks ty sub
167+ skolemEscapeCheck ty
168+ return $ varIfUnknown $ desaturateAllTypeSynonyms $ setifyAll ty
169+
170+escapeCheck :: [AnyUnifiable] -> Type -> Substitution -> Check ()
171+escapeCheck checks ty sub =
172+ let
173+ visibleUnknowns = nub $ unknowns ty
174+ in
175+ forM_ checks $ \c -> case c of
176+ AnyUnifiable t -> do
177+ let unsolvedUnknowns = nub . unknowns $ apply sub t
178+ guardWith "Escape check fails" $ null $ unsolvedUnknowns \\ visibleUnknowns
179+
180+skolemEscapeCheck :: Type -> Check ()
181+skolemEscapeCheck ty =
182+ case something (extQ (mkQ Nothing findSkolems) findRSkolems) ty of
183+ Nothing -> return ()
184+ Just _ -> throwError "Skolem variables cannot escape. Consider adding a type signature."
185+ where
186+ findSkolems (Skolem _) = return ()
187+ findSkolems _ = mzero
188+ findRSkolems (RSkolem _) = return ()
189+ findRSkolems _ = mzero
190+
191+setify :: Row -> Row
192+setify = rowFromList . first (M.toList . M.fromList) . rowToList
193+
194+setifyAll :: (D.Data d) => d -> d
195+setifyAll = everywhere (mkT setify)
196+
197+varIfUnknown :: Type -> Type
198+varIfUnknown ty =
199+ let unks = nub $ unknowns ty
200+ toName = (:) 't' . show
201+ ty' = everywhere (mkT rowToVar) . everywhere (mkT typeToVar) $ ty
202+ typeToVar :: Type -> Type
203+ typeToVar (TUnknown (Unknown u)) = TypeVar (toName u)
204+ typeToVar t = t
205+ rowToVar :: Row -> Row
206+ rowToVar (RUnknown (Unknown u)) = RowVar (toName u)
207+ rowToVar t = t
208+ in mkForAll (sort . map toName $ unks) ty'
209+
210+replaceAllTypeVars :: (D.Data d) => [(String, Type)] -> d -> d
211+replaceAllTypeVars = foldl' (\f (name, ty) -> replaceTypeVars name ty . f) id
212+
213+replaceTypeVars :: (D.Data d) => String -> Type -> d -> d
214+replaceTypeVars name t = everywhereBut (mkQ False isShadowed) (mkT replaceTypeVar)
215+ where
216+ replaceTypeVar (TypeVar v) | v == name = t
217+ replaceTypeVar other = other
218+ isShadowed (ForAll v _) | v == name = True
219+ isShadowed _ = False
220+
221+replaceRowVars :: (D.Data d) => String -> Row -> d -> d
222+replaceRowVars name r = everywhere (mkT replaceRowVar)
223+ where
224+ replaceRowVar (RowVar v) | v == name = r
225+ replaceRowVar other = other
226+
227+replaceAllVarsWithUnknowns :: Type -> Subst Type
228+replaceAllVarsWithUnknowns (ForAll ident ty) = replaceVarWithUnknown ident ty >>= replaceAllVarsWithUnknowns
229+replaceAllVarsWithUnknowns ty = return ty
230+
231+replaceVarWithUnknown :: String -> Type -> Subst Type
232+replaceVarWithUnknown ident ty = do
233+ tu <- fresh
234+ ru <- fresh
235+ return $ replaceRowVars ident ru . replaceTypeVars ident tu $ ty
236+
237+replaceAllTypeSynonyms :: (Functor m, MonadState CheckState m, MonadError String m) => (D.Data d) => d -> m d
238+replaceAllTypeSynonyms d = do
239+ env <- getEnv
240+ let syns = map (\((path, name), (args, _)) -> (Qualified path name, length args)) . M.toList $ typeSynonyms env
241+ either throwError return $ saturateAllTypeSynonyms syns d
242+
243+desaturateAllTypeSynonyms :: (D.Data d) => d -> d
244+desaturateAllTypeSynonyms = everywhere (mkT replaceSaturatedTypeSynonym)
245+ where
246+ replaceSaturatedTypeSynonym (SaturatedTypeSynonym name args) = foldl TypeApp (TypeConstructor name) args
247+ replaceSaturatedTypeSynonym t = t
248+
249+expandTypeSynonym :: Qualified ProperName -> [Type] -> Subst Type
250+expandTypeSynonym name args = do
251+ env <- getEnv
252+ modulePath <- checkModulePath `fmap` get
253+ case M.lookup (qualify modulePath name) (typeSynonyms env) of
254+ Just (synArgs, body) -> return $ replaceAllTypeVars (zip synArgs args) body
255+ Nothing -> error "Type synonym was not defined"
256+
257+ensureNoDuplicateProperties :: (MonadError String m) => [(String, Value)] -> m ()
258+ensureNoDuplicateProperties ps = guardWith "Duplicate property names" $ length (nub . map fst $ ps) == length ps
259+
260+infer :: Value -> Subst Type
261+infer val = rethrow (\e -> "Error inferring type of term " ++ prettyPrintValue val ++ ":\n" ++ e) $ do
262+ ty <- infer' val
263+ escapeCheckLater ty
264+ return ty
265+
266+infer' :: Value -> Subst Type
267+infer' (NumericLiteral _) = return Number
268+infer' (StringLiteral _) = return String
269+infer' (BooleanLiteral _) = return Boolean
270+infer' (ArrayLiteral vals) = do
271+ ts <- mapM (infer) vals
272+ els <- fresh
273+ forM_ ts $ \t -> els ~~ Array t
274+ return els
275+infer' (Unary op val) = do
276+ t <- infer val
277+ inferUnary op t
278+infer' (Binary op left right) = do
279+ t1 <- infer left
280+ t2 <- infer right
281+ inferBinary op t1 t2
282+infer' (ObjectLiteral ps) = do
283+ ensureNoDuplicateProperties ps
284+ ts <- mapM (infer . snd) ps
285+ let fields = zipWith (\(name, _) t -> (name, t)) ps ts
286+ return $ Object $ rowFromList (fields, REmpty)
287+infer' (ObjectUpdate o ps) = do
288+ ensureNoDuplicateProperties ps
289+ row <- fresh
290+ newTys <- zipWith (\(name, _) t -> (name, t)) ps <$> mapM (infer . snd) ps
291+ oldTys <- zip (map fst ps) <$> replicateM (length ps) fresh
292+ check o $ Object $ rowFromList (oldTys, row)
293+ return $ Object $ rowFromList (newTys, row)
294+infer' (Indexer index val) = do
295+ el <- fresh
296+ check index Number
297+ check val (Array el)
298+ return el
299+infer' (Accessor prop val) = do
300+ obj <- infer val
301+ propTy <- inferProperty obj prop
302+ case propTy of
303+ Nothing -> do
304+ field <- fresh
305+ rest <- fresh
306+ obj `subsumes` Object (RCons prop field rest)
307+ return field
308+ Just ty -> return ty
309+infer' (Abs args ret) = do
310+ ts <- replicateM (length args) fresh
311+ bindLocalVariables (zip args ts) $ do
312+ body <- infer' ret
313+ return $ Function ts body
314+infer' app@(App _ _) = do
315+ let (f, argss) = unfoldApplication app
316+ ft <- infer f
317+ ret <- fresh
318+ checkFunctionApplications ft argss ret
319+ return ret
320+infer' (Var var) = do
321+ ty <- lookupVariable var
322+ replaceAllTypeSynonyms ty
323+infer' (Block ss) = do
324+ ret <- fresh
325+ (allCodePathsReturn, _) <- checkBlock M.empty ret ss
326+ guardWith "Block is missing a return statement" allCodePathsReturn
327+ return ret
328+infer' (Constructor c) = do
329+ env <- getEnv
330+ modulePath <- checkModulePath `fmap` get
331+ case M.lookup (qualify modulePath c) (dataConstructors env) of
332+ Nothing -> throwError $ "Constructor " ++ show c ++ " is undefined"
333+ Just ty -> replaceAllTypeSynonyms ty
334+infer' (Case val binders) = do
335+ t1 <- infer val
336+ ret <- fresh
337+ checkBinders t1 ret binders
338+ return ret
339+infer' (IfThenElse cond th el) = do
340+ check cond Boolean
341+ t2 <- infer th
342+ t3 <- infer el
343+ t2 ~~ t3
344+ return t2
345+infer' (TypedValue val ty) = do
346+ kind <- liftCheck $ kindOf ty
347+ guardWith ("Expected type of kind *, was " ++ prettyPrintKind kind) $ kind == Star
348+ ty' <- replaceAllTypeSynonyms ty
349+ check val ty'
350+ return ty'
351+infer' _ = error "Invalid argument to infer"
352+
353+inferProperty :: Type -> String -> Subst (Maybe Type)
354+inferProperty (Object row) prop = do
355+ let (props, _) = rowToList row
356+ return $ lookup prop props
357+inferProperty (SaturatedTypeSynonym name args) prop = do
358+ replaced <- expandTypeSynonym name args
359+ inferProperty replaced prop
360+inferProperty (ForAll ident ty) prop = do
361+ replaced <- replaceVarWithUnknown ident ty
362+ inferProperty replaced prop
363+inferProperty _ _ = return Nothing
364+
365+inferUnary :: UnaryOperator -> Type -> Subst Type
366+inferUnary op val =
367+ case fromMaybe (error "Invalid operator") $ lookup op unaryOps of
368+ (valTy, resTy) -> do
369+ val ~~ valTy
370+ return resTy
371+
372+checkUnary :: UnaryOperator -> Value -> Type -> Subst ()
373+checkUnary op val res =
374+ case fromMaybe (error "Invalid operator") $ lookup op unaryOps of
375+ (valTy, resTy) -> do
376+ res ~~ resTy
377+ check val valTy
378+
379+unaryOps :: [(UnaryOperator, (Type, Type))]
380+unaryOps = [ (Negate, (Number, Number))
381+ , (Not, (Boolean, Boolean))
382+ , (BitwiseNot, (Number, Number))
383+ ]
384+
385+inferBinary :: BinaryOperator -> Type -> Type -> Subst Type
386+inferBinary op left right | isEqualityTest op = do
387+ left ~~ right
388+ return Boolean
389+inferBinary op left right =
390+ case fromMaybe (error "Invalid operator") $ lookup op binaryOps of
391+ (valTy, resTy) -> do
392+ left ~~ valTy
393+ right ~~ valTy
394+ return resTy
395+
396+checkBinary :: BinaryOperator -> Value -> Value -> Type -> Subst ()
397+checkBinary op left right res | isEqualityTest op = do
398+ res ~~ Boolean
399+ t1 <- infer left
400+ t2 <- infer right
401+ t1 ~~ t2
402+checkBinary op left right res =
403+ case fromMaybe (error "Invalid operator") $ lookup op binaryOps of
404+ (valTy, resTy) -> do
405+ res ~~ resTy
406+ check left valTy
407+ check right valTy
408+
409+isEqualityTest :: BinaryOperator -> Bool
410+isEqualityTest EqualTo = True
411+isEqualityTest NotEqualTo = True
412+isEqualityTest _ = False
413+
414+binaryOps :: [(BinaryOperator, (Type, Type))]
415+binaryOps = [ (Add, (Number, Number))
416+ , (Subtract, (Number, Number))
417+ , (Multiply, (Number, Number))
418+ , (Divide, (Number, Number))
419+ , (Modulus, (Number, Number))
420+ , (BitwiseAnd, (Number, Number))
421+ , (BitwiseOr, (Number, Number))
422+ , (BitwiseXor, (Number, Number))
423+ , (ShiftRight, (Number, Number))
424+ , (ZeroFillShiftRight, (Number, Number))
425+ , (And, (Boolean, Boolean))
426+ , (Or, (Boolean, Boolean))
427+ , (Concat, (String, String))
428+ , (Modulus, (Number, Number))
429+ , (LessThan, (Number, Boolean))
430+ , (LessThanOrEqualTo, (Number, Boolean))
431+ , (GreaterThan, (Number, Boolean))
432+ , (GreaterThanOrEqualTo, (Number, Boolean))
433+ ]
434+
435+inferBinder :: Type -> Binder -> Subst (M.Map Ident Type)
436+inferBinder _ NullBinder = return M.empty
437+inferBinder val (StringBinder _) = val ~~ String >> return M.empty
438+inferBinder val (NumberBinder _) = val ~~ Number >> return M.empty
439+inferBinder val (BooleanBinder _) = val ~~ Boolean >> return M.empty
440+inferBinder val (VarBinder name) = return $ M.singleton name val
441+inferBinder val (NullaryBinder ctor) = do
442+ env <- getEnv
443+ modulePath <- checkModulePath <$> get
444+ case M.lookup (qualify modulePath ctor) (dataConstructors env) of
445+ Just ty -> do
446+ ty `subsumes` val
447+ return M.empty
448+ _ -> throwError $ "Constructor " ++ show ctor ++ " is not defined"
449+inferBinder val (UnaryBinder ctor binder) = do
450+ env <- getEnv
451+ modulePath <- checkModulePath <$> get
452+ case M.lookup (qualify modulePath ctor) (dataConstructors env) of
453+ Just ty -> do
454+ Function [obj] ret <- replaceAllVarsWithUnknowns ty
455+ val `subsumes` ret
456+ inferBinder obj binder
457+ _ -> throwError $ "Constructor " ++ show ctor ++ " is not defined"
458+inferBinder val (ObjectBinder props) = do
459+ row <- fresh
460+ rest <- fresh
461+ m1 <- inferRowProperties row rest props
462+ val ~~ Object row
463+ return m1
464+ where
465+ inferRowProperties :: Row -> Row -> [(String, Binder)] -> Subst (M.Map Ident Type)
466+ inferRowProperties nrow row [] = nrow ~~ row >> return M.empty
467+ inferRowProperties nrow row ((name, binder):binders) = do
468+ propTy <- fresh
469+ m1 <- inferBinder propTy binder
470+ m2 <- inferRowProperties nrow (RCons name propTy row) binders
471+ return $ m1 `M.union` m2
472+inferBinder val (ArrayBinder binders) = do
473+ el <- fresh
474+ m1 <- M.unions <$> mapM (inferBinder el) binders
475+ val ~~ Array el
476+ return m1
477+inferBinder val (ConsBinder headBinder tailBinder) = do
478+ el <- fresh
479+ m1 <- inferBinder el headBinder
480+ m2 <- inferBinder val tailBinder
481+ val ~~ Array el
482+ return $ m1 `M.union` m2
483+inferBinder val (NamedBinder name binder) = do
484+ m <- inferBinder val binder
485+ return $ M.insert name val m
486+inferBinder _ _ = error "Invalid argument to inferBinder"
487+
488+inferGuardedBinder :: Type -> Binder -> Subst (M.Map Ident Type)
489+inferGuardedBinder val (GuardedBinder cond binder) = do
490+ m1 <- inferBinder val binder
491+ bindLocalVariables (M.toList m1) $ check cond Boolean
492+ return m1
493+inferGuardedBinder val b = inferBinder val b
494+
495+checkBinders :: Type -> Type -> [(Binder, Value)] -> Subst ()
496+checkBinders _ _ [] = return ()
497+checkBinders nval ret ((binder, val):bs) = do
498+ m1 <- inferGuardedBinder nval binder
499+ bindLocalVariables (M.toList m1) $ check val ret
500+ checkBinders nval ret bs
501+
502+assignVariable :: Ident -> Subst ()
503+assignVariable name = do
504+ env <- checkEnv <$> get
505+ modulePath <- checkModulePath <$> get
506+ case M.lookup (modulePath, name) (names env) of
507+ Just (_, LocalVariable) -> throwError $ "Variable with name " ++ show name ++ " already exists."
508+ _ -> return ()
509+
510+checkStatement :: M.Map Ident Type -> Type -> Statement -> Subst (Bool, M.Map Ident Type)
511+checkStatement mass _ (VariableIntroduction name val) = do
512+ assignVariable name
513+ t <- infer val
514+ return (False, M.insert name t mass)
515+checkStatement mass _ (Assignment ident val) = do
516+ t <- infer val
517+ case M.lookup ident mass of
518+ Nothing -> throwError $ "No local variable with name " ++ show ident
519+ Just ty -> do t ~~ ty
520+ return (False, mass)
521+checkStatement mass ret (While val inner) = do
522+ check val Boolean
523+ (allCodePathsReturn, _) <- checkBlock mass ret inner
524+ return (allCodePathsReturn, mass)
525+checkStatement mass ret (If ifst) = do
526+ allCodePathsReturn <- checkIfStatement mass ret ifst
527+ return (allCodePathsReturn, mass)
528+checkStatement mass ret (For ident start end inner) = do
529+ assignVariable ident
530+ check start Number
531+ check end Number
532+ (allCodePathsReturn, _) <- bindLocalVariables [(ident, Number)] $ checkBlock mass ret inner
533+ return (allCodePathsReturn, mass)
534+checkStatement mass ret (ForEach ident vals inner) = do
535+ assignVariable ident
536+ val <- fresh
537+ check vals (Array val)
538+ (allCodePathsReturn, _) <- bindLocalVariables [(ident, val)] $ checkBlock mass ret inner
539+ guardWith "Cannot return from within a foreach block" $ not allCodePathsReturn
540+ return (False, mass)
541+checkStatement mass _ (ValueStatement val) = do
542+ check val unit
543+ return (False, mass)
544+checkStatement mass ret (Return val) = do
545+ check val ret
546+ return (True, mass)
547+
548+checkIfStatement :: M.Map Ident Type -> Type -> IfStatement -> Subst Bool
549+checkIfStatement mass ret (IfStatement val thens Nothing) = do
550+ check val Boolean
551+ _ <- checkBlock mass ret thens
552+ return False
553+checkIfStatement mass ret (IfStatement val thens (Just elses)) = do
554+ check val Boolean
555+ (allCodePathsReturn1, _) <- checkBlock mass ret thens
556+ allCodePathsReturn2 <- checkElseStatement mass ret elses
557+ return $ allCodePathsReturn1 && allCodePathsReturn2
558+
559+checkElseStatement :: M.Map Ident Type -> Type -> ElseStatement -> Subst Bool
560+checkElseStatement mass ret (Else elses) = fst <$> checkBlock mass ret elses
561+checkElseStatement mass ret (ElseIf ifst) = checkIfStatement mass ret ifst
562+
563+checkBlock :: M.Map Ident Type -> Type -> [Statement] -> Subst (Bool, M.Map Ident Type)
564+checkBlock mass _ [] = return (False, mass)
565+checkBlock mass ret (s:ss) = do
566+ (b1, mass1) <- checkStatement mass ret s
567+ bindLocalVariables (M.toList mass1) $ case (b1, ss) of
568+ (True, []) -> return (True, mass1)
569+ (True, _) -> throwError "Unreachable code"
570+ (False, ss') -> checkBlock mass1 ret ss'
571+
572+skolemize :: String -> Type -> Subst Type
573+skolemize ident ty = do
574+ tsk <- Skolem <$> fresh'
575+ rsk <- RSkolem <$> fresh'
576+ return $ replaceRowVars ident rsk $ replaceTypeVars ident tsk ty
577+
578+check :: Value -> Type -> Subst ()
579+check val ty = rethrow errorMessage $ check' val ty
580+ where
581+ errorMessage msg =
582+ "Error checking type of term " ++
583+ prettyPrintValue val ++
584+ " against type " ++
585+ prettyPrintType ty ++
586+ ":\n" ++
587+ msg
588+
589+check' :: Value -> Type -> Subst ()
590+check' val (ForAll idents ty) = do
591+ sk <- skolemize idents ty
592+ check val sk
593+check' val u@(TUnknown _) = do
594+ ty <- infer val
595+ -- Don't unify an unknown with an inferred polytype
596+ ty' <- replaceAllVarsWithUnknowns ty
597+ ty' ~~ u
598+check' (NumericLiteral _) Number = return ()
599+check' (StringLiteral _) String = return ()
600+check' (BooleanLiteral _) Boolean = return ()
601+check' (Unary op val) ty = checkUnary op val ty
602+check' (Binary op left right) ty = checkBinary op left right ty
603+check' (ArrayLiteral vals) (Array ty) = forM_ vals (\val -> check val ty)
604+check' (Indexer index vals) ty = check index Number >> check vals (Array ty)
605+check' (Abs args ret) (Function argTys retTy) = do
606+ guardWith "Incorrect number of function arguments" (length args == length argTys)
607+ bindLocalVariables (zip args argTys) $ check ret retTy
608+check' app@(App _ _) ret = do
609+ let (f, argss) = unfoldApplication app
610+ ft <- infer f
611+ checkFunctionApplications ft argss ret
612+check' (Var var) ty = do
613+ ty1 <- lookupVariable var
614+ repl <- replaceAllTypeSynonyms ty1
615+ repl `subsumes` ty
616+check' (TypedValue val ty1) ty2 = do
617+ kind <- liftCheck $ kindOf ty1
618+ guardWith ("Expected type of kind *, was " ++ prettyPrintKind kind) $ kind == Star
619+ ty1 `subsumes` ty2
620+ check val ty1
621+check' (Case val binders) ret = do
622+ t1 <- infer val
623+ checkBinders t1 ret binders
624+check' (IfThenElse cond th el) ty = do
625+ check cond Boolean
626+ check th ty
627+ check el ty
628+check' (ObjectLiteral ps) (Object row) = do
629+ ensureNoDuplicateProperties ps
630+ checkProperties ps row False
631+check' (ObjectUpdate obj ps) (Object row) = do
632+ ensureNoDuplicateProperties ps
633+ us <- zip (map fst ps) <$> replicateM (length ps) fresh
634+ let (propsToCheck, rest) = rowToList row
635+ propsToRemove = map fst ps
636+ remainingProps = filter (\(p, _) -> p `notElem` propsToRemove) propsToCheck
637+ check obj (Object (rowFromList (us ++ remainingProps, rest)))
638+ checkProperties ps row True
639+check' (Accessor prop val) ty = do
640+ rest <- fresh
641+ check val (Object (RCons prop ty rest))
642+check' (Block ss) ret = do
643+ (allCodePathsReturn, _) <- checkBlock M.empty ret ss
644+ guardWith "Block is missing a return statement" allCodePathsReturn
645+check' (Constructor c) ty = do
646+ env <- getEnv
647+ modulePath <- checkModulePath <$> get
648+ case M.lookup (qualify modulePath c) (dataConstructors env) of
649+ Nothing -> throwError $ "Constructor " ++ show c ++ " is undefined"
650+ Just ty1 -> do
651+ repl <- replaceAllTypeSynonyms ty1
652+ repl `subsumes` ty
653+check' val (SaturatedTypeSynonym name args) = do
654+ ty <- expandTypeSynonym name args
655+ check val ty
656+check' val ty = throwError $ prettyPrintValue val ++ " does not have type " ++ prettyPrintType ty
657+
658+checkProperties :: [(String, Value)] -> Row -> Bool -> Subst ()
659+checkProperties ps row lax = let (ts, r') = rowToList row in go ps ts r' where
660+ go [] [] REmpty = return ()
661+ go [] [] u@(RUnknown _) = u ~~ REmpty
662+ go [] [] (RSkolem _) | lax = return ()
663+ go [] ((p, _): _) _ | lax = return ()
664+ | otherwise = throwError $ prettyPrintValue (ObjectLiteral ps) ++ " does not have property " ++ p
665+ go ((p,_):_) [] REmpty = throwError $ "Property " ++ p ++ " is not present in closed object type " ++ prettyPrintRow row
666+ go ((p,v):ps') [] u@(RUnknown _) = do
667+ ty <- infer v
668+ rest <- fresh
669+ u ~~ RCons p ty rest
670+ go ps' [] rest
671+ go ((p,v):ps') ts r =
672+ case lookup p ts of
673+ Nothing -> do
674+ ty <- infer v
675+ rest <- fresh
676+ r ~~ RCons p ty rest
677+ go ps' ts rest
678+ Just ty -> do
679+ check v ty
680+ go ps' (delete (p, ty) ts) r
681+ go _ _ _ = throwError $ prettyPrintValue (ObjectLiteral ps) ++ " does not have type " ++ prettyPrintType (Object row)
682+
683+unfoldApplication :: Value -> (Value, [[Value]])
684+unfoldApplication = go []
685+ where
686+ go argss (App f args) = go (args:argss) f
687+ go argss f = (f, argss)
688+
689+checkFunctionApplications :: Type -> [[Value]] -> Type -> Subst ()
690+checkFunctionApplications _ [] _ = error "Nullary function application"
691+checkFunctionApplications fnTy [args] ret = checkFunctionApplication fnTy args ret
692+checkFunctionApplications fnTy (args:argss) ret = do
693+ argTys <- mapM (infer) args
694+ f <- inferFunctionApplication fnTy argTys
695+ checkFunctionApplications f argss ret
696+
697+checkFunctionApplication :: Type -> [Value] -> Type -> Subst ()
698+checkFunctionApplication fnTy args ret = rethrow errorMessage $ checkFunctionApplication' fnTy args ret
699+ where
700+ errorMessage msg = "Error applying function of type "
701+ ++ prettyPrintType fnTy
702+ ++ " to arguments " ++ intercalate ", " (map prettyPrintValue args)
703+ ++ ", expecting value of type "
704+ ++ prettyPrintType ret ++ ":\n" ++ msg
705+
706+inferFunctionApplication :: Type -> [Type] -> Subst Type
707+inferFunctionApplication (Function argTys retTy) args = do
708+ guardWith "Incorrect number of function arguments" (length args == length argTys)
709+ zipWithM subsumes args argTys
710+ return retTy
711+inferFunctionApplication (ForAll ident ty) args = do
712+ replaced <- replaceVarWithUnknown ident ty
713+ inferFunctionApplication replaced args
714+inferFunctionApplication u@(TUnknown _) args = do
715+ ret <- fresh
716+ args' <- mapM replaceAllVarsWithUnknowns args
717+ u ~~ Function args' ret
718+ return ret
719+inferFunctionApplication (SaturatedTypeSynonym name tyArgs) args = do
720+ ty <- expandTypeSynonym name tyArgs
721+ inferFunctionApplication ty args
722+inferFunctionApplication fnTy args = throwError $ "Cannot apply function of type "
723+ ++ prettyPrintType fnTy
724+ ++ " to argument(s) of type(s) " ++ intercalate ", " (map prettyPrintType args)
725+
726+checkFunctionApplication' :: Type -> [Value] -> Type -> Subst ()
727+checkFunctionApplication' (Function argTys retTy) args ret = do
728+ guardWith "Incorrect number of function arguments" (length args == length argTys)
729+ zipWithM (check) args argTys
730+ retTy `subsumes` ret
731+checkFunctionApplication' (ForAll ident ty) args ret = do
732+ replaced <- replaceVarWithUnknown ident ty
733+ checkFunctionApplication replaced args ret
734+checkFunctionApplication' u@(TUnknown _) args ret = do
735+ tyArgs <- mapM (\arg -> infer arg >>= replaceAllVarsWithUnknowns) args
736+ u ~~ Function tyArgs ret
737+checkFunctionApplication' (SaturatedTypeSynonym name tyArgs) args ret = do
738+ ty <- expandTypeSynonym name tyArgs
739+ checkFunctionApplication' ty args ret
740+checkFunctionApplication' fnTy args ret = throwError $ "Applying a function of type "
741+ ++ prettyPrintType fnTy
742+ ++ " to argument(s) " ++ intercalate ", " (map prettyPrintValue args)
743+ ++ " does not yield a value of type " ++ prettyPrintType ret ++ "."
744+
745+subsumes :: Type -> Type -> Subst ()
746+subsumes (ForAll ident ty1) ty2 = do
747+ replaced <- replaceVarWithUnknown ident ty1
748+ replaced `subsumes` ty2
749+subsumes (Function args1 ret1) (Function args2 ret2) = do
750+ zipWithM subsumes args2 args1
751+ ret1 `subsumes` ret2
752+subsumes ty1 ty2 = ty1 ~~ ty2
753+
src/Language/PureScript/Types.hsnew file+73
@@ -0,0 +1,73 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.PureScript.Types
4+-- Copyright : (c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+{-# LANGUAGE DeriveDataTypeable #-}
16+
17+module Language.PureScript.Types where
18+
19+import Data.Data
20+import Language.PureScript.Names
21+import Language.PureScript.Unknown (Unknown(..))
22+
23+data Type
24+ = TUnknown (Unknown Type)
25+ | Number
26+ | String
27+ | Boolean
28+ | Array Type
29+ | Object Row
30+ | Function [PolyType] Type
31+ | TypeVar String
32+ | TypeConstructor (Qualified ProperName)
33+ | TypeApp Type Type
34+ | SaturatedTypeSynonym (Qualified ProperName) [Type]
35+ | ForAll String Type
36+ | Skolem Int deriving (Show, Eq, Data, Typeable)
37+
38+type PolyType = Type
39+
40+data Row
41+ = RUnknown (Unknown Row)
42+ | RowVar String
43+ | REmpty
44+ | RCons String Type Row
45+ | RSkolem Int deriving (Show, Eq, Data, Typeable)
46+
47+rowToList :: Row -> ([(String, Type)], Row)
48+rowToList (RCons name ty row) = let (tys, rest) = rowToList row
49+ in ((name, ty):tys, rest)
50+rowToList r = ([], r)
51+
52+rowFromList :: ([(String, Type)], Row) -> Row
53+rowFromList ([], r) = r
54+rowFromList ((name, t):ts, r) = RCons name t (rowFromList (ts, r))
55+
56+isMonoType :: Type -> Bool
57+isMonoType (ForAll _ _) = False
58+isMonoType ty = isPolyType ty
59+
60+isPolyType :: Type -> Bool
61+isPolyType (Array ty) = isMonoType ty
62+isPolyType (Object ps) = all isPolyType (map snd . fst $ rowToList ps)
63+isPolyType (Function args ret) = all isPolyType args && isPolyType ret
64+isPolyType (TypeApp t1 t2) = isMonoType t1 && isMonoType t2
65+isPolyType (SaturatedTypeSynonym _ args) = all isPolyType args
66+isPolyType (ForAll _ ty) = isPolyType ty
67+isPolyType _ = True
68+
69+mkForAll :: [String] -> Type -> Type
70+mkForAll = flip . foldl . flip $ ForAll
71+
72+unit :: Type
73+unit = Object REmpty
src/Language/PureScript/Unknown.hsnew file+24
@@ -0,0 +1,24 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.PureScript.Unknown
4+-- Copyright : (c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+{-# LANGUAGE DeriveDataTypeable #-}
16+
17+module Language.PureScript.Unknown where
18+
19+import Data.Data
20+
21+newtype Unknown t = Unknown { runUnknown :: Int } deriving (Show, Eq, Ord, Data, Typeable)
22+
23+
24+
src/Language/PureScript/Values.hsnew file+101
@@ -0,0 +1,101 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.PureScript.Values
4+-- Copyright : (c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+{-# LANGUAGE DeriveDataTypeable #-}
16+
17+module Language.PureScript.Values where
18+
19+import Language.PureScript.Types
20+import Language.PureScript.Names
21+
22+import Data.Data
23+
24+data UnaryOperator
25+ = Negate
26+ | Not
27+ | BitwiseNot deriving (Show, Eq, Data, Typeable)
28+
29+data BinaryOperator
30+ = Add
31+ | Subtract
32+ | Multiply
33+ | Divide
34+ | Modulus
35+ | EqualTo
36+ | NotEqualTo
37+ | LessThan
38+ | LessThanOrEqualTo
39+ | GreaterThan
40+ | GreaterThanOrEqualTo
41+ | And
42+ | Or
43+ | BitwiseAnd
44+ | BitwiseOr
45+ | BitwiseXor
46+ | ShiftLeft
47+ | ShiftRight
48+ | ZeroFillShiftRight
49+ | Concat deriving (Show, Eq, Data, Typeable)
50+
51+data Value
52+ = NumericLiteral (Either Integer Double)
53+ | StringLiteral String
54+ | BooleanLiteral Bool
55+ | Unary UnaryOperator Value
56+ | Binary BinaryOperator Value Value
57+ | BinaryNoParens (Qualified Ident) Value Value
58+ | Parens Value
59+ | ArrayLiteral [Value]
60+ | Indexer Value Value
61+ | ObjectLiteral [(String, Value)]
62+ | Accessor String Value
63+ | ObjectUpdate Value [(String, Value)]
64+ | Abs [Ident] Value
65+ | App Value [Value]
66+ | Var (Qualified Ident)
67+ | IfThenElse Value Value Value
68+ | Block [Statement]
69+ | Constructor (Qualified ProperName)
70+ | Case Value [(Binder, Value)]
71+ | TypedValue Value PolyType deriving (Show, Data, Typeable)
72+
73+data Statement
74+ = VariableIntroduction Ident Value
75+ | Assignment Ident Value
76+ | While Value [Statement]
77+ | For Ident Value Value [Statement]
78+ | ForEach Ident Value [Statement]
79+ | If IfStatement
80+ | ValueStatement Value
81+ | Return Value deriving (Show, Data, Typeable)
82+
83+data IfStatement = IfStatement Value [Statement] (Maybe ElseStatement) deriving (Show, Data, Typeable)
84+
85+data ElseStatement
86+ = Else [Statement]
87+ | ElseIf IfStatement deriving (Show, Data, Typeable)
88+
89+data Binder
90+ = NullBinder
91+ | BooleanBinder Bool
92+ | StringBinder String
93+ | NumberBinder (Either Integer Double)
94+ | VarBinder Ident
95+ | NullaryBinder (Qualified ProperName)
96+ | UnaryBinder (Qualified ProperName) Binder
97+ | ObjectBinder [(String, Binder)]
98+ | ArrayBinder [Binder]
99+ | ConsBinder Binder Binder
100+ | NamedBinder Ident Binder
101+ | GuardedBinder Value Binder deriving (Show, Data, Typeable)
src/Main.hsnew file+70
@@ -0,0 +1,70 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Main
4+-- Copyright : (c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+module Main where
16+
17+import qualified Language.PureScript as P
18+import System.Console.CmdTheLine
19+import Control.Applicative
20+import Control.Monad (forM)
21+import System.Exit (exitSuccess, exitFailure)
22+import qualified System.IO.UTF8 as U
23+
24+compile :: [FilePath] -> Maybe FilePath -> Maybe FilePath -> IO ()
25+compile input output externs = do
26+ asts <- fmap (fmap concat . sequence) $ forM input $ \inputFile -> do
27+ text <- U.readFile inputFile
28+ return $ P.runIndentParser P.parseDeclarations text
29+ case asts of
30+ Left err -> do
31+ U.print err
32+ exitFailure
33+ Right decls ->
34+ case P.compile decls of
35+ Left err -> do
36+ U.putStrLn err
37+ exitFailure
38+ Right (js, exts, _) -> do
39+ case output of
40+ Just path -> U.writeFile path js
41+ Nothing -> U.putStrLn js
42+ case externs of
43+ Nothing -> return ()
44+ Just filePath -> U.writeFile filePath exts
45+ exitSuccess
46+
47+inputFiles :: Term [FilePath]
48+inputFiles = nonEmpty $ posAny [] $ posInfo
49+ { posDoc = "The input .ps files" }
50+
51+outputFile :: Term (Maybe FilePath)
52+outputFile = value $ opt Nothing $ (optInfo [ "o", "output" ])
53+ { optDoc = "The output .js file" }
54+
55+externsFile :: Term (Maybe FilePath)
56+externsFile = value $ opt Nothing $ (optInfo [ "e", "externs" ])
57+ { optDoc = "The output .e.ps file" }
58+
59+term :: Term (IO ())
60+term = compile <$> inputFiles <*> outputFile <*> externsFile
61+
62+termInfo :: TermInfo
63+termInfo = defTI
64+ { termName = "psc"
65+ , version = "1.0"
66+ , termDoc = "Compiles PureScript to Javascript"
67+ }
68+
69+main :: IO ()
70+main = run (term, termInfo)
src/Tests.hsdeleted−141
@@ -1,141 +0,0 @@
1−module Tests (runTests) where
2−
3−import Prelude.Compat
4−import Data.List
5−import Control.Applicative
6−import Control.Monad
7−
8−import System.Process
9−import System.FilePath
10−import System.IO
11−import System.Directory
12−
13−-------------------------------------------------------------------------------
14−testsDir :: IO (FilePath, FilePath)
15−-------------------------------------------------------------------------------
16−testsDir = do
17− baseDir <- getCurrentDirectory
18− return (baseDir </> "purescript-tests", baseDir)
19−
20−-------------------------------------------------------------------------------
21−runTests :: IO ()
22−-------------------------------------------------------------------------------
23−runTests = do
24−
25− (outputDir, baseDir) <- testsDir
26−
27− outputDirExists <- doesDirectoryExist outputDir
28− when outputDirExists $ removeDirectoryRecursive outputDir
29− createDirectory outputDir
30−
31− let srcDir = outputDir </> "src"
32− createDirectory srcDir
33−
34− callProcess "git" ["clone", "--branch", "v0.13.8", "--depth", "1", "https://github.com/purescript/purescript.git"]
35− let passingDir = baseDir </> "purescript" </> "tests" </> "purs" </> "passing"
36− passingTestCases <- sort . filter (".purs" `isSuffixOf`) <$> getDirectoryContents passingDir
37−
38− setCurrentDirectory outputDir
39−
40− fetchPackages
41−
42− let tests = filter (`notElem` skipped) passingTestCases
43−
44− tmp <- getTemporaryDirectory
45− createDirectoryIfMissing False (tmp </> logpath)
46− outputFile <- openFile (tmp </> logpath </> logfile) WriteMode
47− hClose outputFile
48−
49− -- Run the tests
50− --
51− forM_ tests $ \inputFile -> do
52− --
53− -- Compile/build
54− --
55− putStrLn $ "Compiling test " ++ inputFile ++ " ..."
56− setCurrentDirectory outputDir
57− copyFile (passingDir </> inputFile) (srcDir </> inputFile)
58−
59− let testCaseDir = passingDir </> (takeWhile (/='.') inputFile)
60− testCaseDirExists <- doesDirectoryExist testCaseDir
61− when testCaseDirExists $ callProcess "cp" ["-R", testCaseDir, srcDir]
62−
63− callProcess "rm" ["-rf", "output"]
64− callProcess "rm" ["-rf", "Main"]
65− callProcess "spago" ["build"]
66− --
67− -- Run executable file
68− --
69− outputFile <- openFile (tmp </> logpath </> logfile) AppendMode
70− hPutStrLn outputFile ("\n" ++ inputFile ++ ":")
71− proc <- runProcess ("./" </> "Main") [] Nothing Nothing Nothing (Just outputFile) Nothing
72−
73− removeFile (srcDir </> inputFile)
74− when testCaseDirExists $ callProcess "rm" ["-rf", srcDir </> (takeWhile (/='.') inputFile)]
75−
76− -- TODO: support failing test cases
77− --
78− -- let failing = baseDir </> "examples" </> "failing"
79− -- failingTestCases <- sort . filter (".purs" `isSuffixOf`) <$> getDirectoryContents failing
80− --
81−
82− setCurrentDirectory baseDir
83− putStrLn "PureScript tests finished"
84− putStrLn $ "Total tests available: " ++ show (length passingTestCases)
85− putStrLn $ "Tests run: " ++ show (length tests)
86− putStrLn $ "Tests skipped: " ++ show (length skipped)
87−
88−-------------------------------------------------------------------------------
89−packages :: [String]
90−-------------------------------------------------------------------------------
91−packages =
92− [ "arrays"
93− , "assert"
94− , "console"
95− , "control"
96− , "effect"
97− , "foldable-traversable"
98− , "functions"
99− , "generics-rep"
100− , "invariant"
101− , "newtype"
102− , "partial"
103− , "prelude"
104− , "proxy"
105− , "refs"
106− , "st"
107− , "type-equality"
108− , "typelevel-prelude"
109− ]
110−
111−-------------------------------------------------------------------------------
112−fetchPackages :: IO ()
113−-------------------------------------------------------------------------------
114−fetchPackages = do
115− callProcess "spago" ["init"]
116− callCommand "sed '$s/\\(}\\)/, backend = \"psgo\"\\1/' < spago.dhall | dhall format > spago.tmp"
117− callCommand "mv spago.tmp spago.dhall"
118−
119− mapM (callProcess "spago" . (\p -> ["install", p])) packages
120− return ()
121−
122−-------------------------------------------------------------------------------
123−skipped :: [String]
124−-------------------------------------------------------------------------------
125−skipped =
126− [ "2172.purs" -- foreign in Main needed (test manually)
127− , "EffFn.purs" -- foreign in Main needed (test manually)
128− , "FunWithFunDeps.purs" -- foreign in Main needed (test manually)
129− , "NumberLiterals.purs" -- unreliable float string comparison (test manually)
130− , "PolyLabels.purs" -- foreign in Main needed (test manually)
131− , "RowUnion.purs" -- foreign in Main needed (test manually)
132− , "ShadowedModuleName.purs" -- ?
133− , "StringEdgeCases.purs" -- javascript char encoding specific
134− , "StringEscapes.purs" -- Partially javascript-char-encoding-specific (test manually)
135− ]
136−
137−logpath :: FilePath
138−logpath = "purescript-output"
139−
140−logfile :: FilePath
141−logfile = "purescript-tests.out"
stack.nixdeleted−23
@@ -1,23 +0,0 @@
1−#
2−# You may need to manually run `unset STACK_IN_NIX_SHELL`
3−# (doesn't work from a `shellHook`; see: https://github.com/commercialhaskell/stack/issues/5000
4−#
5−#
6−# You may need to set LC_ALL when building, e.g.
7−# LC_ALL=en_US.iso88591 stack --nix build; see e.g. https://github.com/koalaman/shellcheck/issues/324
8−#
9−#
10−{ pkgs ?
11− import (builtins.fetchTarball {
12− name = "nixos-release-19.03";
13− url = https://github.com/nixos/nixpkgs/archive/34c7eb7545d155cc5b6f499b23a7cb1c96ab4d59.tar.gz;
14− sha256 = "11z6ajj108fy2q5g8y4higlcaqncrbjm3dnv17pvif6avagw4mcb";
15− }) {}
16−}:
17−
18−pkgs.haskell.lib.buildStackProject {
19− name = "default-stack-shell";
20− ghc = pkgs.haskell.compiler.ghc865;
21− buildInputs = with pkgs; [git git-lfs gmp ncurses zlib];
22− LANG = "en_US.UTF-8";
23−}
stack.yamldeleted−24
@@ -1,24 +0,0 @@
1−resolver: lts-13.26
2−
3−packages:
4−- .
5−
6−extra-deps:
7−- git: https://github.com/purescript/purescript
8− commit: 9cad73ed8ea7df3011032ddbd2f3de5a0c08629c
9−- happy-1.19.9
10−- language-javascript-0.7.0.0
11−- network-3.0.1.1
12−- these-1.0.1
13−- semialign-1
14−
15−nix:
16− enable: false
17− shell-file: stack.nix
18−
19−flags:
20− aeson-pretty:
21− lib-only: true
22− these:
23− assoc: false
24− quickcheck: false
support/ffi-loader.godeleted−30
@@ -1,30 +0,0 @@
1−package ffi_loader
2−
3−// Load PureScript standard library FFI packages. Comment out the ones you don't need for
4−// faster/smaller builds.
5−
6−import (
7− _ "github.com/purescript-native/go-ffi/purescript-arrays"
8− _ "github.com/purescript-native/go-ffi/purescript-assert"
9− _ "github.com/purescript-native/go-ffi/purescript-console"
10− _ "github.com/purescript-native/go-ffi/purescript-effect"
11− _ "github.com/purescript-native/go-ffi/purescript-enums"
12− _ "github.com/purescript-native/go-ffi/purescript-exceptions"
13− _ "github.com/purescript-native/go-ffi/purescript-foldable-traversable"
14− _ "github.com/purescript-native/go-ffi/purescript-functions"
15− _ "github.com/purescript-native/go-ffi/purescript-globals"
16− _ "github.com/purescript-native/go-ffi/purescript-integers"
17− _ "github.com/purescript-native/go-ffi/purescript-lazy"
18− _ "github.com/purescript-native/go-ffi/purescript-math"
19− _ "github.com/purescript-native/go-ffi/purescript-partial"
20− _ "github.com/purescript-native/go-ffi/purescript-prelude"
21− _ "github.com/purescript-native/go-ffi/purescript-random"
22− _ "github.com/purescript-native/go-ffi/purescript-refs"
23− _ "github.com/purescript-native/go-ffi/purescript-st"
24− _ "github.com/purescript-native/go-ffi/purescript-strings"
25− _ "github.com/purescript-native/go-ffi/purescript-unfoldable"
26−)
27−
28−// Add your own FFI packages here.
29−
30−import ()
support/go.mod.ffi-loaderdeleted−2
@@ -1,2 +0,0 @@
1−module $PROJECT/purescript-native/ffi-loader
2−
support/go.mod.outputdeleted−2
@@ -1,2 +0,0 @@
1−module $PROJECT/purescript-native/output
2−
support/go.mod.workingdeleted−1
@@ -1 +0,0 @@
1−module $PROJECT/example
test/Spec.hsdeleted−2
@@ -1,2 +0,0 @@
1−main :: IO ()
2−main = putStrLn "Test suite not yet implemented"
tests/Main.hsnew file+71
@@ -0,0 +1,71 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Main
4+-- Copyright : (c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+module Main (main) where
16+
17+import qualified Language.PureScript as P
18+
19+import Data.List (isSuffixOf)
20+import Control.Applicative
21+import Control.Monad
22+import System.Exit (exitSuccess, exitFailure)
23+import System.FilePath (pathSeparator)
24+import System.Directory (getCurrentDirectory, getDirectoryContents)
25+import qualified System.IO.UTF8 as U
26+import qualified Data.Map as M
27+
28+compile :: FilePath -> IO (Either String P.Environment)
29+compile inputFile = do
30+ ast <- P.runIndentParser P.parseDeclarations <$> U.readFile inputFile
31+ case ast of
32+ Left parseError -> do
33+ return (Left $ show parseError)
34+ Right decls -> do
35+ case P.compile decls of
36+ Left typeError -> do
37+ return (Left typeError)
38+ Right (_, _, env) -> do
39+ return (Right env)
40+
41+assert :: FilePath -> (Either String P.Environment -> Maybe String) -> IO ()
42+assert inputFile f = do
43+ e <- compile inputFile
44+ case f e of
45+ Just err -> exitFailure
46+ Nothing -> return ()
47+
48+assertCompiles :: FilePath -> IO ()
49+assertCompiles inputFile = do
50+ putStrLn $ "assert " ++ inputFile ++ " compiles successfully"
51+ assert inputFile $ either Just (const Nothing)
52+
53+assertDoesNotCompile :: FilePath -> IO ()
54+assertDoesNotCompile inputFile = do
55+ putStrLn $ "assert " ++ inputFile ++ " does not compile"
56+ assert inputFile $ either (const Nothing) (const $ Just "Should not have compiled")
57+
58+main :: IO ()
59+main = do
60+ cd <- getCurrentDirectory
61+ putStrLn $ cd
62+ let examples = cd ++ pathSeparator : "examples"
63+ let passing = examples ++ pathSeparator : "passing"
64+ passingTestCases <- getDirectoryContents passing
65+ forM_ passingTestCases $ \inputFile -> when (".purs" `isSuffixOf` inputFile) $
66+ assertCompiles (passing ++ pathSeparator : inputFile)
67+ let failing = examples ++ pathSeparator : "failing"
68+ failingTestCases <- getDirectoryContents failing
69+ forM_ failingTestCases $ \inputFile -> when (".purs" `isSuffixOf` inputFile) $
70+ assertDoesNotCompile (failing ++ pathSeparator : inputFile)
71+ exitSuccess