@jhlagado/azm 0.2.4 → 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/register-care/boundaryHints.d.ts +1 -0
- package/dist/src/register-care/boundaryHints.js +69 -0
- package/dist/src/register-care/liveness.js +8 -5
- package/dist/src/register-care/profiles.d.ts +6 -1
- package/dist/src/register-care/profiles.js +162 -0
- package/dist/src/register-care/programModel.js +80 -2
- package/dist/src/register-care/summaries.js +15 -2
- package/dist/src/register-care/types.d.ts +2 -0
- package/dist/src/syntax/parse-expression.js +1 -1
- package/dist/src/syntax/parse-line.js +2 -26
- package/package.json +1 -1
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
import type { RegisterCareInstruction } from './types.js';
|
|
2
2
|
export declare function precedingCServiceName(item: RegisterCareInstruction | undefined): string | undefined;
|
|
3
|
+
export declare function precedingRegisterImmediateValue(item: RegisterCareInstruction | undefined, register: string): number | undefined;
|
|
@@ -9,3 +9,72 @@ export function precedingCServiceName(item) {
|
|
|
9
9
|
}
|
|
10
10
|
return undefined;
|
|
11
11
|
}
|
|
12
|
+
function evaluateKnownConstant(expression, constants) {
|
|
13
|
+
switch (expression.kind) {
|
|
14
|
+
case 'number':
|
|
15
|
+
return expression.value;
|
|
16
|
+
case 'symbol':
|
|
17
|
+
return constants.get(expression.name);
|
|
18
|
+
case 'unary': {
|
|
19
|
+
const value = evaluateKnownConstant(expression.expression, constants);
|
|
20
|
+
if (value === undefined)
|
|
21
|
+
return undefined;
|
|
22
|
+
switch (expression.operator) {
|
|
23
|
+
case '+':
|
|
24
|
+
return value;
|
|
25
|
+
case '-':
|
|
26
|
+
return -value;
|
|
27
|
+
case '~':
|
|
28
|
+
return ~value;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
case 'binary': {
|
|
32
|
+
const left = evaluateKnownConstant(expression.left, constants);
|
|
33
|
+
const right = evaluateKnownConstant(expression.right, constants);
|
|
34
|
+
if (left === undefined || right === undefined)
|
|
35
|
+
return undefined;
|
|
36
|
+
switch (expression.operator) {
|
|
37
|
+
case '+':
|
|
38
|
+
return left + right;
|
|
39
|
+
case '-':
|
|
40
|
+
return left - right;
|
|
41
|
+
case '*':
|
|
42
|
+
return left * right;
|
|
43
|
+
case '/':
|
|
44
|
+
return right === 0 ? undefined : Math.trunc(left / right);
|
|
45
|
+
case '%':
|
|
46
|
+
return right === 0 ? undefined : left % right;
|
|
47
|
+
case '&':
|
|
48
|
+
return left & right;
|
|
49
|
+
case '^':
|
|
50
|
+
return left ^ right;
|
|
51
|
+
case '|':
|
|
52
|
+
return left | right;
|
|
53
|
+
case '<<':
|
|
54
|
+
return left << right;
|
|
55
|
+
case '>>':
|
|
56
|
+
return left >> right;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
case 'byte-function': {
|
|
60
|
+
const value = evaluateKnownConstant(expression.expression, constants);
|
|
61
|
+
if (value === undefined)
|
|
62
|
+
return undefined;
|
|
63
|
+
return expression.function === 'LSB' ? value & 0xff : (value >> 8) & 0xff;
|
|
64
|
+
}
|
|
65
|
+
default:
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
export function precedingRegisterImmediateValue(item, register) {
|
|
70
|
+
const instruction = item?.instruction;
|
|
71
|
+
if (!instruction || instruction.mnemonic !== 'ld')
|
|
72
|
+
return undefined;
|
|
73
|
+
if (instruction.target?.kind !== 'reg8' ||
|
|
74
|
+
instruction.target.register !== register.toLowerCase()) {
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
if (instruction.source.kind !== 'imm')
|
|
78
|
+
return undefined;
|
|
79
|
+
return evaluateKnownConstant(instruction.source.expression, item.constants ?? new Map());
|
|
80
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getZ80InstructionEffect } from '../z80/effects.js';
|
|
2
|
-
import { precedingCServiceName } from './boundaryHints.js';
|
|
2
|
+
import { precedingCServiceName, precedingRegisterImmediateValue } from './boundaryHints.js';
|
|
3
3
|
import { instructionSuccessors, labelIndex } from './controlFlow.js';
|
|
4
|
-
import { rstServiceTargetName, rstTargetName } from './profiles.js';
|
|
4
|
+
import { rstDispatcherServiceTargetNames, rstServiceTargetName, rstTargetName, } from './profiles.js';
|
|
5
5
|
function unique(units) {
|
|
6
6
|
return [...new Set(units)];
|
|
7
7
|
}
|
|
@@ -27,10 +27,13 @@ function boundaryTarget(routine, index, effect) {
|
|
|
27
27
|
}
|
|
28
28
|
if (effect.control.kind === 'rst' && effect.control.vector !== undefined) {
|
|
29
29
|
const target = rstTargetName(effect.control.vector);
|
|
30
|
+
const previous = routine.instructions[index - 1];
|
|
30
31
|
const service = precedingCServiceName(routine.instructions[index - 1]);
|
|
31
|
-
const targets =
|
|
32
|
-
|
|
33
|
-
: [
|
|
32
|
+
const targets = [
|
|
33
|
+
...rstDispatcherServiceTargetNames(effect.control.vector, (register) => precedingRegisterImmediateValue(previous, register)),
|
|
34
|
+
...(service ? [rstServiceTargetName(effect.control.vector, service)] : []),
|
|
35
|
+
target,
|
|
36
|
+
];
|
|
34
37
|
return { targets, conditional: false, subject: target };
|
|
35
38
|
}
|
|
36
39
|
return undefined;
|
|
@@ -1,9 +1,14 @@
|
|
|
1
|
-
import type { RoutineSummary } from './types.js';
|
|
1
|
+
import type { RegisterCareUnit, RoutineSummary } from './types.js';
|
|
2
2
|
export interface RegisterCareProfileSummary {
|
|
3
3
|
name: 'mon3';
|
|
4
4
|
rst: Map<number, RoutineSummary>;
|
|
5
5
|
rstServices: Map<string, RoutineSummary>;
|
|
6
|
+
rstDispatchers: Map<number, {
|
|
7
|
+
selector: RegisterCareUnit;
|
|
8
|
+
services: Map<number, RoutineSummary>;
|
|
9
|
+
}>;
|
|
6
10
|
}
|
|
7
11
|
export declare function rstTargetName(vector: number): string;
|
|
8
12
|
export declare function rstServiceTargetName(vector: number, service: string): string;
|
|
13
|
+
export declare function rstDispatcherServiceTargetNames(vector: number, selectorValue: (register: RegisterCareUnit) => number | undefined): string[];
|
|
9
14
|
export declare function getRegisterCareProfile(name: 'mon3' | undefined): RegisterCareProfileSummary | undefined;
|
|
@@ -8,9 +8,156 @@ function normalizeServiceName(raw) {
|
|
|
8
8
|
export function rstServiceTargetName(vector, service) {
|
|
9
9
|
return `${rstTargetName(vector)}:${normalizeServiceName(service)}`;
|
|
10
10
|
}
|
|
11
|
+
function mon3ApiTargetName(api, name) {
|
|
12
|
+
return `MON3_API_${api}_${name.replace(/[^A-Za-z0-9_]/gu, '').toUpperCase()}`;
|
|
13
|
+
}
|
|
14
|
+
function conservativeMon3ApiSummary(api, name) {
|
|
15
|
+
return {
|
|
16
|
+
name: mon3ApiTargetName(api, name),
|
|
17
|
+
mayRead: ['A', 'B', 'C', 'D', 'E', 'H', 'L', ...FLAG_UNITS],
|
|
18
|
+
mayWrite: ['A', 'B', 'C', 'D', 'E', 'H', 'L', ...FLAG_UNITS],
|
|
19
|
+
mayOutput: [],
|
|
20
|
+
preserved: [],
|
|
21
|
+
valueRelations: [],
|
|
22
|
+
stackBalanced: true,
|
|
23
|
+
hasUnknownStackEffect: false,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function mon3ApiServices(overrides) {
|
|
27
|
+
const names = [
|
|
28
|
+
'SOFTWARE_ID',
|
|
29
|
+
'VERSION_ID',
|
|
30
|
+
'PRE_INIT',
|
|
31
|
+
'BEEP_ALWAYS',
|
|
32
|
+
'CONV_A_TO_SEG',
|
|
33
|
+
'REG_A_TO_ASCII',
|
|
34
|
+
'ASCII_TO_SEGMENT',
|
|
35
|
+
'STRING_COMPARE',
|
|
36
|
+
'HL_TO_STRING',
|
|
37
|
+
'A_TO_STRING',
|
|
38
|
+
'SCAN_SEGMENTS',
|
|
39
|
+
'DISPLAY_ERROR',
|
|
40
|
+
'LCD_BUSY',
|
|
41
|
+
'STRING_TO_LCD',
|
|
42
|
+
'CHAR_TO_LCD',
|
|
43
|
+
'COMMAND_TO_LCD',
|
|
44
|
+
'SCAN_KEYS',
|
|
45
|
+
'SCAN_KEYS_WAIT',
|
|
46
|
+
'MATRIX_SCAN',
|
|
47
|
+
'JOYSTICK_SCAN',
|
|
48
|
+
'SERIAL_ENABLE',
|
|
49
|
+
'SERIAL_DISABLE',
|
|
50
|
+
'TX_BYTE',
|
|
51
|
+
'RX_BYTE',
|
|
52
|
+
'INTEL_HEX_LOAD',
|
|
53
|
+
'SEND_TO_SERIAL_API',
|
|
54
|
+
'RECEIVE_FROM_SERIAL_API',
|
|
55
|
+
'SEND_ASSEMBLY_API',
|
|
56
|
+
'SEND_HEX_API',
|
|
57
|
+
'GEN_DATA_DUMP',
|
|
58
|
+
'CHECK_START_END',
|
|
59
|
+
'MENU_DRIVER',
|
|
60
|
+
'PARAM_DRIVER',
|
|
61
|
+
'TIME_DELAY',
|
|
62
|
+
'PLAY_NOTE',
|
|
63
|
+
'PLAY_TUNE',
|
|
64
|
+
'PLAY_TUNE_MENU',
|
|
65
|
+
'GET_CAPS',
|
|
66
|
+
'GET_SHADOW',
|
|
67
|
+
'GET_PROTECT',
|
|
68
|
+
'GET_EXPAND',
|
|
69
|
+
'SET_CAPS',
|
|
70
|
+
'SET_SHADOW',
|
|
71
|
+
'SET_PROTECT',
|
|
72
|
+
'SET_EXPAND',
|
|
73
|
+
'STRING_TO_SERIAL',
|
|
74
|
+
'RTC_API',
|
|
75
|
+
'MENU_POP',
|
|
76
|
+
'TOGGLE_CAPS',
|
|
77
|
+
'RANDOM',
|
|
78
|
+
'SET_DIS_START',
|
|
79
|
+
'GET_DIS_NEXT',
|
|
80
|
+
'GET_DISASSEMBLY',
|
|
81
|
+
'MATRIX_SCAN_ASCII',
|
|
82
|
+
'PARSE_MATRIX_SCAN',
|
|
83
|
+
'LCD_CONFIRM',
|
|
84
|
+
'GET_GLCD_TERM',
|
|
85
|
+
'SET_GLCD_TERM',
|
|
86
|
+
'LOAD_FROM_DISK',
|
|
87
|
+
'OPEN_FILE',
|
|
88
|
+
'READ_SECTOR',
|
|
89
|
+
'WRITE_SECTOR',
|
|
90
|
+
'RGB_SCAN',
|
|
91
|
+
];
|
|
92
|
+
return new Map(names.map((serviceName, api) => [
|
|
93
|
+
api,
|
|
94
|
+
overrides.get(api) ?? conservativeMon3ApiSummary(api, serviceName),
|
|
95
|
+
]));
|
|
96
|
+
}
|
|
97
|
+
export function rstDispatcherServiceTargetNames(vector, selectorValue) {
|
|
98
|
+
const mon3 = getRegisterCareProfile('mon3');
|
|
99
|
+
const dispatcher = mon3?.rstDispatchers.get(vector);
|
|
100
|
+
if (dispatcher === undefined)
|
|
101
|
+
return [];
|
|
102
|
+
const value = selectorValue(dispatcher.selector);
|
|
103
|
+
if (value === undefined)
|
|
104
|
+
return [];
|
|
105
|
+
const service = dispatcher.services.get(value);
|
|
106
|
+
return service ? [service.name] : [];
|
|
107
|
+
}
|
|
11
108
|
export function getRegisterCareProfile(name) {
|
|
12
109
|
if (name !== 'mon3')
|
|
13
110
|
return undefined;
|
|
111
|
+
const matrixScan = {
|
|
112
|
+
name: mon3ApiTargetName(18, 'MATRIX_SCAN'),
|
|
113
|
+
mayRead: ['C'],
|
|
114
|
+
mayWrite: ['A', 'B', 'C', 'D', 'E', 'H', 'L', ...FLAG_UNITS],
|
|
115
|
+
mayOutput: ['D', 'E', 'zero'],
|
|
116
|
+
preserved: [],
|
|
117
|
+
valueRelations: [{ out: ['D', 'E', 'zero'], from: [] }],
|
|
118
|
+
stackBalanced: true,
|
|
119
|
+
hasUnknownStackEffect: false,
|
|
120
|
+
};
|
|
121
|
+
const stringToLcd = {
|
|
122
|
+
name: mon3ApiTargetName(13, 'STRING_TO_LCD'),
|
|
123
|
+
mayRead: ['C', 'H', 'L'],
|
|
124
|
+
mayWrite: ['A', 'H', 'L', ...FLAG_UNITS],
|
|
125
|
+
mayOutput: [],
|
|
126
|
+
preserved: ['B', 'C', 'D', 'E'],
|
|
127
|
+
valueRelations: [],
|
|
128
|
+
stackBalanced: true,
|
|
129
|
+
hasUnknownStackEffect: false,
|
|
130
|
+
};
|
|
131
|
+
const charToLcd = {
|
|
132
|
+
name: mon3ApiTargetName(14, 'CHAR_TO_LCD'),
|
|
133
|
+
mayRead: ['A', 'C'],
|
|
134
|
+
mayWrite: [],
|
|
135
|
+
mayOutput: [],
|
|
136
|
+
preserved: ['A', 'B', 'C', 'D', 'E', 'H', 'L', ...FLAG_UNITS],
|
|
137
|
+
valueRelations: [],
|
|
138
|
+
stackBalanced: true,
|
|
139
|
+
hasUnknownStackEffect: false,
|
|
140
|
+
};
|
|
141
|
+
const commandToLcd = {
|
|
142
|
+
name: mon3ApiTargetName(15, 'COMMAND_TO_LCD'),
|
|
143
|
+
mayRead: ['B', 'C'],
|
|
144
|
+
mayWrite: [],
|
|
145
|
+
mayOutput: [],
|
|
146
|
+
preserved: ['A', 'B', 'C', 'D', 'E', 'H', 'L', ...FLAG_UNITS],
|
|
147
|
+
valueRelations: [],
|
|
148
|
+
stackBalanced: true,
|
|
149
|
+
hasUnknownStackEffect: false,
|
|
150
|
+
};
|
|
151
|
+
const parseMatrixScan = {
|
|
152
|
+
name: mon3ApiTargetName(54, 'PARSE_MATRIX_SCAN'),
|
|
153
|
+
mayRead: ['C', 'D', 'E', 'zero'],
|
|
154
|
+
mayWrite: ['A', 'B', 'C', 'H', 'L', 'carry', 'sign', 'parity', 'halfCarry'],
|
|
155
|
+
mayOutput: ['A', 'carry'],
|
|
156
|
+
preserved: ['D', 'E'],
|
|
157
|
+
valueRelations: [{ out: ['A', 'carry'], from: ['D', 'E', 'zero'] }],
|
|
158
|
+
stackBalanced: true,
|
|
159
|
+
hasUnknownStackEffect: false,
|
|
160
|
+
};
|
|
14
161
|
return {
|
|
15
162
|
name: 'mon3',
|
|
16
163
|
rst: new Map([
|
|
@@ -43,5 +190,20 @@ export function getRegisterCareProfile(name) {
|
|
|
43
190
|
},
|
|
44
191
|
],
|
|
45
192
|
]),
|
|
193
|
+
rstDispatchers: new Map([
|
|
194
|
+
[
|
|
195
|
+
0x10,
|
|
196
|
+
{
|
|
197
|
+
selector: 'C',
|
|
198
|
+
services: mon3ApiServices(new Map([
|
|
199
|
+
[13, stringToLcd],
|
|
200
|
+
[14, charToLcd],
|
|
201
|
+
[15, commandToLcd],
|
|
202
|
+
[18, matrixScan],
|
|
203
|
+
[54, parseMatrixScan],
|
|
204
|
+
])),
|
|
205
|
+
},
|
|
206
|
+
],
|
|
207
|
+
]),
|
|
46
208
|
};
|
|
47
209
|
}
|
|
@@ -4,6 +4,80 @@ function isGlobalLabel(name) {
|
|
|
4
4
|
function routineNameFromExpression(expression) {
|
|
5
5
|
return expression.kind === 'symbol' ? expression.name : undefined;
|
|
6
6
|
}
|
|
7
|
+
function evaluateConstantExpression(expression, constants) {
|
|
8
|
+
switch (expression.kind) {
|
|
9
|
+
case 'number':
|
|
10
|
+
return expression.value;
|
|
11
|
+
case 'symbol':
|
|
12
|
+
return constants.get(expression.name);
|
|
13
|
+
case 'unary': {
|
|
14
|
+
const value = evaluateConstantExpression(expression.expression, constants);
|
|
15
|
+
if (value === undefined)
|
|
16
|
+
return undefined;
|
|
17
|
+
switch (expression.operator) {
|
|
18
|
+
case '+':
|
|
19
|
+
return value;
|
|
20
|
+
case '-':
|
|
21
|
+
return -value;
|
|
22
|
+
case '~':
|
|
23
|
+
return ~value;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
case 'binary': {
|
|
27
|
+
const left = evaluateConstantExpression(expression.left, constants);
|
|
28
|
+
const right = evaluateConstantExpression(expression.right, constants);
|
|
29
|
+
if (left === undefined || right === undefined)
|
|
30
|
+
return undefined;
|
|
31
|
+
switch (expression.operator) {
|
|
32
|
+
case '+':
|
|
33
|
+
return left + right;
|
|
34
|
+
case '-':
|
|
35
|
+
return left - right;
|
|
36
|
+
case '*':
|
|
37
|
+
return left * right;
|
|
38
|
+
case '/':
|
|
39
|
+
return right === 0 ? undefined : Math.trunc(left / right);
|
|
40
|
+
case '%':
|
|
41
|
+
return right === 0 ? undefined : left % right;
|
|
42
|
+
case '&':
|
|
43
|
+
return left & right;
|
|
44
|
+
case '^':
|
|
45
|
+
return left ^ right;
|
|
46
|
+
case '|':
|
|
47
|
+
return left | right;
|
|
48
|
+
case '<<':
|
|
49
|
+
return left << right;
|
|
50
|
+
case '>>':
|
|
51
|
+
return left >> right;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
case 'byte-function': {
|
|
55
|
+
const value = evaluateConstantExpression(expression.expression, constants);
|
|
56
|
+
if (value === undefined)
|
|
57
|
+
return undefined;
|
|
58
|
+
return expression.function === 'LSB' ? value & 0xff : (value >> 8) & 0xff;
|
|
59
|
+
}
|
|
60
|
+
default:
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function collectConstants(items) {
|
|
65
|
+
const constants = new Map();
|
|
66
|
+
let changed = true;
|
|
67
|
+
while (changed) {
|
|
68
|
+
changed = false;
|
|
69
|
+
for (const item of items) {
|
|
70
|
+
if (item.kind !== 'equ' || constants.has(item.name))
|
|
71
|
+
continue;
|
|
72
|
+
const value = evaluateConstantExpression(item.expression, constants);
|
|
73
|
+
if (value === undefined)
|
|
74
|
+
continue;
|
|
75
|
+
constants.set(item.name, value);
|
|
76
|
+
changed = true;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return constants;
|
|
80
|
+
}
|
|
7
81
|
function instructionCallTarget(item) {
|
|
8
82
|
if (item.kind !== 'instruction')
|
|
9
83
|
return undefined;
|
|
@@ -28,13 +102,14 @@ function instructionTailJumpTarget(item, entryNames) {
|
|
|
28
102
|
return undefined;
|
|
29
103
|
return target;
|
|
30
104
|
}
|
|
31
|
-
function toInstruction(item, labels) {
|
|
105
|
+
function toInstruction(item, labels, constants) {
|
|
32
106
|
return {
|
|
33
107
|
instruction: item.instruction,
|
|
34
108
|
file: item.span.sourceName,
|
|
35
109
|
line: item.span.line,
|
|
36
110
|
column: item.span.column,
|
|
37
111
|
labels: [...labels],
|
|
112
|
+
constants,
|
|
38
113
|
};
|
|
39
114
|
}
|
|
40
115
|
function pushDirectBoundary(boundaries, target, subject, file, line, column) {
|
|
@@ -43,6 +118,7 @@ function pushDirectBoundary(boundaries, target, subject, file, line, column) {
|
|
|
43
118
|
export function buildRegisterCareProgramModel(items) {
|
|
44
119
|
const routines = [];
|
|
45
120
|
const directCalls = [];
|
|
121
|
+
const constants = collectConstants(items);
|
|
46
122
|
const filesWithEntryLabels = new Set(items
|
|
47
123
|
.filter((item) => item.kind === 'label')
|
|
48
124
|
.filter((item) => item.isEntry === true)
|
|
@@ -73,6 +149,7 @@ export function buildRegisterCareProgramModel(items) {
|
|
|
73
149
|
labels: [...labels],
|
|
74
150
|
entryLabels: [...entryLabels],
|
|
75
151
|
instructions: [],
|
|
152
|
+
constants,
|
|
76
153
|
span: {
|
|
77
154
|
file: sourceName ?? '',
|
|
78
155
|
start: { line: routineStartLine, column: routineStartColumn ?? 1 },
|
|
@@ -89,6 +166,7 @@ export function buildRegisterCareProgramModel(items) {
|
|
|
89
166
|
labels: [...labels],
|
|
90
167
|
entryLabels: [...entryLabels],
|
|
91
168
|
instructions: [...instructions],
|
|
169
|
+
constants,
|
|
92
170
|
span: {
|
|
93
171
|
file: sourceName ?? '',
|
|
94
172
|
start: { line: routineStartLine, column: routineStartColumn ?? 1 },
|
|
@@ -121,7 +199,7 @@ export function buildRegisterCareProgramModel(items) {
|
|
|
121
199
|
if (item.span.sourceName !== sourceName) {
|
|
122
200
|
continue;
|
|
123
201
|
}
|
|
124
|
-
instructions.push(toInstruction(item, labels));
|
|
202
|
+
instructions.push(toInstruction(item, labels, constants));
|
|
125
203
|
const directTarget = instructionCallTarget(item);
|
|
126
204
|
if (directTarget !== undefined) {
|
|
127
205
|
pushDirectBoundary(directCalls, directTarget, `CALL ${directTarget}`, item.span.sourceName, item.span.line, item.span.column);
|
|
@@ -24,7 +24,13 @@ export function buildProfileSummaries(profileName) {
|
|
|
24
24
|
if (profile === undefined) {
|
|
25
25
|
return [];
|
|
26
26
|
}
|
|
27
|
-
return [
|
|
27
|
+
return [
|
|
28
|
+
...profile.rst.values(),
|
|
29
|
+
...profile.rstServices.values(),
|
|
30
|
+
...[...profile.rstDispatchers.values()].flatMap((dispatcher) => [
|
|
31
|
+
...dispatcher.services.values(),
|
|
32
|
+
]),
|
|
33
|
+
];
|
|
28
34
|
}
|
|
29
35
|
export function buildProfileSummaryLookup(profileName) {
|
|
30
36
|
const profile = getRegisterCareProfile(profileName);
|
|
@@ -37,6 +43,11 @@ export function buildProfileSummaryLookup(profileName) {
|
|
|
37
43
|
for (const summary of profile.rstServices.values()) {
|
|
38
44
|
out.set(summary.name, summary);
|
|
39
45
|
}
|
|
46
|
+
for (const dispatcher of profile.rstDispatchers.values()) {
|
|
47
|
+
for (const summary of dispatcher.services.values()) {
|
|
48
|
+
out.set(summary.name, summary);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
40
51
|
return out;
|
|
41
52
|
}
|
|
42
53
|
export function routineNames(routines) {
|
|
@@ -44,7 +55,9 @@ export function routineNames(routines) {
|
|
|
44
55
|
}
|
|
45
56
|
export function buildSummaries(routines, contractMap, profileSummaries = []) {
|
|
46
57
|
const names = routineNameSet(routines);
|
|
47
|
-
const routineSummaries = inferRoutineSummariesToFixedPoint([...routines], contractMap, names, [
|
|
58
|
+
const routineSummaries = inferRoutineSummariesToFixedPoint([...routines], contractMap, names, [
|
|
59
|
+
...profileSummaries,
|
|
60
|
+
]);
|
|
48
61
|
const summaries = routineSummaries.map((item) => item.summary);
|
|
49
62
|
return summariesWithExternalContracts(summaries, contractMap, names);
|
|
50
63
|
}
|
|
@@ -44,12 +44,14 @@ export interface RegisterCareInstruction {
|
|
|
44
44
|
line: number;
|
|
45
45
|
column: number;
|
|
46
46
|
labels: string[];
|
|
47
|
+
constants?: ReadonlyMap<string, number>;
|
|
47
48
|
}
|
|
48
49
|
export interface RegisterCareRoutine {
|
|
49
50
|
name: string;
|
|
50
51
|
labels: string[];
|
|
51
52
|
entryLabels: string[];
|
|
52
53
|
instructions: RegisterCareInstruction[];
|
|
54
|
+
constants?: ReadonlyMap<string, number>;
|
|
53
55
|
span: {
|
|
54
56
|
file: string;
|
|
55
57
|
start: {
|
|
@@ -86,13 +86,6 @@ function withLineComment(line, result) {
|
|
|
86
86
|
}
|
|
87
87
|
function parseEquItem(line, name, expressionText, span) {
|
|
88
88
|
const stringValue = parseWholeQuotedString(expressionText.trim());
|
|
89
|
-
const quotePolicyError = parseEquQuotePolicyError(expressionText.trim());
|
|
90
|
-
if (quotePolicyError) {
|
|
91
|
-
return {
|
|
92
|
-
items: [],
|
|
93
|
-
diagnostics: [parseError(line, quotePolicyError)],
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
89
|
const expression = stringValue !== undefined && stringValue.length > 1
|
|
97
90
|
? { kind: 'number', value: 0 }
|
|
98
91
|
: parseExpression(expressionText);
|
|
@@ -152,10 +145,9 @@ function parseCanonicalStatement(line, text, span) {
|
|
|
152
145
|
? parts.map(parseDataValue).filter((value) => value !== undefined)
|
|
153
146
|
: parts.map(parseExpression).filter((value) => value !== undefined);
|
|
154
147
|
if (values.length !== parts.length) {
|
|
155
|
-
const quotePolicyError = parts.map(parseDataQuotePolicyError).find(Boolean);
|
|
156
148
|
return {
|
|
157
149
|
items: [],
|
|
158
|
-
diagnostics: [parseError(line,
|
|
150
|
+
diagnostics: [parseError(line, `invalid .${directive} value list`)],
|
|
159
151
|
};
|
|
160
152
|
}
|
|
161
153
|
return {
|
|
@@ -362,7 +354,7 @@ function parseDataValue(text) {
|
|
|
362
354
|
function parseWholeQuotedString(text) {
|
|
363
355
|
const input = text.trim();
|
|
364
356
|
const quote = input[0];
|
|
365
|
-
if (quote !== '"' || input[input.length - 1] !== quote) {
|
|
357
|
+
if ((quote !== '"' && quote !== "'") || input[input.length - 1] !== quote) {
|
|
366
358
|
return undefined;
|
|
367
359
|
}
|
|
368
360
|
let value = '';
|
|
@@ -383,22 +375,6 @@ function parseWholeQuotedString(text) {
|
|
|
383
375
|
}
|
|
384
376
|
return value;
|
|
385
377
|
}
|
|
386
|
-
function parseEquQuotePolicyError(text) {
|
|
387
|
-
if (!text.startsWith('"') || !text.endsWith('"')) {
|
|
388
|
-
return undefined;
|
|
389
|
-
}
|
|
390
|
-
const stringValue = parseWholeQuotedString(text);
|
|
391
|
-
return stringValue !== undefined && stringValue.length === 1
|
|
392
|
-
? 'double-quoted values are strings; use single quotes for character literals'
|
|
393
|
-
: undefined;
|
|
394
|
-
}
|
|
395
|
-
function parseDataQuotePolicyError(text) {
|
|
396
|
-
const input = text.trim();
|
|
397
|
-
if (!input.startsWith("'") || !input.endsWith("'")) {
|
|
398
|
-
return undefined;
|
|
399
|
-
}
|
|
400
|
-
return 'single quotes are for one character literal; use double quotes for strings';
|
|
401
|
-
}
|
|
402
378
|
function normalizeEntryLabelName(raw) {
|
|
403
379
|
return raw.startsWith('@') ? raw.slice(1) : raw;
|
|
404
380
|
}
|