parser-lr 0.7.0 → 0.8.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.
Files changed (54) hide show
  1. package/bin/parser-lr.js +404 -874
  2. package/bin/parser-lr.js.map +1 -1
  3. package/dist/lib/diagnostics/format-diagnostic.d.ts +31 -0
  4. package/dist/lib/diagnostics/format-diagnostic.d.ts.map +1 -0
  5. package/dist/lib/diagnostics/format-diagnostic.js +36 -0
  6. package/dist/lib/diagnostics/format-diagnostic.js.map +1 -0
  7. package/dist/lib/diagnostics/index.d.ts +5 -0
  8. package/dist/lib/diagnostics/index.d.ts.map +1 -0
  9. package/dist/lib/diagnostics/index.js +3 -0
  10. package/dist/lib/diagnostics/index.js.map +1 -0
  11. package/dist/lib/diagnostics/source-position.d.ts +24 -0
  12. package/dist/lib/diagnostics/source-position.d.ts.map +1 -0
  13. package/dist/lib/diagnostics/source-position.js +39 -0
  14. package/dist/lib/diagnostics/source-position.js.map +1 -0
  15. package/dist/lib/grammar/ast-type.d.ts +2 -0
  16. package/dist/lib/grammar/ast-type.d.ts.map +1 -1
  17. package/dist/lib/grammar/grammar-from-cst.js +8 -4
  18. package/dist/lib/grammar/grammar-from-cst.js.map +1 -1
  19. package/dist/lib/grammar/grammar.json +86 -820
  20. package/dist/lib/grammar/index.d.ts +1 -1
  21. package/dist/lib/grammar/index.d.ts.map +1 -1
  22. package/dist/lib/grammar/production.d.ts +2 -0
  23. package/dist/lib/grammar/production.d.ts.map +1 -1
  24. package/dist/lib/grammar/table-validator.d.ts +12 -2
  25. package/dist/lib/grammar/table-validator.d.ts.map +1 -1
  26. package/dist/lib/grammar/table-validator.js +68 -25
  27. package/dist/lib/grammar/table-validator.js.map +1 -1
  28. package/dist/lib/grammar/transform-rule.d.ts +3 -0
  29. package/dist/lib/grammar/transform-rule.d.ts.map +1 -1
  30. package/dist/lib/grammar-entry.d.ts +1 -1
  31. package/dist/lib/grammar-entry.d.ts.map +1 -1
  32. package/dist/lib/grammar-entry.js.map +1 -1
  33. package/dist/lib/index.d.ts +3 -0
  34. package/dist/lib/index.d.ts.map +1 -1
  35. package/dist/lib/index.js +1 -0
  36. package/dist/lib/index.js.map +1 -1
  37. package/dist/lib/parse-context.d.ts +7 -0
  38. package/dist/lib/parse-context.d.ts.map +1 -1
  39. package/dist/lib/parse-context.js +9 -0
  40. package/dist/lib/parse-context.js.map +1 -1
  41. package/dist/lib/parse-table/table/lr-parse-table.d.ts.map +1 -1
  42. package/dist/lib/parse-table/table/lr-parse-table.js +10 -3
  43. package/dist/lib/parse-table/table/lr-parse-table.js.map +1 -1
  44. package/dist/lib/parser-lr.d.ts +15 -0
  45. package/dist/lib/parser-lr.d.ts.map +1 -1
  46. package/dist/lib/parser-lr.js +38 -9
  47. package/dist/lib/parser-lr.js.map +1 -1
  48. package/docs/The Ferrite Programming Language.md +568 -0
  49. package/docs/grammar.md +55 -3
  50. package/grammars/6502.grammar +2 -2
  51. package/grammars/ferrite.grammar +107 -97
  52. package/grammars/grammar.grammar +19 -140
  53. package/grammars/lisp.grammar +3 -2
  54. package/package.json +1 -1
package/docs/grammar.md CHANGED
@@ -116,6 +116,19 @@ ast
116
116
  #binary [left]:expr [operator]:operator [right]:expr
117
117
  | #literal number
118
118
  ;
