@skillstech/thunderlang 0.1.7 → 0.3.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/CHANGELOG.md +53 -0
- package/dist/core.cjs +240 -20
- package/dist/index.cjs +241 -21
- package/intent-graph.schema.json +227 -4
- package/package.json +1 -1
- package/src/cli.mjs +632 -23
- package/src/conformance.mjs +35 -0
- package/src/coverage.mjs +56 -0
- package/src/emit.mjs +3 -1
- package/src/expr.mjs +11 -7
- package/src/intent-graph.mjs +29 -0
- package/src/lift.mjs +79 -3
- package/src/mutate.mjs +46 -0
- package/src/parse.mjs +82 -3
- package/src/property.mjs +108 -0
- package/src/target-cs.mjs +98 -0
- package/src/target-java.mjs +87 -0
- package/src/target-py.mjs +90 -0
- package/src/target-ts.mjs +66 -0
- package/src/target-util.mjs +95 -0
- package/src/testing.mjs +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,59 @@
|
|
|
3
3
|
All notable changes to `@skillstech/thunderlang`. Pre-1.0: the language and the
|
|
4
4
|
`intent-graph-v1` schema version independently and may still change.
|
|
5
5
|
|
|
6
|
+
## 0.3.0
|
|
7
|
+
|
|
8
|
+
The 14-language lift release. Additive; no breaking changes. Restores the OpenThunder -> ThunderLang
|
|
9
|
+
recover-intent loop for every language OpenThunder's archgraph core can now discover.
|
|
10
|
+
|
|
11
|
+
- **Kotlin, Scala, and Elixir lift adapters.** `thunder lift` now extracts candidate intent from
|
|
12
|
+
`.kt`/`.kts`, `.scala`/`.sc`, and `.ex`/`.exs`, bringing the supported-language count to 14. A new
|
|
13
|
+
`name: Type` parameter parser handles the JVM signature shape; Elixir joins the dynamic-language set.
|
|
14
|
+
- **Fix: repo-walk language coverage.** `LIFT_EXTS` was stale and silently skipped Python, Java, C#,
|
|
15
|
+
Go, C++, PHP, and Ruby during repo/`--all` lifts; it now mirrors the full adapter set.
|
|
16
|
+
- **Fix: single-file lift language detection.** `thunder lift <file>` now auto-detects the language by
|
|
17
|
+
extension instead of defaulting to TypeScript, consistent with `--all`/repo modes.
|
|
18
|
+
|
|
19
|
+
## 0.2.0
|
|
20
|
+
|
|
21
|
+
The cross-language target execution release. Additive; no breaking changes.
|
|
22
|
+
|
|
23
|
+
Version alignment: starting with 0.2.0, ThunderLang and OpenThunder version in lockstep, since most
|
|
24
|
+
OpenThunder changes land in the language first. (0.1.8 and 0.1.9 were never published; the last
|
|
25
|
+
release on npm was 0.1.7.)
|
|
26
|
+
|
|
27
|
+
- **Live target adapters.** `thunder test <file> --target typescript|python|csharp|java` and
|
|
28
|
+
`thunder conform <file> --run <targets>` now compile the generated decision(s) and execute them
|
|
29
|
+
for real, grading the actual outputs instead of fed results. TypeScript/JS runs in-process,
|
|
30
|
+
Python through `python3`, C# through a throwaway `dotnet` console project, and Java through the
|
|
31
|
+
JDK 11+ single-file launcher (`java ThunderTarget.java`).
|
|
32
|
+
- **`--all-targets`.** `thunder test <file> --all-targets` and `thunder conform <file> --all-targets`
|
|
33
|
+
run every target whose toolchain is available in one pass, side by side, and skip any target whose
|
|
34
|
+
runtime is absent (it stays declared in the conformance matrix) rather than failing.
|
|
35
|
+
- **Static-target type inference.** Because C# and Java are statically typed, each decision
|
|
36
|
+
parameter's type is inferred from the test-case values it receives (numeric wins, then boolean,
|
|
37
|
+
else string), and each language gets its own equality translation.
|
|
38
|
+
- Missing toolchains are detected via cached smoke checks and skipped cleanly, so builds never fail
|
|
39
|
+
for a runtime that is not installed.
|
|
40
|
+
|
|
41
|
+
## 0.1.8
|
|
42
|
+
|
|
43
|
+
The testing and verification release. Additive; no breaking changes.
|
|
44
|
+
|
|
45
|
+
- **Rename to ThunderLang.** Package is `@skillstech/thunderlang`; the CLI binary is `thunder`
|
|
46
|
+
(with `intent` kept as a legacy alias). `.thunder` is the canonical source extension; `.tl` and
|
|
47
|
+
`.intent` are accepted. The Intent Graph and Intent-Oriented Programming vocabulary are retained.
|
|
48
|
+
- **`thunder prove`** emits an `intent-proof-v1` artifact with honest per-claim verdicts
|
|
49
|
+
(verified / failed / declared / needs_verification, with `provenBy`). An unverified claim never
|
|
50
|
+
reads as proven.
|
|
51
|
+
- **`thunder test --contracts [--strict]`** derives an obligation from every `guarantee` and
|
|
52
|
+
`never`, resolved against the specific test that verifies it. `--strict` fails CI on any unverified.
|
|
53
|
+
- **Verification classification**: `verify by assertion|static|runtime|evidence|tool|approval|formal`.
|
|
54
|
+
- **Stable IDs**: explicit `id INV-G-001` on guarantees / never-rules (slug fallback).
|
|
55
|
+
- **Proof freshness**: the proof records intent hash, compiler, git commit, dependency-lockfile hash,
|
|
56
|
+
and environment; `thunder verify` marks a proof STALE when the implementation, dependencies, or
|
|
57
|
+
compiler move since it was generated.
|
|
58
|
+
|
|
6
59
|
## 0.1.6
|
|
7
60
|
|
|
8
61
|
The agent-conformance release (skills on intents + 12-factor scoring). Additive; no breaking
|
package/dist/core.cjs
CHANGED
|
@@ -789,6 +789,60 @@ function parseDecision(name, node2) {
|
|
|
789
789
|
}
|
|
790
790
|
return dec;
|
|
791
791
|
}
|
|
792
|
+
function parseProperty(name, node2) {
|
|
793
|
+
const prop = { name, vars: [], decide: null, expects: [], line: node2.line };
|
|
794
|
+
for (const c of node2.children.filter((x) => !isNote(x))) {
|
|
795
|
+
const k = firstWord(c.text);
|
|
796
|
+
const a = rest(c.text);
|
|
797
|
+
if (k === "forAll" || k === "forall") {
|
|
798
|
+
for (const v of c.children.filter((x) => !isNote(x))) {
|
|
799
|
+
const m = v.text.trim().match(/^([A-Za-z_]\w*)\s*:\s*(\w+)(?:\s+where\s+(.+))?$/);
|
|
800
|
+
if (!m) continue;
|
|
801
|
+
const whereChild = (v.children || []).find((x) => firstWord(x.text) === "where");
|
|
802
|
+
prop.vars.push({ name: m[1], type: m[2], where: m[3] || (whereChild ? rest(whereChild.text) : null) });
|
|
803
|
+
}
|
|
804
|
+
} else if (k === "decide") prop.decide = a || c.children[0] && c.children[0].text.trim() || null;
|
|
805
|
+
else if (k === "when") {
|
|
806
|
+
const dm = a.match(/(?:decide|execute)\s+(\w+)/);
|
|
807
|
+
if (dm) prop.decide = dm[1];
|
|
808
|
+
} else if (k === "expect") for (const e of c.children.filter((x) => !isNote(x))) prop.expects.push(e.text.trim());
|
|
809
|
+
}
|
|
810
|
+
return prop;
|
|
811
|
+
}
|
|
812
|
+
function parseEvaluation(name, node2) {
|
|
813
|
+
const ev = { name, dataset: null, requires: [], line: node2.line };
|
|
814
|
+
for (const c of node2.children.filter((x) => !isNote(x))) {
|
|
815
|
+
const k = firstWord(c.text);
|
|
816
|
+
const a = rest(c.text);
|
|
817
|
+
if (k === "dataset") ev.dataset = a || c.children[0] && c.children[0].text.trim() || null;
|
|
818
|
+
else if (k === "require") {
|
|
819
|
+
for (const r of c.children.filter((x) => !isNote(x))) {
|
|
820
|
+
const m = r.text.trim().match(/^([A-Za-z_][\w.]*)\s*(>=|<=|==|!=|>|<)\s*(-?[\d.]+)$/);
|
|
821
|
+
if (m) ev.requires.push({ metric: m[1], op: m[2], threshold: parseFloat(m[3]) });
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
return ev;
|
|
826
|
+
}
|
|
827
|
+
function parseScenario(name, node2) {
|
|
828
|
+
const sc = { name, given: [], when: [], then: [], never: [], eventually: [], line: node2.line };
|
|
829
|
+
const clausesOf = (c) => {
|
|
830
|
+
const items = leafItems(c);
|
|
831
|
+
return items.length ? items : rest(c.text) ? [rest(c.text)] : [];
|
|
832
|
+
};
|
|
833
|
+
for (const c of node2.children.filter((x) => !isNote(x))) {
|
|
834
|
+
const k = firstWord(c.text);
|
|
835
|
+
if (k === "given") sc.given.push(...clausesOf(c));
|
|
836
|
+
else if (k === "when") sc.when.push(...clausesOf(c));
|
|
837
|
+
else if (k === "then") sc.then.push(...clausesOf(c));
|
|
838
|
+
else if (k === "never") sc.never.push(...clausesOf(c));
|
|
839
|
+
else if (k === "eventually") {
|
|
840
|
+
const within = (rest(c.text).match(/within\s+(.+)/) || [])[1] || null;
|
|
841
|
+
sc.eventually.push({ within, clauses: leafItems(c) });
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
return sc;
|
|
845
|
+
}
|
|
792
846
|
function parseLifecycle(name, node2) {
|
|
793
847
|
const lc = { name, states: [], transitions: [], terminals: [], line: node2.line };
|
|
794
848
|
for (const c of node2.children.filter((x) => !isNote(x))) {
|
|
@@ -821,7 +875,13 @@ function parseInvariant(name, node2) {
|
|
|
821
875
|
for (const c of kids(node2)) {
|
|
822
876
|
const k = firstWord(c.text);
|
|
823
877
|
if (k === "verify") inv.verify.push(rest(c.text));
|
|
824
|
-
else if (k === "
|
|
878
|
+
else if (k === "id") {
|
|
879
|
+
const v = rest(c.text);
|
|
880
|
+
if (v) {
|
|
881
|
+
inv.id = v;
|
|
882
|
+
inv.stableId = v;
|
|
883
|
+
}
|
|
884
|
+
} else if (k === "because") inv.because = rest(c.text);
|
|
825
885
|
else if (k === "statement") inv.statement = rest(c.text) || leafItems(c).join(" ");
|
|
826
886
|
else if (k === "scope") inv.scope = (rest(c.text) || leafItems(c).join(" ")).toLowerCase();
|
|
827
887
|
else if (k === "severity") inv.severity = (rest(c.text) || leafItems(c).join(" ")).toLowerCase();
|
|
@@ -907,11 +967,12 @@ function parseHandler(trigger, node2) {
|
|
|
907
967
|
actions
|
|
908
968
|
};
|
|
909
969
|
}
|
|
970
|
+
var VERIFY_KINDS = /* @__PURE__ */ new Set(["assertion", "static", "runtime", "evidence", "tool", "approval", "formal"]);
|
|
910
971
|
function upsertRule(list, statement, line) {
|
|
911
972
|
const id = slug(statement);
|
|
912
973
|
let r = list.find((x) => x.id === id);
|
|
913
974
|
if (!r) {
|
|
914
|
-
r = { id, statement, because: null, verify: [], notes: [], line };
|
|
975
|
+
r = { id, statement, because: null, verify: [], verifications: [], notes: [], line };
|
|
915
976
|
list.push(r);
|
|
916
977
|
}
|
|
917
978
|
return r;
|
|
@@ -920,8 +981,28 @@ function applyDetail(rule, node2) {
|
|
|
920
981
|
for (const c of node2.children) {
|
|
921
982
|
const kw = firstWord(c.text);
|
|
922
983
|
if (kw === "because") rule.because = rest(c.text) || c.children[0] && c.children[0].text || null;
|
|
923
|
-
else if (kw === "verify")
|
|
924
|
-
|
|
984
|
+
else if (kw === "verify") {
|
|
985
|
+
const a = rest(c.text);
|
|
986
|
+
const m = a && a.match(/^by\s+(\w+)/i);
|
|
987
|
+
if (m) {
|
|
988
|
+
const kind = m[1].toLowerCase();
|
|
989
|
+
const details = (c.children || []).filter((x) => !isNote(x)).map((x) => x.text.trim()).filter(Boolean);
|
|
990
|
+
rule.verifications.push({ kind: VERIFY_KINDS.has(kind) ? kind : "unknown", declared: kind, details });
|
|
991
|
+
rule.verify.push(details.length ? `${kind}: ${details.join("; ")}` : `by ${kind}`);
|
|
992
|
+
} else {
|
|
993
|
+
const v = a || c.children[0] && c.children[0].text || "";
|
|
994
|
+
if (v) {
|
|
995
|
+
rule.verify.push(v);
|
|
996
|
+
rule.verifications.push({ kind: "unclassified", declared: null, details: [v] });
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
} else if (kw === "id") {
|
|
1000
|
+
const v = rest(c.text) || c.children[0] && c.children[0].text;
|
|
1001
|
+
if (v) {
|
|
1002
|
+
rule.id = v;
|
|
1003
|
+
rule.stableId = v;
|
|
1004
|
+
}
|
|
1005
|
+
} else if (isNote(c)) rule.notes.push(parseNoteNode(c));
|
|
925
1006
|
}
|
|
926
1007
|
rule.verify = rule.verify.filter(Boolean);
|
|
927
1008
|
}
|
|
@@ -986,6 +1067,9 @@ function parseIntent(source) {
|
|
|
986
1067
|
handlers: [],
|
|
987
1068
|
// Decisions, rules, process (Gap 4)
|
|
988
1069
|
decisions: [],
|
|
1070
|
+
properties: [],
|
|
1071
|
+
scenarios: [],
|
|
1072
|
+
evaluations: [],
|
|
989
1073
|
// Governance: waivers , governed exceptions to blocking diagnostics (Gap 5)
|
|
990
1074
|
waivers: [],
|
|
991
1075
|
// Data purpose + privacy , governed data elements (Gap 6)
|
|
@@ -1214,6 +1298,15 @@ function parseIntent(source) {
|
|
|
1214
1298
|
case "decision":
|
|
1215
1299
|
ast.decisions.push(parseDecision(arg, node2));
|
|
1216
1300
|
break;
|
|
1301
|
+
case "property":
|
|
1302
|
+
ast.properties.push(parseProperty(arg, node2));
|
|
1303
|
+
break;
|
|
1304
|
+
case "scenario":
|
|
1305
|
+
ast.scenarios.push(parseScenario(arg, node2));
|
|
1306
|
+
break;
|
|
1307
|
+
case "evaluation":
|
|
1308
|
+
ast.evaluations.push(parseEvaluation(arg, node2));
|
|
1309
|
+
break;
|
|
1217
1310
|
// ── System profile ──
|
|
1218
1311
|
case "capability": {
|
|
1219
1312
|
const cap = { name: arg, description: null, implements: [], line: node2.line };
|
|
@@ -3198,21 +3291,23 @@ function compileExpr(src) {
|
|
|
3198
3291
|
function evalExpr(src, inputs = {}) {
|
|
3199
3292
|
return compileExpr(src)(inputs);
|
|
3200
3293
|
}
|
|
3294
|
+
var C_LOGIC = { and: (a, b) => `(${a} && ${b})`, or: (a, b) => `(${a} || ${b})`, not: (a) => `!${a}`, bool: (v) => String(v) };
|
|
3201
3295
|
var DIALECTS = {
|
|
3202
|
-
js: { eq: (a, b) => `${a} === ${b}`, neq: (a, b) => `${a} !== ${b}`, inList: (list, x) => `[${list.join(", ")}].includes(${x})`, nil: "undefined" },
|
|
3203
|
-
csharp: { eq: (a, b) => `${a} == ${b}`, neq: (a, b) => `${a} != ${b}`, inList: (list, x) => `new[]{${list.join(", ")}}.Contains(${x})`, nil: "null" },
|
|
3204
|
-
java: { eq: (a, b) => `java.util.Objects.equals(${a}, ${b})`, neq: (a, b) => `!java.util.Objects.equals(${a}, ${b})`, inList: (list, x) => `java.util.List.of(${list.join(", ")}).contains(${x})`, nil: "null" }
|
|
3296
|
+
js: { ...C_LOGIC, eq: (a, b) => `${a} === ${b}`, neq: (a, b) => `${a} !== ${b}`, inList: (list, x) => `[${list.join(", ")}].includes(${x})`, nil: "undefined" },
|
|
3297
|
+
csharp: { ...C_LOGIC, eq: (a, b) => `${a} == ${b}`, neq: (a, b) => `${a} != ${b}`, inList: (list, x) => `new[]{${list.join(", ")}}.Contains(${x})`, nil: "null" },
|
|
3298
|
+
java: { ...C_LOGIC, eq: (a, b) => `java.util.Objects.equals(${a}, ${b})`, neq: (a, b) => `!java.util.Objects.equals(${a}, ${b})`, inList: (list, x) => `java.util.List.of(${list.join(", ")}).contains(${x})`, nil: "null" },
|
|
3299
|
+
python: { and: (a, b) => `(${a} and ${b})`, or: (a, b) => `(${a} or ${b})`, not: (a) => `(not ${a})`, bool: (v) => v ? "True" : "False", eq: (a, b) => `${a} == ${b}`, neq: (a, b) => `${a} != ${b}`, inList: (list, x) => `${x} in [${list.join(", ")}]`, nil: "None" }
|
|
3205
3300
|
};
|
|
3206
3301
|
function renderExpr(src, inputs, D) {
|
|
3207
3302
|
const known = new Set(inputs);
|
|
3208
3303
|
const r = (n) => {
|
|
3209
3304
|
switch (n.k) {
|
|
3210
3305
|
case "or":
|
|
3211
|
-
return
|
|
3306
|
+
return D.or(r(n.a), r(n.b));
|
|
3212
3307
|
case "and":
|
|
3213
|
-
return
|
|
3308
|
+
return D.and(r(n.a), r(n.b));
|
|
3214
3309
|
case "not":
|
|
3215
|
-
return
|
|
3310
|
+
return D.not(r(n.a));
|
|
3216
3311
|
case "neg":
|
|
3217
3312
|
return `-${r(n.a)}`;
|
|
3218
3313
|
case "arith":
|
|
@@ -3226,7 +3321,7 @@ function renderExpr(src, inputs, D) {
|
|
|
3226
3321
|
case "list":
|
|
3227
3322
|
return `[${n.items.map(r).join(", ")}]`;
|
|
3228
3323
|
case "lit":
|
|
3229
|
-
return typeof n.v === "string" ? JSON.stringify(n.v) : String(n.v);
|
|
3324
|
+
return typeof n.v === "string" ? JSON.stringify(n.v) : typeof n.v === "boolean" ? D.bool(n.v) : String(n.v);
|
|
3230
3325
|
case "ref":
|
|
3231
3326
|
return known.has(n.path) || known.has(n.path.split(".")[0]) ? n.path : JSON.stringify(n.path);
|
|
3232
3327
|
default:
|
|
@@ -4193,7 +4288,7 @@ function notesSummary(ast) {
|
|
|
4193
4288
|
byLens
|
|
4194
4289
|
};
|
|
4195
4290
|
}
|
|
4196
|
-
var COMPILER_VERSION = "0.
|
|
4291
|
+
var COMPILER_VERSION = "0.3.0";
|
|
4197
4292
|
var PROOF_SCHEMA_VERSION = "0.1.0";
|
|
4198
4293
|
var SOURCE_PRODUCT = "skillstech-compiler";
|
|
4199
4294
|
function buildContractGraph(ast, generatedAt) {
|
|
@@ -4686,13 +4781,15 @@ function buildProof(ast, { sourceFile, sourceHash, targetsRequested, targetsGene
|
|
|
4686
4781
|
id: g.id,
|
|
4687
4782
|
text: g.statement,
|
|
4688
4783
|
status: g.verify.length > 0 || verifiedText ? "planned" : "needs_verification",
|
|
4689
|
-
evidence: g.verify
|
|
4784
|
+
evidence: g.verify,
|
|
4785
|
+
verifications: g.verifications || []
|
|
4690
4786
|
})),
|
|
4691
4787
|
neverRules: ast.neverRules.map((n) => ({
|
|
4692
4788
|
id: n.id,
|
|
4693
4789
|
text: n.statement,
|
|
4694
4790
|
status: n.verify.length > 0 ? "planned" : "needs_verification",
|
|
4695
|
-
evidence: n.verify
|
|
4791
|
+
evidence: n.verify,
|
|
4792
|
+
verifications: n.verifications || []
|
|
4696
4793
|
})),
|
|
4697
4794
|
errors: (ast.errors || []).map((e) => ({ name: e.name })),
|
|
4698
4795
|
examples: (ast.examples || []).map((ex) => ({ given: ex.given, expect: ex.expect })),
|
|
@@ -5281,6 +5378,112 @@ function extractFactsRuby(source, file = "input.rb") {
|
|
|
5281
5378
|
}
|
|
5282
5379
|
return { schemaVersion: IR_SCHEMA_VERSION, sourceLanguage: "ruby", sourceRoot: file, functions, tests, errors };
|
|
5283
5380
|
}
|
|
5381
|
+
function parseNameColonTypeParams(raw) {
|
|
5382
|
+
return splitTopLevel(raw, ",").map((p) => p.trim()).filter(Boolean).map((p) => {
|
|
5383
|
+
const cleaned = p.replace(/@\w+(\([^)]*\))?/g, "").replace(/^(?:val|var|vararg|implicit|final|lazy)\s+/, "").replace(/=.*$/, "").trim();
|
|
5384
|
+
const mm = cleaned.match(/^([A-Za-z_]\w*)\s*:\s*(.+)$/);
|
|
5385
|
+
if (mm) return { name: mm[1], type: mm[2].trim() };
|
|
5386
|
+
return { name: cleaned.replace(/[^\w].*$/, "") || cleaned, type: null };
|
|
5387
|
+
});
|
|
5388
|
+
}
|
|
5389
|
+
function extractFactsKotlin(source, file = "input.kt") {
|
|
5390
|
+
let m;
|
|
5391
|
+
const functions = [];
|
|
5392
|
+
const tests = [];
|
|
5393
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5394
|
+
const testMethods = /* @__PURE__ */ new Set();
|
|
5395
|
+
const ta = /@Test\b[\s\S]{0,120}?\bfun\s+(?:`([^`]+)`|(\w+))\s*\(/g;
|
|
5396
|
+
while (m = ta.exec(source)) testMethods.add(m[1] || m[2]);
|
|
5397
|
+
const fnRe = /\bfun\s+(?:<[^>]*>\s*)?(?:[A-Za-z_][\w.]*\.)?(?:`([^`]+)`|([A-Za-z_]\w*))\s*\(([^)]*)\)\s*(?::\s*([^{=\n]+))?/g;
|
|
5398
|
+
while (m = fnRe.exec(source)) {
|
|
5399
|
+
const name = m[1] || m[2];
|
|
5400
|
+
if (testMethods.has(name)) {
|
|
5401
|
+
if (!tests.some((t) => t.name === name)) tests.push({ name, file, line: lineOf(source, m.index) });
|
|
5402
|
+
continue;
|
|
5403
|
+
}
|
|
5404
|
+
if (seen.has(name)) continue;
|
|
5405
|
+
seen.add(name);
|
|
5406
|
+
functions.push({ name, file, line: lineOf(source, m.index), parameters: parseNameColonTypeParams(m[3] || ""), returnType: m[4] ? m[4].trim() : null, evidence: [{ kind: "fun", file, line: lineOf(source, m.index) }] });
|
|
5407
|
+
}
|
|
5408
|
+
const errors = [];
|
|
5409
|
+
const addErr = addErrOf(errors, /* @__PURE__ */ new Set(), { _src: source, _file: file });
|
|
5410
|
+
let mm;
|
|
5411
|
+
const ce = /class\s+(\w*(?:Exception|Error))\b/g;
|
|
5412
|
+
while (mm = ce.exec(source)) addErr(mm[1], mm.index);
|
|
5413
|
+
const th = /throw\s+(\w+)\s*\(/g;
|
|
5414
|
+
while (mm = th.exec(source)) addErr(mm[1], mm.index);
|
|
5415
|
+
return { schemaVersion: IR_SCHEMA_VERSION, sourceLanguage: "kotlin", sourceRoot: file, functions, tests, errors };
|
|
5416
|
+
}
|
|
5417
|
+
function extractFactsScala(source, file = "input.scala") {
|
|
5418
|
+
let m;
|
|
5419
|
+
const functions = [];
|
|
5420
|
+
const tests = [];
|
|
5421
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5422
|
+
const fnRe = /\bdef\s+([A-Za-z_]\w*)\s*(?:\[[^\]]*\])?\s*\(([^)]*)\)\s*(?::\s*([^={\n]+))?/g;
|
|
5423
|
+
while (m = fnRe.exec(source)) {
|
|
5424
|
+
const name = m[1];
|
|
5425
|
+
if (seen.has(name)) continue;
|
|
5426
|
+
seen.add(name);
|
|
5427
|
+
functions.push({ name, file, line: lineOf(source, m.index), parameters: parseNameColonTypeParams(m[2] || ""), returnType: m[3] ? m[3].trim() : null, evidence: [{ kind: "def", file, line: lineOf(source, m.index) }] });
|
|
5428
|
+
}
|
|
5429
|
+
const seenT = /* @__PURE__ */ new Set();
|
|
5430
|
+
const addTest = (n, idx) => {
|
|
5431
|
+
const k = String(n).toLowerCase();
|
|
5432
|
+
if (n && !seenT.has(k)) {
|
|
5433
|
+
seenT.add(k);
|
|
5434
|
+
tests.push({ name: n, file, line: lineOf(source, idx) });
|
|
5435
|
+
}
|
|
5436
|
+
};
|
|
5437
|
+
const tr = /\btest\s*\(\s*"([^"]+)"/g;
|
|
5438
|
+
while (m = tr.exec(source)) addTest(m[1], m.index);
|
|
5439
|
+
const inRe = /"([^"]+)"\s+(?:in|should|must)\b/g;
|
|
5440
|
+
while (m = inRe.exec(source)) addTest(m[1], m.index);
|
|
5441
|
+
const errors = [];
|
|
5442
|
+
const addErr = addErrOf(errors, /* @__PURE__ */ new Set(), { _src: source, _file: file });
|
|
5443
|
+
let mm;
|
|
5444
|
+
const ce = /class\s+(\w*(?:Exception|Error))\b/g;
|
|
5445
|
+
while (mm = ce.exec(source)) addErr(mm[1], mm.index);
|
|
5446
|
+
const th = /throw\s+new\s+(\w+)\s*\(/g;
|
|
5447
|
+
while (mm = th.exec(source)) addErr(mm[1], mm.index);
|
|
5448
|
+
return { schemaVersion: IR_SCHEMA_VERSION, sourceLanguage: "scala", sourceRoot: file, functions, tests, errors };
|
|
5449
|
+
}
|
|
5450
|
+
function extractFactsElixir(source, file = "input.ex") {
|
|
5451
|
+
let m;
|
|
5452
|
+
const functions = [];
|
|
5453
|
+
const tests = [];
|
|
5454
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5455
|
+
const seenT = /* @__PURE__ */ new Set();
|
|
5456
|
+
const addTest = (n, idx) => {
|
|
5457
|
+
const k = String(n).toLowerCase();
|
|
5458
|
+
if (n && !seenT.has(k)) {
|
|
5459
|
+
seenT.add(k);
|
|
5460
|
+
tests.push({ name: n, file, line: lineOf(source, idx) });
|
|
5461
|
+
}
|
|
5462
|
+
};
|
|
5463
|
+
const tr = /\btest\s+"([^"]+)"/g;
|
|
5464
|
+
while (m = tr.exec(source)) addTest(m[1], m.index);
|
|
5465
|
+
const fnRe = /^[ \t]*defp?\s+([a-z_]\w*[!?]?)\s*(?:\(([^)]*)\))?/gm;
|
|
5466
|
+
while (m = fnRe.exec(source)) {
|
|
5467
|
+
const name = m[1];
|
|
5468
|
+
if (seen.has(name)) continue;
|
|
5469
|
+
seen.add(name);
|
|
5470
|
+
const parameters = splitTopLevel(m[2] || "", ",").map((p) => p.trim()).filter(Boolean).map((p) => ({ name: p.split(/\\\\/)[0].trim().replace(/^%\{?/, "").replace(/[^\w].*$/, "") || p, type: null }));
|
|
5471
|
+
functions.push({ name, file, line: lineOf(source, m.index), indent: (m[0].match(/^[ \t]*/) || [""])[0].length, parameters, returnType: null, evidence: [{ kind: "def", file, line: lineOf(source, m.index) }] });
|
|
5472
|
+
}
|
|
5473
|
+
const errors = [];
|
|
5474
|
+
const seenErr = /* @__PURE__ */ new Set();
|
|
5475
|
+
const addErr = (n, idx) => {
|
|
5476
|
+
if (n && !seenErr.has(n)) {
|
|
5477
|
+
seenErr.add(n);
|
|
5478
|
+
errors.push({ name: n, file, line: lineOf(source, idx) });
|
|
5479
|
+
}
|
|
5480
|
+
};
|
|
5481
|
+
const modErr = /defmodule\s+([\w.]*(?:Error|Exception))\b/g;
|
|
5482
|
+
while (m = modErr.exec(source)) addErr(m[1].split(".").pop(), m.index);
|
|
5483
|
+
const raiseRe = /raise\s+([A-Z][\w.]*)/g;
|
|
5484
|
+
while (m = raiseRe.exec(source)) addErr(m[1].split(".").pop(), m.index);
|
|
5485
|
+
return { schemaVersion: IR_SCHEMA_VERSION, sourceLanguage: "elixir", sourceRoot: file, functions, tests, errors };
|
|
5486
|
+
}
|
|
5284
5487
|
var ADAPTERS = {
|
|
5285
5488
|
typescript: extractFactsTypeScript,
|
|
5286
5489
|
ts: extractFactsTypeScript,
|
|
@@ -5304,10 +5507,18 @@ var ADAPTERS = {
|
|
|
5304
5507
|
cc: extractFactsCpp,
|
|
5305
5508
|
php: extractFactsPhp,
|
|
5306
5509
|
ruby: extractFactsRuby,
|
|
5307
|
-
rb: extractFactsRuby
|
|
5510
|
+
rb: extractFactsRuby,
|
|
5511
|
+
kotlin: extractFactsKotlin,
|
|
5512
|
+
kt: extractFactsKotlin,
|
|
5513
|
+
kts: extractFactsKotlin,
|
|
5514
|
+
scala: extractFactsScala,
|
|
5515
|
+
sc: extractFactsScala,
|
|
5516
|
+
elixir: extractFactsElixir,
|
|
5517
|
+
ex: extractFactsElixir,
|
|
5518
|
+
exs: extractFactsElixir
|
|
5308
5519
|
};
|
|
5309
|
-
var SUPPORTED_LANGUAGES = ["typescript", "javascript", "python", "java", "csharp", "go", "rust", "cpp", "php", "ruby", "perl"];
|
|
5310
|
-
var DYNAMIC_LANGUAGES = /* @__PURE__ */ new Set(["perl", "javascript", "python", "ruby", "php"]);
|
|
5520
|
+
var SUPPORTED_LANGUAGES = ["typescript", "javascript", "python", "java", "csharp", "go", "rust", "cpp", "php", "ruby", "perl", "kotlin", "scala", "elixir"];
|
|
5521
|
+
var DYNAMIC_LANGUAGES = /* @__PURE__ */ new Set(["perl", "javascript", "python", "ruby", "php", "elixir"]);
|
|
5311
5522
|
var LANG_DISPLAY = {
|
|
5312
5523
|
typescript: "TypeScript",
|
|
5313
5524
|
javascript: "JavaScript",
|
|
@@ -5319,7 +5530,10 @@ var LANG_DISPLAY = {
|
|
|
5319
5530
|
cpp: "C++",
|
|
5320
5531
|
php: "PHP",
|
|
5321
5532
|
ruby: "Ruby",
|
|
5322
|
-
perl: "Perl"
|
|
5533
|
+
perl: "Perl",
|
|
5534
|
+
kotlin: "Kotlin",
|
|
5535
|
+
scala: "Scala",
|
|
5536
|
+
elixir: "Elixir"
|
|
5323
5537
|
};
|
|
5324
5538
|
function unwrapReturn(ret) {
|
|
5325
5539
|
if (!ret) return { output: null, error: null };
|
|
@@ -5443,6 +5657,9 @@ function languageForFile(file) {
|
|
|
5443
5657
|
if (/\.(cpp|cc|cxx|hpp|hh|c|h)$/i.test(file)) return "cpp";
|
|
5444
5658
|
if (/\.php$/i.test(file)) return "php";
|
|
5445
5659
|
if (/\.rb$/i.test(file)) return "ruby";
|
|
5660
|
+
if (/\.kts?$/i.test(file)) return "kotlin";
|
|
5661
|
+
if (/\.(scala|sc)$/i.test(file)) return "scala";
|
|
5662
|
+
if (/\.exs?$/i.test(file)) return "elixir";
|
|
5446
5663
|
if (/\.(mjs|cjs|jsx?)$/i.test(file)) return "javascript";
|
|
5447
5664
|
return "typescript";
|
|
5448
5665
|
}
|
|
@@ -5450,7 +5667,7 @@ function isPublicFn(fn, language) {
|
|
|
5450
5667
|
const name = fn.name || "";
|
|
5451
5668
|
if (/(?:Internal|Impl|_impl|_helper|_test|Helper)$/.test(name)) return false;
|
|
5452
5669
|
if (language === "go" || language === "golang") return /^[A-Z]/.test(name) && name !== "Test";
|
|
5453
|
-
if (language === "python" || language === "ruby") return !name.startsWith("_") && (fn.indent == null || fn.indent <= 4);
|
|
5670
|
+
if (language === "python" || language === "ruby" || language === "elixir") return !name.startsWith("_") && (fn.indent == null || fn.indent <= 4);
|
|
5454
5671
|
return !name.startsWith("_") && name !== "init" && name !== "constructor";
|
|
5455
5672
|
}
|
|
5456
5673
|
function liftAll(source, { language = "typescript", file = "", publicOnly = true } = {}) {
|
|
@@ -5525,7 +5742,10 @@ var LANG_EXT = {
|
|
|
5525
5742
|
cpp: "cpp",
|
|
5526
5743
|
php: "php",
|
|
5527
5744
|
ruby: "rb",
|
|
5528
|
-
perl: "pl"
|
|
5745
|
+
perl: "pl",
|
|
5746
|
+
kotlin: "kt",
|
|
5747
|
+
scala: "scala",
|
|
5748
|
+
elixir: "ex"
|
|
5529
5749
|
};
|
|
5530
5750
|
function liftSource(source, { language = "typescript", file = "", seeds = void 0 } = {}) {
|
|
5531
5751
|
const key = String(language).toLowerCase();
|