add lists

these will eventually become arrays when the type-system gets overhauled and makes it easier to implement new types and the functions and operators they support
This commit is contained in:
2024-10-15 15:05:12 -04:00
parent bc612f74ae
commit f571fac6f1
4 changed files with 57 additions and 15 deletions

View File

@@ -116,6 +116,8 @@ 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([x, vec![y]].concat())),
(x, Value::Array(y)) => Ok(Value::Array([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)?) {
@@ -216,7 +218,7 @@ where
self.exec(scope, &mut Cow::Borrowed(&locals)) self.exec(scope, &mut Cow::Borrowed(&locals))
} }
}, },
ParseTree::FunctionDefinition(ident, args, r, body, scope) => { ParseTree::FunctionDefinition(ident, args, body, scope) => {
let existing = locals.get(&ident).or(self.globals.get(&ident)).cloned(); let existing = locals.get(&ident).or(self.globals.get(&ident)).cloned();
match existing { match existing {
@@ -225,7 +227,7 @@ where
let locals = locals.to_mut(); let locals = locals.to_mut();
locals.insert(ident.clone(), Object::Function(Function { locals.insert(ident.clone(), Object::Function(Function {
decl: FunctionDeclaration { _name: ident.clone(), _r: r, args }, decl: FunctionDeclaration { _name: ident.clone(), args },
body: Some(body) body: Some(body)
})); }));
@@ -243,6 +245,7 @@ where
Value::Int(i) => i != 0, Value::Int(i) => i != 0,
Value::Bool(b) => b, Value::Bool(b) => b,
Value::String(s) => !s.is_empty(), Value::String(s) => !s.is_empty(),
Value::Array(vec) => !vec.is_empty(),
Value::Nil => false, Value::Nil => false,
} { } {
self.exec(body, locals) self.exec(body, locals)
@@ -254,6 +257,7 @@ where
Value::Int(i) => i != 0, Value::Int(i) => i != 0,
Value::Bool(b) => b, Value::Bool(b) => b,
Value::String(s) => !s.is_empty(), Value::String(s) => !s.is_empty(),
Value::Array(vec) => !vec.is_empty(),
Value::Nil => false, Value::Nil => false,
} { } {
self.exec(istrue, locals) self.exec(istrue, locals)
@@ -267,7 +271,7 @@ where
let locals = locals.to_mut(); let locals = locals.to_mut();
let body = f.body.ok_or(RuntimeError::FunctionUndefined(ident.clone()))?; let body = f.body.ok_or(RuntimeError::FunctionUndefined(ident.clone()))?;
for ((name, _), tree) in std::iter::zip(f.decl.args, args) { for (name, tree) in std::iter::zip(f.decl.args, args) {
locals.insert(name.clone(), Object::Variable(Evaluation::Computed(self.exec(Box::new(tree), &mut Cow::Borrowed(locals))?))); locals.insert(name.clone(), Object::Variable(Evaluation::Computed(self.exec(Box::new(tree), &mut Cow::Borrowed(locals))?)));
} }
@@ -321,6 +325,7 @@ where
Value::Float(x) => Ok(Value::Bool(x != 0.0)), Value::Float(x) => Ok(Value::Bool(x != 0.0)),
Value::Bool(x) => Ok(Value::Bool(x)), Value::Bool(x) => Ok(Value::Bool(x)),
Value::String(x) => Ok(Value::Bool(!x.is_empty())), Value::String(x) => Ok(Value::Bool(!x.is_empty())),
Value::Array(vec) => Ok(Value::Bool(!vec.is_empty())),
x => Err(RuntimeError::NoOverloadForTypes("bool".into(), vec![x])), x => Err(RuntimeError::NoOverloadForTypes("bool".into(), vec![x])),
}, },
ParseTree::StringCast(x) => Ok(Value::String(format!("{}", self.exec(x, locals)?))), ParseTree::StringCast(x) => Ok(Value::String(format!("{}", self.exec(x, locals)?))),

View File

@@ -1,4 +1,3 @@
mod tokenizer; mod tokenizer;
mod parser; mod parser;
mod executor; mod executor;
@@ -10,15 +9,16 @@ use tokenizer::Tokenizer;
use std::fmt::Display; use std::fmt::Display;
use std::io::{Write, Read, BufRead}; use std::io::{Write, Read, BufRead};
#[derive(Clone, Debug)] #[derive(Clone, Debug, PartialEq)]
pub enum Type { pub enum Type {
Float, Float,
Int, Int,
Bool, Bool,
String, String,
Array,
_Function(Box<Type>, Vec<Type>),
Nil, Nil,
Any, Any,
_Function(Box<Type>, Vec<Type>),
} }
impl Display for Type { impl Display for Type {
@@ -28,9 +28,10 @@ impl Display for Type {
Self::Int => "Int".into(), Self::Int => "Int".into(),
Self::Bool => "Bool".into(), Self::Bool => "Bool".into(),
Self::String => "String".into(), Self::String => "String".into(),
Self::Array => format!("Array"),
Self::_Function(r, _) => format!("Function -> {}", *r),
Self::Nil => "Nil".into(), Self::Nil => "Nil".into(),
Self::Any => "Any".into(), Self::Any => "Any".into(),
Self::_Function(r, _) => format!("Function -> {}", *r)
}) })
} }
} }
@@ -42,6 +43,7 @@ pub enum Value {
Int(i64), Int(i64),
Bool(bool), Bool(bool),
String(String), String(String),
Array(Vec<Value>),
Nil, Nil,
} }
@@ -52,6 +54,7 @@ impl Value {
Self::Int(_) => Type::Int, Self::Int(_) => Type::Int,
Self::Bool(_) => Type::Bool, Self::Bool(_) => Type::Bool,
Self::String(_) => Type::String, Self::String(_) => Type::String,
Self::Array(_) => Type::Array,
Self::Nil => Type::Nil, Self::Nil => Type::Nil,
} }
} }
@@ -64,6 +67,7 @@ impl Display for Value {
Self::Int(x) => write!(f, "{x}"), Self::Int(x) => write!(f, "{x}"),
Self::Bool(x) => write!(f, "{}", if *x { "true" } else { "false" }), Self::Bool(x) => write!(f, "{}", if *x { "true" } else { "false" }),
Self::String(x) => write!(f, "{x}"), Self::String(x) => write!(f, "{x}"),
Self::Array(v) => write!(f, "[{}]", v.iter().map(|x| format!("{x}")).collect::<Vec<_>>().join(" ")),
Self::Nil => write!(f, "nil"), Self::Nil => write!(f, "nil"),
} }
} }
@@ -72,8 +76,7 @@ impl Display for Value {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub(crate) struct FunctionDeclaration { pub(crate) struct FunctionDeclaration {
_name: String, _name: String,
_r: Type, args: Vec<String>,
args: Vec<(String, Type)>,
} }
pub struct Runtime<'a, R: BufRead> { pub struct Runtime<'a, R: BufRead> {

View File

@@ -14,6 +14,7 @@ pub enum ParseError {
InvalidIdentifier, InvalidIdentifier,
FunctionUndefined(String), FunctionUndefined(String),
VariableUndefined(String), VariableUndefined(String),
UnmatchedArrayClose,
TokenizeError(TokenizeError), TokenizeError(TokenizeError),
} }
@@ -26,6 +27,7 @@ impl Display for ParseError {
ParseError::FunctionUndefined(name) => write!(f, "Undefined function `{name}`"), ParseError::FunctionUndefined(name) => write!(f, "Undefined function `{name}`"),
ParseError::VariableUndefined(name) => write!(f, "Undefined variable `{name}`"), ParseError::VariableUndefined(name) => write!(f, "Undefined variable `{name}`"),
ParseError::NoInput => write!(f, "No input given"), ParseError::NoInput => write!(f, "No input given"),
ParseError::UnmatchedArrayClose => write!(f, "there was an unmatched array closing operator `]`"),
ParseError::TokenizeError(e) => write!(f, "{e}"), ParseError::TokenizeError(e) => write!(f, "{e}"),
} }
} }
@@ -54,7 +56,7 @@ pub(crate) enum ParseTree {
// Defining Objects // Defining Objects
Equ(String, Box<ParseTree>, Box<ParseTree>), Equ(String, Box<ParseTree>, Box<ParseTree>),
LazyEqu(String, Box<ParseTree>, Box<ParseTree>), LazyEqu(String, Box<ParseTree>, Box<ParseTree>),
FunctionDefinition(String, Vec<(String, Type)>, Type, Box<ParseTree>, Box<ParseTree>), FunctionDefinition(String, Vec<String>, Box<ParseTree>, Box<ParseTree>),
// Functional Operations // Functional Operations
Compose(Box<ParseTree>, Box<ParseTree>), Compose(Box<ParseTree>, Box<ParseTree>),
@@ -157,9 +159,9 @@ impl ParseTree {
.map_err(|e| ParseError::TokenizeError(e))?; .map_err(|e| ParseError::TokenizeError(e))?;
if let Token::Identifier(ident) = token { if let Token::Identifier(ident) = token {
let args: Vec<(String, Type)> = tokens.take(nargs) let args: Vec<String> = tokens.take(nargs)
.map(|token| match token { .map(|token| match token {
Ok(Token::Identifier(ident)) => Ok((ident, Type::Any)), Ok(Token::Identifier(ident)) => Ok(ident),
Ok(_) => Err(ParseError::InvalidIdentifier), Ok(_) => Err(ParseError::InvalidIdentifier),
Err(e) => Err(ParseError::TokenizeError(e)), Err(e) => Err(ParseError::TokenizeError(e)),
}) })
@@ -169,14 +171,12 @@ impl ParseTree {
locals.insert(ident.clone(), FunctionDeclaration { locals.insert(ident.clone(), FunctionDeclaration {
_name: ident.clone(), _name: ident.clone(),
_r: Type::Any,
args: args.clone(), args: args.clone(),
}); });
Ok(ParseTree::FunctionDefinition( Ok(ParseTree::FunctionDefinition(
ident, ident,
args, args,
Type::Any,
Box::new(ParseTree::parse(tokens, globals, &mut Cow::Borrowed(&*locals))?), Box::new(ParseTree::parse(tokens, globals, &mut Cow::Borrowed(&*locals))?),
Box::new(ParseTree::parse(tokens, globals, &mut Cow::Borrowed(&*locals))?))) Box::new(ParseTree::parse(tokens, globals, &mut Cow::Borrowed(&*locals))?)))
} else { } else {
@@ -224,7 +224,35 @@ impl ParseTree {
Op::FloatCast => Ok(ParseTree::FloatCast(Box::new(ParseTree::parse(tokens, globals, locals)?))), Op::FloatCast => Ok(ParseTree::FloatCast(Box::new(ParseTree::parse(tokens, globals, locals)?))),
Op::BoolCast => Ok(ParseTree::BoolCast(Box::new(ParseTree::parse(tokens, globals, locals)?))), Op::BoolCast => Ok(ParseTree::BoolCast(Box::new(ParseTree::parse(tokens, globals, locals)?))),
Op::StringCast => Ok(ParseTree::StringCast(Box::new(ParseTree::parse(tokens, globals, locals)?))), Op::StringCast => Ok(ParseTree::StringCast(Box::new(ParseTree::parse(tokens, globals, locals)?))),
Op::Print => Ok(ParseTree::Print(Box::new(ParseTree::parse(tokens, globals, locals)?))) Op::Print => Ok(ParseTree::Print(Box::new(ParseTree::parse(tokens, globals, locals)?))),
Op::OpenArray => {
let mut depth = 1;
// take tokens until we reach the end of this array
// if we don't collect them here it causes rust to overflow computing the types
let mut array_tokens = tokens.by_ref().take_while(|t| match t {
Ok(Token::Operator(Op::OpenArray)) => {
depth += 1;
true
},
Ok(Token::Operator(Op::CloseArray)) => {
depth -= 1;
depth > 0
}
_ => true,
}).collect::<Vec<Result<_, TokenizeError>>>().into_iter();
let parser: Vec<ParseTree> = Parser::new(&mut array_tokens).collect::<Result<_, ParseError>>()?;
let tree = parser.iter().fold(
ParseTree::Constant(Value::Array(vec![])),
|acc, x| ParseTree::Add(Box::new(acc), Box::new(x.clone())),
);
Ok(tree)
}
Op::Empty => Ok(ParseTree::Constant(Value::Array(vec![]))),
Op::CloseArray => Err(ParseError::UnmatchedArrayClose),
} }
} }
} }

