/* * test_grammar_probe_b.c — Grammar-only node/import/inheritance probe for 12 languages. * * Scope: glsl, hare, hlsl, ispc, julia, luau, matlab, odin, pascal, powershell, * racket, rescript. * * CALLS coverage for these languages is already handled by P5 (contract_calls_breadth). * This suite probes: * - NODE creation: Function/Procedure/Struct/Type nodes reach the graph after * a full index_repository pipeline run (shader entrypoints, shader struct types, * OOP types, module-like constructs). * - IMPORTS edges: where the language has a cross-file import mechanism that the * extractor knows about (julia `using`/`import`, racket `require`, * powershell `using module`, rescript `open`, pascal `uses`, odin `import`, * hare `use`). MATLAB has no standard inter-file import syntax → skip. * glsl/hlsl/ispc use `#include` / preprocessor — not modeled as IMPORTS → skip. * - INHERITANCE: powershell classes (`: Base`), pascal OOP (`: Base`), julia * abstract-type subtyping (`<:`) where same-file base is resolvable. * Odin, GLSL, HLSL, ISPC, Hare, MATLAB, ReScript, Racket, Luau have no * class inheritance → skip. * * Convention: * GREEN = contract that SHOULD hold; a failure here is a real regression. * RED = known bug reproduced as a failing assertion; kept as a regression * guard until fixed (brief // BUG comment explains root cause). * * Do NOT register this SUITE in test_main.c — the build system discovers it * separately alongside _a and _c. */ #include "../src/foundation/compat.h" #include "test_framework.h" #include "test_helpers.h" #include "cbm.h" #include #include #include #include #include #include #include #include #include #if !defined(_WIN32) #include #endif /* ── Harness (mirrors test_lang_contract.c) ────────────────────────────────── */ typedef struct { char tmpdir[256]; char dbpath[512]; char *project; cbm_mcp_server_t *srv; } ProbeLangProj; typedef struct { const char *name; const char *content; } ProbeLangFile; static void pb_fwd_slashes(char *p) { for (; *p; p++) { if (*p == '\\') *p = '/'; } } static cbm_store_t *pb_open_indexed(ProbeLangProj *lp) { lp->project = cbm_project_name_from_path(lp->tmpdir); if (!lp->project) return NULL; const char *home = getenv("HOME"); if (!home) home = "/tmp"; char cache_dir[512]; snprintf(cache_dir, sizeof(cache_dir), "%s/.cache/codebase-memory-mcp", home); cbm_mkdir(cache_dir); snprintf(lp->dbpath, sizeof(lp->dbpath), "%s/%s.db", cache_dir, lp->project); unlink(lp->dbpath); lp->srv = cbm_mcp_server_new(NULL); if (!lp->srv) return NULL; char args[700]; snprintf(args, sizeof(args), "{\"repo_path\":\"%s\"}", lp->tmpdir); char *resp = cbm_mcp_handle_tool(lp->srv, "index_repository", args); if (resp) free(resp); return cbm_store_open_path(lp->dbpath); } static cbm_store_t *pb_index_files(ProbeLangProj *lp, const ProbeLangFile *files, int nfiles) { memset(lp, 0, sizeof(*lp)); snprintf(lp->tmpdir, sizeof(lp->tmpdir), "/tmp/cbm_pb_XXXXXX"); if (!cbm_mkdtemp(lp->tmpdir)) return NULL; pb_fwd_slashes(lp->tmpdir); for (int i = 0; i < nfiles; i++) { char path[700]; snprintf(path, sizeof(path), "%s/%s", lp->tmpdir, files[i].name); char *slash = strrchr(path, '/'); if (slash && slash > path + strlen(lp->tmpdir)) { *slash = '\0'; cbm_mkdir_p(path, 0755); *slash = '/'; } FILE *f = fopen(path, "wb"); if (!f) return NULL; fputs(files[i].content, f); fclose(f); } return pb_open_indexed(lp); } static cbm_store_t *pb_index(ProbeLangProj *lp, const char *filename, const char *content) { ProbeLangFile f = {filename, content}; return pb_index_files(lp, &f, 1); } static void pb_cleanup(ProbeLangProj *lp, cbm_store_t *store) { if (store) cbm_store_close(store); if (lp->srv) { cbm_mcp_server_free(lp->srv); lp->srv = NULL; } free(lp->project); lp->project = NULL; th_rmtree(lp->tmpdir); unlink(lp->dbpath); char wal[600], shm[600]; snprintf(wal, sizeof(wal), "%s-wal", lp->dbpath); snprintf(shm, sizeof(shm), "%s-shm", lp->dbpath); unlink(wal); unlink(shm); } /* Count nodes by label. Returns -1 on store error. */ static int pb_count_label(cbm_store_t *store, const char *project, const char *label) { cbm_node_t *nodes = NULL; int count = 0; if (cbm_store_find_nodes_by_label(store, project, label, &nodes, &count) != CBM_STORE_OK) return -1; cbm_store_free_nodes(nodes, count); return count; } /* Sum across the labels different languages use for callables. */ static int pb_callable_nodes(cbm_store_t *store, const char *project) { int fn = pb_count_label(store, project, "Function"); int mt = pb_count_label(store, project, "Method"); return (fn < 0 ? 0 : fn) + (mt < 0 ? 0 : mt); } /* Sum across type-like labels. */ static int pb_type_nodes(cbm_store_t *store, const char *project) { static const char *labels[] = {"Class", "Struct", "Interface", "Enum", "Trait", "Type", NULL}; int total = 0; for (int i = 0; labels[i]; i++) { int n = pb_count_label(store, project, labels[i]); if (n > 0) total += n; } return total; } /* ══════════════════════════════════════════════════════════════════════════════ * GLSL — ext: .glsl * Shader language: functions + void main() entrypoint + struct types. * No imports (uses #include which is a preprocessor directive, not modeled). * No inheritance. * ══════════════════════════════════════════════════════════════════════════════ */ /* GLSL: helper + main entrypoint must both reach graph as Function nodes. */ TEST(glsl_function_nodes) { ProbeLangProj lp; cbm_store_t *store = pb_index(&lp, "shader.glsl", "float luminance(vec3 color) {\n" " return dot(color, vec3(0.299, 0.587, 0.114));\n" "}\n\n" "void main() {\n" " float lum = luminance(vec3(1.0, 0.5, 0.2));\n" " gl_FragColor = vec4(lum, lum, lum, 1.0);\n" "}\n"); int fns = store ? pb_callable_nodes(store, lp.project) : -1; pb_cleanup(&lp, store); ASSERT_TRUE(fns >= 1); /* luminance + main */ PASS(); } /* GLSL: struct type (e.g. Light) must appear as a type node. */ TEST(glsl_struct_node) { ProbeLangProj lp; cbm_store_t *store = pb_index(&lp, "light.glsl", "struct Light {\n" " vec3 position;\n" " vec3 color;\n" " float intensity;\n" "};\n\n" "float attenuation(Light l, vec3 pos) {\n" " float d = length(l.position - pos);\n" " return l.intensity / (d * d);\n" "}\n\n" "void main() {\n" " Light sun;\n" " sun.intensity = 1.0;\n" " gl_FragColor = vec4(attenuation(sun, vec3(0.0)), 1.0);\n" "}\n"); int types = store ? pb_type_nodes(store, lp.project) : -1; pb_cleanup(&lp, store); ASSERT_TRUE(types >= 1); /* struct Light */ PASS(); } /* GLSL: vertex shader with multiple stage-specific functions. */ TEST(glsl_vertex_shader_functions) { ProbeLangProj lp; cbm_store_t *store = pb_index(&lp, "vert.glsl", "vec4 transform(vec4 pos, mat4 mvp) {\n" " return mvp * pos;\n" "}\n\n" "vec3 compute_normal(vec3 n, mat3 normal_mat) {\n" " return normalize(normal_mat * n);\n" "}\n\n" "void main() {\n" " gl_Position = transform(vec4(0.0), mat4(1.0));\n" "}\n"); int fns = store ? pb_callable_nodes(store, lp.project) : -1; pb_cleanup(&lp, store); ASSERT_TRUE(fns >= 1); /* transform, compute_normal, main */ PASS(); } /* ══════════════════════════════════════════════════════════════════════════════ * HARE — ext: .ha * Systems language: fn declarations, type definitions, `use` imports. * No class inheritance. * ══════════════════════════════════════════════════════════════════════════════ */ /* Hare: function nodes from top-level fn declarations. */ TEST(hare_function_nodes) { ProbeLangProj lp; cbm_store_t *store = pb_index(&lp, "math.ha", "fn square(x: i64) i64 = x * x;\n\n" "fn cube(x: i64) i64 = x * x * x;\n\n" "fn sum_of_squares(a: i64, b: i64) i64 = {\n" " return square(a) + square(b);\n" "};\n"); int fns = store ? pb_callable_nodes(store, lp.project) : -1; pb_cleanup(&lp, store); ASSERT_TRUE(fns >= 1); /* square, cube, sum_of_squares */ PASS(); } /* Hare: type definition (struct) reaches graph as a type node. */ TEST(hare_struct_node) { ProbeLangProj lp; cbm_store_t *store = pb_index(&lp, "point.ha", "type point = struct {\n" " x: i64,\n" " y: i64,\n" "};\n\n" "fn distance(a: point, b: point) f64 = {\n" " let dx = (b.x - a.x): f64;\n" " let dy = (b.y - a.y): f64;\n" " return 0.0; // simplified\n" "};\n"); int types = store ? pb_type_nodes(store, lp.project) : -1; pb_cleanup(&lp, store); ASSERT_TRUE(types >= 1); /* type point */ PASS(); } /* Hare: `use` import statement → IMPORTS edge (extraction-level check). * The graph-level IMPORTS edge requires cross-file resolution which a single * fixture cannot trigger — so we probe at extraction level via cbm_extract_file. */ TEST(hare_use_import_extracted) { static const char *src = "use fmt;\n" "use strings;\n\n" "fn greet(name: str) void = {\n" " fmt::println(name);\n" "};\n"; CBMFileResult *r = cbm_extract_file(src, (int)strlen(src), CBM_LANG_HARE, "lc", "greet.ha", 0, NULL, NULL); ASSERT_NOT_NULL(r); int n = r->imports.count; cbm_free_result(r); ASSERT_TRUE(n >= 1); /* use fmt; use strings; → BUG if 0: hare `use` not extracted */ PASS(); } /* ══════════════════════════════════════════════════════════════════════════════ * HLSL — ext: .hlsl * DirectX shader language: functions + cbuffer struct-like types. * No imports. No inheritance. * ══════════════════════════════════════════════════════════════════════════════ */ /* HLSL: pixel shader functions must reach graph. */ TEST(hlsl_function_nodes) { ProbeLangProj lp; cbm_store_t *store = pb_index(&lp, "pixel.hlsl", "float4 tint(float4 color, float factor) {\n" " return color * factor;\n" "}\n\n" "float luminance(float4 c) {\n" " return dot(c.rgb, float3(0.299, 0.587, 0.114));\n" "}\n\n" "float4 PSMain(float4 pos : SV_Position) : SV_Target {\n" " float4 c = float4(1.0, 0.5, 0.2, 1.0);\n" " return tint(c, luminance(c));\n" "}\n"); int fns = store ? pb_callable_nodes(store, lp.project) : -1; pb_cleanup(&lp, store); ASSERT_TRUE(fns >= 1); /* tint, luminance, PSMain */ PASS(); } /* HLSL: struct definition reaches graph as type node. */ TEST(hlsl_struct_node) { ProbeLangProj lp; cbm_store_t *store = pb_index(&lp, "types.hlsl", "struct VSInput {\n" " float4 position : POSITION;\n" " float2 texcoord : TEXCOORD0;\n" "};\n\n" "struct VSOutput {\n" " float4 position : SV_Position;\n" " float2 texcoord : TEXCOORD0;\n" "};\n\n" "VSOutput VSMain(VSInput input) {\n" " VSOutput output;\n" " output.position = input.position;\n" " output.texcoord = input.texcoord;\n" " return output;\n" "}\n"); int types = store ? pb_type_nodes(store, lp.project) : -1; pb_cleanup(&lp, store); ASSERT_TRUE(types >= 1); /* VSInput, VSOutput */ PASS(); } /* HLSL: compute shader kernel (another entrypoint style). */ TEST(hlsl_compute_shader_function) { ProbeLangProj lp; cbm_store_t *store = pb_index(&lp, "compute.hlsl", "RWBuffer gOutput : register(u0);\n\n" "float transform(float x) {\n" " return x * x;\n" "}\n\n" "[numthreads(64, 1, 1)]\n" "void CSMain(uint3 id : SV_DispatchThreadID) {\n" " gOutput[id.x] = transform((float)id.x);\n" "}\n"); int fns = store ? pb_callable_nodes(store, lp.project) : -1; pb_cleanup(&lp, store); ASSERT_TRUE(fns >= 1); /* transform, CSMain */ PASS(); } /* ══════════════════════════════════════════════════════════════════════════════ * ISPC — ext: .ispc * Intel SPMD language: task/export/uniform functions, struct types. * No imports. No inheritance. * ══════════════════════════════════════════════════════════════════════════════ */ /* ISPC: uniform and varying function nodes. */ TEST(ispc_function_nodes) { ProbeLangProj lp; cbm_store_t *store = pb_index(&lp, "kernel.ispc", "float square(float x) {\n" " return x * x;\n" "}\n\n" "export void add_arrays(uniform float output[],\n" " uniform float a[],\n" " uniform float b[],\n" " uniform int count) {\n" " foreach (i = 0 ... count) {\n" " output[i] = a[i] + square(b[i]);\n" " }\n" "}\n"); int fns = store ? pb_callable_nodes(store, lp.project) : -1; pb_cleanup(&lp, store); ASSERT_TRUE(fns >= 1); /* square, add_arrays */ PASS(); } /* ISPC: struct definition reaches graph as type node. */ TEST(ispc_struct_node) { ProbeLangProj lp; cbm_store_t *store = pb_index(&lp, "types.ispc", "struct Vec3 {\n" " float x, y, z;\n" "};\n\n" "float dot3(Vec3 a, Vec3 b) {\n" " return a.x*b.x + a.y*b.y + a.z*b.z;\n" "}\n\n" "export void normalize_batch(uniform Vec3 vecs[], uniform int n) {\n" " foreach (i = 0 ... n) {\n" " float len = dot3(vecs[i], vecs[i]);\n" " (void)len;\n" " }\n" "}\n"); int types = store ? pb_type_nodes(store, lp.project) : -1; pb_cleanup(&lp, store); ASSERT_TRUE(types >= 1); /* struct Vec3 */ PASS(); } /* ISPC: task function (another callable kind). */ TEST(ispc_task_function) { ProbeLangProj lp; cbm_store_t *store = pb_index(&lp, "tasks.ispc", "uniform float compute(uniform float x) {\n" " return x * 2.0f;\n" "}\n\n" "task void render_tile(uniform float buf[], uniform int w, uniform int h) {\n" " foreach (j = 0 ... h, i = 0 ... w) {\n" " buf[j*w + i] = compute((float)i);\n" " }\n" "}\n"); int fns = store ? pb_callable_nodes(store, lp.project) : -1; pb_cleanup(&lp, store); ASSERT_TRUE(fns >= 1); /* compute, render_tile */ PASS(); } /* ══════════════════════════════════════════════════════════════════════════════ * JULIA — ext: .jl * Dynamic language: function defs, struct types, `using`/`import` imports, * abstract type subtyping with `<:`. * ══════════════════════════════════════════════════════════════════════════════ */ /* Julia: function and struct nodes. */ TEST(julia_function_and_struct_nodes) { ProbeLangProj lp; cbm_store_t *store = pb_index(&lp, "geom.jl", "struct Point\n" " x::Float64\n" " y::Float64\n" "end\n\n" "function distance(a::Point, b::Point)::Float64\n" " return sqrt((b.x - a.x)^2 + (b.y - a.y)^2)\n" "end\n\n" "function midpoint(a::Point, b::Point)::Point\n" " return Point((a.x + b.x) / 2, (a.y + b.y) / 2)\n" "end\n"); int fns = store ? pb_callable_nodes(store, lp.project) : -1; int types = store ? pb_type_nodes(store, lp.project) : -1; pb_cleanup(&lp, store); ASSERT_TRUE(fns >= 1); /* distance, midpoint */ ASSERT_TRUE(types >= 1); /* struct Point */ PASS(); } /* Julia: `using` import statement captured at extraction level. */ TEST(julia_using_import_extracted) { static const char *src = "using LinearAlgebra\n" "using Statistics: mean, std\n\n" "function norm_vec(v::Vector{Float64})::Float64\n" " return norm(v)\n" "end\n"; CBMFileResult *r = cbm_extract_file(src, (int)strlen(src), CBM_LANG_JULIA, "lc", "vec.jl", 0, NULL, NULL); ASSERT_NOT_NULL(r); int n = r->imports.count; cbm_free_result(r); ASSERT_TRUE(n >= 1); /* using LinearAlgebra; using Statistics → BUG if 0 */ PASS(); } /* Julia: `import` statement captured at extraction level. */ TEST(julia_import_extracted) { static const char *src = "import Base: show, length\n" "import Random\n\n" "struct MyVec\n" " data::Vector{Float64}\n" "end\n\n" "Base.length(v::MyVec) = length(v.data)\n"; CBMFileResult *r = cbm_extract_file(src, (int)strlen(src), CBM_LANG_JULIA, "lc", "myvec.jl", 0, NULL, NULL); ASSERT_NOT_NULL(r); int n = r->imports.count; cbm_free_result(r); ASSERT_TRUE(n >= 1); /* import Base; import Random → BUG if 0 */ PASS(); } /* Julia: abstract type subtyping (`<:`) — extraction captures base_classes. */ TEST(julia_abstract_subtype_extracted) { static const char *src = "abstract type Shape end\n\n" "abstract type Polygon <: Shape end\n\n" "struct Triangle <: Polygon\n" " a::Float64\n" " b::Float64\n" " c::Float64\n" "end\n\n" "function area(t::Triangle)::Float64\n" " s = (t.a + t.b + t.c) / 2\n" " return sqrt(s * (s-t.a) * (s-t.b) * (s-t.c))\n" "end\n"; CBMFileResult *r = cbm_extract_file(src, (int)strlen(src), CBM_LANG_JULIA, "lc", "shapes.jl", 0, NULL, NULL); ASSERT_NOT_NULL(r); /* We expect at least 1 def whose base_classes is non-empty (Polygon<:Shape or * Triangle<:Polygon). Iterate defs to check. */ int found_base = 0; for (int i = 0; i < r->defs.count; i++) { if (r->defs.items[i].base_classes && r->defs.items[i].base_classes[0] != NULL) { found_base = 1; break; } } cbm_free_result(r); ASSERT_TRUE(found_base); /* BUG if 0: julia `<:` base not extracted */ PASS(); } /* Julia: full-pipeline INHERITS edge for same-file abstract subtype chain. */ TEST(julia_inherits_edge) { ProbeLangProj lp; cbm_store_t *store = pb_index(&lp, "animals.jl", "abstract type Animal end\n\n" "abstract type Pet <: Animal end\n\n" "struct Dog <: Pet\n" " name::String\n" "end\n\n" "function speak(d::Dog)::String\n" " return \"Woof: \" * d.name\n" "end\n"); int inherits = store ? cbm_store_count_edges_by_type(store, lp.project, "INHERITS") : -1; pb_cleanup(&lp, store); ASSERT_TRUE(inherits >= 1); /* Pet<:Animal, Dog<:Pet → BUG if 0 */ PASS(); } /* ══════════════════════════════════════════════════════════════════════════════ * LUAU — ext: .luau * Roblox Lua dialect: function nodes. No class inheritance, no module imports * that map to IMPORTS edges (require() is dynamic, not a static import). * ══════════════════════════════════════════════════════════════════════════════ */ /* Luau: function nodes reach the graph. */ TEST(luau_function_nodes) { ProbeLangProj lp; cbm_store_t *store = pb_index(&lp, "utils.luau", "local function clamp(value: number, min: number, max: number): number\n" " if value < min then return min end\n" " if value > max then return max end\n" " return value\n" "end\n\n" "local function lerp(a: number, b: number, t: number): number\n" " return a + (b - a) * clamp(t, 0, 1)\n" "end\n\n" "return { clamp = clamp, lerp = lerp }\n"); int fns = store ? pb_callable_nodes(store, lp.project) : -1; pb_cleanup(&lp, store); ASSERT_TRUE(fns >= 1); /* clamp, lerp */ PASS(); } /* Luau: type alias and interface-like construct (type keyword). */ TEST(luau_type_nodes) { ProbeLangProj lp; cbm_store_t *store = pb_index(&lp, "types.luau", "type Vector2 = {\n" " x: number,\n" " y: number,\n" "}\n\n" "type Entity = {\n" " id: number,\n" " name: string,\n" " position: Vector2,\n" "}\n\n" "local function make_entity(id: number, name: string): Entity\n" " return { id = id, name = name, position = { x = 0, y = 0 } }\n" "end\n\n" "return { make_entity = make_entity }\n"); /* Either type nodes or function nodes must be present */ int nodes = store ? (pb_callable_nodes(store, lp.project) + pb_type_nodes(store, lp.project)) : -1; pb_cleanup(&lp, store); ASSERT_TRUE(nodes >= 1); /* make_entity function at minimum */ PASS(); } /* Luau: class-style OOP via metatables (functions only — no native class keyword). */ TEST(luau_class_style_functions) { ProbeLangProj lp; cbm_store_t *store = pb_index(&lp, "player.luau", "local Player = {}\n" "Player.__index = Player\n\n" "function Player.new(name: string): Player\n" " return setmetatable({ name = name, health = 100 }, Player)\n" "end\n\n" "function Player:takeDamage(amount: number): ()\n" " self.health = self.health - amount\n" "end\n\n" "function Player:isAlive(): boolean\n" " return self.health > 0\n" "end\n\n" "return Player\n"); int fns = store ? pb_callable_nodes(store, lp.project) : -1; pb_cleanup(&lp, store); ASSERT_TRUE(fns >= 1); /* Player.new, Player:takeDamage, Player:isAlive */ PASS(); } /* ══════════════════════════════════════════════════════════════════════════════ * MATLAB — ext: .m * Scientific language: function definitions in .m files. * No standard cross-file import syntax modeled as IMPORTS. * No inheritance (classdef exists but rarely used; probe basic functions only). * ══════════════════════════════════════════════════════════════════════════════ */ /* MATLAB: top-level function and local subfunctions reach graph. */ TEST(matlab_function_nodes) { ProbeLangProj lp; cbm_store_t *store = pb_index(&lp, "stats.m", "function result = compute_mean(data)\n" " result = sum_values(data) / length(data);\n" "end\n\n" "function s = sum_values(v)\n" " s = 0;\n" " for i = 1:length(v)\n" " s = s + v(i);\n" " end\n" "end\n"); int fns = store ? pb_callable_nodes(store, lp.project) : -1; pb_cleanup(&lp, store); ASSERT_TRUE(fns >= 1); /* compute_mean, sum_values */ PASS(); } /* MATLAB: multiple function file — all functions are nodes. */ TEST(matlab_multiple_functions) { ProbeLangProj lp; cbm_store_t *store = pb_index(&lp, "linalg.m", "function c = dot_product(a, b)\n" " c = sum(a .* b);\n" "end\n\n" "function n = vec_norm(v)\n" " n = sqrt(dot_product(v, v));\n" "end\n\n" "function u = normalize(v)\n" " u = v / vec_norm(v);\n" "end\n"); int fns = store ? pb_callable_nodes(store, lp.project) : -1; pb_cleanup(&lp, store); ASSERT_TRUE(fns >= 1); /* dot_product, vec_norm, normalize */ PASS(); } /* ══════════════════════════════════════════════════════════════════════════════ * ODIN — ext: .odin * Systems language: proc declarations, struct types, `import` statements. * No class inheritance (Odin is not OOP). * ══════════════════════════════════════════════════════════════════════════════ */ /* Odin: proc nodes reach graph. */ TEST(odin_proc_nodes) { ProbeLangProj lp; cbm_store_t *store = pb_index(&lp, "math.odin", "package math\n\n" "square :: proc(x: f64) -> f64 {\n" " return x * x\n" "}\n\n" "cube :: proc(x: f64) -> f64 {\n" " return x * square(x)\n" "}\n\n" "clamp :: proc(v, lo, hi: f64) -> f64 {\n" " if v < lo { return lo }\n" " if v > hi { return hi }\n" " return v\n" "}\n"); int fns = store ? pb_callable_nodes(store, lp.project) : -1; pb_cleanup(&lp, store); ASSERT_TRUE(fns >= 1); /* square, cube, clamp */ PASS(); } /* Odin: struct type node. */ TEST(odin_struct_node) { ProbeLangProj lp; cbm_store_t *store = pb_index(&lp, "point.odin", "package point\n\n" "Vec2 :: struct {\n" " x, y: f64,\n" "}\n\n" "Vec3 :: struct {\n" " x, y, z: f64,\n" "}\n\n" "length2 :: proc(v: Vec2) -> f64 {\n" " return v.x*v.x + v.y*v.y\n" "}\n"); int types = store ? pb_type_nodes(store, lp.project) : -1; pb_cleanup(&lp, store); ASSERT_TRUE(types >= 1); /* Vec2, Vec3 */ PASS(); } /* Odin: `import` statement captured at extraction level. */ TEST(odin_import_extracted) { static const char *src = "package main\n\n" "import \"core:fmt\"\n" "import \"core:os\"\n" "import math \"core:math\"\n\n" "main :: proc() {\n" " fmt.println(\"hello\")\n" "}\n"; CBMFileResult *r = cbm_extract_file(src, (int)strlen(src), CBM_LANG_ODIN, "lc", "main.odin", 0, NULL, NULL); ASSERT_NOT_NULL(r); int n = r->imports.count; cbm_free_result(r); ASSERT_TRUE(n >= 1); /* import "core:fmt"; import "core:os" → BUG if 0 */ PASS(); } /* ══════════════════════════════════════════════════════════════════════════════ * PASCAL — ext: .pas * Classic structured language: procedures/functions, record/class types, * `uses` import clause, OOP inheritance via `: TBase`. * ══════════════════════════════════════════════════════════════════════════════ */ /* Pascal: procedure and function nodes. */ TEST(pascal_function_nodes) { ProbeLangProj lp; cbm_store_t *store = pb_index(&lp, "arith.pas", "unit Arith;\n\n" "interface\n\n" "function Add(a, b: Integer): Integer;\n" "function Multiply(a, b: Integer): Integer;\n" "procedure PrintResult(val: Integer);\n\n" "implementation\n\n" "function Add(a, b: Integer): Integer;\n" "begin\n" " Result := a + b;\n" "end;\n\n" "function Multiply(a, b: Integer): Integer;\n" "begin\n" " Result := a * b;\n" "end;\n\n" "procedure PrintResult(val: Integer);\n" "begin\n" " Writeln(val);\n" "end;\n\n" "end.\n"); int fns = store ? pb_callable_nodes(store, lp.project) : -1; pb_cleanup(&lp, store); ASSERT_TRUE(fns >= 1); /* Add, Multiply, PrintResult */ PASS(); } /* Pascal: record type node. */ TEST(pascal_record_node) { ProbeLangProj lp; cbm_store_t *store = pb_index(&lp, "geom.pas", "unit Geom;\n\n" "interface\n\n" "type\n" " TPoint = record\n" " X, Y: Double;\n" " end;\n\n" " TRect = record\n" " TopLeft: TPoint;\n" " BottomRight: TPoint;\n" " end;\n\n" "function Distance(P1, P2: TPoint): Double;\n\n" "implementation\n\n" "function Distance(P1, P2: TPoint): Double;\n" "begin\n" " Result := 0.0;\n" "end;\n\n" "end.\n"); int types = store ? pb_type_nodes(store, lp.project) : -1; pb_cleanup(&lp, store); ASSERT_TRUE(types >= 1); /* TPoint, TRect */ PASS(); } /* Pascal: `uses` clause captured at extraction level. */ TEST(pascal_uses_import_extracted) { static const char *src = "unit MyUnit;\n\n" "interface\n\n" "uses\n" " SysUtils, Classes, Math;\n\n" "procedure DoWork;\n\n" "implementation\n\n" "procedure DoWork;\n" "begin\n" " Writeln(IntToStr(42));\n" "end;\n\n" "end.\n"; CBMFileResult *r = cbm_extract_file(src, (int)strlen(src), CBM_LANG_PASCAL, "lc", "myunit.pas", 0, NULL, NULL); ASSERT_NOT_NULL(r); int n = r->imports.count; cbm_free_result(r); ASSERT_TRUE(n >= 1); /* uses SysUtils, Classes, Math → BUG if 0 */ PASS(); } /* Pascal: OOP class inheritance (`: TAnimal`) — extraction captures base_classes. */ TEST(pascal_class_inheritance_extracted) { static const char *src = "unit Zoo;\n\n" "interface\n\n" "type\n" " TAnimal = class\n" " procedure Speak; virtual;\n" " end;\n\n" " TDog = class(TAnimal)\n" " procedure Speak; override;\n" " end;\n\n" " TCat = class(TAnimal)\n" " procedure Speak; override;\n" " end;\n\n" "implementation\n\n" "procedure TAnimal.Speak; begin end;\n" "procedure TDog.Speak; begin Writeln('Woof'); end;\n" "procedure TCat.Speak; begin Writeln('Meow'); end;\n\n" "end.\n"; CBMFileResult *r = cbm_extract_file(src, (int)strlen(src), CBM_LANG_PASCAL, "lc", "zoo.pas", 0, NULL, NULL); ASSERT_NOT_NULL(r); int found_base = 0; for (int i = 0; i < r->defs.count; i++) { if (r->defs.items[i].base_classes && r->defs.items[i].base_classes[0] != NULL) { found_base = 1; break; } } cbm_free_result(r); ASSERT_TRUE(found_base); /* TDog(TAnimal), TCat(TAnimal) → BUG if 0 */ PASS(); } /* Pascal: full-pipeline INHERITS edge for OOP class hierarchy. */ TEST(pascal_inherits_edge) { ProbeLangProj lp; cbm_store_t *store = pb_index(&lp, "vehicles.pas", "unit Vehicles;\n\n" "interface\n\n" "type\n" " TVehicle = class\n" " function GetSpeed: Integer; virtual;\n" " end;\n\n" " TCar = class(TVehicle)\n" " function GetSpeed: Integer; override;\n" " end;\n\n" " TTruck = class(TVehicle)\n" " function GetSpeed: Integer; override;\n" " end;\n\n" "implementation\n\n" "function TVehicle.GetSpeed: Integer; begin Result := 0; end;\n" "function TCar.GetSpeed: Integer; begin Result := 100; end;\n" "function TTruck.GetSpeed: Integer; begin Result := 80; end;\n\n" "end.\n"); int inherits = store ? cbm_store_count_edges_by_type(store, lp.project, "INHERITS") : -1; pb_cleanup(&lp, store); ASSERT_TRUE(inherits >= 1); /* TCar->TVehicle, TTruck->TVehicle → BUG if 0 */ PASS(); } /* ══════════════════════════════════════════════════════════════════════════════ * POWERSHELL — ext: .ps1 * Scripting language: function + class definitions, `using module` imports. * OOP inheritance via `: Base`. * ══════════════════════════════════════════════════════════════════════════════ */ /* PowerShell: function nodes reach graph. */ TEST(powershell_function_nodes) { ProbeLangProj lp; cbm_store_t *store = pb_index(&lp, "utils.ps1", "function Get-Square {\n" " param([double]$x)\n" " return $x * $x\n" "}\n\n" "function Get-Cube {\n" " param([double]$x)\n" " return $x * (Get-Square $x)\n" "}\n\n" "function Test-Positive {\n" " param([double]$x)\n" " return $x -gt 0\n" "}\n"); int fns = store ? pb_callable_nodes(store, lp.project) : -1; pb_cleanup(&lp, store); ASSERT_TRUE(fns >= 1); /* Get-Square, Get-Cube, Test-Positive */ PASS(); } /* PowerShell: class definition reaches graph as type node. */ TEST(powershell_class_node) { ProbeLangProj lp; cbm_store_t *store = pb_index(&lp, "shapes.ps1", "class Shape {\n" " [string]$Name\n" " Shape([string]$name) { $this.Name = $name }\n" " [double] Area() { return 0.0 }\n" "}\n\n" "class Circle : Shape {\n" " [double]$Radius\n" " Circle([double]$r) : base('Circle') { $this.Radius = $r }\n" " [double] Area() { return [Math]::PI * $this.Radius * $this.Radius }\n" "}\n"); int types = store ? pb_type_nodes(store, lp.project) : -1; pb_cleanup(&lp, store); ASSERT_TRUE(types >= 1); /* Shape, Circle */ PASS(); } /* PowerShell: `using module` / `using namespace` captured at extraction level. */ TEST(powershell_using_import_extracted) { static const char *src = "using module ./MyModule\n" "using namespace System.IO\n" "using assembly System.Drawing\n\n" "function Invoke-Task {\n" " param([string]$Path)\n" " [System.IO.File]::Exists($Path)\n" "}\n"; CBMFileResult *r = cbm_extract_file(src, (int)strlen(src), CBM_LANG_POWERSHELL, "lc", "task.ps1", 0, NULL, NULL); ASSERT_NOT_NULL(r); int n = r->imports.count; cbm_free_result(r); ASSERT_TRUE(n >= 1); /* using module / using namespace → BUG if 0 */ PASS(); } /* PowerShell: class inheritance (`: Base`) — extraction captures base_classes. */ TEST(powershell_class_inheritance_extracted) { static const char *src = "class Animal {\n" " [string]$Name\n" " Animal([string]$name) { $this.Name = $name }\n" " [string] Speak() { return 'some sound' }\n" "}\n\n" "class Dog : Animal {\n" " Dog([string]$name) : base($name) {}\n" " [string] Speak() { return 'Woof' }\n" "}\n\n" "class Cat : Animal {\n" " Cat([string]$name) : base($name) {}\n" " [string] Speak() { return 'Meow' }\n" "}\n"; CBMFileResult *r = cbm_extract_file(src, (int)strlen(src), CBM_LANG_POWERSHELL, "lc", "animals.ps1", 0, NULL, NULL); ASSERT_NOT_NULL(r); int found_base = 0; for (int i = 0; i < r->defs.count; i++) { if (r->defs.items[i].base_classes && r->defs.items[i].base_classes[0] != NULL) { found_base = 1; break; } } cbm_free_result(r); ASSERT_TRUE(found_base); /* Dog : Animal, Cat : Animal → BUG if 0 */ PASS(); } /* PowerShell: full-pipeline INHERITS edge from same-file class hierarchy. */ TEST(powershell_inherits_edge) { ProbeLangProj lp; cbm_store_t *store = pb_index(&lp, "exceptions.ps1", "class AppException {\n" " [string]$Message\n" " AppException([string]$msg) { $this.Message = $msg }\n" "}\n\n" "class DatabaseException : AppException {\n" " DatabaseException([string]$msg) : base($msg) {}\n" "}\n\n" "class NetworkException : AppException {\n" " NetworkException([string]$msg) : base($msg) {}\n" "}\n"); int inherits = store ? cbm_store_count_edges_by_type(store, lp.project, "INHERITS") : -1; pb_cleanup(&lp, store); ASSERT_TRUE(inherits >= 1); /* Database->App, Network->App → BUG if 0 */ PASS(); } /* ══════════════════════════════════════════════════════════════════════════════ * RACKET — ext: .rkt * Lisp dialect: define functions/procedures, `require` imports. * No class-style inheritance (uses `struct` for types, not OOP classes). * ══════════════════════════════════════════════════════════════════════════════ */ /* Racket: function (define) nodes reach graph. */ TEST(racket_function_nodes) { ProbeLangProj lp; cbm_store_t *store = pb_index(&lp, "math.rkt", "#lang racket\n\n" "(define (square x) (* x x))\n\n" "(define (cube x) (* x (square x)))\n\n" "(define (sum-of-squares a b)\n" " (+ (square a) (square b)))\n\n" "(define (hypotenuse a b)\n" " (sqrt (sum-of-squares a b)))\n"); int fns = store ? pb_callable_nodes(store, lp.project) : -1; pb_cleanup(&lp, store); ASSERT_TRUE(fns >= 1); /* square, cube, sum-of-squares, hypotenuse */ PASS(); } /* Racket: struct definition reaches graph as type node. */ TEST(racket_struct_node) { ProbeLangProj lp; cbm_store_t *store = pb_index(&lp, "point.rkt", "#lang racket\n\n" "(struct point (x y) #:transparent)\n\n" "(struct circle (center radius) #:transparent)\n\n" "(define (distance p1 p2)\n" " (let ([dx (- (point-x p2) (point-x p1))]\n" " [dy (- (point-y p2) (point-y p1))])\n" " (sqrt (+ (* dx dx) (* dy dy)))))\n"); int types = store ? pb_type_nodes(store, lp.project) : -1; pb_cleanup(&lp, store); ASSERT_TRUE(types >= 1); /* struct point, struct circle → BUG if 0 */ PASS(); } /* Racket: `require` import captured at extraction level. */ TEST(racket_require_import_extracted) { static const char *src = "#lang racket\n\n" "(require racket/list)\n" "(require racket/string)\n" "(require (only-in racket/math pi))\n\n" "(define (circle-area r)\n" " (* pi r r))\n\n" "(define (join-words words)\n" " (string-join words \" \"))\n"; CBMFileResult *r = cbm_extract_file(src, (int)strlen(src), CBM_LANG_RACKET, "lc", "utils.rkt", 0, NULL, NULL); ASSERT_NOT_NULL(r); int n = r->imports.count; cbm_free_result(r); ASSERT_TRUE(n >= 1); /* require racket/list etc. → BUG if 0 */ PASS(); } /* Racket: graph-level IMPORTS edge from two-file project with require. */ TEST(racket_imports_edge) { static const ProbeLangFile files[] = { {"math.rkt", "#lang racket\n" "(provide square cube)\n" "(define (square x) (* x x))\n" "(define (cube x) (* x (square x)))\n"}, {"main.rkt", "#lang racket\n" "(require \"math.rkt\")\n" "(define (run n) (+ (square n) (cube n)))\n"}}; ProbeLangProj lp; cbm_store_t *store = pb_index_files(&lp, files, 2); int imports = store ? cbm_store_count_edges_by_type(store, lp.project, "IMPORTS") : -1; pb_cleanup(&lp, store); ASSERT_TRUE(imports >= 1); /* main.rkt requires math.rkt → BUG if 0 */ PASS(); } /* ══════════════════════════════════════════════════════════════════════════════ * RESCRIPT — ext: .res * ReasonML-like JS compiler: let-bound functions, type definitions, * `open` module imports. No class inheritance. * ══════════════════════════════════════════════════════════════════════════════ */ /* ReScript: let-bound function nodes reach graph. */ TEST(rescript_function_nodes) { ProbeLangProj lp; cbm_store_t *store = pb_index(&lp, "math.res", "let square = (x: float) => x *. x\n\n" "let cube = (x: float) => x *. square(x)\n\n" "let clamp = (v: float, lo: float, hi: float) =>\n" " if v < lo { lo } else if v > hi { hi } else { v }\n"); int fns = store ? pb_callable_nodes(store, lp.project) : -1; pb_cleanup(&lp, store); ASSERT_TRUE(fns >= 1); /* square, cube, clamp */ PASS(); } /* ReScript: type definition reaches graph as type node. */ TEST(rescript_type_node) { ProbeLangProj lp; cbm_store_t *store = pb_index(&lp, "types.res", "type color = Red | Green | Blue\n\n" "type point = {\n" " x: float,\n" " y: float,\n" "}\n\n" "let origin: point = {x: 0.0, y: 0.0}\n\n" "let to_hex = (c: color) =>\n" " switch c {\n" " | Red => \"#ff0000\"\n" " | Green => \"#00ff00\"\n" " | Blue => \"#0000ff\"\n" " }\n"); int types = store ? pb_type_nodes(store, lp.project) : -1; pb_cleanup(&lp, store); ASSERT_TRUE(types >= 1); /* type color, type point → BUG if 0 */ PASS(); } /* ReScript: `open` import captured at extraction level. */ TEST(rescript_open_import_extracted) { static const char *src = "open Belt\n" "open Belt.Array\n\n" "let sum = (arr: array) =>\n" " Array.reduce(arr, 0, (acc, x) => acc + x)\n\n" "let max_val = (arr: array) =>\n" " Array.reduce(arr, Int.min_int, (acc, x) => if x > acc { x } else { acc })\n"; CBMFileResult *r = cbm_extract_file(src, (int)strlen(src), CBM_LANG_RESCRIPT, "lc", "utils.res", 0, NULL, NULL); ASSERT_NOT_NULL(r); int n = r->imports.count; cbm_free_result(r); ASSERT_TRUE(n >= 1); /* open Belt; open Belt.Array → BUG if 0 */ PASS(); } /* ReScript: graph-level IMPORTS edge from two-file project. */ TEST(rescript_imports_edge) { static const ProbeLangFile files[] = { {"Utils.res", "let square = (x: float) => x *. x\n" "let cube = (x: float) => x *. square(x)\n"}, {"Main.res", "open Utils\n\n" "let run = (n: float) => square(n) +. cube(n)\n"}}; ProbeLangProj lp; cbm_store_t *store = pb_index_files(&lp, files, 2); int imports = store ? cbm_store_count_edges_by_type(store, lp.project, "IMPORTS") : -1; pb_cleanup(&lp, store); ASSERT_TRUE(imports >= 1); /* Main opens Utils → BUG if 0 */ PASS(); } /* ══════════════════════════════════════════════════════════════════════════════ * SUITE REGISTRATION * ══════════════════════════════════════════════════════════════════════════════ */ SUITE(grammar_probe_b) { /* ── GLSL (3 cases) ── */ RUN_TEST(glsl_function_nodes); RUN_TEST(glsl_struct_node); RUN_TEST(glsl_vertex_shader_functions); /* ── Hare (3 cases) ── */ RUN_TEST(hare_function_nodes); RUN_TEST(hare_struct_node); RUN_TEST(hare_use_import_extracted); /* ── HLSL (3 cases) ── */ RUN_TEST(hlsl_function_nodes); RUN_TEST(hlsl_struct_node); RUN_TEST(hlsl_compute_shader_function); /* ── ISPC (3 cases) ── */ RUN_TEST(ispc_function_nodes); RUN_TEST(ispc_struct_node); RUN_TEST(ispc_task_function); /* ── Julia (5 cases) ── */ RUN_TEST(julia_function_and_struct_nodes); RUN_TEST(julia_using_import_extracted); RUN_TEST(julia_import_extracted); RUN_TEST(julia_abstract_subtype_extracted); RUN_TEST(julia_inherits_edge); /* ── Luau (3 cases) ── */ RUN_TEST(luau_function_nodes); RUN_TEST(luau_type_nodes); RUN_TEST(luau_class_style_functions); /* ── MATLAB (2 cases) ── */ RUN_TEST(matlab_function_nodes); RUN_TEST(matlab_multiple_functions); /* ── Odin (3 cases) ── */ RUN_TEST(odin_proc_nodes); RUN_TEST(odin_struct_node); RUN_TEST(odin_import_extracted); /* ── Pascal (5 cases) ── */ RUN_TEST(pascal_function_nodes); RUN_TEST(pascal_record_node); RUN_TEST(pascal_uses_import_extracted); RUN_TEST(pascal_class_inheritance_extracted); RUN_TEST(pascal_inherits_edge); /* ── PowerShell (5 cases) ── */ RUN_TEST(powershell_function_nodes); RUN_TEST(powershell_class_node); RUN_TEST(powershell_using_import_extracted); RUN_TEST(powershell_class_inheritance_extracted); RUN_TEST(powershell_inherits_edge); /* ── Racket (4 cases) ── */ RUN_TEST(racket_function_nodes); RUN_TEST(racket_struct_node); RUN_TEST(racket_require_import_extracted); RUN_TEST(racket_imports_edge); /* ── ReScript (4 cases) ── */ RUN_TEST(rescript_function_nodes); RUN_TEST(rescript_type_node); RUN_TEST(rescript_open_import_extracted); RUN_TEST(rescript_imports_edge); }