5-phase compiler pipeline: C++(lex)→Haskell(parse)→Rust(typecheck)→Lisp(optimize)→Go(emit) Components: - CompilerPhaseSpec + CompilerPhaseRegistry: phase descriptors sorted by order field - CompilerIRNode + IRNodePipeline: IR node kinds (Source→Token→AST→TypedAST→OptimizedAST→Bytecode) + queue - CompilerPhaseRouter: IRNodeKind→phaseId routing with optional fallback - CompilerPipelineRunner: chains PhaseTransform functions in order, supports runUpTo 26/26 tests pass. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
90 lines
3.2 KiB
C++
90 lines
3.2 KiB
C++
// Step 1950: CompilerPhaseRouter
|
|
//
|
|
// t1: registerRoute and routeCount
|
|
// t2: routePhase returns correct phaseId
|
|
// t3: routePhase throws on unregistered kind with no fallback
|
|
// t4: fallback phaseId used when no route matches
|
|
// t5: hasRoute and registeredKinds
|
|
|
|
#include "CompilerPhaseRouter.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(registerRoute_and_routeCount);
|
|
ws::CompilerPhaseRouter r;
|
|
C(r.routeCount() == 0, "empty initially");
|
|
r.registerRoute(ws::IRNodeKind::Source, "lex");
|
|
r.registerRoute(ws::IRNodeKind::Token, "parse");
|
|
C(r.routeCount() == 2, "two routes");
|
|
P();
|
|
}
|
|
|
|
void t2(){
|
|
T(routePhase_returns_correct_phaseId);
|
|
ws::CompilerPhaseRouter r;
|
|
r.registerRoute(ws::IRNodeKind::Source, "lex");
|
|
r.registerRoute(ws::IRNodeKind::Token, "parse");
|
|
r.registerRoute(ws::IRNodeKind::AST, "typecheck");
|
|
r.registerRoute(ws::IRNodeKind::TypedAST, "optimize");
|
|
r.registerRoute(ws::IRNodeKind::OptimizedAST, "emit");
|
|
C(r.routePhase(ws::IRNodeKind::Source) == "lex", "Source→lex");
|
|
C(r.routePhase(ws::IRNodeKind::Token) == "parse", "Token→parse");
|
|
C(r.routePhase(ws::IRNodeKind::AST) == "typecheck", "AST→typecheck");
|
|
C(r.routePhase(ws::IRNodeKind::TypedAST) == "optimize", "TypedAST→optimize");
|
|
C(r.routePhase(ws::IRNodeKind::OptimizedAST) == "emit", "OptimizedAST→emit");
|
|
P();
|
|
}
|
|
|
|
void t3(){
|
|
T(routePhase_throws_on_unregistered_no_fallback);
|
|
ws::CompilerPhaseRouter r;
|
|
r.registerRoute(ws::IRNodeKind::Source, "lex");
|
|
bool threw = false;
|
|
try { r.routePhase(ws::IRNodeKind::Bytecode); }
|
|
catch (const std::out_of_range&) { threw = true; }
|
|
C(threw, "throws on unregistered kind");
|
|
P();
|
|
}
|
|
|
|
void t4(){
|
|
T(fallback_phaseId_used_when_no_route_matches);
|
|
ws::CompilerPhaseRouter r;
|
|
r.registerRoute(ws::IRNodeKind::Source, "lex");
|
|
r.setFallback("unknown-phase");
|
|
C(r.routePhase(ws::IRNodeKind::Source) == "lex", "explicit route wins");
|
|
C(r.routePhase(ws::IRNodeKind::Bytecode)== "unknown-phase", "fallback used");
|
|
r.clearFallback();
|
|
bool threw = false;
|
|
try { r.routePhase(ws::IRNodeKind::Bytecode); }
|
|
catch (const std::out_of_range&) { threw = true; }
|
|
C(threw, "throws after clearFallback");
|
|
P();
|
|
}
|
|
|
|
void t5(){
|
|
T(hasRoute_and_registeredKinds);
|
|
ws::CompilerPhaseRouter r;
|
|
r.registerRoute(ws::IRNodeKind::Source, "lex");
|
|
r.registerRoute(ws::IRNodeKind::Token, "parse");
|
|
C( r.hasRoute(ws::IRNodeKind::Source), "Source registered");
|
|
C( r.hasRoute(ws::IRNodeKind::Token), "Token registered");
|
|
C(!r.hasRoute(ws::IRNodeKind::AST), "AST not registered");
|
|
auto kinds = r.registeredKinds();
|
|
C(kinds.size() == 2, "two registered kinds");
|
|
P();
|
|
}
|
|
|
|
int main(){
|
|
std::cout << "Step 1950: CompilerPhaseRouter\n";
|
|
t1(); t2(); t3(); t4(); t5();
|
|
std::cout << "\n" << p << "/" << (p+f) << " passed\n";
|
|
return f > 0 ? 1 : 0;
|
|
}
|