use super::{op::*, Error, Value};
struct Stack<'a>(Vec<Value<'a>>);
impl<'a> Stack<'a> {
pub fn new() -> Self {
Self(Vec::new())
}
pub fn push(&mut self, v: impl Into<Value<'a>>) {
self.0.push(v.into())
}
pub fn pop(&mut self) -> Result<Value<'a>, Error> {
self.0.pop().ok_or(Error::StackUnderflow)
}
pub fn pop_int(&mut self) -> Result<i32, Error> {
match self.pop()? {
Value::Int(x) => Ok(x),
_ => Err(Error::InvalidOperandType),
}
}
pub fn pop_char(&mut self) -> Result<char, Error> {
match self.pop()? {
Value::Int(i) => char::try_from(i as u32).map_err(|_| Error::ValueOutOfRange),
_ => Err(Error::InvalidOperandType),
}
}
pub fn pop_bool(&mut self) -> Result<bool, Error> {
match self.pop()? {
Value::Int(x) => Ok(x != 0),
Value::Bytes(_) => Ok(true),
}
}
pub fn pop_string(&mut self) -> Result<&'a [u8], Error> {
match self.pop()? {
Value::Bytes(x) => Ok(x),
_ => Err(Error::InvalidOperandType),
}
}
}
#[derive(Default)]
struct Variables<'a> {
s: [Value<'a>; 26],
d: [Value<'a>; 26],
}
#[derive(Copy, Clone, Eq, PartialEq)]
enum IfState {
None,
SkipThen(usize),
SkipElse(usize),
}
pub struct State<'a> {
args: &'a [Value<'a>],
stack: Stack<'a>,
vars: Variables<'a>,
ansi: bool,
if_level: usize,
if_state: IfState,
saw_t: bool,
}
fn format_num(format: Format, num: i32) -> Vec<u8> {
let Format {
flags,
width,
precision,
spec,
} = format;
let fmt_base = match spec {
FormatSpec::Decimal => format!("{num}"),
FormatSpec::Octal => format!("{num:o}"),
FormatSpec::LowerHex => format!("{num:x}"),
FormatSpec::UpperHex => format!("{num:X}"),
FormatSpec::String => unreachable!(),
};
let mut out = Vec::new();
match spec {
FormatSpec::Octal if flags.hash => out.push(b'0'),
FormatSpec::LowerHex if flags.hash => out.extend_from_slice(b"0x"),
FormatSpec::UpperHex if flags.hash => out.extend_from_slice(b"0X"),
FormatSpec::Decimal => {
if num >= 0 {
if flags.plus {
out.push(b'+');
} else if flags.space {
out.push(b' ');
}
} else {
out.push(b'-')
}
}
FormatSpec::String => unreachable!(),
_ => {}
}
if let Some(p) = precision {
if p as usize > fmt_base.len() {
out.extend(std::iter::repeat(b'0').take(p as usize - fmt_base.len()));
}
}
if !(precision == Some(0) && num == 0) {
out.extend(fmt_base.into_bytes());
}
if let Some(w) = width {
if w as usize > out.len() {
let n = w as usize - out.len();
out.extend(std::iter::repeat(b' ').take(n));
if !flags.minus {
out.rotate_right(n);
}
}
}
out
}
fn format_str(format: Format, mut str: &[u8]) -> Vec<u8> {
let Format {
flags,
width,
precision,
..
} = format;
if let Some(p) = precision {
str = &str[..(std::cmp::min(p as usize, str.len()))];
}
let mut out = Vec::from(str);
if let Some(w) = width {
if w as usize > out.len() {
let n = w as usize - out.len();
out.extend(std::iter::repeat(b' ').take(n));
if !flags.minus {
out.rotate_right(n);
}
}
}
out
}
impl<'a> State<'a> {
pub fn new(args: &'a [Value<'a>]) -> Self {
Self {
args,
stack: Stack::new(),
vars: Variables::default(),
ansi: false,
if_level: 0,
if_state: IfState::None,
saw_t: false,
}
}
pub fn execute(&mut self, op: Op<'a>, out: &mut Vec<u8>) -> Result<(), Error> {
let State {
args,
stack,
vars,
ansi,
if_level,
if_state,
saw_t,
} = self;
if *if_state != IfState::None {
match op {
Op::IfStart => {
*if_level += 1;
}
Op::Else if *if_state == IfState::SkipThen(*if_level) => {
*saw_t = false;
*if_state = IfState::None;
}
Op::IfEnd => {
if *if_state == IfState::SkipThen(*if_level)
|| *if_state == IfState::SkipElse(*if_level)
{
*if_state = IfState::None;
}
*if_level -= 1;
}
_ => { }
}
return Ok(());
}
match op {
Op::OutputBytes(s) => {
out.extend_from_slice(s);
}
Op::OutputPercent => {
out.push(b'%');
}
Op::OutputFormatted(format @ Format { spec, .. }) => match spec {
FormatSpec::String => out.extend(format_str(format, stack.pop_string()?)),
_ => out.extend(format_num(format, stack.pop_int()?)),
},
Op::OutputChar => {
let c = stack.pop_char()?;
let mut b = [0; 4];
out.extend_from_slice(c.encode_utf8(&mut b).as_bytes());
}
Op::PushParameter(n) => {
let arg = args.get(n).copied().unwrap_or_default();
if *ansi && n < 2 {
stack.push(arg.increment());
} else {
stack.push(arg);
}
}
Op::SetDynamic(n) => {
vars.d[n] = stack.pop()?;
}
Op::GetDynamic(n) => {
stack.push(vars.d[n]);
}
Op::SetStatic(n) => {
vars.s[n] = stack.pop()?;
}
Op::GetStatic(n) => {
stack.push(vars.s[n]);
}
Op::PushChar(c) => {
stack.push(c);
}
Op::PushInt(n) => {
stack.push(n);
}
Op::PushStringLength => {
let l = stack.pop_string()?.len();
let l = i32::try_from(l).map_err(|_| Error::ValueOutOfRange)?;
stack.push(l);
}
Op::Add => {
let b = stack.pop_int()?;
let a = stack.pop_int()?;
stack.push(a + b);
}
Op::Sub => {
let b = stack.pop_int()?;
let a = stack.pop_int()?;
stack.push(a - b);
}
Op::Mul => {
let b = stack.pop_int()?;
let a = stack.pop_int()?;
stack.push(a * b);
}
Op::Div => {
let b = stack.pop_int()?;
let a = stack.pop_int()?;
if b == 0 {
return Err(Error::DivisionByZero);
}
stack.push(a / b);
}
Op::Mod => {
let b = stack.pop_int()?;
let a = stack.pop_int()?;
if b == 0 {
return Err(Error::DivisionByZero);
}
stack.push(a % b);
}
Op::BitAnd => {
let b = stack.pop_int()?;
let a = stack.pop_int()?;
stack.push(a & b);
}
Op::BitOr => {
let b = stack.pop_int()?;
let a = stack.pop_int()?;
stack.push(a | b);
}
Op::BitXor => {
let b = stack.pop_int()?;
let a = stack.pop_int()?;
stack.push(a ^ b);
}
Op::Eq => {
let b = stack.pop_int()?;
let a = stack.pop_int()?;
stack.push(a == b);
}
Op::Lt => {
let b = stack.pop_int()?;
let a = stack.pop_int()?;
stack.push(a < b);
}
Op::Gt => {
let b = stack.pop_int()?;
let a = stack.pop_int()?;
stack.push(a > b);
}
Op::BoolAnd => {
let b = stack.pop_int()?;
let a = stack.pop_int()?;
stack.push((a & b) != 0);
}
Op::BoolOr => {
let b = stack.pop_int()?;
let a = stack.pop_int()?;
stack.push((a | b) != 0);
}
Op::BitNot => {
let a = stack.pop_int()?;
stack.push(!a);
}
Op::BoolNot => {
let a = stack.pop_int()?;
stack.push(a == 0);
}
Op::AnsiIncrement => {
*ansi = true;
}
Op::IfStart => {
*if_level += 1;
}
Op::Then => {
if *if_level == 0 {
return Err(Error::BrokenConditional);
}
*saw_t = true;
if !stack.pop_bool()? {
*if_state = IfState::SkipThen(*if_level);
}
}
Op::Else => {
if *if_level == 0 || !*saw_t {
return Err(Error::BrokenConditional);
}
*saw_t = false;
*if_state = IfState::SkipElse(*if_level);
}
Op::IfEnd => {
if *if_level == 0 {
return Err(Error::BrokenConditional);
} else {
*if_level -= 1;
}
}
Op::End => {
if *if_level != 0 {
return Err(Error::BrokenConditional);
}
}
};
Ok(())
}
}