Assembler passes up to label placement
This commit is contained in:
+15
-1
@@ -25,7 +25,7 @@ pub enum Node<'a> {
|
|||||||
Expr(Box<Node<'a>>, Vec<(Operator, Node<'a>)>),
|
Expr(Box<Node<'a>>, Vec<(Operator, Node<'a>)>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Copy, Clone)]
|
||||||
pub struct Label<'a>(pub &'a str);
|
pub struct Label<'a>(pub &'a str);
|
||||||
|
|
||||||
impl<'a> Display for Label<'a> {
|
impl<'a> Display for Label<'a> {
|
||||||
@@ -43,3 +43,17 @@ pub enum VASMLine<'a> {
|
|||||||
Equ(Label<'a>, Node<'a>),
|
Equ(Label<'a>, Node<'a>),
|
||||||
LabelDef(Label<'a>),
|
LabelDef(Label<'a>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> VASMLine<'a> {
|
||||||
|
pub fn label(&self) -> Option<Label<'a>> {
|
||||||
|
match self {
|
||||||
|
VASMLine::Instruction(Some(lbl), _, _)
|
||||||
|
| VASMLine::Db(Some(lbl), _)
|
||||||
|
| VASMLine::StringDb(Some(lbl), _)
|
||||||
|
| VASMLine::Org(Some(lbl), _)
|
||||||
|
| VASMLine::Equ(lbl, _)
|
||||||
|
| VASMLine::LabelDef(lbl) => Some(*lbl),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,5 +4,6 @@ extern crate pest_derive;
|
|||||||
|
|
||||||
pub mod ast;
|
pub mod ast;
|
||||||
pub mod parse_error;
|
pub mod parse_error;
|
||||||
|
pub mod vasm_assembler;
|
||||||
pub mod vasm_evaluator;
|
pub mod vasm_evaluator;
|
||||||
pub mod vasm_parser;
|
pub mod vasm_parser;
|
||||||
|
|||||||
@@ -0,0 +1,275 @@
|
|||||||
|
use crate::ast::{Label, VASMLine};
|
||||||
|
use crate::vasm_evaluator::{eval, EvalError, Scope};
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
use std::fmt::{Display, Formatter};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum AssembleError<'a> {
|
||||||
|
EquResolveError(i32, &'a str, EvalError<'a>),
|
||||||
|
EquDuplicateError(i32, &'a str),
|
||||||
|
OrgResolveError(i32, EvalError<'a>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Display for AssembleError<'a> {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
AssembleError::EquResolveError(line, name, err) => {
|
||||||
|
write!(f, "Cannot resolve .equ {} on line {}: {}", name, line, err)
|
||||||
|
}
|
||||||
|
AssembleError::EquDuplicateError(line, name) => {
|
||||||
|
write!(f, "Duplicate .equ {} on line {}", name, line)
|
||||||
|
}
|
||||||
|
AssembleError::OrgResolveError(line, err) => {
|
||||||
|
write!(f, "Cannot resolve .org on line {}: {}", line, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This will solve all the .equ directives and return a symbol table of them.
|
||||||
|
/// .equ directives must be able to be solved in order, that is, in terms of
|
||||||
|
/// only preceding .equ directives. Anything else is an error.
|
||||||
|
pub fn solve_equs<'a>(lines: &[VASMLine<'a>]) -> Result<Scope<'a>, AssembleError<'a>> {
|
||||||
|
let mut scope: Scope = Scope::new();
|
||||||
|
let line_nums: BTreeMap<i32, i32> = BTreeMap::new();
|
||||||
|
for (line_idx, line) in lines.iter().enumerate() {
|
||||||
|
let line_num = (line_idx + 1) as i32;
|
||||||
|
if let VASMLine::Equ(Label(name), expr) = line {
|
||||||
|
let value = eval(expr, line_num as i32, &line_nums, &scope)
|
||||||
|
.map_err(|e| AssembleError::EquResolveError(line_num as i32, name, e))?;
|
||||||
|
|
||||||
|
if let Some(_old_value) = scope.insert(name, value) {
|
||||||
|
return Err(AssembleError::EquDuplicateError(line_num as i32, name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(scope)
|
||||||
|
}
|
||||||
|
|
||||||
|
type LineLengths = BTreeMap<i32, usize>;
|
||||||
|
type LineAddresses = BTreeMap<i32, i32>;
|
||||||
|
|
||||||
|
fn arg_length(val: i32) -> usize {
|
||||||
|
if val < 0 {
|
||||||
|
3
|
||||||
|
} else if val < 256 {
|
||||||
|
1
|
||||||
|
} else if val < 65536 {
|
||||||
|
2
|
||||||
|
} else {
|
||||||
|
3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This figures out the instruction lengths. We'll do this naively; if we can't
|
||||||
|
/// immediately tell that an instruction needs only a 0/1/2 byte argument (because it's
|
||||||
|
/// a constant, or a .equ that we've solved, or something) then we'll assume it's a
|
||||||
|
/// full 24-bit argument.
|
||||||
|
///
|
||||||
|
/// - Lines that don't represent output (.equ, .org, etc) have length 0
|
||||||
|
/// - .db directives are either strings (set aside the length of the string), or
|
||||||
|
/// numbers (set aside three bytes. If it's shorter than that it still may be a variable,
|
||||||
|
/// which might grow to be larger).
|
||||||
|
/// - Opcodes with no argument are 1 byte long.
|
||||||
|
/// - Opcodes with an argument, if that argument is a constant or decidable solely with
|
||||||
|
/// what we know right now (.equs), are however long that argument is. If we don't
|
||||||
|
/// know right now (based on a label, say) then we'll set aside the full 3 bytes (so it's
|
||||||
|
/// 4 bytes long, with the instruction byte).
|
||||||
|
pub fn measure_instructions<'a>(lines: &[VASMLine<'a>], scope: &Scope) -> LineLengths {
|
||||||
|
let line_nums: BTreeMap<i32, i32> = BTreeMap::new();
|
||||||
|
let mut lengths = LineLengths::new();
|
||||||
|
for (line_idx, line) in lines.iter().enumerate() {
|
||||||
|
let line_num = (line_idx + 1) as i32;
|
||||||
|
match line {
|
||||||
|
VASMLine::Instruction(_, _, None) => {
|
||||||
|
lengths.insert(line_num, 1);
|
||||||
|
}
|
||||||
|
VASMLine::Instruction(_, _, Some(node)) => {
|
||||||
|
let len = eval(node, line_num as i32, &line_nums, scope).map_or(3, arg_length);
|
||||||
|
lengths.insert(line_num, len + 1);
|
||||||
|
}
|
||||||
|
VASMLine::Db(_, _) => {
|
||||||
|
lengths.insert(line_num, 3);
|
||||||
|
}
|
||||||
|
VASMLine::StringDb(_, value) => {
|
||||||
|
lengths.insert(line_num, value.len());
|
||||||
|
}
|
||||||
|
VASMLine::Org(_, _) | VASMLine::Equ(_, _) | VASMLine::LabelDef(_) => {
|
||||||
|
lengths.insert(line_num, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lengths
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Time to start placing labels. The tricky part here is the .org directives, which can have
|
||||||
|
/// expressions as their arguments. We'll compromise a little bit and say that a .org directive
|
||||||
|
/// can only refer to labels that precede it, so, you can use .orgs to generate (say) a jump table
|
||||||
|
/// but still make it easy for me to figure out what refers to what.
|
||||||
|
///
|
||||||
|
/// We'll go through the lines, adding each one's length (calculated in measure_instructions) to it.
|
||||||
|
/// If it has a label, we'll store that label's new value to the scope.
|
||||||
|
///
|
||||||
|
/// But, we'll skip labels that come before .equs: that would make every .equ set to its address,
|
||||||
|
/// rather than the argument.
|
||||||
|
fn place_labels<'a>(
|
||||||
|
lines: &[VASMLine<'a>],
|
||||||
|
scope: Scope<'a>,
|
||||||
|
lengths: &LineLengths,
|
||||||
|
) -> Result<(LineAddresses, Scope<'a>), AssembleError<'a>> {
|
||||||
|
let mut scope = scope;
|
||||||
|
let mut address = 0;
|
||||||
|
let mut addresses = LineAddresses::new();
|
||||||
|
for (line_idx, line) in lines.iter().enumerate() {
|
||||||
|
let line_num = (line_idx + 1) as i32;
|
||||||
|
|
||||||
|
if let VASMLine::Org(_, expr) = line {
|
||||||
|
address = eval(expr, line_num, &addresses, &scope)
|
||||||
|
.map_err(|err| AssembleError::OrgResolveError(line_num, err))?;
|
||||||
|
addresses.insert(line_num, address);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(Label(label)) = line.label() {
|
||||||
|
if !scope.contains_key(label) {
|
||||||
|
scope.insert(label, address as i32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match line {
|
||||||
|
VASMLine::Org(_, _) => {}
|
||||||
|
_ => {
|
||||||
|
addresses.insert(line_num, address);
|
||||||
|
address += *lengths.get(&line_num).unwrap_or(&0) as i32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok((addresses, scope))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::AssembleError::*;
|
||||||
|
use super::EvalError::*;
|
||||||
|
use super::*;
|
||||||
|
use crate::ast::VASMLine;
|
||||||
|
use crate::vasm_parser::parse_vasm_line;
|
||||||
|
|
||||||
|
fn parse<'a>(lines: &'a [&str]) -> Vec<VASMLine<'a>> {
|
||||||
|
lines
|
||||||
|
.iter()
|
||||||
|
.map(|line| parse_vasm_line(*line).unwrap())
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn place_labels_pass<'a>(
|
||||||
|
lines: &'a [&str],
|
||||||
|
) -> Result<(LineAddresses, Scope<'a>), AssembleError<'a>> {
|
||||||
|
let lines = parse(lines);
|
||||||
|
let scope = solve_equs(&lines).unwrap();
|
||||||
|
let lengths = measure_instructions(&lines, &scope);
|
||||||
|
place_labels(&lines, scope, &lengths)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_equs() {
|
||||||
|
assert_eq!(
|
||||||
|
solve_equs(&parse(&["blah: .equ 5+3"])),
|
||||||
|
Ok([("blah", 8)].into())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
solve_equs(&parse(&["blah: .equ 5", "foo: .equ 3"])),
|
||||||
|
Ok([("blah", 5), ("foo", 3)].into())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
solve_equs(&parse(&["blah: .equ 5", "foo: .equ blah + 7"])),
|
||||||
|
Ok([("blah", 5), ("foo", 12)].into())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
solve_equs(&parse(&["add", "blah: .equ 5"])),
|
||||||
|
Ok([("blah", 5)].into())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_unsolvable_equs() {
|
||||||
|
assert_eq!(
|
||||||
|
solve_equs(&parse(&["blah: .equ 5", "foo: .equ banana"])),
|
||||||
|
Err(EquResolveError(2, "foo", MissingLabel("banana")))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
solve_equs(&parse(&["blah: .equ foo+3", "foo: .equ 7"])),
|
||||||
|
Err(EquResolveError(1, "blah", MissingLabel("foo")))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
solve_equs(&parse(&["blah: .equ 3", "blah: .equ 7"])),
|
||||||
|
Err(EquDuplicateError(2, "blah"))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_lengths() {
|
||||||
|
assert_eq!(
|
||||||
|
measure_instructions(
|
||||||
|
&parse(&["add", "add 1", "add 500", "add 70000", "add -7"]),
|
||||||
|
&[].into()
|
||||||
|
),
|
||||||
|
[(1, 1), (2, 2), (3, 3), (4, 4), (5, 4)].into()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
measure_instructions(&parse(&[".db 7", ".db \"hello\\0\""]), &[].into()),
|
||||||
|
[(1, 3), (2, 6)].into()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
measure_instructions(&parse(&[".org 256", "blah:", "foo: .equ 7"]), &[].into()),
|
||||||
|
[(1, 0), (2, 0), (3, 0)].into()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
measure_instructions(
|
||||||
|
&parse(&["add 2 + foo", "add 3 + blah", "jmpr @foo"]),
|
||||||
|
&[("blah", 300)].into()
|
||||||
|
),
|
||||||
|
[(1, 4), (2, 3), (3, 4)].into()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_place_labels() {
|
||||||
|
assert_eq!(
|
||||||
|
place_labels_pass(&["start: .org 256", "add", "dup"]),
|
||||||
|
Ok((
|
||||||
|
[(1, 256), (2, 256), (3, 257)].into(),
|
||||||
|
[("start", 256)].into()
|
||||||
|
))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
place_labels_pass(&["push 70000", "dup"]),
|
||||||
|
Ok(([(1, 0), (2, 4)].into(), [].into()))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
place_labels_pass(&["start: .equ 256", "blah: .org start + 4", "add"]),
|
||||||
|
Ok((
|
||||||
|
[(1, 0), (2, 260), (3, 260)].into(),
|
||||||
|
[("blah", 260), ("start", 256)].into()
|
||||||
|
))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
place_labels_pass(&["start: .org 256", "blah: .org start + 10", "add"]),
|
||||||
|
Ok((
|
||||||
|
[(1, 256), (2, 266), (3, 266)].into(),
|
||||||
|
[("blah", 266), ("start", 256)].into()
|
||||||
|
))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_unresolvable_orgs() {
|
||||||
|
assert_eq!(
|
||||||
|
place_labels_pass(&[".org 0xffffff - blah"]),
|
||||||
|
Err(OrgResolveError(1, EvalError::MissingLabel("blah")))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
place_labels_pass(&["blah: .org blah"]),
|
||||||
|
Err(OrgResolveError(1, EvalError::MissingLabel("blah")))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,14 +21,38 @@ impl<'a> Display for EvalError<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type Scope<'a> = BTreeMap<&'a str, i32>;
|
||||||
|
|
||||||
|
/// ## Evaluating expressions
|
||||||
|
/// Now that we have a parsed file, that file has a bunch of numeric symbols in it: labels,
|
||||||
|
/// .equ directives, that sort of thing. We need to resolve all of those to constant values
|
||||||
|
/// before we can generate code. So, first part of that is being able to evaluate expressions.
|
||||||
|
///
|
||||||
|
/// This evaluates an expression in the context of a symbol table, and returns either what the
|
||||||
|
/// expression evaluates to (a number) or an error (if it references a symbol not in
|
||||||
|
/// the given symbol table, or needs to know an address that's not calculated yet).
|
||||||
|
///
|
||||||
|
/// It's a depth-first recursive traversal of the expression AST:
|
||||||
|
///
|
||||||
|
/// - If the node is a number, then it returns that number.
|
||||||
|
/// - If the node is a string, it tries to look it up in the symbol table or explodes.
|
||||||
|
/// - If the node is a relative label, it tries to look it up in the symbol table, and then
|
||||||
|
/// subtracts a given start_address. If start_address is nil (as when we're solving .equs)
|
||||||
|
/// then it errors.
|
||||||
|
/// - If the node is an expr or term, then it evaluates the children: the children are a
|
||||||
|
/// sequence of evaluate-able nodes separated by operators. So first evaluate the left-most
|
||||||
|
/// child, then use the operator to combine it with the following one, and so on.
|
||||||
|
/// - If the node is an absolute or relative line offset, it attempts to look up the start of
|
||||||
|
/// the given line in the table of line start addresses and gives that address relatively or
|
||||||
|
/// absolutely.
|
||||||
pub fn eval<'a>(
|
pub fn eval<'a>(
|
||||||
node: Node<'a>,
|
node: &Node<'a>,
|
||||||
line_num: i32,
|
line_num: i32,
|
||||||
line_addresses: &BTreeMap<i32, i32>,
|
line_addresses: &BTreeMap<i32, i32>,
|
||||||
scope: &BTreeMap<&'a str, i32>,
|
scope: &Scope,
|
||||||
) -> Result<i32, EvalError<'a>> {
|
) -> Result<i32, EvalError<'a>> {
|
||||||
match node {
|
match node {
|
||||||
Node::Number(n) => Ok(n),
|
Node::Number(n) => Ok(*n),
|
||||||
Node::Label(label) => scope
|
Node::Label(label) => scope
|
||||||
.get(label)
|
.get(label)
|
||||||
.map_or_else(|| Err(EvalError::MissingLabel(label)), |val| Ok(*val)),
|
.map_or_else(|| Err(EvalError::MissingLabel(label)), |val| Ok(*val)),
|
||||||
@@ -60,10 +84,10 @@ pub fn eval<'a>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Node::Expr(car, cdr) => {
|
Node::Expr(car, cdr) => {
|
||||||
let car = eval(*car, line_num, line_addresses, scope);
|
let car = eval(car, line_num, line_addresses, scope);
|
||||||
if let Ok(mut acc) = car {
|
if let Ok(mut acc) = car {
|
||||||
for (op, node) in cdr {
|
for (op, node) in cdr {
|
||||||
if let Ok(rhs) = eval(node, line_num, line_addresses, scope) {
|
let rhs = eval(node, line_num, line_addresses, scope)?;
|
||||||
match op {
|
match op {
|
||||||
Operator::Add => acc += rhs,
|
Operator::Add => acc += rhs,
|
||||||
Operator::Sub => acc -= rhs,
|
Operator::Sub => acc -= rhs,
|
||||||
@@ -72,7 +96,6 @@ pub fn eval<'a>(
|
|||||||
Operator::Mod => acc %= rhs,
|
Operator::Mod => acc %= rhs,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Ok(acc)
|
Ok(acc)
|
||||||
} else {
|
} else {
|
||||||
car
|
car
|
||||||
@@ -111,7 +134,7 @@ mod test {
|
|||||||
line: &'a str,
|
line: &'a str,
|
||||||
) -> Result<i32, EvalError<'a>> {
|
) -> Result<i32, EvalError<'a>> {
|
||||||
if let Ok(VASMLine::Instruction(_, _, Some(arg))) = parse_vasm_line(line) {
|
if let Ok(VASMLine::Instruction(_, _, Some(arg))) = parse_vasm_line(line) {
|
||||||
eval(arg, 1, &line_addresses, &scope)
|
eval(&arg, 1, &line_addresses, &scope)
|
||||||
} else {
|
} else {
|
||||||
panic!("Failed to parse an instruction line with an argument")
|
panic!("Failed to parse an instruction line with an argument")
|
||||||
}
|
}
|
||||||
@@ -134,6 +157,10 @@ mod test {
|
|||||||
test_scope_eval([("apple", 5)].into(), "add apple + 7"),
|
test_scope_eval([("apple", 5)].into(), "add apple + 7"),
|
||||||
Ok(12)
|
Ok(12)
|
||||||
);
|
);
|
||||||
|
assert_eq!(
|
||||||
|
test_scope_eval([("apple", 5)].into(), "add 5 + apple"),
|
||||||
|
Ok(10)
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
test_scope_eval([("apple", 5), ("banana", 3)].into(), "add apple * banana"),
|
test_scope_eval([("apple", 5), ("banana", 3)].into(), "add apple * banana"),
|
||||||
Ok(15)
|
Ok(15)
|
||||||
@@ -142,6 +169,14 @@ mod test {
|
|||||||
test_scope_eval([("apple", 5)].into(), "add banana"),
|
test_scope_eval([("apple", 5)].into(), "add banana"),
|
||||||
Err(EvalError::MissingLabel("banana"))
|
Err(EvalError::MissingLabel("banana"))
|
||||||
);
|
);
|
||||||
|
assert_eq!(
|
||||||
|
test_scope_eval([].into(), "add apple + 2"),
|
||||||
|
Err(EvalError::MissingLabel("apple"))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
test_scope_eval([].into(), "add 2 + apple"),
|
||||||
|
Err(EvalError::MissingLabel("apple"))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -307,6 +307,32 @@ mod test {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_label_exprs() {
|
||||||
|
assert_eq!(
|
||||||
|
parse_vasm_line("add 2 + foo"),
|
||||||
|
Ok(VASMLine::Instruction(
|
||||||
|
None,
|
||||||
|
Add,
|
||||||
|
Some(Node::Expr(
|
||||||
|
Box::from(Node::Number(2)),
|
||||||
|
vec![(Operator::Add, Node::Label("foo"))]
|
||||||
|
))
|
||||||
|
))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
parse_vasm_line("add foo + 2"),
|
||||||
|
Ok(VASMLine::Instruction(
|
||||||
|
None,
|
||||||
|
Add,
|
||||||
|
Some(Node::Expr(
|
||||||
|
Box::from(Node::Label("foo")),
|
||||||
|
vec![(Operator::Add, Node::Number(2))]
|
||||||
|
))
|
||||||
|
))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_expr_labels() {
|
fn test_expr_labels() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|||||||
+131
-45
@@ -65,49 +65,135 @@ pub struct InvalidMnemonic<'a>(pub &'a str);
|
|||||||
impl Display for Opcode {
|
impl Display for Opcode {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Opcode::Nop => { write!(f, "nop") }
|
Opcode::Nop => {
|
||||||
Opcode::Add => { write!(f, "add") }
|
write!(f, "nop")
|
||||||
Opcode::Sub => { write!(f, "sub") }
|
}
|
||||||
Opcode::Mul => { write!(f, "mul") }
|
Opcode::Add => {
|
||||||
Opcode::Div => { write!(f, "div") }
|
write!(f, "add")
|
||||||
Opcode::Mod => { write!(f, "mod") }
|
}
|
||||||
Opcode::Rand => { write!(f, "rand") }
|
Opcode::Sub => {
|
||||||
Opcode::And => { write!(f, "and") }
|
write!(f, "sub")
|
||||||
Opcode::Or => { write!(f, "or") }
|
}
|
||||||
Opcode::Xor => { write!(f, "xor") }
|
Opcode::Mul => {
|
||||||
Opcode::Not => { write!(f, "not") }
|
write!(f, "mul")
|
||||||
Opcode::Gt => { write!(f, "gt") }
|
}
|
||||||
Opcode::Lt => { write!(f, "lt") }
|
Opcode::Div => {
|
||||||
Opcode::Agt => { write!(f, "agt") }
|
write!(f, "div")
|
||||||
Opcode::Alt => { write!(f, "alt") }
|
}
|
||||||
Opcode::Lshift => { write!(f, "lshift") }
|
Opcode::Mod => {
|
||||||
Opcode::Rshift => { write!(f, "rshift") }
|
write!(f, "mod")
|
||||||
Opcode::Arshift => { write!(f, "arshift") }
|
}
|
||||||
Opcode::Pop => { write!(f, "pop") }
|
Opcode::Rand => {
|
||||||
Opcode::Dup => { write!(f, "dup") }
|
write!(f, "rand")
|
||||||
Opcode::Swap => { write!(f, "swap") }
|
}
|
||||||
Opcode::Pick => { write!(f, "pick") }
|
Opcode::And => {
|
||||||
Opcode::Rot => { write!(f, "rot") }
|
write!(f, "and")
|
||||||
Opcode::Jmp => { write!(f, "jmp") }
|
}
|
||||||
Opcode::Jmpr => { write!(f, "jmpr") }
|
Opcode::Or => {
|
||||||
Opcode::Call => { write!(f, "call") }
|
write!(f, "or")
|
||||||
Opcode::Ret => { write!(f, "ret") }
|
}
|
||||||
Opcode::Brz => { write!(f, "brz") }
|
Opcode::Xor => {
|
||||||
Opcode::Brnz => { write!(f, "brnz") }
|
write!(f, "xor")
|
||||||
Opcode::Hlt => { write!(f, "hlt") }
|
}
|
||||||
Opcode::Load => { write!(f, "load") }
|
Opcode::Not => {
|
||||||
Opcode::Loadw => { write!(f, "loadw") }
|
write!(f, "not")
|
||||||
Opcode::Store => { write!(f, "store") }
|
}
|
||||||
Opcode::Storew => { write!(f, "storew") }
|
Opcode::Gt => {
|
||||||
Opcode::Inton => { write!(f, "inton") }
|
write!(f, "gt")
|
||||||
Opcode::Intoff => { write!(f, "intoff") }
|
}
|
||||||
Opcode::Setiv => { write!(f, "setiv") }
|
Opcode::Lt => {
|
||||||
Opcode::Sdp => { write!(f, "sdp") }
|
write!(f, "lt")
|
||||||
Opcode::Setsdp => { write!(f, "setsdp") }
|
}
|
||||||
Opcode::Pushr => { write!(f, "pushr") }
|
Opcode::Agt => {
|
||||||
Opcode::Popr => { write!(f, "popr") }
|
write!(f, "agt")
|
||||||
Opcode::Peekr => { write!(f, "peekr") }
|
}
|
||||||
Opcode::Debug => { write!(f, "debug") }
|
Opcode::Alt => {
|
||||||
|
write!(f, "alt")
|
||||||
|
}
|
||||||
|
Opcode::Lshift => {
|
||||||
|
write!(f, "lshift")
|
||||||
|
}
|
||||||
|
Opcode::Rshift => {
|
||||||
|
write!(f, "rshift")
|
||||||
|
}
|
||||||
|
Opcode::Arshift => {
|
||||||
|
write!(f, "arshift")
|
||||||
|
}
|
||||||
|
Opcode::Pop => {
|
||||||
|
write!(f, "pop")
|
||||||
|
}
|
||||||
|
Opcode::Dup => {
|
||||||
|
write!(f, "dup")
|
||||||
|
}
|
||||||
|
Opcode::Swap => {
|
||||||
|
write!(f, "swap")
|
||||||
|
}
|
||||||
|
Opcode::Pick => {
|
||||||
|
write!(f, "pick")
|
||||||
|
}
|
||||||
|
Opcode::Rot => {
|
||||||
|
write!(f, "rot")
|
||||||
|
}
|
||||||
|
Opcode::Jmp => {
|
||||||
|
write!(f, "jmp")
|
||||||
|
}
|
||||||
|
Opcode::Jmpr => {
|
||||||
|
write!(f, "jmpr")
|
||||||
|
}
|
||||||
|
Opcode::Call => {
|
||||||
|
write!(f, "call")
|
||||||
|
}
|
||||||
|
Opcode::Ret => {
|
||||||
|
write!(f, "ret")
|
||||||
|
}
|
||||||
|
Opcode::Brz => {
|
||||||
|
write!(f, "brz")
|
||||||
|
}
|
||||||
|
Opcode::Brnz => {
|
||||||
|
write!(f, "brnz")
|
||||||
|
}
|
||||||
|
Opcode::Hlt => {
|
||||||
|
write!(f, "hlt")
|
||||||
|
}
|
||||||
|
Opcode::Load => {
|
||||||
|
write!(f, "load")
|
||||||
|
}
|
||||||
|
Opcode::Loadw => {
|
||||||
|
write!(f, "loadw")
|
||||||
|
}
|
||||||
|
Opcode::Store => {
|
||||||
|
write!(f, "store")
|
||||||
|
}
|
||||||
|
Opcode::Storew => {
|
||||||
|
write!(f, "storew")
|
||||||
|
}
|
||||||
|
Opcode::Inton => {
|
||||||
|
write!(f, "inton")
|
||||||
|
}
|
||||||
|
Opcode::Intoff => {
|
||||||
|
write!(f, "intoff")
|
||||||
|
}
|
||||||
|
Opcode::Setiv => {
|
||||||
|
write!(f, "setiv")
|
||||||
|
}
|
||||||
|
Opcode::Sdp => {
|
||||||
|
write!(f, "sdp")
|
||||||
|
}
|
||||||
|
Opcode::Setsdp => {
|
||||||
|
write!(f, "setsdp")
|
||||||
|
}
|
||||||
|
Opcode::Pushr => {
|
||||||
|
write!(f, "pushr")
|
||||||
|
}
|
||||||
|
Opcode::Popr => {
|
||||||
|
write!(f, "popr")
|
||||||
|
}
|
||||||
|
Opcode::Peekr => {
|
||||||
|
write!(f, "peekr")
|
||||||
|
}
|
||||||
|
Opcode::Debug => {
|
||||||
|
write!(f, "debug")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -172,7 +258,7 @@ impl<'a> TryFrom<&'a str> for Opcode {
|
|||||||
fn try_from(value: &'a str) -> Result<Self, Self::Error> {
|
fn try_from(value: &'a str) -> Result<Self, Self::Error> {
|
||||||
use Opcode::*;
|
use Opcode::*;
|
||||||
Ok(match value {
|
Ok(match value {
|
||||||
"nop" => Nop,
|
"nop" | "push" => Nop,
|
||||||
"add" => Add,
|
"add" => Add,
|
||||||
"sub" => Sub,
|
"sub" => Sub,
|
||||||
"mul" => Mul,
|
"mul" => Mul,
|
||||||
@@ -215,7 +301,7 @@ impl<'a> TryFrom<&'a str> for Opcode {
|
|||||||
"popr" => Popr,
|
"popr" => Popr,
|
||||||
"peekr" => Peekr,
|
"peekr" => Peekr,
|
||||||
"debug" => Debug,
|
"debug" => Debug,
|
||||||
_ => return Err(InvalidMnemonic(value))
|
_ => return Err(InvalidMnemonic(value)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user