Step 501: add cross-language port scenario and tests
This commit is contained in:
@@ -3388,4 +3388,13 @@ target_link_libraries(step500_test PRIVATE
|
||||
tree_sitter_javascript tree_sitter_typescript
|
||||
tree_sitter_java tree_sitter_rust tree_sitter_go)
|
||||
|
||||
add_executable(step501_test tests/step501_test.cpp)
|
||||
target_include_directories(step501_test PRIVATE src)
|
||||
target_link_libraries(step501_test PRIVATE
|
||||
nlohmann_json::nlohmann_json
|
||||
unofficial::tree-sitter::tree-sitter
|
||||
tree_sitter_python tree_sitter_cpp tree_sitter_elisp
|
||||
tree_sitter_javascript tree_sitter_typescript
|
||||
tree_sitter_java tree_sitter_rust tree_sitter_go)
|
||||
|
||||
# Step 12: Dear ImGui shell scaffolding created (main.cpp exists but not built due to dependencies)
|
||||
|
||||
127
editor/src/CrossLanguagePortScenarioRunner.h
Normal file
127
editor/src/CrossLanguagePortScenarioRunner.h
Normal file
@@ -0,0 +1,127 @@
|
||||
#pragma once
|
||||
|
||||
// Step 501: Scenario - Cross-Language Port
|
||||
// End-to-end scenario for porting a Python ML pipeline to Rust.
|
||||
|
||||
#include "BehavioralEquivalence.h"
|
||||
#include "ConcurrencyTranslator.h"
|
||||
#include "IntentTranslator.h"
|
||||
#include "MemoryModelTranslator.h"
|
||||
#include "TranspilationConfidence.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct PortedFunctionResult {
|
||||
std::string name;
|
||||
std::string source;
|
||||
std::string intent;
|
||||
bool hotLoop = false;
|
||||
std::vector<std::string> annotations;
|
||||
TranslationChoice translation;
|
||||
ConfidenceScore confidence;
|
||||
BehavioralCheckResult equivalence;
|
||||
};
|
||||
|
||||
struct CrossLanguagePortScenarioResult {
|
||||
std::string sourceLanguage;
|
||||
std::string targetLanguage;
|
||||
std::string pipelineSource;
|
||||
std::vector<PortedFunctionResult> functions;
|
||||
IntentTranslationResult intentTranslations;
|
||||
MemoryAnalysisResult memoryAnalysis;
|
||||
ConcurrencyAnalysisResult concurrencyAnalysis;
|
||||
std::vector<std::string> reviewRequiredFunctions;
|
||||
std::vector<std::string> notes;
|
||||
|
||||
float averageConfidence() const {
|
||||
if (functions.empty()) return 0.0f;
|
||||
float sum = 0.0f;
|
||||
for (const auto& f : functions) sum += f.confidence.score;
|
||||
return sum / static_cast<float>(functions.size());
|
||||
}
|
||||
};
|
||||
|
||||
class CrossLanguagePortScenarioRunner {
|
||||
public:
|
||||
static CrossLanguagePortScenarioResult run() {
|
||||
CrossLanguagePortScenarioResult out;
|
||||
out.sourceLanguage = "python";
|
||||
out.targetLanguage = "rust";
|
||||
|
||||
const auto funcs = sampleFunctions();
|
||||
out.pipelineSource = composePipelineSource(funcs);
|
||||
out.intentTranslations = IntentTranslator::translate(funcs, out.sourceLanguage, out.targetLanguage);
|
||||
out.memoryAnalysis = MemoryModelTranslator::analyze(
|
||||
out.pipelineSource, out.sourceLanguage, out.targetLanguage);
|
||||
out.concurrencyAnalysis = ConcurrencyTranslator::analyze(
|
||||
out.pipelineSource, out.sourceLanguage, out.targetLanguage);
|
||||
|
||||
for (size_t i = 0; i < funcs.size(); ++i) {
|
||||
PortedFunctionResult pf;
|
||||
pf.name = funcs[i].name;
|
||||
pf.source = funcs[i].source;
|
||||
pf.intent = funcs[i].intent;
|
||||
pf.hotLoop = isHotLoop(funcs[i].source);
|
||||
if (pf.hotLoop) pf.annotations.push_back("@HotLoop(true)");
|
||||
if (!funcs[i].intent.empty()) pf.annotations.push_back("@Intent(" + funcs[i].intent + ")");
|
||||
|
||||
pf.translation = out.intentTranslations.getTranslation(funcs[i].name);
|
||||
pf.confidence = TranspilationScorer::score(
|
||||
funcs[i].name, funcs[i].source, pf.translation.targetCode,
|
||||
out.sourceLanguage, out.targetLanguage, !funcs[i].intent.empty());
|
||||
pf.equivalence = BehavioralChecker::check(
|
||||
funcs[i].name, funcs[i].source, pf.translation.targetCode,
|
||||
out.sourceLanguage, out.targetLanguage, {});
|
||||
|
||||
if (pf.confidence.reviewRequired) out.reviewRequiredFunctions.push_back(pf.name);
|
||||
out.functions.push_back(std::move(pf));
|
||||
}
|
||||
|
||||
out.notes.push_back("Cross-language Python->Rust scenario executed");
|
||||
out.notes.push_back("Includes hot-loop tagging, transpilation confidence, and review surfacing");
|
||||
return out;
|
||||
}
|
||||
|
||||
private:
|
||||
static std::vector<FunctionIntent> sampleFunctions() {
|
||||
return {
|
||||
{"train_epoch", "reduce gradient sum",
|
||||
"def train_epoch(samples):\n"
|
||||
" total = 0\n"
|
||||
" for i in range(len(samples)):\n"
|
||||
" total += samples[i]\n"
|
||||
" return total\n"},
|
||||
{"normalize_scores", "map transform normalize",
|
||||
"def normalize_scores(scores):\n"
|
||||
" return [s / 255.0 for s in scores]\n"},
|
||||
{"async_fetch_batch", "",
|
||||
"import asyncio\n"
|
||||
"async def async_fetch_batch(client, urls):\n"
|
||||
" out = []\n"
|
||||
" for u in urls:\n"
|
||||
" out.append(await client.get(u))\n"
|
||||
" return out\n"}
|
||||
};
|
||||
}
|
||||
|
||||
static std::string composePipelineSource(const std::vector<FunctionIntent>& funcs) {
|
||||
std::string out;
|
||||
for (const auto& f : funcs) {
|
||||
if (!out.empty()) out += "\n";
|
||||
out += f.source;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static bool isHotLoop(const std::string& source) {
|
||||
std::string s = source;
|
||||
std::transform(s.begin(), s.end(), s.begin(),
|
||||
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
|
||||
return s.find("for ") != std::string::npos ||
|
||||
s.find("while ") != std::string::npos ||
|
||||
s.find("range(") != std::string::npos;
|
||||
}
|
||||
};
|
||||
165
editor/tests/step501_test.cpp
Normal file
165
editor/tests/step501_test.cpp
Normal file
@@ -0,0 +1,165 @@
|
||||
// Step 501: Scenario - Cross-Language Port Tests (12 tests)
|
||||
|
||||
#include "CrossLanguagePortScenarioRunner.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
static int passed = 0, failed = 0;
|
||||
#define TEST(name) { std::cout << " " << #name << "... "; }
|
||||
#define PASS() { std::cout << "PASS\n"; ++passed; }
|
||||
#define FAIL(msg) { std::cout << "FAIL: " << msg << "\n"; ++failed; }
|
||||
#define CHECK(cond, msg) if (!(cond)) { FAIL(msg); return; } else {}
|
||||
|
||||
static CrossLanguagePortScenarioResult runNow() {
|
||||
return CrossLanguagePortScenarioRunner::run();
|
||||
}
|
||||
|
||||
void test_source_and_target_languages_are_python_to_rust() {
|
||||
TEST(source_and_target_languages_are_python_to_rust);
|
||||
auto r = runNow();
|
||||
CHECK(r.sourceLanguage == "python", "expected python source");
|
||||
CHECK(r.targetLanguage == "rust", "expected rust target");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_pipeline_source_contains_asyncio_and_loop_constructs() {
|
||||
TEST(pipeline_source_contains_asyncio_and_loop_constructs);
|
||||
auto r = runNow();
|
||||
CHECK(r.pipelineSource.find("async") != std::string::npos, "expected async pipeline source");
|
||||
CHECK(r.pipelineSource.find("for ") != std::string::npos, "expected loop in pipeline source");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_function_extraction_and_port_result_population() {
|
||||
TEST(function_extraction_and_port_result_population);
|
||||
auto r = runNow();
|
||||
CHECK(r.functions.size() == 3, "expected three functions in scenario");
|
||||
CHECK(r.intentTranslations.translations.size() == 3, "expected three translations");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_hot_loop_identification_tags_training_loop_function() {
|
||||
TEST(hot_loop_identification_tags_training_loop_function);
|
||||
auto r = runNow();
|
||||
bool foundHot = false;
|
||||
for (const auto& f : r.functions) {
|
||||
if (f.name == "train_epoch") {
|
||||
foundHot = true;
|
||||
CHECK(f.hotLoop, "train_epoch should be tagged hot loop");
|
||||
CHECK(!f.annotations.empty(), "expected hot loop annotation");
|
||||
}
|
||||
}
|
||||
CHECK(foundHot, "missing train_epoch function");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_semantic_transpilation_produces_idiomatic_rust_constructs() {
|
||||
TEST(semantic_transpilation_produces_idiomatic_rust_constructs);
|
||||
auto r = runNow();
|
||||
CHECK(r.intentTranslations.countIdiomatic() >= 2, "expected idiomatic rust translations");
|
||||
CHECK(r.intentTranslations.getTranslation("train_epoch").targetCode.find("fold") != std::string::npos,
|
||||
"train_epoch should map to reduce/fold pattern");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_memory_model_translation_reflects_gc_to_borrowchecked_shift() {
|
||||
TEST(memory_model_translation_reflects_gc_to_borrowchecked_shift);
|
||||
auto r = runNow();
|
||||
CHECK(r.memoryAnalysis.sourceModel == OwnershipModel::GC, "python source should be GC model");
|
||||
CHECK(r.memoryAnalysis.targetModel == OwnershipModel::BorrowChecked,
|
||||
"rust target should be borrow-checked model");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_concurrency_translation_maps_asyncio_to_tokio_pattern() {
|
||||
TEST(concurrency_translation_maps_asyncio_to_tokio_pattern);
|
||||
auto r = runNow();
|
||||
CHECK(r.concurrencyAnalysis.hasPattern("async/await"), "expected async/await translation");
|
||||
bool hasTokio = false;
|
||||
for (const auto& t : r.concurrencyAnalysis.translations) {
|
||||
if (t.pattern == "async/await" &&
|
||||
(t.annotation.find("tokio") != std::string::npos ||
|
||||
t.note.find("tokio") != std::string::npos)) {
|
||||
hasTokio = true;
|
||||
}
|
||||
}
|
||||
CHECK(hasTokio, "expected tokio runtime annotation or note");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_confidence_scoring_is_generated_for_every_translation() {
|
||||
TEST(confidence_scoring_is_generated_for_every_translation);
|
||||
auto r = runNow();
|
||||
for (const auto& f : r.functions) {
|
||||
CHECK(f.confidence.score > 0.0f, "missing confidence score");
|
||||
CHECK(!f.confidence.annotation.empty(), "missing confidence annotation");
|
||||
}
|
||||
CHECK(r.averageConfidence() > 0.0f, "expected positive average confidence");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_low_confidence_translation_is_flagged_for_human_review() {
|
||||
TEST(low_confidence_translation_is_flagged_for_human_review);
|
||||
auto r = runNow();
|
||||
bool flagged = false;
|
||||
for (const auto& f : r.functions) {
|
||||
if (f.name == "async_fetch_batch") {
|
||||
flagged = true;
|
||||
CHECK(f.confidence.reviewRequired, "async function should require review");
|
||||
}
|
||||
}
|
||||
CHECK(flagged, "missing async_fetch_batch function");
|
||||
CHECK(!r.reviewRequiredFunctions.empty(), "expected review-required function list");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_equivalence_assertions_are_generated_per_function() {
|
||||
TEST(equivalence_assertions_are_generated_per_function);
|
||||
auto r = runNow();
|
||||
for (const auto& f : r.functions) {
|
||||
CHECK(f.equivalence.assertionCount() > 0, "missing equivalence assertions");
|
||||
CHECK(!f.equivalence.annotation.empty(), "missing equivalence annotation");
|
||||
}
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_review_queue_matches_low_confidence_functions() {
|
||||
TEST(review_queue_matches_low_confidence_functions);
|
||||
auto r = runNow();
|
||||
int lowConfidence = 0;
|
||||
for (const auto& f : r.functions) {
|
||||
if (f.confidence.reviewRequired) ++lowConfidence;
|
||||
}
|
||||
CHECK(static_cast<int>(r.reviewRequiredFunctions.size()) == lowConfidence,
|
||||
"review queue size mismatch");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_scenario_notes_include_cross_language_execution_summary() {
|
||||
TEST(scenario_notes_include_cross_language_execution_summary);
|
||||
auto r = runNow();
|
||||
CHECK(r.notes.size() >= 2, "expected scenario notes");
|
||||
CHECK(r.notes[0].find("Python->Rust") != std::string::npos, "missing scenario summary note");
|
||||
PASS();
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::cout << "Step 501: Scenario - Cross-Language Port Tests\n";
|
||||
|
||||
test_source_and_target_languages_are_python_to_rust(); // 1
|
||||
test_pipeline_source_contains_asyncio_and_loop_constructs(); // 2
|
||||
test_function_extraction_and_port_result_population(); // 3
|
||||
test_hot_loop_identification_tags_training_loop_function(); // 4
|
||||
test_semantic_transpilation_produces_idiomatic_rust_constructs(); // 5
|
||||
test_memory_model_translation_reflects_gc_to_borrowchecked_shift(); // 6
|
||||
test_concurrency_translation_maps_asyncio_to_tokio_pattern(); // 7
|
||||
test_confidence_scoring_is_generated_for_every_translation(); // 8
|
||||
test_low_confidence_translation_is_flagged_for_human_review(); // 9
|
||||
test_equivalence_assertions_are_generated_per_function(); // 10
|
||||
test_review_queue_matches_low_confidence_functions(); // 11
|
||||
test_scenario_notes_include_cross_language_execution_summary(); // 12
|
||||
|
||||
std::cout << "\nResults: " << passed << "/" << (passed + failed)
|
||||
<< " passed\n";
|
||||
return failed == 0 ? 0 : 1;
|
||||
}
|
||||
34
progress.md
34
progress.md
@@ -7678,3 +7678,37 @@ with Rust as the migration target.
|
||||
- `editor/src/LegacyModernizationScenarioRunner.h` within header-size limit (`125` <= `600`)
|
||||
- `editor/tests/step500_test.cpp` within test-file size guidance (`147` lines)
|
||||
- Header-only architecture and naming conventions remain aligned with `ARCHITECTURE.md`
|
||||
|
||||
### Step 501: Scenario - Cross-Language Port
|
||||
**Status:** PASS (12/12 tests)
|
||||
|
||||
Implements the third Phase 25b end-to-end scenario for porting a Python ML-style
|
||||
pipeline to Rust with confidence and review gating.
|
||||
|
||||
**Files added:**
|
||||
- `editor/src/CrossLanguagePortScenarioRunner.h` - scenario orchestration across:
|
||||
- Python function intake and hot-loop tagging
|
||||
- intent-driven semantic transpilation (`python` -> `rust`)
|
||||
- memory model analysis (GC -> borrow-checked target model)
|
||||
- concurrency translation (`asyncio`/async-await -> Rust tokio-oriented runtime notes)
|
||||
- per-function confidence scoring and low-confidence review surfacing
|
||||
- behavioral equivalence assertion generation
|
||||
- `editor/tests/step501_test.cpp` - 12 scenario tests covering:
|
||||
- language/source expectations and function extraction
|
||||
- hot-loop identification
|
||||
- idiomatic transpilation patterns
|
||||
- memory/concurrency translation expectations
|
||||
- confidence/review semantics
|
||||
- equivalence assertion generation and scenario notes
|
||||
- `editor/CMakeLists.txt` - `step501_test` target
|
||||
|
||||
**Verification run:**
|
||||
- `cmake -S editor -B editor/build-native` - PASS
|
||||
- `cmake --build editor/build-native --target step501_test step500_test` - PASS
|
||||
- `./editor/build-native/step501_test` - PASS (12/12)
|
||||
- `./editor/build-native/step500_test` - PASS (12/12) regression coverage
|
||||
|
||||
**Architecture gate check:**
|
||||
- `editor/src/CrossLanguagePortScenarioRunner.h` within header-size limit (`127` <= `600`)
|
||||
- `editor/tests/step501_test.cpp` within test-file size guidance (`165` lines)
|
||||
- Header-only architecture and naming conventions remain aligned with `ARCHITECTURE.md`
|
||||
|
||||
Reference in New Issue
Block a user