├── .gitignore ├── .vscode └── settings.json ├── LICENSE ├── README.md ├── docs └── example.png ├── package-lock.json ├── package.json ├── packages └── resuggest │ ├── .gitignore │ ├── package-lock.json │ ├── package.json │ ├── scripts │ ├── functions.js │ └── generate-db.js │ ├── src │ ├── __tests__ │ │ ├── isTypeAssignable.test.ts │ │ ├── parseType.test.ts │ │ ├── suggest.test.ts │ │ └── uniquePermutations.test.ts │ ├── bs-platform.d.ts │ ├── flatten.ts │ ├── generated │ │ └── db.js │ ├── index.ts │ ├── isTypeAssignable.ts │ ├── makeExample.ts │ ├── parseType.ts │ ├── reasonExpToJs.ts │ ├── suggest.ts │ ├── types.ts │ └── uniquePermutations.ts │ └── tsconfig.json ├── public ├── bsReasonReact.js ├── favicon.ico ├── index.html ├── manifest.json ├── refmt.js └── stdlibBundle.js ├── scripts └── generate-doc.js └── src ├── App.css ├── App.js ├── App.test.js ├── ExpectedMutations.js ├── InputsForm.js ├── ReasonExpressionInput.js ├── debounce.js ├── functionNameToReasonApiAnchorId.js ├── generated └── doc.js ├── index.css ├── index.js └── registerServiceWorker.js /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "editor.formatOnSave": true 4 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Guillaume Salles 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [resuggest](https://GuillaumeSalles.github.io/resuggest) 2 | 3 | ![Blazing Fast](https://img.shields.io/badge/speed-blazing%20%F0%9F%94%A5-brightgreen.svg) 4 | 5 | ![resuggest screenshot](https://github.com/GuillaumeSalles/resuggest/blob/master/docs/example.png?raw=true) 6 | 7 | Discover [Reason](https://reasonml.github.io/) functions based on examples. You supply some arguments and the desired output, then it makes suggestions. 8 | 9 | ## Features 10 | 11 | - Support 150+ functions without side effects 12 | - Display documentation for each functions 13 | - Permute arguments to increase the number of matches 14 | - Try function in the Reason [playground](https://github.com/GuillaumeSalles/resuggest) 15 | 16 | ## Thanks 17 | 18 | * [Cheng Lou](https://twitter.com/_chenglou) for the original [idea](https://github.com/chenglou/reason-project-ideas#api-search-by-giving-example-input--output) 19 | * [Wielfried Hughes](https://twitter.com/_wilfredh) for his brilliant article [Example Driven Development](http://www.wilfred.me.uk/blog/2016/07/30/example-driven-development/) 20 | * [Sarah Drasner](https://twitter.com/sarah_edo) for the beautiful design of [JavaScript Array Explorer](https://sdras.github.io/array-explorer/) and [JavaScript Object Explorer](https://sdras.github.io/object-explorer/) 21 | -------------------------------------------------------------------------------- /docs/example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuillaumeSalles/resuggest/bb821bd333b550f03c0e9b3d9aa3e95569779862/docs/example.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "resuggest-demo", 3 | "version": "0.3.0", 4 | "license": "MIT", 5 | "homepage": "https://GuillaumeSalles.github.io/resuggest", 6 | "dependencies": { 7 | "bs-platform": "3.0.0", 8 | "gh-pages": "1.0.0", 9 | "lz-string": "^1.4.4", 10 | "react": "16.3.2", 11 | "react-dom": "16.3.2", 12 | "react-scripts": "1.1.4", 13 | "resuggest": "file:./packages/resuggest" 14 | }, 15 | "scripts": { 16 | "predeploy": "npm run build", 17 | "deploy": "gh-pages -d build", 18 | "start": "react-scripts start", 19 | "build": "react-scripts build", 20 | "test": "react-scripts test --env=jsdom --watch", 21 | "eject": "react-scripts eject", 22 | "generate-doc": "node scripts/generate-doc.js" 23 | }, 24 | "devDependencies": { 25 | "axios": "^0.18.0", 26 | "jsdom": "^11.10.0" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/resuggest/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist -------------------------------------------------------------------------------- /packages/resuggest/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "resuggest", 3 | "version": "0.3.0", 4 | "description": "", 5 | "main": "./dist/index.js", 6 | "scripts": { 7 | "start": "tsc --watch", 8 | "test": "jest --watch", 9 | "generate-db": "node scripts/generate-db.js" 10 | }, 11 | "author": "", 12 | "license": "MIT", 13 | "devDependencies": { 14 | "@types/jest": "^23.1.1", 15 | "jest": "^23.1.0", 16 | "ts-jest": "^22.4.6", 17 | "typescript": "^2.9.2" 18 | }, 19 | "dependencies": { 20 | "bs-platform": "^3.1.5" 21 | }, 22 | "jest": { 23 | "moduleFileExtensions": [ 24 | "ts", 25 | "js" 26 | ], 27 | "transform": { 28 | "^.+\\.(ts)$": "ts-jest" 29 | }, 30 | "globals": { 31 | "ts-jest": { 32 | "tsConfigFile": "tsconfig.json" 33 | } 34 | }, 35 | "testPathIgnorePatterns": [ 36 | "/node_modules/", 37 | "/dist/" 38 | ], 39 | "testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /packages/resuggest/scripts/functions.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | // Pervasives 3 | 4 | // Comparisons 5 | "(==)", 6 | "(!=)", 7 | "(<)", 8 | "(>)", 9 | "(<=)", 10 | "(>=)", 11 | "compare", 12 | "min", 13 | "max", 14 | "(===)", 15 | "(!==)", 16 | 17 | // Boolean 18 | "(!)", 19 | "(&&)", 20 | "(||)", 21 | 22 | // Composition Operators 23 | "(|>)", 24 | "(@@)", 25 | 26 | // Integer arithmetic 27 | "(~-)", 28 | "(~+)", 29 | "succ", 30 | "pred", 31 | "(+)", 32 | "(-)", 33 | "(*)", 34 | "(/)", 35 | "(mod)", 36 | "abs", 37 | 38 | // Bitwise operations 39 | "lnot", 40 | "(land)", 41 | "(lor)", 42 | "(lxor)", 43 | "(lsl)", 44 | "(lsr)", 45 | "(asr)", 46 | 47 | // Floating-point arithmetic 48 | "(~-.)", 49 | "(~+.)", 50 | "(+.)", 51 | "(-.)", 52 | "(*.)", 53 | "(/.)", 54 | "(**)", 55 | "sqrt", 56 | "exp", 57 | "log", 58 | "log10", 59 | "expm1", 60 | "log1p", 61 | "cos", 62 | "sin", 63 | "tan", 64 | "acos", 65 | "asin", 66 | "atan", 67 | "atan2", 68 | "hypot", 69 | "cosh", 70 | "sinh", 71 | "tanh", 72 | "ceil", 73 | "floor", 74 | "abs_float", 75 | "copysign", 76 | "mod_float", 77 | "frexp", 78 | "ldexp", 79 | "modf", 80 | "float", 81 | "float_of_int", 82 | "truncate", 83 | "int_of_float", 84 | 85 | // String operations 86 | "(++)", 87 | 88 | // Character operations 89 | "int_of_char", 90 | "char_of_int", 91 | 92 | // String conversion functions 93 | "string_of_bool", 94 | "bool_of_string", 95 | "string_of_int", 96 | "int_of_string", 97 | "string_of_float", 98 | "float_of_string", 99 | 100 | // Pair operations 101 | "fst", 102 | "snd", 103 | 104 | // List operations 105 | "(@)", 106 | 107 | "Char.code", 108 | "Char.chr", 109 | "Char.escaped", 110 | "Char.lowercase", 111 | "Char.uppercase", 112 | 113 | "String.get", 114 | "String.make", 115 | "String.init", 116 | "String.sub", 117 | "String.mapi", 118 | "String.map", 119 | "String.trim", 120 | "String.escaped", 121 | "String.uppercase", 122 | "String.lowercase", 123 | "String.capitalize", 124 | "String.uncapitalize", 125 | "String.index", 126 | "String.rindex", 127 | "String.index_from", 128 | "String.rindex_from", 129 | "String.contains", 130 | "String.contains_from", 131 | "String.rcontains_from", 132 | "String.length", 133 | 134 | "List.append", 135 | "List.rev_append", 136 | "List.length", 137 | "List.hd", 138 | "List.tl", 139 | "List.rev", 140 | "List.nth", 141 | "List.concat", 142 | "List.flatten", 143 | "List.map", 144 | "List.rev_map", 145 | "List.mapi", 146 | "List.fold_left", 147 | "List.fold_right", 148 | "List.map2", 149 | "List.rev_map2", 150 | "List.fold_left2", 151 | "List.fold_right2", 152 | "List.exists", 153 | "List.exists2", 154 | "List.mem", 155 | "List.memq", 156 | "List.find", 157 | "List.filter", 158 | "List.find_all", 159 | "List.sort", 160 | "List.stable_sort", 161 | "List.fast_sort", 162 | "List.sort_uniq", 163 | "List.merge", 164 | 165 | "Array.length", 166 | "Array.get", 167 | "Array.set", 168 | "Array.make", 169 | "Array.init", 170 | "Array.make_matrix", 171 | "Array.append", 172 | "Array.concat", 173 | "Array.sub", 174 | "Array.copy", 175 | // "Array.fill", 176 | // "Array.blit", 177 | "Array.to_list", 178 | "Array.of_list", 179 | "Array.map", 180 | "Array.mapi", 181 | "Array.fold_left", 182 | "Array.fold_right", 183 | "Array.make_float", 184 | "Array.sort", 185 | "Array.stable_sort", 186 | "Array.fast_sort" 187 | ]; 188 | -------------------------------------------------------------------------------- /packages/resuggest/scripts/generate-db.js: -------------------------------------------------------------------------------- 1 | const functions = require("./functions"); 2 | require("../../../public/bsReasonReact"); 3 | const { printML, parseRE } = require("../../../public/refmt"); 4 | var fs = require("fs"); 5 | 6 | function compileReason(reason) { 7 | const ocamlCode = printML(parseRE(reason)); 8 | return ocaml.compile(ocamlCode); 9 | } 10 | 11 | const guessType = reasonExpression => { 12 | const compilationResult = compileReason( 13 | `let exp = (${reasonExpression}) == 1;` 14 | ); 15 | if (compilationResult.js_code) { 16 | return "int"; 17 | } else { 18 | // error format : "This expression has type int but an expression was expected of type {expressionType}" 19 | const type = compilationResult.text.substring(69).trim(); 20 | return type; 21 | } 22 | }; 23 | 24 | function makeReasonArrayOfFunctions(name, type, functions) { 25 | const fns = functions.map(fnName => `(${fnName},"${fnName}")`).join(","); 26 | return `let ${name} = ("${type}",[${fns}]);`; 27 | } 28 | 29 | const functionsByType = new Map(); 30 | 31 | for (var functionName of functions) { 32 | const type = guessType(functionName); 33 | if (!functionsByType.has(type)) { 34 | functionsByType.set(type, []); 35 | } 36 | functionsByType.get(type).push(functionName); 37 | } 38 | 39 | const tmpReasonFileContent = Array.from(functionsByType) 40 | .map(([type, fns], i) => makeReasonArrayOfFunctions("f" + i, type, fns)) 41 | .join("\n"); 42 | 43 | fs.writeFileSync( 44 | "./src/generated/db.js", 45 | compileReason(tmpReasonFileContent).js_code.replace( 46 | /stdlib/g, 47 | "bs-platform/lib/js" 48 | ) 49 | ); 50 | -------------------------------------------------------------------------------- /packages/resuggest/src/__tests__/isTypeAssignable.test.ts: -------------------------------------------------------------------------------- 1 | import isTypeAssignable from "../isTypeAssignable"; 2 | import parseType from "../parseType"; 3 | 4 | function testCase(left: string, right: string, result: boolean) { 5 | test(`${right} should ${ 6 | result ? "" : "NOT" 7 | } be assignable to ${left}`, () => { 8 | expect(isTypeAssignable(parseType(left), parseType(right))).toEqual(result); 9 | }); 10 | } 11 | 12 | testCase("int", "int", true); 13 | testCase("string", "string", true); 14 | testCase("int", "string", false); 15 | testCase("string list", "string list", true); 16 | testCase("string list list", "string list list", true); 17 | testCase("int -> int", "int -> int", true); 18 | testCase("'a -> 'a", "int -> int", true); 19 | testCase("'a -> 'a", "int -> string", false); 20 | testCase("int -> int -> int", "int -> int -> int", true); 21 | testCase("'a -> 'a -> int", "'a -> 'a -> int", true); 22 | testCase( 23 | "('a -> 'a -> bool) -> 'a list -> 'a list", 24 | "(int -> int -> bool) -> int list -> int list", 25 | true 26 | ); 27 | testCase( 28 | "('a -> 'b) -> 'a list -> 'b list", 29 | "(int -> int) -> int list -> int list", 30 | true 31 | ); 32 | testCase( 33 | "int -> (int -> char) -> string", 34 | "int -> ('a -> char) -> string", 35 | true 36 | ); 37 | testCase("'a array -> int", "int array -> int", true); 38 | testCase("int * string", "int * string", true); 39 | testCase("int * int", "int * string", false); 40 | testCase("int * string * char", "int * string * char", true); 41 | testCase("'a * 'b * 'a", "int * string * int", true); 42 | testCase("'a * 'b -> 'a", "int * string -> int", true); 43 | testCase("'a * 'b -> 'a", "int * int * int -> int", false); 44 | testCase("int -> unit", "int -> unit", true); 45 | -------------------------------------------------------------------------------- /packages/resuggest/src/__tests__/parseType.test.ts: -------------------------------------------------------------------------------- 1 | import parseType, { 2 | createPostfixExpression, 3 | tokenStream, 4 | tokenKinds 5 | } from "../parseType"; 6 | import { AstTypeKind } from "../types"; 7 | 8 | describe("createPostfixExpression", () => { 9 | it("should parse int * string -> int", () => { 10 | expect(createPostfixExpression(tokenStream("int * string -> int"))).toEqual( 11 | [ 12 | { kind: tokenKinds.simple, value: "int" }, 13 | { kind: tokenKinds.simple, value: "string" }, 14 | { kind: tokenKinds.star }, 15 | { kind: tokenKinds.simple, value: "int" }, 16 | { kind: tokenKinds.arrow } 17 | ] 18 | ); 19 | }); 20 | 21 | it("should parse int * int * int", () => { 22 | expect(createPostfixExpression(tokenStream("int * int * int"))).toEqual([ 23 | { kind: tokenKinds.simple, value: "int" }, 24 | { kind: tokenKinds.simple, value: "int" }, 25 | { kind: tokenKinds.simple, value: "int" }, 26 | { kind: tokenKinds.star }, 27 | { kind: tokenKinds.star } 28 | ]); 29 | }); 30 | 31 | it("ratata should parse int * int * int -> int", () => { 32 | expect( 33 | createPostfixExpression(tokenStream("int * int * int -> int")) 34 | ).toEqual([ 35 | { kind: tokenKinds.simple, value: "int" }, 36 | { kind: tokenKinds.simple, value: "int" }, 37 | { kind: tokenKinds.simple, value: "int" }, 38 | { kind: tokenKinds.star }, 39 | { kind: tokenKinds.star }, 40 | { kind: tokenKinds.simple, value: "int" }, 41 | { kind: tokenKinds.arrow } 42 | ]); 43 | }); 44 | 45 | it("should parse int -> string * int", () => { 46 | expect(createPostfixExpression(tokenStream("int -> string * int"))).toEqual( 47 | [ 48 | { kind: tokenKinds.simple, value: "int" }, 49 | { kind: tokenKinds.simple, value: "string" }, 50 | { kind: tokenKinds.simple, value: "int" }, 51 | { kind: tokenKinds.star }, 52 | { kind: tokenKinds.arrow } 53 | ] 54 | ); 55 | }); 56 | }); 57 | 58 | describe("parseType", () => { 59 | it("should parse bool", () => { 60 | expect(parseType("bool")).toEqual({ 61 | kind: AstTypeKind.Simple, 62 | type: "bool" 63 | }); 64 | }); 65 | 66 | it("should parse int", () => { 67 | expect(parseType("int")).toEqual({ kind: AstTypeKind.Simple, type: "int" }); 68 | }); 69 | 70 | it("should parse float", () => { 71 | expect(parseType("float")).toEqual({ 72 | kind: AstTypeKind.Simple, 73 | type: "float" 74 | }); 75 | }); 76 | 77 | it("should parse char", () => { 78 | expect(parseType("char")).toEqual({ 79 | kind: AstTypeKind.Simple, 80 | type: "char" 81 | }); 82 | }); 83 | 84 | it("should parse string", () => { 85 | expect(parseType("string")).toEqual({ 86 | kind: AstTypeKind.Simple, 87 | type: "string" 88 | }); 89 | }); 90 | 91 | it("should parse int * string -> int", () => { 92 | expect(parseType("int * string -> int")).toEqual({ 93 | kind: AstTypeKind.Func, 94 | input: { 95 | kind: AstTypeKind.Tuple, 96 | types: [ 97 | { kind: AstTypeKind.Simple, type: "int" }, 98 | { kind: AstTypeKind.Simple, type: "string" } 99 | ] 100 | }, 101 | output: { kind: AstTypeKind.Simple, type: "int" } 102 | }); 103 | }); 104 | 105 | it("should parse list of string", () => { 106 | expect(parseType("string list")).toEqual({ 107 | kind: AstTypeKind.List, 108 | itemType: { kind: AstTypeKind.Simple, type: "string" } 109 | }); 110 | }); 111 | 112 | it("should parse list of list of string", () => { 113 | expect(parseType("string list list")).toEqual({ 114 | kind: AstTypeKind.List, 115 | itemType: { 116 | kind: AstTypeKind.List, 117 | itemType: { kind: AstTypeKind.Simple, type: "string" } 118 | } 119 | }); 120 | }); 121 | 122 | it("should parse int -> string", () => { 123 | expect(parseType("int -> string")).toEqual({ 124 | kind: AstTypeKind.Func, 125 | input: { kind: AstTypeKind.Simple, type: "int" }, 126 | output: { kind: AstTypeKind.Simple, type: "string" } 127 | }); 128 | }); 129 | 130 | it("should parse int -> string -> float", () => { 131 | expect(parseType("int -> string -> float")).toEqual({ 132 | kind: AstTypeKind.Func, 133 | input: { kind: AstTypeKind.Simple, type: "int" }, 134 | output: { 135 | kind: AstTypeKind.Func, 136 | input: { kind: AstTypeKind.Simple, type: "string" }, 137 | output: { kind: AstTypeKind.Simple, type: "float" } 138 | } 139 | }); 140 | }); 141 | 142 | it("should parse 'a -> 'a", () => { 143 | expect(parseType("'a -> 'a")).toEqual({ 144 | kind: AstTypeKind.Func, 145 | input: { kind: AstTypeKind.Generic, type: "'a" }, 146 | output: { kind: AstTypeKind.Generic, type: "'a" } 147 | }); 148 | }); 149 | 150 | it("should parse int -> ('a -> char) -> string", () => { 151 | expect(parseType("int -> ('a -> char) -> string")).toEqual({ 152 | kind: AstTypeKind.Func, 153 | input: { kind: AstTypeKind.Simple, type: "int" }, 154 | output: { 155 | kind: AstTypeKind.Func, 156 | input: { 157 | kind: AstTypeKind.Func, 158 | input: { kind: AstTypeKind.Generic, type: "'a" }, 159 | output: { kind: AstTypeKind.Simple, type: "char" } 160 | }, 161 | output: { kind: AstTypeKind.Simple, type: "string" } 162 | } 163 | }); 164 | }); 165 | 166 | it("should parse ('a -> 'b) -> 'a list -> 'b list", () => { 167 | expect(parseType("('a -> 'b) -> 'a list -> 'b list")).toEqual({ 168 | kind: AstTypeKind.Func, 169 | input: { 170 | kind: AstTypeKind.Func, 171 | input: { kind: AstTypeKind.Generic, type: "'a" }, 172 | output: { kind: AstTypeKind.Generic, type: "'b" } 173 | }, 174 | output: { 175 | kind: AstTypeKind.Func, 176 | input: { 177 | kind: AstTypeKind.List, 178 | itemType: { 179 | kind: AstTypeKind.Generic, 180 | type: "'a" 181 | } 182 | }, 183 | output: { 184 | kind: AstTypeKind.List, 185 | itemType: { 186 | kind: AstTypeKind.Generic, 187 | type: "'b" 188 | } 189 | } 190 | } 191 | }); 192 | }); 193 | 194 | it("should parse 'a array -> int", () => { 195 | expect(parseType("'a array -> int")).toEqual({ 196 | kind: AstTypeKind.Func, 197 | input: { 198 | kind: AstTypeKind.Array, 199 | itemType: { 200 | kind: AstTypeKind.Generic, 201 | type: "'a" 202 | } 203 | }, 204 | output: { kind: AstTypeKind.Simple, type: "int" } 205 | }); 206 | }); 207 | 208 | it("should parse int * string", () => { 209 | expect(parseType("int * string")).toEqual({ 210 | kind: AstTypeKind.Tuple, 211 | types: [ 212 | { kind: AstTypeKind.Simple, type: "int" }, 213 | { kind: AstTypeKind.Simple, type: "string" } 214 | ] 215 | }); 216 | }); 217 | 218 | it("should parse string * int * char", () => { 219 | expect(parseType("string * int * char")).toEqual({ 220 | kind: AstTypeKind.Tuple, 221 | types: [ 222 | { kind: AstTypeKind.Simple, type: "string" }, 223 | { kind: AstTypeKind.Simple, type: "int" }, 224 | { kind: AstTypeKind.Simple, type: "char" } 225 | ] 226 | }); 227 | }); 228 | 229 | it("should parse float * string * int * char", () => { 230 | expect(parseType("float * string * int * char")).toEqual({ 231 | kind: AstTypeKind.Tuple, 232 | types: [ 233 | { kind: AstTypeKind.Simple, type: "float" }, 234 | { kind: AstTypeKind.Simple, type: "string" }, 235 | { kind: AstTypeKind.Simple, type: "int" }, 236 | { kind: AstTypeKind.Simple, type: "char" } 237 | ] 238 | }); 239 | }); 240 | 241 | it("should parse 'a array -> unit", () => { 242 | expect(parseType("'a array -> unit")).toEqual({ 243 | kind: AstTypeKind.Func, 244 | input: { 245 | kind: AstTypeKind.Array, 246 | itemType: { 247 | kind: AstTypeKind.Generic, 248 | type: "'a" 249 | } 250 | }, 251 | output: { 252 | kind: AstTypeKind.Unit 253 | } 254 | }); 255 | }); 256 | }); 257 | -------------------------------------------------------------------------------- /packages/resuggest/src/__tests__/suggest.test.ts: -------------------------------------------------------------------------------- 1 | import suggest from "../suggest"; 2 | import { 3 | ValidCompilationResult, 4 | Suggestion, 5 | CompiledInput, 6 | EmptyCompilationResult 7 | } from "../types"; 8 | import parseType from "../parseType"; 9 | 10 | function immutableInput( 11 | code: string, 12 | jsValue: any, 13 | type: string 14 | ): CompiledInput { 15 | return { 16 | expression: cr(code, jsValue, type), 17 | expectedMutation: null 18 | }; 19 | } 20 | 21 | function mutableInput( 22 | code: string, 23 | jsValue: any, 24 | mCode: string, 25 | mJsValue: any, 26 | type: string 27 | ): CompiledInput { 28 | return { 29 | expression: cr(code, jsValue, type), 30 | expectedMutation: cr(mCode, mJsValue, type) 31 | }; 32 | } 33 | 34 | function empty(): EmptyCompilationResult { 35 | return { 36 | kind: "empty", 37 | code: "" 38 | }; 39 | } 40 | 41 | function cr(code: string, jsValue: any, type: string): ValidCompilationResult { 42 | return { 43 | kind: "valid", 44 | code, 45 | jsValue, 46 | type: parseType(type) 47 | }; 48 | } 49 | 50 | function suggestion(functionName: string, example: string): Suggestion { 51 | return { 52 | functionName, 53 | example 54 | }; 55 | } 56 | 57 | test("(+)", () => { 58 | expect( 59 | suggest( 60 | [immutableInput("1", 1, "int"), immutableInput("3", 3, "int")], 61 | cr("4", 4, "int") 62 | ) 63 | ).toEqual([ 64 | suggestion("(+)", "Js.log(1 + 3 == 4); /* true */"), 65 | suggestion("(+)", "Js.log(3 + 1 == 4); /* true */") 66 | ]); 67 | }); 68 | 69 | test("No output / no mutations", () => { 70 | expect( 71 | suggest( 72 | [ 73 | immutableInput("[|0,1,2|]", [0, 1, 2], "array int"), 74 | immutableInput("0", 0, "int"), 75 | immutableInput("4", 4, "int") 76 | ], 77 | empty() 78 | ) 79 | ).toEqual([]); 80 | }); 81 | 82 | test("Array.set invalid mutation", () => { 83 | expect( 84 | suggest( 85 | [ 86 | mutableInput( 87 | "[|0,1,2|]", 88 | [0, 1, 2], 89 | "[|4,5,6|]", 90 | [4, 5, 6], 91 | "array int" 92 | ), 93 | immutableInput("0", 0, "int"), 94 | immutableInput("4", 4, "int") 95 | ], 96 | empty() 97 | ) 98 | ).toEqual([]); 99 | }); 100 | 101 | test("Array.set ordered", () => { 102 | expect( 103 | suggest( 104 | [ 105 | mutableInput( 106 | "[|0,1,2|]", 107 | [0, 1, 2], 108 | "[|3,1,2|]", 109 | [3, 1, 2], 110 | "array int" 111 | ), 112 | immutableInput("0", 0, "int"), 113 | immutableInput("3", 3, "int") 114 | ], 115 | empty() 116 | ) 117 | ).toEqual([ 118 | suggestion( 119 | "Array.set", 120 | `let arg1 = [|0,1,2|]; 121 | Array.set(arg1, 0, 3); 122 | Js.log(arg1 == [|3,1,2|]); /* true */` 123 | ) 124 | ]); 125 | }); 126 | 127 | test("Array.set unordered", () => { 128 | expect( 129 | suggest( 130 | [ 131 | immutableInput("0", 0, "int"), 132 | mutableInput( 133 | "[|0,1,2|]", 134 | [0, 1, 2], 135 | "[|3,1,2|]", 136 | [3, 1, 2], 137 | "array int" 138 | ), 139 | immutableInput("3", 3, "int") 140 | ], 141 | empty() 142 | ) 143 | ).toEqual([ 144 | suggestion( 145 | "Array.set", 146 | `let arg1 = [|0,1,2|]; 147 | Array.set(arg1, 0, 3); 148 | Js.log(arg1 == [|3,1,2|]); /* true */` 149 | ) 150 | ]); 151 | }); 152 | -------------------------------------------------------------------------------- /packages/resuggest/src/__tests__/uniquePermutations.test.ts: -------------------------------------------------------------------------------- 1 | import uniquePermutations from "../uniquePermutations"; 2 | 3 | test("should return all possibilities", () => { 4 | expect(uniquePermutations(["1", "2"], s => s)).toEqual([ 5 | ["1", "2"], 6 | ["2", "1"] 7 | ]); 8 | }); 9 | -------------------------------------------------------------------------------- /packages/resuggest/src/bs-platform.d.ts: -------------------------------------------------------------------------------- 1 | declare module "bs-platform/lib/js/caml_obj" { 2 | export function caml_equal(a: any, b: any): boolean; 3 | } 4 | 5 | declare module "bs-platform/lib/js/array.js" { 6 | export function of_list(list: any): any[]; 7 | } 8 | -------------------------------------------------------------------------------- /packages/resuggest/src/flatten.ts: -------------------------------------------------------------------------------- 1 | export default function flatten(arrayOfArrays: T[][]): T[] { 2 | var result = []; 3 | for (var array of arrayOfArrays) { 4 | for (var item of array) { 5 | result.push(item); 6 | } 7 | } 8 | return result; 9 | } 10 | -------------------------------------------------------------------------------- /packages/resuggest/src/generated/db.js: -------------------------------------------------------------------------------- 1 | // Generated by BUCKLESCRIPT VERSION 3.0.1, PLEASE EDIT WITH CARE 2 | 'use strict'; 3 | 4 | var Char = require("bs-platform/lib/js/char"); 5 | var List = require("bs-platform/lib/js/list"); 6 | var $$Array = require("bs-platform/lib/js/array"); 7 | var Curry = require("bs-platform/lib/js/curry"); 8 | var $$String = require("bs-platform/lib/js/string"); 9 | var Caml_obj = require("bs-platform/lib/js/caml_obj"); 10 | var Caml_array = require("bs-platform/lib/js/caml_array"); 11 | var Caml_float = require("bs-platform/lib/js/caml_float"); 12 | var Caml_int32 = require("bs-platform/lib/js/caml_int32"); 13 | var Pervasives = require("bs-platform/lib/js/pervasives"); 14 | var Caml_format = require("bs-platform/lib/js/caml_format"); 15 | var Caml_string = require("bs-platform/lib/js/caml_string"); 16 | 17 | var f0_001 = /* :: */[ 18 | /* tuple */[ 19 | Caml_obj.caml_equal, 20 | "(==)" 21 | ], 22 | /* :: */[ 23 | /* tuple */[ 24 | Caml_obj.caml_notequal, 25 | "(!=)" 26 | ], 27 | /* :: */[ 28 | /* tuple */[ 29 | Caml_obj.caml_lessthan, 30 | "(<)" 31 | ], 32 | /* :: */[ 33 | /* tuple */[ 34 | Caml_obj.caml_greaterthan, 35 | "(>)" 36 | ], 37 | /* :: */[ 38 | /* tuple */[ 39 | Caml_obj.caml_lessequal, 40 | "(<=)" 41 | ], 42 | /* :: */[ 43 | /* tuple */[ 44 | Caml_obj.caml_greaterequal, 45 | "(>=)" 46 | ], 47 | /* :: */[ 48 | /* tuple */[ 49 | (function (prim, prim$1) { 50 | return prim === prim$1; 51 | }), 52 | "(===)" 53 | ], 54 | /* :: */[ 55 | /* tuple */[ 56 | (function (prim, prim$1) { 57 | return prim !== prim$1; 58 | }), 59 | "(!==)" 60 | ], 61 | /* [] */0 62 | ] 63 | ] 64 | ] 65 | ] 66 | ] 67 | ] 68 | ] 69 | ]; 70 | 71 | var f0 = /* tuple */[ 72 | "'a -> 'a -> bool", 73 | f0_001 74 | ]; 75 | 76 | var f1_001 = /* :: */[ 77 | /* tuple */[ 78 | Caml_obj.caml_compare, 79 | "compare" 80 | ], 81 | /* [] */0 82 | ]; 83 | 84 | var f1 = /* tuple */[ 85 | "'a -> 'a -> int", 86 | f1_001 87 | ]; 88 | 89 | var f2_001 = /* :: */[ 90 | /* tuple */[ 91 | Caml_obj.caml_min, 92 | "min" 93 | ], 94 | /* :: */[ 95 | /* tuple */[ 96 | Caml_obj.caml_max, 97 | "max" 98 | ], 99 | /* [] */0 100 | ] 101 | ]; 102 | 103 | var f2 = /* tuple */[ 104 | "'a -> 'a -> 'a", 105 | f2_001 106 | ]; 107 | 108 | var f3_001 = /* :: */[ 109 | /* tuple */[ 110 | (function (prim) { 111 | return !prim; 112 | }), 113 | "(!)" 114 | ], 115 | /* [] */0 116 | ]; 117 | 118 | var f3 = /* tuple */[ 119 | "bool -> bool", 120 | f3_001 121 | ]; 122 | 123 | var f4_001 = /* :: */[ 124 | /* tuple */[ 125 | (function (prim, prim$1) { 126 | if (prim) { 127 | return prim$1; 128 | } else { 129 | return false; 130 | } 131 | }), 132 | "(&&)" 133 | ], 134 | /* :: */[ 135 | /* tuple */[ 136 | (function (prim, prim$1) { 137 | if (prim) { 138 | return true; 139 | } else { 140 | return prim$1; 141 | } 142 | }), 143 | "(||)" 144 | ], 145 | /* [] */0 146 | ] 147 | ]; 148 | 149 | var f4 = /* tuple */[ 150 | "bool -> bool -> bool", 151 | f4_001 152 | ]; 153 | 154 | var f5_001 = /* :: */[ 155 | /* tuple */[ 156 | (function (prim, prim$1) { 157 | return Curry._1(prim$1, prim); 158 | }), 159 | "(|>)" 160 | ], 161 | /* [] */0 162 | ]; 163 | 164 | var f5 = /* tuple */[ 165 | "'a -> ('a -> 'b) -> 'b", 166 | f5_001 167 | ]; 168 | 169 | var f6_001 = /* :: */[ 170 | /* tuple */[ 171 | (function (prim, prim$1) { 172 | return Curry._1(prim, prim$1); 173 | }), 174 | "(@@)" 175 | ], 176 | /* [] */0 177 | ]; 178 | 179 | var f6 = /* tuple */[ 180 | "('a -> 'b) -> 'a -> 'b", 181 | f6_001 182 | ]; 183 | 184 | var f7_001 = /* :: */[ 185 | /* tuple */[ 186 | (function (prim) { 187 | return -prim | 0; 188 | }), 189 | "(~-)" 190 | ], 191 | /* :: */[ 192 | /* tuple */[ 193 | (function (prim) { 194 | return prim; 195 | }), 196 | "(~+)" 197 | ], 198 | /* :: */[ 199 | /* tuple */[ 200 | (function (prim) { 201 | return prim + 1 | 0; 202 | }), 203 | "succ" 204 | ], 205 | /* :: */[ 206 | /* tuple */[ 207 | (function (prim) { 208 | return prim - 1 | 0; 209 | }), 210 | "pred" 211 | ], 212 | /* :: */[ 213 | /* tuple */[ 214 | Pervasives.abs, 215 | "abs" 216 | ], 217 | /* :: */[ 218 | /* tuple */[ 219 | Pervasives.lnot, 220 | "lnot" 221 | ], 222 | /* [] */0 223 | ] 224 | ] 225 | ] 226 | ] 227 | ] 228 | ]; 229 | 230 | var f7 = /* tuple */[ 231 | "int -> int", 232 | f7_001 233 | ]; 234 | 235 | var f8_001 = /* :: */[ 236 | /* tuple */[ 237 | (function (prim, prim$1) { 238 | return prim + prim$1 | 0; 239 | }), 240 | "(+)" 241 | ], 242 | /* :: */[ 243 | /* tuple */[ 244 | (function (prim, prim$1) { 245 | return prim - prim$1 | 0; 246 | }), 247 | "(-)" 248 | ], 249 | /* :: */[ 250 | /* tuple */[ 251 | Caml_int32.imul, 252 | "(*)" 253 | ], 254 | /* :: */[ 255 | /* tuple */[ 256 | Caml_int32.div, 257 | "(/)" 258 | ], 259 | /* :: */[ 260 | /* tuple */[ 261 | Caml_int32.mod_, 262 | "(mod)" 263 | ], 264 | /* :: */[ 265 | /* tuple */[ 266 | (function (prim, prim$1) { 267 | return prim & prim$1; 268 | }), 269 | "(land)" 270 | ], 271 | /* :: */[ 272 | /* tuple */[ 273 | (function (prim, prim$1) { 274 | return prim | prim$1; 275 | }), 276 | "(lor)" 277 | ], 278 | /* :: */[ 279 | /* tuple */[ 280 | (function (prim, prim$1) { 281 | return prim ^ prim$1; 282 | }), 283 | "(lxor)" 284 | ], 285 | /* :: */[ 286 | /* tuple */[ 287 | (function (prim, prim$1) { 288 | return (prim << prim$1); 289 | }), 290 | "(lsl)" 291 | ], 292 | /* :: */[ 293 | /* tuple */[ 294 | (function (prim, prim$1) { 295 | return (prim >>> prim$1) | 0; 296 | }), 297 | "(lsr)" 298 | ], 299 | /* :: */[ 300 | /* tuple */[ 301 | (function (prim, prim$1) { 302 | return (prim >> prim$1); 303 | }), 304 | "(asr)" 305 | ], 306 | /* [] */0 307 | ] 308 | ] 309 | ] 310 | ] 311 | ] 312 | ] 313 | ] 314 | ] 315 | ] 316 | ] 317 | ]; 318 | 319 | var f8 = /* tuple */[ 320 | "int -> int -> int", 321 | f8_001 322 | ]; 323 | 324 | var f9_001 = /* :: */[ 325 | /* tuple */[ 326 | (function (prim) { 327 | return -prim; 328 | }), 329 | "(~-.)" 330 | ], 331 | /* :: */[ 332 | /* tuple */[ 333 | (function (prim) { 334 | return prim; 335 | }), 336 | "(~+.)" 337 | ], 338 | /* :: */[ 339 | /* tuple */[ 340 | (function (prim) { 341 | return Math.sqrt(prim); 342 | }), 343 | "sqrt" 344 | ], 345 | /* :: */[ 346 | /* tuple */[ 347 | (function (prim) { 348 | return Math.exp(prim); 349 | }), 350 | "exp" 351 | ], 352 | /* :: */[ 353 | /* tuple */[ 354 | (function (prim) { 355 | return Math.log(prim); 356 | }), 357 | "log" 358 | ], 359 | /* :: */[ 360 | /* tuple */[ 361 | (function (prim) { 362 | return Math.log10(prim); 363 | }), 364 | "log10" 365 | ], 366 | /* :: */[ 367 | /* tuple */[ 368 | Caml_float.caml_expm1_float, 369 | "expm1" 370 | ], 371 | /* :: */[ 372 | /* tuple */[ 373 | (function (prim) { 374 | return Math.log1p(prim); 375 | }), 376 | "log1p" 377 | ], 378 | /* :: */[ 379 | /* tuple */[ 380 | (function (prim) { 381 | return Math.cos(prim); 382 | }), 383 | "cos" 384 | ], 385 | /* :: */[ 386 | /* tuple */[ 387 | (function (prim) { 388 | return Math.sin(prim); 389 | }), 390 | "sin" 391 | ], 392 | /* :: */[ 393 | /* tuple */[ 394 | (function (prim) { 395 | return Math.tan(prim); 396 | }), 397 | "tan" 398 | ], 399 | /* :: */[ 400 | /* tuple */[ 401 | (function (prim) { 402 | return Math.acos(prim); 403 | }), 404 | "acos" 405 | ], 406 | /* :: */[ 407 | /* tuple */[ 408 | (function (prim) { 409 | return Math.asin(prim); 410 | }), 411 | "asin" 412 | ], 413 | /* :: */[ 414 | /* tuple */[ 415 | (function (prim) { 416 | return Math.atan(prim); 417 | }), 418 | "atan" 419 | ], 420 | /* :: */[ 421 | /* tuple */[ 422 | (function (prim) { 423 | return Math.cosh(prim); 424 | }), 425 | "cosh" 426 | ], 427 | /* :: */[ 428 | /* tuple */[ 429 | (function (prim) { 430 | return Math.sinh(prim); 431 | }), 432 | "sinh" 433 | ], 434 | /* :: */[ 435 | /* tuple */[ 436 | (function (prim) { 437 | return Math.tanh(prim); 438 | }), 439 | "tanh" 440 | ], 441 | /* :: */[ 442 | /* tuple */[ 443 | (function (prim) { 444 | return Math.ceil(prim); 445 | }), 446 | "ceil" 447 | ], 448 | /* :: */[ 449 | /* tuple */[ 450 | (function (prim) { 451 | return Math.floor(prim); 452 | }), 453 | "floor" 454 | ], 455 | /* :: */[ 456 | /* tuple */[ 457 | (function (prim) { 458 | return Math.abs(prim); 459 | }), 460 | "abs_float" 461 | ], 462 | /* [] */0 463 | ] 464 | ] 465 | ] 466 | ] 467 | ] 468 | ] 469 | ] 470 | ] 471 | ] 472 | ] 473 | ] 474 | ] 475 | ] 476 | ] 477 | ] 478 | ] 479 | ] 480 | ] 481 | ] 482 | ]; 483 | 484 | var f9 = /* tuple */[ 485 | "float -> float", 486 | f9_001 487 | ]; 488 | 489 | var f10_001 = /* :: */[ 490 | /* tuple */[ 491 | (function (prim, prim$1) { 492 | return prim + prim$1; 493 | }), 494 | "(+.)" 495 | ], 496 | /* :: */[ 497 | /* tuple */[ 498 | (function (prim, prim$1) { 499 | return prim - prim$1; 500 | }), 501 | "(-.)" 502 | ], 503 | /* :: */[ 504 | /* tuple */[ 505 | (function (prim, prim$1) { 506 | return prim * prim$1; 507 | }), 508 | "(*.)" 509 | ], 510 | /* :: */[ 511 | /* tuple */[ 512 | (function (prim, prim$1) { 513 | return prim / prim$1; 514 | }), 515 | "(/.)" 516 | ], 517 | /* :: */[ 518 | /* tuple */[ 519 | (function (prim, prim$1) { 520 | return Math.pow(prim, prim$1); 521 | }), 522 | "(**)" 523 | ], 524 | /* :: */[ 525 | /* tuple */[ 526 | (function (prim, prim$1) { 527 | return Math.atan2(prim, prim$1); 528 | }), 529 | "atan2" 530 | ], 531 | /* :: */[ 532 | /* tuple */[ 533 | Caml_float.caml_hypot_float, 534 | "hypot" 535 | ], 536 | /* :: */[ 537 | /* tuple */[ 538 | Caml_float.caml_copysign_float, 539 | "copysign" 540 | ], 541 | /* :: */[ 542 | /* tuple */[ 543 | (function (prim, prim$1) { 544 | return prim % prim$1; 545 | }), 546 | "mod_float" 547 | ], 548 | /* [] */0 549 | ] 550 | ] 551 | ] 552 | ] 553 | ] 554 | ] 555 | ] 556 | ] 557 | ]; 558 | 559 | var f10 = /* tuple */[ 560 | "float -> float -> float", 561 | f10_001 562 | ]; 563 | 564 | var f11_001 = /* :: */[ 565 | /* tuple */[ 566 | Caml_float.caml_frexp_float, 567 | "frexp" 568 | ], 569 | /* [] */0 570 | ]; 571 | 572 | var f11 = /* tuple */[ 573 | "float -> float * int", 574 | f11_001 575 | ]; 576 | 577 | var f12_001 = /* :: */[ 578 | /* tuple */[ 579 | Caml_float.caml_ldexp_float, 580 | "ldexp" 581 | ], 582 | /* [] */0 583 | ]; 584 | 585 | var f12 = /* tuple */[ 586 | "float -> int -> float", 587 | f12_001 588 | ]; 589 | 590 | var f13_001 = /* :: */[ 591 | /* tuple */[ 592 | Caml_float.caml_modf_float, 593 | "modf" 594 | ], 595 | /* [] */0 596 | ]; 597 | 598 | var f13 = /* tuple */[ 599 | "float -> float * float", 600 | f13_001 601 | ]; 602 | 603 | var f14_001 = /* :: */[ 604 | /* tuple */[ 605 | (function (prim) { 606 | return prim; 607 | }), 608 | "float" 609 | ], 610 | /* :: */[ 611 | /* tuple */[ 612 | (function (prim) { 613 | return prim; 614 | }), 615 | "float_of_int" 616 | ], 617 | /* [] */0 618 | ] 619 | ]; 620 | 621 | var f14 = /* tuple */[ 622 | "int -> float", 623 | f14_001 624 | ]; 625 | 626 | var f15_001 = /* :: */[ 627 | /* tuple */[ 628 | (function (prim) { 629 | return prim | 0; 630 | }), 631 | "truncate" 632 | ], 633 | /* :: */[ 634 | /* tuple */[ 635 | (function (prim) { 636 | return prim | 0; 637 | }), 638 | "int_of_float" 639 | ], 640 | /* [] */0 641 | ] 642 | ]; 643 | 644 | var f15 = /* tuple */[ 645 | "float -> int", 646 | f15_001 647 | ]; 648 | 649 | var f16_001 = /* :: */[ 650 | /* tuple */[ 651 | (function (prim, prim$1) { 652 | return prim + prim$1; 653 | }), 654 | "(++)" 655 | ], 656 | /* [] */0 657 | ]; 658 | 659 | var f16 = /* tuple */[ 660 | "string -> string -> string", 661 | f16_001 662 | ]; 663 | 664 | var f17_001 = /* :: */[ 665 | /* tuple */[ 666 | (function (prim) { 667 | return prim; 668 | }), 669 | "int_of_char" 670 | ], 671 | /* :: */[ 672 | /* tuple */[ 673 | (function (prim) { 674 | return prim; 675 | }), 676 | "Char.code" 677 | ], 678 | /* [] */0 679 | ] 680 | ]; 681 | 682 | var f17 = /* tuple */[ 683 | "char -> int", 684 | f17_001 685 | ]; 686 | 687 | var f18_001 = /* :: */[ 688 | /* tuple */[ 689 | Pervasives.char_of_int, 690 | "char_of_int" 691 | ], 692 | /* :: */[ 693 | /* tuple */[ 694 | Char.chr, 695 | "Char.chr" 696 | ], 697 | /* [] */0 698 | ] 699 | ]; 700 | 701 | var f18 = /* tuple */[ 702 | "int -> char", 703 | f18_001 704 | ]; 705 | 706 | var f19_001 = /* :: */[ 707 | /* tuple */[ 708 | Pervasives.string_of_bool, 709 | "string_of_bool" 710 | ], 711 | /* [] */0 712 | ]; 713 | 714 | var f19 = /* tuple */[ 715 | "bool -> string", 716 | f19_001 717 | ]; 718 | 719 | var f20_001 = /* :: */[ 720 | /* tuple */[ 721 | Pervasives.bool_of_string, 722 | "bool_of_string" 723 | ], 724 | /* [] */0 725 | ]; 726 | 727 | var f20 = /* tuple */[ 728 | "string -> bool", 729 | f20_001 730 | ]; 731 | 732 | var f21_001 = /* :: */[ 733 | /* tuple */[ 734 | (function (prim) { 735 | return String(prim); 736 | }), 737 | "string_of_int" 738 | ], 739 | /* [] */0 740 | ]; 741 | 742 | var f21 = /* tuple */[ 743 | "int -> string", 744 | f21_001 745 | ]; 746 | 747 | var f22_001 = /* :: */[ 748 | /* tuple */[ 749 | Caml_format.caml_int_of_string, 750 | "int_of_string" 751 | ], 752 | /* :: */[ 753 | /* tuple */[ 754 | (function (prim) { 755 | return prim.length; 756 | }), 757 | "String.length" 758 | ], 759 | /* [] */0 760 | ] 761 | ]; 762 | 763 | var f22 = /* tuple */[ 764 | "string -> int", 765 | f22_001 766 | ]; 767 | 768 | var f23_001 = /* :: */[ 769 | /* tuple */[ 770 | Pervasives.string_of_float, 771 | "string_of_float" 772 | ], 773 | /* [] */0 774 | ]; 775 | 776 | var f23 = /* tuple */[ 777 | "float -> string", 778 | f23_001 779 | ]; 780 | 781 | var f24_001 = /* :: */[ 782 | /* tuple */[ 783 | Caml_format.caml_float_of_string, 784 | "float_of_string" 785 | ], 786 | /* [] */0 787 | ]; 788 | 789 | var f24 = /* tuple */[ 790 | "string -> float", 791 | f24_001 792 | ]; 793 | 794 | var f25_001 = /* :: */[ 795 | /* tuple */[ 796 | (function (prim) { 797 | return prim[0]; 798 | }), 799 | "fst" 800 | ], 801 | /* [] */0 802 | ]; 803 | 804 | var f25 = /* tuple */[ 805 | "'a * 'b -> 'a", 806 | f25_001 807 | ]; 808 | 809 | var f26_001 = /* :: */[ 810 | /* tuple */[ 811 | (function (prim) { 812 | return prim[1]; 813 | }), 814 | "snd" 815 | ], 816 | /* [] */0 817 | ]; 818 | 819 | var f26 = /* tuple */[ 820 | "'a * 'b -> 'b", 821 | f26_001 822 | ]; 823 | 824 | var f27_001 = /* :: */[ 825 | /* tuple */[ 826 | Pervasives.$at, 827 | "(@)" 828 | ], 829 | /* :: */[ 830 | /* tuple */[ 831 | List.append, 832 | "List.append" 833 | ], 834 | /* :: */[ 835 | /* tuple */[ 836 | List.rev_append, 837 | "List.rev_append" 838 | ], 839 | /* [] */0 840 | ] 841 | ] 842 | ]; 843 | 844 | var f27 = /* tuple */[ 845 | "'a list -> 'a list -> 'a list", 846 | f27_001 847 | ]; 848 | 849 | var f28_001 = /* :: */[ 850 | /* tuple */[ 851 | Char.escaped, 852 | "Char.escaped" 853 | ], 854 | /* [] */0 855 | ]; 856 | 857 | var f28 = /* tuple */[ 858 | "char -> string", 859 | f28_001 860 | ]; 861 | 862 | var f29_001 = /* :: */[ 863 | /* tuple */[ 864 | Char.lowercase, 865 | "Char.lowercase" 866 | ], 867 | /* :: */[ 868 | /* tuple */[ 869 | Char.uppercase, 870 | "Char.uppercase" 871 | ], 872 | /* [] */0 873 | ] 874 | ]; 875 | 876 | var f29 = /* tuple */[ 877 | "char -> char", 878 | f29_001 879 | ]; 880 | 881 | var f30_001 = /* :: */[ 882 | /* tuple */[ 883 | Caml_string.get, 884 | "String.get" 885 | ], 886 | /* [] */0 887 | ]; 888 | 889 | var f30 = /* tuple */[ 890 | "string -> int -> char", 891 | f30_001 892 | ]; 893 | 894 | var f31_001 = /* :: */[ 895 | /* tuple */[ 896 | $$String.make, 897 | "String.make" 898 | ], 899 | /* [] */0 900 | ]; 901 | 902 | var f31 = /* tuple */[ 903 | "int -> char -> string", 904 | f31_001 905 | ]; 906 | 907 | var f32_001 = /* :: */[ 908 | /* tuple */[ 909 | $$String.init, 910 | "String.init" 911 | ], 912 | /* [] */0 913 | ]; 914 | 915 | var f32 = /* tuple */[ 916 | "int -> (int -> char) -> string", 917 | f32_001 918 | ]; 919 | 920 | var f33_001 = /* :: */[ 921 | /* tuple */[ 922 | $$String.sub, 923 | "String.sub" 924 | ], 925 | /* [] */0 926 | ]; 927 | 928 | var f33 = /* tuple */[ 929 | "string -> int -> int -> string", 930 | f33_001 931 | ]; 932 | 933 | var f34_001 = /* :: */[ 934 | /* tuple */[ 935 | $$String.mapi, 936 | "String.mapi" 937 | ], 938 | /* [] */0 939 | ]; 940 | 941 | var f34 = /* tuple */[ 942 | "(int -> char -> char) -> string -> string", 943 | f34_001 944 | ]; 945 | 946 | var f35_001 = /* :: */[ 947 | /* tuple */[ 948 | $$String.map, 949 | "String.map" 950 | ], 951 | /* [] */0 952 | ]; 953 | 954 | var f35 = /* tuple */[ 955 | "(char -> char) -> string -> string", 956 | f35_001 957 | ]; 958 | 959 | var f36_001 = /* :: */[ 960 | /* tuple */[ 961 | $$String.trim, 962 | "String.trim" 963 | ], 964 | /* :: */[ 965 | /* tuple */[ 966 | $$String.escaped, 967 | "String.escaped" 968 | ], 969 | /* :: */[ 970 | /* tuple */[ 971 | $$String.uppercase, 972 | "String.uppercase" 973 | ], 974 | /* :: */[ 975 | /* tuple */[ 976 | $$String.lowercase, 977 | "String.lowercase" 978 | ], 979 | /* :: */[ 980 | /* tuple */[ 981 | $$String.capitalize, 982 | "String.capitalize" 983 | ], 984 | /* :: */[ 985 | /* tuple */[ 986 | $$String.uncapitalize, 987 | "String.uncapitalize" 988 | ], 989 | /* [] */0 990 | ] 991 | ] 992 | ] 993 | ] 994 | ] 995 | ]; 996 | 997 | var f36 = /* tuple */[ 998 | "string -> string", 999 | f36_001 1000 | ]; 1001 | 1002 | var f37_001 = /* :: */[ 1003 | /* tuple */[ 1004 | $$String.index, 1005 | "String.index" 1006 | ], 1007 | /* :: */[ 1008 | /* tuple */[ 1009 | $$String.rindex, 1010 | "String.rindex" 1011 | ], 1012 | /* [] */0 1013 | ] 1014 | ]; 1015 | 1016 | var f37 = /* tuple */[ 1017 | "string -> char -> int", 1018 | f37_001 1019 | ]; 1020 | 1021 | var f38_001 = /* :: */[ 1022 | /* tuple */[ 1023 | $$String.index_from, 1024 | "String.index_from" 1025 | ], 1026 | /* :: */[ 1027 | /* tuple */[ 1028 | $$String.rindex_from, 1029 | "String.rindex_from" 1030 | ], 1031 | /* [] */0 1032 | ] 1033 | ]; 1034 | 1035 | var f38 = /* tuple */[ 1036 | "string -> int -> char -> int", 1037 | f38_001 1038 | ]; 1039 | 1040 | var f39_001 = /* :: */[ 1041 | /* tuple */[ 1042 | $$String.contains, 1043 | "String.contains" 1044 | ], 1045 | /* [] */0 1046 | ]; 1047 | 1048 | var f39 = /* tuple */[ 1049 | "string -> char -> bool", 1050 | f39_001 1051 | ]; 1052 | 1053 | var f40_001 = /* :: */[ 1054 | /* tuple */[ 1055 | $$String.contains_from, 1056 | "String.contains_from" 1057 | ], 1058 | /* :: */[ 1059 | /* tuple */[ 1060 | $$String.rcontains_from, 1061 | "String.rcontains_from" 1062 | ], 1063 | /* [] */0 1064 | ] 1065 | ]; 1066 | 1067 | var f40 = /* tuple */[ 1068 | "string -> int -> char -> bool", 1069 | f40_001 1070 | ]; 1071 | 1072 | var f41_001 = /* :: */[ 1073 | /* tuple */[ 1074 | List.length, 1075 | "List.length" 1076 | ], 1077 | /* [] */0 1078 | ]; 1079 | 1080 | var f41 = /* tuple */[ 1081 | "'a list -> int", 1082 | f41_001 1083 | ]; 1084 | 1085 | var f42_001 = /* :: */[ 1086 | /* tuple */[ 1087 | List.hd, 1088 | "List.hd" 1089 | ], 1090 | /* [] */0 1091 | ]; 1092 | 1093 | var f42 = /* tuple */[ 1094 | "'a list -> 'a", 1095 | f42_001 1096 | ]; 1097 | 1098 | var f43_001 = /* :: */[ 1099 | /* tuple */[ 1100 | List.tl, 1101 | "List.tl" 1102 | ], 1103 | /* :: */[ 1104 | /* tuple */[ 1105 | List.rev, 1106 | "List.rev" 1107 | ], 1108 | /* [] */0 1109 | ] 1110 | ]; 1111 | 1112 | var f43 = /* tuple */[ 1113 | "'a list -> 'a list", 1114 | f43_001 1115 | ]; 1116 | 1117 | var f44_001 = /* :: */[ 1118 | /* tuple */[ 1119 | List.nth, 1120 | "List.nth" 1121 | ], 1122 | /* [] */0 1123 | ]; 1124 | 1125 | var f44 = /* tuple */[ 1126 | "'a list -> int -> 'a", 1127 | f44_001 1128 | ]; 1129 | 1130 | var f45_001 = /* :: */[ 1131 | /* tuple */[ 1132 | List.concat, 1133 | "List.concat" 1134 | ], 1135 | /* :: */[ 1136 | /* tuple */[ 1137 | List.flatten, 1138 | "List.flatten" 1139 | ], 1140 | /* [] */0 1141 | ] 1142 | ]; 1143 | 1144 | var f45 = /* tuple */[ 1145 | "'a list list -> 'a list", 1146 | f45_001 1147 | ]; 1148 | 1149 | var f46_001 = /* :: */[ 1150 | /* tuple */[ 1151 | List.map, 1152 | "List.map" 1153 | ], 1154 | /* :: */[ 1155 | /* tuple */[ 1156 | List.rev_map, 1157 | "List.rev_map" 1158 | ], 1159 | /* [] */0 1160 | ] 1161 | ]; 1162 | 1163 | var f46 = /* tuple */[ 1164 | "('a -> 'b) -> 'a list -> 'b list", 1165 | f46_001 1166 | ]; 1167 | 1168 | var f47_001 = /* :: */[ 1169 | /* tuple */[ 1170 | List.mapi, 1171 | "List.mapi" 1172 | ], 1173 | /* [] */0 1174 | ]; 1175 | 1176 | var f47 = /* tuple */[ 1177 | "(int -> 'a -> 'b) -> 'a list -> 'b list", 1178 | f47_001 1179 | ]; 1180 | 1181 | var f48_001 = /* :: */[ 1182 | /* tuple */[ 1183 | List.fold_left, 1184 | "List.fold_left" 1185 | ], 1186 | /* [] */0 1187 | ]; 1188 | 1189 | var f48 = /* tuple */[ 1190 | "('a -> 'b -> 'a) -> 'a -> 'b list -> 'a", 1191 | f48_001 1192 | ]; 1193 | 1194 | var f49_001 = /* :: */[ 1195 | /* tuple */[ 1196 | List.fold_right, 1197 | "List.fold_right" 1198 | ], 1199 | /* [] */0 1200 | ]; 1201 | 1202 | var f49 = /* tuple */[ 1203 | "('a -> 'b -> 'b) -> 'a list -> 'b -> 'b", 1204 | f49_001 1205 | ]; 1206 | 1207 | var f50_001 = /* :: */[ 1208 | /* tuple */[ 1209 | List.map2, 1210 | "List.map2" 1211 | ], 1212 | /* :: */[ 1213 | /* tuple */[ 1214 | List.rev_map2, 1215 | "List.rev_map2" 1216 | ], 1217 | /* [] */0 1218 | ] 1219 | ]; 1220 | 1221 | var f50 = /* tuple */[ 1222 | "('a -> 'b -> 'c) -> 'a list -> 'b list -> 'c list", 1223 | f50_001 1224 | ]; 1225 | 1226 | var f51_001 = /* :: */[ 1227 | /* tuple */[ 1228 | List.fold_left2, 1229 | "List.fold_left2" 1230 | ], 1231 | /* [] */0 1232 | ]; 1233 | 1234 | var f51 = /* tuple */[ 1235 | "('a -> 'b -> 'c -> 'a) -> 'a -> 'b list -> 'c list -> 'a", 1236 | f51_001 1237 | ]; 1238 | 1239 | var f52_001 = /* :: */[ 1240 | /* tuple */[ 1241 | List.fold_right2, 1242 | "List.fold_right2" 1243 | ], 1244 | /* [] */0 1245 | ]; 1246 | 1247 | var f52 = /* tuple */[ 1248 | "('a -> 'b -> 'c -> 'c) -> 'a list -> 'b list -> 'c -> 'c", 1249 | f52_001 1250 | ]; 1251 | 1252 | var f53_001 = /* :: */[ 1253 | /* tuple */[ 1254 | List.exists, 1255 | "List.exists" 1256 | ], 1257 | /* [] */0 1258 | ]; 1259 | 1260 | var f53 = /* tuple */[ 1261 | "('a -> bool) -> 'a list -> bool", 1262 | f53_001 1263 | ]; 1264 | 1265 | var f54_001 = /* :: */[ 1266 | /* tuple */[ 1267 | List.exists2, 1268 | "List.exists2" 1269 | ], 1270 | /* [] */0 1271 | ]; 1272 | 1273 | var f54 = /* tuple */[ 1274 | "('a -> 'b -> bool) -> 'a list -> 'b list -> bool", 1275 | f54_001 1276 | ]; 1277 | 1278 | var f55_001 = /* :: */[ 1279 | /* tuple */[ 1280 | List.mem, 1281 | "List.mem" 1282 | ], 1283 | /* :: */[ 1284 | /* tuple */[ 1285 | List.memq, 1286 | "List.memq" 1287 | ], 1288 | /* [] */0 1289 | ] 1290 | ]; 1291 | 1292 | var f55 = /* tuple */[ 1293 | "'a -> 'a list -> bool", 1294 | f55_001 1295 | ]; 1296 | 1297 | var f56_001 = /* :: */[ 1298 | /* tuple */[ 1299 | List.find, 1300 | "List.find" 1301 | ], 1302 | /* [] */0 1303 | ]; 1304 | 1305 | var f56 = /* tuple */[ 1306 | "('a -> bool) -> 'a list -> 'a", 1307 | f56_001 1308 | ]; 1309 | 1310 | var f57_001 = /* :: */[ 1311 | /* tuple */[ 1312 | List.filter, 1313 | "List.filter" 1314 | ], 1315 | /* :: */[ 1316 | /* tuple */[ 1317 | List.find_all, 1318 | "List.find_all" 1319 | ], 1320 | /* [] */0 1321 | ] 1322 | ]; 1323 | 1324 | var f57 = /* tuple */[ 1325 | "('a -> bool) -> 'a list -> 'a list", 1326 | f57_001 1327 | ]; 1328 | 1329 | var f58_001 = /* :: */[ 1330 | /* tuple */[ 1331 | List.sort, 1332 | "List.sort" 1333 | ], 1334 | /* :: */[ 1335 | /* tuple */[ 1336 | List.stable_sort, 1337 | "List.stable_sort" 1338 | ], 1339 | /* :: */[ 1340 | /* tuple */[ 1341 | List.fast_sort, 1342 | "List.fast_sort" 1343 | ], 1344 | /* :: */[ 1345 | /* tuple */[ 1346 | List.sort_uniq, 1347 | "List.sort_uniq" 1348 | ], 1349 | /* [] */0 1350 | ] 1351 | ] 1352 | ] 1353 | ]; 1354 | 1355 | var f58 = /* tuple */[ 1356 | "('a -> 'a -> int) -> 'a list -> 'a list", 1357 | f58_001 1358 | ]; 1359 | 1360 | var f59_001 = /* :: */[ 1361 | /* tuple */[ 1362 | List.merge, 1363 | "List.merge" 1364 | ], 1365 | /* [] */0 1366 | ]; 1367 | 1368 | var f59 = /* tuple */[ 1369 | "('a -> 'a -> int) -> 'a list -> 'a list -> 'a list", 1370 | f59_001 1371 | ]; 1372 | 1373 | var f60_001 = /* :: */[ 1374 | /* tuple */[ 1375 | (function (prim) { 1376 | return prim.length; 1377 | }), 1378 | "Array.length" 1379 | ], 1380 | /* [] */0 1381 | ]; 1382 | 1383 | var f60 = /* tuple */[ 1384 | "'a array -> int", 1385 | f60_001 1386 | ]; 1387 | 1388 | var f61_001 = /* :: */[ 1389 | /* tuple */[ 1390 | Caml_array.caml_array_get, 1391 | "Array.get" 1392 | ], 1393 | /* [] */0 1394 | ]; 1395 | 1396 | var f61 = /* tuple */[ 1397 | "'a array -> int -> 'a", 1398 | f61_001 1399 | ]; 1400 | 1401 | var f62_001 = /* :: */[ 1402 | /* tuple */[ 1403 | Caml_array.caml_array_set, 1404 | "Array.set" 1405 | ], 1406 | /* [] */0 1407 | ]; 1408 | 1409 | var f62 = /* tuple */[ 1410 | "'a array -> int -> 'a -> unit", 1411 | f62_001 1412 | ]; 1413 | 1414 | var f63_001 = /* :: */[ 1415 | /* tuple */[ 1416 | Caml_array.caml_make_vect, 1417 | "Array.make" 1418 | ], 1419 | /* [] */0 1420 | ]; 1421 | 1422 | var f63 = /* tuple */[ 1423 | "int -> 'a -> 'a array", 1424 | f63_001 1425 | ]; 1426 | 1427 | var f64_001 = /* :: */[ 1428 | /* tuple */[ 1429 | $$Array.init, 1430 | "Array.init" 1431 | ], 1432 | /* [] */0 1433 | ]; 1434 | 1435 | var f64 = /* tuple */[ 1436 | "int -> (int -> 'a) -> 'a array", 1437 | f64_001 1438 | ]; 1439 | 1440 | var f65_001 = /* :: */[ 1441 | /* tuple */[ 1442 | $$Array.make_matrix, 1443 | "Array.make_matrix" 1444 | ], 1445 | /* [] */0 1446 | ]; 1447 | 1448 | var f65 = /* tuple */[ 1449 | "int -> int -> 'a -> 'a array array", 1450 | f65_001 1451 | ]; 1452 | 1453 | var f66_001 = /* :: */[ 1454 | /* tuple */[ 1455 | $$Array.append, 1456 | "Array.append" 1457 | ], 1458 | /* [] */0 1459 | ]; 1460 | 1461 | var f66 = /* tuple */[ 1462 | "'a array -> 'a array -> 'a array", 1463 | f66_001 1464 | ]; 1465 | 1466 | var f67_001 = /* :: */[ 1467 | /* tuple */[ 1468 | $$Array.concat, 1469 | "Array.concat" 1470 | ], 1471 | /* [] */0 1472 | ]; 1473 | 1474 | var f67 = /* tuple */[ 1475 | "'a array list -> 'a array", 1476 | f67_001 1477 | ]; 1478 | 1479 | var f68_001 = /* :: */[ 1480 | /* tuple */[ 1481 | $$Array.sub, 1482 | "Array.sub" 1483 | ], 1484 | /* [] */0 1485 | ]; 1486 | 1487 | var f68 = /* tuple */[ 1488 | "'a array -> int -> int -> 'a array", 1489 | f68_001 1490 | ]; 1491 | 1492 | var f69_001 = /* :: */[ 1493 | /* tuple */[ 1494 | $$Array.copy, 1495 | "Array.copy" 1496 | ], 1497 | /* [] */0 1498 | ]; 1499 | 1500 | var f69 = /* tuple */[ 1501 | "'a array -> 'a array", 1502 | f69_001 1503 | ]; 1504 | 1505 | var f70_001 = /* :: */[ 1506 | /* tuple */[ 1507 | $$Array.to_list, 1508 | "Array.to_list" 1509 | ], 1510 | /* [] */0 1511 | ]; 1512 | 1513 | var f70 = /* tuple */[ 1514 | "'a array -> 'a list", 1515 | f70_001 1516 | ]; 1517 | 1518 | var f71_001 = /* :: */[ 1519 | /* tuple */[ 1520 | $$Array.of_list, 1521 | "Array.of_list" 1522 | ], 1523 | /* [] */0 1524 | ]; 1525 | 1526 | var f71 = /* tuple */[ 1527 | "'a list -> 'a array", 1528 | f71_001 1529 | ]; 1530 | 1531 | var f72_001 = /* :: */[ 1532 | /* tuple */[ 1533 | $$Array.map, 1534 | "Array.map" 1535 | ], 1536 | /* [] */0 1537 | ]; 1538 | 1539 | var f72 = /* tuple */[ 1540 | "('a -> 'b) -> 'a array -> 'b array", 1541 | f72_001 1542 | ]; 1543 | 1544 | var f73_001 = /* :: */[ 1545 | /* tuple */[ 1546 | $$Array.mapi, 1547 | "Array.mapi" 1548 | ], 1549 | /* [] */0 1550 | ]; 1551 | 1552 | var f73 = /* tuple */[ 1553 | "(int -> 'a -> 'b) -> 'a array -> 'b array", 1554 | f73_001 1555 | ]; 1556 | 1557 | var f74_001 = /* :: */[ 1558 | /* tuple */[ 1559 | $$Array.fold_left, 1560 | "Array.fold_left" 1561 | ], 1562 | /* [] */0 1563 | ]; 1564 | 1565 | var f74 = /* tuple */[ 1566 | "('a -> 'b -> 'a) -> 'a -> 'b array -> 'a", 1567 | f74_001 1568 | ]; 1569 | 1570 | var f75_001 = /* :: */[ 1571 | /* tuple */[ 1572 | $$Array.fold_right, 1573 | "Array.fold_right" 1574 | ], 1575 | /* [] */0 1576 | ]; 1577 | 1578 | var f75 = /* tuple */[ 1579 | "('a -> 'b -> 'b) -> 'a array -> 'b -> 'b", 1580 | f75_001 1581 | ]; 1582 | 1583 | var f76_001 = /* :: */[ 1584 | /* tuple */[ 1585 | Caml_array.caml_make_float_vect, 1586 | "Array.make_float" 1587 | ], 1588 | /* [] */0 1589 | ]; 1590 | 1591 | var f76 = /* tuple */[ 1592 | "int -> float array", 1593 | f76_001 1594 | ]; 1595 | 1596 | var f77_001 = /* :: */[ 1597 | /* tuple */[ 1598 | $$Array.sort, 1599 | "Array.sort" 1600 | ], 1601 | /* :: */[ 1602 | /* tuple */[ 1603 | $$Array.stable_sort, 1604 | "Array.stable_sort" 1605 | ], 1606 | /* :: */[ 1607 | /* tuple */[ 1608 | $$Array.fast_sort, 1609 | "Array.fast_sort" 1610 | ], 1611 | /* [] */0 1612 | ] 1613 | ] 1614 | ]; 1615 | 1616 | var f77 = /* tuple */[ 1617 | "('a -> 'a -> int) -> 'a array -> unit", 1618 | f77_001 1619 | ]; 1620 | 1621 | exports.f0 = f0; 1622 | exports.f1 = f1; 1623 | exports.f2 = f2; 1624 | exports.f3 = f3; 1625 | exports.f4 = f4; 1626 | exports.f5 = f5; 1627 | exports.f6 = f6; 1628 | exports.f7 = f7; 1629 | exports.f8 = f8; 1630 | exports.f9 = f9; 1631 | exports.f10 = f10; 1632 | exports.f11 = f11; 1633 | exports.f12 = f12; 1634 | exports.f13 = f13; 1635 | exports.f14 = f14; 1636 | exports.f15 = f15; 1637 | exports.f16 = f16; 1638 | exports.f17 = f17; 1639 | exports.f18 = f18; 1640 | exports.f19 = f19; 1641 | exports.f20 = f20; 1642 | exports.f21 = f21; 1643 | exports.f22 = f22; 1644 | exports.f23 = f23; 1645 | exports.f24 = f24; 1646 | exports.f25 = f25; 1647 | exports.f26 = f26; 1648 | exports.f27 = f27; 1649 | exports.f28 = f28; 1650 | exports.f29 = f29; 1651 | exports.f30 = f30; 1652 | exports.f31 = f31; 1653 | exports.f32 = f32; 1654 | exports.f33 = f33; 1655 | exports.f34 = f34; 1656 | exports.f35 = f35; 1657 | exports.f36 = f36; 1658 | exports.f37 = f37; 1659 | exports.f38 = f38; 1660 | exports.f39 = f39; 1661 | exports.f40 = f40; 1662 | exports.f41 = f41; 1663 | exports.f42 = f42; 1664 | exports.f43 = f43; 1665 | exports.f44 = f44; 1666 | exports.f45 = f45; 1667 | exports.f46 = f46; 1668 | exports.f47 = f47; 1669 | exports.f48 = f48; 1670 | exports.f49 = f49; 1671 | exports.f50 = f50; 1672 | exports.f51 = f51; 1673 | exports.f52 = f52; 1674 | exports.f53 = f53; 1675 | exports.f54 = f54; 1676 | exports.f55 = f55; 1677 | exports.f56 = f56; 1678 | exports.f57 = f57; 1679 | exports.f58 = f58; 1680 | exports.f59 = f59; 1681 | exports.f60 = f60; 1682 | exports.f61 = f61; 1683 | exports.f62 = f62; 1684 | exports.f63 = f63; 1685 | exports.f64 = f64; 1686 | exports.f65 = f65; 1687 | exports.f66 = f66; 1688 | exports.f67 = f67; 1689 | exports.f68 = f68; 1690 | exports.f69 = f69; 1691 | exports.f70 = f70; 1692 | exports.f71 = f71; 1693 | exports.f72 = f72; 1694 | exports.f73 = f73; 1695 | exports.f74 = f74; 1696 | exports.f75 = f75; 1697 | exports.f76 = f76; 1698 | exports.f77 = f77; 1699 | /* No side effect */ 1700 | -------------------------------------------------------------------------------- /packages/resuggest/src/index.ts: -------------------------------------------------------------------------------- 1 | import suggest from "./suggest"; 2 | import reasonExpToJs from "./reasonExpToJs"; 3 | import { CompilationResult, Input } from "./types"; 4 | 5 | var compilationCache = new Map(); 6 | 7 | function memoizedReasonExpToJs(exp: string): CompilationResult { 8 | if (compilationCache.has(exp)) { 9 | return compilationCache.get(exp); 10 | } 11 | 12 | let result = reasonExpToJs(exp); 13 | compilationCache.set(exp, result); 14 | return result; 15 | } 16 | 17 | export default function(inputs: Input[], output: string) { 18 | const compiledInputs = inputs.map(input => ({ 19 | expression: memoizedReasonExpToJs(input.code), 20 | expectedMutation: 21 | input.expectedMutation !== null 22 | ? memoizedReasonExpToJs(input.expectedMutation) 23 | : null 24 | })); 25 | const compiledOutput = memoizedReasonExpToJs(output); 26 | return { 27 | inputs: compiledInputs, 28 | output: compiledOutput, 29 | suggestions: suggest(compiledInputs, compiledOutput) 30 | }; 31 | } 32 | -------------------------------------------------------------------------------- /packages/resuggest/src/isTypeAssignable.ts: -------------------------------------------------------------------------------- 1 | import { AstType, AstTypeKind } from "./types"; 2 | 3 | export default function isTypeAssignable( 4 | left: AstType, 5 | right: AstType, 6 | genericsMap: { [id: string]: AstType } = {} 7 | ): boolean { 8 | if ( 9 | left.kind === AstTypeKind.Generic && 10 | right.kind === AstTypeKind.Generic && 11 | left.type === right.type 12 | ) { 13 | return true; 14 | } 15 | 16 | if (left.kind === AstTypeKind.Generic) { 17 | if (genericsMap[left.type] === undefined) { 18 | genericsMap[left.type] = right; 19 | return true; 20 | } else { 21 | return isTypeAssignable(genericsMap[left.type], right, genericsMap); 22 | } 23 | } 24 | 25 | if (right.kind === AstTypeKind.Generic) { 26 | if (genericsMap[right.type] === undefined) { 27 | genericsMap[right.type] = left; 28 | return true; 29 | } else { 30 | return isTypeAssignable(left, genericsMap[right.type], genericsMap); 31 | } 32 | } 33 | 34 | if (left.kind !== right.kind) { 35 | return false; 36 | } 37 | 38 | if (left.kind === AstTypeKind.Simple && right.kind === AstTypeKind.Simple) { 39 | return left.type === right.type; 40 | } 41 | 42 | if (left.kind === AstTypeKind.List && right.kind === AstTypeKind.List) { 43 | return isTypeAssignable(left.itemType, right.itemType, genericsMap); 44 | } 45 | 46 | if (left.kind === AstTypeKind.Array && right.kind === AstTypeKind.Array) { 47 | return isTypeAssignable(left.itemType, right.itemType, genericsMap); 48 | } 49 | 50 | if (left.kind === AstTypeKind.Func && right.kind === AstTypeKind.Func) { 51 | return ( 52 | isTypeAssignable(left.input, right.input, genericsMap) && 53 | isTypeAssignable(left.output, right.output, genericsMap) 54 | ); 55 | } 56 | 57 | if (left.kind === AstTypeKind.Unit && right.kind === AstTypeKind.Unit) { 58 | return true; 59 | } 60 | 61 | if (left.kind === AstTypeKind.Tuple && right.kind === AstTypeKind.Tuple) { 62 | return ( 63 | left.types.length === right.types.length && 64 | left.types.every((leftSubType, index) => 65 | isTypeAssignable(leftSubType, right.types[index], genericsMap) 66 | ) 67 | ); 68 | } 69 | 70 | throw new Error(`Unsupported type kind ${left.kind}`); 71 | } 72 | -------------------------------------------------------------------------------- /packages/resuggest/src/makeExample.ts: -------------------------------------------------------------------------------- 1 | import { ValidCompilationResult, CompiledInput, AstTypeKind } from "./types"; 2 | 3 | function functionNameToDisplayUsage(name: string) { 4 | if (name[0] === "(") { 5 | return name.substr(1, name.length - 2); 6 | } 7 | 8 | return name; 9 | } 10 | 11 | function comparison(a: string, b: string): string { 12 | return `${a} == ${b}`; 13 | } 14 | 15 | function functionCallWithNInputs(functionName: string, inputs: string[]) { 16 | return ( 17 | functionNameToDisplayUsage(functionName) + 18 | "(" + 19 | inputs.join(", ").concat(")") 20 | ); 21 | } 22 | 23 | function applyOperatorWithOneInput(functionName: string, input: string) { 24 | return `${functionNameToDisplayUsage(functionName)} ${input}`; 25 | } 26 | 27 | function applyOperatorWithTwoInputs(functionName: string, inputs: string[]) { 28 | return `${inputs[0]} ${functionNameToDisplayUsage(functionName)} ${ 29 | inputs[1] 30 | }`; 31 | } 32 | 33 | function functionCall(functionName: string, inputs: string[]): string { 34 | switch (functionName) { 35 | case "(~-)": 36 | case "(~+)": 37 | case "(~-.)": 38 | case "(~+.)": 39 | return applyOperatorWithOneInput(functionName, inputs[0]); 40 | case "(+)": 41 | case "(-)": 42 | case "(*)": 43 | case "(/)": 44 | case "(==)": 45 | case "(===)": 46 | case "(!=)": 47 | case "(!==)": 48 | case "(<=)": 49 | case "(>=)": 50 | case "(<)": 51 | case "(>)": 52 | case "(!)": 53 | case "(&&)": 54 | case "(||)": 55 | case "(@)": 56 | case "(|>)": 57 | case "(@@)": 58 | case "(mod)": 59 | case "(land)": 60 | case "(lor)": 61 | case "(lxor)": 62 | case "(lsl)": 63 | case "(lsr)": 64 | case "(asr)": 65 | case "(+.)": 66 | case "(-.)": 67 | case "(*.)": 68 | case "(/.)": 69 | case "(**)": 70 | case "(++)": 71 | return applyOperatorWithTwoInputs(functionName, inputs); 72 | default: 73 | return functionCallWithNInputs(functionName, inputs); 74 | } 75 | } 76 | 77 | function wrapWithJsLog(str: string) { 78 | return `Js.log(${str}); /* true */`; 79 | } 80 | 81 | function assignment(argName: string, value: string): string { 82 | return `let ${argName} = ${value};`; 83 | } 84 | 85 | function assignmentsForInputsWithMutation(inputs: CompiledInput[]) { 86 | return inputs 87 | .map((input, index) => { 88 | if (input.expectedMutation === null) { 89 | return null; 90 | } 91 | return assignment("arg" + (index + 1), input.expression.code); 92 | }) 93 | .filter(str => str !== null); 94 | } 95 | 96 | function inputsOrPreviousAssigments(inputs: CompiledInput[]) { 97 | return inputs.map((input, index) => { 98 | if (input.expectedMutation === null) { 99 | return input.expression.code; 100 | } else { 101 | return "arg" + (index + 1); 102 | } 103 | }); 104 | } 105 | 106 | function compareExpectedMutation(inputs: CompiledInput[]) { 107 | return inputs 108 | .map((input, index) => { 109 | if (input.expectedMutation === null) { 110 | return null; 111 | } 112 | return wrapWithJsLog( 113 | comparison("arg" + (index + 1), input.expectedMutation.code) 114 | ); 115 | }) 116 | .filter(str => str !== null); 117 | } 118 | 119 | export default function makeExample( 120 | functionName: string, 121 | inputs: CompiledInput[], 122 | output: ValidCompilationResult 123 | ): string { 124 | const assignments = assignmentsForInputsWithMutation(inputs); 125 | let fnCall = functionCall(functionName, inputsOrPreviousAssigments(inputs)); 126 | 127 | if (output.type.kind !== AstTypeKind.Unit) { 128 | fnCall = wrapWithJsLog(comparison(fnCall, output.code)); 129 | } else { 130 | fnCall = fnCall + ";"; 131 | } 132 | 133 | return assignments 134 | .concat(fnCall) 135 | .concat(compareExpectedMutation(inputs)) 136 | .join("\n"); 137 | } 138 | -------------------------------------------------------------------------------- /packages/resuggest/src/parseType.ts: -------------------------------------------------------------------------------- 1 | import { AstType, AstTypeKind, AstSimpleType } from "./types"; 2 | 3 | type Token = { 4 | kind: string; 5 | value: null | string; 6 | }; 7 | 8 | type TokenStream = { 9 | next: () => Token; 10 | }; 11 | 12 | const simpleTypes = ["bool", "int", "float", "string", "char"]; 13 | 14 | export const tokenKinds = { 15 | simple: "simple", 16 | list: "list", 17 | array: "array", 18 | arrow: "arrow", 19 | generic: "generic", 20 | star: "star", 21 | unit: "unit", 22 | openParenthesis: "openParenthesis", 23 | closeParenthesis: "closeParenthesis" 24 | }; 25 | 26 | export function tokenStream(str: string) { 27 | const words: string[] = []; 28 | let lastEnd = 0; 29 | 30 | for (var i = 0; i < str.length; i++) { 31 | switch (str[i]) { 32 | case " ": 33 | words.push(str.substring(lastEnd, i)); 34 | lastEnd = i + 1; 35 | break; 36 | case "(": 37 | words.push("("); 38 | lastEnd = i + 1; 39 | break; 40 | case ")": 41 | words.push(str.substring(lastEnd, i)); 42 | words.push(str[i]); 43 | i++; 44 | lastEnd = i + 1; 45 | break; 46 | default: 47 | break; 48 | } 49 | } 50 | words.push(str.substring(lastEnd, str.length)); 51 | 52 | let current: any = null; 53 | let pos = 0; 54 | 55 | function isSimpleType(word: string): boolean { 56 | return simpleTypes.includes(word); 57 | } 58 | 59 | function readNext() { 60 | if (pos === words.length) { 61 | return null; 62 | } 63 | const word = words[pos++]; 64 | if (isSimpleType(word)) { 65 | return { kind: tokenKinds.simple, value: word }; 66 | } else if (word === "list") { 67 | return { kind: tokenKinds.list }; 68 | } else if (word === "array") { 69 | return { kind: tokenKinds.array }; 70 | } else if (word === "->") { 71 | return { kind: tokenKinds.arrow }; 72 | } else if (word[0] === "'") { 73 | return { kind: tokenKinds.generic, value: word }; 74 | } else if (word === "(") { 75 | return { kind: tokenKinds.openParenthesis }; 76 | } else if (word === ")") { 77 | return { kind: tokenKinds.closeParenthesis }; 78 | } else if (word === "*") { 79 | return { kind: tokenKinds.star }; 80 | } else if (word === "unit") { 81 | return { kind: tokenKinds.unit }; 82 | } 83 | 84 | throw new Error("Unkown word: '" + word + "' words: " + words); 85 | } 86 | 87 | function peek() { 88 | return current || (current = readNext()); 89 | } 90 | 91 | return { 92 | peek: peek, 93 | next: () => { 94 | var tok = current; 95 | current = null; 96 | return tok || readNext(); 97 | } 98 | }; 99 | } 100 | 101 | function getLastOrDefault(arr: T[]): T { 102 | if (arr.length === 0) { 103 | return null; 104 | } 105 | return arr[arr.length - 1]; 106 | } 107 | 108 | const priorityMap = { 109 | [tokenKinds.openParenthesis]: 0, 110 | [tokenKinds.arrow]: 1, 111 | [tokenKinds.star]: 2, 112 | [tokenKinds.list]: 3, 113 | [tokenKinds.array]: 3 114 | }; 115 | 116 | function hasLowerPrecedenceThanTopOperator( 117 | operatorsStack: Token[], 118 | token: Token 119 | ): boolean { 120 | let topOperator = getLastOrDefault(operatorsStack); 121 | if (topOperator === null) { 122 | return false; 123 | } 124 | 125 | return priorityMap[topOperator.kind] > priorityMap[token.kind]; 126 | } 127 | 128 | export function createPostfixExpression(tokenStream: TokenStream) { 129 | let operatorStack = []; 130 | let postfix = []; 131 | 132 | let token = null; 133 | 134 | while ((token = tokenStream.next()) != null) { 135 | switch (token.kind) { 136 | case tokenKinds.simple: 137 | case tokenKinds.generic: 138 | case tokenKinds.unit: 139 | postfix.push(token); 140 | break; 141 | case tokenKinds.star: 142 | case tokenKinds.arrow: 143 | case tokenKinds.list: 144 | case tokenKinds.array: 145 | while (hasLowerPrecedenceThanTopOperator(operatorStack, token)) { 146 | postfix.push(operatorStack.pop()); 147 | } 148 | operatorStack.push(token); 149 | break; 150 | case tokenKinds.openParenthesis: 151 | operatorStack.push(token); 152 | break; 153 | case tokenKinds.closeParenthesis: 154 | let operator = null; 155 | while ( 156 | (operator = operatorStack.pop()).kind !== tokenKinds.openParenthesis 157 | ) { 158 | postfix.push(operator); 159 | } 160 | break; 161 | default: 162 | throw new Error("Unknown token: " + JSON.stringify(token)); 163 | } 164 | 165 | // console.log( 166 | // "Token: ", 167 | // token, 168 | // "\nOps: ", 169 | // operatorStack, 170 | // "\nPostfix: ", 171 | // postfix 172 | // ); 173 | } 174 | 175 | while ((token = operatorStack.pop()) != null) { 176 | postfix.push(token); 177 | } 178 | 179 | return postfix; 180 | } 181 | 182 | function makeType(tokenStream: TokenStream) { 183 | let postfix = createPostfixExpression(tokenStream); 184 | 185 | let types: AstType[] = []; 186 | 187 | for (let token of postfix) { 188 | switch (token.kind) { 189 | case tokenKinds.unit: 190 | types.push({ kind: AstTypeKind.Unit }); 191 | break; 192 | case tokenKinds.simple: 193 | types.push({ kind: AstTypeKind.Simple, type: token.value }); 194 | break; 195 | case tokenKinds.generic: 196 | types.push({ kind: AstTypeKind.Generic, type: token.value }); 197 | break; 198 | case tokenKinds.star: 199 | const previousType = types.pop(); 200 | if (previousType.kind === AstTypeKind.Tuple) { 201 | previousType.types.unshift(types.pop()); 202 | types.push(previousType); 203 | } else { 204 | const tupleTypes = []; 205 | tupleTypes.unshift(previousType); 206 | tupleTypes.unshift(types.pop()); 207 | types.push({ 208 | kind: AstTypeKind.Tuple, 209 | types: tupleTypes 210 | }); 211 | } 212 | break; 213 | case tokenKinds.arrow: 214 | types.push({ 215 | kind: AstTypeKind.Func, 216 | output: types.pop(), 217 | input: types.pop() 218 | }); 219 | break; 220 | case tokenKinds.list: 221 | types.push({ 222 | kind: AstTypeKind.List, 223 | itemType: types.pop() 224 | }); 225 | break; 226 | case tokenKinds.array: 227 | types.push({ 228 | kind: AstTypeKind.Array, 229 | itemType: types.pop() 230 | }); 231 | break; 232 | default: 233 | throw new Error("Unknown token: " + JSON.stringify(token)); 234 | } 235 | } 236 | 237 | return types[0]; 238 | } 239 | 240 | // Parse type extracted from compilation error 241 | // The Shunting Yard Algorithm: http://www.oxfordmathcenter.com/drupal7/node/628 242 | // 3.9.3. Postfix Evaluation: http://interactivepython.org/runestone/static/pythonds/BasicDS/InfixPrefixandPostfixExpressions.html 243 | export default function parseType(str: string): AstType { 244 | return makeType(tokenStream(str)); 245 | } 246 | -------------------------------------------------------------------------------- /packages/resuggest/src/reasonExpToJs.ts: -------------------------------------------------------------------------------- 1 | import { CompilationResult } from "./types"; 2 | import parseType from "./parseType"; 3 | 4 | function wrapInExports(code: string): string { 5 | return `(function(exports) {${code}})(window.exports = {})`; 6 | } 7 | 8 | function compileReason(reason: string) { 9 | try { 10 | const ocaml = (window).printML((window).parseRE(reason)); 11 | return (window).ocaml.compile(ocaml); 12 | } catch (er) { 13 | return { 14 | text: er.message 15 | }; 16 | } 17 | } 18 | 19 | function guessType(reasonExpression: string): string { 20 | const compilationResult = compileReason( 21 | `let exp = (${reasonExpression}) == 1;` 22 | ); 23 | if (compilationResult.js_code) { 24 | return "int"; 25 | } else { 26 | // error format : "This expression has type int but an expression was expected of type {expressionType}" 27 | const type = compilationResult.text.substring(69).trim(); 28 | return type; 29 | } 30 | } 31 | 32 | function reasonExpToJs(reasonExp: string): CompilationResult { 33 | if (reasonExp.trim().length === 0) { 34 | return { 35 | kind: "empty", 36 | code: reasonExp 37 | }; 38 | } 39 | 40 | const reasonCode = `let exp = ${reasonExp};`; 41 | const compilationResult = compileReason(reasonCode); 42 | if (compilationResult.js_code) { 43 | (window).eval(wrapInExports(compilationResult.js_code)); 44 | return { 45 | kind: "valid", 46 | code: reasonExp, 47 | jsValue: (window).exports.exp, 48 | type: parseType(guessType(reasonExp)) 49 | }; 50 | } else { 51 | return { 52 | kind: "fail", 53 | code: reasonExp, 54 | error: compilationResult.text 55 | }; 56 | } 57 | } 58 | 59 | export default reasonExpToJs; 60 | -------------------------------------------------------------------------------- /packages/resuggest/src/suggest.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CompilationResult, 3 | AstTypeKind, 4 | AstType, 5 | ValidCompilationResult, 6 | Suggestion, 7 | Input, 8 | CompiledInput 9 | } from "./types"; 10 | 11 | import parseType from "./parseType"; 12 | import isTypeAssignable from "./isTypeAssignable"; 13 | import { caml_equal } from "bs-platform/lib/js/caml_obj"; 14 | import * as $$Array from "bs-platform/lib/js/array.js"; 15 | import uniquePermutations from "./uniquePermutations"; 16 | import flatten from "./flatten"; 17 | import * as db from "./generated/db.js"; 18 | import makeExample from "./makeExample"; 19 | 20 | type AstTypeToFuncs = [AstType, any[]]; 21 | 22 | const strToFunctionPair = Object.values(db) as [string, any[]][]; 23 | const astTypeToFunctionPairs = strToFunctionPair.map(([type, funcs]) => { 24 | return [parseType(type), $$Array.of_list(funcs)] as AstTypeToFuncs; 25 | }); 26 | 27 | export function makeAstFunctionType( 28 | inputs: AstType[], 29 | output: AstType 30 | ): AstType { 31 | if (inputs.length === 0) { 32 | return output; 33 | } 34 | return { 35 | kind: AstTypeKind.Func, 36 | input: inputs[0], 37 | output: makeAstFunctionType(inputs.slice(1), output) 38 | }; 39 | } 40 | 41 | function prepareInputValues(inputs: CompiledInput[]): any[] { 42 | return inputs.map(i => { 43 | if (i.expectedMutation !== null) { 44 | // If mutation is expected, we assume jsValue is an array so we clone it 45 | return i.expression.jsValue.slice(0); 46 | } else { 47 | return i.expression.jsValue; 48 | } 49 | }); 50 | } 51 | 52 | function doesFunctionPassExample( 53 | func: Function, 54 | inputs: CompiledInput[], 55 | output: ValidCompilationResult 56 | ): boolean { 57 | try { 58 | const preparedInputs = prepareInputValues(inputs); 59 | const expectedOutput = output.jsValue; 60 | const actualOutput = func.apply(null, preparedInputs); 61 | const areOutputsEqual = caml_equal(actualOutput, expectedOutput) === true; 62 | const mutationsEqualityResult = preparedInputs.map((input, index) => { 63 | if (inputs[index].expectedMutation === null) { 64 | return caml_equal(input, inputs[index].expression.jsValue) === true; 65 | } else { 66 | return ( 67 | caml_equal(input, inputs[index].expectedMutation.jsValue) === true 68 | ); 69 | } 70 | }); 71 | return areOutputsEqual && mutationsEqualityResult.every(r => r); 72 | } catch (ex) { 73 | return false; 74 | } 75 | } 76 | 77 | export function orderedSuggest( 78 | inputs: CompiledInput[], 79 | output: ValidCompilationResult 80 | ): Suggestion[] { 81 | const expectedFunctionType = makeAstFunctionType( 82 | inputs.map(i => i.expression.type), 83 | output.type 84 | ); 85 | 86 | const functionsWithMatchingSignature = flatten( 87 | astTypeToFunctionPairs 88 | .filter(([ast, funcs]) => { 89 | return isTypeAssignable(ast, expectedFunctionType); 90 | }) 91 | .map(([ast, funcs]) => funcs) 92 | ); 93 | 94 | return functionsWithMatchingSignature 95 | .filter(([func, _name]) => { 96 | return doesFunctionPassExample(func, inputs, output); 97 | }) 98 | .map(([_func, name]) => name) 99 | .map(functionName => ({ 100 | functionName, 101 | example: makeExample(functionName, inputs, output) 102 | })); 103 | } 104 | 105 | function validateOutput( 106 | output: CompilationResult 107 | ): ValidCompilationResult | null { 108 | if (output.kind === "valid") { 109 | return output; 110 | } else if (output.kind === "empty") { 111 | return { 112 | kind: "valid", 113 | code: "", 114 | type: { kind: AstTypeKind.Unit }, 115 | jsValue: 0 116 | }; 117 | } else { 118 | return null; 119 | } 120 | } 121 | 122 | function filterValidInputs( 123 | inputs: Array<{ 124 | expression: CompilationResult; 125 | expectedMutation: CompilationResult | null; 126 | }> 127 | ): CompiledInput[] { 128 | return inputs.filter(input => { 129 | return ( 130 | input.expression.kind === "valid" && 131 | (input.expectedMutation === null || 132 | input.expectedMutation.kind === "valid") 133 | ); 134 | }) as CompiledInput[]; 135 | } 136 | 137 | export default function suggest( 138 | inputs: Array<{ 139 | expression: CompilationResult; 140 | expectedMutation: CompilationResult | null; 141 | }>, 142 | output: CompilationResult 143 | ) { 144 | const validOuput = validateOutput(output); 145 | const validInputs = filterValidInputs(inputs); 146 | 147 | if ( 148 | validInputs.length === 0 || 149 | validOuput === null || 150 | (validOuput.type.kind === AstTypeKind.Unit && 151 | validInputs.every(i => i.expectedMutation === null)) 152 | ) { 153 | return []; 154 | } 155 | 156 | return flatten( 157 | uniquePermutations(validInputs, input => input.expression.code).map( 158 | permutedInputs => orderedSuggest(permutedInputs, validOuput) 159 | ) 160 | ); 161 | } 162 | -------------------------------------------------------------------------------- /packages/resuggest/src/types.ts: -------------------------------------------------------------------------------- 1 | export type Input = { 2 | code: string; 3 | expectedMutation: string | null; 4 | }; 5 | 6 | export type EmptyCompilationResult = { 7 | kind: "empty"; 8 | code: string; 9 | }; 10 | 11 | export type ValidCompilationResult = { 12 | kind: "valid"; 13 | code: string; 14 | jsValue: any; 15 | type: AstType; 16 | }; 17 | 18 | export type FailedCompilationResult = { 19 | kind: "fail"; 20 | code: string; 21 | error: string; 22 | }; 23 | 24 | export type CompilationResult = 25 | | ValidCompilationResult 26 | | FailedCompilationResult 27 | | EmptyCompilationResult; 28 | 29 | export type CompiledInput = { 30 | expression: ValidCompilationResult; 31 | expectedMutation: ValidCompilationResult | null; 32 | }; 33 | 34 | export type Suggestion = { 35 | functionName: string; 36 | example: string; 37 | }; 38 | 39 | export enum AstTypeKind { 40 | Simple, 41 | Array, 42 | List, 43 | Func, 44 | Generic, 45 | Tuple, 46 | Unit 47 | } 48 | 49 | export type AstType = 50 | | AstUnitType 51 | | AstFunctionType 52 | | AstTupleType 53 | | AstSimpleType 54 | | AstGenericType 55 | | AstArrayType 56 | | AstListType; 57 | 58 | export type AstUnitType = { 59 | kind: AstTypeKind.Unit; 60 | }; 61 | 62 | export type AstFunctionType = { 63 | kind: AstTypeKind.Func; 64 | input: AstType; 65 | output: AstType; 66 | }; 67 | 68 | export type AstTupleType = { 69 | kind: AstTypeKind.Tuple; 70 | types: AstType[]; 71 | }; 72 | 73 | export type AstSimpleType = { 74 | kind: AstTypeKind.Simple; 75 | type: string; 76 | }; 77 | 78 | export type AstGenericType = { 79 | kind: AstTypeKind.Generic; 80 | type: string; 81 | }; 82 | 83 | export type AstListType = { 84 | kind: AstTypeKind.List; 85 | itemType: AstType; 86 | }; 87 | 88 | export type AstArrayType = { 89 | kind: AstTypeKind.Array; 90 | itemType: AstType; 91 | }; 92 | -------------------------------------------------------------------------------- /packages/resuggest/src/uniquePermutations.ts: -------------------------------------------------------------------------------- 1 | function permutator(inputArr: T[]): T[][] { 2 | let result: T[][] = []; 3 | const permute = (arr: T[], m: T[] = []) => { 4 | if (arr.length === 0) { 5 | result.push(m); 6 | } else { 7 | for (let i = 0; i < arr.length; i++) { 8 | let curr = arr.slice(); 9 | let next = curr.splice(i, 1); 10 | permute(curr.slice(), m.concat(next)); 11 | } 12 | } 13 | }; 14 | permute(inputArr); 15 | return result; 16 | } 17 | 18 | function computeKey( 19 | inputs: T[], 20 | keySelector: (input: T) => string, 21 | keyMap: Map 22 | ): number { 23 | var result = 0; 24 | for (let input of inputs) { 25 | const value = keyMap.get(keySelector(input)); 26 | if (value === undefined) { 27 | throw new Error(`Key map does not contains ${keySelector(input)}`); 28 | } 29 | result += value; 30 | } 31 | return result; 32 | } 33 | 34 | // Weird way to compute unique permutations but 35 | // that's the first idea that came to my brain ¯\_(ツ)_/¯ 36 | // Todo: Handle input with mutation 37 | export default function uniquePermutations( 38 | inputs: T[], 39 | keySelector: (input: T) => string 40 | ): T[][] { 41 | if (inputs.length === 0) { 42 | return []; 43 | } 44 | 45 | if (inputs.length === 1) { 46 | return [inputs]; 47 | } 48 | 49 | let uniquePermutations = []; 50 | let generatedKeys = new Set(); 51 | 52 | for (let permutation of permutator(inputs)) { 53 | let key = permutation.map(keySelector).join(","); 54 | if (!generatedKeys.has(key)) { 55 | generatedKeys.add(key); 56 | uniquePermutations.push(permutation); 57 | } 58 | } 59 | 60 | return uniquePermutations; 61 | } 62 | -------------------------------------------------------------------------------- /packages/resuggest/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "noImplicitAny": true, 5 | "removeComments": true, 6 | "strictNullChecks": false, 7 | "sourceMap": true, 8 | "allowJs": true, 9 | "outDir": "dist", 10 | "lib": ["es2017", "dom"] 11 | }, 12 | "include": ["src/**/*"], 13 | "exclude": ["node_modules", "**/*.spec.ts"] 14 | } 15 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuillaumeSalles/resuggest/bb821bd333b550f03c0e9b3d9aa3e95569779862/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 10 | 14 | 15 | 16 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | Resuggest 32 | 33 | 34 | 35 | 38 |
39 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /scripts/generate-doc.js: -------------------------------------------------------------------------------- 1 | const axios = require("axios"); 2 | const fs = require("fs"); 3 | const { JSDOM } = require("jsdom"); 4 | const functions = require("../packages/resuggest/scripts/functions"); 5 | const functionNameToReasonApiAnchorId = require("../src/functionNameToReasonApiAnchorId"); 6 | 7 | function makeFnToDocMap(functionNameAndDocTuples) { 8 | return ` 9 | let map = new Map([ 10 | ${functionNameAndDocTuples.map(([fn, doc]) => `["${fn}",\`${doc}\`]`)} 11 | ]); 12 | 13 | export default map;`; 14 | } 15 | 16 | function escapeCodeElementContent(jsDom) { 17 | for (let codeElem of jsDom.window.document.getElementsByClassName("code")) { 18 | codeElem.innerHTML = codeElem.innerHTML.replace("\\", "\\\\"); 19 | } 20 | } 21 | 22 | function transformExternalLink(jsDom) { 23 | for (let a of jsDom.window.document.getElementsByTagName("a")) { 24 | a.setAttribute( 25 | "href", 26 | "https://reasonml.github.io/api/" + a.getAttribute("href") 27 | ); 28 | a.setAttribute("target", "_blank"); 29 | } 30 | } 31 | 32 | function moduleNameToJsDom(moduleName) { 33 | return axios 34 | .get(`https://reasonml.github.io/api/${moduleName}.html`) 35 | .then(res => { 36 | let dom = new JSDOM(res.data); 37 | escapeCodeElementContent(dom); 38 | transformExternalLink(dom); 39 | return [moduleName, dom]; 40 | }); 41 | } 42 | 43 | function functionNameToModuleName(name) { 44 | if (name.startsWith("(")) { 45 | return "Pervasives"; // Operators are all in Pervasives 46 | } 47 | var words = name.split("."); 48 | if (words.length === 1) { 49 | return "Pervasives"; 50 | } else if (words.length === 2) { 51 | return words[0]; 52 | } else if (words.length === 3) { 53 | // Belt 54 | return `${words[0]}.${words[1]}`; 55 | } 56 | } 57 | 58 | function extractDoc(functionName, moduleNameToJsDomMap) { 59 | console.log("Extract " + functionName); 60 | let dom = moduleNameToJsDomMap.get(functionNameToModuleName(functionName)); 61 | 62 | if (dom === undefined) { 63 | return ""; 64 | } 65 | 66 | return dom.window.document.getElementById( 67 | "VAL" + functionNameToReasonApiAnchorId(functionName) 68 | ).nextSibling.innerHTML; 69 | } 70 | 71 | let docsToFetch = ["Pervasives", "String", "Char", "List", "Array"]; 72 | 73 | Promise.all(docsToFetch.map(moduleNameToJsDom)) 74 | .then(modulesJsDoms => { 75 | let moduleNameToJsDomMap = new Map(modulesJsDoms); 76 | 77 | let documentation = makeFnToDocMap( 78 | functions.map(fn => [fn, extractDoc(fn, moduleNameToJsDomMap)]) 79 | ); 80 | 81 | fs.writeFileSync("./src/generated/doc.js", documentation); 82 | }) 83 | .catch(er => console.error(er)); 84 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | margin: 0; 4 | padding: 0; 5 | } 6 | 7 | body { 8 | color: hsla(0, 0%, 0%, 0.8); 9 | background: #f6f4f4; 10 | } 11 | 12 | h4 { 13 | font-size: 20px; 14 | margin-bottom: 0.5em; 15 | } 16 | 17 | h5 { 18 | color: #db4d3f; 19 | font-weight: 300; 20 | margin-bottom: 0; 21 | font-size: 24px; 22 | } 23 | 24 | pre { 25 | margin: 0 0 25px; 26 | padding: 10px 20px; 27 | background: #333; 28 | border: 2px solid #db4d3f; 29 | transition: all 0.2s ease; 30 | display: block; 31 | border-radius: 5px; 32 | line-height: 1.8em; 33 | font-family: monospace; 34 | font-size: 19px; 35 | color: rgba(255, 255, 255, 0.8); 36 | -webkit-font-smoothing: antialiased; 37 | overflow-x: auto; 38 | } 39 | 40 | input { 41 | width: 250px; 42 | margin-bottom: 10px; 43 | padding: 8px 16px; 44 | background: #333; 45 | border: 2px solid #db4d3f; 46 | transition: all 0.2s ease; 47 | display: block; 48 | border-radius: 5px; 49 | line-height: 1.8em; 50 | font-family: monospace; 51 | font-size: 16px; 52 | color: rgba(255, 255, 255, 0.8); 53 | -webkit-font-smoothing: antialiased; 54 | } 55 | 56 | main { 57 | margin: 15px; 58 | display: grid; 59 | grid-gap: 40px; 60 | grid-template-columns: 325px auto; 61 | grid-auto-columns: minmax(500px, auto); 62 | } 63 | 64 | @media screen and (max-width: 850px) { 65 | main { 66 | grid-gap: 0px; 67 | grid-template-columns: 100%; 68 | } 69 | } 70 | 71 | section { 72 | display: block; 73 | } 74 | 75 | .app { 76 | display: flex; 77 | flex-direction: column; 78 | } 79 | 80 | .app-description { 81 | margin: 0 15px; 82 | font-style: italic; 83 | } 84 | 85 | .info { 86 | margin: 5px 0; 87 | font-style: italic; 88 | } 89 | 90 | .desired-mutations-title-container { 91 | display: flex; 92 | flex-direction: row; 93 | align-items: flex-end; 94 | } 95 | 96 | .desired-mutations-help-icon { 97 | position: relative; 98 | display: inline-block; 99 | margin: 6px; 100 | fill: #333; 101 | } 102 | 103 | .desired-mutations-help-icon .desired-mutations-help-tooltip { 104 | visibility: hidden; 105 | width: 300px; 106 | background-color: #333; 107 | color: rgba(255, 255, 255, 0.8); 108 | text-align: left; 109 | padding: 8px 16px; 110 | border-radius: 6px; 111 | position: absolute; 112 | z-index: 1; 113 | left: 30px; 114 | top: 0px; 115 | } 116 | 117 | .desired-mutations-help-icon:hover .desired-mutations-help-tooltip { 118 | visibility: visible; 119 | } 120 | 121 | .logo { 122 | display: flex; 123 | flex-direction: row; 124 | font-family: "Montserrat", sans-serif; 125 | font-size: 50px; 126 | font-weight: 700; 127 | align-items: flex-end; 128 | margin: 15px; 129 | } 130 | 131 | .logo-re-container { 132 | position: relative; 133 | background: #db4d3f; 134 | color: white; 135 | height: 100px; 136 | width: 100px; 137 | } 138 | 139 | .logo-re { 140 | position: absolute; 141 | bottom: 0px; 142 | right: 3px; 143 | } 144 | 145 | .github { 146 | position: absolute; 147 | top: 15px; 148 | right: 15px; 149 | } 150 | 151 | .suggestion-header { 152 | margin-bottom: 20px; 153 | border: 1px solid #ccc; 154 | padding: 0 20px; 155 | background: #fff; 156 | border-radius: 5px; 157 | } 158 | 159 | .suggestion-name { 160 | color: #db4d3f; 161 | font-size: 20px; 162 | } 163 | 164 | .suggestion-usage { 165 | margin: 20px 0; 166 | } 167 | 168 | .suggestion-links { 169 | margin: 15px 0; 170 | } 171 | 172 | .external-link { 173 | background-color: transparent; 174 | position: relative; 175 | text-decoration: none; 176 | text-transform: capitalize; 177 | font-weight: 500; 178 | user-select: none; 179 | opacity: 0.75; 180 | color: hsla(0, 0%, 0%, 0.8); 181 | } 182 | 183 | .external-link:hover { 184 | opacity: 1; 185 | } 186 | 187 | .external-link:before { 188 | content: ""; 189 | position: absolute; 190 | width: 100%; 191 | height: 1px; 192 | bottom: -5px; 193 | left: 0; 194 | visibility: hidden; 195 | transform: scaleX(0); 196 | transition: all 0.25s cubic-bezier(0.82, 0, 0.12, 1); 197 | background-color: hsla(0, 0%, 0%, 0.8); 198 | } 199 | 200 | .external-link:hover:before { 201 | visibility: visible; 202 | transform: scaleX(1); 203 | } 204 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import "./App.css"; 3 | import InputsForm from "./InputsForm"; 4 | import ExpectedMutations from "./ExpectedMutations"; 5 | import debounce from "./debounce"; 6 | import ReasonExpressionInput from "./ReasonExpressionInput"; 7 | import suggest from "resuggest"; 8 | import * as lzString from "lz-string"; 9 | import documentation from "./generated/doc"; 10 | import functionNameToReasonApiAnchorId from "./functionNameToReasonApiAnchorId"; 11 | 12 | function safeSuggest(inputs, output) { 13 | try { 14 | return suggest( 15 | inputs.map(input => { 16 | return { 17 | code: input.expression.code, 18 | expectedMutation: 19 | input.expression.type && 20 | input.expression.type.kind === 1 && 21 | input.expectedMutation.code.trim().length > 0 22 | ? input.expectedMutation.code 23 | : null 24 | }; 25 | }), 26 | output 27 | ); 28 | } catch (er) { 29 | return { 30 | output: { 31 | code: output, 32 | jsValue: null, 33 | type: null, 34 | error: null 35 | }, 36 | inputs: inputs.map(input => ({ 37 | code: input, 38 | jsValue: null, 39 | type: null, 40 | error: null 41 | })), 42 | suggestions: [] 43 | }; 44 | } 45 | } 46 | 47 | function waitUntilScriptsLoaded(done) { 48 | const tout = setInterval(() => { 49 | if (window.printML && window.ocaml && window.require) { 50 | clearInterval(tout); 51 | done(); 52 | } 53 | }, 10); 54 | } 55 | 56 | function functionNameToDisplayUsage(name) { 57 | if (name[0] === "(") { 58 | return name.substr(1, name.length - 2); 59 | } 60 | 61 | return name; 62 | } 63 | 64 | function functionNameToDocumentionLink(name) { 65 | var words = name.split("."); 66 | if (words.length === 1) { 67 | return "Pervasives.html#VAL" + functionNameToReasonApiAnchorId(name); 68 | } else { 69 | return words[0] + ".html#VAL" + functionNameToReasonApiAnchorId(name); 70 | } 71 | } 72 | 73 | function renderDocumentationLink(functionName) { 74 | return ( 75 | 84 | see the docs 85 | 86 | ); 87 | } 88 | 89 | function renderSuggestion(suggestion) { 90 | return ( 91 |
92 |

