feat: dependency resolver (Phase 1) + formatter + doctor + assertion evaluation

Resolver (Phase 1):
- resolve::resolve() takes SystemConfig + Registry → BuildPlan
- Three-layer feature/config merge (uses config::resolve_package)
- Feature-gated dependencies: deps with feature=X skipped if feature disabled
- Topological sort via Kahn's algorithm (BFS on in-degree)
- Cycle detection, missing package warnings
- CLI: kappa resolve <config> [<package>]

Formatter:
- format_package() + format_config() → canonical output
- Consistent 4-space indent, canonical declaration order

Doctor:
- check_package() + check_config() → warnings for common issues
- Missing fields, empty configs, root shell, feature warnings

Assertion evaluation:
- evaluate_assertions() resolves dotted field paths
- Supports == and != operators for config validation
- kappa validate now runs assertion checks

Review fixes:
- plan.steps.empty() exit code corrected to 0
- Removed unused <unordered_set> include
- Eliminated duplicate merge logic (uses config::resolve_package)
This commit is contained in:
2026-07-30 00:36:38 -04:00
parent 0c1e82d66b
commit ee9f280346
10 changed files with 746 additions and 23 deletions
+20 -2
View File
@@ -3,6 +3,7 @@
#include <format>
#include <stdexcept>
#include <string>
#include <string_view>
namespace kappa::dsl {
@@ -18,16 +19,33 @@ inline int parse_int(int line, int col, const std::string& lexeme) {
int val = std::stoi(lexeme, &pos);
if (pos != lexeme.size()) {
throw ParseError(line, col,
std::format("expected integer, got '{}'", lexeme));
std::format("expected an integer, got '{}'", lexeme));
}
return val;
} catch (const std::invalid_argument&) {
throw ParseError(line, col,
std::format("expected integer, got '{}'", lexeme));
std::format("expected an integer, got '{}'", lexeme));
} catch (const std::out_of_range&) {
throw ParseError(line, col,
std::format("integer out of range: '{}'", lexeme));
}
}
inline std::string msg_expected(std::string_view expected, std::string_view got) {
return std::format("expected {}, got '{}'", expected, got);
}
inline std::string msg_unclosed_block(std::string_view block) {
return std::format("unclosed {} — missing '}}' before end of file", block);
}
inline std::string msg_unknown_decl(std::string_view token) {
return std::format("unknown declaration '{}'", token);
}
inline std::string msg_not_a_string() {
return "expected a quoted string value, got bare word — wrap it in \"quotes\"";
}
} // namespace kappa::dsl