#pragma once // Step 284-286: Environment Layer // // EnvironmentSpec — first-class environment model attached to modules. // Capability vocabulary, validation, and environment-aware diagnostics. #include "ast/ASTNode.h" #include "ast/Annotation.h" #include #include #include #include using json = nlohmann::json; // --- EnvironmentSpec AST Node --- class EnvironmentSpec : public ASTNode { public: std::string envId; // "posix_process", "browser", "jvm", etc. std::string envVersion; std::vector capabilities; std::vector constraints; std::string scheduler; // "event_loop"|"threads"|"fibers"|"coroutines"|"single_thread" std::string memory; // "manual"|"raii"|"refcount"|"tracing_gc"|"region" json bindingTimes; // per-feature: "compile"|"link"|"load"|"runtime" std::string exceptions; // "unwind"|"checked"|"typed"|"untyped" std::string ffi; // "c_abi"|"dynamic_linking"|"none" EnvironmentSpec() { conceptType = "EnvironmentSpec"; bindingTimes = json::object(); } }; // --- CapabilityRequirement Annotation --- class CapabilityRequirement : public Annotation { public: std::string capability; bool required = true; CapabilityRequirement() { conceptType = "CapabilityRequirement"; } }; // --- Capability Vocabulary --- inline const std::set& knownCapabilities() { static const std::set caps = { "io.fs", "io.net", "io.stdinout", "clock.time", "timers", "event_loop", "threads", "process", "signals", "modules", "dynamic_loading", "reflection", "gc", "jit", "ui.dom", "editor.emacs", "host.imgui" }; return caps; } inline bool isKnownCapability(const std::string& cap) { return knownCapabilities().count(cap) > 0; } // --- Capability Validation --- struct CapabilityDiagnostic { std::string severity; // "error" | "warning" std::string message; std::string nodeId; std::string capability; }; inline std::vector validateCapabilities( const ASTNode* root, const EnvironmentSpec* env) { std::vector diags; if (!root || !env) return diags; std::set envCaps(env->capabilities.begin(), env->capabilities.end()); // Collect all CapabilityRequirements from the tree std::vector toVisit = {root}; while (!toVisit.empty()) { const ASTNode* node = toVisit.back(); toVisit.pop_back(); for (const auto* child : node->allChildren()) { if (child->conceptType == "CapabilityRequirement") { auto* cr = static_cast(child); if (cr->required && envCaps.find(cr->capability) == envCaps.end()) { diags.push_back({ "error", "E0501: Unmet capability requirement: " + cr->capability, node->id, cr->capability }); } } toVisit.push_back(child); } } return diags; } // --- Environment-Incompatible Annotation Diagnostics (Step 286) --- inline std::vector validateEnvAnnotations( const ASTNode* root, const EnvironmentSpec* env) { std::vector diags; if (!root || !env) return diags; std::vector toVisit = {root}; while (!toVisit.empty()) { const ASTNode* node = toVisit.back(); toVisit.pop_back(); for (const auto* child : node->allChildren()) { // ParallelAnnotation(data) in single_thread env if (child->conceptType == "ParallelAnnotation" && env->scheduler == "single_thread") { auto* pa = static_cast(child); diags.push_back({ "error", "E0502: @Parallel(" + pa->kind + ") incompatible with single_thread scheduler", node->id, "" }); } // ThreadModelAnnotation(os) in single_thread env if (child->conceptType == "ThreadModelAnnotation" && env->scheduler == "single_thread") { diags.push_back({ "error", "E0503: @ThreadModel incompatible with single_thread scheduler", node->id, "" }); } // AtomicAnnotation in single_thread env (warning, not error) if (child->conceptType == "AtomicAnnotation" && env->scheduler == "single_thread") { diags.push_back({ "warning", "E0504: @Atomic unnecessary in single_thread environment", node->id, "" }); } // ExecAnnotation(async) requires event_loop, threads, fibers, or coroutines if (child->conceptType == "ExecAnnotation") { auto* ea = static_cast(child); if (ea->mode == "async" && env->scheduler == "single_thread") { diags.push_back({ "error", "E0505: @Exec(async) incompatible with single_thread scheduler", node->id, "" }); } } toVisit.push_back(child); } } return diags; } // --- Environment-Aware Lowering Hints (Step 287) --- struct LoweringHint { std::string pattern; // e.g. "callback", "std_async", "try_catch" std::string description; }; inline std::vector getLoweringHints( const ASTNode* node, const EnvironmentSpec* env) { std::vector hints; if (!node || !env) return hints; for (const auto* child : node->getChildren("annotations")) { if (child->conceptType == "ExecAnnotation") { auto* ea = static_cast(child); if (ea->mode == "async") { if (env->scheduler == "event_loop") hints.push_back({"callback", "async → callback-based pattern"}); else if (env->scheduler == "threads") hints.push_back({"std_async", "async → std::async/std::future"}); else if (env->scheduler == "coroutines") hints.push_back({"coroutine", "async → co_await coroutine"}); } } } // Memory model hints if (env->memory == "tracing_gc") hints.push_back({"suppress_dealloc", "GC env → suppress explicit delete"}); else if (env->memory == "manual") hints.push_back({"explicit_delete", "manual env → inject explicit delete"}); else if (env->memory == "raii") hints.push_back({"raii_cleanup", "RAII env → destructor-based cleanup"}); // Exception model hints if (env->exceptions == "unwind") hints.push_back({"try_catch", "unwind → try/catch blocks"}); else if (env->exceptions == "typed") hints.push_back({"expected", "typed → std::expected return types"}); else if (env->exceptions == "checked") hints.push_back({"checked_throw", "checked → explicit throw declarations"}); return hints; } // --- EnvironmentSpec JSON serialization helpers --- inline json envSpecToJson(const EnvironmentSpec* env) { if (!env) return json(); json j; j["envId"] = env->envId; if (!env->envVersion.empty()) j["envVersion"] = env->envVersion; if (!env->capabilities.empty()) j["capabilities"] = env->capabilities; if (!env->constraints.empty()) j["constraints"] = env->constraints; if (!env->scheduler.empty()) j["scheduler"] = env->scheduler; if (!env->memory.empty()) j["memory"] = env->memory; if (!env->bindingTimes.empty()) j["bindingTimes"] = env->bindingTimes; if (!env->exceptions.empty()) j["exceptions"] = env->exceptions; if (!env->ffi.empty()) j["ffi"] = env->ffi; return j; } inline void envSpecFromJson(EnvironmentSpec* env, const json& j) { if (!env) return; if (j.contains("envId")) env->envId = j["envId"].get(); if (j.contains("envVersion")) env->envVersion = j["envVersion"].get(); if (j.contains("capabilities") && j["capabilities"].is_array()) { env->capabilities.clear(); for (const auto& c : j["capabilities"]) if (c.is_string()) env->capabilities.push_back(c.get()); } if (j.contains("constraints") && j["constraints"].is_array()) { env->constraints.clear(); for (const auto& c : j["constraints"]) if (c.is_string()) env->constraints.push_back(c.get()); } if (j.contains("scheduler")) env->scheduler = j["scheduler"].get(); if (j.contains("memory")) env->memory = j["memory"].get(); if (j.contains("bindingTimes")) env->bindingTimes = j["bindingTimes"]; if (j.contains("exceptions")) env->exceptions = j["exceptions"].get(); if (j.contains("ffi")) env->ffi = j["ffi"].get(); }