diff --git a/README.md b/README.md index 1e01482..f20063d 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,20 @@ run() will return what the function returns while thread() will return a Thread using switch! forces you to cover all possible types. Try removing the string or int case and see what happens! +### Reading /tmp/ and filtering for files/directories + + for file fs_list("/tmp/") { + list = file.fs_list() + switch! list { + [] { + "\"{0}\" is a file".format(file).println() + } + [string] { + "\"{0}\" is a directory".format(file).println() + } + } + } + ### Running a thread and awaiting it, passing arguments to the thread when starting it and sharing a variable because the thread's function captured it (useful for reporting progress, i.e. changing a float from 0.0 to 100.0 while downloading and using the main thread to animate a progress bar, then using .await() only when the float is set to 100 to avoid blocking) print( "Starting" ) @@ -94,5 +108,3 @@ using switch! forces you to cover all possible types. Try removing the string or ## Quirks currently, f(a b c) is the same as a.f(b c). var.function(args) will use var as the function's first argument, moving all other arguments back. This removes the need for struct/class syntax. Simply declare a function scream(str string) { str.to_upper().print() } and you can now use var.scream() on all strings. - -function(var) will break the parser, every argument has to be followed by a whitespace (space, tab, newline, ...). This will hopefully be fixed soon, but for now, function(var ) or just var.function() is the syntax that works. diff --git a/src/parse/file.rs b/src/parse/file.rs index 79780fc..53451e0 100644 --- a/src/parse/file.rs +++ b/src/parse/file.rs @@ -65,6 +65,12 @@ impl File { None => None, } } + pub fn peek(&self) -> Option { + match self.chars.get(self.pos.current_char_index) { + Some((_, c)) => Some(*c), + None => None, + } + } } impl Iterator for File { diff --git a/src/parse/parse.rs b/src/parse/parse.rs index 508afc0..ae59df1 100644 --- a/src/parse/parse.rs +++ b/src/parse/parse.rs @@ -1,9 +1,9 @@ use crate::script::{ block::{ - to_runnable, to_runnable::ToRunnableError, RFunction, RScript, SBlock, SFunction, - SStatement, SStatementEnum, + to_runnable, to_runnable::ToRunnableError, RScript, SBlock, SFunction, SStatement, + SStatementEnum, }, - value::{VData, VDataEnum, VSingleType, VType}, + value::{VDataEnum, VSingleType, VType}, }; use super::file::File; @@ -113,18 +113,30 @@ fn parse_statement_adv( ) -> Result { file.skip_whitespaces(); let mut start = String::new(); - let out = match file.get_char(file.get_char_index()) { + let out = match file.peek() { Some('{') => Some(SStatementEnum::Block(parse_block(file)?).into()), Some('[') => { + file.next(); let mut v = vec![]; + let mut list = false; loop { file.skip_whitespaces(); - if let Some(']') = file.get_char(file.get_char_index()) { + if let Some(']') = file.peek() { + file.next(); + if file[file.get_char_index()..].starts_with("[]") { + list = true; + file.next(); + file.next(); + } break; } v.push(parse_statement(file)?); } - Some(SStatementEnum::Tuple(v).into()) + Some(if list { + SStatementEnum::List(v).into() + } else { + SStatementEnum::Tuple(v).into() + }) } Some('$') => { file.next(); @@ -163,12 +175,16 @@ fn parse_statement_adv( out } else { loop { - match file.next() { + match match file.peek() { + Some(ch) if matches!(ch, '}' | ')' | '.') => Some(ch), + _ => file.next(), + } { Some('=') => { break parse_statement(file)?.output_to(start.trim().to_string()); } - Some(ch) if ch.is_whitespace() || matches!(ch, '}' | ')' | '.') => { - if let Some('=') = file.get_char(file.get_char_index()) { + Some(ch) if (ch.is_whitespace() || ch == '}' || ch == ')' || ch == '.') => { + file.skip_whitespaces(); + if let Some('=') = file.peek() { continue; } else { let start = start.trim(); @@ -230,7 +246,14 @@ fn parse_statement_adv( } "switch" | "switch!" => { let force = start.ends_with("!"); - let switch_on_what = parse_statement(file)?; + let mut switch_on_what = String::new(); + loop { + match file.next() { + None => break, + Some(ch) if ch.is_whitespace() => break, + Some(ch) => switch_on_what.push(ch), + } + } file.skip_whitespaces(); if let Some('{') = file.next() { } else { @@ -239,12 +262,17 @@ fn parse_statement_adv( let mut cases = vec![]; loop { file.skip_whitespaces(); - if let Some('}') = file.get_char(file.get_char_index()) { + if let Some('}') = file.peek() { break; } cases.push((parse_type(file)?, parse_statement(file)?)); } - break SStatementEnum::Switch(switch_on_what, cases, force).into(); + break SStatementEnum::Switch( + SStatementEnum::Variable(switch_on_what).into(), + cases, + force, + ) + .into(); } "true" => { break SStatementEnum::Value(VDataEnum::Bool(true).to()).into() @@ -350,7 +378,7 @@ fn parse_type_adv(file: &mut File, in_fn_args: bool) -> Result<(VType, bool), Pa break; } file.skip_whitespaces(); - match file.get_char(file.get_char_index()) { + match file.peek() { Some('/') => (), _ => break, } @@ -375,12 +403,15 @@ fn parse_single_type_adv( Some('[') => { let mut types = vec![]; loop { - types.push(parse_single_type(file)?.into()); file.skip_whitespaces(); - match file.get_char(file.get_char_index()) { - Some(']') => break, + match file.peek() { + Some(']') => { + file.next(); + break; + } _ => (), } + types.push(parse_single_type(file)?.into()); } if types.len() == 1 { VSingleType::List(types.pop().unwrap()) @@ -391,6 +422,10 @@ fn parse_single_type_adv( Some(ch) => { let mut name = ch.to_string(); loop { + match file.peek() { + Some(']') => break, + _ => (), + } match file.next() { Some(ch) if ch.is_whitespace() => break, Some(')') if in_fn_args => { diff --git a/src/script/block.rs b/src/script/block.rs index 502261f..3771286 100644 --- a/src/script/block.rs +++ b/src/script/block.rs @@ -2,17 +2,15 @@ // Types starting with S are directly parsed from Strings and unchecked. Types starting with T are type-checked templates for R-types. Types starting with R are runnable. S are converted to T after parsing is done, and T are converted to R whenever they need to run. use std::{ - collections::HashMap, fmt::Display, sync::{Arc, Mutex}, - time::Duration, }; use self::to_runnable::ToRunnableError; use super::{ builtins::BuiltinFunction, - value::{VData, VDataEnum, VDataThreadEnum, VSingleType, VType}, + value::{VData, VDataEnum, VSingleType, VType}, }; // Represents a block of code @@ -60,6 +58,7 @@ impl SStatement { pub enum SStatementEnum { Value(VData), Tuple(Vec), + List(Vec), Variable(String), FunctionCall(String, Vec), FunctionDefinition(Option, SFunction), @@ -94,11 +93,11 @@ pub mod to_runnable { sync::Arc, }; - use crate::script::value::{VData, VDataEnum, VSingleType, VType}; + use crate::script::value::{VDataEnum, VSingleType, VType}; use super::{ - Am, BuiltinFunction, RBlock, RFunction, RScript, RStatement, RStatementEnum, SBlock, - SFunction, SStatement, SStatementEnum, + BuiltinFunction, RBlock, RFunction, RScript, RStatement, RStatementEnum, SBlock, SFunction, + SStatement, SStatementEnum, }; pub enum ToRunnableError { @@ -253,12 +252,16 @@ pub mod to_runnable { ) -> Result { let mut statement = match &*s.statement { SStatementEnum::Value(v) => RStatementEnum::Value(v.clone()), - SStatementEnum::Tuple(v) => { + SStatementEnum::Tuple(v) | SStatementEnum::List(v) => { let mut w = Vec::with_capacity(v.len()); for v in v { w.push(statement(v, ginfo, linfo)?); } - RStatementEnum::Tuple(w) + if let SStatementEnum::List(_) = &*s.statement { + RStatementEnum::List(w) + } else { + RStatementEnum::Tuple(w) + } } SStatementEnum::Variable(v) => { if let Some(var) = linfo.vars.get(v) { @@ -376,7 +379,6 @@ pub mod to_runnable { let mut ncases = Vec::with_capacity(cases.len()); for case in cases { ncases.push((case.0.clone(), statement(&case.1, ginfo, linfo)?)); - eprintln!("NCASE: {:#?}", ncases.last().unwrap().0); } let switch_on = statement(switch_on, ginfo, linfo)?; let switch_on_out = switch_on.out(); @@ -386,7 +388,6 @@ pub mod to_runnable { 'force: { for (case_type, _) in ncases.iter() { if val_type.fits_in(&case_type).is_empty() { - eprintln!("Breaking."); break 'force; } } @@ -503,6 +504,7 @@ impl RStatement { pub enum RStatementEnum { Value(VData), Tuple(Vec), + List(Vec), Variable(usize, VType), // Arc> here, because imagine variable in for loop that is used in a different thread -> we need multiple "same" variables FunctionCall(Arc, Vec), BuiltinFunction(BuiltinFunction, Vec), @@ -523,6 +525,16 @@ impl RStatementEnum { } VDataEnum::Tuple(w).to() } + Self::List(v) => { + let mut w = vec![]; + let mut out = VType { types: vec![] }; + for v in v { + let val = v.run(vars); + out = out | val.out(); + w.push(val); + } + VDataEnum::List(out, w).to() + } Self::Variable(v, _) => vars[*v].lock().unwrap().clone(), Self::FunctionCall(func, args) => { for (i, input) in func.inputs.iter().enumerate() { @@ -576,7 +588,7 @@ impl RStatementEnum { in_loop(VDataEnum::String(ch.to_string()).to()) } } - VDataEnum::Tuple(v) | VDataEnum::List(v) => { + VDataEnum::Tuple(v) | VDataEnum::List(_, v) => { for v in v { in_loop(v) } @@ -603,7 +615,9 @@ impl RStatementEnum { pub fn out(&self) -> VType { match self { Self::Value(v) => v.out(), - Self::Tuple(v) => VSingleType::Tuple(v.iter().map(|v| v.out()).collect()).into(), + Self::Tuple(v) | Self::List(v) => { + VSingleType::Tuple(v.iter().map(|v| v.out()).collect()).into() + } Self::Variable(_, t) => t.clone(), Self::FunctionCall(f, _) => { eprintln!("Warn: generalizing a functions return type regardless of the inputs. Type-checker might assume this value can have more types than it really can."); @@ -658,6 +672,7 @@ impl RScript { pub fn run(&self, args: Vec) -> VData { let mut vars = Vec::with_capacity(self.vars); vars.push(am(VDataEnum::List( + VSingleType::String.into(), args.into_iter() .map(|v| VDataEnum::String(v).to()) .collect(), @@ -744,6 +759,13 @@ impl Display for SStatementEnum { v.iter().map(|v| format!("{} ", v)).collect::() ) } + SStatementEnum::List(v) => { + write!( + f, + "[{} ...]", + v.iter().map(|v| format!("{} ", v)).collect::() + ) + } SStatementEnum::Variable(v) => write!(f, "{v}"), SStatementEnum::FunctionCall(func, args) => { write!(f, "{func}(")?; @@ -805,7 +827,7 @@ impl Display for VDataEnum { Self::Int(v) => write!(f, "{v}"), Self::Float(v) => write!(f, "{v}"), Self::String(v) => write!(f, "{v}"), - Self::Tuple(v) | Self::List(v) => { + Self::Tuple(v) | Self::List(_, v) => { write!(f, "[")?; for v in v { write!(f, "{v}")?; diff --git a/src/script/value.rs b/src/script/value.rs index c8a0b50..e02e134 100644 --- a/src/script/value.rs +++ b/src/script/value.rs @@ -26,9 +26,7 @@ impl VData { VDataEnum::Float(..) => VSingleType::Float, VDataEnum::String(..) => VSingleType::String, VDataEnum::Tuple(v) => VSingleType::Tuple(v.iter().map(|v| v.out()).collect()), - VDataEnum::List(v) => { - VSingleType::List(v.iter().fold(VType { types: vec![] }, |a, b| a | b.out())) - } + VDataEnum::List(t, _) => VSingleType::List(t.clone()), VDataEnum::Function(f) => VSingleType::Function(f.in_types().clone(), { eprintln!("Warn: generalizing function return type, disregarding input types. might make the type checker think it can return types it can only return with different arguments as the ones that were actually provided."); f.out_all() @@ -45,7 +43,7 @@ pub enum VDataEnum { Float(f64), String(String), Tuple(Vec), - List(Vec), + List(VType, Vec), Function(RFunction), Thread(VDataThread, VType), }