added label support

This commit is contained in:
nora 2021-09-18 21:02:06 +02:00
parent 13a0ac5dec
commit dba4a2e2ec
2 changed files with 78 additions and 36 deletions

View file

@ -1,3 +1,4 @@
use std::collections::HashMap;
use std::fmt::Formatter; use std::fmt::Formatter;
use std::num::ParseIntError; use std::num::ParseIntError;
@ -10,21 +11,67 @@ pub enum Stmt {
Stop, Stop,
} }
enum IrStmt<'a> {
Inc(usize),
Dec(usize),
IsZero(usize, &'a str),
Jump(&'a str),
Label(&'a str),
Stop,
}
pub fn parse(text: &str) -> Result<Vec<Stmt>, String> { pub fn parse(text: &str) -> Result<Vec<Stmt>, String> {
text.lines() let mut labels = HashMap::new();
.filter(|line| line.split_whitespace().next().is_some())
.map(parse_line) let mut statements = Vec::new();
let mut statement_number = 0;
for (line_number, line) in text.lines().enumerate() {
if line.split_whitespace().next().is_none() {
continue;
}
let result = parse_line(line);
match result {
Ok(IrStmt::Label(name)) => {
labels.insert(name, statement_number);
}
Ok(stmt) => {
statement_number += 1;
statements.push((stmt, line_number));
}
Err(msg) => return Err(format!("error on line {}: {}", line_number, msg)),
}
}
statements
.iter()
.map(|stmt| match stmt.0 {
IrStmt::Inc(r) => Ok(Stmt::Inc(r)),
IrStmt::Dec(r) => Ok(Stmt::Dec(r)),
IrStmt::IsZero(r, label) => Ok(Stmt::IsZero(
r,
match labels.get(label) {
Some(line) => *line,
None => return Err(format!("Label '{}' not found on line {}", label, stmt.1)),
},
)),
IrStmt::Jump(label) => Ok(Stmt::Jump(match labels.get(label) {
Some(line) => *line,
None => return Err(format!("Label '{}' not found on line {}", label, stmt.1)),
})),
IrStmt::Stop => Ok(Stmt::Stop),
IrStmt::Label(_) => unreachable!(),
})
.collect() .collect()
} }
fn parse_line(line: &str) -> Result<Stmt, String> { fn parse_line(line: &str) -> Result<IrStmt, String> {
let no_register = || "No register".to_string(); let no_register = || "No register provided".to_string();
let no_line_number = || "No line number".to_string(); let no_label = || "No label provided".to_string();
let empty_line = || "Empty line not allowed".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_ascii_whitespace(); let mut iter = line.split_whitespace();
let first = iter.next().ok_or_else(empty_line)?; let first = iter.next().expect("Empty lines filtered out");
Ok(match first { Ok(match first {
"INC" => { "INC" => {
@ -33,7 +80,7 @@ fn parse_line(line: &str) -> Result<Stmt, String> {
.ok_or_else(no_register)? .ok_or_else(no_register)?
.parse() .parse()
.map_err(display_err)?; .map_err(display_err)?;
Stmt::Inc(register) IrStmt::Inc(register)
} }
"DEC" => { "DEC" => {
let register = iter let register = iter
@ -41,7 +88,7 @@ fn parse_line(line: &str) -> Result<Stmt, String> {
.ok_or_else(no_register)? .ok_or_else(no_register)?
.parse() .parse()
.map_err(display_err)?; .map_err(display_err)?;
Stmt::Dec(register) IrStmt::Dec(register)
} }
"IS_ZERO" => { "IS_ZERO" => {
let register = iter let register = iter
@ -49,23 +96,21 @@ fn parse_line(line: &str) -> Result<Stmt, String> {
.ok_or_else(no_register)? .ok_or_else(no_register)?
.parse() .parse()
.map_err(display_err)?; .map_err(display_err)?;
let line_number = iter let label = iter.next().ok_or_else(no_label)?;
.next() IrStmt::IsZero(register, label)
.ok_or_else(no_line_number)?
.parse()
.map_err(display_err)?;
Stmt::IsZero(register, line_number)
} }
"JUMP" => { "JUMP" => {
let line_number = iter let label = iter.next().ok_or_else(no_label)?;
.next() IrStmt::Jump(label)
.ok_or_else(no_line_number)? }
.parse() "STOP" => IrStmt::Stop,
.map_err(display_err)?; stmt => {
Stmt::Jump(line_number) if stmt.starts_with('.') {
IrStmt::Label(&stmt[1..])
} else {
return Err(format!("Illegal instruction: '{}'", stmt));
}
} }
"STOP" => Stmt::Stop,
stmt => return Err(format!("Illegal instruction: '{}'", stmt)),
}) })
} }

19
test.m8
View file

@ -1,11 +1,8 @@
INC 1 INC 0
INC 1 INC 0
INC 1 JUMP end
INC 1 DEC 0
INC 1 DEC 0
INC 1 DEC 0
INC 1 .end
INC 1 STOP
INC 1
INC 1
INC 1