Add C backend to nxc-frontend: - Implement CEmitter with HIR-to-C translation - Add emit_c() function for code generation - Support function prototypes, statements, expressions, and control flow - Generate C11-compatible code with proper type mapping - Add main() wrapper generation for entry points Extend driver crate with build pipeline: - Add emit_c_from_frontend() to generate C source from HIR - Add write_c_file() to write generated
95 lines
2.6 KiB
Rust
95 lines
2.6 KiB
Rust
use nxc_frontend::{analyze, emit_c, lower_to_hir, HirExprKind, HirStmtKind, Lexer, Parser};
|
|
|
|
fn lower(source: &str) -> nxc_frontend::LoweringResult {
|
|
let lexed = Lexer::new(source).lex();
|
|
assert!(lexed.diagnostics.is_empty(), "{:?}", lexed.diagnostics);
|
|
|
|
let parsed = Parser::new(lexed.tokens).parse_module();
|
|
assert!(parsed.diagnostics.is_empty(), "{:?}", parsed.diagnostics);
|
|
|
|
let semantic = analyze(&parsed.module);
|
|
assert!(semantic.diagnostics.is_empty(), "{:?}", semantic.diagnostics);
|
|
|
|
lower_to_hir(&semantic.module)
|
|
}
|
|
|
|
#[test]
|
|
fn lowers_simple_program_to_hir() {
|
|
let lowered = lower(
|
|
"\
|
|
fn add(a: Int, b: Int) -> Int:
|
|
let sum = a + b
|
|
return sum
|
|
",
|
|
);
|
|
|
|
assert!(lowered.diagnostics.is_empty(), "{:?}", lowered.diagnostics);
|
|
assert_eq!(lowered.module.functions.len(), 1);
|
|
let function = &lowered.module.functions[0];
|
|
assert_eq!(function.params.len(), 2);
|
|
assert_eq!(function.body.statements.len(), 2);
|
|
|
|
let HirStmtKind::Let { value, .. } = &function.body.statements[0].kind else {
|
|
panic!("expected let statement");
|
|
};
|
|
match &value.kind {
|
|
HirExprKind::Binary { .. } => {}
|
|
other => panic!("expected lowered binary expression, got {other:?}"),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn emits_c_for_precedence_and_return() {
|
|
let lowered = lower(
|
|
"\
|
|
fn predicate() -> Bool:
|
|
return 1 + 2 * 3 == 7 || false
|
|
",
|
|
);
|
|
let emitted = emit_c(&lowered.module);
|
|
assert!(emitted.diagnostics.is_empty(), "{:?}", emitted.diagnostics);
|
|
|
|
let code = emitted.code.expect("expected generated C");
|
|
assert!(code.contains("return (((1LL + (2LL * 3LL)) == 7LL) || false);"));
|
|
assert!(code.contains("bool nxc_fn_0_predicate(void)"));
|
|
}
|
|
|
|
#[test]
|
|
fn emits_if_else_and_function_calls() {
|
|
let lowered = lower(
|
|
"\
|
|
fn choose(flag: Bool) -> Int:
|
|
if flag:
|
|
return answer()
|
|
else:
|
|
return 0
|
|
|
|
fn answer() -> Int:
|
|
return 42
|
|
",
|
|
);
|
|
let emitted = emit_c(&lowered.module);
|
|
assert!(emitted.diagnostics.is_empty(), "{:?}", emitted.diagnostics);
|
|
|
|
let code = emitted.code.expect("expected generated C");
|
|
assert!(code.contains("if (nxc_local_0_flag) {"));
|
|
assert!(code.contains("return nxc_fn_1_answer();"));
|
|
assert!(code.contains("else {"));
|
|
}
|
|
|
|
#[test]
|
|
fn rejects_unsupported_string_binary_ops_in_backend() {
|
|
let lowered = lower(
|
|
"\
|
|
fn main() -> Bool:
|
|
return \"a\" == \"b\"
|
|
",
|
|
);
|
|
let emitted = emit_c(&lowered.module);
|
|
assert!(emitted.code.is_none());
|
|
assert!(emitted
|
|
.diagnostics
|
|
.iter()
|
|
.any(|diag| diag.message.contains("string binary operations are not supported")));
|
|
}
|