├── .gitignore
├── LICENSE
├── Main.hs
├── README.md
├── Setup.hs
├── docs
├── dicussion.md
├── infernu.png
├── infernu.svg
├── inst-graph.png
├── make.sh
├── memoize.js
├── semantics.hs
└── type-system.md
├── doctest.sh
├── infernu.cabal
├── lambdabot-plugin
├── LICENSE
├── Lambdabot
│ └── Plugin
│ │ └── Infernu.hs
├── Setup.hs
└── lambdabot-plugin-inferno.cabal
├── others
├── ConstId.cs
├── GetPut.java
├── flow
│ ├── .flowconfig
│ ├── debounce.js
│ ├── guards.js
│ ├── id.js
│ ├── memoize
│ │ └── memoize.js
│ ├── problem-id.js
│ ├── problem.js
│ ├── row.js
│ └── simple-bug.js
├── lint
│ └── string_constr.js
├── notabug.caml
└── typescript
│ ├── bugs.ts
│ └── memoize.ts
├── src
└── Infernu
│ ├── Builtins.hs
│ ├── Builtins
│ ├── Array.hs
│ ├── DOM.hs
│ ├── Date.hs
│ ├── Math.hs
│ ├── Names.hs
│ ├── Number.hs
│ ├── Object.hs
│ ├── Operators.hs
│ ├── Regex.hs
│ ├── String.hs
│ ├── StringMap.hs
│ ├── TypeClasses.hs
│ └── Util.hs
│ ├── Decycle.hs
│ ├── Expr.hs
│ ├── Fix.hs
│ ├── Infer.hs
│ ├── InferState.hs
│ ├── Lib.hs
│ ├── Log.hs
│ ├── Options.hs
│ ├── Parse.hs
│ ├── Prelude.hs
│ ├── Pretty.hs
│ ├── Source.hs
│ ├── Types.hs
│ ├── Unify.hs
│ └── Util.hs
├── stack.yaml
├── test
├── .gitignore
├── Demo.hs
├── Test.hs
├── fail.txt
├── ghcjs
│ └── all.js
├── invalid
│ ├── .flowconfig
│ ├── array-assign-in-func.js
│ ├── array-assign.js
│ ├── array-idx.js
│ ├── array-param.js
│ ├── array.js
│ ├── blo.js
│ ├── blo2.js
│ ├── builtins
│ │ ├── Math.js
│ │ ├── binary-not.js
│ │ ├── equality.js
│ │ ├── isFinite.js
│ │ ├── logical-not.js
│ │ ├── minus.js
│ │ ├── minusminus.js
│ │ ├── or.js
│ │ ├── parse.js
│ │ ├── parseInt.js
│ │ ├── plus-func.js
│ │ ├── plus.js
│ │ ├── plusplus.js
│ │ ├── regex-exec.js
│ │ ├── string-error.js
│ │ ├── string-replace.js
│ │ ├── string_constr.js
│ │ ├── typeof.js
│ │ └── uri.js
│ ├── call2.js
│ ├── cond.js
│ ├── debounce-simplified.js
│ ├── debounce.js
│ ├── dowhile.js
│ ├── fix-usage.js
│ ├── for-1.js
│ ├── for-2.js
│ ├── for-in.js
│ ├── for.js
│ ├── func-as-row.js
│ ├── func-no-currying.js
│ ├── generalize.js
│ ├── getput.js
│ ├── if.js
│ ├── infer-from-call.js
│ ├── infinite.js
│ ├── infinite2.js
│ ├── inheritance.js
│ ├── method-type.js
│ ├── mutability
│ │ ├── bug.js
│ │ ├── bug2.js
│ │ ├── bug3.js
│ │ ├── bug5.js
│ │ ├── bug6.js
│ │ ├── bug7.js
│ │ └── mutable-id.js
│ ├── naked_array.js
│ ├── new-override.js
│ ├── new-return.js
│ ├── new.js
│ ├── not-a-method.js
│ ├── num.js
│ ├── object-chaining.js
│ ├── occurs-erisco.js
│ ├── occurs.js
│ ├── occurs2.js
│ ├── open-row-erisco.js
│ ├── open-row.js
│ ├── polyfunc.js
│ ├── polyfunc2.js
│ ├── polyfunc3.js
│ ├── promise-method-workaround-simplified.js
│ ├── promise-method-workaround.js
│ ├── promise-simple-api.js
│ ├── prop-polymorphic-assign.js
│ ├── prop-polymorphic-assign2-b.js
│ ├── prop-polymorphic-assign2-this.js
│ ├── prop-polymorphic-assign2.js
│ ├── prop-polymorphic-assign3-b.js
│ ├── prop-polymorphic-assign3.js
│ ├── prop-polymorphic-closure.js
│ ├── prop-polymorphic-closure2.js
│ ├── prop-wrong.js
│ ├── prop_assign.js
│ ├── rank-2-method-b.js
│ ├── rank-2-method-simple.js
│ ├── rank-2-method.js
│ ├── recursive-type.js
│ ├── return-multi.js
│ ├── row-func.js
│ ├── row-return.js
│ ├── row1.js
│ ├── stringmap-preds.js
│ ├── switch-1.js
│ ├── switch-2.js
│ ├── this-func.js
│ ├── this.js
│ ├── undefined.js
│ ├── unfortunately
│ │ ├── inheritance.js
│ │ ├── mauke.js
│ │ ├── new-recursive-prototype.js
│ │ └── polymorphic-row-fields
│ │ │ ├── prop-polymorphic-assign.js
│ │ │ ├── prop-polymorphic-assign2-this.js
│ │ │ └── prop-polymorphic-assign3.js
│ ├── value_restriction_apply.js
│ ├── var-recursive-nonfunc.js
│ ├── vartest.js
│ ├── while.js
│ └── y.js
├── makebig.hs
├── num.js
├── purescript
│ ├── Assert.js
│ ├── Control.Monad.Eff.js
│ ├── Control.Monad.ST.js
│ ├── Data.Function.js
│ ├── Debug.Trace.js
│ ├── Prelude.Unsafe.js
│ └── Prelude.js
├── regexlike-exec-2.js
├── runall.sh
├── runtest.sh
├── test.sh
└── valid
│ ├── .flowconfig
│ ├── arguably
│ └── if-undefined.js
│ ├── array-2d-get.js
│ ├── array-idx.js
│ ├── array-length.js
│ ├── array-with-magic-props.js
│ ├── array.js
│ ├── blo.js
│ ├── break.js
│ ├── builtins
│ ├── Boolean.js
│ ├── Date.js
│ ├── JSON.js
│ ├── Math.js
│ ├── Number.js
│ ├── String.js
│ ├── assign-ops-index.js
│ ├── assign-ops-property.js
│ ├── assign-ops.js
│ ├── binary-not.js
│ ├── isFinite.js
│ ├── isNaN.js
│ ├── logical-not.js
│ ├── map.js
│ ├── map2.js
│ ├── minus.js
│ ├── minusminus.js
│ ├── or.js
│ ├── parse.js
│ ├── plus-func.js
│ ├── plus.js
│ ├── plusplus.js
│ ├── regex-exec-2.js
│ ├── regex-exec.js
│ ├── regex-test.js
│ ├── regex.js
│ ├── string-ops.js
│ ├── string-replace.js
│ ├── typeof.js
│ └── uri.js
│ ├── call-this.js
│ ├── call.js
│ ├── complex
│ ├── kinda-underscore.js
│ └── sortedUniq.js
│ ├── cond.js
│ ├── continue.js
│ ├── debounce-indexable.js
│ ├── debounce.js
│ ├── double-func.js
│ ├── fix.js
│ ├── for-in.js
│ ├── for.js
│ ├── func-stmt-expr-erisco.js
│ ├── func.js
│ ├── get-length.js
│ ├── if.js
│ ├── index-and-plus.js
│ ├── indexable.js
│ ├── list.js
│ ├── map.js
│ ├── memoize.js
│ ├── method-as-func.js
│ ├── method.js
│ ├── mutability
│ ├── array-of-polyfuncs-eta-expanded.js
│ ├── array-of-polyfuncs.js
│ ├── indirect-mutable.js
│ ├── indirect-mutable2.js
│ ├── mutable-assign-app.js
│ ├── mutable-indexassign-app.js
│ ├── mutable-propassign-app.js
│ ├── ok7.js
│ ├── ok7b.js
│ ├── ok7c.js
│ └── var.js
│ ├── new-func.js
│ ├── new-recursive.js
│ ├── new-return.js
│ ├── new-simple.js
│ ├── new.js
│ ├── nothis-as-obj-param.js
│ ├── nothis-as-param.js
│ ├── nothis.js
│ ├── nothis2.js
│ ├── object-chaining-long.js
│ ├── object-chaining-triple.js
│ ├── object-chaining.js
│ ├── objlit-func.js
│ ├── objlit.js
│ ├── objlit2.js
│ ├── once.js
│ ├── passing-obj-with-poly-method.js
│ ├── point.js
│ ├── poll.js
│ ├── poly-array-assign.js
│ ├── poly-array-assign2.js
│ ├── poly-method-rank-3-nothis.js
│ ├── poly-method-rank-3.js
│ ├── poly-method-recursive-this.js
│ ├── poly-method-simple.js
│ ├── poly-row-field-using-let.js
│ ├── poly-row-field.js
│ ├── poly-row-type-func.js
│ ├── poly-this-new.js
│ ├── poly-this.js
│ ├── poly-this2.js
│ ├── polyfunc.js
│ ├── polyfunc2.js
│ ├── promise-checking-subsumption.js
│ ├── promise-method-workaround.js
│ ├── promise-simple-api.js
│ ├── promise-simplified.js
│ ├── promise-simplified2.js
│ ├── promise.js
│ ├── prop-nested-get.js
│ ├── prop_access.js
│ ├── prop_assign.js
│ ├── record.js
│ ├── recurse.js
│ ├── recurse2.js
│ ├── recursive-type.js
│ ├── recursive_value.js
│ ├── regex.js
│ ├── return-multi.js
│ ├── return.js
│ ├── row-return.js
│ ├── switch.js
│ ├── test.js
│ ├── this.js
│ ├── trivial.js
│ ├── try.js
│ ├── var-empty.js
│ └── while.js
└── webidl
├── Document.webidl
├── Event.webidl
├── EventTarget.webidl
└── Node.webidl
/.gitignore:
--------------------------------------------------------------------------------
1 | dist
2 | cabal-dev
3 | *.o
4 | *.hi
5 | *.chi
6 | *.chs.h
7 | .virtualenv
8 | .hsenv
9 | .cabal-sandbox/
10 | cabal.sandbox.config
11 | *~
12 | \#*
13 | .#*
14 |
15 | *.dyn*
16 | TAGS
17 | *.orig
18 |
19 | .stack-work/
20 |
--------------------------------------------------------------------------------
/Main.hs:
--------------------------------------------------------------------------------
1 | module Main (main) where
2 |
3 | import Control.Monad (forM, when)
4 | import qualified Options.Applicative as OptParse
5 | import System.Exit (exitFailure)
6 | import qualified Text.Parsec.Pos as Pos
7 |
8 | import Text.PrettyPrint.ANSI.Leijen (Pretty (..))
9 | import Infernu.Options (Options (..), opts)
10 | import Infernu.Types (QualType)
11 | import Infernu.Source (Source(..))
12 | import Infernu.Util
13 | import Infernu.Prelude
14 |
15 | process :: [(Source, QualType)] -> [(Pos.SourceName, [String])] -> String
16 | process ts sourceCodes = concatMap (\(f, ds) -> annotatedSource (filteredTypes f ts) ds) sourceCodes
17 | where filteredTypes f' = filter (\(Source (_, p), _) -> (Pos.sourceName p == f'))
18 |
19 | main :: IO ()
20 | main = do
21 | options <- OptParse.execParser opts
22 | let files = optFileNames options
23 | res <- checkFiles options files
24 | case res of
25 | Right ts -> when (not $ optQuiet options) $ do sourceCodes <- forM files $ \f -> do d <- readFile f
26 | return (f, lines d)
27 | putStrLn $ process ts sourceCodes
28 | Left e -> putStrLn (show $ pretty e) >> exitFailure
29 |
30 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 
2 |
3 | Static type inference for JavaScript.
4 |
5 | *Inside every big ugly language there is a small beautiful language trying to come out.*
6 |
7 | ## MOTHBALLED
8 |
9 | I have little time for this project, and don't use JS at work anymore so I'm less motivated to continue. But I still believe it's the right way to go with type safety in JS.
10 |
11 | ## Features
12 |
13 | * It's just JavaScript: runs as-is in your browser. No transpilation or compilation required for running.
14 | * Full type inference: no type annotations necessary.
15 | * Safety: a strict type system with no workarounds sets a high bar for code correctness. *So you can sleep at night!*
16 | * **Work in progress**: it can set your computer on fire!
17 |
18 | ### Type System
19 |
20 | * Parametric polymorphism (aka "generics"), based on Hindley-Milner type inference.
21 | * Row-type polymorphism, otherwise known as "static duck typing".
22 | * Simple type classes (which allow for example correct support of JS `+` and `[]` operators).
23 | * Recursive types for true representation of object-oriented methods.
24 | * Correct handling of JS's `this` dynamic scoping rules.
25 |
26 | For more information see [Infernu's Type System](docs/type-system.md).
27 |
28 | Also see the [intro blog post](https://noamlewis.wordpress.com/2015/01/20/introducing-sjs-a-type-inferer-and-checker-for-javascript/) for a short discussion comparing infernu to **other type checkers**.
29 |
30 |
31 | ## Installation
32 |
33 | ### Quick and Dirty
34 |
35 | First, install [haskell stack](https://docs.haskellstack.org/en/stable/install_and_upgrade/).
36 |
37 | Then use stack to install infernu:
38 |
39 | git clone git@github.com:sinelaw/infernu.git
40 | cd infernu/
41 | stack setup
42 | stack install
43 |
44 | Usage: see `infernu --help`
45 |
46 | Quick example usage:
47 |
48 | echo 'function getLength(x) { return x.length; }' > getLength.js
49 |
50 | infernu getLength.js
51 |
52 | Output:
53 |
54 | ```javascript
55 | // getLength : a.({length: b, ..c} -> b)
56 | function getLength(x) { return x.length; }
57 | ```
58 |
59 |
60 | ### A bit more detailed instructions
61 |
62 | 1. Install [Haskell stack](https://docs.haskellstack.org/en/stable/install_and_upgrade/).
63 | 2. Clone this repository.
64 |
65 | Then run:
66 |
67 | cd infernu/
68 | stack setup
69 | stack install
70 |
71 | The `infernu` executable will be installed to your `~/.local/bin`. You may want to add it to your `PATH`.
72 |
73 | ### Running tests
74 |
75 | cd infernu
76 | stack setup
77 | stack build
78 | cd test
79 | ./test.sh
80 |
81 | Currently there are still a few failing tests due to unclosed issues in the type system.
82 |
83 | The file `test/fail.txt` records the test failures and is kept in git. This makes it easier to track progress on outstanding bugs.
84 |
85 | ## Examples
86 |
87 | ### Basic
88 |
89 | JavaScript:
90 |
91 | var num = 2;
92 | var arrNums = [num, num];
93 |
94 | Infernu infers:
95 |
96 | // num : Number
97 | // arrNums : [Number]
98 |
99 | That is, an array of numbers.
100 |
101 | Objects:
102 |
103 | var obj = { something: 'hi', value: num };
104 |
105 | Inferred type:
106 |
107 | // obj : {something: String,
108 | value: Number}
109 |
110 | That is, an object with two properties: 'something', of type string, and 'value' of type number.
111 |
112 | ### Functions and `this`
113 |
114 | In JS, `this` is one truly awful part. `this` is a dynamically scoped variable that takes on values depending on how the current function was invoked. Infernu knows about this (pun intended) and infers types for functions indicating what `this` must be.
115 |
116 | For example:
117 |
118 | function useThisData() {
119 | return this.data + 3;
120 | }
121 |
122 | Infernu infers:
123 |
124 | // useThisData : {data: Number, ..a}.(() -> Number)
125 |
126 | In words: a function which expects `this` to be an object with at least one property, "data" of type `Number`. It takes no arguments (hence the empty `()`). It returns a `Number`.
127 |
128 | If we call a function that needs `this` incorrectly, Infernu will be angry:
129 |
130 | Error: Could not unify:
131 | {data: Number, ..a}
132 | With:
133 | Undefined
134 |
135 | Because we called `useThisData` without a preceding object property access (e.g. `obj.useThisData`), it will get `undefined` for `this`. Infernu is telling us that our expected type for `this` is not unifiable with the type `undefined`.
136 |
137 | ### Polymorphism
138 |
139 | Given the following function:
140 |
141 | function makeData(x) {
142 | return {data: x};
143 | }
144 |
145 | Infernu infers the following type:
146 |
147 | a.(b -> {data: b})
148 |
149 | In words: A function that takes anything for its `this`, and an argument of any type `b`. It returns an object containing a single field, `data` of the same type `b` as the argument.
150 |
151 | ### Row-type polymorphism (static duck typing)
152 |
153 | Given the following function:
154 |
155 | function getData(obj) {
156 | return obj.data;
157 | }
158 |
159 | Infernu infers:
160 |
161 | h.({data: i, ..j} -> i)
162 |
163 | In words: a function taking any type `h` for `this`, and a parameter that contains **at least one property**, named "data" that has some type `i` (could be any type). The function returns the same type `i` as the data property.
164 |
165 |
166 | ### Type Classes
167 |
168 | See [here](docs/type-system.md#type-classes) for more about Infernu's type classes.
169 |
170 | The basic example is for the `+` operator:
171 |
172 | function add(x,y) { return x + y; }
173 |
174 | The type for `add` is inferred to be:
175 |
176 | // add : Plus b => a.((b, b) -> b)
177 |
178 | Meaning: given any type `a` that is an instance of the `Plus` type class, the function takes two `a`s and returns an `a`.
179 |
180 | The two instances of `Plus` currently defined are the types `Number` and `String`.
181 |
182 |
183 |
184 | ------------
185 |
186 | ## Author
187 |
188 | Noam Lewis - [@noam_lewis](https://twitter.com/noam_lewis)
189 |
190 | ## Road map
191 |
192 | The following needs to be done to make infernu reasonably usable:
193 |
194 | - Finish support for basic builtin JS apis.
195 | - Add support for browser / DOM / web apis.
196 | - Add ability to declare types for, or wrap external libraries.
197 | - Add support for some kind of module system.
198 | - Better error messages.
199 |
200 | ## Pending discussion
201 |
202 | Things that could be done, but may not be so important:
203 |
204 | - [ ] Allow empty var decls (use first assignment as starting point for types) - how to prevent uninitialized variable issues? General approach should be to add a translation pass that moves var decls down to the first assignment, but care must be taken to avoid escaping usage-before-assignment to an otherwise shadowed name.
205 | - [ ] When concluding that two recursive types are equivalent, use that information to simplify the resulting types (perhaps using the simpler of the two everywhere) - nice to have, because currently recursive types are displayed opaquely anyway.
206 |
207 | More important but also more complicated or impossible to implement:
208 |
209 | - [ ] Find a reasonable solution for optional parameters - perhaps using an implicit "Maybe"-like type or implicit type unions, and require guards?
210 | - [ ] An (implicitly inferred) maybe type, for better safety of things like array access at index. Unfortunately because the maybe wrap/unwrap will have to implicit, this isn't easy to solve. See branch `maybe`.
211 | - [ ] Sum types with guards as pattern matchers. Required because some functions, like array index access, can return 'undefined' (e.g. if index is out of range) - breaks parametricity and complicates the inference greatly.
212 | - [ ] Allow defining constructor-object properties using the notation `obj.prototype.something = ...` - requires non-local context to deteremine the type of a constructor function.
213 | - [ ] support `arguments` (a tuple?) and function `bind`
214 | - [ ] Should we treat functions as objects with properties? the only properties they have are: length (very weird! we might as well leave it out), and call/bind/apply (which need special handling)
215 |
216 | ### Future
217 |
218 | - [ ] Type annotations
219 | - [ ] Add support for CommonJS modules
220 |
221 | p.s. *(Formerly known as Inferno / Safe JS / SJS)*
222 |
223 |
225 |
227 |
--------------------------------------------------------------------------------
/Setup.hs:
--------------------------------------------------------------------------------
1 | import Distribution.Simple
2 | main = defaultMain
3 |
--------------------------------------------------------------------------------
/docs/dicussion.md:
--------------------------------------------------------------------------------
1 | **Note**: Most of this is outdated and/or unmaintained.
2 |
3 | # Discussions
4 |
5 | ## AST translation to core language: yes or no?
6 |
7 | There are two general approaches to implement type inference on a new syntax:
8 |
9 | 1. Implement type inference directly on the given AST
10 | 2. Map the AST to a simpler core language, and use well-understood algorithms to infer types on the core language. Finally, map the inferred types back to the original AST.
11 |
12 | Currently the (incomplete, buggy) code takes the first approach - it infers types directly on (a subset of) the JS syntax. However to make the code simpler and more robust, it may make more sense to take the translation approach.
13 |
14 | One advantage of the direct approach (no translation) is that it allows us to directly deal with language constructs such as while, for, if, return, and the quirks that come with them.
15 |
16 | ### Mapping to a core language
17 |
18 | One possible target core language is Damas-Hindley-Milner (simply typed lambda calculus with parameteric polymorphism and let-bindings) plus row type polymorphism, reference cells, and something similar to SML-style value restriction.
19 |
20 | The mapping will have to deal with statement sequences (function and if/while/for blocks), various statement types, etc. An example mapping is:
21 |
22 | - Statment sequences such as `st1; st2;` to `(\_ -> st2) st1`
23 | - `function f(a,b) { statements... }` to `\a -> \b -> ...`
24 | - `var x = 2; x = x + 1` to `let x = ref 2 in x := !x + 1`
25 | - and so on.
26 |
27 |
28 | ## Polymorphism and the value restriction
29 |
30 | Javascript variables are storage cells. If we compare JS to Standard ML, JS variables seem to be equivalent to `ref` values. SML has the concept of value restriction: but it applies to let-bound names, and `ref` variables in SML are *always* monotypes. If we followed suit when inferring JS variable types, all our variables would be monotypes and we would lose all polymorphism.
31 |
32 | A safe behavior for a JS variable can be either a monotype (`'a ref` in ML) or, when storing polymorphic values (such as an empty list or polymorphic functions), something more like an ocaml record with a mutable cell with a quantified type variable (forall).
33 |
34 | A mutable monotype variable has the same type as ocaml's `ref`, which is:
35 |
36 | ```ocaml
37 | type 'a ref = { mutable content : 'a } ;;
38 | ```
39 |
40 | Here's (in ocaml) a mutable variable that can only contain the polymorphic empty list. The inner record field has a `forall` quantifier on the type of the list item.
41 |
42 | ```ocaml
43 | type t1 = { mutable v1 : 'a. 'a list } ;; (* notice the quantifier 'a. *)
44 |
45 | let x = { v1 = [] } ;;
46 | x.v1 <- [] ;; (* not too many things we can put in there... *)
47 | ```
48 |
49 | More interesting is the type of a mutable variable that contains polymorphic functions of type `'a -> 'a list` (or `a -> [a]` if you prefer):
50 |
51 | ```ocaml
52 | type t2 = { mutable v2 : 'a. 'a -> 'a list } ;;
53 |
54 | let y = { v2 = fun x -> [ x; x; ] };;
55 | y.v2 3;; (* evaluates to [ 3; 3 ]*)
56 | y.v2 'a';; (* [ 'a'; 'a' ] *)
57 |
58 | y.v2 <- fun x -> [ x; x; x; ] ;;
59 | y.v2 3;; (* [ 3; 3; 3 ] *)
60 | etc..
61 | ```
62 |
63 | ### Possibly solutions to polymorphism of mutable variables
64 |
65 | 1. Infer types, and allow full polymorphism of mutable variables, allowing even nonsense like `x = 'a'; x = 3;`, which makes this solution useless.
66 | 2. Infer types, and disable polymorphism (treating JS vars like ML `ref`s). Variables are still allowed to be of types where **`a` is free** (such as `a -> a`), but not allowed to be of **closed types** (such as `forall a. a -> a`).
67 | 3. Don't infer types - require programmer to annotate code with type ascriptions. Interpret all type variables as universally quantified, e.g. `a` is interpreted as `forall a. a` (and no value can ever be assigned) or more usefully, `a -> [a]` will be interpreted as `forall a. a -> [a]` (and many list-constructing functions will inhabit this type). This approach is similar to ocaml's **mutable record fields**.
68 | 4. Infer types, but allow polymorphism only in certain cases. **TODO: Which cases?**
69 |
70 |
71 | ### Examples
72 |
73 | Consider the following examples:
74 |
75 | ```javascript
76 | var x = 1;
77 | x = 'a'; // should not type check
78 | ```
79 |
80 | Here we have assigned two types to the same variable (recall that variables are reference cells), which should not be allowed.
81 |
82 | ```javascript
83 | var x = [];
84 | x = [1];
85 | x = ['a']; // should not type check
86 | ```
87 |
88 | Here again we used two types. Unlike the previous example, we initially assign a value with unknown type (`[a]`, but we don't know what `a` is).
89 |
90 | ```javascript
91 | var x;
92 | x = 1;
93 | x = 'a'; // should not type check
94 | ```
95 |
96 | In the above example we have no information at all about `x`'s type at declaration time. There is no analogy for this in SML-like languages.
97 |
98 | ```javascript
99 | var f = function (x) { return x; }
100 | var n = f(1);
101 | var s = f('a'); // ok
102 | ```
103 |
104 | Here we have assigned a polymorphic function (of type `a -> a`) to the variable `f`. Later we invoke this function twice, using different types each time, which should be allowed. This is an example of desired polymorphism.
105 |
106 | ```javascript
107 | var f = function() { return function(x) { return x; } }
108 | var g = f(); // g should also be polymorphic
109 | var n = g(1);
110 | var s = g('a'); // this is ok, but value restriction would not allow this to type check
111 | ```
112 |
113 | Here again we make use of polymorphism. However, because we're assigning `g` from an expression that isn't a syntactic value (`f()` is a function invocation), languages such as SML will restrict `g` to a monotype and unify `g` to type `number -> number` after the call to `g(1)`. When designing our type system we must consider applying this limitation, to avoid other problems that the value restriction was designed to avoid.
114 |
115 | ```javascript
116 | var x = 1;
117 | var f = function(y) { var res = x; x = y; return res; } // should get type: number -> number
118 | ```
119 |
120 | The above function, in pure (dynamically typed) JS will return the value that was passed "last time" the function was called, regardless of its type. With unmodified HM, the inferred type is `number -> number` because `x` has been assigned a number `1`, and everything is fine. What should happen when `x` is not assigned (only declared)?
121 |
122 | ```javascript
123 | var x;
124 | var f = function(y) { var res = x; x = y; return res; } // type ???
125 | ```
126 |
127 | A variable's type can't be determined at declaration time (`var x;`). Only when the variable is assigned `x = expr` we can infer its type. The declaration serves simply to bind the variable's name to the current scope and to possibly shadow variables declared in outer scopes (a variable's scope in JS is always the nearest function, if any, or otherwise the global scope).
128 |
129 | To solve this problem we must "wait" until the first assignment to the newly declared variable occurs.
130 |
131 | ### Desired polymorphism
132 |
133 | - function polymorphism (function calls instantiate type schemes)
134 | - row type polymorphism
135 |
136 |
137 | Implementation options:
138 |
139 | 1. In unification allow also negative rules such as: t != function, and we can use them in assignment expressions (in `f = ...` => f must not be a function)
140 | 2. In call expressions, tell the lvalue subexpr that it can instantiate a type scheme. Otherwise, var exprs do not really instantiate type schemes (they don't allow any bound tvars)
141 | 3. Switch from type schemes to "forall" as a basic type (higher order types):
142 |
143 | example (of option 3):
144 |
145 | makeFunc :: () -> (forall t. t -> t)
146 | obj.a = makeFunc() // will get: obj.a :: forall t. t -> t
147 |
148 |
149 | Examples:
150 |
151 | var x, y, f, g, h;
152 | x = { a: 1 }; // x has type '{ "a": number, ... }'
153 | x = 2; // type error
154 | y = { a: 2, b: 'bla' }; // y has type '{ "a": number, "b": string, ... }'
155 | x = y; // OK! x is more general (a la 'subtyping' but not really)
156 | y = x; // type error - x's type has no "b" field
157 |
158 | // f has type *scheme*: \t -> function(t) : [t]
159 | f = function(a) { return [a]; };
160 | f = 2; // type error
161 | g = f(2); // g has type [number]
162 | h = f('a'); // h has type [string] -- crucial point! function calls cause instantiation of type scheme
163 |
164 |
165 | # Normalizing recursive types on outermost scope
166 |
167 | t = { method: t, x: String } -> Int
168 | { method: { method: t, x: String } -> Int, x: String } -> Int
169 |
170 | => o = { method: t, x: String }
171 |
172 | t = o -> Int
173 |
174 | => o = { method: o -> Int, x: String }
175 |
176 | { method: o -> Int, x: String } -> Int
177 | o -> Int
178 |
179 |
180 | # Higher-rank polymorphism for object methods
181 |
182 | See:
183 |
184 | - http://blog.huoc.org/higher-rank-polymorphism.html
185 | - "Semi-Explicit First-Class Polymorphism for ML" (1999), by Jacques Guarrigue and Didier Rémy. (1999) http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.46.4858
186 |
187 |
--------------------------------------------------------------------------------
/docs/infernu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sinelaw/infernu/e8b7aff6ea628c9099fb82aa011eb5c12aca36ba/docs/infernu.png
--------------------------------------------------------------------------------
/docs/infernu.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
109 |
--------------------------------------------------------------------------------
/docs/inst-graph.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sinelaw/infernu/e8b7aff6ea628c9099fb82aa011eb5c12aca36ba/docs/inst-graph.png
--------------------------------------------------------------------------------
/docs/make.sh:
--------------------------------------------------------------------------------
1 | pandoc --from=markdown_github+lhs --to=markdown_github semantics.md.lhs -o out/semantics.md
2 |
--------------------------------------------------------------------------------
/docs/memoize.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | // 'f' is a function that takes a recursion function, and the current value. It should call the recursion function instead of itself when it needs to recurse.
3 | function memoize(toKeyString, f) {
4 | var mem = {};
5 |
6 | return function memed(x) {
7 | var key = toKeyString(x);
8 | mem[key] = mem[key] || f(memed, x);
9 | return mem[key];
10 | };
11 | }
12 |
13 | var facTest = memoize(String, function fac(memed, x) { if (x < 2) { return 1; } return x * memed(x - 1); });
14 |
15 | // The number of n-dimensional vectors whose scalar's sums to k
16 | // Hat-tip to https://stackoverflow.com/questions/3242597/what-is-memoization-good-for-and-is-it-really-all-that-helpful
17 | var nkTest = memoize(
18 | function (x) {
19 | return String(x.k) + ', ' + String(x.n); },
20 | function f(m, x) {
21 | if (x.k === 0) { return 1; }
22 | if (x.n === 0) { return 0; }
23 | return m({ n: x.n-1, k: x.k }) + m({ n: x.n, k: x.k-1 }) + m({ n: x.n-1, k: x.k-1 });
24 | });
25 |
26 |
27 | nkTest({ n: 30, k: 30 });
28 |
--------------------------------------------------------------------------------
/docs/semantics.hs:
--------------------------------------------------------------------------------
1 | -- | Semantics of JS according to Infernu
2 | -- These definitions don't necessarily correspond to ECMAScript.
3 |
4 | module Semantics where
5 |
6 | import Data.Map (Map)
7 | import qualified Data.Map as Map
8 |
9 | -- | The term language
10 |
11 | newtype Id = Id String
12 | deriving (Show, Eq, Ord)
13 |
14 | data Value = VString String
15 | | VBool Bool
16 | | VNum Double
17 | | VUndef
18 | | VRecord (Map String Location)
19 | | VFun (RealWorld -> Value -> Value -> (RealWorld, Value))
20 |
21 | instance Show Value where
22 | show (VString s) = "VString " ++ show s
23 | show (VBool b) = "VBool " ++ show b
24 | show (VNum b) = "VNum " ++ show b
25 | show VUndef = "undefined"
26 | show (VRecord props) = "VRecord " ++ show props
27 | show (VFun _) = "VFun" -- "VFun (" ++ show props ++ ", )"
28 |
29 | data Operator = Plus | Minus | GreaterThan
30 | deriving (Show)
31 |
32 | data Expr = Var String
33 | | Lit Value
34 | | App Expr Expr
35 | | Abs String Statement
36 | | Op Operator Expr Expr
37 | deriving (Show)
38 |
39 | (^+^) :: Expr -> Expr -> Expr
40 | (^+^) x y = Op Plus x y
41 | (^-^) x y = Op Minus x y
42 | (^>^) x y = Op GreaterThan x y
43 | (^$^) f x = App f x
44 |
45 |
46 | data Statement = Empty
47 | | Seq Statement Statement
48 | | Return Expr
49 | | JustExpr Expr
50 | | VarDecl String
51 | | Assign String Expr
52 | | While Expr Statement
53 | | If Expr Statement Statement
54 | -- Let String Expr Statement
55 | deriving (Show)
56 |
57 | (^=) x v = Assign x v
58 |
59 | -- | RealWorld
60 |
61 | newtype Location = Location Int deriving (Ord, Eq, Show)
62 | data RealWorld = RealWorld (Map Location Value) Location
63 | deriving (Show)
64 |
65 | initialWorld :: RealWorld
66 | initialWorld = RealWorld Map.empty (Location 0)
67 |
68 | alloc :: RealWorld -> (RealWorld, Location)
69 | alloc (RealWorld m (Location l)) = ( RealWorld m (Location (l + 1))
70 | , Location l)
71 |
72 | store :: Location -> Value -> RealWorld -> RealWorld
73 | store l v (RealWorld m maxLoc) = RealWorld (Map.insert l v m) maxLoc
74 |
75 | load :: RealWorld -> Location -> Value
76 | load (RealWorld m _) l = case Map.lookup l m of
77 | Just v -> v
78 | Nothing -> error $ "Unallocated location: " ++ show l
79 |
80 | -- | Environment
81 |
82 | data Env = Env { bindings :: [(Id, Location)]
83 | , returns :: [Value] }
84 | deriving (Show)
85 |
86 | emptyEnv :: Env
87 | emptyEnv = Env [] []
88 |
89 | push :: Id -> Location -> Env -> Env
90 | push x l (Env bs rs) = Env ((x, l) : bs) rs
91 |
92 | pushReturn :: Value -> Env -> Env
93 | pushReturn r (Env bs rs) = Env bs (r:rs)
94 |
95 | popReturn :: Env -> (Env, Value)
96 | popReturn (Env bs []) = error "No more returns"
97 | popReturn (Env bs (r:rs)) = (Env bs rs, r)
98 |
99 | lookup' :: Id -> Env -> Location
100 | lookup' x (Env bs _) = case lookup x bs of
101 | Just p -> p
102 | Nothing -> error $ "Undeclared name: " ++ show x ++ " not in env: " ++ show bs
103 |
104 | get :: Id -> Env -> RealWorld -> Value
105 | get x env rw = load rw $ lookup' x env
106 |
107 | location :: Id -> Env -> Location
108 | location = lookup'
109 |
110 | -- | Expressions
111 |
112 | emean :: Expr -> Env -> RealWorld -> (RealWorld, Value)
113 |
114 | emean (Lit v) = \_env rw -> (rw, v)
115 |
116 | emean (Var x) = \env rw -> (rw, get (Id x) env rw)
117 |
118 | emean (Op op x y) = \env rw ->
119 | case emean x env rw of
120 | (rw', VNum vx) -> case emean y env rw' of
121 | (rw'', VNum vy) -> (rw'', vx `f` vy)
122 | where f = case op of
123 | Plus -> \a b -> VNum (a + b)
124 | Minus -> \a b -> VNum (a - b)
125 | GreaterThan -> \a b -> VBool (a > b)
126 |
127 | -- | Function expressions
128 | --
129 | -- The meaning of Abs is a function that takes values and returns a value.
130 | --
131 | -- Because the function takes values, not locations, and because there
132 | -- is no "pointer" in the language, calls are by value. The record
133 | -- value is special because it's a map from names to locations, so you
134 | -- can mutably modify the data.
135 | --
136 | -- Return values are passed using a special "return" name pushed onto
137 | -- the environment. The result of a function is the value bound in the
138 | -- environment to that value when the function completes.
139 |
140 | emean (Abs args body) =
141 | \env rw ->
142 | ( rw
143 | , VFun
144 | $ \rw1 ->
145 | \this args' ->
146 | let (rw2, argLoc) = alloc rw1
147 | (rw3, thisLoc) = alloc rw2
148 | rw4 = store argLoc args' rw3
149 | rw5 = store thisLoc this rw4
150 | bodyEnv =
151 | push (Id args) argLoc
152 | . push (Id "this") thisLoc
153 | $ env
154 |
155 | in case (smean body) halt bodyEnv rw5 of
156 | (env'', rw6) -> (rw6, snd $ popReturn env'')
157 | )
158 |
159 | -- | Function Call
160 |
161 | emean (App f args) =
162 | \env rw ->
163 | case emean f env rw of
164 | (rw', VFun f') -> case (emean args env rw') of (rw'', v) -> f' rw'' VUndef v
165 | _ -> error "Expected a function"
166 |
167 |
168 |
169 | -- | Statements
170 |
171 | halt :: Env -> RealWorld -> (Env, RealWorld)
172 | halt = (,)
173 |
174 | smean :: Statement
175 | -> (Env -> RealWorld -> (Env, RealWorld))
176 | -> Env -> RealWorld -> (Env, RealWorld)
177 |
178 | smean Empty = id
179 |
180 | -- | Return statement
181 |
182 | smean (Return expr) =
183 | \k env rw -> case (emean expr) env rw of
184 | (rw', val) -> halt (pushReturn val env) rw'
185 |
186 | -- | Expression statements
187 |
188 | smean (JustExpr expr) = \k env rw -> k env . fst . (emean expr) env $ rw
189 |
190 | -- | Statement sequence ;
191 |
192 | smean (Seq stmtA stmtB) = \k env rw ->
193 | smean stmtA (smean stmtB k) env rw
194 |
195 | -- | (Mutable) variable declaration
196 |
197 | smean (VarDecl x) = \k env rw ->
198 | case alloc rw of
199 | (rw', loc) -> k (push (Id x) loc env) rw'
200 |
201 | -- | Assignment
202 |
203 | smean (Assign x expr) = \k env rw ->
204 | case ((emean expr) env rw) of
205 | (rw', val) -> k env (store (location (Id x) env) val rw')
206 |
207 | -- | While loop
208 |
209 | smean (While expr body) = while
210 | where
211 | while =
212 | \k env rw ->
213 | case (emean expr) env rw of
214 | (rw', VBool False) -> k env rw'
215 | (rw', VBool True) -> smean body (while k) env rw'
216 | _ -> error "Expected boolean"
217 |
218 | --Note: Recursive let using in the meaning here. It should be the same as using `fix`.
219 |
220 | -- | If statement
221 |
222 | smean (If expr b1 b2) =
223 | \k env rw ->
224 | case (emean expr) env rw of
225 | (rw', VBool True) -> (smean b1) k env rw'
226 | (rw', VBool False) -> (smean b2) k env rw'
227 | _ -> error "Expected boolean"
228 |
229 |
230 |
231 | -- | Non-JS fragments
232 |
233 | --These syntax constructs are added:
234 |
235 | -- | Let expression (immutable variable)
236 |
237 | --Non-recursive ('x' not free in 'expr'):
238 |
239 | -- smean (Let x expr stmt) = \k env rw ->
240 | -- case (emean expr) env rw of
241 | -- (rw', v) -> (smean stmt) k (push (Id x) v env) rw'
242 |
243 | --Recursive:
244 |
245 | -- s[[ let x = expr in stmt ]] = s[[ let x = fix(\x -> expr) in stmt ]]
246 |
247 | --Where `x` is free in `expr`.
248 |
249 | --Note that this definition restricts to non-polymorphic recursion.
250 |
251 |
252 |
253 | -- | Fix
254 |
255 | --TODO
256 |
257 | -- e[[ fix ]] = \env -> \rw ->
258 | block = foldr Seq Empty
259 |
260 | -- | test
261 | -- >>> snd $ emean (testReturn True) emptyEnv initialWorld
262 | -- VNum 1.0
263 | testReturn :: Bool -> Expr
264 | testReturn f = (App (Abs "y"
265 | (Seq
266 | (Return (Lit $ VNum 1) )
267 | (If (Var "y")
268 | (Return (Lit $ VNum 2))
269 | (Return (Lit $ VNum 3))
270 | ))
271 | )
272 | (Lit $ VBool f))
273 |
274 | -- | test
275 | -- >>> snd $ emean (testWhile 3) emptyEnv initialWorld
276 | -- VNum 6.0
277 | testWhile :: Double -> Expr
278 | testWhile x = App (Abs "x"
279 | $ block [ sumUpToXIntoY
280 | , Return (Var "y")
281 | ]
282 | ) (Lit $ VNum x)
283 | where
284 | sumUpToXIntoY =
285 | block
286 | [ VarDecl "y"
287 | , "y" ^= Lit (VNum 0)
288 | , While
289 | (Var "x" ^>^ Lit (VNum 0))
290 | $ block
291 | [ "y" ^= (Var "y" ^+^ Var "x")
292 | , "x" ^= (Var "x" ^-^ Lit (VNum 1))
293 | ]
294 | ]
295 |
296 | -- | test
297 | -- >>> snd $ emean (test True) emptyEnv initialWorld
298 | -- VNum 2.0
299 | test :: Bool -> Expr
300 | test f = (App (Abs "y"
301 | (If (Var "y")
302 | (Return (Lit $ VNum 2))
303 | (Return (Lit $ VNum 3))
304 | )
305 | )
306 | (Lit $ VBool f))
307 |
308 |
--------------------------------------------------------------------------------
/doctest.sh:
--------------------------------------------------------------------------------
1 | find src -iname "*.hs" | xargs doctest
2 |
--------------------------------------------------------------------------------
/infernu.cabal:
--------------------------------------------------------------------------------
1 | name: infernu
2 | version: 0.0.0.1
3 | synopsis: Type inference and checker for JavaScript (experimental)
4 | description: This version is highly experimental and may set your computer on fire (also, a lot of JS is not supported yet, so it may not be very useful.)
5 | .
6 | Infernu is a type checker for JavaScript. Since JavaScript is dynamically and weakly typed, it makes no sense to talk about "type errors" in arbitrary JavaScript code.
7 | .
8 | Consequently Infernu makes assumptions about the code and expects it to follow certain rules that
9 | are not required by plain JavaScript (for example, implicit coercions such as `3 + 'a'` are not
10 | allowed.)
11 | .
12 | Infernu's type system is designed for writing dynamic-looking code in a safe statically type-checked
13 | environment. Type annotations are not required (though they would be nice to support, for various
14 | reasons). Instead, Infernu *infers* the types of expressions by examining the code. If the inferred
15 | types contradict each other, Infernu reports the contradiction as an error.
16 | .
17 | Infernu places restrictions on JS programs that are otherwise valid. In other words, Infernu is a
18 | **subset of JavaScript**. Infernu tries to strike a balance between type system complexity and
19 | dynamic-style coding flexibility.
20 | .
21 | See the .md files included in the package for more information.
22 |
23 | license: GPL-2
24 | homepage: https://github.com/sinelaw/infernu
25 | bug-reports: https://github.com/sinelaw/infernu/issues
26 | license-file: LICENSE
27 | author: Noam Lewis
28 | maintainer: jones.noamle@gmail.com
29 | copyright: Noam Lewis, 2014-2015
30 | -- category:
31 | build-type: Simple
32 | extra-source-files: README.md
33 | cabal-version: >=1.10
34 | stability: experimental
35 |
36 | source-repository head
37 | type: git
38 | location: git@github.com:sinelaw/infernu.git
39 |
40 |
41 | flag quickcheck
42 | default: False
43 | manual: True
44 |
45 | flag trace
46 | default: False
47 | manual: True
48 |
49 | flag debug
50 | default: False
51 | manual: True
52 |
53 | library
54 | hs-source-dirs: src
55 | exposed-modules: Infernu.Builtins.Array
56 | , Infernu.Builtins.Date
57 | -- WIP: Dom
58 | --, Infernu.Builtins.DOM
59 | , Infernu.Builtins.Math
60 | , Infernu.Builtins.Names
61 | , Infernu.Builtins.Number
62 | , Infernu.Builtins.Object
63 | , Infernu.Builtins.Operators
64 | , Infernu.Builtins.Regex
65 | , Infernu.Builtins.String
66 | , Infernu.Builtins.StringMap
67 | , Infernu.Builtins.TypeClasses
68 | , Infernu.Builtins
69 | , Infernu.Decycle
70 | , Infernu.Expr
71 | , Infernu.Fix
72 | , Infernu.Infer
73 | , Infernu.InferState
74 | , Infernu.Lib
75 | , Infernu.Log
76 | , Infernu.Options
77 | , Infernu.Parse
78 | , Infernu.Prelude
79 | , Infernu.Pretty
80 | , Infernu.Source
81 | , Infernu.Types
82 | , Infernu.Unify
83 | , Infernu.Util
84 | other-modules: Infernu.Builtins.Util
85 | -- TODO: use only mtl (not transformers)
86 | build-depends: base >= 4.6 && < 5, base-compat, base-orphans, mtl, containers, transformers, either, language-ecmascript, digits, parsec, optparse-applicative, ansi-wl-pprint, unordered-containers, hashable
87 | default-language: Haskell2010
88 | default-extensions: NoImplicitPrelude
89 | ghc-options: -Wall -O2 -rtsopts -threaded
90 | ghc-prof-options: -threaded -auto-all -caf-all -O2 -rtsopts
91 | if flag(debug)
92 | ghc-options: -g
93 | if flag(trace)
94 | cpp-options: -DTRACE
95 | if flag(quickcheck)
96 | cpp-options: -DQUICKCHECK
97 | build-depends: QuickCheck, derive
98 | default-extensions: TemplateHaskell, DeriveGeneric, FlexibleInstances
99 |
100 | executable infernu
101 | main-is: Main.hs
102 | build-depends: base, parsec, infernu, optparse-applicative, ansi-wl-pprint
103 | default-language: Haskell2010
104 | default-extensions: NoImplicitPrelude
105 | ghc-options: -Wall -O2 -rtsopts -threaded
106 | ghc-prof-options: -threaded -auto-all -caf-all -O2 -rtsopts
107 | if flag(debug)
108 | ghc-options: -g
109 | if flag(trace)
110 | cpp-options: -DTRACE
111 | if flag(quickcheck)
112 | cpp-options: -DQUICKCHECK
113 | build-depends: QuickCheck, derive
114 | default-extensions: TemplateHaskell, DeriveGeneric
115 |
116 | executable infernu-demo
117 | main-is: Demo.hs
118 | hs-source-dirs: test
119 | build-depends: base, infernu, ansi-wl-pprint
120 | default-language: Haskell2010
121 | default-extensions: NoImplicitPrelude
122 | ghc-options: -Wall -O2 -main-is Demo
123 | if flag(trace)
124 | cpp-options: -DTRACE
125 | if flag(quickcheck)
126 | cpp-options: -DQUICKCHECK
127 | build-depends: QuickCheck, derive
128 | default-extensions: TemplateHaskell, DeriveGeneric
129 |
130 | executable test
131 | if flag(quickcheck)
132 | cpp-options: -DQUICKCHECK
133 | build-depends: QuickCheck, derive
134 | default-extensions: TemplateHaskell, DeriveGeneric
135 | else
136 | buildable: False
137 | main-is: Test.hs
138 | hs-source-dirs: test
139 | build-depends: base, infernu
140 | default-language: Haskell2010
141 | default-extensions: NoImplicitPrelude
142 | ghc-options: -Wall -O2 -main-is Test
143 | if flag(trace)
144 | cpp-options: -DTRACE
145 |
--------------------------------------------------------------------------------
/lambdabot-plugin/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2014 Noam Lewis
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be included
12 | in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/lambdabot-plugin/Lambdabot/Plugin/Infernu.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE MultiParamTypeClasses #-}
2 | {-# LANGUAGE TemplateHaskell #-}
3 | {-# LANGUAGE TypeSynonymInstances #-}
4 | -- | Infernu
5 | module Lambdabot.Plugin.Infernu (infernuPlugin) where
6 |
7 | import Data.Maybe (catMaybes)
8 | import qualified Data.ByteString.Char8 as P
9 | import Data.Functor ((<$>))
10 | import Data.List (intersperse)
11 | import qualified Data.Map as Map
12 | import Lambdabot.Plugin
13 | import qualified Language.ECMAScript3.Parser as ES3Parser
14 | import qualified Language.ECMAScript3.Syntax as ES3
15 |
16 | import Infernu.Infer (getAnnotations, minifyVars,
17 | pretty, runTypeInference)
18 | import Infernu.Parse (translate)
19 | import Infernu.Types (GenInfo(..), Source(..))
20 |
21 | import Text.PrettyPrint.ANSI.Leijen (plain, displayS, renderPretty, Doc)
22 |
23 | infernuPlugin :: Module ()
24 | infernuPlugin = newModule
25 | { moduleCmds = return
26 | [ (command "js")
27 | { aliases = []
28 | , help = say "js "
29 | , process = say . sayType
30 | }
31 | ]
32 | }
33 |
34 | showWidth :: Int -> Doc -> String
35 | showWidth w x = displayS (renderPretty 0.4 w x) ""
36 |
37 | showDoc = showWidth 120
38 |
39 | --sayType :: Monad m => String -> Cmd m ()
40 | sayType :: String -> String
41 | sayType rest = case runTypeInference . fmap Source . translate . ES3.unJavaScript <$> ES3Parser.parseFromString rest of
42 | Left e -> show e
43 | Right res -> case getAnnotations <$> res of
44 | Left e' -> showDoc $ pretty e'
45 | Right [] -> show "There is nothing there."
46 | Right xs -> concat . intersperse "\n" $ filterGen xs
47 | where filterGen = catMaybes . map (\(Source (GenInfo g n, _), t) ->
48 | case (g,n) of
49 | (False, Just n) -> Just (showDoc (plain $ pretty n) ++ " : " ++ (showDoc $ plain $ pretty $ minifyVars t))
50 | _ -> Nothing
51 | )
52 |
53 |
--------------------------------------------------------------------------------
/lambdabot-plugin/Setup.hs:
--------------------------------------------------------------------------------
1 | import Distribution.Simple
2 | main = defaultMain
3 |
--------------------------------------------------------------------------------
/lambdabot-plugin/lambdabot-plugin-inferno.cabal:
--------------------------------------------------------------------------------
1 | -- Initial lambdabot-plugin-infernu.cabal generated by cabal init. For further
2 | -- documentation, see http://haskell.org/cabal/users-guide/
3 |
4 | name: lambdabot-plugin-infernu
5 | version: 0.1.0.0
6 | synopsis: Lambdabot plugin for infernu
7 | -- description:
8 | license: MIT
9 | license-file: LICENSE
10 | author: Noam Lewis
11 | maintainer: jones.noamle@gmail.com
12 | -- copyright:
13 | -- category:
14 | build-type: Simple
15 | -- extra-source-files:
16 | cabal-version: >=1.10
17 |
18 | library
19 | exposed-modules: Lambdabot.Plugin.Infernu
20 | -- other-modules:
21 | -- other-extensions:
22 | other-extensions: TemplateHaskell, MultiParamTypeClasses, TypeSynonymInstances
23 | build-depends: base >=4.7 && < 5, lambdabot-core, containers, bytestring, language-ecmascript, ansi-wl-pprint, infernu >= 0.0.0.1
24 | -- build-depends: base >=4.7 && <4.8, language-ecmascript, infernu == 0.1.0.0
25 | -- hs-source-dirs:
26 | default-language: Haskell2010
27 |
--------------------------------------------------------------------------------
/others/ConstId.cs:
--------------------------------------------------------------------------------
1 | class Test
2 | {
3 | public Func ConstId(U a)
4 | {
5 | return x => x;
6 | }
7 | }
--------------------------------------------------------------------------------
/others/GetPut.java:
--------------------------------------------------------------------------------
1 | public class GetPut
2 | {
3 | private T _value;
4 |
5 | public T Get()
6 | {
7 | return _value;
8 | }
9 |
10 | public T Put(T x)
11 | {
12 | T old = _value;
13 | _value = x;
14 | return old;
15 | }
16 |
17 | }
18 |
19 |
--------------------------------------------------------------------------------
/others/flow/.flowconfig:
--------------------------------------------------------------------------------
1 | [ignore]
2 |
3 | [include]
4 |
5 | [libs]
6 |
7 | [options]
8 |
--------------------------------------------------------------------------------
/others/flow/debounce.js:
--------------------------------------------------------------------------------
1 | function f1(x) { return x; }
2 | function debounce(f) {
3 | var current = [];
4 | var has = false;
5 | return function(arg) {
6 | var old = has ? current[0] : arg;
7 | has = true;
8 | current = [arg];
9 | return f(old);
10 | };
11 | }
12 | var fonce = debounce(function (x) { return x[0]; });
13 | var x = fonce([3]);
14 | var fonce2 = debounce(function (x) { return x[0]; });
15 | var y = fonce2(['s']);
16 | //x.length;
17 |
--------------------------------------------------------------------------------
/others/flow/guards.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | function foo(b) { if (b) { return 21; } else { return ''; } }
3 | function bar(b) { // : number
4 | var x = foo(b);
5 | var y = foo(b);
6 | if (typeof x == 'number' && typeof y == 'number') { return x + y; }
7 | return 0;
8 | }
9 |
10 | function poly(x : X) : X {
11 | if (typeof x == 'number') { return 0; }
12 | return x;
13 | }
--------------------------------------------------------------------------------
/others/flow/id.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | var id = function (x) { return x; };
3 | var x = id(3);
4 | var y = id('s');
5 | var z = x.length;
6 |
--------------------------------------------------------------------------------
/others/flow/memoize/memoize.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 |
3 | function memoize(f) {
4 | var mem = {};
5 |
6 | return function(x) {
7 | var key = String(x);
8 | mem[key] = mem[key] || function(){return f(x);};
9 | return mem[key]();
10 | };
11 | }
12 |
13 | var test = memoize(function(x) { return x + 2; });
14 |
15 | test('a');
16 |
--------------------------------------------------------------------------------
/others/flow/problem-id.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | var id = function (x) { return x; };
3 | var x = id(3);
4 | var y = id('s');
5 |
6 |
--------------------------------------------------------------------------------
/others/flow/problem.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | function debounce(f) {
3 | return function(arg) {
4 | return f(arg);
5 | };
6 | }
7 | var id = function (x) { return x; };
8 | var id1 = debounce(id);
9 | var x = id1(3);
10 | var id2 = debounce(id);
11 | var y = id2('s');
12 | //x.length;
13 |
--------------------------------------------------------------------------------
/others/flow/row.js:
--------------------------------------------------------------------------------
1 | /* flow */
2 | function f(x) {
3 | return x.length;
4 | }
5 |
6 | //var x = f({ length: 2});
7 | //var y = x.foo;
8 |
9 | var a = [0,1,2,'b'];
10 |
11 | var x = 1+2;
12 | var y = a[x];
13 |
14 | var z = y.length;
15 |
16 |
--------------------------------------------------------------------------------
/others/flow/simple-bug.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | var y = 2;
3 | y = 'hi';
4 |
5 | var r = y + 2;
6 |
7 | var l = r.length;
8 |
9 |
10 | // flow allows all of the above without errors or warnings.
11 | // r will be 'hi2' and l will be equal to 3.
12 |
13 |
--------------------------------------------------------------------------------
/others/lint/string_constr.js:
--------------------------------------------------------------------------------
1 | var Foo = String;
2 | var x = new Foo('b');
3 |
--------------------------------------------------------------------------------
/others/notabug.caml:
--------------------------------------------------------------------------------
1 | let x = ref None ;;
2 | let setX v = x := Some v ;;
3 | let y = setX (fun b -> b) ;;
4 | let z = setX (fun _ -> "a") ;;
5 | let z' = match !x with
6 | Some f -> f 2;;
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/others/typescript/bugs.ts:
--------------------------------------------------------------------------------
1 | b = 3; // var hoisting
2 | var a = 'a';
3 | var b = +a; // implicit conversion to string
4 | var c = !a; // implicit conversion to bool
5 | delete a; // delete works on anything, not just object properties
6 | var d = void a; // why do we need void?
7 | var e = this.foo; // 'this'' has same horrible behavior as in javascript (and is always in context)
8 |
--------------------------------------------------------------------------------
/others/typescript/memoize.ts:
--------------------------------------------------------------------------------
1 | /* @flow */
2 |
3 | function memoize(f) {
4 | var mem = {};
5 |
6 | return function(x) {
7 | var key = String(x);
8 | mem[key] = mem[key] || function(){return f(x);};
9 | return mem[key]();
10 | };
11 | }
12 |
13 | var test = memoize(function(x) { return x + 2; });
14 |
15 | test('a');
16 |
--------------------------------------------------------------------------------
/src/Infernu/Builtins.hs:
--------------------------------------------------------------------------------
1 | module Infernu.Builtins
2 | ( builtins
3 | , arrayRowType
4 | , dateRowType
5 | , regexRowType
6 | , stringRowType
7 | , numberRowType
8 | , stringMapRowType
9 | )
10 | where
11 |
12 | import Infernu.Builtins.Array (arrayRowType)
13 | import Infernu.Builtins.Date (dateRowType)
14 | import qualified Infernu.Builtins.Operators as Operators
15 | import Infernu.Types (TypeScheme)
16 | import Infernu.Expr (EVarName)
17 | import Infernu.Builtins.Regex (regexRowType)
18 | import Infernu.Builtins.String (stringRowType)
19 | import Infernu.Builtins.Number (numberRowType)
20 | import Infernu.Builtins.StringMap (stringMapRowType)
21 |
22 | import Data.Map (Map)
23 |
24 | builtins :: Map EVarName TypeScheme
25 | builtins = Operators.builtins
26 |
27 |
--------------------------------------------------------------------------------
/src/Infernu/Builtins/Array.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE TupleSections #-}
2 | module Infernu.Builtins.Array
3 | (arrayRowType)
4 | where
5 |
6 | import Control.Monad (foldM)
7 |
8 | import Infernu.Builtins.Util
9 | import Infernu.InferState (Infer)
10 | import Infernu.Prelude
11 | import Infernu.Types
12 | import Infernu.Expr (EPropName(..))
13 |
14 | arrayProps :: Type -> [(String, TypeScheme)]
15 | arrayProps elemType = let aType = array elemType in
16 | [ ("length", ty number)
17 | , ("concat", ty $ func aType aType aType)
18 | -- TODO support thisArg (requires type variables)
19 | , ("every", ts [] $ func aType (funcN [tvar 1, elemType] boolean) boolean) -- missing thisArg
20 | , ("filter", ts [] $ func aType (funcN [tvar 1, elemType] boolean) aType) -- missing thisArg
21 | , ("forEach", ts [2] $ func aType (funcN [tvar 1, elemType] (tvar 2)) undef) -- missing thisArg
22 | -- TODO support optional argument for fromIndex (last parameter)
23 | , ("indexOf", ty $ funcN [aType, elemType, number] number)
24 | , ("join", ty $ func aType string string)
25 | , ("lastIndexOf", ty $ func aType number number)
26 | , ("map", ts [2] $ func aType (funcN [tvar 1, elemType] (tvar 2)) (array $ tvar 2))
27 | , ("pop", ty $ funcN [aType] elemType)
28 | , ("push", ty $ funcN [aType, elemType] number)
29 | , ("reduce", ts [1] $ funcN [aType, funcN [tvar 0, tvar 1, elemType] (tvar 1), (tvar 1)] (tvar 1))
30 | , ("reverse", ty $ funcN [aType] aType)
31 | , ("shift", ty $ funcN [aType] elemType)
32 | , ("slice", ty $ funcN [aType, number, number] aType)
33 | , ("some", ts [] $ func aType (funcN [tvar 0, elemType] boolean) aType) -- missing thisArg
34 | , ("sort", ts [] $ func aType (funcN [tvar 0, elemType, elemType] number) aType)
35 | , ("splice", ty $ funcN [aType, number, number] aType)
36 | , ("unshift", ty $ funcN [aType] elemType)
37 | ]
38 |
39 | -- TODO: when inserting builtin types, do fresh renaming of scheme qvars
40 | arrayRowType :: Type -> Infer (TRowList Type)
41 | arrayRowType elemType = TRowProp (TPropGetName EPropSetIndex) (ty $ funcN [aType, number, elemType] undef)
42 | . TRowProp (TPropGetName EPropGetIndex) (ty $ func aType number elemType)
43 | <$> foldM addProp (TRowEnd Nothing) (arrayProps elemType)
44 | where aType = array elemType
45 |
--------------------------------------------------------------------------------
/src/Infernu/Builtins/DOM.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE TupleSections #-}
2 | module Infernu.Builtins.DOM
3 | where
4 |
5 | import Infernu.Builtins.Util
6 | import Infernu.Prelude
7 | import Infernu.Types
8 |
9 | data PropType t = R t | RW t
10 | -- [Constructor(DOMString type, optional EventInit eventInitDict),
11 | -- Exposed=(Window,Worker,System)]
12 | -- interface Event {
13 | eventProps eventTarget =
14 | -- [Pure]
15 | -- readonly attribute DOMString type;
16 | [ ("type", R $ ty string)
17 | -- [Pure]
18 | -- readonly attribute EventTarget? target;
19 | , ("target", R $ ty eventTarget) -- TODO: "nullable"
20 | -- [Pure]
21 | -- readonly attribute EventTarget? currentTarget;
22 | , ("currentTarget", R $ ty eventTarget)
23 |
24 | -- const unsigned short NONE = 0;
25 | , ("NONE", R $ ty number)
26 | -- const unsigned short CAPTURING_PHASE = 1;
27 | , ("CAPTURING_PHASE", R $ ty number)
28 | -- const unsigned short AT_TARGET = 2;
29 | , ("AT_TARGET", R $ ty number)
30 | -- const unsigned short BUBBLING_PHASE = 3;
31 | , ("BUBBLING_PHASE", R $ ty number)
32 | -- [Pure]
33 | -- readonly attribute unsigned short eventPhase;
34 | , ("eventPhase", R $ ty number)
35 |
36 | -- void stopPropagation();
37 | , ("stopPropagation", RW $ ts [0] $ funcN [tvar 0])
38 | -- void stopImmediatePropagation();
39 | , ("stopImmediatePropagation", RW $ ts [0] $ funcN [tvar 0])
40 |
41 | -- [Pure]
42 | -- readonly attribute boolean bubbles;
43 | -- [Pure]
44 | -- readonly attribute boolean cancelable;
45 | -- void preventDefault();
46 | -- [Pure]
47 | -- readonly attribute boolean defaultPrevented;
48 |
49 | -- [Unforgeable, Pure]
50 | -- readonly attribute boolean isTrusted;
51 | -- [Pure]
52 | -- readonly attribute DOMHighResTimeStamp timeStamp;
53 |
54 | -- [Throws]
55 | -- void initEvent(DOMString type, boolean bubbles, boolean cancelable);
56 | ]
57 | -- };
58 |
59 | --eventRowType = foldM addProp (TRowEnd Nothing) eventProps
60 |
61 | --interface EventTarget {
62 | eventTargetProps eventListener event =
63 | -- /* Passing null for wantsUntrusted means "default behavior", which
64 | -- differs in content and chrome. In content that default boolean
65 | -- value is true, while in chrome the default boolean value is
66 | -- false. */
67 | -- [Throws]
68 | -- void addEventListener(DOMString type,
69 | -- EventListener? listener,
70 | -- optional boolean capture = false,
71 | -- optional boolean? wantsUntrusted = null);
72 | [ ("addEventListener", ts [0] $ funcN [tvar 0, string, eventListener]) -- TODO optional: , boolean, boolean
73 | -- [Throws]
74 | -- void removeEventListener(DOMString type,
75 | -- EventListener? listener,
76 | -- optional boolean capture = false);
77 | , ("removeEventListener", ts [0] $ funcN [tvar 0, string, eventListener]) -- boolean
78 | -- [Throws]
79 | -- boolean dispatchEvent(Event event);
80 | , ("dispatchEvent", ts [0] $ funcN [tvar 0, event])
81 | ]
82 | -- };
83 |
84 | elementGlobalAttributes :: [(String, TypeScheme)]
85 | elementGlobalAttributes =
86 | -- TODO add more
87 | [ ("accesskey", ty string)
88 | , ("class", ty string)
89 | , ("id", ty string)
90 | , ("style", ty string)
91 | , ("tabindex", ty number)
92 | , ("title", ty string)
93 | ]
94 |
95 |
--------------------------------------------------------------------------------
/src/Infernu/Builtins/Date.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE TupleSections #-}
2 | module Infernu.Builtins.Date
3 | (dateRowType)
4 | where
5 |
6 | import Control.Monad (foldM)
7 |
8 | import Infernu.Builtins.Util
9 | import Infernu.InferState (Infer)
10 | import Infernu.Prelude
11 | import Infernu.Types
12 |
13 | dateFunc :: Type -> Type
14 | dateFunc = funcN [date]
15 |
16 | getNumFromDate :: TScheme Type
17 | getNumFromDate = ty $ dateFunc number
18 |
19 | getNumFuncs :: [String]
20 | getNumFuncs = [ "getDate"
21 | , "getDay"
22 | , "getFullYear"
23 | , "getHours"
24 | , "getMilliseconds"
25 | , "getMinutes"
26 | , "getMonth"
27 | , "getSeconds"
28 | , "getTime"
29 | , "getTimezoneOffset"
30 | , "getUTCDate"
31 | , "getUTCDay"
32 | , "getUTCFullYear"
33 | , "getUTCHours"
34 | , "getUTCMilliseconds"
35 | , "getUTCMinutes"
36 | , "getUTCMonth"
37 | , "getUTCSeconds"
38 | , "valueOf"
39 | ]
40 |
41 | setNumOnDate :: TScheme Type
42 | setNumOnDate = ty $ funcN [date, number] number
43 |
44 | -- Note: many of these functions accept multiple optional parameters, but there are other ways to
45 | -- achieve the same result. Leaving these optional parameters out.
46 | setNumFuncs :: [String]
47 | setNumFuncs =
48 | [ "setDate"
49 | , "setFullYear"
50 | , "setHours"
51 | , "setMilliseconds"
52 | , "setMinutes"
53 | , "setMonth"
54 | , "setSeconds"
55 | , "setTime"
56 | , "setUTCDate"
57 | , "setUTCFullYear"
58 | , "setUTCHours"
59 | , "setUTCMilliseconds"
60 | , "setUTCMinutes"
61 | , "setUTCMonth"
62 | , "setUTCSeconds"
63 | -- , "setYear"
64 | ]
65 |
66 | getStringFuncType :: TScheme (Fix FType)
67 | getStringFuncType = ty $ funcN [date] string
68 |
69 | getStringFuncs :: [String]
70 | getStringFuncs =
71 | [ "toDateString"
72 | -- , "toGMTString"
73 | , "toISOString"
74 | , "toJSON"
75 | -- , "toLocaleFormat"
76 | -- , "toSource"
77 | , "toString"
78 | , "toTimeString"
79 | , "toUTCString"
80 | ]
81 | -- , "toLocaleDateString"
82 | -- , "toLocaleString"
83 | -- , "toLocaleTimeString"
84 |
85 | dateProps :: [(String, TypeScheme)]
86 | dateProps =
87 | map (,getNumFromDate) getNumFuncs
88 | ++ map (,setNumOnDate) setNumFuncs
89 | ++ map (,getStringFuncType) getStringFuncs
90 |
91 | dateRowType :: Infer (TRowList Type)
92 | dateRowType = namedProps
93 | where namedProps = foldM addProp (TRowEnd Nothing) dateProps
94 |
--------------------------------------------------------------------------------
/src/Infernu/Builtins/Math.hs:
--------------------------------------------------------------------------------
1 | module Infernu.Builtins.Math
2 | (math)
3 | where
4 |
5 | import Infernu.Builtins.Util
6 | import Infernu.Prelude
7 | import Infernu.Types
8 | import Infernu.Expr (EPropName(..))
9 |
10 | numProp :: String -> TRowList Type -> TRowList Type
11 | numProp name = TRowProp (TPropGetName $ EPropName name) $ ts [] number
12 |
13 | numFuncProp :: String -> TRowList (Fix FType) -> TRowList (Fix FType)
14 | numFuncProp name = TRowProp (TPropGetName $ EPropName name) $ ts [0] $ Fix $ TFunc [tvar 0, number] number
15 |
16 | numFuncProp2 :: String -> TRowList (Fix FType) -> TRowList (Fix FType)
17 | numFuncProp2 name = TRowProp (TPropGetName $ EPropName name) $ ts [0] $ Fix $ TFunc [tvar 0, number, number] number
18 |
19 | math :: TScheme (Fix FType)
20 | math = ts [] $ Fix $ TRow (Just "Math")
21 | $ numProp "E"
22 | $ numProp "LN10"
23 | $ numProp "LN2"
24 | $ numProp "LOG10E"
25 | $ numProp "LOG2E"
26 | $ numProp "PI"
27 | $ numProp "SQRT1_2"
28 | $ numProp "SQRT2"
29 | $ numFuncProp "abs"
30 | $ numFuncProp "acos"
31 | -- $ numFuncProp "acosh"
32 | $ numFuncProp "asin"
33 | -- $ numFuncProp "asinh"
34 | $ numFuncProp "atan"
35 | $ numFuncProp2 "atan2"
36 | -- $ numFuncProp "atanh"
37 | -- $ numFuncProp "cbrt"
38 | $ numFuncProp "ceil"
39 | -- $ numFuncProp "clz32"
40 | $ numFuncProp "cos"
41 | -- $ numFuncProp "cosh"
42 | $ numFuncProp "exp"
43 | -- $ numFuncProp "expm1"
44 | $ numFuncProp "floor"
45 | -- $ numFuncProp "fround"
46 | -- $ numFuncProp "hypot"
47 | -- $ numFuncProp "imul"
48 | $ numFuncProp "log"
49 | -- $ numFuncProp "log10"
50 | -- $ numFuncProp "log1p"
51 | -- $ numFuncProp "log2"
52 | -- Requires support for variable number of arguments
53 | $ numFuncProp2 "max"
54 | $ numFuncProp2 "min"
55 | $ numFuncProp2 "pow"
56 | $ TRowProp (TPropGetName $ EPropName "random") (ts [] $ Fix $ TFunc [tvar 0] number)
57 | $ numFuncProp "round"
58 | $ numFuncProp "sign"
59 | $ numFuncProp "sin"
60 | -- $ numFuncProp "sinh"
61 | $ numFuncProp "sqrt"
62 | $ numFuncProp "tan"
63 | -- $ numFuncProp "tanh"
64 | -- $ numFuncProp "trunc"
65 | $ TRowEnd Nothing
66 |
--------------------------------------------------------------------------------
/src/Infernu/Builtins/Names.hs:
--------------------------------------------------------------------------------
1 | module Infernu.Builtins.Names where
2 |
3 | import Infernu.Prelude
4 |
5 | refOp :: String
6 | refOp = "`ref"
7 |
8 | derefOp :: String
9 | derefOp = "`deref"
10 |
11 | refAssignOp :: String
12 | refAssignOp = "`:="
13 |
14 |
--------------------------------------------------------------------------------
/src/Infernu/Builtins/Number.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE TupleSections #-}
2 | module Infernu.Builtins.Number
3 | (numberRowType)
4 | where
5 |
6 | import Control.Monad (foldM)
7 |
8 | import Infernu.Builtins.Util
9 | import Infernu.InferState (Infer)
10 | import Infernu.Prelude
11 | import Infernu.Types
12 | import Infernu.Expr (EPropName(..))
13 |
14 |
15 | numberProps :: [(String, TypeScheme)]
16 | numberProps =
17 | [
18 | ("toFixed", ty $ func number number string)
19 | ]
20 |
21 | -- TODO: when inserting builtin types, do fresh renaming of scheme qvars
22 | -- TODO: this code is actually pure, refactor to pure function and 'return' wrapper.
23 | numberRowType :: Infer (TRowList Type)
24 | numberRowType = foldM addProp (TRowEnd Nothing) $ numberProps
25 |
--------------------------------------------------------------------------------
/src/Infernu/Builtins/Object.hs:
--------------------------------------------------------------------------------
1 | module Infernu.Builtins.Object
2 | (object)
3 | where
4 |
5 | import Infernu.Builtins.Util
6 | import Infernu.Prelude
7 | import Infernu.Types
8 |
9 | keyObj :: Int -> Type -> TQual Type
10 | keyObj tv = withTypeClass "StringKeys" (tvar tv)
11 |
12 | object :: TScheme (Fix FType)
13 | object = ts []
14 | $ Fix $ TRow (Just "Object")
15 | -- assign - ES6
16 | $ prop "create" (ts [0, 1] $ funcN [tvar 0, openRow 1] (openRow 1))
17 | -- can't be supported in a sane way:
18 | -- "defineProperties"
19 | -- "defineProperty"
20 | -- "getOwnPropertyDescriptor"
21 | $ prop "freeze" (tsq [0, 1] $ keyObj 1 $ funcN [tvar 0, tvar 1] (tvar 1))
22 | $ prop "getOwnPropertyNames" (tsq [0, 1] $ keyObj 1 $ funcN [tvar 0, tvar 1] (array string))
23 | -- "getPrototypeOf" -- should we just return the same type? we don't support prototypes
24 | $ prop "isExtensible" (tsq [0, 1] $ keyObj 1 $ funcN [tvar 0, tvar 1] boolean)
25 | $ prop "isFrozen" (tsq [0, 1] $ keyObj 1 $ funcN [tvar 0, tvar 1] boolean)
26 | $ prop "isSealed" (tsq [0, 1] $ keyObj 1 $ funcN [tvar 0, tvar 1] boolean)
27 | $ prop "keys" (tsq [0, 1] $ keyObj 1 $ funcN [tvar 0, tvar 1] (array string))
28 | $ prop "preventExtensions" (tsq [0, 1] $ keyObj 1 $ funcN [tvar 0, tvar 1] (tvar 1))
29 | $ prop "seal" (tsq [0, 1] $ keyObj 1 $ funcN [tvar 0, tvar 1] (tvar 1))
30 | $ TRowEnd Nothing
31 |
--------------------------------------------------------------------------------
/src/Infernu/Builtins/Operators.hs:
--------------------------------------------------------------------------------
1 | module Infernu.Builtins.Operators
2 | (builtins)
3 | where
4 |
5 | import Infernu.Prelude
6 | import Infernu.Types
7 |
8 | import Data.Map.Strict (Map)
9 | import qualified Data.Map.Strict as Map
10 |
11 | import Infernu.Builtins.Math (math)
12 | import Infernu.Builtins.Object (object)
13 | import Infernu.Builtins.Util
14 | import qualified Infernu.Builtins.Names as Names
15 | import Infernu.Expr (EPropName(..), EVarName)
16 |
17 | unaryFunc :: Type -> Type -> TypeScheme
18 | unaryFunc t1 t2 = ts [0] $ Fix $ TFunc [tvar 0, t1] t2
19 |
20 | binaryFunc :: Type -> Type -> Type -> Type -> Fix FType
21 | binaryFunc tThis t1 t2 t3 = Fix $ TFunc [tThis, t1, t2] t3
22 |
23 | binarySimpleFunc :: Type -> Type -> Type
24 | binarySimpleFunc tThis t = Fix $ TFunc [tThis, t, t] t
25 |
26 | binaryFuncS :: Type -> Type -> Type -> TypeScheme
27 | binaryFuncS t1 t2 t3 = ts [0] $ binaryFunc (tvar 0) t1 t2 t3
28 |
29 | numRelation :: TypeScheme
30 | numRelation = binaryFuncS number number boolean
31 |
32 | numOp :: TypeScheme
33 | numOp = binaryFuncS number number number
34 |
35 | builtins :: Map EVarName TypeScheme
36 | builtins = Map.fromList [
37 |
38 | -- TODO: refOp and derefOp should be data constructors of the built-in Ref type (need to add
39 | -- support for data constructors)
40 | (Names.refOp, ts [1] $ Fix $ TFunc [tvar 1] (Fix $ TCons TRef [tvar 1])),
41 | (Names.refAssignOp, ts [1] $ Fix $ TFunc [Fix $ TCons TRef [tvar 1], tvar 1] (tvar 1)),
42 | (Names.derefOp, ts [1] $ Fix $ TFunc [Fix $ TCons TRef [tvar 1]] (tvar 1)),
43 |
44 | ("!", unaryFunc boolean boolean),
45 | ("~", unaryFunc number number),
46 | ("typeof", ts [0, 1] $ Fix $ TFunc [tvar 1, tvar 0] string),
47 | ("instanceof", ts [0, 1, 2] $ Fix $ TFunc [tvar 2, tvar 0, tvar 1] boolean),
48 | ("+", TScheme [Flex 0, Flex 1] TQual { qualPred = [TPredIsIn (ClassName "Plus") (tvar 1)]
49 | , qualType = binarySimpleFunc (tvar 0) (tvar 1) }),
50 | ("-", numOp),
51 | ("*", numOp),
52 | ("/", numOp),
53 | ("%", numOp),
54 | ("<<", numOp),
55 | (">>", numOp),
56 | (">>>", numOp),
57 | ("&", numOp),
58 | ("^", numOp),
59 | ("|", numOp),
60 | ("<", numRelation),
61 | ("<=", numRelation),
62 | (">", numRelation),
63 | (">=", numRelation),
64 | ("===", ts [0, 1, 2] $ Fix $ TFunc [tvar 2, tvar 0, tvar 1] boolean),
65 | ("!==", ts [0, 1, 2] $ Fix $ TFunc [tvar 2, tvar 0, tvar 1] boolean),
66 | ("&&", ts [0, 1] $ Fix $ TFunc [tvar 0, tvar 1, tvar 1] (tvar 1)),
67 | ("||", ts [0, 1] $ Fix $ TFunc [tvar 0, tvar 1, tvar 1] (tvar 1)),
68 | -- avoid coercions on == and !=
69 | ("==", ts [0, 1] $ Fix $ TFunc [tvar 1, tvar 0, tvar 0] boolean),
70 | ("!=", ts [0, 1] $ Fix $ TFunc [tvar 1, tvar 0, tvar 0] boolean),
71 | ("RegExp", ts [] $ Fix $ TFunc [undef, string, string] regex),
72 | ("String", ts [1] $ Fix $ TFunc [undef, tvar 1] string),
73 | ("Number", ts [1] $ Fix $ TFunc [undef, tvar 1] number),
74 | ("Boolean", ts [1] $ Fix $ TFunc [undef, tvar 1] boolean),
75 | ("NaN", ts [] number),
76 | ("Infinity", ts [] number),
77 | ("undefined", ts [0] undef),
78 | ("isFinite", ts [0] $ Fix $ TFunc [tvar 0, number] boolean),
79 | ("isNaN", ts [0] $ Fix $ TFunc [tvar 0, number] boolean),
80 | ("parseFloat", ts [0] $ Fix $ TFunc [tvar 0, string] number),
81 | ("parseInt", ts [0] $ Fix $ TFunc [tvar 0, string, number] number),
82 | ("decodeURI", ts [0] $ Fix $ TFunc [tvar 0, string] string),
83 | ("decodeURIComponent", ts [0] $ Fix $ TFunc [tvar 0, string] string),
84 | ("encodeURI", ts [0] $ Fix $ TFunc [tvar 0, string] string),
85 | ("encodeURIComponent", ts [0] $ Fix $ TFunc [tvar 0, string] string),
86 | ("Date", ts [] $ Fix $ TRow (Just "Date[Constructor]")
87 | $ TRowProp (TPropGetName EPropFun) (ts [] $ Fix $ TFunc [Fix $ TBody TDate] string)
88 | $ prop "now" (ts [0] $ Fix $ TFunc [tvar 0] number)
89 | $ prop "parse" (ts [0] $ Fix $ TFunc [tvar 0, string] (Fix $ TBody TDate))
90 | $ prop "UTC" (ts [0] $ Fix $ TFunc [tvar 0, number, number, number, number, number, number, number] (Fix $ TBody TDate))
91 | $ TRowEnd Nothing),
92 | ("Math", math),
93 | ("Object", object),
94 | ("JSON", ts [] $ Fix $ TRow (Just "JSON")
95 | $ prop "stringify" (ts [0, 1] $ Fix $ TFunc [tvar 0, tvar 1] string)
96 | -- TODO: should really be "maybe (tvar 1)"
97 | $ prop "parse" (ts [0, 1] $ Fix $ TFunc [tvar 0, string] (tvar 1))
98 | $ TRowEnd Nothing)
99 | ]
100 |
--------------------------------------------------------------------------------
/src/Infernu/Builtins/Regex.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE TupleSections #-}
2 | module Infernu.Builtins.Regex
3 | (regexRowType)
4 | where
5 |
6 | import Control.Monad (foldM)
7 |
8 | import Infernu.Builtins.Util
9 | import Infernu.InferState (Infer)
10 | import Infernu.Prelude
11 | import Infernu.Types
12 | import Infernu.Expr (EPropName(..))
13 |
14 | regexMatch :: Type
15 | regexMatch = Fix . TRow (Just "RegexMatch")
16 | -- TODO: instead of quantifying 'this', it should be a recursive type (regexMatch itself)
17 | . TRowProp (TPropGetName EPropGetIndex) (ts [] $ func (tvar 0) number string)
18 | . TRowProp (TPropGetName $ EPropName "index") (ty number)
19 | . TRowProp (TPropGetName $ EPropName "input") (ty string)
20 | $ TRowEnd Nothing
21 |
22 | regexProps :: [(String, TypeScheme)]
23 | regexProps =
24 | [ ("source", ty string)
25 | , ("exec", ty $ func regex string regexMatch)
26 | , ("lastIndex", ty number)
27 | , ("global", ty boolean)
28 | , ("ignoreCase", ty boolean)
29 | , ("multiline", ty boolean)
30 | , ("source", ty string)
31 | , ("test", ty $ func regex string boolean)
32 | ]
33 |
34 | -- TODO: when inserting builtin types, do fresh renaming of scheme qvars
35 | -- TODO: this code is actually pure, refactor to pure function and 'return' wrapper.
36 | regexRowType :: Infer (TRowList Type)
37 | regexRowType = foldM addProp (TRowEnd Nothing) $ regexProps
38 |
--------------------------------------------------------------------------------
/src/Infernu/Builtins/String.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE TupleSections #-}
2 | module Infernu.Builtins.String
3 | (stringRowType)
4 | where
5 |
6 | import Control.Monad (foldM)
7 |
8 | import Infernu.Builtins.Util
9 | import Infernu.InferState (Infer)
10 | import Infernu.Prelude
11 | import Infernu.Types
12 | import Infernu.Expr (EPropName(..))
13 |
14 |
15 | stringProps :: [(String, TypeScheme)]
16 | stringProps =
17 | [ ("length", ty number)
18 | , ("charAt", ty $ func string number string)
19 | , ("charCodeAt", ty $ func string number number)
20 | , ("concat", ty $ func string string string) -- TODO: concat really accepty a variable number of arguments
21 | , ("indexOf", ty $ func string string number) -- TODO: optional parameter
22 | , ("lastIndexOf", ty $ func string string number) -- TODO: optional parameter
23 | , ("localeCompare", ty $ func string string number) -- TODO: optional parameters
24 |
25 | -- To support 'match' we need to allow different result types, different for global and non-global
26 | -- regexes. One possibility is to define two regex types RegexSingle and RegexGlobal and use
27 | -- associated types:
28 | -- class Regex r where
29 | -- type R r = r
30 | -- type M RegexLocal = -- match result type for RegexLocal
31 | -- type M RegexGlobal = [String]
32 | --
33 | -- , ("match", ty $ func string regex
34 |
35 | , ("replace", TScheme [Flex 0] $ withTypeClass "Pattern" (tvar 0) $ funcN [string, tvar 0, string] string)
36 | , ("split", ty $ func string string (array string) ) -- TODO review
37 | ]
38 |
39 | -- TODO: when inserting builtin types, do fresh renaming of scheme qvars
40 | -- TODO: this code is actually pure, refactor to pure function and 'return' wrapper.
41 | stringRowType :: Infer (TRowList Type)
42 | stringRowType = TRowProp (TPropGetName EPropGetIndex) (ty $ func string number string) <$> namedProps
43 | where namedProps = foldM addProp (TRowEnd Nothing) $ stringProps
44 |
--------------------------------------------------------------------------------
/src/Infernu/Builtins/StringMap.hs:
--------------------------------------------------------------------------------
1 | module Infernu.Builtins.StringMap where
2 |
3 | import Infernu.Builtins.Util
4 | import Infernu.Prelude
5 | import Infernu.Types
6 | import Infernu.Expr (EPropName(..))
7 |
8 | stringMapRowType :: Monad m => Type -> m (TRowList Type)
9 | stringMapRowType elemType = return
10 | . TRowProp (TPropGetName EPropSetIndex) (ty $ funcN [aType, string, elemType] undef)
11 | . TRowProp (TPropGetName EPropGetIndex) (ty $ func aType string elemType)
12 | $ TRowEnd Nothing
13 | where aType = Fix $ TCons TStringMap [elemType]
14 |
--------------------------------------------------------------------------------
/src/Infernu/Builtins/TypeClasses.hs:
--------------------------------------------------------------------------------
1 | module Infernu.Builtins.TypeClasses
2 | (typeClasses)
3 | where
4 |
5 | import Infernu.Prelude
6 | import Infernu.Types
7 |
8 | typeClasses :: [(ClassName, Class (Fix FType))]
9 | typeClasses =
10 | [
11 | (ClassName "Pattern", Class { classInstances =
12 | [ schemeEmpty $ Fix $ TBody TRegex
13 | , schemeEmpty $ Fix $ TBody TString
14 | ]})
15 | , (ClassName "Plus", Class { classInstances =
16 | [ schemeEmpty $ Fix $ TBody TNumber
17 | , schemeEmpty $ Fix $ TBody TString
18 | ]})
19 | , (ClassName "StringKeys", Class { classInstances =
20 | [ TScheme [Flex 0] $ qualEmpty $ Fix $ TRow Nothing $ TRowEnd $ Just $ RowTVar (Flex 0)
21 | , TScheme [Flex 0] $ qualEmpty $ Fix $ TCons TArray [Fix . TBody . TVar $ Flex 0]
22 | , TScheme [Flex 0] $ qualEmpty $ Fix $ TCons TStringMap [Fix . TBody . TVar $ Flex 0]
23 | ]})
24 | ]
25 |
--------------------------------------------------------------------------------
/src/Infernu/Builtins/Util.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE TupleSections #-}
2 | module Infernu.Builtins.Util
3 | where
4 |
5 | import Control.Monad (forM)
6 |
7 | import Infernu.InferState (fresh, Infer)
8 | import Infernu.Lib (safeLookup)
9 | import Infernu.Prelude
10 | import Infernu.Expr (EPropName(..))
11 | import Infernu.Types
12 |
13 | func :: Type -> Type -> Type -> Type
14 | func this x y = Fix $ TFunc [this, x] y
15 |
16 | funcN :: [Fix FType] -> Fix FType -> Fix FType
17 | funcN xs tres = Fix $ TFunc xs tres
18 |
19 | string :: Type
20 | string = Fix $ TBody TString
21 |
22 | date :: Type
23 | date = Fix $ TBody TDate
24 |
25 | regex :: Type
26 | regex = Fix $ TBody TRegex
27 |
28 | nullT :: Type
29 | nullT = Fix $ TBody TNull
30 |
31 | number :: Type
32 | number = Fix $ TBody TNumber
33 |
34 | array :: Type -> Type
35 | array t = Fix $ TCons TArray [t]
36 |
37 | stringMap :: Type -> Type
38 | stringMap t = Fix $ TCons TStringMap [t]
39 |
40 | boolean :: Fix FType
41 | boolean = Fix $ TBody TBoolean
42 |
43 | undef :: Type
44 | undef = Fix $ TBody TUndefined
45 |
46 | ts :: [Int] -> t -> TScheme t
47 | ts vs t = TScheme (map Flex vs) $ qualEmpty t
48 |
49 | tsq :: [Int] -> TQual t -> TScheme t
50 | tsq vs = TScheme (map Flex vs)
51 |
52 | ty :: t -> TScheme t
53 | ty t = TScheme [] $ qualEmpty t
54 |
55 | tvar :: Int -> Type
56 | tvar = Fix . TBody . TVar . Flex
57 |
58 | withTypeClass :: String -> a -> a -> TQual a
59 | withTypeClass n t t' = TQual { qualPred = [TPredIsIn { predClass = ClassName n, predType = t }], qualType = t' }
60 |
61 | openRow :: Int -> Type
62 | openRow tv = Fix $ TRow Nothing $ TRowEnd $ Just $ RowTVar (Flex tv)
63 |
64 | prop :: String -> TScheme t -> TRowList t -> TRowList t
65 | prop name = TRowProp (TPropGetName $ EPropName name)
66 |
67 | addProp :: VarNames t => TRowList t -> (String, TScheme t) -> Infer (TRowList t)
68 | addProp rowlist (name, propTS) =
69 | do allocNames <- forM (schemeVars propTS) $ \tvName -> (tvName,) . Flex <$> fresh
70 | let ts' = mapVarNames (safeLookup allocNames) propTS
71 | return $ TRowProp (TPropGetName $ EPropName name) ts' rowlist
72 |
--------------------------------------------------------------------------------
/src/Infernu/Decycle.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE ScopedTypeVariables #-}
2 |
3 | -- | copied from https://raw.githubusercontent.com/Peaker/lamdu/wip_integration/bottlelib/Data/Function/Decycle.hs
4 |
5 | module Infernu.Decycle(decycleOn, decycle, decycle2, decycle3) where
6 |
7 | import Infernu.Prelude
8 | import qualified Data.Set as Set
9 |
10 | -- | A fix for functions that terminates recursive cycles
11 | decycleOn :: forall a b res . Ord b => (a -> b) -> (Maybe (a -> res) -> a -> res) -> a -> res
12 | decycleOn toOrd f = go Set.empty
13 | where
14 | go :: Set.Set b -> a -> res
15 | go visited x = f (mRecurse visited (toOrd x)) x
16 |
17 | mRecurse :: Set.Set b -> b -> Maybe (a -> res)
18 | mRecurse visited o = if Set.member o visited
19 | then Nothing
20 | else Just $ go (Set.insert o visited)
21 |
22 | decycle :: Ord a => (Maybe (a -> res) -> a -> res) -> a -> res
23 | decycle = decycleOn id
24 |
25 | decycle2 :: (Ord b, Ord a) => (Maybe (a -> b -> c) -> a -> b -> c) -> a -> b -> c
26 | decycle2 f arg1 arg2 = decycle (\recurse (arg1', arg2') -> f (curry <$> recurse) arg1' arg2') (arg1, arg2)
27 |
28 | decycle3 :: (Ord b, Ord a, Ord c) => (Maybe (a -> b -> c -> res) -> a -> b -> c -> res) -> a -> b -> c -> res
29 | decycle3 f arg1 arg2 arg3 = decycle f3 (arg1, arg2, arg3)
30 | where f3 Nothing (x,y,z) = f Nothing x y z
31 | f3 (Just rec3) (x,y,z) = f (Just (\a b c -> rec3 (a,b,c))) x y z
32 |
--------------------------------------------------------------------------------
/src/Infernu/Expr.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE DeriveFoldable #-}
2 | {-# LANGUAGE DeriveFunctor #-}
3 | {-# LANGUAGE DeriveGeneric #-}
4 | -- |
5 |
6 | module Infernu.Expr
7 | ( Exp(..)
8 | , mapTopAnnotation
9 | , LitVal(..)
10 | , EVarName(..), EPropName(..)
11 | ) where
12 |
13 | import Data.Hashable (Hashable(..))
14 | import GHC.Generics (Generic)
15 |
16 | import Infernu.Prelude
17 |
18 |
19 | type EVarName = String
20 | data EPropName = EPropName String
21 | | EPropGetIndex
22 | | EPropSetIndex
23 | | EPropFun
24 | deriving (Show, Eq, Ord, Generic)
25 |
26 | instance Hashable EPropName where
27 |
28 | data LitVal = LitNumber !Double
29 | | LitBoolean !Bool
30 | | LitString !String
31 | | LitRegex !String !Bool !Bool
32 | | LitUndefined
33 | | LitNull
34 | | LitEmptyThis
35 | deriving (Show, Eq, Ord)
36 |
37 | data Exp a = EVar !a !EVarName
38 | | EApp !a !(Exp a) ![Exp a]
39 | | EAbs !a ![EVarName] !(Exp a)
40 | | ELet !a !EVarName !(Exp a) !(Exp a)
41 | | ECase !a !(Exp a) ![(LitVal, Exp a)]
42 | | EProp !a !(Exp a) !EPropName
43 | | EPropAssign !a !(Exp a) !EPropName !(Exp a) !(Exp a)
44 | -- TODO consider better options for causing rows to become closed outside the 'new' call
45 | | ENew !a !(Exp a) ![Exp a]
46 | -- Various literal expressions
47 | | ELit !a !LitVal
48 | | EArray !a ![Exp a]
49 | | ETuple !a ![Exp a]
50 | | ERow !a !Bool ![(EPropName, Exp a)]
51 | | EStringMap !a ![(String, Exp a)]
52 | deriving (Show, Eq, Ord, Functor, Foldable)
53 |
54 | ----------------------------------------------------------------------
55 |
56 | -- TODO: Horrible, terrible boilerplate. get rid of it.
57 | mapTopAnnotation :: (a -> a) -> Exp a -> Exp a
58 | mapTopAnnotation f expr =
59 | case expr of
60 | (EVar a b) -> EVar (f a) b
61 | (EApp a x y) -> EApp (f a) x y
62 | (EAbs a x y) -> EAbs (f a) x y
63 | (ELet a x y z) -> ELet (f a) x y z
64 | (ELit a x) -> ELit (f a) x
65 | (EPropAssign a x y z v) -> EPropAssign (f a) x y z v
66 | (EArray a x) -> EArray (f a) x
67 | (ETuple a x) -> ETuple (f a) x
68 | (ERow a x y) -> ERow (f a) x y
69 | (EStringMap a x) -> EStringMap (f a) x
70 | (ECase a x ys) -> ECase (f a) x ys
71 | (EProp a x y) -> EProp (f a) x y
72 | (ENew a x y) -> ENew (f a) x y
73 |
74 | ----------------------------------------------------------------------
75 |
--------------------------------------------------------------------------------
/src/Infernu/Fix.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE UndecidableInstances #-}
2 | {-# LANGUAGE FlexibleContexts #-}
3 | {-# LANGUAGE StandaloneDeriving #-}
4 | module Infernu.Fix
5 | ( Fix(..)
6 | , fmapReplace
7 | , replaceFix
8 | , fixToList
9 | )
10 | where
11 |
12 | import Infernu.Prelude
13 |
14 | newtype Fix f = Fix { unFix :: f (Fix f) }
15 |
16 | instance Show (f (Fix f)) => Show (Fix f) where
17 | show (Fix x) = "Fix (" ++ (show x) ++ ")"
18 | deriving instance Eq (f (Fix f)) => Eq (Fix f)
19 | deriving instance Ord (f (Fix f)) => Ord (Fix f)
20 |
21 | fmapReplace :: (Functor f, Eq (f a)) => (f a -> f b -> a -> b) -> f a -> f b -> f a -> f b
22 | fmapReplace recurse tsource tdest t =
23 | if t == tsource
24 | then tdest
25 | else fmap (recurse tsource tdest) t
26 |
27 | replaceFix :: (Functor f, Eq (f (Fix f))) => f (Fix f) -> f (Fix f) -> Fix f -> Fix f
28 | replaceFix tsource tdest (Fix t') = Fix $ fmapReplace replaceFix tsource tdest t'
29 |
30 |
31 | -- | Flattens a fix-type to a list of all tree nodes
32 | --
33 | -- >>> fixToList $ (Fix $ TCons TArray [Fix $ TCons TArray [Fix $ TBody TNumber]])
34 | -- [Fix (TCons TArray [Fix (TCons TArray [Fix (TBody TNumber)])]),Fix (TCons TArray [Fix (TBody TNumber)]),Fix (TBody TNumber)]
35 | -- >>> fixToList $ (Fix $ TRow $ TRowProp "x" (TScheme [] $ Fix $ TBody TNumber) (TRowEnd Nothing))
36 | -- [Fix (TRow (TRowProp "x" (TScheme {schemeVars = [], schemeType = Fix (TBody TNumber)}) (TRowEnd Nothing))),Fix (TBody TNumber)]
37 | fixToList :: Foldable t => Fix t -> [Fix t]
38 | fixToList (Fix t) = (Fix t) : (foldr (\t' b -> fixToList t' ++ b) [] t)
39 |
--------------------------------------------------------------------------------
/src/Infernu/Lib.hs:
--------------------------------------------------------------------------------
1 | module Infernu.Lib where
2 |
3 | import Data.Maybe (fromMaybe)
4 | import Data.Map.Strict (Map)
5 | import qualified Data.Map.Strict as Map
6 | import Data.Set (Set)
7 | import qualified Data.Set as Set
8 |
9 | import Infernu.Prelude
10 |
11 |
12 | matchZip :: [a] -> [b] -> Maybe [(a,b)]
13 | matchZip [] [] = Just []
14 | matchZip (_:_) [] = Nothing
15 | matchZip [] (_:_) = Nothing
16 | matchZip (x:xs) (y:ys) = fmap ((x,y):) $ matchZip xs ys
17 |
18 | safeLookup :: Eq a => [(a,a)] -> a -> a
19 | safeLookup assoc n = fromMaybe n $ lookup n assoc
20 |
21 | -- | Creates an inverse map. Multiple keys mapping to the same values are collected into Sets.
22 | --
23 | -- >>> flipMap $ Map.fromList [(1,2),(2,2)]
24 | -- fromList [(2,fromList [1,2])]
25 | flipMap :: (Ord k, Ord v) => Map k v -> Map v (Set k)
26 | flipMap m = Map.foldrWithKey (\k v m' -> Map.alter (Just . addKeyToSet' k) v m') Map.empty m
27 | where addKeyToSet' k Nothing = Set.singleton k
28 | addKeyToSet' k (Just s) = Set.insert k s
29 |
30 | splatMap :: Ord k => Map (Set k) a -> Map k a
31 | splatMap m = Map.foldrWithKey (\ks v m' -> foldr (\k m'' -> Map.insert k v m'') m' (Set.toList ks)) Map.empty m
32 |
33 |
--------------------------------------------------------------------------------
/src/Infernu/Log.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE CPP #-}
2 | {-# LANGUAGE TupleSections #-}
3 | {-# LANGUAGE BangPatterns #-}
4 |
5 | module Infernu.Log
6 | (trace, tracePretty, traceLog, traceLogVal)
7 | where
8 |
9 | import Infernu.Prelude
10 | import Text.PrettyPrint.ANSI.Leijen (Pretty(..), (<+>), Doc)
11 |
12 | #if TRACE
13 | import Debug.Trace (trace)
14 | #else
15 | trace :: a -> b -> b
16 | trace _ y = y
17 | #endif
18 |
19 | tracePretty :: Pretty a => Doc -> a -> a
20 | tracePretty prefix x = trace (show $ prefix <+> pretty x) x
21 |
22 | traceLogVal :: Applicative f => String -> a -> f a
23 | traceLogVal !s !r = pure $! trace s r `seq` r
24 |
25 | traceLog :: Applicative f => Doc -> f ()
26 | traceLog !s = pure $! trace (show s) () `seq` ()
27 |
--------------------------------------------------------------------------------
/src/Infernu/Options.hs:
--------------------------------------------------------------------------------
1 | module Infernu.Options
2 | (Options(..), defaultOptions, opts)
3 | where
4 |
5 | import Infernu.Prelude
6 |
7 | import Options.Applicative
8 |
9 | data Options = Options
10 | { optQuiet :: Bool
11 | , optShowCore :: Bool
12 | , optShowParsed :: Bool
13 | , optFileNames :: [String]
14 | }
15 |
16 | defaultOptions :: Options
17 | defaultOptions = Options { optQuiet = False, optShowCore = False, optShowParsed = False, optFileNames = [] }
18 |
19 | opts :: ParserInfo Options
20 | opts = info (helper <*> parseOpts)
21 | ( fullDesc
22 | <> progDesc "Infer types in the given JavaScript FILES and check for type errors. Unless -q is given, the source annotated with type signatures is outputted."
23 | <> header "infernu - static type checker for JavaScript using full type inference" )
24 |
25 | parseOpts :: Parser Options
26 | parseOpts = Options
27 | <$> switch (long "quiet"
28 | <> short 'q'
29 | <> help "Report only errors; don't output the annotated source with inferred types")
30 | <*> switch (long "dump-translation"
31 | <> help "Dump internal translation (used for debugging infernu)" )
32 | <*> switch (long "dump-parsed"
33 | <> help "Dump parsed JS syntax tree (used for debugging infernu)" )
34 | <*> some (argument str (metavar "FILES..."))
35 |
36 |
37 |
--------------------------------------------------------------------------------
/src/Infernu/Prelude.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE CPP #-}
2 |
3 | -- | The sole purpose of this module is to fix pre/post ghc 7.10 compatibility issues
4 | module Infernu.Prelude
5 | ( module Prelude.Compat
6 | , bool
7 | )
8 | where
9 |
10 | import Prelude.Compat
11 | import Data.Orphans ()
12 |
13 | #if MIN_VERSION_base(4,7,0)
14 | import Data.Bool (bool)
15 | #else
16 |
17 | bool :: a -> a -> Bool -> a
18 | bool f _ False = f
19 | bool _ t True = t
20 |
21 | #endif
22 |
--------------------------------------------------------------------------------
/src/Infernu/Pretty.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE FlexibleInstances #-}
2 | {-# OPTIONS_GHC -fno-warn-orphans #-}
3 | module Infernu.Pretty where
4 |
5 |
6 |
7 | import Infernu.Prelude
8 | import Infernu.Expr
9 | import Infernu.Source
10 | import Infernu.Types
11 | import qualified Infernu.Builtins.Names as Names
12 |
13 | import Data.Char (chr, ord)
14 | import qualified Data.Digits as Digits
15 | import qualified Data.List as List
16 | import Data.Map.Strict (Map)
17 | import qualified Data.Map.Strict as Map
18 |
19 | import qualified Data.Set as Set
20 | import qualified Text.Parsec.Pos as Pos
21 |
22 | import Text.PrettyPrint.ANSI.Leijen hiding ((<$>))
23 |
24 | -- tab :: Int -> String
25 | -- tab t = replicate (t*4) ' '
26 |
27 | -- class Pretty a where
28 | -- prettyTab :: Int -> a -> String
29 |
30 | -- instance Pretty a => Pretty (Maybe a) where
31 | -- pretty x = maybe "Nothing" pretty x
32 |
33 | -- instance (Pretty a, Pretty b) => Pretty (a,b) where
34 | -- pretty (a,b) = "(" ++ pretty a ++ ", " ++ pretty b ++ ")"
35 |
36 | -- instance (Pretty a, Pretty b, Pretty c) => Pretty (a,b,c) where
37 | -- pretty (a,b,c) = "(" ++ pretty a ++ ", " ++ pretty b ++ ", " ++ pretty c ++ ")"
38 |
39 | -- prettyList :: Pretty a => [a] -> String
40 | -- prettyList [] = "[]"
41 | -- prettyList xs = "[" ++ intercalate "," (map pretty xs) ++ "]"
42 |
43 | -- instance Pretty [String] where
44 | -- pretty = prettyList
45 |
46 | -- instance Pretty [Type] where
47 | -- pretty = prettyList
48 |
49 | -- instance (Pretty a, Pretty b) => Pretty [(a,b)] where
50 | -- pretty xs = "[" ++ intercalate "," (map pretty xs) ++ "]"
51 |
52 | -- pretty :: Pretty a => a -> String
53 | -- pretty = prettyTab 0
54 |
55 |
56 |
57 | instance Pretty Pos.SourcePos where
58 | pretty p = string (Pos.sourceName p) <> string ":" <> (string . show $ Pos.sourceLine p) <> string ":" <> (string . show $ Pos.sourceColumn p)
59 |
60 | ifStr :: Bool -> String -> Doc
61 | ifStr b s = if b then string s else empty
62 |
63 | instance Pretty Source where
64 | pretty (Source (genInfo, pos)) =
65 | ifStr (isGen genInfo) "*"
66 | <> pretty pos
67 | <> maybe empty (\x -> string ":" <> string x <> string ":") (declName genInfo)
68 |
69 |
70 | instance Pretty LitVal where
71 | pretty (LitNumber x) = pretty x
72 | pretty (LitBoolean x) = pretty x
73 | pretty (LitString x) = pretty x
74 | pretty (LitRegex x g i) = enclose (string "/") (string "/") (string x) <> (ifStr g "g") <> (ifStr i "i")
75 | pretty LitUndefined = string "undefined"
76 | pretty LitNull = string "null"
77 | pretty LitEmptyThis = string "(undefined 'this')"
78 |
79 | nakedSingleOrTuple :: Pretty a => [a] -> Doc
80 | nakedSingleOrTuple [] = string "()"
81 | nakedSingleOrTuple [x] = pretty x
82 | nakedSingleOrTuple xs = tupled $ map pretty xs
83 |
84 | instance Pretty EPropName where
85 | pretty (EPropName x) = string x
86 | pretty EPropGetIndex = string "=[]"
87 | pretty EPropSetIndex = string "[]="
88 | pretty EPropFun = string "call()"
89 |
90 | instance Pretty (Exp a) where
91 | pretty (EVar _ n) = string n
92 | pretty (EApp _ (EVar _ n) [arg1,arg2]) | n == Names.refAssignOp = parens $ pretty arg1 <+> string ":=" <+> pretty arg2
93 | pretty (EApp _ e1 args) = parens $ pretty e1 <+> nakedSingleOrTuple args
94 | pretty (EAbs _ args e) = parens $ string "\\" <> nakedSingleOrTuple args <+> string "->" <+> pretty e
95 | pretty (ELet _ n e1 e2) = string "let" <+> align (vsep $ letLine n e1 e2)
96 | where letLine n' e' eBody = curLine n' e' : rest eBody
97 | curLine n' e' = pretty n' <+> string "=" <+> pretty e'
98 | rest eBody = case eBody of
99 | ELet _ nb e1b e2b -> letLine nb e1b e2b
100 | _ -> [string "in" <+> align (pretty eBody)]
101 |
102 | pretty (ELit _ l) = pretty l
103 | pretty (EPropAssign _ obj n e1 e2) = align $ vsep [ pretty obj <> dot <> pretty n <+> string ":=" <+> pretty e1 <> string ";"
104 | , pretty e2]
105 | pretty (EArray _ es) = encloseSep lbracket rbracket comma $ map pretty es
106 | pretty (ETuple _ es) = pretty es
107 | pretty (ERow _ _ props) = encloseSep lbrace rbrace comma
108 | $ map (\(n,v) -> fill 6 (pretty n) <> string ":" <> space <> pretty v) props
109 | -- <> ifStr isOpen " | ? "
110 | pretty (ECase _ ep es) = hang 4 $
111 | vsep [ string "case" <+> pretty ep <+> string "of"
112 | , hang 4 $ align (vsep (map formatBranch' es))
113 | ]
114 | where formatBranch' (pat, branch) = fill 6 (pretty pat) <+> string "->" <+> align (pretty branch)
115 | pretty (EProp _ e n) = pretty e <> dot <> pretty n
116 | pretty (ENew _ e args) = string "new" <> space <> pretty e <> space <> nakedSingleOrTuple (map pretty args)
117 | pretty (EStringMap _ exprs) = encloseSep langle rangle comma $ map (\(n,v) -> pretty n <+> string "=>" <+> pretty v) exprs
118 |
119 |
120 | -----------------------------------------------------------------------------
121 |
122 | toChr :: Int -> Char
123 | toChr n = chr (ord 'a' + (n - 1))
124 |
125 | ptv :: Int -> String
126 | ptv x = foldr ((++) . (:[]) . toChr) [] (Digits.digits 26 (x + 1))
127 |
128 | colorFuncs :: [Doc -> Doc]
129 | colorFuncs = [ red
130 | , green
131 | , yellow
132 | , blue
133 | , magenta
134 | , cyan
135 | , white
136 | ]
137 |
138 | colorBy :: Int -> Doc -> Doc
139 | colorBy n = colorFuncs!!(n `mod` length colorFuncs)
140 |
141 | -- |
142 | -- >>> pretty (0 :: TVarName)
143 | -- "a"
144 | -- >>> pretty (26 :: TVarName)
145 | -- "aa"
146 | instance Pretty TVarName where
147 | pretty tvn = bold $ case tvn of
148 | Flex n -> colorBy n $ string $ ptv n
149 | Skolem n -> colorBy n $ string $ '!' : ptv n
150 |
151 | -- instance Pretty Bool where
152 | -- prettyTab _ x = show x
153 |
154 | instance Pretty TypeId where
155 | pretty (TypeId n) = dullred $ text $ 'R' : ptv n
156 |
157 | instance Pretty TBody where
158 | pretty (TVar n) = pretty n
159 | pretty x = bold $ text $ case show x of
160 | 'T':xs -> xs
161 | xs -> xs
162 |
163 | instance Pretty TConsName where
164 | pretty = dullgreen . text . show
165 |
166 | instance Pretty RowTVar where
167 | pretty t = dullblue $ pretty (getRowTVar t)
168 |
169 | instance Show t => Pretty (FlatRowEnd t) where
170 | pretty = text . show
171 |
172 | instance Pretty Type where
173 | pretty = prettyType . unFix
174 |
175 | instance Pretty (FType Type) where
176 | pretty = prettyType
177 |
178 | prettyType :: FType Type -> Doc
179 | prettyType (TBody t) = pretty t
180 | prettyType (TFunc ts tres) = wrapThis this $ parens $ args <+> string "->" <+> pretty tres
181 | where nonThisArgs = map pretty . drop 1 $ ts
182 | (this, args) = case ts of
183 | [] -> (Nothing, nakedSingleOrTuple nonThisArgs)
184 | (this_:_) -> (Just this_, nakedSingleOrTuple nonThisArgs)
185 | wrapThis Nothing s = s
186 | wrapThis (Just (Fix (TBody TUndefined))) s = s
187 | wrapThis (Just (Fix (TBody TEmptyThis))) s = s
188 | -- if "this" is a recursive type, only show the recursive type name (no params) - this is "lossy"
189 | wrapThis (Just (Fix (TCons (TName name) _))) s = pretty name <> dot <> s
190 | wrapThis (Just (Fix (TBody (TVar n)))) s | not (n `Set.member` (freeTypeVars $ drop 1 ts)) = s
191 | wrapThis (Just t) s = pretty t <> dot <> s
192 | -- prettyTab _ (TCons TFunc ts) = error $ "Malformed TFunc: " ++ intercalate ", " (map pretty ts)
193 | prettyType (TCons TArray [t]) = brackets $ pretty t
194 | prettyType (TCons TTuple ts) = pretty ts
195 | prettyType (TCons (TName name) ts) = angles $ pretty name <> colon <+> hsep (map pretty ts)
196 | prettyType (TCons TStringMap [t]) = text "StringMap " <+> pretty t
197 | prettyType (TCons TRef [t]) = text "Mut" <+> pretty t
198 | prettyType (TCons tcn ts) = error $ "Malformed TCons: " ++ show (pretty tcn <+> pretty ts)
199 | prettyType (TRow label rl) =
200 | hsep [ case label of
201 | Just l' -> string l' <> string "="
202 | Nothing -> empty
203 | , encloseSep (string "{ ") space (string ", ") body'
204 | <> case r of
205 | FlatRowEndTVar r' -> maybe empty ((text "|" <+>) . pretty) r'
206 | FlatRowEndRec tid ts -> comma <+> indent 4 (pretty (Fix $ TCons (TName tid) ts)) -- TODO
207 | <> rbrace
208 | ]
209 | where (props, r) = flattenRow rl
210 | isGet (TPropGetName _) = True
211 | isGet _ = False
212 | isSet (TPropSetName _) = True
213 | isSet _ = False
214 |
215 | propKeysByName = map (\ps -> let name = tpropName . fst $ head ps
216 | keys = map fst ps
217 | in (name, snd $ head ps, (any isGet keys, any isSet keys)))
218 | $ List.groupBy (\(x,xv) (y,yv) -> tpropName x == tpropName y && xv == yv) $ Map.toList props
219 | printProp' (n,v,getSet) = pn <> string ":" <+> align (pretty v)
220 | where pn = case getSet of
221 | (True, True) -> pretty n
222 | (True, False) -> string "get" <+> pretty n
223 | (False, True) -> string "set" <+> pretty n
224 | _ -> error "Expected at least one of get or set"
225 | body' = map printProp' propKeysByName
226 |
227 | instance Pretty TProp where
228 | pretty (TPropSetName n) = text "set" <+> pretty n
229 | pretty (TPropGetName n) = text "get" <+> pretty n
230 |
231 | instance Pretty ClassName where
232 | pretty (ClassName c) = text c
233 |
234 | instance (Pretty t) => Pretty (TPred t) where
235 | pretty (TPredIsIn cn t) = pretty cn <+> pretty t
236 |
237 | instance (VarNames t, Pretty t) => Pretty (TQual t) where
238 | pretty (TQual [] t) = pretty t
239 | pretty (TQual preds t) = pretty preds <+> pretty "=>" <+> align (pretty t)
240 |
241 | instance (Ord t, VarNames t, Pretty t) => Pretty (TScheme t) where
242 | pretty (TScheme vars t) = forall <> align (pretty t)
243 | where forall = if null vars then empty else text "forall" <+> hsep (map pretty vars) <> pretty "." <> space
244 |
245 | -- instance (Pretty a, Pretty b) => Pretty (Either a b) where
246 | -- prettyTab n (Left x) = "Error: " ++ prettyTab n x
247 | -- prettyTab n (Right x) = prettyTab n x
248 |
249 | instance (Pretty k, Pretty v) => Pretty (Map k v) where
250 | pretty s = string "Map" <+> encloseSep lbrace rbrace comma (map (\(k,v) -> pretty k <+> pretty "=>" <+> pretty v) $ Map.toList s)
251 |
252 | instance (Pretty k) => Pretty (Set.Set k) where
253 | pretty s = string "Set" <+> encloseSep lbrace rbrace comma (map pretty $ Set.toList s)
254 |
255 | -- instance Pretty GenInfo where
256 | -- prettyTab _ g = show g
257 |
258 | instance Pretty TypeError where
259 | pretty (TypeError s m) = pretty s <> text ": Error:" <+> line <+> indent 4 (pretty m)
260 |
261 | instance Pretty NameSource where
262 | pretty = string . show
263 |
264 | instance Pretty VarId where
265 | pretty = string . show
266 |
267 | instance (Ord t, VarNames t, Pretty t) => Pretty (Class t) where
268 | pretty c = braces $ string "instances = " <+> pretty (classInstances c)
269 |
270 | instance Pretty InferState where
271 | pretty (InferState ns sub vs tn cs pu) =
272 | text "InferState"
273 | <+> (align . encloseSep lbrace rbrace comma
274 | $ [ fill 10 (string "nameSource: ") <+> pretty ns
275 | , fill 10 (string "subst: ") <+> pretty sub
276 | , fill 10 (string "varSchemes: ") <+> pretty vs
277 | , fill 10 (string "namedTypes: ") <+> pretty tn
278 | , fill 10 (string "pendingUni: ") <+> pretty pu
279 | , fill 10 (string "classes: ") <+> pretty cs
280 | ])
281 |
--------------------------------------------------------------------------------
/src/Infernu/Source.hs:
--------------------------------------------------------------------------------
1 | -- |
2 |
3 | module Infernu.Source
4 | ( GenInfo(..)
5 | , Source(..)
6 | , emptySource
7 | , TypeError(..)
8 | ) where
9 |
10 | import qualified Text.Parsec.Pos as Pos
11 | import Text.PrettyPrint.ANSI.Leijen (Doc)
12 |
13 | import Infernu.Prelude
14 |
15 | data GenInfo = GenInfo { isGen :: Bool, declName :: Maybe String }
16 | deriving (Show, Eq, Ord)
17 |
18 | newtype Source = Source (GenInfo, Pos.SourcePos)
19 | deriving (Show, Eq, Ord)
20 |
21 | emptySource :: Source
22 | emptySource = Source (GenInfo True Nothing, Pos.initialPos "")
23 |
24 | data TypeError = TypeError { source :: Source, message :: Doc }
25 | deriving (Show)
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/Infernu/Util.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE CPP #-}
2 | module Infernu.Util (checkFiles, annotatedSource, checkSource) where
3 |
4 | import Control.Monad (forM, when)
5 | import Data.Maybe (catMaybes)
6 | import Data.List (intercalate)
7 | import qualified Data.Set as Set
8 | import qualified Language.ECMAScript3.Parser as ES3Parser
9 | import qualified Language.ECMAScript3.PrettyPrint as ES3Pretty
10 | import qualified Language.ECMAScript3.Syntax as ES3
11 | import qualified Text.Parsec.Pos as Pos
12 | import Text.PrettyPrint.ANSI.Leijen (Pretty (..), (<+>), string, renderPretty, Doc, displayS)
13 |
14 | import Infernu.Prelude
15 | import Infernu.Options (Options(..))
16 | import Infernu.Parse (translate)
17 | -- TODO move pretty stuff to Pretty module
18 | import Infernu.Infer (getAnnotations, minifyVars, runTypeInference)
19 | import Infernu.Types (QualType)
20 | import Infernu.Source (GenInfo(..), Source(..), TypeError(..))
21 |
22 | zipByPos :: [(Pos.SourcePos, String)] -> [(Int, String)] -> [String]
23 | zipByPos [] xs = map snd xs
24 | zipByPos _ [] = []
25 | zipByPos ps'@((pos, s):ps) xs'@((i,x):xs) = if Pos.sourceLine pos == i
26 | then formattedAnnotation : zipByPos ps xs'
27 | else x : zipByPos ps' xs
28 | where indentToColumn n = replicate (n-1) ' '
29 | isMultiline = length sLines > 1
30 | sLines = lines s
31 | formattedAnnotation = if isMultiline
32 | then ("/*"
33 | ++ indentToColumn (Pos.sourceColumn pos - 2)
34 | ++ head sLines
35 | ++ "\n"
36 | ++ (intercalate "\n" . map (\l -> indentToColumn (Pos.sourceColumn pos) ++ l) $ tail sLines) ++ " */")
37 | else "//" ++ indentToColumn (Pos.sourceColumn pos - 2) ++ s
38 |
39 |
40 | indexList :: [a] -> [(Int, a)]
41 | indexList = zip [1..]
42 |
43 |
44 | checkSource :: String -> Either TypeError [(Source, QualType)]
45 | checkSource src = case ES3Parser.parseFromString src of
46 | Left parseError -> Left $ TypeError { source = Source (GenInfo True Nothing, Pos.initialPos ""), message = string (show parseError) }
47 | Right expr -> -- case ES3.isValid expr of
48 | -- False -> Left $ TypeError { source = Source (GenInfo True, Pos.initialPos ""), message = "Invalid syntax" }
49 | -- True ->
50 | fmap getAnnotations $ fmap minifyVars $ runTypeInference $ fmap Source $ translate $ ES3.unJavaScript expr
51 |
52 | checkFiles :: Options -> [String] -> IO (Either TypeError [(Source, QualType)])
53 | checkFiles options fileNames = do
54 | expr <- concatMap ES3.unJavaScript <$> forM fileNames ES3Parser.parseFromFile
55 | when (optShowParsed options) $ putStrLn $ show $ ES3Pretty.prettyPrint expr
56 | let expr' = fmap Source $ translate $ expr
57 | when (optShowCore options) $ putStrLn $ show $ pretty expr'
58 | let expr'' = fmap minifyVars $ runTypeInference expr'
59 | res = fmap getAnnotations expr''
60 | return res
61 |
62 | showWidth :: Int -> Doc -> String
63 | showWidth w x = displayS (renderPretty 0.4 w x) ""
64 |
65 | showDoc :: Doc -> String
66 | showDoc = showWidth 120
67 |
68 | annotatedSource :: [(Source, QualType)] -> [String] -> String
69 | annotatedSource xs sourceCode = unlines $ zipByPos (prettyRes $ unGenInfo $ filterGen xs) indexedSource
70 | where indexedSource = indexList sourceCode
71 | unGenInfo :: [(Source, QualType)] -> [(String, Pos.SourcePos, QualType)]
72 | unGenInfo = catMaybes . map (\(Source (g, s), q) -> fmap (\n -> (n, s, q)) $ declName g)
73 | filterGen :: [(Source, QualType)] -> [(Source, QualType)]
74 | filterGen = filter (\(Source (g, _), _) -> not . isGen $ g)
75 | prettyRes = Set.toList . Set.fromList . fmap (\(n, s, q) -> (s, showDoc $ pretty n <+> string ":" <+> pretty q))
76 |
--------------------------------------------------------------------------------
/stack.yaml:
--------------------------------------------------------------------------------
1 | flags: {}
2 | packages:
3 | - '.'
4 | extra-deps:
5 | resolver: lts-6.27
6 |
--------------------------------------------------------------------------------
/test/.gitignore:
--------------------------------------------------------------------------------
1 | .psci_modules/
2 |
--------------------------------------------------------------------------------
/test/Demo.hs:
--------------------------------------------------------------------------------
1 | module Demo where
2 |
3 | import Data.List (intercalate)
4 |
5 | import Text.PrettyPrint.ANSI.Leijen (Pretty (..))
6 | import Infernu.Util (checkFiles)
7 | import Infernu.Options (defaultOptions)
8 | import Infernu.Prelude
9 |
10 | import System.Environment (getArgs)
11 |
12 | isRight :: Either a b -> Bool
13 | isRight (Right _) = True
14 | isRight _ = False
15 |
16 | main :: IO ()
17 | main = do
18 | args <- getArgs
19 | let [shouldPassS, fileName] = args
20 | res <- fmap last <$> checkFiles defaultOptions [fileName]
21 | let shouldPass = if shouldPassS == "y" then id else not
22 | typeChecked = isRight res
23 | message = case res of
24 | Left e -> show $ pretty e
25 | Right _ -> ""
26 | toOk = bool "FAIL" "OK" . shouldPass
27 | --print $ fmap (pretty . snd) res
28 | putStrLn $ "// " ++ toOk typeChecked ++ " " ++ (intercalate " | " $ lines message)
29 |
--------------------------------------------------------------------------------
/test/Test.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE TemplateHaskell, TypeSynonymInstances, FlexibleInstances #-}
2 | module Test where
3 |
4 | import Infernu.Types
5 |
6 | main :: IO ()
7 | main = do
8 | res <- runAllTests
9 | print res
10 | return ()
11 |
12 |
--------------------------------------------------------------------------------
/test/fail.txt:
--------------------------------------------------------------------------------
1 | // FAIL valid/arguably/if-undefined.js:8:3: Error: | Failed unif y valid/arguably/if-undefined.js
2 | // FAIL valid/nothis-as-obj-param.js:10:7: Error: | Failed unify y valid/nothis-as-obj-param.js
3 | // FAIL valid/nothis-as-param.js:8:7: Error: | Failed unifying: y valid/nothis-as-param.js
4 | // FAIL valid/objlit-func.js:9:1: Error: | Failed unifying: | y valid/objlit-func.js
5 | // FAIL valid/poly-method-recursive-this.js:18:17: Error: | Fail y valid/poly-method-recursive-this.js
6 |
--------------------------------------------------------------------------------
/test/invalid/.flowconfig:
--------------------------------------------------------------------------------
1 | [ignore]
2 |
3 | [include]
4 |
--------------------------------------------------------------------------------
/test/invalid/array-assign-in-func.js:
--------------------------------------------------------------------------------
1 | function f(arr, x) {
2 | arr[0] = x;
3 | }
4 | f;
5 | var a = [1];
6 | f(a, 's');
7 |
--------------------------------------------------------------------------------
/test/invalid/array-assign.js:
--------------------------------------------------------------------------------
1 | var arr = [];
2 | arr[0] = 1;
3 | arr[1] = 'a';
4 |
--------------------------------------------------------------------------------
/test/invalid/array-idx.js:
--------------------------------------------------------------------------------
1 | var x = [0,1,2];
2 | var y = ['a','b'];
3 |
4 | var z = [x[0], y[0]];
5 |
--------------------------------------------------------------------------------
/test/invalid/array-param.js:
--------------------------------------------------------------------------------
1 | function f(x) {
2 | x.stuff = 2;
3 | return x;
4 | }
5 | var arr = [];
6 | f(arr[0]);
7 | arr[0].muff = 3;
8 | arr[0] = { muff: 123 }; // should require also .stuff = to work.
9 | return arr;
10 |
--------------------------------------------------------------------------------
/test/invalid/array.js:
--------------------------------------------------------------------------------
1 | var x = [1, 2, 'a'];
2 |
--------------------------------------------------------------------------------
/test/invalid/blo.js:
--------------------------------------------------------------------------------
1 | function f(x) {
2 | var len = x.length;
3 | return { obj: x, len: len };
4 | }
5 | var y = f({ moshe: 3, length: 'a' });
6 | var z = f({ length: 2 });
7 |
8 | var g = y.len;
9 | g = z.obj.length;
10 | g;
11 |
--------------------------------------------------------------------------------
/test/invalid/blo2.js:
--------------------------------------------------------------------------------
1 | var x = 2;
2 | var y = { obj: { bla: x }, blo: x};
3 | y.obj.bla = 2;
4 | y.blo = 'a';
5 | y;
6 |
--------------------------------------------------------------------------------
/test/invalid/builtins/Math.js:
--------------------------------------------------------------------------------
1 | Math.E = 0;
2 |
--------------------------------------------------------------------------------
/test/invalid/builtins/binary-not.js:
--------------------------------------------------------------------------------
1 | var x = ~false;
2 |
--------------------------------------------------------------------------------
/test/invalid/builtins/equality.js:
--------------------------------------------------------------------------------
1 | var x = 2 == 'a';
2 |
--------------------------------------------------------------------------------
/test/invalid/builtins/isFinite.js:
--------------------------------------------------------------------------------
1 | var x = isFinite('a');
2 |
--------------------------------------------------------------------------------
/test/invalid/builtins/logical-not.js:
--------------------------------------------------------------------------------
1 | var x = !2;
2 |
--------------------------------------------------------------------------------
/test/invalid/builtins/minus.js:
--------------------------------------------------------------------------------
1 | var x = true - 4;
2 |
--------------------------------------------------------------------------------
/test/invalid/builtins/minusminus.js:
--------------------------------------------------------------------------------
1 | var x = false;
2 | var y = x--;
3 |
--------------------------------------------------------------------------------
/test/invalid/builtins/or.js:
--------------------------------------------------------------------------------
1 | var y = '' || 2;
2 |
--------------------------------------------------------------------------------
/test/invalid/builtins/parse.js:
--------------------------------------------------------------------------------
1 | var x = parseFloat(3.0);
2 |
3 |
4 |
--------------------------------------------------------------------------------
/test/invalid/builtins/parseInt.js:
--------------------------------------------------------------------------------
1 | var y = parseInt('3'); // base always required
2 |
--------------------------------------------------------------------------------
/test/invalid/builtins/plus-func.js:
--------------------------------------------------------------------------------
1 | function f(x,y) { return x + y; }
2 |
3 | f;
4 |
5 |
6 | f(false,false);
7 |
8 |
--------------------------------------------------------------------------------
/test/invalid/builtins/plus.js:
--------------------------------------------------------------------------------
1 | var x = 2 + false;
2 |
--------------------------------------------------------------------------------
/test/invalid/builtins/plusplus.js:
--------------------------------------------------------------------------------
1 | var x = false;
2 | var y = x++;
3 |
--------------------------------------------------------------------------------
/test/invalid/builtins/regex-exec.js:
--------------------------------------------------------------------------------
1 | var r = /123/g;
2 |
3 | var m = r.exec('12');
4 |
5 |
6 | var x = 0;
7 | x = m.index;
8 |
9 | var s = '';
10 | s = m[0];
11 | s = m.input;
12 |
13 | m[0] = 'a';
14 |
15 |
--------------------------------------------------------------------------------
/test/invalid/builtins/string-error.js:
--------------------------------------------------------------------------------
1 | function f(o) { return o.moo; }
2 | f('hi');
3 |
--------------------------------------------------------------------------------
/test/invalid/builtins/string-replace.js:
--------------------------------------------------------------------------------
1 | function f(p, r) { return 'hi'.replace(p, r); }
2 | var g = function(p, r) { return 'hi'.replace(p, r); };
3 | var s = 'abc'.replace('junk', 'punk');
4 | var t = 'abd'.replace(/bd/, 'pink');
5 | f(2,'a');
6 |
7 |
--------------------------------------------------------------------------------
/test/invalid/builtins/string_constr.js:
--------------------------------------------------------------------------------
1 | var Foo = String;
2 | var x = new Foo('b');
3 |
--------------------------------------------------------------------------------
/test/invalid/builtins/typeof.js:
--------------------------------------------------------------------------------
1 | var x = typeof 3;
2 | x = 2;
3 |
4 |
--------------------------------------------------------------------------------
/test/invalid/builtins/uri.js:
--------------------------------------------------------------------------------
1 | decodeURI('bla') == 1;
2 | decodeURIComponent(2) == 'bla';
3 | encodeURI();
4 | encodeURIComponent('bla') == 3;
5 |
--------------------------------------------------------------------------------
/test/invalid/call2.js:
--------------------------------------------------------------------------------
1 | function f(x) { x = 0; return x(2); }
2 |
--------------------------------------------------------------------------------
/test/invalid/cond.js:
--------------------------------------------------------------------------------
1 | var x = false ? false : 0;
2 | x;
3 |
--------------------------------------------------------------------------------
/test/invalid/debounce-simplified.js:
--------------------------------------------------------------------------------
1 | function debounce(f) {
2 | var scheduledArgs = [];
3 | return function(arg) {
4 | scheduledArgs = [arg];
5 | return {
6 | invoke: function() {
7 | f(scheduledArgs[0]);
8 | }
9 | };
10 | };
11 | }
12 | var fonce = debounce(function (x) { return x[0]; });
13 | fonce(false);
14 | //return debounce;
15 |
--------------------------------------------------------------------------------
/test/invalid/debounce.js:
--------------------------------------------------------------------------------
1 | var $timeout = {
2 | set: function(f, num) { num = 0; return 0; },
3 | cancel: function(t) { t=0; }
4 | };
5 |
6 | function debounce(f, millis) {
7 | var timer = 0;
8 | var timerSet = false;
9 | var scheduledArgs = [];
10 | function resetTimeout() {
11 | if (timerSet) {
12 | $timeout.cancel(timer);
13 | }
14 | timerSet = true;
15 | timer = $timeout.set(function() {
16 | // Note: f may end up running more than once per args set.
17 | f(scheduledArgs[0]);//.apply(null, scheduledArgs);
18 | }, millis);
19 | return;
20 | }
21 | return function(arg) {
22 | scheduledArgs = [arg]; //Array.prototype.slice.call(arguments, 0);
23 | return {
24 | invoke: function() {
25 | resetTimeout();
26 | }
27 | };
28 | };
29 | }
30 | var fonce = debounce(function (x) { return x[0]; }, 0);
31 | fonce(false);
32 | //return debounce;
33 |
--------------------------------------------------------------------------------
/test/invalid/dowhile.js:
--------------------------------------------------------------------------------
1 | do { 1; } while (2)
2 |
--------------------------------------------------------------------------------
/test/invalid/fix-usage.js:
--------------------------------------------------------------------------------
1 | function fix(f) { return f(fix(f)); }
2 |
3 | var g = fix(function (x) { var y = 0; y = x; return 'a'; });
4 | g;
5 |
--------------------------------------------------------------------------------
/test/invalid/for-1.js:
--------------------------------------------------------------------------------
1 | var z = false;
2 | var i = 0;
3 | for (i = 2; i; i = 3) {
4 | z = i;
5 | }
6 |
--------------------------------------------------------------------------------
/test/invalid/for-2.js:
--------------------------------------------------------------------------------
1 | var z = 0;
2 | for (i = 2; i; i = 3) {
3 | z = i;
4 | }
5 |
--------------------------------------------------------------------------------
/test/invalid/for-in.js:
--------------------------------------------------------------------------------
1 | var arr = ['a','b'];
2 |
3 | var i = 0;
4 | for (i in arr) {
5 |
6 | }
7 |
--------------------------------------------------------------------------------
/test/invalid/for.js:
--------------------------------------------------------------------------------
1 | var i = 0;
2 | for (i = 2; i; i = 3) {
3 | 3;
4 | }
5 |
--------------------------------------------------------------------------------
/test/invalid/func-as-row.js:
--------------------------------------------------------------------------------
1 | function f(g) { return g.length; }
2 | f(function(x) { return x;});
3 |
4 |
--------------------------------------------------------------------------------
/test/invalid/func-no-currying.js:
--------------------------------------------------------------------------------
1 | var f = function (x) {
2 | return function (y) {
3 | return [x,y];
4 | };
5 | };
6 | function g(x, y) {
7 | return [x, y];
8 | };
9 | var z = [f(0), g(0)];
10 | z;
11 |
--------------------------------------------------------------------------------
/test/invalid/generalize.js:
--------------------------------------------------------------------------------
1 | var x = 0;
2 |
3 | var f = function(a) { a = x; return a; };
4 |
5 |
6 | var obj = { method: function(a) { return a; } };
7 |
8 | obj.method = function(a) { a = x; return a; };
9 |
--------------------------------------------------------------------------------
/test/invalid/getput.js:
--------------------------------------------------------------------------------
1 | function getput(_) {
2 | var x = [];
3 | return function(y) { var old = x; x = y; return old; };
4 | };
5 |
6 | var gp = getput(false);
7 | gp([1]);
8 | gp([false]);
9 | gp;
10 |
--------------------------------------------------------------------------------
/test/invalid/if.js:
--------------------------------------------------------------------------------
1 | if (2) { 1; } else { false; }
2 |
--------------------------------------------------------------------------------
/test/invalid/infer-from-call.js:
--------------------------------------------------------------------------------
1 | function f(o) { o.momo(2); }
2 | f({momo: 'baboon'}); // should fail, momo is not a function
3 |
--------------------------------------------------------------------------------
/test/invalid/infinite.js:
--------------------------------------------------------------------------------
1 | function f(x) { return x(x); }
2 |
--------------------------------------------------------------------------------
/test/invalid/infinite2.js:
--------------------------------------------------------------------------------
1 | var f = function(x) { return function(z) { return f(x); }; };
2 | f
3 |
--------------------------------------------------------------------------------
/test/invalid/inheritance.js:
--------------------------------------------------------------------------------
1 | function Base() {
2 | this.x = 0;
3 | }
4 |
5 | function Sub() {
6 | this.y = 'b';
7 | }
8 |
9 | var a = new Sub();
10 | a.x = 2;
11 | a.y = 'c';
12 |
--------------------------------------------------------------------------------
/test/invalid/method-type.js:
--------------------------------------------------------------------------------
1 | function test(o) { return o.method(2); }
2 | test;
3 | var y = test({method: function(x) { return x; }});
4 | y='a';
5 |
--------------------------------------------------------------------------------
/test/invalid/mutability/bug.js:
--------------------------------------------------------------------------------
1 | var x = function(a) { return a; };
2 | function setX(v) { x = v; return false; }
3 | setX(function (a) { return 'a'; });
4 | setX;
5 | x = function(a) { return 2; };
6 |
--------------------------------------------------------------------------------
/test/invalid/mutability/bug2.js:
--------------------------------------------------------------------------------
1 | var x = function (a) { return a; };
2 | x = function (a) { return false; };
3 | x;
4 | x('a');
5 |
--------------------------------------------------------------------------------
/test/invalid/mutability/bug3.js:
--------------------------------------------------------------------------------
1 | var x = function (a) { return a; };
2 | var y = x('bla');
3 | x = function (b) { return false; }; // should cause an error, because x was already used as a string -> string
4 | x;
5 |
6 |
--------------------------------------------------------------------------------
/test/invalid/mutability/bug5.js:
--------------------------------------------------------------------------------
1 | var x = function (a) { return a; }; // forall a. a -> a | c -> c | (String -> String)
2 | var getX = function (v1) { return x; }; // forall b a. b -> (a -> a) | forall b. b -> (c -> c) | forall b. b -> (String -> String)
3 | var setX = function (v2) { x = v2; return true; }; // x is mutable => | (c -> c) -> Bool | (String -> String) -> Bool
4 | setX(function(a2) { return 'a'; }); // | c => String
5 | getX(false)(false);
6 |
7 |
--------------------------------------------------------------------------------
/test/invalid/mutability/bug6.js:
--------------------------------------------------------------------------------
1 | var x = [];
2 | var getX = function (v1) { return x; }; // forall b. X = forall b X(a). X(a)
3 | var setX = function (v2) { x = v2; return true; }; // X -> Bool
4 | setX([1]); // X ~ forall c. c -> String ==> forall a. a -> a ~ forall c. c -> String ==> a=c, a=String ==> X = String -> String
5 | getX(false);
6 | setX([false]);
7 |
8 |
--------------------------------------------------------------------------------
/test/invalid/mutability/bug7.js:
--------------------------------------------------------------------------------
1 | function makeX(_) {
2 | var x = [];
3 | var setX = function (v2) { x = [v2]; return true; };
4 | return setX;
5 | }
6 | var mx1 = makeX(0);
7 | mx1(0);
8 | mx1(false);
9 | var mx2 = makeX(false);
10 | mx2(false);
11 | mx1;
12 |
--------------------------------------------------------------------------------
/test/invalid/mutability/mutable-id.js:
--------------------------------------------------------------------------------
1 | var x = function(z) { return z; };
2 | var z = x(true);
3 | var y = x('a');
4 | x = function(z1) { return z1; };
5 | x;
6 |
--------------------------------------------------------------------------------
/test/invalid/naked_array.js:
--------------------------------------------------------------------------------
1 |
2 | function test() {
3 | var result = [];
4 | result[0] = 2;
5 | return result[0];
6 | }
7 |
8 | var s = 'a';
9 | s = test();
10 |
--------------------------------------------------------------------------------
/test/invalid/new-override.js:
--------------------------------------------------------------------------------
1 | function Constr(x) { this.bla = x; }
2 | var constr = new Constr(2);
3 | constr.bla = 'a';
4 |
--------------------------------------------------------------------------------
/test/invalid/new-return.js:
--------------------------------------------------------------------------------
1 | function Constr(x) { this.bla = x; return 2; }
2 | var constr = new Constr(2);
3 | constr.another = 2;
4 |
5 |
--------------------------------------------------------------------------------
/test/invalid/new.js:
--------------------------------------------------------------------------------
1 | function Constr(x) { this.bla = x; }
2 | var constr = new Constr(2);
3 | constr.another = 2;
4 |
5 |
--------------------------------------------------------------------------------
/test/invalid/not-a-method.js:
--------------------------------------------------------------------------------
1 | function func() { }
2 |
3 | var obj = {};
4 |
5 | obj.func(); // should fail.
6 |
--------------------------------------------------------------------------------
/test/invalid/num.js:
--------------------------------------------------------------------------------
1 | function f(x) { var y = 0; y = x[0]; return false; }
2 | var j = ['b'];
3 | f(j);
4 | j;
5 |
--------------------------------------------------------------------------------
/test/invalid/object-chaining.js:
--------------------------------------------------------------------------------
1 | function Obj(y) {
2 | this.x = y;
3 | this.doIt = function(x) { this.x = x; return this; };
4 | }
5 | var o = new Obj(2);
6 | var o2 = o.doIt(2);
7 | var o3 = o2.doIt('a');
8 |
--------------------------------------------------------------------------------
/test/invalid/occurs-erisco.js:
--------------------------------------------------------------------------------
1 | var f = function (x) {
2 | x.y = function (z) { return f({y:z}); };
3 | return x;
4 | };
5 |
6 | f({ y : function (x){
7 | var z = 2;
8 | z = x;
9 | return x;}
10 | }).y(0);
11 |
--------------------------------------------------------------------------------
/test/invalid/occurs.js:
--------------------------------------------------------------------------------
1 | //[a] -> [a]
2 | var f = function (x) {
3 | // [a, z -> [z]]
4 | var bla = [x[0], function (z) {
5 | return f([z]); }];
6 | return x;
7 | };
8 | f;
9 |
10 | f([false]);
11 |
12 |
--------------------------------------------------------------------------------
/test/invalid/occurs2.js:
--------------------------------------------------------------------------------
1 | var f = function (x) {
2 | return function (z) { return f(z); };
3 | };
4 |
--------------------------------------------------------------------------------
/test/invalid/open-row-erisco.js:
--------------------------------------------------------------------------------
1 | function open(o) {
2 | o.x = 0;
3 | return o;
4 | }
5 | var y = open({x:0});
6 | y.z = 3;
7 | y;
8 |
--------------------------------------------------------------------------------
/test/invalid/open-row.js:
--------------------------------------------------------------------------------
1 | var o = {bla: 1};
2 | o.blo = 2;
3 | o;
4 |
--------------------------------------------------------------------------------
/test/invalid/polyfunc.js:
--------------------------------------------------------------------------------
1 | var f = function(x) { return function(y) { return y[x]; }; };
2 | var num = f([3])([2]);
3 | f;
4 |
5 |
--------------------------------------------------------------------------------
/test/invalid/polyfunc2.js:
--------------------------------------------------------------------------------
1 | var f = function(x, y) { return function(z) { x = z; return y; }; };
2 | f(1, 'a')(['b']);
3 | f;
4 |
5 |
--------------------------------------------------------------------------------
/test/invalid/polyfunc3.js:
--------------------------------------------------------------------------------
1 | function debounce(f) {
2 | var timer = 0;
3 | var timerSet = false;
4 | var scheduledArgs = [];
5 | function resetTimeout() {
6 | if (timerSet) {
7 | }
8 | timerSet = true;
9 | function g() {
10 | // Note: f may end up running more than once per args set.
11 | f(scheduledArgs[0]);//.apply(null, scheduledArgs);
12 | }
13 | return g;
14 | }
15 | return function(arg) {
16 | scheduledArgs = [arg]; //Array.prototype.slice.call(arguments, 0);
17 | return {
18 | invoke: function() {
19 | resetTimeout();
20 | }
21 | };
22 | };
23 | }
24 | var fonce = debounce(function (x) { return x[0]; }, 0);
25 | fonce('a');
26 | //return debounce;
27 |
--------------------------------------------------------------------------------
/test/invalid/promise-method-workaround-simplified.js:
--------------------------------------------------------------------------------
1 | // Based on https://gist.github.com/unscriptable/814052 - (c) copyright unscriptable.com / John Hann, License MIT
2 |
3 | function Promise () {
4 | var _thens = [];
5 | var that = this;
6 |
7 | this.then = function (onResolve, onReject) {
8 | _thens.push({ resolve: onResolve, reject: onReject });
9 | };
10 |
11 | var resolve = function (val) {
12 | var errorFunc = function(val) { throw new Error('Already completed.'); };
13 | var t3 = that;
14 | that.then = function(resolve, reject) { resolve(val); };
15 | that.resolve = errorFunc;
16 | var r = _thens[0].resolve;
17 | r(val);
18 | _thens = [];
19 | };
20 |
21 | var t1 = resolve;
22 | this.resolve = resolve;
23 | var t2 = this;
24 | };
25 |
26 | var promise = new Promise();
27 |
28 | var str = 'a';
29 |
30 | promise.then(function(x) {str =x; }, function (ex) { });
31 |
32 | promise.resolve(3); // error; 3 is not a string
33 |
34 |
--------------------------------------------------------------------------------
/test/invalid/promise-method-workaround.js:
--------------------------------------------------------------------------------
1 | // Based on https://gist.github.com/unscriptable/814052 - (c) copyright unscriptable.com / John Hann, License MIT
2 |
3 | function Promise () {
4 | var _thens = [];
5 | var that = this;
6 | /* This is the "front end" API. */
7 |
8 | // then(onResolve, onReject): Code waiting for this promise uses the
9 | // then() method to be notified when the promise is complete. There
10 | // are two completion callbacks: onReject and onResolve. A more
11 | // robust promise implementation will also have an onProgress handler.
12 | this.then = function (onResolve, onReject) {
13 | // capture calls to then()
14 | _thens.push({ resolve: onResolve, reject: onReject });
15 | };
16 |
17 | /* This is the "back end" API. */
18 |
19 | // resolve(resolvedValue): The resolve() method is called when a promise
20 | // is resolved (duh). The resolved value (if any) is passed by the resolver
21 | // to this method. All waiting onResolve callbacks are called
22 | // and any future ones are, too, each being passed the resolved value.
23 | this.resolve = function (val) {
24 | var errorFunc = function(val) { throw new Error('Already completed.'); };
25 | var i = 0;
26 | that.then = function(resolve, reject) { resolve(val); };
27 | that.resolve = errorFunc;
28 | that.reject = errorFunc;
29 | for (i = 0; i < _thens.length; i++) {
30 | var r = _thens[i].resolve;
31 | r(val);
32 | }
33 | _thens = [];
34 | };
35 |
36 | // reject(exception): The reject() method is called when a promise cannot
37 | // be resolved. Typically, you'd pass an exception as the single parameter,
38 | // but any other argument, including none at all, is acceptable.
39 | // All waiting and all future onReject callbacks are called when reject()
40 | // is called and are passed the exception parameter.
41 | this.reject = function (ex) {
42 | var errorFunc = function(val) { throw new Error('Already completed.'); };
43 | var i = 0;
44 | that.then = function(resolve, reject) { reject(ex); };
45 | that.resolve = errorFunc;
46 | that.reject = errorFunc;
47 | for (i = 0; i < _thens.length; i++) {
48 | var r = _thens[i].reject;
49 | r(ex);
50 | }
51 | _thens = [];
52 | };
53 |
54 | };
55 |
56 | var promise = new Promise();
57 |
58 | var str = 'a';
59 |
60 | promise.then(function(x) {str =x; }, function (ex) { });
61 |
62 | promise.resolve(3); // error; 3 is not a string
63 |
64 |
--------------------------------------------------------------------------------
/test/invalid/promise-simple-api.js:
--------------------------------------------------------------------------------
1 | function Promise() {
2 | var thens = [];
3 | var that = this;
4 | that.then = function(cb) {
5 | var p = new Promise();
6 | thens.push(function(val) { p.resolve(cb(val)); });
7 | return p;
8 | };
9 | that.resolve = function(val) {
10 | var i = 0;
11 | for (i = 0; i < thens.length; i++) {
12 | thens[i](val);
13 | }
14 | that.then = function(cb) {
15 | var p = new Promise();
16 | p.resolve(cb(val));
17 | return p;
18 | };
19 | };
20 | };
21 |
22 |
23 | var p = new Promise();
24 | p.kaki = 2;
25 |
--------------------------------------------------------------------------------
/test/invalid/prop-polymorphic-assign.js:
--------------------------------------------------------------------------------
1 | var o2 = { method: function(x) { return x; }, jank: [] };
2 |
3 | o2.jank[0] = 3;
4 | o2.jank[1] = 'a';
5 |
--------------------------------------------------------------------------------
/test/invalid/prop-polymorphic-assign2-b.js:
--------------------------------------------------------------------------------
1 | var o2 = { method: function(x) { return x; } };
2 |
3 | var x = o2.method('a');
4 |
5 | // requires o2.method to become more specific (Number -> Number)
6 | // should not work.
7 | o2.method = function (x) { x = 2; return x; };
8 |
9 |
10 |
--------------------------------------------------------------------------------
/test/invalid/prop-polymorphic-assign2-this.js:
--------------------------------------------------------------------------------
1 | var o2 = { method: function(x) { return x; } };
2 |
3 | // Should fail because it's less general than forall a. a -> a
4 | o2.method = function (x) { x = this; return x; };
5 |
6 |
--------------------------------------------------------------------------------
/test/invalid/prop-polymorphic-assign2.js:
--------------------------------------------------------------------------------
1 | var o2 = { method: function(x) { return x; } };
2 |
3 | // causes o2.method to become more specific (Number -> Number)
4 | // and so should fail
5 | o2.method = function (x) { x = 2; return x; };
6 |
7 |
--------------------------------------------------------------------------------
/test/invalid/prop-polymorphic-assign3-b.js:
--------------------------------------------------------------------------------
1 | var obj = { prop : function(x) { return x; } }; var barr = [function(x) { return x; }]; barr[0](2); obj.prop = barr[0];
2 |
--------------------------------------------------------------------------------
/test/invalid/prop-polymorphic-assign3.js:
--------------------------------------------------------------------------------
1 | var obj = { prop : function(x) { return x; } }; var barr = [function(x) { return x; }]; obj.prop = barr[0]; barr[0](2);
2 |
--------------------------------------------------------------------------------
/test/invalid/prop-polymorphic-closure.js:
--------------------------------------------------------------------------------
1 | var o2 = { y: [], setY: function(x) { this.y = [x]; }, getY: function() { return this.y; } };
2 |
3 | o2.setY(2);
4 |
5 | var z = 'string';
6 |
7 | z = o2.getY();
8 |
--------------------------------------------------------------------------------
/test/invalid/prop-polymorphic-closure2.js:
--------------------------------------------------------------------------------
1 | var o2 = {
2 | last: function() {
3 | var y = [];
4 | return function(x2) {
5 | var z = y;
6 | y = [x2];
7 | return z;
8 | };
9 | }
10 | };
11 |
12 | o2;
13 |
14 |
15 | var f = o2.last();
16 |
17 | f('a');
18 |
19 | f(3);
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/test/invalid/prop-wrong.js:
--------------------------------------------------------------------------------
1 | function setX(obj, val) {
2 | obj.x = val;
3 | }
4 |
5 | setX({ notX: 2 }, 3);
6 |
--------------------------------------------------------------------------------
/test/invalid/prop_assign.js:
--------------------------------------------------------------------------------
1 | function setX(obj, val) {
2 | obj.x = val;
3 | }
4 |
5 | var z = { x: 3 };
6 | setX(z, 'a');
7 | z;
8 |
--------------------------------------------------------------------------------
/test/invalid/rank-2-method-b.js:
--------------------------------------------------------------------------------
1 | var obj = { bla: function(x) {return x; } };
2 | obj = { bla: function(x) {return 2; } };
3 | var x = 'a';
4 | x = obj.bla('b'); // returns 2, incompatible with 'a'
5 |
6 |
--------------------------------------------------------------------------------
/test/invalid/rank-2-method-simple.js:
--------------------------------------------------------------------------------
1 | var x = { ap: function(f) { f(); } };
2 |
3 | var y = [];
4 | x.ap(function() {y.push('a');});
5 | x.ap(function() {y.push(3);});
6 |
--------------------------------------------------------------------------------
/test/invalid/rank-2-method.js:
--------------------------------------------------------------------------------
1 | // node doesn't like _
2 | var _u = {
3 | each: function(list, iteratee) {
4 | var i = 0;
5 | for (i = 0; i < list.length; i++) {
6 | iteratee(list[i], i, list);
7 | }
8 | return list;
9 | }
10 | };
11 |
12 |
13 |
14 | var p = [];
15 |
16 | function test(f) {
17 | _u.each([1,2,3], f);
18 | }
19 |
20 |
21 | _u.each([1,2,3], function(e,i,l) { p.push(e + 'aba'); });
22 |
23 | p;
24 |
--------------------------------------------------------------------------------
/test/invalid/recursive-type.js:
--------------------------------------------------------------------------------
1 | var getObj = function (x) {
2 | return {
3 | x: x,
4 | method: function(y) { this.x = y; return this; }
5 | };
6 | };
7 | var x = getObj(2).method(3);
8 | var y = getObj('a').method('a');
9 | var z = x == y; // should fail. checker should enforce weak equality to only be applied when both sides have the same type.
10 |
11 |
--------------------------------------------------------------------------------
/test/invalid/return-multi.js:
--------------------------------------------------------------------------------
1 | function f() {
2 | var g = 'bla';
3 | if (false) {
4 | return 2;
5 | }
6 | return 3;
7 | g = 'momo';
8 | return g;
9 | }
10 |
11 | var x = f();
12 | var y = 2;
13 | y = x;
14 |
--------------------------------------------------------------------------------
/test/invalid/row-func.js:
--------------------------------------------------------------------------------
1 | function f(obj) {
2 | var x = obj.stuff;
3 | return 2;
4 | };
5 | f({});
6 |
--------------------------------------------------------------------------------
/test/invalid/row-return.js:
--------------------------------------------------------------------------------
1 | function f(parent) {
2 | return parent.child;
3 | }
4 | var x = f({ child: { grandChild: false } });
5 | x.smurf;
6 |
--------------------------------------------------------------------------------
/test/invalid/row1.js:
--------------------------------------------------------------------------------
1 | function f(x) {
2 | return x.c;
3 | }
4 | //f({a:2, c:3});
5 | //f({b:true, c:3});
6 | f({a:3});
7 | // var x = { a: 2, b: 'a' };
8 | // x = { a:3, b:'b',c:0 };
9 | // x.c;
10 |
--------------------------------------------------------------------------------
/test/invalid/stringmap-preds.js:
--------------------------------------------------------------------------------
1 | var m = { 'hi': function(x,y) { return x + y; },
2 | 'baoo': function(x,y) { return x; }
3 | };
4 |
5 | m['hi'](false, true);
6 |
--------------------------------------------------------------------------------
/test/invalid/switch-1.js:
--------------------------------------------------------------------------------
1 | switch (3) {
2 | case 'a': 'a';
3 | case 'b': 'b';
4 | defualt: null;
5 | }
6 |
7 |
--------------------------------------------------------------------------------
/test/invalid/switch-2.js:
--------------------------------------------------------------------------------
1 | switch (3) {
2 | case 2: 'a';
3 | case 'b': 'b';
4 | defualt: null;
5 | }
6 |
7 |
--------------------------------------------------------------------------------
/test/invalid/this-func.js:
--------------------------------------------------------------------------------
1 | function f(x) {
2 | this.doStuff(x);
3 | return x;
4 | }
5 | var bla={ f:f, doStuff: 789};
6 | bla.f(2); // should fail, doStuff isn't a function.
7 |
--------------------------------------------------------------------------------
/test/invalid/this.js:
--------------------------------------------------------------------------------
1 | var obj = { name: '3', getName: function() { return this.name; } };
2 | obj;
3 | var x = obj.getName();
4 | x;
5 | var f = obj.getName;
6 | f();
7 |
--------------------------------------------------------------------------------
/test/invalid/undefined.js:
--------------------------------------------------------------------------------
1 | function getUndefined() {}
2 | var x = 3;
3 | x = getUndefined();
4 |
--------------------------------------------------------------------------------
/test/invalid/unfortunately/inheritance.js:
--------------------------------------------------------------------------------
1 | function Base() {
2 | this.x = 0;
3 | }
4 |
5 | function Sub() {
6 | Base.call(this);
7 |
8 | this.y = 'b';
9 | }
10 |
11 | var a = new Sub();
12 | a.x = 2;
13 | a.y = 'c';
14 |
--------------------------------------------------------------------------------
/test/invalid/unfortunately/mauke.js:
--------------------------------------------------------------------------------
1 | var a = "x";
2 | function foo(x) {
3 | var e;
4 | if (false) { e = "equal"; }
5 | else { e = "not equal"; }
6 | return x;
7 | }
8 | foo("x");
9 | a = 42;
10 | foo(0);
11 |
--------------------------------------------------------------------------------
/test/invalid/unfortunately/new-recursive-prototype.js:
--------------------------------------------------------------------------------
1 | function Foo() {
2 |
3 | }
4 |
5 | Foo.prototype.bar = function() { return new Foo(); };
6 |
7 |
8 | var x = new Foo();
9 |
10 | var y = x.bar();
11 |
--------------------------------------------------------------------------------
/test/invalid/unfortunately/polymorphic-row-fields/prop-polymorphic-assign.js:
--------------------------------------------------------------------------------
1 | var o2 = { method: function(x) { return x; } };
2 |
3 | // Should succeed because it's more general than forall a. (this: a, a) -> a
4 | o2.method = function (x) { return x; };
5 |
6 |
--------------------------------------------------------------------------------
/test/invalid/unfortunately/polymorphic-row-fields/prop-polymorphic-assign2-this.js:
--------------------------------------------------------------------------------
1 | var o2 = { method: function(x) { x = this; return x; } };
2 |
3 | // Should succeed because it's more general than forall a. (this: a, a) -> a
4 | o2.method = function (x) { return x; };
5 |
6 |
--------------------------------------------------------------------------------
/test/invalid/unfortunately/polymorphic-row-fields/prop-polymorphic-assign3.js:
--------------------------------------------------------------------------------
1 | var o2 = { method: function(x) { x = 2; return x; } };
2 |
3 | function f(x) { return x; };
4 |
5 | // Should succeed because it's more general than Number -> Number
6 | o2.method = f;
7 |
8 |
--------------------------------------------------------------------------------
/test/invalid/value_restriction_apply.js:
--------------------------------------------------------------------------------
1 | function getId(_) { return function (x) { return x; }; }
2 | var id1 = getId(false);
3 | id1(2);
4 | id1('a');
5 | id1;
6 |
--------------------------------------------------------------------------------
/test/invalid/var-recursive-nonfunc.js:
--------------------------------------------------------------------------------
1 | var x = [[0], [x]];
2 | x; // should fail, because a. it's an infinite type, and b. in javascript the value of [x] above will be [undefined]
3 |
--------------------------------------------------------------------------------
/test/invalid/vartest.js:
--------------------------------------------------------------------------------
1 | var x = false;
2 | x = 'a';
3 |
4 |
--------------------------------------------------------------------------------
/test/invalid/while.js:
--------------------------------------------------------------------------------
1 | while (2) { 1; }
2 |
--------------------------------------------------------------------------------
/test/invalid/y.js:
--------------------------------------------------------------------------------
1 | function h(f) {
2 | var g = function(x) {
3 | f(x(x));
4 | };
5 | return g(g);
6 | }
7 |
--------------------------------------------------------------------------------
/test/makebig.hs:
--------------------------------------------------------------------------------
1 | module Main where
2 |
3 | main = do
4 | putStrLn "var a0=0;"
5 | mapM_ (\x -> putStrLn $ "var a"++show x++"=1+a"++show (x-1)++";") [1..2000]
6 |
7 |
--------------------------------------------------------------------------------
/test/num.js:
--------------------------------------------------------------------------------
1 | function f(x) { var y = 0; y = x[0]; return 'a'; }
2 | var j = [];
3 | f(j);
4 | //j=[];
5 | return j;
6 |
--------------------------------------------------------------------------------
/test/purescript/Assert.js:
--------------------------------------------------------------------------------
1 | /* global exports */
2 | "use strict";
3 |
4 | function Error(x) { this.name = ''; this.message = x; }
5 | var console = { log: function(s) { } };
6 |
7 | // module Assert
8 | function module(exports) {
9 |
10 | exports.error = function(msg) {
11 | throw msg;
12 | };
13 |
14 | exports.assertPartial = function(f) {
15 | return function() {
16 | try {
17 | return f();
18 | } catch (e) {
19 | if (e instanceof Error) return;
20 | throw new Error('Pattern match failure is not Error');
21 | }
22 | };
23 | };
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/test/purescript/Control.Monad.Eff.js:
--------------------------------------------------------------------------------
1 | /* global exports */
2 | "use strict";
3 |
4 | // module Control.Monad.Eff
5 | function module(exports) {
6 |
7 | exports.returnE = function (a) {
8 | return function () {
9 | return a;
10 | };
11 | };
12 |
13 | exports.bindE = function (a) {
14 | return function (f) {
15 | return function () {
16 | return f(a())();
17 | };
18 | };
19 | };
20 |
21 | exports.runPure = function (f) {
22 | return f();
23 | };
24 |
25 | exports.untilE = function (f) {
26 | return function () {
27 | while (!f());
28 | return {};
29 | };
30 | };
31 |
32 | exports.whileE = function (f) {
33 | return function (a) {
34 | return function () {
35 | while (f()) {
36 | a();
37 | }
38 | return {};
39 | };
40 | };
41 | };
42 | }
43 |
--------------------------------------------------------------------------------
/test/purescript/Control.Monad.ST.js:
--------------------------------------------------------------------------------
1 | /* global exports */
2 | "use strict";
3 |
4 | // module Control.Monad.ST
5 | function module(exports) {
6 |
7 | exports.newSTRef = function (val) {
8 | return function () {
9 | return { value: val };
10 | };
11 | };
12 |
13 | exports.readSTRef = function (ref) {
14 | return function () {
15 | return ref.value;
16 | };
17 | };
18 |
19 | exports.modifySTRef = function (ref) {
20 | return function (f) {
21 | return function () {
22 | /* jshint boss: true */
23 | return ref.value = f(ref.value);
24 | };
25 | };
26 | };
27 |
28 | exports.writeSTRef = function (ref) {
29 | return function (a) {
30 | return function () {
31 | /* jshint boss: true */
32 | return ref.value = a;
33 | };
34 | };
35 | };
36 |
37 | exports.runST = function (f) {
38 | return f;
39 | };
40 | }
41 |
--------------------------------------------------------------------------------
/test/purescript/Data.Function.js:
--------------------------------------------------------------------------------
1 | /* global exports */
2 | "use strict";
3 |
4 | // module Data.Function
5 | function module(exports) {
6 |
7 | exports.mkFn0 = function (fn) {
8 | return function () {
9 | return fn({});
10 | };
11 | };
12 |
13 | exports.mkFn1 = function (fn) {
14 | return function (a) {
15 | return fn(a);
16 | };
17 | };
18 |
19 | exports.mkFn2 = function (fn) {
20 | /* jshint maxparams: 2 */
21 | return function (a, b) {
22 | return fn(a)(b);
23 | };
24 | };
25 |
26 | exports.mkFn3 = function (fn) {
27 | /* jshint maxparams: 3 */
28 | return function (a, b, c) {
29 | return fn(a)(b)(c);
30 | };
31 | };
32 |
33 | exports.mkFn4 = function (fn) {
34 | /* jshint maxparams: 4 */
35 | return function (a, b, c, d) {
36 | return fn(a)(b)(c)(d);
37 | };
38 | };
39 |
40 | exports.mkFn5 = function (fn) {
41 | /* jshint maxparams: 5 */
42 | return function (a, b, c, d, e) {
43 | return fn(a)(b)(c)(d)(e);
44 | };
45 | };
46 |
47 | exports.mkFn6 = function (fn) {
48 | /* jshint maxparams: 6 */
49 | return function (a, b, c, d, e, f) {
50 | return fn(a)(b)(c)(d)(e)(f);
51 | };
52 | };
53 |
54 | exports.mkFn7 = function (fn) {
55 | /* jshint maxparams: 7 */
56 | return function (a, b, c, d, e, f, g) {
57 | return fn(a)(b)(c)(d)(e)(f)(g);
58 | };
59 | };
60 |
61 | exports.mkFn8 = function (fn) {
62 | /* jshint maxparams: 8 */
63 | return function (a, b, c, d, e, f, g, h) {
64 | return fn(a)(b)(c)(d)(e)(f)(g)(h);
65 | };
66 | };
67 |
68 | exports.mkFn9 = function (fn) {
69 | /* jshint maxparams: 9 */
70 | return function (a, b, c, d, e, f, g, h, i) {
71 | return fn(a)(b)(c)(d)(e)(f)(g)(h)(i);
72 | };
73 | };
74 |
75 | exports.mkFn10 = function (fn) {
76 | /* jshint maxparams: 10 */
77 | return function (a, b, c, d, e, f, g, h, i, j) {
78 | return fn(a)(b)(c)(d)(e)(f)(g)(h)(i)(j);
79 | };
80 | };
81 |
82 | exports.runFn0 = function (fn) {
83 | return fn();
84 | };
85 |
86 | exports.runFn1 = function (fn) {
87 | return function (a) {
88 | return fn(a);
89 | };
90 | };
91 |
92 | exports.runFn2 = function (fn) {
93 | return function (a) {
94 | return function (b) {
95 | return fn(a, b);
96 | };
97 | };
98 | };
99 |
100 | exports.runFn3 = function (fn) {
101 | return function (a) {
102 | return function (b) {
103 | return function (c) {
104 | return fn(a, b, c);
105 | };
106 | };
107 | };
108 | };
109 |
110 | exports.runFn4 = function (fn) {
111 | return function (a) {
112 | return function (b) {
113 | return function (c) {
114 | return function (d) {
115 | return fn(a, b, c, d);
116 | };
117 | };
118 | };
119 | };
120 | };
121 |
122 | exports.runFn5 = function (fn) {
123 | return function (a) {
124 | return function (b) {
125 | return function (c) {
126 | return function (d) {
127 | return function (e) {
128 | return fn(a, b, c, d, e);
129 | };
130 | };
131 | };
132 | };
133 | };
134 | };
135 |
136 | exports.runFn6 = function (fn) {
137 | return function (a) {
138 | return function (b) {
139 | return function (c) {
140 | return function (d) {
141 | return function (e) {
142 | return function (f) {
143 | return fn(a, b, c, d, e, f);
144 | };
145 | };
146 | };
147 | };
148 | };
149 | };
150 | };
151 |
152 | exports.runFn7 = function (fn) {
153 | return function (a) {
154 | return function (b) {
155 | return function (c) {
156 | return function (d) {
157 | return function (e) {
158 | return function (f) {
159 | return function (g) {
160 | return fn(a, b, c, d, e, f, g);
161 | };
162 | };
163 | };
164 | };
165 | };
166 | };
167 | };
168 | };
169 |
170 | exports.runFn8 = function (fn) {
171 | return function (a) {
172 | return function (b) {
173 | return function (c) {
174 | return function (d) {
175 | return function (e) {
176 | return function (f) {
177 | return function (g) {
178 | return function (h) {
179 | return fn(a, b, c, d, e, f, g, h);
180 | };
181 | };
182 | };
183 | };
184 | };
185 | };
186 | };
187 | };
188 | };
189 |
190 | exports.runFn9 = function (fn) {
191 | return function (a) {
192 | return function (b) {
193 | return function (c) {
194 | return function (d) {
195 | return function (e) {
196 | return function (f) {
197 | return function (g) {
198 | return function (h) {
199 | return function (i) {
200 | return fn(a, b, c, d, e, f, g, h, i);
201 | };
202 | };
203 | };
204 | };
205 | };
206 | };
207 | };
208 | };
209 | };
210 | };
211 |
212 | exports.runFn10 = function (fn) {
213 | return function (a) {
214 | return function (b) {
215 | return function (c) {
216 | return function (d) {
217 | return function (e) {
218 | return function (f) {
219 | return function (g) {
220 | return function (h) {
221 | return function (i) {
222 | return function (j) {
223 | return fn(a, b, c, d, e, f, g, h, i, j);
224 | };
225 | };
226 | };
227 | };
228 | };
229 | };
230 | };
231 | };
232 | };
233 | };
234 | };
235 | }
236 |
--------------------------------------------------------------------------------
/test/purescript/Debug.Trace.js:
--------------------------------------------------------------------------------
1 | /* global exports */
2 | "use strict";
3 |
4 | // module Debug.Trace
5 | function module(exports) {
6 |
7 | exports.trace = function(s) {
8 | return function() {
9 | console.log(s);
10 | return {};
11 | };
12 | };
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/test/purescript/Prelude.Unsafe.js:
--------------------------------------------------------------------------------
1 | /* global exports */
2 | "use strict";
3 |
4 | // module Prelude.Unsafe
5 | function module(exports) {
6 |
7 | exports.unsafeIndex = function(xs) {
8 | return function(n) {
9 | return xs[n];
10 | };
11 | };
12 | }
13 |
--------------------------------------------------------------------------------
/test/purescript/Prelude.js:
--------------------------------------------------------------------------------
1 | /* global exports */
2 | "use strict";
3 | var JSON={stringify: function(x) { return ''; } };
4 | function prelude(exports) {
5 | // module Prelude
6 |
7 | exports.cons = function(e) {
8 | return function(l) {
9 | return [e].concat(l);
10 | };
11 | };
12 |
13 | exports.concat = function(l1) {
14 | return function(l2) {
15 | return l1.concat(l2);
16 | };
17 | };
18 |
19 | exports.length = function(a) {
20 | return a.length;
21 | };
22 |
23 | exports.concatString = function(s1) {
24 | return function(s2) {
25 | return s1 + s2;
26 | };
27 | };
28 |
29 | exports.numAdd = function (n1) {
30 | return function (n2) {
31 | return n1 + n2;
32 | };
33 | };
34 |
35 | exports.numMul = function (n1) {
36 | return function (n2) {
37 | return n1 * n2;
38 | };
39 | };
40 |
41 | exports.numDiv = function (n1) {
42 | return function (n2) {
43 | return n1 / n2;
44 | };
45 | };
46 |
47 | exports.numSub = function (n1) {
48 | return function (n2) {
49 | return n1 - n2;
50 | };
51 | };
52 |
53 | exports.jsMod = function(x) {
54 | return function (y) {
55 | return x % y;
56 | };
57 | };
58 |
59 | exports.refEq = function(r1) {
60 | return function(r2) {
61 | return r1 === r2;
62 | };
63 | };
64 |
65 | exports.refIneq = function(r1) {
66 | return function(r2) {
67 | return r1 !== r2;
68 | };
69 | };
70 |
71 | exports.eqArrayImpl = function(f) {
72 | return function(xs) {
73 | return function(ys) {
74 | if (xs.length !== ys.length) return false;
75 | for (var i = 0; i < xs.length; i++) {
76 | if (!f(xs[i])(ys[i])) return false;
77 | }
78 | return true;
79 | };
80 | };
81 | };
82 |
83 | exports.ordArrayImpl = function(f) {
84 | return function (xs) {
85 | return function (ys) {
86 | var i = 0;
87 | while (i < xs.length && i < ys.length) {
88 | var x = xs[i];
89 | var y = ys[i];
90 | var o = f(x)(y);
91 | if (o !== 0) {
92 | return o;
93 | }
94 | }
95 | if (xs.length == ys.length) {
96 | return 0;
97 | } else if (xs.length > ys.length) {
98 | return 1;
99 | } else {
100 | return -1;
101 | }
102 | };
103 | };
104 | };
105 |
106 | exports.unsafeCompareImpl = function(lt) {
107 | return function(eq) {
108 | return function(gt) {
109 | return function(x) {
110 | return function(y) {
111 | return x < y ? lt : x > y ? gt : eq;
112 | };
113 | };
114 | };
115 | };
116 | };
117 |
118 | exports.boolOr = function (b1) {
119 | return function (b2) {
120 | return b1 || b2;
121 | };
122 | };
123 |
124 | exports.boolAnd = function (b1) {
125 | return function (b2) {
126 | return b1 && b2;
127 | };
128 | };
129 |
130 | exports.boolNot = function (b) {
131 | return !b;
132 | };
133 |
134 | exports.showNumberImpl = function (n) {
135 | /* jshint bitwise: false */
136 | return String(n) + (n === (n | 0) ? ".0" : "");
137 | };
138 |
139 | exports.showStringImpl = function (s) {
140 | return JSON.stringify(s);
141 | };
142 |
143 | exports.showArrayImpl = function (f) {
144 | return function (xs) {
145 | var ss = [];
146 | for (var i = 0, l = xs.length; i < l; i++) {
147 | ss[i] = f(xs[i]);
148 | }
149 | return "[" + ss.join(",") + "]";
150 | };
151 | };
152 | return exports;
153 | }
154 |
--------------------------------------------------------------------------------
/test/regexlike-exec-2.js:
--------------------------------------------------------------------------------
1 | function getGroup(regex, str, idx) {
2 | return regex.exec(str)[idx];
3 | }
4 |
5 | var myRegex = { exec: function(s) { return [123]; } };
6 | var result = getGroup(myRegex, 'hi', 2);
7 |
8 |
--------------------------------------------------------------------------------
/test/runall.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e
3 | set -u
4 | find valid -type f -iname "*.js" -print0 | xargs -0 -n1 ./runtest.sh y | sort
5 | find invalid -type f -iname "*.js" -print0 | xargs -0 -n1 ./runtest.sh n | sort
6 | >&2 echo ""
7 |
8 |
--------------------------------------------------------------------------------
/test/runtest.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e
3 | set -u
4 | SCRIPT_DIR=$(dirname $(readlink -f $0))
5 | cd $SCRIPT_DIR
6 | ARGS="$@"
7 | #echo $ARGS
8 | >&2 printf "."
9 | ((timeout 3 stack exec infernu-demo $ARGS) || echo "ERROR") 2>&1 | tail -1 | cut -b-70 | xargs -0 -d'\n' -iresult printf "%-5s %s\n" result "$ARGS"
10 |
--------------------------------------------------------------------------------
/test/test.sh:
--------------------------------------------------------------------------------
1 | ./runall.sh |grep -v OK | tee fail.txt
2 |
--------------------------------------------------------------------------------
/test/valid/.flowconfig:
--------------------------------------------------------------------------------
1 | [ignore]
2 |
3 | [include]
4 |
--------------------------------------------------------------------------------
/test/valid/arguably/if-undefined.js:
--------------------------------------------------------------------------------
1 | function f(x) {
2 | if (x != undefined) {
3 | // do somethin'
4 | // should be 'dead code' and could be eliminated
5 | }
6 | }
7 |
8 | f(3); // bug!
9 |
--------------------------------------------------------------------------------
/test/valid/array-2d-get.js:
--------------------------------------------------------------------------------
1 | function f(x) { return x[0][0]; };
2 | f([[0]]);
3 |
--------------------------------------------------------------------------------
/test/valid/array-idx.js:
--------------------------------------------------------------------------------
1 | var x = [0,1,2];
2 | var y = [3,4];
3 |
4 | var z = [x[0], y[0]];
5 |
--------------------------------------------------------------------------------
/test/valid/array-length.js:
--------------------------------------------------------------------------------
1 | var a = [1,2,3];
2 | a.length;
3 |
--------------------------------------------------------------------------------
/test/valid/array-with-magic-props.js:
--------------------------------------------------------------------------------
1 | function f1(x) { return x.o; }
2 | function f2(x) { return x[0]; }
3 |
4 | var g = [];
5 | f1(g[0]);
6 | f2(g[0]);
7 |
--------------------------------------------------------------------------------
/test/valid/array.js:
--------------------------------------------------------------------------------
1 | var x = [1,2,3];
2 | var y = ['a','b','c'];
3 | var empty = [];
4 | var i = 0;
5 | empty = [1];
6 | empty[0];
7 | x[1];
8 | y[i];
9 | null;
10 |
--------------------------------------------------------------------------------
/test/valid/blo.js:
--------------------------------------------------------------------------------
1 | function f(x) {
2 | var len = x.length;
3 | return { obj: x, len: len };
4 | }
5 | var y = f({ moshe: 3, length: 4 });
6 | var z = f({ length: 'a' });
7 | f;
8 |
9 |
--------------------------------------------------------------------------------
/test/valid/break.js:
--------------------------------------------------------------------------------
1 | while (false) { 1; break; }
2 |
--------------------------------------------------------------------------------
/test/valid/builtins/Boolean.js:
--------------------------------------------------------------------------------
1 | var x = Boolean(123);
2 | x = false;
3 | x = Boolean({});
4 | x = Boolean('a');
5 | x = Boolean(NaN);
6 | x = Boolean([]);
7 | x = Boolean(null);
8 | x = Boolean(false);
9 |
--------------------------------------------------------------------------------
/test/valid/builtins/Date.js:
--------------------------------------------------------------------------------
1 | var x = new Date();
2 | var y = Date.now();
3 | var z = Date.parse('2015-01-01');
4 | var u = Date.UTC(2015,3,1,11,10,9,932);
5 |
6 |
7 | var n = 1;
8 |
9 | n = x.getDate();
10 | n = x.getDay();
11 | n = x.getFullYear();
12 | n = x.getHours();
13 | n = x.getMilliseconds();
14 | n = x.getMinutes();
15 | n = x.getMonth();
16 | n = x.getSeconds();
17 | n = x.getTime();
18 | n = x.getTimezoneOffset();
19 | n = x.getUTCDate();
20 | n = x.getUTCDay();
21 | n = x.getUTCFullYear();
22 | n = x.getUTCHours();
23 | n = x.getUTCMilliseconds();
24 | n = x.getUTCMinutes();
25 | n = x.getUTCMonth();
26 | n = x.getUTCSeconds();
27 | n = x.valueOf();
28 |
29 | x.setDate(n);
30 | x.setFullYear(n);
31 | x.setHours(n);
32 | x.setMilliseconds(n);
33 | x.setMinutes(n);
34 | x.setMonth(n);
35 | x.setSeconds(n);
36 | x.setTime(n);
37 | x.setUTCDate(n);
38 | x.setUTCFullYear(n);
39 | x.setUTCHours(n);
40 | x.setUTCMilliseconds(n);
41 | x.setUTCMinutes(n);
42 | x.setUTCMonth(n);
43 | x.setUTCSeconds(n);
44 | //x.setYear(n);
45 |
46 | var s = '';
47 | s = x.toDateString();
48 | s = x.toISOString();
49 | s = x.toJSON();
50 | s = x.toString();
51 | s = x.toTimeString();
52 | s = x.toUTCString();
53 |
54 |
--------------------------------------------------------------------------------
/test/valid/builtins/JSON.js:
--------------------------------------------------------------------------------
1 | var s = '';
2 | s = JSON.stringify(3);
3 | var n = 0;
4 | n = JSON.parse(s);
5 |
--------------------------------------------------------------------------------
/test/valid/builtins/Math.js:
--------------------------------------------------------------------------------
1 | var n = 0;
2 | n = Math.E;
3 | n = Math.LN10;
4 | n = Math.LN2;
5 | n = Math.LOG10E;
6 | n = Math.LOG2E;
7 | n = Math.PI;
8 | n = Math.SQRT1_2;
9 | n = Math.SQRT2;
10 |
11 | n = Math.abs(1);
12 | n = Math.acos(1);
13 | n = Math.asin(1);
14 | n = Math.atan(1);
15 | n = Math.atan2(1,2);
16 | n = Math.ceil(1);
17 | n = Math.cos(1);
18 | n = Math.exp(1);
19 | n = Math.floor(1);
20 | n = Math.log(1);
21 | n = Math.pow(2,3);
22 | n = Math.random();
23 | n = Math.round(1);
24 | n = Math.sign(1);
25 | n = Math.sin(1);
26 | n = Math.sqrt(1);
27 | n = Math.tan(1);
28 |
29 |
30 |
--------------------------------------------------------------------------------
/test/valid/builtins/Number.js:
--------------------------------------------------------------------------------
1 | var x = Number(123);
2 | x = 2;
3 | x = Number({});
4 | x = Number('a');
5 | x = Number(NaN);
6 | x = Number([]);
7 | x = Number(null);
8 | x = Number(false);
9 |
--------------------------------------------------------------------------------
/test/valid/builtins/String.js:
--------------------------------------------------------------------------------
1 | var x = String(123);
2 | x = 'b';
3 | x = String({});
4 | x = String('a');
5 | x = String(NaN);
6 | x = String([]);
7 | x = String(null);
8 | x = String(false);
9 |
--------------------------------------------------------------------------------
/test/valid/builtins/assign-ops-index.js:
--------------------------------------------------------------------------------
1 | var arr = [432];
2 | arr[0] = 1;
3 | arr[0] += 2;
4 | arr[0] -= 3;
5 | arr[0] *= 4;
6 | arr[0] /= 5;
7 | arr[0] %= 8;
8 | arr[0] <<= 6;
9 | arr[0] >>= 7;
10 | arr[0] >>>= 8;
11 | arr[0] &= 9;
12 | arr[0] ^= 10;
13 | arr[0] |= 11;
14 |
--------------------------------------------------------------------------------
/test/valid/builtins/assign-ops-property.js:
--------------------------------------------------------------------------------
1 | var obj = { x: 0 };
2 | obj.x = 1;
3 | obj.x += 2;
4 | obj.x -= 3;
5 | obj.x *= 4;
6 | obj.x /= 5;
7 | obj.x %= 8;
8 | obj.x <<= 6;
9 | obj.x >>= 7;
10 | obj.x >>>= 8;
11 | obj.x &= 9;
12 | obj.x ^= 10;
13 | obj.x |= 11;
14 |
--------------------------------------------------------------------------------
/test/valid/builtins/assign-ops.js:
--------------------------------------------------------------------------------
1 | var x = 0;
2 | x = 1;
3 | x += 2;
4 | x -= 3;
5 | x *= 4;
6 | x /= 5;
7 | x %= 8;
8 | x <<= 6;
9 | x >>= 7;
10 | x >>>= 8;
11 | x &= 9;
12 | x ^= 10;
13 | x |= 11;
14 |
--------------------------------------------------------------------------------
/test/valid/builtins/binary-not.js:
--------------------------------------------------------------------------------
1 | var x = ~2;
2 |
--------------------------------------------------------------------------------
/test/valid/builtins/isFinite.js:
--------------------------------------------------------------------------------
1 | var x = isFinite(3);
2 |
3 | var y = isFinite(-3.8) == false;
4 |
--------------------------------------------------------------------------------
/test/valid/builtins/isNaN.js:
--------------------------------------------------------------------------------
1 | var x = isNaN(3);
2 |
3 | var y = isNaN(NaN) == false;
4 |
--------------------------------------------------------------------------------
/test/valid/builtins/logical-not.js:
--------------------------------------------------------------------------------
1 | var x = !false;
2 |
--------------------------------------------------------------------------------
/test/valid/builtins/map.js:
--------------------------------------------------------------------------------
1 | var a = [1,2,3];
2 | var n = a.map(function (x) { return x; });
3 | var b = a.map(function (x) { return 'bla'; });
4 | a;
5 |
6 | n;
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/test/valid/builtins/map2.js:
--------------------------------------------------------------------------------
1 | var a = [1,2,3];
2 | var b = ['a','b','c'];
3 |
4 | var a1 = a.map(function(x) { return String(x); });
5 | var b1 = b.map(function(x) { return parseInt(x, 10); });
6 |
--------------------------------------------------------------------------------
/test/valid/builtins/minus.js:
--------------------------------------------------------------------------------
1 | var x = 2 - 3;
2 |
--------------------------------------------------------------------------------
/test/valid/builtins/minusminus.js:
--------------------------------------------------------------------------------
1 | var x = 0;
2 | var y = x--;
3 |
--------------------------------------------------------------------------------
/test/valid/builtins/or.js:
--------------------------------------------------------------------------------
1 | var x = '' || 'bla';
2 | var y = 0 || 2;
3 |
--------------------------------------------------------------------------------
/test/valid/builtins/parse.js:
--------------------------------------------------------------------------------
1 | var x = parseFloat('3.0') == 3.0;
2 |
3 | var y = parseInt('3', 10) == 3;
4 |
--------------------------------------------------------------------------------
/test/valid/builtins/plus-func.js:
--------------------------------------------------------------------------------
1 | // let _/_ = undefined
2 | // in let f = (\(this, x, y) -> let return = []
3 | // in return[0.0] := (+ (null, x, y)); // + :: forall b. Plus b => b -> b -> b instantiated here -> Plus c => c -> c -> c
4 | // return[0.0])
5 | // in let _/_ = f
6 | // in _/_
7 | function f(x,y) { return x + y; }
8 |
9 |
10 | f;
11 |
12 |
13 | f(1,2);
14 |
15 |
16 | f('b','c');
17 |
--------------------------------------------------------------------------------
/test/valid/builtins/plus.js:
--------------------------------------------------------------------------------
1 | var x = 2 + 3;
2 | var s = 'a' + 'b';
3 |
4 |
--------------------------------------------------------------------------------
/test/valid/builtins/plusplus.js:
--------------------------------------------------------------------------------
1 | var x = 0;
2 | var y = x++;
3 |
--------------------------------------------------------------------------------
/test/valid/builtins/regex-exec-2.js:
--------------------------------------------------------------------------------
1 | function getGroup(regex, str, idx) {
2 | return regex.exec(str)[idx];
3 | }
4 |
5 | var result = getGroup(/bla/, 'hi', 2)
6 |
--------------------------------------------------------------------------------
/test/valid/builtins/regex-exec.js:
--------------------------------------------------------------------------------
1 | var r = /123/g;
2 |
3 | var m = r.exec('12');
4 |
5 |
6 | var x = 0;
7 | x = m.index;
8 |
9 | var s = '';
10 | s = m[0];
11 | s = m.input;
12 |
13 |
--------------------------------------------------------------------------------
/test/valid/builtins/regex-test.js:
--------------------------------------------------------------------------------
1 | var x = /hi/;
2 |
3 | function matchesHi() {
4 | if (x.test('hi')) {
5 | return true;
6 | }
7 | return false;
8 | }
9 |
10 | var m = matchesHi();
11 |
12 |
--------------------------------------------------------------------------------
/test/valid/builtins/regex.js:
--------------------------------------------------------------------------------
1 | var r = /<%-([\s\S]+?)%>/g;
2 | var s = r.source;
3 |
--------------------------------------------------------------------------------
/test/valid/builtins/string-ops.js:
--------------------------------------------------------------------------------
1 | var x = 'bla';
2 | var y = x.length + 2;
3 | 'b' + x.charAt(0);
4 | x.concat('hello') + 'abc';
5 | x.indexOf('hello') + 2;
6 |
--------------------------------------------------------------------------------
/test/valid/builtins/string-replace.js:
--------------------------------------------------------------------------------
1 | function f(p, r) { return 'hi'.replace(p, r); }
2 | var s = 'abc'.replace('junk', 'punk');
3 | var t = 'abd'.replace(/bd/, 'pink');
4 | f('2','a');
5 |
6 |
--------------------------------------------------------------------------------
/test/valid/builtins/typeof.js:
--------------------------------------------------------------------------------
1 | var x = typeof 3;
2 |
--------------------------------------------------------------------------------
/test/valid/builtins/uri.js:
--------------------------------------------------------------------------------
1 | decodeURI('bla') == 'bla';
2 | decodeURIComponent('bla') == 'bla';
3 | encodeURI('bla') == 'bla';
4 | encodeURIComponent('bla') == 'bla';
5 |
--------------------------------------------------------------------------------
/test/valid/call-this.js:
--------------------------------------------------------------------------------
1 | function f() { return this.x; }
2 | var res = f.call({x:3});
3 | res = 2;
4 | var res1 = f.call({x:'a'});
5 | res1 = 'b';
6 |
--------------------------------------------------------------------------------
/test/valid/call.js:
--------------------------------------------------------------------------------
1 | function f(x) { x = 0; return x; }
2 | f(2);
3 |
--------------------------------------------------------------------------------
/test/valid/complex/kinda-underscore.js:
--------------------------------------------------------------------------------
1 | var each = function(list, iteratee) {
2 | var i = 0;
3 | for (i = 0; i < list.length; i++) {
4 | iteratee(list[i], i, list);
5 | }
6 | return list;
7 | };
8 |
9 | var p = [];
10 | each([1,2,3], function(e,i,l) { p.push(e + 55); });
11 | // would give an obscure error:
12 | //each([1,2,3], function(e,i,l) { p.push(e + 'ah'); });
13 |
14 | //p;
15 |
--------------------------------------------------------------------------------
/test/valid/complex/sortedUniq.js:
--------------------------------------------------------------------------------
1 | // from lodash
2 | function sortedUniq(array, iteratee) {
3 | var seen,
4 | index = -1,
5 | length = array.length,
6 | resIndex = -1,
7 | result = [];
8 |
9 | while (++index < length) {
10 | var value = array[index],
11 | computed = iteratee(value, index, array);
12 |
13 | if (index === 0 || seen !== computed) {
14 | seen = computed;
15 | result[++resIndex] = value;
16 | }
17 | }
18 | return result;
19 | }
20 |
--------------------------------------------------------------------------------
/test/valid/cond.js:
--------------------------------------------------------------------------------
1 | var x = false ? 1 : 0;
2 | x;
3 |
--------------------------------------------------------------------------------
/test/valid/continue.js:
--------------------------------------------------------------------------------
1 | while (false) { 1; continue; }
2 |
--------------------------------------------------------------------------------
/test/valid/debounce-indexable.js:
--------------------------------------------------------------------------------
1 | var $timeout = {
2 | set: function(f, num) { num = 0; return 0; },
3 | cancel: function(t) { t=0; }
4 | };
5 |
6 | function debounce(f, millis) {
7 | var timer = 0;
8 | var timerSet = false;
9 | var scheduledArgs = [];
10 | function resetTimeout() {
11 | if (timerSet) {
12 | $timeout.cancel(timer);
13 | }
14 | timerSet = true;
15 | timer = $timeout.set(function() {
16 | // Note: f may end up running more than once per args set.
17 | f(scheduledArgs[0]);//.apply(null, scheduledArgs);
18 | }, millis);
19 | return;
20 | }
21 | return function(arg) {
22 | scheduledArgs = [arg]; //Array.prototype.slice.call(arguments, 0);
23 | return {
24 | invoke: function() {
25 | resetTimeout();
26 | }
27 | };
28 | };
29 | }
30 | var fonce = debounce(function (x) { return x[0]; }, 0);
31 | fonce('a');
32 | //return debounce;
33 |
--------------------------------------------------------------------------------
/test/valid/debounce.js:
--------------------------------------------------------------------------------
1 | var $timeout = {
2 | set: function(f, num) { num = 0; return 0; },
3 | cancel: function(t) { t=0; }
4 | };
5 |
6 | function debounce(f, initial, millis) {
7 | var timer = 0;
8 | var res = initial;
9 | var timerSet = false;
10 | var scheduledArgs = [];
11 | function resetTimeout() {
12 | if (timerSet) {
13 | $timeout.cancel(timer);
14 | }
15 | timerSet = true;
16 | timer = $timeout.set(function() {
17 | // Note: f may end up running more than once per args set.
18 | res = f(scheduledArgs[0]);//.apply(null, scheduledArgs);
19 | }, millis);
20 | return res;
21 | }
22 | return function(arg) {
23 | scheduledArgs = [arg]; //Array.prototype.slice.call(arguments, 0);
24 | return {
25 | invoke: function() {
26 | return resetTimeout();
27 | }
28 | };
29 | };
30 | }
31 | var fonce = debounce(function (x) { /*x = 0;*/ return 'a'; }, 'c', 3);
32 | var y = fonce(2).invoke();
33 | y;
34 | //return debounce;
35 |
--------------------------------------------------------------------------------
/test/valid/double-func.js:
--------------------------------------------------------------------------------
1 | function f(x) { return x()(); };
2 | f(function(){return function(){return 0; }; });
3 |
--------------------------------------------------------------------------------
/test/valid/fix.js:
--------------------------------------------------------------------------------
1 | function Y(g) { return g(Y(g)); }
2 | Y;
3 |
--------------------------------------------------------------------------------
/test/valid/for-in.js:
--------------------------------------------------------------------------------
1 | var arr = ['a','b'];
2 |
3 | var i = '';
4 | for (i in arr) {
5 |
6 | }
7 |
--------------------------------------------------------------------------------
/test/valid/for.js:
--------------------------------------------------------------------------------
1 | var z = 0;
2 | var i = 0;
3 | for (i = 2; false; i = 3) {
4 | z = i;
5 | }
6 |
--------------------------------------------------------------------------------
/test/valid/func-stmt-expr-erisco.js:
--------------------------------------------------------------------------------
1 | var f = function () { return 0; };
2 | function g() { return 0; };
3 | var z = [f, g];
4 | z;
5 |
--------------------------------------------------------------------------------
/test/valid/func.js:
--------------------------------------------------------------------------------
1 | function f() { var x = 1; return x; }
2 | f;
3 |
--------------------------------------------------------------------------------
/test/valid/get-length.js:
--------------------------------------------------------------------------------
1 | function getLength(x) {
2 | return x.length;
3 | }
4 |
5 | var n = getLength('hi');
6 |
7 | n;
8 |
--------------------------------------------------------------------------------
/test/valid/if.js:
--------------------------------------------------------------------------------
1 | if (false) { 1; } else { false; }
2 |
3 | function f(x,y) {
4 | if (x) { y = 1; } else { return 4; }
5 | return 2;
6 | }
7 |
8 |
--------------------------------------------------------------------------------
/test/valid/index-and-plus.js:
--------------------------------------------------------------------------------
1 | function f(x, i) {
2 | return x[i] + x[i];
3 | }
4 |
5 | f([1,2,3], 0);
6 |
7 | f({'mo': 3 }, 'mo');
8 |
--------------------------------------------------------------------------------
/test/valid/indexable.js:
--------------------------------------------------------------------------------
1 | function zzz() { return 0; }
2 |
3 | function test(y) {
4 | function callback() {
5 | return y;
6 | }
7 | var x= {
8 | invoke: function() {
9 | return callback();
10 | }
11 | };
12 |
13 | }
14 |
15 |
16 | test('hi');
17 |
--------------------------------------------------------------------------------
/test/valid/list.js:
--------------------------------------------------------------------------------
1 | var x = true;
2 | x = (1,'a',false);
3 |
4 |
--------------------------------------------------------------------------------
/test/valid/map.js:
--------------------------------------------------------------------------------
1 | function map(arr, f) {
2 | var i = 0;
3 | var res = [];
4 | for (i = 0; i < arr.length; i = i + 1) {
5 | res.push(f(arr[i]));
6 | }
7 | return res;
8 | }
9 |
10 |
11 | var arnum = map([1,2,3], function (x) { return x + 1; });
12 |
13 | //var arstr = map(['a','b','c'], function (x) { return x; });
14 |
15 | //arnum;
16 | //arstr;
17 |
--------------------------------------------------------------------------------
/test/valid/memoize.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | // 'f' is a function that takes a recursion function, and the current value. It should call the recursion function instead of itself when it needs to recurse.
3 | function memoize(toKeyString, f) {
4 | var mem = {};
5 |
6 | return function memed(x) {
7 | var key = toKeyString(x);
8 | mem[key] = mem[key] || f(memed, x);
9 | return mem[key];
10 | };
11 | }
12 |
13 | function recFac(memed, x) {
14 | if (x < 2) {
15 | return 1;
16 | }
17 | return x * memed(x - 1);
18 | };
19 |
20 | var facTest = memoize(String, recFac);
21 |
22 | // The number of n-dimensional vectors whose scalar's sums to k
23 | // Hat-tip to https://stackoverflow.com/questions/3242597/what-is-memoization-good-for-and-is-it-really-all-that-helpful
24 | var nkTest = memoize(
25 | function (x) {
26 | return String(x.k) + ', ' + String(x.n); },
27 | function f(m, x) {
28 | if (x.k === 0) { return 1; }
29 | if (x.n === 0) { return 0; }
30 | return m({ n: x.n-1, k: x.k }) + m({ n: x.n, k: x.k-1 }) + m({ n: x.n-1, k: x.k-1 });
31 | });
32 |
33 |
34 | nkTest({ n: 30, k: 30 });
35 |
36 |
--------------------------------------------------------------------------------
/test/valid/method-as-func.js:
--------------------------------------------------------------------------------
1 | var o = { method: function() { return 3; } };
2 | var mo = function(){return o;};
3 | o.method();
4 | var f = o.method;
5 | f();
6 |
--------------------------------------------------------------------------------
/test/valid/method.js:
--------------------------------------------------------------------------------
1 | function f(obj, x) { return obj.method(x); }
2 | f;
3 |
4 |
--------------------------------------------------------------------------------
/test/valid/mutability/array-of-polyfuncs-eta-expanded.js:
--------------------------------------------------------------------------------
1 | var arr = [function(x) { return x; }];
2 | // because 'arr' is not generalized, the next line causes arr to be the monomorphic type [a.(Number -> Number)] where 'a' is free (undetermined so far, but not quantified)
3 | arr.push(function(x) { return 0; });
4 | // here the call uses 'undefined' for 'this', so the 'a' tvar is unified with Undefined, leaving arr with type [Undefined.(Number -> Number)]
5 | arr[0](1);
6 |
7 | // will give us: obj : {f: Undefined.(Number -> Number)}
8 | var obj = { f: function(x) { return arr[0](x); } };
9 |
10 | // passes obj as 'this' to 'f', so requires the compiler to allow passing any type when Undefined is expected as an argument
11 | obj.f(1);
12 |
--------------------------------------------------------------------------------
/test/valid/mutability/array-of-polyfuncs.js:
--------------------------------------------------------------------------------
1 | var arr = [function(x) { return x; }];
2 | // because 'arr' is not generalized, the next line causes arr to be the monomorphic type [a.(Number -> Number)] where 'a' is free (undetermined so far, but not quantified)
3 | arr.push(function(x) { return 0; });
4 | // here the call uses 'undefined' for 'this', so the 'a' tvar is unified with Undefined, leaving arr with type [Undefined.(Number -> Number)]
5 | arr[0](1);
6 |
7 | // will give us: obj : {f: Undefined.(Number -> Number)}
8 | var obj = { f: arr[0] };
9 |
10 | // passes obj as 'this' to 'f', so requires the compiler to allow passing any type when Undefined is expected as an argument
11 | obj.f(1);
12 |
--------------------------------------------------------------------------------
/test/valid/mutability/indirect-mutable.js:
--------------------------------------------------------------------------------
1 | var x = function(q){return q;};
2 | var y = x;
3 | y = function(q){return false;};
4 | x(2);
5 | x;
6 | y;
7 |
--------------------------------------------------------------------------------
/test/valid/mutability/indirect-mutable2.js:
--------------------------------------------------------------------------------
1 | function x(q){return q;};
2 | var y = x;
3 | y = function(q){return false;};
4 | x(2);
5 | x;
6 | y;
7 |
--------------------------------------------------------------------------------
/test/valid/mutability/mutable-assign-app.js:
--------------------------------------------------------------------------------
1 | function f(x) { return x; }
2 |
3 | var x = 0;
4 |
5 | x = f(3);
6 |
7 | f('a');
8 |
--------------------------------------------------------------------------------
/test/valid/mutability/mutable-indexassign-app.js:
--------------------------------------------------------------------------------
1 | function f(x) { return x; }
2 |
3 | var x = [];
4 |
5 | x[0] = f(3);
6 |
7 | f('a');
8 |
--------------------------------------------------------------------------------
/test/valid/mutability/mutable-propassign-app.js:
--------------------------------------------------------------------------------
1 | function f(x) { return x; }
2 |
3 | var x = { v: 0 };
4 |
5 | x.v = f(3);
6 |
7 | f('a');
8 |
--------------------------------------------------------------------------------
/test/valid/mutability/ok7.js:
--------------------------------------------------------------------------------
1 | function makeX() {
2 | var x = [];
3 | var setX = function (v2) { x = [v2]; return true; };
4 | return { setX: setX, getX: function() { return x; } };
5 | }
6 | var mx1 = makeX();
7 | mx1.setX(false);
8 | var x1 = mx1.getX();
9 | var mx2 = makeX();
10 | mx2.setX(3);
11 | var x2 = mx2.getX();
12 |
--------------------------------------------------------------------------------
/test/valid/mutability/ok7b.js:
--------------------------------------------------------------------------------
1 | function makeX(_) {
2 | var x = [];
3 | var setX = function (v2) { x = [v2]; return true; };
4 | return setX;
5 | }
6 | var mx1 = makeX(false);
7 | mx1(false);
8 | var mx2 = makeX(false);
9 | mx2(3);
10 |
--------------------------------------------------------------------------------
/test/valid/mutability/ok7c.js:
--------------------------------------------------------------------------------
1 | function makeX(_) {
2 | var x = [];
3 | var setX = function (v2, v3) { x = [v2]; return v3; };
4 | return setX;
5 | }
6 | var mx1 = makeX(false);
7 | mx1(false, 1);
8 | var mx2 = makeX(false);
9 | mx2(1, 'bla');
10 | mx1;
11 | mx2;
12 |
--------------------------------------------------------------------------------
/test/valid/mutability/var.js:
--------------------------------------------------------------------------------
1 | var x = 0;
2 | x = 1;
3 |
--------------------------------------------------------------------------------
/test/valid/new-func.js:
--------------------------------------------------------------------------------
1 | function mkn(f, a) { return new f(a); }
2 |
3 | mkn;
4 |
5 | function Foo(x) { this.bla = x; }
6 |
7 | mkn(Foo, 2);
8 |
9 |
--------------------------------------------------------------------------------
/test/valid/new-recursive.js:
--------------------------------------------------------------------------------
1 | function Foo() {
2 | this.bar = function() { return new Foo(); };
3 | }
4 |
5 | var f = new Foo();
6 |
7 |
--------------------------------------------------------------------------------
/test/valid/new-return.js:
--------------------------------------------------------------------------------
1 | function Constr(x) { this.bla = x; return 2; }
2 | var constr = new Constr(2);
3 | constr.bla = 3;
4 |
5 |
--------------------------------------------------------------------------------
/test/valid/new-simple.js:
--------------------------------------------------------------------------------
1 | function Foo() {}
2 | var z = new Foo();
3 |
--------------------------------------------------------------------------------
/test/valid/new.js:
--------------------------------------------------------------------------------
1 | function Constr(x) { this.bla = x; }
2 | var constr = new Constr(2);
3 | var x = constr.bla;
4 | x = 2;
5 |
6 |
7 |
--------------------------------------------------------------------------------
/test/valid/nothis-as-obj-param.js:
--------------------------------------------------------------------------------
1 |
2 | function id(x) {
3 | return x;
4 | }
5 | function test(obj) {
6 | obj.f(2);
7 |
8 | var g = obj.f;
9 |
10 | g(3);
11 | }
12 |
--------------------------------------------------------------------------------
/test/valid/nothis-as-param.js:
--------------------------------------------------------------------------------
1 | function id(x) { return x; }
2 |
3 | function test(f) {
4 | var obj = { f: f };
5 | obj.f(2);
6 | var g = obj.f;
7 | // error, because 'f' was already inferred to take a 'this' parameter with the same type as 'obj'
8 | g(3);
9 | }
10 |
11 | test(id);
12 |
--------------------------------------------------------------------------------
/test/valid/nothis.js:
--------------------------------------------------------------------------------
1 | function f(x) {
2 | return x;
3 | }
4 |
5 | var obj = { f: f };
6 |
7 | obj.f(2);
8 |
9 | obj;
10 |
11 | var g = obj.f;
12 |
13 | g(3);
14 |
--------------------------------------------------------------------------------
/test/valid/nothis2.js:
--------------------------------------------------------------------------------
1 | function f(x) {
2 | return x;
3 | }
4 |
5 | var obj = { f: f };
6 |
7 | var g = obj.f;
8 |
9 | g(3);
10 |
11 | obj.f(2);
12 |
--------------------------------------------------------------------------------
/test/valid/object-chaining-long.js:
--------------------------------------------------------------------------------
1 | ({ method: function() { return this; } }).method().method().method().method().method().method().method().method().method().method().method().method().method().method().method().method().method().method().method().method().method().method().method().method().method().method().method().method().method().method().method().method().method().method().method().method().method().method().method().method().method().method().method().method().method().method().method().method().method().method().method().method().method().method();
2 |
--------------------------------------------------------------------------------
/test/valid/object-chaining-triple.js:
--------------------------------------------------------------------------------
1 | ({ method: function() { return this; } }).method().method().method();
2 |
--------------------------------------------------------------------------------
/test/valid/object-chaining.js:
--------------------------------------------------------------------------------
1 | function Obj(y) {
2 | this.x = y;
3 | this.doIt = function(x) { this.x = x; return this; };
4 | }
5 | var o = new Obj(2);
6 | o.doIt(2).doIt(3);
7 | var o2 = new Obj('b');
8 | o2.doIt('fa').doIt('mo');
9 |
--------------------------------------------------------------------------------
/test/valid/objlit-func.js:
--------------------------------------------------------------------------------
1 | var f = function(obj, x) {
2 | return obj.doStuff(x);
3 | };
4 |
5 | var testObj = {bla: 3, doStuff: function(r) { return false;}};
6 |
7 | f(testObj, 'b');
8 |
9 | f = function(obj, x) {
10 | return obj.doStuff('a'); // ignores x, uses 'a' instead.
11 | };
12 |
13 |
--------------------------------------------------------------------------------
/test/valid/objlit.js:
--------------------------------------------------------------------------------
1 | var x = { a: 2, b: 'a' };
2 | x;
3 |
--------------------------------------------------------------------------------
/test/valid/objlit2.js:
--------------------------------------------------------------------------------
1 | function f(x) {
2 | return x.c;
3 | }
4 | f({a:2, c:3});
5 | f({b:true, c:3});
6 | f;
7 | // var x = { a: 2, b: 'a' };
8 | // x = { a:3, b:'b',c:0 };
9 | // x.c;
10 |
--------------------------------------------------------------------------------
/test/valid/once.js:
--------------------------------------------------------------------------------
1 | function once(f) {
2 | var ran = true;
3 | var result = [];
4 | return function(x) {
5 | if (ran) {
6 | ran = false;
7 | result = [f(x)];
8 | }
9 | return result[0];
10 | };
11 | }
12 |
13 | var h = once(function(x) { return 'shimon'; });
14 | h;
15 |
--------------------------------------------------------------------------------
/test/valid/passing-obj-with-poly-method.js:
--------------------------------------------------------------------------------
1 | function bar(objArg) { return objArg.foo(5) == 2; }
2 | var obj = { foo : function(x) { return x; } };
3 | bar(obj);
4 |
--------------------------------------------------------------------------------
/test/valid/point.js:
--------------------------------------------------------------------------------
1 | // # let rotate angle p = (p#new_x (cos angle *. p#get_x -. sin angle *. p#get_y))#new_y (sin angle *. p#get_x +. cos angle *. p#get_y);;
2 | function cos(x) { return x; }
3 | function sin(x) { return x; }
4 | function rotate(angle, p) {
5 | p.new_x(cos(angle) * p.get_x() - sin(angle) * p.get_y());
6 | p.new_y(sin(angle) * p.get_x() + cos(angle) * p.get_y());
7 | }
8 |
9 | function Point(x0, y0) {
10 | var x = x0, y = y0;
11 | this.new_x = function(x1) { x = x1; };
12 | this.new_y = function(y1) { y = y1; };
13 | this.get_x = function() { return x; };
14 | this.get_y = function() { return y; };
15 | }
16 |
17 | var p = new Point(1,2);
18 |
19 | rotate(20, p);
20 |
21 |
22 |
--------------------------------------------------------------------------------
/test/valid/poll.js:
--------------------------------------------------------------------------------
1 | function Date() {
2 |
3 | }
4 | var window = {
5 | setTimeout: function(f, t) {
6 | while (t > 0) {
7 | t -= 1;
8 | }
9 | f();
10 | }
11 | };
12 | function Error(s) { this.message = s; }
13 |
14 | // Copied from http://davidwalsh.name/essential-javascript-functions
15 | function poll(fn, callback, errback, timeout, interval) {
16 | var endTime = Number(new Date()) + (timeout || 2000);
17 | interval = interval || 100;
18 |
19 | (function p() {
20 | // If the condition is met, we're done!
21 | if(fn()) {
22 | callback();
23 | }
24 | // If the condition isn't met but the timeout hasn't elapsed, go again
25 | else if (Number(new Date()) < endTime) {
26 | window.setTimeout(p, interval);
27 | }
28 | // Didn't match and too much time, reject!
29 | else {
30 | errback(new Error('timed out'));
31 | }
32 | })();
33 | }
34 |
--------------------------------------------------------------------------------
/test/valid/poly-array-assign.js:
--------------------------------------------------------------------------------
1 |
2 | function setFirst(arr, val) { arr[0] = val; }
3 |
4 | setFirst([1,2,3], 2);
5 |
6 | setFirst(['a','b'], 'd');
7 |
--------------------------------------------------------------------------------
/test/valid/poly-array-assign2.js:
--------------------------------------------------------------------------------
1 |
2 | function setFirst(arr, val) { arr[0] = val; }
3 |
4 | function DoStuff() {
5 | var elems = [];
6 | this.set = function(y) { setFirst(elems, y); };
7 | }
8 |
9 | var x = new DoStuff();
10 | x.set(2);
11 | var y = new DoStuff();
12 | y.set('a');
13 |
--------------------------------------------------------------------------------
/test/valid/poly-method-rank-3-nothis.js:
--------------------------------------------------------------------------------
1 | // supposedly equivalent to this problem:
2 | // -- forall a b. ((() -> (a -> b)), a) -> b
3 | // -- forall . ((() -> (forall d. (d -> d)), Bool) -> Bool
4 |
5 | function forceUndef(x) { x = undefined; }
6 |
7 | var obj = { getIdHolder: function() { forceUndef(this); return { id: function(x) { forceUndef(this); return x; } }; } };
8 |
9 | function doId(o,x) {
10 | forceUndef(this);
11 | var f = o.getIdHolder;
12 | var o2 = f();
13 | var g = o2.id;
14 | return g(x);
15 | }
16 |
17 | doId(obj, 2);
18 |
--------------------------------------------------------------------------------
/test/valid/poly-method-rank-3.js:
--------------------------------------------------------------------------------
1 |
2 | var obj = { getIdHolder: function() { return { id: function(x) { return x; } }; } };
3 |
4 | function doId(o,x) { return o.getIdHolder().id(x); }
5 |
6 | doId(obj, 2);
7 |
--------------------------------------------------------------------------------
/test/valid/poly-method-recursive-this.js:
--------------------------------------------------------------------------------
1 | function foo(x, y) { return { o: x, v: x.bla(y) }; }
2 |
3 | var obj = { bla: function(z) { return z == this.val; },
4 | val: false
5 | };
6 |
7 |
8 | var detachedBla = obj.bla;
9 | var otherObj = { val: 3, bloop: detachedBla };
10 | otherObj.bloop(2);
11 |
12 | // the returned obj2 is the same object as obj, but has a different type!
13 | var obj2 = foo(obj, true).o;
14 |
15 | var detachedBla2 = obj2.bla;
16 | var otherObj2 = { val: 3, bloop: detachedBla2 };
17 | // This fails becase otherObj2 is monomorphic. Disable the next line and compare the resulting type of otherObj2 with that of otherObj.
18 | otherObj2.bloop(2);
19 |
--------------------------------------------------------------------------------
/test/valid/poly-method-simple.js:
--------------------------------------------------------------------------------
1 |
2 | var obj = { id: function(x) { return x; } };
3 |
4 | function doId(o,x) { return o.id(x); }
5 |
6 | doId(obj, 2);
7 |
--------------------------------------------------------------------------------
/test/valid/poly-row-field-using-let.js:
--------------------------------------------------------------------------------
1 | var obj = { magnitude:
2 | function(p) {
3 | var a = p.x;
4 | var b = p.y;//(p.x*p.x) + (p.y*p.y);
5 | return [a,b];
6 | }
7 | };
8 |
9 | var m1 = obj.magnitude;
10 | m1({ x: 1, y: 2 });
11 | m1({ x: false, y: true });
12 |
13 |
--------------------------------------------------------------------------------
/test/valid/poly-row-field.js:
--------------------------------------------------------------------------------
1 | var obj = { magnitude:
2 | function(p) {
3 | var a = p.x;
4 | var b = p.y;//(p.x*p.x) + (p.y*p.y);
5 | return [a,b];
6 | }
7 | };
8 |
9 | obj.magnitude({ x: 1, y: 2 });
10 | obj.magnitude({ x: false, y: true });
11 |
12 |
--------------------------------------------------------------------------------
/test/valid/poly-row-type-func.js:
--------------------------------------------------------------------------------
1 | function f(o) { return o.length; }
2 |
3 | f({ length: 2 });
4 | f({ length: false });
5 |
--------------------------------------------------------------------------------
/test/valid/poly-this-new.js:
--------------------------------------------------------------------------------
1 |
2 | function MkObj() {
3 | this.m = function(x) { return x; };
4 | }
5 | var obj1 = new MkObj();
6 | var obj2 = new MkObj();
7 |
8 | obj1.m(2);
9 |
10 | var g = obj2.m;
11 | g(3);
12 |
--------------------------------------------------------------------------------
/test/valid/poly-this.js:
--------------------------------------------------------------------------------
1 |
2 | function makeObj() {
3 | return { m: function(x) { return x; } }; // 'this' isn't used.
4 | }
5 |
6 |
7 | var f = makeObj().m;
8 | makeObj().m(2);
9 |
10 | f(3);
11 |
--------------------------------------------------------------------------------
/test/valid/poly-this2.js:
--------------------------------------------------------------------------------
1 |
2 | function f(x) { return x; }
3 | var obj = { m: f }; // 'this' isn't used.
4 |
5 | var g = obj.m;
6 | obj.m(2);
7 |
8 | g(3);
9 |
--------------------------------------------------------------------------------
/test/valid/polyfunc.js:
--------------------------------------------------------------------------------
1 | var f = function(x) { return x; };
2 | var num = f(3);
3 | var str = f('a');
4 | f;
5 |
--------------------------------------------------------------------------------
/test/valid/polyfunc2.js:
--------------------------------------------------------------------------------
1 | var f = function(x, y) { return function(z) { x = z; return y; }; };
2 | f([2], 'a')([3]);
3 | f;
4 |
5 |
--------------------------------------------------------------------------------
/test/valid/promise-checking-subsumption.js:
--------------------------------------------------------------------------------
1 | function Promise () {
2 | var that = this;
3 | this.then = function (rej) { };
4 | this.reject = function (ex) {
5 | that.then = function(rej) { rej(ex); };
6 | };
7 | };
8 |
9 |
--------------------------------------------------------------------------------
/test/valid/promise-method-workaround.js:
--------------------------------------------------------------------------------
1 | // Based on https://gist.github.com/unscriptable/814052 - (c) copyright unscriptable.com / John Hann, License MIT
2 |
3 | function Promise () {
4 | var _thens = [];
5 | var that = this;
6 | /* This is the "front end" API. */
7 |
8 | // then(onResolve, onReject): Code waiting for this promise uses the
9 | // then() method to be notified when the promise is complete. There
10 | // are two completion callbacks: onReject and onResolve. A more
11 | // robust promise implementation will also have an onProgress handler.
12 | this.then = function (onResolve, onReject) {
13 | // capture calls to then()
14 | _thens.push({ resolve: onResolve, reject: onReject });
15 | };
16 |
17 | /* This is the "back end" API. */
18 |
19 | // resolve(resolvedValue): The resolve() method is called when a promise
20 | // is resolved (duh). The resolved value (if any) is passed by the resolver
21 | // to this method. All waiting onResolve callbacks are called
22 | // and any future ones are, too, each being passed the resolved value.
23 | this.resolve = function (val) {
24 | var errorFunc = function(val) { throw new Error('Already completed.'); };
25 | var i = 0;
26 | that.then = function(resolve, reject) { resolve(val); };
27 | that.resolve = errorFunc;
28 | that.reject = errorFunc;
29 | for (i = 0; i < _thens.length; i++) {
30 | var r = _thens[i].resolve;
31 | r(val);
32 | }
33 | _thens = [];
34 | };
35 |
36 | // reject(exception): The reject() method is called when a promise cannot
37 | // be resolved. Typically, you'd pass an exception as the single parameter,
38 | // but any other argument, including none at all, is acceptable.
39 | // All waiting and all future onReject callbacks are called when reject()
40 | // is called and are passed the exception parameter.
41 | this.reject = function (ex) {
42 | var errorFunc = function(val) { throw new Error('Already completed.'); };
43 | var i = 0;
44 | that.then = function(resolve, reject) { reject(ex); };
45 | that.resolve = errorFunc;
46 | that.reject = errorFunc;
47 | for (i = 0; i < _thens.length; i++) {
48 | var r = _thens[i].reject;
49 | r(ex);
50 | }
51 | _thens = [];
52 | };
53 |
54 | };
55 |
56 | var promise = new Promise();
57 |
58 | var str = 'a';
59 |
60 | promise.then(function(x) {str =x; }, function (ex) { });
61 |
62 | promise.resolve('a');
63 | // //promise.resolve(2);
64 |
--------------------------------------------------------------------------------
/test/valid/promise-simple-api.js:
--------------------------------------------------------------------------------
1 | function Promise() {
2 | var thens = [];
3 | var that = this;
4 | that.then = function(cb) {
5 | var p = new Promise();
6 | thens.push(function(val) { p.resolve(cb(val)); });
7 | return p;
8 | };
9 | that.resolve = function(val) {
10 | var i = 0;
11 | for (i = 0; i < thens.length; i++) {
12 | thens[i](val);
13 | }
14 | that.then = function(cb) {
15 | var p = new Promise();
16 | p.resolve(cb(val));
17 | return p;
18 | };
19 | };
20 | };
21 |
22 |
23 |
--------------------------------------------------------------------------------
/test/valid/promise-simplified.js:
--------------------------------------------------------------------------------
1 | function Promise(x) {
2 | var _thens = [];
3 | _thens.push(x);
4 | };
5 |
6 | var p = Promise(123);
7 | var pb = Promise('a');
8 |
9 |
10 |
--------------------------------------------------------------------------------
/test/valid/promise-simplified2.js:
--------------------------------------------------------------------------------
1 | function Promise() {
2 | var _thens = [];
3 | _thens = [];
4 | this.push = function(x) {
5 | _thens.push(x);
6 | };
7 | };
8 |
9 | var p = new Promise();
10 | var p1 = p.push(1);
11 |
12 | var pb = new Promise();
13 | var pb1 = pb.push('2');
14 |
15 |
16 |
--------------------------------------------------------------------------------
/test/valid/promise.js:
--------------------------------------------------------------------------------
1 | // Based on https://gist.github.com/unscriptable/814052 - (c) copyright unscriptable.com / John Hann, License MIT
2 |
3 | function Promise () {
4 | var _thens = [];
5 | var that = this;
6 | /* This is the "front end" API. */
7 |
8 | // then(onResolve, onReject): Code waiting for this promise uses the
9 | // then() method to be notified when the promise is complete. There
10 | // are two completion callbacks: onReject and onResolve. A more
11 | // robust promise implementation will also have an onProgress handler.
12 | this.then = function (onResolve, onReject) {
13 | // capture calls to then()
14 | _thens.push({ resolve: onResolve, reject: onReject });
15 | };
16 |
17 | /* This is the "back end" API. */
18 |
19 | // resolve(resolvedValue): The resolve() method is called when a promise
20 | // is resolved (duh). The resolved value (if any) is passed by the resolver
21 | // to this method. All waiting onResolve callbacks are called
22 | // and any future ones are, too, each being passed the resolved value.
23 | this.resolve = function (val) {
24 | var errorFunc = function(val) { throw new Error('Already completed.'); };
25 | var i = 0;
26 | that.then = function(resolve, reject) { resolve(val); };
27 | that.resolve = errorFunc;
28 | that.reject = errorFunc;
29 | for (i = 0; i < _thens.length; i++) {
30 | _thens[i].resolve(val);
31 | }
32 | _thens = [];
33 | };
34 |
35 | // reject(exception): The reject() method is called when a promise cannot
36 | // be resolved. Typically, you'd pass an exception as the single parameter,
37 | // but any other argument, including none at all, is acceptable.
38 | // All waiting and all future onReject callbacks are called when reject()
39 | // is called and are passed the exception parameter.
40 | this.reject = function (ex) {
41 | var errorFunc = function(val) { throw new Error('Already completed.'); };
42 | var i = 0;
43 | that.then = function(resolve, reject) { reject(ex); };
44 | that.resolve = errorFunc;
45 | that.reject = errorFunc;
46 | for (i = 0; i < _thens.length; i++) {
47 | _thens[i].reject(ex);
48 | }
49 | _thens = [];
50 | };
51 |
52 | };
53 |
54 | var p = new Promise();
55 | var num = 32;
56 | var p1 = p.then(function(x) { num = x; }, function (ex) { });
57 |
58 | var pb = new Promise();
59 | var str = 'b';
60 | var pb1 = pb.then(function(x) { str = x; }, function (ex) { });
61 |
62 |
63 |
--------------------------------------------------------------------------------
/test/valid/prop-nested-get.js:
--------------------------------------------------------------------------------
1 | function f(x) { return x.foo.foo; };
2 | var x = f({ foo: { foo: 0 } });
3 |
--------------------------------------------------------------------------------
/test/valid/prop_access.js:
--------------------------------------------------------------------------------
1 | function magnitude(p) {
2 | var a = p.x;
3 | var b = p.y;//(p.x*p.x) + (p.y*p.y);
4 | return [a,b];
5 | }
6 |
7 | magnitude({ x: 1, y: 2 });
8 | magnitude({ x: false, y: true });
9 |
--------------------------------------------------------------------------------
/test/valid/prop_assign.js:
--------------------------------------------------------------------------------
1 | function setX(obj, val) {
2 | obj.x = val;
3 | }
4 |
5 | var z = { x: 3 };
6 | setX(z, 2);
7 | z;
8 |
--------------------------------------------------------------------------------
/test/valid/record.js:
--------------------------------------------------------------------------------
1 | var o = { x: 1, y: 2};
2 | function f(t) { return t.x; }
3 | f(o);
4 |
--------------------------------------------------------------------------------
/test/valid/recurse.js:
--------------------------------------------------------------------------------
1 | function f(x) { return f(x); }
2 | f;
3 |
--------------------------------------------------------------------------------
/test/valid/recurse2.js:
--------------------------------------------------------------------------------
1 | function f(x) { return f(x); }
2 | f;
3 |
--------------------------------------------------------------------------------
/test/valid/recursive-type.js:
--------------------------------------------------------------------------------
1 | var o = { method: function() { return this; } };
2 | var x = o.method();
3 | var y = x.method();
4 | var z = x === y;
5 | z;
6 |
--------------------------------------------------------------------------------
/test/valid/recursive_value.js:
--------------------------------------------------------------------------------
1 | function getUndefined() {}
2 | var x = x; // in JS, x is now 'undefined'
3 | x = getUndefined();
4 |
--------------------------------------------------------------------------------
/test/valid/regex.js:
--------------------------------------------------------------------------------
1 | var x = /[0-9].*\w/g;
2 | x;
3 |
--------------------------------------------------------------------------------
/test/valid/return-multi.js:
--------------------------------------------------------------------------------
1 | function f() {
2 | var g = 'bla';
3 | if (false) {
4 | return 2;
5 | }
6 | return 3;
7 | g = 'momo';
8 | }
9 |
10 | var x = f();
11 | var y = 2;
12 | y = x;
13 |
--------------------------------------------------------------------------------
/test/valid/return.js:
--------------------------------------------------------------------------------
1 | function f(x) { return; }
2 | var y = f(2);
3 |
4 |
--------------------------------------------------------------------------------
/test/valid/row-return.js:
--------------------------------------------------------------------------------
1 | function f(parent) {
2 | return parent.child;
3 | }
4 | var x = f({ child: { grandChild: false } });
5 | x.grandChild;
6 |
--------------------------------------------------------------------------------
/test/valid/switch.js:
--------------------------------------------------------------------------------
1 | switch (3) {
2 | case 0: 'a';
3 | case 1: 'b';
4 | defualt: null;
5 | }
6 |
7 |
--------------------------------------------------------------------------------
/test/valid/test.js:
--------------------------------------------------------------------------------
1 | function bla(options) {
2 | options.x = options.y;
3 | options.x = 3;
4 | return options.y;
5 | }
6 | bla;
7 |
--------------------------------------------------------------------------------
/test/valid/this.js:
--------------------------------------------------------------------------------
1 | var obj = { name: '3', getName: function() { return this.name; } };
2 | obj;
3 | var x = obj.getName();
4 | x;
5 |
--------------------------------------------------------------------------------
/test/valid/trivial.js:
--------------------------------------------------------------------------------
1 | var x = 3;
2 | x;
3 |
--------------------------------------------------------------------------------
/test/valid/try.js:
--------------------------------------------------------------------------------
1 | try {
2 | }
3 | catch (e) {
4 | e = 2;
5 | }
6 | finally {
7 | }
8 |
9 | var x = 2;
10 | try {
11 | x = 3;
12 | throw Error("hi");
13 | }
14 | catch (bla) {
15 | x = bla;
16 | }
17 | finally {
18 | x = 3;
19 | }
20 |
--------------------------------------------------------------------------------
/test/valid/var-empty.js:
--------------------------------------------------------------------------------
1 | var x;
2 | x;
3 |
--------------------------------------------------------------------------------
/test/valid/while.js:
--------------------------------------------------------------------------------
1 | while (true) { 1; }
2 | do { 1; } while (true)
3 |
--------------------------------------------------------------------------------
/webidl/Event.webidl:
--------------------------------------------------------------------------------
1 | /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 | /* This Source Code Form is subject to the terms of the Mozilla Public
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this file,
4 | * You can obtain one at http://mozilla.org/MPL/2.0/.
5 | *
6 | * The origin of this IDL file is
7 | * http://www.w3.org/TR/2012/WD-dom-20120105/
8 | *
9 | * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
10 | * liability, trademark and document use rules apply.
11 | */
12 |
13 | [Constructor(DOMString type, optional EventInit eventInitDict),
14 | Exposed=(Window,Worker,System)]
15 | interface Event {
16 | [Pure]
17 | readonly attribute DOMString type;
18 | [Pure]
19 | readonly attribute EventTarget? target;
20 | [Pure]
21 | readonly attribute EventTarget? currentTarget;
22 |
23 | const unsigned short NONE = 0;
24 | const unsigned short CAPTURING_PHASE = 1;
25 | const unsigned short AT_TARGET = 2;
26 | const unsigned short BUBBLING_PHASE = 3;
27 | [Pure]
28 | readonly attribute unsigned short eventPhase;
29 |
30 | void stopPropagation();
31 | void stopImmediatePropagation();
32 |
33 | [Pure]
34 | readonly attribute boolean bubbles;
35 | [Pure]
36 | readonly attribute boolean cancelable;
37 | void preventDefault();
38 | [Pure]
39 | readonly attribute boolean defaultPrevented;
40 |
41 | [Unforgeable, Pure]
42 | readonly attribute boolean isTrusted;
43 | [Pure]
44 | readonly attribute DOMHighResTimeStamp timeStamp;
45 |
46 | [Throws]
47 | void initEvent(DOMString type, boolean bubbles, boolean cancelable);
48 | };
49 |
50 | // Mozilla specific legacy stuff.
51 | partial interface Event {
52 | const long ALT_MASK = 0x00000001;
53 | const long CONTROL_MASK = 0x00000002;
54 | const long SHIFT_MASK = 0x00000004;
55 | const long META_MASK = 0x00000008;
56 |
57 | readonly attribute EventTarget? originalTarget;
58 | readonly attribute EventTarget? explicitOriginalTarget;
59 | [ChromeOnly] readonly attribute EventTarget? composedTarget;
60 | [ChromeOnly] readonly attribute boolean multipleActionsPrevented;
61 | [ChromeOnly] readonly attribute boolean isSynthesized;
62 |
63 | boolean getPreventDefault();
64 | };
65 |
66 | dictionary EventInit {
67 | boolean bubbles = false;
68 | boolean cancelable = false;
69 | };
70 |
--------------------------------------------------------------------------------
/webidl/EventTarget.webidl:
--------------------------------------------------------------------------------
1 | /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 | /* This Source Code Form is subject to the terms of the Mozilla Public
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this file,
4 | * You can obtain one at http://mozilla.org/MPL/2.0/.
5 | *
6 | * The origin of this IDL file is
7 | * http://www.w3.org/TR/2012/WD-dom-20120105/
8 | *
9 | * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
10 | * liability, trademark and document use rules apply.
11 | */
12 |
13 | [Exposed=(Window,Worker,WorkerDebugger,System)]
14 | interface EventTarget {
15 | /* Passing null for wantsUntrusted means "default behavior", which
16 | differs in content and chrome. In content that default boolean
17 | value is true, while in chrome the default boolean value is
18 | false. */
19 | [Throws]
20 | void addEventListener(DOMString type,
21 | EventListener? listener,
22 | optional boolean capture = false,
23 | optional boolean? wantsUntrusted = null);
24 | [Throws]
25 | void removeEventListener(DOMString type,
26 | EventListener? listener,
27 | optional boolean capture = false);
28 | [Throws]
29 | boolean dispatchEvent(Event event);
30 | };
31 |
32 | // Mozilla extensions for use by JS-implemented event targets to
33 | // implement on* properties.
34 | partial interface EventTarget {
35 | // The use of [TreatNonCallableAsNull] here is a bit of a hack: it just makes
36 | // the codegen check whether the type involved is either
37 | // [TreatNonCallableAsNull] or [TreatNonObjectAsNull] and if it is handle it
38 | // accordingly. In particular, it will NOT actually treat a non-null
39 | // non-callable object as null here.
40 | [ChromeOnly, Throws]
41 | void setEventHandler(DOMString type,
42 | [TreatNonCallableAsNull] EventHandler handler);
43 |
44 | [ChromeOnly]
45 | EventHandler getEventHandler(DOMString type);
46 | };
47 |
48 | // Mozilla extension to make firing events on event targets from
49 | // chrome easier. This returns the window which can be used to create
50 | // events to fire at this EventTarget, or null if there isn't one.
51 | partial interface EventTarget {
52 | [ChromeOnly, Exposed=Window, BinaryName="ownerGlobalForBindings"]
53 | readonly attribute WindowProxy? ownerGlobal;
54 | };
55 |
--------------------------------------------------------------------------------
/webidl/Node.webidl:
--------------------------------------------------------------------------------
1 | /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 | /* This Source Code Form is subject to the terms of the Mozilla Public
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this file,
4 | * You can obtain one at http://mozilla.org/MPL/2.0/.
5 | *
6 | * The origin of this IDL file is
7 | * http://www.w3.org/TR/2012/WD-dom-20120105/
8 | *
9 | * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
10 | * liability, trademark and document use rules apply.
11 | */
12 |
13 | interface Principal;
14 | interface URI;
15 |
16 | interface Node : EventTarget {
17 | const unsigned short ELEMENT_NODE = 1;
18 | const unsigned short ATTRIBUTE_NODE = 2; // historical
19 | const unsigned short TEXT_NODE = 3;
20 | const unsigned short CDATA_SECTION_NODE = 4; // historical
21 | const unsigned short ENTITY_REFERENCE_NODE = 5; // historical
22 | const unsigned short ENTITY_NODE = 6; // historical
23 | const unsigned short PROCESSING_INSTRUCTION_NODE = 7;
24 | const unsigned short COMMENT_NODE = 8;
25 | const unsigned short DOCUMENT_NODE = 9;
26 | const unsigned short DOCUMENT_TYPE_NODE = 10;
27 | const unsigned short DOCUMENT_FRAGMENT_NODE = 11;
28 | const unsigned short NOTATION_NODE = 12; // historical
29 | [Constant]
30 | readonly attribute unsigned short nodeType;
31 | [Pure]
32 | readonly attribute DOMString nodeName;
33 |
34 | [Pure]
35 | readonly attribute DOMString? baseURI;
36 |
37 | [Pure]
38 | readonly attribute Document? ownerDocument;
39 | [Pure]
40 | readonly attribute Node? parentNode;
41 | [Pure]
42 | readonly attribute Element? parentElement;
43 | [Pure]
44 | boolean hasChildNodes();
45 | [SameObject]
46 | readonly attribute NodeList childNodes;
47 | [Pure]
48 | readonly attribute Node? firstChild;
49 | [Pure]
50 | readonly attribute Node? lastChild;
51 | [Pure]
52 | readonly attribute Node? previousSibling;
53 | [Pure]
54 | readonly attribute Node? nextSibling;
55 |
56 | [SetterThrows, Pure]
57 | attribute DOMString? nodeValue;
58 | [Throws, Pure]
59 | attribute DOMString? textContent;
60 | [Throws]
61 | Node insertBefore(Node node, Node? child);
62 | [Throws]
63 | Node appendChild(Node node);
64 | [Throws]
65 | Node replaceChild(Node node, Node child);
66 | [Throws]
67 | Node removeChild(Node child);
68 | void normalize();
69 |
70 | [Throws]
71 | Node cloneNode(optional boolean deep = false);
72 | [Pure]
73 | boolean isEqualNode(Node? node);
74 |
75 | const unsigned short DOCUMENT_POSITION_DISCONNECTED = 0x01;
76 | const unsigned short DOCUMENT_POSITION_PRECEDING = 0x02;
77 | const unsigned short DOCUMENT_POSITION_FOLLOWING = 0x04;
78 | const unsigned short DOCUMENT_POSITION_CONTAINS = 0x08;
79 | const unsigned short DOCUMENT_POSITION_CONTAINED_BY = 0x10;
80 | const unsigned short DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = 0x20; // historical
81 | [Pure]
82 | unsigned short compareDocumentPosition(Node other);
83 | [Pure]
84 | boolean contains(Node? other);
85 |
86 | [Pure]
87 | DOMString? lookupPrefix(DOMString? namespace);
88 | [Pure]
89 | DOMString? lookupNamespaceURI(DOMString? prefix);
90 | [Pure]
91 | boolean isDefaultNamespace(DOMString? namespace);
92 |
93 | // Mozilla-specific stuff
94 | // These have been moved to Element in the spec.
95 | // If we move namespaceURI, prefix and localName to Element they should return
96 | // a non-nullable type.
97 | [Constant]
98 | readonly attribute DOMString? namespaceURI;
99 | [Constant]
100 | readonly attribute DOMString? prefix;
101 | [Constant]
102 | readonly attribute DOMString? localName;
103 |
104 | [Throws, Func="IsChromeOrXBL"]
105 | any setUserData(DOMString key, any data);
106 | [Throws, Func="IsChromeOrXBL"]
107 | any getUserData(DOMString key);
108 | [ChromeOnly]
109 | readonly attribute Principal nodePrincipal;
110 | [ChromeOnly]
111 | readonly attribute URI? baseURIObject;
112 | [ChromeOnly]
113 | sequence getBoundMutationObservers();
114 | };
115 |
--------------------------------------------------------------------------------