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::Float(x), Value::Float(y)) => Ok(Value::Float(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]))
},
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))
}
},
ParseTree::FunctionDefinition(ident, args, r, body, scope) => {
ParseTree::FunctionDefinition(ident, args, body, scope) => {
let existing = locals.get(&ident).or(self.globals.get(&ident)).cloned();
match existing {
@@ -225,7 +227,7 @@ where
let locals = locals.to_mut();
locals.insert(ident.clone(), Object::Function(Function {
decl: FunctionDeclaration { _name: ident.clone(), _r: r, args },
decl: FunctionDeclaration { _name: ident.clone(), args },
body: Some(body)
}));
@@ -243,6 +245,7 @@ where
Value::Int(i) => i != 0,
Value::Bool(b) => b,
Value::String(s) => !s.is_empty(),
Value::Array(vec) => !vec.is_empty(),
Value::Nil => false,
} {
self.exec(body, locals)
@@ -254,6 +257,7 @@ where
Value::Int(i) => i != 0,
Value::Bool(b) => b,
Value::String(s) => !s.is_empty(),
Value::Array(vec) => !vec.is_empty(),
Value::Nil => false,
} {
self.exec(istrue, locals)
@@ -267,7 +271,7 @@ where
let locals = locals.to_mut();
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))?)));
}
@@ -321,6 +325,7 @@ where
Value::Float(x) => Ok(Value::Bool(x != 0.0)),
Value::Bool(x) => Ok(Value::Bool(x)),
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])),
},
ParseTree::StringCast(x) => Ok(Value::String(format!("{}", self.exec(x, locals)?))),

View File

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

View File

@@ -14,6 +14,7 @@ pub enum ParseError {
InvalidIdentifier,
FunctionUndefined(String),
VariableUndefined(String),
UnmatchedArrayClose,
TokenizeError(TokenizeError),
}
@@ -26,6 +27,7 @@ impl Display for ParseError {
ParseError::FunctionUndefined(name) => write!(f, "Undefined function `{name}`"),
ParseError::VariableUndefined(name) => write!(f, "Undefined variable `{name}`"),
ParseError::NoInput => write!(f, "No input given"),
ParseError::UnmatchedArrayClose => write!(f, "there was an unmatched array closing operator `]`"),
ParseError::TokenizeError(e) => write!(f, "{e}"),
}
}
@@ -54,7 +56,7 @@ pub(crate) enum ParseTree {
// Defining Objects
Equ(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
Compose(Box<ParseTree>, Box<ParseTree>),
@@ -157,9 +159,9 @@ impl ParseTree {
.map_err(|e| ParseError::TokenizeError(e))?;
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 {
Ok(Token::Identifier(ident)) => Ok((ident, Type::Any)),
Ok(Token::Identifier(ident)) => Ok(ident),
Ok(_) => Err(ParseError::InvalidIdentifier),
Err(e) => Err(ParseError::TokenizeError(e)),
})
@@ -169,14 +171,12 @@ impl ParseTree {
locals.insert(ident.clone(), FunctionDeclaration {
_name: ident.clone(),
_r: Type::Any,
args: args.clone(),
});
Ok(ParseTree::FunctionDefinition(
ident,
args,
Type::Any,
Box::new(ParseTree::parse(tokens, globals, &mut Cow::Borrowed(&*locals))?),
Box::new(ParseTree::parse(tokens, globals, &mut Cow::Borrowed(&*locals))?)))
} else {
@@ -224,7 +224,35 @@ impl ParseTree {
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::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,
StringCast,
Print,
OpenArray,
CloseArray,
Empty,
}
#[derive(Debug, Clone)]
@@ -110,6 +113,8 @@ impl Token {
">=" => Ok(Token::Operator(Op::GreaterThanOrEqualTo)),
"<=" => Ok(Token::Operator(Op::LessThanOrEqualTo)),
"==" => Ok(Token::Operator(Op::EqualTo)),
"[" => Ok(Token::Operator(Op::OpenArray)),
"]" => Ok(Token::Operator(Op::CloseArray)),
// then some keywords
"true" => Ok(Token::Constant(Value::Bool(true))),
@@ -124,6 +129,7 @@ impl Token {
// misc
"print" => Ok(Token::Operator(Op::Print)),
"empty" => Ok(Token::Operator(Op::Empty)),
// then variable length keywords
_ => {