-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtyping.ts
126 lines (112 loc) · 3.23 KB
/
typing.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
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
/**
* Helpers for doing type analysis. Contains definitions for primitive types and
* error messages.
*/
import type { RA } from '../utils/types.js';
import { AstNode } from './definitions/AstNode.js';
import { FunctionTypeNode } from './definitions/types/FunctionTypeNode.js';
export class LanguageType {
public toString(): string {
throw new Error('Not implemented');
}
}
export class BoolType extends LanguageType {
public toString(): string {
return 'bool';
}
}
export class ErrorType extends LanguageType {
public toString(): string {
return 'error';
}
}
export class FunctionType extends LanguageType {
constructor(public readonly type: FunctionTypeNode) {
super();
}
public toString(): string {
return this.type.print({
indent: 0,
mode: 'pretty',
debug: false,
needWrapping: false,
});
}
}
export class IntType extends LanguageType {
public toString(): string {
return 'int';
}
}
export class StringType extends LanguageType {
public toString(): string {
return 'string';
}
}
export class VoidType extends LanguageType {
public toString(): string {
return 'void';
}
}
const types = {
bool: BoolType,
error: ErrorType,
function: FunctionType,
int: IntType,
string: StringType,
void: VoidType,
} as const;
type ReturnTypes = typeof types;
export type TypeCheckContext = {
readonly reportError: (
node: AstNode,
message: keyof typeof typeErrors
) => ErrorType;
};
export function assertType<TYPES extends keyof ReturnTypes>(
context: TypeCheckContext,
node: AstNode,
errorCode: keyof typeof typeErrors,
...expectedTypes: RA<TYPES>
): ErrorType | ReturnTypes[TYPES] {
const type = node.typeCheck(context);
if (type instanceof ErrorType) return type;
else if (
expectedTypes.every(
(expectedType) => !(type instanceof types[expectedType])
)
) {
context.reportError(node, errorCode);
return new ErrorType();
} else return type as ReturnTypes[TYPES];
}
/**
* If there is any error in an expression, the whole expression should be of
* error type
*/
export const cascadeError = (
...types: RA<LanguageType | undefined>
): LanguageType =>
types.find((node) => node instanceof ErrorType) ??
types.at(-1) ??
new VoidType();
export const typeErrors = {
nonBoolLoop: 'Non-bool expression used as a loop condition',
nonBoolIf: 'Non-bool expression used as an if condition',
nonBoolLogic: 'Logical operator applied to non-bool operand',
nonIntArith: 'Arithmetic operator applied to invalid operand',
inputOnFunction: 'Attempt to assign user input to function',
outputOnVoid: 'Attempt to output void',
outputOnFunction: 'Attempt to output a function',
invalidEqOperand: 'Invalid equality operand',
invalidEqOperation: 'Invalid equality operation',
relationalInt: 'Relational operator applied to non-numeric operand',
invalidOperand: 'Invalid assignment operand',
invalidAssign: 'Invalid assignment operation',
nonFuncCall: 'Attempt to call a non-function',
wrongReturn: 'Bad return value',
voidReturn: 'Return with a value in void function',
noReturn: 'Missing return value',
argLength: 'Function call with wrong number of args',
argType: 'Type of actual does not match type of formal',
} as const;