Skip to main content

ternlang_codegen/
lib.rs

1// SPDX-License-Identifier: LGPL-3.0-or-later
2// Ternlang — RFI-IRFOS Ternary Intelligence Stack
3// Copyright (C) 2026 RFI-IRFOS
4// Open-core compiler. See LICENSE-LGPL in the repository root.
5
6//! ternlang-codegen — AST → C transpiler backend.
7//!
8//! Converts a ternlang `Program` (produced by `ternlang-core::Parser`) into
9//! a valid, self-contained C source file that can be compiled with any C11
10//! compiler.
11//!
12//! # Ternary representation
13//! Trits are represented as `int8_t` with values `-1`, `0`, `+1`.
14//! The generated file includes a small header of inline trit primitives so it
15//! has no external dependencies beyond `<stdint.h>` and `<stdio.h>`.
16//!
17//! # Usage
18//! ```rust
19//! use ternlang_codegen::CTranspiler;
20//! use ternlang_core::{Parser, StdlibLoader};
21//!
22//! let src = r#"fn main() -> trit { return consensus(1, -1); }"#;
23//! let mut parser = ternlang_core::Parser::new(src);
24//! let mut prog = parser.parse_program().unwrap();
25//! StdlibLoader::resolve(&mut prog);
26//!
27//! let c_src = CTranspiler::new().emit(&prog);
28//! println!("{c_src}");
29//! ```
30
31use ternlang_core::ast::*;
32
33// ─── C file header (trit primitives) ─────────────────────────────────────────
34
35const C_HEADER: &str = r#"/* Generated by ternlang-codegen — do not edit. */
36#include <stdint.h>
37#include <stdio.h>
38
39typedef int8_t trit;
40
41/* Ternary primitives */
42static inline trit trit_neg(trit a) { return (trit)(-a); }
43static inline trit trit_add(trit a, trit b) {
44    int s = (int)a + (int)b;
45    if (s > 1)  return  1;
46    if (s < -1) return -1;
47    return (trit)s;
48}
49static inline trit trit_mul(trit a, trit b)       { return (trit)((int)a * (int)b); }
50static inline trit trit_consensus(trit a, trit b) { return trit_add(a, b); }
51static inline trit trit_invert(trit a)             { return trit_neg(a); }
52static inline trit trit_truth()    { return  1; }
53static inline trit trit_hold()     { return  0; }
54static inline trit trit_conflict() { return -1; }
55static inline trit trit_abs(trit a){ return a < 0 ? (trit)-a : a; }
56static inline trit trit_min(trit a, trit b) { return a < b ? a : b; }
57static inline trit trit_max(trit a, trit b) { return a > b ? a : b; }
58
59"#;
60
61// ─── Transpiler ──────────────────────────────────────────────────────────────
62
63pub struct CTranspiler {
64    indent: usize,
65    output: String,
66}
67
68impl CTranspiler {
69    pub fn new() -> Self {
70        Self { indent: 0, output: String::new() }
71    }
72
73    /// Transpile a `Program` to a complete C source string.
74    pub fn emit(mut self, program: &Program) -> String {
75        self.output.push_str(C_HEADER);
76
77        // Forward-declare structs
78        for s in &program.structs {
79            self.emit_struct_decl(s);
80        }
81        if !program.structs.is_empty() { self.output.push('\n'); }
82
83        // Forward-declare all functions (needed for mutual recursion)
84        for f in &program.functions {
85            self.emit_fn_forward(f);
86        }
87        if !program.functions.is_empty() { self.output.push('\n'); }
88
89        // Full function bodies
90        for f in &program.functions {
91            self.emit_function(f);
92            self.output.push('\n');
93        }
94
95        // C entry point: call ternlang main() and print the trit result
96        self.output.push_str(
97            "int main(void) {\n    trit result = tern_main();\n    printf(\"trit: %d\\n\", (int)result);\n    return result == -1 ? 1 : 0;\n}\n"
98        );
99
100        self.output
101    }
102
103    // ── Struct declarations ───────────────────────────────────────────────────
104
105    fn emit_struct_decl(&mut self, s: &StructDef) {
106        self.push(&format!("typedef struct {{\n"));
107        self.indent += 1;
108        for (field, ty) in &s.fields {
109            self.push(&format!("{} {};\n", self.c_type(ty), field));
110        }
111        self.indent -= 1;
112        self.push(&format!("}} {};\n", s.name));
113    }
114
115    // ── Function forward declarations ─────────────────────────────────────────
116
117    fn emit_fn_forward(&mut self, f: &Function) {
118        let params = self.c_params(&f.params);
119        let name   = self.mangle_name(&f.name);
120        self.push(&format!("{} {}({});\n", self.c_type(&f.return_type), name, params));
121    }
122
123    // ── Function bodies ───────────────────────────────────────────────────────
124
125    fn emit_function(&mut self, f: &Function) {
126        let params = self.c_params(&f.params);
127        let name   = self.mangle_name(&f.name);
128        self.push(&format!("{} {}({}) {{\n", self.c_type(&f.return_type), name, params));
129        self.indent += 1;
130        for stmt in &f.body {
131            self.emit_stmt(stmt);
132        }
133        self.indent -= 1;
134        self.push("}\n");
135    }
136
137    // ── Statements ────────────────────────────────────────────────────────────
138
139    fn emit_stmt(&mut self, stmt: &Stmt) {
140        match stmt {
141            Stmt::Let { name, ty, value } => {
142                let cty = self.c_type(ty);
143                let val = self.emit_expr(value);
144                self.push(&format!("{cty} {name} = {val};\n"));
145            }
146            Stmt::Return(expr) => {
147                let val = self.emit_expr(expr);
148                self.push(&format!("return {val};\n"));
149            }
150            Stmt::Expr(expr) => {
151                let val = self.emit_expr(expr);
152                self.push(&format!("{val};\n"));
153            }
154            Stmt::Block(stmts) => {
155                self.push("{\n");
156                self.indent += 1;
157                for s in stmts { self.emit_stmt(s); }
158                self.indent -= 1;
159                self.push("}\n");
160            }
161            Stmt::IfTernary { condition, on_pos, on_zero, on_neg } => {
162                let cond = self.emit_expr(condition);
163                self.push(&format!("if ({cond} > 0) "));
164                self.emit_stmt(on_pos);
165                self.push("else if (0 == (int)");
166                // re-emit condition (cheap for simple exprs)
167                let cond2 = self.emit_expr(condition);
168                self.push(&format!("{cond2}) "));
169                self.emit_stmt(on_zero);
170                self.push("else ");
171                self.emit_stmt(on_neg);
172            }
173            Stmt::Match { condition, arms } => {
174                let cond = self.emit_expr(condition);
175                self.push(&format!("switch ((int){cond}) {{\n"));
176                self.indent += 1;
177                for (pattern, arm) in arms {
178                    if matches!(pattern, Pattern::Wildcard) {
179                        self.push("default: ");
180                        self.emit_stmt(arm);
181                        self.push("break;\n");
182                        continue;
183                    }
184                    let val = match pattern {
185                        Pattern::Int(v) => *v,
186                        Pattern::Trit(t) => *t as i64,
187                        Pattern::Float(f) => *f as i64,
188                        Pattern::Wildcard => unreachable!(),
189                    };
190                    self.push(&format!("case {val}: "));
191                    self.emit_stmt(arm);
192                    self.push("break;\n");
193                }
194                self.indent -= 1;
195                self.push("}\n");
196            }
197            Stmt::WhileTernary { condition, on_pos, on_zero, on_neg } => {
198                let cond = self.emit_expr(condition);
199                // Emit as a do-while that re-evaluates each iteration
200                self.push(&format!("while (1) {{\n"));
201                self.indent += 1;
202                self.push(&format!("trit __cond = {cond};\n"));
203                self.push("if (__cond > 0) ");
204                self.emit_stmt(on_pos);
205                self.push("else if (__cond == 0) ");
206                self.emit_stmt(on_zero);
207                self.push("else ");
208                self.emit_stmt(on_neg);
209                self.indent -= 1;
210                self.push("}\n");
211            }
212            Stmt::Loop { body } => {
213                self.push("for (;;) ");
214                self.emit_stmt(body);
215            }
216            Stmt::ForIn { var, iter, body } => {
217                // For simplicity: emit a comment and skip (tensor iteration requires heap)
218                let iter_expr = self.emit_expr(iter);
219                self.push(&format!("/* for {var} in {iter_expr}: tensor iteration omitted in C backend */\n"));
220                let _ = body;
221            }
222            Stmt::Break    => self.push("break;\n"),
223            Stmt::Continue => self.push("continue;\n"),
224            Stmt::Use { .. } => { /* module imports resolved before codegen */ }
225            Stmt::FromImport { .. } => { /* module imports resolved before codegen */ }
226            Stmt::Send { target, message } => {
227                let t = self.emit_expr(target);
228                let m = self.emit_expr(message);
229                self.push(&format!("/* send {m} to agent {t} — actor model not implemented in C backend */\n"));
230            }
231            Stmt::FieldSet { object, field, value } => {
232                let val = self.emit_expr(value);
233                self.push(&format!("{object}.{field} = {val};\n"));
234            }
235            Stmt::Decorated { stmt, .. } => self.emit_stmt(stmt),
236            Stmt::IndexSet { object, row, col, value } => {
237                let r = self.emit_expr(row);
238                let c = self.emit_expr(col);
239                let val = self.emit_expr(value);
240                self.push(&format!("{object}[{r}][{c}] = {val};\n"));
241            }
242            Stmt::Set { name, value } => {
243                let val = self.emit_expr(value);
244                self.push(&format!("{name} = {val};\n"));
245            }
246        }
247    }
248
249    // ── Expressions → C string ────────────────────────────────────────────────
250
251    fn emit_expr(&self, expr: &Expr) -> String {
252        match expr {
253            Expr::TritLiteral(v)    => format!("((trit){v})"),
254            Expr::IntLiteral(v)     => format!("{v}"),
255            Expr::StringLiteral(s)  => format!("\"{}\"", s.replace('"', "\\\"")),
256            Expr::Ident(name)       => name.clone(),
257
258            Expr::BinaryOp { op, lhs, rhs } => {
259                let l = self.emit_expr(lhs);
260                let r = self.emit_expr(rhs);
261                match op {
262                    BinOp::Add      => format!("trit_add({l}, {r})"),
263                    BinOp::Sub      => format!("trit_add({l}, trit_neg({r}))"),
264                    BinOp::Mul      => format!("trit_mul({l}, {r})"),
265                    BinOp::Equal    => format!("trit_consensus({l}, {r})"),
266                    BinOp::NotEqual => format!("trit_neg(trit_consensus({l}, {r}))"),
267                    BinOp::And      => format!("trit_mul({l}, {r})"),
268                    BinOp::Or       => format!("trit_consensus({l}, {r})"),
269                    BinOp::Less        => format!("(({l}) < ({r}) ? 1 : (({l}) == ({r}) ? 0 : -1))"),
270                    BinOp::Greater     => format!("(({l}) > ({r}) ? 1 : (({l}) == ({r}) ? 0 : -1))"),
271                    BinOp::LessEqual   => format!("(({l}) <= ({r}) ? 1 : -1)"),
272                    BinOp::GreaterEqual=> format!("(({l}) >= ({r}) ? 1 : -1)"),
273                    BinOp::Div         => format!("(({l}) / ({r}))"),
274                    BinOp::Mod         => format!("(({l}) % ({r}))"),
275                }
276            }
277
278            Expr::UnaryOp { op: UnOp::Neg, expr } => {
279                let inner = self.emit_expr(expr);
280                format!("trit_neg({inner})")
281            }
282
283            Expr::Call { callee, args } => {
284                let a: Vec<String> = args.iter().map(|a| self.emit_expr(a)).collect();
285                let args_str = a.join(", ");
286                // Map built-in names to C primitives
287                match callee.as_str() {
288                    "consensus" => format!("trit_consensus({args_str})"),
289                    "invert"    => format!("trit_invert({args_str})"),
290                    "truth"     => "trit_truth()".into(),
291                    "hold"      => "trit_hold()".into(),
292                    "conflict"  => "trit_conflict()".into(),
293                    "abs"       => format!("trit_abs({args_str})"),
294                    "min"       => format!("trit_min({args_str})"),
295                    "max"       => format!("trit_max({args_str})"),
296                    _           => format!("{}({args_str})", self.mangle_name(callee)),
297                }
298            }
299
300            Expr::Cast { expr, .. } => self.emit_expr(expr),
301
302            Expr::FieldAccess { object, field } => {
303                let obj = self.emit_expr(object);
304                format!("{obj}.{field}")
305            }
306
307            Expr::Propagate { expr } => {
308                // `expr?` — emit inline early-return idiom via a GCC/Clang statement expression.
309                // C doesn't have a built-in propagation operator, so we use a local variable.
310                // The caller's emit_stmt wraps this in a let-binding, giving us the variable name.
311                // Here we emit a ternary expression that the caller stores; the early-return
312                // is approximated as: ((__prop = expr) == -1 ? (return -1, __prop) : __prop)
313                // We emit a helper macro call instead for clarity.
314                let inner = self.emit_expr(expr);
315                format!("__TERN_PROPAGATE({inner})")
316            }
317
318            Expr::Spawn { agent_name, .. } =>
319                format!("/* spawn {agent_name} — actor model not implemented in C backend */ 0"),
320            Expr::Await { target } => {
321                let t = self.emit_expr(target);
322                format!("/* await {t} — actor model not implemented in C backend */ 0")
323            }
324            Expr::NodeId => "/* nodeid */ 0".into(),
325            Expr::Index { object, row, col } => {
326                let obj = self.emit_expr(object);
327                let r = self.emit_expr(row);
328                let c = self.emit_expr(col);
329                format!("{obj}[{r}][{c}]")
330            }
331            Expr::FloatLiteral(v) => format!("{v}"),
332            Expr::TritTensorLiteral(elems) => {
333                let parts: Vec<String> = elems.iter().map(|e| e.to_string()).collect();
334                format!("/* trittensor{{{}}} */ 0", parts.join(", "))
335            }
336            Expr::StructLiteral { name, fields } => {
337                let f: Vec<String> = fields.iter()
338                    .map(|(fname, val)| format!(".{fname} = {}", self.emit_expr(val)))
339                    .collect();
340                format!("({}){{ {} }}", name, f.join(", "))
341            }
342            Expr::Slice { object, .. } => {
343                let obj = self.emit_expr(object);
344                format!("/* slice on {obj} — zero-copy views not implemented in C backend */ {obj}")
345            }
346        }
347    }
348
349    // ── Helpers ───────────────────────────────────────────────────────────────
350
351    fn c_type(&self, ty: &Type) -> &'static str {
352        match ty {
353            Type::Trit              => "trit",
354            Type::Int               => "int64_t",
355            Type::Bool              => "int8_t",
356            Type::Float             => "double",
357            Type::String            => "const char*",
358            Type::TritTensor { .. } => "trit*",
359            Type::IntTensor { .. }  => "int64_t*",
360            Type::FloatTensor { .. } => "double*",
361            Type::Named(_)          => "trit",  // struct handled separately
362            Type::AgentRef          => "int",   // opaque ID in C backend
363            Type::PackedTritTensor { .. } => "uint8_t*", // opaque packed buffer
364        }
365    }
366
367    fn c_params(&self, params: &[(String, Type)]) -> String {
368        if params.is_empty() {
369            return "void".into();
370        }
371        params.iter()
372            .map(|(name, ty)| format!("{} {name}", self.c_type(ty)))
373            .collect::<Vec<_>>()
374            .join(", ")
375    }
376
377    /// Map `main` → `tern_main` to avoid clashing with C's `main`.
378    fn mangle_name(&self, name: &str) -> String {
379        match name {
380            "main" => "tern_main".into(),
381            other  => other.to_string(),
382        }
383    }
384
385    fn push(&mut self, s: &str) {
386        let indent = "    ".repeat(self.indent);
387        for line in s.split_inclusive('\n') {
388            if line == "\n" {
389                self.output.push('\n');
390            } else {
391                self.output.push_str(&indent);
392                self.output.push_str(line);
393            }
394        }
395    }
396}
397
398impl Default for CTranspiler {
399    fn default() -> Self { Self::new() }
400}
401
402// ─── Tests ────────────────────────────────────────────────────────────────────
403
404#[cfg(test)]
405mod tests {
406    use super::*;
407    use ternlang_core::{Parser, StdlibLoader};
408
409    fn transpile(src: &str) -> String {
410        let mut parser = Parser::new(src);
411        let mut prog = parser.parse_program().expect("parse failed");
412        StdlibLoader::resolve(&mut prog);
413        CTranspiler::new().emit(&prog)
414    }
415
416    #[test]
417    fn emits_valid_c_header() {
418        let c = transpile("fn main() -> trit { return 1; }");
419        assert!(c.contains("typedef int8_t trit;"), "missing trit typedef");
420        assert!(c.contains("trit_consensus"), "missing consensus primitive");
421    }
422
423    #[test]
424    fn simple_return_emits_return_stmt() {
425        let c = transpile("fn main() -> trit { return 1; }");
426        assert!(c.contains("return 1;"), "missing return 1");
427    }
428
429    #[test]
430    fn consensus_call_maps_to_primitive() {
431        let c = transpile("fn main() -> trit { return consensus(1, -1); }");
432        assert!(c.contains("trit_consensus("), "consensus not mapped to trit_consensus");
433    }
434
435    #[test]
436    fn function_forward_declared() {
437        let c = transpile("fn helper() -> trit { return 0; } fn main() -> trit { return helper(); }");
438        // Forward declarations should appear before function bodies
439        // Note: only "main" is mangled to "tern_main"; other functions keep their names.
440        let fwd_pos  = c.find("trit helper(void);").unwrap_or(usize::MAX);
441        let body_pos = c.find("trit helper(void) {").unwrap_or(usize::MAX);
442        assert!(fwd_pos < body_pos, "forward declaration must precede body");
443    }
444
445    #[test]
446    fn match_emits_switch() {
447        let c = transpile(r#"
448fn main() -> trit {
449    let x: trit = 1;
450    match x {
451        1  => { return 1;  }
452        0  => { return 0;  }
453        -1 => { return -1; }
454    }
455}
456"#);
457        assert!(c.contains("switch"), "match should emit switch");
458        assert!(c.contains("case 1:"),  "missing case 1");
459        assert!(c.contains("case 0:"),  "missing case 0");
460        assert!(c.contains("case -1:"), "missing case -1");
461    }
462
463    #[test]
464    fn struct_emits_typedef_struct() {
465        let c = transpile(r#"
466struct Point { x: trit, y: trit }
467fn main() -> trit { return 0; }
468"#);
469        assert!(c.contains("typedef struct {"), "missing typedef struct");
470        assert!(c.contains("trit x;"), "missing field x");
471    }
472
473    #[test]
474    fn c_entry_point_calls_tern_main() {
475        let c = transpile("fn main() -> trit { return 1; }");
476        assert!(c.contains("int main(void)"), "missing C main");
477        assert!(c.contains("tern_main()"), "missing tern_main call");
478    }
479
480    #[test]
481    fn propagate_emits_helper_macro() {
482        let c = transpile(r#"
483fn check() -> trit { return -1; }
484fn main() -> trit { return check()?; }
485"#);
486        assert!(c.contains("__TERN_PROPAGATE"), "missing propagate macro");
487    }
488
489}