arc-lang 0.5.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.
- package/LICENSE +21 -0
- package/README.md +148 -0
- package/dist/ast.d.ts +298 -0
- package/dist/ast.js +2 -0
- package/dist/build.d.ts +7 -0
- package/dist/build.js +138 -0
- package/dist/codegen-js.d.ts +2 -0
- package/dist/codegen-js.js +168 -0
- package/dist/codegen.d.ts +2 -0
- package/dist/codegen.js +364 -0
- package/dist/errors.d.ts +52 -0
- package/dist/errors.js +229 -0
- package/dist/formatter.d.ts +5 -0
- package/dist/formatter.js +361 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +165 -0
- package/dist/interpreter.d.ts +39 -0
- package/dist/interpreter.js +668 -0
- package/dist/ir.d.ts +126 -0
- package/dist/ir.js +610 -0
- package/dist/lexer.d.ts +79 -0
- package/dist/lexer.js +335 -0
- package/dist/linter.d.ts +15 -0
- package/dist/linter.js +382 -0
- package/dist/lsp.d.ts +1 -0
- package/dist/lsp.js +253 -0
- package/dist/modules.d.ts +24 -0
- package/dist/modules.js +115 -0
- package/dist/optimizer.d.ts +17 -0
- package/dist/optimizer.js +481 -0
- package/dist/package-manager.d.ts +31 -0
- package/dist/package-manager.js +180 -0
- package/dist/parser.d.ts +42 -0
- package/dist/parser.js +779 -0
- package/dist/repl.d.ts +1 -0
- package/dist/repl.js +120 -0
- package/dist/security.d.ts +48 -0
- package/dist/security.js +198 -0
- package/dist/semantic.d.ts +7 -0
- package/dist/semantic.js +327 -0
- package/dist/typechecker.d.ts +7 -0
- package/dist/typechecker.js +132 -0
- package/dist/version.d.ts +26 -0
- package/dist/version.js +71 -0
- package/package.json +51 -0
package/dist/semantic.js
ADDED
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
// Arc Language Semantic Analyzer
|
|
2
|
+
// Performs: name resolution, scope validation, mutability checking, arity checking, match exhaustiveness warnings
|
|
3
|
+
class Scope {
|
|
4
|
+
parent;
|
|
5
|
+
symbols = new Map();
|
|
6
|
+
constructor(parent) {
|
|
7
|
+
this.parent = parent;
|
|
8
|
+
}
|
|
9
|
+
define(name, info) {
|
|
10
|
+
// Allow redefinition in same scope (shadowing)
|
|
11
|
+
this.symbols.set(name, info);
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
lookup(name) {
|
|
15
|
+
const s = this.symbols.get(name);
|
|
16
|
+
if (s)
|
|
17
|
+
return s;
|
|
18
|
+
if (this.parent)
|
|
19
|
+
return this.parent.lookup(name);
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
lookupLocal(name) {
|
|
23
|
+
return this.symbols.get(name);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
// Built-in names that are always available
|
|
27
|
+
const BUILTINS = new Set([
|
|
28
|
+
"print", "println", "len", "push", "pop", "map", "filter", "reduce",
|
|
29
|
+
"range", "keys", "values", "entries", "type", "str", "int", "float",
|
|
30
|
+
"sort", "reverse", "join", "split", "trim", "contains", "starts_with",
|
|
31
|
+
"ends_with", "replace", "to_upper", "to_lower", "slice", "flat",
|
|
32
|
+
"flat_map", "zip", "enumerate", "sum", "min", "max", "abs",
|
|
33
|
+
"head", "tail", "take", "drop", "find", "any", "all", "count",
|
|
34
|
+
"unique", "group_by", "sort_by", "chunk", "window", "scan",
|
|
35
|
+
"assert", "assert_eq", "Some", "None", "Ok", "Err",
|
|
36
|
+
"Math", "String", "List", "Map", "JSON", "Error",
|
|
37
|
+
"true", "false", "nil",
|
|
38
|
+
"is_nil", "is_some", "unwrap", "unwrap_or",
|
|
39
|
+
"typeof", "chars", "char_at", "index_of", "last_index_of",
|
|
40
|
+
"parse_int", "parse_float", "to_string", "format",
|
|
41
|
+
"now", "sleep", "random", "floor", "ceil", "round", "sqrt", "pow", "log",
|
|
42
|
+
"read_file", "write_file", "http_get", "http_post",
|
|
43
|
+
"append", "prepend", "concat", "flatten", "each", "fold",
|
|
44
|
+
"first", "last", "rest", "init", "is_empty", "size",
|
|
45
|
+
"repeat", "pad_left", "pad_right", "upper", "lower",
|
|
46
|
+
"every", "some", "none", "includes",
|
|
47
|
+
]);
|
|
48
|
+
export function analyze(program) {
|
|
49
|
+
const diagnostics = [];
|
|
50
|
+
const globalScope = new Scope();
|
|
51
|
+
function error(loc, message) {
|
|
52
|
+
diagnostics.push({ level: "error", message, loc });
|
|
53
|
+
}
|
|
54
|
+
function warning(loc, message) {
|
|
55
|
+
diagnostics.push({ level: "warning", message, loc });
|
|
56
|
+
}
|
|
57
|
+
function analyzeExpr(expr, scope) {
|
|
58
|
+
switch (expr.kind) {
|
|
59
|
+
case "IntLiteral":
|
|
60
|
+
case "FloatLiteral":
|
|
61
|
+
case "BoolLiteral":
|
|
62
|
+
case "NilLiteral":
|
|
63
|
+
case "StringLiteral":
|
|
64
|
+
break;
|
|
65
|
+
case "StringInterp":
|
|
66
|
+
for (const part of expr.parts) {
|
|
67
|
+
if (typeof part !== "string") {
|
|
68
|
+
analyzeExpr(part, scope);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
break;
|
|
72
|
+
case "Identifier": {
|
|
73
|
+
const sym = scope.lookup(expr.name);
|
|
74
|
+
if (!sym && !BUILTINS.has(expr.name)) {
|
|
75
|
+
error(expr.loc, `Undefined variable: '${expr.name}'`);
|
|
76
|
+
}
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
case "BinaryExpr":
|
|
80
|
+
analyzeExpr(expr.left, scope);
|
|
81
|
+
analyzeExpr(expr.right, scope);
|
|
82
|
+
break;
|
|
83
|
+
case "UnaryExpr":
|
|
84
|
+
analyzeExpr(expr.operand, scope);
|
|
85
|
+
break;
|
|
86
|
+
case "CallExpr": {
|
|
87
|
+
analyzeExpr(expr.callee, scope);
|
|
88
|
+
for (const arg of expr.args) {
|
|
89
|
+
analyzeExpr(arg, scope);
|
|
90
|
+
}
|
|
91
|
+
// Arity check for known functions
|
|
92
|
+
if (expr.callee.kind === "Identifier") {
|
|
93
|
+
const sym = scope.lookup(expr.callee.name);
|
|
94
|
+
if (sym && sym.kind === "function" && sym.arity !== undefined) {
|
|
95
|
+
if (expr.args.length !== sym.arity) {
|
|
96
|
+
error(expr.loc, `Function '${expr.callee.name}' expects ${sym.arity} argument(s), but got ${expr.args.length}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
case "MemberExpr":
|
|
103
|
+
analyzeExpr(expr.object, scope);
|
|
104
|
+
break;
|
|
105
|
+
case "IndexExpr":
|
|
106
|
+
analyzeExpr(expr.object, scope);
|
|
107
|
+
analyzeExpr(expr.index, scope);
|
|
108
|
+
break;
|
|
109
|
+
case "PipelineExpr":
|
|
110
|
+
analyzeExpr(expr.left, scope);
|
|
111
|
+
analyzeExpr(expr.right, scope);
|
|
112
|
+
break;
|
|
113
|
+
case "IfExpr":
|
|
114
|
+
analyzeExpr(expr.condition, scope);
|
|
115
|
+
analyzeExpr(expr.then, scope);
|
|
116
|
+
if (expr.else_)
|
|
117
|
+
analyzeExpr(expr.else_, scope);
|
|
118
|
+
break;
|
|
119
|
+
case "MatchExpr":
|
|
120
|
+
analyzeExpr(expr.subject, scope);
|
|
121
|
+
analyzeMatchExhaustiveness(expr, scope);
|
|
122
|
+
for (const arm of expr.arms) {
|
|
123
|
+
const armScope = new Scope(scope);
|
|
124
|
+
analyzePattern(arm.pattern, armScope);
|
|
125
|
+
if (arm.guard)
|
|
126
|
+
analyzeExpr(arm.guard, armScope);
|
|
127
|
+
analyzeExpr(arm.body, armScope);
|
|
128
|
+
}
|
|
129
|
+
break;
|
|
130
|
+
case "LambdaExpr": {
|
|
131
|
+
const lambdaScope = new Scope(scope);
|
|
132
|
+
for (const p of expr.params) {
|
|
133
|
+
lambdaScope.define(p, { mutable: false, kind: "parameter" });
|
|
134
|
+
}
|
|
135
|
+
analyzeExpr(expr.body, lambdaScope);
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
case "ListLiteral":
|
|
139
|
+
for (const el of expr.elements)
|
|
140
|
+
analyzeExpr(el, scope);
|
|
141
|
+
break;
|
|
142
|
+
case "MapLiteral":
|
|
143
|
+
for (const entry of expr.entries) {
|
|
144
|
+
if (typeof entry.key !== "string")
|
|
145
|
+
analyzeExpr(entry.key, scope);
|
|
146
|
+
analyzeExpr(entry.value, scope);
|
|
147
|
+
}
|
|
148
|
+
break;
|
|
149
|
+
case "ListComprehension": {
|
|
150
|
+
analyzeExpr(expr.iterable, scope);
|
|
151
|
+
const compScope = new Scope(scope);
|
|
152
|
+
compScope.define(expr.variable, { mutable: false, kind: "loop-var" });
|
|
153
|
+
analyzeExpr(expr.expr, compScope);
|
|
154
|
+
if (expr.filter)
|
|
155
|
+
analyzeExpr(expr.filter, compScope);
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
case "ToolCallExpr":
|
|
159
|
+
analyzeExpr(expr.arg, scope);
|
|
160
|
+
if (expr.body)
|
|
161
|
+
analyzeExpr(expr.body, scope);
|
|
162
|
+
break;
|
|
163
|
+
case "RangeExpr":
|
|
164
|
+
analyzeExpr(expr.start, scope);
|
|
165
|
+
analyzeExpr(expr.end, scope);
|
|
166
|
+
break;
|
|
167
|
+
case "BlockExpr": {
|
|
168
|
+
const blockScope = new Scope(scope);
|
|
169
|
+
analyzeStmts(expr.stmts, blockScope);
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
case "AsyncExpr":
|
|
173
|
+
analyzeExpr(expr.body, scope);
|
|
174
|
+
break;
|
|
175
|
+
case "AwaitExpr":
|
|
176
|
+
analyzeExpr(expr.expr, scope);
|
|
177
|
+
break;
|
|
178
|
+
case "FetchExpr":
|
|
179
|
+
for (const t of expr.targets)
|
|
180
|
+
analyzeExpr(t, scope);
|
|
181
|
+
break;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
function analyzePattern(pattern, scope) {
|
|
185
|
+
switch (pattern.kind) {
|
|
186
|
+
case "WildcardPattern":
|
|
187
|
+
break;
|
|
188
|
+
case "LiteralPattern":
|
|
189
|
+
break;
|
|
190
|
+
case "BindingPattern":
|
|
191
|
+
scope.define(pattern.name, { mutable: false, kind: "variable" });
|
|
192
|
+
break;
|
|
193
|
+
case "ArrayPattern":
|
|
194
|
+
for (const el of pattern.elements)
|
|
195
|
+
analyzePattern(el, scope);
|
|
196
|
+
break;
|
|
197
|
+
case "OrPattern":
|
|
198
|
+
for (const p of pattern.patterns)
|
|
199
|
+
analyzePattern(p, scope);
|
|
200
|
+
break;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
function analyzeMatchExhaustiveness(expr, _scope) {
|
|
204
|
+
const arms = expr.arms;
|
|
205
|
+
if (arms.length === 0) {
|
|
206
|
+
warning(expr.loc, "Match expression has no arms");
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
// Check if there's a wildcard or binding (catch-all) pattern without a guard
|
|
210
|
+
const hasCatchAll = arms.some(arm => !arm.guard && isCatchAllPattern(arm.pattern));
|
|
211
|
+
if (!hasCatchAll) {
|
|
212
|
+
warning(expr.loc, "Match expression may not be exhaustive — consider adding a wildcard '_' arm");
|
|
213
|
+
}
|
|
214
|
+
// Check for unreachable arms (arms after a catch-all without guard)
|
|
215
|
+
for (let i = 0; i < arms.length - 1; i++) {
|
|
216
|
+
if (!arms[i].guard && isCatchAllPattern(arms[i].pattern)) {
|
|
217
|
+
warning(arms[i + 1].body.loc, "Unreachable match arm — previous arm catches all patterns");
|
|
218
|
+
break;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
function isCatchAllPattern(pattern) {
|
|
223
|
+
return pattern.kind === "WildcardPattern" || pattern.kind === "BindingPattern";
|
|
224
|
+
}
|
|
225
|
+
function analyzeStmts(stmts, scope) {
|
|
226
|
+
// First pass: register all function declarations (hoisting)
|
|
227
|
+
for (const stmt of stmts) {
|
|
228
|
+
if (stmt.kind === "FnStmt") {
|
|
229
|
+
scope.define(stmt.name, {
|
|
230
|
+
mutable: false,
|
|
231
|
+
kind: "function",
|
|
232
|
+
arity: stmt.params.length,
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
for (const stmt of stmts) {
|
|
237
|
+
analyzeStmt(stmt, scope);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
function analyzeStmt(stmt, scope) {
|
|
241
|
+
switch (stmt.kind) {
|
|
242
|
+
case "LetStmt": {
|
|
243
|
+
analyzeExpr(stmt.value, scope);
|
|
244
|
+
if (typeof stmt.name === "string") {
|
|
245
|
+
scope.define(stmt.name, {
|
|
246
|
+
mutable: stmt.mutable,
|
|
247
|
+
kind: "variable",
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
// DestructureTarget
|
|
252
|
+
for (const n of stmt.name.names) {
|
|
253
|
+
scope.define(n, {
|
|
254
|
+
mutable: stmt.mutable,
|
|
255
|
+
kind: "destructured",
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
break;
|
|
260
|
+
}
|
|
261
|
+
case "FnStmt": {
|
|
262
|
+
// Already registered in first pass; now analyze body
|
|
263
|
+
const fnScope = new Scope(scope);
|
|
264
|
+
for (const p of stmt.params) {
|
|
265
|
+
fnScope.define(p, { mutable: false, kind: "parameter" });
|
|
266
|
+
}
|
|
267
|
+
analyzeExpr(stmt.body, fnScope);
|
|
268
|
+
break;
|
|
269
|
+
}
|
|
270
|
+
case "ForStmt": {
|
|
271
|
+
analyzeExpr(stmt.iterable, scope);
|
|
272
|
+
const forScope = new Scope(scope);
|
|
273
|
+
forScope.define(stmt.variable, { mutable: false, kind: "loop-var" });
|
|
274
|
+
analyzeExpr(stmt.body, forScope);
|
|
275
|
+
break;
|
|
276
|
+
}
|
|
277
|
+
case "DoStmt":
|
|
278
|
+
analyzeExpr(stmt.body, scope);
|
|
279
|
+
analyzeExpr(stmt.condition, scope);
|
|
280
|
+
break;
|
|
281
|
+
case "ExprStmt":
|
|
282
|
+
analyzeExpr(stmt.expr, scope);
|
|
283
|
+
break;
|
|
284
|
+
case "UseStmt":
|
|
285
|
+
// Register imported names
|
|
286
|
+
if (stmt.imports) {
|
|
287
|
+
for (const imp of stmt.imports) {
|
|
288
|
+
scope.define(imp, { mutable: false, kind: "import" });
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
else if (stmt.wildcard) {
|
|
292
|
+
// Wildcard import — can't track names statically
|
|
293
|
+
}
|
|
294
|
+
else {
|
|
295
|
+
// Default: import the module name
|
|
296
|
+
const moduleName = stmt.path[stmt.path.length - 1];
|
|
297
|
+
scope.define(moduleName, { mutable: false, kind: "import" });
|
|
298
|
+
}
|
|
299
|
+
break;
|
|
300
|
+
case "TypeStmt":
|
|
301
|
+
scope.define(stmt.name, { mutable: false, kind: "type" });
|
|
302
|
+
break;
|
|
303
|
+
case "AssignStmt": {
|
|
304
|
+
const sym = scope.lookup(stmt.target);
|
|
305
|
+
if (!sym && !BUILTINS.has(stmt.target)) {
|
|
306
|
+
error(stmt.loc, `Undefined variable: '${stmt.target}'`);
|
|
307
|
+
}
|
|
308
|
+
else if (sym && !sym.mutable) {
|
|
309
|
+
error(stmt.loc, `Cannot reassign immutable variable: '${stmt.target}'`);
|
|
310
|
+
}
|
|
311
|
+
analyzeExpr(stmt.value, scope);
|
|
312
|
+
break;
|
|
313
|
+
}
|
|
314
|
+
case "MemberAssignStmt":
|
|
315
|
+
analyzeExpr(stmt.object, scope);
|
|
316
|
+
analyzeExpr(stmt.value, scope);
|
|
317
|
+
break;
|
|
318
|
+
case "IndexAssignStmt":
|
|
319
|
+
analyzeExpr(stmt.object, scope);
|
|
320
|
+
analyzeExpr(stmt.index, scope);
|
|
321
|
+
analyzeExpr(stmt.value, scope);
|
|
322
|
+
break;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
analyzeStmts(program.stmts, globalScope);
|
|
326
|
+
return diagnostics;
|
|
327
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// Arc Language Type Checker (basic)
|
|
2
|
+
export function typecheck(program) {
|
|
3
|
+
const diagnostics = [];
|
|
4
|
+
const typeDefinitions = new Map();
|
|
5
|
+
const knownBaseTypes = new Set(["Int", "Float", "String", "Bool", "Nil", "Any"]);
|
|
6
|
+
// First pass: collect type definitions
|
|
7
|
+
for (const stmt of program.stmts) {
|
|
8
|
+
if (stmt.kind === "TypeStmt") {
|
|
9
|
+
typeDefinitions.set(stmt.name, stmt.def);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
// Second pass: validate type definitions
|
|
13
|
+
for (const [name, def] of typeDefinitions) {
|
|
14
|
+
validateTypeExpr(def, name);
|
|
15
|
+
}
|
|
16
|
+
// Third pass: check match exhaustiveness
|
|
17
|
+
for (const stmt of program.stmts) {
|
|
18
|
+
walkStmt(stmt);
|
|
19
|
+
}
|
|
20
|
+
function validateTypeExpr(typeExpr, context) {
|
|
21
|
+
switch (typeExpr.kind) {
|
|
22
|
+
case "NamedType":
|
|
23
|
+
if (!knownBaseTypes.has(typeExpr.name) && !typeDefinitions.has(typeExpr.name)) {
|
|
24
|
+
// Could be a type parameter, so just warn
|
|
25
|
+
// Don't warn for single-letter names (type params)
|
|
26
|
+
if (typeExpr.name.length > 1) {
|
|
27
|
+
diagnostics.push({
|
|
28
|
+
level: "warning",
|
|
29
|
+
message: `Unknown type '${typeExpr.name}' in definition of '${context}'`,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
break;
|
|
34
|
+
case "ConstrainedType":
|
|
35
|
+
validateTypeExpr(typeExpr.base, context);
|
|
36
|
+
if (typeExpr.base.kind === "NamedType") {
|
|
37
|
+
const baseName = typeExpr.base.name;
|
|
38
|
+
if (typeExpr.constraint === "matching" && baseName !== "String") {
|
|
39
|
+
diagnostics.push({
|
|
40
|
+
level: "error",
|
|
41
|
+
message: `'matching' constraint can only be applied to String type, got '${baseName}' in '${context}'`,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
if (typeExpr.constraint === "where" && !knownBaseTypes.has(baseName) && !typeDefinitions.has(baseName)) {
|
|
45
|
+
diagnostics.push({
|
|
46
|
+
level: "warning",
|
|
47
|
+
message: `'where' constraint on unknown base type '${baseName}' in '${context}'`,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
break;
|
|
52
|
+
case "RecordType":
|
|
53
|
+
for (const field of typeExpr.fields) {
|
|
54
|
+
validateTypeExpr(field.type, context);
|
|
55
|
+
}
|
|
56
|
+
break;
|
|
57
|
+
case "FunctionType":
|
|
58
|
+
for (const param of typeExpr.params) {
|
|
59
|
+
validateTypeExpr(param, context);
|
|
60
|
+
}
|
|
61
|
+
validateTypeExpr(typeExpr.ret, context);
|
|
62
|
+
break;
|
|
63
|
+
case "EnumType":
|
|
64
|
+
for (const variant of typeExpr.variants) {
|
|
65
|
+
if (variant.params) {
|
|
66
|
+
for (const p of variant.params) {
|
|
67
|
+
validateTypeExpr(p, context);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
break;
|
|
72
|
+
case "GenericType":
|
|
73
|
+
for (const p of typeExpr.params) {
|
|
74
|
+
validateTypeExpr(p, context);
|
|
75
|
+
}
|
|
76
|
+
break;
|
|
77
|
+
case "UnionType":
|
|
78
|
+
for (const v of typeExpr.variants) {
|
|
79
|
+
validateTypeExpr(v, context);
|
|
80
|
+
}
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function walkStmt(stmt) {
|
|
85
|
+
if (stmt.kind === "ExprStmt") {
|
|
86
|
+
walkExpr(stmt.expr);
|
|
87
|
+
}
|
|
88
|
+
else if (stmt.kind === "LetStmt") {
|
|
89
|
+
walkExpr(stmt.value);
|
|
90
|
+
}
|
|
91
|
+
else if (stmt.kind === "FnStmt") {
|
|
92
|
+
walkExpr(stmt.body);
|
|
93
|
+
}
|
|
94
|
+
else if (stmt.kind === "ForStmt") {
|
|
95
|
+
walkExpr(stmt.iterable);
|
|
96
|
+
walkExpr(stmt.body);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function walkExpr(expr) {
|
|
100
|
+
if (expr.kind === "MatchExpr") {
|
|
101
|
+
checkMatchExhaustiveness(expr);
|
|
102
|
+
for (const arm of expr.arms) {
|
|
103
|
+
walkExpr(arm.body);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
else if (expr.kind === "BlockExpr") {
|
|
107
|
+
for (const s of expr.stmts) {
|
|
108
|
+
walkStmt(s);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
else if (expr.kind === "IfExpr") {
|
|
112
|
+
walkExpr(expr.then);
|
|
113
|
+
if (expr.else_)
|
|
114
|
+
walkExpr(expr.else_);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
function checkMatchExhaustiveness(matchExpr) {
|
|
118
|
+
const hasWildcard = matchExpr.arms.some(a => a.pattern.kind === "WildcardPattern");
|
|
119
|
+
if (!hasWildcard) {
|
|
120
|
+
// Check if subject is a known enum type
|
|
121
|
+
if (matchExpr.subject.kind === "Identifier") {
|
|
122
|
+
// Can't easily determine type without full type inference, so just warn
|
|
123
|
+
diagnostics.push({
|
|
124
|
+
level: "warning",
|
|
125
|
+
message: `Match expression may not be exhaustive (no wildcard pattern)`,
|
|
126
|
+
loc: matchExpr.loc,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return diagnostics;
|
|
132
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export declare const ARC_VERSION = "0.5.0";
|
|
2
|
+
export declare const ARC_BUILD_DATE: string;
|
|
3
|
+
export declare const ARC_PLATFORM: string;
|
|
4
|
+
/** Print version info */
|
|
5
|
+
export declare function printVersion(): void;
|
|
6
|
+
/** Semver comparison: returns -1, 0, or 1 */
|
|
7
|
+
export declare function compareSemver(a: string, b: string): number;
|
|
8
|
+
/** Check if a manifest's arc version requirement is compatible */
|
|
9
|
+
export declare function checkVersionCompatibility(required: string): {
|
|
10
|
+
compatible: boolean;
|
|
11
|
+
message: string;
|
|
12
|
+
};
|
|
13
|
+
/** Deprecation registry */
|
|
14
|
+
interface DeprecationEntry {
|
|
15
|
+
feature: string;
|
|
16
|
+
since: string;
|
|
17
|
+
removeIn: string;
|
|
18
|
+
migration: string;
|
|
19
|
+
}
|
|
20
|
+
/** Register a deprecated feature */
|
|
21
|
+
export declare function deprecate(feature: string, since: string, removeIn: string, migration: string): void;
|
|
22
|
+
/** Emit a deprecation warning (once per feature per session) */
|
|
23
|
+
export declare function emitDeprecationWarning(feature: string): void;
|
|
24
|
+
/** Get all registered deprecations */
|
|
25
|
+
export declare function getDeprecations(): readonly DeprecationEntry[];
|
|
26
|
+
export {};
|
package/dist/version.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// Arc Version System
|
|
2
|
+
export const ARC_VERSION = "0.5.0";
|
|
3
|
+
export const ARC_BUILD_DATE = new Date().toISOString().split("T")[0];
|
|
4
|
+
export const ARC_PLATFORM = `${process.platform}-${process.arch}`;
|
|
5
|
+
/** Print version info */
|
|
6
|
+
export function printVersion() {
|
|
7
|
+
console.log(`Arc ${ARC_VERSION}`);
|
|
8
|
+
console.log(`Build date: ${ARC_BUILD_DATE}`);
|
|
9
|
+
console.log(`Platform: ${ARC_PLATFORM}`);
|
|
10
|
+
console.log(`Node.js: ${process.version}`);
|
|
11
|
+
}
|
|
12
|
+
/** Semver comparison: returns -1, 0, or 1 */
|
|
13
|
+
export function compareSemver(a, b) {
|
|
14
|
+
const pa = a.split(".").map(Number);
|
|
15
|
+
const pb = b.split(".").map(Number);
|
|
16
|
+
for (let i = 0; i < 3; i++) {
|
|
17
|
+
if (pa[i] < pb[i])
|
|
18
|
+
return -1;
|
|
19
|
+
if (pa[i] > pb[i])
|
|
20
|
+
return 1;
|
|
21
|
+
}
|
|
22
|
+
return 0;
|
|
23
|
+
}
|
|
24
|
+
/** Check if a manifest's arc version requirement is compatible */
|
|
25
|
+
export function checkVersionCompatibility(required) {
|
|
26
|
+
// Support formats: "0.5.0", ">=0.4.0", "^0.5.0", "~0.5.0"
|
|
27
|
+
const current = ARC_VERSION;
|
|
28
|
+
if (required.startsWith(">=")) {
|
|
29
|
+
const min = required.slice(2);
|
|
30
|
+
const ok = compareSemver(current, min) >= 0;
|
|
31
|
+
return { compatible: ok, message: ok ? "Compatible" : `Requires Arc >=${min}, current is ${current}` };
|
|
32
|
+
}
|
|
33
|
+
if (required.startsWith("^")) {
|
|
34
|
+
const base = required.slice(1);
|
|
35
|
+
const [major] = base.split(".").map(Number);
|
|
36
|
+
const [curMajor] = current.split(".").map(Number);
|
|
37
|
+
const ok = curMajor === major && compareSemver(current, base) >= 0;
|
|
38
|
+
return { compatible: ok, message: ok ? "Compatible" : `Requires Arc ${required}, current is ${current}` };
|
|
39
|
+
}
|
|
40
|
+
if (required.startsWith("~")) {
|
|
41
|
+
const base = required.slice(1);
|
|
42
|
+
const parts = base.split(".").map(Number);
|
|
43
|
+
const curParts = current.split(".").map(Number);
|
|
44
|
+
const ok = curParts[0] === parts[0] && curParts[1] === parts[1] && compareSemver(current, base) >= 0;
|
|
45
|
+
return { compatible: ok, message: ok ? "Compatible" : `Requires Arc ${required}, current is ${current}` };
|
|
46
|
+
}
|
|
47
|
+
// Exact match
|
|
48
|
+
const ok = compareSemver(current, required) >= 0;
|
|
49
|
+
return { compatible: ok, message: ok ? "Compatible" : `Requires Arc ${required}, current is ${current}` };
|
|
50
|
+
}
|
|
51
|
+
const deprecations = [];
|
|
52
|
+
const emittedWarnings = new Set();
|
|
53
|
+
/** Register a deprecated feature */
|
|
54
|
+
export function deprecate(feature, since, removeIn, migration) {
|
|
55
|
+
deprecations.push({ feature, since, removeIn, migration });
|
|
56
|
+
}
|
|
57
|
+
/** Emit a deprecation warning (once per feature per session) */
|
|
58
|
+
export function emitDeprecationWarning(feature) {
|
|
59
|
+
if (emittedWarnings.has(feature))
|
|
60
|
+
return;
|
|
61
|
+
const entry = deprecations.find(d => d.feature === feature);
|
|
62
|
+
if (!entry)
|
|
63
|
+
return;
|
|
64
|
+
emittedWarnings.add(feature);
|
|
65
|
+
console.warn(`⚠ DEPRECATED: '${feature}' was deprecated in v${entry.since} and will be removed in v${entry.removeIn}.`);
|
|
66
|
+
console.warn(` Migration: ${entry.migration}`);
|
|
67
|
+
}
|
|
68
|
+
/** Get all registered deprecations */
|
|
69
|
+
export function getDeprecations() {
|
|
70
|
+
return deprecations;
|
|
71
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "arc-lang",
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "Arc ⚡ — A programming language designed by AI agents, for AI agents. 27-63% fewer tokens than JavaScript.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"arc": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/index.js",
|
|
10
|
+
"files": [
|
|
11
|
+
"dist/",
|
|
12
|
+
"README.md",
|
|
13
|
+
"LICENSE"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsc",
|
|
17
|
+
"prepublishOnly": "tsc",
|
|
18
|
+
"run": "tsx src/index.ts run",
|
|
19
|
+
"parse": "tsx src/index.ts parse"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"arc",
|
|
23
|
+
"programming-language",
|
|
24
|
+
"ai",
|
|
25
|
+
"ai-agents",
|
|
26
|
+
"compiler",
|
|
27
|
+
"token-efficient",
|
|
28
|
+
"language",
|
|
29
|
+
"interpreter",
|
|
30
|
+
"repl"
|
|
31
|
+
],
|
|
32
|
+
"author": "Kai <nebula7458@proton.me> (https://arclang.dev)",
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"homepage": "https://arclang.dev",
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "git+https://github.com/kai-builds-ai/arc-lang.git"
|
|
38
|
+
},
|
|
39
|
+
"bugs": {
|
|
40
|
+
"url": "https://github.com/kai-builds-ai/arc-lang/issues"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"tsx": "^4.7.0",
|
|
44
|
+
"typescript": "^5.3.0",
|
|
45
|
+
"vscode-languageserver": "^9.0.1",
|
|
46
|
+
"vscode-languageserver-textdocument": "^1.0.12"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@types/node": "^25.2.3"
|
|
50
|
+
}
|
|
51
|
+
}
|