From dae03ac8274fe4fda8a55327e57b12e67b57a75d Mon Sep 17 00:00:00 2001 From: Bill Date: Mon, 16 Feb 2026 19:42:21 -0700 Subject: [PATCH] Complete step452 concurrency model translation with tests --- editor/CMakeLists.txt | 9 ++ editor/src/ConcurrencyTranslator.h | 249 +++++++++++++++++++++++++++++ editor/tests/step452_test.cpp | 146 +++++++++++++++++ progress.md | 28 ++++ 4 files changed, 432 insertions(+) create mode 100644 editor/src/ConcurrencyTranslator.h create mode 100644 editor/tests/step452_test.cpp diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index 6b3a807..352fa0d 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -2947,4 +2947,13 @@ target_link_libraries(step451_test PRIVATE tree_sitter_javascript tree_sitter_typescript tree_sitter_java tree_sitter_rust tree_sitter_go) +add_executable(step452_test tests/step452_test.cpp) +target_include_directories(step452_test PRIVATE src) +target_link_libraries(step452_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) diff --git a/editor/src/ConcurrencyTranslator.h b/editor/src/ConcurrencyTranslator.h new file mode 100644 index 0000000..30eaea0 --- /dev/null +++ b/editor/src/ConcurrencyTranslator.h @@ -0,0 +1,249 @@ +#pragma once + +// Step 452: Concurrency Model Translation — maps concurrency patterns across +// language runtimes with @Exec, @Sync, @ThreadModel, @Blocking annotations. + +#include +#include + +enum class ConcurrencyModel { Threads, AsyncAwait, Channels, Coroutines, EventLoop, None }; +enum class ConcurrencySafety { Safe, NeedsReview, Unsafe }; + +struct ConcurrencyTranslation { + std::string pattern; // e.g. "async/await", "thread spawn" + ConcurrencyModel sourceModel; + ConcurrencyModel targetModel; + std::string sourceLanguage; + std::string targetLanguage; + std::string sourceCode; + std::string targetCode; + ConcurrencySafety safety = ConcurrencySafety::Safe; + std::string annotation; // e.g. @Blocking(false) + std::string note; +}; + +struct ConcurrencyAnalysisResult { + std::string sourceLanguage; + std::string targetLanguage; + ConcurrencyModel sourceModel = ConcurrencyModel::None; + ConcurrencyModel targetModel = ConcurrencyModel::None; + std::vector translations; + + bool hasPattern(const std::string& p) const { + for (const auto& t : translations) + if (t.pattern == p) return true; + return false; + } + + int countNeedsReview() const { + int n = 0; + for (const auto& t : translations) + if (t.safety == ConcurrencySafety::NeedsReview) ++n; + return n; + } +}; + +class ConcurrencyTranslator { +public: + static ConcurrencyAnalysisResult analyze(const std::string& source, + const std::string& srcLang, + const std::string& tgtLang) { + ConcurrencyAnalysisResult result; + result.sourceLanguage = srcLang; + result.targetLanguage = tgtLang; + result.sourceModel = detectModel(source, srcLang); + result.targetModel = defaultModel(tgtLang); + + if (hasAsync(source)) { + result.translations.push_back(translateAsync(srcLang, tgtLang)); + } + if (hasThreads(source)) { + result.translations.push_back(translateThreads(srcLang, tgtLang)); + } + if (hasChannels(source)) { + result.translations.push_back(translateChannels(srcLang, tgtLang)); + } + if (hasMutex(source)) { + result.translations.push_back(translateMutex(srcLang, tgtLang)); + } + + return result; + } + +private: + static bool hasAsync(const std::string& src) { + return src.find("async ") != std::string::npos || + src.find("await ") != std::string::npos || + src.find("asyncio") != std::string::npos || + src.find("Promise") != std::string::npos; + } + + static bool hasThreads(const std::string& src) { + return src.find("Thread") != std::string::npos || + src.find("thread") != std::string::npos || + src.find("pthread") != std::string::npos || + src.find("goroutine") != std::string::npos || + src.find("go func") != std::string::npos; + } + + static bool hasChannels(const std::string& src) { + return src.find("chan ") != std::string::npos || + src.find("channel") != std::string::npos || + src.find("mpsc::") != std::string::npos; + } + + static bool hasMutex(const std::string& src) { + return src.find("mutex") != std::string::npos || + src.find("Mutex") != std::string::npos || + src.find("Lock") != std::string::npos || + src.find("synchronized") != std::string::npos; + } + + static ConcurrencyModel detectModel(const std::string& src, const std::string& lang) { + if (hasAsync(src)) return ConcurrencyModel::AsyncAwait; + if (hasChannels(src)) return ConcurrencyModel::Channels; + if (hasThreads(src)) return ConcurrencyModel::Threads; + if (lang == "go") return ConcurrencyModel::Channels; + if (lang == "javascript" || lang == "js") return ConcurrencyModel::EventLoop; + return ConcurrencyModel::None; + } + + static ConcurrencyModel defaultModel(const std::string& lang) { + if (lang == "go") return ConcurrencyModel::Channels; + if (lang == "javascript" || lang == "js") return ConcurrencyModel::EventLoop; + if (lang == "rust") return ConcurrencyModel::AsyncAwait; + if (lang == "python") return ConcurrencyModel::AsyncAwait; + if (lang == "java") return ConcurrencyModel::Threads; + if (lang == "c" || lang == "cpp") return ConcurrencyModel::Threads; + return ConcurrencyModel::Threads; + } + + static ConcurrencyTranslation translateAsync(const std::string& src, const std::string& tgt) { + ConcurrencyTranslation t; + t.pattern = "async/await"; + t.sourceModel = ConcurrencyModel::AsyncAwait; + t.sourceLanguage = src; + t.targetLanguage = tgt; + + if (tgt == "rust") { + t.targetModel = ConcurrencyModel::AsyncAwait; + t.targetCode = "async fn f() -> Result { ... .await }"; + t.safety = ConcurrencySafety::NeedsReview; + t.annotation = "@Exec(async), @ThreadModel(tokio)"; + t.note = "Rust async requires explicit runtime (tokio/async-std)"; + } else if (tgt == "go") { + t.targetModel = ConcurrencyModel::Channels; + t.targetCode = "go func() { ch <- result }()"; + t.safety = ConcurrencySafety::NeedsReview; + t.annotation = "@Exec(goroutine), @Blocking(false)"; + t.note = "Go uses goroutines+channels instead of async/await"; + } else if (tgt == "python") { + t.targetModel = ConcurrencyModel::AsyncAwait; + t.targetCode = "async def f(): result = await coro()"; + t.safety = ConcurrencySafety::Safe; + t.annotation = "@Exec(async), @ThreadModel(asyncio)"; + } else if (tgt == "java") { + t.targetModel = ConcurrencyModel::Threads; + t.targetCode = "CompletableFuture.supplyAsync(() -> { ... })"; + t.safety = ConcurrencySafety::NeedsReview; + t.annotation = "@Exec(future), @ThreadModel(executor)"; + t.note = "Java uses CompletableFuture, not native async/await"; + } else if (tgt == "c") { + t.targetModel = ConcurrencyModel::Threads; + t.targetCode = "pthread_create(&thread, NULL, callback, arg)"; + t.safety = ConcurrencySafety::Unsafe; + t.annotation = "@Exec(thread), @Blocking(true), @Risk(high)"; + t.note = "C has no async/await — manual thread management"; + } else { + t.targetModel = ConcurrencyModel::AsyncAwait; + t.targetCode = "async { ... }"; + t.safety = ConcurrencySafety::NeedsReview; + } + return t; + } + + static ConcurrencyTranslation translateThreads(const std::string& src, const std::string& tgt) { + ConcurrencyTranslation t; + t.pattern = "thread_spawn"; + t.sourceModel = ConcurrencyModel::Threads; + t.sourceLanguage = src; + t.targetLanguage = tgt; + + if (tgt == "rust") { + t.targetCode = "std::thread::spawn(move || { ... })"; + t.safety = ConcurrencySafety::Safe; + t.annotation = "@Sync(Send+Sync), @ThreadModel(native)"; + } else if (tgt == "go") { + t.targetCode = "go func() { ... }()"; + t.safety = ConcurrencySafety::Safe; + t.annotation = "@Exec(goroutine)"; + } else if (tgt == "python") { + t.targetCode = "threading.Thread(target=func).start()"; + t.safety = ConcurrencySafety::NeedsReview; + t.annotation = "@ThreadModel(GIL), @Blocking(true)"; + t.note = "Python GIL limits true parallelism"; + } else if (tgt == "java") { + t.targetCode = "new Thread(() -> { ... }).start()"; + t.safety = ConcurrencySafety::Safe; + } else { + t.targetCode = "spawn_thread(func)"; + t.safety = ConcurrencySafety::NeedsReview; + } + t.targetModel = defaultModel(tgt); + return t; + } + + static ConcurrencyTranslation translateChannels(const std::string& src, const std::string& tgt) { + ConcurrencyTranslation t; + t.pattern = "channels"; + t.sourceModel = ConcurrencyModel::Channels; + t.sourceLanguage = src; + t.targetLanguage = tgt; + + if (tgt == "rust") { + t.targetCode = "let (tx, rx) = mpsc::channel(); tx.send(value);"; + t.safety = ConcurrencySafety::Safe; + t.annotation = "@Sync(channel)"; + } else if (tgt == "go") { + t.targetCode = "ch := make(chan T); ch <- value"; + t.safety = ConcurrencySafety::Safe; + } else if (tgt == "java") { + t.targetCode = "BlockingQueue queue = new LinkedBlockingQueue<>();"; + t.safety = ConcurrencySafety::Safe; + } else { + t.targetCode = "channel.send(value)"; + t.safety = ConcurrencySafety::NeedsReview; + } + t.targetModel = defaultModel(tgt); + return t; + } + + static ConcurrencyTranslation translateMutex(const std::string& src, const std::string& tgt) { + ConcurrencyTranslation t; + t.pattern = "mutex"; + t.sourceModel = ConcurrencyModel::Threads; + t.sourceLanguage = src; + t.targetLanguage = tgt; + + if (tgt == "rust") { + t.targetCode = "let data = Mutex::new(value); let guard = data.lock().unwrap();"; + t.safety = ConcurrencySafety::Safe; + t.annotation = "@Sync(Mutex)"; + } else if (tgt == "go") { + t.targetCode = "var mu sync.Mutex; mu.Lock(); defer mu.Unlock();"; + t.safety = ConcurrencySafety::Safe; + } else if (tgt == "java") { + t.targetCode = "synchronized(lock) { ... }"; + t.safety = ConcurrencySafety::Safe; + } else if (tgt == "c") { + t.targetCode = "pthread_mutex_lock(&mutex); ... pthread_mutex_unlock(&mutex);"; + t.safety = ConcurrencySafety::NeedsReview; + t.annotation = "@Sync(pthread_mutex)"; + } else { + t.targetCode = "lock.acquire(); ... lock.release();"; + t.safety = ConcurrencySafety::NeedsReview; + } + t.targetModel = defaultModel(tgt); + return t; + } +}; diff --git a/editor/tests/step452_test.cpp b/editor/tests/step452_test.cpp new file mode 100644 index 0000000..ee5521e --- /dev/null +++ b/editor/tests/step452_test.cpp @@ -0,0 +1,146 @@ +// Step 452: Concurrency Model Translation Tests (12 tests) + +#include "ConcurrencyTranslator.h" + +#include + +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 {} + +void test_async_python_to_rust_needs_runtime_review() { + TEST(async_python_to_rust_needs_runtime_review); + auto r = ConcurrencyTranslator::analyze("async def f(): await g()", "python", "rust"); + CHECK(r.hasPattern("async/await"), "expected async pattern"); + CHECK(r.countNeedsReview() >= 1, "rust async runtime should require review"); + CHECK(r.translations[0].annotation.find("tokio") != std::string::npos, + "expected tokio runtime annotation"); + PASS(); +} + +void test_async_to_c_is_unsafe() { + TEST(async_to_c_is_unsafe); + auto r = ConcurrencyTranslator::analyze("async def f(): await g()", "python", "c"); + CHECK(r.hasPattern("async/await"), "expected async pattern"); + CHECK(r.translations[0].safety == ConcurrencySafety::Unsafe, "async->C should be unsafe"); + CHECK(r.translations[0].annotation.find("@Risk(high)") != std::string::npos, + "expected high risk annotation"); + PASS(); +} + +void test_thread_java_to_go_maps_to_goroutine() { + TEST(thread_java_to_go_maps_to_goroutine); + auto r = ConcurrencyTranslator::analyze("new Thread(() -> {}).start();", "java", "go"); + CHECK(r.hasPattern("thread_spawn"), "expected thread spawn pattern"); + CHECK(r.translations[0].targetCode.find("go func") != std::string::npos, + "expected goroutine mapping"); + CHECK(r.translations[0].safety == ConcurrencySafety::Safe, "expected safe mapping"); + PASS(); +} + +void test_thread_to_python_marks_gil_review() { + TEST(thread_to_python_marks_gil_review); + auto r = ConcurrencyTranslator::analyze("std::thread t(f);", "cpp", "python"); + CHECK(r.hasPattern("thread_spawn"), "expected thread spawn pattern"); + CHECK(r.translations[0].safety == ConcurrencySafety::NeedsReview, "expected review"); + CHECK(r.translations[0].annotation.find("GIL") != std::string::npos, "expected GIL annotation"); + PASS(); +} + +void test_channels_go_to_rust_mpsc() { + TEST(channels_go_to_rust_mpsc); + auto r = ConcurrencyTranslator::analyze("ch := make(chan int); ch <- 1", "go", "rust"); + CHECK(r.hasPattern("channels"), "expected channels pattern"); + CHECK(r.translations[0].targetCode.find("mpsc::channel") != std::string::npos, + "expected rust mpsc mapping"); + PASS(); +} + +void test_mutex_java_to_c_requires_review() { + TEST(mutex_java_to_c_requires_review); + auto r = ConcurrencyTranslator::analyze("synchronized(lock) { x++; }", "java", "c"); + CHECK(r.hasPattern("mutex"), "expected mutex pattern"); + CHECK(r.translations[0].safety == ConcurrencySafety::NeedsReview, "expected review in C"); + CHECK(r.translations[0].targetCode.find("pthread_mutex_lock") != std::string::npos, + "expected pthread mutex mapping"); + PASS(); +} + +void test_source_model_prefers_async_when_present() { + TEST(source_model_prefers_async_when_present); + auto r = ConcurrencyTranslator::analyze( + "async def f(): await g()\nthreading.Thread(target=h).start()", + "python", + "rust"); + CHECK(r.sourceModel == ConcurrencyModel::AsyncAwait, "async should win source model detection"); + PASS(); +} + +void test_target_model_defaults_for_javascript() { + TEST(target_model_defaults_for_javascript); + auto r = ConcurrencyTranslator::analyze("value = 1;", "python", "javascript"); + CHECK(r.targetModel == ConcurrencyModel::EventLoop, "javascript target should default to event loop"); + PASS(); +} + +void test_no_patterns_on_plain_code() { + TEST(no_patterns_on_plain_code); + auto r = ConcurrencyTranslator::analyze("x = a + b;", "python", "rust"); + CHECK(r.translations.empty(), "plain code should not emit translations"); + CHECK(!r.hasPattern("async/await"), "should not detect async pattern"); + PASS(); +} + +void test_multiple_patterns_detected() { + TEST(multiple_patterns_detected); + auto r = ConcurrencyTranslator::analyze( + "async def f(): await g()\nmutex.lock()\nmutex.unlock()", + "python", + "rust"); + CHECK(r.hasPattern("async/await"), "expected async"); + CHECK(r.hasPattern("mutex"), "expected mutex"); + CHECK(r.translations.size() >= 2, "expected multiple translations"); + PASS(); +} + +void test_async_python_to_python_stays_safe() { + TEST(async_python_to_python_stays_safe); + auto r = ConcurrencyTranslator::analyze("async def f(): await g()", "python", "python"); + CHECK(r.hasPattern("async/await"), "expected async pattern"); + CHECK(r.translations[0].safety == ConcurrencySafety::Safe, "python async should stay safe"); + PASS(); +} + +void test_review_count_aggregates_multiple_translations() { + TEST(review_count_aggregates_multiple_translations); + auto r = ConcurrencyTranslator::analyze( + "async def f(): await g()\nnew Thread(() -> {}).start();", + "python", + "java"); + CHECK(r.translations.size() >= 2, "expected async + thread translations"); + CHECK(r.countNeedsReview() >= 1, "expected review-required mappings"); + PASS(); +} + +int main() { + std::cout << "Step 452: Concurrency Model Translation Tests\n"; + + test_async_python_to_rust_needs_runtime_review(); // 1 + test_async_to_c_is_unsafe(); // 2 + test_thread_java_to_go_maps_to_goroutine(); // 3 + test_thread_to_python_marks_gil_review(); // 4 + test_channels_go_to_rust_mpsc(); // 5 + test_mutex_java_to_c_requires_review(); // 6 + test_source_model_prefers_async_when_present(); // 7 + test_target_model_defaults_for_javascript(); // 8 + test_no_patterns_on_plain_code(); // 9 + test_multiple_patterns_detected(); // 10 + test_async_python_to_python_stays_safe(); // 11 + test_review_count_aggregates_multiple_translations(); // 12 + + std::cout << "\nResults: " << passed << "/" << (passed + failed) + << " passed\n"; + return failed == 0 ? 0 : 1; +} diff --git a/progress.md b/progress.md index b33ec9f..ba9c2e9 100644 --- a/progress.md +++ b/progress.md @@ -5765,3 +5765,31 @@ classification and risk annotations for lossy projections. **Architecture gate check:** - `editor/src/TypeSystemTranslator.h` within header-size limit (`140` <= `600`) - `editor/tests/step451_test.cpp` within test-file size guidance (`146` lines) + +### Step 452: Concurrency Model Translation +**Status:** PASS (12/12 tests) + +Adds concurrency-pattern translation with explicit runtime/safety annotations +for async/thread/channel/mutex constructs across language runtime models. + +**Files added:** +- `editor/src/ConcurrencyTranslator.h` — concurrency translation engine: + - source/target model detection (`Threads`, `AsyncAwait`, `Channels`, `EventLoop`) + - pattern translators for `async/await`, `thread_spawn`, `channels`, `mutex` + - runtime-aware annotations (`@Exec`, `@ThreadModel`, `@Sync`, `@Blocking`) + - safety grading (`Safe`, `NeedsReview`, `Unsafe`) and review counts +- `editor/tests/step452_test.cpp` — 12 tests covering: + - async mapping across Rust/Python/C targets + - thread/channel/mutex projection to Go/Rust/C + - source/target model detection behavior + - plain-code no-pattern behavior and multi-pattern aggregation +- `editor/CMakeLists.txt` — `step452_test` target + +**Verification run:** +- `cmake --build editor/build-native --target step452_test` — PASS +- `./editor/build-native/step452_test` — PASS (12/12) +- `./editor/build-native/step451_test` — PASS (12/12) regression coverage + +**Architecture gate check:** +- `editor/src/ConcurrencyTranslator.h` within header-size limit (`249` <= `600`) +- `editor/tests/step452_test.cpp` within test-file size guidance (`146` lines)