@jondotsoy/don 0.0.2

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/README.md ADDED
@@ -0,0 +1,416 @@
1
+ # DON: Directive Object Notation
2
+
3
+ DON es un formato de serialización de datos legible por humanos, diseñado alrededor del concepto de directivas y subdirectivas. Combina la simplicidad de una sintaxis declarativa con la flexibilidad de estructuras anidadas, permitiendo definir configuraciones jerárquicas usando bloques de directivas intuitivos. Cada directiva puede aceptar argumentos y contener subdirectivas anidadas, haciéndolo ideal para archivos de configuración, definiciones de infraestructura y representación de datos estructurados donde la legibilidad y expresividad son prioridades.
4
+
5
+ ## Características Principales
6
+
7
+ - **Sintaxis Minimalista**: Menos caracteres especiales, más legibilidad
8
+ - **Procesamiento Secuencial**: Las directivas se procesan como un pipeline, permitiendo sobrescritura y composición incremental
9
+ - **Anidación Flexible**: Soporta jerarquías de cualquier profundidad
10
+ - **Múltiples Tipos de Datos**: Keywords, strings, numbers, booleans y null
11
+ - **Comentarios**: Soporta comentarios de línea (`//`, `#`) y multilínea (`/* */`)
12
+ - **Sin Ambigüedades**: Reglas claras de parsing y delimitación
13
+
14
+ ## Instalación
15
+
16
+ ```bash
17
+ npm install @jondotsoy/don
18
+ ```
19
+
20
+ ```bash
21
+ bun add @jondotsoy/don
22
+ ```
23
+
24
+ ## Uso Básico
25
+
26
+ ```typescript
27
+ import { DocumentEncoder, LexerEncoder } from "@jondotsoy/don";
28
+
29
+ // Parsear un documento DON
30
+ const source = `
31
+ name "my-app"
32
+ version 1.0.0
33
+ container {
34
+ image "nginx"
35
+ port 8080
36
+ }
37
+ `;
38
+
39
+ const lexer = new LexerEncoder();
40
+ const tokens = lexer.encode(source);
41
+
42
+ const document = new DocumentEncoder();
43
+ const directives = document.encode(tokens);
44
+
45
+ console.log(directives);
46
+ ```
47
+
48
+ ## Sintaxis Básica
49
+
50
+ ### Estructura de una Directiva
51
+
52
+ Una directiva tiene la siguiente estructura:
53
+
54
+ ```
55
+ <nombre> [argumentos...] [{ subdirectivas }]
56
+ ```
57
+
58
+ **Componentes:**
59
+
60
+ - **Nombre**: Identificador de la directiva (keyword, string, number o boolean)
61
+ - **Argumentos**: Cero o más valores separados por espacios
62
+ - **Bloque**: Opcional, contiene subdirectivas entre llaves `{ }`
63
+
64
+ ### Ejemplos
65
+
66
+ **Directivas simples:**
67
+
68
+ ```don
69
+ name "my-app"
70
+ version 1.0.0
71
+ enabled true
72
+ port 8080
73
+ ```
74
+
75
+ **Directivas con bloques:**
76
+
77
+ ```don
78
+ container {
79
+ image "nginx"
80
+ port 8080
81
+ }
82
+ ```
83
+
84
+ **Directivas con argumentos y bloques:**
85
+
86
+ ```don
87
+ service "web" replicas 3 {
88
+ container "nginx" {
89
+ port 80
90
+ image "nginx:latest"
91
+ }
92
+ }
93
+ ```
94
+
95
+ **Directivas repetidas:**
96
+
97
+ ```don
98
+ container {
99
+ image "nginx"
100
+ port 80
101
+ }
102
+
103
+ container {
104
+ image "redis"
105
+ port 6379
106
+ }
107
+ ```
108
+
109
+ ## Tipos de Datos
110
+
111
+ DON reconoce los siguientes tipos de tokens:
112
+
113
+ - **Keywords**: Identificadores que comienzan con letra o guion bajo (`name`, `version`, `container`)
114
+ - **Strings**: Cadenas de texto entre comillas dobles o simples (`"texto"`, `'texto'`)
115
+ - Soportan caracteres escapados: `\"`, `\'`, `\\`
116
+ - **Numbers**: Enteros o decimales (`123`, `45.67`)
117
+ - Pueden incluir guiones bajos como separadores: `1_000_000`
118
+ - **Booleans**: Valores `true` o `false`
119
+ - **Null**: Valor nulo `null`
120
+ - **Punctuators**: Símbolos delimitadores: `{`, `}`, `[`, `]`, `,`, `+`, `-`, `/`, `*`, `<`, `>`
121
+ - **Comments**:
122
+ - Línea: `// comentario` o `# comentario`
123
+ - Multilínea: `/* comentario */`
124
+
125
+ ## Reglas de Sintaxis
126
+
127
+ 1. **Nombre de directiva**: Puede ser un keyword, string, number o boolean
128
+ 2. **Argumentos**: Cero o más tokens separados por espacios
129
+ 3. **Bloque de subdirectivas**: Opcional, delimitado por llaves `{ }`
130
+ 4. **Delimitación**: Las directivas se separan por saltos de línea
131
+ 5. **Anidación**: Las subdirectivas siguen las mismas reglas
132
+ 6. **Restricción**: No puede haber tokens después de un bloque en la misma línea
133
+
134
+ **Sintaxis válida:**
135
+
136
+ ```don
137
+ // Directiva simple
138
+ name "my-app"
139
+ version 1.0.0
140
+
141
+ // Directiva con bloque
142
+ container {
143
+ image "nginx"
144
+ port 8080
145
+ }
146
+
147
+ // Comentarios
148
+ # Comentario de línea
149
+ /* Comentario multilínea */
150
+ ```
151
+
152
+ **Sintaxis inválida:**
153
+
154
+ ```don
155
+ // ❌ Tokens después del bloque
156
+ container { image "nginx" } extra
157
+
158
+ // ❌ String sin cerrar
159
+ name "unclosed
160
+ ```
161
+
162
+ ## Casos de Uso
163
+
164
+ ### Archivos de Configuración
165
+
166
+ ```don
167
+ app "my-service" {
168
+ version 2.1.0
169
+ environment "production"
170
+
171
+ database {
172
+ host "localhost"
173
+ port 5432
174
+ name "mydb"
175
+ }
176
+
177
+ cache {
178
+ enabled true
179
+ ttl 3600
180
+ }
181
+ }
182
+ ```
183
+
184
+ ### Definiciones de Infraestructura (Kubernetes)
185
+
186
+ ```yaml
187
+ apiVersion: v1
188
+ kind: Pod
189
+ metadata:
190
+ name: command-demo
191
+ labels:
192
+ purpose: demonstrate-command
193
+ spec:
194
+ containers:
195
+ - name: command-demo-container
196
+ image: debian
197
+ command: ["printenv"]
198
+ args: ["HOSTNAME", "KUBERNETES_PORT"]
199
+ restartPolicy: OnFailure
200
+ ```
201
+
202
+ ```don
203
+ apiVersion v1 {
204
+ pod {
205
+ metadata {
206
+ name "command-demo"
207
+ labels {
208
+ purpose "demonstrate-command"
209
+ }
210
+ }
211
+ spec {
212
+ restartPolicy "OnFailure"
213
+ container debian "command-demo-container" {
214
+ command printenv HOSTNAME KUBERNETES_PORT
215
+ }
216
+ }
217
+ }
218
+ }
219
+ ```
220
+
221
+ ### Schemas de Datos
222
+
223
+ ```don
224
+ schema "user" {
225
+ id integer primaryKey autoIncrement
226
+ email string unique required
227
+ name string required
228
+ createdAt timestamp default now
229
+ }
230
+ ```
231
+
232
+ ### Reglas de Seguridad (Firebase)
233
+
234
+ ```don
235
+ service "firebase.storage" {
236
+ match "/b/{bucket}/o" {
237
+ match "/someFolder/{fileName}" {
238
+ allow read write {
239
+ if "request.auth" "!=" null
240
+ }
241
+ }
242
+ }
243
+ }
244
+ ```
245
+
246
+ ### Servicios con Múltiples Contenedores
247
+
248
+ ```don
249
+ service "my-service" {
250
+ replicas 2
251
+ container {
252
+ image "my-image"
253
+ port 8080
254
+ }
255
+ container {
256
+ image "my-other-image"
257
+ port 8081
258
+ }
259
+ }
260
+ ```
261
+
262
+ ## Equivalencias con JSON
263
+
264
+ DON ofrece una sintaxis más limpia y legible para representar estructuras de datos comunes en JSON:
265
+
266
+ **Propiedades simples:**
267
+
268
+ - `{"name":"foo"}` → `name "foo"`
269
+ - `{"version":1}` → `version 1`
270
+ - `{"private": true}` → `private true`
271
+
272
+ **Objetos anidados:**
273
+
274
+ ```json
275
+ { "name": "foo", "meta": { "format": "json", "version": 1 } }
276
+ ```
277
+
278
+ ```don
279
+ name "foo"
280
+ meta {
281
+ format "json"
282
+ version 1
283
+ }
284
+ ```
285
+
286
+ **Arrays o Listas:**
287
+
288
+ ```json
289
+ { "icons": ["foo", "taz"] }
290
+ ```
291
+
292
+ ```don
293
+ icons {
294
+ icon "foo"
295
+ icon "taz"
296
+ }
297
+ ```
298
+
299
+ ## Equivalencias con YAML
300
+
301
+ DON puede representar estructuras YAML de manera más concisa. Los tags de YAML (como `!!date`, `!!str`, `!!int`) se convierten en nombres de directivas en DON:
302
+
303
+ **Tags de tipo:**
304
+
305
+ ```yaml
306
+ ---
307
+ createdAt: !!date 2002-04-28
308
+ count: !!int 42
309
+ description: !!str "Hello World"
310
+ enabled: !!bool true
311
+ ```
312
+
313
+ ```don
314
+ createdAt date "2002-04-28"
315
+ count int 42
316
+ description str "Hello World"
317
+ enabled bool true
318
+ ```
319
+
320
+ **Objetos anidados con tags:**
321
+
322
+ ```yaml
323
+ ---
324
+ metadata:
325
+ createdAt: !!date 2002-04-28
326
+ version: !!int 1
327
+ ```
328
+
329
+ ```don
330
+ metadata {
331
+ createdAt date "2002-04-28"
332
+ version int 1
333
+ }
334
+ ```
335
+
336
+ ## Inspiración
337
+
338
+ DON toma inspiración de la sintaxis de Python para el paso de argumentos, permitiendo tanto argumentos posicionales como argumentos con nombre:
339
+
340
+ ```python
341
+ # Python - Argumentos posicionales
342
+ print("foo")
343
+
344
+ # Python - Argumentos con nombre
345
+ dict(name="foo")
346
+
347
+ # Python - Combinación
348
+ Container("nginx", replicas=3, port=80)
349
+ ```
350
+
351
+ ```don
352
+ # DON - Argumentos posicionales
353
+ print "foo"
354
+
355
+ # DON - Argumentos con nombre (mediante subdirectivas)
356
+ dict {
357
+ name "foo"
358
+ }
359
+
360
+ # DON - Combinación
361
+ Container "nginx" {
362
+ replicas 3
363
+ port 80
364
+ }
365
+ ```
366
+
367
+ ## Desarrollo
368
+
369
+ ### Scripts Disponibles
370
+
371
+ ```bash
372
+ # Compilar el proyecto
373
+ npm run build
374
+
375
+ # Limpiar archivos compilados
376
+ npm run clean
377
+
378
+ # Formatear código
379
+ npm run fmt
380
+
381
+ # Verificar formato
382
+ npm run lint
383
+ ```
384
+
385
+ ### Estructura del Proyecto
386
+
387
+ ```
388
+ .
389
+ ├── src/ # Código fuente TypeScript
390
+ │ ├── lexer.ts # Analizador léxico
391
+ │ ├── document.ts # Parser de documentos
392
+ │ ├── chunk.ts # Manejo de chunks
393
+ │ └── utils/ # Utilidades
394
+ ├── lib/ # Código compilado
395
+ │ └── esm/ # Módulos ES
396
+ ├── docs/ # Documentación
397
+ └── index.ts # Punto de entrada
398
+ ```
399
+
400
+ ## Documentación
401
+
402
+ Para más información sobre DON, consulta la [documentación completa](./docs/index.md):
403
+
404
+ - [Formato .DON](./docs/don-file.md): Especificación completa del formato
405
+ - [Directivas](./docs/concepts/directive.md): Anatomía y uso de directivas
406
+ - [Heredocs](./docs/concepts/heredoc.md): Contenido multilínea
407
+ - [Tipos de Datos](./docs/concepts/types/): Especificación de tipos soportados
408
+ - [Inspiración](./docs/inspiration.md): Objetivos y visión del proyecto
409
+
410
+ ## Licencia
411
+
412
+ MIT
413
+
414
+ ## Autor
415
+
416
+ [@jondotsoy](https://github.com/jondotsoy)
@@ -0,0 +1,28 @@
1
+ export declare class Chunk {
2
+ readonly pos: number;
3
+ readonly length: number;
4
+ readonly value: Iterable<number>;
5
+ readonly debug_str: string;
6
+ constructor(pos: number, length: number, value: Iterable<number>, debug_str?: string);
7
+ }
8
+ export declare class ChunkList {
9
+ readonly length: number;
10
+ readonly chunks: Iterable<Chunk>;
11
+ constructor(length: number, chunks: Iterable<Chunk>);
12
+ }
13
+ export declare class Expression {
14
+ readonly m: number[];
15
+ constructor(m: number[]);
16
+ test(buff: number[], fromIndex: number): boolean;
17
+ exec(buff: number[], fromIndex: number): number[];
18
+ static keywordExpression: Expression;
19
+ static spaceExpression: Expression;
20
+ }
21
+ type LexerEncoderOptions = {};
22
+ export declare class ChunkEncoder {
23
+ private options?;
24
+ constructor(options?: LexerEncoderOptions | undefined);
25
+ encode(buff: string | Iterable<number>): ChunkList;
26
+ }
27
+ export {};
28
+ //# sourceMappingURL=chunk.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunk.d.ts","sourceRoot":"","sources":["../../src/chunk.ts"],"names":[],"mappings":"AAAA,qBAAa,KAAK;IAEd,QAAQ,CAAC,GAAG,EAAE,MAAM;IACpB,QAAQ,CAAC,MAAM,EAAE,MAAM;IACvB,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC;IAChC,QAAQ,CAAC,SAAS;gBAHT,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,EACvB,SAAS,SAEjB;CAEJ;AAED,qBAAa,SAAS;IAElB,QAAQ,CAAC,MAAM,EAAE,MAAM;IACvB,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,KAAK,CAAC;gBADvB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,QAAQ,CAAC,KAAK,CAAC;CAEnC;AAKD,qBAAa,UAAU;IACT,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE;gBAAX,CAAC,EAAE,MAAM,EAAE;IAEhC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO;IAMhD,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE;IAUjD,MAAM,CAAC,iBAAiB,aASrB;IAEH,MAAM,CAAC,eAAe,aAAwB;CAC/C;AAED,KAAK,mBAAmB,GAAG,EAAE,CAAC;AAE9B,qBAAa,YAAY;IACX,OAAO,CAAC,OAAO,CAAC;gBAAR,OAAO,CAAC,EAAE,mBAAmB,YAAA;IAEjD,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,GAAG,SAAS;CAiCnD"}
@@ -0,0 +1,84 @@
1
+ export class Chunk {
2
+ pos;
3
+ length;
4
+ value;
5
+ debug_str;
6
+ constructor(pos, length, value, debug_str = JSON.stringify(new TextDecoder().decode(new Uint8Array(value)))) {
7
+ this.pos = pos;
8
+ this.length = length;
9
+ this.value = value;
10
+ this.debug_str = debug_str;
11
+ }
12
+ }
13
+ export class ChunkList {
14
+ length;
15
+ chunks;
16
+ constructor(length, chunks) {
17
+ this.length = length;
18
+ this.chunks = chunks;
19
+ }
20
+ }
21
+ const range = (start, end) => Array.from({ length: -start + end + 1 }, (_, i) => i + start);
22
+ export class Expression {
23
+ m;
24
+ constructor(m) {
25
+ this.m = m;
26
+ }
27
+ test(buff, fromIndex) {
28
+ const charCode = buff[fromIndex];
29
+ if (charCode === undefined)
30
+ return false;
31
+ return this.m.includes(charCode);
32
+ }
33
+ exec(buff, fromIndex) {
34
+ let end = fromIndex;
35
+ while (end < buff.length && this.test(buff, end)) {
36
+ end++;
37
+ }
38
+ return buff.slice(fromIndex, end);
39
+ }
40
+ static keywordExpression = new Expression([
41
+ // a-z (97 ... 122)
42
+ ...range(97, 122),
43
+ // A-Z (65 ... 90)
44
+ ...range(65, 90),
45
+ // _ (95)
46
+ 95,
47
+ // 0-9 (48 ... 57)
48
+ ...range(48, 57),
49
+ ]);
50
+ static spaceExpression = new Expression([32]);
51
+ }
52
+ export class ChunkEncoder {
53
+ options;
54
+ constructor(options) {
55
+ this.options = options;
56
+ }
57
+ encode(buff) {
58
+ const numberIterable = typeof buff === "string" ? new TextEncoder().encode(buff) : buff;
59
+ const bytes = Array.from(numberIterable);
60
+ const tokens = [];
61
+ let pos = 0;
62
+ const expressions = [
63
+ Expression.keywordExpression,
64
+ Expression.spaceExpression,
65
+ ];
66
+ while (pos < bytes.length) {
67
+ const charCode = bytes[pos];
68
+ if (charCode === undefined)
69
+ break;
70
+ const expression = expressions.find((expression) => expression.test(bytes, pos));
71
+ if (expression) {
72
+ const value = expression.exec(bytes, pos);
73
+ tokens.push(new Chunk(pos, value.length, value));
74
+ pos += value.length;
75
+ continue;
76
+ }
77
+ // Symbols
78
+ tokens.push(new Chunk(pos, 1, [charCode]));
79
+ pos++;
80
+ }
81
+ return new ChunkList(tokens.length, tokens);
82
+ }
83
+ }
84
+ //# sourceMappingURL=chunk.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunk.js","sourceRoot":"","sources":["../../src/chunk.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import { ChunkList } from "./chunk";
2
+ import { Lexema, Token } from "./lexer";
3
+ export declare class Directive {
4
+ readonly name: Token;
5
+ readonly args: Token[];
6
+ readonly children: Directive[];
7
+ constructor(name: Token, args: Token[], children: Directive[]);
8
+ static isDirective(doc: unknown): doc is Directive;
9
+ }
10
+ export declare class Document {
11
+ readonly children: Directive[];
12
+ constructor(children: Directive[]);
13
+ static isDocument(doc: unknown): doc is Document;
14
+ }
15
+ export declare class DocumentEncoder {
16
+ encode(input: string | Iterable<number> | ChunkList | Lexema): Document;
17
+ private segmentByBrackets;
18
+ private parseDirectives;
19
+ }
20
+ //# sourceMappingURL=document.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document.d.ts","sourceRoot":"","sources":["../../src/document.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAgB,MAAM,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAEtD,qBAAa,SAAS;IAElB,QAAQ,CAAC,IAAI,EAAE,KAAK;IACpB,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE;IACtB,QAAQ,CAAC,QAAQ,EAAE,SAAS,EAAE;gBAFrB,IAAI,EAAE,KAAK,EACX,IAAI,EAAE,KAAK,EAAE,EACb,QAAQ,EAAE,SAAS,EAAE;IAGhC,MAAM,CAAC,WAAW,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,SAAS;CAGnD;AAED,qBAAa,QAAQ;IACP,QAAQ,CAAC,QAAQ,EAAE,SAAS,EAAE;gBAArB,QAAQ,EAAE,SAAS,EAAE;IAE1C,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,QAAQ;CAGjD;AAUD,qBAAa,eAAe;IAC1B,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ;IAavE,OAAO,CAAC,iBAAiB;IAsCzB,OAAO,CAAC,eAAe;CAiGxB"}
@@ -0,0 +1,138 @@
1
+ import { LexerEncoder, Lexema } from "./lexer";
2
+ export class Directive {
3
+ name;
4
+ args;
5
+ children;
6
+ constructor(name, args, children) {
7
+ this.name = name;
8
+ this.args = args;
9
+ this.children = children;
10
+ }
11
+ static isDirective(doc) {
12
+ return doc instanceof Directive;
13
+ }
14
+ }
15
+ export class Document {
16
+ children;
17
+ constructor(children) {
18
+ this.children = children;
19
+ }
20
+ static isDocument(doc) {
21
+ return doc instanceof Document;
22
+ }
23
+ }
24
+ export class DocumentEncoder {
25
+ encode(input) {
26
+ const lexema = input instanceof Lexema ? input : new LexerEncoder().encode(input);
27
+ const tokens = Array.from(lexema.tokens);
28
+ // Segmentar por grupos de brackets
29
+ const groups = this.segmentByBrackets(tokens);
30
+ // Parsear las directivas
31
+ const directives = this.parseDirectives(tokens, 0, tokens.length, groups);
32
+ return new Document(directives);
33
+ }
34
+ segmentByBrackets(tokens) {
35
+ const groups = [];
36
+ const stack = [];
37
+ for (let i = 0; i < tokens.length; i++) {
38
+ const token = tokens[i];
39
+ if (!token || token.type !== "punctuator")
40
+ continue;
41
+ const value = token.raw;
42
+ if (value === "{") {
43
+ stack.push({ type: "braces", openIndex: i });
44
+ }
45
+ else if (value === "}") {
46
+ const open = stack.pop();
47
+ if (open && open.type === "braces") {
48
+ groups.push({
49
+ openIndex: open.openIndex,
50
+ closeIndex: i,
51
+ type: "braces",
52
+ });
53
+ }
54
+ }
55
+ else if (value === "[") {
56
+ stack.push({ type: "brackets", openIndex: i });
57
+ }
58
+ else if (value === "]") {
59
+ const open = stack.pop();
60
+ if (open && open.type === "brackets") {
61
+ groups.push({
62
+ openIndex: open.openIndex,
63
+ closeIndex: i,
64
+ type: "brackets",
65
+ });
66
+ }
67
+ }
68
+ }
69
+ return groups;
70
+ }
71
+ parseDirectives(tokens, startIndex, endIndex, groups) {
72
+ const directives = [];
73
+ let i = startIndex;
74
+ while (i < endIndex) {
75
+ const token = tokens[i];
76
+ if (!token)
77
+ break;
78
+ // Skip newlines
79
+ if (token.type === "newline") {
80
+ i++;
81
+ continue;
82
+ }
83
+ // Skip closing brackets (ya fueron procesados)
84
+ if (token.type === "punctuator" &&
85
+ (token.raw === "}" || token.raw === "]")) {
86
+ break;
87
+ }
88
+ // Parse directive: only keyword, string, number, or boolean can be directive names
89
+ if (token.type === "keyword" ||
90
+ token.type === "string" ||
91
+ token.type === "number" ||
92
+ token.type === "boolean") {
93
+ const name = token;
94
+ const args = [];
95
+ i++;
96
+ // Collect arguments until newline, block start, or end
97
+ while (i < endIndex) {
98
+ const argToken = tokens[i];
99
+ if (!argToken ||
100
+ argToken.type === "newline" ||
101
+ (argToken.type === "punctuator" && argToken.raw === "{") ||
102
+ (argToken.type === "punctuator" && argToken.raw === "}")) {
103
+ break;
104
+ }
105
+ args.push(argToken);
106
+ i++;
107
+ // If we just added a heredoc, stop collecting arguments
108
+ // because heredoc already includes its own newline
109
+ if (argToken.type === "heredoc") {
110
+ break;
111
+ }
112
+ }
113
+ // Check for block - buscar el grupo que empieza en la posición actual
114
+ let children = [];
115
+ const group = groups.find((g) => g.openIndex === i && g.type === "braces");
116
+ if (group) {
117
+ // Parsear recursivamente el contenido del grupo
118
+ children = this.parseDirectives(tokens, group.openIndex + 1, group.closeIndex, groups);
119
+ i = group.closeIndex + 1; // Saltar al final del grupo
120
+ // After a block, there should be no more tokens before newline
121
+ if (i < endIndex) {
122
+ const nextToken = tokens[i];
123
+ if (nextToken && nextToken.type !== "newline") {
124
+ throw new Error(`Syntax error: Unexpected token after block. Expected newline but got ${nextToken.type} "${nextToken.raw}"`);
125
+ }
126
+ }
127
+ }
128
+ directives.push(new Directive(name, args, children));
129
+ }
130
+ else {
131
+ // Invalid directive name - throw syntax error
132
+ throw new Error(`Syntax error: Invalid directive name. Expected keyword, string, number, or boolean, but got ${token.type} "${token.raw}"`);
133
+ }
134
+ }
135
+ return directives;
136
+ }
137
+ }
138
+ //# sourceMappingURL=document.js.map