diff --git a/src/parse/parse.rs b/src/parse/parse.rs index fb41c1d..23993ee 100644 --- a/src/parse/parse.rs +++ b/src/parse/parse.rs @@ -266,6 +266,7 @@ fn parse_statement_adv( loop { file.skip_whitespaces(); if let Some('}') = file.peek() { + file.next(); break; } cases.push((parse_type(file)?, parse_statement(file)?)); diff --git a/src/script/block.rs b/src/script/block.rs index 1981693..3b40c88 100644 --- a/src/script/block.rs +++ b/src/script/block.rs @@ -400,9 +400,19 @@ pub mod to_runnable { } .to(); if let Some(opt) = &s.output_to { - if let Some(var) = linfo.vars.get_mut(opt) { - var.1 = var.1.clone() | statement.out(); - statement.output_to = Some(var.0); + if let Some(var) = linfo.vars.get(opt) { + let out = statement.out(); + let var_id = var.0; + let var_out = &var.1; + let inv_types = out.fits_in(&var_out); + if !inv_types.is_empty() { + eprintln!("Warn: shadowing variable {opt} because statement's output type {out} does not fit in the original variable's {var_out}. This might become an error in the future, or it might stop shadowing the variiable entirely - for stable scripts, avoid this by giving the variable a different name."); + linfo.vars.insert(opt.clone(), (ginfo.vars, out)); + statement.output_to = Some(ginfo.vars); + ginfo.vars += 1; + } else { + statement.output_to = Some(var_id); + } } else { linfo .vars @@ -728,8 +738,12 @@ impl Display for VSingleType { Self::String => write!(f, "string"), Self::Tuple(types) => { write!(f, "[")?; - for t in types { - write!(f, "{t}")?; + for (i, t) in types.iter().enumerate() { + if i != 0 { + write!(f, " {t}")?; + } else { + write!(f, "{t}")?; + } } write!(f, "]")?; Ok(()) @@ -829,8 +843,12 @@ impl Display for VDataEnum { Self::String(v) => write!(f, "{v}"), Self::Tuple(v) | Self::List(_, v) => { write!(f, "[")?; - for v in v { - write!(f, "{v}")?; + for (i, v) in v.iter().enumerate() { + if i != 0 { + write!(f, " {v}")?; + } else { + write!(f, "{v}")?; + } } match self { Self::List(..) => write!(f, "...")?, diff --git a/src/script/builtins.rs b/src/script/builtins.rs index ebfea90..b914d85 100644 --- a/src/script/builtins.rs +++ b/src/script/builtins.rs @@ -38,6 +38,7 @@ pub enum BuiltinFunction { StringToBytes, // OS RunCommand, + RunCommandGetBytes, } impl BuiltinFunction { @@ -60,6 +61,7 @@ impl BuiltinFunction { "bytes_to_string" => Self::BytesToString, "string_to_bytes" => Self::StringToBytes, "run_command" => Self::RunCommand, + "run_command_get_bytes" => Self::RunCommandGetBytes, _ => return None, }) } @@ -120,6 +122,20 @@ impl BuiltinFunction { ]), ], }, + Self::RunCommandGetBytes => VType { + types: vec![ + // error + VSingleType::String.into(), + // success: Option, stdout, stderr + VSingleType::Tuple(vec![ + VType { + types: vec![VSingleType::Tuple(vec![]).into(), VSingleType::Int.into()], + }, + VSingleType::List(VSingleType::Int.into()).into(), + VSingleType::List(VSingleType::Int.into()).into(), + ]), + ], + }, } } pub fn run(&self, args: &Vec, vars: &Vec>>) -> VData { @@ -129,7 +145,7 @@ impl BuiltinFunction { print!("{}", arg); VDataEnum::Tuple(vec![]).to() } else { - unreachable!() + unreachable!("print function called with non-string arg") } } BuiltinFunction::Println => { @@ -365,7 +381,7 @@ impl BuiltinFunction { unreachable!("string_to_bytes not 1 arg") } } - Self::RunCommand => { + Self::RunCommand | Self::RunCommandGetBytes => { if args.len() > 0 { if let VDataEnum::String(s) = args[0].run(vars).data { let mut command = std::process::Command::new(s); @@ -390,13 +406,31 @@ impl BuiltinFunction { VDataEnum::Tuple(vec![]) } .to(), - VDataEnum::String( - String::from_utf8_lossy(&out.stdout).into_owned(), - ) + match self { + Self::RunCommandGetBytes => VDataEnum::List( + VSingleType::Int.into(), + out.stdout + .iter() + .map(|v| VDataEnum::Int(*v as _).to()) + .collect(), + ), + _ => VDataEnum::String( + String::from_utf8_lossy(&out.stdout).into_owned(), + ), + } .to(), - VDataEnum::String( - String::from_utf8_lossy(&out.stderr).into_owned(), - ) + match self { + Self::RunCommandGetBytes => VDataEnum::List( + VSingleType::Int.into(), + out.stderr + .iter() + .map(|v| VDataEnum::Int(*v as _).to()) + .collect(), + ), + _ => VDataEnum::String( + String::from_utf8_lossy(&out.stderr).into_owned(), + ), + } .to(), ]) .to(),