feat: update PLAN.md and add end to end test of the compiler
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
A Rust-flavored, C-targeting language - built pipeline-first.
|
A Rust-flavored, C-targeting language - built pipeline-first.
|
||||||
|
|
||||||
**Implementation language:** Rust
|
**Implementation language:** Rust
|
||||||
**Code generation target:** x86-64 (AT&T / Intel syntax `.s` → assembled via GAS or NASM)
|
**Code generation target:** Native object files (`.o`) via Cranelift JIT/AOT
|
||||||
|
|
||||||
## Phase 1 - Lexer
|
## Phase 1 - Lexer
|
||||||
|
|
||||||
@@ -25,23 +25,25 @@ A Rust-flavored, C-targeting language - built pipeline-first.
|
|||||||
|
|
||||||
## Phase 3 - Semantic Analysis
|
## Phase 3 - Semantic Analysis
|
||||||
|
|
||||||
- [ ] Implement scope-aware symbol table
|
- [x] Implement scope-aware symbol table (environment)
|
||||||
- [ ] Name resolution pass - resolve all `Ident` nodes to their declarations
|
- [x] Name resolution pass - resolve all `Ident` nodes to their declarations
|
||||||
- [ ] Type inference / checking for `int` and `bool`
|
- [x] Hindley-Milner type inference (unification, type variables, occurs check)
|
||||||
- [ ] Validate function return types match declared signature
|
- [x] Integer literal sizing and unary minus type promotion logic
|
||||||
- [ ] Error on use-before-declaration and undeclared symbols
|
- [x] Translate untyped AST directly into a fully-typed AST (Typed AST)
|
||||||
- [ ] Unit-test: ill-typed programs produce correct diagnostics
|
- [x] Validate function return types match declared signature
|
||||||
|
- [x] Error on use-before-declaration, undeclared symbols, and type mismatches
|
||||||
|
- [x] Unit-test: HM unification, type mappings, and ill-typed program diagnostics
|
||||||
|
|
||||||
## Phase 4 - x86-64 Code Generation
|
## Phase 4 - Code Generation via Cranelift
|
||||||
|
|
||||||
- [ ] Design a simple intermediate representation (linear IR, or use AST directly)
|
- [x] Integrate `cranelift-codegen`, `cranelift-frontend`, and `cranelift-object`
|
||||||
- [ ] Implement stack-frame layout for local variables
|
- [x] Implement CLI with `clap` (`--emit-ir` flag, input/output files)
|
||||||
- [ ] Emit System V AMD64 ABI-compliant function prologues / epilogues
|
- [x] Map Typed AST types (`Ty`) to Cranelift IR types
|
||||||
- [ ] Codegen for arithmetic & comparison expressions
|
- [x] Lower functions, parameters, and variable definitions to Cranelift IR
|
||||||
- [ ] Codegen for function calls (argument passing via registers)
|
- [x] Codegen for arithmetic, unary operations, and `return` statements
|
||||||
- [ ] Codegen for `return` statements
|
- [x] Run built-in optimization passes (constant folding, e-graphs, DCE)
|
||||||
- [ ] Output `.s` file, assemble with NASM / GAS
|
- [x] Output System V AMD64 ABI-compliant `.o` machine code files
|
||||||
- [ ] End-to-end test: compile a simple `fn` → run → correct exit code
|
- [x] End-to-end test: compile a simple `fn` → link via `gcc` → run → correct exit code
|
||||||
|
|
||||||
## Planned Features (Backlog)
|
## Planned Features (Backlog)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Exit immediately if a command exits with a non-zero status.
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# --- Helper Function ---
|
||||||
|
run_test() {
|
||||||
|
local src_file="$1"
|
||||||
|
local expected_code="$2"
|
||||||
|
local base_name
|
||||||
|
base_name=$(basename "$src_file" .src)
|
||||||
|
local obj_file="tests/$base_name.o"
|
||||||
|
local exec_file="tests/$base_name"
|
||||||
|
|
||||||
|
echo "--- Running test: $src_file ---"
|
||||||
|
|
||||||
|
# 1. Compile the source file using our compiler
|
||||||
|
echo " [1/4] Compiling..."
|
||||||
|
cargo run --release -- "$src_file" -o "$obj_file" > /dev/null 2>&1
|
||||||
|
|
||||||
|
# 2. Link the object file with the system linker (gcc)
|
||||||
|
echo " [2/4] Linking with gcc..."
|
||||||
|
gcc "$obj_file" -o "$exec_file"
|
||||||
|
|
||||||
|
# 3. Run the executable
|
||||||
|
echo " [3/4] Running..."
|
||||||
|
set +e
|
||||||
|
./"$exec_file"
|
||||||
|
local actual_code=$?
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# 4. Check the exit code
|
||||||
|
echo " [4/4] Verifying exit code..."
|
||||||
|
if [ "$actual_code" -eq "$expected_code" ]; then
|
||||||
|
echo "SUCCESS: Exit code is $actual_code as expected."
|
||||||
|
else
|
||||||
|
echo "FAILURE: Expected exit code $expected_code, but got $actual_code."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Clean up generated files
|
||||||
|
rm "$obj_file" "$exec_file"
|
||||||
|
echo "------------------------------------"
|
||||||
|
echo
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Test Cases ---
|
||||||
|
|
||||||
|
# Test a simple positive return value.
|
||||||
|
run_test "tests/return_42.src" 42
|
||||||
|
|
||||||
|
# Test a negative return value. Shell exit codes are unsigned 8-bit integers,
|
||||||
|
# so -69 (i8) wraps around to 187.
|
||||||
|
run_test "tests/return_neg_69.src" 187
|
||||||
|
|
||||||
|
echo "All end-to-end tests passed!"
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fn main() -> i32 {
|
||||||
|
return 42;
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fn main() -> i8 {
|
||||||
|
return -69;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user