├── fastpack.json
├── doc
├── array.png
├── diffB.png
├── union.png
├── diffA1A2.png
├── inference.png
├── singleton.png
├── TypeCheckingTypes.md
├── Erasure.md
├── SumOperations.md
├── TypeCheckingStyp.md
├── Abduction.md
├── UnionExtension.md
└── Diff.md
├── .gitignore
├── src
├── Component1.re
├── Serialize.rei
├── Component1.js
├── Index.re
├── Index.js
├── Samples.rei
├── Samples.re
├── Serialize.re
├── DynTypedJson.re
├── Serialize.js
├── Samples.js
├── Demo.re
├── TypeCheck.re
├── Styp.re
├── DynTypedJson.js
├── Demo.js
├── UI.re
├── Diff.re
├── Styp.js
├── TypeCheck.js
├── UI.js
└── Diff.js
├── webpack.config.js
├── bsconfig.json
├── index-parcel.html
├── index-rollup.html
├── index-fastpack.html
├── index-webpack.html
├── react-treeview.css
├── rollup.config.browser.js
├── LICENSE.md
├── opinionated.css
├── package.json
└── README.md
/fastpack.json:
--------------------------------------------------------------------------------
1 | {
2 | "entryPoints": ["src/Index.js"]
3 | }
--------------------------------------------------------------------------------
/doc/array.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cristianoc/REInfer/HEAD/doc/array.png
--------------------------------------------------------------------------------
/doc/diffB.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cristianoc/REInfer/HEAD/doc/diffB.png
--------------------------------------------------------------------------------
/doc/union.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cristianoc/REInfer/HEAD/doc/union.png
--------------------------------------------------------------------------------
/doc/diffA1A2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cristianoc/REInfer/HEAD/doc/diffA1A2.png
--------------------------------------------------------------------------------
/doc/inference.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cristianoc/REInfer/HEAD/doc/inference.png
--------------------------------------------------------------------------------
/doc/singleton.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cristianoc/REInfer/HEAD/doc/singleton.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /yarn.lock
3 | /src/*.bs.js
4 | /.merlin
5 | /lib
6 | /.bsb.lock
7 | /.cache
8 | /bundledOutputs
9 |
--------------------------------------------------------------------------------
/src/Component1.re:
--------------------------------------------------------------------------------
1 | [@react.component]
2 | let make = (~message) => {
3 |
Js.log("clicked!")}>
4 | {React.string(message)}
5 |
;
6 | };
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 |
3 | module.exports = {
4 | entry: {
5 | index: "./src/Index.js"
6 | },
7 | mode: "production",
8 | output: {
9 | path: path.join(__dirname, "bundledOutputs"),
10 | filename: "webpack.js"
11 | }
12 | };
13 |
--------------------------------------------------------------------------------
/src/Serialize.rei:
--------------------------------------------------------------------------------
1 | /**
2 | * Serializer for arbitrary values.
3 | * Note: might not work across versions of the compiler (as the runtime representation might change).
4 | * Note: uses json stringify under the hood: does not support functions or cyclic values.
5 | */
6 |
7 | let toString: 'a => string;
8 |
9 | let fromString: string => 'a;
--------------------------------------------------------------------------------
/bsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-template",
3 | "reason": {
4 | "react-jsx": 3
5 | },
6 | "sources": {
7 | "dir" : "src",
8 | "subdirs" : true
9 | },
10 | "package-specs": [{
11 | "module": "es6",
12 | "in-source": true
13 | }],
14 | "suffix": ".js",
15 | "namespace": true,
16 | "bs-dependencies": [
17 | "reason-react"
18 | ],
19 | "refmt": 3
20 | }
21 |
--------------------------------------------------------------------------------
/doc/TypeCheckingTypes.md:
--------------------------------------------------------------------------------
1 | ```
2 | |- 123 : number
3 | ```
4 |
5 | ```
6 | |- “abc” : string
7 | ```
8 |
9 | ```
10 | |- true : boolean |- false : boolean
11 | ```
12 |
13 | ```
14 | |- null : t?
15 | ```
16 |
17 | ```
18 | |- val1:t1 … |- valn:tn
19 | ————————————————————————————————————————————————
20 | |- {x1:val1, …, xn:valn} : {x1:t1, …, xn:tn}
21 | ```
22 |
23 | ```
24 | |- [] : [empty]
25 | ```
26 |
27 | ```
28 | |- val1:t … |- valn:t (n>0)
29 | ——————————————————————————————————
30 | |- [val1, …, valn] : [t]
31 | ```
32 |
--------------------------------------------------------------------------------
/src/Component1.js:
--------------------------------------------------------------------------------
1 | // Generated by BUCKLESCRIPT, PLEASE EDIT WITH CARE
2 |
3 | import * as React from "react";
4 |
5 | function Component1(Props) {
6 | var message = Props.message;
7 | return React.createElement("div", {
8 | onClick: (function (_event) {
9 | console.log("clicked!");
10 | return /* () */0;
11 | })
12 | }, message);
13 | }
14 |
15 | var make = Component1;
16 |
17 | export {
18 | make ,
19 |
20 | }
21 | /* react Not a pure module */
22 |
--------------------------------------------------------------------------------
/doc/Erasure.md:
--------------------------------------------------------------------------------
1 | ```
2 | |- |typ| = t
3 | ———————————————————
4 | |- |(typ,o)::p| = t
5 | ```
6 |
7 | ```
8 | |- |empty| = empty
9 | ```
10 |
11 | ```
12 | |- |number| = number
13 | ```
14 |
15 | ```
16 | |- |string| = string
17 | ```
18 |
19 | ```
20 | |- |boolean| = boolean
21 | ```
22 |
23 | ```
24 | |- |styp1| = t1 … |- |stypn| = tn
25 | ————————————————————————————————————————————————————
26 | |- |{x1:styp1, …, xn:stypn}| = {x1:t1, …, xn:tn}
27 | ```
28 |
29 | ```
30 | |- |styp| = t
31 | ———————————————————
32 | |- |[styp]| = [t]
33 | ```
34 |
--------------------------------------------------------------------------------
/src/Index.re:
--------------------------------------------------------------------------------
1 | module Main = {
2 | [@react.component]
3 | let make = () => {
4 | let diffs = Demo.test();
5 |
6 | {diffs
7 | ->Belt.List.toArray
8 | ->(
9 | Belt.Array.mapWithIndex((i, diff) =>
10 |
string_of_int}>
11 |
{React.string("Sample " ++ string_of_int(i))}
12 |
13 |
14 | )
15 | )
16 | ->React.array}
17 |
;
18 | };
19 | };
20 |
21 | ReactDOMRe.renderToElementWithId(, "main");
--------------------------------------------------------------------------------
/index-parcel.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | REInfer: Runtime Extended Inference
6 |
7 |
8 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/index-rollup.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | REInfer: Runtime Extended Inference
6 |
7 |
8 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/index-fastpack.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | REInfer: Runtime Extended Inference
6 |
7 |
8 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/index-webpack.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | REInfer: Runtime Extended Inference
6 |
7 |
8 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/react-treeview.css:
--------------------------------------------------------------------------------
1 | /* the tree node's style */
2 | .tree-view {
3 | overflow-y: hidden;
4 | }
5 |
6 | .tree-view_item {
7 | /* immediate child of .tree-view, for styling convenience */
8 | }
9 |
10 | /* style for the children nodes container */
11 | .tree-view_children {
12 | margin-left: 16px;
13 | }
14 |
15 | .tree-view_children-collapsed {
16 | height: 0px;
17 | }
18 |
19 | .tree-view_arrow {
20 | cursor: pointer;
21 | margin-right: 6px;
22 | display: inline-block;
23 | -webkit-user-select: none;
24 | -moz-user-select: none;
25 | -ms-user-select: none;
26 | user-select: none;
27 | }
28 |
29 | .tree-view_arrow:after {
30 | content: '▾';
31 | }
32 |
33 | /* rotate the triangle to close it */
34 | .tree-view_arrow-collapsed {
35 | -webkit-transform: rotate(-90deg);
36 | -moz-transform: rotate(-90deg);
37 | -ms-transform: rotate(-90deg);
38 | transform: rotate(-90deg);
39 | }
40 |
--------------------------------------------------------------------------------
/rollup.config.browser.js:
--------------------------------------------------------------------------------
1 | import resolve from "rollup-plugin-node-resolve";
2 | import commonjs from "rollup-plugin-commonjs";
3 | import replace from "rollup-plugin-replace";
4 | import { uglify } from "rollup-plugin-uglify";
5 |
6 | export default {
7 | input: "src/Index.js",
8 | output: {
9 | file: "bundledOutputs/rollup.js",
10 | format: "iife",
11 | name: "main"
12 | },
13 | plugins: [
14 | resolve(),
15 | commonjs({
16 | include: "node_modules/**",
17 | namedExports: {
18 | // left-hand side can be an absolute path, a path
19 | // relative to the current directory, or the name
20 | // of a module in node_modules
21 | "react-dom": ["render", "hydrate"],
22 | react: ["createElement", "useReducer"]
23 | }
24 | }),
25 | replace({
26 | "process.env.NODE_ENV": "'production'"
27 | }),
28 | uglify()
29 | ]
30 | };
31 |
--------------------------------------------------------------------------------
/doc/SumOperations.md:
--------------------------------------------------------------------------------
1 | ```
2 | |- typ1+typ2=typ |-o1+o2=o p1+p2=p
3 | ——————————————————————————————————————————————
4 | |- (typ1,o1)::p1 + (typ2,o2)::p2 = (typ::o)::p
5 | ```
6 |
7 | ```
8 | |- notOpt+o = o+notOpt = o |- opt(p1)+opt(p2) = opt(p1+p2)
9 | ```
10 |
11 |
12 | ```
13 | |- empty + typ = typ + empty = typ
14 | ```
15 |
16 | ```
17 | |- number + number = number
18 | ```
19 |
20 | ```
21 | |- string + string = string
22 | ```
23 |
24 | ```
25 | |- boolean + boolean = boolean
26 | ```
27 |
28 | ```
29 | |- styp1 + styp1’ = styp1’’ … |- stypn + stypn’ = stypn’’
30 | |- o’’ = o + o’
31 | —————————————————————————————————————————————————————————
32 | |- ({x1:styp1, …, xn:stypn}, o) :: p +
33 | ({x1:styp1’, …, xn:stypn’}, o’) :: p’ =
34 | ({x1:styp1’’, …, xn:stypn’’}, o’’) :: p’’
35 | ```
36 |
37 | ```
38 | |- styp1 + styp2 = styp
39 | ————————————————————————————
40 | |- [styp1::p1] + [styp2::p2]
41 | ```
42 |
--------------------------------------------------------------------------------
/doc/TypeCheckingStyp.md:
--------------------------------------------------------------------------------
1 | ```
2 | |- 123 : number::1
3 | ```
4 |
5 | ```
6 | |- “abc” : string::1
7 | ```
8 |
9 | ```
10 | |- true : boolean::1 |- false : boolean::1
11 | ```
12 |
13 | ```
14 | ———————————————————-
15 | |- null : (typ?1 :: 1)
16 | ```
17 |
18 | ```
19 | |- val1:styp1 … |- valn:stypn
20 | —————————————————————————————————————————————————————
21 | |- {x1:val1, …, xn:valn} : {x1:styp1, …, xn:stypn}::1
22 | ```
23 |
24 | ```
25 | |- [] : [empty]
26 | ```
27 |
28 | ```
29 | |- val1:styp1 … |- valn:stypn |- styp1 + … + stypn = styp (n>0)
30 | ———————————————————————————————————————————————————————————————————
31 | |- [val1, …, valn] : [styp]
32 | ```
33 |
34 | weakening nullable:
35 |
36 | ```
37 | |- val : (typ::p)
38 | ——————————————————
39 | |- val : (typ?0 :: p)
40 | ```
41 |
42 | weakening obj:
43 |
44 | ```
45 | |- val : ({x1:styp1, …, xn:stypn}, o) :: p
46 | ———————————————————————————————————————————————————————
47 | |- val : ({x1:styp1, …, xn:stypn, x0:(typ :: 0)}, o) :: p
48 | ```
49 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Cristiano Calcagno
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 |
--------------------------------------------------------------------------------
/opinionated.css:
--------------------------------------------------------------------------------
1 | .node {
2 | -moz-transition: all 0.5s;
3 | -o-transition: all 0.5s;
4 | -ms-transition: all 0.5s;
5 | -webkit-transition: all 0.5s;
6 | transition: all 0.5s;
7 | border-radius: 3px;
8 | }
9 |
10 | .node:hover {
11 | background-color: rgb(220, 245, 243);
12 | cursor: pointer;
13 | }
14 |
15 | .info, .node {
16 | padding: 2px 10px 2px 5px;
17 | font: 14px Helvetica, Arial, sans-serif;
18 | -webkit-user-select: none;
19 | -moz-user-select: none;
20 | -ms-user-select: none;
21 | user-select: none;
22 | }
23 |
24 | .tree-view_arrow {
25 | -moz-transition: all 0.1s;
26 | -o-transition: all 0.1s;
27 | -ms-transition: all 0.1s;
28 | -webkit-transition: all 0.1s;
29 | transition: all 0.1s;
30 | }
31 |
32 | .tree-view_arrow-empty {
33 | color: yellow;
34 | }
35 |
36 | .column1 {
37 | float: right;
38 | width: 75%;
39 | }
40 |
41 | .column2 {
42 | float: left;
43 | width: 50%;
44 | }
45 |
46 | .column3 {
47 | float: left;
48 | width: 33.33%;
49 | }
50 |
51 | /* Clear floats after the columns */
52 | .row:after {
53 | content: "";
54 | display: table;
55 | clear: both;
56 | }
57 |
58 | .centerText {
59 | text-align: center;
60 | width: 66.666%;
61 | }
62 |
--------------------------------------------------------------------------------
/src/Index.js:
--------------------------------------------------------------------------------
1 | // Generated by BUCKLESCRIPT, PLEASE EDIT WITH CARE
2 |
3 | import * as React from "react";
4 | import * as Belt_List from "bs-platform/lib/es6/belt_List.js";
5 | import * as Belt_Array from "bs-platform/lib/es6/belt_Array.js";
6 | import * as ReactDOMRe from "reason-react/src/ReactDOMRe.js";
7 | import * as UI$ReactTemplate from "./UI.js";
8 | import * as Demo$ReactTemplate from "./Demo.js";
9 |
10 | function Index$Main(Props) {
11 | var diffs = Demo$ReactTemplate.test(/* () */0);
12 | return React.createElement("div", undefined, Belt_Array.mapWithIndex(Belt_List.toArray(diffs), (function (i, diff) {
13 | return React.createElement("div", {
14 | key: String(i)
15 | }, React.createElement("h3", undefined, "Sample " + String(i)), React.createElement(UI$ReactTemplate.Diff.make, {
16 | diff: diff
17 | }));
18 | })));
19 | }
20 |
21 | var Main = {
22 | make: Index$Main
23 | };
24 |
25 | ReactDOMRe.renderToElementWithId(React.createElement(Index$Main, { }), "main");
26 |
27 | export {
28 | Main ,
29 |
30 | }
31 | /* Not a pure module */
32 |
--------------------------------------------------------------------------------
/src/Samples.rei:
--------------------------------------------------------------------------------
1 | /**
2 | * Type for samples.
3 | *
4 | * Samples are obtained by adding types in a given order.
5 | * For each addition, the common type and sum type are updated.
6 | * The common type is the type in common to all samples.
7 | * The sum type is the aggregate sum of all samples.
8 | * There is access to the previous version of all values.
9 | */
10 | type t;
11 |
12 | /** Add one sample. */
13 | let add: (t, Styp.styp) => t;
14 |
15 | /**
16 | * Add a list as a single sample.
17 | * Equivalent to adding a single sample with the sum type of the elements.
18 | */
19 | let addMany: (t, list(Styp.styp)) => t;
20 |
21 | /** Empty samples. */
22 | let empty: t;
23 |
24 | /** Add the samples in the order they appear in the list. */
25 | let fromList: list(Styp.styp) => t;
26 |
27 | /** Get the list of diffs between the common type and each sample. */
28 | let getAllDiffs: t => list(Diff.t);
29 |
30 | /** Get the type in common to all the samples. */
31 | let getCommon: t => Styp.styp;
32 |
33 | /** Get the last sample added. */
34 | let getLast: t => Styp.styp;
35 |
36 | /** Get the second-last sample added. */
37 | let getPrev: t => Styp.styp;
38 |
39 | /** Get the common type as it was before adding the last sample. */
40 | let getPrevCommon: t => Styp.styp;
41 |
42 | /** Get the sum type as it was before adding the last sample. */
43 | let getPrevSum: t => Styp.styp;
44 |
45 | /** Get the type of the aggregate sum of all the samples. */
46 | let getSum: t => Styp.styp;
47 |
48 | /** Get the list of samples in the order they were added. */
49 | let toList: t => list(Styp.styp);
--------------------------------------------------------------------------------
/src/Samples.re:
--------------------------------------------------------------------------------
1 | type t = {
2 | samples: list(Styp.styp), /* stored in reverse order */
3 | sum: Styp.styp,
4 | prevSum: Styp.styp,
5 | common: Styp.styp,
6 | prevCommon: Styp.styp,
7 | };
8 |
9 | let empty = {
10 | samples: [],
11 | sum: Styp.stypEmpty,
12 | prevSum: Styp.stypEmpty,
13 | common: Styp.stypEmpty,
14 | prevCommon: Styp.stypEmpty,
15 | };
16 |
17 | let toList = ({samples}) => samples->Belt.List.reverse;
18 |
19 | let getSum = ({sum}) => sum;
20 |
21 | let getCommon = ({common}) => common;
22 |
23 | let getLast = ({samples}) =>
24 | switch (samples) {
25 | | [last, ..._] => last
26 | | [] => Styp.stypEmpty
27 | };
28 |
29 | let getPrev = ({samples}) =>
30 | switch (samples) {
31 | | [_, prev, ..._] => prev
32 | | [_]
33 | | [] => Styp.stypEmpty
34 | };
35 |
36 | let getPrevSum = ({prevSum}) => prevSum;
37 |
38 | let getPrevCommon = ({prevCommon}) => prevCommon;
39 |
40 | let add = (t, styp) => {
41 | let samples = [styp, ...t.samples];
42 | let prevSum = t.sum;
43 | let sum = TypeCheck.(++)(t.sum, styp);
44 | let prevCommon = t.common;
45 | let common =
46 | t.samples == []
47 | ? styp : Diff.diff(t.common, styp).stypB->Styp.stripDiffStyp;
48 | {samples, sum, prevSum, common, prevCommon};
49 | };
50 |
51 | let addMany = (t, styps) => {
52 | let styp = styps->(Belt.List.reduce(Styp.stypEmpty, TypeCheck.(++)));
53 | t->(add(styp));
54 | };
55 |
56 | let fromList = styps => styps->(Belt.List.reduce(empty, add));
57 |
58 | let getAllDiffs = t => {
59 | let common = t->getCommon;
60 | t->toList->(Belt.List.map(styp => Diff.diff(common, styp)));
61 | };
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "statistical-type-inference",
3 | "version": "0.1.0",
4 | "scripts": {
5 | "build": "bsb -make-world",
6 | "start": "bsb -make-world -w -ws _ ",
7 | "clean": "bsb -clean-world",
8 | "serve": "http-server",
9 | "webpack": "webpack",
10 | "parcel": "parcel build index-parcel.html --no-source-maps --out-dir bundledOutputs",
11 | "rollup": "rollup -c rollup.config.browser.js",
12 | "fastpack": "fpack build -o bundledOutputs -n fastpack.js --dev --no-cache",
13 | "test": "rm -rf .cache && time node_modules/fastpack/fpack.exe build -o bundledOutputs -n fastpack.js --dev --no-cache && time yarn webpack && time yarn parcel && time yarn rollup && wc bundledOutputs/*"
14 | },
15 | "keywords": [
16 | "BuckleScript"
17 | ],
18 | "author": "",
19 | "license": "MIT",
20 | "dependencies": {
21 | "react": "^16.10.2",
22 | "react-dom": "^16.10.2",
23 | "reason-react": "^0.7.0"
24 | },
25 | "devDependencies": {
26 | "@babel/core": "^7.0.0-beta.54",
27 | "@babel/plugin-proposal-class-properties": "^7.2.3",
28 | "@babel/preset-env": "^7.0.0-beta.54",
29 | "@babel/preset-flow": "^7.0.0-beta.54",
30 | "@babel/preset-react": "^7.0.0-beta.54",
31 | "babel-loader": "^8.0.0-beta.4",
32 | "bs-platform": "^6.2.0",
33 | "fastpack": "^0.9.1",
34 | "parcel-bundler": "^1.12.3",
35 | "rollup-plugin-commonjs": "^10.1.0",
36 | "rollup-plugin-node-resolve": "^5.2.0",
37 | "rollup-plugin-replace": "^2.2.0",
38 | "rollup-plugin-uglify": "^6.0.3",
39 | "webpack": "^4.41.0",
40 | "webpack-cli": "^3.1.2"
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/doc/Abduction.md:
--------------------------------------------------------------------------------
1 | ```
2 | |- typ1 + = typ2 |- o1 + = o2 |- p1 + = p2
3 | —————————————————————————————————————————————————————————————
4 | |- (typ1,o1)::p2 + <(typA,oA)::pA> = (typ2,o2)::p2
5 | ```
6 |
7 | ```
8 | |- notOpt + = notOpt
9 | ```
10 |
11 | ```
12 | |- notOpt + = opt(p)
13 | ```
14 |
15 | ```
16 | |- opt(p) + = opt(p)
17 | ```
18 |
19 | ```
20 | p2 > p1
21 | -----------------------------------
22 | |- opt(p1) + = opt(p2)
23 | ```
24 |
25 |
26 | ```
27 | |- empty + = typ
28 | ```
29 |
30 | ```
31 | |- number + = number
32 | ```
33 |
34 | ```
35 | |- string + = string
36 | ```
37 |
38 | ```
39 | |- boolean + = boolean
40 | ```
41 |
42 | ```
43 | |- {G1} + <{GA}> = {G2} |- styp1 + = styp2
44 | ———————————————————————————————————————————————————
45 | |- {G1, x:styp1} + <{GA, x:stypA}> = {G2, x:styp2}
46 | ```
47 |
48 | ```
49 | |- {G1} + <{GA}> = {G2} (x not in G1)
50 | —————————————————————————————————————————
51 | |- {G1} + <{GA, x:styp2}> = {G2, x:styp2}
52 | ```
53 |
54 | ```
55 | |- {G1} + <{GA}> = {} |- typ1 + = typ1 |- o1 + = notOpt
56 | —————————————————————————————————————————————————————————————————————
57 | |- {G1, x:((typ1,o1)::p1)} + <{GA, x:((typA,oA)::(-p1))}> = {}
58 | ```
59 |
60 | ```
61 | |- typ1 + <{G, x:(empty, notOpt)::0> = typ2
62 | ———————————————————————————————————————
63 | |- typ1 + <{G}> = typ2
64 | ```
65 |
66 | ```
67 | |- typ1 + <{}> = typ2
68 | ————————————————————
69 | |- typ1 + = typ2
70 | ```
71 |
72 | ```
73 | |- styp1 + = styp2
74 | ————————————————————————————————
75 | |- [styp1] + <[stypA]> = [styp2]
76 | ```
77 |
78 | ```
79 | |- typ1 + <[(empty, notOpt)::0]> = typ2
80 | ———————————————————————————————————
81 | |- typ1 + = typ2
82 | ```
83 |
--------------------------------------------------------------------------------
/src/Serialize.re:
--------------------------------------------------------------------------------
1 | /* wrap tags as they are not supported by json stringify */
2 | let tagID = "tag320775503";
3 | /* wrap undefined as it is not supported by json stringify */
4 | let undefinedID = "undefined290180182";
5 |
6 | [@bs.val] external isArray: 'a => bool = "Array.isArray";
7 |
8 | let copy = (o_: 'a): 'a => {
9 | let o = o_->Obj.magic;
10 | isArray(o)
11 | ? Belt.Array.copy(o)->Obj.magic
12 | : Js.Obj.assign(Js.Obj.empty(), o)->Obj.magic;
13 | };
14 | let rec wrapTag = o_ => {
15 | let o = Obj.magic(o_);
16 | switch (Js.typeof(o)) {
17 | | "undefined" => [|undefinedID|]->Obj.magic
18 | | "object" when o === Js.null => o
19 | | "object" =>
20 | let o1 = copy(o);
21 | let keys = Js.Dict.keys(o1);
22 | for (i in 1 to Belt.Array.length(keys)) {
23 | let key = keys->(Belt.Array.getExn(i - 1));
24 | o1->(Js.Dict.set(key, o->(Js.Dict.get(key))->Obj.magic->wrapTag));
25 | };
26 | let tagOpt = Js.Dict.get(o, "tag");
27 | switch (tagOpt) {
28 | | None => o1
29 | | Some(tag) => [|tagID, tag, o1|]->Obj.magic
30 | };
31 | | _ => o
32 | };
33 | };
34 | let rec unwrapTag = o_ => {
35 | let o = Obj.magic(o_);
36 | switch (Js.typeof(o)) {
37 | | "object" when o === Js.null => o
38 | | "object" =>
39 | switch (isArray(o) ? o->Belt.Array.get(0) : None) {
40 | | Some(id) when id == undefinedID => Js.undefined->Obj.magic
41 | | Some(id) when id == tagID =>
42 | let tag = o->(Belt.Array.getExn(1));
43 | let innerObj = o->(Belt.Array.getExn(2));
44 | innerObj->(Js.Dict.set("tag", tag));
45 | unwrapKeys(innerObj);
46 | innerObj;
47 | | _ =>
48 | let o1 = copy(o);
49 | unwrapKeys(o1);
50 | o1;
51 | }
52 | | _ => o
53 | };
54 | }
55 | and unwrapKeys = o => {
56 | let keys = Js.Dict.keys(o);
57 | for (i in 1 to Array.length(keys)) {
58 | let key = keys->(Belt.Array.getExn(i - 1));
59 | o->(Js.Dict.set(key, o->(Js.Dict.get(key))->Obj.magic->unwrapTag));
60 | };
61 | };
62 | let toString = o => {
63 | let o1 = wrapTag(o);
64 | Js.Json.stringify(o1);
65 | };
66 | let fromString = s => {
67 | let o = Js.Json.parseExn(s);
68 | unwrapTag(o);
69 | };
--------------------------------------------------------------------------------
/src/DynTypedJson.re:
--------------------------------------------------------------------------------
1 | open Styp;
2 |
3 | type t = {
4 | mutable json: Js.Json.t,
5 | styp: ref(styp),
6 | };
7 |
8 | let empty = () => {json: Js.Json.null, styp: ref(stypEmpty)};
9 |
10 | let log = x => {
11 | Js.log2("json:", x.json);
12 | Js.log2("styp:", (x.styp^)->stypToJson->Js.Json.stringify);
13 | };
14 |
15 | let assignJson = (x, json) => {
16 | x.styp := TypeCheck.(++)(x.styp^, TypeCheck.fromJson(json));
17 | x.json = json;
18 | };
19 |
20 | let getFld = (x, fld) => {
21 | if (x.styp^.o != NotOpt) {
22 | Js.log(
23 | "Type error: access field " ++ fld ++ " of object with nullable type",
24 | );
25 | log(x);
26 | assert(false);
27 | };
28 | let styp1 =
29 | switch (x.styp^.typ) {
30 | | Styp.Object(dict) =>
31 | let styp1 = dict->(Js.Dict.unsafeGet(fld));
32 | let stypP = x.styp^.p;
33 | let styp1P = styp1.p;
34 | let stypOP =
35 | switch (x.styp^.o) {
36 | | NotOpt => P.zero
37 | | Opt(p) => p
38 | };
39 | if (stypP != P.(++)(styp1P, stypOP)) {
40 | Js.log(
41 | "Type error: access field " ++ fld ++ " of object with optional type",
42 | );
43 | log(x);
44 | assert(false);
45 | };
46 | styp1;
47 | | _ => assert(false)
48 | };
49 | let json =
50 | switch (x.json->Js.Json.decodeObject) {
51 | | None => assert(false)
52 | | Some(dict) => dict->(Js.Dict.unsafeGet(fld))
53 | };
54 | {json, styp: ref(styp1)};
55 | };
56 |
57 | let null = empty();
58 |
59 | let makeNotNullable = x => {
60 | let styp = x.styp^;
61 | if (styp.o != NotOpt) {
62 | let stypOP =
63 | switch (styp.o) {
64 | | NotOpt => P.zero
65 | | Opt(p) => p
66 | };
67 | x.styp := {...styp, o: NotOpt, p: P.(styp.p -- stypOP)};
68 | };
69 | };
70 | let (==) = (x, y) => {
71 | let magicX = Obj.magic(x);
72 | let magicY = Obj.magic(y);
73 | switch (magicX == null, magicY == null) {
74 | | (true, true) => true
75 | | (true, false) =>
76 | makeNotNullable(magicY);
77 | false;
78 | | (false, true) =>
79 | makeNotNullable(magicX);
80 | false;
81 | | (false, false) => x == y
82 | };
83 | };
84 |
85 | let (!=) = (x, y) => !(x == y);
86 |
87 | let ref = json => {
88 | let x = empty();
89 | x->(assignJson(json));
90 | x;
91 | };
92 |
93 | let (:=) = assignJson;
94 |
95 | let (@) = getFld;
--------------------------------------------------------------------------------
/doc/UnionExtension.md:
--------------------------------------------------------------------------------
1 | ```
2 | styp = (typ:o)::p
3 | typ not of the form union(stypU)
4 | ————————————————————————————————
5 | |- u(styp) = styp
6 | ```
7 |
8 | ```
9 | styp = (typ:o)::p
10 | typ = union(stypU)
11 | ——————————————————
12 | |- u(styp) = stypU
13 | ```
14 |
15 | ```
16 | styp1 = (typ1,o1)::p1 styp2 = (typ2,o2)::p2
17 | (typ1 # typ2)
18 | u(styp1) = stypU1 u(styp2) = stypU2
19 | |- stypU1 + stypU2 = stypU |-o1+o2=o p1+p2=p
20 | ——————————————————————————————————————————————
21 | |- styp1 + styp2 = (union(stypU)::o)::p
22 | ```
23 |
24 | ```
25 | |- styp1 + styp2 = styp |- stypU1 + stypU2 = stypU
26 | ———————————————————————————————————————————————————————
27 | |- (styp1 | stypU1) + (styp2 | stypU2) = (styp | stypU)
28 | ```
29 |
30 | ```
31 | stypU1 # stypU2 (for all pairs)
32 | —————————————————————————————————————————————————————
33 | |- stypU1 + stypU2 = stypU1 | stypU2
34 | ```
35 |
36 | Diff is extended as follows:
37 |
38 | ```
39 | styp1 = (typ1,o1)::p1 styp2 = (typ2,o2)::p2
40 | typ1 = union(stypU1)
41 | typ2 not of the form union(-)
42 | stypU2 = styp2
43 | styp2' = (union(stypU2),o2)::p2
44 | |- + = styp1,styp2'
45 | ————————————————————————————————————————————
46 | |- + = styp1,styp2
47 | ```
48 |
49 | ```
50 | styp1 = (typ1,o1)::p1 styp2 = (typ2,o2)::p2
51 | typ1 not of the form union(-)
52 | typ2 = union(stypU2)
53 | stypU1 = styp1
54 | styp1' = (union(stypU1),o1)::p1
55 | |- + = styp1',styp2
56 | ————————————————————————————————————————————
57 | |- + = styp1,styp2
58 | ```
59 |
60 | ```
61 | styp1 = (typ1,o1)::p1 styp2 = (typ2,o2)::p2
62 | typ1 = union(stypU1) typ2 = union(stypU2)
63 | |- + = stypU1,stypU2
64 | oA1 = sumO(stypUA1) pA1 = sumP(stypUA1)
65 | oA2 = sumO(stypUA2) pA2 = sumP(stypUA2)
66 | oB = sumO(stypUB) pB = sumP(stypUB)
67 | stypA1 = (union(stypUA1),oA1)::pA1 stypA2 = (union(stypUA2),oA2)::pA2
68 | stypB = (union(stypUB),oB)::pB
69 | ——————————————————————————————————————————————————————————————————————
70 | |- + = styp1,styp2
71 | ```
72 |
73 | ```
74 | styp1 = (typ1,o1)::p1 styp2 = (typ2,o2)::p2
75 | |- typ1 + typ2 = typ
76 | |- + = styp1,styp2
77 | |- + = stypU1,stypU2
78 | stypUA1' = stypA1 | stypUA1 stypUA2' = stypA2 | stypUA2
79 | stypUB' = stypB | stypUB
80 | ——————————————————————————————————————————————————————————————————————
81 | |- + = (styp1 | stypU1),(styp2 | stypU2)
82 | ```
83 |
84 | ```
85 | stypU1 # stypU2 (for all pairs)
86 | ——————————————————————————————————————————————————————————————————————
87 | |- + = stypU1,stypU2
88 | ```
89 |
--------------------------------------------------------------------------------
/src/Serialize.js:
--------------------------------------------------------------------------------
1 | // Generated by BUCKLESCRIPT, PLEASE EDIT WITH CARE
2 |
3 | import * as Js_dict from "bs-platform/lib/es6/js_dict.js";
4 | import * as Belt_Array from "bs-platform/lib/es6/belt_Array.js";
5 | import * as Caml_option from "bs-platform/lib/es6/caml_option.js";
6 |
7 | var tagID = "tag320775503";
8 |
9 | var undefinedID = "undefined290180182";
10 |
11 | function copy(o_) {
12 | var match = Array.isArray(o_);
13 | if (match) {
14 | return o_.slice(0);
15 | } else {
16 | return Object.assign({ }, o_);
17 | }
18 | }
19 |
20 | function wrapTag(o_) {
21 | var match = typeof o_;
22 | switch (match) {
23 | case "object" :
24 | if (o_ === null) {
25 | return o_;
26 | } else {
27 | var o1 = copy(o_);
28 | var keys = Object.keys(o1);
29 | for(var i = 1 ,i_finish = keys.length; i <= i_finish; ++i){
30 | var key = Belt_Array.getExn(keys, i - 1 | 0);
31 | o1[key] = wrapTag(Js_dict.get(o_, key));
32 | }
33 | var tagOpt = Js_dict.get(o_, "tag");
34 | if (tagOpt !== undefined) {
35 | return /* array */[
36 | tagID,
37 | Caml_option.valFromOption(tagOpt),
38 | o1
39 | ];
40 | } else {
41 | return o1;
42 | }
43 | }
44 | case "undefined" :
45 | return /* array */[undefinedID];
46 | default:
47 | return o_;
48 | }
49 | }
50 |
51 | function unwrapTag(o_) {
52 | var match = typeof o_;
53 | if (match === "object" && o_ !== null) {
54 | var match$1 = Array.isArray(o_);
55 | var match$2 = match$1 ? Belt_Array.get(o_, 0) : undefined;
56 | if (match$2 !== undefined) {
57 | var id = Caml_option.valFromOption(match$2);
58 | if (id === undefinedID) {
59 | return ;
60 | } else if (id === tagID) {
61 | var tag = Belt_Array.getExn(o_, 1);
62 | var innerObj = Belt_Array.getExn(o_, 2);
63 | innerObj["tag"] = tag;
64 | unwrapKeys(innerObj);
65 | return innerObj;
66 | }
67 |
68 | }
69 | var o1 = copy(o_);
70 | unwrapKeys(o1);
71 | return o1;
72 | } else {
73 | return o_;
74 | }
75 | }
76 |
77 | function unwrapKeys(o) {
78 | var keys = Object.keys(o);
79 | for(var i = 1 ,i_finish = keys.length; i <= i_finish; ++i){
80 | var key = Belt_Array.getExn(keys, i - 1 | 0);
81 | o[key] = unwrapTag(Js_dict.get(o, key));
82 | }
83 | return /* () */0;
84 | }
85 |
86 | function toString(o) {
87 | return JSON.stringify(wrapTag(o));
88 | }
89 |
90 | function fromString(s) {
91 | return unwrapTag(JSON.parse(s));
92 | }
93 |
94 | export {
95 | toString ,
96 | fromString ,
97 |
98 | }
99 | /* No side effect */
100 |
--------------------------------------------------------------------------------
/doc/Diff.md:
--------------------------------------------------------------------------------
1 | Let `stypEmpty = (empty, notOpt, 0)`.
2 |
3 | ```
4 | |- + = (typ1,typ2) |- + = o1,o2
5 | pB = min(p1,p2) pA1 = p1-pB pA2 = p2-pB
6 | stypA1 = (typA1,oA1)::pA1 stypA2 = (typA2,oA2)::pA2
7 | stypB = (typB,oB)::pB
8 | ————————————————————————————————————————————————————————————————————
9 | |- + = (typ1,o1)::p1,(typ2,o2)::p2
10 | ```
11 |
12 | ```
13 | |- + = notOpt,o2
14 | ```
15 |
16 | ```
17 | |- + = o1,notOpt
18 | ```
19 |
20 | ```
21 | |- + = opt(p),opt(p)
22 | ```
23 |
24 | ```
25 | p1 < p2
26 | ————————————————————————————————————————————————————
27 | |- + = opt(p1),opt(p2)
28 | ```
29 |
30 | ```
31 | p2 < p1
32 | ————————————————————————————————————————————————————
33 | |- + = opt(p1),opt(p2)
34 | ```
35 |
36 | ```
37 | |- + = empty,typ2
38 | ```
39 |
40 | ```
41 | |- + = typ1,empty
42 | ```
43 |
44 | ```
45 | |- + = number,number
46 | ```
47 |
48 | ```
49 | |- + = string,string
50 | ```
51 |
52 | ```
53 | |- + = boolean,boolean
54 | ```
55 |
56 | ```
57 | |- <{GA1},{GA2}> + <{GB}> = {G1},{G2}
58 | |- + = styp1,styp2
59 | typA1 = stypA1==stypEmpty ? {GA1,x:stypA1} : {GA1}
60 | typA2 = stypA2==stypEmpty ? {GA2,x:stypA2} : {GA2}
61 | typB = {GB,x:stypB}
62 | ——————————————————————————————————————————————————————
63 | |- + = {G1,x:styp1},{G2,x2:styp2}
64 | ```
65 |
66 | ```
67 | |- <{GA1},{GA2}> + <{GB}> = {G1},{G2} (x not in G2)
68 | typA1 = styp1==stypEmpty ? {GA1,x:styp1} : {GA1}
69 | ————————————————————————————————————————————————————
70 | |- + <{GB}> = {G1,x:styp1},{G2}
71 | ```
72 |
73 | ```
74 | |- <{GA1},{GA2}> + <{GB}> = {G1},{G2} (x not in G1)
75 | typA2 = styp2==stypEmpty ? {GA2,x:styp2} : {GA2}
76 | ————————————————————————————————————————————————————
77 | |- <{GA1},tA2> + <{GB}> = {G1},{G2,x:styp2}
78 | ```
79 |
80 | ```
81 | |- <{},typA2> + = typ1,typ2
82 | —————————————————————————————————————
83 | |- + = typ1,typ2
84 | ```
85 |
86 | ```
87 | |- + = typ1,typ2
88 | —————————————————————————————————————
89 | |- + = typ1,typ2
90 | ```
91 |
92 | ```
93 | |- + = styp1,styp2
94 | ————————————————————————————————————————————————————
95 | |- <[stypA1],[stypA2]> + <[stypB]> = [styp1],[styp2]
96 | ```
97 |
98 | ```
99 | |- <[stypEmpty], typA2> + = typ1,typ2
100 | ————————————————————————————————————————————
101 | |- + = typ1,typ2
102 | ```
103 |
104 | ```
105 | |- + = typ1,typ2
106 | ————————————————————————————————————————————
107 | |- + = typ1,typ2
108 | ```
109 |
--------------------------------------------------------------------------------
/src/Samples.js:
--------------------------------------------------------------------------------
1 | // Generated by BUCKLESCRIPT, PLEASE EDIT WITH CARE
2 |
3 | import * as Belt_List from "bs-platform/lib/es6/belt_List.js";
4 | import * as Diff$ReactTemplate from "./Diff.js";
5 | import * as Styp$ReactTemplate from "./Styp.js";
6 | import * as TypeCheck$ReactTemplate from "./TypeCheck.js";
7 |
8 | var empty = /* record */[
9 | /* samples : [] */0,
10 | /* sum */Styp$ReactTemplate.stypEmpty,
11 | /* prevSum */Styp$ReactTemplate.stypEmpty,
12 | /* common */Styp$ReactTemplate.stypEmpty,
13 | /* prevCommon */Styp$ReactTemplate.stypEmpty
14 | ];
15 |
16 | function toList(param) {
17 | return Belt_List.reverse(param[/* samples */0]);
18 | }
19 |
20 | function getSum(param) {
21 | return param[/* sum */1];
22 | }
23 |
24 | function getCommon(param) {
25 | return param[/* common */3];
26 | }
27 |
28 | function getLast(param) {
29 | var samples = param[/* samples */0];
30 | if (samples) {
31 | return samples[0];
32 | } else {
33 | return Styp$ReactTemplate.stypEmpty;
34 | }
35 | }
36 |
37 | function getPrev(param) {
38 | var samples = param[/* samples */0];
39 | if (samples) {
40 | var match = samples[1];
41 | if (match) {
42 | return match[0];
43 | } else {
44 | return Styp$ReactTemplate.stypEmpty;
45 | }
46 | } else {
47 | return Styp$ReactTemplate.stypEmpty;
48 | }
49 | }
50 |
51 | function getPrevSum(param) {
52 | return param[/* prevSum */2];
53 | }
54 |
55 | function getPrevCommon(param) {
56 | return param[/* prevCommon */4];
57 | }
58 |
59 | function add(t, styp) {
60 | var samples_001 = t[/* samples */0];
61 | var samples = /* :: */[
62 | styp,
63 | samples_001
64 | ];
65 | var prevSum = t[/* sum */1];
66 | var sum = TypeCheck$ReactTemplate.$caret(t[/* sum */1], styp);
67 | var prevCommon = t[/* common */3];
68 | var match = t[/* samples */0] === /* [] */0;
69 | var common = match ? styp : Styp$ReactTemplate.stripDiffStyp(Diff$ReactTemplate.diff(t[/* common */3], styp)[/* stypB */4]);
70 | return /* record */[
71 | /* samples */samples,
72 | /* sum */sum,
73 | /* prevSum */prevSum,
74 | /* common */common,
75 | /* prevCommon */prevCommon
76 | ];
77 | }
78 |
79 | function addMany(t, styps) {
80 | var styp = Belt_List.reduce(styps, Styp$ReactTemplate.stypEmpty, TypeCheck$ReactTemplate.$caret);
81 | return add(t, styp);
82 | }
83 |
84 | function fromList(styps) {
85 | return Belt_List.reduce(styps, empty, add);
86 | }
87 |
88 | function getAllDiffs(t) {
89 | var common = getCommon(t);
90 | return Belt_List.map(toList(t), (function (styp) {
91 | return Diff$ReactTemplate.diff(common, styp);
92 | }));
93 | }
94 |
95 | export {
96 | add ,
97 | addMany ,
98 | empty ,
99 | fromList ,
100 | getAllDiffs ,
101 | getCommon ,
102 | getLast ,
103 | getPrev ,
104 | getPrevCommon ,
105 | getPrevSum ,
106 | getSum ,
107 | toList ,
108 |
109 | }
110 | /* No side effect */
111 |
--------------------------------------------------------------------------------
/src/Demo.re:
--------------------------------------------------------------------------------
1 | let testSmall = () => {
2 | let small = Js.Json.parseExn({| [{"name":null} ] |});
3 |
4 | let styp = TypeCheck.fromJson(small);
5 | Js.log(styp->Styp.stypToJson->Js.Json.stringify);
6 | };
7 |
8 | let logDiff = diff => Js.log(diff->Diff.toJson->Js.Json.stringify);
9 |
10 | let testSmallDiff = (~mode=TypeCheck.defaultMode, n) => {
11 | let examples = [|
12 | ({| {"x": "hello"} |}, {| {"x": null, "y":0} |}),
13 | ({| [ "hell", 0, "world"] |}, {| [ "hell", "o", "world"] |}),
14 | (
15 | {| ["h", "e", "l", "l", "o", "w", "o", "r", "l", "d"] |},
16 | {| ["h", "e", "l", "l", 0, "w", "o", "r", "l", "d"] |},
17 | ),
18 | ("[null,2,3,4]", "[3]"),
19 | ({| [{"x": {"y" : "hello"}}] |}, {| [{"x": {"z" : "hello"}}] |}),
20 | ("null", "3"),
21 | ({| [{"a":3},{"b":3}] |}, {| [] |}),
22 | ({| [null,{"b":3}] |}, {| [] |}),
23 | ({| [3, "hello", true, 4] |}, {| [1,2,3] |}),
24 | ({| 3 |}, {| "abc" |}),
25 | (
26 | {|[{"x": ["p","s"]}]|},
27 | {|[{"x":["p","b"]},
28 | {"x":["p","s"]},
29 | {"x": ["p","s"]}]|},
30 | ),
31 | |];
32 | let styp1 =
33 | examples
34 | ->(Belt.Array.getExn(n))
35 | ->fst
36 | ->Js.Json.parseExn
37 | ->(TypeCheck.fromJson(~mode));
38 | let styp2 =
39 | examples
40 | ->(Belt.Array.getExn(n))
41 | ->snd
42 | ->Js.Json.parseExn
43 | ->(TypeCheck.fromJson(~mode));
44 | let diff = Diff.diffCheck(styp1, styp2);
45 | logDiff(diff);
46 | diff;
47 | };
48 |
49 | let testSamples = (~mode=TypeCheck.defaultMode, ()) => {
50 | let styps =
51 | [{| {"x": 1, "y":"hello"} |}, {| {"x": 2} |}, {| {"x": 3, "y":null} |}]
52 | ->(Belt.List.map(Js.Json.parseExn))
53 | ->(Belt.List.map(TypeCheck.fromJson(~mode)));
54 | Samples.(styps->fromList->getAllDiffs);
55 | };
56 |
57 | /* let testBigDiff = () => {
58 | let styp1 =
59 | Query.reasonBronzeThread |. Js.Json.parseExn |. TypeCheck.fromJson;
60 | let styp2 =
61 | Query.reasonPlatinumThread |. Js.Json.parseExn |. TypeCheck.fromJson;
62 | let diff = Diff.diffCheck(styp1, styp2);
63 | logDiff(diff);
64 | diff;
65 | }; */
66 |
67 | let testDynamicallyTypedJson = () => {
68 | open! DynTypedJson;
69 |
70 | let json1 = Js.Json.parseExn({| null |});
71 | let json2 = Js.Json.parseExn({| {"b":3} |});
72 |
73 | let x = ref(json1);
74 | Js.log2("x:", x.json);
75 |
76 | x := json2;
77 | Js.log2("x:", x.json);
78 |
79 | if (x != null) {
80 | let x1 = x @ "b";
81 | Js.log2("x1:", x1.json);
82 | };
83 | };
84 |
85 | let testSerializer = (o, testName) => {
86 | let s = Serialize.toString(o);
87 | let o1 = Serialize.fromString(s);
88 | if (o != o1) {
89 | Js.log("Serialize " ++ testName);
90 | Js.log2("o:", o);
91 | Js.log2("s:", s);
92 | Js.log2("o1:", o1);
93 | assert(false);
94 | };
95 | o;
96 | };
97 |
98 | let res = [testSmallDiff(0)];
99 | /* let res = [testSmallDiff(~mode=TypeCheck.singletonMode, 2)]; */
100 | /* let res = [testBigDiff()]; */
101 | /* let res = testSamples(); */
102 | // let test = () => res->(testSerializer("diff"));
103 | let test = () => res;
104 |
105 | /* fails: serialization of functions
106 | let _ = (x => x + 1) |. testSerializer("function");
107 | */
108 |
109 | /* fails: serialization of cyclic values
110 | type cyclic =
111 | | C(cyclic);
112 | let rec cyclic = C(cyclic);
113 | let _ = cyclic |. testSerializer("cyclic");
114 | */
115 |
116 | testDynamicallyTypedJson();
--------------------------------------------------------------------------------
/src/TypeCheck.re:
--------------------------------------------------------------------------------
1 | open Styp;
2 |
3 | type mode = {singletonTypes: bool};
4 |
5 | let defaultMode = {singletonTypes: false};
6 | let singletonMode = {singletonTypes: true};
7 |
8 | let rec fromJson = (~mode=defaultMode, json: Js.Json.t): styp =>
9 | switch (Js.Json.classify(json)) {
10 | | JSONFalse => {
11 | typ: Boolean(mode.singletonTypes ? Some(false) : None),
12 | o: NotOpt,
13 | p: P.one,
14 | }
15 | | JSONTrue => {
16 | typ: Boolean(mode.singletonTypes ? Some(true) : None),
17 | o: NotOpt,
18 | p: P.one,
19 | }
20 | | JSONNull => {typ: Empty, o: Opt(P.one), p: P.one}
21 | | JSONString(x) => {
22 | typ: String(mode.singletonTypes ? Some(x) : None),
23 | o: NotOpt,
24 | p: P.one,
25 | }
26 | | JSONNumber(x) => {
27 | typ: Number(mode.singletonTypes ? Some(x) : None),
28 | o: NotOpt,
29 | p: P.one,
30 | }
31 | | JSONObject(dict) =>
32 | let do_entry = ((lbl, v)) => {
33 | let styp = fromJson(~mode, v);
34 | (lbl, styp);
35 | };
36 | {
37 | typ: Js.Dict.entries(dict)->(Belt.Array.map(do_entry))->makeObject,
38 | o: NotOpt,
39 | p: P.one,
40 | };
41 | | JSONArray(a) =>
42 | a
43 | ->(
44 | Belt.Array.reduce({typ: Empty, o: NotOpt, p: P.zero}, (styp, json) =>
45 | styp ++ fromJson(~mode, json)
46 | )
47 | )
48 | ->(styp => {typ: Array(styp), o: NotOpt, p: P.one})
49 | }
50 | and plusStyp = (styp1, styp2) => {
51 | let typ =
52 | switch (plusTyp(styp1.typ, styp2.typ)) {
53 | | Some(typ) => typ
54 | | None => plusUnion(styp1->stypToUnion, styp2->stypToUnion)
55 | };
56 | let o = plusO(styp1.o, styp2.o);
57 | open! P;
58 | let p = styp1.p ++ styp2.p;
59 | {typ, o, p};
60 | }
61 | and plusO = (o1, o2) =>
62 | switch (o1, o2) {
63 | | (NotOpt, o)
64 | | (o, NotOpt) => o
65 | | (Opt(p1), Opt(p2)) =>
66 | open! P;
67 | Opt(p1 ++ p2);
68 | }
69 | and plusTyp = (typ1, typ2): option(typ) =>
70 | switch (typ1, typ2) {
71 | | (Diff(typ, _, _), _) => plusTyp(typ, typ2)
72 | | (_, Diff(typ, _, _)) => plusTyp(typ1, typ)
73 | | (Empty, t)
74 | | (t, Empty) => t->Some
75 | | (Same(t1), t)
76 | | (t, Same(t1)) => plusTyp(t, t1)
77 | | (Number(x), Number(y)) => x == y ? Number(x)->Some : None
78 | | (String(x), String(y)) => x == y ? String(x)->Some : None
79 | | (Boolean(x), Boolean(y)) => x == y ? Boolean(x)->Some : None
80 | | (Object(d1), Object(d2)) =>
81 | let d = Js.Dict.empty();
82 | let doItem = ((lbl, styp)) =>
83 | switch (d->(Js.Dict.get(lbl))) {
84 | | None => d->(Js.Dict.set(lbl, styp))
85 | | Some(styp1) => d->(Js.Dict.set(lbl, styp ++ styp1))
86 | };
87 | d1->Js.Dict.entries->(Belt.Array.forEach(doItem));
88 | d2->Js.Dict.entries->(Belt.Array.forEach(doItem));
89 | d->Js.Dict.entries->makeObject->Some;
90 | | (Array(styp1), Array(styp2)) => (styp1 ++ styp2)->Array->Some
91 | | (Number(_), _)
92 | | (_, Number(_))
93 | | (String(_), _)
94 | | (_, String(_))
95 | | (Boolean(_), _)
96 | | (_, Boolean(_))
97 | | (Object(_), _)
98 | | (_, Object(_))
99 | | (Union(_), _)
100 | | (_, Union(_)) => None
101 | }
102 | and plusUnion = (styps1, styps2) => {
103 | let rec findMatch = (t, ts, acc) =>
104 | switch (ts) {
105 | | [t1, ...ts1] =>
106 | if (plusTyp(t.typ, t1.typ) != None) {
107 | Some((t1, acc->Belt.List.reverse->(Belt.List.concat(ts1))));
108 | } else {
109 | findMatch(t, ts1, [t1, ...acc]);
110 | }
111 | | [] => None
112 | };
113 | let rec plus = (ls1, ls2) =>
114 | switch (ls1, ls2) {
115 | | ([t1, ...ts1], _) =>
116 | switch (findMatch(t1, ls2, [])) {
117 | | None => [t1, ...plus(ts1, ls2)]
118 | | Some((t2, ts2)) => [plusStyp(t1, t2), ...plus(ts1, ts2)]
119 | }
120 | | ([], ts) => ts
121 | };
122 | plus(styps1, styps2)->makeUnion;
123 | }
124 | and (++) = (styp1, styp2) => plusStyp(styp1, styp2);
--------------------------------------------------------------------------------
/src/Styp.re:
--------------------------------------------------------------------------------
1 | module P: {
2 | type t;
3 | let zero: t;
4 | let one: t;
5 | let (++): (t, t) => t;
6 | let (--): (t, t) => t;
7 | let toString: t => string;
8 | let toFloat: t => float;
9 | } = {
10 | type t = int;
11 | let zero = 0;
12 | let one = 1;
13 |
14 | let (++) = (+);
15 |
16 | let (--) = (-);
17 | let toString = string_of_int;
18 | let toFloat = float_of_int;
19 | };
20 |
21 | type p = P.t;
22 |
23 | type o =
24 | | NotOpt
25 | | Opt(p);
26 |
27 | type typ =
28 | | Empty
29 | | Same(typ)
30 | | Number(option(float))
31 | | String(option(string))
32 | | Boolean(option(bool))
33 | | Object(Js.Dict.t(styp))
34 | | Array(styp)
35 | | Union(list(styp))
36 | | Diff(typ, styp, styp)
37 | and styp = {
38 | typ,
39 | o,
40 | p,
41 | };
42 |
43 | let string_of_bool = b => b ? "true" : "false";
44 |
45 | let constToString = typ =>
46 | switch (typ) {
47 | | Number(x) =>
48 | "number"
49 | ++ x->(Belt.Option.mapWithDefault("", f => ":" ++ Js.Float.toString(f)))
50 | | String(x) => "string" ++ x->(Belt.Option.mapWithDefault("", s => ":" ++ s))
51 | | Boolean(x) =>
52 | "boolean"
53 | ++ x->(Belt.Option.mapWithDefault("", b => ":" ++ string_of_bool(b)))
54 | | _ => assert(false)
55 | };
56 |
57 | let rec stripDiffStyp = styp => {...styp, typ: styp.typ->stripDiffTyp}
58 | and stripDiffTyp = typ =>
59 | switch (typ) {
60 | | Empty
61 | | Number(_)
62 | | String(_)
63 | | Boolean(_) => typ
64 | | Same(typ) => Same(typ->stripDiffTyp)
65 | | Object(d) => Js.Dict.map((. styp) => stripDiffStyp(styp), d)->Object
66 | | Array(styp) => Array(styp->stripDiffStyp)
67 | | Union(styps) => styps->(Belt.List.map(stripDiffStyp))->Union
68 | | Diff(t, _, _) => t->stripDiffTyp
69 | };
70 |
71 | let typIsEmpty = typ =>
72 | switch (typ) {
73 | | Empty
74 | | Same(_) => true
75 | | _ => false
76 | };
77 |
78 | let stypIsNull = (styp: styp) =>
79 | switch (styp.o) {
80 | | NotOpt => false
81 | | Opt(p) => styp.typ->typIsEmpty && styp.p == p
82 | };
83 |
84 | let stypEmpty = {typ: Empty, o: NotOpt, p: P.zero};
85 |
86 | let stypIsEmpty = styp =>
87 | switch (styp) {
88 | | {typ, o: NotOpt, p} when typ->typIsEmpty && p == P.zero => true
89 | | _ => false
90 | };
91 |
92 | let stypToUnion = styp =>
93 | switch (styp.typ) {
94 | | Union(styps) => styps
95 | | _ => [styp]
96 | };
97 |
98 | let compareEntries = ((lbl1: string, _), (lbl2: string, _)) =>
99 | compare(lbl1, lbl2);
100 |
101 | let makeObject = arr =>
102 | arr
103 | ->Belt.List.fromArray
104 | ->(Belt.List.sort(compareEntries))
105 | ->Js.Dict.fromList
106 | ->Object;
107 |
108 | let compareStyp = (x: styp, y: styp): int => compare(x, y);
109 | let makeUnion = styps => styps->(Belt.List.sort(compareStyp))->Union;
110 |
111 | let pToJson = p => p->P.toString->Js.Json.string;
112 |
113 | let rec stypToJson = (styp: styp): Js.Json.t => {
114 | let dict = Js.Dict.empty();
115 | dict->(Js.Dict.set("typ", styp.typ->typToJson));
116 | switch (styp.o) {
117 | | NotOpt => ()
118 | | Opt(p1) => dict->(Js.Dict.set("opt", p1->pToJson))
119 | };
120 | dict->(Js.Dict.set("p", styp.p->pToJson));
121 | dict->Js.Json.object_;
122 | }
123 | and typToJson = (typ: typ): Js.Json.t => {
124 | open Js.Json;
125 | let arr = a => a->Js.Dict.fromArray->object_;
126 | switch (typ) {
127 | | Empty => [|("kind", "Empty"->string)|]->Js.Dict.fromArray->object_
128 | | Same(typ) => [|("kind", "Same"->string), ("typ", typ->typToJson)|]->arr
129 | | Number(_)
130 | | String(_)
131 | | Boolean(_) =>
132 | let kind =
133 | (
134 | switch (typ) {
135 | | Number(_) => "Number"
136 | | String(_) => "String"
137 | | Boolean(_) => "Boolean"
138 | | _ => assert(false)
139 | }
140 | )
141 | ->string;
142 | [|("kind", kind), ("value", typ->constToString->string)|]->arr;
143 | | Object(d) =>
144 | let entries =
145 | Js.Dict.entries(d)
146 | ->(Belt.Array.map(((lbl, styp)) => (lbl, styp->stypToJson)))
147 | ->arr;
148 | [|("kind", "Object"->string), ("entries", entries)|]->arr;
149 | | Array(styp) =>
150 | let typ = styp->stypToJson;
151 | [|("kind", "Array"->string), ("typ", typ)|]->arr;
152 | | Union(styps) =>
153 | let entries =
154 | styps
155 | ->Belt.List.toArray
156 | ->(
157 | Belt.Array.mapWithIndex((i, styp) =>
158 | ("u" ++ string_of_int(i), styp->stypToJson)
159 | )
160 | )
161 | ->arr;
162 | [|("kind", "Union"->string), ("entries", entries)|]->arr;
163 | | Diff(t, lhs, rhs) =>
164 | let common = t->typToJson;
165 | let lhs = lhs->stypToJson;
166 | let rhs = rhs->stypToJson;
167 | [|
168 | ("kind", "Diff"->string),
169 | ("common", common),
170 | ("lhs", lhs),
171 | ("rhs", rhs),
172 | |]
173 | ->arr;
174 | };
175 | };
--------------------------------------------------------------------------------
/src/DynTypedJson.js:
--------------------------------------------------------------------------------
1 | // Generated by BUCKLESCRIPT, PLEASE EDIT WITH CARE
2 |
3 | import * as Js_json from "bs-platform/lib/es6/js_json.js";
4 | import * as Caml_obj from "bs-platform/lib/es6/caml_obj.js";
5 | import * as Caml_option from "bs-platform/lib/es6/caml_option.js";
6 | import * as Styp$ReactTemplate from "./Styp.js";
7 | import * as Caml_builtin_exceptions from "bs-platform/lib/es6/caml_builtin_exceptions.js";
8 | import * as TypeCheck$ReactTemplate from "./TypeCheck.js";
9 |
10 | function empty(param) {
11 | return /* record */[
12 | /* json */null,
13 | /* styp : record */[/* contents */Styp$ReactTemplate.stypEmpty]
14 | ];
15 | }
16 |
17 | function log(x) {
18 | console.log("json:", x[/* json */0]);
19 | console.log("styp:", JSON.stringify(Styp$ReactTemplate.stypToJson(x[/* styp */1][0])));
20 | return /* () */0;
21 | }
22 |
23 | function assignJson(x, json) {
24 | x[/* styp */1][0] = TypeCheck$ReactTemplate.$caret(x[/* styp */1][0], TypeCheck$ReactTemplate.fromJson(undefined, json));
25 | x[/* json */0] = json;
26 | return /* () */0;
27 | }
28 |
29 | function getFld(x, fld) {
30 | if (x[/* styp */1][0][/* o */1] !== /* NotOpt */0) {
31 | console.log("Type error: access field " + (fld + " of object with nullable type"));
32 | log(x);
33 | throw [
34 | Caml_builtin_exceptions.assert_failure,
35 | /* tuple */[
36 | "DynTypedJson.re",
37 | 26,
38 | 4
39 | ]
40 | ];
41 | }
42 | var match = x[/* styp */1][0][/* typ */0];
43 | var styp1;
44 | if (typeof match === "number") {
45 | throw [
46 | Caml_builtin_exceptions.assert_failure,
47 | /* tuple */[
48 | "DynTypedJson.re",
49 | 47,
50 | 11
51 | ]
52 | ];
53 | } else if (match.tag === /* Object */4) {
54 | var styp1$1 = match[0][fld];
55 | var stypP = x[/* styp */1][0][/* p */2];
56 | var styp1P = styp1$1[/* p */2];
57 | var match$1 = x[/* styp */1][0][/* o */1];
58 | var stypOP = match$1 ? match$1[0] : Styp$ReactTemplate.P.zero;
59 | if (Caml_obj.caml_notequal(stypP, Styp$ReactTemplate.P["^"](styp1P, stypOP))) {
60 | console.log("Type error: access field " + (fld + " of object with optional type"));
61 | log(x);
62 | throw [
63 | Caml_builtin_exceptions.assert_failure,
64 | /* tuple */[
65 | "DynTypedJson.re",
66 | 44,
67 | 8
68 | ]
69 | ];
70 | }
71 | styp1 = styp1$1;
72 | } else {
73 | throw [
74 | Caml_builtin_exceptions.assert_failure,
75 | /* tuple */[
76 | "DynTypedJson.re",
77 | 47,
78 | 11
79 | ]
80 | ];
81 | }
82 | var match$2 = Js_json.decodeObject(x[/* json */0]);
83 | var json;
84 | if (match$2 !== undefined) {
85 | json = Caml_option.valFromOption(match$2)[fld];
86 | } else {
87 | throw [
88 | Caml_builtin_exceptions.assert_failure,
89 | /* tuple */[
90 | "DynTypedJson.re",
91 | 51,
92 | 14
93 | ]
94 | ];
95 | }
96 | return /* record */[
97 | /* json */json,
98 | /* styp : record */[/* contents */styp1]
99 | ];
100 | }
101 |
102 | var $$null$1 = /* record */[
103 | /* json */null,
104 | /* styp : record */[/* contents */Styp$ReactTemplate.stypEmpty]
105 | ];
106 |
107 | function makeNotNullable(x) {
108 | var styp = x[/* styp */1][0];
109 | if (styp[/* o */1] !== /* NotOpt */0) {
110 | var match = styp[/* o */1];
111 | var stypOP = match ? match[0] : Styp$ReactTemplate.P.zero;
112 | x[/* styp */1][0] = /* record */[
113 | /* typ */styp[/* typ */0],
114 | /* o : NotOpt */0,
115 | /* p */Styp$ReactTemplate.P["--"](styp[/* p */2], stypOP)
116 | ];
117 | return /* () */0;
118 | } else {
119 | return 0;
120 | }
121 | }
122 |
123 | function $eq(x, y) {
124 | var match = Caml_obj.caml_equal(x, $$null$1);
125 | var match$1 = Caml_obj.caml_equal(y, $$null$1);
126 | if (match) {
127 | if (match$1) {
128 | return true;
129 | } else {
130 | makeNotNullable(y);
131 | return false;
132 | }
133 | } else if (match$1) {
134 | makeNotNullable(x);
135 | return false;
136 | } else {
137 | return Caml_obj.caml_equal(x, y);
138 | }
139 | }
140 |
141 | function $less$great(x, y) {
142 | return !$eq(x, y);
143 | }
144 |
145 | function ref(json) {
146 | var x = /* record */[
147 | /* json */null,
148 | /* styp : record */[/* contents */Styp$ReactTemplate.stypEmpty]
149 | ];
150 | assignJson(x, json);
151 | return x;
152 | }
153 |
154 | var $colon$eq = assignJson;
155 |
156 | var $at = getFld;
157 |
158 | export {
159 | empty ,
160 | log ,
161 | assignJson ,
162 | getFld ,
163 | $$null$1 as $$null,
164 | makeNotNullable ,
165 | $eq ,
166 | $less$great ,
167 | ref ,
168 | $colon$eq ,
169 | $at ,
170 |
171 | }
172 | /* null Not a pure module */
173 |
--------------------------------------------------------------------------------
/src/Demo.js:
--------------------------------------------------------------------------------
1 | // Generated by BUCKLESCRIPT, PLEASE EDIT WITH CARE
2 |
3 | import * as Caml_obj from "bs-platform/lib/es6/caml_obj.js";
4 | import * as Belt_List from "bs-platform/lib/es6/belt_List.js";
5 | import * as Belt_Array from "bs-platform/lib/es6/belt_Array.js";
6 | import * as Diff$ReactTemplate from "./Diff.js";
7 | import * as Styp$ReactTemplate from "./Styp.js";
8 | import * as Samples$ReactTemplate from "./Samples.js";
9 | import * as Caml_builtin_exceptions from "bs-platform/lib/es6/caml_builtin_exceptions.js";
10 | import * as Serialize$ReactTemplate from "./Serialize.js";
11 | import * as TypeCheck$ReactTemplate from "./TypeCheck.js";
12 | import * as DynTypedJson$ReactTemplate from "./DynTypedJson.js";
13 |
14 | function testSmall(param) {
15 | var small = JSON.parse(" [{\"name\":null} ] ");
16 | var styp = TypeCheck$ReactTemplate.fromJson(undefined, small);
17 | console.log(JSON.stringify(Styp$ReactTemplate.stypToJson(styp)));
18 | return /* () */0;
19 | }
20 |
21 | function logDiff(diff) {
22 | console.log(JSON.stringify(Diff$ReactTemplate.toJson(diff)));
23 | return /* () */0;
24 | }
25 |
26 | function testSmallDiff($staropt$star, n) {
27 | var mode = $staropt$star !== undefined ? $staropt$star : TypeCheck$ReactTemplate.defaultMode;
28 | var examples = /* array */[
29 | /* tuple */[
30 | " {\"x\": \"hello\"} ",
31 | " {\"x\": null, \"y\":0} "
32 | ],
33 | /* tuple */[
34 | " [ \"hell\", 0, \"world\"] ",
35 | " [ \"hell\", \"o\", \"world\"] "
36 | ],
37 | /* tuple */[
38 | " [\"h\", \"e\", \"l\", \"l\", \"o\", \"w\", \"o\", \"r\", \"l\", \"d\"] ",
39 | " [\"h\", \"e\", \"l\", \"l\", 0, \"w\", \"o\", \"r\", \"l\", \"d\"] "
40 | ],
41 | /* tuple */[
42 | "[null,2,3,4]",
43 | "[3]"
44 | ],
45 | /* tuple */[
46 | " [{\"x\": {\"y\" : \"hello\"}}] ",
47 | " [{\"x\": {\"z\" : \"hello\"}}] "
48 | ],
49 | /* tuple */[
50 | "null",
51 | "3"
52 | ],
53 | /* tuple */[
54 | " [{\"a\":3},{\"b\":3}] ",
55 | " [] "
56 | ],
57 | /* tuple */[
58 | " [null,{\"b\":3}] ",
59 | " [] "
60 | ],
61 | /* tuple */[
62 | " [3, \"hello\", true, 4] ",
63 | " [1,2,3] "
64 | ],
65 | /* tuple */[
66 | " 3 ",
67 | " \"abc\" "
68 | ],
69 | /* tuple */[
70 | "[{\"x\": [\"p\",\"s\"]}]",
71 | "[{\"x\":[\"p\",\"b\"]},\n {\"x\":[\"p\",\"s\"]},\n {\"x\": [\"p\",\"s\"]}]"
72 | ]
73 | ];
74 | var styp1 = TypeCheck$ReactTemplate.fromJson(mode, JSON.parse(Belt_Array.getExn(examples, n)[0]));
75 | var styp2 = TypeCheck$ReactTemplate.fromJson(mode, JSON.parse(Belt_Array.getExn(examples, n)[1]));
76 | var diff = Diff$ReactTemplate.diffCheck(styp1, styp2);
77 | logDiff(diff);
78 | return diff;
79 | }
80 |
81 | function testSamples($staropt$star, param) {
82 | var mode = $staropt$star !== undefined ? $staropt$star : TypeCheck$ReactTemplate.defaultMode;
83 | var partial_arg = mode;
84 | var styps = Belt_List.map(Belt_List.map(/* :: */[
85 | " {\"x\": 1, \"y\":\"hello\"} ",
86 | /* :: */[
87 | " {\"x\": 2} ",
88 | /* :: */[
89 | " {\"x\": 3, \"y\":null} ",
90 | /* [] */0
91 | ]
92 | ]
93 | ], (function (prim) {
94 | return JSON.parse(prim);
95 | })), (function (param) {
96 | return TypeCheck$ReactTemplate.fromJson(partial_arg, param);
97 | }));
98 | return Samples$ReactTemplate.getAllDiffs(Samples$ReactTemplate.fromList(styps));
99 | }
100 |
101 | function testDynamicallyTypedJson(param) {
102 | var json1 = JSON.parse(" null ");
103 | var json2 = JSON.parse(" {\"b\":3} ");
104 | var x = DynTypedJson$ReactTemplate.ref(json1);
105 | console.log("x:", x[/* json */0]);
106 | DynTypedJson$ReactTemplate.$colon$eq(x, json2);
107 | console.log("x:", x[/* json */0]);
108 | if (DynTypedJson$ReactTemplate.$less$great(x, DynTypedJson$ReactTemplate.$$null)) {
109 | var x1 = DynTypedJson$ReactTemplate.$at(x, "b");
110 | console.log("x1:", x1[/* json */0]);
111 | return /* () */0;
112 | } else {
113 | return 0;
114 | }
115 | }
116 |
117 | function testSerializer(o, testName) {
118 | var s = Serialize$ReactTemplate.toString(o);
119 | var o1 = Serialize$ReactTemplate.fromString(s);
120 | if (Caml_obj.caml_notequal(o, o1)) {
121 | console.log("Serialize " + testName);
122 | console.log("o:", o);
123 | console.log("s:", s);
124 | console.log("o1:", o1);
125 | throw [
126 | Caml_builtin_exceptions.assert_failure,
127 | /* tuple */[
128 | "Demo.re",
129 | 93,
130 | 4
131 | ]
132 | ];
133 | }
134 | return o;
135 | }
136 |
137 | var res_000 = testSmallDiff(undefined, 0);
138 |
139 | var res = /* :: */[
140 | res_000,
141 | /* [] */0
142 | ];
143 |
144 | function test(param) {
145 | return res;
146 | }
147 |
148 | testDynamicallyTypedJson(/* () */0);
149 |
150 | export {
151 | testSmall ,
152 | logDiff ,
153 | testSmallDiff ,
154 | testSamples ,
155 | testDynamicallyTypedJson ,
156 | testSerializer ,
157 | res ,
158 | test ,
159 |
160 | }
161 | /* res Not a pure module */
162 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## REInfer: Runtime Extended Inference
2 |
3 | REInfer performs **R**untime **E**xtended type **Infer**ence on json data. Compared to traditional types, the extended types incorporate some numerical information, such as the number of times a field appears in the data, or the number of times it is null.
4 |
5 | A facility is provided to compare inferred types. This follows the idea of a diff algorithm which takes two values and returns the difference. The difference consists of a common part plus two deltas. Deltas are applied using a sum operation for extended types. The diff algorithm borrows and extends ideas from abduction for shape analysis, applied to type theory instead of program logic.
6 |
7 | A simple UI is provided to experiment with the primitives: it can be used to visualize inferred types and their difference.
8 |
9 | There is a thought experiment exploring of the use of runtime type inference in conjunction with runtime type checking. This combination gives an instrumented semantics that can be used to execute programs. This instrumented semantics has the peculiar property that it can fail at run time in cases where the program is not statically typable. In contrast to ordinary testing, this also applies to programs that **do not fail** when executed under a normal semantics.
10 |
11 |
12 | ## Run Project
13 |
14 | ```sh
15 | npm install
16 | npm start
17 | npm run webpack
18 | # in another tab
19 | npm run serve
20 | ```
21 |
22 | Then open the served page in the browser and edit `Demo.re`.
23 |
24 |
25 | ### Example of type inference
26 |
27 | ```
28 | val1:
29 | {"x":"hello"}
30 |
31 | val2:
32 | {"x":null, "y":0}
33 | ```
34 |
35 | Type inference will produce types `styp1` and `styp2`
36 |
37 |
38 |
39 | The numerical information indicates that fied `x` occurs once. But in the second value it has optional type `? 1`, indicating that 1 (out of 1) value of x is null.
40 |
41 | Numbers begin to add up when using arrays, or when sampling multiple values.
42 | For example, `[null,2,3,4]` has this type:
43 |
44 |
45 |
46 |
47 | ### Example of diff
48 |
49 | Once the types for `val1` and `val2` have been computed, a difference algorithm computes a type `stypB`:
50 |
51 |
52 |
53 | The type highlights what sub-parts which are in common. Also, `lhs` highlights the subpart that the first type has in addition to the common part, and correspondingly for `rhs`.
54 |
55 | It is also possible to look at `stypA1` and `stypA2` that indicate the overall difference between the common type and the two resulting ones:
56 |
57 |
58 |
59 | ### Example of union types
60 |
61 | Some data formats allow differet types of data in the same position.
62 |
63 | For example: `[ "hell", 0, "world"]` has this inferred type:
64 |
65 |
66 |
67 | Diff is also supported with union types.
68 |
69 | ### Example of singleton types
70 |
71 | While by default basic types are at the granularity of string/number/boolean, it's possible to turn on singleton types mode so each constant has a different type.
72 |
73 | For example, `["h", "e", "l", "l", "o", "w", "o", "r", "l", "d"]` in singleton type mode has inferred type:
74 |
75 |
76 |
77 |
78 | ## References
79 |
80 | * Sean Grove's talk at ReasonConf 2018 .
81 |
82 | * Tree view UI adapted from [react-treeview](https://github.com/chenglou/react-treeview).
83 |
84 | * Abduction in shape analysis .
85 |
86 | ## License
87 |
88 | This project is [MIT-licensed](./LICENSE.md).
89 |
90 |
91 | ## Formalisation
92 |
93 |
94 | ### Values
95 |
96 | ```
97 | val ::=
98 | 123 |
99 | “abc” |
100 | true | false |
101 | null |
102 | obj |
103 | [ val1, …, valn ]
104 |
105 | obj ::= { x1:val, …, xn:val }
106 | ```
107 |
108 |
109 | ### Types
110 |
111 | Types: `t` are ordinary types of a programming language, with `t?` an optional type.
112 |
113 | ```
114 | t ::=
115 | empty |
116 | number |
117 | string |
118 | boolean |
119 | t? |
120 | {x1:t, …, xn:t} |
121 | [ t ]
122 | ```
123 |
124 |
125 | ### Statistical Types
126 |
127 | Statistical types are a mutual definition of `styp` and `typ`.
128 |
129 | ```
130 | o ::= opt(p) | notOpt
131 | ```
132 |
133 | ```
134 | styp ::= (typ,o)::p
135 | ```
136 |
137 | ```
138 | typ ::=
139 | empty |
140 | number |
141 | string |
142 | boolean |
143 | {x1:styp1, …, xn:stypn} |
144 | [styp]
145 | ```
146 |
147 | Abbreviation: write `typ?n::p` or `typ::p`.
148 |
149 | ### Type checking for Types
150 |
151 | [Type checking for t](doc/TypeCheckingTypes.md): `|- val : t`
152 |
153 |
154 | ### Erasure of Statistical Types
155 |
156 | [Erasure](doc/Erasure.md): `|- |styp| = t`
157 |
158 |
159 | ### Type checking for Statistical Types
160 |
161 | [Type checking for styp](doc/TypeCheckingStyp.md): `|- val : styp`
162 |
163 |
164 | ### Sum of Statistical Types
165 |
166 | [Sum operations](doc/SumOperations.md): `|- styp1 + styp2 = styp` and `|- typ1 + typ2 = typ` and `|- o1 + o2 = o`.
167 |
168 | Notice this defines a partial commutative monoid.
169 |
170 |
171 | ### Inference of Statistical Types from value samples
172 |
173 | Given a set of sampled data `val1, …, valn` define a process of type inference `|- val1, …, valn -> styp`. The process consists of using the existing inference for arrays:
174 |
175 | ```
176 | |- [val1, …, valn] : [styp]
177 | ———————————————————————————
178 | |- val1, …, valn -> styp
179 | ```
180 |
181 |
182 | ### Abduction for Statistical Types
183 |
184 | [Abduction](doc/Abduction.md): `|- styp1 + = styp2` and `|- typ1 + = typ2` and `|- o1 + = o2`.
185 |
186 | With abduction, the inputs are `styp1` and `styp2`, and the output is `stypA`, the abduced value.
187 |
188 | There are many solutions to the abduction question. We want smallest solution w.r.t. `<=` where `styp1 <= styp2` if there is `styp` such that `|- styp1 + styp = styp2`.
189 |
190 | Abduction aims to compute the smallest representation of the difference between statistical types.
191 |
192 | It turns out that this form of difference is not sufficient because there are incomparable types and the negation corresponding to `+` does not exist. So another form of diff is required.
193 |
194 |
195 | ### Diff for Statistical Types
196 |
197 | [Diff](doc/Diff.md): `|- + = styp1,styp2`
198 | and `|- + = typ1,typ2` and `|- + = o1,o2`.
199 |
200 | The inputs are `styp1` and `styp2` and the outputs are `stypB` and `stypA1` and `stypA2`. The common part is `stypB` and the two deltas are `stypA1` and `stypA2`.
201 | The correctness critera are that `stypA1 + stypB = styp1` and `stypA2 + stypB = styp2`.
202 |
203 | There are many solutions to the diff question. We want the largest solution w.r.t. `<=` for the `B` part, and smallest for the `A1` and `A2` parts, where `styp1 <= styp2` if there is `styp` such that `|- styp1 + styp = styp2`.
204 |
205 |
206 | ### Extension: Union Types
207 |
208 | ```
209 | stypU ::= styp1 | ... | stypn
210 |
211 | typ += union(stypU)
212 | ```
213 |
214 | Write `typ1 # typ2` when there is no `typ` such that `|- typ1 + typ2 = typ`, with the corresponding extension `styp1 # styp2`.
215 |
216 | The sum of `stypU` written `|- stypU1 + stypU2 = stypU` is defined below.
217 | The conversion `|- u(styp) = stypU` is also defined below.
218 |
219 | [Union Extension](doc/UnionExtension.md) of `|- styp1 + styp2 = styp` and `|- + = styp1,styp2`.
220 |
221 |
222 | ### Extension: Singleton Types
223 |
224 | ```
225 | typ ::=
226 | number | 0 | 1 | 2 | ...
227 | string | "hello" | ...
228 | boolean | true | false
229 | ...
230 | ```
231 |
--------------------------------------------------------------------------------
/src/UI.re:
--------------------------------------------------------------------------------
1 | open Styp;
2 |
3 | module Key = {
4 | let counter = ref(0);
5 | let gen = () => {
6 | incr(counter);
7 | string_of_int(counter^);
8 | };
9 | };
10 |
11 | module Color = {
12 | let black = "#000000";
13 | let blue = "#0000FF";
14 | let brown = "#795E26";
15 | let green = "#09885A";
16 | let grey = "#979797";
17 | let red = "#D60A0A";
18 | let style = color => ReactDOMRe.Style.make(~color, ());
19 | };
20 |
21 | module TreeView = {
22 | type state = {collapsed: bool};
23 | type actions =
24 | | Click;
25 |
26 | let reducer = (state, action) => {
27 | switch (action) {
28 | | Click => {collapsed: !state.collapsed}
29 | };
30 | };
31 |
32 | [@bs.module "react"]
33 | external useReducer:
34 | ([@bs.uncurry] (('state, 'action) => 'state), 'state) =>
35 | ('state, (. 'action) => unit) =
36 | "useReducer";
37 |
38 | [@react.component]
39 | let make = (~nodeLabel, ~collapsed, ~child, _) => {
40 | let (state, dispatch) = useReducer(reducer, {collapsed: collapsed});
41 | let arrowClassName =
42 | "tree-view_arrow"
43 | ++ (state.collapsed ? " tree-view_arrow-collapsed" : "");
44 | let containerClassName =
45 | "tree-view_children"
46 | ++ (state.collapsed ? " tree-view_children-collapsed" : "");
47 |
48 |
49 |
dispatch(. Click)}>
50 |
51 |
nodeLabel
52 |
53 |
54 | {state.collapsed ? React.null : child}
55 |
56 |
;
57 | };
58 | };
59 |
60 | let node = (~style=?, x) =>
61 | {React.string(x)} ;
62 |
63 | let nodeGreen = x => x->(node(~style=Color.(style(green))));
64 |
65 | let nodeBrown = x => x->(node(~style=Color.(style(brown))));
66 |
67 | let questionMark = p =>
68 |
69 | {React.string(" ? " ++ P.toString(p))}
70 | ;
71 |
72 | type fmt = {
73 | plus: bool /* print '+' in front of number */,
74 | percent: bool /* show percentage instead of absolute numbers */,
75 | hideZeroOne: bool /* hide p when it's 0 or 1 */,
76 | hideP: bool /* hide p completely */,
77 | };
78 |
79 | let fmtDefault = {
80 | plus: false,
81 | percent: false,
82 | hideZeroOne: false,
83 | hideP: false,
84 | };
85 | let fmtDelta = {plus: true, percent: false, hideZeroOne: false, hideP: false};
86 |
87 | let rec toComponentStyp = (styp: styp, ~ctx: p, ~fmt: fmt): React.element => {
88 | let typ = styp.typ->(toComponentT(~ctx=styp.p, ~fmt));
89 | let style = Color.(style(stypIsNull(styp) ? red : black));
90 | let shouldAddDecorator =
91 | switch (styp.typ) {
92 | | Empty
93 | | Number(_)
94 | | String(_)
95 | | Boolean(_) => true
96 | | _ => false
97 | };
98 | stypIsNull(styp)
99 | ? {React.string("null")}
100 | :
101 | {shouldAddDecorator
102 | ? typ->(addDecorator(~styp, ~right=true, ~ctx, ~fmt)) : typ}
103 |
;
104 | }
105 | and addDecorator =
106 | (x: React.element, ~styp: styp, ~right, ~ctx: p, ~fmt: fmt): React.element => {
107 | let pUnchanged = ctx == P.zero && styp.p == P.zero;
108 | let pString =
109 | if (fmt.percent && ctx != P.zero) {
110 | (P.toFloat(styp.p) /. P.toFloat(ctx))->Js.Float.toString;
111 | } else {
112 | (fmt.plus ? "+" : "") ++ P.toString(styp.p);
113 | };
114 | let p =
115 | fmt.hideP || fmt.hideZeroOne && (pUnchanged || styp.p == P.one)
116 | ? React.null
117 | : {React.string(pString)} ;
118 | let o =
119 | switch (styp.o) {
120 | | NotOpt => React.null
121 | | Opt(p1) => questionMark(p1)
122 | };
123 | right ? x p o : p o x ;
124 | }
125 | and toComponentT = (typ: typ, ~ctx: p, ~fmt: fmt): React.element =>
126 | switch (typ) {
127 | | Empty => nodeBrown("empty")
128 | | Same(typ) =>
129 | (toComponentT(~ctx, ~fmt))}
134 | />
135 | | Number(_)
136 | | String(_)
137 | | Boolean(_) => typ->constToString->nodeGreen
138 | | Object(d) =>
139 | let doEntry = (i, (lbl, styp)) =>
140 |
143 | (addDecorator(~styp, ~right=true, ~ctx, ~fmt))
147 | }
148 | collapsed=false
149 | child={styp->(toComponentStyp(~ctx, ~fmt))}
150 | />
151 | ;
152 |
153 | React.array(Js.Dict.entries(d)->(Belt.Array.mapWithIndex(doEntry)));
154 | | Array(styp) when stypIsEmpty(styp) =>
155 |
156 | {node("[")->(addDecorator(~styp, ~right=false, ~ctx, ~fmt))}
157 | {node("]")}
158 |
159 | | Array(styp) =>
160 |
161 | (addDecorator(~styp, ~right=false, ~ctx, ~fmt))
164 | }
165 | collapsed=false
166 | child={styp->(toComponentStyp(~ctx, ~fmt))}
167 | />
168 | {node("]")}
169 |
170 | | Union(styps) =>
171 | let doEntry = (i, styp) =>
172 | (addDecorator(~styp, ~right=true, ~ctx, ~fmt))
177 | }
178 | collapsed=false
179 | child={styp->(toComponentStyp(~ctx, ~fmt))}
180 | />;
181 |
182 |
183 | "union"->nodeBrown
184 | {styps
185 | ->(Belt.List.mapWithIndex(doEntry))
186 | ->Belt.List.toArray
187 | ->React.array}
188 |
;
189 |
190 | | Diff(t, lhs, rhs) =>
191 | let side = (~left) => {
192 | let lbl = left ? "lhs" : "rhs";
193 | let styp = left ? lhs : rhs;
194 | nodeBrown}
197 | collapsed=false
198 | child={
199 | Styp.stypIsEmpty(styp)
200 | ? React.null : styp->(toComponentStyp(~ctx, ~fmt=fmtDelta))
201 | }
202 | />;
203 | };
204 |
205 |
{side(~left=true)}
206 |
207 | nodeBrown}
209 | collapsed=false
210 | child={t->(toComponentT(~ctx, ~fmt))}
211 | />
212 |
213 |
{side(~left=false)}
214 |
;
215 | };
216 |
217 | module Styp = {
218 | [@react.component]
219 | let make = (~name, ~styp, ~fmt=fmtDefault, _) => {
220 | (toComponentStyp(~ctx=P.zero, ~fmt))}
224 | />;
225 | };
226 | };
227 |
228 | module Diff = {
229 | [@react.component]
230 | let make = (~diff: Diff.t, _) => {
231 |
232 |
{React.string @@ "Inferred Types"}
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
{React.string @@ "Common Part"}
242 |
243 |
{React.string @@ "Deltas"}
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
;
253 | };
254 | };
--------------------------------------------------------------------------------
/src/Diff.re:
--------------------------------------------------------------------------------
1 | open Styp;
2 |
3 | type diffStyp = {
4 | styp1: styp,
5 | styp2: styp,
6 | stypA1: styp,
7 | stypA2: styp,
8 | stypB: styp,
9 | };
10 |
11 | type diffTyp = {
12 | typA1: typ,
13 | typA2: typ,
14 | typB: typ,
15 | };
16 |
17 | type diffUnion = {
18 | stypUA1: list(styp),
19 | stypUA2: list(styp),
20 | stypUB: list(styp),
21 | };
22 |
23 | type t = diffStyp;
24 |
25 | /* Inline the differences in the B part */
26 | let inlineDifferences = true;
27 |
28 | let rec diffStyp = (styp1: styp, styp2: styp): t =>
29 | switch (styp1.typ, styp2.typ) {
30 | | (Union(styps1), Union(styps2)) =>
31 | diffUnion(styp1, styp2, styps1, styps2)
32 | | (Union(styps1), _) => diffUnion(styp1, styp2, styps1, [styp2])
33 | | (_, Union(styps2)) => diffUnion(styp1, styp2, [styp1], styps2)
34 | | (typ1, typ2) when TypeCheck.plusTyp(typ1, typ2) == None =>
35 | diffUnion(styp1, styp2, [styp1], [styp2])
36 | | (typ1, typ2) =>
37 | let {typA1, typA2, typB} = diffTyp(typ1, typ2);
38 | let (oA1, oA2, oB) = diffO(styp1.o, styp2.o);
39 | let pB = min(styp1.p, styp2.p);
40 | let (pA1, pA2) = (P.(styp1.p -- pB), P.(styp2.p -- pB));
41 | let stypA1 = {typ: typA1, o: oA1, p: pA1};
42 | let stypA2 = {typ: typA2, o: oA2, p: pA2};
43 | let stypB = {typ: typB, o: oB, p: pB};
44 | open! TypeCheck;
45 | {styp1, styp2, stypA1, stypA2, stypB};
46 | }
47 | and diffO = (o1: o, o2: o): (o, o, o) =>
48 | switch (o1, o2) {
49 | | (NotOpt, _) => (NotOpt, o2, NotOpt)
50 | | (_, NotOpt) => (o1, NotOpt, NotOpt)
51 | | (Opt(p1), Opt(p2)) => (
52 | p1 > p2 ? Opt(P.(p1 -- p2)) : NotOpt,
53 | p2 > p1 ? Opt(P.(p2 -- p1)) : NotOpt,
54 | Opt(min(p1, p2)),
55 | )
56 | }
57 | and diffTyp = (typ1: typ, typ2: typ): diffTyp => {
58 | let makeSame = typ => {typA1: Same(typ), typA2: Same(typ), typB: typ};
59 | switch (typ1, typ2) {
60 | | (Empty | Same(_), _)
61 | | (_, Empty | Same(_)) => {typA1: typ1, typA2: typ2, typB: Empty}
62 | | (Diff(_), _) => {typA1: Empty, typA2: typ2, typB: Empty}
63 | | (_, Diff(_)) => {typA1: typ1, typA2: Empty, typB: Empty}
64 |
65 | | (Number(x), Number(y)) when x == y => makeSame(typ1)
66 | | (String(x), String(y)) when x == y => makeSame(typ1)
67 | | (Boolean(x), Boolean(y)) when x == y => makeSame(typ1)
68 |
69 | | (Object(d1), Object(d2)) =>
70 | let dA1 = Js.Dict.empty();
71 | let dA2 = Js.Dict.empty();
72 | let dB = Js.Dict.empty();
73 | let doItem2 = ((lbl, styp2)) =>
74 | switch (d1->(Js.Dict.get(lbl))) {
75 | | None =>
76 | if (!stypIsEmpty(styp2)) {
77 | dA2->(Js.Dict.set(lbl, styp2));
78 | }
79 | | Some(styp1) =>
80 | let {stypA1, stypA2, stypB} = diffStyp(styp1, styp2);
81 | if (!stypIsEmpty(stypA1)) {
82 | dA1->(Js.Dict.set(lbl, stypA1));
83 | };
84 | if (!stypIsEmpty(stypA2)) {
85 | dA2->(Js.Dict.set(lbl, stypA2));
86 | };
87 | dB->(Js.Dict.set(lbl, stypB));
88 | };
89 | let doItem1 = ((lbl, styp1)) =>
90 | switch (d2->(Js.Dict.get(lbl))) {
91 | | None =>
92 | if (!stypIsEmpty(styp1)) {
93 | dA1->(Js.Dict.set(lbl, styp1));
94 | }
95 | | Some(_) => ()
96 | };
97 | d2->Js.Dict.entries->(Belt.Array.forEach(doItem2));
98 | d1->Js.Dict.entries->(Belt.Array.forEach(doItem1));
99 | let entries1 = dA1->Js.Dict.entries;
100 | let entries2 = dA2->Js.Dict.entries;
101 | let typA1 = {
102 | let t = entries1->makeObject;
103 | Array.length(entries1) == 0 ? t->Same : t;
104 | };
105 | let typA2 = {
106 | let t = entries2->makeObject;
107 | Array.length(entries2) == 0 ? t->Same : t;
108 | };
109 | let typB = dB->Js.Dict.entries->makeObject;
110 | {typA1, typA2, typB};
111 |
112 | | (Array(styp1), Array(styp2)) =>
113 | let {stypA1, stypA2, stypB} = diffStyp(styp1, styp2);
114 | let typA1 = stypIsEmpty(stypA1) ? Same(Array(stypA1)) : Array(stypA1);
115 | let typA2 = stypIsEmpty(stypA2) ? Same(Array(stypA2)) : Array(stypA2);
116 | let typB = Array(stypB);
117 | {typA1, typA2, typB};
118 | | (Number(_), _)
119 | | (_, Number(_))
120 | | (String(_), _)
121 | | (_, String(_))
122 | | (Boolean(_), _)
123 | | (_, Boolean(_))
124 | | (Object(_), _)
125 | | (_, Object(_))
126 | | (Union(_), _)
127 | | (_, Union(_)) => assert(false)
128 | };
129 | }
130 | and diffUnion = (styp1, styp2, styps1: list(styp), styps2: list(styp)): t => {
131 | let rec findMatch = (t, ts, acc) =>
132 | switch (ts) {
133 | | [t1, ...ts1] =>
134 | if (TypeCheck.plusTyp(t.typ, t1.typ) != None) {
135 | Some((t1, acc->Belt.List.reverse->(Belt.List.concat(ts1))));
136 | } else {
137 | findMatch(t, ts1, [t1, ...acc]);
138 | }
139 | | [] => None
140 | };
141 | let rec plus = (ls1, ls2): diffUnion =>
142 | switch (ls1, ls2) {
143 | | ([t1, ...ts1], _) =>
144 | switch (findMatch(t1, ls2, [])) {
145 | | None =>
146 | let diffUnion = plus(ts1, ls2);
147 | {...diffUnion, stypUA1: [t1, ...diffUnion.stypUA1]};
148 | | Some((t2, ts2)) =>
149 | let {stypUA1, stypUA2, stypUB} = plus(ts1, ts2);
150 | let {stypA1, stypA2, stypB} = diffStyp(t1, t2);
151 | {
152 | stypUA1: [stypA1, ...stypUA1],
153 | stypUA2: [stypA2, ...stypUA2],
154 | stypUB: [stypB, ...stypUB],
155 | };
156 | }
157 | | ([], _) => {stypUA1: [], stypUA2: ls2, stypUB: []}
158 | };
159 | let {stypUA1, stypUA2, stypUB} = plus(styps1, styps2);
160 | let toUnion = styps =>
161 | switch (styps->(Belt.List.keep(styp => !stypIsEmpty(styp)))) {
162 | | [] => Empty
163 | | [styp] => styp.typ
164 | | styps1 => styps1->makeUnion
165 | };
166 | let toStyp = stypU => {
167 | let typ = stypU->toUnion;
168 | let p = stypU->(Belt.List.reduce(P.zero, (p, styp) => p->(P.(++)(styp.p))));
169 | let o =
170 | stypU->(
171 | Belt.List.reduce(NotOpt, (o, styp) => o->(TypeCheck.plusO(styp.o)))
172 | );
173 | {typ, o, p};
174 | };
175 | {
176 | styp1,
177 | styp2,
178 | stypA1: stypUA1->toStyp,
179 | stypA2: stypUA2->toStyp,
180 | stypB: stypUB->toStyp,
181 | };
182 | };
183 |
184 | let rec combineStyp = (stypA1: styp, stypA2: styp, stypB: styp): styp =>
185 | if (stypA1.p != P.zero
186 | || stypA1.o != NotOpt
187 | || stypA2.p != P.zero
188 | || stypA2.o != NotOpt) {
189 | {...stypB, typ: Diff(stypB.typ, stypA1, stypA2)};
190 | } else {
191 | {...stypB, typ: combineTyp(stypA1.typ, stypA2.typ, stypB.typ)};
192 | }
193 | and combineTyp = (typA1: typ, typA2: typ, typB: typ): typ =>
194 | switch (typA1, typA2, typB) {
195 | | (Array(_) | Empty | Same(_), Array(_) | Empty | Same(_), Array(stypB)) =>
196 | let getStyp = typ =>
197 | switch (typ) {
198 | | Array(styp) => styp
199 | | _ => stypEmpty
200 | };
201 | let stypA1 = typA1->getStyp;
202 | let stypA2 = typA2->getStyp;
203 | combineStyp(stypA1, stypA2, stypB)->Array;
204 | | (Object(_) | Empty | Same(_), Object(_) | Empty | Same(_), Object(dictB)) =>
205 | let d = Js.Dict.empty();
206 | let getDict = typ =>
207 | switch (typ) {
208 | | Object(dict) => dict
209 | | _ => Js.Dict.empty()
210 | };
211 | let dictA1 = typA1->getDict;
212 | let dictA2 = typA2->getDict;
213 | let doItem = lbl => {
214 | let getStyp = dict =>
215 | switch (dict->(Js.Dict.get(lbl))) {
216 | | None => stypEmpty
217 | | Some(styp) => styp
218 | };
219 | d->(
220 | Js.Dict.set(
221 | lbl,
222 | combineStyp(dictA1->getStyp, dictA2->getStyp, dictB->getStyp),
223 | )
224 | );
225 | };
226 | Belt.Set.String.(
227 | dictA1
228 | ->Js.Dict.keys
229 | ->fromArray
230 | ->(union(dictA2->Js.Dict.keys->fromArray))
231 | ->(union(dictB->Js.Dict.keys->fromArray))
232 | ->(forEach(doItem))
233 | );
234 | d->Js.Dict.entries->makeObject;
235 | | _ => typB
236 | };
237 |
238 | let diff = (styp1, styp2) => {
239 | let d = diffStyp(styp1, styp2);
240 | inlineDifferences
241 | ? {...d, stypB: combineStyp(d.stypA1, d.stypA2, d.stypB)} : d;
242 | };
243 | let diffCheck = (styp1, styp2) => {
244 | let d = diffStyp(styp1, styp2);
245 | open! TypeCheck;
246 | assert(d.stypB ++ d.stypA1 == styp1);
247 | assert(d.stypB ++ d.stypA2 == styp2);
248 | inlineDifferences
249 | ? {...d, stypB: combineStyp(d.stypA1, d.stypA2, d.stypB)} : d;
250 | };
251 |
252 | let toJson = (diff: t): Js.Json.t => {
253 | let styp1 = diff.styp1->stypToJson;
254 | let styp2 = diff.styp2->stypToJson;
255 | let stypB = diff.stypB->stypToJson;
256 | let stypA1 = diff.stypA1->stypToJson;
257 | let stypA2 = diff.stypA2->stypToJson;
258 | [|
259 | ("styp1", styp1),
260 | ("styp2", styp2),
261 | ("stypB", stypB),
262 | ("stypA1", stypA1),
263 | ("stypA2", stypA2),
264 | |]
265 | ->Js.Dict.fromArray
266 | ->Js.Json.object_;
267 | };
--------------------------------------------------------------------------------
/src/Styp.js:
--------------------------------------------------------------------------------
1 | // Generated by BUCKLESCRIPT, PLEASE EDIT WITH CARE
2 |
3 | import * as Block from "bs-platform/lib/es6/block.js";
4 | import * as Js_dict from "bs-platform/lib/es6/js_dict.js";
5 | import * as Caml_obj from "bs-platform/lib/es6/caml_obj.js";
6 | import * as Belt_List from "bs-platform/lib/es6/belt_List.js";
7 | import * as Belt_Array from "bs-platform/lib/es6/belt_Array.js";
8 | import * as Belt_Option from "bs-platform/lib/es6/belt_Option.js";
9 | import * as Caml_primitive from "bs-platform/lib/es6/caml_primitive.js";
10 | import * as Caml_builtin_exceptions from "bs-platform/lib/es6/caml_builtin_exceptions.js";
11 |
12 | function $caret(prim, prim$1) {
13 | return prim + prim$1 | 0;
14 | }
15 |
16 | function $neg$neg(prim, prim$1) {
17 | return prim - prim$1 | 0;
18 | }
19 |
20 | function toString(prim) {
21 | return String(prim);
22 | }
23 |
24 | function toFloat(prim) {
25 | return prim;
26 | }
27 |
28 | var P = {
29 | zero: 0,
30 | one: 1,
31 | "^": $caret,
32 | "--": $neg$neg,
33 | toString: toString,
34 | toFloat: toFloat
35 | };
36 |
37 | function string_of_bool(b) {
38 | if (b) {
39 | return "true";
40 | } else {
41 | return "false";
42 | }
43 | }
44 |
45 | function constToString(typ) {
46 | if (typeof typ === "number") {
47 | throw [
48 | Caml_builtin_exceptions.assert_failure,
49 | /* tuple */[
50 | "Styp.re",
51 | 54,
52 | 9
53 | ]
54 | ];
55 | } else {
56 | switch (typ.tag | 0) {
57 | case /* Number */1 :
58 | return "number" + Belt_Option.mapWithDefault(typ[0], "", (function (f) {
59 | return ":" + f.toString();
60 | }));
61 | case /* String */2 :
62 | return "string" + Belt_Option.mapWithDefault(typ[0], "", (function (s) {
63 | return ":" + s;
64 | }));
65 | case /* Boolean */3 :
66 | return "boolean" + Belt_Option.mapWithDefault(typ[0], "", (function (b) {
67 | return ":" + (
68 | b ? "true" : "false"
69 | );
70 | }));
71 | default:
72 | throw [
73 | Caml_builtin_exceptions.assert_failure,
74 | /* tuple */[
75 | "Styp.re",
76 | 54,
77 | 9
78 | ]
79 | ];
80 | }
81 | }
82 | }
83 |
84 | function stripDiffStyp(styp) {
85 | return /* record */[
86 | /* typ */stripDiffTyp(styp[/* typ */0]),
87 | /* o */styp[/* o */1],
88 | /* p */styp[/* p */2]
89 | ];
90 | }
91 |
92 | function stripDiffTyp(_typ) {
93 | while(true) {
94 | var typ = _typ;
95 | if (typeof typ === "number") {
96 | return typ;
97 | } else {
98 | switch (typ.tag | 0) {
99 | case /* Same */0 :
100 | return /* Same */Block.__(0, [stripDiffTyp(typ[0])]);
101 | case /* Object */4 :
102 | return /* Object */Block.__(4, [Js_dict.map(stripDiffStyp, typ[0])]);
103 | case /* Array */5 :
104 | return /* Array */Block.__(5, [stripDiffStyp(typ[0])]);
105 | case /* Union */6 :
106 | return /* Union */Block.__(6, [Belt_List.map(typ[0], stripDiffStyp)]);
107 | case /* Diff */7 :
108 | _typ = typ[0];
109 | continue ;
110 | default:
111 | return typ;
112 | }
113 | }
114 | };
115 | }
116 |
117 | function typIsEmpty(typ) {
118 | if (typeof typ === "number" || !typ.tag) {
119 | return true;
120 | } else {
121 | return false;
122 | }
123 | }
124 |
125 | function stypIsNull(styp) {
126 | var match = styp[/* o */1];
127 | if (match && typIsEmpty(styp[/* typ */0])) {
128 | return Caml_obj.caml_equal(styp[/* p */2], match[0]);
129 | } else {
130 | return false;
131 | }
132 | }
133 |
134 | var stypEmpty = /* record */[
135 | /* typ : Empty */0,
136 | /* o : NotOpt */0,
137 | /* p */0
138 | ];
139 |
140 | function stypIsEmpty(styp) {
141 | if (styp[/* o */1] || !typIsEmpty(styp[/* typ */0])) {
142 | return false;
143 | } else {
144 | return Caml_obj.caml_equal(styp[/* p */2], 0);
145 | }
146 | }
147 |
148 | function stypToUnion(styp) {
149 | var match = styp[/* typ */0];
150 | if (typeof match === "number" || match.tag !== /* Union */6) {
151 | return /* :: */[
152 | styp,
153 | /* [] */0
154 | ];
155 | } else {
156 | return match[0];
157 | }
158 | }
159 |
160 | function compareEntries(param, param$1) {
161 | return Caml_primitive.caml_string_compare(param[0], param$1[0]);
162 | }
163 |
164 | function makeObject(arr) {
165 | return /* Object */Block.__(4, [Js_dict.fromList(Belt_List.sort(Belt_List.fromArray(arr), compareEntries))]);
166 | }
167 |
168 | var compareStyp = Caml_obj.caml_compare;
169 |
170 | function makeUnion(styps) {
171 | return /* Union */Block.__(6, [Belt_List.sort(styps, compareStyp)]);
172 | }
173 |
174 | function pToJson(p) {
175 | return String(p);
176 | }
177 |
178 | function stypToJson(styp) {
179 | var dict = { };
180 | dict["typ"] = typToJson(styp[/* typ */0]);
181 | var match = styp[/* o */1];
182 | if (match) {
183 | dict["opt"] = String(match[0]);
184 | }
185 | dict["p"] = String(styp[/* p */2]);
186 | return dict;
187 | }
188 |
189 | function typToJson(typ) {
190 | if (typeof typ === "number") {
191 | return Js_dict.fromArray(/* array */[/* tuple */[
192 | "kind",
193 | "Empty"
194 | ]]);
195 | } else {
196 | switch (typ.tag | 0) {
197 | case /* Same */0 :
198 | return Js_dict.fromArray(/* array */[
199 | /* tuple */[
200 | "kind",
201 | "Same"
202 | ],
203 | /* tuple */[
204 | "typ",
205 | typToJson(typ[0])
206 | ]
207 | ]);
208 | case /* Object */4 :
209 | var entries = Js_dict.fromArray(Belt_Array.map(Js_dict.entries(typ[0]), (function (param) {
210 | return /* tuple */[
211 | param[0],
212 | stypToJson(param[1])
213 | ];
214 | })));
215 | return Js_dict.fromArray(/* array */[
216 | /* tuple */[
217 | "kind",
218 | "Object"
219 | ],
220 | /* tuple */[
221 | "entries",
222 | entries
223 | ]
224 | ]);
225 | case /* Array */5 :
226 | var typ$1 = stypToJson(typ[0]);
227 | return Js_dict.fromArray(/* array */[
228 | /* tuple */[
229 | "kind",
230 | "Array"
231 | ],
232 | /* tuple */[
233 | "typ",
234 | typ$1
235 | ]
236 | ]);
237 | case /* Union */6 :
238 | var entries$1 = Js_dict.fromArray(Belt_Array.mapWithIndex(Belt_List.toArray(typ[0]), (function (i, styp) {
239 | return /* tuple */[
240 | "u" + String(i),
241 | stypToJson(styp)
242 | ];
243 | })));
244 | return Js_dict.fromArray(/* array */[
245 | /* tuple */[
246 | "kind",
247 | "Union"
248 | ],
249 | /* tuple */[
250 | "entries",
251 | entries$1
252 | ]
253 | ]);
254 | case /* Diff */7 :
255 | var common = typToJson(typ[0]);
256 | var lhs = stypToJson(typ[1]);
257 | var rhs = stypToJson(typ[2]);
258 | return Js_dict.fromArray(/* array */[
259 | /* tuple */[
260 | "kind",
261 | "Diff"
262 | ],
263 | /* tuple */[
264 | "common",
265 | common
266 | ],
267 | /* tuple */[
268 | "lhs",
269 | lhs
270 | ],
271 | /* tuple */[
272 | "rhs",
273 | rhs
274 | ]
275 | ]);
276 | default:
277 | var kind;
278 | if (typeof typ === "number") {
279 | throw [
280 | Caml_builtin_exceptions.assert_failure,
281 | /* tuple */[
282 | "Styp.re",
283 | 138,
284 | 15
285 | ]
286 | ];
287 | } else {
288 | switch (typ.tag | 0) {
289 | case /* Number */1 :
290 | kind = "Number";
291 | break;
292 | case /* String */2 :
293 | kind = "String";
294 | break;
295 | case /* Boolean */3 :
296 | kind = "Boolean";
297 | break;
298 | default:
299 | throw [
300 | Caml_builtin_exceptions.assert_failure,
301 | /* tuple */[
302 | "Styp.re",
303 | 138,
304 | 15
305 | ]
306 | ];
307 | }
308 | }
309 | return Js_dict.fromArray(/* array */[
310 | /* tuple */[
311 | "kind",
312 | kind
313 | ],
314 | /* tuple */[
315 | "value",
316 | constToString(typ)
317 | ]
318 | ]);
319 | }
320 | }
321 | }
322 |
323 | export {
324 | P ,
325 | string_of_bool ,
326 | constToString ,
327 | stripDiffStyp ,
328 | stripDiffTyp ,
329 | typIsEmpty ,
330 | stypIsNull ,
331 | stypEmpty ,
332 | stypIsEmpty ,
333 | stypToUnion ,
334 | compareEntries ,
335 | makeObject ,
336 | compareStyp ,
337 | makeUnion ,
338 | pToJson ,
339 | stypToJson ,
340 | typToJson ,
341 |
342 | }
343 | /* No side effect */
344 |
--------------------------------------------------------------------------------
/src/TypeCheck.js:
--------------------------------------------------------------------------------
1 | // Generated by BUCKLESCRIPT, PLEASE EDIT WITH CARE
2 |
3 | import * as Block from "bs-platform/lib/es6/block.js";
4 | import * as Js_dict from "bs-platform/lib/es6/js_dict.js";
5 | import * as Js_json from "bs-platform/lib/es6/js_json.js";
6 | import * as Caml_obj from "bs-platform/lib/es6/caml_obj.js";
7 | import * as Belt_List from "bs-platform/lib/es6/belt_List.js";
8 | import * as Belt_Array from "bs-platform/lib/es6/belt_Array.js";
9 | import * as Styp$ReactTemplate from "./Styp.js";
10 |
11 | var defaultMode = /* record */[/* singletonTypes */false];
12 |
13 | function fromJson($staropt$star, json) {
14 | var mode = $staropt$star !== undefined ? $staropt$star : defaultMode;
15 | var match = Js_json.classify(json);
16 | if (typeof match === "number") {
17 | switch (match) {
18 | case /* JSONFalse */0 :
19 | var match$1 = mode[/* singletonTypes */0];
20 | return /* record */[
21 | /* typ : Boolean */Block.__(3, [match$1 ? false : undefined]),
22 | /* o : NotOpt */0,
23 | /* p */Styp$ReactTemplate.P.one
24 | ];
25 | case /* JSONTrue */1 :
26 | var match$2 = mode[/* singletonTypes */0];
27 | return /* record */[
28 | /* typ : Boolean */Block.__(3, [match$2 ? true : undefined]),
29 | /* o : NotOpt */0,
30 | /* p */Styp$ReactTemplate.P.one
31 | ];
32 | case /* JSONNull */2 :
33 | return /* record */[
34 | /* typ : Empty */0,
35 | /* o : Opt */[Styp$ReactTemplate.P.one],
36 | /* p */Styp$ReactTemplate.P.one
37 | ];
38 |
39 | }
40 | } else {
41 | switch (match.tag | 0) {
42 | case /* JSONString */0 :
43 | var match$3 = mode[/* singletonTypes */0];
44 | return /* record */[
45 | /* typ : String */Block.__(2, [match$3 ? match[0] : undefined]),
46 | /* o : NotOpt */0,
47 | /* p */Styp$ReactTemplate.P.one
48 | ];
49 | case /* JSONNumber */1 :
50 | var match$4 = mode[/* singletonTypes */0];
51 | return /* record */[
52 | /* typ : Number */Block.__(1, [match$4 ? match[0] : undefined]),
53 | /* o : NotOpt */0,
54 | /* p */Styp$ReactTemplate.P.one
55 | ];
56 | case /* JSONObject */2 :
57 | var do_entry = function (param) {
58 | var styp = fromJson(mode, param[1]);
59 | return /* tuple */[
60 | param[0],
61 | styp
62 | ];
63 | };
64 | return /* record */[
65 | /* typ */Styp$ReactTemplate.makeObject(Belt_Array.map(Js_dict.entries(match[0]), do_entry)),
66 | /* o : NotOpt */0,
67 | /* p */Styp$ReactTemplate.P.one
68 | ];
69 | case /* JSONArray */3 :
70 | var styp = Belt_Array.reduce(match[0], /* record */[
71 | /* typ : Empty */0,
72 | /* o : NotOpt */0,
73 | /* p */Styp$ReactTemplate.P.zero
74 | ], (function (styp, json) {
75 | return plusStyp(styp, fromJson(mode, json));
76 | }));
77 | return /* record */[
78 | /* typ : Array */Block.__(5, [styp]),
79 | /* o : NotOpt */0,
80 | /* p */Styp$ReactTemplate.P.one
81 | ];
82 |
83 | }
84 | }
85 | }
86 |
87 | function plusO(o1, o2) {
88 | if (o1) {
89 | if (o2) {
90 | return /* Opt */[Styp$ReactTemplate.P["^"](o1[0], o2[0])];
91 | } else {
92 | return o1;
93 | }
94 | } else {
95 | return o2;
96 | }
97 | }
98 |
99 | function plusTyp(_typ1, _typ2) {
100 | while(true) {
101 | var typ2 = _typ2;
102 | var typ1 = _typ1;
103 | var exit = 0;
104 | var exit$1 = 0;
105 | if (typeof typ1 === "number") {
106 | exit$1 = 3;
107 | } else {
108 | switch (typ1.tag | 0) {
109 | case /* Number */1 :
110 | var x = typ1[0];
111 | if (typeof typ2 === "number") {
112 | exit = 2;
113 | } else {
114 | switch (typ2.tag | 0) {
115 | case /* Number */1 :
116 | var match = Caml_obj.caml_equal(x, typ2[0]);
117 | if (match) {
118 | return /* Number */Block.__(1, [x]);
119 | } else {
120 | return ;
121 | }
122 | case /* Array */5 :
123 | return ;
124 | case /* Diff */7 :
125 | exit$1 = 3;
126 | break;
127 | default:
128 |
129 | }
130 | }
131 | break;
132 | case /* String */2 :
133 | var x$1 = typ1[0];
134 | if (typeof typ2 === "number") {
135 | exit = 2;
136 | } else {
137 | switch (typ2.tag | 0) {
138 | case /* String */2 :
139 | var match$1 = Caml_obj.caml_equal(x$1, typ2[0]);
140 | if (match$1) {
141 | return /* String */Block.__(2, [x$1]);
142 | } else {
143 | return ;
144 | }
145 | case /* Array */5 :
146 | return ;
147 | case /* Diff */7 :
148 | exit$1 = 3;
149 | break;
150 | default:
151 |
152 | }
153 | }
154 | break;
155 | case /* Boolean */3 :
156 | var x$2 = typ1[0];
157 | if (typeof typ2 === "number") {
158 | exit = 2;
159 | } else {
160 | switch (typ2.tag | 0) {
161 | case /* Boolean */3 :
162 | var match$2 = Caml_obj.caml_equal(x$2, typ2[0]);
163 | if (match$2) {
164 | return /* Boolean */Block.__(3, [x$2]);
165 | } else {
166 | return ;
167 | }
168 | case /* Array */5 :
169 | return ;
170 | case /* Diff */7 :
171 | exit$1 = 3;
172 | break;
173 | default:
174 |
175 | }
176 | }
177 | break;
178 | case /* Object */4 :
179 | if (typeof typ2 === "number") {
180 | exit = 2;
181 | } else {
182 | switch (typ2.tag | 0) {
183 | case /* Object */4 :
184 | var d = { };
185 | var doItem = (function(d){
186 | return function doItem(param) {
187 | var styp = param[1];
188 | var lbl = param[0];
189 | var match = Js_dict.get(d, lbl);
190 | if (match !== undefined) {
191 | d[lbl] = plusStyp(styp, match);
192 | return /* () */0;
193 | } else {
194 | d[lbl] = styp;
195 | return /* () */0;
196 | }
197 | }
198 | }(d));
199 | Belt_Array.forEach(Js_dict.entries(typ1[0]), doItem);
200 | Belt_Array.forEach(Js_dict.entries(typ2[0]), doItem);
201 | return Styp$ReactTemplate.makeObject(Js_dict.entries(d));
202 | case /* Array */5 :
203 | return ;
204 | case /* Diff */7 :
205 | exit$1 = 3;
206 | break;
207 | default:
208 |
209 | }
210 | }
211 | break;
212 | case /* Array */5 :
213 | if (typeof typ2 === "number") {
214 | exit = 2;
215 | } else {
216 | switch (typ2.tag | 0) {
217 | case /* Array */5 :
218 | return /* Array */Block.__(5, [plusStyp(typ1[0], typ2[0])]);
219 | case /* Diff */7 :
220 | exit$1 = 3;
221 | break;
222 | default:
223 |
224 | }
225 | }
226 | break;
227 | case /* Diff */7 :
228 | _typ1 = typ1[0];
229 | continue ;
230 | default:
231 | exit$1 = 3;
232 | }
233 | }
234 | if (exit$1 === 3) {
235 | if (typeof typ2 === "number") {
236 | if (typeof typ1 === "number") {
237 | return typ2;
238 | } else {
239 | exit = 2;
240 | }
241 | } else if (typ2.tag === /* Diff */7) {
242 | _typ2 = typ2[0];
243 | continue ;
244 | } else if (typeof typ1 === "number") {
245 | return typ2;
246 | } else {
247 | exit = 2;
248 | }
249 | }
250 | if (exit === 2) {
251 | if (typeof typ2 === "number") {
252 | return typ1;
253 | } else if (typeof typ1 !== "number" && !typ1.tag) {
254 | _typ2 = typ1[0];
255 | _typ1 = typ2;
256 | continue ;
257 | }
258 |
259 | }
260 | if (typeof typ2 === "number") {
261 | return ;
262 | } else {
263 | switch (typ2.tag | 0) {
264 | case /* Same */0 :
265 | _typ2 = typ2[0];
266 | continue ;
267 | case /* Array */5 :
268 | return ;
269 | default:
270 | return ;
271 | }
272 | }
273 | };
274 | }
275 |
276 | function plusUnion(styps1, styps2) {
277 | var findMatch = function (t, _ts, _acc) {
278 | while(true) {
279 | var acc = _acc;
280 | var ts = _ts;
281 | if (ts) {
282 | var ts1 = ts[1];
283 | var t1 = ts[0];
284 | if (plusTyp(t[/* typ */0], t1[/* typ */0]) !== undefined) {
285 | return /* tuple */[
286 | t1,
287 | Belt_List.concat(Belt_List.reverse(acc), ts1)
288 | ];
289 | } else {
290 | _acc = /* :: */[
291 | t1,
292 | acc
293 | ];
294 | _ts = ts1;
295 | continue ;
296 | }
297 | } else {
298 | return ;
299 | }
300 | };
301 | };
302 | var plus = function (ls1, ls2) {
303 | if (ls1) {
304 | var ts1 = ls1[1];
305 | var t1 = ls1[0];
306 | var match = findMatch(t1, ls2, /* [] */0);
307 | if (match !== undefined) {
308 | var match$1 = match;
309 | return /* :: */[
310 | plusStyp(t1, match$1[0]),
311 | plus(ts1, match$1[1])
312 | ];
313 | } else {
314 | return /* :: */[
315 | t1,
316 | plus(ts1, ls2)
317 | ];
318 | }
319 | } else {
320 | return ls2;
321 | }
322 | };
323 | return Styp$ReactTemplate.makeUnion(plus(styps1, styps2));
324 | }
325 |
326 | function plusStyp(styp1, styp2) {
327 | var match = plusTyp(styp1[/* typ */0], styp2[/* typ */0]);
328 | var typ = match !== undefined ? match : plusUnion(Styp$ReactTemplate.stypToUnion(styp1), Styp$ReactTemplate.stypToUnion(styp2));
329 | var o = plusO(styp1[/* o */1], styp2[/* o */1]);
330 | var p = Styp$ReactTemplate.P["^"](styp1[/* p */2], styp2[/* p */2]);
331 | return /* record */[
332 | /* typ */typ,
333 | /* o */o,
334 | /* p */p
335 | ];
336 | }
337 |
338 | var $caret = plusStyp;
339 |
340 | var singletonMode = /* record */[/* singletonTypes */true];
341 |
342 | export {
343 | defaultMode ,
344 | singletonMode ,
345 | fromJson ,
346 | plusStyp ,
347 | plusO ,
348 | plusTyp ,
349 | plusUnion ,
350 | $caret ,
351 |
352 | }
353 | /* No side effect */
354 |
--------------------------------------------------------------------------------
/src/UI.js:
--------------------------------------------------------------------------------
1 | // Generated by BUCKLESCRIPT, PLEASE EDIT WITH CARE
2 |
3 | import * as React from "react";
4 | import * as Js_dict from "bs-platform/lib/es6/js_dict.js";
5 | import * as Caml_obj from "bs-platform/lib/es6/caml_obj.js";
6 | import * as Belt_List from "bs-platform/lib/es6/belt_List.js";
7 | import * as Belt_Array from "bs-platform/lib/es6/belt_Array.js";
8 | import * as Caml_option from "bs-platform/lib/es6/caml_option.js";
9 | import * as Styp$ReactTemplate from "./Styp.js";
10 |
11 | var counter = /* record */[/* contents */0];
12 |
13 | function gen(param) {
14 | counter[0] = counter[0] + 1 | 0;
15 | return String(counter[0]);
16 | }
17 |
18 | var Key = {
19 | counter: counter,
20 | gen: gen
21 | };
22 |
23 | var black = "#000000";
24 |
25 | var blue = "#0000FF";
26 |
27 | var brown = "#795E26";
28 |
29 | var green = "#09885A";
30 |
31 | var grey = "#979797";
32 |
33 | var red = "#D60A0A";
34 |
35 | function style(color) {
36 | return {
37 | color: color
38 | };
39 | }
40 |
41 | var Color = {
42 | black: black,
43 | blue: blue,
44 | brown: brown,
45 | green: green,
46 | grey: grey,
47 | red: red,
48 | style: style
49 | };
50 |
51 | function reducer(state, action) {
52 | return /* record */[/* collapsed */!state[/* collapsed */0]];
53 | }
54 |
55 | function UI$TreeView(Props) {
56 | var nodeLabel = Props.nodeLabel;
57 | var collapsed = Props.collapsed;
58 | var child = Props.child;
59 | var match = React.useReducer(reducer, /* record */[/* collapsed */collapsed]);
60 | var dispatch = match[1];
61 | var state = match[0];
62 | var match$1 = state[/* collapsed */0];
63 | var arrowClassName = "tree-view_arrow" + (
64 | match$1 ? " tree-view_arrow-collapsed" : ""
65 | );
66 | var match$2 = state[/* collapsed */0];
67 | var containerClassName = "tree-view_children" + (
68 | match$2 ? " tree-view_children-collapsed" : ""
69 | );
70 | var match$3 = state[/* collapsed */0];
71 | return React.createElement("div", {
72 | className: "tree-view"
73 | }, React.createElement("div", {
74 | className: "tree-view_item",
75 | onClick: (function (param) {
76 | return dispatch(/* Click */0);
77 | })
78 | }, React.createElement("div", {
79 | className: arrowClassName
80 | }), React.createElement("span", undefined, nodeLabel)), React.createElement("div", {
81 | className: containerClassName
82 | }, match$3 ? null : child));
83 | }
84 |
85 | var TreeView = {
86 | reducer: reducer,
87 | make: UI$TreeView
88 | };
89 |
90 | function node(style, x) {
91 | var tmp = {
92 | className: "node"
93 | };
94 | if (style !== undefined) {
95 | tmp.style = Caml_option.valFromOption(style);
96 | }
97 | return React.createElement("span", tmp, x);
98 | }
99 |
100 | function nodeGreen(x) {
101 | return node(Caml_option.some(style(green)), x);
102 | }
103 |
104 | function nodeBrown(x) {
105 | return node(Caml_option.some(style(brown)), x);
106 | }
107 |
108 | function questionMark(p) {
109 | return React.createElement("span", {
110 | style: style(red)
111 | }, " ? " + Styp$ReactTemplate.P.toString(p));
112 | }
113 |
114 | var fmtDefault = /* record */[
115 | /* plus */false,
116 | /* percent */false,
117 | /* hideZeroOne */false,
118 | /* hideP */false
119 | ];
120 |
121 | var fmtDelta = /* record */[
122 | /* plus */true,
123 | /* percent */false,
124 | /* hideZeroOne */false,
125 | /* hideP */false
126 | ];
127 |
128 | function toComponentStyp(styp, ctx, fmt) {
129 | var typ = toComponentT(styp[/* typ */0], styp[/* p */2], fmt);
130 | var match = Styp$ReactTemplate.stypIsNull(styp);
131 | var style$1 = style(match ? red : black);
132 | var match$1 = styp[/* typ */0];
133 | var shouldAddDecorator;
134 | if (typeof match$1 === "number") {
135 | shouldAddDecorator = true;
136 | } else {
137 | switch (match$1.tag | 0) {
138 | case /* Number */1 :
139 | case /* String */2 :
140 | case /* Boolean */3 :
141 | shouldAddDecorator = true;
142 | break;
143 | default:
144 | shouldAddDecorator = false;
145 | }
146 | }
147 | var match$2 = Styp$ReactTemplate.stypIsNull(styp);
148 | if (match$2) {
149 | return React.createElement("div", {
150 | className: "node",
151 | style: style$1
152 | }, "null");
153 | } else {
154 | return React.createElement("div", {
155 | style: style$1
156 | }, shouldAddDecorator ? addDecorator(typ, styp, true, ctx, fmt) : typ);
157 | }
158 | }
159 |
160 | function addDecorator(x, styp, right, ctx, fmt) {
161 | var pUnchanged = Caml_obj.caml_equal(ctx, Styp$ReactTemplate.P.zero) && Caml_obj.caml_equal(styp[/* p */2], Styp$ReactTemplate.P.zero);
162 | var pString;
163 | if (fmt[/* percent */1] && Caml_obj.caml_notequal(ctx, Styp$ReactTemplate.P.zero)) {
164 | pString = (Styp$ReactTemplate.P.toFloat(styp[/* p */2]) / Styp$ReactTemplate.P.toFloat(ctx)).toString();
165 | } else {
166 | var match = fmt[/* plus */0];
167 | pString = (
168 | match ? "+" : ""
169 | ) + Styp$ReactTemplate.P.toString(styp[/* p */2]);
170 | }
171 | var match$1 = fmt[/* hideP */3] || fmt[/* hideZeroOne */2] && (pUnchanged || Caml_obj.caml_equal(styp[/* p */2], Styp$ReactTemplate.P.one));
172 | var p = match$1 ? null : React.createElement("span", {
173 | style: style(blue)
174 | }, pString);
175 | var match$2 = styp[/* o */1];
176 | var o = match$2 ? questionMark(match$2[0]) : null;
177 | if (right) {
178 | return React.createElement("span", undefined, x, p, o);
179 | } else {
180 | return React.createElement("span", undefined, p, o, x);
181 | }
182 | }
183 |
184 | function toComponentT(typ, ctx, fmt) {
185 | if (typeof typ === "number") {
186 | return nodeBrown("empty");
187 | } else {
188 | switch (typ.tag | 0) {
189 | case /* Same */0 :
190 | return React.createElement(UI$TreeView, {
191 | nodeLabel: nodeBrown("same"),
192 | collapsed: true,
193 | child: toComponentT(typ[0], ctx, fmt),
194 | key: "same"
195 | });
196 | case /* Object */4 :
197 | var doEntry = function (i, param) {
198 | var styp = param[1];
199 | var match = Caml_obj.caml_equal(styp[/* p */2], Styp$ReactTemplate.P.zero);
200 | return React.createElement("span", {
201 | key: String(i),
202 | style: style(match ? grey : black)
203 | }, React.createElement(UI$TreeView, {
204 | nodeLabel: addDecorator(node(undefined, param[0]), styp, true, ctx, fmt),
205 | collapsed: false,
206 | child: toComponentStyp(styp, ctx, fmt),
207 | key: String(i)
208 | }));
209 | };
210 | return Belt_Array.mapWithIndex(Js_dict.entries(typ[0]), doEntry);
211 | case /* Array */5 :
212 | var styp = typ[0];
213 | if (Styp$ReactTemplate.stypIsEmpty(styp)) {
214 | return React.createElement("span", undefined, addDecorator(node(undefined, "["), styp, false, ctx, fmt), node(undefined, "]"));
215 | } else {
216 | var match = Caml_obj.caml_equal(styp[/* p */2], Styp$ReactTemplate.P.zero);
217 | return React.createElement("span", {
218 | style: style(match ? grey : black)
219 | }, React.createElement(UI$TreeView, {
220 | nodeLabel: addDecorator(node(undefined, "["), styp, false, ctx, fmt),
221 | collapsed: false,
222 | child: toComponentStyp(styp, ctx, fmt)
223 | }), node(undefined, "]"));
224 | }
225 | case /* Union */6 :
226 | var doEntry$1 = function (i, styp) {
227 | return React.createElement(UI$TreeView, {
228 | nodeLabel: addDecorator(node(undefined, "u" + String(i + 1 | 0)), styp, true, ctx, fmt),
229 | collapsed: false,
230 | child: toComponentStyp(styp, ctx, fmt),
231 | key: String(i)
232 | });
233 | };
234 | return React.createElement("div", undefined, nodeBrown("union"), Belt_List.toArray(Belt_List.mapWithIndex(typ[0], doEntry$1)));
235 | case /* Diff */7 :
236 | var rhs = typ[2];
237 | var lhs = typ[1];
238 | var side = function (left) {
239 | var lbl = left ? "lhs" : "rhs";
240 | var styp = left ? lhs : rhs;
241 | var match = Styp$ReactTemplate.stypIsEmpty(styp);
242 | return React.createElement(UI$TreeView, {
243 | nodeLabel: nodeBrown(lbl),
244 | collapsed: false,
245 | child: match ? null : toComponentStyp(styp, ctx, fmtDelta),
246 | key: lbl
247 | });
248 | };
249 | return React.createElement("div", {
250 | className: "row"
251 | }, React.createElement("div", {
252 | className: "column3"
253 | }, side(true)), React.createElement("div", {
254 | className: "column3"
255 | }, React.createElement(UI$TreeView, {
256 | nodeLabel: nodeBrown("common"),
257 | collapsed: false,
258 | child: toComponentT(typ[0], ctx, fmt)
259 | })), React.createElement("div", {
260 | className: "column3"
261 | }, side(false)));
262 | default:
263 | return nodeGreen(Styp$ReactTemplate.constToString(typ));
264 | }
265 | }
266 | }
267 |
268 | function UI$Styp(Props) {
269 | var name = Props.name;
270 | var styp = Props.styp;
271 | var match = Props.fmt;
272 | var fmt = match !== undefined ? match : fmtDefault;
273 | return React.createElement(UI$TreeView, {
274 | nodeLabel: node(undefined, name),
275 | collapsed: true,
276 | child: toComponentStyp(styp, Styp$ReactTemplate.P.zero, fmt)
277 | });
278 | }
279 |
280 | var Styp = {
281 | make: UI$Styp
282 | };
283 |
284 | function UI$Diff(Props) {
285 | var diff = Props.diff;
286 | return React.createElement("div", undefined, React.createElement("div", {
287 | className: "centerText"
288 | }, "Inferred Types"), React.createElement("div", {
289 | className: "row"
290 | }, React.createElement("div", {
291 | className: "column2"
292 | }, React.createElement(UI$Styp, {
293 | name: "styp1",
294 | styp: diff[/* styp1 */0]
295 | })), React.createElement("div", {
296 | className: "column2"
297 | }, React.createElement(UI$Styp, {
298 | name: "styp2",
299 | styp: diff[/* styp2 */1]
300 | }))), React.createElement("div", {
301 | className: "centerText"
302 | }, "Common Part"), React.createElement("div", undefined, React.createElement(UI$Styp, {
303 | name: "stypB",
304 | styp: diff[/* stypB */4]
305 | })), React.createElement("div", {
306 | className: "centerText"
307 | }, "Deltas"), React.createElement("div", {
308 | className: "row"
309 | }, React.createElement("div", {
310 | className: "column2"
311 | }, React.createElement(UI$Styp, {
312 | name: "stypA1",
313 | styp: diff[/* stypA1 */2],
314 | fmt: fmtDelta
315 | })), React.createElement("div", {
316 | className: "column2"
317 | }, React.createElement(UI$Styp, {
318 | name: "stypA2",
319 | styp: diff[/* stypA2 */3],
320 | fmt: fmtDelta
321 | }))));
322 | }
323 |
324 | var Diff = {
325 | make: UI$Diff
326 | };
327 |
328 | export {
329 | Key ,
330 | Color ,
331 | TreeView ,
332 | node ,
333 | nodeGreen ,
334 | nodeBrown ,
335 | questionMark ,
336 | fmtDefault ,
337 | fmtDelta ,
338 | toComponentStyp ,
339 | addDecorator ,
340 | toComponentT ,
341 | Styp ,
342 | Diff ,
343 |
344 | }
345 | /* react Not a pure module */
346 |
--------------------------------------------------------------------------------
/src/Diff.js:
--------------------------------------------------------------------------------
1 | // Generated by BUCKLESCRIPT, PLEASE EDIT WITH CARE
2 |
3 | import * as Block from "bs-platform/lib/es6/block.js";
4 | import * as Js_dict from "bs-platform/lib/es6/js_dict.js";
5 | import * as Caml_obj from "bs-platform/lib/es6/caml_obj.js";
6 | import * as Belt_List from "bs-platform/lib/es6/belt_List.js";
7 | import * as Belt_Array from "bs-platform/lib/es6/belt_Array.js";
8 | import * as Belt_SetString from "bs-platform/lib/es6/belt_SetString.js";
9 | import * as Styp$ReactTemplate from "./Styp.js";
10 | import * as Caml_builtin_exceptions from "bs-platform/lib/es6/caml_builtin_exceptions.js";
11 | import * as TypeCheck$ReactTemplate from "./TypeCheck.js";
12 |
13 | function diffStyp(styp1, styp2) {
14 | var match = styp1[/* typ */0];
15 | var match$1 = styp2[/* typ */0];
16 | var exit = 0;
17 | if (typeof match === "number" || match.tag !== /* Union */6) {
18 | exit = 2;
19 | } else {
20 | var styps1 = match[0];
21 | var exit$1 = 0;
22 | if (typeof match$1 === "number" || match$1.tag !== /* Union */6) {
23 | exit$1 = 3;
24 | } else {
25 | return diffUnion(styp1, styp2, styps1, match$1[0]);
26 | }
27 | if (exit$1 === 3) {
28 | return diffUnion(styp1, styp2, styps1, /* :: */[
29 | styp2,
30 | /* [] */0
31 | ]);
32 | }
33 |
34 | }
35 | if (exit === 2 && typeof match$1 !== "number" && match$1.tag === /* Union */6) {
36 | return diffUnion(styp1, styp2, /* :: */[
37 | styp1,
38 | /* [] */0
39 | ], match$1[0]);
40 | }
41 | if (TypeCheck$ReactTemplate.plusTyp(match, match$1) === undefined) {
42 | return diffUnion(styp1, styp2, /* :: */[
43 | styp1,
44 | /* [] */0
45 | ], /* :: */[
46 | styp2,
47 | /* [] */0
48 | ]);
49 | } else {
50 | var match$2 = diffTyp(match, match$1);
51 | var match$3 = diffO(styp1[/* o */1], styp2[/* o */1]);
52 | var pB = Caml_obj.caml_min(styp1[/* p */2], styp2[/* p */2]);
53 | var pA1 = Styp$ReactTemplate.P["--"](styp1[/* p */2], pB);
54 | var pA2 = Styp$ReactTemplate.P["--"](styp2[/* p */2], pB);
55 | var stypA1_000 = /* typ */match$2[/* typA1 */0];
56 | var stypA1_001 = /* o */match$3[0];
57 | var stypA1 = /* record */[
58 | stypA1_000,
59 | stypA1_001,
60 | /* p */pA1
61 | ];
62 | var stypA2_000 = /* typ */match$2[/* typA2 */1];
63 | var stypA2_001 = /* o */match$3[1];
64 | var stypA2 = /* record */[
65 | stypA2_000,
66 | stypA2_001,
67 | /* p */pA2
68 | ];
69 | var stypB_000 = /* typ */match$2[/* typB */2];
70 | var stypB_001 = /* o */match$3[2];
71 | var stypB = /* record */[
72 | stypB_000,
73 | stypB_001,
74 | /* p */pB
75 | ];
76 | return /* record */[
77 | /* styp1 */styp1,
78 | /* styp2 */styp2,
79 | /* stypA1 */stypA1,
80 | /* stypA2 */stypA2,
81 | /* stypB */stypB
82 | ];
83 | }
84 | }
85 |
86 | function diffO(o1, o2) {
87 | if (o1) {
88 | if (o2) {
89 | var p2 = o2[0];
90 | var p1 = o1[0];
91 | var match = Caml_obj.caml_greaterthan(p1, p2);
92 | var match$1 = Caml_obj.caml_greaterthan(p2, p1);
93 | return /* tuple */[
94 | match ? /* Opt */[Styp$ReactTemplate.P["--"](p1, p2)] : /* NotOpt */0,
95 | match$1 ? /* Opt */[Styp$ReactTemplate.P["--"](p2, p1)] : /* NotOpt */0,
96 | /* Opt */[Caml_obj.caml_min(p1, p2)]
97 | ];
98 | } else {
99 | return /* tuple */[
100 | o1,
101 | /* NotOpt */0,
102 | /* NotOpt */0
103 | ];
104 | }
105 | } else {
106 | return /* tuple */[
107 | /* NotOpt */0,
108 | o2,
109 | /* NotOpt */0
110 | ];
111 | }
112 | }
113 |
114 | function diffTyp(typ1, typ2) {
115 | var makeSame = function (typ) {
116 | return /* record */[
117 | /* typA1 : Same */Block.__(0, [typ]),
118 | /* typA2 : Same */Block.__(0, [typ]),
119 | /* typB */typ
120 | ];
121 | };
122 | var exit = 0;
123 | var exit$1 = 0;
124 | if (typeof typ1 === "number") {
125 | exit$1 = 3;
126 | } else {
127 | switch (typ1.tag | 0) {
128 | case /* Number */1 :
129 | if (typeof typ2 === "number") {
130 | exit$1 = 3;
131 | } else {
132 | switch (typ2.tag | 0) {
133 | case /* Same */0 :
134 | exit$1 = 3;
135 | break;
136 | case /* Number */1 :
137 | if (Caml_obj.caml_equal(typ1[0], typ2[0])) {
138 | return makeSame(typ1);
139 | } else {
140 | exit = 2;
141 | }
142 | break;
143 | case /* Array */5 :
144 | break;
145 | default:
146 | exit = 2;
147 | }
148 | }
149 | break;
150 | case /* String */2 :
151 | if (typeof typ2 === "number") {
152 | exit$1 = 3;
153 | } else {
154 | switch (typ2.tag | 0) {
155 | case /* Same */0 :
156 | exit$1 = 3;
157 | break;
158 | case /* String */2 :
159 | if (Caml_obj.caml_equal(typ1[0], typ2[0])) {
160 | return makeSame(typ1);
161 | } else {
162 | exit = 2;
163 | }
164 | break;
165 | case /* Array */5 :
166 | break;
167 | default:
168 | exit = 2;
169 | }
170 | }
171 | break;
172 | case /* Boolean */3 :
173 | if (typeof typ2 === "number") {
174 | exit$1 = 3;
175 | } else {
176 | switch (typ2.tag | 0) {
177 | case /* Same */0 :
178 | exit$1 = 3;
179 | break;
180 | case /* Boolean */3 :
181 | if (Caml_obj.caml_equal(typ1[0], typ2[0])) {
182 | return makeSame(typ1);
183 | } else {
184 | exit = 2;
185 | }
186 | break;
187 | case /* Array */5 :
188 | break;
189 | default:
190 | exit = 2;
191 | }
192 | }
193 | break;
194 | case /* Object */4 :
195 | var d1 = typ1[0];
196 | if (typeof typ2 === "number") {
197 | exit$1 = 3;
198 | } else {
199 | switch (typ2.tag | 0) {
200 | case /* Same */0 :
201 | exit$1 = 3;
202 | break;
203 | case /* Object */4 :
204 | var d2 = typ2[0];
205 | var dA1 = { };
206 | var dA2 = { };
207 | var dB = { };
208 | var doItem2 = function (param) {
209 | var styp2 = param[1];
210 | var lbl = param[0];
211 | var match = Js_dict.get(d1, lbl);
212 | if (match !== undefined) {
213 | var match$1 = diffStyp(match, styp2);
214 | var stypA2 = match$1[/* stypA2 */3];
215 | var stypA1 = match$1[/* stypA1 */2];
216 | if (!Styp$ReactTemplate.stypIsEmpty(stypA1)) {
217 | dA1[lbl] = stypA1;
218 | }
219 | if (!Styp$ReactTemplate.stypIsEmpty(stypA2)) {
220 | dA2[lbl] = stypA2;
221 | }
222 | dB[lbl] = match$1[/* stypB */4];
223 | return /* () */0;
224 | } else if (Styp$ReactTemplate.stypIsEmpty(styp2)) {
225 | return 0;
226 | } else {
227 | dA2[lbl] = styp2;
228 | return /* () */0;
229 | }
230 | };
231 | var doItem1 = function (param) {
232 | var styp1 = param[1];
233 | var lbl = param[0];
234 | var match = Js_dict.get(d2, lbl);
235 | if (match !== undefined || Styp$ReactTemplate.stypIsEmpty(styp1)) {
236 | return /* () */0;
237 | } else {
238 | dA1[lbl] = styp1;
239 | return /* () */0;
240 | }
241 | };
242 | Belt_Array.forEach(Js_dict.entries(d2), doItem2);
243 | Belt_Array.forEach(Js_dict.entries(d1), doItem1);
244 | var entries1 = Js_dict.entries(dA1);
245 | var entries2 = Js_dict.entries(dA2);
246 | var t = Styp$ReactTemplate.makeObject(entries1);
247 | var match = entries1.length === 0;
248 | var typA1 = match ? /* Same */Block.__(0, [t]) : t;
249 | var t$1 = Styp$ReactTemplate.makeObject(entries2);
250 | var match$1 = entries2.length === 0;
251 | var typA2 = match$1 ? /* Same */Block.__(0, [t$1]) : t$1;
252 | var typB = Styp$ReactTemplate.makeObject(Js_dict.entries(dB));
253 | return /* record */[
254 | /* typA1 */typA1,
255 | /* typA2 */typA2,
256 | /* typB */typB
257 | ];
258 | case /* Array */5 :
259 | break;
260 | default:
261 | exit = 2;
262 | }
263 | }
264 | break;
265 | case /* Array */5 :
266 | if (typeof typ2 === "number") {
267 | exit$1 = 3;
268 | } else {
269 | switch (typ2.tag | 0) {
270 | case /* Same */0 :
271 | exit$1 = 3;
272 | break;
273 | case /* Array */5 :
274 | var match$2 = diffStyp(typ1[0], typ2[0]);
275 | var stypA2 = match$2[/* stypA2 */3];
276 | var stypA1 = match$2[/* stypA1 */2];
277 | var match$3 = Styp$ReactTemplate.stypIsEmpty(stypA1);
278 | var typA1$1 = match$3 ? /* Same */Block.__(0, [/* Array */Block.__(5, [stypA1])]) : /* Array */Block.__(5, [stypA1]);
279 | var match$4 = Styp$ReactTemplate.stypIsEmpty(stypA2);
280 | var typA2$1 = match$4 ? /* Same */Block.__(0, [/* Array */Block.__(5, [stypA2])]) : /* Array */Block.__(5, [stypA2]);
281 | var typB$1 = /* Array */Block.__(5, [match$2[/* stypB */4]]);
282 | return /* record */[
283 | /* typA1 */typA1$1,
284 | /* typA2 */typA2$1,
285 | /* typB */typB$1
286 | ];
287 | default:
288 | exit = 2;
289 | }
290 | }
291 | break;
292 | default:
293 | exit$1 = 3;
294 | }
295 | }
296 | if (exit$1 === 3) {
297 | if (typeof typ2 === "number" || !(typ2.tag && typeof typ1 !== "number")) {
298 | return /* record */[
299 | /* typA1 */typ1,
300 | /* typA2 */typ2,
301 | /* typB : Empty */0
302 | ];
303 | } else {
304 | switch (typ1.tag | 0) {
305 | case /* Same */0 :
306 | return /* record */[
307 | /* typA1 */typ1,
308 | /* typA2 */typ2,
309 | /* typB : Empty */0
310 | ];
311 | case /* Union */6 :
312 | exit = 2;
313 | break;
314 | case /* Diff */7 :
315 | return /* record */[
316 | /* typA1 : Empty */0,
317 | /* typA2 */typ2,
318 | /* typB : Empty */0
319 | ];
320 |
321 | }
322 | }
323 | }
324 | if (exit === 2 && typeof typ2 !== "number") {
325 | switch (typ2.tag | 0) {
326 | case /* Array */5 :
327 | break;
328 | case /* Diff */7 :
329 | return /* record */[
330 | /* typA1 */typ1,
331 | /* typA2 : Empty */0,
332 | /* typB : Empty */0
333 | ];
334 | default:
335 |
336 | }
337 | }
338 | throw [
339 | Caml_builtin_exceptions.assert_failure,
340 | /* tuple */[
341 | "Diff.re",
342 | 127,
343 | 21
344 | ]
345 | ];
346 | }
347 |
348 | function diffUnion(styp1, styp2, styps1, styps2) {
349 | var findMatch = function (t, _ts, _acc) {
350 | while(true) {
351 | var acc = _acc;
352 | var ts = _ts;
353 | if (ts) {
354 | var ts1 = ts[1];
355 | var t1 = ts[0];
356 | if (TypeCheck$ReactTemplate.plusTyp(t[/* typ */0], t1[/* typ */0]) !== undefined) {
357 | return /* tuple */[
358 | t1,
359 | Belt_List.concat(Belt_List.reverse(acc), ts1)
360 | ];
361 | } else {
362 | _acc = /* :: */[
363 | t1,
364 | acc
365 | ];
366 | _ts = ts1;
367 | continue ;
368 | }
369 | } else {
370 | return ;
371 | }
372 | };
373 | };
374 | var plus = function (ls1, ls2) {
375 | if (ls1) {
376 | var ts1 = ls1[1];
377 | var t1 = ls1[0];
378 | var match = findMatch(t1, ls2, /* [] */0);
379 | if (match !== undefined) {
380 | var match$1 = match;
381 | var match$2 = plus(ts1, match$1[1]);
382 | var match$3 = diffStyp(t1, match$1[0]);
383 | return /* record */[
384 | /* stypUA1 : :: */[
385 | match$3[/* stypA1 */2],
386 | match$2[/* stypUA1 */0]
387 | ],
388 | /* stypUA2 : :: */[
389 | match$3[/* stypA2 */3],
390 | match$2[/* stypUA2 */1]
391 | ],
392 | /* stypUB : :: */[
393 | match$3[/* stypB */4],
394 | match$2[/* stypUB */2]
395 | ]
396 | ];
397 | } else {
398 | var diffUnion = plus(ts1, ls2);
399 | return /* record */[
400 | /* stypUA1 : :: */[
401 | t1,
402 | diffUnion[/* stypUA1 */0]
403 | ],
404 | /* stypUA2 */diffUnion[/* stypUA2 */1],
405 | /* stypUB */diffUnion[/* stypUB */2]
406 | ];
407 | }
408 | } else {
409 | return /* record */[
410 | /* stypUA1 : [] */0,
411 | /* stypUA2 */ls2,
412 | /* stypUB : [] */0
413 | ];
414 | }
415 | };
416 | var match = plus(styps1, styps2);
417 | var toUnion = function (styps) {
418 | var styps1 = Belt_List.keep(styps, (function (styp) {
419 | return !Styp$ReactTemplate.stypIsEmpty(styp);
420 | }));
421 | if (styps1) {
422 | if (styps1[1]) {
423 | return Styp$ReactTemplate.makeUnion(styps1);
424 | } else {
425 | return styps1[0][/* typ */0];
426 | }
427 | } else {
428 | return /* Empty */0;
429 | }
430 | };
431 | var toStyp = function (stypU) {
432 | var typ = toUnion(stypU);
433 | var p = Belt_List.reduce(stypU, Styp$ReactTemplate.P.zero, (function (p, styp) {
434 | return Styp$ReactTemplate.P["^"](p, styp[/* p */2]);
435 | }));
436 | var o = Belt_List.reduce(stypU, /* NotOpt */0, (function (o, styp) {
437 | return TypeCheck$ReactTemplate.plusO(o, styp[/* o */1]);
438 | }));
439 | return /* record */[
440 | /* typ */typ,
441 | /* o */o,
442 | /* p */p
443 | ];
444 | };
445 | return /* record */[
446 | /* styp1 */styp1,
447 | /* styp2 */styp2,
448 | /* stypA1 */toStyp(match[/* stypUA1 */0]),
449 | /* stypA2 */toStyp(match[/* stypUA2 */1]),
450 | /* stypB */toStyp(match[/* stypUB */2])
451 | ];
452 | }
453 |
454 | function combineStyp(stypA1, stypA2, stypB) {
455 | if (Caml_obj.caml_notequal(stypA1[/* p */2], Styp$ReactTemplate.P.zero) || stypA1[/* o */1] !== /* NotOpt */0 || Caml_obj.caml_notequal(stypA2[/* p */2], Styp$ReactTemplate.P.zero) || stypA2[/* o */1] !== /* NotOpt */0) {
456 | return /* record */[
457 | /* typ : Diff */Block.__(7, [
458 | stypB[/* typ */0],
459 | stypA1,
460 | stypA2
461 | ]),
462 | /* o */stypB[/* o */1],
463 | /* p */stypB[/* p */2]
464 | ];
465 | } else {
466 | return /* record */[
467 | /* typ */combineTyp(stypA1[/* typ */0], stypA2[/* typ */0], stypB[/* typ */0]),
468 | /* o */stypB[/* o */1],
469 | /* p */stypB[/* p */2]
470 | ];
471 | }
472 | }
473 |
474 | function combineTyp(typA1, typA2, typB) {
475 | var exit = 0;
476 | if (typeof typA1 === "number") {
477 | exit = 2;
478 | } else {
479 | switch (typA1.tag | 0) {
480 | case /* Object */4 :
481 | break;
482 | case /* Same */0 :
483 | case /* Array */5 :
484 | exit = 2;
485 | break;
486 | default:
487 | return typB;
488 | }
489 | }
490 | if (exit === 2) {
491 | var exit$1 = 0;
492 | if (typeof typA2 === "number") {
493 | exit$1 = 3;
494 | } else {
495 | switch (typA2.tag | 0) {
496 | case /* Object */4 :
497 | break;
498 | case /* Same */0 :
499 | case /* Array */5 :
500 | exit$1 = 3;
501 | break;
502 | default:
503 | return typB;
504 | }
505 | }
506 | if (exit$1 === 3) {
507 | if (typeof typB === "number") {
508 | return typB;
509 | } else {
510 | switch (typB.tag | 0) {
511 | case /* Object */4 :
512 | break;
513 | case /* Array */5 :
514 | var getStyp = function (typ) {
515 | if (typeof typ === "number" || typ.tag !== /* Array */5) {
516 | return Styp$ReactTemplate.stypEmpty;
517 | } else {
518 | return typ[0];
519 | }
520 | };
521 | var stypA1 = getStyp(typA1);
522 | var stypA2 = getStyp(typA2);
523 | return /* Array */Block.__(5, [combineStyp(stypA1, stypA2, typB[0])]);
524 | default:
525 | return typB;
526 | }
527 | }
528 | }
529 |
530 | }
531 | if (typeof typA1 !== "number" && typA1.tag === /* Array */5) {
532 | return typB;
533 | }
534 | if (typeof typA2 !== "number") {
535 | switch (typA2.tag | 0) {
536 | case /* Same */0 :
537 | case /* Object */4 :
538 | break;
539 | default:
540 | return typB;
541 | }
542 | }
543 | if (typeof typB === "number" || typB.tag !== /* Object */4) {
544 | return typB;
545 | } else {
546 | var dictB = typB[0];
547 | var d = { };
548 | var getDict = function (typ) {
549 | if (typeof typ === "number") {
550 | return { };
551 | } else if (typ.tag === /* Object */4) {
552 | return typ[0];
553 | } else {
554 | return { };
555 | }
556 | };
557 | var dictA1 = getDict(typA1);
558 | var dictA2 = getDict(typA2);
559 | var doItem = function (lbl) {
560 | var getStyp = function (dict) {
561 | var match = Js_dict.get(dict, lbl);
562 | if (match !== undefined) {
563 | return match;
564 | } else {
565 | return Styp$ReactTemplate.stypEmpty;
566 | }
567 | };
568 | d[lbl] = combineStyp(getStyp(dictA1), getStyp(dictA2), getStyp(dictB));
569 | return /* () */0;
570 | };
571 | Belt_SetString.forEach(Belt_SetString.union(Belt_SetString.union(Belt_SetString.fromArray(Object.keys(dictA1)), Belt_SetString.fromArray(Object.keys(dictA2))), Belt_SetString.fromArray(Object.keys(dictB))), doItem);
572 | return Styp$ReactTemplate.makeObject(Js_dict.entries(d));
573 | }
574 | }
575 |
576 | function diff(styp1, styp2) {
577 | var d = diffStyp(styp1, styp2);
578 | return /* record */[
579 | /* styp1 */d[/* styp1 */0],
580 | /* styp2 */d[/* styp2 */1],
581 | /* stypA1 */d[/* stypA1 */2],
582 | /* stypA2 */d[/* stypA2 */3],
583 | /* stypB */combineStyp(d[/* stypA1 */2], d[/* stypA2 */3], d[/* stypB */4])
584 | ];
585 | }
586 |
587 | function diffCheck(styp1, styp2) {
588 | var d = diffStyp(styp1, styp2);
589 | if (!Caml_obj.caml_equal(TypeCheck$ReactTemplate.$caret(d[/* stypB */4], d[/* stypA1 */2]), styp1)) {
590 | throw [
591 | Caml_builtin_exceptions.assert_failure,
592 | /* tuple */[
593 | "Diff.re",
594 | 246,
595 | 2
596 | ]
597 | ];
598 | }
599 | if (!Caml_obj.caml_equal(TypeCheck$ReactTemplate.$caret(d[/* stypB */4], d[/* stypA2 */3]), styp2)) {
600 | throw [
601 | Caml_builtin_exceptions.assert_failure,
602 | /* tuple */[
603 | "Diff.re",
604 | 247,
605 | 2
606 | ]
607 | ];
608 | }
609 | return /* record */[
610 | /* styp1 */d[/* styp1 */0],
611 | /* styp2 */d[/* styp2 */1],
612 | /* stypA1 */d[/* stypA1 */2],
613 | /* stypA2 */d[/* stypA2 */3],
614 | /* stypB */combineStyp(d[/* stypA1 */2], d[/* stypA2 */3], d[/* stypB */4])
615 | ];
616 | }
617 |
618 | function toJson(diff) {
619 | var styp1 = Styp$ReactTemplate.stypToJson(diff[/* styp1 */0]);
620 | var styp2 = Styp$ReactTemplate.stypToJson(diff[/* styp2 */1]);
621 | var stypB = Styp$ReactTemplate.stypToJson(diff[/* stypB */4]);
622 | var stypA1 = Styp$ReactTemplate.stypToJson(diff[/* stypA1 */2]);
623 | var stypA2 = Styp$ReactTemplate.stypToJson(diff[/* stypA2 */3]);
624 | return Js_dict.fromArray(/* array */[
625 | /* tuple */[
626 | "styp1",
627 | styp1
628 | ],
629 | /* tuple */[
630 | "styp2",
631 | styp2
632 | ],
633 | /* tuple */[
634 | "stypB",
635 | stypB
636 | ],
637 | /* tuple */[
638 | "stypA1",
639 | stypA1
640 | ],
641 | /* tuple */[
642 | "stypA2",
643 | stypA2
644 | ]
645 | ]);
646 | }
647 |
648 | var inlineDifferences = true;
649 |
650 | export {
651 | inlineDifferences ,
652 | diffStyp ,
653 | diffO ,
654 | diffTyp ,
655 | diffUnion ,
656 | combineStyp ,
657 | combineTyp ,
658 | diff ,
659 | diffCheck ,
660 | toJson ,
661 |
662 | }
663 | /* No side effect */
664 |
--------------------------------------------------------------------------------