mirror of
https://github.com/Noratrieb/m8db.git
synced 2026-01-16 00:05:03 +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;
|
use std::io::Write;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct Vm<'a> {
|
struct Vm<'a> {
|
||||||
stmts: Vec<Stmt>,
|
stmts: Vec<Stmt>,
|
||||||
span: Vec<usize>,
|
span: Vec<Span>,
|
||||||
code_lines: Vec<&'a str>,
|
code_lines: Vec<&'a str>,
|
||||||
pc: usize,
|
pc: usize,
|
||||||
registers: Vec<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)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
|
@ -122,8 +126,22 @@ fn debug_input(vm: &Vm) -> VmInstruction {
|
||||||
"p" | "program" => print_program(vm),
|
"p" | "program" => print_program(vm),
|
||||||
"h" | "?" | "help" => print_help(),
|
"h" | "?" | "help" => print_help(),
|
||||||
"b" | "break" => match iter.next() {
|
"b" | "break" => match iter.next() {
|
||||||
Some(num) => match num.parse() {
|
Some(line_number) => match line_number.parse::<usize>() {
|
||||||
Ok(num) => return VmInstruction::Break(num),
|
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"),
|
Err(_) => println!("Invalid argument provided"),
|
||||||
},
|
},
|
||||||
None => print_breakpoints(vm),
|
None => print_breakpoints(vm),
|
||||||
|
|
@ -175,17 +193,27 @@ fn print_registers(vm: &Vm) {
|
||||||
fn print_program(vm: &Vm) {
|
fn print_program(vm: &Vm) {
|
||||||
use std::cmp::min;
|
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 lower_stmt = if vm.pc > 5 { vm.pc - 5 } else { 0 };
|
||||||
let len = vm.stmts.len();
|
let len = vm.stmts.len();
|
||||||
let higher_stmt = if len < 5 { len } else { min(vm.pc + 5, len) };
|
let higher_stmt = if len < 5 { len } else { min(vm.pc + 5, len) };
|
||||||
|
|
||||||
let lower_code = vm.span[lower_stmt];
|
let lower_code = vm.span[lower_stmt].0;
|
||||||
let higher_code = vm.span[higher_stmt - 1];
|
let higher_code = vm.span[higher_stmt - 1].0;
|
||||||
|
|
||||||
for line_index in lower_code..higher_code {
|
for line_index in lower_code..higher_code {
|
||||||
let code_line = vm.code_lines[line_index];
|
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)
|
println!("> {} {}", line_index + 1, code_line)
|
||||||
} else {
|
} else {
|
||||||
println!("{} {}", line_index + 1, code_line);
|
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::fmt::Formatter;
|
||||||
use std::num::ParseIntError;
|
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)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub enum Stmt {
|
pub enum Stmt {
|
||||||
Inc(usize),
|
Inc(usize),
|
||||||
|
|
@ -11,18 +31,21 @@ pub enum Stmt {
|
||||||
Stop,
|
Stop,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct Code<'a> {
|
pub struct Code<'a> {
|
||||||
pub stmts: Vec<Stmt>,
|
pub stmts: Vec<Stmt>,
|
||||||
/// Has the same length as `stmts`, points to line numbers where the instructions come from
|
/// 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>,
|
pub code_lines: Vec<&'a str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum IrStmt<'a> {
|
enum IrStmt<'a> {
|
||||||
Inc(usize),
|
Inc(usize),
|
||||||
Dec(usize),
|
Dec(usize),
|
||||||
IsZero(usize, &'a str),
|
IsZeroLabel(usize, &'a str),
|
||||||
Jump(&'a str),
|
IsZeroLine(usize, LineNumber),
|
||||||
|
JumpLabel(&'a str),
|
||||||
|
JumpLine(LineNumber),
|
||||||
Label(&'a str),
|
Label(&'a str),
|
||||||
Stop,
|
Stop,
|
||||||
}
|
}
|
||||||
|
|
@ -35,7 +58,7 @@ pub fn parse(text: &str) -> Result<Code, String> {
|
||||||
|
|
||||||
let code_lines = text.lines().collect::<Vec<_>>();
|
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() {
|
if line.split_whitespace().next().is_none() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -46,33 +69,82 @@ pub fn parse(text: &str) -> Result<Code, String> {
|
||||||
}
|
}
|
||||||
Ok(stmt) => {
|
Ok(stmt) => {
|
||||||
statement_number += 1;
|
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()
|
.iter()
|
||||||
.map(|(stmt, span)| match *stmt {
|
.map(|(stmt, span)| match *stmt {
|
||||||
IrStmt::Inc(r) => Ok((Stmt::Inc(r), *span)),
|
IrStmt::Inc(r) => Ok((Stmt::Inc(r), *span)),
|
||||||
IrStmt::Dec(r) => Ok((Stmt::Dec(r), *span)),
|
IrStmt::Dec(r) => Ok((Stmt::Dec(r), *span)),
|
||||||
IrStmt::IsZero(r, label) => Ok((
|
IrStmt::IsZeroLine(r, line_number) => Ok((
|
||||||
Stmt::IsZero(
|
Stmt::IsZero(
|
||||||
r,
|
r,
|
||||||
match labels.get(label) {
|
match statements
|
||||||
Some(line) => *line,
|
.iter()
|
||||||
|
.position(|(_, stmt_span)| stmt_span.line_number() == line_number.0)
|
||||||
|
{
|
||||||
|
Some(stmt_number) => stmt_number,
|
||||||
None => {
|
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,
|
*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) {
|
Stmt::Jump(match labels.get(label) {
|
||||||
Some(line) => *line,
|
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,
|
*span,
|
||||||
)),
|
)),
|
||||||
|
|
@ -93,7 +165,7 @@ pub fn parse(text: &str) -> Result<Code, String> {
|
||||||
|
|
||||||
fn parse_line(line: &str) -> Result<IrStmt, String> {
|
fn parse_line(line: &str) -> Result<IrStmt, String> {
|
||||||
let no_register = || "No register provided".to_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 display_err = |parse_err: ParseIntError| parse_err.to_string();
|
||||||
|
|
||||||
let mut iter = line.split_whitespace();
|
let mut iter = line.split_whitespace();
|
||||||
|
|
@ -122,12 +194,20 @@ fn parse_line(line: &str) -> Result<IrStmt, String> {
|
||||||
.ok_or_else(no_register)?
|
.ok_or_else(no_register)?
|
||||||
.parse()
|
.parse()
|
||||||
.map_err(display_err)?;
|
.map_err(display_err)?;
|
||||||
let label = iter.next().ok_or_else(no_label)?;
|
let jump_target = iter.next().ok_or_else(no_label_or_line_number)?;
|
||||||
IrStmt::IsZero(register, label)
|
if let Ok(line_number) = jump_target.parse::<usize>() {
|
||||||
|
IrStmt::IsZeroLine(register, LineNumber(line_number))
|
||||||
|
} else {
|
||||||
|
IrStmt::IsZeroLabel(register, jump_target)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"JUMP" => {
|
"JUMP" => {
|
||||||
let label = iter.next().ok_or_else(no_label)?;
|
let jump_target = iter.next().ok_or_else(no_label_or_line_number)?;
|
||||||
IrStmt::Jump(label)
|
if let Ok(line_number) = jump_target.parse::<usize>() {
|
||||||
|
IrStmt::JumpLine(LineNumber(line_number))
|
||||||
|
} else {
|
||||||
|
IrStmt::JumpLabel(jump_target)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"STOP" => IrStmt::Stop,
|
"STOP" => IrStmt::Stop,
|
||||||
stmt => {
|
stmt => {
|
||||||
|
|
|
||||||
25
test.m8
25
test.m8
|
|
@ -1,7 +1,20 @@
|
||||||
|
.test
|
||||||
INC 1
|
INC 1
|
||||||
.start
|
.test
|
||||||
DEC 1
|
INC 2
|
||||||
IS_ZERO 1 end
|
.test
|
||||||
JUMP start
|
INC 3
|
||||||
.end
|
.test
|
||||||
STOP
|
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