strata-lang 2.7.5
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/LICENSE +339 -0
- package/README.md +968 -0
- package/bun.lock +283 -0
- package/dist/index.js +1866 -0
- package/package.json +50 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1866 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// STRATA ARCHITECTURE & MODULE SYSTEM SPECIFICATION
|
|
3
|
+
// ============================================================================
|
|
4
|
+
//
|
|
5
|
+
// This file documents the core architecture design for Strata, including:
|
|
6
|
+
// - Module & Import System
|
|
7
|
+
// - Standard Library Boundary
|
|
8
|
+
// - Deterministic Builds
|
|
9
|
+
// - Package Manager Integration
|
|
10
|
+
//
|
|
11
|
+
// ============================================================================
|
|
12
|
+
// 1. MODULE & IMPORT SYSTEM
|
|
13
|
+
// ============================================================================
|
|
14
|
+
//
|
|
15
|
+
// DESIGN PRINCIPLES:
|
|
16
|
+
// - Explicit over implicit: all imports must be explicit, no implicit exports
|
|
17
|
+
// - File-based modules: each .str file is a module with a single public namespace
|
|
18
|
+
// - Deterministic resolution: no magic path resolution or environment variables
|
|
19
|
+
// - Portable: works identically on Windows, macOS, Linux
|
|
20
|
+
// - Compiler-agnostic: same import semantics for interpreter and C/JS generators
|
|
21
|
+
//
|
|
22
|
+
// IMPORT SYNTAX:
|
|
23
|
+
//
|
|
24
|
+
// import <name> from <module_path>
|
|
25
|
+
// import <name> from <"relative_path">
|
|
26
|
+
// import <name> from <"absolute_path">
|
|
27
|
+
//
|
|
28
|
+
// Examples:
|
|
29
|
+
//
|
|
30
|
+
// import io from std::io // Standard library (always available)
|
|
31
|
+
// import math from std::math // Standard library math
|
|
32
|
+
// import util from ./util // Relative path (sibling)
|
|
33
|
+
// import config from ../config // Parent directory
|
|
34
|
+
// import http from myapp::http // Package: myapp/modules/http.str
|
|
35
|
+
//
|
|
36
|
+
// MODULE PATH RESOLUTION:
|
|
37
|
+
//
|
|
38
|
+
// 1. STANDARD LIBRARY (std::*)
|
|
39
|
+
// - Always available, shipped with Strata compiler
|
|
40
|
+
// - Location: <compiler_root>/stdlib/
|
|
41
|
+
// - Files: std::io → stdlib/io.str, std::math → stdlib/math.str
|
|
42
|
+
// - Immutable, pinned to compiler version
|
|
43
|
+
// - Cannot be overridden by user code
|
|
44
|
+
//
|
|
45
|
+
// 2. ABSOLUTE PATHS (<"path">)
|
|
46
|
+
// - Explicit file-system paths relative to project root
|
|
47
|
+
// - Format: <"./file">, <"../parent/file">, <"/absolute/to/root">
|
|
48
|
+
// - Always interpreted relative to strata.toml location
|
|
49
|
+
// - Extension (.str) optional but recommended for clarity
|
|
50
|
+
//
|
|
51
|
+
// 3. RELATIVE PATHS (./path)
|
|
52
|
+
// - Relative to the importing file's directory
|
|
53
|
+
// - Format: ./sibling.str, ../parent.str, ../../ancestor.str
|
|
54
|
+
// - Must start with ./ or ../
|
|
55
|
+
// - No bare imports (no "util" without package context)
|
|
56
|
+
//
|
|
57
|
+
// 4. PACKAGE IMPORTS (package::module)
|
|
58
|
+
// - Format: <package_name>::<module_path>
|
|
59
|
+
// - Maps to: <project_root>/.strata/packages/<package_name>/<module_path>.str
|
|
60
|
+
// - Resolved via strata.lock for exact versions
|
|
61
|
+
// - Cannot shadow stdlib
|
|
62
|
+
//
|
|
63
|
+
// MODULE DEFINITION:
|
|
64
|
+
//
|
|
65
|
+
// A module is a .str file that exports a namespace. All top-level bindings
|
|
66
|
+
// (functions, types, constants) are part of the module's public API.
|
|
67
|
+
//
|
|
68
|
+
// Example module: myapp/util.str
|
|
69
|
+
//
|
|
70
|
+
// func add(a: int, b: int) => int {
|
|
71
|
+
// return a + b
|
|
72
|
+
// }
|
|
73
|
+
//
|
|
74
|
+
// const VERSION: string = "1.0"
|
|
75
|
+
//
|
|
76
|
+
// Usage:
|
|
77
|
+
//
|
|
78
|
+
// import util from myapp::util
|
|
79
|
+
// let x: int = util.add(1, 2) // Access via namespace
|
|
80
|
+
// io.print(util.VERSION)
|
|
81
|
+
//
|
|
82
|
+
// PRIVATE (INTERNAL) MODULES:
|
|
83
|
+
//
|
|
84
|
+
// Module names starting with _ are internal and should not be imported by
|
|
85
|
+
// external packages. Example: myapp::_internal
|
|
86
|
+
// This is a convention, not enforced by the compiler (for simplicity).
|
|
87
|
+
//
|
|
88
|
+
// IMPORT BEHAVIOR:
|
|
89
|
+
//
|
|
90
|
+
// - Imports are evaluated at compile time (static)
|
|
91
|
+
// - Circular imports are errors (detected during compilation)
|
|
92
|
+
// - Each module is evaluated exactly once per compilation
|
|
93
|
+
// - Import creates a namespace binding in the importing module's scope
|
|
94
|
+
// - Imported names do NOT leak to code that imports the importing module
|
|
95
|
+
// (i.e., imports are not transitive)
|
|
96
|
+
//
|
|
97
|
+
// ============================================================================
|
|
98
|
+
// 2. STANDARD LIBRARY BOUNDARY
|
|
99
|
+
// ============================================================================
|
|
100
|
+
//
|
|
101
|
+
// STDLIB MODULES:
|
|
102
|
+
//
|
|
103
|
+
// The standard library is the minimal set of modules shipped with Strata.
|
|
104
|
+
// All stdlib imports use the std:: namespace prefix.
|
|
105
|
+
//
|
|
106
|
+
// Current stdlib (v1.0):
|
|
107
|
+
//
|
|
108
|
+
// std::io → input/output, print, read, file operations
|
|
109
|
+
// std::math → arithmetic functions, sin, cos, sqrt, etc.
|
|
110
|
+
// std::text → string operations, split, join, trim
|
|
111
|
+
// std::util → misc utilities (later expansion point)
|
|
112
|
+
// std::time → time/date operations (future)
|
|
113
|
+
//
|
|
114
|
+
// Each stdlib module is versioned with the compiler.
|
|
115
|
+
// Compiler version 1.5.2 includes stdlib version 1.5.2 (locked together).
|
|
116
|
+
//
|
|
117
|
+
// PREVENTION OF SHADOWING:
|
|
118
|
+
//
|
|
119
|
+
// Strata's import system prevents accidental shadowing:
|
|
120
|
+
//
|
|
121
|
+
// 1. User code cannot create a module named "std" (reserved)
|
|
122
|
+
// 2. Import resolution checks stdlib first
|
|
123
|
+
// 3. If a file is imported via both std:: and package::, compilation error
|
|
124
|
+
// 4. No $ variables or _ prefixes that could collide with stdlib internals
|
|
125
|
+
//
|
|
126
|
+
// EXTERNAL PACKAGES:
|
|
127
|
+
//
|
|
128
|
+
// Packages are modules not in stdlib. They must:
|
|
129
|
+
// - Be declared in strata.toml
|
|
130
|
+
// - Be explicitly versioned in strata.lock
|
|
131
|
+
// - Be imported via package::module syntax
|
|
132
|
+
// - Cannot use std:: namespace
|
|
133
|
+
// - Cannot override stdlib modules
|
|
134
|
+
//
|
|
135
|
+
// ============================================================================
|
|
136
|
+
// 3. DETERMINISTIC BUILDS
|
|
137
|
+
// ============================================================================
|
|
138
|
+
//
|
|
139
|
+
// REPRODUCIBILITY GUARANTEES:
|
|
140
|
+
//
|
|
141
|
+
// The same source code compiled with the same environment produces
|
|
142
|
+
// identical output across machines.
|
|
143
|
+
//
|
|
144
|
+
// Components that affect output:
|
|
145
|
+
// 1. Compiler version (strata 1.5.2)
|
|
146
|
+
// 2. Stdlib version (locked to compiler version)
|
|
147
|
+
// 3. Package versions (locked in strata.lock)
|
|
148
|
+
// 4. Source code (of course)
|
|
149
|
+
// 5. Target platform (x86-64-linux, arm64-macos, x86-64-windows)
|
|
150
|
+
// 6. Optimization flags (specified in strata.toml)
|
|
151
|
+
//
|
|
152
|
+
// Components that do NOT affect determinism:
|
|
153
|
+
// - File system order (all files processed in sorted order)
|
|
154
|
+
// - Build machine (Windows, macOS, Linux produce identical bytecode)
|
|
155
|
+
// - System environment variables
|
|
156
|
+
// - System time
|
|
157
|
+
// - Cache state (rebuild from scratch = same result)
|
|
158
|
+
//
|
|
159
|
+
// STRATA.TOML (Project Configuration):
|
|
160
|
+
//
|
|
161
|
+
// [project]
|
|
162
|
+
// name = "my-app"
|
|
163
|
+
// version = "1.0.0"
|
|
164
|
+
// strata = "1.5.2" # Compiler version (exact)
|
|
165
|
+
//
|
|
166
|
+
// [build]
|
|
167
|
+
// target = "c" # or "js", "bytecode"
|
|
168
|
+
// optimization = "O2" # or "O0", "O1", "O3"
|
|
169
|
+
// output = "./dist/my-app"
|
|
170
|
+
//
|
|
171
|
+
// [dependencies]
|
|
172
|
+
// http = "1.2.0" # Package name = version
|
|
173
|
+
// crypto = ">=2.0.0,<3.0.0" # Version range (resolved at lock-time)
|
|
174
|
+
// utils = { path = "./vendor/utils" } # Local package (development)
|
|
175
|
+
// git-lib = "git+https://github.com/org/lib#v1.2.0" # Git dependency
|
|
176
|
+
//
|
|
177
|
+
// [[warnings]]
|
|
178
|
+
// level = "strict" # or "warn", "allow"
|
|
179
|
+
//
|
|
180
|
+
// STRATA.LOCK (Dependency Lock File):
|
|
181
|
+
//
|
|
182
|
+
// [metadata]
|
|
183
|
+
// strata = "1.5.2" # Compiler version that created lock
|
|
184
|
+
// generated = 2024-01-15T10:30:00Z
|
|
185
|
+
//
|
|
186
|
+
// [[packages]]
|
|
187
|
+
// name = "http"
|
|
188
|
+
// requested = "1.2.0"
|
|
189
|
+
// resolved = "1.2.0"
|
|
190
|
+
// source = "registry" # or "git", "path"
|
|
191
|
+
// hash = "sha256:abc123..." # Content hash for verification
|
|
192
|
+
//
|
|
193
|
+
// [[packages]]
|
|
194
|
+
// name = "crypto"
|
|
195
|
+
// requested = ">=2.0.0,<3.0.0"
|
|
196
|
+
// resolved = "2.5.1"
|
|
197
|
+
// source = "registry"
|
|
198
|
+
// hash = "sha256:def456..."
|
|
199
|
+
//
|
|
200
|
+
// [[packages]]
|
|
201
|
+
// name = "utils"
|
|
202
|
+
// requested = "path:./vendor/utils"
|
|
203
|
+
// resolved = "./vendor/utils"
|
|
204
|
+
// source = "path"
|
|
205
|
+
//
|
|
206
|
+
// COMPILER VERSION PINNING:
|
|
207
|
+
//
|
|
208
|
+
// If strata.toml specifies strata = "1.5.2", only that version may be used.
|
|
209
|
+
// Attempting to compile with 1.5.3 or 1.6.0 fails with clear error:
|
|
210
|
+
//
|
|
211
|
+
// Error: Project requires Strata 1.5.2, but you have 1.6.0
|
|
212
|
+
// Use: strata update-compiler 1.5.2
|
|
213
|
+
//
|
|
214
|
+
// This prevents subtle differences in compiler behavior from silently
|
|
215
|
+
// affecting builds.
|
|
216
|
+
//
|
|
217
|
+
// ============================================================================
|
|
218
|
+
// 4. PACKAGE MANAGER DESIGN
|
|
219
|
+
// ============================================================================
|
|
220
|
+
//
|
|
221
|
+
// PHILOSOPHY:
|
|
222
|
+
// - Explicit over implicit (no magic)
|
|
223
|
+
// - Deterministic (same input → same output)
|
|
224
|
+
// - Offline-first (local cache, no network unless needed)
|
|
225
|
+
// - Simple (avoid npm-style complexity)
|
|
226
|
+
// - Future-proof (allow centralized registry, but don't require it)
|
|
227
|
+
//
|
|
228
|
+
// PACKAGE MANAGER COMMANDS:
|
|
229
|
+
//
|
|
230
|
+
// strata init # Create new project (generates strata.toml)
|
|
231
|
+
// strata build # Compile to target (uses strata.lock)
|
|
232
|
+
// strata run [args...] # Compile and execute
|
|
233
|
+
// strata add <package> [version] # Add dependency (updates strata.toml + lock)
|
|
234
|
+
// strata remove <package> # Remove dependency
|
|
235
|
+
// strata update [package] # Update packages to latest allowed version
|
|
236
|
+
// strata lock # Regenerate strata.lock from strata.toml
|
|
237
|
+
// strata verify # Verify all packages match lock file hashes
|
|
238
|
+
// strata doctor # Diagnose environment and dependencies
|
|
239
|
+
// strata publish # (Future) publish to registry
|
|
240
|
+
//
|
|
241
|
+
// PACKAGE STORAGE:
|
|
242
|
+
//
|
|
243
|
+
// All packages stored in:
|
|
244
|
+
// <project_root>/.strata/packages/<package_name>/
|
|
245
|
+
//
|
|
246
|
+
// Lock file location:
|
|
247
|
+
// <project_root>/strata.lock
|
|
248
|
+
//
|
|
249
|
+
// Cache location:
|
|
250
|
+
// <home>/.strata/cache/
|
|
251
|
+
// registry/ # Downloaded from registry
|
|
252
|
+
// git/ # Cloned from git
|
|
253
|
+
//
|
|
254
|
+
// PROJECT LAYOUT EXAMPLE:
|
|
255
|
+
//
|
|
256
|
+
// my-app/
|
|
257
|
+
// ├── strata.toml # Project manifest
|
|
258
|
+
// ├── strata.lock # Locked dependencies
|
|
259
|
+
// ├── .gitignore # Excludes .strata/ and dist/
|
|
260
|
+
// ├── src/
|
|
261
|
+
// │ ├── main.str
|
|
262
|
+
// │ ├── util.str
|
|
263
|
+
// │ └── _internal.str # Private module
|
|
264
|
+
// ├── .strata/ # Generated by package manager (gitignored)
|
|
265
|
+
// │ ├── packages/
|
|
266
|
+
// │ │ ├── http/
|
|
267
|
+
// │ │ │ ├── client.str
|
|
268
|
+
// │ │ │ ├── server.str
|
|
269
|
+
// │ │ │ └── _utils.str
|
|
270
|
+
// │ │ └── crypto/
|
|
271
|
+
// │ │ └── aes.str
|
|
272
|
+
// │ └── metadata/ # Package metadata, not needed at runtime
|
|
273
|
+
// └── dist/
|
|
274
|
+
// └── my-app.c # Generated C code
|
|
275
|
+
//
|
|
276
|
+
// DEPENDENCY RESOLUTION:
|
|
277
|
+
//
|
|
278
|
+
// 1. User runs: strata add http 1.2.0
|
|
279
|
+
// 2. strata checks cache, finds http@1.2.0, downloads if needed
|
|
280
|
+
// 3. strata verifies hash matches (if in lock file)
|
|
281
|
+
// 4. strata extracts to .strata/packages/http/
|
|
282
|
+
// 5. strata.toml updated: http = "1.2.0"
|
|
283
|
+
// 6. strata.lock updated with exact version and hash
|
|
284
|
+
// 7. No script execution, no post-install hooks
|
|
285
|
+
//
|
|
286
|
+
// VERSION RESOLUTION:
|
|
287
|
+
//
|
|
288
|
+
// When strata.toml has: http = ">=1.2.0,<2.0.0"
|
|
289
|
+
// strata lock command finds highest version matching range in registry
|
|
290
|
+
// and locks it in strata.lock.
|
|
291
|
+
// Next developer runs strata build → uses exact version from lock.
|
|
292
|
+
//
|
|
293
|
+
// OFFLINE MODE:
|
|
294
|
+
//
|
|
295
|
+
// If a package is in cache and hash matches lock file, use it.
|
|
296
|
+
// If not in cache or hash mismatch:
|
|
297
|
+
// - If offline flag set: error (fail fast)
|
|
298
|
+
// - If online: fetch and verify
|
|
299
|
+
//
|
|
300
|
+
// strata build --offline # Only use cached packages
|
|
301
|
+
// strata build # Fetch if needed
|
|
302
|
+
//
|
|
303
|
+
// GIT DEPENDENCIES:
|
|
304
|
+
//
|
|
305
|
+
// strata.toml:
|
|
306
|
+
// git-lib = "git+https://github.com/org/lib#v1.2.0"
|
|
307
|
+
//
|
|
308
|
+
// strata.lock resolves to:
|
|
309
|
+
// resolved = "git+https://github.com/org/lib@abc123def456..."
|
|
310
|
+
// source = "git"
|
|
311
|
+
// ref = "v1.2.0" # Original tag/branch
|
|
312
|
+
// commit = "abc123def456" # Resolved commit hash
|
|
313
|
+
//
|
|
314
|
+
// Path dependencies (for development):
|
|
315
|
+
//
|
|
316
|
+
// strata.toml:
|
|
317
|
+
// my-utils = { path = "./vendor/my-utils" }
|
|
318
|
+
//
|
|
319
|
+
// strata.lock:
|
|
320
|
+
// source = "path"
|
|
321
|
+
// resolved = "./vendor/my-utils"
|
|
322
|
+
//
|
|
323
|
+
// Path deps are NOT versioned but DO include hash for change detection.
|
|
324
|
+
//
|
|
325
|
+
// REGISTRY INTERFACE (Future):
|
|
326
|
+
//
|
|
327
|
+
// When a centralized registry exists, it must support:
|
|
328
|
+
//
|
|
329
|
+
// GET /api/v1/package/<name>/<version>
|
|
330
|
+
// Returns: { name, version, hash, size, dependencies, ... }
|
|
331
|
+
//
|
|
332
|
+
// GET /api/v1/package/<name>/latest
|
|
333
|
+
// Returns: latest version metadata
|
|
334
|
+
//
|
|
335
|
+
// GET /api/v1/search?q=<term>
|
|
336
|
+
// Returns: matching packages
|
|
337
|
+
//
|
|
338
|
+
// Authentication: API keys in ~/.strata/credentials (not in repo)
|
|
339
|
+
//
|
|
340
|
+
// SECURITY CONSIDERATIONS:
|
|
341
|
+
//
|
|
342
|
+
// 1. Hash verification: All packages verified against strata.lock hash
|
|
343
|
+
// 2. No code execution: Package manager never runs user code
|
|
344
|
+
// 3. Lockfile in git: strata.lock MUST be committed to version control
|
|
345
|
+
// 4. Isolation: Each package in isolated directory, cannot modify others
|
|
346
|
+
// 5. No transitive trust: Package B cannot modify what Package A sees
|
|
347
|
+
//
|
|
348
|
+
// EDGE CASES:
|
|
349
|
+
//
|
|
350
|
+
// Q: What if two packages export conflicting names?
|
|
351
|
+
// A: Each is namespaced: import http from pkg1::http, import text from pkg2::text
|
|
352
|
+
//
|
|
353
|
+
// Q: What if a package depends on another package?
|
|
354
|
+
// A: Not allowed in v1.0 (flat dependency model). Packages are self-contained.
|
|
355
|
+
// Future: transitive dependencies with flattening algorithm.
|
|
356
|
+
//
|
|
357
|
+
// Q: What if strata.lock gets corrupted?
|
|
358
|
+
// A: strata verify reports issues. strata lock --force regenerates.
|
|
359
|
+
//
|
|
360
|
+
// Q: Offline development without registry?
|
|
361
|
+
// A: Use path dependencies (point to local directories). Works without registry.
|
|
362
|
+
//
|
|
363
|
+
// ============================================================================
|
|
364
|
+
// 5. IMPORT EXAMPLES IN PRACTICE
|
|
365
|
+
// ============================================================================
|
|
366
|
+
//
|
|
367
|
+
// Project: web-server
|
|
368
|
+
// strata.toml:
|
|
369
|
+
// [project]
|
|
370
|
+
// name = "web-server"
|
|
371
|
+
// version = "2.1.0"
|
|
372
|
+
// strata = "1.5.2"
|
|
373
|
+
//
|
|
374
|
+
// [dependencies]
|
|
375
|
+
// http = "1.2.0"
|
|
376
|
+
// crypto = ">=2.0.0,<3.0.0"
|
|
377
|
+
//
|
|
378
|
+
// Directory structure:
|
|
379
|
+
// web-server/
|
|
380
|
+
// ├── strata.toml
|
|
381
|
+
// ├── strata.lock
|
|
382
|
+
// ├── src/
|
|
383
|
+
// │ ├── main.str
|
|
384
|
+
// │ ├── handlers.str
|
|
385
|
+
// │ └── _crypto_utils.str
|
|
386
|
+
// └── .strata/packages/
|
|
387
|
+
// ├── http/
|
|
388
|
+
// │ ├── client.str
|
|
389
|
+
// │ ├── server.str
|
|
390
|
+
// │ └── _utils.str
|
|
391
|
+
// └── crypto/
|
|
392
|
+
// └── aes.str
|
|
393
|
+
//
|
|
394
|
+
// main.str:
|
|
395
|
+
//
|
|
396
|
+
// import io from std::io
|
|
397
|
+
// import math from std::math
|
|
398
|
+
// import server from http::server # From http package
|
|
399
|
+
// import aes from crypto::aes # From crypto package
|
|
400
|
+
// import handlers from ./handlers # Relative import
|
|
401
|
+
//
|
|
402
|
+
// func main() => int {
|
|
403
|
+
// let port: int = 8080
|
|
404
|
+
// server.listen(port)
|
|
405
|
+
// return 0
|
|
406
|
+
// }
|
|
407
|
+
//
|
|
408
|
+
// handlers.str:
|
|
409
|
+
//
|
|
410
|
+
// import io from std::io
|
|
411
|
+
// import utils from http::_utils # Can import private modules
|
|
412
|
+
// import crypto from ./_crypto_utils # Local private module
|
|
413
|
+
//
|
|
414
|
+
// func handleRequest(path: string) => string {
|
|
415
|
+
// io.print(path)
|
|
416
|
+
// return "OK"
|
|
417
|
+
// }
|
|
418
|
+
//
|
|
419
|
+
// ============================================================================
|
|
420
|
+
// 6. COMPILER INTEGRATION
|
|
421
|
+
// ============================================================================
|
|
422
|
+
//
|
|
423
|
+
// INTERPRETER (strata run main.str):
|
|
424
|
+
// 1. Read strata.toml and strata.lock
|
|
425
|
+
// 2. Load stdlib modules (std::*)
|
|
426
|
+
// 3. Load package modules from .strata/packages/
|
|
427
|
+
// 4. Parse and type-check main.str (resolves imports)
|
|
428
|
+
// 5. Execute AST in interpreter
|
|
429
|
+
//
|
|
430
|
+
// COMPILER TO C (strata build --target c):
|
|
431
|
+
// 1. Same as interpreter but generates C code instead of interpreting
|
|
432
|
+
// 2. Include all referenced modules in output
|
|
433
|
+
// 3. Generate C with proper namespacing to avoid symbol collisions
|
|
434
|
+
// 4. Output: dist/my-app.c
|
|
435
|
+
//
|
|
436
|
+
// COMPILER TO JS (strata build --target js):
|
|
437
|
+
// 1. Generate JavaScript modules
|
|
438
|
+
// 2. Output: dist/my-app.js
|
|
439
|
+
// 3. Each Strata module → JavaScript module with namespace
|
|
440
|
+
//
|
|
441
|
+
// ============================================================================
|
|
442
|
+
// 7. DESIGN RATIONALE
|
|
443
|
+
// ============================================================================
|
|
444
|
+
//
|
|
445
|
+
// WHY explicit imports over implicit?
|
|
446
|
+
// → Avoids magic, makes dependencies visible, easier to understand code
|
|
447
|
+
// → Enables dead code elimination and dependency tracking
|
|
448
|
+
//
|
|
449
|
+
// WHY file-based modules?
|
|
450
|
+
// → Simple, no complex registry or export syntax
|
|
451
|
+
// → 1 file = 1 module = 1 namespace = easy mental model
|
|
452
|
+
// → Matches most developers' expectations
|
|
453
|
+
//
|
|
454
|
+
// WHY no bare imports?
|
|
455
|
+
// → "import util" is ambiguous (local? stdlib? package?)
|
|
456
|
+
// → Force explicit: ./util (relative) or pkg::util (package)
|
|
457
|
+
// → Prevents mistakes, makes refactoring safe
|
|
458
|
+
//
|
|
459
|
+
// WHY strata.toml + strata.lock?
|
|
460
|
+
// → Similar to Cargo.toml + Cargo.lock, proven model
|
|
461
|
+
// → Separates intent (toml) from reality (lock)
|
|
462
|
+
// → Lock file enables reproducible builds
|
|
463
|
+
// → Both committed to git for team consistency
|
|
464
|
+
//
|
|
465
|
+
// WHY no transitive dependencies in v1.0?
|
|
466
|
+
// → Eliminates dependency hell and version conflicts
|
|
467
|
+
// → Simpler resolver, easier to understand
|
|
468
|
+
// → Can upgrade to transitive later with flattening
|
|
469
|
+
//
|
|
470
|
+
// WHY hash verification?
|
|
471
|
+
// → Detects tampering, corrupted downloads, cache invalidation
|
|
472
|
+
// → Essential for security and reliability
|
|
473
|
+
//
|
|
474
|
+
// WHY no post-install scripts?
|
|
475
|
+
// → Unsafe (arbitrary code execution from untrusted sources)
|
|
476
|
+
// → Slow (requires toolchain to be installed)
|
|
477
|
+
// → Unpredictable (system-dependent)
|
|
478
|
+
// → Instead: packages must be pre-built, fully portable
|
|
479
|
+
//
|
|
480
|
+
// ============================================================================
|
|
481
|
+
import * as fs from "fs";
|
|
482
|
+
import * as process from "process";
|
|
483
|
+
const TYPE_REGISTRY = {
|
|
484
|
+
// Core Primitive Types
|
|
485
|
+
int: { kind: "primitive", primitive: "int" },
|
|
486
|
+
float: { kind: "primitive", primitive: "float" },
|
|
487
|
+
bool: { kind: "primitive", primitive: "bool" },
|
|
488
|
+
char: { kind: "primitive", primitive: "char" },
|
|
489
|
+
string: { kind: "primitive", primitive: "string" },
|
|
490
|
+
any: { kind: "primitive", primitive: "any" },
|
|
491
|
+
// Rust-style Signed Integers
|
|
492
|
+
i8: { kind: "primitive", primitive: "i8" },
|
|
493
|
+
i16: { kind: "primitive", primitive: "i16" },
|
|
494
|
+
i32: { kind: "primitive", primitive: "i32" },
|
|
495
|
+
i64: { kind: "primitive", primitive: "i64" },
|
|
496
|
+
// Rust-style Unsigned Integers
|
|
497
|
+
u8: { kind: "primitive", primitive: "u8" },
|
|
498
|
+
u16: { kind: "primitive", primitive: "u16" },
|
|
499
|
+
u32: { kind: "primitive", primitive: "u32" },
|
|
500
|
+
u64: { kind: "primitive", primitive: "u64" },
|
|
501
|
+
// Floating Point Types
|
|
502
|
+
f32: { kind: "primitive", primitive: "f32" },
|
|
503
|
+
f64: { kind: "primitive", primitive: "f64" },
|
|
504
|
+
// Collection Types (Python, Go, Ruby, JavaScript)
|
|
505
|
+
array: { kind: "primitive", primitive: "array" },
|
|
506
|
+
list: { kind: "primitive", primitive: "list" },
|
|
507
|
+
map: { kind: "primitive", primitive: "map" },
|
|
508
|
+
dict: { kind: "primitive", primitive: "dict" },
|
|
509
|
+
set: { kind: "primitive", primitive: "set" },
|
|
510
|
+
tuple: { kind: "primitive", primitive: "tuple" },
|
|
511
|
+
// Advanced Types (TypeScript, Rust, Go)
|
|
512
|
+
option: { kind: "primitive", primitive: "option" },
|
|
513
|
+
result: { kind: "primitive", primitive: "result" },
|
|
514
|
+
promise: { kind: "primitive", primitive: "promise" },
|
|
515
|
+
void: { kind: "primitive", primitive: "void" },
|
|
516
|
+
null: { kind: "primitive", primitive: "null" },
|
|
517
|
+
undefined: { kind: "primitive", primitive: "undefined" },
|
|
518
|
+
// Regular Expression Support (Python, Ruby, JavaScript, Go)
|
|
519
|
+
regex: { kind: "primitive", primitive: "regex" },
|
|
520
|
+
pattern: { kind: "primitive", primitive: "pattern" },
|
|
521
|
+
// R/NumPy Scientific Computing Types
|
|
522
|
+
complex: { kind: "primitive", primitive: "complex" },
|
|
523
|
+
matrix: { kind: "primitive", primitive: "matrix" },
|
|
524
|
+
dataframe: { kind: "primitive", primitive: "dataframe" },
|
|
525
|
+
// Function Types (JavaScript, TypeScript, Python, Ruby, Rust)
|
|
526
|
+
callable: { kind: "primitive", primitive: "callable" },
|
|
527
|
+
lambda: { kind: "primitive", primitive: "lambda" },
|
|
528
|
+
closure: { kind: "primitive", primitive: "closure" },
|
|
529
|
+
};
|
|
530
|
+
// ============================================================================
|
|
531
|
+
// EXTENDED BUILT-IN FUNCTIONS - Multi-Language Features
|
|
532
|
+
// ============================================================================
|
|
533
|
+
// This registry provides language features from: Ruby, Python, JavaScript,
|
|
534
|
+
// TypeScript, Go, Rust, C++, C#, R, and C while maintaining Strata syntax
|
|
535
|
+
const BUILTIN_FUNCTIONS = {
|
|
536
|
+
// STRING OPERATIONS (Python, Ruby, JavaScript)
|
|
537
|
+
strlen: (args) => args[0]?.length ?? 0,
|
|
538
|
+
substr: (args) => args[0]?.substring(args[1], args[2]) ?? "",
|
|
539
|
+
toUpperCase: (args) => args[0]?.toUpperCase?.() ?? "",
|
|
540
|
+
toLowerCase: (args) => args[0]?.toLowerCase?.() ?? "",
|
|
541
|
+
trim: (args) => args[0]?.trim?.() ?? "",
|
|
542
|
+
split: (args) => (args[0]?.split?.(args[1]) ?? []).join(","),
|
|
543
|
+
join: (args) => args[0]?.join?.(args[1]) ?? "",
|
|
544
|
+
startsWith: (args) => args[0]?.startsWith?.(args[1]) ?? false,
|
|
545
|
+
endsWith: (args) => args[0]?.endsWith?.(args[1]) ?? false,
|
|
546
|
+
includes: (args) => args[0]?.includes?.(args[1]) ?? false,
|
|
547
|
+
indexOf: (args) => args[0]?.indexOf?.(args[1]) ?? -1,
|
|
548
|
+
replace: (args) => args[0]?.replace?.(args[1], args[2]) ?? "",
|
|
549
|
+
replaceAll: (args) => args[0]?.replaceAll?.(args[1], args[2]) ?? "",
|
|
550
|
+
repeat: (args) => args[0]?.repeat?.(args[1]) ?? "",
|
|
551
|
+
slice: (args) => args[0]?.slice?.(args[1], args[2]) ?? "",
|
|
552
|
+
// ARRAY/LIST OPERATIONS (Python, JavaScript, Go, Rust)
|
|
553
|
+
push: (args) => { args[0]?.push?.(args[1]); return args[0]; },
|
|
554
|
+
pop: (args) => args[0]?.pop?.(),
|
|
555
|
+
shift: (args) => args[0]?.shift?.(),
|
|
556
|
+
unshift: (args) => { args[0]?.unshift?.(args[1]); return args[0]; },
|
|
557
|
+
splice: (args) => args[0]?.splice?.(args[1], args[2]) ?? [],
|
|
558
|
+
map: (args) => args[0]?.map?.(args[1]) ?? [],
|
|
559
|
+
filter: (args) => args[0]?.filter?.(args[1]) ?? [],
|
|
560
|
+
reduce: (args) => args[0]?.reduce?.(args[1], args[2]),
|
|
561
|
+
forEach: (args) => { args[0]?.forEach?.(args[1]); },
|
|
562
|
+
find: (args) => args[0]?.find?.(args[1]),
|
|
563
|
+
findIndex: (args) => args[0]?.findIndex?.(args[1]) ?? -1,
|
|
564
|
+
some: (args) => args[0]?.some?.(args[1]) ?? false,
|
|
565
|
+
every: (args) => args[0]?.every?.(args[1]) ?? false,
|
|
566
|
+
reverse: (args) => { args[0]?.reverse?.(); return args[0]; },
|
|
567
|
+
sort: (args) => { args[0]?.sort?.(args[1]); return args[0]; },
|
|
568
|
+
concat: (args) => args[0]?.concat?.(args[1]) ?? [],
|
|
569
|
+
flat: (args) => args[0]?.flat?.(args[1] ?? 1) ?? [],
|
|
570
|
+
flatMap: (args) => args[0]?.flatMap?.(args[1]) ?? [],
|
|
571
|
+
includes_arr: (args) => args[0]?.includes?.(args[1]) ?? false,
|
|
572
|
+
lastIndexOf: (args) => args[0]?.lastIndexOf?.(args[1]) ?? -1,
|
|
573
|
+
// DICTIONARY/MAP OPERATIONS (Python, JavaScript, Go, Rust)
|
|
574
|
+
keys: (args) => Object.keys(args[0] ?? {}) ?? [],
|
|
575
|
+
values: (args) => Object.values(args[0] ?? {}) ?? [],
|
|
576
|
+
entries: (args) => Object.entries(args[0] ?? {}) ?? [],
|
|
577
|
+
has: (args) => (args[0] ?? {})?.[args[1]] !== undefined,
|
|
578
|
+
delete: (args) => { delete (args[0] ?? {})[args[1]]; return args[0]; },
|
|
579
|
+
clear: (args) => { for (let k in args[0])
|
|
580
|
+
delete args[0][k]; return args[0]; },
|
|
581
|
+
get: (args) => (args[0] ?? {})[args[1]],
|
|
582
|
+
set: (args) => { (args[0] ?? {})[args[1]] = args[2]; return args[0]; },
|
|
583
|
+
// SET OPERATIONS (Python, Go, Rust)
|
|
584
|
+
add: (args) => { args[0]?.add?.(args[1]); return args[0]; },
|
|
585
|
+
remove: (args) => { args[0]?.delete?.(args[1]); return args[0]; },
|
|
586
|
+
union: (args) => new Set([...(args[0] ?? []), ...(args[1] ?? [])]),
|
|
587
|
+
intersection: (args) => new Set([...(args[0] ?? [])].filter(x => args[1]?.has?.(x))),
|
|
588
|
+
difference: (args) => new Set([...(args[0] ?? [])].filter(x => !args[1]?.has?.(x))),
|
|
589
|
+
// MATH OPERATIONS (R, Python, C, C++)
|
|
590
|
+
abs: (args) => Math.abs(args[0]),
|
|
591
|
+
sqrt: (args) => Math.sqrt(args[0]),
|
|
592
|
+
pow: (args) => Math.pow(args[0], args[1]),
|
|
593
|
+
sin: (args) => Math.sin(args[0]),
|
|
594
|
+
cos: (args) => Math.cos(args[0]),
|
|
595
|
+
tan: (args) => Math.tan(args[0]),
|
|
596
|
+
asin: (args) => Math.asin(args[0]),
|
|
597
|
+
acos: (args) => Math.acos(args[0]),
|
|
598
|
+
atan: (args) => Math.atan(args[0]),
|
|
599
|
+
atan2: (args) => Math.atan2(args[0], args[1]),
|
|
600
|
+
exp: (args) => Math.exp(args[0]),
|
|
601
|
+
log: (args) => Math.log(args[0]),
|
|
602
|
+
log10: (args) => Math.log10(args[0]),
|
|
603
|
+
log2: (args) => Math.log2(args[0]),
|
|
604
|
+
ceil: (args) => Math.ceil(args[0]),
|
|
605
|
+
floor: (args) => Math.floor(args[0]),
|
|
606
|
+
round: (args) => Math.round(args[0]),
|
|
607
|
+
trunc: (args) => Math.trunc(args[0]),
|
|
608
|
+
max: (args) => Math.max(...args),
|
|
609
|
+
min: (args) => Math.min(...args),
|
|
610
|
+
gcd: (args) => {
|
|
611
|
+
let a = Math.abs(args[0]), b = Math.abs(args[1]);
|
|
612
|
+
while (b)
|
|
613
|
+
[a, b] = [b, a % b];
|
|
614
|
+
return a;
|
|
615
|
+
},
|
|
616
|
+
lcm: (args) => Math.abs(args[0] * args[1]) / BUILTIN_FUNCTIONS.gcd([args[0], args[1]]),
|
|
617
|
+
// RANDOM OPERATIONS (Python, Go, JavaScript, Ruby)
|
|
618
|
+
random: (args) => Math.random(),
|
|
619
|
+
randomInt: (args) => Math.floor(Math.random() * (args[1] - args[0] + 1)) + args[0],
|
|
620
|
+
randomFloat: (args) => Math.random() * (args[1] - args[0]) + args[0],
|
|
621
|
+
// TYPE CHECKING/CONVERSION (Python, JavaScript, TypeScript)
|
|
622
|
+
typeof: (args) => typeof args[0],
|
|
623
|
+
parseInt: (args) => parseInt(args[0], args[1] ?? 10),
|
|
624
|
+
parseFloat: (args) => parseFloat(args[0]),
|
|
625
|
+
toString: (args) => String(args[0]),
|
|
626
|
+
toBoolean: (args) => Boolean(args[0]),
|
|
627
|
+
toNumber: (args) => Number(args[0]),
|
|
628
|
+
isNaN: (args) => isNaN(args[0]),
|
|
629
|
+
isFinite: (args) => isFinite(args[0]),
|
|
630
|
+
isInteger: (args) => Number.isInteger(args[0]),
|
|
631
|
+
isArray: (args) => Array.isArray(args[0]),
|
|
632
|
+
isObject: (args) => args[0] !== null && typeof args[0] === "object",
|
|
633
|
+
isNull: (args) => args[0] === null,
|
|
634
|
+
isUndefined: (args) => args[0] === undefined,
|
|
635
|
+
// ERROR HANDLING (Go, Rust, C++)
|
|
636
|
+
try: (args) => { try {
|
|
637
|
+
return args[0]?.();
|
|
638
|
+
}
|
|
639
|
+
catch (e) {
|
|
640
|
+
return e;
|
|
641
|
+
} },
|
|
642
|
+
catch: (args) => args[0] instanceof Error ? args[1]?.(args[0]) : args[0],
|
|
643
|
+
panic: (args) => { throw new Error(args[0]); },
|
|
644
|
+
defer: (args) => { /* deferred execution placeholder */ return args[0]; },
|
|
645
|
+
// FILE OPERATIONS (Python, Go, C, C++)
|
|
646
|
+
readFile: (args) => { try {
|
|
647
|
+
return fs.readFileSync(args[0], "utf-8");
|
|
648
|
+
}
|
|
649
|
+
catch {
|
|
650
|
+
return null;
|
|
651
|
+
} },
|
|
652
|
+
writeFile: (args) => { try {
|
|
653
|
+
fs.writeFileSync(args[0], args[1]);
|
|
654
|
+
return true;
|
|
655
|
+
}
|
|
656
|
+
catch {
|
|
657
|
+
return false;
|
|
658
|
+
} },
|
|
659
|
+
appendFile: (args) => { try {
|
|
660
|
+
fs.appendFileSync(args[0], args[1]);
|
|
661
|
+
return true;
|
|
662
|
+
}
|
|
663
|
+
catch {
|
|
664
|
+
return false;
|
|
665
|
+
} },
|
|
666
|
+
deleteFile: (args) => { try {
|
|
667
|
+
fs.unlinkSync(args[0]);
|
|
668
|
+
return true;
|
|
669
|
+
}
|
|
670
|
+
catch {
|
|
671
|
+
return false;
|
|
672
|
+
} },
|
|
673
|
+
exists: (args) => fs.existsSync(args[0]),
|
|
674
|
+
isFile: (args) => { try {
|
|
675
|
+
return fs.statSync(args[0]).isFile();
|
|
676
|
+
}
|
|
677
|
+
catch {
|
|
678
|
+
return false;
|
|
679
|
+
} },
|
|
680
|
+
isDirectory: (args) => { try {
|
|
681
|
+
return fs.statSync(args[0]).isDirectory();
|
|
682
|
+
}
|
|
683
|
+
catch {
|
|
684
|
+
return false;
|
|
685
|
+
} },
|
|
686
|
+
mkdir: (args) => { try {
|
|
687
|
+
fs.mkdirSync(args[0], { recursive: true });
|
|
688
|
+
return true;
|
|
689
|
+
}
|
|
690
|
+
catch {
|
|
691
|
+
return false;
|
|
692
|
+
} },
|
|
693
|
+
// REGEX OPERATIONS (Python, Ruby, JavaScript, Go)
|
|
694
|
+
match: (args) => {
|
|
695
|
+
try {
|
|
696
|
+
const m = args[0]?.match?.(new RegExp(args[1], args[2] ?? ""));
|
|
697
|
+
return m ?? null;
|
|
698
|
+
}
|
|
699
|
+
catch {
|
|
700
|
+
return null;
|
|
701
|
+
}
|
|
702
|
+
},
|
|
703
|
+
test: (args) => {
|
|
704
|
+
try {
|
|
705
|
+
return new RegExp(args[1], args[2] ?? "").test(args[0]);
|
|
706
|
+
}
|
|
707
|
+
catch {
|
|
708
|
+
return false;
|
|
709
|
+
}
|
|
710
|
+
},
|
|
711
|
+
search: (args) => {
|
|
712
|
+
try {
|
|
713
|
+
return args[0]?.search?.(new RegExp(args[1], args[2] ?? ""));
|
|
714
|
+
}
|
|
715
|
+
catch {
|
|
716
|
+
return -1;
|
|
717
|
+
}
|
|
718
|
+
},
|
|
719
|
+
matchAll: (args) => {
|
|
720
|
+
try {
|
|
721
|
+
return [...args[0]?.matchAll?.(new RegExp(args[1], "g")) ?? []];
|
|
722
|
+
}
|
|
723
|
+
catch {
|
|
724
|
+
return [];
|
|
725
|
+
}
|
|
726
|
+
},
|
|
727
|
+
// DATETIME OPERATIONS (Python, Go, JavaScript, Ruby)
|
|
728
|
+
now: (args) => new Date().getTime(),
|
|
729
|
+
timestamp: (args) => Math.floor(new Date().getTime() / 1000),
|
|
730
|
+
getDate: (args) => new Date(args[0]).getDate(),
|
|
731
|
+
getMonth: (args) => new Date(args[0]).getMonth() + 1,
|
|
732
|
+
getYear: (args) => new Date(args[0]).getFullYear(),
|
|
733
|
+
getHours: (args) => new Date(args[0]).getHours(),
|
|
734
|
+
getMinutes: (args) => new Date(args[0]).getMinutes(),
|
|
735
|
+
getSeconds: (args) => new Date(args[0]).getSeconds(),
|
|
736
|
+
// PROMISE/ASYNC OPERATIONS (JavaScript, TypeScript, Python async)
|
|
737
|
+
Promise: (args) => new Promise(args[0]),
|
|
738
|
+
resolve: (args) => Promise.resolve(args[0]),
|
|
739
|
+
reject: (args) => Promise.reject(args[0]),
|
|
740
|
+
// TUPLES (Python, Go, Rust)
|
|
741
|
+
tuple: (args) => Object.freeze(Array.from(args)),
|
|
742
|
+
untuple: (args) => Array.from(args[0] ?? []),
|
|
743
|
+
// OPTIONAL/NULL HANDLING (Rust, TypeScript, Go)
|
|
744
|
+
Some: (args) => ({ type: "some", value: args[0] }),
|
|
745
|
+
None: (args) => ({ type: "none" }),
|
|
746
|
+
unwrap: (args) => args[0]?.type === "some" ? args[0].value : (() => { throw new Error("unwrap of None"); })(),
|
|
747
|
+
unwrapOr: (args) => args[0]?.type === "some" ? args[0].value : args[1],
|
|
748
|
+
isSome: (args) => args[0]?.type === "some",
|
|
749
|
+
isNone: (args) => args[0]?.type === "none",
|
|
750
|
+
// RESULT OPERATIONS (Rust, Go)
|
|
751
|
+
Ok: (args) => ({ type: "ok", value: args[0] }),
|
|
752
|
+
Err: (args) => ({ type: "err", error: args[0] }),
|
|
753
|
+
isOk: (args) => args[0]?.type === "ok",
|
|
754
|
+
isErr: (args) => args[0]?.type === "err",
|
|
755
|
+
// ITERATOR/GENERATOR OPERATIONS (Python, JavaScript, Go)
|
|
756
|
+
range: (args) => Array.from({ length: args[1] - args[0] }, (_, i) => i + args[0]),
|
|
757
|
+
enumerate: (args) => args[0]?.map?.((v, i) => [i, v]) ?? [],
|
|
758
|
+
zip: (args) => args[0]?.map?.((v, i) => [v, args[1]?.[i]]) ?? [],
|
|
759
|
+
reversed: (args) => [...args[0] ?? []].reverse(),
|
|
760
|
+
sorted: (args) => [...args[0] ?? []].sort(args[1]),
|
|
761
|
+
iter: (args) => (args[0] ?? [])[Symbol.iterator]?.(),
|
|
762
|
+
next: (args) => args[0]?.next?.(),
|
|
763
|
+
// HASH/DIGEST OPERATIONS (Python, Go, C++)
|
|
764
|
+
hash: (args) => {
|
|
765
|
+
let hash = 0;
|
|
766
|
+
const str = String(args[0]);
|
|
767
|
+
for (let i = 0; i < str.length; i++)
|
|
768
|
+
hash = ((hash << 5) - hash) + str.charCodeAt(i), hash |= 0;
|
|
769
|
+
return hash;
|
|
770
|
+
},
|
|
771
|
+
// REFLECTION (Python, JavaScript, TypeScript)
|
|
772
|
+
hasProperty: (args) => args[1] in args[0],
|
|
773
|
+
getProperty: (args) => args[0][args[1]],
|
|
774
|
+
setProperty: (args) => { args[0][args[1]] = args[2]; return args[0]; },
|
|
775
|
+
deleteProperty: (args) => { delete args[0][args[1]]; return args[0]; },
|
|
776
|
+
getPrototype: (args) => Object.getPrototypeOf(args[0]),
|
|
777
|
+
setPrototype: (args) => { Object.setPrototypeOf(args[0], args[1]); return args[0]; },
|
|
778
|
+
// DEEP OPERATIONS
|
|
779
|
+
clone: (args) => JSON.parse(JSON.stringify(args[0])),
|
|
780
|
+
deepEqual: (args) => JSON.stringify(args[0]) === JSON.stringify(args[1]),
|
|
781
|
+
assign: (args) => Object.assign(args[0], ...args.slice(1)),
|
|
782
|
+
// TYPE ALIASES FOR COMPATIBILITY
|
|
783
|
+
uint: (args) => Math.abs(Math.floor(args[0])),
|
|
784
|
+
sint: (args) => Math.floor(args[0]),
|
|
785
|
+
byte: (args) => Math.floor(args[0]) & 0xFF,
|
|
786
|
+
rune: (args) => String.fromCharCode(args[0]),
|
|
787
|
+
// FUNCTIONAL PROGRAMMING (JavaScript, Python, Rust)
|
|
788
|
+
compose: (args) => (x) => args.reduceRight((v, f) => f(v), x),
|
|
789
|
+
pipe: (args) => (x) => args.reduce((v, f) => f(v), x),
|
|
790
|
+
curry: (args) => {
|
|
791
|
+
const fn = args[0];
|
|
792
|
+
const arity = args[1] ?? fn.length;
|
|
793
|
+
return function curried(...args) {
|
|
794
|
+
return args.length >= arity ? fn(...args) : (...more) => curried(...args, ...more);
|
|
795
|
+
};
|
|
796
|
+
},
|
|
797
|
+
partial: (args) => args[0].bind(null, ...args.slice(1)),
|
|
798
|
+
memoize: (args) => {
|
|
799
|
+
const cache = new Map();
|
|
800
|
+
return (...args) => {
|
|
801
|
+
const key = JSON.stringify(args);
|
|
802
|
+
return cache.has(key) ? cache.get(key) : (cache.set(key, args[0](...args)), cache.get(key));
|
|
803
|
+
};
|
|
804
|
+
},
|
|
805
|
+
// SYMBOL/ENUM OPERATIONS
|
|
806
|
+
symbol: (args) => Symbol(args[0]),
|
|
807
|
+
// GENERIC/TEMPLATE OPERATIONS (C++, C#, TypeScript)
|
|
808
|
+
generic: (args) => args[0],
|
|
809
|
+
// BITWISE OPERATIONS (C, C++, Rust, Go)
|
|
810
|
+
bitwiseAnd: (args) => args[0] & args[1],
|
|
811
|
+
bitwiseOr: (args) => args[0] | args[1],
|
|
812
|
+
bitwiseXor: (args) => args[0] ^ args[1],
|
|
813
|
+
bitwiseNot: (args) => ~args[0],
|
|
814
|
+
leftShift: (args) => args[0] << args[1],
|
|
815
|
+
rightShift: (args) => args[0] >> args[1],
|
|
816
|
+
unsignedRightShift: (args) => args[0] >>> args[1],
|
|
817
|
+
};
|
|
818
|
+
function parseTypeAnnotation(token) {
|
|
819
|
+
if (token in TYPE_REGISTRY)
|
|
820
|
+
return TYPE_REGISTRY[token];
|
|
821
|
+
if (token.endsWith("?"))
|
|
822
|
+
return {
|
|
823
|
+
kind: "optional",
|
|
824
|
+
innerType: parseTypeAnnotation(token.slice(0, -1)) ||
|
|
825
|
+
{ kind: "primitive", primitive: "any" },
|
|
826
|
+
};
|
|
827
|
+
if (token in TYPE_REGISTRY)
|
|
828
|
+
return TYPE_REGISTRY[token];
|
|
829
|
+
return { kind: "primitive", primitive: "any" };
|
|
830
|
+
}
|
|
831
|
+
function typeCompatible(actual, expected) {
|
|
832
|
+
if (expected.primitive === "any" || actual.primitive === "any")
|
|
833
|
+
return true;
|
|
834
|
+
if (actual.kind === "primitive" && expected.kind === "primitive") {
|
|
835
|
+
if (actual.primitive === expected.primitive)
|
|
836
|
+
return true;
|
|
837
|
+
// Allow numeric conversions: int → float
|
|
838
|
+
if (actual.primitive === "int" &&
|
|
839
|
+
expected.primitive === "float")
|
|
840
|
+
return true;
|
|
841
|
+
// Allow char → string
|
|
842
|
+
if (actual.primitive === "char" &&
|
|
843
|
+
expected.primitive === "string")
|
|
844
|
+
return true;
|
|
845
|
+
return false;
|
|
846
|
+
}
|
|
847
|
+
if (actual.kind === "union" && expected.kind === "union") {
|
|
848
|
+
return ((actual.types
|
|
849
|
+
?.every((t) => expected.types?.some((e) => typeCompatible(t, e))) ?? false));
|
|
850
|
+
}
|
|
851
|
+
return false;
|
|
852
|
+
}
|
|
853
|
+
class Lexer {
|
|
854
|
+
constructor(input) {
|
|
855
|
+
this.input = input;
|
|
856
|
+
this.pos = 0;
|
|
857
|
+
this.line = 1;
|
|
858
|
+
this.column = 1;
|
|
859
|
+
this.lineStart = 0;
|
|
860
|
+
}
|
|
861
|
+
peek() {
|
|
862
|
+
return this.input[this.pos];
|
|
863
|
+
}
|
|
864
|
+
advance() {
|
|
865
|
+
const ch = this.input[this.pos++];
|
|
866
|
+
if (ch === "\n") {
|
|
867
|
+
this.line++;
|
|
868
|
+
this.column = 1;
|
|
869
|
+
this.lineStart = this.pos;
|
|
870
|
+
}
|
|
871
|
+
else {
|
|
872
|
+
this.column++;
|
|
873
|
+
}
|
|
874
|
+
return ch;
|
|
875
|
+
}
|
|
876
|
+
getLocation() {
|
|
877
|
+
return {
|
|
878
|
+
line: this.line,
|
|
879
|
+
column: this.column,
|
|
880
|
+
source: this.input.substring(this.lineStart, this.pos),
|
|
881
|
+
};
|
|
882
|
+
}
|
|
883
|
+
nextToken() {
|
|
884
|
+
// Skip whitespace
|
|
885
|
+
while (this.peek() === " " ||
|
|
886
|
+
this.peek() === "\n" ||
|
|
887
|
+
this.peek() === "\r" ||
|
|
888
|
+
this.peek() === "\t") {
|
|
889
|
+
this.advance();
|
|
890
|
+
}
|
|
891
|
+
// Skip comments
|
|
892
|
+
if (this.peek() === "/" &&
|
|
893
|
+
this.input[this.pos + 1] === "/") {
|
|
894
|
+
while (this.peek() && this.peek() !== "\n")
|
|
895
|
+
this.advance();
|
|
896
|
+
return this.nextToken();
|
|
897
|
+
}
|
|
898
|
+
if (!this.peek())
|
|
899
|
+
return null;
|
|
900
|
+
const loc = this.getLocation();
|
|
901
|
+
// Multi-character operators
|
|
902
|
+
const twoCharOps = [
|
|
903
|
+
"==",
|
|
904
|
+
"!=",
|
|
905
|
+
"<=",
|
|
906
|
+
">=",
|
|
907
|
+
"=>",
|
|
908
|
+
"||",
|
|
909
|
+
"&&",
|
|
910
|
+
"++",
|
|
911
|
+
"--",
|
|
912
|
+
];
|
|
913
|
+
const twoChar = this.input.substring(this.pos, this.pos + 2);
|
|
914
|
+
if (twoCharOps.includes(twoChar)) {
|
|
915
|
+
this.advance();
|
|
916
|
+
this.advance();
|
|
917
|
+
return { token: twoChar, location: loc };
|
|
918
|
+
}
|
|
919
|
+
// Identifiers / keywords
|
|
920
|
+
if (/[a-zA-Z_]/.test(this.peek() || "")) {
|
|
921
|
+
let word = "";
|
|
922
|
+
while (/[a-zA-Z0-9_]/.test(this.peek() || ""))
|
|
923
|
+
word += this.advance();
|
|
924
|
+
return { token: word, location: loc };
|
|
925
|
+
}
|
|
926
|
+
// Strings
|
|
927
|
+
if (this.peek() === '"') {
|
|
928
|
+
this.advance(); // Skip opening quote
|
|
929
|
+
let str = "";
|
|
930
|
+
while (this.peek() && this.peek() !== '"') {
|
|
931
|
+
if (this.peek() === "\\") {
|
|
932
|
+
this.advance();
|
|
933
|
+
const escaped = this.advance();
|
|
934
|
+
str += escaped === "n" ? "\n" : escaped;
|
|
935
|
+
}
|
|
936
|
+
else {
|
|
937
|
+
str += this.advance();
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
if (this.peek() === '"')
|
|
941
|
+
this.advance(); // Skip closing quote
|
|
942
|
+
return { token: `"${str}"`, location: loc };
|
|
943
|
+
}
|
|
944
|
+
// Numbers
|
|
945
|
+
if (/[0-9]/.test(this.peek() || "")) {
|
|
946
|
+
let num = "";
|
|
947
|
+
while (/[0-9.]/.test(this.peek() || ""))
|
|
948
|
+
num += this.advance();
|
|
949
|
+
return { token: num, location: loc };
|
|
950
|
+
}
|
|
951
|
+
// Single character tokens
|
|
952
|
+
const ch = this.advance();
|
|
953
|
+
return { token: ch, location: loc };
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
// ============================================================================
|
|
957
|
+
// PARSER
|
|
958
|
+
// ============================================================================
|
|
959
|
+
class Parser {
|
|
960
|
+
constructor(input) {
|
|
961
|
+
this.tokens = [];
|
|
962
|
+
this.pos = 0;
|
|
963
|
+
const lexer = new Lexer(input);
|
|
964
|
+
let token;
|
|
965
|
+
while ((token = lexer.nextToken())) {
|
|
966
|
+
this.tokens.push(token);
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
current() {
|
|
970
|
+
return this.tokens[this.pos];
|
|
971
|
+
}
|
|
972
|
+
advance() {
|
|
973
|
+
this.pos++;
|
|
974
|
+
}
|
|
975
|
+
expect(token) {
|
|
976
|
+
if (!this.current() || this.current().token !== token) {
|
|
977
|
+
throw new Error(`Expected ${token} at line ${this.current()?.location.line}`);
|
|
978
|
+
}
|
|
979
|
+
this.advance();
|
|
980
|
+
}
|
|
981
|
+
precedence(op) {
|
|
982
|
+
const precs = {
|
|
983
|
+
"||": 1,
|
|
984
|
+
"&&": 2,
|
|
985
|
+
"==": 3,
|
|
986
|
+
"!=": 3,
|
|
987
|
+
"<": 4,
|
|
988
|
+
">": 4,
|
|
989
|
+
"<=": 4,
|
|
990
|
+
">=": 4,
|
|
991
|
+
"+": 5,
|
|
992
|
+
"-": 5,
|
|
993
|
+
"*": 6,
|
|
994
|
+
"/": 6,
|
|
995
|
+
"%": 6,
|
|
996
|
+
};
|
|
997
|
+
return precs[op] ?? 0;
|
|
998
|
+
}
|
|
999
|
+
parseUnary() {
|
|
1000
|
+
if (this.current() &&
|
|
1001
|
+
["!", "-", "+", "~"].includes(this.current().token)) {
|
|
1002
|
+
const op = this.current().token;
|
|
1003
|
+
this.advance();
|
|
1004
|
+
return { kind: "unary", op, operand: this.parseUnary() };
|
|
1005
|
+
}
|
|
1006
|
+
return this.parsePrimary();
|
|
1007
|
+
}
|
|
1008
|
+
parsePrimary() {
|
|
1009
|
+
if (!this.current())
|
|
1010
|
+
throw new Error("Unexpected end of input");
|
|
1011
|
+
const token = this.current().token;
|
|
1012
|
+
if (/^[0-9]/.test(token)) {
|
|
1013
|
+
this.advance();
|
|
1014
|
+
return {
|
|
1015
|
+
kind: "literal",
|
|
1016
|
+
value: token.includes(".") ? parseFloat(token) : parseInt(token),
|
|
1017
|
+
type: token.includes(".") ? { kind: "primitive", primitive: "float" } : { kind: "primitive", primitive: "int" },
|
|
1018
|
+
};
|
|
1019
|
+
}
|
|
1020
|
+
if (token.startsWith('"')) {
|
|
1021
|
+
this.advance();
|
|
1022
|
+
return {
|
|
1023
|
+
kind: "literal",
|
|
1024
|
+
value: token.slice(1, -1),
|
|
1025
|
+
type: { kind: "primitive", primitive: "string" },
|
|
1026
|
+
};
|
|
1027
|
+
}
|
|
1028
|
+
if (token === "true" || token === "false") {
|
|
1029
|
+
this.advance();
|
|
1030
|
+
return {
|
|
1031
|
+
kind: "literal",
|
|
1032
|
+
value: token === "true",
|
|
1033
|
+
type: { kind: "primitive", primitive: "bool" },
|
|
1034
|
+
};
|
|
1035
|
+
}
|
|
1036
|
+
if (/[a-zA-Z_]/.test(token)) {
|
|
1037
|
+
let expr = { kind: "identifier", name: token };
|
|
1038
|
+
this.advance();
|
|
1039
|
+
// Handle member access and calls: io.print(...), obj.method(...), etc.
|
|
1040
|
+
while (this.current()?.token === ".") {
|
|
1041
|
+
this.advance();
|
|
1042
|
+
const property = this.current()?.token;
|
|
1043
|
+
if (!property)
|
|
1044
|
+
throw new Error("Expected property name after .");
|
|
1045
|
+
this.advance();
|
|
1046
|
+
// Check for function call
|
|
1047
|
+
if (this.current()?.token === "(") {
|
|
1048
|
+
this.advance();
|
|
1049
|
+
const args = [];
|
|
1050
|
+
while (this.current()?.token !== ")") {
|
|
1051
|
+
args.push(this.parseUnary());
|
|
1052
|
+
if (this.current()?.token === ",")
|
|
1053
|
+
this.advance();
|
|
1054
|
+
}
|
|
1055
|
+
this.expect(")");
|
|
1056
|
+
expr = {
|
|
1057
|
+
kind: "call",
|
|
1058
|
+
func: { kind: "member", object: expr, property },
|
|
1059
|
+
args,
|
|
1060
|
+
};
|
|
1061
|
+
}
|
|
1062
|
+
else {
|
|
1063
|
+
// Property access without call
|
|
1064
|
+
expr = { kind: "member", object: expr, property };
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
return expr;
|
|
1068
|
+
}
|
|
1069
|
+
if (token === "(") {
|
|
1070
|
+
this.advance();
|
|
1071
|
+
const expr = this.parseBinary();
|
|
1072
|
+
this.expect(")");
|
|
1073
|
+
return expr;
|
|
1074
|
+
}
|
|
1075
|
+
throw new Error(`Unexpected token: ${token}`);
|
|
1076
|
+
}
|
|
1077
|
+
parseBinary(minPrec = 0) {
|
|
1078
|
+
let left = this.parseUnary();
|
|
1079
|
+
while (this.current() &&
|
|
1080
|
+
this.precedence(this.current().token) >= minPrec) {
|
|
1081
|
+
const op = this.current().token;
|
|
1082
|
+
const prec = this.precedence(op);
|
|
1083
|
+
this.advance();
|
|
1084
|
+
const right = this.parseBinary(prec + 1);
|
|
1085
|
+
left = { kind: "binary", op, left, right };
|
|
1086
|
+
}
|
|
1087
|
+
return left;
|
|
1088
|
+
}
|
|
1089
|
+
parse() {
|
|
1090
|
+
const statements = [];
|
|
1091
|
+
while (this.current()) {
|
|
1092
|
+
statements.push(this.parseStatement());
|
|
1093
|
+
}
|
|
1094
|
+
return statements;
|
|
1095
|
+
}
|
|
1096
|
+
parseStatement() {
|
|
1097
|
+
const token = this.current()?.token;
|
|
1098
|
+
if (token === "import") {
|
|
1099
|
+
this.advance();
|
|
1100
|
+
const name = this.current().token;
|
|
1101
|
+
this.advance();
|
|
1102
|
+
this.expect("from");
|
|
1103
|
+
const module = this.current().token;
|
|
1104
|
+
this.advance();
|
|
1105
|
+
return { kind: "import", name, module };
|
|
1106
|
+
}
|
|
1107
|
+
if (token === "let" || token === "const" || token === "var") {
|
|
1108
|
+
const mutable = token === "var";
|
|
1109
|
+
this.advance();
|
|
1110
|
+
const name = this.current().token;
|
|
1111
|
+
this.advance();
|
|
1112
|
+
this.expect(":");
|
|
1113
|
+
const typeStr = this.current().token;
|
|
1114
|
+
this.advance();
|
|
1115
|
+
this.expect("=");
|
|
1116
|
+
const value = this.parseBinary();
|
|
1117
|
+
return {
|
|
1118
|
+
kind: "let",
|
|
1119
|
+
name,
|
|
1120
|
+
type: parseTypeAnnotation(typeStr) || { kind: "primitive", primitive: "any" },
|
|
1121
|
+
value,
|
|
1122
|
+
mutable,
|
|
1123
|
+
};
|
|
1124
|
+
}
|
|
1125
|
+
if (token === "func") {
|
|
1126
|
+
this.advance();
|
|
1127
|
+
const name = this.current().token;
|
|
1128
|
+
this.advance();
|
|
1129
|
+
this.expect("(");
|
|
1130
|
+
const params = [];
|
|
1131
|
+
while (this.current()?.token !== ")") {
|
|
1132
|
+
const pname = this.current().token;
|
|
1133
|
+
this.advance();
|
|
1134
|
+
this.expect(":");
|
|
1135
|
+
const ptype = this.current().token;
|
|
1136
|
+
this.advance();
|
|
1137
|
+
params.push({
|
|
1138
|
+
name: pname,
|
|
1139
|
+
type: parseTypeAnnotation(ptype) || { kind: "primitive", primitive: "any" },
|
|
1140
|
+
});
|
|
1141
|
+
if (this.current()?.token === ",")
|
|
1142
|
+
this.advance();
|
|
1143
|
+
}
|
|
1144
|
+
this.expect(")");
|
|
1145
|
+
this.expect("=>");
|
|
1146
|
+
const returnTypeStr = this.current().token;
|
|
1147
|
+
this.advance();
|
|
1148
|
+
this.expect("{");
|
|
1149
|
+
const body = [];
|
|
1150
|
+
while (this.current()?.token !== "}") {
|
|
1151
|
+
body.push(this.parseStatement());
|
|
1152
|
+
}
|
|
1153
|
+
this.expect("}");
|
|
1154
|
+
return {
|
|
1155
|
+
kind: "function",
|
|
1156
|
+
name,
|
|
1157
|
+
params,
|
|
1158
|
+
returnType: parseTypeAnnotation(returnTypeStr) || { kind: "primitive", primitive: "any" },
|
|
1159
|
+
body,
|
|
1160
|
+
};
|
|
1161
|
+
}
|
|
1162
|
+
if (token === "return") {
|
|
1163
|
+
this.advance();
|
|
1164
|
+
const value = this.current() ? this.parseBinary() : undefined;
|
|
1165
|
+
return { kind: "return", value };
|
|
1166
|
+
}
|
|
1167
|
+
if (token === "if") {
|
|
1168
|
+
this.advance();
|
|
1169
|
+
this.expect("(");
|
|
1170
|
+
const condition = this.parseBinary();
|
|
1171
|
+
this.expect(")");
|
|
1172
|
+
this.expect("{");
|
|
1173
|
+
const then = [];
|
|
1174
|
+
while (this.current()?.token !== "}") {
|
|
1175
|
+
then.push(this.parseStatement());
|
|
1176
|
+
}
|
|
1177
|
+
this.expect("}");
|
|
1178
|
+
return { kind: "if", condition, then };
|
|
1179
|
+
}
|
|
1180
|
+
if (token === "while") {
|
|
1181
|
+
this.advance();
|
|
1182
|
+
this.expect("(");
|
|
1183
|
+
const condition = this.parseBinary();
|
|
1184
|
+
this.expect(")");
|
|
1185
|
+
this.expect("{");
|
|
1186
|
+
const body = [];
|
|
1187
|
+
while (this.current()?.token !== "}") {
|
|
1188
|
+
body.push(this.parseStatement());
|
|
1189
|
+
}
|
|
1190
|
+
this.expect("}");
|
|
1191
|
+
return { kind: "while", condition, body };
|
|
1192
|
+
}
|
|
1193
|
+
if (token === "break") {
|
|
1194
|
+
this.advance();
|
|
1195
|
+
return { kind: "break" };
|
|
1196
|
+
}
|
|
1197
|
+
if (token === "continue") {
|
|
1198
|
+
this.advance();
|
|
1199
|
+
return { kind: "continue" };
|
|
1200
|
+
}
|
|
1201
|
+
const expr = this.parseBinary();
|
|
1202
|
+
// Check for assignment: identifier = value
|
|
1203
|
+
if (this.current()?.token === "=" && expr.kind === "identifier") {
|
|
1204
|
+
const target = expr.name;
|
|
1205
|
+
this.advance();
|
|
1206
|
+
const value = this.parseBinary();
|
|
1207
|
+
return { kind: "assignment", target, value };
|
|
1208
|
+
}
|
|
1209
|
+
return { kind: "expression", expr };
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
class TypeChecker {
|
|
1213
|
+
constructor() {
|
|
1214
|
+
this.env = {
|
|
1215
|
+
vars: new Map(),
|
|
1216
|
+
functions: new Map(),
|
|
1217
|
+
};
|
|
1218
|
+
this.modules = new Map();
|
|
1219
|
+
}
|
|
1220
|
+
check(statements) {
|
|
1221
|
+
for (const stmt of statements) {
|
|
1222
|
+
this.checkStatement(stmt);
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
checkStatement(stmt) {
|
|
1226
|
+
switch (stmt.kind) {
|
|
1227
|
+
case "let":
|
|
1228
|
+
this.env.vars.set(stmt.name, {
|
|
1229
|
+
type: stmt.type,
|
|
1230
|
+
mutable: stmt.mutable,
|
|
1231
|
+
});
|
|
1232
|
+
this.checkExpression(stmt.value, stmt.type);
|
|
1233
|
+
break;
|
|
1234
|
+
case "function":
|
|
1235
|
+
this.env.functions.set(stmt.name, {
|
|
1236
|
+
params: stmt.params.map((p) => p.type),
|
|
1237
|
+
returnType: stmt.returnType,
|
|
1238
|
+
});
|
|
1239
|
+
const oldEnv = this.env;
|
|
1240
|
+
this.env = { vars: new Map(), functions: new Map(), parent: oldEnv };
|
|
1241
|
+
for (const param of stmt.params) {
|
|
1242
|
+
this.env.vars.set(param.name, {
|
|
1243
|
+
type: param.type,
|
|
1244
|
+
mutable: false,
|
|
1245
|
+
});
|
|
1246
|
+
}
|
|
1247
|
+
for (const s of stmt.body) {
|
|
1248
|
+
this.checkStatement(s);
|
|
1249
|
+
}
|
|
1250
|
+
this.env = oldEnv;
|
|
1251
|
+
break;
|
|
1252
|
+
case "if":
|
|
1253
|
+
this.checkExpression(stmt.condition, { kind: "primitive", primitive: "bool" });
|
|
1254
|
+
for (const s of stmt.then) {
|
|
1255
|
+
this.checkStatement(s);
|
|
1256
|
+
}
|
|
1257
|
+
if (stmt.else) {
|
|
1258
|
+
for (const s of stmt.else) {
|
|
1259
|
+
this.checkStatement(s);
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
break;
|
|
1263
|
+
case "while":
|
|
1264
|
+
this.checkExpression(stmt.condition, { kind: "primitive", primitive: "bool" });
|
|
1265
|
+
for (const s of stmt.body) {
|
|
1266
|
+
this.checkStatement(s);
|
|
1267
|
+
}
|
|
1268
|
+
break;
|
|
1269
|
+
case "expression":
|
|
1270
|
+
this.checkExpression(stmt.expr, { kind: "primitive", primitive: "any" });
|
|
1271
|
+
break;
|
|
1272
|
+
case "import":
|
|
1273
|
+
break;
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
checkExpression(expr, expectedType) {
|
|
1277
|
+
const actualType = this.inferType(expr);
|
|
1278
|
+
if (!typeCompatible(actualType, expectedType)) {
|
|
1279
|
+
throw new Error(`Type mismatch: expected ${JSON.stringify(expectedType)}, got ${JSON.stringify(actualType)}`);
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
inferType(expr) {
|
|
1283
|
+
switch (expr.kind) {
|
|
1284
|
+
case "literal":
|
|
1285
|
+
return expr.type;
|
|
1286
|
+
case "identifier":
|
|
1287
|
+
return this.env.vars.get(expr.name)?.type || { kind: "primitive", primitive: "any" };
|
|
1288
|
+
case "binary":
|
|
1289
|
+
if (["==", "!=", "<", ">", "<=", ">=", "&&", "||"].includes(expr.op)) {
|
|
1290
|
+
return { kind: "primitive", primitive: "bool" };
|
|
1291
|
+
}
|
|
1292
|
+
return this.inferType(expr.left);
|
|
1293
|
+
case "unary":
|
|
1294
|
+
if (expr.op === "!")
|
|
1295
|
+
return { kind: "primitive", primitive: "bool" };
|
|
1296
|
+
return this.inferType(expr.operand);
|
|
1297
|
+
default:
|
|
1298
|
+
return { kind: "primitive", primitive: "any" };
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
class Environment {
|
|
1303
|
+
constructor() {
|
|
1304
|
+
this.vars = new Map();
|
|
1305
|
+
this.functions = new Map();
|
|
1306
|
+
this.modules = new Map();
|
|
1307
|
+
this.parent = null;
|
|
1308
|
+
}
|
|
1309
|
+
set(name, value, mutable = false) {
|
|
1310
|
+
this.vars.set(name, { value, mutable });
|
|
1311
|
+
}
|
|
1312
|
+
get(name) {
|
|
1313
|
+
if (this.vars.has(name)) {
|
|
1314
|
+
return this.vars.get(name).value;
|
|
1315
|
+
}
|
|
1316
|
+
if (this.parent)
|
|
1317
|
+
return this.parent.get(name);
|
|
1318
|
+
throw new Error(`Undefined variable: ${name}`);
|
|
1319
|
+
}
|
|
1320
|
+
update(name, value) {
|
|
1321
|
+
if (this.vars.has(name)) {
|
|
1322
|
+
const entry = this.vars.get(name);
|
|
1323
|
+
if (!entry.mutable) {
|
|
1324
|
+
throw new Error(`Cannot reassign immutable variable: ${name}`);
|
|
1325
|
+
}
|
|
1326
|
+
entry.value = value;
|
|
1327
|
+
return;
|
|
1328
|
+
}
|
|
1329
|
+
if (this.parent) {
|
|
1330
|
+
this.parent.update(name, value);
|
|
1331
|
+
return;
|
|
1332
|
+
}
|
|
1333
|
+
throw new Error(`Undefined variable: ${name}`);
|
|
1334
|
+
}
|
|
1335
|
+
setFunction(name, params, body) {
|
|
1336
|
+
this.functions.set(name, { params, body });
|
|
1337
|
+
}
|
|
1338
|
+
getFunction(name) {
|
|
1339
|
+
if (this.functions.has(name)) {
|
|
1340
|
+
return this.functions.get(name);
|
|
1341
|
+
}
|
|
1342
|
+
if (this.parent)
|
|
1343
|
+
return this.parent.getFunction(name);
|
|
1344
|
+
return null;
|
|
1345
|
+
}
|
|
1346
|
+
setModule(name, module) {
|
|
1347
|
+
this.modules.set(name, module);
|
|
1348
|
+
}
|
|
1349
|
+
getModule(name) {
|
|
1350
|
+
if (this.modules.has(name)) {
|
|
1351
|
+
return this.modules.get(name);
|
|
1352
|
+
}
|
|
1353
|
+
if (this.parent)
|
|
1354
|
+
return this.parent.getModule(name);
|
|
1355
|
+
return null;
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
class Interpreter {
|
|
1359
|
+
constructor() {
|
|
1360
|
+
this.env = new Environment();
|
|
1361
|
+
this.controlFlow = { type: null };
|
|
1362
|
+
this.setupStdlib();
|
|
1363
|
+
}
|
|
1364
|
+
setupStdlib() {
|
|
1365
|
+
// I/O Module
|
|
1366
|
+
const ioModule = {
|
|
1367
|
+
print: (value) => { console.log(value); return null; },
|
|
1368
|
+
println: (value) => { console.log(value); return null; },
|
|
1369
|
+
};
|
|
1370
|
+
this.env.setModule("std::io", ioModule);
|
|
1371
|
+
this.env.setModule("str", ioModule); // Alias for std::io
|
|
1372
|
+
// Math Module (Python, R, C, C++)
|
|
1373
|
+
this.env.setModule("std::math", {
|
|
1374
|
+
sqrt: (x) => Math.sqrt(x),
|
|
1375
|
+
sin: (x) => Math.sin(x),
|
|
1376
|
+
cos: (x) => Math.cos(x),
|
|
1377
|
+
tan: (x) => Math.tan(x),
|
|
1378
|
+
asin: (x) => Math.asin(x),
|
|
1379
|
+
acos: (x) => Math.acos(x),
|
|
1380
|
+
atan: (x) => Math.atan(x),
|
|
1381
|
+
exp: (x) => Math.exp(x),
|
|
1382
|
+
log: (x) => Math.log(x),
|
|
1383
|
+
log10: (x) => Math.log10(x),
|
|
1384
|
+
log2: (x) => Math.log2(x),
|
|
1385
|
+
floor: (x) => Math.floor(x),
|
|
1386
|
+
ceil: (x) => Math.ceil(x),
|
|
1387
|
+
round: (x) => Math.round(x),
|
|
1388
|
+
abs: (x) => Math.abs(x),
|
|
1389
|
+
pow: (x, y) => Math.pow(x, y),
|
|
1390
|
+
max: (...args) => Math.max(...args),
|
|
1391
|
+
min: (...args) => Math.min(...args),
|
|
1392
|
+
gcd: (a, b) => { let x = Math.abs(a), y = Math.abs(b); while (y)
|
|
1393
|
+
[x, y] = [y, x % y]; return x; },
|
|
1394
|
+
PI: Math.PI,
|
|
1395
|
+
E: Math.E,
|
|
1396
|
+
});
|
|
1397
|
+
// String/Text Module (Python, Ruby, JavaScript)
|
|
1398
|
+
this.env.setModule("std::text", {
|
|
1399
|
+
split: (s, sep) => s.split(sep),
|
|
1400
|
+
join: (arr, sep) => arr.join(sep),
|
|
1401
|
+
trim: (s) => s.trim(),
|
|
1402
|
+
toUpperCase: (s) => s.toUpperCase(),
|
|
1403
|
+
toLowerCase: (s) => s.toLowerCase(),
|
|
1404
|
+
startsWith: (s, prefix) => s.startsWith(prefix),
|
|
1405
|
+
endsWith: (s, suffix) => s.endsWith(suffix),
|
|
1406
|
+
includes: (s, substr) => s.includes(substr),
|
|
1407
|
+
indexOf: (s, substr) => s.indexOf(substr),
|
|
1408
|
+
replace: (s, old, newStr) => s.replace(old, newStr),
|
|
1409
|
+
replaceAll: (s, old, newStr) => s.replaceAll(old, newStr),
|
|
1410
|
+
substring: (s, start, end) => s.substring(start, end),
|
|
1411
|
+
substr: (s, start, length) => s.substr(start, length),
|
|
1412
|
+
slice: (s, start, end) => s.slice(start, end),
|
|
1413
|
+
repeat: (s, count) => s.repeat(count),
|
|
1414
|
+
length: (s) => s.length,
|
|
1415
|
+
charAt: (s, index) => s.charAt(index),
|
|
1416
|
+
charCodeAt: (s, index) => s.charCodeAt(index),
|
|
1417
|
+
});
|
|
1418
|
+
// Array/List Module (Python, JavaScript, Go, Rust)
|
|
1419
|
+
this.env.setModule("std::list", {
|
|
1420
|
+
map: (arr, fn) => arr.map(fn),
|
|
1421
|
+
filter: (arr, fn) => arr.filter(fn),
|
|
1422
|
+
reduce: (arr, fn, init) => arr.reduce(fn, init),
|
|
1423
|
+
forEach: (arr, fn) => { arr.forEach(fn); },
|
|
1424
|
+
find: (arr, fn) => arr.find(fn),
|
|
1425
|
+
findIndex: (arr, fn) => arr.findIndex(fn),
|
|
1426
|
+
some: (arr, fn) => arr.some(fn),
|
|
1427
|
+
every: (arr, fn) => arr.every(fn),
|
|
1428
|
+
includes: (arr, item) => arr.includes(item),
|
|
1429
|
+
indexOf: (arr, item) => arr.indexOf(item),
|
|
1430
|
+
push: (arr, item) => { arr.push(item); return arr; },
|
|
1431
|
+
pop: (arr) => arr.pop(),
|
|
1432
|
+
shift: (arr) => arr.shift(),
|
|
1433
|
+
unshift: (arr, item) => { arr.unshift(item); return arr; },
|
|
1434
|
+
reverse: (arr) => { arr.reverse(); return arr; },
|
|
1435
|
+
sort: (arr, fn) => { arr.sort(fn); return arr; },
|
|
1436
|
+
concat: (arr, ...others) => arr.concat(...others),
|
|
1437
|
+
flat: (arr, depth) => arr.flat(depth),
|
|
1438
|
+
length: (arr) => arr.length,
|
|
1439
|
+
});
|
|
1440
|
+
// Dictionary/Map Module (Python, JavaScript, Go, Rust)
|
|
1441
|
+
this.env.setModule("std::map", {
|
|
1442
|
+
keys: (obj) => Object.keys(obj),
|
|
1443
|
+
values: (obj) => Object.values(obj),
|
|
1444
|
+
entries: (obj) => Object.entries(obj),
|
|
1445
|
+
has: (obj, key) => key in obj,
|
|
1446
|
+
get: (obj, key) => obj[key],
|
|
1447
|
+
set: (obj, key, value) => { obj[key] = value; return obj; },
|
|
1448
|
+
delete: (obj, key) => { delete obj[key]; return obj; },
|
|
1449
|
+
clear: (obj) => { for (let k in obj)
|
|
1450
|
+
delete obj[k]; return obj; },
|
|
1451
|
+
length: (obj) => Object.keys(obj).length,
|
|
1452
|
+
assign: (target, ...sources) => Object.assign(target, ...sources),
|
|
1453
|
+
});
|
|
1454
|
+
// Type Module (Python, JavaScript, TypeScript, Go)
|
|
1455
|
+
this.env.setModule("std::type", {
|
|
1456
|
+
typeof: (x) => typeof x,
|
|
1457
|
+
isArray: (x) => Array.isArray(x),
|
|
1458
|
+
isObject: (x) => x !== null && typeof x === "object",
|
|
1459
|
+
isNull: (x) => x === null,
|
|
1460
|
+
isUndefined: (x) => x === undefined,
|
|
1461
|
+
isNumber: (x) => typeof x === "number",
|
|
1462
|
+
isString: (x) => typeof x === "string",
|
|
1463
|
+
isBoolean: (x) => typeof x === "boolean",
|
|
1464
|
+
isNaN: (x) => isNaN(x),
|
|
1465
|
+
isFinite: (x) => isFinite(x),
|
|
1466
|
+
isInteger: (x) => Number.isInteger(x),
|
|
1467
|
+
toNumber: (x) => Number(x),
|
|
1468
|
+
toString: (x) => String(x),
|
|
1469
|
+
toBoolean: (x) => Boolean(x),
|
|
1470
|
+
toInt: (x) => Math.floor(Number(x)),
|
|
1471
|
+
toFloat: (x) => parseFloat(String(x)),
|
|
1472
|
+
});
|
|
1473
|
+
// File Module (Python, Go, C, C++)
|
|
1474
|
+
this.env.setModule("std::file", {
|
|
1475
|
+
read: (path) => { try {
|
|
1476
|
+
return fs.readFileSync(path, "utf-8");
|
|
1477
|
+
}
|
|
1478
|
+
catch {
|
|
1479
|
+
return null;
|
|
1480
|
+
} },
|
|
1481
|
+
write: (path, content) => { try {
|
|
1482
|
+
fs.writeFileSync(path, content);
|
|
1483
|
+
return true;
|
|
1484
|
+
}
|
|
1485
|
+
catch {
|
|
1486
|
+
return false;
|
|
1487
|
+
} },
|
|
1488
|
+
append: (path, content) => { try {
|
|
1489
|
+
fs.appendFileSync(path, content);
|
|
1490
|
+
return true;
|
|
1491
|
+
}
|
|
1492
|
+
catch {
|
|
1493
|
+
return false;
|
|
1494
|
+
} },
|
|
1495
|
+
exists: (path) => fs.existsSync(path),
|
|
1496
|
+
delete: (path) => { try {
|
|
1497
|
+
fs.unlinkSync(path);
|
|
1498
|
+
return true;
|
|
1499
|
+
}
|
|
1500
|
+
catch {
|
|
1501
|
+
return false;
|
|
1502
|
+
} },
|
|
1503
|
+
isFile: (path) => { try {
|
|
1504
|
+
return fs.statSync(path).isFile();
|
|
1505
|
+
}
|
|
1506
|
+
catch {
|
|
1507
|
+
return false;
|
|
1508
|
+
} },
|
|
1509
|
+
isDirectory: (path) => { try {
|
|
1510
|
+
return fs.statSync(path).isDirectory();
|
|
1511
|
+
}
|
|
1512
|
+
catch {
|
|
1513
|
+
return false;
|
|
1514
|
+
} },
|
|
1515
|
+
mkdir: (path) => { try {
|
|
1516
|
+
fs.mkdirSync(path, { recursive: true });
|
|
1517
|
+
return true;
|
|
1518
|
+
}
|
|
1519
|
+
catch {
|
|
1520
|
+
return false;
|
|
1521
|
+
} },
|
|
1522
|
+
});
|
|
1523
|
+
// Regex Module (Python, Ruby, JavaScript, Go)
|
|
1524
|
+
this.env.setModule("std::regex", {
|
|
1525
|
+
match: (str, pattern, flags) => { try {
|
|
1526
|
+
const m = str.match(new RegExp(pattern, flags ?? ""));
|
|
1527
|
+
return m ?? null;
|
|
1528
|
+
}
|
|
1529
|
+
catch {
|
|
1530
|
+
return null;
|
|
1531
|
+
} },
|
|
1532
|
+
test: (str, pattern, flags) => { try {
|
|
1533
|
+
return new RegExp(pattern, flags ?? "").test(str);
|
|
1534
|
+
}
|
|
1535
|
+
catch {
|
|
1536
|
+
return false;
|
|
1537
|
+
} },
|
|
1538
|
+
search: (str, pattern, flags) => { try {
|
|
1539
|
+
return str.search(new RegExp(pattern, flags ?? ""));
|
|
1540
|
+
}
|
|
1541
|
+
catch {
|
|
1542
|
+
return -1;
|
|
1543
|
+
} },
|
|
1544
|
+
replace: (str, pattern, replacement, flags) => { try {
|
|
1545
|
+
return str.replace(new RegExp(pattern, flags ?? "g"), replacement);
|
|
1546
|
+
}
|
|
1547
|
+
catch {
|
|
1548
|
+
return str;
|
|
1549
|
+
} },
|
|
1550
|
+
});
|
|
1551
|
+
// DateTime Module (Python, Go, JavaScript, Ruby)
|
|
1552
|
+
this.env.setModule("std::time", {
|
|
1553
|
+
now: () => new Date().getTime(),
|
|
1554
|
+
timestamp: () => Math.floor(new Date().getTime() / 1000),
|
|
1555
|
+
getDate: (ms) => new Date(ms).getDate(),
|
|
1556
|
+
getMonth: (ms) => new Date(ms).getMonth() + 1,
|
|
1557
|
+
getYear: (ms) => new Date(ms).getFullYear(),
|
|
1558
|
+
getHours: (ms) => new Date(ms).getHours(),
|
|
1559
|
+
getMinutes: (ms) => new Date(ms).getMinutes(),
|
|
1560
|
+
getSeconds: (ms) => new Date(ms).getSeconds(),
|
|
1561
|
+
});
|
|
1562
|
+
// Set Module (Python, Go, Rust)
|
|
1563
|
+
this.env.setModule("std::set", {
|
|
1564
|
+
create: () => new Set(),
|
|
1565
|
+
add: (set, item) => { set.add(item); return set; },
|
|
1566
|
+
remove: (set, item) => { set.delete(item); return set; },
|
|
1567
|
+
has: (set, item) => set.has(item),
|
|
1568
|
+
size: (set) => set.size,
|
|
1569
|
+
clear: (set) => { set.clear(); return set; },
|
|
1570
|
+
union: (set1, set2) => new Set([...set1, ...set2]),
|
|
1571
|
+
intersection: (set1, set2) => new Set([...set1].filter(x => set2.has(x))),
|
|
1572
|
+
difference: (set1, set2) => new Set([...set1].filter(x => !set2.has(x))),
|
|
1573
|
+
});
|
|
1574
|
+
}
|
|
1575
|
+
interpret(statements) {
|
|
1576
|
+
for (const stmt of statements) {
|
|
1577
|
+
this.interpretStatement(stmt);
|
|
1578
|
+
if (this.controlFlow.type)
|
|
1579
|
+
break;
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
interpretStatement(stmt) {
|
|
1583
|
+
switch (stmt.kind) {
|
|
1584
|
+
case "let":
|
|
1585
|
+
const value = this.evaluateExpression(stmt.value);
|
|
1586
|
+
this.env.set(stmt.name, value, stmt.mutable);
|
|
1587
|
+
break;
|
|
1588
|
+
case "assignment":
|
|
1589
|
+
const newValue = this.evaluateExpression(stmt.value);
|
|
1590
|
+
this.env.update(stmt.target, newValue);
|
|
1591
|
+
break;
|
|
1592
|
+
case "expression":
|
|
1593
|
+
this.evaluateExpression(stmt.expr);
|
|
1594
|
+
break;
|
|
1595
|
+
case "if":
|
|
1596
|
+
const condition = this.evaluateExpression(stmt.condition);
|
|
1597
|
+
if (condition) {
|
|
1598
|
+
for (const s of stmt.then) {
|
|
1599
|
+
this.interpretStatement(s);
|
|
1600
|
+
if (this.controlFlow.type)
|
|
1601
|
+
return;
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
else if (stmt.else) {
|
|
1605
|
+
for (const s of stmt.else) {
|
|
1606
|
+
this.interpretStatement(s);
|
|
1607
|
+
if (this.controlFlow.type)
|
|
1608
|
+
return;
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
break;
|
|
1612
|
+
case "while":
|
|
1613
|
+
while (this.evaluateExpression(stmt.condition)) {
|
|
1614
|
+
for (const s of stmt.body) {
|
|
1615
|
+
this.interpretStatement(s);
|
|
1616
|
+
if (this.controlFlow.type === "break") {
|
|
1617
|
+
this.controlFlow.type = null;
|
|
1618
|
+
return;
|
|
1619
|
+
}
|
|
1620
|
+
if (this.controlFlow.type === "continue") {
|
|
1621
|
+
this.controlFlow.type = null;
|
|
1622
|
+
break;
|
|
1623
|
+
}
|
|
1624
|
+
if (this.controlFlow.type === "return")
|
|
1625
|
+
return;
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
break;
|
|
1629
|
+
case "for":
|
|
1630
|
+
this.interpretStatement(stmt.init);
|
|
1631
|
+
while (this.evaluateExpression(stmt.condition)) {
|
|
1632
|
+
for (const s of stmt.body) {
|
|
1633
|
+
this.interpretStatement(s);
|
|
1634
|
+
if (this.controlFlow.type === "break") {
|
|
1635
|
+
this.controlFlow.type = null;
|
|
1636
|
+
return;
|
|
1637
|
+
}
|
|
1638
|
+
if (this.controlFlow.type === "continue") {
|
|
1639
|
+
this.controlFlow.type = null;
|
|
1640
|
+
break;
|
|
1641
|
+
}
|
|
1642
|
+
if (this.controlFlow.type === "return")
|
|
1643
|
+
return;
|
|
1644
|
+
}
|
|
1645
|
+
this.interpretStatement(stmt.update);
|
|
1646
|
+
}
|
|
1647
|
+
break;
|
|
1648
|
+
case "return":
|
|
1649
|
+
this.controlFlow.value = stmt.value
|
|
1650
|
+
? this.evaluateExpression(stmt.value)
|
|
1651
|
+
: null;
|
|
1652
|
+
this.controlFlow.type = "return";
|
|
1653
|
+
break;
|
|
1654
|
+
case "break":
|
|
1655
|
+
this.controlFlow.type = "break";
|
|
1656
|
+
break;
|
|
1657
|
+
case "continue":
|
|
1658
|
+
this.controlFlow.type = "continue";
|
|
1659
|
+
break;
|
|
1660
|
+
case "function":
|
|
1661
|
+
this.env.setFunction(stmt.name, [
|
|
1662
|
+
...stmt.params.map((p) => p.name),
|
|
1663
|
+
], stmt.body);
|
|
1664
|
+
break;
|
|
1665
|
+
case "import":
|
|
1666
|
+
// Bind module to variable name
|
|
1667
|
+
const module = this.env.getModule(stmt.module);
|
|
1668
|
+
if (!module) {
|
|
1669
|
+
throw new Error(`Module not found: ${stmt.module}`);
|
|
1670
|
+
}
|
|
1671
|
+
this.env.set(stmt.name, module, false);
|
|
1672
|
+
break;
|
|
1673
|
+
}
|
|
1674
|
+
}
|
|
1675
|
+
evaluateExpression(expr) {
|
|
1676
|
+
switch (expr.kind) {
|
|
1677
|
+
case "literal":
|
|
1678
|
+
return expr.value;
|
|
1679
|
+
case "identifier":
|
|
1680
|
+
return this.env.get(expr.name);
|
|
1681
|
+
case "binary":
|
|
1682
|
+
const left = this.evaluateExpression(expr.left);
|
|
1683
|
+
const right = this.evaluateExpression(expr.right);
|
|
1684
|
+
switch (expr.op) {
|
|
1685
|
+
case "+":
|
|
1686
|
+
return left + right;
|
|
1687
|
+
case "-":
|
|
1688
|
+
return left - right;
|
|
1689
|
+
case "*":
|
|
1690
|
+
return left * right;
|
|
1691
|
+
case "/":
|
|
1692
|
+
return left / right;
|
|
1693
|
+
case "%":
|
|
1694
|
+
return left % right;
|
|
1695
|
+
case "==":
|
|
1696
|
+
return left === right;
|
|
1697
|
+
case "!=":
|
|
1698
|
+
return left !== right;
|
|
1699
|
+
case "<":
|
|
1700
|
+
return left < right;
|
|
1701
|
+
case ">":
|
|
1702
|
+
return left > right;
|
|
1703
|
+
case "<=":
|
|
1704
|
+
return left <= right;
|
|
1705
|
+
case ">=":
|
|
1706
|
+
return left >= right;
|
|
1707
|
+
case "&&":
|
|
1708
|
+
return left && right;
|
|
1709
|
+
case "||":
|
|
1710
|
+
return left || right;
|
|
1711
|
+
default:
|
|
1712
|
+
return null;
|
|
1713
|
+
}
|
|
1714
|
+
case "unary":
|
|
1715
|
+
const operand = this.evaluateExpression(expr.operand);
|
|
1716
|
+
switch (expr.op) {
|
|
1717
|
+
case "-":
|
|
1718
|
+
return -operand;
|
|
1719
|
+
case "+":
|
|
1720
|
+
return +operand;
|
|
1721
|
+
case "!":
|
|
1722
|
+
return !operand;
|
|
1723
|
+
case "~":
|
|
1724
|
+
return ~operand;
|
|
1725
|
+
default:
|
|
1726
|
+
return null;
|
|
1727
|
+
}
|
|
1728
|
+
case "call":
|
|
1729
|
+
const func = this.evaluateExpression(expr.func);
|
|
1730
|
+
const args = expr.args.map((a) => this.evaluateExpression(a));
|
|
1731
|
+
// Check for built-in functions from extended language features
|
|
1732
|
+
if (expr.func.kind === "identifier" && expr.func.name in BUILTIN_FUNCTIONS) {
|
|
1733
|
+
return BUILTIN_FUNCTIONS[expr.func.name](args);
|
|
1734
|
+
}
|
|
1735
|
+
if (typeof func === "function") {
|
|
1736
|
+
return func(...args);
|
|
1737
|
+
}
|
|
1738
|
+
throw new Error("Not a function");
|
|
1739
|
+
case "member":
|
|
1740
|
+
const obj = this.evaluateExpression(expr.object);
|
|
1741
|
+
return obj?.[expr.property];
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
}
|
|
1745
|
+
// ============================================================================
|
|
1746
|
+
// C CODE GENERATOR
|
|
1747
|
+
// ============================================================================
|
|
1748
|
+
class CGenerator {
|
|
1749
|
+
constructor() {
|
|
1750
|
+
this.code = [];
|
|
1751
|
+
}
|
|
1752
|
+
generate(statements) {
|
|
1753
|
+
this.code = [];
|
|
1754
|
+
this.code.push("#include <stdio.h>");
|
|
1755
|
+
this.code.push("#include <math.h>");
|
|
1756
|
+
this.code.push("int main() {");
|
|
1757
|
+
for (const stmt of statements) {
|
|
1758
|
+
this.generateStatement(stmt);
|
|
1759
|
+
}
|
|
1760
|
+
this.code.push("return 0;");
|
|
1761
|
+
this.code.push("}");
|
|
1762
|
+
return this.code.join("\n");
|
|
1763
|
+
}
|
|
1764
|
+
generateStatement(stmt) {
|
|
1765
|
+
switch (stmt.kind) {
|
|
1766
|
+
case "let":
|
|
1767
|
+
const ctype = this.typeToCString(stmt.type);
|
|
1768
|
+
const value = this.generateExpression(stmt.value);
|
|
1769
|
+
this.code.push(`${ctype} ${stmt.name} = ${value};`);
|
|
1770
|
+
break;
|
|
1771
|
+
case "expression":
|
|
1772
|
+
const expr = this.generateExpression(stmt.expr);
|
|
1773
|
+
this.code.push(`${expr};`);
|
|
1774
|
+
break;
|
|
1775
|
+
case "if":
|
|
1776
|
+
const condition = this.generateExpression(stmt.condition);
|
|
1777
|
+
this.code.push(`if (${condition}) {`);
|
|
1778
|
+
for (const s of stmt.then) {
|
|
1779
|
+
this.generateStatement(s);
|
|
1780
|
+
}
|
|
1781
|
+
this.code.push("}");
|
|
1782
|
+
break;
|
|
1783
|
+
case "return":
|
|
1784
|
+
const value2 = stmt.value
|
|
1785
|
+
? this.generateExpression(stmt.value)
|
|
1786
|
+
: "0";
|
|
1787
|
+
this.code.push(`return ${value2};`);
|
|
1788
|
+
break;
|
|
1789
|
+
}
|
|
1790
|
+
}
|
|
1791
|
+
generateExpression(expr) {
|
|
1792
|
+
switch (expr.kind) {
|
|
1793
|
+
case "literal":
|
|
1794
|
+
if (typeof expr.value === "string") {
|
|
1795
|
+
return `"${expr.value}"`;
|
|
1796
|
+
}
|
|
1797
|
+
return String(expr.value);
|
|
1798
|
+
case "identifier":
|
|
1799
|
+
return expr.name;
|
|
1800
|
+
case "binary":
|
|
1801
|
+
const left = this.generateExpression(expr.left);
|
|
1802
|
+
const right = this.generateExpression(expr.right);
|
|
1803
|
+
return `(${left} ${expr.op} ${right})`;
|
|
1804
|
+
case "unary":
|
|
1805
|
+
const operand = this.generateExpression(expr.operand);
|
|
1806
|
+
return `(${expr.op}${operand})`;
|
|
1807
|
+
case "call":
|
|
1808
|
+
const func = this.generateExpression(expr.func);
|
|
1809
|
+
const args = expr.args.map((a) => this.generateExpression(a));
|
|
1810
|
+
return `${func}(${args.join(", ")})`;
|
|
1811
|
+
case "member":
|
|
1812
|
+
const obj = this.generateExpression(expr.object);
|
|
1813
|
+
return `${obj}.${expr.property}`;
|
|
1814
|
+
default:
|
|
1815
|
+
return "";
|
|
1816
|
+
}
|
|
1817
|
+
}
|
|
1818
|
+
typeToCString(type) {
|
|
1819
|
+
if (type.kind === "primitive") {
|
|
1820
|
+
switch (type.primitive) {
|
|
1821
|
+
case "int":
|
|
1822
|
+
return "int";
|
|
1823
|
+
case "float":
|
|
1824
|
+
return "double";
|
|
1825
|
+
case "bool":
|
|
1826
|
+
return "int";
|
|
1827
|
+
case "char":
|
|
1828
|
+
return "char";
|
|
1829
|
+
case "string":
|
|
1830
|
+
return "char*";
|
|
1831
|
+
default:
|
|
1832
|
+
return "int";
|
|
1833
|
+
}
|
|
1834
|
+
}
|
|
1835
|
+
return "int";
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
// ============================================================================
|
|
1839
|
+
// MAIN
|
|
1840
|
+
// ============================================================================
|
|
1841
|
+
const args = process.argv.slice(2);
|
|
1842
|
+
if (args.length === 0) {
|
|
1843
|
+
console.error("Usage: strata <file.str>");
|
|
1844
|
+
process.exit(1);
|
|
1845
|
+
}
|
|
1846
|
+
const startTime = performance.now();
|
|
1847
|
+
const filePath = args[0];
|
|
1848
|
+
const source = fs.readFileSync(filePath, "utf-8");
|
|
1849
|
+
try {
|
|
1850
|
+
const parser = new Parser(source);
|
|
1851
|
+
const statements = parser.parse();
|
|
1852
|
+
const typeChecker = new TypeChecker();
|
|
1853
|
+
typeChecker.check(statements);
|
|
1854
|
+
const interpreter = new Interpreter();
|
|
1855
|
+
interpreter.interpret(statements);
|
|
1856
|
+
const generator = new CGenerator();
|
|
1857
|
+
const cCode = generator.generate(statements);
|
|
1858
|
+
fs.writeFileSync("out.c", cCode);
|
|
1859
|
+
const endTime = performance.now();
|
|
1860
|
+
const elapsed = (endTime - startTime).toFixed(2);
|
|
1861
|
+
console.error(`Executed in ${elapsed}ms`);
|
|
1862
|
+
}
|
|
1863
|
+
catch (error) {
|
|
1864
|
+
console.error("Error:", error instanceof Error ? error.message : String(error));
|
|
1865
|
+
process.exit(1);
|
|
1866
|
+
}
|