119
+
120
+ list =
121
+ #list { item }
122
+ ;
123
+ ```
124
+
125
+ Every `type.#variant` referenced from `transform` must appear as a `#variant` label on that AST type, including types with only one alternative:
126
+
127
+ ```ebnf
128
+ ast
129
+ program =
130
+ #program { form }
131
+ ;
119
132
  ```
120
133
 
121
134
  When present, transform rules map parse trees to these types.
@@ -161,9 +174,48 @@ transform
161
174
  |-----------|----------|
162
175
  | `pass(slot)` | Lift one bound child unchanged. `pass(boundSlot)` preserves the bound production symbol and its `#` variant. `pass(terminalName)` at the same rule may collapse to that terminal. |
163
176
  | `build(type.#variant, …)` | Construct an AST node with a fixed shape. Omit absent optional bindings from the argument list. |
164
- | `flatten(type.#variant, head, tail)` | Turn a `{ repeat }` list into one AST node with ordered children. Use the repeat non-terminal (for example `list$repeat_0`) as `tail` on the head production. |
177
+ | `flatten(type.#variant, head, tail)` | Turn a `head { }` list into one AST node with ordered children. Prefer putting `flatten` on the **parent** production that owns the required head and the synthetic repeat tail. |
178
+
179
+ Preferred parent-flatten pattern for a required head plus optional rest:
180
+
181
+ ```ebnf
182
+ grammar
183
+ list =
184
+ #list [first]:item { comma [rest]:item }
185
+ ;
186
+
187
+ ast
188
+ list =
189
+ #list { item }
190
+ ;
191
+
192
+ transform
193
+ list ->
194
+ #list flatten(list.#list, first, list$repeat_0) ;
195
+ ```
196
+
197
+ Use a `$repeat_*` helper transform when the list is zero-or-more and nested among other fields, or when the parent must `build(...)` a wrapper around the flattened list. The transform language cannot nest `flatten(...)` inside `type.#variant(...)` arguments:
198
+
199
+ ```ebnf
200
+ transform
201
+ program ->
202
+ #main pass(program$repeat_0)
203
+ ;
204
+
205
+ program$repeat_0 ->
206
+ #main flatten(program.#program, item, program$repeat_0)
207
+ ;
208
+
209
+ wrapped_list ->
210
+ #main wrapped_list.#wrapped(open, wrapped_list$repeat_0, close)
211
+ ;
212
+
213
+ wrapped_list$repeat_0 ->
214
+ #main flatten(wrapped_list.#items, member, wrapped_list$repeat_0)
215
+ ;
216
+ ```
165
217
 
166
- **Production symbols are preserved.** When a CST node has parse-table metadata, transforms keep its production name even if it has only one terminal child. Bare `CLS` through `pass(stmt)` where `[stmt]:cls_stmt` yields an `cls_stmt` node, not `kw_cls`.
218
+ **Production symbols are preserved.** When a CST node has parse-table metadata, transforms keep its production name even if it has only one terminal child. Bare `CLS` through `pass(stmt)` where `[stmt]:cls_stmt` yields a `cls_stmt` node, not `kw_cls`.
167
219
 
168
220
  ### Transform contract
169
221
 
@@ -195,7 +247,7 @@ Before / after for `CASE IS < 0` with `case_selector = #relational kw_is compari
195
247
  Run `parser-lr table validate -g mylang.grammar` to check `ast` / `transform` consistency. The validator:
196
248
 
197
249
  - warns when `pass(boundSlot)` targets a production that can match a single terminal and has no transform rule;
198
- - errors when a `build` / `fold` / `flatten` references a `type.#variant` not declared in `ast`;
250
+ - errors when a `build` / `fold` / `flatten` references a `type.#variant` not declared in `ast`, including single-alternative labeled types;
199
251
  - warns when a production, `ast` type, or `transform` rule is declared more than once (the later definition silently overrides the earlier one).
200
252
 
201
253
  Add `--strict` to fail on warnings in CI.
@@ -112,7 +112,7 @@ grammar
112
112
 
113
113
  ast
114
114
  program =
115
- { line }
115
+ #program { line }
116
116
  ;
117
117
 
118
118
  line =
@@ -122,7 +122,7 @@ ast
122
122
  ;
123
123
 
124
124
  directive =
125
- [dotTok]:dot
125
+ #directive [dotTok]:dot
126
126
  [sym]:identifier
