stone-lang 0.1.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 (68) hide show
  1. package/README.md +52 -0
  2. package/StoneEngine.js +879 -0
  3. package/StoneEngineService.js +1727 -0
  4. package/adapters/FileSystemAdapter.js +230 -0
  5. package/adapters/OutputAdapter.js +208 -0
  6. package/adapters/index.js +6 -0
  7. package/cli/CLIOutputAdapter.js +196 -0
  8. package/cli/DaemonClient.js +349 -0
  9. package/cli/JSONOutputAdapter.js +135 -0
  10. package/cli/ReplSession.js +567 -0
  11. package/cli/ViewerServer.js +590 -0
  12. package/cli/commands/check.js +84 -0
  13. package/cli/commands/daemon.js +189 -0
  14. package/cli/commands/kill.js +66 -0
  15. package/cli/commands/package.js +713 -0
  16. package/cli/commands/ps.js +65 -0
  17. package/cli/commands/run.js +537 -0
  18. package/cli/entry.js +169 -0
  19. package/cli/index.js +14 -0
  20. package/cli/stonec.js +358 -0
  21. package/cli/test-compiler.js +181 -0
  22. package/cli/viewer/index.html +495 -0
  23. package/daemon/IPCServer.js +455 -0
  24. package/daemon/ProcessManager.js +327 -0
  25. package/daemon/ProcessRunner.js +307 -0
  26. package/daemon/daemon.js +398 -0
  27. package/daemon/index.js +16 -0
  28. package/frontend/analysis/index.js +5 -0
  29. package/frontend/analysis/livenessAnalyzer.js +568 -0
  30. package/frontend/analysis/treeShaker.js +265 -0
  31. package/frontend/index.js +20 -0
  32. package/frontend/parsing/astBuilder.js +2196 -0
  33. package/frontend/parsing/index.js +7 -0
  34. package/frontend/parsing/sonParser.js +592 -0
  35. package/frontend/parsing/stoneAstTypes.js +703 -0
  36. package/frontend/parsing/terminal-registry.js +435 -0
  37. package/frontend/parsing/tokenizer.js +692 -0
  38. package/frontend/type-checker/OverloadedFunctionType.js +43 -0
  39. package/frontend/type-checker/TypeEnvironment.js +165 -0
  40. package/frontend/type-checker/bidirectionalInference.js +149 -0
  41. package/frontend/type-checker/index.js +10 -0
  42. package/frontend/type-checker/moduleAnalysis.js +248 -0
  43. package/frontend/type-checker/operatorMappings.js +35 -0
  44. package/frontend/type-checker/overloadResolution.js +605 -0
  45. package/frontend/type-checker/typeChecker.js +452 -0
  46. package/frontend/type-checker/typeCompatibility.js +389 -0
  47. package/frontend/type-checker/visitors/controlFlow.js +483 -0
  48. package/frontend/type-checker/visitors/functions.js +604 -0
  49. package/frontend/type-checker/visitors/index.js +38 -0
  50. package/frontend/type-checker/visitors/literals.js +341 -0
  51. package/frontend/type-checker/visitors/modules.js +159 -0
  52. package/frontend/type-checker/visitors/operators.js +109 -0
  53. package/frontend/type-checker/visitors/statements.js +768 -0
  54. package/frontend/types/index.js +5 -0
  55. package/frontend/types/operatorMap.js +134 -0
  56. package/frontend/types/types.js +2046 -0
  57. package/frontend/utils/errorCollector.js +244 -0
  58. package/frontend/utils/index.js +5 -0
  59. package/frontend/utils/moduleResolver.js +479 -0
  60. package/package.json +50 -0
  61. package/packages/browserCache.js +359 -0
  62. package/packages/fetcher.js +236 -0
  63. package/packages/index.js +130 -0
  64. package/packages/lockfile.js +271 -0
  65. package/packages/manifest.js +291 -0
  66. package/packages/packageResolver.js +356 -0
  67. package/packages/resolver.js +310 -0
  68. package/packages/semver.js +635 -0
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Frontend Parsing - Barrel exports
3
+ */
4
+ export { Lexer, TokenType, tokenizeSource, tokenize } from './tokenizer.js';
5
+ export { Parser, buildASTFromSource, buildASTFromTokens, parse } from './astBuilder.js';
6
+ export * from './stoneAstTypes.js';
7
+ export { createTerminalConstructors } from './terminal-registry.js';
@@ -0,0 +1,592 @@
1
+ /**
2
+ * Stone Object Notation (.son) Parser
3
+ *
4
+ * Parses .son files - a subset of Stone syntax for data/config.
5
+ * Supports: strings, numbers, booleans, arrays, objects, comments.
6
+ * No functions, imports, or control flow.
7
+ */
8
+
9
+ // Token types for SON (simplified subset)
10
+ const SONTokenType = {
11
+ // Literals
12
+ NUMBER: 'NUMBER',
13
+ STRING: 'STRING',
14
+ TRUE: 'TRUE',
15
+ FALSE: 'FALSE',
16
+ NONE: 'NONE',
17
+
18
+ // Identifiers (for keys)
19
+ IDENTIFIER: 'IDENTIFIER',
20
+
21
+ // Delimiters
22
+ LBRACE: 'LBRACE', // {
23
+ RBRACE: 'RBRACE', // }
24
+ LBRACKET: 'LBRACKET', // [
25
+ RBRACKET: 'RBRACKET', // ]
26
+
27
+ // Punctuation
28
+ COMMA: 'COMMA', // ,
29
+ ASSIGN: 'ASSIGN', // =
30
+ COLON: 'COLON', // :
31
+
32
+ // Special
33
+ EOF: 'EOF',
34
+ };
35
+
36
+ /**
37
+ * SON Token
38
+ */
39
+ class SONToken {
40
+ constructor(type, value, line, column) {
41
+ this.type = type;
42
+ this.value = value;
43
+ this.line = line;
44
+ this.column = column;
45
+ }
46
+ }
47
+
48
+ /**
49
+ * SON Lexer - tokenizes .son files
50
+ */
51
+ class SONLexer {
52
+ constructor(source, filename = '<son>') {
53
+ this.source = source;
54
+ this.filename = filename;
55
+ this.pos = 0;
56
+ this.line = 1;
57
+ this.column = 1;
58
+ this.tokens = [];
59
+ }
60
+
61
+ tokenize() {
62
+ while (this.pos < this.source.length) {
63
+ this.skipWhitespaceAndComments();
64
+ if (this.pos >= this.source.length) break;
65
+
66
+ const token = this.nextToken();
67
+ if (token) {
68
+ this.tokens.push(token);
69
+ }
70
+ }
71
+
72
+ this.tokens.push(new SONToken(SONTokenType.EOF, null, this.line, this.column));
73
+ return this.tokens;
74
+ }
75
+
76
+ nextToken() {
77
+ const char = this.peek();
78
+ const startLine = this.line;
79
+ const startColumn = this.column;
80
+
81
+ // Numbers (including negative)
82
+ if (this.isDigit(char) || (char === '-' && this.isDigit(this.peek(1)))) {
83
+ return this.readNumber(startLine, startColumn);
84
+ }
85
+
86
+ // Strings
87
+ if (char === '"' || char === "'") {
88
+ return this.readString(char, startLine, startColumn);
89
+ }
90
+
91
+ // Identifiers and keywords
92
+ if (this.isAlpha(char) || char === '_') {
93
+ return this.readIdentifier(startLine, startColumn);
94
+ }
95
+
96
+ // Single-character tokens
97
+ this.advance();
98
+ switch (char) {
99
+ case '{':
100
+ return new SONToken(SONTokenType.LBRACE, '{', startLine, startColumn);
101
+ case '}':
102
+ return new SONToken(SONTokenType.RBRACE, '}', startLine, startColumn);
103
+ case '[':
104
+ return new SONToken(SONTokenType.LBRACKET, '[', startLine, startColumn);
105
+ case ']':
106
+ return new SONToken(SONTokenType.RBRACKET, ']', startLine, startColumn);
107
+ case ',':
108
+ return new SONToken(SONTokenType.COMMA, ',', startLine, startColumn);
109
+ case '=':
110
+ return new SONToken(SONTokenType.ASSIGN, '=', startLine, startColumn);
111
+ case ':':
112
+ return new SONToken(SONTokenType.COLON, ':', startLine, startColumn);
113
+ default:
114
+ throw new SONParseError(
115
+ `Unexpected character '${char}'`,
116
+ this.filename,
117
+ startLine,
118
+ startColumn
119
+ );
120
+ }
121
+ }
122
+
123
+ readNumber(line, column) {
124
+ let num = '';
125
+ let isFloat = false;
126
+
127
+ // Handle negative sign
128
+ if (this.peek() === '-') {
129
+ num += '-';
130
+ this.advance();
131
+ }
132
+
133
+ while (this.isDigit(this.peek()) || this.peek() === '.') {
134
+ if (this.peek() === '.') {
135
+ // Check if it's really a decimal point
136
+ if (!this.isDigit(this.peek(1))) {
137
+ break;
138
+ }
139
+ if (isFloat) {
140
+ throw new SONParseError(
141
+ 'Invalid number: multiple decimal points',
142
+ this.filename,
143
+ line,
144
+ column
145
+ );
146
+ }
147
+ isFloat = true;
148
+ }
149
+ num += this.peek();
150
+ this.advance();
151
+ }
152
+
153
+ // Scientific notation
154
+ if (this.peek() === 'e' || this.peek() === 'E') {
155
+ num += this.peek();
156
+ this.advance();
157
+ isFloat = true;
158
+
159
+ if (this.peek() === '+' || this.peek() === '-') {
160
+ num += this.peek();
161
+ this.advance();
162
+ }
163
+
164
+ if (!this.isDigit(this.peek())) {
165
+ throw new SONParseError(
166
+ 'Invalid number: expected digit after exponent',
167
+ this.filename,
168
+ line,
169
+ column
170
+ );
171
+ }
172
+
173
+ while (this.isDigit(this.peek())) {
174
+ num += this.peek();
175
+ this.advance();
176
+ }
177
+ }
178
+
179
+ const value = isFloat ? parseFloat(num) : parseInt(num, 10);
180
+ return new SONToken(SONTokenType.NUMBER, value, line, column);
181
+ }
182
+
183
+ readString(quote, line, column) {
184
+ this.advance(); // Skip opening quote
185
+ let str = '';
186
+
187
+ while (this.peek() !== quote && this.pos < this.source.length) {
188
+ if (this.peek() === '\\') {
189
+ this.advance();
190
+ const escaped = this.peek();
191
+ switch (escaped) {
192
+ case 'n': str += '\n'; break;
193
+ case 't': str += '\t'; break;
194
+ case 'r': str += '\r'; break;
195
+ case '\\': str += '\\'; break;
196
+ case quote: str += quote; break;
197
+ default: str += escaped;
198
+ }
199
+ this.advance();
200
+ } else {
201
+ if (this.peek() === '\n') {
202
+ this.line++;
203
+ this.column = 0;
204
+ }
205
+ str += this.peek();
206
+ this.advance();
207
+ }
208
+ }
209
+
210
+ if (this.peek() !== quote) {
211
+ throw new SONParseError(
212
+ 'Unterminated string',
213
+ this.filename,
214
+ line,
215
+ column
216
+ );
217
+ }
218
+
219
+ this.advance(); // Skip closing quote
220
+ return new SONToken(SONTokenType.STRING, str, line, column);
221
+ }
222
+
223
+ readIdentifier(line, column) {
224
+ let id = '';
225
+
226
+ while (this.isAlphaNumeric(this.peek()) || this.peek() === '_' || this.peek() === '-') {
227
+ id += this.peek();
228
+ this.advance();
229
+ }
230
+
231
+ // Keywords
232
+ if (id === 'true') {
233
+ return new SONToken(SONTokenType.TRUE, true, line, column);
234
+ }
235
+ if (id === 'false') {
236
+ return new SONToken(SONTokenType.FALSE, false, line, column);
237
+ }
238
+ if (id === 'none' || id === 'null') {
239
+ return new SONToken(SONTokenType.NONE, null, line, column);
240
+ }
241
+
242
+ return new SONToken(SONTokenType.IDENTIFIER, id, line, column);
243
+ }
244
+
245
+ skipWhitespaceAndComments() {
246
+ while (this.pos < this.source.length) {
247
+ const char = this.peek();
248
+
249
+ // Whitespace
250
+ if (char === ' ' || char === '\t' || char === '\r' || char === '\n') {
251
+ if (char === '\n') {
252
+ this.line++;
253
+ this.column = 1;
254
+ } else {
255
+ this.column++;
256
+ }
257
+ this.pos++;
258
+ continue;
259
+ }
260
+
261
+ // Comments: // to end of line
262
+ if (char === '/' && this.peek(1) === '/') {
263
+ while (this.peek() !== '\n' && this.pos < this.source.length) {
264
+ this.advance();
265
+ }
266
+ continue;
267
+ }
268
+
269
+ break;
270
+ }
271
+ }
272
+
273
+ peek(offset = 0) {
274
+ const pos = this.pos + offset;
275
+ return pos < this.source.length ? this.source[pos] : null;
276
+ }
277
+
278
+ advance(n = 1) {
279
+ for (let i = 0; i < n; i++) {
280
+ if (this.pos < this.source.length) {
281
+ if (this.source[this.pos] === '\n') {
282
+ this.line++;
283
+ this.column = 1;
284
+ } else {
285
+ this.column++;
286
+ }
287
+ this.pos++;
288
+ }
289
+ }
290
+ }
291
+
292
+ isDigit(char) {
293
+ return char !== null && char >= '0' && char <= '9';
294
+ }
295
+
296
+ isAlpha(char) {
297
+ return char !== null && ((char >= 'a' && char <= 'z') || (char >= 'A' && char <= 'Z'));
298
+ }
299
+
300
+ isAlphaNumeric(char) {
301
+ return this.isAlpha(char) || this.isDigit(char);
302
+ }
303
+ }
304
+
305
+ /**
306
+ * SON Parse Error
307
+ */
308
+ export class SONParseError extends Error {
309
+ constructor(message, filename, line, column) {
310
+ super(`${message} at ${filename}:${line}:${column}`);
311
+ this.name = 'SONParseError';
312
+ this.filename = filename;
313
+ this.line = line;
314
+ this.column = column;
315
+ }
316
+ }
317
+
318
+ /**
319
+ * SON Parser - parses tokens into a JavaScript object
320
+ */
321
+ class SONParser {
322
+ constructor(tokens, filename = '<son>') {
323
+ this.tokens = tokens;
324
+ this.filename = filename;
325
+ this.pos = 0;
326
+ }
327
+
328
+ parse() {
329
+ const result = {};
330
+
331
+ // Parse top-level key = value pairs
332
+ while (!this.isAtEnd()) {
333
+ if (this.check(SONTokenType.EOF)) break;
334
+
335
+ const { key, value } = this.parseAssignment();
336
+ result[key] = value;
337
+ }
338
+
339
+ return result;
340
+ }
341
+
342
+ parseAssignment() {
343
+ const keyToken = this.expect(SONTokenType.IDENTIFIER, 'Expected identifier');
344
+ const key = keyToken.value;
345
+
346
+ this.expect(SONTokenType.ASSIGN, "Expected '=' after key");
347
+
348
+ const value = this.parseValue();
349
+
350
+ // Optional comma (allow trailing commas and no commas between top-level pairs)
351
+ this.match(SONTokenType.COMMA);
352
+
353
+ return { key, value };
354
+ }
355
+
356
+ parseValue() {
357
+ // Object
358
+ if (this.match(SONTokenType.LBRACE)) {
359
+ return this.parseObject();
360
+ }
361
+
362
+ // Array
363
+ if (this.match(SONTokenType.LBRACKET)) {
364
+ return this.parseArray();
365
+ }
366
+
367
+ // String
368
+ if (this.check(SONTokenType.STRING)) {
369
+ return this.advance().value;
370
+ }
371
+
372
+ // Number
373
+ if (this.check(SONTokenType.NUMBER)) {
374
+ return this.advance().value;
375
+ }
376
+
377
+ // Boolean
378
+ if (this.check(SONTokenType.TRUE) || this.check(SONTokenType.FALSE)) {
379
+ return this.advance().value;
380
+ }
381
+
382
+ // None/null
383
+ if (this.check(SONTokenType.NONE)) {
384
+ this.advance();
385
+ return null;
386
+ }
387
+
388
+ // Identifier as string (for unquoted values)
389
+ if (this.check(SONTokenType.IDENTIFIER)) {
390
+ return this.advance().value;
391
+ }
392
+
393
+ const token = this.peek();
394
+ throw new SONParseError(
395
+ `Unexpected token: ${token.type}`,
396
+ this.filename,
397
+ token.line,
398
+ token.column
399
+ );
400
+ }
401
+
402
+ parseObject() {
403
+ const obj = {};
404
+
405
+ while (!this.check(SONTokenType.RBRACE) && !this.isAtEnd()) {
406
+ // Key can be identifier or string
407
+ let key;
408
+ if (this.check(SONTokenType.IDENTIFIER)) {
409
+ key = this.advance().value;
410
+ } else if (this.check(SONTokenType.STRING)) {
411
+ key = this.advance().value;
412
+ } else {
413
+ const token = this.peek();
414
+ throw new SONParseError(
415
+ 'Expected object key (identifier or string)',
416
+ this.filename,
417
+ token.line,
418
+ token.column
419
+ );
420
+ }
421
+
422
+ // = or : for assignment
423
+ if (!this.match(SONTokenType.ASSIGN) && !this.match(SONTokenType.COLON)) {
424
+ const token = this.peek();
425
+ throw new SONParseError(
426
+ "Expected '=' or ':' after object key",
427
+ this.filename,
428
+ token.line,
429
+ token.column
430
+ );
431
+ }
432
+
433
+ obj[key] = this.parseValue();
434
+
435
+ // Optional comma
436
+ this.match(SONTokenType.COMMA);
437
+ }
438
+
439
+ this.expect(SONTokenType.RBRACE, "Expected '}' to close object");
440
+ return obj;
441
+ }
442
+
443
+ parseArray() {
444
+ const arr = [];
445
+
446
+ while (!this.check(SONTokenType.RBRACKET) && !this.isAtEnd()) {
447
+ arr.push(this.parseValue());
448
+
449
+ // Optional comma
450
+ this.match(SONTokenType.COMMA);
451
+ }
452
+
453
+ this.expect(SONTokenType.RBRACKET, "Expected ']' to close array");
454
+ return arr;
455
+ }
456
+
457
+ // Helper methods
458
+ peek() {
459
+ return this.tokens[this.pos];
460
+ }
461
+
462
+ advance() {
463
+ if (!this.isAtEnd()) {
464
+ this.pos++;
465
+ }
466
+ return this.tokens[this.pos - 1];
467
+ }
468
+
469
+ check(type) {
470
+ return !this.isAtEnd() && this.peek().type === type;
471
+ }
472
+
473
+ match(type) {
474
+ if (this.check(type)) {
475
+ this.advance();
476
+ return true;
477
+ }
478
+ return false;
479
+ }
480
+
481
+ expect(type, message) {
482
+ if (this.check(type)) {
483
+ return this.advance();
484
+ }
485
+ const token = this.peek();
486
+ throw new SONParseError(
487
+ message,
488
+ this.filename,
489
+ token.line,
490
+ token.column
491
+ );
492
+ }
493
+
494
+ isAtEnd() {
495
+ return this.pos >= this.tokens.length || this.peek().type === SONTokenType.EOF;
496
+ }
497
+ }
498
+
499
+ /**
500
+ * Parse a .son file content into a JavaScript object
501
+ * @param {string} source - The .son file content
502
+ * @param {string} filename - Optional filename for error messages
503
+ * @returns {object} Parsed JavaScript object
504
+ */
505
+ export function parseSON(source, filename = '<son>') {
506
+ const lexer = new SONLexer(source, filename);
507
+ const tokens = lexer.tokenize();
508
+ const parser = new SONParser(tokens, filename);
509
+ return parser.parse();
510
+ }
511
+
512
+ /**
513
+ * Stringify a JavaScript object to .son format
514
+ * @param {object} obj - The object to stringify
515
+ * @param {number} indent - Indentation level (default 2)
516
+ * @returns {string} .son formatted string
517
+ */
518
+ export function stringifySON(obj, indent = 2) {
519
+ // Top-level objects are written as bare key = value pairs (no wrapping braces)
520
+ if (typeof obj === 'object' && obj !== null && !Array.isArray(obj)) {
521
+ const keys = Object.keys(obj);
522
+ if (keys.length === 0) return '';
523
+
524
+ const pairs = keys.map(key => {
525
+ const keyStr = /^[a-zA-Z_][a-zA-Z0-9_-]*$/.test(key) ? key : JSON.stringify(key);
526
+ return keyStr + ' = ' + stringifyValue(obj[key], 0, indent);
527
+ });
528
+
529
+ return pairs.join('\n') + '\n';
530
+ }
531
+
532
+ return stringifyValue(obj, 0, indent);
533
+ }
534
+
535
+ function stringifyValue(value, depth, indent) {
536
+ if (value === null || value === undefined) {
537
+ return 'none';
538
+ }
539
+
540
+ if (typeof value === 'boolean') {
541
+ return value ? 'true' : 'false';
542
+ }
543
+
544
+ if (typeof value === 'number') {
545
+ return String(value);
546
+ }
547
+
548
+ if (typeof value === 'string') {
549
+ // Use quotes if string contains special chars, otherwise can be unquoted for simple identifiers
550
+ if (/^[a-zA-Z_][a-zA-Z0-9_-]*$/.test(value) && !isKeyword(value)) {
551
+ return value;
552
+ }
553
+ return JSON.stringify(value);
554
+ }
555
+
556
+ if (Array.isArray(value)) {
557
+ if (value.length === 0) return '[]';
558
+
559
+ const spaces = ' '.repeat((depth + 1) * indent);
560
+ const closingSpaces = ' '.repeat(depth * indent);
561
+
562
+ const items = value.map(v => spaces + stringifyValue(v, depth + 1, indent));
563
+ return '[\n' + items.join(',\n') + '\n' + closingSpaces + ']';
564
+ }
565
+
566
+ if (typeof value === 'object') {
567
+ const keys = Object.keys(value);
568
+ if (keys.length === 0) return '{}';
569
+
570
+ const spaces = ' '.repeat((depth + 1) * indent);
571
+ const closingSpaces = ' '.repeat(depth * indent);
572
+
573
+ const pairs = keys.map(key => {
574
+ const keyStr = /^[a-zA-Z_][a-zA-Z0-9_-]*$/.test(key) ? key : JSON.stringify(key);
575
+ return spaces + keyStr + ' = ' + stringifyValue(value[key], depth + 1, indent);
576
+ });
577
+
578
+ return '{\n' + pairs.join(',\n') + '\n' + closingSpaces + '}';
579
+ }
580
+
581
+ return String(value);
582
+ }
583
+
584
+ function isKeyword(str) {
585
+ return str === 'true' || str === 'false' || str === 'none' || str === 'null';
586
+ }
587
+
588
+ export default {
589
+ parse: parseSON,
590
+ stringify: stringifySON,
591
+ SONParseError,
592
+ };