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>
119 lines
4.2 KiB
C++
119 lines
4.2 KiB
C++
// Step 1951: CompilerPipelineRunner
|
|
//
|
|
// t1: addPhase and phaseCount
|
|
// t2: runAll chains transforms in order
|
|
// t3: runUpTo stops at named phase
|
|
// t4: runUpTo throws on unknown phase
|
|
// t5: phaseOrder reflects insertion order by spec.order; empty runner throws
|
|
|
|
#include "CompilerPipelineRunner.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;}
|
|
|
|
// Transform helpers
|
|
static ws::CompilerIRNode toToken(const ws::CompilerIRNode& n) {
|
|
return {ws::IRNodeKind::Token, "tok:" + n.payload, n.sourceId};
|
|
}
|
|
static ws::CompilerIRNode toAST(const ws::CompilerIRNode& n) {
|
|
return {ws::IRNodeKind::AST, "ast:" + n.payload, n.sourceId};
|
|
}
|
|
static ws::CompilerIRNode toTyped(const ws::CompilerIRNode& n) {
|
|
return {ws::IRNodeKind::TypedAST, "typed:" + n.payload, n.sourceId};
|
|
}
|
|
static ws::CompilerIRNode toOpt(const ws::CompilerIRNode& n) {
|
|
return {ws::IRNodeKind::OptimizedAST, "opt:" + n.payload, n.sourceId};
|
|
}
|
|
static ws::CompilerIRNode toBytecode(const ws::CompilerIRNode& n) {
|
|
return {ws::IRNodeKind::Bytecode, "bc:" + n.payload, n.sourceId};
|
|
}
|
|
|
|
static ws::CompilerPipelineRunner makeRunner() {
|
|
ws::CompilerPipelineRunner r;
|
|
r.addPhase({"lex", "C++", "Lexing", 1, "Source", "Token"}, toToken);
|
|
r.addPhase({"parse", "Haskell", "Parsing", 2, "Token", "AST"}, toAST);
|
|
r.addPhase({"typecheck","Rust", "Typechecking",3, "AST", "TypedAST"}, toTyped);
|
|
r.addPhase({"optimize", "Lisp", "Optimization",4, "TypedAST", "OptimizedAST"},toOpt);
|
|
r.addPhase({"emit", "Go", "Emit", 5, "OptimizedAST", "Bytecode"}, toBytecode);
|
|
return r;
|
|
}
|
|
|
|
void t1(){
|
|
T(addPhase_and_phaseCount);
|
|
ws::CompilerPipelineRunner r;
|
|
C(r.phaseCount() == 0, "empty initially");
|
|
C(r.empty(), "empty() true");
|
|
r.addPhase({"lex","C++","",1,"",""}, toToken);
|
|
C(r.phaseCount() == 1, "one phase");
|
|
C(!r.empty(), "not empty");
|
|
P();
|
|
}
|
|
|
|
void t2(){
|
|
T(runAll_chains_transforms);
|
|
auto r = makeRunner();
|
|
ws::CompilerIRNode input{ws::IRNodeKind::Source, "hello", "main"};
|
|
auto result = r.runAll(input);
|
|
C(result.kind == ws::IRNodeKind::Bytecode, "final kind Bytecode");
|
|
// Payload was chained: bc:opt:typed:ast:tok:hello
|
|
C(result.payload == "bc:opt:typed:ast:tok:hello", "chained payload");
|
|
C(result.sourceId == "main", "sourceId preserved");
|
|
P();
|
|
}
|
|
|
|
void t3(){
|
|
T(runUpTo_stops_at_named_phase);
|
|
auto r = makeRunner();
|
|
ws::CompilerIRNode input{ws::IRNodeKind::Source, "x", ""};
|
|
auto result = r.runUpTo(input, "parse");
|
|
C(result.kind == ws::IRNodeKind::AST, "kind AST after parse");
|
|
C(result.payload == "ast:tok:x", "payload after lex+parse");
|
|
auto tc = r.runUpTo(input, "typecheck");
|
|
C(tc.kind == ws::IRNodeKind::TypedAST, "TypedAST after typecheck");
|
|
P();
|
|
}
|
|
|
|
void t4(){
|
|
T(runUpTo_throws_on_unknown_phase);
|
|
auto r = makeRunner();
|
|
ws::CompilerIRNode input{ws::IRNodeKind::Source, "y", ""};
|
|
bool threw = false;
|
|
try { r.runUpTo(input, "nonexistent"); }
|
|
catch (const std::out_of_range&) { threw = true; }
|
|
C(threw, "throws on unknown phase");
|
|
P();
|
|
}
|
|
|
|
void t5(){
|
|
T(phaseOrder_and_empty_runner_throws);
|
|
ws::CompilerPipelineRunner r;
|
|
// Add out of order — should sort by spec.order
|
|
r.addPhase({"emit", "Go", "",5,"",""}, toBytecode);
|
|
r.addPhase({"lex", "C++", "",1,"",""}, toToken);
|
|
r.addPhase({"parse", "Haskell","",2,"",""}, toAST);
|
|
auto order = r.phaseOrder();
|
|
C(order.size() == 3, "three phases");
|
|
C(order[0] == "lex", "lex first");
|
|
C(order[1] == "parse", "parse second");
|
|
C(order[2] == "emit", "emit third");
|
|
|
|
ws::CompilerPipelineRunner empty;
|
|
bool threw = false;
|
|
try { empty.runAll({ws::IRNodeKind::Source,"",""});}
|
|
catch (const std::runtime_error&) { threw = true; }
|
|
C(threw, "empty runner throws");
|
|
P();
|
|
}
|
|
|
|
int main(){
|
|
std::cout << "Step 1951: CompilerPipelineRunner\n";
|
|
t1(); t2(); t3(); t4(); t5();
|
|
std::cout << "\n" << p << "/" << (p+f) << " passed\n";
|
|
return f > 0 ? 1 : 0;
|
|
}
|