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:
@@ -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)?))),
|
||||
|
||||
15
src/lib.rs
15
src/lib.rs
@@ -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> {
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
_ => {
|
||||
|
||||
Reference in New Issue
Block a user