Files
wehub-resource-sync 41cb1c0170
OpenSSF Scorecard / scorecard (push) Failing after 0s
DCO / dco (push) Failing after 0s
CodeQL SAST / analyze (push) Failing after 1s
Deploy Pages / deploy (push) Failing after 1s
chore: import upstream snapshot with attribution
2026-07-13 12:28:05 +08:00

1205 lines
47 KiB
C

/*
* 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 <mcp/mcp.h>
#include <store/store.h>
#include <pipeline/pipeline.h>
#include <foundation/log.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#if !defined(_WIN32)
#include <sys/wait.h>
#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<float> 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<int>) =>\n"
" Array.reduce(arr, 0, (acc, x) => acc + x)\n\n"
"let max_val = (arr: array<int>) =>\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);
}