add print builtin function

This commit is contained in:
2024-10-15 00:28:39 -04:00
parent 290393a62f
commit e0e33c868b
3 changed files with 60 additions and 15 deletions

View File

@@ -5,6 +5,7 @@ use std::collections::HashMap;
use std::borrow::Cow; use std::borrow::Cow;
use std::fmt::Display; use std::fmt::Display;
use std::error::Error; use std::error::Error;
use std::io::{self, Read, Write};
#[derive(Debug)] #[derive(Debug)]
pub enum RuntimeError { pub enum RuntimeError {
@@ -16,6 +17,7 @@ pub enum RuntimeError {
FunctionUndefined(String), FunctionUndefined(String),
NotAVariable(String), NotAVariable(String),
ParseFail(String, Type), ParseFail(String, Type),
IO(io::Error),
} }
impl Display for RuntimeError { impl Display for RuntimeError {
@@ -31,6 +33,7 @@ impl Display for RuntimeError {
Self::FunctionUndefined(ident) => write!(f, "function `{ident}` was not defined"), Self::FunctionUndefined(ident) => write!(f, "function `{ident}` was not defined"),
Self::NotAVariable(ident) => write!(f, "`{ident}` is a function but was attempted to be used like a variable"), Self::NotAVariable(ident) => write!(f, "`{ident}` is a function but was attempted to be used like a variable"),
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}"),
} }
} }
} }
@@ -60,16 +63,44 @@ enum Object {
} }
/// Executes an input of ParseTrees /// Executes an input of ParseTrees
pub struct Executor<I: Iterator<Item = Result<ParseTree, ParseError>>> { pub struct Executor<'a, I>
where
I: Iterator<Item = Result<ParseTree, ParseError>>,
{
exprs: I, exprs: I,
globals: HashMap<String, Object>, globals: HashMap<String, Object>,
stdout: Box<dyn Write + 'a>,
stdin: Box<dyn Read + 'a>,
} }
impl<I: Iterator<Item = Result<ParseTree, ParseError>>> Executor<I> { impl<'a, I> Executor<'a, I>
where
I: Iterator<Item = Result<ParseTree, ParseError>>,
{
pub fn new(exprs: I) -> Self { pub fn new(exprs: I) -> Self {
Self { Self {
exprs, exprs,
globals: HashMap::new(), globals: HashMap::new(),
stdout: Box::new(io::stdout()),
stdin: Box::new(io::stdin()),
}
}
pub fn stdout(self, writer: impl Write + 'a) -> Self {
Self {
exprs: self.exprs,
globals: self.globals,
stdout: Box::new(writer),
stdin: self.stdin,
}
}
pub fn stdin(self, reader: impl Read + 'a) -> Self {
Self {
exprs: self.exprs,
globals: self.globals,
stdout: self.stdout,
stdin: Box::new(reader),
} }
} }
@@ -265,7 +296,7 @@ impl<I: Iterator<Item = Result<ParseTree, ParseError>>> Executor<I> {
} }
}, },
ParseTree::Constant(value) => Ok(value), ParseTree::Constant(value) => Ok(value),
ParseTree::ToInt(x) => match self.exec(x, locals)? { ParseTree::IntCast(x) => match self.exec(x, locals)? {
Value::Int(x) => Ok(Value::Int(x)), Value::Int(x) => Ok(Value::Int(x)),
Value::Float(x) => Ok(Value::Int(x as i64)), Value::Float(x) => Ok(Value::Int(x as i64)),
Value::Bool(x) => Ok(Value::Int(if x { 1 } else { 0 })), Value::Bool(x) => Ok(Value::Int(if x { 1 } else { 0 })),
@@ -275,7 +306,7 @@ impl<I: Iterator<Item = Result<ParseTree, ParseError>>> Executor<I> {
} }
x => Err(RuntimeError::NoOverloadForTypes("int".into(), vec![x])), x => Err(RuntimeError::NoOverloadForTypes("int".into(), vec![x])),
}, },
ParseTree::ToFloat(x) => match self.exec(x, locals)? { ParseTree::FloatCast(x) => match self.exec(x, locals)? {
Value::Int(x) => Ok(Value::Float(x as f64)), Value::Int(x) => Ok(Value::Float(x as f64)),
Value::Float(x) => Ok(Value::Float(x)), Value::Float(x) => Ok(Value::Float(x)),
Value::Bool(x) => Ok(Value::Float(if x { 1.0 } else { 0.0 })), Value::Bool(x) => Ok(Value::Float(if x { 1.0 } else { 0.0 })),
@@ -285,19 +316,25 @@ impl<I: Iterator<Item = Result<ParseTree, ParseError>>> Executor<I> {
} }
x => Err(RuntimeError::NoOverloadForTypes("float".into(), vec![x])), x => Err(RuntimeError::NoOverloadForTypes("float".into(), vec![x])),
}, },
ParseTree::ToBool(x) => match self.exec(x, locals)? { ParseTree::BoolCast(x) => match self.exec(x, locals)? {
Value::Int(x) => Ok(Value::Bool(x != 0)), Value::Int(x) => Ok(Value::Bool(x != 0)),
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())),
x => Err(RuntimeError::NoOverloadForTypes("bool".into(), vec![x])), x => Err(RuntimeError::NoOverloadForTypes("bool".into(), vec![x])),
}, },
ParseTree::ToString(x) => Ok(Value::String(format!("{}", self.exec(x, locals)?))), ParseTree::StringCast(x) => Ok(Value::String(format!("{}", self.exec(x, locals)?))),
ParseTree::Print(x) => match self.exec(x, locals)? {
x => {
writeln!(self.stdout, "{x}").map_err(|e| RuntimeError::IO(e));
Ok(Value::Nil)
}
}
} }
} }
} }
impl<I: Iterator<Item = Result<ParseTree, ParseError>>> Iterator for Executor<I> { impl<'a, I: Iterator<Item = Result<ParseTree, ParseError>>> Iterator for Executor<'a, I> {
type Item = Result<Value, RuntimeError>; type Item = Result<Value, RuntimeError>;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {

View File

@@ -70,10 +70,13 @@ pub(crate) enum ParseTree {
Constant(Value), Constant(Value),
// Type Casts // Type Casts
ToInt(Box<ParseTree>), IntCast(Box<ParseTree>),
ToFloat(Box<ParseTree>), FloatCast(Box<ParseTree>),
ToBool(Box<ParseTree>), BoolCast(Box<ParseTree>),
ToString(Box<ParseTree>), StringCast(Box<ParseTree>),
// Misc
Print(Box<ParseTree>),
} }
impl ParseTree { impl ParseTree {
@@ -217,10 +220,11 @@ impl ParseTree {
Box::new(ParseTree::parse(tokens, globals, locals)?) Box::new(ParseTree::parse(tokens, globals, locals)?)
)), )),
Op::Not => Ok(ParseTree::Not(Box::new(ParseTree::parse(tokens, globals, locals)?))), Op::Not => Ok(ParseTree::Not(Box::new(ParseTree::parse(tokens, globals, locals)?))),
Op::IntCast => Ok(ParseTree::ToInt(Box::new(ParseTree::parse(tokens, globals, locals)?))), Op::IntCast => Ok(ParseTree::IntCast(Box::new(ParseTree::parse(tokens, globals, locals)?))),
Op::FloatCast => Ok(ParseTree::ToFloat(Box::new(ParseTree::parse(tokens, globals, locals)?))), Op::FloatCast => Ok(ParseTree::FloatCast(Box::new(ParseTree::parse(tokens, globals, locals)?))),
Op::BoolCast => Ok(ParseTree::ToBool(Box::new(ParseTree::parse(tokens, globals, locals)?))), Op::BoolCast => Ok(ParseTree::BoolCast(Box::new(ParseTree::parse(tokens, globals, locals)?))),
Op::StringCast => Ok(ParseTree::ToString(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)?)))
} }
} }
} }

View File

@@ -64,6 +64,7 @@ pub(crate) enum Op {
FloatCast, FloatCast,
BoolCast, BoolCast,
StringCast, StringCast,
Print,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -121,6 +122,9 @@ impl Token {
"bool" => Ok(Token::Operator(Op::BoolCast)), "bool" => Ok(Token::Operator(Op::BoolCast)),
"string" => Ok(Token::Operator(Op::StringCast)), "string" => Ok(Token::Operator(Op::StringCast)),
// misc
"print" => Ok(Token::Operator(Op::Print)),
// then variable length keywords // then variable length keywords
_ => { _ => {
if s.starts_with(":") { if s.starts_with(":") {