use ternlang_core::ast::*;
const C_HEADER: &str = r#"/* Generated by ternlang-codegen — do not edit. */
#include <stdint.h>
#include <stdio.h>
typedef int8_t trit;
/* Ternary primitives */
static inline trit trit_neg(trit a) { return (trit)(-a); }
static inline trit trit_add(trit a, trit b) {
int s = (int)a + (int)b;
if (s > 1) return 1;
if (s < -1) return -1;
return (trit)s;
}
static inline trit trit_mul(trit a, trit b) { return (trit)((int)a * (int)b); }
static inline trit trit_consensus(trit a, trit b) { return trit_add(a, b); }
static inline trit trit_invert(trit a) { return trit_neg(a); }
static inline trit trit_truth() { return 1; }
static inline trit trit_hold() { return 0; }
static inline trit trit_conflict() { return -1; }
static inline trit trit_abs(trit a){ return a < 0 ? (trit)-a : a; }
static inline trit trit_min(trit a, trit b) { return a < b ? a : b; }
static inline trit trit_max(trit a, trit b) { return a > b ? a : b; }
"#;
pub struct CTranspiler {
indent: usize,
output: String,
}
impl CTranspiler {
pub fn new() -> Self {
Self { indent: 0, output: String::new() }
}
pub fn emit(mut self, program: &Program) -> String {
self.output.push_str(C_HEADER);
for s in &program.structs {
self.emit_struct_decl(s);
}
if !program.structs.is_empty() { self.output.push('\n'); }
for f in &program.functions {
self.emit_fn_forward(f);
}
if !program.functions.is_empty() { self.output.push('\n'); }
for f in &program.functions {
self.emit_function(f);
self.output.push('\n');
}
self.output.push_str(
"int main(void) {\n trit result = tern_main();\n printf(\"trit: %d\\n\", (int)result);\n return result == -1 ? 1 : 0;\n}\n"
);
self.output
}
fn emit_struct_decl(&mut self, s: &StructDef) {
self.push(&format!("typedef struct {{\n"));
self.indent += 1;
for (field, ty) in &s.fields {
self.push(&format!("{} {};\n", self.c_type(ty), field));
}
self.indent -= 1;
self.push(&format!("}} {};\n", s.name));
}
fn emit_fn_forward(&mut self, f: &Function) {
let params = self.c_params(&f.params);
let name = self.mangle_name(&f.name);
self.push(&format!("{} {}({});\n", self.c_type(&f.return_type), name, params));
}
fn emit_function(&mut self, f: &Function) {
let params = self.c_params(&f.params);
let name = self.mangle_name(&f.name);
self.push(&format!("{} {}({}) {{\n", self.c_type(&f.return_type), name, params));
self.indent += 1;
for stmt in &f.body {
self.emit_stmt(stmt);
}
self.indent -= 1;
self.push("}\n");
}
fn emit_stmt(&mut self, stmt: &Stmt) {
match stmt {
Stmt::Let { name, ty, value } => {
let cty = self.c_type(ty);
let val = self.emit_expr(value);
self.push(&format!("{cty} {name} = {val};\n"));
}
Stmt::Return(expr) => {
let val = self.emit_expr(expr);
self.push(&format!("return {val};\n"));
}
Stmt::Expr(expr) => {
let val = self.emit_expr(expr);
self.push(&format!("{val};\n"));
}
Stmt::Block(stmts) => {
self.push("{\n");
self.indent += 1;
for s in stmts { self.emit_stmt(s); }
self.indent -= 1;
self.push("}\n");
}
Stmt::IfTernary { condition, on_pos, on_zero, on_neg } => {
let cond = self.emit_expr(condition);
self.push(&format!("if ({cond} > 0) "));
self.emit_stmt(on_pos);
self.push("else if (0 == (int)");
let cond2 = self.emit_expr(condition);
self.push(&format!("{cond2}) "));
self.emit_stmt(on_zero);
self.push("else ");
self.emit_stmt(on_neg);
}
Stmt::Match { condition, arms } => {
let cond = self.emit_expr(condition);
self.push(&format!("switch ((int){cond}) {{\n"));
self.indent += 1;
for (pattern, arm) in arms {
let val = match pattern {
Pattern::Int(v) => *v,
Pattern::Trit(t) => *t as i64,
Pattern::Float(f) => *f as i64,
};
self.push(&format!("case {val}: "));
self.emit_stmt(arm);
self.push("break;\n");
}
self.indent -= 1;
self.push("}\n");
}
Stmt::WhileTernary { condition, on_pos, on_zero, on_neg } => {
let cond = self.emit_expr(condition);
self.push(&format!("while (1) {{\n"));
self.indent += 1;
self.push(&format!("trit __cond = {cond};\n"));
self.push("if (__cond > 0) ");
self.emit_stmt(on_pos);
self.push("else if (__cond == 0) ");
self.emit_stmt(on_zero);
self.push("else ");
self.emit_stmt(on_neg);
self.indent -= 1;
self.push("}\n");
}
Stmt::Loop { body } => {
self.push("for (;;) ");
self.emit_stmt(body);
}
Stmt::ForIn { var, iter, body } => {
let iter_expr = self.emit_expr(iter);
self.push(&format!("/* for {var} in {iter_expr}: tensor iteration omitted in C backend */\n"));
let _ = body;
}
Stmt::Break => self.push("break;\n"),
Stmt::Continue => self.push("continue;\n"),
Stmt::Use { .. } => { }
Stmt::FromImport { .. } => { }
Stmt::Send { target, message } => {
let t = self.emit_expr(target);
let m = self.emit_expr(message);
self.push(&format!("/* send {m} to agent {t} — actor model not implemented in C backend */\n"));
}
Stmt::FieldSet { object, field, value } => {
let val = self.emit_expr(value);
self.push(&format!("{object}.{field} = {val};\n"));
}
Stmt::Decorated { stmt, .. } => self.emit_stmt(stmt),
Stmt::IndexSet { object, row, col, value } => {
let r = self.emit_expr(row);
let c = self.emit_expr(col);
let val = self.emit_expr(value);
self.push(&format!("{object}[{r}][{c}] = {val};\n"));
}
Stmt::Set { name, value } => {
let val = self.emit_expr(value);
self.push(&format!("{name} = {val};\n"));
}
}
}
fn emit_expr(&self, expr: &Expr) -> String {
match expr {
Expr::TritLiteral(v) => format!("((trit){v})"),
Expr::IntLiteral(v) => format!("{v}"),
Expr::StringLiteral(s) => format!("\"{}\"", s.replace('"', "\\\"")),
Expr::Ident(name) => name.clone(),
Expr::BinaryOp { op, lhs, rhs } => {
let l = self.emit_expr(lhs);
let r = self.emit_expr(rhs);
match op {
BinOp::Add => format!("trit_add({l}, {r})"),
BinOp::Sub => format!("trit_add({l}, trit_neg({r}))"),
BinOp::Mul => format!("trit_mul({l}, {r})"),
BinOp::Equal => format!("trit_consensus({l}, {r})"),
BinOp::NotEqual => format!("trit_neg(trit_consensus({l}, {r}))"),
BinOp::And => format!("trit_mul({l}, {r})"),
BinOp::Or => format!("trit_consensus({l}, {r})"),
BinOp::Less => format!("(({l}) < ({r}) ? 1 : (({l}) == ({r}) ? 0 : -1))"),
BinOp::Greater => format!("(({l}) > ({r}) ? 1 : (({l}) == ({r}) ? 0 : -1))"),
BinOp::LessEqual => format!("(({l}) <= ({r}) ? 1 : -1)"),
BinOp::GreaterEqual=> format!("(({l}) >= ({r}) ? 1 : -1)"),
BinOp::Div => format!("(({l}) / ({r}))"),
BinOp::Mod => format!("(({l}) % ({r}))"),
}
}
Expr::UnaryOp { op: UnOp::Neg, expr } => {
let inner = self.emit_expr(expr);
format!("trit_neg({inner})")
}
Expr::Call { callee, args } => {
let a: Vec<String> = args.iter().map(|a| self.emit_expr(a)).collect();
let args_str = a.join(", ");
match callee.as_str() {
"consensus" => format!("trit_consensus({args_str})"),
"invert" => format!("trit_invert({args_str})"),
"truth" => "trit_truth()".into(),
"hold" => "trit_hold()".into(),
"conflict" => "trit_conflict()".into(),
"abs" => format!("trit_abs({args_str})"),
"min" => format!("trit_min({args_str})"),
"max" => format!("trit_max({args_str})"),
_ => format!("{}({args_str})", self.mangle_name(callee)),
}
}
Expr::Cast { expr, .. } => self.emit_expr(expr),
Expr::FieldAccess { object, field } => {
let obj = self.emit_expr(object);
format!("{obj}.{field}")
}
Expr::Propagate { expr } => {
let inner = self.emit_expr(expr);
format!("__TERN_PROPAGATE({inner})")
}
Expr::Spawn { agent_name, .. } =>
format!("/* spawn {agent_name} — actor model not implemented in C backend */ 0"),
Expr::Await { target } => {
let t = self.emit_expr(target);
format!("/* await {t} — actor model not implemented in C backend */ 0")
}
Expr::NodeId => "/* nodeid */ 0".into(),
Expr::Index { object, row, col } => {
let obj = self.emit_expr(object);
let r = self.emit_expr(row);
let c = self.emit_expr(col);
format!("{obj}[{r}][{c}]")
}
Expr::FloatLiteral(v) => format!("{v}"),
Expr::TritTensorLiteral(elems) => {
let parts: Vec<String> = elems.iter().map(|e| e.to_string()).collect();
format!("/* trittensor{{{}}} */ 0", parts.join(", "))
}
Expr::StructLiteral { name, fields } => {
let f: Vec<String> = fields.iter()
.map(|(fname, val)| format!(".{fname} = {}", self.emit_expr(val)))
.collect();
format!("({}){{ {} }}", name, f.join(", "))
}
}
}
fn c_type(&self, ty: &Type) -> &'static str {
match ty {
Type::Trit => "trit",
Type::Int => "int64_t",
Type::Bool => "int8_t",
Type::Float => "double",
Type::String => "const char*",
Type::TritTensor { .. } => "trit*",
Type::IntTensor { .. } => "int64_t*",
Type::FloatTensor { .. } => "double*",
Type::Named(_) => "trit", Type::AgentRef => "int", }
}
fn c_params(&self, params: &[(String, Type)]) -> String {
if params.is_empty() {
return "void".into();
}
params.iter()
.map(|(name, ty)| format!("{} {name}", self.c_type(ty)))
.collect::<Vec<_>>()
.join(", ")
}
fn mangle_name(&self, name: &str) -> String {
match name {
"main" => "tern_main".into(),
other => other.to_string(),
}
}
fn push(&mut self, s: &str) {
let indent = " ".repeat(self.indent);
for line in s.split_inclusive('\n') {
if line == "\n" {
self.output.push('\n');
} else {
self.output.push_str(&indent);
self.output.push_str(line);
}
}
}
}
impl Default for CTranspiler {
fn default() -> Self { Self::new() }
}
#[cfg(test)]
mod tests {
use super::*;
use ternlang_core::{Parser, StdlibLoader};
fn transpile(src: &str) -> String {
let mut parser = Parser::new(src);
let mut prog = parser.parse_program().expect("parse failed");
StdlibLoader::resolve(&mut prog);
CTranspiler::new().emit(&prog)
}
#[test]
fn emits_valid_c_header() {
let c = transpile("fn main() -> trit { return 1; }");
assert!(c.contains("typedef int8_t trit;"), "missing trit typedef");
assert!(c.contains("trit_consensus"), "missing consensus primitive");
}
#[test]
fn simple_return_emits_return_stmt() {
let c = transpile("fn main() -> trit { return 1; }");
assert!(c.contains("return 1;"), "missing return 1");
}
#[test]
fn consensus_call_maps_to_primitive() {
let c = transpile("fn main() -> trit { return consensus(1, -1); }");
assert!(c.contains("trit_consensus("), "consensus not mapped to trit_consensus");
}
#[test]
fn function_forward_declared() {
let c = transpile("fn helper() -> trit { return 0; } fn main() -> trit { return helper(); }");
let fwd_pos = c.find("trit helper(void);").unwrap_or(usize::MAX);
let body_pos = c.find("trit helper(void) {").unwrap_or(usize::MAX);
assert!(fwd_pos < body_pos, "forward declaration must precede body");
}
#[test]
fn match_emits_switch() {
let c = transpile(r#"
fn main() -> trit {
let x: trit = 1;
match x {
1 => { return 1; }
0 => { return 0; }
-1 => { return -1; }
}
}
"#);
assert!(c.contains("switch"), "match should emit switch");
assert!(c.contains("case 1:"), "missing case 1");
assert!(c.contains("case 0:"), "missing case 0");
assert!(c.contains("case -1:"), "missing case -1");
}
#[test]
fn struct_emits_typedef_struct() {
let c = transpile(r#"
struct Point { x: trit, y: trit }
fn main() -> trit { return 0; }
"#);
assert!(c.contains("typedef struct {"), "missing typedef struct");
assert!(c.contains("trit x;"), "missing field x");
}
#[test]
fn c_entry_point_calls_tern_main() {
let c = transpile("fn main() -> trit { return 1; }");
assert!(c.contains("int main(void)"), "missing C main");
assert!(c.contains("tern_main()"), "missing tern_main call");
}
#[test]
fn propagate_emits_helper_macro() {
let c = transpile(r#"
fn check() -> trit { return -1; }
fn main() -> trit { return check()?; }
"#);
assert!(c.contains("__TERN_PROPAGATE"), "missing propagate macro");
}
}