1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
mod translate;

pub struct CBackend;

use std::fmt::Write;

use translate::Translate;

#[derive(Clone, Copy, PartialEq)]
pub enum Buffer {
    C,
    H,
}

pub struct FileModule {
    name: String,
    buffer: Buffer,
    c_file: String,
    h_file: String,
    label_idx: usize,
}

struct Label(String);

impl FileModule {
    pub fn new(name: String) -> Self {
        const HEADER_FILES: &str = "#include <math.h>\n#include <stdbool.h>\n#include <stdint.h>\n";
        Self {
            name,
            buffer: Buffer::C,
            c_file: String::from(HEADER_FILES),
            h_file: String::from(HEADER_FILES),
            label_idx: 0,
        }
    }

    fn label(&mut self) -> Label {
        let label = format!("L{}", self.label_idx);
        self.label_idx += 1;
        Label(label)
    }

    fn goto(&mut self, label: &Label) -> Result<(), std::fmt::Error> {
        write!(self, "goto {};", label.0)
    }

    fn swap_to(&mut self, target: Buffer) {
        if self.buffer != target {
            std::mem::swap(&mut self.c_file, &mut self.h_file);
            self.buffer = target;
        }
    }

    pub fn write_header_file(
        &mut self,
        writer: impl FnOnce(&mut Self) -> std::fmt::Result,
    ) -> std::fmt::Result {
        self.swap_to(Buffer::H);
        writer(self)?;
        self.swap_to(Buffer::C);
        Ok(())
    }

    pub fn write_source_file(
        &mut self,
        writer: impl FnOnce(&mut Self) -> std::fmt::Result,
    ) -> std::fmt::Result {
        self.swap_to(Buffer::C);
        writer(self)
    }

    fn eol(&mut self) -> Result<(), std::fmt::Error> {
        self.write_char(';')
    }

    fn if_else(
        &mut self,
        cond: &py_ir::value::Value,
        then: &Label,
        or: &Label,
    ) -> Result<(), std::fmt::Error> {
        self.write_str("if(")?;
        self.translate(cond)?;
        self.write_char(')')?;
        self.goto(then)?;
        self.write_str("else ")?;
        self.goto(or)
    }

    #[inline]
    pub fn name(&self) -> &str {
        &self.name
    }

    #[inline]
    pub fn c_file(&self) -> &str {
        &self.c_file
    }

    #[inline]
    pub fn h_file(&self) -> &str {
        &self.h_file
    }
}

impl std::fmt::Write for FileModule {
    fn write_str(&mut self, s: &str) -> std::fmt::Result {
        self.c_file.write_str(s)
    }
}

impl py_codegen::Backend for CBackend {
    type Error = std::fmt::Error;

    type Config = ();

    type Module<'m> = FileModule
    where
        Self: 'm;

    fn init(_config: Self::Config) -> Self {
        CBackend
    }

    fn module(&self, name: &str, items: &[py_ir::Item]) -> Result<Self::Module<'_>, Self::Error> {
        let mut module = FileModule::new(name.to_string());
        for item in items {
            module.translate(item)?;
        }
        Ok(module)
    }
}