brainfuck interpreter
brainfuck cli rust
5

Configure Feed

Select the types of activity you want to include in your feed.

evaluator: migrate errors to miette's diagnostic derive macro

- implemented Arc to solve excessive cloning
- implemented NamedSource for richer errors
- updated tests
- bumped version to 0.8.0
- checked off ROADMAP milestone

digi.rip (Jun 9, 2026, 4:59 PM EDT) 3c2129dc a754d3b0

+184 -106
+1 -1
Cargo.lock
··· 338 338 339 339 [[package]] 340 340 name = "tachyon" 341 - version = "0.7.4" 341 + version = "0.8.0" 342 342 dependencies = [ 343 343 "clap", 344 344 "miette",
+1 -1
Cargo.toml
··· 1 1 [package] 2 2 name = "tachyon" 3 - version = "0.7.4" 3 + version = "0.8.0" 4 4 edition = "2024" 5 5 description = "brainfuck interpreter written in rust" 6 6
+1 -1
ROADMAP.md
··· 21 21 - `error`: crash on overflow/underflow 22 22 23 23 ## better diagnostics 24 - - [ ] **errors**: switch to `miette` for richer error display with source snippets 24 + - [x] **errors**: switch to `miette` for richer error display with source snippets 25 25 - [x] **tracing**: cli flag to print state of vm/tape after every instruction 26 26 27 27 ## visualization
+176 -100
src/evaluator.rs
··· 1 1 use crate::lexer::Token; 2 2 use clap::ValueEnum; 3 + use miette::{Diagnostic, NamedSource, SourceSpan}; 3 4 use std::fmt; 4 5 use std::io::Write; 6 + use std::sync::Arc; 5 7 use std::thread; 6 8 use std::time; 7 9 8 - #[derive(Debug)] 10 + #[derive(Debug, Diagnostic)] 9 11 pub enum RuntimeError { 10 - DataPointerOutsideTapeLength(usize, usize, usize), 11 - DataPointerNegative(usize, usize), 12 - CellUnderflow(usize, usize), 13 - CellOverflow(usize, usize), 14 - InvalidToken(u8, usize, usize), 15 - IoReadError(std::io::Error), 16 - IoWriteError(std::io::Error), 12 + #[diagnostic( 13 + code(tachyon::runtime::data_pointer_oob_right), 14 + help("remove the '>' instruction that triggers this") 15 + )] 16 + DataPointerOutsideTapeLength { 17 + #[source_code] 18 + src: NamedSource<Arc<str>>, 19 + #[label("offending instruction here")] 20 + span: SourceSpan, 21 + }, 22 + 23 + #[diagnostic( 24 + code(tachyon::runtime::data_pointer_oob_left), 25 + help("remove the '<' that triggers this") 26 + )] 27 + DataPointerNegative { 28 + #[source_code] 29 + src: NamedSource<Arc<str>>, 30 + #[label("offending instruction here")] 31 + span: SourceSpan, 32 + }, 33 + 34 + #[diagnostic( 35 + code(tachyon::runtime::cell_underflow), 36 + help("remove the '-' that triggers this") 37 + )] 38 + CellUnderflow { 39 + #[source_code] 40 + src: NamedSource<Arc<str>>, 41 + #[label("offending instruction here")] 42 + span: SourceSpan, 43 + }, 44 + 45 + #[diagnostic( 46 + code(tachyon::runtime::cell_overflow), 47 + help("remove the '+' that triggers this") 48 + )] 49 + CellOverflow { 50 + #[source_code] 51 + src: NamedSource<Arc<str>>, 52 + #[label("offending instruction here")] 53 + span: SourceSpan, 54 + }, 55 + 56 + #[diagnostic( 57 + code(tachyon::runtime::invalid_token), 58 + help("this is a bug with the tokenizer. please report it!"), 59 + url("https://tangled.org/digi.rip/tachyon/issues") 60 + )] 61 + InvalidToken { 62 + #[source_code] 63 + src: NamedSource<Arc<str>>, 64 + #[label("offending token here")] 65 + span: SourceSpan, 66 + }, 67 + 68 + #[diagnostic( 69 + code(tachyon::runtime::io_read), 70 + help("check that stdin is available and not closed") 71 + )] 72 + IoReadError { err: std::io::Error }, 73 + 74 + #[diagnostic( 75 + code(tachyon::runtime::io_write), 76 + help("check that stdout is available and not closed") 77 + )] 78 + IoWriteError { err: std::io::Error }, 17 79 } 18 80 19 81 impl fmt::Display for RuntimeError { 20 82 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 21 83 match self { 22 - RuntimeError::DataPointerOutsideTapeLength(tape_length, line, col) => { 23 - write!( 24 - f, 25 - "Data pointer moved past position {} (end of tape) at line {}, col {}", 26 - tape_length, line, col 27 - ) 84 + RuntimeError::DataPointerOutsideTapeLength { .. } => { 85 + write!(f, "data pointer moved past end of tape") 28 86 } 29 - RuntimeError::DataPointerNegative(line, col) => { 30 - write!( 31 - f, 32 - "Data pointer went left of position 0 at line {}, col {}", 33 - line, col 34 - ) 87 + RuntimeError::DataPointerNegative { .. } => { 88 + write!(f, "data pointer moved past start of tape") 35 89 } 36 - RuntimeError::CellUnderflow(line, col) => { 37 - write!(f, "Cell value went below 0 at line {}, col {}", line, col) 90 + RuntimeError::CellUnderflow { .. } => { 91 + write!(f, "cell value went below 0") 38 92 } 39 - RuntimeError::CellOverflow(line, col) => { 40 - write!(f, "Cell value exceeded 255 at line {}, col {}", line, col) 93 + RuntimeError::CellOverflow { .. } => { 94 + write!(f, "cell value went over 255") 41 95 } 42 - RuntimeError::InvalidToken(token, line, col) => { 43 - write!( 44 - f, 45 - "Invalid token {} at line {}, col {}", 46 - char::from(*token), 47 - line, 48 - col 49 - ) 96 + RuntimeError::InvalidToken { .. } => { 97 + write!(f, "invalid token found") 50 98 } 51 - RuntimeError::IoReadError(err) => { 52 - write!(f, "Failed while reading stdin: {}", err) 99 + RuntimeError::IoReadError { err, .. } => { 100 + write!(f, "failed to read input: {}", err) 53 101 } 54 - RuntimeError::IoWriteError(err) => { 55 - write!(f, "Failed while writing stdout: {}", err) 102 + RuntimeError::IoWriteError { err, .. } => { 103 + write!(f, "failed to write output: {}", err) 56 104 } 57 105 } 58 106 } 59 107 } 60 108 61 - impl std::error::Error for RuntimeError { 62 - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { 63 - match self { 64 - RuntimeError::IoReadError(e) | RuntimeError::IoWriteError(e) => Some(e), 65 - _ => None, 66 - } 67 - } 68 - } 109 + impl std::error::Error for RuntimeError {} 69 110 70 111 #[derive(Clone, ValueEnum, Debug, PartialEq)] 71 112 pub enum OOBBehavior { ··· 80 121 } 81 122 82 123 pub struct VM { 124 + filename: Arc<str>, 125 + source: Arc<str>, 83 126 tape: Vec<u8>, 84 127 data_pointer: usize, 85 128 inst_pointer: usize, ··· 92 135 } 93 136 94 137 pub struct VMBuilder { 138 + filename: Arc<str>, 139 + source: Arc<str>, 95 140 tape: Vec<u8>, 96 141 data_oob: OOBBehavior, 97 142 cell_oob: OOBBehavior, ··· 104 149 impl VMBuilder { 105 150 fn new() -> Self { 106 151 VMBuilder { 152 + filename: Arc::from(""), 153 + source: Arc::from(""), 107 154 tape: vec![0; 30_000], 108 155 data_oob: OOBBehavior::Error, 109 156 cell_oob: OOBBehavior::Wrap, ··· 114 161 } 115 162 } 116 163 164 + pub fn source(mut self, filename: String, source: String) -> Self { 165 + self.filename = Arc::from(filename.as_str()); 166 + self.source = Arc::from(source.as_str()); 167 + self 168 + } 169 + 117 170 pub fn tape(mut self, length: usize) -> Self { 118 171 self.tape = vec![0; length]; 119 172 self ··· 151 204 152 205 pub fn build(self) -> VM { 153 206 VM { 207 + filename: self.filename, 208 + source: self.source, 154 209 tape: self.tape, 155 210 data_pointer: 0, 156 211 inst_pointer: 0, ··· 172 227 fn move_right(&mut self, current: &Token) -> Result<(), RuntimeError> { 173 228 if self.data_pointer + 1 >= self.tape.len() { 174 229 if self.data_oob == OOBBehavior::Error { 175 - return Err(RuntimeError::DataPointerOutsideTapeLength( 176 - self.tape.len(), 177 - current.line, 178 - current.col, 179 - )); 230 + return Err(RuntimeError::DataPointerOutsideTapeLength { 231 + src: NamedSource::new(Arc::clone(&self.filename), Arc::clone(&self.source)), 232 + span: (current.offset, 1).into(), 233 + }); 180 234 } else if self.data_oob == OOBBehavior::Wrap { 181 235 self.data_pointer = 0; 182 236 } ··· 190 244 fn move_left(&mut self, current: &Token) -> Result<(), RuntimeError> { 191 245 if self.data_pointer == 0 { 192 246 if self.data_oob == OOBBehavior::Error { 193 - return Err(RuntimeError::DataPointerNegative(current.line, current.col)); 247 + return Err(RuntimeError::DataPointerNegative { 248 + src: NamedSource::new(Arc::clone(&self.filename), Arc::clone(&self.source)), 249 + span: (current.offset, 1).into(), 250 + }); 194 251 } else if self.data_oob == OOBBehavior::Wrap { 195 252 self.data_pointer = self.tape.len() - 1; 196 253 } ··· 208 265 self.tape[self.data_pointer] = val; 209 266 } 210 267 None => { 211 - return Err(RuntimeError::CellOverflow(current.line, current.col)); 268 + return Err(RuntimeError::CellOverflow { 269 + src: NamedSource::new(Arc::clone(&self.filename), Arc::clone(&self.source)), 270 + span: (current.offset, 1).into(), 271 + }); 212 272 } 213 273 } 214 274 } else if self.cell_oob == OOBBehavior::Wrap { ··· 225 285 self.tape[self.data_pointer] = val; 226 286 } 227 287 None => { 228 - return Err(RuntimeError::CellUnderflow(current.line, current.col)); 288 + return Err(RuntimeError::CellUnderflow { 289 + src: NamedSource::new(Arc::clone(&self.filename), Arc::clone(&self.source)), 290 + span: (current.offset, 1).into(), 291 + }); 229 292 } 230 293 } 231 294 } else if self.cell_oob == OOBBehavior::Wrap { ··· 240 303 241 304 input 242 305 .read_exact(&mut buffer) 243 - .map_err(RuntimeError::IoReadError)?; 306 + .map_err(|e| RuntimeError::IoReadError { err: e })?; 244 307 self.tape[self.data_pointer] = buffer[0]; 245 308 246 309 loop { 247 310 match input.fill_buf() { 248 - Err(e) => return Err(RuntimeError::IoReadError(e)), 311 + Err(e) => { 312 + return Err(RuntimeError::IoReadError { err: e }); 313 + } 249 314 Ok([]) => break, 250 315 Ok(r) if r[0].is_ascii_alphanumeric() => input.consume(1), 251 316 Ok(_) => { ··· 261 326 fn write_ascii<W: std::io::Write>(&mut self, output: &mut W) -> Result<(), RuntimeError> { 262 327 output 263 328 .write_all(&[self.tape[self.data_pointer]]) 264 - .map_err(RuntimeError::IoWriteError)?; 329 + .map_err(|e| RuntimeError::IoWriteError { err: e })?; 265 330 266 331 Ok(()) 267 332 } ··· 280 345 Ok(0) => break, // eof 281 346 Ok(_) if single[0].is_ascii_digit() => buffer.push(single[0] as char), 282 347 Ok(_) => break, 283 - Err(e) => return Err(RuntimeError::IoReadError(e)), 348 + Err(e) => return Err(RuntimeError::IoReadError { err: e }), 284 349 } 285 350 } 286 351 ··· 289 354 loop { 290 355 match input.fill_buf() { 291 356 Err(e) => { 292 - return Err(RuntimeError::IoReadError(e)); 357 + return Err(RuntimeError::IoReadError { err: e }); 293 358 } 294 359 Ok([]) => break, 295 360 Ok(r) if r[0].is_ascii_digit() => input.consume(1), ··· 303 368 304 369 match buffer.parse::<u16>() { 305 370 Ok(val) if val > 255 && self.cell_oob == OOBBehavior::Error => { 306 - return Err(RuntimeError::CellOverflow(current.line, current.col)); 371 + return Err(RuntimeError::CellOverflow { 372 + src: NamedSource::new(Arc::clone(&self.filename), Arc::clone(&self.source)), 373 + span: (current.offset, 1).into(), 374 + }); 307 375 } 308 376 Ok(val) => self.tape[self.data_pointer] = val as u8, 309 377 Err(_) => self.tape[self.data_pointer] = 0, ··· 313 381 } 314 382 315 383 fn write_raw<W: std::io::Write>(&mut self, output: &mut W) -> Result<(), RuntimeError> { 316 - write!(output, "{} ", self.tape[self.data_pointer]).map_err(RuntimeError::IoWriteError)?; 384 + write!(output, "{} ", self.tape[self.data_pointer]) 385 + .map_err(|e| RuntimeError::IoWriteError { err: e })?; 317 386 318 387 Ok(()) 319 388 } ··· 392 461 } 393 462 } 394 463 395 - _ => Err(RuntimeError::InvalidToken( 396 - current.byte, 397 - current.line, 398 - current.col, 399 - ))?, 464 + _ => Err(RuntimeError::InvalidToken { 465 + src: NamedSource::new(Arc::clone(&self.filename), Arc::clone(&self.source)), 466 + span: (current.offset, 1).into(), 467 + })?, 400 468 } 401 469 402 470 if self.tracing { ··· 420 488 fn data_pointer() { 421 489 let source = "+>++>+++-<-<-"; 422 490 let tokens = tokenizer(source); 423 - let map = parse_brackets(&tokens, source).unwrap(); 491 + let map = parse_brackets(&tokens, "test", source).unwrap(); 424 492 let mut machine = VM::builder().tape(3).build(); 425 493 426 494 machine ··· 435 503 fn data_pointer_oob_left() { 436 504 let source = "<"; 437 505 let tokens = tokenizer(source); 438 - let map = parse_brackets(&tokens, source).unwrap(); 439 - let mut machine = VM::builder().tape(1).build(); 506 + let map = parse_brackets(&tokens, "test", source).unwrap(); 507 + let mut machine = VM::builder() 508 + .source("test".to_string(), source.to_string()) 509 + .tape(1) 510 + .build(); 440 511 let result = machine.run(&tokens, &map, std::io::empty(), std::io::empty()); 441 - 442 - eprintln!("{}", result.as_ref().unwrap_err()); 443 - assert!(result.is_err()); 512 + eprintln!("{:?}", miette::Report::new(result.unwrap_err())); 444 513 } 445 514 446 515 #[test] 447 516 fn data_pointer_oob_right() { 448 517 let source = ">>>>"; 449 518 let tokens = tokenizer(source); 450 - let map = parse_brackets(&tokens, source).unwrap(); 451 - let mut machine = VM::builder().tape(1).build(); 519 + let map = parse_brackets(&tokens, "test", source).unwrap(); 520 + let mut machine = VM::builder() 521 + .source("test".to_string(), source.to_string()) 522 + .tape(1) 523 + .build(); 452 524 let result = machine.run(&tokens, &map, std::io::empty(), std::io::empty()); 453 - 454 - eprintln!("{}", result.as_ref().unwrap_err()); 455 - assert!(result.is_err()); 525 + eprintln!("{:?}", miette::Report::new(result.unwrap_err())); 456 526 } 457 527 458 528 #[test] 459 529 fn increment() { 460 530 let source = "++++"; 461 531 let tokens = tokenizer(source); 462 - let map = parse_brackets(&tokens, source).unwrap(); 532 + let map = parse_brackets(&tokens, "test", source).unwrap(); 463 533 let mut machine = VM::builder().tape(1).build(); 464 534 465 535 machine ··· 472 542 fn decrement() { 473 543 let source = "++++---"; 474 544 let tokens = tokenizer(source); 475 - let map = parse_brackets(&tokens, source).unwrap(); 545 + let map = parse_brackets(&tokens, "test", source).unwrap(); 476 546 let mut machine = VM::builder().tape(1).build(); 477 547 478 548 machine ··· 488 558 // from 255 back to 0 489 559 let source = "->-+"; 490 560 let tokens = tokenizer(source); 491 - let map = parse_brackets(&tokens, source).unwrap(); 561 + let map = parse_brackets(&tokens, "test", source).unwrap(); 492 562 let mut machine = VM::builder().tape(2).build(); 493 563 494 564 machine ··· 502 572 fn input() { 503 573 let source = ","; 504 574 let tokens = tokenizer(source); 505 - let map = parse_brackets(&tokens, source).unwrap(); 575 + let map = parse_brackets(&tokens, "test", source).unwrap(); 506 576 let mut machine = VM::builder().tape(1).build(); 507 577 let fake_input = std::io::Cursor::new(b"A"); 508 578 ··· 516 586 fn input_overflow() { 517 587 let source = ","; 518 588 let tokens = tokenizer(source); 519 - let map = parse_brackets(&tokens, source).unwrap(); 589 + let map = parse_brackets(&tokens, "test", source).unwrap(); 520 590 let mut machine = VM::builder().tape(1).build(); 521 591 let fake_input = std::io::Cursor::new(b"AB"); 522 592 ··· 530 600 fn input_zero() { 531 601 let source = ","; 532 602 let tokens = tokenizer(source); 533 - let map = parse_brackets(&tokens, source).unwrap(); 603 + let map = parse_brackets(&tokens, "test", source).unwrap(); 534 604 let mut machine = VM::builder().tape(1).build(); 535 605 let fake_input = std::io::Cursor::new(b"0"); 536 606 ··· 544 614 fn output() { 545 615 let source = ",."; 546 616 let tokens = tokenizer(source); 547 - let map = parse_brackets(&tokens, source).unwrap(); 617 + let map = parse_brackets(&tokens, "test", source).unwrap(); 548 618 let mut machine = VM::builder().tape(1).build(); 549 619 let fake_input = std::io::Cursor::new(b"1"); 550 620 let mut capture_buffer: Vec<u8> = Vec::new(); ··· 559 629 fn looping() { 560 630 let source = "+++[>+<-]>"; 561 631 let tokens = tokenizer(source); 562 - let map = parse_brackets(&tokens, source).unwrap(); 632 + let map = parse_brackets(&tokens, "test", source).unwrap(); 563 633 let mut machine = VM::builder().tape(3).build(); 564 634 565 635 machine ··· 574 644 // making sure loop code isn't skipping 575 645 let source = "++[-]>++"; 576 646 let tokens = tokenizer(source); 577 - let map = parse_brackets(&tokens, source).unwrap(); 647 + let map = parse_brackets(&tokens, "test", source).unwrap(); 578 648 let mut machine = VM::builder().tape(2).build(); 579 649 580 650 machine ··· 589 659 fn hello_world() { 590 660 let source = "++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++."; 591 661 let tokens = tokenizer(source); 592 - let map = parse_brackets(&tokens, source).unwrap(); 662 + let map = parse_brackets(&tokens, "test", source).unwrap(); 593 663 let mut machine = VM::builder().build(); 594 664 let mut capture_buffer: Vec<u8> = Vec::new(); 595 665 ··· 604 674 fn repeat() { 605 675 let source = ",>,[<.>-]"; 606 676 let tokens = tokenizer(source); 607 - let map = parse_brackets(&tokens, source).unwrap(); 677 + let map = parse_brackets(&tokens, "test", source).unwrap(); 608 678 let mut machine = VM::builder().build(); 609 679 let mut capture_buffer: Vec<u8> = Vec::new(); 610 680 let fake_input = std::io::Cursor::new(b"A\nB"); ··· 621 691 fn nesting_loops() { 622 692 let source = "[[][]]"; 623 693 let tokens = tokenizer(source); 624 - let map = parse_brackets(&tokens, source).unwrap(); 694 + let map = parse_brackets(&tokens, "test", source).unwrap(); 625 695 let mut machine = VM::builder().tape(1).build(); 626 696 let result = machine.run(&tokens, &map, std::io::empty(), std::io::empty()); 627 697 ··· 632 702 fn data_pointer_wrap_right() { 633 703 let source = ">"; 634 704 let tokens = tokenizer(source); 635 - let map = parse_brackets(&tokens, source).unwrap(); 705 + let map = parse_brackets(&tokens, "test", source).unwrap(); 636 706 let mut machine = VM::builder().tape(1).data_oob(OOBBehavior::Wrap).build(); 637 707 let result = machine.run(&tokens, &map, std::io::empty(), std::io::empty()); 638 708 assert!(result.is_ok()); ··· 643 713 fn data_pointer_wrap_left() { 644 714 let source = "<"; 645 715 let tokens = tokenizer(source); 646 - let map = parse_brackets(&tokens, source).unwrap(); 716 + let map = parse_brackets(&tokens, "test", source).unwrap(); 647 717 let mut machine = VM::builder().tape(1).data_oob(OOBBehavior::Wrap).build(); 648 718 let result = machine.run(&tokens, &map, std::io::empty(), std::io::empty()); 649 719 assert!(result.is_ok()); ··· 654 724 fn cell_oob_error_decrement() { 655 725 let source = "-+"; 656 726 let tokens = tokenizer(source); 657 - let map = parse_brackets(&tokens, source).unwrap(); 658 - let mut machine = VM::builder().tape(1).cell_oob(OOBBehavior::Error).build(); 727 + let map = parse_brackets(&tokens, "test", source).unwrap(); 728 + let mut machine = VM::builder() 729 + .source("test".to_string(), source.to_string()) 730 + .tape(1) 731 + .cell_oob(OOBBehavior::Error) 732 + .build(); 659 733 let result = machine.run(&tokens, &map, std::io::empty(), std::io::empty()); 660 - eprintln!("{}", result.as_ref().unwrap_err()); 661 - assert!(result.is_err()); 734 + eprintln!("{:?}", miette::Report::new(result.unwrap_err())); 662 735 } 663 736 664 737 #[test] 665 738 fn cell_oob_error_increment() { 666 739 let source = &"+".repeat(256); 667 740 let tokens = tokenizer(source); 668 - let map = parse_brackets(&tokens, source).unwrap(); 669 - let mut machine = VM::builder().tape(1).cell_oob(OOBBehavior::Error).build(); 741 + let map = parse_brackets(&tokens, "test", source).unwrap(); 742 + let mut machine = VM::builder() 743 + .source("test".to_string(), source.to_string()) 744 + .tape(1) 745 + .cell_oob(OOBBehavior::Error) 746 + .build(); 670 747 let result = machine.run(&tokens, &map, std::io::empty(), std::io::empty()); 671 - eprintln!("{}", result.as_ref().unwrap_err()); 672 - assert!(result.is_err()); 748 + eprintln!("{:?}", miette::Report::new(result.unwrap_err())); 673 749 } 674 750 675 751 #[test] 676 752 fn read_raw() { 677 753 let source = ","; 678 754 let tokens = tokenizer(source); 679 - let map = parse_brackets(&tokens, source).unwrap(); 755 + let map = parse_brackets(&tokens, "test", source).unwrap(); 680 756 let mut machine = VM::builder().tape(1).input_type(IOFormat::Raw).build(); 681 757 let fake_input = std::io::Cursor::new("1"); 682 758 machine ··· 689 765 fn write_raw() { 690 766 let source = ",."; 691 767 let tokens = tokenizer(source); 692 - let map = parse_brackets(&tokens, source).unwrap(); 768 + let map = parse_brackets(&tokens, "test", source).unwrap(); 693 769 let mut machine = VM::builder() 694 770 .tape(1) 695 771 .input_type(IOFormat::Raw) ··· 710 786 fn read_raw_discard() { 711 787 let source = ",>,<.>."; 712 788 let tokens = tokenizer(source); 713 - let map = parse_brackets(&tokens, source).unwrap(); 789 + let map = parse_brackets(&tokens, "test", source).unwrap(); 714 790 let mut machine = VM::builder() 715 791 .input_type(IOFormat::Raw) 716 792 .output_type(IOFormat::Raw)
+5 -3
src/main.rs
··· 1 1 use clap::Parser; 2 + use miette::IntoDiagnostic; 2 3 3 4 mod evaluator; 4 5 mod lexer; ··· 51 52 delay: u64, 52 53 } 53 54 54 - fn main() -> Result<(), Box<dyn std::error::Error>> { 55 + fn main() -> miette::Result<()> { 55 56 let args = Args::parse(); 56 - let content = std::fs::read_to_string(args.path)?; 57 + let content = std::fs::read_to_string(&args.path).into_diagnostic()?; 57 58 let tokens = tokenizer(&content); 58 - let map = parse_brackets(&tokens, &content)?; 59 + let map = parse_brackets(&tokens, &args.path, &content)?; 59 60 60 61 let mut machine = VM::builder() 62 + .source(args.path, content) 61 63 .tape(args.length) 62 64 .data_oob(args.data_oob) 63 65 .cell_oob(args.cell_oob)