basic-lang 0.7.1

The BASIC programming language as it was in the 8-bit era.
Documentation
use super::Address;
use crate::error;
use crate::lang::{Error, LineNumber, MaxValue};
use std::convert::TryFrom;
use std::rc::Rc;

/// ## Runtime values for stack and variables

#[derive(Debug, Clone, PartialEq)]
pub enum Val {
    String(Rc<str>),
    Single(f32),
    Double(f64),
    Integer(i16),
    Return(Address),
    Next(Address),
}

impl std::fmt::Display for Val {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        use Val::*;
        let mut s = match self {
            String(s) => return write!(f, "{}", s),
            Integer(num) => format!("{}", num),
            Single(num) => {
                let s = format!("{}", num);
                if s.chars().filter(char::is_ascii_digit).count() > 9 {
                    format!("{:E}", num)
                } else {
                    format!("{}", num)
                }
            }
            Double(num) => {
                let s = format!("{}", num);
                if s.chars().filter(char::is_ascii_digit).count() > 17 {
                    format!("{:E}", num)
                } else {
                    format!("{}", num)
                }
            }
            Return(..) | Next(..) => {
                debug_assert!(false);
                return write!(f, "");
            }
        };
        if !s.starts_with('-') {
            s.insert(0, ' ');
        }
        write!(f, "{}", s)
    }
}

impl TryFrom<LineNumber> for Val {
    type Error = Error;
    fn try_from(line_number: LineNumber) -> std::result::Result<Self, Self::Error> {
        match line_number {
            Some(num) => Ok(Val::Single(num as f32)),
            None => Err(error!(UndefinedLine)),
        }
    }
}

impl TryFrom<Val> for LineNumber {
    type Error = Error;
    fn try_from(val: Val) -> std::result::Result<Self, Self::Error> {
        let num = u16::try_from(val)?;
        if num <= LineNumber::max_value() {
            Ok(Some(num))
        } else {
            Err(error!(UndefinedLine))
        }
    }
}

impl TryFrom<Val> for u16 {
    type Error = Error;
    fn try_from(val: Val) -> std::result::Result<Self, Self::Error> {
        match val {
            Val::Integer(num) => {
                if num >= 0 {
                    Ok(num as u16)
                } else {
                    Err(error!(Overflow))
                }
            }
            Val::Single(num) => {
                let num = num.floor();
                if num >= 0.0 && num <= u16::max_value() as f32 {
                    Ok(num as u16)
                } else {
                    Err(error!(Overflow))
                }
            }
            Val::Double(num) => {
                let num = num.floor();
                if num >= 0.0 && num <= u16::max_value() as f64 {
                    Ok(num as u16)
                } else {
                    Err(error!(Overflow))
                }
            }
            Val::String(_) | Val::Return(_) | Val::Next(..) => Err(error!(TypeMismatch)),
        }
    }
}

impl TryFrom<Val> for i16 {
    type Error = Error;
    fn try_from(val: Val) -> std::result::Result<Self, Self::Error> {
        match val {
            Val::Integer(num) => Ok(num),
            Val::Single(num) => {
                let num = num.floor();
                if num >= i16::min_value() as f32 && num <= i16::max_value() as f32 {
                    Ok(num as i16)
                } else {
                    Err(error!(Overflow))
                }
            }
            Val::Double(num) => {
                let num = num.floor();
                if num >= i16::min_value() as f64 && num <= i16::max_value() as f64 {
                    Ok(num as i16)
                } else {
                    Err(error!(Overflow))
                }
            }
            Val::String(_) | Val::Return(_) | Val::Next(..) => Err(error!(TypeMismatch)),
        }
    }
}

impl TryFrom<Val> for u32 {
    type Error = Error;
    fn try_from(val: Val) -> std::result::Result<Self, Self::Error> {
        match val {
            Val::Integer(num) => {
                if num >= 0 {
                    Ok(num as u32)
                } else {
                    Err(error!(Overflow))
                }
            }
            Val::Single(num) => {
                let num = num.floor();
                if num >= 0.0 && num <= u32::max_value() as f32 {
                    Ok(num as u32)
                } else {
                    Err(error!(Overflow))
                }
            }
            Val::Double(num) => {
                let num = num.floor();
                if num >= 0.0 && num <= u32::max_value() as f64 {
                    Ok(num as u32)
                } else {
                    Err(error!(Overflow))
                }
            }
            Val::String(_) | Val::Return(_) | Val::Next(..) => Err(error!(TypeMismatch)),
        }
    }
}

impl TryFrom<Val> for usize {
    type Error = Error;
    fn try_from(val: Val) -> std::result::Result<Self, Self::Error> {
        match val {
            Val::Integer(num) => {
                if num >= 0 {
                    Ok(num as usize)
                } else {
                    Err(error!(Overflow))
                }
            }
            Val::Single(num) => {
                let num = num.floor();
                if num >= 0.0 && num <= usize::max_value() as f32 {
                    Ok(num as usize)
                } else {
                    Err(error!(Overflow))
                }
            }
            Val::Double(num) => {
                let num = num.floor();
                if num >= 0.0 && num <= usize::max_value() as f64 {
                    Ok(num as usize)
                } else {
                    Err(error!(Overflow))
                }
            }
            Val::String(_) | Val::Return(_) | Val::Next(..) => Err(error!(TypeMismatch)),
        }
    }
}

impl TryFrom<Val> for f32 {
    type Error = Error;
    fn try_from(val: Val) -> std::result::Result<Self, Self::Error> {
        match val {
            Val::Integer(num) => Ok(num as f32),
            Val::Single(num) => Ok(num),
            Val::Double(num) => Ok(num as f32),
            Val::String(_) | Val::Return(_) | Val::Next(..) => Err(error!(TypeMismatch)),
        }
    }
}

impl TryFrom<Val> for f64 {
    type Error = Error;
    fn try_from(val: Val) -> std::result::Result<Self, Self::Error> {
        match val {
            Val::Integer(num) => Ok(num as f64),
            Val::Single(num) => Ok(num as f64),
            Val::Double(num) => Ok(num),
            Val::String(_) | Val::Return(_) | Val::Next(..) => Err(error!(TypeMismatch)),
        }
    }
}

impl TryFrom<Val> for Rc<str> {
    type Error = Error;
    fn try_from(val: Val) -> std::result::Result<Self, Self::Error> {
        match val {
            Val::String(s) => Ok(s),
            _ => Err(error!(TypeMismatch)),
        }
    }
}

impl From<&str> for Val {
    fn from(string: &str) -> Self {
        if string.starts_with('&') {
            let string = &string[1..];
            if string.starts_with('H') || string.starts_with('h') {
                if let Ok(num) = i16::from_str_radix(&string[1..], 16) {
                    return Val::Integer(num);
                }
            } else if let Ok(num) = i16::from_str_radix(&string, 8) {
                return Val::Integer(num);
            }
        }
        let mut s = String::from(string).replace("D", "E").replace("d", "e");
        match s.chars().last() {
            Some('!') | Some('#') | Some('%') => {
                s.pop();
            }
            _ => {}
        };
        if let Ok(num) = s.parse::<f64>() {
            Val::Double(num)
        } else {
            Val::String(string.into())
        }
    }
}

impl TryFrom<usize> for Val {
    type Error = Error;
    fn try_from(num: usize) -> std::result::Result<Self, Self::Error> {
        match i16::try_from(num) {
            Ok(len) => Ok(Val::Integer(len)),
            Err(_) => {
                debug_assert!(false, "LEN VAL TOO BIG");
                Err(error!(Overflow))
            }
        }
    }
}