basic array functions

This commit is contained in:
2024-10-17 00:32:17 -04:00
parent 8c7993bacc
commit 9f64446687
4 changed files with 60 additions and 6 deletions

View File

@@ -18,6 +18,7 @@ pub enum RuntimeError {
NotAVariable(String), NotAVariable(String),
ParseFail(String, Type), ParseFail(String, Type),
TypeError(Type, Type), TypeError(Type, Type),
EmptyArray,
IO(io::Error), IO(io::Error),
} }
@@ -26,7 +27,7 @@ impl Display for RuntimeError {
match self { match self {
Self::ParseError(e) => write!(f, "Parser Error: {e}"), Self::ParseError(e) => write!(f, "Parser Error: {e}"),
Self::NoOverloadForTypes(op, values) Self::NoOverloadForTypes(op, values)
=> write!(f, "No overload of `{op}` exists for the operands `[{}]`", => write!(f, "No overload of `{op}` exists for the operands `{}`",
values.iter().map(|x| format!("{}({x})", x.get_type())).collect::<Vec<_>>().join(", ")), values.iter().map(|x| format!("{}({x})", x.get_type())).collect::<Vec<_>>().join(", ")),
Self::ImmutableError(ident) => write!(f, "`{ident}` already exists and cannot be redefined"), Self::ImmutableError(ident) => write!(f, "`{ident}` already exists and cannot be redefined"),
Self::VariableUndefined(ident) => write!(f, "variable `{ident}` was not defined"), Self::VariableUndefined(ident) => write!(f, "variable `{ident}` was not defined"),
@@ -36,6 +37,7 @@ impl Display for RuntimeError {
Self::ParseFail(s, t) => write!(f, "`\"{s}\"` couldn't be parsed into {}", t), Self::ParseFail(s, t) => write!(f, "`\"{s}\"` couldn't be parsed into {}", t),
Self::IO(e) => write!(f, "{e}"), Self::IO(e) => write!(f, "{e}"),
Self::TypeError(left, right) => write!(f, "expected type `{left}` but got type `{right}`"), Self::TypeError(left, right) => write!(f, "expected type `{left}` but got type `{right}`"),
Self::EmptyArray => write!(f, "attempt to access element from an empty array"),
} }
} }
} }
@@ -112,8 +114,28 @@ where
(Value::Int(x), Value::Float(y)) => Ok(Value::Float(x as f64 + y)), (Value::Int(x), Value::Float(y)) => Ok(Value::Float(x as f64 + y)),
(Value::Float(x), Value::Float(y)) => Ok(Value::Float(x + y)), (Value::Float(x), Value::Float(y)) => Ok(Value::Float(x + y)),
(Value::String(x), Value::String(y)) => Ok(Value::String(format!("{x}{y}"))), (Value::String(x), Value::String(y)) => Ok(Value::String(format!("{x}{y}"))),
(Value::Array(_, x), y) => Ok(Value::Array(Type::Any, [x, vec![y]].concat())), (Value::Array(t, x), y) => {
(x, Value::Array(_, y)) => Ok(Value::Array(Type::Any, [vec![x], y].concat())), let ytype = y.get_type();
if t != ytype {
return Err(RuntimeError::TypeError(t, ytype));
}
// NOTE: use y's type instead of the arrays type.
// an `empty` array has Any type, but any value will have a fixed type.
// this converts the empty array into a typed array.
Ok(Value::Array(ytype, [x, vec![y]].concat()))
},
(x, Value::Array(t, y)) => {
let xtype = x.get_type();
if t != xtype {
return Err(RuntimeError::TypeError(t, xtype));
}
// NOTE: read above
Ok(Value::Array(xtype, [vec![x], y].concat()))
},
(x, y) => Err(RuntimeError::NoOverloadForTypes("+".into(), vec![x, y])) (x, y) => Err(RuntimeError::NoOverloadForTypes("+".into(), vec![x, y]))
}, },
ParseTree::Sub(x, y) => match (self.exec(x, locals)?, self.exec(y, locals)?) { ParseTree::Sub(x, y) => match (self.exec(x, locals)?, self.exec(y, locals)?) {
@@ -371,6 +393,22 @@ where
} }
} }
} }
ParseTree::Head(x) => match self.exec(x, locals)? {
Value::Array(_, x) => Ok(x.first().ok_or(RuntimeError::EmptyArray)?.clone()),
t => Err(RuntimeError::NoOverloadForTypes("head".into(), vec![t]))
},
ParseTree::Tail(x) => match self.exec(x, locals)? {
Value::Array(t, x) => Ok(Value::Array(t, if x.len() > 0 { x[1..].to_vec() } else { vec![] })),
t => Err(RuntimeError::NoOverloadForTypes("tail".into(), vec![t]))
},
ParseTree::Init(x) => match self.exec(x, locals)? {
Value::Array(t, x) => Ok(Value::Array(t, if x.len() > 0 { x[..x.len() - 1].to_vec() } else { vec![] })),
t => Err(RuntimeError::NoOverloadForTypes("init".into(), vec![t]))
},
ParseTree::Fini(x) => match self.exec(x, locals)? {
Value::Array(_, x) => Ok(x.last().ok_or(RuntimeError::EmptyArray)?.clone()),
t => Err(RuntimeError::NoOverloadForTypes("fini".into(), vec![t]))
},
} }
} }
} }

