@aexol/spectral 0.9.221 → 0.9.222
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/agent/index.d.ts +7 -3
- package/dist/agent/index.d.ts.map +1 -1
- package/dist/agent/index.js +35 -8
- package/dist/extensions/aexol-backend-client.d.ts +85 -0
- package/dist/extensions/aexol-backend-client.d.ts.map +1 -0
- package/dist/extensions/aexol-backend-client.js +159 -0
- package/dist/extensions/aexol-mcp.d.ts +10 -6
- package/dist/extensions/aexol-mcp.d.ts.map +1 -1
- package/dist/extensions/aexol-mcp.js +21 -106
- package/dist/extensions/logic-trace-backend-draft.d.ts +68 -0
- package/dist/extensions/logic-trace-backend-draft.d.ts.map +1 -0
- package/dist/extensions/logic-trace-backend-draft.js +171 -0
- package/dist/extensions/logic-trace-validate.d.ts +33 -0
- package/dist/extensions/logic-trace-validate.d.ts.map +1 -0
- package/dist/extensions/logic-trace-validate.js +73 -0
- package/dist/extensions/logic-trace.d.ts +213 -0
- package/dist/extensions/logic-trace.d.ts.map +1 -0
- package/dist/extensions/logic-trace.js +1098 -0
- package/dist/generated/parser/lexer.d.ts +148 -0
- package/dist/generated/parser/lexer.d.ts.map +1 -0
- package/dist/generated/parser/lexer.js +877 -0
- package/dist/generated/parser/mod.d.ts +26 -0
- package/dist/generated/parser/mod.d.ts.map +1 -0
- package/dist/generated/parser/mod.js +30 -0
- package/dist/generated/parser/parser.d.ts +161 -0
- package/dist/generated/parser/parser.d.ts.map +1 -0
- package/dist/generated/parser/parser.js +754 -0
- package/dist/generated/parser/tokenizer.d.ts +107 -0
- package/dist/generated/parser/tokenizer.d.ts.map +1 -0
- package/dist/generated/parser/tokenizer.js +202 -0
- package/dist/generated/parser/validator.d.ts +45 -0
- package/dist/generated/parser/validator.d.ts.map +1 -0
- package/dist/generated/parser/validator.js +202 -0
- package/dist/generated/zeus/const.d.ts.map +1 -1
- package/dist/generated/zeus/const.js +4 -170
- package/dist/generated/zeus/index.d.ts +8 -873
- package/dist/generated/zeus/index.d.ts.map +1 -1
- package/dist/generated/zeus/index.js +0 -7
- package/dist/mcp/ui-stream-types.d.ts +2 -2
- package/dist/memory/tools/share-project-observation.js +1 -1
- package/dist/memory/tools/write-project-observation.d.ts.map +1 -1
- package/dist/memory/tools/write-project-observation.js +4 -3
- package/dist/sdk/agent-core/harness/types.d.ts +0 -1
- package/dist/sdk/agent-core/harness/types.d.ts.map +1 -1
- package/dist/sdk/agent-core/index.d.ts +0 -8
- package/dist/sdk/agent-core/index.d.ts.map +1 -1
- package/dist/sdk/agent-core/index.js +0 -9
- package/dist/sdk/coding-agent/core/extensions/index.d.ts +1 -1
- package/dist/sdk/coding-agent/core/extensions/index.d.ts.map +1 -1
- package/dist/sdk/coding-agent/core/extensions/native-extensions.js +2 -2
- package/dist/sdk/coding-agent/core/extensions/types.d.ts +20 -1
- package/dist/sdk/coding-agent/core/extensions/types.d.ts.map +1 -1
- package/dist/sdk/coding-agent/core/tools/tool-definition-wrapper.d.ts +9 -4
- package/dist/sdk/coding-agent/core/tools/tool-definition-wrapper.d.ts.map +1 -1
- package/dist/sdk/coding-agent/core/tools/tool-definition-wrapper.js +15 -2
- package/dist/sdk/coding-agent/index.d.ts +1 -1
- package/dist/sdk/coding-agent/index.d.ts.map +1 -1
- package/dist/server/agent-bridge/headless-ui.d.ts +20 -0
- package/dist/server/agent-bridge/headless-ui.d.ts.map +1 -0
- package/dist/server/agent-bridge/headless-ui.js +85 -0
- package/dist/server/agent-bridge/pricing.d.ts +37 -0
- package/dist/server/agent-bridge/pricing.d.ts.map +1 -0
- package/dist/server/agent-bridge/pricing.js +202 -0
- package/dist/server/agent-bridge/rehydrate.d.ts +35 -0
- package/dist/server/agent-bridge/rehydrate.d.ts.map +1 -0
- package/dist/server/agent-bridge/rehydrate.js +122 -0
- package/dist/server/agent-bridge/synthetic-model.d.ts +27 -0
- package/dist/server/agent-bridge/synthetic-model.d.ts.map +1 -0
- package/dist/server/agent-bridge/synthetic-model.js +92 -0
- package/dist/server/agent-bridge.d.ts +11 -1
- package/dist/server/agent-bridge.d.ts.map +1 -1
- package/dist/server/agent-bridge.js +42 -481
- package/dist/server/session-stream/compaction-events.d.ts +13 -0
- package/dist/server/session-stream/compaction-events.d.ts.map +1 -0
- package/dist/server/session-stream/compaction-events.js +32 -0
- package/dist/server/session-stream/history-prune.d.ts +35 -0
- package/dist/server/session-stream/history-prune.d.ts.map +1 -0
- package/dist/server/session-stream/history-prune.js +182 -0
- package/dist/server/session-stream/memory.d.ts +48 -0
- package/dist/server/session-stream/memory.d.ts.map +1 -0
- package/dist/server/session-stream/memory.js +136 -0
- package/dist/server/session-stream/replay-tail.d.ts +4 -0
- package/dist/server/session-stream/replay-tail.d.ts.map +1 -0
- package/dist/server/session-stream/replay-tail.js +39 -0
- package/dist/server/session-stream/turn-events.d.ts +33 -0
- package/dist/server/session-stream/turn-events.d.ts.map +1 -0
- package/dist/server/session-stream/turn-events.js +65 -0
- package/dist/server/session-stream/turn-silence.d.ts +8 -0
- package/dist/server/session-stream/turn-silence.d.ts.map +1 -0
- package/dist/server/session-stream/turn-silence.js +72 -0
- package/dist/server/session-stream.d.ts +137 -36
- package/dist/server/session-stream.d.ts.map +1 -1
- package/dist/server/session-stream.js +18 -509
- package/dist/server/storage/observation-ranking.d.ts +14 -0
- package/dist/server/storage/observation-ranking.d.ts.map +1 -0
- package/dist/server/storage/observation-ranking.js +37 -0
- package/dist/server/storage/preflight.d.ts +20 -0
- package/dist/server/storage/preflight.d.ts.map +1 -0
- package/dist/server/storage/preflight.js +22 -0
- package/dist/server/storage/project-binding.d.ts +5 -0
- package/dist/server/storage/project-binding.d.ts.map +1 -0
- package/dist/server/storage/project-binding.js +32 -0
- package/dist/server/storage/rows.d.ts +104 -0
- package/dist/server/storage/rows.d.ts.map +1 -0
- package/dist/server/storage/rows.js +2 -0
- package/dist/server/storage/schema.d.ts +20 -0
- package/dist/server/storage/schema.d.ts.map +1 -0
- package/dist/server/storage/schema.js +134 -0
- package/dist/server/storage/types.d.ts +66 -0
- package/dist/server/storage/types.d.ts.map +1 -0
- package/dist/server/storage/types.js +1 -0
- package/dist/server/storage.d.ts +7 -94
- package/dist/server/storage.d.ts.map +1 -1
- package/dist/server/storage.js +6 -224
- package/dist/server/wire.d.ts +6 -0
- package/dist/server/wire.d.ts.map +1 -1
- package/package.json +2 -1
- package/dist/sdk/agent-core/harness/agent-harness.d.ts +0 -92
- package/dist/sdk/agent-core/harness/agent-harness.d.ts.map +0 -1
- package/dist/sdk/agent-core/harness/agent-harness.js +0 -899
- package/dist/sdk/agent-core/harness/env/nodejs.d.ts +0 -51
- package/dist/sdk/agent-core/harness/env/nodejs.d.ts.map +0 -1
- package/dist/sdk/agent-core/harness/env/nodejs.js +0 -485
- package/dist/sdk/agent-core/harness/prompt-templates.d.ts +0 -48
- package/dist/sdk/agent-core/harness/prompt-templates.d.ts.map +0 -1
- package/dist/sdk/agent-core/harness/prompt-templates.js +0 -229
- package/dist/sdk/agent-core/harness/session/jsonl-repo.d.ts +0 -26
- package/dist/sdk/agent-core/harness/session/jsonl-repo.d.ts.map +0 -1
- package/dist/sdk/agent-core/harness/session/jsonl-repo.js +0 -100
- package/dist/sdk/agent-core/harness/session/jsonl-storage.d.ts +0 -33
- package/dist/sdk/agent-core/harness/session/jsonl-storage.d.ts.map +0 -1
- package/dist/sdk/agent-core/harness/session/jsonl-storage.js +0 -230
- package/dist/sdk/agent-core/harness/session/memory-repo.d.ts +0 -18
- package/dist/sdk/agent-core/harness/session/memory-repo.d.ts.map +0 -1
- package/dist/sdk/agent-core/harness/session/memory-repo.js +0 -41
- package/dist/sdk/agent-core/harness/session/memory-storage.d.ts +0 -25
- package/dist/sdk/agent-core/harness/session/memory-storage.d.ts.map +0 -1
- package/dist/sdk/agent-core/harness/session/memory-storage.js +0 -113
- package/dist/sdk/agent-core/harness/session/repo-utils.d.ts +0 -11
- package/dist/sdk/agent-core/harness/session/repo-utils.d.ts.map +0 -1
- package/dist/sdk/agent-core/harness/session/repo-utils.js +0 -38
- package/dist/sdk/agent-core/harness/skills.d.ts +0 -44
- package/dist/sdk/agent-core/harness/skills.d.ts.map +0 -1
- package/dist/sdk/agent-core/harness/skills.js +0 -310
- package/dist/sdk/agent-core/harness/utils/shell-output.d.ts +0 -14
- package/dist/sdk/agent-core/harness/utils/shell-output.d.ts.map +0 -1
- package/dist/sdk/agent-core/harness/utils/shell-output.js +0 -125
- package/dist/sdk/agent-core/node.d.ts +0 -3
- package/dist/sdk/agent-core/node.d.ts.map +0 -1
- package/dist/sdk/agent-core/node.js +0 -2
- package/dist/sdk/agent-core/proxy.d.ts +0 -69
- package/dist/sdk/agent-core/proxy.d.ts.map +0 -1
- package/dist/sdk/agent-core/proxy.js +0 -297
|
@@ -0,0 +1,754 @@
|
|
|
1
|
+
// GENERATED by cli-node/scripts/vendor-parser.mjs — do not edit; regenerate with `node scripts/vendor-parser.mjs`; source: packages/parser/
|
|
2
|
+
/**
|
|
3
|
+
* Aexol Language Parser
|
|
4
|
+
*
|
|
5
|
+
* Builds an Abstract Syntax Tree (AST) from a token stream.
|
|
6
|
+
* Supports the full Aexol language grammar for capability definitions,
|
|
7
|
+
* workflows, and agent structures.
|
|
8
|
+
*/
|
|
9
|
+
import { Tokenizer } from "./tokenizer.js";
|
|
10
|
+
import { TokenType } from "./lexer.js";
|
|
11
|
+
export class Parser {
|
|
12
|
+
tokenizer;
|
|
13
|
+
currentToken;
|
|
14
|
+
constructor(source) {
|
|
15
|
+
this.tokenizer = new Tokenizer(source);
|
|
16
|
+
this.currentToken = this.tokenizer.current();
|
|
17
|
+
}
|
|
18
|
+
advance() {
|
|
19
|
+
const token = this.currentToken;
|
|
20
|
+
this.tokenizer.advance();
|
|
21
|
+
this.currentToken = this.tokenizer.current();
|
|
22
|
+
return token;
|
|
23
|
+
}
|
|
24
|
+
check(type) {
|
|
25
|
+
return this.currentToken.type === type;
|
|
26
|
+
}
|
|
27
|
+
checkAny(...types) {
|
|
28
|
+
return types.includes(this.currentToken.type);
|
|
29
|
+
}
|
|
30
|
+
consume(type, message) {
|
|
31
|
+
if (!this.check(type)) {
|
|
32
|
+
throw new SyntaxError(message ||
|
|
33
|
+
`Expected ${type} but got ${this.currentToken.type} at line ${this.currentToken.location.line}:${this.currentToken.location.column}`);
|
|
34
|
+
}
|
|
35
|
+
return this.advance();
|
|
36
|
+
}
|
|
37
|
+
match(...types) {
|
|
38
|
+
if (this.checkAny(...types)) {
|
|
39
|
+
return this.advance();
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
// Keywords that can be used as identifiers in certain contexts (like field names, enum values)
|
|
44
|
+
consumeIdentifierOrKeyword(message) {
|
|
45
|
+
// Allow any token that has a string value - this includes IDENTIFIER and most keywords
|
|
46
|
+
if (this.currentToken.value && typeof this.currentToken.value === "string") {
|
|
47
|
+
return this.advance();
|
|
48
|
+
}
|
|
49
|
+
throw new SyntaxError(message ||
|
|
50
|
+
`Expected identifier but got ${this.currentToken.type} at line ${this.currentToken.location.line}:${this.currentToken.location.column}`);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Check if the current token is a keyword being used as an identifier in a
|
|
54
|
+
* capability context. Many common English words (import, from, context, etc.)
|
|
55
|
+
* are reserved keywords in the lexer but should be accepted as-is when they
|
|
56
|
+
* appear as unquoted capability names (e.g. "can import catalog").
|
|
57
|
+
*/
|
|
58
|
+
isKeywordUsedAsIdentifier() {
|
|
59
|
+
// Structural / top-level keywords that should NOT be treated as identifiers
|
|
60
|
+
const STRUCTURAL_KEYWORDS = new Set([
|
|
61
|
+
TokenType.VISITOR,
|
|
62
|
+
TokenType.TYPE,
|
|
63
|
+
TokenType.ENUM,
|
|
64
|
+
TokenType.INTERFACE,
|
|
65
|
+
TokenType.AGENT,
|
|
66
|
+
TokenType.WORKFLOW,
|
|
67
|
+
TokenType.ROLE,
|
|
68
|
+
TokenType.PERMISSION,
|
|
69
|
+
TokenType.STATE,
|
|
70
|
+
TokenType.EXECUTION_PLAN,
|
|
71
|
+
TokenType.EXECUTION_GRAPH,
|
|
72
|
+
// Structural punctuation / control flow that signal block end
|
|
73
|
+
// are not TokenType keywords, so no need to list them here.
|
|
74
|
+
]);
|
|
75
|
+
const t = this.currentToken.type;
|
|
76
|
+
// Must be a keyword-type token (not IDENTIFIER/STRING/NUMBER/punctuation)
|
|
77
|
+
// and not a structural top-level keyword.
|
|
78
|
+
return (t !== TokenType.IDENTIFIER &&
|
|
79
|
+
t !== TokenType.STRING &&
|
|
80
|
+
t !== TokenType.NUMBER &&
|
|
81
|
+
t !== TokenType.EOF &&
|
|
82
|
+
t !== TokenType.LBRACE &&
|
|
83
|
+
t !== TokenType.RBRACE &&
|
|
84
|
+
t !== TokenType.NEWLINE &&
|
|
85
|
+
t !== TokenType.INDENT &&
|
|
86
|
+
t !== TokenType.DEDENT &&
|
|
87
|
+
t !== TokenType.COMMA &&
|
|
88
|
+
!STRUCTURAL_KEYWORDS.has(t) &&
|
|
89
|
+
// Ensure the token has an actual word value
|
|
90
|
+
this.currentToken.value.length > 0);
|
|
91
|
+
}
|
|
92
|
+
skipNewlines() {
|
|
93
|
+
while (this.checkAny(TokenType.NEWLINE, TokenType.INDENT, TokenType.DEDENT, TokenType.COMMENT)) {
|
|
94
|
+
this.advance();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
isAtEnd() {
|
|
98
|
+
return this.check(TokenType.EOF);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Check for and consume a description (PROMPT token with triple single-quotes) if present
|
|
102
|
+
*/
|
|
103
|
+
parseDescriptionIfPresent() {
|
|
104
|
+
if (this.check(TokenType.PROMPT)) {
|
|
105
|
+
const description = this.advance().value;
|
|
106
|
+
this.skipNewlines();
|
|
107
|
+
return description;
|
|
108
|
+
}
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
111
|
+
throwUnexpectedTopLevelToken(description) {
|
|
112
|
+
const token = this.currentToken;
|
|
113
|
+
const tokenValue = token.value ? ` '${token.value}'` : "";
|
|
114
|
+
const descriptionHint = description ? " after a description block" : "";
|
|
115
|
+
throw new SyntaxError(`Unexpected top-level token ${token.type}${tokenValue}${descriptionHint} at line ${token.location.line}:${token.location.column}. ` +
|
|
116
|
+
"Expected one of: visitor, role, workflow, agent, type, enum, interface.");
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Parse the entire program
|
|
120
|
+
*/
|
|
121
|
+
parse() {
|
|
122
|
+
const body = [];
|
|
123
|
+
const startLocation = this.currentToken.location;
|
|
124
|
+
this.skipNewlines();
|
|
125
|
+
while (!this.isAtEnd()) {
|
|
126
|
+
// Check for description before any definition
|
|
127
|
+
const description = this.parseDescriptionIfPresent();
|
|
128
|
+
if (this.check(TokenType.VISITOR)) {
|
|
129
|
+
body.push(this.parseVisitor(description));
|
|
130
|
+
}
|
|
131
|
+
else if (this.check(TokenType.ROLE)) {
|
|
132
|
+
body.push(this.parseRole(description));
|
|
133
|
+
}
|
|
134
|
+
else if (this.check(TokenType.WORKFLOW)) {
|
|
135
|
+
body.push(this.parseWorkflow(description));
|
|
136
|
+
}
|
|
137
|
+
else if (this.check(TokenType.AGENT)) {
|
|
138
|
+
body.push(this.parseAgent(description));
|
|
139
|
+
}
|
|
140
|
+
else if (this.check(TokenType.TYPE) || this.check(TokenType.ENUM) ||
|
|
141
|
+
this.check(TokenType.INTERFACE)) {
|
|
142
|
+
body.push(this.parseTypeDefinition(description));
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
this.throwUnexpectedTopLevelToken(description);
|
|
146
|
+
}
|
|
147
|
+
this.skipNewlines();
|
|
148
|
+
}
|
|
149
|
+
return {
|
|
150
|
+
type: "Program",
|
|
151
|
+
body,
|
|
152
|
+
location: {
|
|
153
|
+
start: startLocation.offset,
|
|
154
|
+
end: this.currentToken.location.offset,
|
|
155
|
+
line: startLocation.line,
|
|
156
|
+
column: startLocation.column,
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
parseVisitor(description) {
|
|
161
|
+
const startToken = this.consume(TokenType.VISITOR);
|
|
162
|
+
let name;
|
|
163
|
+
if (this.check(TokenType.IDENTIFIER)) {
|
|
164
|
+
name = this.advance().value;
|
|
165
|
+
}
|
|
166
|
+
this.skipNewlines();
|
|
167
|
+
this.consume(TokenType.LBRACE, "Expected '{' after visitor");
|
|
168
|
+
this.skipNewlines();
|
|
169
|
+
const capabilities = [];
|
|
170
|
+
while (!this.check(TokenType.RBRACE) && !this.isAtEnd()) {
|
|
171
|
+
capabilities.push(this.parseCapability());
|
|
172
|
+
this.skipNewlines();
|
|
173
|
+
}
|
|
174
|
+
this.consume(TokenType.RBRACE, "Expected '}' after visitor body");
|
|
175
|
+
return {
|
|
176
|
+
type: "VisitorDefinition",
|
|
177
|
+
name,
|
|
178
|
+
description,
|
|
179
|
+
capabilities,
|
|
180
|
+
location: {
|
|
181
|
+
start: startToken.location.offset,
|
|
182
|
+
end: this.currentToken.location.offset,
|
|
183
|
+
line: startToken.location.line,
|
|
184
|
+
column: startToken.location.column,
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
parseCapability() {
|
|
189
|
+
// Check for description before capability
|
|
190
|
+
const description = this.parseDescriptionIfPresent();
|
|
191
|
+
const startToken = this.currentToken;
|
|
192
|
+
// Accept STRING token (quoted), IDENTIFIER token (unquoted), or any
|
|
193
|
+
// keyword token used as an identifier (e.g. "import", "from", "context").
|
|
194
|
+
// Keywords are context-sensitive: inside a capability list, any word-like
|
|
195
|
+
// token should be treated as a plain identifier.
|
|
196
|
+
let action;
|
|
197
|
+
if (this.check(TokenType.STRING)) {
|
|
198
|
+
action = this.advance().value;
|
|
199
|
+
}
|
|
200
|
+
else if (this.check(TokenType.IDENTIFIER) || this.isKeywordUsedAsIdentifier()) {
|
|
201
|
+
// Single identifier for unquoted capability
|
|
202
|
+
// For multi-word capabilities, use quotes: "browse products"
|
|
203
|
+
action = this.advance().value;
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
throw new SyntaxError(`Expected capability (string or identifier) at line ${this.currentToken.location.line}, got token type ${this.currentToken.type} with value '${this.currentToken.value}'`);
|
|
207
|
+
}
|
|
208
|
+
this.skipNewlines();
|
|
209
|
+
const nested = [];
|
|
210
|
+
const conditions = [];
|
|
211
|
+
// Check for nested capabilities
|
|
212
|
+
if (this.check(TokenType.LBRACE)) {
|
|
213
|
+
this.advance();
|
|
214
|
+
this.skipNewlines();
|
|
215
|
+
while (!this.check(TokenType.RBRACE) && !this.isAtEnd()) {
|
|
216
|
+
const loopStartPos = this.currentToken.location.offset;
|
|
217
|
+
nested.push(this.parseCapability());
|
|
218
|
+
// Allow optional comma separator
|
|
219
|
+
if (this.check(TokenType.COMMA)) {
|
|
220
|
+
this.advance();
|
|
221
|
+
}
|
|
222
|
+
this.skipNewlines();
|
|
223
|
+
// Safety: ensure outer loop is advancing
|
|
224
|
+
if (this.currentToken.location.offset === loopStartPos &&
|
|
225
|
+
!this.check(TokenType.RBRACE)) {
|
|
226
|
+
throw new SyntaxError(`Parser stuck at line ${this.currentToken.location.line}: unexpected token '${this.currentToken.value}' in nested capabilities`);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
this.consume(TokenType.RBRACE, "Expected '}' after nested capabilities");
|
|
230
|
+
this.skipNewlines();
|
|
231
|
+
}
|
|
232
|
+
// Allow optional comma after capability
|
|
233
|
+
if (this.check(TokenType.COMMA)) {
|
|
234
|
+
this.advance();
|
|
235
|
+
}
|
|
236
|
+
// Check for inline conditions
|
|
237
|
+
if (this.check(TokenType.IF) || this.check(TokenType.WHEN)) {
|
|
238
|
+
this.advance();
|
|
239
|
+
conditions.push(this.parseCondition());
|
|
240
|
+
}
|
|
241
|
+
return {
|
|
242
|
+
type: "Capability",
|
|
243
|
+
verb: undefined,
|
|
244
|
+
action,
|
|
245
|
+
description,
|
|
246
|
+
nested: nested.length > 0 ? nested : undefined,
|
|
247
|
+
conditions: conditions.length > 0 ? conditions : undefined,
|
|
248
|
+
location: {
|
|
249
|
+
start: startToken.location.offset,
|
|
250
|
+
end: this.currentToken.location.offset,
|
|
251
|
+
line: startToken.location.line,
|
|
252
|
+
column: startToken.location.column,
|
|
253
|
+
},
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
parseCondition() {
|
|
257
|
+
const startToken = this.currentToken;
|
|
258
|
+
const expression = this.parseExpression();
|
|
259
|
+
return {
|
|
260
|
+
type: "Condition",
|
|
261
|
+
expression,
|
|
262
|
+
location: {
|
|
263
|
+
start: startToken.location.offset,
|
|
264
|
+
end: this.currentToken.location.offset,
|
|
265
|
+
line: startToken.location.line,
|
|
266
|
+
column: startToken.location.column,
|
|
267
|
+
},
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
parseExpression() {
|
|
271
|
+
return this.parseOrExpression();
|
|
272
|
+
}
|
|
273
|
+
parseOrExpression() {
|
|
274
|
+
const startToken = this.currentToken;
|
|
275
|
+
let left = this.parseAndExpression();
|
|
276
|
+
while (this.check(TokenType.OR)) {
|
|
277
|
+
this.advance();
|
|
278
|
+
const right = this.parseAndExpression();
|
|
279
|
+
left = {
|
|
280
|
+
type: "Expression",
|
|
281
|
+
operator: "or",
|
|
282
|
+
left,
|
|
283
|
+
right,
|
|
284
|
+
location: {
|
|
285
|
+
start: startToken.location.offset,
|
|
286
|
+
end: this.currentToken.location.offset,
|
|
287
|
+
line: startToken.location.line,
|
|
288
|
+
column: startToken.location.column,
|
|
289
|
+
},
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
return left;
|
|
293
|
+
}
|
|
294
|
+
parseAndExpression() {
|
|
295
|
+
const startToken = this.currentToken;
|
|
296
|
+
let left = this.parseComparisonExpression();
|
|
297
|
+
while (this.check(TokenType.AND)) {
|
|
298
|
+
this.advance();
|
|
299
|
+
const right = this.parseComparisonExpression();
|
|
300
|
+
left = {
|
|
301
|
+
type: "Expression",
|
|
302
|
+
operator: "and",
|
|
303
|
+
left,
|
|
304
|
+
right,
|
|
305
|
+
location: {
|
|
306
|
+
start: startToken.location.offset,
|
|
307
|
+
end: this.currentToken.location.offset,
|
|
308
|
+
line: startToken.location.line,
|
|
309
|
+
column: startToken.location.column,
|
|
310
|
+
},
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
return left;
|
|
314
|
+
}
|
|
315
|
+
parseComparisonExpression() {
|
|
316
|
+
const startToken = this.currentToken;
|
|
317
|
+
let left = this.parsePrimaryExpression();
|
|
318
|
+
while (this.checkAny(TokenType.EQ, TokenType.NEQ, TokenType.LT, TokenType.LTE, TokenType.GT, TokenType.GTE)) {
|
|
319
|
+
const op = this.advance().value;
|
|
320
|
+
const right = this.parsePrimaryExpression();
|
|
321
|
+
left = {
|
|
322
|
+
type: "Expression",
|
|
323
|
+
operator: op,
|
|
324
|
+
left,
|
|
325
|
+
right,
|
|
326
|
+
location: {
|
|
327
|
+
start: startToken.location.offset,
|
|
328
|
+
end: this.currentToken.location.offset,
|
|
329
|
+
line: startToken.location.line,
|
|
330
|
+
column: startToken.location.column,
|
|
331
|
+
},
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
return left;
|
|
335
|
+
}
|
|
336
|
+
parsePrimaryExpression() {
|
|
337
|
+
const startToken = this.currentToken;
|
|
338
|
+
if (this.check(TokenType.TRUE)) {
|
|
339
|
+
this.advance();
|
|
340
|
+
return {
|
|
341
|
+
type: "Expression",
|
|
342
|
+
value: true,
|
|
343
|
+
location: {
|
|
344
|
+
start: startToken.location.offset,
|
|
345
|
+
end: this.currentToken.location.offset,
|
|
346
|
+
line: startToken.location.line,
|
|
347
|
+
column: startToken.location.column,
|
|
348
|
+
},
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
if (this.check(TokenType.FALSE)) {
|
|
352
|
+
this.advance();
|
|
353
|
+
return {
|
|
354
|
+
type: "Expression",
|
|
355
|
+
value: false,
|
|
356
|
+
location: {
|
|
357
|
+
start: startToken.location.offset,
|
|
358
|
+
end: this.currentToken.location.offset,
|
|
359
|
+
line: startToken.location.line,
|
|
360
|
+
column: startToken.location.column,
|
|
361
|
+
},
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
if (this.check(TokenType.NUMBER)) {
|
|
365
|
+
const value = Number(this.advance().value);
|
|
366
|
+
return {
|
|
367
|
+
type: "Expression",
|
|
368
|
+
value,
|
|
369
|
+
location: {
|
|
370
|
+
start: startToken.location.offset,
|
|
371
|
+
end: this.currentToken.location.offset,
|
|
372
|
+
line: startToken.location.line,
|
|
373
|
+
column: startToken.location.column,
|
|
374
|
+
},
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
if (this.check(TokenType.STRING)) {
|
|
378
|
+
const value = this.advance().value;
|
|
379
|
+
return {
|
|
380
|
+
type: "Expression",
|
|
381
|
+
value,
|
|
382
|
+
location: {
|
|
383
|
+
start: startToken.location.offset,
|
|
384
|
+
end: this.currentToken.location.offset,
|
|
385
|
+
line: startToken.location.line,
|
|
386
|
+
column: startToken.location.column,
|
|
387
|
+
},
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
if (this.check(TokenType.IDENTIFIER)) {
|
|
391
|
+
const value = this.advance().value;
|
|
392
|
+
return {
|
|
393
|
+
type: "Expression",
|
|
394
|
+
value,
|
|
395
|
+
location: {
|
|
396
|
+
start: startToken.location.offset,
|
|
397
|
+
end: this.currentToken.location.offset,
|
|
398
|
+
line: startToken.location.line,
|
|
399
|
+
column: startToken.location.column,
|
|
400
|
+
},
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
if (this.check(TokenType.LPAREN)) {
|
|
404
|
+
this.advance();
|
|
405
|
+
const expr = this.parseExpression();
|
|
406
|
+
this.consume(TokenType.RPAREN, "Expected ')' after expression");
|
|
407
|
+
return expr;
|
|
408
|
+
}
|
|
409
|
+
throw new SyntaxError(`Unexpected token in expression: ${this.currentToken.type}`);
|
|
410
|
+
}
|
|
411
|
+
parseRole(description) {
|
|
412
|
+
const startToken = this.consume(TokenType.ROLE);
|
|
413
|
+
const name = this.consume(TokenType.IDENTIFIER, "Expected role name").value;
|
|
414
|
+
this.skipNewlines();
|
|
415
|
+
this.consume(TokenType.LBRACE, "Expected '{' after role name");
|
|
416
|
+
this.skipNewlines();
|
|
417
|
+
const permissions = [];
|
|
418
|
+
const inherits = [];
|
|
419
|
+
while (!this.check(TokenType.RBRACE) && !this.isAtEnd()) {
|
|
420
|
+
const loopStart = this.currentToken.location.offset;
|
|
421
|
+
// Check for description before permission
|
|
422
|
+
const permissionDescription = this.parseDescriptionIfPresent();
|
|
423
|
+
if (this.check(TokenType.PERMISSION)) {
|
|
424
|
+
this.advance();
|
|
425
|
+
const resource = this.consume(TokenType.IDENTIFIER, "Expected resource name").value;
|
|
426
|
+
const actions = [];
|
|
427
|
+
if (this.check(TokenType.LBRACE)) {
|
|
428
|
+
this.advance();
|
|
429
|
+
this.skipNewlines();
|
|
430
|
+
while (!this.check(TokenType.RBRACE) && !this.isAtEnd()) {
|
|
431
|
+
actions.push(this.consume(TokenType.IDENTIFIER, "Expected action name").value);
|
|
432
|
+
if (this.check(TokenType.COMMA)) {
|
|
433
|
+
this.advance();
|
|
434
|
+
this.skipNewlines();
|
|
435
|
+
}
|
|
436
|
+
this.skipNewlines();
|
|
437
|
+
}
|
|
438
|
+
this.consume(TokenType.RBRACE, "Expected '}' after actions");
|
|
439
|
+
}
|
|
440
|
+
permissions.push({
|
|
441
|
+
type: "Permission",
|
|
442
|
+
resource,
|
|
443
|
+
description: permissionDescription,
|
|
444
|
+
actions,
|
|
445
|
+
location: {
|
|
446
|
+
start: startToken.location.offset,
|
|
447
|
+
end: this.currentToken.location.offset,
|
|
448
|
+
line: startToken.location.line,
|
|
449
|
+
column: startToken.location.column,
|
|
450
|
+
},
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
else {
|
|
454
|
+
// If we hit an unexpected token, throw an error instead of hanging
|
|
455
|
+
throw new SyntaxError(`Unexpected token '${this.currentToken.value}' (type: ${this.currentToken.type}) at line ${this.currentToken.location.line}. ` +
|
|
456
|
+
`Expected 'permission' keyword or '}' to close role definition. ` +
|
|
457
|
+
`Note: Role definitions use 'permission resource { actions }' syntax, not field syntax with colons.`);
|
|
458
|
+
}
|
|
459
|
+
this.skipNewlines();
|
|
460
|
+
// Safety: ensure we're advancing
|
|
461
|
+
if (this.currentToken.location.offset === loopStart &&
|
|
462
|
+
!this.check(TokenType.RBRACE)) {
|
|
463
|
+
throw new SyntaxError(`Parser stuck in role definition at line ${this.currentToken.location.line}`);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
this.consume(TokenType.RBRACE, "Expected '}' after role body");
|
|
467
|
+
return {
|
|
468
|
+
type: "RoleDefinition",
|
|
469
|
+
name,
|
|
470
|
+
description,
|
|
471
|
+
permissions,
|
|
472
|
+
inherits: inherits.length > 0 ? inherits : undefined,
|
|
473
|
+
location: {
|
|
474
|
+
start: startToken.location.offset,
|
|
475
|
+
end: this.currentToken.location.offset,
|
|
476
|
+
line: startToken.location.line,
|
|
477
|
+
column: startToken.location.column,
|
|
478
|
+
},
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
parseWorkflow(description) {
|
|
482
|
+
const startToken = this.consume(TokenType.WORKFLOW);
|
|
483
|
+
const name = this.consume(TokenType.IDENTIFIER, "Expected workflow name").value;
|
|
484
|
+
this.skipNewlines();
|
|
485
|
+
this.consume(TokenType.LBRACE, "Expected '{' after workflow name");
|
|
486
|
+
this.skipNewlines();
|
|
487
|
+
const steps = [];
|
|
488
|
+
let initialState = "";
|
|
489
|
+
const finalStates = [];
|
|
490
|
+
while (!this.check(TokenType.RBRACE) && !this.isAtEnd()) {
|
|
491
|
+
// Check for description before state
|
|
492
|
+
const stepDescription = this.parseDescriptionIfPresent();
|
|
493
|
+
// Handle 'initial <stateName>' to set the initial state
|
|
494
|
+
if (this.check(TokenType.INITIAL)) {
|
|
495
|
+
this.advance();
|
|
496
|
+
initialState = this.consume(TokenType.IDENTIFIER, "Expected state name after 'initial'").value;
|
|
497
|
+
this.skipNewlines();
|
|
498
|
+
continue;
|
|
499
|
+
}
|
|
500
|
+
if (this.check(TokenType.STATE)) {
|
|
501
|
+
this.advance();
|
|
502
|
+
const stateName = this.consume(TokenType.IDENTIFIER, "Expected state name").value;
|
|
503
|
+
if (this.match(TokenType.ARROW)) {
|
|
504
|
+
const transitions = [];
|
|
505
|
+
while (!this.check(TokenType.NEWLINE) && !this.check(TokenType.RBRACE) &&
|
|
506
|
+
!this.isAtEnd()) {
|
|
507
|
+
const toState = this.consume(TokenType.IDENTIFIER, "Expected target state").value;
|
|
508
|
+
transitions.push({
|
|
509
|
+
type: "Transition",
|
|
510
|
+
from: stateName,
|
|
511
|
+
to: toState,
|
|
512
|
+
location: {
|
|
513
|
+
start: startToken.location.offset,
|
|
514
|
+
end: this.currentToken.location.offset,
|
|
515
|
+
line: startToken.location.line,
|
|
516
|
+
column: startToken.location.column,
|
|
517
|
+
},
|
|
518
|
+
});
|
|
519
|
+
if (this.match(TokenType.COMMA)) {
|
|
520
|
+
// Continue parsing transitions
|
|
521
|
+
}
|
|
522
|
+
else {
|
|
523
|
+
break;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
steps.push({
|
|
527
|
+
type: "WorkflowStep",
|
|
528
|
+
name: stateName,
|
|
529
|
+
description: stepDescription,
|
|
530
|
+
action: "transition",
|
|
531
|
+
transitions,
|
|
532
|
+
location: {
|
|
533
|
+
start: startToken.location.offset,
|
|
534
|
+
end: this.currentToken.location.offset,
|
|
535
|
+
line: startToken.location.line,
|
|
536
|
+
column: startToken.location.column,
|
|
537
|
+
},
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
this.skipNewlines();
|
|
541
|
+
continue;
|
|
542
|
+
}
|
|
543
|
+
// If we reach here, we have an unexpected token in the workflow body
|
|
544
|
+
throw new SyntaxError(`Unexpected token '${this.currentToken.type}' in workflow definition at line ${this.currentToken.location.line}:${this.currentToken.location.column}. ` +
|
|
545
|
+
`Workflows should contain 'initial <state>' or 'state <name> -> <transitions>' declarations. ` +
|
|
546
|
+
`Note: Workflows use state machine syntax, not method declarations.`);
|
|
547
|
+
}
|
|
548
|
+
this.consume(TokenType.RBRACE, "Expected '}' after workflow body");
|
|
549
|
+
return {
|
|
550
|
+
type: "WorkflowDefinition",
|
|
551
|
+
name,
|
|
552
|
+
description,
|
|
553
|
+
steps,
|
|
554
|
+
initialState,
|
|
555
|
+
finalStates,
|
|
556
|
+
location: {
|
|
557
|
+
start: startToken.location.offset,
|
|
558
|
+
end: this.currentToken.location.offset,
|
|
559
|
+
line: startToken.location.line,
|
|
560
|
+
column: startToken.location.column,
|
|
561
|
+
},
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
parseAgent(description) {
|
|
565
|
+
const startToken = this.consume(TokenType.AGENT);
|
|
566
|
+
const name = this.consume(TokenType.IDENTIFIER, "Expected agent name").value;
|
|
567
|
+
let role = "";
|
|
568
|
+
if (this.match(TokenType.COLON)) {
|
|
569
|
+
role = this.consume(TokenType.IDENTIFIER, "Expected role name").value;
|
|
570
|
+
}
|
|
571
|
+
this.skipNewlines();
|
|
572
|
+
this.consume(TokenType.LBRACE, "Expected '{' after agent name");
|
|
573
|
+
this.skipNewlines();
|
|
574
|
+
const capabilities = [];
|
|
575
|
+
const responsibilities = [];
|
|
576
|
+
let skills;
|
|
577
|
+
let model;
|
|
578
|
+
let maxConcurrent;
|
|
579
|
+
while (!this.check(TokenType.RBRACE) && !this.isAtEnd()) {
|
|
580
|
+
if (this.check(TokenType.CAPABILITIES)) {
|
|
581
|
+
this.advance();
|
|
582
|
+
this.consume(TokenType.COLON, "Expected ':' after capabilities");
|
|
583
|
+
const capList = this.parseStringArray();
|
|
584
|
+
// Convert to capability nodes
|
|
585
|
+
for (const cap of capList) {
|
|
586
|
+
capabilities.push({
|
|
587
|
+
type: "Capability",
|
|
588
|
+
verb: "can",
|
|
589
|
+
action: cap,
|
|
590
|
+
location: {
|
|
591
|
+
start: startToken.location.offset,
|
|
592
|
+
end: this.currentToken.location.offset,
|
|
593
|
+
line: startToken.location.line,
|
|
594
|
+
column: startToken.location.column,
|
|
595
|
+
},
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
else if (this.check(TokenType.SKILLS)) {
|
|
600
|
+
this.advance();
|
|
601
|
+
this.consume(TokenType.COLON, "Expected ':' after skills");
|
|
602
|
+
skills = this.parseStringArray();
|
|
603
|
+
}
|
|
604
|
+
else if (this.check(TokenType.MODEL)) {
|
|
605
|
+
this.advance();
|
|
606
|
+
this.consume(TokenType.COLON, "Expected ':' after model");
|
|
607
|
+
model = this.consume(TokenType.STRING, "Expected string value").value;
|
|
608
|
+
}
|
|
609
|
+
else if (this.check(TokenType.MAX_CONCURRENT)) {
|
|
610
|
+
this.advance();
|
|
611
|
+
this.consume(TokenType.COLON, "Expected ':' after max_concurrent");
|
|
612
|
+
maxConcurrent = parseInt(this.consume(TokenType.NUMBER, "Expected number value").value);
|
|
613
|
+
}
|
|
614
|
+
else if (this.check(TokenType.STRING) || this.check(TokenType.IDENTIFIER) ||
|
|
615
|
+
this.check(TokenType.PROMPT)) {
|
|
616
|
+
// Parse the full capability with nested capabilities support (including descriptions)
|
|
617
|
+
capabilities.push(this.parseCapability());
|
|
618
|
+
}
|
|
619
|
+
else {
|
|
620
|
+
this.advance();
|
|
621
|
+
}
|
|
622
|
+
this.skipNewlines();
|
|
623
|
+
}
|
|
624
|
+
this.consume(TokenType.RBRACE, "Expected '}' after agent body");
|
|
625
|
+
// Determine agent kind based on properties
|
|
626
|
+
// Builder agents have skills, model, or maxConcurrent properties
|
|
627
|
+
// Domain agents have business logic capabilities and may have roles
|
|
628
|
+
const agentKind = (skills !== undefined || model !== undefined ||
|
|
629
|
+
maxConcurrent !== undefined)
|
|
630
|
+
? "builder"
|
|
631
|
+
: "domain";
|
|
632
|
+
return {
|
|
633
|
+
type: "AgentDefinition",
|
|
634
|
+
name,
|
|
635
|
+
description,
|
|
636
|
+
agentKind,
|
|
637
|
+
role,
|
|
638
|
+
responsibilities,
|
|
639
|
+
capabilities,
|
|
640
|
+
skills,
|
|
641
|
+
model,
|
|
642
|
+
maxConcurrent,
|
|
643
|
+
location: {
|
|
644
|
+
start: startToken.location.offset,
|
|
645
|
+
end: this.currentToken.location.offset,
|
|
646
|
+
line: startToken.location.line,
|
|
647
|
+
column: startToken.location.column,
|
|
648
|
+
},
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
parseTypeDefinition(description) {
|
|
652
|
+
const startToken = this.currentToken;
|
|
653
|
+
let kind = "type";
|
|
654
|
+
if (this.check(TokenType.TYPE)) {
|
|
655
|
+
kind = "type";
|
|
656
|
+
this.advance();
|
|
657
|
+
}
|
|
658
|
+
else if (this.check(TokenType.ENUM)) {
|
|
659
|
+
kind = "enum";
|
|
660
|
+
this.advance();
|
|
661
|
+
}
|
|
662
|
+
else if (this.check(TokenType.INTERFACE)) {
|
|
663
|
+
kind = "interface";
|
|
664
|
+
this.advance();
|
|
665
|
+
}
|
|
666
|
+
this.skipNewlines();
|
|
667
|
+
const name = this.consumeIdentifierOrKeyword(`Expected type name after '${kind}' keyword at line ${this.currentToken.location.line}:${this.currentToken.location.column}. Got '${this.currentToken.value || this.currentToken.type}' instead.`).value;
|
|
668
|
+
this.skipNewlines();
|
|
669
|
+
this.consume(TokenType.LBRACE, "Expected '{' after type name");
|
|
670
|
+
this.skipNewlines();
|
|
671
|
+
const fields = [];
|
|
672
|
+
const values = [];
|
|
673
|
+
while (!this.check(TokenType.RBRACE) && !this.isAtEnd()) {
|
|
674
|
+
if (kind === "enum") {
|
|
675
|
+
// Enum values remain plain strings (no descriptions)
|
|
676
|
+
values.push(this.consumeIdentifierOrKeyword("Expected enum value").value);
|
|
677
|
+
}
|
|
678
|
+
else {
|
|
679
|
+
// Check for description before field
|
|
680
|
+
const fieldDescription = this.parseDescriptionIfPresent();
|
|
681
|
+
const fieldName = this.consumeIdentifierOrKeyword("Expected field name").value;
|
|
682
|
+
this.consume(TokenType.COLON, "Expected ':' after field name");
|
|
683
|
+
// Parse field type with optional array syntax
|
|
684
|
+
let fieldType = this.consumeIdentifierOrKeyword("Expected field type").value;
|
|
685
|
+
// Handle array syntax: string[], TypeName[], TypeName[][]
|
|
686
|
+
while (this.check(TokenType.LBRACKET)) {
|
|
687
|
+
this.advance(); // consume '['
|
|
688
|
+
this.consume(TokenType.RBRACKET, "Expected ']' after '['");
|
|
689
|
+
fieldType += "[]";
|
|
690
|
+
}
|
|
691
|
+
fields.push({
|
|
692
|
+
type: "TypeField",
|
|
693
|
+
name: fieldName,
|
|
694
|
+
description: fieldDescription,
|
|
695
|
+
fieldType,
|
|
696
|
+
required: true,
|
|
697
|
+
location: {
|
|
698
|
+
start: startToken.location.offset,
|
|
699
|
+
end: this.currentToken.location.offset,
|
|
700
|
+
line: startToken.location.line,
|
|
701
|
+
column: startToken.location.column,
|
|
702
|
+
},
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
if (this.check(TokenType.COMMA)) {
|
|
706
|
+
this.advance();
|
|
707
|
+
}
|
|
708
|
+
this.skipNewlines();
|
|
709
|
+
}
|
|
710
|
+
this.consume(TokenType.RBRACE, "Expected '}' after type body");
|
|
711
|
+
return {
|
|
712
|
+
type: "TypeDefinition",
|
|
713
|
+
name,
|
|
714
|
+
description,
|
|
715
|
+
kind,
|
|
716
|
+
fields: fields.length > 0 ? fields : undefined,
|
|
717
|
+
values: values.length > 0 ? values : undefined,
|
|
718
|
+
location: {
|
|
719
|
+
start: startToken.location.offset,
|
|
720
|
+
end: this.currentToken.location.offset,
|
|
721
|
+
line: startToken.location.line,
|
|
722
|
+
column: startToken.location.column,
|
|
723
|
+
},
|
|
724
|
+
};
|
|
725
|
+
}
|
|
726
|
+
/**
|
|
727
|
+
* Parse string array: [item1, item2, item3]
|
|
728
|
+
*/
|
|
729
|
+
parseStringArray() {
|
|
730
|
+
const items = [];
|
|
731
|
+
this.consume(TokenType.LBRACKET, "Expected '['");
|
|
732
|
+
this.skipNewlines();
|
|
733
|
+
while (!this.check(TokenType.RBRACKET) && !this.isAtEnd()) {
|
|
734
|
+
if (this.check(TokenType.IDENTIFIER)) {
|
|
735
|
+
items.push(this.advance().value);
|
|
736
|
+
}
|
|
737
|
+
else if (this.check(TokenType.STRING)) {
|
|
738
|
+
items.push(this.advance().value);
|
|
739
|
+
}
|
|
740
|
+
else {
|
|
741
|
+
throw new SyntaxError(`Expected identifier or string but got ${this.currentToken.type} at line ${this.currentToken.location.line}:${this.currentToken.location.column}`);
|
|
742
|
+
}
|
|
743
|
+
if (this.check(TokenType.COMMA)) {
|
|
744
|
+
this.advance();
|
|
745
|
+
this.skipNewlines();
|
|
746
|
+
}
|
|
747
|
+
else {
|
|
748
|
+
break;
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
this.consume(TokenType.RBRACKET, "Expected ']'");
|
|
752
|
+
return items;
|
|
753
|
+
}
|
|
754
|
+
}
|