93 | {functionNameToDisplayUsage(suggestion.functionName)} 94 |

95 |
100 |
101 |         {suggestion.example}
102 |       
103 |
104 | {renderDocumentationLink(suggestion.functionName)} 105 | 106 | {renderPlaygroundLink(suggestion)} 107 |
108 |
109 | ); 110 | } 111 | 112 | function makePlaygroundLink(suggestion) { 113 | return ( 114 | "https://reasonml.github.io/en/try.html?rrjsx=true&reason=" + 115 | lzString.compressToEncodedURIComponent(suggestion.example) 116 | ); 117 | } 118 | 119 | function renderPlaygroundLink(suggestion) { 120 | return ( 121 | 127 | try it in playground 128 | 129 | ); 130 | } 131 | 132 | function emptyExpression() { 133 | return { 134 | code: "", 135 | error: null 136 | }; 137 | } 138 | 139 | class App extends Component { 140 | constructor() { 141 | super(); 142 | 143 | this.state = { 144 | inputs: [ 145 | { 146 | expression: { 147 | code: '"Hello World"', 148 | error: null 149 | }, 150 | expectedMutation: emptyExpression() 151 | }, 152 | { 153 | expression: emptyExpression(), 154 | expectedMutation: emptyExpression() 155 | }, 156 | { 157 | expression: emptyExpression(), 158 | expectedMutation: emptyExpression() 159 | } 160 | ], 161 | output: { code: '"HELLO WORLD"', error: null }, 162 | suggestions: null, 163 | duration: null 164 | }; 165 | } 166 | 167 | componentDidMount() { 168 | waitUntilScriptsLoaded(() => { 169 | this.suggest(this.state.inputs, this.state.output); 170 | }); 171 | } 172 | 173 | onInputsChange = inputs => { 174 | this.setState( 175 | { 176 | inputs, 177 | suggestions: null 178 | }, 179 | () => this.suggest(this.state.inputs, this.state.output) 180 | ); 181 | }; 182 | 183 | onOutputChange = output => { 184 | this.setState( 185 | { 186 | output: { ...this.state.output, code: output }, 187 | suggestions: null 188 | }, 189 | () => this.suggest(this.state.inputs, this.state.output) 190 | ); 191 | }; 192 | 193 | suggest = debounce((newInputs, newOutput, newMutations) => { 194 | const t0 = performance.now(); 195 | const result = safeSuggest(newInputs, newOutput.code); 196 | this.setState({ 197 | inputs: result.inputs.map((input, index) => ({ 198 | ...input, 199 | expectedMutation: 200 | input.expectedMutation || newInputs[index].expectedMutation 201 | })), 202 | output: result.output, 203 | suggestions: result.suggestions, 204 | duration: performance.now() - t0 205 | }); 206 | }, 0); 207 | 208 | render() { 209 | return ( 210 |
211 |
212 | 216 | 217 | 218 |
219 |
220 |
221 |
RE
222 |
223 |
SUGGEST
224 |
225 |

226 | Discover Reason functions based on examples. You supply some arguments 227 | and the desired output, then it makes suggestions. 228 |

229 |
230 |
231 | 235 |

Desired Output

236 | 242 |
243 |

Desired Mutations

244 |
245 | 252 | 253 | 254 | 255 |
256 | Some functions do not return anything, instead they mutate 257 | their inputs (E.g. Array.set). If you are looking for a 258 | function that mutates arguments, provide arguments with a 259 | mutable type (E.g. array). 260 |
261 |
262 |
263 | 267 |
268 |
269 |

270 | Suggestions{" "} 271 | {this.state.duration && ( 272 | 273 | (computed in {this.state.duration.toFixed(2)}ms) 274 | 275 | )} 276 |

277 | {this.renderSuggestions(this.state.suggestions)} 278 |
279 |
280 |
281 | ); 282 | } 283 | 284 | renderSuggestions(suggestions) { 285 | if (suggestions === null) { 286 | return "Loading..."; 287 | } 288 | 289 | if (suggestions.length === 0) { 290 | return "Nothing to suggest..."; 291 | } 292 | 293 | return suggestions.map((suggestion, i) => ( 294 |
{renderSuggestion(suggestion)}
295 | )); 296 | } 297 | } 298 | 299 | export default App; 300 | 301 | var GithubIcon = () => ( 302 | 311 | 312 | 334 | 335 | 336 | ); 337 | -------------------------------------------------------------------------------- /src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | }); 9 | -------------------------------------------------------------------------------- /src/ExpectedMutations.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import ReasonExpressionInput from "./ReasonExpressionInput"; 3 | 4 | function indexToArgLabel(index) { 5 | switch (index) { 6 | case 0: 7 | return "1st argument mutation"; 8 | case 1: 9 | return "2nd argument mutation"; 10 | case 2: 11 | return "3rd argument mutation"; 12 | default: 13 | throw new Error(`${index} not supported`); 14 | } 15 | } 16 | 17 | function isMutationInvisible(input) { 18 | return input.expression.type == null || input.expression.type.kind !== 1; 19 | } 20 | 21 | class ExpectedMutations extends Component { 22 | renderMutation(input, index) { 23 | if (isMutationInvisible(input)) { 24 | return ; 25 | } 26 | 27 | return ( 28 | 29 |
{indexToArgLabel(index)}
30 | { 37 | const newInputs = this.props.inputs.map( 38 | (input, i) => 39 | index === i 40 | ? { ...input, expectedMutation: { code: newCode } } 41 | : input 42 | ); 43 | this.props.onChange(newInputs); 44 | }} 45 | /> 46 |
47 | ); 48 | } 49 | 50 | render() { 51 | const areAllMutationsInvisible = this.props.inputs.every( 52 | isMutationInvisible 53 | ); 54 | return ( 55 |
56 | {areAllMutationsInvisible && ( 57 |

No arguments given are mutable.

58 | )} 59 | {this.props.inputs.map((input, index) => 60 | this.renderMutation(input, index) 61 | )} 62 |
63 | ); 64 | } 65 | } 66 | 67 | export default ExpectedMutations; 68 | -------------------------------------------------------------------------------- /src/InputsForm.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import ReasonExpressionInput from "./ReasonExpressionInput"; 3 | 4 | function indexToArgLabel(index) { 5 | switch (index) { 6 | case 0: 7 | return "1st argument"; 8 | case 1: 9 | return "2nd argument"; 10 | case 2: 11 | return "3rd argument"; 12 | default: 13 | throw new Error(`${index} not supported`); 14 | } 15 | } 16 | 17 | class InputsForm extends Component { 18 | render() { 19 | return ( 20 |
21 |

Arguments

22 | {this.props.inputs.map((input, index) => ( 23 | 24 |
{indexToArgLabel(index)}
25 | { 32 | const newInputs = this.props.inputs.map( 33 | (input, i) => 34 | index === i 35 | ? { ...input, expression: { code: newCode } } 36 | : input 37 | ); 38 | this.props.onChange(newInputs); 39 | }} 40 | /> 41 |
42 | ))} 43 |
44 | ); 45 | } 46 | } 47 | 48 | export default InputsForm; 49 | -------------------------------------------------------------------------------- /src/ReasonExpressionInput.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | 3 | class ReasonExpressionInput extends Component { 4 | onChange = event => { 5 | this.props.onChange(event.target.value); 6 | }; 7 | 8 | render() { 9 | return ( 10 |
17 | 26 | {this.props.error && ( 27 | 34 | 35 | 39 | 40 | )} 41 |
42 | ); 43 | } 44 | } 45 | 46 | export default ReasonExpressionInput; 47 | -------------------------------------------------------------------------------- /src/debounce.js: -------------------------------------------------------------------------------- 1 | export default function debounce(func, wait, immediate) { 2 | let timeout; 3 | return function() { 4 | let context = this, 5 | args = arguments; 6 | const later = function() { 7 | timeout = null; 8 | if (!immediate) func.apply(context, args); 9 | }; 10 | const callNow = immediate && !timeout; 11 | clearTimeout(timeout); 12 | timeout = setTimeout(later, wait); 13 | if (callNow) func.apply(context, args); 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /src/functionNameToReasonApiAnchorId.js: -------------------------------------------------------------------------------- 1 | module.exports = function functionNameToReasonApiAnchorId(functionName) { 2 | switch (functionName) { 3 | case "(===)": 4 | return "(==)"; 5 | case "(!==)": 6 | return "(!=)"; 7 | case "(==)": 8 | return "(=)"; 9 | case "(!=)": 10 | return "(<>)"; 11 | case "(*)": 12 | return "( * )"; 13 | case "(*.)": 14 | return "( *. )"; 15 | case "(**)": 16 | return "( ** )"; 17 | case "(++)": 18 | return "(^)"; 19 | default: 20 | let words = functionName.split("."); 21 | return functionName.startsWith("(") || words.length === 1 22 | ? functionName 23 | : words[1]; 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /src/generated/doc.js: -------------------------------------------------------------------------------- 1 | 2 | let map = new Map([ 3 | ["(==)",` 4 | e1 = e2 tests for structural equality of e1 and e2. 5 | Mutable structures (e.g. references and arrays) are equal 6 | if and only if their current contents are structurally equal, 7 | even if the two mutable objects are not the same physical object. 8 | Equality between functional values raises Invalid_argument. 9 | Equality between cyclic data structures may not terminate.
10 | `],["(!=)",` 11 | Negation of Pervasives.(=).
12 | `],["(<)",` 13 | See Pervasives.(>=).
14 | `],["(>)",` 15 | See Pervasives.(>=).
16 | `],["(<=)",` 17 | See Pervasives.(>=).
18 | `],["(>=)",` 19 | Structural ordering functions. These functions coincide with 20 | the usual orderings over integers, characters, strings, byte sequences 21 | and floating-point numbers, and extend them to a 22 | total ordering over all types. 23 | The ordering is compatible with ( = ). As in the case 24 | of ( = ), mutable structures are compared by contents. 25 | Comparison between functional values raises Invalid_argument. 26 | Comparison between cyclic structures may not terminate.
27 | `],["compare",` 28 | compare x y returns 0 if x is equal to y, 29 | a negative integer if x is less than y, and a positive integer 30 | if x is greater than y. The ordering implemented by compare 31 | is compatible with the comparison predicates =, < and > 32 | defined above, with one difference on the treatment of the float value 33 | Pervasives.nan. Namely, the comparison predicates treat nan 34 | as different from any other float value, including itself; 35 | while compare treats nan as equal to itself and less than any 36 | other float value. This treatment of nan ensures that compare 37 | defines a total ordering relation. 38 |

39 | 40 | compare applied to functional values may raise Invalid_argument. 41 | compare applied to cyclic structures may not terminate. 42 |

43 | 44 | The compare function can be used as the comparison function 45 | required by the Set.Make and Map.Make functors, as well as 46 | the List.sort and Array.sort functions.
47 |

`],["min",` 48 | Return the smaller of the two arguments. 49 | The result is unspecified if one of the arguments contains 50 | the float value nan.
51 | `],["max",` 52 | Return the greater of the two arguments. 53 | The result is unspecified if one of the arguments contains 54 | the float value nan.
55 | `],["(===)",` 56 | e1 == e2 tests for physical equality of e1 and e2. 57 | On mutable types such as references, arrays, byte sequences, records with 58 | mutable fields and objects with mutable instance variables, 59 | e1 == e2 is true if and only if physical modification of e1 60 | also affects e2. 61 | On non-mutable types, the behavior of ( == ) is 62 | implementation-dependent; however, it is guaranteed that 63 | e1 == e2 implies compare e1 e2 = 0.
64 | `],["(!==)",` 65 | Negation of Pervasives.(==).
66 | `],["(!)",` 67 | !r returns the current contents of reference r. 68 | Equivalent to fun r -> r.contents.
69 | `],["(&&)",` 70 | The boolean 'and'. Evaluation is sequential, left-to-right: 71 | in e1 && e2, e1 is evaluated first, and if it returns false, 72 | e2 is not evaluated at all.
73 | `],["(||)",` 74 | The boolean 'or'. Evaluation is sequential, left-to-right: 75 | in e1 || e2, e1 is evaluated first, and if it returns true, 76 | e2 is not evaluated at all.
77 | `],["(|>)",` 78 | Reverse-application operator: x |> f |> g is exactly equivalent 79 | to g (f (x)).
80 | Since 4.01
81 | `],["(@@)",` 82 | Application operator: g @@ f @@ x is exactly equivalent to 83 | g (f (x)).
84 | Since 4.01
85 | `],["(~-)",` 86 | Unary negation. You can also write - e instead of ~- e.
87 | `],["(~+)",` 88 | Unary addition. You can also write + e instead of ~+ e.
89 | Since 3.12.0
90 | `],["succ",` 91 | succ x is x + 1.
92 | `],["pred",` 93 | pred x is x - 1.
94 | `],["(+)",` 95 | Integer addition.
96 | `],["(-)",` 97 | Integer subtraction.
98 | `],["(*)",` 99 | Integer multiplication.
100 | `],["(/)",` 101 | Integer division. 102 | Raise Division_by_zero if the second argument is 0. 103 | Integer division rounds the real quotient of its arguments towards zero. 104 | More precisely, if x >= 0 and y > 0, x / y is the greatest integer 105 | less than or equal to the real quotient of x by y. Moreover, 106 | (- x) / y = x / (- y) = - (x / y).
107 | `],["(mod)",` 108 | Integer remainder. If y is not zero, the result 109 | of x mod y satisfies the following properties: 110 | x = (x / y) * y + x mod y and 111 | abs(x mod y) <= abs(y) - 1. 112 | If y = 0, x mod y raises Division_by_zero. 113 | Note that x mod y is negative only if x < 0. 114 | Raise Division_by_zero if y is zero.
115 | `],["abs",` 116 | Return the absolute value of the argument. Note that this may be 117 | negative if the argument is min_int.
118 | `],["lnot",` 119 | Bitwise logical negation.
120 | `],["(land)",` 121 | Bitwise logical and.
122 | `],["(lor)",` 123 | Bitwise logical or.
124 | `],["(lxor)",` 125 | Bitwise logical exclusive or.
126 | `],["(lsl)",` 127 | n lsl m shifts n to the left by m bits. 128 | The result is unspecified if m < 0 or m >= bitsize, 129 | where bitsize is 32 on a 32-bit platform and 130 | 64 on a 64-bit platform.
131 | `],["(lsr)",` 132 | n lsr m shifts n to the right by m bits. 133 | This is a logical shift: zeroes are inserted regardless of 134 | the sign of n. 135 | The result is unspecified if m < 0 or m >= bitsize.
136 | `],["(asr)",` 137 | n asr m shifts n to the right by m bits. 138 | This is an arithmetic shift: the sign bit of n is replicated. 139 | The result is unspecified if m < 0 or m >= bitsize.
140 | `],["(~-.)",` 141 | Unary negation. You can also write -. e instead of ~-. e.
142 | `],["(~+.)",` 143 | Unary addition. You can also write +. e instead of ~+. e.
144 | Since 3.12.0
145 | `],["(+.)",` 146 | Floating-point addition
147 | `],["(-.)",` 148 | Floating-point subtraction
149 | `],["(*.)",` 150 | Floating-point multiplication
151 | `],["(/.)",` 152 | Floating-point division.
153 | `],["(**)",` 154 | Exponentiation.
155 | `],["sqrt",` 156 | Square root.
157 | `],["exp",` 158 | Exponential.
159 | `],["log",` 160 | Natural logarithm.
161 | `],["log10",` 162 | Base 10 logarithm.
163 | `],["expm1",` 164 | expm1 x computes exp x -. 1.0, giving numerically-accurate results 165 | even if x is close to 0.0.
166 | Since 3.12.0
167 | `],["log1p",` 168 | log1p x computes log(1.0 +. x) (natural logarithm), 169 | giving numerically-accurate results even if x is close to 0.0.
170 | Since 3.12.0
171 | `],["cos",` 172 | Cosine. Argument is in radians.
173 | `],["sin",` 174 | Sine. Argument is in radians.
175 | `],["tan",` 176 | Tangent. Argument is in radians.
177 | `],["acos",` 178 | Arc cosine. The argument must fall within the range [-1.0, 1.0]. 179 | Result is in radians and is between 0.0 and pi.
180 | `],["asin",` 181 | Arc sine. The argument must fall within the range [-1.0, 1.0]. 182 | Result is in radians and is between -pi/2 and pi/2.
183 | `],["atan",` 184 | Arc tangent. 185 | Result is in radians and is between -pi/2 and pi/2.
186 | `],["atan2",` 187 | atan2 y x returns the arc tangent of y /. x. The signs of x 188 | and y are used to determine the quadrant of the result. 189 | Result is in radians and is between -pi and pi.
190 | `],["hypot",` 191 | hypot x y returns sqrt(x *. x + y *. y), that is, the length 192 | of the hypotenuse of a right-angled triangle with sides of length 193 | x and y, or, equivalently, the distance of the point (x,y) 194 | to origin.
195 | Since 4.00.0
196 | `],["cosh",` 197 | Hyperbolic cosine. Argument is in radians.
198 | `],["sinh",` 199 | Hyperbolic sine. Argument is in radians.
200 | `],["tanh",` 201 | Hyperbolic tangent. Argument is in radians.
202 | `],["ceil",` 203 | Round above to an integer value. 204 | ceil f returns the least integer value greater than or equal to f. 205 | The result is returned as a float.
206 | `],["floor",` 207 | Round below to an integer value. 208 | floor f returns the greatest integer value less than or 209 | equal to f. 210 | The result is returned as a float.
211 | `],["abs_float",` 212 | abs_float f returns the absolute value of f.
213 | `],["copysign",` 214 | copysign x y returns a float whose absolute value is that of x 215 | and whose sign is that of y. If x is nan, returns nan. 216 | If y is nan, returns either x or -. x, but it is not 217 | specified which.
218 | Since 4.00.0
219 | `],["mod_float",` 220 | mod_float a b returns the remainder of a with respect to 221 | b. The returned value is a -. n *. b, where n 222 | is the quotient a /. b rounded towards zero to an integer.
223 | `],["frexp",` 224 | frexp f returns the pair of the significant 225 | and the exponent of f. When f is zero, the 226 | significant x and the exponent n of f are equal to 227 | zero. When f is non-zero, they are defined by 228 | f = x *. 2 ** n and 0.5 <= x < 1.0.
229 | `],["ldexp",` 230 | ldexp x n returns x *. 2 ** n.
231 | `],["modf",` 232 | modf f returns the pair of the fractional and integral 233 | part of f.
234 | `],["float",` 235 | Same as Pervasives.float_of_int.
236 | `],["float_of_int",` 237 | Convert an integer to floating-point.
238 | `],["truncate",` 239 | Same as Pervasives.int_of_float.
240 | `],["int_of_float",` 241 | Truncate the given floating-point number to an integer. 242 | The result is unspecified if the argument is nan or falls outside the 243 | range of representable integers.
244 | `],["(++)",` 245 | String concatenation.
246 | `],["int_of_char",` 247 | Return the ASCII code of the argument.
248 | `],["char_of_int",` 249 | Return the character with the given ASCII code. 250 | Raise Invalid_argument "char_of_int" if the argument is 251 | outside the range 0--255.
252 | `],["string_of_bool",` 253 | Return the string representation of a boolean. As the returned values 254 | may be shared, the user should not modify them directly.
255 | `],["bool_of_string",` 256 | Convert the given string to a boolean. 257 | Raise Invalid_argument "bool_of_string" if the string is not 258 | "true" or "false".
259 | `],["string_of_int",` 260 | Return the string representation of an integer, in decimal.
261 | `],["int_of_string",` 262 | Convert the given string to an integer. 263 | The string is read in decimal (by default) or in hexadecimal (if it 264 | begins with 0x or 0X), octal (if it begins with 0o or 0O), 265 | or binary (if it begins with 0b or 0B). 266 | Raise Failure "int_of_string" if the given string is not 267 | a valid representation of an integer, or if the integer represented 268 | exceeds the range of integers representable in type int.
269 | `],["string_of_float",` 270 | Return the string representation of a floating-point number.
271 | `],["float_of_string",` 272 | Convert the given string to a float. Raise Failure "float_of_string" 273 | if the given string is not a valid representation of a float.
274 | `],["fst",` 275 | Return the first component of a pair.
276 | `],["snd",` 277 | Return the second component of a pair.
278 | `],["(@)",` 279 | List concatenation.
280 | `],["Char.code",` 281 | Return the ASCII code of the argument.
282 | `],["Char.chr",` 283 | Return the character with the given ASCII code. 284 | Raise Invalid_argument "Char.chr" if the argument is 285 | outside the range 0--255.
286 | `],["Char.escaped",` 287 | Return a string representing the given character, 288 | with special characters escaped following the lexical conventions 289 | of OCaml.
290 | `],["Char.lowercase",` 291 | Convert the given character to its equivalent lowercase character.
292 | `],["Char.uppercase",` 293 | Convert the given character to its equivalent uppercase character.
294 | `],["String.get",` 295 | String.get s n returns the character at index n in string s. 296 | You can also write s.[n] instead of String.get s n. 297 |

298 | 299 | Raise Invalid_argument if n not a valid index in s.
300 |

`],["String.make",` 301 | String.make n c returns a fresh string of length n, 302 | filled with the character c. 303 |

304 | 305 | Raise Invalid_argument if n < 0 or n > Sys.max_string_length.
306 |

`],["String.init",` 307 | String.init n f returns a string of length n, with character 308 | i initialized to the result of f i (called in increasing 309 | index order). 310 |

311 | 312 | Raise Invalid_argument if n < 0 or n > Sys.max_string_length.
313 | Since 4.02.0
314 |

`],["String.sub",` 315 | String.sub s start len returns a fresh string of length len, 316 | containing the substring of s that starts at position start and 317 | has length len. 318 |

319 | 320 | Raise Invalid_argument if start and len do not 321 | designate a valid substring of s.
322 |

`],["String.mapi",` 323 | String.mapi f s calls f with each character of s and its 324 | index (in increasing index order) and stores the results in a new 325 | string that is returned.
326 | Since 4.02.0
327 | `],["String.map",` 328 | String.map f s applies function f in turn to all the 329 | characters of s (in increasing index order) and stores the 330 | results in a new string that is returned.
331 | Since 4.00.0
332 | `],["String.trim",` 333 | Return a copy of the argument, without leading and trailing 334 | whitespace. The characters regarded as whitespace are: ' ', 335 | '\\012', '\\n', '\\r', and '\\t'. If there is neither leading nor 336 | trailing whitespace character in the argument, return the original 337 | string itself, not a copy.
338 | Since 4.00.0
339 | `],["String.escaped",` 340 | Return a copy of the argument, with special characters 341 | represented by escape sequences, following the lexical 342 | conventions of OCaml. If there is no special 343 | character in the argument, return the original string itself, 344 | not a copy. Its inverse function is Scanf.unescaped. 345 |

346 | 347 | Raise Invalid_argument if the result is longer than 348 | Sys.max_string_length bytes.
349 |

`],["String.uppercase",` 350 | Return a copy of the argument, with all lowercase letters 351 | translated to uppercase, including accented letters of the ISO 352 | Latin-1 (8859-1) character set.
353 | `],["String.lowercase",` 354 | Return a copy of the argument, with all uppercase letters 355 | translated to lowercase, including accented letters of the ISO 356 | Latin-1 (8859-1) character set.
357 | `],["String.capitalize",` 358 | Return a copy of the argument, with the first character set to uppercase.
359 | `],["String.uncapitalize",` 360 | Return a copy of the argument, with the first character set to lowercase.
361 | `],["String.index",` 362 | String.index s c returns the index of the first 363 | occurrence of character c in string s. 364 |

365 | 366 | Raise Not_found if c does not occur in s.
367 |

`],["String.rindex",` 368 | String.rindex s c returns the index of the last 369 | occurrence of character c in string s. 370 |

371 | 372 | Raise Not_found if c does not occur in s.
373 |

`],["String.index_from",` 374 | String.index_from s i c returns the index of the 375 | first occurrence of character c in string s after position i. 376 | String.index s c is equivalent to String.index_from s 0 c. 377 |

378 | 379 | Raise Invalid_argument if i is not a valid position in s. 380 | Raise Not_found if c does not occur in s after position i.
381 |

`],["String.rindex_from",` 382 | String.rindex_from s i c returns the index of the 383 | last occurrence of character c in string s before position i+1. 384 | String.rindex s c is equivalent to 385 | String.rindex_from s (String.length s - 1) c. 386 |

387 | 388 | Raise Invalid_argument if i+1 is not a valid position in s. 389 | Raise Not_found if c does not occur in s before position i+1.
390 |

`],["String.contains",` 391 | String.contains s c tests if character c 392 | appears in the string s.
393 | `],["String.contains_from",` 394 | String.contains_from s start c tests if character c 395 | appears in s after position start. 396 | String.contains s c is equivalent to 397 | String.contains_from s 0 c. 398 |

399 | 400 | Raise Invalid_argument if start is not a valid position in s.
401 |

`],["String.rcontains_from",` 402 | String.rcontains_from s stop c tests if character c 403 | appears in s before position stop+1. 404 |

405 | 406 | Raise Invalid_argument if stop < 0 or stop+1 is not a valid 407 | position in s.
408 |

`],["String.length",` 409 | Return the length (number of characters) of the given string.
410 | `],["List.append",` 411 | Catenate two lists. Same function as the infix operator @. 412 | Not tail-recursive (length of the first argument). The @ 413 | operator is not tail-recursive either.
414 | `],["List.rev_append",` 415 | List.rev_append l1 l2 reverses l1 and concatenates it to l2. 416 | This is equivalent to List.rev l1 @ l2, but rev_append is 417 | tail-recursive and more efficient.
418 | `],["List.length",` 419 | Return the length (number of elements) of the given list.
420 | `],["List.hd",` 421 | Return the first element of the given list. Raise 422 | Failure "hd" if the list is empty.
423 | `],["List.tl",` 424 | Return the given list without its first element. Raise 425 | Failure "tl" if the list is empty.
426 | `],["List.rev",` 427 | List reversal.
428 | `],["List.nth",` 429 | Return the n-th element of the given list. 430 | The first element (head of the list) is at position 0. 431 | Raise Failure "nth" if the list is too short. 432 | Raise Invalid_argument "List.nth" if n is negative.
433 | `],["List.concat",` 434 | Concatenate a list of lists. The elements of the argument are all 435 | concatenated together (in the same order) to give the result. 436 | Not tail-recursive 437 | (length of the argument + length of the longest sub-list).
438 | `],["List.flatten",` 439 | Same as concat. Not tail-recursive 440 | (length of the argument + length of the longest sub-list).
441 | `],["List.map",` 442 | List.map f [a1; ...; an] applies function f to a1, ..., an, 443 | and builds the list [f a1; ...; f an] 444 | with the results returned by f. Not tail-recursive.
445 | `],["List.rev_map",` 446 | List.rev_map f l gives the same result as 447 | List.rev (List.map f l), but is tail-recursive and 448 | more efficient.
449 | `],["List.mapi",` 450 | Same as List.map, but the function is applied to the index of 451 | the element as first argument (counting from 0), and the element 452 | itself as second argument. Not tail-recursive.
453 | Since 4.00.0
454 | `],["List.fold_left",` 455 | List.fold_left f a [b1; ...; bn] is 456 | f (... (f (f a b1) b2) ...) bn.
457 | `],["List.fold_right",` 458 | List.fold_right f [a1; ...; an] b is 459 | f a1 (f a2 (... (f an b) ...)). Not tail-recursive.
460 | `],["List.map2",` 461 | List.map2 f [a1; ...; an] [b1; ...; bn] is 462 | [f a1 b1; ...; f an bn]. 463 | Raise Invalid_argument if the two lists have 464 | different lengths. Not tail-recursive.
465 | `],["List.rev_map2",` 466 | List.rev_map2 f l1 l2 gives the same result as 467 | List.rev (List.map2 f l1 l2), but is tail-recursive and 468 | more efficient.
469 | `],["List.fold_left2",` 470 | List.fold_left2 f a [b1; ...; bn] [c1; ...; cn] is 471 | f (... (f (f a b1 c1) b2 c2) ...) bn cn. 472 | Raise Invalid_argument if the two lists have 473 | different lengths.
474 | `],["List.fold_right2",` 475 | List.fold_right2 f [a1; ...; an] [b1; ...; bn] c is 476 | f a1 b1 (f a2 b2 (... (f an bn c) ...)). 477 | Raise Invalid_argument if the two lists have 478 | different lengths. Not tail-recursive.
479 | `],["List.exists",` 480 | exists p [a1; ...; an] checks if at least one element of 481 | the list satisfies the predicate p. That is, it returns 482 | (p a1) || (p a2) || ... || (p an).
483 | `],["List.exists2",` 484 | Same as List.exists, but for a two-argument predicate. 485 | Raise Invalid_argument if the two lists have 486 | different lengths.
487 | `],["List.mem",` 488 | mem a l is true if and only if a is equal 489 | to an element of l.
490 | `],["List.memq",` 491 | Same as List.mem, but uses physical equality instead of structural 492 | equality to compare list elements.
493 | `],["List.find",` 494 | find p l returns the first element of the list l 495 | that satisfies the predicate p. 496 | Raise Not_found if there is no value that satisfies p in the 497 | list l.
498 | `],["List.filter",` 499 | filter p l returns all the elements of the list l 500 | that satisfy the predicate p. The order of the elements 501 | in the input list is preserved.
502 | `],["List.find_all",` 503 | find_all is another name for List.filter.
504 | `],["List.sort",` 505 | Sort a list in increasing order according to a comparison 506 | function. The comparison function must return 0 if its arguments 507 | compare as equal, a positive integer if the first is greater, 508 | and a negative integer if the first is smaller (see Array.sort for 509 | a complete specification). For example, 510 | Pervasives.compare is a suitable comparison function. 511 | The resulting list is sorted in increasing order. 512 | List.sort is guaranteed to run in constant heap space 513 | (in addition to the size of the result list) and logarithmic 514 | stack space. 515 |

516 | 517 | The current implementation uses Merge Sort. It runs in constant 518 | heap space and logarithmic stack space.
519 |

`],["List.stable_sort",` 520 | Same as List.sort, but the sorting algorithm is guaranteed to 521 | be stable (i.e. elements that compare equal are kept in their 522 | original order) . 523 |

524 | 525 | The current implementation uses Merge Sort. It runs in constant 526 | heap space and logarithmic stack space.
527 |

`],["List.fast_sort",` 528 | Same as List.sort or List.stable_sort, whichever is faster 529 | on typical input.
530 | `],["List.sort_uniq",` 531 | Same as List.sort, but also remove duplicates.
532 | Since 4.02.0
533 | `],["List.merge",` 534 | Merge two lists: 535 | Assuming that l1 and l2 are sorted according to the 536 | comparison function cmp, merge cmp l1 l2 will return a 537 | sorted list containting all the elements of l1 and l2. 538 | If several elements compare equal, the elements of l1 will be 539 | before the elements of l2. 540 | Not tail-recursive (sum of the lengths of the arguments).
541 | `],["Array.length",` 542 | Return the length (number of elements) of the given array.
543 | `],["Array.get",` 544 | Array.get a n returns the element number n of array a. 545 | The first element has number 0. 546 | The last element has number Array.length a - 1. 547 | You can also write a.(n) instead of Array.get a n. 548 |

549 | 550 | Raise Invalid_argument "index out of bounds" 551 | if n is outside the range 0 to (Array.length a - 1).
552 |

`],["Array.set",` 553 | Array.set a n x modifies array a in place, replacing 554 | element number n with x. 555 | You can also write a.(n) <- x instead of Array.set a n x. 556 |

557 | 558 | Raise Invalid_argument "index out of bounds" 559 | if n is outside the range 0 to Array.length a - 1.
560 |

`],["Array.make",` 561 | Array.make n x returns a fresh array of length n, 562 | initialized with x. 563 | All the elements of this new array are initially 564 | physically equal to x (in the sense of the == predicate). 565 | Consequently, if x is mutable, it is shared among all elements 566 | of the array, and modifying x through one of the array entries 567 | will modify all other entries at the same time. 568 |

569 | 570 | Raise Invalid_argument if n < 0 or n > Sys.max_array_length. 571 | If the value of x is a floating-point number, then the maximum 572 | size is only Sys.max_array_length / 2.
573 |

`],["Array.init",` 574 | Array.init n f returns a fresh array of length n, 575 | with element number i initialized to the result of f i. 576 | In other terms, Array.init n f tabulates the results of f 577 | applied to the integers 0 to n-1. 578 |

579 | 580 | Raise Invalid_argument if n < 0 or n > Sys.max_array_length. 581 | If the return type of f is float, then the maximum 582 | size is only Sys.max_array_length / 2.
583 |

`],["Array.make_matrix",` 584 | Array.make_matrix dimx dimy e returns a two-dimensional array 585 | (an array of arrays) with first dimension dimx and 586 | second dimension dimy. All the elements of this new matrix 587 | are initially physically equal to e. 588 | The element (x,y) of a matrix m is accessed 589 | with the notation m.(x).(y). 590 |

591 | 592 | Raise Invalid_argument if dimx or dimy is negative or 593 | greater than Sys.max_array_length. 594 | If the value of e is a floating-point number, then the maximum 595 | size is only Sys.max_array_length / 2.
596 |

`],["Array.append",` 597 | Array.append v1 v2 returns a fresh array containing the 598 | concatenation of the arrays v1 and v2.
599 | `],["Array.concat",` 600 | Same as Array.append, but concatenates a list of arrays.
601 | `],["Array.sub",` 602 | Array.sub a start len returns a fresh array of length len, 603 | containing the elements number start to start + len - 1 604 | of array a. 605 |

606 | 607 | Raise Invalid_argument "Array.sub" if start and len do not 608 | designate a valid subarray of a; that is, if 609 | start < 0, or len < 0, or start + len > Array.length a.
610 |

`],["Array.copy",` 611 | Array.copy a returns a copy of a, that is, a fresh array 612 | containing the same elements as a.
613 | `],["Array.to_list",` 614 | Array.to_list a returns the list of all the elements of a.
615 | `],["Array.of_list",` 616 | Array.of_list l returns a fresh array containing the elements 617 | of l.
618 | `],["Array.map",` 619 | Array.map f a applies function f to all the elements of a, 620 | and builds an array with the results returned by f: 621 | [| f a.(0); f a.(1); ...; f a.(Array.length a - 1) |].
622 | `],["Array.mapi",` 623 | Same as Array.map, but the 624 | function is applied to the index of the element as first argument, 625 | and the element itself as second argument.
626 | `],["Array.fold_left",` 627 | Array.fold_left f x a computes 628 | f (... (f (f x a.(0)) a.(1)) ...) a.(n-1), 629 | where n is the length of the array a.
630 | `],["Array.fold_right",` 631 | Array.fold_right f a x computes 632 | f a.(0) (f a.(1) ( ... (f a.(n-1) x) ...)), 633 | where n is the length of the array a.
634 | `],["Array.make_float",` 635 | Array.make_float n returns a fresh float array of length n, 636 | with uninitialized data.
637 | Since 4.02
638 | `],["Array.sort",` 639 | Sort an array in increasing order according to a comparison 640 | function. The comparison function must return 0 if its arguments 641 | compare as equal, a positive integer if the first is greater, 642 | and a negative integer if the first is smaller (see below for a 643 | complete specification). For example, Pervasives.compare is 644 | a suitable comparison function, provided there are no floating-point 645 | NaN values in the data. After calling Array.sort, the 646 | array is sorted in place in increasing order. 647 | Array.sort is guaranteed to run in constant heap space 648 | and (at most) logarithmic stack space. 649 |

650 | 651 | The current implementation uses Heap Sort. It runs in constant 652 | stack space. 653 |

654 | 655 | Specification of the comparison function: 656 | Let a be the array and cmp the comparison function. The following 657 | must be true for all x, y, z in a :

    658 |
  • cmp x y > 0 if and only if cmp y x < 0
  • 659 |
  • if cmp x y >= 0 and cmp y z >= 0 then cmp x z >= 0
  • 660 |
661 | 662 | When Array.sort returns, a contains the same elements as before, 663 | reordered in such a way that for all i and j valid indices of a :
    664 |
  • cmp a.(i) a.(j) >= 0 if and only if i >= j
  • 665 |
666 |
667 | `],["Array.stable_sort",` 668 | Same as Array.sort, but the sorting algorithm is stable (i.e. 669 | elements that compare equal are kept in their original order) and 670 | not guaranteed to run in constant heap space. 671 |

672 | 673 | The current implementation uses Merge Sort. It uses n/2 674 | words of heap space, where n is the length of the array. 675 | It is usually faster than the current implementation of Array.sort.
676 |

`],["Array.fast_sort",` 677 | Same as Array.sort or Array.stable_sort, whichever is faster 678 | on typical input.
679 | `] 680 | ]); 681 | 682 | export default map; -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import registerServiceWorker from './registerServiceWorker'; 6 | 7 | ReactDOM.render(, document.getElementById('root')); 8 | registerServiceWorker(); 9 | -------------------------------------------------------------------------------- /src/registerServiceWorker.js: -------------------------------------------------------------------------------- 1 | // In production, we register a service worker to serve assets from local cache. 2 | 3 | // This lets the app load faster on subsequent visits in production, and gives 4 | // it offline capabilities. However, it also means that developers (and users) 5 | // will only see deployed updates on the "N+1" visit to a page, since previously 6 | // cached resources are updated in the background. 7 | 8 | // To learn more about the benefits of this model, read https://goo.gl/KwvDNy. 9 | // This link also includes instructions on opting out of this behavior. 10 | 11 | const isLocalhost = Boolean( 12 | window.location.hostname === 'localhost' || 13 | // [::1] is the IPv6 localhost address. 14 | window.location.hostname === '[::1]' || 15 | // 127.0.0.1/8 is considered localhost for IPv4. 16 | window.location.hostname.match( 17 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 18 | ) 19 | ); 20 | 21 | export default function register() { 22 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 23 | // The URL constructor is available in all browsers that support SW. 24 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location); 25 | if (publicUrl.origin !== window.location.origin) { 26 | // Our service worker won't work if PUBLIC_URL is on a different origin 27 | // from what our page is served on. This might happen if a CDN is used to 28 | // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374 29 | return; 30 | } 31 | 32 | window.addEventListener('load', () => { 33 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 34 | 35 | if (!isLocalhost) { 36 | // Is not local host. Just register service worker 37 | registerValidSW(swUrl); 38 | } else { 39 | // This is running on localhost. Lets check if a service worker still exists or not. 40 | checkValidServiceWorker(swUrl); 41 | } 42 | }); 43 | } 44 | } 45 | 46 | function registerValidSW(swUrl) { 47 | navigator.serviceWorker 48 | .register(swUrl) 49 | .then(registration => { 50 | registration.onupdatefound = () => { 51 | const installingWorker = registration.installing; 52 | installingWorker.onstatechange = () => { 53 | if (installingWorker.state === 'installed') { 54 | if (navigator.serviceWorker.controller) { 55 | // At this point, the old content will have been purged and 56 | // the fresh content will have been added to the cache. 57 | // It's the perfect time to display a "New content is 58 | // available; please refresh." message in your web app. 59 | console.log('New content is available; please refresh.'); 60 | } else { 61 | // At this point, everything has been precached. 62 | // It's the perfect time to display a 63 | // "Content is cached for offline use." message. 64 | console.log('Content is cached for offline use.'); 65 | } 66 | } 67 | }; 68 | }; 69 | }) 70 | .catch(error => { 71 | console.error('Error during service worker registration:', error); 72 | }); 73 | } 74 | 75 | function checkValidServiceWorker(swUrl) { 76 | // Check if the service worker can be found. If it can't reload the page. 77 | fetch(swUrl) 78 | .then(response => { 79 | // Ensure service worker exists, and that we really are getting a JS file. 80 | if ( 81 | response.status === 404 || 82 | response.headers.get('content-type').indexOf('javascript') === -1 83 | ) { 84 | // No service worker found. Probably a different app. Reload the page. 85 | navigator.serviceWorker.ready.then(registration => { 86 | registration.unregister().then(() => { 87 | window.location.reload(); 88 | }); 89 | }); 90 | } else { 91 | // Service worker found. Proceed as normal. 92 | registerValidSW(swUrl); 93 | } 94 | }) 95 | .catch(() => { 96 | console.log( 97 | 'No internet connection found. App is running in offline mode.' 98 | ); 99 | }); 100 | } 101 | 102 | export function unregister() { 103 | if ('serviceWorker' in navigator) { 104 | navigator.serviceWorker.ready.then(registration => { 105 | registration.unregister(); 106 | }); 107 | } 108 | } 109 | --------------------------------------------------------------------------------