use std::cmp::Ordering;
use std::convert::TryInto;
use std::error::Error;
use std::fmt::{self, Display, Formatter};
use std::iter;
#[derive(Debug)]
pub struct RpnExpr(Vec<u8>);
impl RpnExpr {
pub fn from_bytes(bytes: Vec<u8>) -> Self {
Self(bytes)
}
pub fn bytes(&self) -> &[u8] {
&self.0
}
pub fn iter(&self) -> Iter {
Iter::new(self)
}
}
pub struct Iter<'a>(&'a RpnExpr, usize);
impl<'a> Iter<'a> {
fn new(expr: &'a RpnExpr) -> Self {
Self(expr, 0)
}
fn read_u32(&mut self) -> Option<u32> {
self.0.bytes().get(self.1..self.1 + 4).map(|bytes| {
let val = u32::from_le_bytes(bytes.try_into().unwrap());
self.1 += 4;
val
})
}
fn read_string(&mut self) -> Option<&'a [u8]> {
let start = self.1;
loop {
let c = *self.0.bytes().get(self.1)?;
self.1 += 1;
if c == 0 {
return Some(&self.0.bytes()[start..self.1]);
}
}
}
}
impl<'a> Iterator for Iter<'a> {
type Item = Result<RpnOp<'a>, RpnIterError>;
fn next(&mut self) -> Option<Self::Item> {
if self.1 == self.0.bytes().len() {
return None;
}
let err = Err(RpnIterError::new(self.1));
let operator = self.0.bytes()[self.1];
self.1 += 1;
Some(match operator {
0x00 => Ok(RpnOp::Add),
0x01 => Ok(RpnOp::Sub),
0x02 => Ok(RpnOp::Mul),
0x03 => Ok(RpnOp::Div),
0x04 => Ok(RpnOp::Mod),
0x05 => Ok(RpnOp::Neg),
0x06 => Ok(RpnOp::Pow),
0x10 => Ok(RpnOp::BinOr),
0x11 => Ok(RpnOp::BinAnd),
0x12 => Ok(RpnOp::Xor),
0x13 => Ok(RpnOp::Cpl),
0x21 => Ok(RpnOp::And),
0x22 => Ok(RpnOp::Or),
0x23 => Ok(RpnOp::Not),
0x30 => Ok(RpnOp::Eq),
0x31 => Ok(RpnOp::Neq),
0x32 => Ok(RpnOp::Gt),
0x33 => Ok(RpnOp::Lt),
0x34 => Ok(RpnOp::Gte),
0x35 => Ok(RpnOp::Lte),
0x40 => Ok(RpnOp::Lsh),
0x41 => Ok(RpnOp::Rsh),
0x50 => self.read_u32().map_or(err, |id| Ok(RpnOp::BankSym(id))),
0x51 => self
.read_string()
.map_or(err, |string| Ok(RpnOp::BankSect(string))),
0x52 => Ok(RpnOp::BankSelf),
0x53 => self
.read_string()
.map_or(err, |string| Ok(RpnOp::SizeofSect(string))),
0x54 => self
.read_string()
.map_or(err, |string| Ok(RpnOp::StartofSect(string))),
0x60 => Ok(RpnOp::HramCheck),
0x61 => Ok(RpnOp::RstCheck),
0x80 => self.read_u32().map_or(err, |id| Ok(RpnOp::Int(id))),
0x81 => self.read_u32().map_or(err, |id| Ok(RpnOp::Sym(id))),
_ => err,
})
}
fn size_hint(&self) -> (usize, Option<usize>) {
if self.1 == self.0.bytes().len() {
return (0, Some(0));
}
(1, Some(self.0.bytes().len() - self.1))
}
}
#[derive(Debug)]
pub struct RpnIterError(usize);
impl RpnIterError {
fn new(ofs: usize) -> Self {
Self(ofs)
}
pub fn offset(&self) -> usize {
self.0
}
}
impl Display for RpnIterError {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
write!(fmt, "Error parsing RPN expression at offset {}", self.0)
}
}
impl Error for RpnIterError {}
impl RpnExpr {
fn fmt_rpn(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
for (op, prefix) in self.iter().zip(iter::successors(Some(""), |_| Some(" "))) {
match op {
Err(err) => write!(fmt, "{}<RPN error@${:04x}>", prefix, err.offset())?,
Ok(op) => write!(fmt, "{}{}", prefix, op)?,
}
}
Ok(())
}
fn fmt_infix(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
let mut nodes = Vec::new();
let mut stack = Vec::new();
macro_rules! pop {
($parent:expr) => {
if let Some(tree) = stack.pop() {
tree
} else {
return write!(fmt, "<bad RPN expr, emptied stack>");
}
};
}
for op in self.iter() {
match op {
Err(err) => return write!(fmt, "<RPN error@${:04x}>", err.offset()),
Ok(op) => {
let next_id = nodes.len();
let children = match op.arity() {
Arity::Literal => RpnTreeNodeType::Literal,
Arity::Unary => {
let operand = pop!(next_id);
RpnTreeNodeType::Unary(operand)
}
Arity::Binary => {
let rhs = pop!(next_id);
let lhs = pop!(next_id);
RpnTreeNodeType::Binary { lhs, rhs }
}
};
nodes.push(RpnTreeNode::new(op, children));
stack.push(next_id);
}
}
}
if stack.len() != 1 {
return write!(fmt, "<bad RPN expr, finished with {} elems>", stack.len());
}
write_node(&nodes, stack[0], fmt)
}
}
impl Display for RpnExpr {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
if fmt.alternate() {
self.fmt_infix(fmt)
} else {
self.fmt_rpn(fmt)
}
}
}
#[derive(Debug, PartialEq)]
pub enum RpnOp<'a> {
Add,
Sub,
Mul,
Div,
Mod,
Neg,
Pow,
BinOr,
BinAnd,
Xor,
Cpl,
And,
Or,
Not,
Eq,
Neq,
Gt,
Lt,
Gte,
Lte,
Lsh,
Rsh,
BankSym(u32),
BankSect(&'a [u8]),
BankSelf,
SizeofSect(&'a [u8]),
StartofSect(&'a [u8]),
HramCheck,
RstCheck,
Int(u32),
Sym(u32),
}
pub enum Arity {
Literal,
Unary,
Binary,
}
impl RpnOp<'_> {
pub fn arity(&self) -> Arity {
use Arity::*;
use RpnOp::*;
match self {
Add => Binary,
Sub => Binary,
Mul => Binary,
Div => Binary,
Mod => Binary,
Neg => Unary,
Pow => Binary,
BinOr => Binary,
BinAnd => Binary,
Xor => Binary,
Cpl => Unary,
And => Binary,
Or => Binary,
Not => Unary,
Eq => Binary,
Neq => Binary,
Gt => Binary,
Lt => Binary,
Gte => Binary,
Lte => Binary,
Lsh => Binary,
Rsh => Binary,
BankSym(..) => Literal,
BankSect(..) => Literal,
BankSelf => Literal,
SizeofSect(..) => Literal,
StartofSect(..) => Literal,
HramCheck => Unary,
RstCheck => Unary,
Int(..) => Literal,
Sym(..) => Literal,
}
}
pub fn precedence(&self) -> u8 {
use RpnOp::*;
match self {
Pow => 6,
Mul | Div | Mod => 5,
Lsh | Rsh => 4,
BinAnd | BinOr | Xor => 3,
Add | Sub => 2,
Eq | Neq | Gt | Lt | Gte | Lte => 1,
And | Or => 0,
Neg | Cpl | Not | BankSym(..) | BankSect(..) | BankSelf | SizeofSect(..)
| StartofSect(..) | HramCheck | RstCheck | Int(..) | Sym(..) => unreachable!(),
}
}
pub fn is_associative(&self) -> bool {
use RpnOp::*;
match self {
Add | Mul | BinOr | BinAnd | Xor | And | Or => true,
Sub | Div | Mod | Pow | Eq | Neq | Gt | Lt | Gte | Lte | Lsh | Rsh => false,
Neg | Cpl | Not | BankSym(..) | BankSect(..) | BankSelf | SizeofSect(..)
| StartofSect(..) | HramCheck | RstCheck | Int(..) | Sym(..) => unreachable!(),
}
}
pub fn needs_parens(&self, parent: &RpnOp<'_>, is_left: bool) -> bool {
use Arity::*;
match self.arity() {
Literal | Unary => false,
Binary => {
use Ordering::*;
match parent.precedence().cmp(&self.precedence()) {
Less => false,
Greater => true,
Equal => {
!is_left && (self != parent || !self.is_associative())
}
}
}
}
}
}
impl Display for RpnOp<'_> {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
use RpnOp::*;
match self {
Add => write!(fmt, "+"),
Sub => write!(fmt, "-"),
Mul => write!(fmt, "*"),
Div => write!(fmt, "/"),
Mod => write!(fmt, "%"),
Neg => write!(fmt, "-()"),
Pow => write!(fmt, "**"),
BinOr => write!(fmt, "|"),
BinAnd => write!(fmt, "&"),
Xor => write!(fmt, "^"),
Cpl => write!(fmt, "~"),
And => write!(fmt, "&&"),
Or => write!(fmt, "||"),
Not => write!(fmt, "!"),
Eq => write!(fmt, "=="),
Neq => write!(fmt, "!="),
Gt => write!(fmt, ">"),
Lt => write!(fmt, "<"),
Gte => write!(fmt, ">="),
Lte => write!(fmt, "<="),
Lsh => write!(fmt, "<<"),
Rsh => write!(fmt, ">>"),
BankSym(id) => write!(fmt, "BANK(Sym#{})", id),
BankSect(name) => write!(fmt, "BANK(\"{}\")", String::from_utf8_lossy(&name)),
BankSelf => write!(fmt, "BANK(@)"),
SizeofSect(name) => write!(fmt, "SIZEOF(\"{}\")", String::from_utf8_lossy(&name)),
StartofSect(name) => write!(fmt, "STARTOF(\"{}\")", String::from_utf8_lossy(&name)),
HramCheck => write!(fmt, "HRAM?"),
RstCheck => write!(fmt, "RST?"),
Int(val) => write!(fmt, "${:04x}", val),
Sym(id) => write!(fmt, "Sym#{}", id),
}
}
}
#[derive(Debug)]
struct RpnTreeNode<'op> {
op: RpnOp<'op>,
children: RpnTreeNodeType,
}
impl<'op> RpnTreeNode<'op> {
pub fn new(op: RpnOp<'op>, children: RpnTreeNodeType) -> Self {
Self { op, children }
}
}
#[derive(Debug)]
enum RpnTreeNodeType {
Literal,
Unary(usize),
Binary { lhs: usize, rhs: usize },
}
fn write_node(nodes: &[RpnTreeNode], id: usize, fmt: &mut Formatter) -> Result<(), fmt::Error> {
use RpnOp::*;
use RpnTreeNodeType::*;
let node = &nodes[id];
let write_child_node = |id, fmt: &mut Formatter, is_left: bool| {
let child_node: &RpnTreeNode = &nodes[id];
let needs_parens = child_node.op.needs_parens(&node.op, is_left);
if needs_parens {
write!(fmt, "(")?;
}
write_node(nodes, id, fmt)?;
if needs_parens {
write!(fmt, ")")?;
}
Ok(())
};
match node.children {
Literal => write!(fmt, "{}", node.op),
Unary(operand) => match node.op {
HramCheck | RstCheck => {
write!(fmt, "{}(", node.op)?;
write_child_node(operand, fmt, true)?;
write!(fmt, ")")
}
_ => {
if matches!(node.op, Neg) {
write!(fmt, "-")?;
} else {
write!(fmt, "{}", node.op)?;
}
write_child_node(operand, fmt, true)
}
},
Binary { lhs, rhs } => {
write_child_node(lhs, fmt, true)?;
write!(fmt, " {} ", node.op)?;
write_child_node(rhs, fmt, false)
}
}
}