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,230 @@
1
+ /**
2
+ * FileSystemAdapter - Base class for Stone file system access
3
+ *
4
+ * Provides an abstraction layer between the Stone execution engine and
5
+ * the file system. Allows different implementations for:
6
+ * - Node.js (CLI) - uses actual filesystem
7
+ * - Web app - uses in-memory file map or API
8
+ * - Testing - uses mock filesystem
9
+ */
10
+ export class FileSystemAdapter {
11
+ /**
12
+ * Read file contents
13
+ * @param {string} path - File path
14
+ * @returns {Promise<string>} File contents
15
+ * @throws {Error} If file not found or read fails
16
+ */
17
+ async readFile(path) {
18
+ throw new Error('FileSystemAdapter.readFile() not implemented');
19
+ }
20
+
21
+ /**
22
+ * Write file contents
23
+ * @param {string} path - File path
24
+ * @param {string} content - Content to write
25
+ * @returns {Promise<void>}
26
+ * @throws {Error} If write fails
27
+ */
28
+ async writeFile(path, content) {
29
+ throw new Error('FileSystemAdapter.writeFile() not implemented');
30
+ }
31
+
32
+ /**
33
+ * Check if file exists
34
+ * @param {string} path - File path
35
+ * @returns {Promise<boolean>}
36
+ */
37
+ async exists(path) {
38
+ throw new Error('FileSystemAdapter.exists() not implemented');
39
+ }
40
+
41
+ /**
42
+ * List files in directory
43
+ * @param {string} path - Directory path
44
+ * @returns {Promise<string[]>} List of file names
45
+ * @throws {Error} If directory not found or read fails
46
+ */
47
+ async readDir(path) {
48
+ throw new Error('FileSystemAdapter.readDir() not implemented');
49
+ }
50
+
51
+ /**
52
+ * Resolve path relative to base
53
+ * @param {string} base - Base path
54
+ * @param {string} relative - Relative path
55
+ * @returns {string} Resolved absolute path
56
+ */
57
+ resolvePath(base, relative) {
58
+ throw new Error('FileSystemAdapter.resolvePath() not implemented');
59
+ }
60
+
61
+ /**
62
+ * Get directory name from path
63
+ * @param {string} path - File path
64
+ * @returns {string} Directory path
65
+ */
66
+ dirname(path) {
67
+ throw new Error('FileSystemAdapter.dirname() not implemented');
68
+ }
69
+
70
+ /**
71
+ * Get base name from path
72
+ * @param {string} path - File path
73
+ * @returns {string} File name
74
+ */
75
+ basename(path) {
76
+ throw new Error('FileSystemAdapter.basename() not implemented');
77
+ }
78
+
79
+ /**
80
+ * Join path segments
81
+ * @param {...string} segments - Path segments
82
+ * @returns {string} Joined path
83
+ */
84
+ join(...segments) {
85
+ throw new Error('FileSystemAdapter.join() not implemented');
86
+ }
87
+
88
+ /**
89
+ * Check if path is absolute
90
+ * @param {string} path - Path to check
91
+ * @returns {boolean}
92
+ */
93
+ isAbsolute(path) {
94
+ throw new Error('FileSystemAdapter.isAbsolute() not implemented');
95
+ }
96
+ }
97
+
98
+ /**
99
+ * MemoryFileSystemAdapter - In-memory file system for testing and web
100
+ *
101
+ * Uses a Map to store file contents. Useful for:
102
+ * - Unit testing without touching real filesystem
103
+ * - Web app where files are loaded from API/memory
104
+ */
105
+ export class MemoryFileSystemAdapter extends FileSystemAdapter {
106
+ constructor(files = {}) {
107
+ super();
108
+ // files is an object { path: content }
109
+ this.files = new Map(Object.entries(files));
110
+ }
111
+
112
+ async readFile(path) {
113
+ const normalizedPath = this._normalizePath(path);
114
+ if (!this.files.has(normalizedPath)) {
115
+ throw new Error(`File not found: ${path}`);
116
+ }
117
+ return this.files.get(normalizedPath);
118
+ }
119
+
120
+ async writeFile(path, content) {
121
+ const normalizedPath = this._normalizePath(path);
122
+ this.files.set(normalizedPath, content);
123
+ }
124
+
125
+ async exists(path) {
126
+ const normalizedPath = this._normalizePath(path);
127
+ return this.files.has(normalizedPath);
128
+ }
129
+
130
+ async readDir(path) {
131
+ const normalizedPath = this._normalizePath(path);
132
+ const prefix = normalizedPath.endsWith('/') ? normalizedPath : normalizedPath + '/';
133
+
134
+ const entries = new Set();
135
+ for (const filePath of this.files.keys()) {
136
+ if (filePath.startsWith(prefix)) {
137
+ const remaining = filePath.slice(prefix.length);
138
+ const firstPart = remaining.split('/')[0];
139
+ if (firstPart) {
140
+ entries.add(firstPart);
141
+ }
142
+ }
143
+ }
144
+ return Array.from(entries);
145
+ }
146
+
147
+ resolvePath(base, relative) {
148
+ if (this.isAbsolute(relative)) {
149
+ return this._normalizePath(relative);
150
+ }
151
+ const baseParts = base.split('/').filter(Boolean);
152
+ const relativeParts = relative.split('/').filter(Boolean);
153
+
154
+ // Remove filename from base if it doesn't end with /
155
+ if (base && !base.endsWith('/')) {
156
+ baseParts.pop();
157
+ }
158
+
159
+ for (const part of relativeParts) {
160
+ if (part === '..') {
161
+ baseParts.pop();
162
+ } else if (part !== '.') {
163
+ baseParts.push(part);
164
+ }
165
+ }
166
+
167
+ return '/' + baseParts.join('/');
168
+ }
169
+
170
+ dirname(path) {
171
+ const parts = path.split('/').filter(Boolean);
172
+ parts.pop();
173
+ return '/' + parts.join('/');
174
+ }
175
+
176
+ basename(path) {
177
+ const parts = path.split('/').filter(Boolean);
178
+ return parts[parts.length - 1] || '';
179
+ }
180
+
181
+ join(...segments) {
182
+ return this._normalizePath(segments.join('/'));
183
+ }
184
+
185
+ isAbsolute(path) {
186
+ return path.startsWith('/');
187
+ }
188
+
189
+ /**
190
+ * Add a file to the in-memory filesystem
191
+ * @param {string} path - File path
192
+ * @param {string} content - File content
193
+ */
194
+ addFile(path, content) {
195
+ this.files.set(this._normalizePath(path), content);
196
+ }
197
+
198
+ /**
199
+ * Remove a file from the in-memory filesystem
200
+ * @param {string} path - File path
201
+ */
202
+ removeFile(path) {
203
+ this.files.delete(this._normalizePath(path));
204
+ }
205
+
206
+ /**
207
+ * Clear all files
208
+ */
209
+ clear() {
210
+ this.files.clear();
211
+ }
212
+
213
+ _normalizePath(path) {
214
+ // Normalize path: remove duplicate slashes, resolve . and ..
215
+ const parts = path.split('/').filter(Boolean);
216
+ const result = [];
217
+
218
+ for (const part of parts) {
219
+ if (part === '..') {
220
+ result.pop();
221
+ } else if (part !== '.') {
222
+ result.push(part);
223
+ }
224
+ }
225
+
226
+ return '/' + result.join('/');
227
+ }
228
+ }
229
+
230
+ export default FileSystemAdapter;
@@ -0,0 +1,208 @@
1
+ /**
2
+ * OutputAdapter - Base class for Stone execution output handling
3
+ *
4
+ * Provides an abstraction layer between the Stone execution engine and
5
+ * the output destination (CLI terminal, web app React state, JSON stream, etc.)
6
+ *
7
+ * All methods are no-ops by default - subclasses override what they need.
8
+ */
9
+ export class OutputAdapter {
10
+ /**
11
+ * Called when print() builtin is invoked
12
+ * @param {string} text - Text to print
13
+ */
14
+ print(text) {
15
+ // Default: no-op
16
+ }
17
+
18
+ /**
19
+ * Called when a new terminal is created (graph2d, graph3d, console, etc.)
20
+ * @param {string} id - Unique terminal ID (e.g., 'graph2d_0')
21
+ * @param {string} type - Terminal type ('graph2d', 'graph3d', 'console')
22
+ * @param {Object} config - Terminal configuration (title, x_label, etc.)
23
+ */
24
+ createTerminal(id, type, config) {
25
+ // Default: no-op
26
+ }
27
+
28
+ /**
29
+ * Called when data is added to a terminal (e.g., graph.add(data))
30
+ * @param {string} id - Terminal ID
31
+ * @param {any} data - Data to add (format depends on terminal type)
32
+ */
33
+ addToTerminal(id, data) {
34
+ // Default: no-op
35
+ }
36
+
37
+ /**
38
+ * Called when terminal data is replaced (e.g., graph.set(data))
39
+ * @param {string} id - Terminal ID
40
+ * @param {any} data - New data (replaces existing)
41
+ */
42
+ setTerminal(id, data) {
43
+ // Default: no-op
44
+ }
45
+
46
+ /**
47
+ * Called when a terminal is cleared
48
+ * @param {string} id - Terminal ID
49
+ */
50
+ clearTerminal(id) {
51
+ // Default: no-op
52
+ }
53
+
54
+ /**
55
+ * Called when script execution finishes
56
+ * @param {boolean} success - Whether execution succeeded
57
+ * @param {any} result - Final result value (or error)
58
+ */
59
+ finish(success, result) {
60
+ // Default: no-op
61
+ }
62
+
63
+ /**
64
+ * Called when a type-checking or compilation error occurs
65
+ * @param {string} message - Error message
66
+ * @param {Object} location - Source location {line, column, file}
67
+ */
68
+ error(message, location) {
69
+ // Default: no-op
70
+ }
71
+
72
+ /**
73
+ * Called for progress updates during execution
74
+ * @param {Object} info - Progress information {phase, message, percent}
75
+ */
76
+ progress(info) {
77
+ // Default: no-op
78
+ }
79
+ }
80
+
81
+ /**
82
+ * BufferedOutputAdapter - Collects output in memory for batch return
83
+ *
84
+ * Used for backward compatibility with existing StoneEngineService API
85
+ * that returns all output at once after execution.
86
+ */
87
+ export class BufferedOutputAdapter extends OutputAdapter {
88
+ constructor() {
89
+ super();
90
+ this.output = [];
91
+ this.terminalsData = {};
92
+ this.errors = [];
93
+ this.result = null;
94
+ this.success = true;
95
+ this._debug = false;
96
+ }
97
+
98
+ print(text) {
99
+ if (this._debug) console.log('[BufferedAdapter] print:', text);
100
+ this.output.push(text);
101
+ }
102
+
103
+ createTerminal(id, type, config) {
104
+ if (this._debug) console.log('[BufferedAdapter] createTerminal:', id, type);
105
+ this.terminalsData[id] = {
106
+ type,
107
+ config,
108
+ plots: type === 'graph2d' ? [] : undefined,
109
+ objects: type === 'graph3d' ? [] : undefined,
110
+ lines: type === 'console' ? [] : undefined,
111
+ };
112
+ }
113
+
114
+ addToTerminal(id, data) {
115
+ if (this._debug) console.log('[BufferedAdapter] addToTerminal:', id, 'items:', Array.isArray(data) ? data.length : 1);
116
+ const terminal = this.terminalsData[id];
117
+ if (!terminal) {
118
+ if (this._debug) console.log('[BufferedAdapter] WARNING: terminal not found:', id);
119
+ return;
120
+ }
121
+
122
+ const items = Array.isArray(data) ? data : [data];
123
+
124
+ switch (terminal.type) {
125
+ case 'graph2d':
126
+ terminal.plots.push(...items);
127
+ break;
128
+ case 'graph3d':
129
+ terminal.objects.push(...items);
130
+ if (this._debug) console.log('[BufferedAdapter] graph3d objects count:', terminal.objects.length);
131
+ break;
132
+ case 'console':
133
+ terminal.lines.push(...items);
134
+ break;
135
+ }
136
+ }
137
+
138
+ setTerminal(id, data) {
139
+ const terminal = this.terminalsData[id];
140
+ if (!terminal) return;
141
+
142
+ const items = Array.isArray(data) ? data : [data];
143
+
144
+ switch (terminal.type) {
145
+ case 'graph2d':
146
+ terminal.plots = items;
147
+ break;
148
+ case 'graph3d':
149
+ terminal.objects = items;
150
+ break;
151
+ case 'console':
152
+ terminal.lines = items;
153
+ break;
154
+ }
155
+ }
156
+
157
+ clearTerminal(id) {
158
+ const terminal = this.terminalsData[id];
159
+ if (!terminal) return;
160
+
161
+ switch (terminal.type) {
162
+ case 'graph2d':
163
+ terminal.plots = [];
164
+ break;
165
+ case 'graph3d':
166
+ terminal.objects = [];
167
+ break;
168
+ case 'console':
169
+ terminal.lines = [];
170
+ break;
171
+ }
172
+ }
173
+
174
+ finish(success, result) {
175
+ this.success = success;
176
+ this.result = result;
177
+ }
178
+
179
+ error(message, location) {
180
+ this.errors.push({ message, location });
181
+ }
182
+
183
+ /**
184
+ * Get collected output for batch return
185
+ */
186
+ getOutput() {
187
+ return {
188
+ output: this.output,
189
+ terminalsData: this.terminalsData,
190
+ errors: this.errors,
191
+ result: this.result,
192
+ success: this.success,
193
+ };
194
+ }
195
+
196
+ /**
197
+ * Reset adapter state for reuse
198
+ */
199
+ reset() {
200
+ this.output = [];
201
+ this.terminalsData = {};
202
+ this.errors = [];
203
+ this.result = null;
204
+ this.success = true;
205
+ }
206
+ }
207
+
208
+ export default OutputAdapter;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Stone Adapters - Platform abstraction for output and file system
3
+ */
4
+
5
+ export { OutputAdapter, BufferedOutputAdapter } from './OutputAdapter.js';
6
+ export { FileSystemAdapter, MemoryFileSystemAdapter } from './FileSystemAdapter.js';
@@ -0,0 +1,196 @@
1
+ /**
2
+ * CLIOutputAdapter - Human-readable terminal output with colors
3
+ *
4
+ * Formats Stone execution output for the command line:
5
+ * - Colorized print output
6
+ * - Terminal updates (for console terminals)
7
+ * - Error messages with source locations
8
+ * - Progress indicators
9
+ *
10
+ * Graph terminals (graph2d, graph3d) trigger browser viewing
11
+ * instead of printing to console.
12
+ */
13
+
14
+ import { OutputAdapter } from '../adapters/OutputAdapter.js';
15
+
16
+ // ANSI color codes
17
+ const colors = {
18
+ reset: '\x1b[0m',
19
+ bold: '\x1b[1m',
20
+ dim: '\x1b[2m',
21
+
22
+ // Text colors
23
+ black: '\x1b[30m',
24
+ red: '\x1b[31m',
25
+ green: '\x1b[32m',
26
+ yellow: '\x1b[33m',
27
+ blue: '\x1b[34m',
28
+ magenta: '\x1b[35m',
29
+ cyan: '\x1b[36m',
30
+ white: '\x1b[37m',
31
+
32
+ // Bright colors
33
+ brightRed: '\x1b[91m',
34
+ brightGreen: '\x1b[92m',
35
+ brightYellow: '\x1b[93m',
36
+ brightBlue: '\x1b[94m',
37
+ brightMagenta: '\x1b[95m',
38
+ brightCyan: '\x1b[96m',
39
+ };
40
+
41
+ const c = (color, text) => `${colors[color]}${text}${colors.reset}`;
42
+
43
+ /**
44
+ * CLIOutputAdapter - Formats output for terminal display
45
+ */
46
+ export class CLIOutputAdapter extends OutputAdapter {
47
+ constructor(options = {}) {
48
+ super();
49
+ this.colorize = options.colorize !== false;
50
+ this.verbose = options.verbose || false;
51
+ this.quiet = options.quiet || false;
52
+ this.stream = options.stream || process.stdout;
53
+ this.errorStream = options.errorStream || process.stderr;
54
+
55
+ // Track terminals for graph viewing
56
+ this.terminals = {};
57
+ this.hasGraphTerminals = false;
58
+
59
+ // Callback for when graphs are created
60
+ this.onGraphCreated = options.onGraphCreated || null;
61
+ }
62
+
63
+ _write(text) {
64
+ if (!this.quiet) {
65
+ this.stream.write(text);
66
+ }
67
+ }
68
+
69
+ _writeError(text) {
70
+ this.errorStream.write(text);
71
+ }
72
+
73
+ _color(color, text) {
74
+ return this.colorize ? c(color, text) : text;
75
+ }
76
+
77
+ print(text) {
78
+ this._write(text + '\n');
79
+ }
80
+
81
+ createTerminal(id, type, config) {
82
+ this.terminals[id] = {
83
+ type,
84
+ config,
85
+ data: [],
86
+ };
87
+
88
+ if (type === 'graph2d' || type === 'graph3d') {
89
+ this.hasGraphTerminals = true;
90
+
91
+ if (this.verbose) {
92
+ this._write(this._color('dim', `[Created ${type} terminal: ${config.title || id}]\n`));
93
+ }
94
+
95
+ // Notify about graph creation
96
+ if (this.onGraphCreated) {
97
+ this.onGraphCreated(id, type, config);
98
+ }
99
+ }
100
+ }
101
+
102
+ addToTerminal(id, data) {
103
+ const terminal = this.terminals[id];
104
+ if (!terminal) return;
105
+
106
+ const items = Array.isArray(data) ? data : [data];
107
+ terminal.data.push(...items);
108
+
109
+ // For console terminals, print lines
110
+ if (terminal.type === 'console') {
111
+ for (const item of items) {
112
+ this.print(String(item));
113
+ }
114
+ }
115
+ }
116
+
117
+ setTerminal(id, data) {
118
+ const terminal = this.terminals[id];
119
+ if (!terminal) return;
120
+
121
+ const items = Array.isArray(data) ? data : [data];
122
+ terminal.data = items;
123
+
124
+ // For console terminals, re-print all lines
125
+ // (This is mainly for interactive/REPL use)
126
+ if (terminal.type === 'console') {
127
+ for (const item of items) {
128
+ this.print(String(item));
129
+ }
130
+ }
131
+ }
132
+
133
+ clearTerminal(id) {
134
+ const terminal = this.terminals[id];
135
+ if (terminal) {
136
+ terminal.data = [];
137
+ }
138
+ }
139
+
140
+ error(message, location) {
141
+ let output = this._color('brightRed', 'Error: ');
142
+
143
+ if (location) {
144
+ const loc = `${location.file || '<script>'}:${location.line}:${location.column}`;
145
+ output += this._color('cyan', loc) + ' ';
146
+ }
147
+
148
+ output += message + '\n';
149
+ this._writeError(output);
150
+ }
151
+
152
+ finish(success, result) {
153
+ if (this.verbose) {
154
+ if (success) {
155
+ this._write(this._color('green', '\n✓ Execution completed successfully\n'));
156
+ if (result !== null && result !== undefined) {
157
+ this._write(this._color('dim', `Result: ${JSON.stringify(result)}\n`));
158
+ }
159
+ } else {
160
+ this._write(this._color('red', '\n✗ Execution failed\n'));
161
+ }
162
+ }
163
+ }
164
+
165
+ progress(info) {
166
+ if (this.verbose && !this.quiet) {
167
+ const { phase, message, percent } = info;
168
+ let output = this._color('dim', '[');
169
+
170
+ if (phase) output += phase;
171
+ if (message) output += ': ' + message;
172
+ if (percent !== undefined) output += ` (${percent}%)`;
173
+
174
+ output += this._color('dim', ']\n');
175
+ this._write(output);
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Get all terminal data (for graph viewing)
181
+ * @returns {Object} Terminal data by ID
182
+ */
183
+ getTerminals() {
184
+ return this.terminals;
185
+ }
186
+
187
+ /**
188
+ * Check if there are any graph terminals
189
+ * @returns {boolean}
190
+ */
191
+ hasGraphs() {
192
+ return this.hasGraphTerminals;
193
+ }
194
+ }
195
+
196
+ export default CLIOutputAdapter;