forked from rulespace/rulespace
-
Notifications
You must be signed in to change notification settings - Fork 0
/
sexp2rsp.js
187 lines (173 loc) · 4.13 KB
/
sexp2rsp.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
import { Null, Pair, Sym, Keyword, Tuple } from './str2sexp.js';
import { Program, Rule, Neg, Agg, Atom, Lit, Var, App, Assign, Lam } from './rsp.js';
class SexpRspCompilationError extends Error
{
constructor(msg)
{
super(msg);
this.name = 'SexpRspCompilationError';
}
}
export function sexp2rsp(sexps)
{
const relations = [];
const rules = [];
if (!(sexps instanceof Null))
{
for (const sexp of sexps)
{
if (sexp instanceof Pair)
{
if (sexp.car instanceof Sym)
{
switch (sexp.car.name)
{
case 'rule':
{
rules.push(compileRuleDeclaration(sexp));
break;
}
case 'relation':
{
relations.push(compileRelDeclaration(sexp));
break;
}
default: throw new Error(`unknown declaration ${sexp}`);
}
}
else
{
throw new Error(`expected declaration, got ${sexp}`);
}
}
else if (sexp instanceof Tuple)
{
rules.push(compileFact(sexp));
}
else
{
throw new Error("cannot handle " + sexp);
}
}
}
return new Program(relations, rules);
}
function compileRuleDeclaration(ruleExp)
{
const headExp = ruleExp.cdr.car;
const head = compileAtom(headExp);
const bodyExps = ruleExp.cdr.cdr;
const body = [...bodyExps].map(compileTerm);
return new Rule(head, body);
}
function compileRelDeclaration(tupleExp)
{
const decl = compileAtom(tupleExp.cdr.car);
return decl;
}
function compileFact(headExp)
{
const head = compileAtom(headExp);
const body = [];
return new Rule(head, body);
}
function compileAtom(tuple)
{
const pred = tuple.pred.name;
const termExps = [...tuple.terms];
const terms = [];
while (termExps.length > 0)
{
const termExp = termExps.shift();
if (termExp instanceof Keyword)
{
const aggregator = termExp.name;
const aggregand = compileTerm(termExps.shift());
terms.push(new Agg(aggregator, aggregand));
}
else
{
terms.push(compileTerm(termExp));
}
}
return new Atom(pred, terms);
}
function compileTerm(term)
{
if (term instanceof Tuple)
{
return compileAtom(term);
}
return compileExp(term);
}
export function compileExp(exp)
{
if (exp instanceof Number || exp instanceof String || exp instanceof Boolean)
{
return new Lit(exp.valueOf());
}
if (exp instanceof Sym)
{
return new Var(exp.name);
}
if (exp instanceof Tuple)
{
return compileAtom(exp);
}
if (exp instanceof Pair)
{
const rator = exp.car;
if (rator instanceof Sym)
{
const name = rator.name;
switch (name)
{
case 'quote':
{
const quoted = exp.cdr.car;
return new Lit(quoted.valueOf()); // TODO introduce Ref?
}
case 'not': // TODO 'not' as app (= when arg is not an atom)
{
const atomOrExp = exp.cdr.car;
if (atomOrExp instanceof Tuple)
{
const negated = compileTerm(atomOrExp);
return new Neg(negated);
}
else
{
return new App(compileTerm(rator), [compileTerm(atomOrExp)]);
}
}
case '=':
case '!=':
case '<':
case '>':
case '<=':
case '>=':
case '+':
case '-':
case '*':
case '/':
{
return new App(compileTerm(rator), exp.cdr.properToArray().map(compileTerm));
}
case ':=':
{
const lhs = compileTerm(exp.cdr.car);
const rhs = compileTerm(exp.cdr.cdr.car);
return new Assign(name, lhs, rhs); // de-Symmed
}
case 'lambda':
{
const params = [...exp.cdr.car].map(compileExp);
const body = compileExp(exp.cdr.cdr.car); // only one body exp for now
return new Lam(params, body);
}
}
}
return new App(compileTerm(rator), [...exp.cdr].map(compileTerm));
}
throw new SexpRspCompilationError(`cannot handle expression ${exp} of type ${exp.constructor.name}`);
}