1 |
|
2 | "use strict";
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 | var arbitrary = require("./arbitrary.js");
|
26 | var assert = require("assert");
|
27 | var record = require("./record.js");
|
28 | var array = require("./array.js");
|
29 | var fn = require("./fn.js");
|
30 | var typifyParser = require("typify-parser");
|
31 | var utils = require("./utils.js");
|
32 |
|
33 |
|
34 | var compileType;
|
35 | var compileTypeArray;
|
36 |
|
37 | function compileIdent(env, type) {
|
38 | var g = env[type.value];
|
39 | if (!g) {
|
40 | throw new Error("Unknown arbitrary: " + type.value);
|
41 | }
|
42 | return g;
|
43 | }
|
44 |
|
45 | function compileApplication(env, type) {
|
46 | var callee = compileType(env, type.callee);
|
47 | var args = compileTypeArray(env, type.args);
|
48 |
|
49 | return callee.apply(undefined, args);
|
50 | }
|
51 |
|
52 | function compileFunction(env, type) {
|
53 |
|
54 | var result = compileType(env, type.result);
|
55 | return fn.fn(result);
|
56 | }
|
57 |
|
58 | function compileBrackets(env, type) {
|
59 | var arg = compileType(env, type.arg);
|
60 | return array.array(arg);
|
61 | }
|
62 |
|
63 | function compileDisjunction(env, type) {
|
64 | var args = compileTypeArray(env, type.args);
|
65 | return arbitrary.sum(args);
|
66 | }
|
67 |
|
68 | function compileConjunction(env, type) {
|
69 | var args = compileTypeArray(env, type.args);
|
70 | return arbitrary.tuple(args);
|
71 | }
|
72 |
|
73 | function compileRecord(env, type) {
|
74 |
|
75 | var spec = {};
|
76 | Object.keys(type.fields).forEach(function (key) {
|
77 | spec[key] = compileType(env, type.fields[key]);
|
78 | });
|
79 | return record.arbitrary(spec);
|
80 | }
|
81 |
|
82 | function compileRecursive(env, type) {
|
83 | assert(type.arg.type === "disjunction", "recursive type's argument should be disjunction");
|
84 |
|
85 |
|
86 | var name = type.name;
|
87 |
|
88 | var par = utils.partition(type.arg.args, function (t) {
|
89 | return typifyParser.freeVars(t).indexOf(name) === -1;
|
90 | });
|
91 |
|
92 | var terminal = par[0];
|
93 |
|
94 | if (terminal.length === 0) {
|
95 | throw new Error("Recursive type without non-recursive branch");
|
96 | }
|
97 |
|
98 | var terminalArb = compileType(env, {
|
99 | type: "disjunction",
|
100 | args: terminal,
|
101 | });
|
102 |
|
103 | return arbitrary.recursive(terminalArb, function (arb) {
|
104 | var arbEnv = {};
|
105 | arbEnv[name] = arb;
|
106 | var newEnv = utils.merge(env, arbEnv);
|
107 | return compileType(newEnv, type.arg);
|
108 | });
|
109 | }
|
110 |
|
111 | compileType = function compileTypeFn(env, type) {
|
112 | switch (type.type) {
|
113 | case "ident": return compileIdent(env, type);
|
114 | case "application": return compileApplication(env, type);
|
115 | case "function": return compileFunction(env, type);
|
116 | case "brackets": return compileBrackets(env, type);
|
117 | case "disjunction": return compileDisjunction(env, type);
|
118 | case "conjunction": return compileConjunction(env, type);
|
119 | case "record": return compileRecord(env, type);
|
120 | case "number": return type.value;
|
121 | case "recursive": return compileRecursive(env, type);
|
122 | default: throw new Error("Unsupported typify ast type: " + type.type);
|
123 | }
|
124 | };
|
125 |
|
126 | compileTypeArray = function compileTypeArrayFn(env, types) {
|
127 | return types.map(function (type) {
|
128 | return compileType(env, type);
|
129 | });
|
130 | };
|
131 |
|
132 | function parseTypify(env, str) {
|
133 | var type = typifyParser(str);
|
134 | return compileType(env, type);
|
135 | }
|
136 |
|
137 | module.exports = {
|
138 | parseTypify: parseTypify,
|
139 | };
|