View File

@@ -65,6 +65,9 @@ pub(crate) enum Op {
BoolCast, BoolCast,
StringCast, StringCast,
Print, Print,
OpenArray,
CloseArray,
Empty,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -110,6 +113,8 @@ impl Token {
">=" => Ok(Token::Operator(Op::GreaterThanOrEqualTo)), ">=" => Ok(Token::Operator(Op::GreaterThanOrEqualTo)),
"<=" => Ok(Token::Operator(Op::LessThanOrEqualTo)), "<=" => Ok(Token::Operator(Op::LessThanOrEqualTo)),
"==" => Ok(Token::Operator(Op::EqualTo)), "==" => Ok(Token::Operator(Op::EqualTo)),
"[" => Ok(Token::Operator(Op::OpenArray)),
"]" => Ok(Token::Operator(Op::CloseArray)),
// then some keywords // then some keywords
"true" => Ok(Token::Constant(Value::Bool(true))), "true" => Ok(Token::Constant(Value::Bool(true))),
@@ -124,6 +129,7 @@ impl Token {
// misc // misc
"print" => Ok(Token::Operator(Op::Print)), "print" => Ok(Token::Operator(Op::Print)),
"empty" => Ok(Token::Operator(Op::Empty)),
// then variable length keywords // then variable length keywords
_ => { _ => {