Sprint 288: LibrarySymbolAdvisor (steps 1968-1972)
Specific API symbol recommendation layer. Five new headers: - LibrarySymbolRecord: functionName/signature/usagePattern/caveats - LibrarySymbolCatalog: multi-symbol registry per (library, domain) - LSPSymbolExtractor: parses LSP JSON into LibrarySymbolRecords - LibrarySymbolAdvisor: ranked symbols for (library, domain) alphabetically - Sprint288IntegrationSummary: CUDA+nlohmann catalog; cublasSgemm+json::parse verified 25/25 tests passing. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -11114,3 +11114,18 @@ target_include_directories(step1966_test PRIVATE src)
|
||||
|
||||
add_executable(step1967_test tests/step1967_test.cpp)
|
||||
target_include_directories(step1967_test PRIVATE src)
|
||||
|
||||
add_executable(step1968_test tests/step1968_test.cpp)
|
||||
target_include_directories(step1968_test PRIVATE src)
|
||||
|
||||
add_executable(step1969_test tests/step1969_test.cpp)
|
||||
target_include_directories(step1969_test PRIVATE src)
|
||||
|
||||
add_executable(step1970_test tests/step1970_test.cpp)
|
||||
target_include_directories(step1970_test PRIVATE src)
|
||||
|
||||
add_executable(step1971_test tests/step1971_test.cpp)
|
||||
target_include_directories(step1971_test PRIVATE src)
|
||||
|
||||
add_executable(step1972_test tests/step1972_test.cpp)
|
||||
target_include_directories(step1972_test PRIVATE src)
|
||||
|
||||
52
editor/src/LSPSymbolExtractor.h
Normal file
52
editor/src/LSPSymbolExtractor.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
// Step 1970: LSPSymbolExtractor
|
||||
// Parses a simplified LSP-like JSON string into LibrarySymbolRecords.
|
||||
// Input format: {"symbols": [{"name": "...", "signature": "..."}]}
|
||||
// Requires nlohmann_json (available via vcpkg).
|
||||
|
||||
#include "LibrarySymbolRecord.h"
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace whetstone {
|
||||
|
||||
class LSPSymbolExtractor {
|
||||
public:
|
||||
struct ExtractionResult {
|
||||
std::vector<LibrarySymbolRecord> records;
|
||||
bool success = true;
|
||||
std::string error;
|
||||
};
|
||||
|
||||
// Parse lspJson string and produce records for the given (libraryId, operationDomain).
|
||||
ExtractionResult extract(const std::string& libraryId,
|
||||
const std::string& operationDomain,
|
||||
const std::string& lspJson) const {
|
||||
try {
|
||||
auto j = nlohmann::json::parse(lspJson);
|
||||
if (!j.contains("symbols") || !j["symbols"].is_array()) {
|
||||
return {{}, false, "missing or non-array 'symbols' key"};
|
||||
}
|
||||
std::vector<LibrarySymbolRecord> records;
|
||||
for (const auto& sym : j["symbols"]) {
|
||||
LibrarySymbolRecord r;
|
||||
r.libraryId = libraryId;
|
||||
r.operationDomain = operationDomain;
|
||||
r.functionName = sym.value("name", std::string{});
|
||||
r.signature = sym.value("signature", std::string{});
|
||||
r.usagePattern = sym.value("usage", std::string{});
|
||||
if (sym.contains("caveats") && sym["caveats"].is_array()) {
|
||||
for (const auto& c : sym["caveats"])
|
||||
r.caveats.push_back(c.get<std::string>());
|
||||
}
|
||||
if (!r.functionName.empty()) records.push_back(std::move(r));
|
||||
}
|
||||
return {std::move(records), true, ""};
|
||||
} catch (const nlohmann::json::exception& e) {
|
||||
return {{}, false, std::string("json parse error: ") + e.what()};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace whetstone
|
||||
40
editor/src/LibrarySymbolAdvisor.h
Normal file
40
editor/src/LibrarySymbolAdvisor.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
// Step 1971: LibrarySymbolAdvisor
|
||||
// Given (libraryId, operationDomain, catalog) -> ranked symbol recommendations.
|
||||
// Ranking: exact domain match, sorted by functionName alphabetically.
|
||||
|
||||
#include "LibrarySymbolCatalog.h"
|
||||
#include "LibrarySymbolRecord.h"
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace whetstone {
|
||||
|
||||
class LibrarySymbolAdvisor {
|
||||
public:
|
||||
// Returns symbols for (libraryId, operationDomain), sorted by functionName.
|
||||
// Empty if no symbols registered for this pair.
|
||||
std::vector<LibrarySymbolRecord> advise(const std::string& libraryId,
|
||||
const std::string& operationDomain,
|
||||
const LibrarySymbolCatalog& catalog) const {
|
||||
auto symbols = catalog.get(libraryId, operationDomain);
|
||||
std::sort(symbols.begin(), symbols.end(),
|
||||
[](const LibrarySymbolRecord& a, const LibrarySymbolRecord& b) {
|
||||
return a.functionName < b.functionName;
|
||||
});
|
||||
return symbols;
|
||||
}
|
||||
|
||||
// Convenience: returns the top (first alphabetically) symbol, or nullopt.
|
||||
std::optional<LibrarySymbolRecord> topSymbol(const std::string& libraryId,
|
||||
const std::string& operationDomain,
|
||||
const LibrarySymbolCatalog& catalog) const {
|
||||
auto symbols = advise(libraryId, operationDomain, catalog);
|
||||
if (symbols.empty()) return std::nullopt;
|
||||
return symbols[0];
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace whetstone
|
||||
68
editor/src/LibrarySymbolCatalog.h
Normal file
68
editor/src/LibrarySymbolCatalog.h
Normal file
@@ -0,0 +1,68 @@
|
||||
#pragma once
|
||||
// Step 1969: LibrarySymbolCatalog
|
||||
// Registry of LibrarySymbolRecords. Multiple symbols per (libraryId, operationDomain).
|
||||
// Key: libraryId + "::" + operationDomain
|
||||
|
||||
#include "LibrarySymbolRecord.h"
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace whetstone {
|
||||
|
||||
class LibrarySymbolCatalog {
|
||||
public:
|
||||
void add(LibrarySymbolRecord record) {
|
||||
auto key = makeKey(record.libraryId, record.operationDomain);
|
||||
catalog_[key].push_back(std::move(record));
|
||||
}
|
||||
|
||||
// Returns all symbols for (libraryId, operationDomain); empty if none.
|
||||
std::vector<LibrarySymbolRecord> get(const std::string& libraryId,
|
||||
const std::string& operationDomain) const {
|
||||
auto it = catalog_.find(makeKey(libraryId, operationDomain));
|
||||
if (it == catalog_.end()) return {};
|
||||
return it->second;
|
||||
}
|
||||
|
||||
bool has(const std::string& libraryId, const std::string& operationDomain) const {
|
||||
auto it = catalog_.find(makeKey(libraryId, operationDomain));
|
||||
return it != catalog_.end() && !it->second.empty();
|
||||
}
|
||||
|
||||
// Total number of symbol records across all keys
|
||||
size_t size() const {
|
||||
size_t total = 0;
|
||||
for (const auto& [k, v] : catalog_) total += v.size();
|
||||
return total;
|
||||
}
|
||||
|
||||
std::vector<LibrarySymbolRecord> allForLibrary(const std::string& libraryId) const {
|
||||
std::vector<LibrarySymbolRecord> result;
|
||||
for (const auto& [k, v] : catalog_) {
|
||||
for (const auto& r : v) {
|
||||
if (r.libraryId == libraryId) result.push_back(r);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<LibrarySymbolRecord> allForDomain(const std::string& operationDomain) const {
|
||||
std::vector<LibrarySymbolRecord> result;
|
||||
for (const auto& [k, v] : catalog_) {
|
||||
for (const auto& r : v) {
|
||||
if (r.operationDomain == operationDomain) result.push_back(r);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
static std::string makeKey(const std::string& lib, const std::string& domain) {
|
||||
return lib + "::" + domain;
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, std::vector<LibrarySymbolRecord>> catalog_;
|
||||
};
|
||||
|
||||
} // namespace whetstone
|
||||
19
editor/src/LibrarySymbolRecord.h
Normal file
19
editor/src/LibrarySymbolRecord.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
// Step 1968: LibrarySymbolRecord
|
||||
// Specific API symbol recommendation for a (library, operationDomain) pair.
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace whetstone {
|
||||
|
||||
struct LibrarySymbolRecord {
|
||||
std::string libraryId; // e.g. "cublas"
|
||||
std::string functionName; // e.g. "cublasSgemm"
|
||||
std::string signature; // e.g. "cublasStatus_t cublasSgemm(cublasHandle_t, ...)"
|
||||
std::string operationDomain; // e.g. "compute.matrix"
|
||||
std::string usagePattern; // example call showing typical invocation
|
||||
std::vector<std::string> caveats; // known footguns or preconditions
|
||||
};
|
||||
|
||||
} // namespace whetstone
|
||||
83
editor/src/Sprint288IntegrationSummary.h
Normal file
83
editor/src/Sprint288IntegrationSummary.h
Normal file
@@ -0,0 +1,83 @@
|
||||
#pragma once
|
||||
// Step 1972: Sprint 288 Integration Summary
|
||||
// Steps 1968–1972: LibrarySymbolAdvisor
|
||||
//
|
||||
// 1968: LibrarySymbolRecord — functionName/signature/operationDomain/usagePattern/caveats
|
||||
// 1969: LibrarySymbolCatalog — multi-symbol registry per (library, domain)
|
||||
// 1970: LSPSymbolExtractor — parses LSP JSON into LibrarySymbolRecords
|
||||
// 1971: LibrarySymbolAdvisor — returns ranked symbols for (library, domain)
|
||||
// 1972: Integration — CUDA + nlohmann catalog, verify cublasSgemm + json::parse
|
||||
|
||||
#include "LibrarySymbolAdvisor.h"
|
||||
#include "LibrarySymbolCatalog.h"
|
||||
#include <string>
|
||||
|
||||
namespace whetstone {
|
||||
|
||||
struct Sprint288IntegrationSummary {
|
||||
int stepsCompleted = 5;
|
||||
bool success = true;
|
||||
std::string sprintName() const {
|
||||
return "Sprint 288: LibrarySymbolAdvisor";
|
||||
}
|
||||
|
||||
static LibrarySymbolCatalog buildSeedCatalog() {
|
||||
LibrarySymbolCatalog cat;
|
||||
|
||||
// cublas: compute.matrix
|
||||
cat.add({"cublas", "cublasSgemm",
|
||||
"cublasStatus_t cublasSgemm(cublasHandle_t, cublasOperation_t, cublasOperation_t,"
|
||||
" int m, int n, int k, const float* alpha, const float* A, int lda,"
|
||||
" const float* B, int ldb, const float* beta, float* C, int ldc)",
|
||||
"compute.matrix",
|
||||
"cublasSgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N, m, n, k, &alpha, A, lda, B, ldb, &beta, C, ldc)",
|
||||
{"requires-cublasCreate-before-use", "device-memory-only"}});
|
||||
cat.add({"cublas", "cublasDgemm",
|
||||
"cublasStatus_t cublasDgemm(cublasHandle_t, cublasOperation_t, cublasOperation_t,"
|
||||
" int m, int n, int k, const double* alpha, const double* A, int lda,"
|
||||
" const double* B, int ldb, const double* beta, double* C, int ldc)",
|
||||
"compute.matrix",
|
||||
"cublasDgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N, m, n, k, &alpha, A, lda, B, ldb, &beta, C, ldc)",
|
||||
{"double-precision", "requires-cublasCreate-before-use"}});
|
||||
|
||||
// nlohmann_json: serialization.json
|
||||
cat.add({"nlohmann_json", "json::dump",
|
||||
"std::string nlohmann::json::dump(int indent = -1) const",
|
||||
"serialization.json",
|
||||
"std::string s = j.dump(2); // pretty-print with indent=2",
|
||||
{}});
|
||||
cat.add({"nlohmann_json", "json::parse",
|
||||
"static nlohmann::json nlohmann::json::parse(const std::string& s)",
|
||||
"serialization.json",
|
||||
"auto j = nlohmann::json::parse(input_string);",
|
||||
{"throws-parse_error-on-invalid-json"}});
|
||||
|
||||
return cat;
|
||||
}
|
||||
|
||||
static bool verifyCUDALookup(const LibrarySymbolCatalog& cat) {
|
||||
LibrarySymbolAdvisor adv;
|
||||
auto syms = adv.advise("cublas", "compute.matrix", cat);
|
||||
if (syms.empty()) return false;
|
||||
// cublasDgemm < cublasSgemm alphabetically, so [0]=cublasDgemm, [1]=cublasSgemm
|
||||
bool foundSgemm = false;
|
||||
for (const auto& s : syms) {
|
||||
if (s.functionName == "cublasSgemm") { foundSgemm = true; break; }
|
||||
}
|
||||
return foundSgemm;
|
||||
}
|
||||
|
||||
static bool verifyNlohmannLookup(const LibrarySymbolCatalog& cat) {
|
||||
LibrarySymbolAdvisor adv;
|
||||
auto syms = adv.advise("nlohmann_json", "serialization.json", cat);
|
||||
if (syms.empty()) return false;
|
||||
// json::dump < json::parse alphabetically, so [0]=json::dump, [1]=json::parse
|
||||
bool foundParse = false;
|
||||
for (const auto& s : syms) {
|
||||
if (s.functionName == "json::parse") { foundParse = true; break; }
|
||||
}
|
||||
return foundParse;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace whetstone
|
||||
80
editor/tests/step1968_test.cpp
Normal file
80
editor/tests/step1968_test.cpp
Normal file
@@ -0,0 +1,80 @@
|
||||
// Step 1968: LibrarySymbolRecord
|
||||
//
|
||||
// t1: default fields
|
||||
// t2: populated record fields
|
||||
// t3: caveats list
|
||||
// t4: usagePattern field
|
||||
// t5: multiple records for different domains are independent
|
||||
|
||||
#include "LibrarySymbolRecord.h"
|
||||
#include <iostream>
|
||||
|
||||
namespace ws = whetstone;
|
||||
static int p = 0, f = 0;
|
||||
#define T(n) { std::cout << " " << #n << "... "; }
|
||||
#define P() { std::cout << "PASS\n"; ++p; }
|
||||
#define F(m) { std::cout << "FAIL: " << m << "\n"; ++f; }
|
||||
#define C(c, m) if (!(c)) { F(m); return; }
|
||||
|
||||
void t1() {
|
||||
T(default_fields_empty);
|
||||
ws::LibrarySymbolRecord r;
|
||||
C(r.libraryId.empty(), "empty libraryId");
|
||||
C(r.functionName.empty(), "empty functionName");
|
||||
C(r.signature.empty(), "empty signature");
|
||||
C(r.operationDomain.empty(), "empty operationDomain");
|
||||
C(r.caveats.empty(), "empty caveats");
|
||||
P();
|
||||
}
|
||||
|
||||
void t2() {
|
||||
T(populated_record_fields);
|
||||
ws::LibrarySymbolRecord r;
|
||||
r.libraryId = "cublas";
|
||||
r.functionName = "cublasSgemm";
|
||||
r.signature = "cublasStatus_t cublasSgemm(cublasHandle_t, ...)";
|
||||
r.operationDomain = "compute.matrix";
|
||||
C(r.libraryId == "cublas", "libraryId");
|
||||
C(r.functionName == "cublasSgemm", "functionName");
|
||||
C(r.operationDomain == "compute.matrix", "operationDomain");
|
||||
C(!r.signature.empty(), "signature set");
|
||||
P();
|
||||
}
|
||||
|
||||
void t3() {
|
||||
T(caveats_list);
|
||||
ws::LibrarySymbolRecord r;
|
||||
r.caveats = {"requires-device-memory", "no-automatic-handle"};
|
||||
C(r.caveats.size() == 2, "2 caveats");
|
||||
C(r.caveats[0] == "requires-device-memory", "first caveat");
|
||||
C(r.caveats[1] == "no-automatic-handle", "second caveat");
|
||||
P();
|
||||
}
|
||||
|
||||
void t4() {
|
||||
T(usage_pattern_field);
|
||||
ws::LibrarySymbolRecord r;
|
||||
r.libraryId = "nlohmann_json";
|
||||
r.functionName = "json::parse";
|
||||
r.usagePattern = "auto j = nlohmann::json::parse(input_string);";
|
||||
C(!r.usagePattern.empty(), "usagePattern set");
|
||||
C(r.usagePattern.find("parse") != std::string::npos, "contains 'parse'");
|
||||
P();
|
||||
}
|
||||
|
||||
void t5() {
|
||||
T(records_for_different_domains_independent);
|
||||
ws::LibrarySymbolRecord r1{"cublas", "cublasSgemm", "sig1", "compute.matrix", "", {}};
|
||||
ws::LibrarySymbolRecord r2{"cublas", "cublasDnorm", "sig2", "compute.statistics","", {}};
|
||||
C(r1.libraryId == r2.libraryId, "same library");
|
||||
C(r1.operationDomain != r2.operationDomain, "different domains");
|
||||
C(r1.functionName != r2.functionName, "different functions");
|
||||
P();
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::cout << "Step 1968: LibrarySymbolRecord\n";
|
||||
t1(); t2(); t3(); t4(); t5();
|
||||
std::cout << "\n" << p << "/" << (p + f) << " passed\n";
|
||||
return f > 0 ? 1 : 0;
|
||||
}
|
||||
86
editor/tests/step1969_test.cpp
Normal file
86
editor/tests/step1969_test.cpp
Normal file
@@ -0,0 +1,86 @@
|
||||
// Step 1969: LibrarySymbolCatalog
|
||||
//
|
||||
// t1: add and has
|
||||
// t2: get returns all records for (library, domain)
|
||||
// t3: get returns empty for unknown pair
|
||||
// t4: allForLibrary and allForDomain
|
||||
// t5: size counts total records across all keys
|
||||
|
||||
#include "LibrarySymbolCatalog.h"
|
||||
#include <iostream>
|
||||
|
||||
namespace ws = whetstone;
|
||||
static int p = 0, f = 0;
|
||||
#define T(n) { std::cout << " " << #n << "... "; }
|
||||
#define P() { std::cout << "PASS\n"; ++p; }
|
||||
#define F(m) { std::cout << "FAIL: " << m << "\n"; ++f; }
|
||||
#define C(c, m) if (!(c)) { F(m); return; }
|
||||
|
||||
static ws::LibrarySymbolCatalog makeCatalog() {
|
||||
ws::LibrarySymbolCatalog cat;
|
||||
cat.add({"cublas", "cublasSgemm", "sig_sgemm", "compute.matrix", "", {}});
|
||||
cat.add({"cublas", "cublasDgemm", "sig_dgemm", "compute.matrix", "", {}});
|
||||
cat.add({"nlohmann_json", "json::parse", "sig_parse", "serialization.json", "", {}});
|
||||
cat.add({"nlohmann_json", "json::dump", "sig_dump", "serialization.json", "", {}});
|
||||
cat.add({"thrust", "thrust::sort", "sig_sort", "compute.parallel", "", {}});
|
||||
return cat;
|
||||
}
|
||||
|
||||
void t1() {
|
||||
T(add_and_has);
|
||||
ws::LibrarySymbolCatalog cat;
|
||||
C(!cat.has("cublas", "compute.matrix"), "not present before add");
|
||||
cat.add({"cublas", "cublasSgemm", "sig", "compute.matrix", "", {}});
|
||||
C(cat.has("cublas", "compute.matrix"), "present after add");
|
||||
C(!cat.has("cublas", "serialization.json"), "different domain not present");
|
||||
P();
|
||||
}
|
||||
|
||||
void t2() {
|
||||
T(get_returns_all_for_pair);
|
||||
auto cat = makeCatalog();
|
||||
auto cublas_matrix = cat.get("cublas", "compute.matrix");
|
||||
C(cublas_matrix.size() == 2, "2 cublas/compute.matrix records");
|
||||
auto nlohmann_json = cat.get("nlohmann_json", "serialization.json");
|
||||
C(nlohmann_json.size() == 2, "2 nlohmann/serialization.json records");
|
||||
P();
|
||||
}
|
||||
|
||||
void t3() {
|
||||
T(get_returns_empty_for_unknown_pair);
|
||||
auto cat = makeCatalog();
|
||||
auto none = cat.get("unknown_lib", "compute.matrix");
|
||||
C(none.empty(), "empty for unknown library");
|
||||
auto none2 = cat.get("cublas", "serialization.json");
|
||||
C(none2.empty(), "empty for domain cublas has no symbols in");
|
||||
C(!cat.has("cublas", "serialization.json"), "has() false");
|
||||
P();
|
||||
}
|
||||
|
||||
void t4() {
|
||||
T(allForLibrary_and_allForDomain);
|
||||
auto cat = makeCatalog();
|
||||
auto cublas_all = cat.allForLibrary("cublas");
|
||||
C(cublas_all.size() == 2, "2 cublas records total");
|
||||
auto matrix_all = cat.allForDomain("compute.matrix");
|
||||
C(matrix_all.size() == 2, "2 compute.matrix records");
|
||||
auto thrust_all = cat.allForLibrary("thrust");
|
||||
C(thrust_all.size() == 1, "1 thrust record");
|
||||
P();
|
||||
}
|
||||
|
||||
void t5() {
|
||||
T(size_counts_all_records);
|
||||
auto cat = makeCatalog();
|
||||
C(cat.size() == 5, "5 total records, got " + std::to_string(cat.size()));
|
||||
ws::LibrarySymbolCatalog empty;
|
||||
C(empty.size() == 0, "empty catalog size 0");
|
||||
P();
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::cout << "Step 1969: LibrarySymbolCatalog\n";
|
||||
t1(); t2(); t3(); t4(); t5();
|
||||
std::cout << "\n" << p << "/" << (p + f) << " passed\n";
|
||||
return f > 0 ? 1 : 0;
|
||||
}
|
||||
97
editor/tests/step1970_test.cpp
Normal file
97
editor/tests/step1970_test.cpp
Normal file
@@ -0,0 +1,97 @@
|
||||
// Step 1970: LSPSymbolExtractor
|
||||
//
|
||||
// t1: parses valid JSON into correct records
|
||||
// t2: functionName and signature populated from JSON
|
||||
// t3: caveats array parsed correctly
|
||||
// t4: returns error for invalid JSON
|
||||
// t5: empty symbols array produces empty records; missing name skipped
|
||||
|
||||
#include "LSPSymbolExtractor.h"
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
namespace ws = whetstone;
|
||||
static int p = 0, f = 0;
|
||||
#define T(n) { std::cout << " " << #n << "... "; }
|
||||
#define P() { std::cout << "PASS\n"; ++p; }
|
||||
#define F(m) { std::cout << "FAIL: " << m << "\n"; ++f; }
|
||||
#define C(c, m) if (!(c)) { F(m); return; }
|
||||
|
||||
// Use named delimiter to avoid R"(...)"-terminator collision with JSON content.
|
||||
static const std::string CUBLAS_JSON = R"JSON({
|
||||
"symbols": [
|
||||
{"name": "cublasSgemm", "signature": "cublasStatus_t cublasSgemm(...)", "usage": "cublasSgemm(handle, ...)"},
|
||||
{"name": "cublasDgemm", "signature": "cublasStatus_t cublasDgemm(...)"}
|
||||
]
|
||||
})JSON";
|
||||
|
||||
void t1() {
|
||||
T(parses_valid_json_into_records);
|
||||
ws::LSPSymbolExtractor ex;
|
||||
auto res = ex.extract("cublas", "compute.matrix", CUBLAS_JSON);
|
||||
C(res.success, "success");
|
||||
C(res.error.empty(), "no error");
|
||||
C(res.records.size() == 2, "2 records parsed");
|
||||
C(res.records[0].libraryId == "cublas", "libraryId set");
|
||||
C(res.records[0].operationDomain == "compute.matrix", "domain set");
|
||||
P();
|
||||
}
|
||||
|
||||
void t2() {
|
||||
T(functionName_and_signature_populated);
|
||||
ws::LSPSymbolExtractor ex;
|
||||
auto res = ex.extract("cublas", "compute.matrix", CUBLAS_JSON);
|
||||
C(res.records[0].functionName == "cublasSgemm", "first functionName");
|
||||
C(res.records[0].signature.find("cublasSgemm") != std::string::npos, "first signature");
|
||||
C(res.records[0].usagePattern.find("handle") != std::string::npos, "usagePattern");
|
||||
C(res.records[1].functionName == "cublasDgemm", "second functionName");
|
||||
P();
|
||||
}
|
||||
|
||||
void t3() {
|
||||
T(caveats_array_parsed);
|
||||
const std::string json = R"JSON({"symbols": [
|
||||
{"name": "myFunc", "signature": "void myFunc()",
|
||||
"caveats": ["must-call-init-first", "not-thread-safe"]}
|
||||
]})JSON";
|
||||
ws::LSPSymbolExtractor ex;
|
||||
auto res = ex.extract("mylib", "mydom", json);
|
||||
C(res.success, "success");
|
||||
C(res.records.size() == 1, "1 record");
|
||||
C(res.records[0].caveats.size() == 2, "2 caveats");
|
||||
C(res.records[0].caveats[0] == "must-call-init-first", "first caveat");
|
||||
P();
|
||||
}
|
||||
|
||||
void t4() {
|
||||
T(returns_error_for_invalid_json);
|
||||
ws::LSPSymbolExtractor ex;
|
||||
auto res = ex.extract("lib", "dom", "{ not valid json !!!");
|
||||
C(!res.success, "failure for invalid json");
|
||||
C(!res.error.empty(), "error message set");
|
||||
C(res.records.empty(), "no records on error");
|
||||
// Missing symbols key
|
||||
auto res2 = ex.extract("lib", "dom", R"JSON({"other": []})JSON");
|
||||
C(!res2.success, "failure for missing symbols key");
|
||||
P();
|
||||
}
|
||||
|
||||
void t5() {
|
||||
T(empty_symbols_and_missing_name_skipped);
|
||||
ws::LSPSymbolExtractor ex;
|
||||
auto res = ex.extract("lib", "dom", R"JSON({"symbols": []})JSON");
|
||||
C(res.success, "success for empty array");
|
||||
C(res.records.empty(), "no records for empty array");
|
||||
// Symbol with empty name skipped
|
||||
auto res2 = ex.extract("lib", "dom", R"JSON({"symbols": [{"name": "", "signature": "sig"}]})JSON");
|
||||
C(res2.success, "success");
|
||||
C(res2.records.empty(), "empty-name record skipped");
|
||||
P();
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::cout << "Step 1970: LSPSymbolExtractor\n";
|
||||
t1(); t2(); t3(); t4(); t5();
|
||||
std::cout << "\n" << p << "/" << (p + f) << " passed\n";
|
||||
return f > 0 ? 1 : 0;
|
||||
}
|
||||
98
editor/tests/step1971_test.cpp
Normal file
98
editor/tests/step1971_test.cpp
Normal file
@@ -0,0 +1,98 @@
|
||||
// Step 1971: LibrarySymbolAdvisor
|
||||
//
|
||||
// t1: advise returns symbols sorted by functionName
|
||||
// t2: advise returns empty for unknown pair
|
||||
// t3: topSymbol returns first alphabetically
|
||||
// t4: multiple symbols correctly ordered
|
||||
// t5: advisee scoped to exact (library, domain) pair
|
||||
|
||||
#include "LibrarySymbolAdvisor.h"
|
||||
#include "LibrarySymbolCatalog.h"
|
||||
#include <iostream>
|
||||
|
||||
namespace ws = whetstone;
|
||||
static int p = 0, f = 0;
|
||||
#define T(n) { std::cout << " " << #n << "... "; }
|
||||
#define P() { std::cout << "PASS\n"; ++p; }
|
||||
#define F(m) { std::cout << "FAIL: " << m << "\n"; ++f; }
|
||||
#define C(c, m) if (!(c)) { F(m); return; }
|
||||
|
||||
static ws::LibrarySymbolCatalog makeCatalog() {
|
||||
ws::LibrarySymbolCatalog cat;
|
||||
cat.add({"cublas", "cublasSgemm", "sig_s", "compute.matrix", "", {}});
|
||||
cat.add({"cublas", "cublasDgemm", "sig_d", "compute.matrix", "", {}});
|
||||
cat.add({"nlohmann_json", "json::parse", "sig_p", "serialization.json", "", {}});
|
||||
cat.add({"nlohmann_json", "json::dump", "sig_du","serialization.json", "", {}});
|
||||
return cat;
|
||||
}
|
||||
|
||||
void t1() {
|
||||
T(advise_sorted_by_functionName);
|
||||
auto cat = makeCatalog();
|
||||
ws::LibrarySymbolAdvisor adv;
|
||||
auto syms = adv.advise("cublas", "compute.matrix", cat);
|
||||
C(syms.size() == 2, "2 cublas symbols");
|
||||
// cublasDgemm < cublasSgemm alphabetically
|
||||
C(syms[0].functionName == "cublasDgemm", "Dgemm first (D < S)");
|
||||
C(syms[1].functionName == "cublasSgemm", "Sgemm second");
|
||||
P();
|
||||
}
|
||||
|
||||
void t2() {
|
||||
T(advise_empty_for_unknown_pair);
|
||||
auto cat = makeCatalog();
|
||||
ws::LibrarySymbolAdvisor adv;
|
||||
auto syms = adv.advise("unknown_lib", "compute.matrix", cat);
|
||||
C(syms.empty(), "empty for unknown library");
|
||||
auto syms2 = adv.advise("cublas", "serialization.json", cat);
|
||||
C(syms2.empty(), "empty for domain cublas has no symbols in");
|
||||
P();
|
||||
}
|
||||
|
||||
void t3() {
|
||||
T(topSymbol_returns_first_alphabetically);
|
||||
auto cat = makeCatalog();
|
||||
ws::LibrarySymbolAdvisor adv;
|
||||
auto top = adv.topSymbol("nlohmann_json", "serialization.json", cat);
|
||||
C(top.has_value(), "has top symbol");
|
||||
// json::dump < json::parse alphabetically
|
||||
C(top->functionName == "json::dump", "json::dump is top (d < p)");
|
||||
auto none = adv.topSymbol("missing", "missing", cat);
|
||||
C(!none.has_value(), "nullopt for missing pair");
|
||||
P();
|
||||
}
|
||||
|
||||
void t4() {
|
||||
T(nlohmann_symbols_correct_order);
|
||||
auto cat = makeCatalog();
|
||||
ws::LibrarySymbolAdvisor adv;
|
||||
auto syms = adv.advise("nlohmann_json", "serialization.json", cat);
|
||||
C(syms.size() == 2, "2 nlohmann symbols");
|
||||
C(syms[0].functionName == "json::dump", "dump first (d < p)");
|
||||
C(syms[1].functionName == "json::parse", "parse second");
|
||||
// Both symbols in serialization.json (parse IS present)
|
||||
bool hasParse = false;
|
||||
for (const auto& s : syms) if (s.functionName == "json::parse") hasParse = true;
|
||||
C(hasParse, "json::parse present in results");
|
||||
P();
|
||||
}
|
||||
|
||||
void t5() {
|
||||
T(advise_scoped_to_exact_pair);
|
||||
auto cat = makeCatalog();
|
||||
ws::LibrarySymbolAdvisor adv;
|
||||
// cublas symbols only in compute.matrix, not compute.parallel
|
||||
auto par = adv.advise("cublas", "compute.parallel", cat);
|
||||
C(par.empty(), "no cublas/compute.parallel symbols");
|
||||
// nlohmann symbols only in serialization.json, not serialization.binary
|
||||
auto bin = adv.advise("nlohmann_json", "serialization.binary", cat);
|
||||
C(bin.empty(), "no nlohmann/serialization.binary symbols");
|
||||
P();
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::cout << "Step 1971: LibrarySymbolAdvisor\n";
|
||||
t1(); t2(); t3(); t4(); t5();
|
||||
std::cout << "\n" << p << "/" << (p + f) << " passed\n";
|
||||
return f > 0 ? 1 : 0;
|
||||
}
|
||||
82
editor/tests/step1972_test.cpp
Normal file
82
editor/tests/step1972_test.cpp
Normal file
@@ -0,0 +1,82 @@
|
||||
// Step 1972: Sprint 288 Integration
|
||||
//
|
||||
// t1: buildSeedCatalog has cublas and nlohmann entries
|
||||
// t2: cublasSgemm found for (cublas, compute.matrix)
|
||||
// t3: json::parse found for (nlohmann_json, serialization.json)
|
||||
// t4: verifyCUDALookup and verifyNlohmannLookup both pass
|
||||
// t5: Sprint288IntegrationSummary metadata correct
|
||||
|
||||
#include "Sprint288IntegrationSummary.h"
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
namespace ws = whetstone;
|
||||
static int p = 0, f = 0;
|
||||
#define T(n) { std::cout << " " << #n << "... "; }
|
||||
#define P() { std::cout << "PASS\n"; ++p; }
|
||||
#define F(m) { std::cout << "FAIL: " << m << "\n"; ++f; }
|
||||
#define C(c, m) if (!(c)) { F(m); return; }
|
||||
|
||||
void t1() {
|
||||
T(seed_catalog_has_cublas_and_nlohmann);
|
||||
auto cat = ws::Sprint288IntegrationSummary::buildSeedCatalog();
|
||||
C(cat.has("cublas", "compute.matrix"), "cublas/compute.matrix");
|
||||
C(cat.has("nlohmann_json", "serialization.json"), "nlohmann_json/serialization.json");
|
||||
C(cat.size() == 4, "4 total records, got " + std::to_string(cat.size()));
|
||||
P();
|
||||
}
|
||||
|
||||
void t2() {
|
||||
T(cublasSgemm_found_for_cublas_compute_matrix);
|
||||
auto cat = ws::Sprint288IntegrationSummary::buildSeedCatalog();
|
||||
ws::LibrarySymbolAdvisor adv;
|
||||
auto syms = adv.advise("cublas", "compute.matrix", cat);
|
||||
C(syms.size() == 2, "2 cublas/compute.matrix symbols");
|
||||
bool hasSgemm = false;
|
||||
for (const auto& s : syms) if (s.functionName == "cublasSgemm") hasSgemm = true;
|
||||
C(hasSgemm, "cublasSgemm present");
|
||||
// Verify signature mentions handle
|
||||
for (const auto& s : syms) {
|
||||
if (s.functionName == "cublasSgemm") {
|
||||
C(s.signature.find("cublasHandle_t") != std::string::npos, "signature has handle");
|
||||
C(!s.caveats.empty(), "cublasSgemm has caveats");
|
||||
}
|
||||
}
|
||||
P();
|
||||
}
|
||||
|
||||
void t3() {
|
||||
T(json_parse_found_for_nlohmann_serialization_json);
|
||||
auto cat = ws::Sprint288IntegrationSummary::buildSeedCatalog();
|
||||
ws::LibrarySymbolAdvisor adv;
|
||||
auto syms = adv.advise("nlohmann_json", "serialization.json", cat);
|
||||
C(syms.size() == 2, "2 nlohmann/serialization.json symbols");
|
||||
bool hasParse = false;
|
||||
for (const auto& s : syms) if (s.functionName == "json::parse") hasParse = true;
|
||||
C(hasParse, "json::parse present");
|
||||
P();
|
||||
}
|
||||
|
||||
void t4() {
|
||||
T(verify_CUDA_and_nlohmann_lookups_pass);
|
||||
auto cat = ws::Sprint288IntegrationSummary::buildSeedCatalog();
|
||||
C(ws::Sprint288IntegrationSummary::verifyCUDALookup(cat), "verifyCUDALookup");
|
||||
C(ws::Sprint288IntegrationSummary::verifyNlohmannLookup(cat), "verifyNlohmannLookup");
|
||||
P();
|
||||
}
|
||||
|
||||
void t5() {
|
||||
T(sprint288_metadata);
|
||||
ws::Sprint288IntegrationSummary s;
|
||||
C(s.stepsCompleted == 5, "5 steps");
|
||||
C(s.success, "success");
|
||||
C(s.sprintName() == "Sprint 288: LibrarySymbolAdvisor", "name");
|
||||
P();
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::cout << "Step 1972: Sprint 288 Integration\n";
|
||||
t1(); t2(); t3(); t4(); t5();
|
||||
std::cout << "\n" << p << "/" << (p + f) << " passed\n";
|
||||
return f > 0 ? 1 : 0;
|
||||
}
|
||||
Reference in New Issue
Block a user