-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathexpr.ts
More file actions
253 lines (223 loc) · 6.47 KB
/
expr.ts
File metadata and controls
253 lines (223 loc) · 6.47 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
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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
// Port of expr-lang/expr expr.go — public API.
import { Visitor } from "./ast/visitor.js";
import { Func } from "./builtin/function.js";
import * as checker from "./checker/checker.js";
import * as compiler from "./compiler/compiler.js";
import { Config, CreateNew } from "./conf/config.js";
import { FileError } from "./file/error.js";
import * as optimizer from "./optimizer/optimizer.js";
import * as parser from "./parser/parser.js";
import { OperatorOverloading } from "./patcher/operator_override.js";
import { WithContext } from "./patcher/with_context.js";
import { WithTimezone } from "./patcher/with_timezone.js";
import { Kind } from "./checker/nature/kind.js";
import { Type } from "./checker/nature/type.js";
import { Program } from "./vm/program.js";
import { Run as vmRun } from "./vm/vm.js";
// GoLocation marker for Timezone(). Uses the shared GoLocation from
// vm/runtime/gotime.ts so that the WithTimezone patcher and the date()/now()
// builtins recognize the same type.
import { GoLocation } from "./vm/runtime/gotime.js";
// Option for configuring config.
export type Option = (c: Config) => void;
// Env specifies expected input of env for type checks.
export function Env(env: any): Option {
return (c: Config) => {
c.WithEnv(env);
};
}
// AllowUndefinedVariables allows undefined variables inside expressions.
export function AllowUndefinedVariables(): Option {
return (c: Config) => {
c.Strict = false;
};
}
// Operator allows replacing a binary operator with a function.
export function Operator(operator: string, ...fn: string[]): Option {
return (c: Config) => {
const p = new OperatorOverloading({
Operator: operator,
Overloads: fn,
Env: c.Env,
Functions: c.Functions,
NtCache: c.NtCache,
});
c.Visitors.push(p as unknown as Visitor);
};
}
// ConstExpr defines func expression as constant.
export function ConstExpr(fn: string): Option {
return (c: Config) => {
c.ConstExpr(fn);
};
}
// AsAny tells the compiler to expect any result.
export function AsAny(): Option {
return (c: Config) => {
c.ExpectAny = true;
};
}
// AsKind tells the compiler to expect a kind of the result.
export function AsKind(kind: Kind): Option {
return (c: Config) => {
c.Expect = kind;
c.ExpectAny = true;
};
}
export function AsBool(): Option {
return (c: Config) => {
c.Expect = Kind.Bool;
c.ExpectAny = true;
};
}
export function AsInt(): Option {
return (c: Config) => {
c.Expect = Kind.Int;
c.ExpectAny = true;
};
}
export function AsInt64(): Option {
return (c: Config) => {
c.Expect = Kind.Int64;
c.ExpectAny = true;
};
}
export function AsFloat64(): Option {
return (c: Config) => {
c.Expect = Kind.Float64;
c.ExpectAny = true;
};
}
// DisableIfOperator disables the `if ... else ...` operator syntax.
export function DisableIfOperator(): Option {
return (c: Config) => {
c.DisableIfOperator = true;
};
}
// WarnOnAny tells the compiler to warn if expression returns any type.
export function WarnOnAny(): Option {
return (c: Config) => {
if (c.Expect === Kind.Invalid) {
throw new Error(
"WarnOnAny() works only with combination with AsInt(), AsBool(), etc. options",
);
}
c.ExpectAny = false;
};
}
// Optimize turns optimizations on or off.
export function Optimize(b: boolean): Option {
return (c: Config) => {
c.Optimize = b;
};
}
// DisableShortCircuit turns short circuit off.
export function DisableShortCircuit(): Option {
return (c: Config) => {
c.ShortCircuit = false;
};
}
// Patch adds a visitor applied before compiling AST to bytecode.
export function Patch(visitor: Visitor): Option {
return (c: Config) => {
c.Visitors.push(visitor);
};
}
// Function adds a function available in expressions.
export function Function(
name: string,
fn: (...params: any[]) => any,
...types: Type[]
): Option {
return (c: Config) => {
c.Functions.set(
name,
new Func({ Name: name, Func: fn, Types: types }),
);
};
}
// DisableAllBuiltins disables all builtins.
export function DisableAllBuiltins(): Option {
return (c: Config) => {
for (const name of c.Builtins.keys()) {
c.Disabled.set(name, true);
}
};
}
// DisableBuiltin disables a builtin function.
export function DisableBuiltin(name: string): Option {
return (c: Config) => {
c.Disabled.set(name, true);
};
}
// EnableBuiltin enables a builtin function.
export function EnableBuiltin(name: string): Option {
return (c: Config) => {
c.Disabled.delete(name);
};
}
// WithContext passes context to function calls with a context argument.
export function WithContextOption(name: string): Option {
return (c: Config) => {
c.Visitors.push(
new WithContext({
Name: name,
Functions: c.Functions,
Env: c.Env,
NtCache: c.NtCache,
}) as unknown as Visitor,
);
};
}
export { WithContextOption as WithContext };
// Timezone sets default timezone for date() and now() builtin functions.
export function Timezone(name: string): Option {
const tz = new GoLocation(name);
return Patch(new WithTimezone({ Location: tz }) as unknown as Visitor);
}
// MaxNodes sets the maximum number of nodes allowed in the expression.
export function MaxNodes(n: number): Option {
return (c: Config) => {
c.MaxNodes = n;
};
}
// Compile parses and compiles a given input expression to a bytecode program.
export function Compile(input: string, ...ops: Option[]): Program {
const config = CreateNew();
for (const op of ops) {
op(config);
}
for (const name of config.Disabled.keys()) {
config.Builtins.delete(name);
}
config.Check();
const tree = checker.ParseCheck(input, config);
if (config.Optimize) {
try {
const ref = { node: tree.Node };
optimizer.Optimize(ref, config);
tree.Node = ref.node;
} catch (err) {
if (err instanceof FileError) {
throw err.Bind(tree.Source);
}
throw err;
}
}
return compiler.Compile(tree, config);
}
// Run evaluates a given bytecode program.
export function Run(program: Program, env: any): any {
return vmRun(program, env);
}
// Eval parses, compiles and runs a given input.
export function Eval(input: string, env: any): any {
if (typeof env === "function") {
throw new Error(
"misused expr.Eval: second argument (env) should be passed without expr.Env",
);
}
const tree = parser.Parse(input);
const program = compiler.Compile(tree, null);
return Run(program, env);
}