From a4fbb8dd97222e6494909627abe294a4beaacb4d Mon Sep 17 00:00:00 2001 From: Dummi26 Date: Sun, 12 Mar 2023 15:42:19 +0100 Subject: [PATCH] - fixed a bug with t.fits_in(rhs) regarding inner types - added //...\n and /*...*/ comment syntax - added tuple.n indexing, where n is an int <= the tuples length (this is checked before the script runs) --- src/main.rs | 17 ++++++++-- src/parse/file.rs | 35 +++++++++++++++++++++ src/parse/parse.rs | 11 +++---- src/script/block.rs | 75 +++++++++++++++++++++++++++++++++++---------- src/script/value.rs | 73 ++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 186 insertions(+), 25 deletions(-) diff --git a/src/main.rs b/src/main.rs index 9db0d72..ecd7eb1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,8 +6,21 @@ pub(crate) mod parse; pub(crate) mod script; fn main() { - let str1: VType = VSingleType::String.into(); - assert!(str1.fits_in(&VSingleType::String.into()).is_empty()); + let val: VType = VSingleType::Tuple(vec![ + VSingleType::Int.into(), + VSingleType::String.into(), + VSingleType::String.into(), + ]) + .into(); + let case: VType = VSingleType::Tuple(vec![ + VType { + types: vec![VSingleType::Tuple(vec![]).into(), VSingleType::Int.into()], + }, + VSingleType::String.into(), + VSingleType::String.into(), + ]) + .into(); + assert!(val.fits_in(&case).is_empty()); let script = parse::parse::parse(&mut parse::file::File::new( std::fs::read_to_string("/tmp/script.txt").unwrap(), )) diff --git a/src/parse/file.rs b/src/parse/file.rs index 53451e0..5b13ee0 100644 --- a/src/parse/file.rs +++ b/src/parse/file.rs @@ -25,6 +25,41 @@ impl Display for FilePosition { impl File { pub fn new(data: String) -> Self { + let mut chs = data.chars(); + let mut data = String::with_capacity(data.len()); + loop { + match chs.next() { + Some('/') => match chs.next() { + Some('/') => loop { + match chs.next() { + Some('\n') | None => break, + _ => (), + } + }, + Some('*') => loop { + match chs.next() { + Some('*') => { + if let Some('/') = chs.next() { + break; + } + } + None => break, + _ => (), + } + }, + Some(ch) => { + data.push('/'); + data.push(ch); + } + None => { + data.push('/'); + break; + } + }, + Some(ch) => data.push(ch), + None => break, + } + } let chars = data.char_indices().collect(); Self { data, diff --git a/src/parse/parse.rs b/src/parse/parse.rs index 4108298..3a2b0c1 100644 --- a/src/parse/parse.rs +++ b/src/parse/parse.rs @@ -271,12 +271,7 @@ fn parse_statement_adv( } cases.push((parse_type(file)?, parse_statement(file)?)); } - break SStatementEnum::Switch( - SStatementEnum::Variable(switch_on_what).into(), - cases, - force, - ) - .into(); + break SStatementEnum::Switch(switch_on_what, cases, force).into(); } "true" => { break SStatementEnum::Value(VDataEnum::Bool(true).to()).into() @@ -332,6 +327,10 @@ fn parse_statement_adv( let args = [out].into_iter().chain(args.into_iter()).collect(); SStatementEnum::FunctionCall(func, args).into() } + SStatementEnum::Value(vd) => match vd.data { + VDataEnum::Int(i) => SStatementEnum::IndexFixed(out, i as _).into(), + _ => todo!("fixed-indexing not available with this type."), + }, other => { todo!("Wrapping in this type isn't implemented (yet?). Type: {other:?}") } diff --git a/src/script/block.rs b/src/script/block.rs index 3b40c88..a836efd 100644 --- a/src/script/block.rs +++ b/src/script/block.rs @@ -66,8 +66,9 @@ pub enum SStatementEnum { If(SStatement, SStatement, Option), While(SStatement), For(String, SStatement, SStatement), - Switch(SStatement, Vec<(VType, SStatement)>, bool), + Switch(String, Vec<(VType, SStatement)>, bool), // Match(???), + IndexFixed(SStatement, usize), } impl Into for SStatementEnum { fn into(self) -> SStatement { @@ -112,6 +113,7 @@ pub mod to_runnable { }, InvalidTypeForWhileLoop(VType), CaseForceButTypeNotCovered(VType), + NotIndexableFixed(VType, usize), } impl Debug for ToRunnableError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -137,6 +139,7 @@ pub mod to_runnable { } Self::InvalidTypeForWhileLoop(v) => write!(f, "Invalid type: Expected bool or Tuples of length 0 or 1 as return types for the while loop, but found {v:?} instead."), Self::CaseForceButTypeNotCovered(v) => write!(f, "Switch! statement, but not all types covered. Types to cover: {v}"), + Self::NotIndexableFixed(t, i) => write!(f, "Cannot use fixed-index {i} on type {t}."), } } } @@ -376,26 +379,62 @@ pub mod to_runnable { o } SStatementEnum::Switch(switch_on, cases, force) => { - let mut ncases = Vec::with_capacity(cases.len()); - for case in cases { - ncases.push((case.0.clone(), statement(&case.1, ginfo, linfo)?)); - } - let switch_on = statement(switch_on, ginfo, linfo)?; - let switch_on_out = switch_on.out(); - if *force { - for val_type in switch_on_out.types.iter() { - let val_type: VType = val_type.clone().into(); - 'force: { - for (case_type, _) in ncases.iter() { - if val_type.fits_in(&case_type).is_empty() { - break 'force; + if let Some(switch_on_v) = linfo.vars.get(switch_on).cloned() { + let mut ncases = Vec::with_capacity(cases.len()); + let og_type = linfo.vars.get(switch_on).unwrap().1.clone(); + for case in cases { + linfo.vars.get_mut(switch_on).unwrap().1 = case.0.clone(); + ncases.push((case.0.clone(), statement(&case.1, ginfo, linfo)?)); + } + linfo.vars.get_mut(switch_on).unwrap().1 = og_type; + + let switch_on_out = switch_on_v.1; + if *force { + for val_type in switch_on_out.types.iter() { + let val_type: VType = val_type.clone().into(); + let mut linf2 = linfo.clone(); + linf2.vars.get_mut(switch_on).unwrap().1 = val_type.clone(); + 'force: { + for (case_type, _) in cases { + if val_type.fits_in(&case_type).is_empty() { + break 'force; + } } + return Err(ToRunnableError::CaseForceButTypeNotCovered(val_type)); } - return Err(ToRunnableError::CaseForceButTypeNotCovered(val_type)); } } + RStatementEnum::Switch( + RStatementEnum::Variable(switch_on_v.0, switch_on_out).to(), + ncases, + ) + } else { + return Err(ToRunnableError::UseOfUndefinedVariable(switch_on.clone())); + } + } + SStatementEnum::IndexFixed(st, i) => { + let st = statement(st, ginfo, linfo)?; + let ok = 'ok: { + let mut one = false; + for t in st.out().types { + one = true; + // only if all types are indexable by i + match t { + VSingleType::Tuple(v) => { + if v.len() <= *i { + break 'ok false; + } + } + _ => break 'ok false, + } + } + one + }; + if ok { + RStatementEnum::IndexFixed(st, *i) + } else { + return Err(ToRunnableError::NotIndexableFixed(st.out(), *i)); } - RStatementEnum::Switch(switch_on, ncases) } } .to(); @@ -523,6 +562,7 @@ pub enum RStatementEnum { While(RStatement), For(usize, RStatement, RStatement), Switch(RStatement, Vec<(VType, RStatement)>), + IndexFixed(RStatement, usize), } impl RStatementEnum { pub fn run(&self, vars: &Vec>) -> VData { @@ -620,6 +660,7 @@ impl RStatementEnum { } out } + Self::IndexFixed(st, i) => st.run(vars).get(*i).unwrap(), } } pub fn out(&self) -> VType { @@ -657,6 +698,7 @@ impl RStatementEnum { } out } + Self::IndexFixed(st, i) => st.out().get(*i).unwrap(), } } pub fn to(self) -> RStatement { @@ -820,6 +862,7 @@ impl Display for SStatementEnum { } write!(f, "}}") } + SStatementEnum::IndexFixed(st, i) => write!(f, "{st}.{i}"), } } } diff --git a/src/script/value.rs b/src/script/value.rs index e02e134..7109c30 100644 --- a/src/script/value.rs +++ b/src/script/value.rs @@ -34,6 +34,9 @@ impl VData { VDataEnum::Thread(_, o) => VSingleType::Thread(o.clone()), } } + pub fn get(&self, i: usize) -> Option { + self.data.get(i) + } } #[derive(Clone, Debug)] @@ -118,6 +121,45 @@ impl VDataEnum { } } +// get() +impl VDataEnum { + pub fn get(&self, i: usize) -> Option { + match self { + Self::Bool(..) + | Self::Int(..) + | Self::Float(..) + | Self::Function(..) + | Self::Thread(..) => None, + Self::String(s) => match s.chars().nth(i) { + // Slow! + Some(ch) => Some(Self::String(format!("{ch}")).to()), + None => None, + }, + Self::Tuple(v) | Self::List(_, v) => v.get(i).cloned(), + } + } +} +impl VSingleType { + // None => Cannot get, Some(t) => getting can return t or nothing + pub fn get(&self, i: usize) -> Option { + match self { + Self::Bool | Self::Int | Self::Float | Self::Function(..) | Self::Thread(..) => None, + Self::String => Some(VSingleType::String.into()), + Self::Tuple(t) => t.get(i).cloned(), + Self::List(t) => Some(t.clone()), + } + } +} +impl VType { + pub fn get(&self, i: usize) -> Option { + let mut out = VType { types: vec![] }; + for t in &self.types { + out = out | t.get(i)?; // if we can't use *get* on one type, we can't use it at all. + } + Some(out) + } +} + #[derive(Clone, Debug, PartialEq, Eq)] pub struct VType { pub types: Vec, @@ -127,7 +169,8 @@ impl VType { pub fn fits_in(&self, rhs: &Self) -> Vec { let mut no = vec![]; for t in &self.types { - if !rhs.types.contains(t) { + // if t doesnt fit in any of rhs's types + if !rhs.types.iter().any(|r| t.fits_in(r)) { no.push(t.clone()) } } @@ -187,6 +230,34 @@ impl VSingleType { _ => vec![], } } + pub fn fits_in(&self, rhs: &Self) -> bool { + match (self, rhs) { + (Self::Bool, Self::Bool) + | (Self::Int, Self::Int) + | (Self::Float, Self::Float) + | (Self::String, Self::String) => true, + (Self::Bool | Self::Int | Self::Float | Self::String, _) => false, + (Self::Tuple(a), Self::Tuple(b)) => { + if a.len() == b.len() { + a.iter().zip(b.iter()).all(|(a, b)| a.fits_in(b).is_empty()) + } else { + false + } + } + (Self::Tuple(_), _) => false, + (Self::List(a), Self::List(b)) => a.fits_in(b).is_empty(), + (Self::List(_), _) => false, + (Self::Function(ai, ao), Self::Function(bi, bo)) => { + ai.iter() + .zip(bi.iter()) + .all(|(a, b)| a.fits_in(b).is_empty()) + && ao.fits_in(bo).is_empty() + } + (Self::Function(..), _) => false, + (Self::Thread(a), Self::Thread(b)) => a.fits_in(b).is_empty(), + (Self::Thread(..), _) => false, + } + } } impl Into for VSingleType {