127
127
  [value]:hex_number
128
128
  ;
@@ -1,24 +1,28 @@
1
- // Ferrite — a C-like language with C#-style structs, namespaces, and pointer semantics.
2
- // No preprocessor; sources compile as single units (namespaces + using, like C# assemblies).
1
+ // Ferrite — a C-like language with C#-style structs, file-scoped namespaces, and pointer semantics.
2
+ // No preprocessor; sources compile as single units (namespaces + using).
3
+ // Naming convention (not enforced by the grammar): PascalCase types, camelCase functions and variables.
3
4
  //
4
5
  // Example:
5
6
  //
6
- // namespace Ferrite.Math {
7
- // struct Vec2 {
8
- // float32 x;
9
- // float32 y;
10
- // float32 dot(Vec2* other) {
11
- // return x * other->x + y * other->y;
12
- // }
13
- // }
14
- // }
7
+ // namespace Ferrite.Math;
15
8
  //
16
- // using Ferrite.Math;
9
+ // using Ferrite.Core;
17
10
  //
18
- // int32 main() {
11
+ // public struct Vec2
12
+ // {
13
+ // Float32 x;
14
+ // Float32 y;
15
+ // Float32 dot(Vec2* other)
16
+ // {
17
+ // return x * other->x + y * other->y;
18
+ // }
19
+ // };
20
+ //
21
+ // public Int32 main()
22
+ // {
19
23
  // Vec2 a = { 1.0, 2.0 };
20
24
  // Vec2* p = &a;
21
- // return (int32)p->dot(&a);
25
+ // return (Int32)p->dot(&a);
22
26
  // }
23
27
 
24
28
  name "ferrite" ;
@@ -27,6 +31,8 @@ tokens
27
31
  kw_struct = /struct/ ;
28
32
  kw_namespace = /namespace/ ;
29
33
  kw_using = /using/ ;
34
+ kw_public = /public/ ;
35
+ kw_private = /private/ ;
30
36
  kw_if = /if/ ;
31
37
  kw_else = /else/ ;
32
38
  kw_while = /while/ ;
@@ -38,19 +44,19 @@ tokens
38
44
  kw_null = /null/ ;
39
45
  kw_true = /true/ ;
40
46
  kw_false = /false/ ;
41
- kw_void = /void/ ;
42
- kw_bool = /bool/ ;
43
- kw_int8 = /int8/ ;
44
- kw_int16 = /int16/ ;
45
- kw_int32 = /int32/ ;
46
- kw_int64 = /int64/ ;
47
- kw_uint8 = /uint8/ ;
48
- kw_uint16 = /uint16/ ;
49
- kw_uint32 = /uint32/ ;
50
- kw_uint64 = /uint64/ ;
51
- kw_float32 = /float32/ ;
52
- kw_float64 = /float64/ ;
53
- kw_char = /char/ ;
47
+ kw_void = /Void/ ;
48
+ kw_bool = /Bool/ ;
49
+ kw_int8 = /Int8/ ;
50
+ kw_int16 = /Int16/ ;
51
+ kw_int32 = /Int32/ ;
52
+ kw_int64 = /Int64/ ;
53
+ kw_uint8 = /UInt8/ ;
54
+ kw_uint16 = /UInt16/ ;
55
+ kw_uint32 = /UInt32/ ;
56
+ kw_uint64 = /UInt64/ ;
57
+ kw_float32 = /Float32/ ;
58
+ kw_float64 = /Float64/ ;
59
+ kw_char = /Char/ ;
54
60
 
55
61
  identifier = /[A-Za-z_][A-Za-z0-9_]*/ ;
56
62
  int_literal = /0x[0-9A-F]+|0b[01]+|[0-9]+/i ;
@@ -112,28 +118,27 @@ start program ;
112
118
 
113
119
  grammar
114
120
  program =
121
+ [ [ns]:file_namespace ]
122
+ { [use]:using_directive }
115
123
  { [decl]:top_decl }
116
124
  ;
117
125
 
118
- top_decl =
119
- #namespace [nsKw]:kw_namespace [qname]:qualified_name [body]:namespace_body
120
- | #using [useKw]:kw_using [qname]:qualified_name [semi]:semicolon
121
- | #struct struct_decl
122
- | #function function_decl
126
+ file_namespace =
127
+ [nsKw]:kw_namespace [qname]:qualified_name [semi]:semicolon
123
128
  ;
