bonescript-compiler 0.2.1 → 0.4.0

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 (167) hide show
  1. package/LICENSE +21 -21
  2. package/dist/algorithm_catalog.js +166 -166
  3. package/dist/cli.d.ts +2 -1
  4. package/dist/cli.js +75 -543
  5. package/dist/cli.js.map +1 -1
  6. package/dist/commands/check.d.ts +5 -0
  7. package/dist/commands/check.js +34 -0
  8. package/dist/commands/check.js.map +1 -0
  9. package/dist/commands/compile.d.ts +5 -0
  10. package/dist/commands/compile.js +215 -0
  11. package/dist/commands/compile.js.map +1 -0
  12. package/dist/commands/debug.d.ts +5 -0
  13. package/dist/commands/debug.js +59 -0
  14. package/dist/commands/debug.js.map +1 -0
  15. package/dist/commands/diff.d.ts +5 -0
  16. package/dist/commands/diff.js +125 -0
  17. package/dist/commands/diff.js.map +1 -0
  18. package/dist/commands/fmt.d.ts +5 -0
  19. package/dist/commands/fmt.js +49 -0
  20. package/dist/commands/fmt.js.map +1 -0
  21. package/dist/commands/init.d.ts +5 -0
  22. package/dist/commands/init.js +96 -0
  23. package/dist/commands/init.js.map +1 -0
  24. package/dist/commands/ir.d.ts +5 -0
  25. package/dist/commands/ir.js +27 -0
  26. package/dist/commands/ir.js.map +1 -0
  27. package/dist/commands/lex.d.ts +5 -0
  28. package/dist/commands/lex.js +21 -0
  29. package/dist/commands/lex.js.map +1 -0
  30. package/dist/commands/parse.d.ts +5 -0
  31. package/dist/commands/parse.js +30 -0
  32. package/dist/commands/parse.js.map +1 -0
  33. package/dist/commands/test.d.ts +5 -0
  34. package/dist/commands/test.js +61 -0
  35. package/dist/commands/test.js.map +1 -0
  36. package/dist/commands/verify_determinism.d.ts +5 -0
  37. package/dist/commands/verify_determinism.js +64 -0
  38. package/dist/commands/verify_determinism.js.map +1 -0
  39. package/dist/commands/watch.d.ts +5 -0
  40. package/dist/commands/watch.js +50 -0
  41. package/dist/commands/watch.js.map +1 -0
  42. package/dist/emit_auth.d.ts +6 -0
  43. package/dist/emit_auth.js +69 -0
  44. package/dist/emit_auth.js.map +1 -0
  45. package/dist/emit_capability.d.ts +13 -0
  46. package/dist/emit_capability.js +292 -128
  47. package/dist/emit_capability.js.map +1 -1
  48. package/dist/emit_composition.js +37 -3
  49. package/dist/emit_composition.js.map +1 -1
  50. package/dist/emit_database.d.ts +7 -0
  51. package/dist/emit_database.js +74 -0
  52. package/dist/emit_database.js.map +1 -0
  53. package/dist/emit_deploy.js +162 -162
  54. package/dist/emit_events.d.ts +1 -0
  55. package/dist/emit_events.js +342 -275
  56. package/dist/emit_events.js.map +1 -1
  57. package/dist/emit_full.js +135 -95
  58. package/dist/emit_full.js.map +1 -1
  59. package/dist/emit_index.d.ts +6 -0
  60. package/dist/emit_index.js +157 -0
  61. package/dist/emit_index.js.map +1 -0
  62. package/dist/emit_maintenance.js +249 -249
  63. package/dist/emit_models.d.ts +12 -0
  64. package/dist/emit_models.js +171 -0
  65. package/dist/emit_models.js.map +1 -0
  66. package/dist/emit_openapi.d.ts +9 -0
  67. package/dist/emit_openapi.js +308 -0
  68. package/dist/emit_openapi.js.map +1 -0
  69. package/dist/emit_package.d.ts +7 -0
  70. package/dist/emit_package.js +70 -0
  71. package/dist/emit_package.js.map +1 -0
  72. package/dist/emit_router.d.ts +12 -0
  73. package/dist/emit_router.js +390 -0
  74. package/dist/emit_router.js.map +1 -0
  75. package/dist/emit_runtime.d.ts +17 -11
  76. package/dist/emit_runtime.js +29 -686
  77. package/dist/emit_runtime.js.map +1 -1
  78. package/dist/emit_sourcemap.js +66 -66
  79. package/dist/emit_tests.js +37 -0
  80. package/dist/emit_tests.js.map +1 -1
  81. package/dist/emitter.js +34 -5
  82. package/dist/emitter.js.map +1 -1
  83. package/dist/extension_manager.d.ts +2 -2
  84. package/dist/extension_manager.js +6 -3
  85. package/dist/extension_manager.js.map +1 -1
  86. package/dist/lowering.d.ts +5 -14
  87. package/dist/lowering.js +47 -417
  88. package/dist/lowering.js.map +1 -1
  89. package/dist/lowering_channels.d.ts +11 -0
  90. package/dist/lowering_channels.js +102 -0
  91. package/dist/lowering_channels.js.map +1 -0
  92. package/dist/lowering_entities.d.ts +11 -0
  93. package/dist/lowering_entities.js +222 -0
  94. package/dist/lowering_entities.js.map +1 -0
  95. package/dist/lowering_helpers.d.ts +13 -0
  96. package/dist/lowering_helpers.js +76 -0
  97. package/dist/lowering_helpers.js.map +1 -0
  98. package/dist/module_loader.d.ts +2 -2
  99. package/dist/module_loader.js +20 -23
  100. package/dist/module_loader.js.map +1 -1
  101. package/dist/scaffold.d.ts +2 -2
  102. package/dist/scaffold.js +316 -319
  103. package/dist/scaffold.js.map +1 -1
  104. package/dist/typechecker.js +32 -13
  105. package/dist/typechecker.js.map +1 -1
  106. package/dist/verifier.d.ts +5 -0
  107. package/dist/verifier.js +140 -2
  108. package/dist/verifier.js.map +1 -1
  109. package/package.json +62 -52
  110. package/src/algorithm_catalog.ts +345 -345
  111. package/src/ast.ts +334 -334
  112. package/src/cli.ts +98 -624
  113. package/src/commands/check.ts +33 -0
  114. package/src/commands/compile.ts +191 -0
  115. package/src/commands/debug.ts +33 -0
  116. package/src/commands/diff.ts +108 -0
  117. package/src/commands/fmt.ts +22 -0
  118. package/src/commands/init.ts +72 -0
  119. package/src/commands/ir.ts +23 -0
  120. package/src/commands/lex.ts +17 -0
  121. package/src/commands/parse.ts +24 -0
  122. package/src/commands/test.ts +36 -0
  123. package/src/commands/verify_determinism.ts +66 -0
  124. package/src/commands/watch.ts +25 -0
  125. package/src/emit_auth.ts +67 -0
  126. package/src/emit_batch.ts +140 -140
  127. package/src/emit_capability.ts +617 -436
  128. package/src/emit_composition.ts +229 -196
  129. package/src/emit_database.ts +75 -0
  130. package/src/emit_deploy.ts +190 -190
  131. package/src/emit_events.ts +377 -307
  132. package/src/emit_extras.ts +240 -240
  133. package/src/emit_full.ts +351 -309
  134. package/src/emit_index.ts +161 -0
  135. package/src/emit_maintenance.ts +459 -459
  136. package/src/emit_models.ts +176 -0
  137. package/src/emit_openapi.ts +318 -0
  138. package/src/emit_package.ts +69 -0
  139. package/src/emit_router.ts +409 -0
  140. package/src/emit_runtime.ts +17 -728
  141. package/src/emit_sourcemap.ts +140 -140
  142. package/src/emit_tests.ts +246 -205
  143. package/src/emit_websocket.ts +229 -229
  144. package/src/emitter.ts +31 -5
  145. package/src/extension_manager.ts +189 -187
  146. package/src/formatter.ts +297 -297
  147. package/src/index.ts +88 -88
  148. package/src/ir.ts +215 -215
  149. package/src/lexer.ts +630 -630
  150. package/src/lowering.ts +142 -556
  151. package/src/lowering_channels.ts +107 -0
  152. package/src/lowering_entities.ts +248 -0
  153. package/src/lowering_helpers.ts +75 -0
  154. package/src/module_loader.ts +112 -114
  155. package/src/optimizer.ts +196 -196
  156. package/src/parse_decls.ts +409 -409
  157. package/src/parse_decls2.ts +244 -244
  158. package/src/parse_expr.ts +197 -197
  159. package/src/parse_types.ts +54 -54
  160. package/src/parser.ts +1 -1
  161. package/src/parser_base.ts +57 -57
  162. package/src/parser_recovery.ts +153 -153
  163. package/src/scaffold.ts +372 -375
  164. package/src/solver.ts +330 -330
  165. package/src/typechecker.ts +30 -15
  166. package/src/types.ts +122 -122
  167. package/src/verifier.ts +151 -4
