Skip to content

Commit 3d7fa2d

Browse files
committed
init
1 parent 7aefd5a commit 3d7fa2d

File tree

8 files changed

+3407
-0
lines changed

8 files changed

+3407
-0
lines changed

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,7 @@ typings/
5959

6060
# next.js build output
6161
.next
62+
63+
.lein-*
64+
.nrepl-*
65+
target

package.json

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"name": "json-spec",
3+
"version": "0.1.0-SNAPSHOT",
4+
"description": "A tool for describing the structure of JSON data",
5+
"license": "MIT",
6+
"repository": "github:kawasima/json-spec",
7+
"scripts": {
8+
"test": "jest"
9+
},
10+
"jest": {
11+
"moduleDirectories": [
12+
"src",
13+
"node_modules"
14+
]
15+
},
16+
"devDependencies": {
17+
"jest": "^23.5.0"
18+
},
19+
"dependencies": {
20+
"n64": "^0.2.1"
21+
}
22+
}

project.clj

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
(defproject json-spec "0.1.0-SNAPSHOT"
2+
:dependencies [[org.clojure/clojure "1.9.0"]
3+
[org.clojure/spec.alpha "0.2.176"]]
4+
:profiles {:docker {:local-repo "lib"}})

src/generators.js

+124
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
const { I64 } = require('n64');
2+
3+
class Generator {
4+
constructor(gen) {
5+
this.gen = gen;
6+
}
7+
}
8+
9+
function makeGen(generatorFunc) {
10+
return new Generator(generatorFunc);
11+
}
12+
13+
function callGen(gen, rnd, size) {
14+
return gen.call(null, rnd, size);
15+
}
16+
17+
function choose(lower, upper) {
18+
return (rnd, size) => {
19+
const value = randRange(rnd, lower, upper);
20+
return value;
21+
};
22+
}
23+
24+
function fmap(f, generator) {
25+
return (rnd, size) => f(generator(rnd,size));
26+
}
27+
28+
function repeat(n, el) {
29+
return Array.apply(null, Array(10)).map(() => el);
30+
}
31+
32+
function vector(generator) {
33+
return (rnd, size) => repeat(size, generator).map(gen => gen(rnd, size));
34+
}
35+
36+
function calcLong(factor, lower, upper) {
37+
return Math.floor(lower + (factor * (upper + 1.0)) - (factor * lower));
38+
}
39+
40+
function randRange(rnd, lower, upper) {
41+
return calcLong(rnd.randDouble(), lower, upper);
42+
}
43+
44+
function sized(sizedGen) {
45+
return function(rnd, size) {
46+
const gen = sizedGen.call(null, size);
47+
return callGen(gen, rnd, size)
48+
};
49+
}
50+
51+
function join(coll) {
52+
return coll.join('');
53+
}
54+
55+
const generators = {
56+
'int': sized((size) => choose(-size, size)),
57+
'char': fmap(String.fromCharCode, choose(0, 255)),
58+
'char-ascii': fmap(String.fromCharCode, choose(32, 126)),
59+
'char-alphanumeric': fmap(String.fromCharCode, oneOf(
60+
choose(48, 57),
61+
choose(65, 90),
62+
choose(97, 122)
63+
)),
64+
'char-alpha': fmap(String.fromCharCode, oneOf(
65+
choose(65, 90),
66+
choose(97, 122)
67+
)),
68+
'string': fmap(join, vector(fmap(String.fromCharCode, choose(0, 255))))
69+
}
70+
71+
class RandomUnit {
72+
constructor() {
73+
let x = 1;
74+
for (let i = 53; i > 0; i--) {
75+
x /= 2;
76+
}
77+
this.doubleUnit = x;
78+
this.bigDoubleUnit = this.doubleUnit * 4294967296;
79+
}
80+
81+
randDouble() {
82+
const long = this.randLong();
83+
const x = long.ushrn(11);
84+
return (this.doubleUnit * x.lo) + (this.bigDoubleUnit * x.hi);
85+
}
86+
87+
randLong() {
88+
return I64.random();
89+
}
90+
}
91+
92+
function elements(coll) {
93+
return (rnd, size) => coll[choose(0, coll.length - 1)(rnd, size)];
94+
}
95+
96+
function oneOf(...generators) {
97+
return (rnd, size) => generators[choose(0, generators.length - 1)(rnd, size)](rnd,size);
98+
}
99+
100+
function* rangeCyclic(from, to) {
101+
let i = from;
102+
while(true) {
103+
yield i;
104+
i = i >= to ? from : i + 1;
105+
}
106+
}
107+
108+
function sample(generator, numSample = 10) {
109+
const randomUnit = new RandomUnit();
110+
const range = rangeCyclic(0, numSample);
111+
const result = new Array(numSample);
112+
for (let i = 0; i < numSample; i++) {
113+
result[i] = generator.call(null, randomUnit, range.next());
114+
}
115+
return result;
116+
}
117+
118+
module.exports = {
119+
generators,
120+
fmap,
121+
sample,
122+
oneOf,
123+
choose
124+
}

src/spec.js

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
const INVALID = (function() {
2+
function toString() {
3+
return "INVALID";
4+
}
5+
})();
6+
7+
function Spec(form, pred, gfn, cpred) {
8+
this.form = form;
9+
this.pred = pred;
10+
this.gfn = gfn;
11+
this.cpred = cpred;
12+
}
13+
14+
Spec.prototype = {
15+
conform: function(x) {
16+
const ret = this.pred.call(this, x);
17+
return this.cpred ? ret : ret ? x : INVALID;
18+
}
19+
}
20+
21+
function specImpl(form, pred, gfn, cpred) {
22+
return new Spec(form, pred, gfn, cpred);
23+
}
24+
25+
function specize(spec) {
26+
if (spec instanceof Spec) {
27+
return spec;
28+
} else {
29+
return specImpl(spec, spec);
30+
}
31+
}
32+
33+
function conform(spec, x) {
34+
return specize(spec).conform(x);
35+
}
36+
37+
module.exports = { conform };

test/generators.test.js

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
const { sample, choose,generators } = require('generators');
2+
3+
describe('generators', () => {
4+
test('sample-choose', () => {
5+
const v = sample(choose(200, 800));
6+
console.log(v);
7+
})
8+
9+
test('sample-char', () => {
10+
const v = sample(generators['char']);
11+
console.log(v);
12+
})
13+
14+
test('sample-char-alphanumeric', () => {
15+
const v = sample(generators['char-alphanumeric']);
16+
console.log(v);
17+
})
18+
19+
test('sample-string', () => {
20+
const v = sample(generators['string']);
21+
console.log(v);
22+
})
23+
})

test/spec.test.js

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
const { conform, INVALID } = require('spec');
2+
const { I64 } = require('n64');
3+
4+
describe('conforms', () => {
5+
test('conform', () => {
6+
expect(conform(x => x%2 === 0, 1000)).toBe(1000);
7+
})
8+
test('conform invalid', () => {
9+
expect(conform(x => x%2 === 0, 1001)).toBe(INVALID);
10+
})
11+
12+
});

0 commit comments

Comments
 (0)