mirror of
https://github.com/Noratrieb/m8db.git
synced 2026-01-14 15:25:06 +01:00
more types to fix bugs confusing line numbers and spans
This commit is contained in:
parent
1321903483
commit
9bdfa4f522
3 changed files with 153 additions and 32 deletions
44
src/db.rs
44
src/db.rs
|
|
@ -1,10 +1,10 @@
|
|||
use crate::stmt::{Code, Stmt};
|
||||
use crate::stmt::{Code, LineNumber, Span, Stmt};
|
||||
use std::io::Write;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct Vm<'a> {
|
||||
stmts: Vec<Stmt>,
|
||||
span: Vec<usize>,
|
||||
span: Vec<Span>,
|
||||
code_lines: Vec<&'a str>,
|
||||
pc: usize,
|
||||
registers: Vec<usize>,
|
||||
|
|
@ -53,6 +53,10 @@ impl Vm<'_> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn statement_at_span(&self, search_span: Span) -> Option<usize> {
|
||||
self.span.iter().position(|span| *span >= search_span)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
|
|
@ -122,8 +126,22 @@ fn debug_input(vm: &Vm) -> VmInstruction {
|
|||
"p" | "program" => print_program(vm),
|
||||
"h" | "?" | "help" => print_help(),
|
||||
"b" | "break" => match iter.next() {
|
||||
Some(num) => match num.parse() {
|
||||
Ok(num) => return VmInstruction::Break(num),
|
||||
Some(line_number) => match line_number.parse::<usize>() {
|
||||
Ok(line_number) => {
|
||||
let stmt_pos =
|
||||
match vm.statement_at_span(LineNumber(line_number).span()) {
|
||||
Some(pos) => pos,
|
||||
None => {
|
||||
println!(
|
||||
"Line number '{}' out of bounds for length {}.",
|
||||
line_number,
|
||||
vm.code_lines.len()
|
||||
);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
return VmInstruction::Break(stmt_pos);
|
||||
}
|
||||
Err(_) => println!("Invalid argument provided"),
|
||||
},
|
||||
None => print_breakpoints(vm),
|
||||
|
|
@ -175,17 +193,27 @@ fn print_registers(vm: &Vm) {
|
|||
fn print_program(vm: &Vm) {
|
||||
use std::cmp::min;
|
||||
|
||||
println!("Program: (pc = {}, line = {})", vm.pc, vm.span[vm.pc]);
|
||||
// todo rewrite all of this, it's terrible!
|
||||
|
||||
println!(
|
||||
"Program: (pc = {}, line = {})",
|
||||
vm.pc,
|
||||
vm.span
|
||||
.get(vm.pc)
|
||||
.cloned()
|
||||
.unwrap_or(Span(vm.span.len()))
|
||||
.line_number()
|
||||
);
|
||||
let lower_stmt = if vm.pc > 5 { vm.pc - 5 } else { 0 };
|
||||
let len = vm.stmts.len();
|
||||
let higher_stmt = if len < 5 { len } else { min(vm.pc + 5, len) };
|
||||
|
||||
let lower_code = vm.span[lower_stmt];
|
||||
let higher_code = vm.span[higher_stmt - 1];
|
||||
let lower_code = vm.span[lower_stmt].0;
|
||||
let higher_code = vm.span[higher_stmt - 1].0;
|
||||
|
||||
for line_index in lower_code..higher_code {
|
||||
let code_line = vm.code_lines[line_index];
|
||||
if line_index == vm.span[vm.pc] {
|
||||
if line_index == vm.span.get(vm.pc).cloned().unwrap_or(Span(vm.span.len())).0 {
|
||||
println!("> {} {}", line_index + 1, code_line)
|
||||
} else {
|
||||
println!("{} {}", line_index + 1, code_line);
|
||||
|
|
|
|||
116
src/stmt.rs
116
src/stmt.rs
|
|
@ -2,6 +2,26 @@ use std::collections::HashMap;
|
|||
use std::fmt::Formatter;
|
||||
use std::num::ParseIntError;
|
||||
|
||||
/// A span referencing the line where a statement came from. Starts at 0
|
||||
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
|
||||
pub struct Span(pub usize);
|
||||
|
||||
impl Span {
|
||||
pub fn line_number(&self) -> usize {
|
||||
self.0 + 1
|
||||
}
|
||||
}
|
||||
|
||||
/// A line number, starts at 1
|
||||
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
|
||||
pub struct LineNumber(pub usize);
|
||||
|
||||
impl LineNumber {
|
||||
pub fn span(&self) -> Span {
|
||||
Span(self.0 - 1)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum Stmt {
|
||||
Inc(usize),
|
||||
|
|
@ -11,18 +31,21 @@ pub enum Stmt {
|
|||
Stop,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Code<'a> {
|
||||
pub stmts: Vec<Stmt>,
|
||||
/// Has the same length as `stmts`, points to line numbers where the instructions come from
|
||||
pub span: Vec<usize>,
|
||||
pub span: Vec<Span>,
|
||||
pub code_lines: Vec<&'a str>,
|
||||
}
|
||||
|
||||
enum IrStmt<'a> {
|
||||
Inc(usize),
|
||||
Dec(usize),
|
||||
IsZero(usize, &'a str),
|
||||
Jump(&'a str),
|
||||
IsZeroLabel(usize, &'a str),
|
||||
IsZeroLine(usize, LineNumber),
|
||||
JumpLabel(&'a str),
|
||||
JumpLine(LineNumber),
|
||||
Label(&'a str),
|
||||
Stop,
|
||||
}
|
||||
|
|
@ -35,7 +58,7 @@ pub fn parse(text: &str) -> Result<Code, String> {
|
|||
|
||||
let code_lines = text.lines().collect::<Vec<_>>();
|
||||
|
||||
for (line_number, line) in code_lines.iter().enumerate() {
|
||||
for (line_index, line) in code_lines.iter().enumerate() {
|
||||
if line.split_whitespace().next().is_none() {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -46,33 +69,82 @@ pub fn parse(text: &str) -> Result<Code, String> {
|
|||
}
|
||||
Ok(stmt) => {
|
||||
statement_number += 1;
|
||||
statements.push((stmt, line_number));
|
||||
statements.push((stmt, Span(line_index)));
|
||||
}
|
||||
Err(msg) => return Err(format!("error on line {}: {}", line_number, msg)),
|
||||
Err(msg) => return Err(format!("error on line '{}': {}", line_index - 1, msg)),
|
||||
}
|
||||
}
|
||||
|
||||
let result: Result<Vec<(Stmt, usize)>, String> = statements
|
||||
let result: Result<Vec<(Stmt, Span)>, String> = statements
|
||||
.iter()
|
||||
.map(|(stmt, span)| match *stmt {
|
||||
IrStmt::Inc(r) => Ok((Stmt::Inc(r), *span)),
|
||||
IrStmt::Dec(r) => Ok((Stmt::Dec(r), *span)),
|
||||
IrStmt::IsZero(r, label) => Ok((
|
||||
IrStmt::IsZeroLine(r, line_number) => Ok((
|
||||
Stmt::IsZero(
|
||||
r,
|
||||
match labels.get(label) {
|
||||
Some(line) => *line,
|
||||
match statements
|
||||
.iter()
|
||||
.position(|(_, stmt_span)| stmt_span.line_number() == line_number.0)
|
||||
{
|
||||
Some(stmt_number) => stmt_number,
|
||||
None => {
|
||||
return Err(format!("Label '{}' not found on line {}", label, span))
|
||||
return Err(format!(
|
||||
"Referencing line '{}' on line '{}': {}, out of bounds",
|
||||
line_number.0,
|
||||
span.line_number(),
|
||||
code_lines[span.0]
|
||||
))
|
||||
}
|
||||
},
|
||||
),
|
||||
*span,
|
||||
)),
|
||||
IrStmt::Jump(label) => Ok((
|
||||
IrStmt::JumpLine(line_number) => Ok((
|
||||
Stmt::Jump(
|
||||
match statements
|
||||
.iter()
|
||||
.position(|(_, stmt_span)| stmt_span.line_number() == line_number.0)
|
||||
{
|
||||
Some(stmt_number) => stmt_number,
|
||||
None => {
|
||||
return Err(format!(
|
||||
"Referencing line '{}' on line '{}': {}, out of bounds",
|
||||
line_number.0,
|
||||
span.line_number(),
|
||||
code_lines[span.0]
|
||||
))
|
||||
}
|
||||
},
|
||||
),
|
||||
*span,
|
||||
)),
|
||||
IrStmt::IsZeroLabel(r, label) => Ok((
|
||||
Stmt::IsZero(
|
||||
r,
|
||||
match labels.get(label) {
|
||||
Some(line) => *line,
|
||||
None => {
|
||||
return Err(format!(
|
||||
"Label '{}' not found on line '{}'",
|
||||
label,
|
||||
span.line_number()
|
||||
))
|
||||
}
|
||||
},
|
||||
),
|
||||
*span,
|
||||
)),
|
||||
IrStmt::JumpLabel(label) => Ok((
|
||||
Stmt::Jump(match labels.get(label) {
|
||||
Some(line) => *line,
|
||||
None => return Err(format!("Label '{}' not found on line {}", label, span)),
|
||||
None => {
|
||||
return Err(format!(
|
||||
"Label '{}' not found on line {}",
|
||||
label,
|
||||
span.line_number()
|
||||
))
|
||||
}
|
||||
}),
|
||||
*span,
|
||||
)),
|
||||
|
|
@ -93,7 +165,7 @@ pub fn parse(text: &str) -> Result<Code, String> {
|
|||
|
||||
fn parse_line(line: &str) -> Result<IrStmt, String> {
|
||||
let no_register = || "No register provided".to_string();
|
||||
let no_label = || "No label provided".to_string();
|
||||
let no_label_or_line_number = || "No label or line number provided".to_string();
|
||||
let display_err = |parse_err: ParseIntError| parse_err.to_string();
|
||||
|
||||
let mut iter = line.split_whitespace();
|
||||
|
|
@ -122,12 +194,20 @@ fn parse_line(line: &str) -> Result<IrStmt, String> {
|
|||
.ok_or_else(no_register)?
|
||||
.parse()
|
||||
.map_err(display_err)?;
|
||||
let label = iter.next().ok_or_else(no_label)?;
|
||||
IrStmt::IsZero(register, label)
|
||||
let jump_target = iter.next().ok_or_else(no_label_or_line_number)?;
|
||||
if let Ok(line_number) = jump_target.parse::<usize>() {
|
||||
IrStmt::IsZeroLine(register, LineNumber(line_number))
|
||||
} else {
|
||||
IrStmt::IsZeroLabel(register, jump_target)
|
||||
}
|
||||
}
|
||||
"JUMP" => {
|
||||
let label = iter.next().ok_or_else(no_label)?;
|
||||
IrStmt::Jump(label)
|
||||
let jump_target = iter.next().ok_or_else(no_label_or_line_number)?;
|
||||
if let Ok(line_number) = jump_target.parse::<usize>() {
|
||||
IrStmt::JumpLine(LineNumber(line_number))
|
||||
} else {
|
||||
IrStmt::JumpLabel(jump_target)
|
||||
}
|
||||
}
|
||||
"STOP" => IrStmt::Stop,
|
||||
stmt => {
|
||||
|
|
|
|||
25
test.m8
25
test.m8
|
|
@ -1,7 +1,20 @@
|
|||
.test
|
||||
INC 1
|
||||
.start
|
||||
DEC 1
|
||||
IS_ZERO 1 end
|
||||
JUMP start
|
||||
.end
|
||||
STOP
|
||||
.test
|
||||
INC 2
|
||||
.test
|
||||
INC 3
|
||||
.test
|
||||
INC 4
|
||||
.test
|
||||
INC 5
|
||||
.test
|
||||
INC 6
|
||||
.test
|
||||
INC 7
|
||||
.test
|
||||
INC 8
|
||||
.test
|
||||
INC 9
|
||||
.test
|
||||
INC 10
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue