UNPKG

6.15 kBTypeScriptView Raw
1import { CompileTimeConstants, CompileTimeHeap } from '../program';
2import { Dict, Option } from '../core';
3import { SingleBuilderOperand } from './operands';
4import { NamedBlocks, HandleResult } from '../template';
5import { Op, MachineOp } from '../vm-opcodes';
6import * as WireFormat from './wire-format';
7import { InternalComponentCapabilities } from '../managers/internal/component';
8import { CompileTimeComponent } from '../..';
9
10// These values are used in the same space as standard opcodes, so we need to
11// start them at a higher value to prevent collisions
12export const enum HighLevelBuilderOpcode {
13 Label = 1000,
14 StartLabels = 1001,
15 StopLabels = 1002,
16
17 Start = Label,
18 End = StopLabels,
19}
20
21export const enum HighLevelResolutionOpcode {
22 ResolveModifier = 1003,
23
24 ResolveComponent = 1004,
25
26 ResolveHelper = 1005,
27 ResolveOptionalHelper = 1006,
28
29 ResolveComponentOrHelper = 1007,
30 ResolveOptionalComponentOrHelper = 1008,
31
32 ResolveFree = 1009,
33 ResolveLocal = 1010,
34 ResolveTemplateLocal = 1011,
35
36 Start = ResolveModifier,
37 End = ResolveTemplateLocal,
38}
39
40export interface SimpleArgsOptions {
41 positional: Option<WireFormat.Core.Params>;
42 named: Option<WireFormat.Core.Hash>;
43 atNames: boolean;
44}
45
46export interface ArgsOptions extends SimpleArgsOptions {
47 named: WireFormat.Core.Hash;
48 blocks: NamedBlocks;
49}
50
51export type StartLabelsOp = [op: HighLevelBuilderOpcode.StartLabels];
52
53export type StopLabelsOp = [op: HighLevelBuilderOpcode.StopLabels];
54
55export type LabelOp = [op: HighLevelBuilderOpcode.Label, op1: string];
56
57export type HighLevelBuilderOp = StartLabelsOp | StopLabelsOp | LabelOp;
58
59export type ResolveModifierOp = [
60 op: HighLevelResolutionOpcode.ResolveModifier,
61 op1: WireFormat.Expressions.Expression,
62 op2: (handle: number) => void
63];
64
65export type ResolveComponentOp = [
66 op: HighLevelResolutionOpcode.ResolveComponent,
67 op1: WireFormat.Expressions.Expression,
68 op2: (component: CompileTimeComponent) => void
69];
70
71export type ResolveComponentOrHelperOp = [
72 op: HighLevelResolutionOpcode.ResolveComponentOrHelper,
73 op1: WireFormat.Expressions.Expression,
74 op2: {
75 ifComponent: (component: CompileTimeComponent) => void;
76 ifHelper: (handle: number) => void;
77 }
78];
79
80export type ResolveHelperOp = [
81 op: HighLevelResolutionOpcode.ResolveHelper,
82 op1: WireFormat.Expressions.Expression,
83 op2: (handle: number) => void
84];
85
86export type ResolveOptionalHelperOp = [
87 op: HighLevelResolutionOpcode.ResolveOptionalHelper,
88 op1: WireFormat.Expressions.Expression,
89 op2: {
90 ifHelper: (handle: number, name: string, moduleName: string) => void;
91 ifFallback: (name: string, moduleName: string) => void;
92 }
93];
94
95export type ResolveOptionalComponentOrHelperOp = [
96 op: HighLevelResolutionOpcode.ResolveOptionalComponentOrHelper,
97 op1: WireFormat.Expressions.Expression,
98 op2: {
99 ifComponent: (component: CompileTimeComponent) => void;
100 ifHelper: (handle: number) => void;
101 ifValue: (handle: number) => void;
102 ifFallback: (name: string) => void;
103 }
104];
105
106export type ResolveFreeOp = [
107 op: HighLevelResolutionOpcode.ResolveFree,
108 op1: number,
109 op2: (handle: number) => void
110];
111
112export type ResolveTemplateLocalOp = [
113 op: HighLevelResolutionOpcode.ResolveTemplateLocal,
114 op1: number,
115 op2: (handle: number) => void
116];
117
118export type ResolveLocalOp = [
119 op: HighLevelResolutionOpcode.ResolveLocal,
120 op1: number,
121 op2: (name: string, moduleName: string) => void
122];
123
124export type HighLevelResolutionOp =
125 | ResolveModifierOp
126 | ResolveComponentOp
127 | ResolveComponentOrHelperOp
128 | ResolveHelperOp
129 | ResolveOptionalHelperOp
130 | ResolveOptionalComponentOrHelperOp
131 | ResolveFreeOp
132 | ResolveTemplateLocalOp
133 | ResolveLocalOp;
134
135export type HighLevelOp = HighLevelBuilderOp | HighLevelResolutionOp;
136
137export type BuilderOpcode = Op | MachineOp;
138
139export type BuilderOp = [
140 op: BuilderOpcode,
141 op1?: SingleBuilderOperand,
142 op1?: SingleBuilderOperand,
143 op1?: SingleBuilderOperand
144];
145
146export interface EncoderError {
147 problem: string;
148 span: {
149 start: number;
150 end: number;
151 };
152}
153
154/**
155 * The Encoder receives a stream of opcodes from the syntax compiler and turns
156 * them into a binary program.
157 */
158export interface Encoder {
159 /**
160 * Finalize the current compilation unit, add a `(Return)`, and push the opcodes from
161 * the buffer into the program. At this point, some of the opcodes might still be
162 * placeholders, such as in the case of recursively compiled templates.
163 *
164 * @param compiler
165 * @param size
166 */
167 commit(size: number): HandleResult;
168
169 /**
170 * Push a syscall into the program with up to three optional
171 * operands.
172 *
173 * @param opcode
174 * @param args up to three operands, formatted as
175 * { type: "type", value: value }
176 */
177 push(
178 constants: CompileTimeConstants,
179 opcode: BuilderOpcode,
180 ...args: SingleBuilderOperand[]
181 ): void;
182
183 /**
184 * Start a new labels block. A labels block is a scope for labels that
185 * can be referred to before they are declared. For example, when compiling
186 * an `if`, the `JumpUnless` opcode occurs before the target label. To
187 * accommodate this use-case ergonomically, the `Encoder` allows a syntax
188 * to create a labels block and then refer to labels that have not yet
189 * been declared. Once the block is complete, a second pass replaces the
190 * label names with offsets.
191 *
192 * The pattern is:
193 *
194 * ```ts
195 * encoder.reserve(Op.JumpUnless);
196 * encoder.target(encoder.pos, 'ELSE');
197 * ```
198 *
199 * The `reserve` method creates a placeholder opcode with space for a target
200 * in the future, and the `target` method registers the blank operand position
201 * to be replaced with an offset to `ELSE`, once it's known.
202 */
203 startLabels(): void;
204
205 /**
206 * Finish the current labels block and replace label names with offsets,
207 * now that all of the offsets are known.
208 */
209 stopLabels(): void;
210
211 /**
212 * Mark the current position with a label name. This label name
213 * can be used by any other opcode in this label block.
214 * @param name
215 * @param index
216 */
217 label(name: string): void;
218
219 error(error: EncoderError): void;
220}