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