-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathcompiler.ts
More file actions
88 lines (75 loc) · 3.23 KB
/
Copy pathcompiler.ts
File metadata and controls
88 lines (75 loc) · 3.23 KB
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
/*
* Copyright (c) 2017 MolQL contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import Expression from '../../mini-lisp/expression'
import Environment from './environment'
import RuntimeExpression from './expression'
import SymbolRuntime, { RuntimeArguments } from './symbol'
export type CompiledExpression<C, T> = (ctx: C) => T
type Compiler<C> = <T>(expression: Expression) => CompiledExpression<C, T>
function Compiler<C>(envProvider: (ctx?: C) => Environment): Compiler<C> {
const env = envProvider(void 0);
return expression => wrap(envProvider, compile(env, expression).runtime);
}
type CompileResult = { isConst: boolean, runtime: RuntimeExpression }
namespace CompileResult {
export function Const(value: any): CompileResult { return { isConst: true, runtime: RuntimeExpression.constant(value) } }
export function Dynamic(runtime: RuntimeExpression): CompileResult { return { isConst: false, runtime } }
}
function wrap<C, T>(envProvider: (ctx?: C) => Environment, runtime: RuntimeExpression<C, T>) {
return (ctx: C) => runtime(envProvider(ctx));
}
function noRuntimeFor(symbol: string) {
throw new Error(`Could not find runtime for symbol '${symbol}'.`);
}
function applySymbolStatic(runtime: SymbolRuntime, args: RuntimeArguments) {
return CompileResult.Dynamic(env => runtime(env, args))
}
function applySymbolDynamic(head: RuntimeExpression, args: RuntimeArguments) {
return CompileResult.Dynamic(env => {
const value = head(env);
const symbol = env.symbolTable[value];
if (!symbol) noRuntimeFor(value);
return symbol.runtime(env, args);
})
}
function apply(env: Environment, head: CompileResult, args: RuntimeArguments, constArgs: boolean): CompileResult {
if (head.isConst) {
const value = head.runtime(env);
const symbol = env.symbolTable[value];
if (!symbol) throw new Error(`Could not find runtime for symbol '${value}'.`);
if (symbol.attributes.isStatic && constArgs) return CompileResult.Const(symbol.runtime(env, args));
return applySymbolStatic(symbol.runtime, args);
}
return applySymbolDynamic(head.runtime, args);
}
function compile(env: Environment, expression: Expression): CompileResult {
if (Expression.isLiteral(expression)) {
return CompileResult.Const(expression);
}
const head = compile(env, expression.head);
if (!expression.args) {
return apply(env, head, [], true);
} else if (Expression.isArgumentsArray(expression.args)) {
const args = [];
let constArgs = false;
for (const arg of expression.args) {
const compiled = compile(env, arg);
constArgs = constArgs && compiled.isConst;
args.push(compiled.runtime);
}
return apply(env, head, args, constArgs);
} else {
const args = Object.create(null);
let constArgs = false;
for (const key of Object.keys(expression.args)) {
const compiled = compile(env, expression.args[key]);
constArgs = constArgs && compiled.isConst;
args[key] = compiled.runtime;
}
return apply(env, head, args, constArgs);
}
}
export default Compiler