124
129
 
125
- namespace_body =
126
- [open]:lbrace
127
- { [member]:namespace_member }
128
- [close]:rbrace
130
+ using_directive =
131
+ [useKw]:kw_using [qname]:qualified_name [semi]:semicolon
129
132
  ;
130
133
 
131
- namespace_member =
134
+ top_decl =
132
135
  #struct struct_decl
133
136
  | #function function_decl
134
- | #namespace [nsKw]:kw_namespace [qname]:qualified_name [body]:namespace_body
135
- | #using [useKw]:kw_using [qname]:qualified_name [semi]:semicolon
136
- | #statement [stmt]:statement
137
+ ;
138
+
139
+ access_modifier =
140
+ #public [keyword]:kw_public
141
+ | #private [keyword]:kw_private
137
142
  ;
138
143
 
139
144
  qualified_name =
@@ -145,6 +150,7 @@ grammar
145
150
  ;
146
151
 
147
152
  struct_decl =
153
+ [access]:access_modifier
148
154
  [structKw]:kw_struct
149
155
  [sym]:identifier
150
156
  [body]:struct_body
@@ -186,12 +192,14 @@ grammar
186
192
 
187
193
  function_decl =
188
194
  #defined
195
+ [access]:access_modifier
189
196
  [type]:type_spec
190
197
  [sym]:identifier
191
198
  [open]:lpar
192
199
  [close]:rpar
193
200
  [body]:block
194
201
  | #definedParams
202
+ [access]:access_modifier
195
203
  [type]:type_spec
196
204
  [sym]:identifier
197
205
  [open]:lpar
@@ -199,12 +207,14 @@ grammar
199
207
  [close]:rpar
200
208
  [body]:block
201
209
  | #prototype
210
+ [access]:access_modifier
202
211
  [type]:type_spec
203
212
  [sym]:identifier
204
213
  [open]:lpar
205
214
  [close]:rpar
206
215
  [semi]:semicolon
207
216
  | #prototypeParams
217
+ [access]:access_modifier
208
218
  [type]:type_spec
209
219
  [sym]:identifier
210
220
  [open]:lpar
@@ -273,7 +283,7 @@ grammar
273
283
  ;
274
284
 
275
285
  expr_sep =
276
- [commaTok]:comma [rest]:expression
286
+ #sep [commaTok]:comma [rest]:expression
277
287
  ;
278
288
 
279
289
  for_init =
@@ -425,39 +435,45 @@ grammar
425
435
 
426
436
  ast
427
437
  program =
428
- { top_decl }
438
+ #program [ [ns]:file_namespace ] [usings]:using_list [decls]:decl_list
429
439
  ;
430
440
 
431
- top_decl =
432
- #namespace [nsKw]:kw_namespace [qname]:qualified_name [body]:namespace_body
433
- | #using [useKw]:kw_using [qname]:qualified_name [semi]:semicolon
434
- | #struct struct_decl
435
- | #function function_decl
441
+ using_list =
442
+ #list { using_directive }
436
443
  ;
437
444
 
438
- namespace_body =
439
- [open]:lbrace
440
- { [member]:namespace_member }
441
- [close]:rbrace
445
+ decl_list =
446
+ #list { top_decl }
447
+ ;
448
+
449
+ file_namespace =
450
+ #file_namespace [nsKw]:kw_namespace [qname]:qualified_name [semi]:semicolon
451
+ ;
452
+
453
+ using_directive =
454
+ #using_directive [useKw]:kw_using [qname]:qualified_name [semi]:semicolon
442
455
  ;
443
456
 
444
- namespace_member =
457
+ top_decl =
445
458
  #struct struct_decl
446
459
  | #function function_decl
447
- | #namespace [nsKw]:kw_namespace [qname]:qualified_name [body]:namespace_body
448
- | #using [useKw]:kw_using [qname]:qualified_name [semi]:semicolon
449
- | #statement [stmt]:statement
460
+ ;
461
+
462
+ access_modifier =
463
+ #public [keyword]:kw_public
464
+ | #private [keyword]:kw_private
450
465
  ;
451
466
 
