pico_bytes/
lib.rs

1#[cfg(test)]
2mod tests;
3
4use std::{error::Error, fmt::Display};
5
6pub type Register = u32;
7pub type Address = u32;
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum Locator {
10    Address(Address),
11    FromRegister(Register),
12}
13pub type Bytes = (u8, u32, u32, u32);
14#[derive(Debug, Clone, Copy, PartialEq)]
15#[repr(u8)]
16pub enum ByteCode {
17    None,
18    Halt,
19    Jump {
20        addr: Locator,
21    },
22    JumpIf {
23        cond: Register,
24        addr: Locator,
25    },
26
27    String {
28        dst: Register,
29        addr: u32,
30    },
31    Int {
32        dst: Register,
33        value: u64,
34    },
35    Float {
36        dst: Register,
37        value: f64,
38    },
39    Bool {
40        dst: Register,
41        value: bool,
42    },
43
44    Move {
45        dst: Register,
46        src: Register,
47    },
48    Field {
49        dst: Register,
50        src: Register,
51        field: u32,
52    },
53    Call {
54        addr: Locator,
55        args: u32,
56        dst: Register
57    },
58
59    Binary {
60        op: BinaryOperation,
61        dst: Register,
62        left: Register,
63        right: Register,
64    },
65    Unary {
66        op: UnaryOperation,
67        dst: Register,
68        right: Register,
69    },
70}
71
72#[derive(Debug, Clone, Copy, PartialEq, Eq)]
73#[repr(u8)]
74pub enum BinaryOperation {
75    Add,
76    Sub,
77    Div,
78    Mul,
79}
80#[derive(Debug, Clone, PartialEq)]
81pub struct BinaryOperationError;
82impl TryFrom<u8> for BinaryOperation {
83    type Error = BinaryOperationError;
84    fn try_from(value: u8) -> Result<Self, Self::Error> {
85        match value {
86            0 => Ok(Self::Add),
87            1 => Ok(Self::Sub),
88            2 => Ok(Self::Div),
89            3 => Ok(Self::Mul),
90            _ => Err(BinaryOperationError),
91        }
92    }
93}
94impl From<BinaryOperation> for u8 {
95    fn from(val: BinaryOperation) -> Self {
96        match val {
97            BinaryOperation::Add => 0,
98            BinaryOperation::Sub => 1,
99            BinaryOperation::Div => 2,
100            BinaryOperation::Mul => 3,
101        }
102    }
103}
104#[derive(Debug, Clone, Copy, PartialEq, Eq)]
105#[repr(u8)]
106pub enum UnaryOperation {
107    Neg,
108}
109#[derive(Debug, Clone, PartialEq)]
110pub struct UnaryOperationError;
111impl TryFrom<u8> for UnaryOperation {
112    type Error = UnaryOperationError;
113    fn try_from(value: u8) -> Result<Self, Self::Error> {
114        match value {
115            0 => Ok(Self::Neg),
116            _ => Err(UnaryOperationError),
117        }
118    }
119}
120impl From<UnaryOperation> for u8 {
121    fn from(val: UnaryOperation) -> Self {
122        match val {
123            UnaryOperation::Neg => 0,
124        }
125    }
126}
127
128impl From<Locator> for u32 {
129    fn from(value: Locator) -> Self {
130        match value {
131            Locator::FromRegister(addr) | Locator::Address(addr) => addr,
132        }
133    }
134}
135impl From<ByteCode> for Bytes {
136    fn from(value: ByteCode) -> Self {
137        match value {
138            ByteCode::None => (0x00, 0, 0, 0),
139            ByteCode::Halt => (0x01, 0, 0, 0),
140            ByteCode::Jump { addr } => match addr {
141                Locator::Address(addr) => (0x02, 0, addr, 0),
142                Locator::FromRegister(addr) => (0x03, 0, addr, 0),
143            }
144            ByteCode::JumpIf { cond, addr } => match addr {
145                Locator::Address(addr) => (0x04, cond, addr, 0),
146                Locator::FromRegister(addr) => (0x05, cond, addr, 0),
147            }
148            ByteCode::String { dst, addr } => (0x10, dst, addr, 0),
149            ByteCode::Int { dst, value } => {
150                (0x11, dst, value as u32, (value >> 32) as u32)
151            }
152            ByteCode::Float { dst, value } => {
153                let bits = value.to_bits();
154                (0x12, dst, bits as u32, (bits >> 32) as u32)
155            }
156            ByteCode::Bool { dst, value } => (0x13, dst, value.into(), 0),
157            ByteCode::Move { dst, src } => (0x20, dst, src, 0),
158            ByteCode::Field { dst, src, field } => (0x21, dst, src, field),
159            ByteCode::Call { addr, args, dst } => match addr {
160                Locator::Address(addr) => (0x22, addr, args, dst),
161                Locator::FromRegister(addr) => (0x23, addr, args, dst),
162            }
163            ByteCode::Binary { op, dst, left, right } => (0x30 + op as u8, dst, left, right),
164            ByteCode::Unary { op, dst, right } => (0x40 + op as u8, dst, right, 0),
165        }
166    }
167}
168
169#[derive(Debug, Clone, PartialEq)]
170pub enum ByteCodeError {
171    InvalidOperation,
172    InvalidBinaryOperation(u8),
173    InvalidUnaryOperation(u8),
174}
175impl Display for ByteCodeError {
176    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
177        match self {
178            ByteCodeError::InvalidOperation => write!(f, "invalid operation"),
179            ByteCodeError::InvalidBinaryOperation(op) => {
180                write!(f, "invalid binary operation 0x20 + 0x{op:2x?}")
181            }
182            ByteCodeError::InvalidUnaryOperation(op) => {
183                write!(f, "invalid unary operation 0x30 + 0x{op:2x?}")
184            }
185        }
186    }
187}
188impl Error for ByteCodeError {}
189impl TryFrom<Bytes> for ByteCode {
190    type Error = ByteCodeError;
191    fn try_from(value: Bytes) -> Result<Self, Self::Error> {
192        match value.0 {
193            0x00 => Ok(Self::None),
194            0x01 => Ok(Self::Halt),
195            0x02 => Ok(Self::Jump {
196                addr: Locator::Address(value.2),
197            }),
198            0x03 => Ok(Self::Jump {
199                addr: Locator::FromRegister(value.2),
200            }),
201            0x04 => Ok(Self::JumpIf {
202                cond: value.1,
203                addr: Locator::Address(value.2),
204            }),
205            0x05 => Ok(Self::JumpIf {
206                cond: value.1,
207                addr: Locator::FromRegister(value.2),
208            }),
209
210            0x10 => Ok(Self::String {
211                dst: value.1,
212                addr: value.2,
213            }),
214            0x11 => Ok(Self::Int {
215                dst: value.1,
216                value: (value.2 as u64) | ((value.3 as u64) << 32),
217            }),
218            0x12 => Ok(Self::Float {
219                dst: value.1,
220                value: f64::from_bits((value.2 as u64) | ((value.3 as u64) << 32)),
221            }),
222            0x13 => Ok(Self::Bool {
223                dst: value.1,
224                value: value.2 != 0,
225            }),
226
227            0x20 => Ok(Self::Move {
228                dst: value.1,
229                src: value.2,
230            }),
231            0x21 => Ok(Self::Field {
232                dst: value.1,
233                src: value.2,
234                field: value.3,
235            }),
236            0x22 => Ok(Self::Call {
237                addr: Locator::Address(value.1),
238                args: value.2,
239                dst: value.3
240            }),
241            0x23 => Ok(Self::Call {
242                addr: Locator::FromRegister(value.1),
243                args: value.2,
244                dst: value.3
245            }),
246
247            0x30..=0x3f => Ok(Self::Binary {
248                op: BinaryOperation::try_from(value.0 - 0x20)
249                    .map_err(|_| ByteCodeError::InvalidBinaryOperation(value.0 - 0x20))?,
250                dst: value.1,
251                left: value.2,
252                right: value.3,
253            }),
254            0x40..=0x4f => Ok(Self::Unary {
255                op: UnaryOperation::try_from(value.0 - 0x20)
256                    .map_err(|_| ByteCodeError::InvalidUnaryOperation(value.0 - 0x30))?,
257                dst: value.1,
258                right: value.2,
259            }),
260            _ => Err(ByteCodeError::InvalidOperation),
261        }
262    }
263}
264
265#[derive(Debug, Clone, PartialEq)]
266pub struct Program {
267    pub strings: Vec<String>,
268    pub code: Vec<ByteCode>,
269}
270#[derive(Debug, Clone, PartialEq)]
271pub enum ProgramParseError {
272    InsufficiantBytes,
273    ByteCodeError(ByteCodeError)
274}
275impl Display for ProgramParseError {
276    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
277        match self {
278            ProgramParseError::InsufficiantBytes => write!(f, "insufficiant bytes"),
279            ProgramParseError::ByteCodeError(err) => err.fmt(f),
280        }
281    }
282}
283impl Error for ProgramParseError {}
284impl TryFrom<&[u8]> for Program {
285    type Error = ProgramParseError;
286    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
287        let mut bytes = value.iter();
288
289        let size = {
290            let (Some(n1), Some(n2), Some(n3), Some(n4)) = (bytes.next().copied(), bytes.next().copied(), bytes.next().copied(), bytes.next().copied()) else {
291                return Err(ProgramParseError::InsufficiantBytes);
292            };
293            u32::from_be_bytes([n1, n2, n3, n4])
294        };
295        let mut strings = Vec::with_capacity(size as usize);
296        for _ in 0..size {
297            let string_size = {
298                let (Some(n1), Some(n2), Some(n3), Some(n4)) = (bytes.next().copied(), bytes.next().copied(), bytes.next().copied(), bytes.next().copied()) else {
299                    return Err(ProgramParseError::InsufficiantBytes);
300                };
301                u32::from_be_bytes([n1, n2, n3, n4])
302            };
303            let mut string = String::new();
304            for _ in 0..string_size {
305                let Some(c) = bytes.next().copied() else {
306                    return Err(ProgramParseError::InsufficiantBytes);
307                };
308                string.push(c as char);
309            }
310            strings.push(string);
311        }
312
313        let mut code = vec![];
314        while let Some(instr) = bytes.next().copied() {
315            let arg1 = {
316                let (Some(n1), Some(n2), Some(n3), Some(n4)) = (bytes.next().copied(), bytes.next().copied(), bytes.next().copied(), bytes.next().copied()) else {
317                    return Err(ProgramParseError::InsufficiantBytes);
318                };
319                u32::from_be_bytes([n1, n2, n3, n4])
320            };
321            let arg2 = {
322                let (Some(n1), Some(n2), Some(n3), Some(n4)) = (bytes.next().copied(), bytes.next().copied(), bytes.next().copied(), bytes.next().copied()) else {
323                    return Err(ProgramParseError::InsufficiantBytes);
324                };
325                u32::from_be_bytes([n1, n2, n3, n4])
326            };
327            let arg3 = {
328                let (Some(n1), Some(n2), Some(n3), Some(n4)) = (bytes.next().copied(), bytes.next().copied(), bytes.next().copied(), bytes.next().copied()) else {
329                    return Err(ProgramParseError::InsufficiantBytes);
330                };
331                u32::from_be_bytes([n1, n2, n3, n4])
332            };
333            code.push(ByteCode::try_from((instr, arg1, arg2, arg3)).map_err(ProgramParseError::ByteCodeError)?);
334        }
335
336        Ok(Self { strings, code })
337    }
338}
339impl From<Program> for Vec<u8> {
340    fn from(program: Program) -> Self {
341        let mut bytes = vec![];
342
343        bytes.extend((program.strings.len() as u32).to_be_bytes());
344        for string in program.strings {
345            bytes.extend((string.len() as u32).to_be_bytes());
346            bytes.extend(string.chars().map(|c| c as u8));
347        }
348
349        for bytecode in program.code {
350            let (instr, arg1, arg2, arg3): Bytes = bytecode.into();
351            bytes.push(instr);
352            bytes.extend(arg1.to_be_bytes());
353            bytes.extend(arg2.to_be_bytes());
354            bytes.extend(arg3.to_be_bytes());
355        }
356
357        bytes
358    }
359}