use enum_downcast::EnumDowncast;
use num::ToPrimitive;
use crate::prelude::*;
pub mod values;
#[derive(Clone, Debug, PartialEq)]
pub struct Value {
pub value: ValueEnum,
pub span: Span,
}
impl Value {
pub fn new_value(value: ValueEnum, span: Span) -> Value {
Self { value, span }
}
pub fn new_ok_value(value: ValueEnum, span: Span) -> ValueResult {
ValueResult::Ok(Self { value, span })
}
pub fn new_err_value(value: ValueEnum, span: Span) -> ValueResult {
ValueResult::Err(Self { value, span })
}
pub fn expect_integer(&self) -> Result<i64> {
let number = self.expect::<values::Number>()?;
if number.value.fract() != 0.0 {
return Err(Error::new(
ErrorType::UnexpectedValue {
expected: vec!["Integer".to_string()],
actual: "Float".to_string(),
},
self.span.clone(),
));
}
Ok(number.value.to_i64().unwrap())
}
pub fn expect<T>(&self) -> Result<T>
where
ValueEnum: enum_downcast::AsVariant<T>,
T: Clone,
{
match self.value.enum_downcast_ref::<T>() {
Some(token) => Ok(token.clone()),
None => Err(Error::new(
{
let expected = std::any::type_name::<T>().split("::").last().unwrap();
let actual = self.value.name();
ErrorType::UnexpectedValue {
expected: vec![expected.to_string()],
actual,
}
},
self.span.clone(),
)),
}
}
}
#[derive(Clone, Debug)]
pub enum ValueResult {
Ok(Value),
Err(Value),
}
impl ValueResult {
pub fn ignore_error(&self) -> &Value {
match self {
ValueResult::Ok(value) | ValueResult::Err(value) => value,
}
}
pub fn expect_non_error(self) -> Result<Value> {
match self {
ValueResult::Ok(value) => Ok(value),
ValueResult::Err(Value { span, .. }) => {
Err(Error::new(ErrorType::ExpectedNonErrorValue, span))
}
}
}
pub fn inner_mut(&mut self) -> &mut Value {
match self {
ValueResult::Ok(value) | ValueResult::Err(value) => value,
}
}
}
impl std::fmt::Display for ValueResult {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ValueResult::Ok(value) => write!(f, "{}", value),
ValueResult::Err(value) => write!(f, "~{}", value),
}
}
}
pub trait ExpectValue {
fn expect_value(self, expected: &[&str], span: &Span) -> Result<Value>;
}
impl ExpectValue for Option<&Value> {
fn expect_value(self, expected: &[&str], span: &Span) -> Result<Value> {
match self {
Some(value) => Ok(value.clone()),
None => Err(Error::new(
ErrorType::UnexpectedValue {
expected: expected.iter().map(|v| v.to_string()).collect(),
actual: "EOF".to_string(),
},
span.clone(),
)),
}
}
}
pub trait ExpectArity {
fn expect_exact<const N: usize>(&self, span: &Span) -> Result<&[ValueResult; N], Error>;
fn expect_min<const N: usize>(
&self,
span: &Span,
) -> Result<([ValueResult; N], Vec<ValueResult>), Error>;
}
impl ExpectArity for Vec<ValueResult> {
fn expect_exact<const N: usize>(&self, span: &Span) -> Result<&[ValueResult; N], Error> {
if self.len() == N {
Ok(TryInto::<&[ValueResult; N]>::try_into(self.as_slice()).unwrap())
} else {
Err(Error::new(
ErrorType::UnexpectedAmountOfArguments {
expected: N,
actual: self.len(),
},
span.clone(),
))
}
}
fn expect_min<const N: usize>(
&self,
span: &Span,
) -> Result<([ValueResult; N], Vec<ValueResult>), Error> {
if self.len() >= N {
let mut has_minimum = false;
let mut min = vec![];
let mut rest = vec![];
for argument in self {
if !has_minimum {
min.push(argument.clone());
if min.len() == N {
has_minimum = true;
}
} else {
rest.push(argument.clone());
}
}
let min = TryInto::<&[ValueResult; N]>::try_into(min.as_slice()).unwrap();
Ok((min.clone(), rest))
} else {
Err(Error::new(
ErrorType::NotEnoughArguments {
expected: N,
actual: self.len(),
},
span.clone(),
))
}
}
}
impl std::fmt::Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.value)
}
}
#[derive(Clone, Debug, EnumDowncast, PartialEq)]
pub enum ValueEnum {
Number(values::Number),
Bool(values::Bool),
Nil(values::Nil),
Str(values::Str),
Range(values::Range),
Map(values::Map),
Callable(values::Callable),
}
impl ValueEnum {
pub fn name(&self) -> String {
use ValueEnum::*;
match self {
Number(_) => "Number",
Bool(_) => "Bool",
Nil(_) => "Nil",
Str(_) => "Str",
Range(_) => "Range",
Map(_) => "Map",
Callable(_) => "Callable",
}
.to_string()
}
pub fn get_iterator(&self, span: &Span) -> Result<Vec<Value>> {
match self {
ValueEnum::Range(values::Range::Closed { lhs, rhs }) => {
let mut out = vec![];
for i in { *lhs }..*rhs {
out.push(Value::new_value(
values::Number { value: i as f64 }.into(),
span.clone(),
));
}
Ok(out)
}
ValueEnum::Number(values::Number { value }) => {
let upper_bound = if value.fract() == 0.0 {
*value as i64
} else {
return Err(Error::new(
ErrorType::UnexpectedValue {
expected: vec!["Integer".to_string()],
actual: "Float".to_string(),
},
span.clone(),
));
};
let mut out = vec![];
for i in 0..upper_bound {
out.push(Value::new_value(
values::Number { value: i as f64 }.into(),
span.clone(),
));
}
Ok(out)
}
ValueEnum::Str(values::Str { value }) => {
let mut out = vec![];
for ch in value.chars() {
out.push(Value::new_value(
values::Str {
value: ch.to_string(),
}
.into(),
span.clone(),
));
}
Ok(out)
}
ValueEnum::Map(values::Map { values }) => {
let mut out = vec![];
for (key, value) in values {
out.push(Value::new_value(
values::Map {
values: vec![
(
values::Str {
value: "key".to_string(),
}
.into(),
key.clone(),
),
(
values::Str {
value: "value".to_string(),
}
.into(),
value.clone(),
),
],
}
.into(),
span.clone(),
));
}
Ok(out)
}
value => Err(Error::new(
ErrorType::CannotLoopOver(value.name()),
span.clone(),
)),
}
}
}
impl std::fmt::Display for ValueEnum {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use ValueEnum::*;
match self {
Number(number) => write!(f, "{}", number.value),
Bool(values::Bool { value: true }) => write!(f, "true"),
Bool(values::Bool { value: false }) => write!(f, "false"),
Nil(_) => write!(f, "nil"),
Str(string) => write!(f, "{}", string.value),
Range(range) => match range {
values::Range::Closed { rhs, lhs } => write!(f, "{}..{}", lhs, rhs),
values::Range::From { lhs } => write!(f, "{}..", lhs),
values::Range::To { rhs } => write!(f, "{}..", rhs),
},
Map(map) => {
let mut out = "| ".to_string();
for (key, value) in &map.values {
out.push_str(&format!("[{}] = {}, ", key, value));
}
out.pop();
out.pop();
write!(f, "{} |", out)
}
Callable(callable) => match callable {
values::Callable::User {
parameters,
body: _,
} => {
let mut out = "fn(".to_string();
for parameter in parameters {
out.push_str(&format!("{}, ", parameter.name()));
}
if !parameters.is_empty() {
out.pop();
out.pop();
};
out.push(')');
write!(f, "{}", out)
}
&values::Callable::Lib {
function: _,
can_be_error,
} => write!(
f,
"<builtin_callable>{}",
if can_be_error { "?" } else { "" }
),
},
}
}
}