452
467
  qualified_name =
453
- [head]:identifier { name_part }
468
+ #qualified_name [head]:identifier { name_part }
454
469
  ;
455
470
 
456
471
  name_part =
457
- [dotTok]:dot [part]:identifier
472
+ #part [dotTok]:dot [part]:identifier
458
473
  ;
459
474
 
460
475
  struct_decl =
476
+ #struct_decl [access]:access_modifier
461
477
  [structKw]:kw_struct
462
478
  [sym]:identifier
463
479
  [body]:struct_body
@@ -465,7 +481,7 @@ ast
465
481
  ;
466
482
 
467
483
  struct_body =
468
- [open]:lbrace
484
+ #struct_body [open]:lbrace
469
485
  { [member]:struct_member }
470
486
  [close]:rbrace
471
487
  ;
@@ -476,7 +492,7 @@ ast
476
492
  ;
477
493
 
478
494
  field_decl =
479
- [type]:type_spec
495
+ #field_decl [type]:type_spec
480
496
  [sym]:identifier
481
497
  [semi]:semicolon
482
498
  ;
@@ -499,12 +515,14 @@ ast
499
515
 
500
516
  function_decl =
501
517
  #defined
518
+ [access]:access_modifier
502
519
  [type]:type_spec
503
520
  [sym]:identifier
504
521
  [open]:lpar
505
522
  [close]:rpar
506
523
  [body]:block
507
524
  | #definedParams
525
+ [access]:access_modifier
508
526
  [type]:type_spec
509
527
  [sym]:identifier
510
528
  [open]:lpar
@@ -512,12 +530,14 @@ ast
512
530
  [close]:rpar
513
531
  [body]:block
514
532
  | #prototype
533
+ [access]:access_modifier
515
534
  [type]:type_spec
516
535
  [sym]:identifier
517
536
  [open]:lpar
518
537
  [close]:rpar
519
538
  [semi]:semicolon
520
539
  | #prototypeParams
540
+ [access]:access_modifier
521
541
  [type]:type_spec
522
542
  [sym]:identifier
523
543
  [open]:lpar
@@ -531,16 +551,17 @@ ast
531
551
  ;
532
552
 
533
553
  param_sep =
534
- [commaTok]:comma [rest]:param
554
+ #sep [commaTok]:comma [rest]:param
535
555
  ;
536
556
 
537
557
  param =
538
- [type]:type_spec
558
+ #param [type]:type_spec
539
559
  [sym]:identifier
540
560
  ;
541
561
 
542
562
  type_spec =
543
- #voidType [voidKw]:kw_void { [ptr]:star }
563
+ #pointers { [ptr]:star }
564
+ | #voidType [voidKw]:kw_void { [ptr]:star }
544
565
  | #bool [boolKw]:kw_bool { [ptr]:star }
545
566
  | #int8 [int8Kw]:kw_int8 { [ptr]:star }
546
567
  | #int16 [int16Kw]:kw_int16 { [ptr]:star }
@@ -559,7 +580,7 @@ ast
559
580
  ;
560
581
 
561
582
  block =
562
- [open]:lbrace
583
+ #block [open]:lbrace
563
584
  { statement }
564
585
  [close]:rbrace
565
586
  ;
@@ -586,7 +607,7 @@ ast
586
607
  ;
587
608
 
588
609
  expr_sep =
589
- [commaTok]:comma [rest]:expression
610
+ #sep [commaTok]:comma [rest]:expression
590
611
  ;
591
612
 
592
613
  for_init =
@@ -738,41 +759,39 @@ ast
738
759
 
739
760
  transform
740
761
  program ->
741
- #main pass(program$repeat_0) ;
762
+ #main program.#program(ns, program$repeat_0, program$repeat_1) ;
742
763
 
743
764
  program$repeat_0 ->
744
- #main flatten(program.#program, decl, program$repeat_0) ;
765
+ #main flatten(using_list.#list, use, program$repeat_0) ;
745
766
 
746
- top_decl ->
747
- #namespace top_decl.#namespace(nsKw, qname, body)
748
- | #using top_decl.#using(useKw, qname, semi)
749
- | #struct pass(struct_decl)
750
- | #function pass(function_decl) ;
767
+ program$repeat_1 ->
768
+ #main flatten(decl_list.#list, decl, program$repeat_1) ;
751
769
 
