-
Notifications
You must be signed in to change notification settings - Fork 2
/
check.ts
68 lines (60 loc) · 2.34 KB
/
check.ts
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
import * as AST from '@babel/types';
import { bug, err } from '../util/err';
import * as Trace from '../util/trace';
import Type from '../type';
import Env from './env';
import synth from './synth';
const checkObject = Trace.instrument('checkObject',
function checkObject(env: Env, ast: AST.ObjectExpression, type: Type.Object) {
const astProps: { name: string, expr: AST.Expression, key: AST.Identifier }[] =
ast.properties.map(prop => {
if (!AST.isObjectProperty(prop)) bug(`unimplemented ${prop.type}`);
if (prop.computed) bug(`unimplemented computed`);
if (!AST.isIdentifier(prop.key)) bug(`unimplemented ${prop.key.type}`);
if (!AST.isExpression(prop.value)) bug(`unimplemented ${prop.value.type}`);
return {
name: prop.key.name,
expr: prop.value,
key: prop.key
};
});
type.properties.forEach(({ name }) => {
const astProp = astProps.find(({ name: astName }) => astName === name);
if (!astProp) err(`missing property ${name}`, ast);
});
astProps.forEach(({ name, expr, key }) => {
const propType = Type.propType(type, name);
if (propType) check(env, expr, propType);
else err(`extra property ${name}`, key);
});
}
);
const checkFunction = Trace.instrument('checkFunction',
function checkFunction(env: Env, ast: AST.ArrowFunctionExpression, type: Type.Function) {
if (!AST.isExpression(ast.body)) bug(`unimplemented ${ast.body.type}`)
if (type.args.length != ast.params.length)
err(`expected ${type.args.length} args, got ${ast.params.length} args`, ast);
const bindings = ast.params.map((param, i) => {
if (!AST.isIdentifier(param)) bug(`unimplemented ${param.type}`);
return { name: param.name, type: type.args[i] };
});
const bodyEnv =
bindings.reduce(
(env, { name, type }) => env.set(name, type),
env
);
check(bodyEnv, ast.body, type.ret);
}
);
const check = Trace.instrument('check',
function check(env: Env, ast: AST.Expression, type: Type) {
if (AST.isObjectExpression(ast) && Type.isObject(type))
return checkObject(env, ast, type);
if (AST.isArrowFunctionExpression(ast) && Type.isFunction(type))
return checkFunction(env, ast, type);
const synthType = synth(env, ast);
if (!Type.isSubtype(synthType, type))
err(`expected ${Type.toString(type)}, got ${Type.toString(synthType)}`, ast);
}
);
export default check;