@hyperscale0/hsx 1.0.0-alpha.6 → 1.0.0-rc.1
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/CHANGELOG.md +48 -10
- package/LICENSING.md +3 -3
- package/README.md +37 -149
- package/TRADEMARKS.md +4 -4
- package/dist/src/ast.d.ts +129 -10
- package/dist/src/ast.d.ts.map +1 -1
- package/dist/src/cli.d.ts +2 -1
- package/dist/src/cli.d.ts.map +1 -1
- package/dist/src/cli.js +148 -12
- package/dist/src/cli.js.map +1 -1
- package/dist/src/compile.d.ts +41 -4
- package/dist/src/compile.d.ts.map +1 -1
- package/dist/src/compile.js +82 -15
- package/dist/src/compile.js.map +1 -1
- package/dist/src/cost.d.ts +78 -0
- package/dist/src/cost.d.ts.map +1 -0
- package/dist/src/cost.js +369 -0
- package/dist/src/cost.js.map +1 -0
- package/dist/src/diagnostics.d.ts +11 -0
- package/dist/src/diagnostics.d.ts.map +1 -0
- package/dist/src/diagnostics.js +420 -0
- package/dist/src/diagnostics.js.map +1 -0
- package/dist/src/emit.d.ts +35 -0
- package/dist/src/emit.d.ts.map +1 -0
- package/dist/src/emit.js +359 -0
- package/dist/src/emit.js.map +1 -0
- package/dist/src/entry-overrides.d.ts +17 -3
- package/dist/src/entry-overrides.d.ts.map +1 -1
- package/dist/src/entry-overrides.js +180 -35
- package/dist/src/entry-overrides.js.map +1 -1
- package/dist/src/format.d.ts +10 -0
- package/dist/src/format.d.ts.map +1 -0
- package/dist/src/format.js +183 -0
- package/dist/src/format.js.map +1 -0
- package/dist/src/index.d.ts +10 -4
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +8 -3
- package/dist/src/index.js.map +1 -1
- package/dist/src/ir.d.ts +66 -0
- package/dist/src/ir.d.ts.map +1 -0
- package/dist/src/ir.js +15 -0
- package/dist/src/ir.js.map +1 -0
- package/dist/src/lex.d.ts +9 -1
- package/dist/src/lex.d.ts.map +1 -1
- package/dist/src/lex.js +50 -4
- package/dist/src/lex.js.map +1 -1
- package/dist/src/limits.d.ts +2 -3
- package/dist/src/limits.d.ts.map +1 -1
- package/dist/src/limits.js +2 -3
- package/dist/src/limits.js.map +1 -1
- package/dist/src/modules.d.ts +36 -0
- package/dist/src/modules.d.ts.map +1 -0
- package/dist/src/modules.js +372 -0
- package/dist/src/modules.js.map +1 -0
- package/dist/src/parse.d.ts.map +1 -1
- package/dist/src/parse.js +590 -17
- package/dist/src/parse.js.map +1 -1
- package/dist/src/typecheck.d.ts +8 -0
- package/dist/src/typecheck.d.ts.map +1 -0
- package/dist/src/typecheck.js +2958 -0
- package/dist/src/typecheck.js.map +1 -0
- package/dist/src/version.d.ts +4 -15
- package/dist/src/version.d.ts.map +1 -1
- package/dist/src/version.js +4 -15
- package/dist/src/version.js.map +1 -1
- package/docs/README.md +23 -0
- package/docs/guide/01-first-program.md +24 -0
- package/docs/guide/02-money.md +25 -0
- package/docs/guide/03-instruments.md +22 -0
- package/docs/guide/04-lifecycles.md +25 -0
- package/docs/guide/05-fees-and-splits.md +45 -0
- package/docs/guide/06-schedules.md +22 -0
- package/docs/guide/07-composition.md +46 -0
- package/docs/guide/08-writing-a-module.md +26 -0
- package/docs/guide/09-cost.md +24 -0
- package/docs/guide/10-diagnostics.md +27 -0
- package/docs/llms-full.txt +1738 -0
- package/docs/llms.txt +45 -0
- package/docs/reference/cli.md +33 -0
- package/docs/reference/diagnostics.md +619 -0
- package/docs/reference/grammar.md +39 -0
- package/docs/reference/std/advance.md +35 -0
- package/docs/reference/std/cancellable_booking.md +35 -0
- package/docs/reference/std/captured_payment.md +43 -0
- package/docs/reference/std/conditional_disbursement.md +33 -0
- package/docs/reference/std/credit_facility.md +35 -0
- package/docs/reference/std/held_payment.md +64 -0
- package/docs/reference/std/instant_transfer.md +30 -0
- package/docs/reference/std/metered.md +27 -0
- package/docs/reference/std/pooled_split.md +26 -0
- package/docs/reference/std/premium_forward.md +39 -0
- package/docs/reference/std/reconciled_payout.md +33 -0
- package/docs/reference/std/recurring_collection.md +25 -0
- package/docs/reference/std/rotating_pool.md +42 -0
- package/docs/reference/std/scheduled.md +51 -0
- package/docs/reference/std/security_deposit.md +46 -0
- package/docs/reference/std/settlement_batch.md +42 -0
- package/docs/reference/std/swap.md +46 -0
- package/docs/reference/std/threshold_pool.md +47 -0
- package/docs/reference/std/weighted_distribution.md +39 -0
- package/docs/reference/types.md +20 -0
- package/docs/reference/udl-output.md +14 -0
- package/docs/sessions/2026-09-02-two-instruments.hsx +21 -0
- package/docs/sessions/2026-09-02-two-instruments.md +41 -0
- package/examples/01-first-program/README.md +45 -0
- package/examples/01-first-program/tip-jar.hsx +17 -0
- package/examples/02-imports-and-archetypes/README.md +99 -0
- package/examples/02-imports-and-archetypes/photo-booth.hsx +37 -0
- package/examples/03-diagnostics/README.md +68 -0
- package/examples/03-diagnostics/corner-shop-fixed.hsx +22 -0
- package/examples/03-diagnostics/corner-shop.hsx +19 -0
- package/examples/04-complete-product/README.md +87 -0
- package/examples/04-complete-product/study-hall.hsx +67 -0
- package/examples/05-watch-club/README.md +9 -0
- package/examples/05-watch-club/watch-club.hsx +124 -0
- package/examples/README.md +18 -0
- package/examples/advance/README.md +3 -0
- package/examples/advance/advance.hsx +13 -0
- package/examples/advance/advance.udl +339 -0
- package/examples/cancellable_booking/README.md +3 -0
- package/examples/cancellable_booking/cancellable_booking.hsx +14 -0
- package/examples/cancellable_booking/cancellable_booking.udl +391 -0
- package/examples/captured_payment/README.md +3 -0
- package/examples/captured_payment/captured_payment.hsx +21 -0
- package/examples/captured_payment/captured_payment.udl +677 -0
- package/examples/conditional_disbursement/README.md +3 -0
- package/examples/conditional_disbursement/conditional_disbursement.hsx +17 -0
- package/examples/conditional_disbursement/conditional_disbursement.udl +412 -0
- package/examples/cost-table.json +386 -0
- package/examples/credit_facility/README.md +3 -0
- package/examples/credit_facility/credit_facility.hsx +27 -0
- package/examples/credit_facility/credit_facility.udl +1373 -0
- package/examples/held_payment/README.md +3 -0
- package/examples/held_payment/held_payment.hsx +13 -0
- package/examples/held_payment/held_payment.udl +507 -0
- package/examples/instant_transfer/README.md +3 -0
- package/examples/instant_transfer/instant_transfer.hsx +16 -0
- package/examples/instant_transfer/instant_transfer.udl +364 -0
- package/examples/metered/README.md +3 -0
- package/examples/metered/metered.hsx +13 -0
- package/examples/metered/metered.udl +247 -0
- package/examples/pooled_split/README.md +3 -0
- package/examples/pooled_split/pooled_split.hsx +15 -0
- package/examples/pooled_split/pooled_split.udl +425 -0
- package/examples/premium_forward/README.md +3 -0
- package/examples/premium_forward/premium_forward.hsx +19 -0
- package/examples/premium_forward/premium_forward.udl +594 -0
- package/examples/reconciled_payout/README.md +3 -0
- package/examples/reconciled_payout/reconciled_payout.hsx +13 -0
- package/examples/reconciled_payout/reconciled_payout.udl +303 -0
- package/examples/recurring_collection/README.md +3 -0
- package/examples/recurring_collection/recurring_collection.hsx +21 -0
- package/examples/recurring_collection/recurring_collection.udl +1130 -0
- package/examples/rotating_pool/README.md +3 -0
- package/examples/rotating_pool/rotating_pool.hsx +18 -0
- package/examples/rotating_pool/rotating_pool.udl +4352 -0
- package/examples/scheduled/README.md +3 -0
- package/examples/scheduled/scheduled.hsx +12 -0
- package/examples/scheduled/scheduled.udl +335 -0
- package/examples/security_deposit/README.md +3 -0
- package/examples/security_deposit/security_deposit.hsx +21 -0
- package/examples/security_deposit/security_deposit.udl +293 -0
- package/examples/settlement_batch/README.md +3 -0
- package/examples/settlement_batch/settlement_batch.hsx +19 -0
- package/examples/settlement_batch/settlement_batch.udl +753 -0
- package/examples/swap/README.md +3 -0
- package/examples/swap/swap.hsx +19 -0
- package/examples/swap/swap.udl +876 -0
- package/examples/threshold_pool/README.md +3 -0
- package/examples/threshold_pool/threshold_pool.hsx +16 -0
- package/examples/threshold_pool/threshold_pool.udl +667 -0
- package/examples/weighted_distribution/README.md +3 -0
- package/examples/weighted_distribution/weighted_distribution.hsx +20 -0
- package/examples/weighted_distribution/weighted_distribution.udl +337 -0
- package/package.json +19 -6
- package/skills/hsx/SKILL.md +483 -0
- package/src/ast.ts +163 -9
- package/src/cli.ts +164 -12
- package/src/compile.ts +160 -20
- package/src/cost.ts +579 -0
- package/src/diagnostics.ts +442 -0
- package/src/emit.ts +447 -0
- package/src/entry-overrides.ts +263 -48
- package/src/format.ts +261 -0
- package/src/index.ts +32 -4
- package/src/ir.ts +99 -0
- package/src/lex.ts +59 -4
- package/src/limits.ts +2 -3
- package/src/modules.ts +444 -0
- package/src/parse.ts +655 -21
- package/src/typecheck.ts +3876 -0
- package/src/version.ts +4 -16
- package/std/SEMANTICS.md +130 -0
- package/std/settlements/advance.hsx +177 -0
- package/std/settlements/cancellable_booking.hsx +87 -0
- package/std/settlements/captured_payment.hsx +144 -0
- package/std/settlements/conditional_disbursement.hsx +185 -0
- package/std/settlements/credit_facility.hsx +167 -0
- package/std/settlements/held_payment.hsx +578 -0
- package/std/settlements/index.hsx +3 -0
- package/std/settlements/instant_transfer.hsx +190 -0
- package/std/settlements/metered.hsx +67 -0
- package/std/settlements/pooled_split.hsx +68 -0
- package/std/settlements/premium_forward.hsx +257 -0
- package/std/settlements/reconciled_payout.hsx +77 -0
- package/std/settlements/recurring_collection.hsx +27 -0
- package/std/settlements/rotating_pool.hsx +252 -0
- package/std/settlements/scheduled.hsx +641 -0
- package/std/settlements/security_deposit.hsx +166 -0
- package/std/settlements/settlement_batch.hsx +122 -0
- package/std/settlements/swap.hsx +1114 -0
- package/std/settlements/threshold_pool.hsx +221 -0
- package/std/settlements/weighted_distribution.hsx +141 -0
- package/dist/src/archetypes.d.ts +0 -17
- package/dist/src/archetypes.d.ts.map +0 -1
- package/dist/src/archetypes.js +0 -327
- package/dist/src/archetypes.js.map +0 -1
- package/dist/src/check.d.ts +0 -12
- package/dist/src/check.d.ts.map +0 -1
- package/dist/src/check.js +0 -1910
- package/dist/src/check.js.map +0 -1
- package/dist/src/lower.d.ts +0 -146
- package/dist/src/lower.d.ts.map +0 -1
- package/dist/src/lower.js +0 -4570
- package/dist/src/lower.js.map +0 -1
- package/dist/src/model.d.ts +0 -467
- package/dist/src/model.d.ts.map +0 -1
- package/dist/src/model.js +0 -15
- package/dist/src/model.js.map +0 -1
- package/spec/hsx-ir.schema.json +0 -920
- package/src/archetypes.ts +0 -340
- package/src/check.ts +0 -2938
- package/src/lower.ts +0 -5416
- package/src/model.ts +0 -519
package/dist/src/parse.js
CHANGED
|
@@ -23,6 +23,20 @@ export function parseProgram(source) {
|
|
|
23
23
|
program: { decls: [], span: { end: 0, start: 0 } },
|
|
24
24
|
};
|
|
25
25
|
}
|
|
26
|
+
const first = source.search(/\S/);
|
|
27
|
+
if (first >= 0 && source[first] === "{") {
|
|
28
|
+
return {
|
|
29
|
+
diagnostics: [
|
|
30
|
+
{
|
|
31
|
+
code: "HSX1014",
|
|
32
|
+
fix: "replace the JSON object with HSX declarations",
|
|
33
|
+
message: "JSON is not HSX",
|
|
34
|
+
span: { end: first + 1, start: first },
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
program: { decls: [], span: { end: source.length, start: 0 } },
|
|
38
|
+
};
|
|
39
|
+
}
|
|
26
40
|
const lexed = lex(source);
|
|
27
41
|
const parser = new Parser(lexed.tokens, [...lexed.diagnostics]);
|
|
28
42
|
const program = parser.parseProgram(source.length);
|
|
@@ -40,6 +54,10 @@ class Parser {
|
|
|
40
54
|
parseProgram(sourceLength) {
|
|
41
55
|
const decls = [];
|
|
42
56
|
while (!this.at("eof")) {
|
|
57
|
+
if (this.atPunct(";")) {
|
|
58
|
+
this.advance();
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
43
61
|
const decl = this.parseDecl();
|
|
44
62
|
if (decl) {
|
|
45
63
|
decls.push(decl);
|
|
@@ -50,33 +68,187 @@ class Parser {
|
|
|
50
68
|
return { decls, span: { end: sourceLength, start: 0 } };
|
|
51
69
|
}
|
|
52
70
|
// --- Declarations --------------------------------------------------------
|
|
53
|
-
parseDecl() {
|
|
71
|
+
parseDecl(exported = false) {
|
|
54
72
|
const token = this.peek();
|
|
55
73
|
if (token.kind !== "keyword" || token.text === "from") {
|
|
56
74
|
this.error(token.span, token.kind === "eof"
|
|
57
75
|
? "the program ends unexpectedly"
|
|
58
|
-
: `expected a declaration (program, import, party, asset, settlement, or port), found "${token.text}"`);
|
|
76
|
+
: `expected a declaration (module, program, use, expose, import, party, asset, subject, instrument, settlement, type, const, or port), found "${token.text}"`);
|
|
59
77
|
return undefined;
|
|
60
78
|
}
|
|
61
79
|
switch (token.text) {
|
|
80
|
+
case "export":
|
|
81
|
+
this.advance();
|
|
82
|
+
return this.parseDecl(true);
|
|
83
|
+
case "module":
|
|
84
|
+
return this.parseModuleDecl(exported);
|
|
62
85
|
case "program":
|
|
63
86
|
return this.parseProgramDecl();
|
|
87
|
+
case "use":
|
|
88
|
+
return this.parseUseDecl();
|
|
89
|
+
case "expose":
|
|
90
|
+
return this.parseExposeDecl();
|
|
64
91
|
case "import":
|
|
65
92
|
return this.parseImportDecl();
|
|
66
93
|
case "party":
|
|
67
94
|
return this.parsePartyDecl();
|
|
68
95
|
case "asset":
|
|
69
96
|
return this.parseAssetDecl();
|
|
97
|
+
case "subject":
|
|
98
|
+
return this.parseSubjectDecl(exported);
|
|
99
|
+
case "instrument":
|
|
100
|
+
return this.parseInstrumentDecl(exported);
|
|
70
101
|
case "settlement":
|
|
71
102
|
return this.parseSettlementDecl();
|
|
72
103
|
case "port":
|
|
73
104
|
return this.parsePortDecl();
|
|
105
|
+
case "type":
|
|
106
|
+
return this.parseTypeDecl(exported);
|
|
107
|
+
case "const":
|
|
108
|
+
return this.parseConstDecl(exported);
|
|
74
109
|
default:
|
|
75
110
|
this.error(token.span, `"${token.text}" cannot start a declaration`);
|
|
76
111
|
this.advance();
|
|
77
112
|
return undefined;
|
|
78
113
|
}
|
|
79
114
|
}
|
|
115
|
+
parseModuleDecl(exported) {
|
|
116
|
+
const start = this.advance();
|
|
117
|
+
if (exported) {
|
|
118
|
+
this.error(start.span, "a module declaration cannot be exported");
|
|
119
|
+
}
|
|
120
|
+
const name = this.parsePath("the module needs a dotted name");
|
|
121
|
+
return name
|
|
122
|
+
? { kind: "module", name, span: this.spanFrom(start.span) }
|
|
123
|
+
: undefined;
|
|
124
|
+
}
|
|
125
|
+
parseInstrumentDecl(exported) {
|
|
126
|
+
const start = this.advance();
|
|
127
|
+
const name = this.expectIdent("the instrument needs a snake_case name");
|
|
128
|
+
if (!name)
|
|
129
|
+
return undefined;
|
|
130
|
+
const typeParameters = this.parseTypeParameters();
|
|
131
|
+
if (!typeParameters)
|
|
132
|
+
return undefined;
|
|
133
|
+
const hasParameterList = this.atPunct("(");
|
|
134
|
+
const parameters = hasParameterList ? this.parseParameters() : [];
|
|
135
|
+
if (!parameters)
|
|
136
|
+
return undefined;
|
|
137
|
+
if (this.atPunct("=")) {
|
|
138
|
+
this.advance();
|
|
139
|
+
const application = this.parseApplication();
|
|
140
|
+
if (!application)
|
|
141
|
+
return undefined;
|
|
142
|
+
const metadata = this.atPunct("{") ? this.parseBlock() : undefined;
|
|
143
|
+
return {
|
|
144
|
+
application,
|
|
145
|
+
exported,
|
|
146
|
+
kind: "instrument_apply",
|
|
147
|
+
...(metadata ? { metadata } : {}),
|
|
148
|
+
name,
|
|
149
|
+
span: this.spanFrom(start.span),
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
if (!this.atPunct("{")) {
|
|
153
|
+
this.error(this.peek().span, `instrument ${name.name} needs a body or an exported instrument application`);
|
|
154
|
+
return undefined;
|
|
155
|
+
}
|
|
156
|
+
const body = this.parseBlock();
|
|
157
|
+
if (!body)
|
|
158
|
+
return undefined;
|
|
159
|
+
return {
|
|
160
|
+
body,
|
|
161
|
+
exported,
|
|
162
|
+
hasParameterList,
|
|
163
|
+
kind: "instrument",
|
|
164
|
+
name,
|
|
165
|
+
parameters,
|
|
166
|
+
span: this.spanFrom(start.span),
|
|
167
|
+
typeParameters,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
parseTypeDecl(exported) {
|
|
171
|
+
const start = this.advance();
|
|
172
|
+
const name = this.expectIdent("the type needs a name");
|
|
173
|
+
if (!name)
|
|
174
|
+
return undefined;
|
|
175
|
+
if (!this.expectPunct("=", `type ${name.name} needs = before its definition`)) {
|
|
176
|
+
return undefined;
|
|
177
|
+
}
|
|
178
|
+
const value = this.parseExpr();
|
|
179
|
+
return value
|
|
180
|
+
? { exported, kind: "type", name, span: this.spanFrom(start.span), value }
|
|
181
|
+
: undefined;
|
|
182
|
+
}
|
|
183
|
+
parseConstDecl(exported) {
|
|
184
|
+
const start = this.advance();
|
|
185
|
+
const name = this.expectIdent("the constant needs a name");
|
|
186
|
+
if (!name)
|
|
187
|
+
return undefined;
|
|
188
|
+
let type;
|
|
189
|
+
if (this.atPunct(":")) {
|
|
190
|
+
this.advance();
|
|
191
|
+
type = this.parseExpr();
|
|
192
|
+
if (!type)
|
|
193
|
+
return undefined;
|
|
194
|
+
}
|
|
195
|
+
if (!this.expectPunct("=", `const ${name.name} needs = before its value`)) {
|
|
196
|
+
return undefined;
|
|
197
|
+
}
|
|
198
|
+
const value = this.parseExpr();
|
|
199
|
+
return value
|
|
200
|
+
? {
|
|
201
|
+
exported,
|
|
202
|
+
kind: "const",
|
|
203
|
+
name,
|
|
204
|
+
span: this.spanFrom(start.span),
|
|
205
|
+
...(type ? { type } : {}),
|
|
206
|
+
value,
|
|
207
|
+
}
|
|
208
|
+
: undefined;
|
|
209
|
+
}
|
|
210
|
+
parseTypeParameters() {
|
|
211
|
+
if (!this.atPunct("<"))
|
|
212
|
+
return [];
|
|
213
|
+
this.advance();
|
|
214
|
+
const parameters = [];
|
|
215
|
+
while (!this.atPunct(">") && !this.at("eof")) {
|
|
216
|
+
const name = this.expectIdent("expected a type parameter");
|
|
217
|
+
if (!name)
|
|
218
|
+
return undefined;
|
|
219
|
+
parameters.push({ name, span: name.span });
|
|
220
|
+
if (this.atPunct(","))
|
|
221
|
+
this.advance();
|
|
222
|
+
}
|
|
223
|
+
return this.expectPunct(">", "the type parameter list never closes")
|
|
224
|
+
? parameters
|
|
225
|
+
: undefined;
|
|
226
|
+
}
|
|
227
|
+
parseParameters() {
|
|
228
|
+
this.advance();
|
|
229
|
+
const parameters = [];
|
|
230
|
+
while (!this.atPunct(")") && !this.at("eof")) {
|
|
231
|
+
const name = this.expectIdent("expected a parameter name");
|
|
232
|
+
if (!name)
|
|
233
|
+
return undefined;
|
|
234
|
+
if (!this.expectPunct(":", `parameter ${name.name} needs a type`)) {
|
|
235
|
+
return undefined;
|
|
236
|
+
}
|
|
237
|
+
const type = this.parseExpr();
|
|
238
|
+
if (!type)
|
|
239
|
+
return undefined;
|
|
240
|
+
parameters.push({
|
|
241
|
+
name,
|
|
242
|
+
span: { start: name.span.start, end: type.span.end },
|
|
243
|
+
type,
|
|
244
|
+
});
|
|
245
|
+
if (this.atPunct(","))
|
|
246
|
+
this.advance();
|
|
247
|
+
}
|
|
248
|
+
return this.expectPunct(")", "the parameter list never closes")
|
|
249
|
+
? parameters
|
|
250
|
+
: undefined;
|
|
251
|
+
}
|
|
80
252
|
parseProgramDecl() {
|
|
81
253
|
const start = this.advance();
|
|
82
254
|
const name = this.expectIdent("the program needs a snake_case name");
|
|
@@ -94,9 +266,43 @@ class Parser {
|
|
|
94
266
|
...(title ? { title } : {}),
|
|
95
267
|
};
|
|
96
268
|
}
|
|
269
|
+
parseUseDecl() {
|
|
270
|
+
const start = this.advance();
|
|
271
|
+
const instrument = this.expectIdent("use needs a published instrument id, like: use held_settlement");
|
|
272
|
+
return instrument
|
|
273
|
+
? { instrument, kind: "use", span: this.spanFrom(start.span) }
|
|
274
|
+
: undefined;
|
|
275
|
+
}
|
|
276
|
+
parseExposeDecl() {
|
|
277
|
+
const start = this.advance();
|
|
278
|
+
const instrument = this.expectIdent("expose needs an instrument action, like: expose held_settlement.release as releaseFunds");
|
|
279
|
+
if (!instrument)
|
|
280
|
+
return undefined;
|
|
281
|
+
if (!this.expectPunct(".", "expose separates the instrument and action with .")) {
|
|
282
|
+
return undefined;
|
|
283
|
+
}
|
|
284
|
+
const action = this.expectName("expose needs an action after the instrument id");
|
|
285
|
+
if (!action)
|
|
286
|
+
return undefined;
|
|
287
|
+
if (!this.at("keyword") || this.peek().text !== "as") {
|
|
288
|
+
this.error(this.peek().span, "expose needs as before its public action name");
|
|
289
|
+
return undefined;
|
|
290
|
+
}
|
|
291
|
+
this.advance();
|
|
292
|
+
const publicName = this.expectIdent("expose needs a camelCase public action name");
|
|
293
|
+
return publicName
|
|
294
|
+
? {
|
|
295
|
+
action,
|
|
296
|
+
instrument,
|
|
297
|
+
kind: "expose",
|
|
298
|
+
publicName,
|
|
299
|
+
span: this.spanFrom(start.span),
|
|
300
|
+
}
|
|
301
|
+
: undefined;
|
|
302
|
+
}
|
|
97
303
|
parseImportDecl() {
|
|
98
304
|
const start = this.advance();
|
|
99
|
-
if (!this.expectPunct("{", 'import lists its names in braces: import { held_payment } from "
|
|
305
|
+
if (!this.expectPunct("{", 'import lists its names in braces: import { held_payment } from "std/settlements"')) {
|
|
100
306
|
return undefined;
|
|
101
307
|
}
|
|
102
308
|
const names = [];
|
|
@@ -112,12 +318,12 @@ class Parser {
|
|
|
112
318
|
return undefined;
|
|
113
319
|
}
|
|
114
320
|
if (!this.at("keyword") || this.peek().text !== "from") {
|
|
115
|
-
this.error(this.peek().span, 'import needs from: import { held_payment } from "
|
|
321
|
+
this.error(this.peek().span, 'import needs from: import { held_payment } from "std/settlements"');
|
|
116
322
|
return undefined;
|
|
117
323
|
}
|
|
118
324
|
this.advance();
|
|
119
325
|
if (!this.at("string")) {
|
|
120
|
-
this.error(this.peek().span, 'the import source must be a quoted module name like "
|
|
326
|
+
this.error(this.peek().span, 'the import source must be a quoted module name like "std/settlements"');
|
|
121
327
|
return undefined;
|
|
122
328
|
}
|
|
123
329
|
const sourceToken = this.advance();
|
|
@@ -175,6 +381,23 @@ class Parser {
|
|
|
175
381
|
span: this.spanFrom(start.span),
|
|
176
382
|
};
|
|
177
383
|
}
|
|
384
|
+
parseSubjectDecl(exported) {
|
|
385
|
+
const start = this.advance();
|
|
386
|
+
if (exported) {
|
|
387
|
+
this.error(start.span, "a subject declaration cannot be exported");
|
|
388
|
+
}
|
|
389
|
+
const name = this.expectIdent("the subject needs a snake_case kind name");
|
|
390
|
+
if (!name)
|
|
391
|
+
return undefined;
|
|
392
|
+
if (!this.atPunct("{")) {
|
|
393
|
+
this.error(this.peek().span, `subject ${name.name} needs a body`);
|
|
394
|
+
return undefined;
|
|
395
|
+
}
|
|
396
|
+
const body = this.parseBlock();
|
|
397
|
+
return body
|
|
398
|
+
? { body, kind: "subject", name, span: this.spanFrom(start.span) }
|
|
399
|
+
: undefined;
|
|
400
|
+
}
|
|
178
401
|
parseSettlementDecl() {
|
|
179
402
|
const start = this.advance();
|
|
180
403
|
const name = this.expectIdent("the settlement needs a name, like: settlement sale = held_payment { ... }");
|
|
@@ -194,9 +417,20 @@ class Parser {
|
|
|
194
417
|
if (!body)
|
|
195
418
|
return undefined;
|
|
196
419
|
return {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
420
|
+
application: {
|
|
421
|
+
args: body.entries.map((row) => ({
|
|
422
|
+
kind: "binding",
|
|
423
|
+
name: row.key,
|
|
424
|
+
span: row.value.span,
|
|
425
|
+
type: row.value,
|
|
426
|
+
})),
|
|
427
|
+
callee: { kind: "path", parts: [archetype], span: archetype.span },
|
|
428
|
+
kind: "apply",
|
|
429
|
+
span: { start: archetype.span.start, end: body.span.end },
|
|
430
|
+
typeArgs: [],
|
|
431
|
+
},
|
|
432
|
+
exported: false,
|
|
433
|
+
kind: "instrument_apply",
|
|
200
434
|
name,
|
|
201
435
|
span: this.spanFrom(start.span),
|
|
202
436
|
};
|
|
@@ -238,19 +472,23 @@ class Parser {
|
|
|
238
472
|
this.depth += 1;
|
|
239
473
|
return true;
|
|
240
474
|
}
|
|
241
|
-
parseBlock() {
|
|
475
|
+
parseBlock(context = "general") {
|
|
242
476
|
if (!this.enterNesting(this.peek().span))
|
|
243
477
|
return undefined;
|
|
244
|
-
const block = this.parseBlockUnguarded();
|
|
478
|
+
const block = this.parseBlockUnguarded(context);
|
|
245
479
|
this.depth -= 1;
|
|
246
480
|
return block;
|
|
247
481
|
}
|
|
248
|
-
parseBlockUnguarded() {
|
|
482
|
+
parseBlockUnguarded(context) {
|
|
249
483
|
const open = this.advance();
|
|
250
484
|
const entries = [];
|
|
251
485
|
while (!this.atPunct("}") && !this.at("eof")) {
|
|
486
|
+
if (this.atPunct(";") || this.atPunct(",")) {
|
|
487
|
+
this.advance();
|
|
488
|
+
continue;
|
|
489
|
+
}
|
|
252
490
|
const before = this.index;
|
|
253
|
-
const entry = this.parseEntry();
|
|
491
|
+
const entry = this.parseEntry(context);
|
|
254
492
|
if (!entry) {
|
|
255
493
|
this.recoverInBlock();
|
|
256
494
|
// recoverInBlock bails without consuming when it lands on a depth-0
|
|
@@ -262,7 +500,7 @@ class Parser {
|
|
|
262
500
|
continue;
|
|
263
501
|
}
|
|
264
502
|
entries.push(entry);
|
|
265
|
-
if (this.atPunct(","))
|
|
503
|
+
if (this.atPunct(",") || this.atPunct(";"))
|
|
266
504
|
this.advance();
|
|
267
505
|
}
|
|
268
506
|
if (!this.expectPunct("}", "this block never closes; add the matching }")) {
|
|
@@ -270,15 +508,172 @@ class Parser {
|
|
|
270
508
|
}
|
|
271
509
|
return { entries, kind: "block", span: this.spanFrom(open.span) };
|
|
272
510
|
}
|
|
273
|
-
parseEntry() {
|
|
274
|
-
const
|
|
511
|
+
parseEntry(context) {
|
|
512
|
+
const token = this.peek();
|
|
513
|
+
const key = token.kind === "string"
|
|
514
|
+
? (() => {
|
|
515
|
+
this.advance();
|
|
516
|
+
return {
|
|
517
|
+
kind: "ident",
|
|
518
|
+
name: token.value,
|
|
519
|
+
quoted: !token.value.includes("["),
|
|
520
|
+
span: token.span,
|
|
521
|
+
};
|
|
522
|
+
})()
|
|
523
|
+
: this.isName(token) && (token.text === "action" || token.text === "on")
|
|
524
|
+
? this.parseIdent()
|
|
525
|
+
: this.parseTemplatedName("expected an entry name here");
|
|
275
526
|
if (!key)
|
|
276
527
|
return undefined;
|
|
277
528
|
const qualifiers = [];
|
|
529
|
+
if (key.name === "for") {
|
|
530
|
+
const binding = this.expectIdent("for needs a compile-time binding name");
|
|
531
|
+
const inWord = binding
|
|
532
|
+
? this.expectName("for needs in before its finite bound")
|
|
533
|
+
: undefined;
|
|
534
|
+
if (!binding || inWord?.name !== "in") {
|
|
535
|
+
this.error(this.peek().span, "write `for item in <finite bound> { ... }`");
|
|
536
|
+
return undefined;
|
|
537
|
+
}
|
|
538
|
+
const bound = this.parseExpr();
|
|
539
|
+
if (!bound || !this.atPunct("{")) {
|
|
540
|
+
this.error(this.peek().span, "a bounded comprehension needs a body in braces");
|
|
541
|
+
return undefined;
|
|
542
|
+
}
|
|
543
|
+
const value = this.parseBlock(context);
|
|
544
|
+
return value
|
|
545
|
+
? {
|
|
546
|
+
iteration: { binding, bound },
|
|
547
|
+
key,
|
|
548
|
+
qualifiers,
|
|
549
|
+
span: { start: key.span.start, end: value.span.end },
|
|
550
|
+
value,
|
|
551
|
+
}
|
|
552
|
+
: undefined;
|
|
553
|
+
}
|
|
554
|
+
if (key.name === "action" &&
|
|
555
|
+
(this.isName(this.peek()) || this.atPunct("["))) {
|
|
556
|
+
const action = this.parseTemplatedName("action needs a name");
|
|
557
|
+
if (!action)
|
|
558
|
+
return undefined;
|
|
559
|
+
qualifiers.push(action);
|
|
560
|
+
if (!this.atPunct("{")) {
|
|
561
|
+
this.error(this.peek().span, `action ${qualifiers[0]?.name ?? ""} needs a body`);
|
|
562
|
+
return undefined;
|
|
563
|
+
}
|
|
564
|
+
const value = this.parseBlock();
|
|
565
|
+
return value
|
|
566
|
+
? {
|
|
567
|
+
key,
|
|
568
|
+
qualifiers,
|
|
569
|
+
span: { start: key.span.start, end: value.span.end },
|
|
570
|
+
value,
|
|
571
|
+
}
|
|
572
|
+
: undefined;
|
|
573
|
+
}
|
|
574
|
+
if (context === "lifecycle" && key.name === "states") {
|
|
575
|
+
const items = [];
|
|
576
|
+
while (!this.atPunct(";") && !this.atPunct("}") && !this.at("eof")) {
|
|
577
|
+
const state = this.parseTemplatedName("expected a lifecycle state");
|
|
578
|
+
if (!state)
|
|
579
|
+
return undefined;
|
|
580
|
+
items.push(state);
|
|
581
|
+
}
|
|
582
|
+
const end = items.at(-1)?.span.end ?? key.span.end;
|
|
583
|
+
return {
|
|
584
|
+
key,
|
|
585
|
+
qualifiers,
|
|
586
|
+
span: { start: key.span.start, end },
|
|
587
|
+
value: { items, kind: "list", span: { start: key.span.end, end } },
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
if (context === "lifecycle" && key.name === "initial") {
|
|
591
|
+
const value = this.parseTemplatedName("initial needs one declared lifecycle state");
|
|
592
|
+
return value
|
|
593
|
+
? {
|
|
594
|
+
key,
|
|
595
|
+
qualifiers,
|
|
596
|
+
span: { start: key.span.start, end: value.span.end },
|
|
597
|
+
value,
|
|
598
|
+
}
|
|
599
|
+
: undefined;
|
|
600
|
+
}
|
|
601
|
+
if (context === "lifecycle" && key.name === "parked") {
|
|
602
|
+
const state = this.parseTemplatedName("parked needs a lifecycle state");
|
|
603
|
+
if (!state)
|
|
604
|
+
return undefined;
|
|
605
|
+
qualifiers.push(state);
|
|
606
|
+
const reasonWord = this.expectName('parked needs reason "..."');
|
|
607
|
+
if (!reasonWord || reasonWord.name !== "reason" || !this.at("string")) {
|
|
608
|
+
this.error(this.peek().span, 'parked needs reason "..."');
|
|
609
|
+
return undefined;
|
|
610
|
+
}
|
|
611
|
+
const token = this.advance();
|
|
612
|
+
const value = {
|
|
613
|
+
kind: "string",
|
|
614
|
+
span: token.span,
|
|
615
|
+
value: token.value,
|
|
616
|
+
};
|
|
617
|
+
return {
|
|
618
|
+
key,
|
|
619
|
+
qualifiers,
|
|
620
|
+
span: { start: key.span.start, end: value.span.end },
|
|
621
|
+
value,
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
if (context === "lifecycle" && key.name === "on") {
|
|
625
|
+
const action = this.parseTemplatedName("on needs an action name");
|
|
626
|
+
if (!action)
|
|
627
|
+
return undefined;
|
|
628
|
+
qualifiers.push(action);
|
|
629
|
+
if (!this.expectPunct(":", `on ${action.name} needs : before its source states`)) {
|
|
630
|
+
return undefined;
|
|
631
|
+
}
|
|
632
|
+
while (!this.atPunct("->") && !this.at("eof")) {
|
|
633
|
+
const state = this.parseTemplatedName("expected a source lifecycle state");
|
|
634
|
+
if (!state)
|
|
635
|
+
return undefined;
|
|
636
|
+
qualifiers.push(state);
|
|
637
|
+
if (this.atPunct("|"))
|
|
638
|
+
this.advance();
|
|
639
|
+
}
|
|
640
|
+
if (!this.expectPunct("->", `on ${action.name} needs -> before its target state`)) {
|
|
641
|
+
return undefined;
|
|
642
|
+
}
|
|
643
|
+
const value = this.parseTemplatedName("expected a target lifecycle state");
|
|
644
|
+
return value
|
|
645
|
+
? {
|
|
646
|
+
key,
|
|
647
|
+
qualifiers,
|
|
648
|
+
span: { start: key.span.start, end: value.span.end },
|
|
649
|
+
value,
|
|
650
|
+
}
|
|
651
|
+
: undefined;
|
|
652
|
+
}
|
|
653
|
+
if (key.name === "notify") {
|
|
654
|
+
const role = this.expectName("notify needs a declared role");
|
|
655
|
+
const via = role
|
|
656
|
+
? this.expectName("notify needs via before its channel")
|
|
657
|
+
: undefined;
|
|
658
|
+
const channel = via
|
|
659
|
+
? this.expectName("notify needs a channel")
|
|
660
|
+
: undefined;
|
|
661
|
+
if (!role || via?.name !== "via" || !channel)
|
|
662
|
+
return undefined;
|
|
663
|
+
qualifiers.push(role, channel);
|
|
664
|
+
return {
|
|
665
|
+
key,
|
|
666
|
+
qualifiers,
|
|
667
|
+
span: { start: key.span.start, end: channel.span.end },
|
|
668
|
+
value: { kind: "boolean", span: channel.span, value: true },
|
|
669
|
+
};
|
|
670
|
+
}
|
|
278
671
|
if (this.atPunct("(")) {
|
|
279
672
|
this.advance();
|
|
280
673
|
while (!this.atPunct(")") && !this.at("eof")) {
|
|
281
|
-
const qualifier = this.
|
|
674
|
+
const qualifier = this.at("number")
|
|
675
|
+
? this.parseIdent()
|
|
676
|
+
: this.expectIdent(`expected a qualifier inside ${key.name}( ... )`);
|
|
282
677
|
if (!qualifier)
|
|
283
678
|
return undefined;
|
|
284
679
|
qualifiers.push(qualifier);
|
|
@@ -289,12 +684,26 @@ class Parser {
|
|
|
289
684
|
return undefined;
|
|
290
685
|
}
|
|
291
686
|
}
|
|
687
|
+
if ((key.name === "requires" || key.name === "computes") &&
|
|
688
|
+
this.isName(this.peek())) {
|
|
689
|
+
while (this.isName(this.peek()))
|
|
690
|
+
qualifiers.push(this.parseIdent());
|
|
691
|
+
}
|
|
292
692
|
let value;
|
|
293
693
|
if (this.atPunct("{")) {
|
|
294
|
-
value = this.parseBlock(
|
|
694
|
+
value = this.parseBlock(key.name === "lifecycle" || context === "lifecycle"
|
|
695
|
+
? "lifecycle"
|
|
696
|
+
: "general");
|
|
295
697
|
}
|
|
296
698
|
else if (this.atPunct(":")) {
|
|
297
699
|
this.advance();
|
|
700
|
+
value = this.atPunct("{")
|
|
701
|
+
? this.parseBlock(key.name === "lifecycle" || context === "lifecycle"
|
|
702
|
+
? "lifecycle"
|
|
703
|
+
: "general")
|
|
704
|
+
: this.parseExpr();
|
|
705
|
+
}
|
|
706
|
+
else if (this.canStartExpr(this.peek())) {
|
|
298
707
|
value = this.parseExpr();
|
|
299
708
|
}
|
|
300
709
|
else {
|
|
@@ -392,8 +801,40 @@ class Parser {
|
|
|
392
801
|
...(within ? { within } : {}),
|
|
393
802
|
};
|
|
394
803
|
}
|
|
804
|
+
if (token.kind === "keyword") {
|
|
805
|
+
return this.parseIdent();
|
|
806
|
+
}
|
|
807
|
+
if (token.kind === "ident" &&
|
|
808
|
+
/^[A-Z]{3}$/.test(token.text) &&
|
|
809
|
+
this.peekAt(1).kind === "number") {
|
|
810
|
+
const currency = this.parseIdent();
|
|
811
|
+
const amount = this.advance();
|
|
812
|
+
return {
|
|
813
|
+
currency,
|
|
814
|
+
kind: "money",
|
|
815
|
+
raw: amount.value,
|
|
816
|
+
span: { start: currency.span.start, end: amount.span.end },
|
|
817
|
+
};
|
|
818
|
+
}
|
|
395
819
|
if (token.kind === "ident") {
|
|
396
820
|
const ident = this.parseIdent();
|
|
821
|
+
if (ident.name === "true" || ident.name === "false") {
|
|
822
|
+
return {
|
|
823
|
+
kind: "boolean",
|
|
824
|
+
span: ident.span,
|
|
825
|
+
value: ident.name === "true",
|
|
826
|
+
};
|
|
827
|
+
}
|
|
828
|
+
if (ident.name === "decided" && this.atPunct("{")) {
|
|
829
|
+
const body = this.parseBlock();
|
|
830
|
+
if (!body)
|
|
831
|
+
return undefined;
|
|
832
|
+
return {
|
|
833
|
+
body,
|
|
834
|
+
kind: "decided_amount",
|
|
835
|
+
span: { end: body.span.end, start: ident.span.start },
|
|
836
|
+
};
|
|
837
|
+
}
|
|
397
838
|
if (this.atPunct(".")) {
|
|
398
839
|
this.advance();
|
|
399
840
|
const member = this.expectIdent(`expected the exit after ${ident.name}., like ${ident.name}.release`);
|
|
@@ -406,6 +847,17 @@ class Parser {
|
|
|
406
847
|
span: { end: member.span.end, start: ident.span.start },
|
|
407
848
|
};
|
|
408
849
|
}
|
|
850
|
+
if (this.atPunct("<")) {
|
|
851
|
+
const args = this.parseAngleArguments();
|
|
852
|
+
if (!args)
|
|
853
|
+
return undefined;
|
|
854
|
+
return {
|
|
855
|
+
args,
|
|
856
|
+
callee: ident,
|
|
857
|
+
kind: "type_apply",
|
|
858
|
+
span: this.spanFrom(ident.span),
|
|
859
|
+
};
|
|
860
|
+
}
|
|
409
861
|
if (this.atPunct("(")) {
|
|
410
862
|
this.advance();
|
|
411
863
|
const args = [];
|
|
@@ -482,6 +934,9 @@ class Parser {
|
|
|
482
934
|
peek() {
|
|
483
935
|
return this.tokens[this.index];
|
|
484
936
|
}
|
|
937
|
+
peekAt(offset) {
|
|
938
|
+
return this.tokens[Math.min(this.index + offset, this.tokens.length - 1)];
|
|
939
|
+
}
|
|
485
940
|
previous() {
|
|
486
941
|
return this.tokens[Math.max(0, this.index - 1)];
|
|
487
942
|
}
|
|
@@ -502,6 +957,124 @@ class Parser {
|
|
|
502
957
|
const token = this.advance();
|
|
503
958
|
return { kind: "ident", name: token.text, span: token.span };
|
|
504
959
|
}
|
|
960
|
+
isName(token) {
|
|
961
|
+
return token.kind === "ident" || token.kind === "keyword";
|
|
962
|
+
}
|
|
963
|
+
expectName(message) {
|
|
964
|
+
if (this.isName(this.peek()))
|
|
965
|
+
return this.parseIdent();
|
|
966
|
+
this.error(this.peek().span, message);
|
|
967
|
+
return undefined;
|
|
968
|
+
}
|
|
969
|
+
parseTemplatedName(message) {
|
|
970
|
+
if (this.atPunct("[")) {
|
|
971
|
+
const open = this.advance();
|
|
972
|
+
const binding = this.expectIdent("an indexed name needs its loop binding");
|
|
973
|
+
if (!binding || !this.expectPunct("]", "an indexed name needs ]")) {
|
|
974
|
+
return undefined;
|
|
975
|
+
}
|
|
976
|
+
const suffix = this.isName(this.peek()) ? this.parseIdent() : undefined;
|
|
977
|
+
return {
|
|
978
|
+
kind: "ident",
|
|
979
|
+
name: `[${binding.name}]${suffix?.name ?? ""}`,
|
|
980
|
+
span: {
|
|
981
|
+
start: open.span.start,
|
|
982
|
+
end: suffix?.span.end ?? this.previous().span.end,
|
|
983
|
+
},
|
|
984
|
+
};
|
|
985
|
+
}
|
|
986
|
+
const base = this.expectName(message);
|
|
987
|
+
if (!base || !this.atPunct("["))
|
|
988
|
+
return base;
|
|
989
|
+
this.advance();
|
|
990
|
+
const binding = this.expectIdent("an indexed name needs its loop binding");
|
|
991
|
+
if (!binding || !this.expectPunct("]", "an indexed name needs ]")) {
|
|
992
|
+
return undefined;
|
|
993
|
+
}
|
|
994
|
+
const suffix = this.isName(this.peek()) ? this.parseIdent() : undefined;
|
|
995
|
+
return {
|
|
996
|
+
kind: "ident",
|
|
997
|
+
name: `${base.name}[${binding.name}]${suffix?.name ?? ""}`,
|
|
998
|
+
span: {
|
|
999
|
+
start: base.span.start,
|
|
1000
|
+
end: suffix?.span.end ?? this.previous().span.end,
|
|
1001
|
+
},
|
|
1002
|
+
};
|
|
1003
|
+
}
|
|
1004
|
+
canStartExpr(token) {
|
|
1005
|
+
return (token.kind === "ident" ||
|
|
1006
|
+
token.kind === "keyword" ||
|
|
1007
|
+
token.kind === "number" ||
|
|
1008
|
+
token.kind === "percent" ||
|
|
1009
|
+
token.kind === "string" ||
|
|
1010
|
+
(token.kind === "punct" && (token.text === "{" || token.text === "[")));
|
|
1011
|
+
}
|
|
1012
|
+
parsePath(message) {
|
|
1013
|
+
const first = this.expectName(message);
|
|
1014
|
+
if (!first)
|
|
1015
|
+
return undefined;
|
|
1016
|
+
const parts = [first];
|
|
1017
|
+
while (this.atPunct(".")) {
|
|
1018
|
+
this.advance();
|
|
1019
|
+
const part = this.expectName("expected a name after .");
|
|
1020
|
+
if (!part)
|
|
1021
|
+
return undefined;
|
|
1022
|
+
parts.push(part);
|
|
1023
|
+
}
|
|
1024
|
+
return {
|
|
1025
|
+
kind: "path",
|
|
1026
|
+
parts,
|
|
1027
|
+
span: {
|
|
1028
|
+
start: first.span.start,
|
|
1029
|
+
end: parts.at(-1)?.span.end ?? first.span.end,
|
|
1030
|
+
},
|
|
1031
|
+
};
|
|
1032
|
+
}
|
|
1033
|
+
parseAngleArguments() {
|
|
1034
|
+
this.advance();
|
|
1035
|
+
const args = [];
|
|
1036
|
+
while (!this.atPunct(">") && !this.at("eof")) {
|
|
1037
|
+
const arg = this.parseExpr();
|
|
1038
|
+
if (!arg)
|
|
1039
|
+
return undefined;
|
|
1040
|
+
args.push(arg);
|
|
1041
|
+
if (this.atPunct(","))
|
|
1042
|
+
this.advance();
|
|
1043
|
+
}
|
|
1044
|
+
return this.expectPunct(">", "the type argument list never closes")
|
|
1045
|
+
? args
|
|
1046
|
+
: undefined;
|
|
1047
|
+
}
|
|
1048
|
+
parseApplication() {
|
|
1049
|
+
const callee = this.parsePath("expected an instrument function name");
|
|
1050
|
+
if (!callee)
|
|
1051
|
+
return undefined;
|
|
1052
|
+
const typeArgs = this.atPunct("<") ? this.parseAngleArguments() : [];
|
|
1053
|
+
if (!typeArgs)
|
|
1054
|
+
return undefined;
|
|
1055
|
+
if (!this.expectPunct("(", "an instrument application needs arguments in parentheses")) {
|
|
1056
|
+
return undefined;
|
|
1057
|
+
}
|
|
1058
|
+
const args = [];
|
|
1059
|
+
while (!this.atPunct(")") && !this.at("eof")) {
|
|
1060
|
+
const arg = this.parseExpr();
|
|
1061
|
+
if (!arg)
|
|
1062
|
+
return undefined;
|
|
1063
|
+
args.push(arg);
|
|
1064
|
+
if (this.atPunct(","))
|
|
1065
|
+
this.advance();
|
|
1066
|
+
}
|
|
1067
|
+
if (!this.expectPunct(")", "the instrument argument list never closes")) {
|
|
1068
|
+
return undefined;
|
|
1069
|
+
}
|
|
1070
|
+
return {
|
|
1071
|
+
args,
|
|
1072
|
+
callee,
|
|
1073
|
+
kind: "apply",
|
|
1074
|
+
span: { start: callee.span.start, end: this.previous().span.end },
|
|
1075
|
+
typeArgs,
|
|
1076
|
+
};
|
|
1077
|
+
}
|
|
505
1078
|
expectIdent(message) {
|
|
506
1079
|
if (this.at("ident"))
|
|
507
1080
|
return this.parseIdent();
|