View File

@@ -113,7 +113,7 @@ pub struct Function {
} }
impl Function { impl Function {
pub fn lambda(t: FunctionType, arg_names: Vec<String>, body: Option<Box<ParseTree>>) -> Self { fn lambda(t: FunctionType, arg_names: Vec<String>, body: Option<Box<ParseTree>>) -> Self {
Self { Self {
name: None, name: None,
t, t,
@@ -122,7 +122,7 @@ impl Function {
} }
} }
pub fn named(name: &str, t: FunctionType, arg_names: Option<Vec<String>>, body: Option<Box<ParseTree>>) -> Self { fn named(name: &str, t: FunctionType, arg_names: Option<Vec<String>>, body: Option<Box<ParseTree>>) -> Self {
Self { Self {
name: Some(name.to_string()), name: Some(name.to_string()),
t, t,

View File

@@ -70,6 +70,10 @@ pub(crate) enum ParseTree {
// Functional Operations // Functional Operations
Compose(Box<ParseTree>, Box<ParseTree>), Compose(Box<ParseTree>, Box<ParseTree>),
Id(Box<ParseTree>), Id(Box<ParseTree>),
Head(Box<ParseTree>),
Tail(Box<ParseTree>),
Init(Box<ParseTree>),
Fini(Box<ParseTree>),
// Branching // Branching
If(Box<ParseTree>, Box<ParseTree>), If(Box<ParseTree>, Box<ParseTree>),
@@ -262,6 +266,10 @@ impl ParseTree {
let name = Self::get_identifier(tokens.next())?; let name = Self::get_identifier(tokens.next())?;
Ok(ParseTree::NonCall(name)) Ok(ParseTree::NonCall(name))
}, },
Op::Head => one_arg!(Head, tokens, globals, locals),
Op::Tail => one_arg!(Tail, tokens, globals, locals),
Op::Init => one_arg!(Init, tokens, globals, locals),
Op::Fini => one_arg!(Fini, tokens, globals, locals),
op => Err(ParseError::UnwantedToken(Token::Operator(op))), op => Err(ParseError::UnwantedToken(Token::Operator(op))),
} }
} }

View File

@@ -78,6 +78,10 @@ pub(crate) enum Op {
And, And,
Or, Or,
NonCall, NonCall,
Head,
Tail,
Init,
Fini,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -118,6 +122,10 @@ impl Token {
"string" => Ok(Token::Operator(Op::StringCast)), "string" => Ok(Token::Operator(Op::StringCast)),
"print" => Ok(Token::Operator(Op::Print)), "print" => Ok(Token::Operator(Op::Print)),
"empty" => Ok(Token::Operator(Op::Empty)), "empty" => Ok(Token::Operator(Op::Empty)),
"head" => Ok(Token::Operator(Op::Head)),
"tail" => Ok(Token::Operator(Op::Tail)),
"init" => Ok(Token::Operator(Op::Init)),
"fini" => Ok(Token::Operator(Op::Fini)),
// Types // Types
"Any" => Ok(Token::Type(Type::Any)), "Any" => Ok(Token::Type(Type::Any)),
@@ -412,7 +420,7 @@ mod tests {
#[test] #[test]
fn uwu() { fn uwu() {
let program = ":. apply : f x f x apply ; x ** x 2 10"; let program = ":. map ?: f Any -> Any ?. x [Any] -> [Any] ?? bool x + f head x map 'f tail x empty map ;x ** x 2 [1 2 3 4 5]";
let tokens: Vec<Token> = Tokenizer::from_str(program).unwrap().collect::<Result<_, TokenizeError>>().unwrap(); let tokens: Vec<Token> = Tokenizer::from_str(program).unwrap().collect::<Result<_, TokenizeError>>().unwrap();