1#[derive(Debug, PartialEq)]
32pub enum TasmError {
33 UnknownMnemonic(String),
34 InvalidRegister(String),
35 InvalidImmediate(String),
36 UndefinedLabel(String),
37 MissingOperand { mnemonic: String, expected: usize, got: usize },
38}
39
40impl std::fmt::Display for TasmError {
41 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42 match self {
43 TasmError::UnknownMnemonic(m) => write!(f, "Unknown mnemonic: {}", m),
44 TasmError::InvalidRegister(r) => write!(f, "Invalid register: {}", r),
45 TasmError::InvalidImmediate(v) => write!(f, "Invalid immediate: {}", v),
46 TasmError::UndefinedLabel(l) => write!(f, "Undefined label: {}", l),
47 TasmError::MissingOperand { mnemonic, expected, got } =>
48 write!(f, "{}: expected {} operands, got {}", mnemonic, expected, got),
49 }
50 }
51}
52
53const OP_THALT: u8 = 0x00;
57const OP_TPUSH: u8 = 0x01;
58const OP_TADD: u8 = 0x02;
59const OP_TMUL: u8 = 0x03;
60const OP_TNEG: u8 = 0x04;
61const OP_TJMP: u8 = 0x0b;
62const OP_TJMP_ZERO: u8 = 0x06;
63const OP_TJMP_NEG: u8 = 0x05; const OP_TJMP_POS: u8 = 0x05;
65const OP_TLOAD: u8 = 0x09; const OP_TSTORE: u8 = 0x08; const OP_TCONS: u8 = 0x0e;
68
69const TRIT_NEG: u8 = 0x01; const TRIT_POS: u8 = 0x02; const TRIT_ZERO: u8 = 0x03; pub fn parse_trit_literal(s: &str) -> Result<i32, TasmError> {
81 if s.is_empty() {
82 return Err(TasmError::InvalidImmediate(s.to_string()));
83 }
84 let mut result = 0i32;
85 let mut power = 1i32;
86 for ch in s.chars().rev() {
87 let digit = match ch {
88 '0' => 0,
89 '1' => 1,
90 'T' | 't' => -1,
91 _ => return Err(TasmError::InvalidImmediate(s.to_string())),
92 };
93 result += digit * power;
94 power *= 3;
95 }
96 Ok(result)
97}
98
99fn trit_encode(v: i32) -> u8 {
101 match v.signum() {
102 -1 => TRIT_NEG,
103 1 => TRIT_POS,
104 _ => TRIT_ZERO,
105 }
106}
107
108fn parse_reg(s: &str) -> Result<u8, TasmError> {
110 let digits = s.trim_start_matches('r').trim_start_matches('R');
111 digits.parse::<u8>().map_err(|_| TasmError::InvalidRegister(s.to_string()))
112 .and_then(|n| if n < 27 { Ok(n) } else { Err(TasmError::InvalidRegister(s.to_string())) })
113}
114
115pub struct TasmAssembler {
121 pub bytecode: Vec<u8>,
123 labels: std::collections::HashMap<String, usize>,
125 patches: Vec<(usize, String)>,
127}
128
129impl TasmAssembler {
130 pub fn new() -> Self {
131 TasmAssembler {
132 bytecode: Vec::new(),
133 labels: std::collections::HashMap::new(),
134 patches: Vec::new(),
135 }
136 }
137
138 pub fn assemble(&mut self, source: &str) -> Result<Vec<u8>, TasmError> {
140 self.bytecode.clear();
141 self.labels.clear();
142 self.patches.clear();
143
144 for raw_line in source.lines() {
146 let line = raw_line.trim();
147 if line.is_empty() || line.starts_with(';') || line.starts_with("//") {
148 continue; }
150
151 let line = line.split(';').next().unwrap_or(line).trim();
153 let line = line.split("//").next().unwrap_or(line).trim();
154
155 if line.ends_with(':') {
157 let label = line.trim_end_matches(':').to_string();
158 self.labels.insert(label, self.bytecode.len());
159 continue;
160 }
161 if line.starts_with('.') {
162 let label = line[1..].to_string();
163 self.labels.insert(label, self.bytecode.len());
164 continue;
165 }
166
167 let tokens: Vec<&str> = line.split_whitespace()
169 .flat_map(|t| t.split(','))
170 .map(str::trim)
171 .filter(|t| !t.is_empty())
172 .collect();
173
174 if tokens.is_empty() { continue; }
175
176 self.emit_instruction(&tokens)?;
177 }
178
179 for (offset, label) in &self.patches {
181 let target = self.labels.get(label)
182 .ok_or_else(|| TasmError::UndefinedLabel(label.clone()))?;
183 let addr = *target as u16;
185 self.bytecode[*offset] = (addr & 0xFF) as u8;
186 self.bytecode[*offset + 1] = (addr >> 8) as u8;
187 }
188
189 Ok(self.bytecode.clone())
190 }
191
192 fn emit(&mut self, byte: u8) {
193 self.bytecode.push(byte);
194 }
195
196 fn require(tokens: &[&str], mnemonic: &str, count: usize) -> Result<(), TasmError> {
197 if tokens.len() - 1 < count {
198 return Err(TasmError::MissingOperand {
199 mnemonic: mnemonic.to_string(),
200 expected: count,
201 got: tokens.len() - 1,
202 });
203 }
204 Ok(())
205 }
206
207 fn emit_jump(&mut self, opcode: u8, label: &str) {
208 self.emit(opcode);
209 let patch_offset = self.bytecode.len();
211 self.emit(0x00);
212 self.emit(0x00);
213 self.patches.push((patch_offset, label.to_string()));
214 }
215
216 fn emit_instruction(&mut self, tokens: &[&str]) -> Result<(), TasmError> {
217 let mnemonic = tokens[0].to_uppercase();
218
219 match mnemonic.as_str() {
220 "NOP" => {
221 }
224
225 "HALT" => {
226 self.emit(OP_THALT);
227 }
228
229 "LOAD" => {
231 Self::require(tokens, "LOAD", 2)?;
232 let rd = parse_reg(tokens[1])?;
233 let val = parse_trit_literal(tokens[2])?;
234 self.emit(OP_TPUSH);
236 self.emit(trit_encode(val));
237 self.emit(OP_TSTORE);
238 self.emit(rd);
239 }
240
241 "MOV" => {
243 Self::require(tokens, "MOV", 2)?;
244 let rd = parse_reg(tokens[1])?;
245 let rs = parse_reg(tokens[2])?;
246 self.emit(OP_TLOAD);
247 self.emit(rs);
248 self.emit(OP_TSTORE);
249 self.emit(rd);
250 }
251
252 "ADD" => {
254 Self::require(tokens, "ADD", 3)?;
255 let rd = parse_reg(tokens[1])?;
256 let rs1 = parse_reg(tokens[2])?;
257 let rs2 = parse_reg(tokens[3])?;
258 self.emit(OP_TLOAD); self.emit(rs1);
259 self.emit(OP_TLOAD); self.emit(rs2);
260 self.emit(OP_TADD);
261 self.emit(OP_TSTORE); self.emit(rd);
262 }
263
264 "SUB" => {
266 Self::require(tokens, "SUB", 3)?;
267 let rd = parse_reg(tokens[1])?;
268 let rs1 = parse_reg(tokens[2])?;
269 let rs2 = parse_reg(tokens[3])?;
270 self.emit(OP_TLOAD); self.emit(rs1);
271 self.emit(OP_TLOAD); self.emit(rs2);
272 self.emit(OP_TNEG); self.emit(OP_TADD);
274 self.emit(OP_TSTORE); self.emit(rd);
275 }
276
277 "MUL" => {
279 Self::require(tokens, "MUL", 3)?;
280 let rd = parse_reg(tokens[1])?;
281 let rs1 = parse_reg(tokens[2])?;
282 let rs2 = parse_reg(tokens[3])?;
283 self.emit(OP_TLOAD); self.emit(rs1);
284 self.emit(OP_TLOAD); self.emit(rs2);
285 self.emit(OP_TMUL);
286 self.emit(OP_TSTORE); self.emit(rd);
287 }
288
289 "NEG" => {
291 Self::require(tokens, "NEG", 2)?;
292 let rd = parse_reg(tokens[1])?;
293 let rs = parse_reg(tokens[2])?;
294 self.emit(OP_TLOAD); self.emit(rs);
295 self.emit(OP_TNEG);
296 self.emit(OP_TSTORE); self.emit(rd);
297 }
298
299 "CONS" => {
301 Self::require(tokens, "CONS", 3)?;
302 let rd = parse_reg(tokens[1])?;
303 let rs1 = parse_reg(tokens[2])?;
304 let rs2 = parse_reg(tokens[3])?;
305 self.emit(OP_TLOAD); self.emit(rs1);
306 self.emit(OP_TLOAD); self.emit(rs2);
307 self.emit(OP_TCONS);
308 self.emit(OP_TSTORE); self.emit(rd);
309 }
310
311 "PUSH" => {
313 Self::require(tokens, "PUSH", 1)?;
314 let rs = parse_reg(tokens[1])?;
315 self.emit(OP_TLOAD); self.emit(rs);
316 }
317
318 "POP" => {
320 Self::require(tokens, "POP", 1)?;
321 let rd = parse_reg(tokens[1])?;
322 self.emit(OP_TSTORE); self.emit(rd);
323 }
324
325 "JMP" | "JUMP" => {
327 Self::require(tokens, "JMP", 1)?;
328 self.emit_jump(OP_TJMP, tokens[1]);
329 }
330
331 "BEQ" | "BZ" => {
333 Self::require(tokens, "BEQ", 2)?;
334 let rs = parse_reg(tokens[1])?;
335 self.emit(OP_TLOAD); self.emit(rs);
336 self.emit_jump(OP_TJMP_ZERO, tokens[2]);
337 }
338
339 "BLT" | "BN" => {
341 Self::require(tokens, "BLT", 2)?;
342 let rs = parse_reg(tokens[1])?;
343 self.emit(OP_TLOAD); self.emit(rs);
344 self.emit_jump(OP_TJMP_NEG, tokens[2]);
345 }
346
347 "BGT" | "BP" => {
349 Self::require(tokens, "BGT", 2)?;
350 let rs = parse_reg(tokens[1])?;
351 self.emit(OP_TLOAD); self.emit(rs);
352 self.emit_jump(OP_TJMP_POS, tokens[2]);
353 }
354
355 _ => return Err(TasmError::UnknownMnemonic(tokens[0].to_string())),
356 }
357
358 Ok(())
359 }
360}
361
362impl Default for TasmAssembler {
363 fn default() -> Self { Self::new() }
364}
365
366#[cfg(test)]
371mod tests {
372 use super::*;
373
374 #[test]
375 fn test_parse_trit_literal_simple() {
376 assert_eq!(parse_trit_literal("1"), Ok(1));
377 assert_eq!(parse_trit_literal("0"), Ok(0));
378 assert_eq!(parse_trit_literal("T"), Ok(-1));
379 }
380
381 #[test]
382 fn test_parse_trit_literal_multidigit() {
383 assert_eq!(parse_trit_literal("10T"), Ok(8));
385 assert_eq!(parse_trit_literal("1T1"), Ok(7));
387 assert_eq!(parse_trit_literal("TTT"), Ok(-13));
392 }
393
394 #[test]
395 fn test_parse_trit_literal_invalid() {
396 assert!(parse_trit_literal("2").is_err());
397 assert!(parse_trit_literal("").is_err());
398 }
399
400 #[test]
401 fn test_assemble_halt() {
402 let mut asm = TasmAssembler::new();
403 let code = asm.assemble("HALT").unwrap();
404 assert_eq!(code, vec![0x00]);
405 }
406
407 #[test]
408 fn test_assemble_load_pos() {
409 let mut asm = TasmAssembler::new();
410 let code = asm.assemble("LOAD r0, 1").unwrap();
412 assert_eq!(code[0], 0x01); assert_eq!(code[1], 0x02); assert_eq!(code[2], 0x08); assert_eq!(code[3], 0x00); }
417
418 #[test]
419 fn test_assemble_load_neg() {
420 let mut asm = TasmAssembler::new();
421 let code = asm.assemble("LOAD r1, T").unwrap();
422 assert_eq!(code[1], 0x01); assert_eq!(code[3], 0x01); }
425
426 #[test]
427 fn test_assemble_load_zero() {
428 let mut asm = TasmAssembler::new();
429 let code = asm.assemble("LOAD r2, 0").unwrap();
430 assert_eq!(code[1], 0x03); }
432
433 #[test]
434 fn test_assemble_add() {
435 let mut asm = TasmAssembler::new();
436 let code = asm.assemble("ADD r0, r1, r2\nHALT").unwrap();
437 assert!(!code.is_empty());
438 assert!(code.contains(&0x02)); assert!(code.last() == Some(&0x00)); }
441
442 #[test]
443 fn test_assemble_neg() {
444 let mut asm = TasmAssembler::new();
445 let code = asm.assemble("NEG r0, r1\nHALT").unwrap();
446 assert!(code.contains(&0x04)); }
448
449 #[test]
450 fn test_assemble_label_jump() {
451 let mut asm = TasmAssembler::new();
452 let src = "
453; infinite loop (test label resolution)
454loop:
455 LOAD r0, 1
456 JMP loop
457";
458 let code = asm.assemble(src).unwrap();
459 assert!(!code.is_empty());
460 assert!(code.contains(&0x0b)); }
463
464 #[test]
465 fn test_assemble_undefined_label() {
466 let mut asm = TasmAssembler::new();
467 let result = asm.assemble("JMP nonexistent");
468 assert!(matches!(result, Err(TasmError::UndefinedLabel(_))));
469 }
470
471 #[test]
472 fn test_assemble_unknown_mnemonic() {
473 let mut asm = TasmAssembler::new();
474 let result = asm.assemble("FLOATOP r0, r1");
475 assert!(matches!(result, Err(TasmError::UnknownMnemonic(_))));
476 }
477
478 #[test]
479 fn test_assemble_comments_ignored() {
480 let mut asm = TasmAssembler::new();
481 let code = asm.assemble(
482 "; this is a comment\n// also a comment\nHALT"
483 ).unwrap();
484 assert_eq!(code, vec![0x00]);
485 }
486
487 #[test]
488 fn test_assemble_full_program() {
489 let src = "
491 LOAD r0, 1 ; truth
492 LOAD r1, T ; conflict
493 ADD r2, r0, r1 ; hold (1 + -1 = 0)
494 HALT
495";
496 let mut asm = TasmAssembler::new();
497 let code = asm.assemble(src).unwrap();
498 assert!(!code.is_empty());
499 assert_eq!(*code.last().unwrap(), 0x00); }
501
502 #[test]
503 fn test_trit_encode() {
504 assert_eq!(trit_encode(-1), 0x01);
505 assert_eq!(trit_encode(0), 0x03);
506 assert_eq!(trit_encode(1), 0x02);
507 assert_eq!(trit_encode(5), 0x02); assert_eq!(trit_encode(-9), 0x01); }
510}