package/src/parse_expr.ts CHANGED
@@ -1,197 +1,197 @@
1
- /**
2
- * BoneScript Expression Parser — Pratt-style precedence climbing.
3
- */
4
-
5
- import { TokenKind } from "./lexer";
6
- import { TokenStream, ParseError } from "./parser_base";
7
- import * as AST from "./ast";
8
-
9
- export function parseExpr(s: TokenStream): AST.ExprNode {
10
- return parseLogicalOr(s);
11
- }
12
-
13
- export function parseExprList(s: TokenStream): AST.ExprNode[] {
14
- const exprs: AST.ExprNode[] = [];
15
- if (s.check(TokenKind.RBracket)) return exprs;
16
- do {
17
- exprs.push(parseExpr(s));
18
- } while (s.match(TokenKind.Comma));
19
- return exprs;
20
- }
21
-
22
- function parseLogicalOr(s: TokenStream): AST.ExprNode {
23
- let left = parseLogicalAnd(s);
24
- while (s.check(TokenKind.KwOr)) {
25
- const loc = s.peek().loc;
26
- s.advance();
27
- const right = parseLogicalAnd(s);
28
- left = { kind: "BinaryExpr", loc, op: "or", left, right };
29
- }
30
- return left;
31
- }
32
-
33
- function parseLogicalAnd(s: TokenStream): AST.ExprNode {
34
- let left = parseComparison(s);
35
- while (s.check(TokenKind.KwAnd)) {
36
- const loc = s.peek().loc;
37
- s.advance();
38
- const right = parseComparison(s);
39
- left = { kind: "BinaryExpr", loc, op: "and", left, right };
40
- }
41
- return left;
42
- }
43
-
44
- function parseComparison(s: TokenStream): AST.ExprNode {
45
- let left = parseAdditive(s);
46
- const compOps = [
47
- TokenKind.EqEq, TokenKind.NotEq, TokenKind.LAngle, TokenKind.RAngle,
48
- TokenKind.LtEq, TokenKind.GtEq, TokenKind.KwIn, TokenKind.KwContains,
49
- ];
50
- if (compOps.includes(s.peek().kind)) {
51
- const loc = s.peek().loc;
52
- const op = s.advance().value;
53
- const right = parseAdditive(s);
54
- // Check for range: expr in expr..expr
55
- if (op === "in" && s.check(TokenKind.DotDot)) {
56
- const dotLoc = s.peek().loc;
57
- s.advance(); // consume ..
58
- const upper = parseAdditive(s);
59
- // Desugar "x in low..high" into BinaryExpr with op "in_range"
60
- const range: AST.BinaryExprNode = { kind: "BinaryExpr", loc: dotLoc, op: "..", left: right, right: upper };
61
- left = { kind: "BinaryExpr", loc, op: "in", left, right: range };
62
- } else {
63
- left = { kind: "BinaryExpr", loc, op, left, right };
64
- }
65
- }
66
- return left;
67
- }
68
-
69
- function parseAdditive(s: TokenStream): AST.ExprNode {
70
- let left = parseMultiplicative(s);
71
- while (s.check(TokenKind.Plus) || s.check(TokenKind.Minus)) {
72
- const loc = s.peek().loc;
73
- const op = s.advance().value;
74
- const right = parseMultiplicative(s);
75
- left = { kind: "BinaryExpr", loc, op, left, right };
76
- }
77
- return left;
78
- }
79
-
80
- function parseMultiplicative(s: TokenStream): AST.ExprNode {
81
- let left = parseUnary(s);
82
- while (s.check(TokenKind.Star) || s.check(TokenKind.Slash) || s.check(TokenKind.Percent)) {
83
- const loc = s.peek().loc;
84
- const op = s.advance().value;
85
- const right = parseUnary(s);
86
- left = { kind: "BinaryExpr", loc, op, left, right };
87
- }
88
- return left;
89
- }
90
-
91
- function parseUnary(s: TokenStream): AST.ExprNode {
92
- if (s.check(TokenKind.KwNot)) {
93
- const loc = s.peek().loc;
94
- s.advance();
95
- const operand = parseUnary(s);
96
- return { kind: "UnaryExpr", loc, op: "not", operand };
97
- }
98
- if (s.check(TokenKind.Minus)) {
99
- const loc = s.peek().loc;
100
- s.advance();
101
- const operand = parseUnary(s);
102
- return { kind: "UnaryExpr", loc, op: "-", operand };
103
- }
104
- return parsePrimary(s);
105
- }
106
-
107
- function parsePrimary(s: TokenStream): AST.ExprNode {
108
- const tok = s.peek();
109
- const loc = tok.loc;
110
-
111
- // Parenthesized expression
112
- if (tok.kind === TokenKind.LParen) {
113
- s.advance();
114
- const expr = parseExpr(s);
115
- s.expect(TokenKind.RParen, "parenthesized expression");
116
- return expr;
117
- }
118
-
119
- // List literal
120
- if (tok.kind === TokenKind.LBracket) {
121
- s.advance();
122
- const elements: AST.ExprNode[] = [];
123
- if (!s.check(TokenKind.RBracket)) {
124
- do { elements.push(parseExpr(s)); } while (s.match(TokenKind.Comma));
125
- }
126
- s.expect(TokenKind.RBracket, "list literal close");
127
- return { kind: "Literal", loc, type: "list", value: elements };
128
- }
129
-
130
- // String literal
131
- if (tok.kind === TokenKind.StringLiteral) {
132
- s.advance();
133
- return { kind: "Literal", loc, type: "string", value: tok.value };
134
- }
135
-
136
- // Int literal
137
- if (tok.kind === TokenKind.IntLiteral) {
138
- s.advance();
139
- return { kind: "Literal", loc, type: "int", value: parseInt(tok.value, 10) };
140
- }
141
-
142
- // Float literal
143
- if (tok.kind === TokenKind.FloatLiteral) {
144
- s.advance();
145
- return { kind: "Literal", loc, type: "float", value: parseFloat(tok.value) };
146
- }
147
-
148
- // Boolean
149
- if (tok.kind === TokenKind.KwTrue) { s.advance(); return { kind: "Literal", loc, type: "bool", value: true }; }
150
- if (tok.kind === TokenKind.KwFalse) { s.advance(); return { kind: "Literal", loc, type: "bool", value: false }; }
151
-
152
- // None
153
- if (tok.kind === TokenKind.KwNone) { s.advance(); return { kind: "Literal", loc, type: "none", value: null }; }
154
-
155
- // now()
156
- if (tok.kind === TokenKind.KwNow) {
157
- s.advance();
158
- if (s.match(TokenKind.LParen)) { s.expect(TokenKind.RParen, "now()"); }
159
- return { kind: "CallExpr", loc, name: "now", args: [] };
160
- }
161
-
162
- // Identifier — field ref or function call
163
- if (tok.kind === TokenKind.Identifier || isKeywordIdentifier(tok)) {
164
- const path: string[] = [];
165
- path.push(s.advance().value);
166
-
167
- // Function call
168
- if (s.check(TokenKind.LParen)) {
169
- s.advance();
170
- const args: AST.ExprNode[] = [];
171
- if (!s.check(TokenKind.RParen)) {
172
- do { args.push(parseExpr(s)); } while (s.match(TokenKind.Comma));
173
- }
174
- s.expect(TokenKind.RParen, "function call close");
175
- return { kind: "CallExpr", loc, name: path[0], args };
176
- }
177
-
178
- // Dotted field reference
179
- while (s.match(TokenKind.Dot)) {
180
- const next = s.peek();
181
- if (next.kind === TokenKind.Identifier || next.kind === TokenKind.KwUnique || isKeywordIdentifier(next)) {
182
- path.push(s.advance().value);
183
- } else {
184
- break;
185
- }
186
- }
187
- return { kind: "FieldRef", loc, path };
188
- }
189
-
190
- throw new ParseError(`Expected expression, got ${tok.kind} ('${tok.value}')`, tok.loc);
191
- }
192
-
193
- /** Check if a token is a keyword that can also serve as an identifier */
194
- function isKeywordIdentifier(tok: { kind: TokenKind; value: string }): boolean {
195
- // Keywords that are commonly used as variable/field names
196
- return /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(tok.value) && tok.kind !== TokenKind.EOF;
197
- }
1
+ /**
2
+ * BoneScript Expression Parser — Pratt-style precedence climbing.
3
+ */
4
+
5
+ import { TokenKind } from "./lexer";
6
+ import { TokenStream, ParseError } from "./parser_base";
7
+ import * as AST from "./ast";
8
+
9
+ export function parseExpr(s: TokenStream): AST.ExprNode {
10
+ return parseLogicalOr(s);
11
+ }
12
+
13
+ export function parseExprList(s: TokenStream): AST.ExprNode[] {
14
+ const exprs: AST.ExprNode[] = [];
15
+ if (s.check(TokenKind.RBracket)) return exprs;
16
+ do {
17
+ exprs.push(parseExpr(s));
18
+ } while (s.match(TokenKind.Comma));
19
+ return exprs;
20
+ }
21
+
22
+ function parseLogicalOr(s: TokenStream): AST.ExprNode {
23
+ let left = parseLogicalAnd(s);
24
+ while (s.check(TokenKind.KwOr)) {
25
+ const loc = s.peek().loc;
26
+ s.advance();
27
+ const right = parseLogicalAnd(s);
28
+ left = { kind: "BinaryExpr", loc, op: "or", left, right };
29
+ }
30
+ return left;
31
+ }
32
+
33
+ function parseLogicalAnd(s: TokenStream): AST.ExprNode {
34
+ let left = parseComparison(s);
35
+ while (s.check(TokenKind.KwAnd)) {
36
+ const loc = s.peek().loc;
37
+ s.advance();
38
+ const right = parseComparison(s);
39
+ left = { kind: "BinaryExpr", loc, op: "and", left, right };
40
+ }
41
+ return left;
42
+ }
43
+
44
+ function parseComparison(s: TokenStream): AST.ExprNode {
45
+ let left = parseAdditive(s);
46
+ const compOps = [
47
+ TokenKind.EqEq, TokenKind.NotEq, TokenKind.LAngle, TokenKind.RAngle,
48
+ TokenKind.LtEq, TokenKind.GtEq, TokenKind.KwIn, TokenKind.KwContains,
49
+ ];
50
+ if (compOps.includes(s.peek().kind)) {
51
+ const loc = s.peek().loc;
52
+ const op = s.advance().value;
53
+ const right = parseAdditive(s);
54
+ // Check for range: expr in expr..expr
55
+ if (op === "in" && s.check(TokenKind.DotDot)) {
56
+ const dotLoc = s.peek().loc;
57
+ s.advance(); // consume ..
58
+ const upper = parseAdditive(s);
59
+ // Desugar "x in low..high" into BinaryExpr with op "in_range"
60
+ const range: AST.BinaryExprNode = { kind: "BinaryExpr", loc: dotLoc, op: "..", left: right, right: upper };
61
+ left = { kind: "BinaryExpr", loc, op: "in", left, right: range };
62
+ } else {
63
+ left = { kind: "BinaryExpr", loc, op, left, right };
64
+ }
65
+ }
66
+ return left;
67
+ }
68
+
69
+ function parseAdditive(s: TokenStream): AST.ExprNode {
70
+ let left = parseMultiplicative(s);
71
+ while (s.check(TokenKind.Plus) || s.check(TokenKind.Minus)) {
72
+ const loc = s.peek().loc;
73
+ const op = s.advance().value;
74
+ const right = parseMultiplicative(s);
75
+ left = { kind: "BinaryExpr", loc, op, left, right };
76
+ }
77
+ return left;
78
+ }
79
+
80
+ function parseMultiplicative(s: TokenStream): AST.ExprNode {
81
+ let left = parseUnary(s);
82
+ while (s.check(TokenKind.Star) || s.check(TokenKind.Slash) || s.check(TokenKind.Percent)) {
83
+ const loc = s.peek().loc;
84
+ const op = s.advance().value;
85
+ const right = parseUnary(s);
86
+ left = { kind: "BinaryExpr", loc, op, left, right };
87
+ }
88
+ return left;
89
+ }
90
+
91
+ function parseUnary(s: TokenStream): AST.ExprNode {
92
+ if (s.check(TokenKind.KwNot)) {
93
+ const loc = s.peek().loc;
94
+ s.advance();
95
+ const operand = parseUnary(s);
96
+ return { kind: "UnaryExpr", loc, op: "not", operand };
97
+ }
98
+ if (s.check(TokenKind.Minus)) {
99
+ const loc = s.peek().loc;
100
+ s.advance();
101
+ const operand = parseUnary(s);
102
+ return { kind: "UnaryExpr", loc, op: "-", operand };
103
+ }
104
+ return parsePrimary(s);
105
+ }
106
+
107
+ function parsePrimary(s: TokenStream): AST.ExprNode {
108
+ const tok = s.peek();
109
+ const loc = tok.loc;
110
+
111
+ // Parenthesized expression
112
+ if (tok.kind === TokenKind.LParen) {
113
+ s.advance();
114
+ const expr = parseExpr(s);
115
+ s.expect(TokenKind.RParen, "parenthesized expression");
116
+ return expr;
117
+ }
118
+
119
+ // List literal
120
+ if (tok.kind === TokenKind.LBracket) {
121
+ s.advance();
122
+ const elements: AST.ExprNode[] = [];
123
+ if (!s.check(TokenKind.RBracket)) {
124
+ do { elements.push(parseExpr(s)); } while (s.match(TokenKind.Comma));
125
+ }
126
+ s.expect(TokenKind.RBracket, "list literal close");
127
+ return { kind: "Literal", loc, type: "list", value: elements };
128
+ }
129
+
130
+ // String literal
131
+ if (tok.kind === TokenKind.StringLiteral) {
132
+ s.advance();
133
+ return { kind: "Literal", loc, type: "string", value: tok.value };
134
+ }
135
+
136
+ // Int literal
137
+ if (tok.kind === TokenKind.IntLiteral) {
138
+ s.advance();
139
+ return { kind: "Literal", loc, type: "int", value: parseInt(tok.value, 10) };
140
+ }
141
+
142
+ // Float literal
143
+ if (tok.kind === TokenKind.FloatLiteral) {
144
+ s.advance();
145
+ return { kind: "Literal", loc, type: "float", value: parseFloat(tok.value) };
146
+ }
147
+
148
+ // Boolean
149
+ if (tok.kind === TokenKind.KwTrue) { s.advance(); return { kind: "Literal", loc, type: "bool", value: true }; }
150
+ if (tok.kind === TokenKind.KwFalse) { s.advance(); return { kind: "Literal", loc, type: "bool", value: false }; }
151
+
152
+ // None
153
+ if (tok.kind === TokenKind.KwNone) { s.advance(); return { kind: "Literal", loc, type: "none", value: null }; }
154
+
155
+ // now()
156
+ if (tok.kind === TokenKind.KwNow) {
157
+ s.advance();
158
+ if (s.match(TokenKind.LParen)) { s.expect(TokenKind.RParen, "now()"); }
159
+ return { kind: "CallExpr", loc, name: "now", args: [] };
160
+ }
161
+
162
+ // Identifier — field ref or function call
163
+ if (tok.kind === TokenKind.Identifier || isKeywordIdentifier(tok)) {
164
+ const path: string[] = [];
165
+ path.push(s.advance().value);
166
+
167
+ // Function call
168
+ if (s.check(TokenKind.LParen)) {
169
+ s.advance();
170
+ const args: AST.ExprNode[] = [];
171
+ if (!s.check(TokenKind.RParen)) {
172
+ do { args.push(parseExpr(s)); } while (s.match(TokenKind.Comma));
173
+ }
174
+ s.expect(TokenKind.RParen, "function call close");
175
+ return { kind: "CallExpr", loc, name: path[0], args };
176
+ }
177
+
178
+ // Dotted field reference
179
+ while (s.match(TokenKind.Dot)) {
180
+ const next = s.peek();
181
+ if (next.kind === TokenKind.Identifier || next.kind === TokenKind.KwUnique || isKeywordIdentifier(next)) {
182
+ path.push(s.advance().value);
183
+ } else {
184
+ break;
185
+ }
186
+ }
187
+ return { kind: "FieldRef", loc, path };
188
+ }
189
+
190
+ throw new ParseError(`Expected expression, got ${tok.kind} ('${tok.value}')`, tok.loc);
191
+ }
192
+
193
+ /** Check if a token is a keyword that can also serve as an identifier */
194
+ function isKeywordIdentifier(tok: { kind: TokenKind; value: string }): boolean {
195
+ // Keywords that are commonly used as variable/field names
196
+ return /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(tok.value) && tok.kind !== TokenKind.EOF;
197
+ }
@@ -1,54 +1,54 @@
1
- /**
2
- * BoneScript Type Expression Parser
3
- */
4
-
5
- import { TokenKind } from "./lexer";
6
- import { TokenStream, ParseError } from "./parser_base";
7
- import * as AST from "./ast";
8
-
9
- export function parseTypeExpr(s: TokenStream): AST.TypeExprNode {
10
- const loc = s.peek().loc;
11
- const tok = s.peek();
12
-
13
- // Generic types
14
- const generics = [TokenKind.KwSet, TokenKind.KwList, TokenKind.KwOptional, TokenKind.KwResult, TokenKind.KwMap];
15
- if (generics.includes(tok.kind)) {
16
- const name = s.advance().value;
17
- s.expect(TokenKind.LAngle, "generic type arg");
18
- const typeArgs: AST.TypeExprNode[] = [];
19
- typeArgs.push(parseTypeExpr(s));
20
- while (s.match(TokenKind.Comma)) {
21
- typeArgs.push(parseTypeExpr(s));
22
- }
23
- s.expect(TokenKind.RAngle, "generic type arg close");
24
- return { kind: "GenericType", loc, name, typeArgs };
25
- }
26
-
27
- // Primitive types
28
- const primitives = [
29
- TokenKind.KwString, TokenKind.KwUint, TokenKind.KwInt, TokenKind.KwFloat,
30
- TokenKind.KwBool, TokenKind.KwTimestamp, TokenKind.KwUuid, TokenKind.KwBytes, TokenKind.KwJson,
31
- ];
32
- if (primitives.includes(tok.kind)) {
33
- return { kind: "PrimitiveType", loc, name: s.advance().value };
34
- }
35
-
36
- // Tuple type
37
- if (tok.kind === TokenKind.LParen) {
38
- s.advance();
39
- const elements: AST.TypeExprNode[] = [];
40
- elements.push(parseTypeExpr(s));
41
- while (s.match(TokenKind.Comma)) {
42
- elements.push(parseTypeExpr(s));
43
- }
44
- s.expect(TokenKind.RParen, "tuple type close");
45
- return { kind: "TupleType", loc, elements };
46
- }
47
-
48
- // Entity reference
49
- if (tok.kind === TokenKind.Identifier) {
50
- return { kind: "EntityRefType", loc, name: s.advance().value };
51
- }
52
-
53
- throw new ParseError(`Expected type expression, got ${tok.kind}`, tok.loc);
54
- }
1
+ /**
2
+ * BoneScript Type Expression Parser
3
+ */
4
+
5
+ import { TokenKind } from "./lexer";
6
+ import { TokenStream, ParseError } from "./parser_base";
7
+ import * as AST from "./ast";
8
+
9
+ export function parseTypeExpr(s: TokenStream): AST.TypeExprNode {
10
+ const loc = s.peek().loc;
11
+ const tok = s.peek();
12
+
13
+ // Generic types
14
+ const generics = [TokenKind.KwSet, TokenKind.KwList, TokenKind.KwOptional, TokenKind.KwResult, TokenKind.KwMap];
15
+ if (generics.includes(tok.kind)) {
16
+ const name = s.advance().value;
17
+ s.expect(TokenKind.LAngle, "generic type arg");
18
+ const typeArgs: AST.TypeExprNode[] = [];
19
+ typeArgs.push(parseTypeExpr(s));
20
+ while (s.match(TokenKind.Comma)) {
21
+ typeArgs.push(parseTypeExpr(s));
22
+ }
23
+ s.expect(TokenKind.RAngle, "generic type arg close");
24
+ return { kind: "GenericType", loc, name, typeArgs };
25
+ }
26
+
27
+ // Primitive types
28
+ const primitives = [
29
+ TokenKind.KwString, TokenKind.KwUint, TokenKind.KwInt, TokenKind.KwFloat,
30
+ TokenKind.KwBool, TokenKind.KwTimestamp, TokenKind.KwUuid, TokenKind.KwBytes, TokenKind.KwJson,
31
+ ];
32
+ if (primitives.includes(tok.kind)) {
33
+ return { kind: "PrimitiveType", loc, name: s.advance().value };
34
+ }
35
+
36
+ // Tuple type
37
+ if (tok.kind === TokenKind.LParen) {
38
+ s.advance();
39
+ const elements: AST.TypeExprNode[] = [];
40
+ elements.push(parseTypeExpr(s));
41
+ while (s.match(TokenKind.Comma)) {
42
+ elements.push(parseTypeExpr(s));
43
+ }
44
+ s.expect(TokenKind.RParen, "tuple type close");
45
+ return { kind: "TupleType", loc, elements };
46
+ }
47
+
48
+ // Entity reference
49
+ if (tok.kind === TokenKind.Identifier) {
50
+ return { kind: "EntityRefType", loc, name: s.advance().value };
51
+ }
52
+
53
+ throw new ParseError(`Expected type expression, got ${tok.kind}`, tok.loc);
54
+ }
package/src/parser.ts CHANGED
@@ -61,4 +61,4 @@ export class Parser {
61
61
  throw new ParseError("Expected declaration, got " + tok.kind, tok.loc);
62
62
  }
