From b7ae0f1b526b76594eee20de2ec7d7ac2c405730 Mon Sep 17 00:00:00 2001 From: Jeff Date: Mon, 18 Mar 2024 08:15:30 -0400 Subject: [PATCH] Expand modules and function built-ins --- Cargo.lock | 54 ++++++++++++++++++ Cargo.toml | 1 + src/context.rs | 53 +++++++++++------ src/error.rs | 5 ++ src/lexer.rs | 5 +- src/value.rs | 151 ++++++++++++++++++++++++++++++++++++++++--------- 6 files changed, 223 insertions(+), 46 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1c03e11..204c227 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -205,6 +205,7 @@ dependencies = [ "colored", "env_logger", "log", + "rand", "stanza", ] @@ -231,6 +232,17 @@ dependencies = [ "log", ] +[[package]] +name = "getrandom" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "hashbrown" version = "0.14.3" @@ -300,6 +312,12 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + [[package]] name = "proc-macro2" version = "1.0.79" @@ -327,6 +345,36 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + [[package]] name = "regex" version = "1.10.3" @@ -453,6 +501,12 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "winapi" version = "0.3.9" diff --git a/Cargo.toml b/Cargo.toml index 2dfa786..99f2393 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,4 +19,5 @@ clap = { version = "4.5.2", features = ["derive"] } colored = "2.1.0" env_logger = "0.11.3" log = "0.4.21" +rand = "0.8.5" stanza = "0.5.1" diff --git a/src/context.rs b/src/context.rs index 5f40b26..74e3193 100644 --- a/src/context.rs +++ b/src/context.rs @@ -6,7 +6,7 @@ use std::{ use crate::{ abstract_tree::{Identifier, Type}, error::RwLockPoisonError, - value::{BuiltInFunction, BuiltInValue}, + value::{BUILT_IN_FUNCTIONS, BUILT_IN_MODULES}, Value, }; @@ -56,10 +56,19 @@ impl Context { if self.inner.read()?.contains_key(identifier) { Ok(true) } else { - match identifier.as_str() { - "io" | "output" => Ok(true), - _ => Ok(false), + for module in BUILT_IN_MODULES { + if identifier.as_str() == module.name() { + return Ok(true); + } } + + for function in BUILT_IN_FUNCTIONS { + if identifier.as_str() == function.name() { + return Ok(true); + } + } + + Ok(false) } } @@ -73,26 +82,38 @@ impl Context { return Ok(Some(r#type.clone())); } - let r#type = match identifier.as_str() { - "io" => BuiltInValue::Io.r#type(), - "output" => BuiltInFunction::Output.r#type(), - _ => return Ok(None), - }; + for module in BUILT_IN_MODULES { + if identifier.as_str() == module.name() { + return Ok(Some(module.r#type())); + } + } - Ok(Some(r#type)) + for function in BUILT_IN_MODULES { + if identifier.as_str() == function.name() { + return Ok(Some(function.r#type())); + } + } + + Ok(None) } pub fn get_value(&self, identifier: &Identifier) -> Result, RwLockPoisonError> { if let Some(ValueData::Value(value)) = self.inner.read()?.get(identifier) { Ok(Some(value.clone())) } else { - let value = match identifier.as_str() { - "io" => BuiltInValue::Io.value(), - "output" => Value::built_in_function(BuiltInFunction::Output), - _ => return Ok(None), - }; + for module in BUILT_IN_MODULES { + if identifier.as_str() == module.name() { + return Ok(Some(module.value())); + } + } - Ok(Some(value)) + for function in BUILT_IN_MODULES { + if identifier.as_str() == function.name() { + return Ok(Some(function.value())); + } + } + + Ok(None) } } diff --git a/src/error.rs b/src/error.rs index c3201bf..0cd2056 100644 --- a/src/error.rs +++ b/src/error.rs @@ -176,6 +176,7 @@ impl Error { ValidationError::ExpectedFunction { .. } => todo!(), ValidationError::ExpectedValue(_) => todo!(), ValidationError::PropertyNotFound { .. } => todo!(), + ValidationError::WrongArguments { .. } => todo!(), } } @@ -273,6 +274,10 @@ pub enum ValidationError { /// The position of the item that gave the "expected" type. expected_position: SourcePosition, }, + WrongArguments { + expected: Vec, + actual: Vec, + }, VariableNotFound(Identifier), PropertyNotFound { identifier: Identifier, diff --git a/src/lexer.rs b/src/lexer.rs index fab3bc2..3984828 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -1,6 +1,6 @@ use std::fmt::{self, Display, Formatter}; -use chumsky::prelude::*; +use chumsky::{prelude::*, text::whitespace}; use crate::error::Error; @@ -228,10 +228,11 @@ pub fn lexer<'src>() -> impl Parser< just("loop").padded(), just("while").padded(), )) + .delimited_by(whitespace(), whitespace()) .map(Token::Keyword); choice(( - boolean, float, integer, string, keyword, identifier, control, operator, + boolean, float, integer, string, identifier, keyword, control, operator, )) .map_with(|token, state| (token, state.span())) .padded() diff --git a/src/value.rs b/src/value.rs index 98cfbee..6fff84e 100644 --- a/src/value.rs +++ b/src/value.rs @@ -3,10 +3,12 @@ use std::{ collections::BTreeMap, fmt::{self, Display, Formatter}, io::stdin, + num::ParseIntError, ops::Range, sync::{Arc, OnceLock}, }; +use rand::{thread_rng, Rng}; use stanza::{ renderer::{console::Console, Renderer}, style::{HAlign, MinWidth, Styles}, @@ -16,7 +18,7 @@ use stanza::{ use crate::{ abstract_tree::{AbstractTree, Action, Block, Identifier, Type, WithPosition}, context::Context, - error::RuntimeError, + error::{RuntimeError, ValidationError}, }; #[derive(Clone, Debug, PartialEq)] @@ -278,48 +280,104 @@ pub struct ParsedFunction { body: WithPosition, } +static INT_PARSE: OnceLock = OnceLock::new(); +static INT_RANDOM_RANGE: OnceLock = OnceLock::new(); +static READ_LINE: OnceLock = OnceLock::new(); +static WRITE_LINE: OnceLock = OnceLock::new(); + +pub const BUILT_IN_FUNCTIONS: [BuiltInFunction; 4] = [ + BuiltInFunction::IntParse, + BuiltInFunction::IntRandomRange, + BuiltInFunction::ReadLine, + BuiltInFunction::WriteLine, +]; + #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum BuiltInFunction { - Output, + IntParse, + IntRandomRange, ReadLine, + WriteLine, } impl BuiltInFunction { - pub fn output() -> Value { - static OUTPUT: OnceLock = OnceLock::new(); - - OUTPUT - .get_or_init(|| Value::built_in_function(BuiltInFunction::Output)) - .clone() + pub fn name(&self) -> &'static str { + match self { + BuiltInFunction::IntParse => "parse", + BuiltInFunction::IntRandomRange => "random_range", + BuiltInFunction::ReadLine => "read_line", + BuiltInFunction::WriteLine => "write_line", + } } - pub fn read_line() -> Value { - static READ_LINE: OnceLock = OnceLock::new(); - - READ_LINE - .get_or_init(|| Value::built_in_function(BuiltInFunction::ReadLine)) - .clone() + pub fn value(&self) -> Value { + match self { + BuiltInFunction::IntParse => { + INT_PARSE.get_or_init(|| Value::built_in_function(BuiltInFunction::IntParse)) + } + BuiltInFunction::IntRandomRange => INT_RANDOM_RANGE + .get_or_init(|| Value::built_in_function(BuiltInFunction::IntRandomRange)), + BuiltInFunction::ReadLine => { + READ_LINE.get_or_init(|| Value::built_in_function(BuiltInFunction::ReadLine)) + } + BuiltInFunction::WriteLine => { + WRITE_LINE.get_or_init(|| Value::built_in_function(BuiltInFunction::WriteLine)) + } + } + .clone() } pub fn r#type(&self) -> Type { match self { - BuiltInFunction::Output => Type::Function { - parameter_types: vec![Type::Any], - return_type: Box::new(Type::None), + BuiltInFunction::IntParse => Type::Function { + parameter_types: vec![Type::String], + return_type: Box::new(Type::Integer), + }, + BuiltInFunction::IntRandomRange => Type::Function { + parameter_types: vec![Type::Range], + return_type: Box::new(Type::Integer), }, BuiltInFunction::ReadLine => Type::Function { parameter_types: Vec::with_capacity(0), return_type: Box::new(Type::String), }, + BuiltInFunction::WriteLine => Type::Function { + parameter_types: vec![Type::Any], + return_type: Box::new(Type::None), + }, } } pub fn call(&self, arguments: Vec, _context: &Context) -> Result { match self { - BuiltInFunction::Output => { - println!("{}", arguments[0]); + BuiltInFunction::IntParse => { + let string = arguments.get(0).unwrap(); - Ok(Action::None) + if let ValueInner::String(string) = string.inner().as_ref() { + // let integer = string.parse(); + + todo!() + + // Ok(Action::Return(Value::integer(integer))) + } else { + Err(RuntimeError::ValidationFailure( + ValidationError::WrongArguments { + expected: vec![Type::String], + actual: arguments.iter().map(|value| value.r#type()).collect(), + }, + )) + } + } + BuiltInFunction::IntRandomRange => { + let range = arguments.get(0).unwrap(); + + if let ValueInner::Range(range) = range.inner().as_ref() { + let random = thread_rng().gen_range(range.clone()); + + Ok(Action::Return(Value::integer(random))) + } else { + panic!("Built-in function cannot have a non-function type.") + } } BuiltInFunction::ReadLine => { let mut input = String::new(); @@ -328,6 +386,11 @@ impl BuiltInFunction { Ok(Action::Return(Value::string(input))) } + BuiltInFunction::WriteLine => { + println!("{}", arguments[0]); + + Ok(Action::None) + } } } } @@ -335,25 +398,59 @@ impl BuiltInFunction { impl Display for BuiltInFunction { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { - BuiltInFunction::Output => write!(f, "(to_output : any) : none {{ *MAGIC* }}"), + BuiltInFunction::IntParse => write!(f, "(input : int) : str {{ *MAGIC* }}"), + BuiltInFunction::IntRandomRange => write!(f, "(input: range) : int {{ *MAGIC* }}"), BuiltInFunction::ReadLine => write!(f, "() : str {{ *MAGIC* }}"), + BuiltInFunction::WriteLine => write!(f, "(to_output : any) : none {{ *MAGIC* }}"), } } } +static INT: OnceLock = OnceLock::new(); static IO: OnceLock = OnceLock::new(); -pub enum BuiltInValue { +pub const BUILT_IN_MODULES: [BuiltInModule; 2] = [BuiltInModule::Integer, BuiltInModule::Io]; + +pub enum BuiltInModule { + Integer, Io, } -impl BuiltInValue { +impl BuiltInModule { + pub fn name(&self) -> &'static str { + match self { + BuiltInModule::Integer => "int", + BuiltInModule::Io => "io", + } + } + pub fn value(self) -> Value { match self { - BuiltInValue::Io => { + BuiltInModule::Integer => { let mut properties = BTreeMap::new(); - properties.insert(Identifier::new("read_line"), BuiltInFunction::read_line()); + properties.insert( + Identifier::new("parse"), + Value::built_in_function(BuiltInFunction::IntParse), + ); + properties.insert( + Identifier::new("random_range"), + Value::built_in_function(BuiltInFunction::IntRandomRange), + ); + + INT.get_or_init(|| Value::map(properties)).clone() + } + BuiltInModule::Io => { + let mut properties = BTreeMap::new(); + + properties.insert( + Identifier::new("read_line"), + Value::built_in_function(BuiltInFunction::ReadLine), + ); + properties.insert( + Identifier::new("write_line"), + Value::built_in_function(BuiltInFunction::WriteLine), + ); IO.get_or_init(|| Value::map(properties)).clone() } @@ -361,8 +458,6 @@ impl BuiltInValue { } pub fn r#type(self) -> Type { - match self { - BuiltInValue::Io => Type::Map, - } + Type::Map } }