752
- namespace_body ->
753
- #main namespace_body.#namespace_body(open, namespace_body$repeat_0, close) ;
770
+ file_namespace ->
771
+ #main file_namespace.#file_namespace(nsKw, qname, semi) ;
754
772
 
755
- namespace_body$repeat_0 ->
756
- #main flatten(namespace_body.#namespace_body, member, namespace_body$repeat_0) ;
773
+ using_directive ->
774
+ #main using_directive.#using_directive(useKw, qname, semi) ;
757
775
 
758
- namespace_member ->
776
+ top_decl ->
759
777
  #struct pass(struct_decl)
760
- | #function pass(function_decl)
761
- | #namespace namespace_member.#namespace(nsKw, qname, body)
762
- | #using namespace_member.#using(useKw, qname, semi)
763
- | #statement pass(stmt) ;
778
+ | #function pass(function_decl) ;
779
+
780
+ access_modifier ->
781
+ #public access_modifier.#public(keyword)
782
+ | #private access_modifier.#private(keyword) ;
764
783
 
765
784
  qualified_name ->
766
- #main qualified_name.#qualified_name(head, qualified_name$repeat_1) ;
785
+ #main qualified_name.#qualified_name(head, qualified_name$repeat_0) ;
767
786
 
768
- qualified_name$repeat_1 ->
769
- #main flatten(qualified_name.#qualified_name, name_part, qualified_name$repeat_1) ;
787
+ qualified_name$repeat_0 ->
788
+ #main flatten(qualified_name.#qualified_name, name_part, qualified_name$repeat_0) ;
770
789
 
771
790
  name_part ->
772
791
  #main name_part.#part(dotTok, part) ;
773
792
 
774
793
  struct_decl ->
775
- #main struct_decl.#struct_decl(structKw, sym, body, semi) ;
794
+ #main struct_decl.#struct_decl(access, structKw, sym, body, semi) ;
776
795
 
777
796
  struct_body ->
778
797
  #main struct_body.#struct_body(open, struct_body$repeat_0, close) ;
@@ -792,17 +811,14 @@ transform
792
811
  | #methodParams method_decl.#method(type, sym, open, params, close, body) ;
793
812
 
794
813
  function_decl ->
795
- #defined function_decl.#defined(type, sym, open, close, body)
796
- | #definedParams function_decl.#defined(type, sym, open, params, close, body)
797
- | #prototype function_decl.#prototype(type, sym, open, close, semi)
798
- | #prototypeParams function_decl.#prototype(type, sym, open, params, close, semi) ;
814
+ #defined function_decl.#defined(access, type, sym, open, close, body)
815
+ | #definedParams function_decl.#defined(access, type, sym, open, params, close, body)
816
+ | #prototype function_decl.#prototype(access, type, sym, open, close, semi)
817
+ | #prototypeParams function_decl.#prototype(access, type, sym, open, params, close, semi) ;
799
818
 
800
819
  param_list ->
801
820
  #main flatten(param_list.#list, first, param_list$repeat_0) ;
802
821
 
803
- param_list$repeat_0 ->
804
- #main flatten(param_list.#list, param_sep, param_list$repeat_0) ;
805
-
806
822
  param_sep ->
807
823
  #main param_sep.#sep(commaTok, rest) ;
808
824
 
@@ -854,9 +870,6 @@ transform
854
870
  init_list ->
855
871
  #main flatten(init_list.#list, first, init_list$repeat_0) ;
856
872
 
857
- init_list$repeat_0 ->
858
- #main flatten(init_list.#list, expr_sep, init_list$repeat_0) ;
859
-
860
873
  expr_sep ->
861
874
  #main expr_sep.#sep(commaTok, rest) ;
862
875
 
@@ -974,9 +987,6 @@ transform
974
987
  arg_list ->
975
988
  #main flatten(arg_list.#list, first, arg_list$repeat_0) ;
976
989
 
977
- arg_list$repeat_0 ->
978
- #main flatten(arg_list.#list, expr_sep, arg_list$repeat_0) ;
979
-
980
990
  primary_expr ->
981
991
  #ident pass(sym)
982
992
  | #int pass(value)