63
63
  }
64
- }
64
+ }
@@ -1,57 +1,57 @@
1
- /**
2
- * bone parser Base — Token stream utilities.
3
- */
4
-
5
- import { Token, TokenKind, SourceLocation } from "./lexer";
6
-
7
- export class ParseError extends Error {
8
- constructor(message: string, public loc: SourceLocation) {
9
- super(`Parse error at ${loc.line}:${loc.column}: ${message}`);
10
- this.name = "ParseError";
11
- }
12
- }
13
-
14
- export class TokenStream {
15
- private tokens: Token[];
16
- private pos: number = 0;
17
-
18
- constructor(tokens: Token[]) {
19
- this.tokens = tokens;
20
- }
21
-
22
- peek(offset: number = 0): Token {
23
- return this.tokens[this.pos + offset] ?? this.tokens[this.tokens.length - 1];
24
- }
25
-
26
- check(kind: TokenKind): boolean {
27
- return this.peek().kind === kind;
28
- }
29
-
30
- checkAny(...kinds: TokenKind[]): boolean {
31
- return kinds.includes(this.peek().kind);
32
- }
33
-
34
- advance(): Token {
35
- const t = this.tokens[this.pos];
36
- if (this.pos < this.tokens.length - 1) this.pos++;
37
- return t;
38
- }
39
-
40
- expect(kind: TokenKind, context: string): Token {
41
- if (!this.check(kind)) {
42
- throw new ParseError(
43
- `Expected ${kind} in ${context}, got ${this.peek().kind} ('${this.peek().value}')`,
44
- this.peek().loc
45
- );
46
- }
47
- return this.advance();
48
- }
49
-
50
- match(kind: TokenKind): boolean {
51
- if (this.check(kind)) {
52
- this.advance();
53
- return true;
54
- }
55
- return false;
56
- }
57
- }
1
+ /**
2
+ * bone parser Base — Token stream utilities.
3
+ */
4
+
5
+ import { Token, TokenKind, SourceLocation } from "./lexer";
6
+
7
+ export class ParseError extends Error {
8
+ constructor(message: string, public loc: SourceLocation) {
9
+ super(`Parse error at ${loc.line}:${loc.column}: ${message}`);
10
+ this.name = "ParseError";
11
+ }
12
+ }
13
+
14
+ export class TokenStream {
15
+ private tokens: Token[];
16
+ private pos: number = 0;
17
+
18
+ constructor(tokens: Token[]) {
19
+ this.tokens = tokens;
20
+ }
21
+
22
+ peek(offset: number = 0): Token {
23
+ return this.tokens[this.pos + offset] ?? this.tokens[this.tokens.length - 1];
24
+ }
25
+
26
+ check(kind: TokenKind): boolean {
27
+ return this.peek().kind === kind;
28
+ }
29
+
30
+ checkAny(...kinds: TokenKind[]): boolean {
31
+ return kinds.includes(this.peek().kind);
32
+ }
33
+
34
+ advance(): Token {
35
+ const t = this.tokens[this.pos];
36
+ if (this.pos < this.tokens.length - 1) this.pos++;
37
+ return t;
38
+ }
39
+
40
+ expect(kind: TokenKind, context: string): Token {
41
+ if (!this.check(kind)) {
42
+ throw new ParseError(
43
+ `Expected ${kind} in ${context}, got ${this.peek().kind} ('${this.peek().value}')`,
44
+ this.peek().loc
45
+ );
46
+ }
47
+ return this.advance();
48
+ }
49
+
50
+ match(kind: TokenKind): boolean {
51
+ if (this.check(kind)) {
52
+ this.advance();
53
+ return true;
54
+ }
55
+ return false;
56
+ }
57
+ }