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                    let val = match pattern {
179                        Pattern::Int(v) => *v,
180                        Pattern::Trit(t) => *t as i64,
181                        Pattern::Float(f) => *f as i64,
182                    };
183                    self.push(&format!("case {val}: "));
184                    self.emit_stmt(arm);
185                    self.push("break;\n");
186                }
187                self.indent -= 1;
188                self.push("}\n");
189            }
190            Stmt::WhileTernary { condition, on_pos, on_zero, on_neg } => {
191                let cond = self.emit_expr(condition);
192                // Emit as a do-while that re-evaluates each iteration
193                self.push(&format!("while (1) {{\n"));
194                self.indent += 1;
195                self.push(&format!("trit __cond = {cond};\n"));
196                self.push("if (__cond > 0) ");
197                self.emit_stmt(on_pos);
198                self.push("else if (__cond == 0) ");
199                self.emit_stmt(on_zero);
200                self.push("else ");
201                self.emit_stmt(on_neg);
202                self.indent -= 1;
203                self.push("}\n");
204            }
205            Stmt::Loop { body } => {
206                self.push("for (;;) ");
207                self.emit_stmt(body);
208            }
209            Stmt::ForIn { var, iter, body } => {
210                // For simplicity: emit a comment and skip (tensor iteration requires heap)
211                let iter_expr = self.emit_expr(iter);
212                self.push(&format!("/* for {var} in {iter_expr}: tensor iteration omitted in C backend */\n"));
213                let _ = body;
214            }
215            Stmt::Break    => self.push("break;\n"),
216            Stmt::Continue => self.push("continue;\n"),
217            Stmt::Use { .. } => { /* module imports resolved before codegen */ }
218            Stmt::FromImport { .. } => { /* module imports resolved before codegen */ }
219            Stmt::Send { target, message } => {
220                let t = self.emit_expr(target);
221                let m = self.emit_expr(message);
222                self.push(&format!("/* send {m} to agent {t} — actor model not implemented in C backend */\n"));
223            }
224            Stmt::FieldSet { object, field, value } => {
225                let val = self.emit_expr(value);
226                self.push(&format!("{object}.{field} = {val};\n"));
227            }
228            Stmt::Decorated { stmt, .. } => self.emit_stmt(stmt),
229            Stmt::IndexSet { object, row, col, value } => {
230                let r = self.emit_expr(row);
231                let c = self.emit_expr(col);
232                let val = self.emit_expr(value);
233                self.push(&format!("{object}[{r}][{c}] = {val};\n"));
234            }
235            Stmt::Set { name, value } => {
236                let val = self.emit_expr(value);
237                self.push(&format!("{name} = {val};\n"));
238            }
239        }
240    }
241
242    // ── Expressions → C string ────────────────────────────────────────────────
243
244    fn emit_expr(&self, expr: &Expr) -> String {
245        match expr {
246            Expr::TritLiteral(v)    => format!("((trit){v})"),
247            Expr::IntLiteral(v)     => format!("{v}"),
248            Expr::StringLiteral(s)  => format!("\"{}\"", s.replace('"', "\\\"")),
249            Expr::Ident(name)       => name.clone(),
250
251            Expr::BinaryOp { op, lhs, rhs } => {
252                let l = self.emit_expr(lhs);
253                let r = self.emit_expr(rhs);
254                match op {
255                    BinOp::Add      => format!("trit_add({l}, {r})"),
256                    BinOp::Sub      => format!("trit_add({l}, trit_neg({r}))"),
257                    BinOp::Mul      => format!("trit_mul({l}, {r})"),
258                    BinOp::Equal    => format!("trit_consensus({l}, {r})"),
259                    BinOp::NotEqual => format!("trit_neg(trit_consensus({l}, {r}))"),
260                    BinOp::And      => format!("trit_mul({l}, {r})"),
261                    BinOp::Or       => format!("trit_consensus({l}, {r})"),
262                    BinOp::Less        => format!("(({l}) < ({r}) ? 1 : (({l}) == ({r}) ? 0 : -1))"),
263                    BinOp::Greater     => format!("(({l}) > ({r}) ? 1 : (({l}) == ({r}) ? 0 : -1))"),
264                    BinOp::LessEqual   => format!("(({l}) <= ({r}) ? 1 : -1)"),
265                    BinOp::GreaterEqual=> format!("(({l}) >= ({r}) ? 1 : -1)"),
266                    BinOp::Div         => format!("(({l}) / ({r}))"),
267                    BinOp::Mod         => format!("(({l}) % ({r}))"),
268                }
269            }
270
271            Expr::UnaryOp { op: UnOp::Neg, expr } => {
272                let inner = self.emit_expr(expr);
273                format!("trit_neg({inner})")
274            }
275
276            Expr::Call { callee, args } => {
277                let a: Vec<String> = args.iter().map(|a| self.emit_expr(a)).collect();
278                let args_str = a.join(", ");
279                // Map built-in names to C primitives
280                match callee.as_str() {
281                    "consensus" => format!("trit_consensus({args_str})"),
282                    "invert"    => format!("trit_invert({args_str})"),
283                    "truth"     => "trit_truth()".into(),
284                    "hold"      => "trit_hold()".into(),
285                    "conflict"  => "trit_conflict()".into(),
286                    "abs"       => format!("trit_abs({args_str})"),
287                    "min"       => format!("trit_min({args_str})"),
288                    "max"       => format!("trit_max({args_str})"),
289                    _           => format!("{}({args_str})", self.mangle_name(callee)),
290                }
291            }
292
293            Expr::Cast { expr, .. } => self.emit_expr(expr),
294
295            Expr::FieldAccess { object, field } => {
296                let obj = self.emit_expr(object);
297                format!("{obj}.{field}")
298            }
299
300            Expr::Propagate { expr } => {
301                // `expr?` — emit inline early-return idiom via a GCC/Clang statement expression.
302                // C doesn't have a built-in propagation operator, so we use a local variable.
303                // The caller's emit_stmt wraps this in a let-binding, giving us the variable name.
304                // Here we emit a ternary expression that the caller stores; the early-return
305                // is approximated as: ((__prop = expr) == -1 ? (return -1, __prop) : __prop)
306                // We emit a helper macro call instead for clarity.
307                let inner = self.emit_expr(expr);
308                format!("__TERN_PROPAGATE({inner})")
309            }
310
311            Expr::Spawn { agent_name, .. } =>
312                format!("/* spawn {agent_name} — actor model not implemented in C backend */ 0"),
313            Expr::Await { target } => {
314                let t = self.emit_expr(target);
315                format!("/* await {t} — actor model not implemented in C backend */ 0")
316            }
317            Expr::NodeId => "/* nodeid */ 0".into(),
318            Expr::Index { object, row, col } => {
319                let obj = self.emit_expr(object);
320                let r = self.emit_expr(row);
321                let c = self.emit_expr(col);
322                format!("{obj}[{r}][{c}]")
323            }
324            Expr::FloatLiteral(v) => format!("{v}"),
325            Expr::TritTensorLiteral(elems) => {
326                let parts: Vec<String> = elems.iter().map(|e| e.to_string()).collect();
327                format!("/* trittensor{{{}}} */ 0", parts.join(", "))
328            }
329            Expr::StructLiteral { name, fields } => {
330                let f: Vec<String> = fields.iter()
331                    .map(|(fname, val)| format!(".{fname} = {}", self.emit_expr(val)))
332                    .collect();
333                format!("({}){{ {} }}", name, f.join(", "))
334            }
335        }
336    }
337
338    // ── Helpers ───────────────────────────────────────────────────────────────
339
340    fn c_type(&self, ty: &Type) -> &'static str {
341        match ty {
342            Type::Trit              => "trit",
343            Type::Int               => "int64_t",
344            Type::Bool              => "int8_t",
345            Type::Float             => "double",
346            Type::String            => "const char*",
347            Type::TritTensor { .. } => "trit*",
348            Type::IntTensor { .. }  => "int64_t*",
349            Type::FloatTensor { .. } => "double*",
350            Type::Named(_)          => "trit",  // struct handled separately
351            Type::AgentRef          => "int",   // opaque ID in C backend
352        }
353    }
354
355    fn c_params(&self, params: &[(String, Type)]) -> String {
356        if params.is_empty() {
357            return "void".into();
358        }
359        params.iter()
360            .map(|(name, ty)| format!("{} {name}", self.c_type(ty)))
361            .collect::<Vec<_>>()
362            .join(", ")
363    }
364
365    /// Map `main` → `tern_main` to avoid clashing with C's `main`.
366    fn mangle_name(&self, name: &str) -> String {
367        match name {
368            "main" => "tern_main".into(),
369            other  => other.to_string(),
370        }
371    }
372
373    fn push(&mut self, s: &str) {
374        let indent = "    ".repeat(self.indent);
375        for line in s.split_inclusive('\n') {
376            if line == "\n" {
377                self.output.push('\n');
378            } else {
379                self.output.push_str(&indent);
380                self.output.push_str(line);
381            }
382        }
383    }
384}
385
386impl Default for CTranspiler {
387    fn default() -> Self { Self::new() }
388}
389
390// ─── Tests ────────────────────────────────────────────────────────────────────
391
392#[cfg(test)]
393mod tests {
394    use super::*;
395    use ternlang_core::{Parser, StdlibLoader};
396
397    fn transpile(src: &str) -> String {
398        let mut parser = Parser::new(src);
399        let mut prog = parser.parse_program().expect("parse failed");
400        StdlibLoader::resolve(&mut prog);
401        CTranspiler::new().emit(&prog)
402    }
403
404    #[test]
405    fn emits_valid_c_header() {
406        let c = transpile("fn main() -> trit { return 1; }");
407        assert!(c.contains("typedef int8_t trit;"), "missing trit typedef");
408        assert!(c.contains("trit_consensus"), "missing consensus primitive");
409    }
410
411    #[test]
412    fn simple_return_emits_return_stmt() {
413        let c = transpile("fn main() -> trit { return 1; }");
414        assert!(c.contains("return 1;"), "missing return 1");
415    }
416
417    #[test]
418    fn consensus_call_maps_to_primitive() {
419        let c = transpile("fn main() -> trit { return consensus(1, -1); }");
420        assert!(c.contains("trit_consensus("), "consensus not mapped to trit_consensus");
421    }
422
423    #[test]
424    fn function_forward_declared() {
425        let c = transpile("fn helper() -> trit { return 0; } fn main() -> trit { return helper(); }");
426        // Forward declarations should appear before function bodies
427        // Note: only "main" is mangled to "tern_main"; other functions keep their names.
428        let fwd_pos  = c.find("trit helper(void);").unwrap_or(usize::MAX);
429        let body_pos = c.find("trit helper(void) {").unwrap_or(usize::MAX);
430        assert!(fwd_pos < body_pos, "forward declaration must precede body");
431    }
432
433    #[test]
434    fn match_emits_switch() {
435        let c = transpile(r#"
436fn main() -> trit {
437    let x: trit = 1;
438    match x {
439        1  => { return 1;  }
440        0  => { return 0;  }
441        -1 => { return -1; }
442    }
443}
444"#);
445        assert!(c.contains("switch"), "match should emit switch");
446        assert!(c.contains("case 1:"),  "missing case 1");
447        assert!(c.contains("case 0:"),  "missing case 0");
448        assert!(c.contains("case -1:"), "missing case -1");
449    }
450
451    #[test]
452    fn struct_emits_typedef_struct() {
453        let c = transpile(r#"
454struct Point { x: trit, y: trit }
455fn main() -> trit { return 0; }
456"#);
457        assert!(c.contains("typedef struct {"), "missing typedef struct");
458        assert!(c.contains("trit x;"), "missing field x");
459    }
460
461    #[test]
462    fn c_entry_point_calls_tern_main() {
463        let c = transpile("fn main() -> trit { return 1; }");
464        assert!(c.contains("int main(void)"), "missing C main");
465        assert!(c.contains("tern_main()"), "missing tern_main call");
466    }
467
468    #[test]
469    fn propagate_emits_helper_macro() {
470        let c = transpile(r#"
471fn check() -> trit { return -1; }
472fn main() -> trit { return check()?; }
473"#);
474        assert!(c.contains("__TERN_PROPAGATE"), "missing propagate macro");
475    }
476
477}