Add support for stderr and split output handling.

More percent sign doubling support.
This commit is contained in:
Sage Vaillancourt 2023-05-08 16:59:45 -04:00
parent 36ba81e4c4
commit 3e0030b85f
2 changed files with 75 additions and 30 deletions

View File

@ -13,9 +13,13 @@ if %echo some words%.contains("me wo") {
let mem = %free -h%.lines().nth(1).unwrap() let mem = %free -h%.lines().nth(1).unwrap()
.split_whitespace().nth(3).unwrap().to_owned(); .split_whitespace().nth(3).unwrap().to_owned();
let (_stdout, _stderr) = %^curl bob%;
println!("Collect stdout and stderr separately!");
// for i in 0..10000 { // for i in 0..10000 {
// println!("x contains %% example? [{}]", x.contains("example")); // println!("x contains %% example? [{}]", x.contains("example"));
// } // }
%echo Double-up your percent signs to display them literally 100%%!%
println!("There are {} of memory free", &mem); println!("There are {} of memory free", &mem);
%echo hello%

View File

@ -1,5 +1,3 @@
#![feature(option_result_contains)]
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use std::env; use std::env;
@ -16,12 +14,12 @@ use std::process::Command;
#[derive(PartialEq, Clone)] #[derive(PartialEq, Clone)]
enum PrintType { enum PrintType {
Out, Err, All StdOut, StdErr, AllOutputs
} }
fn com(print: &PrintType, command_string: &str) -> String { fn com(print: &PrintType, command_text: &str) -> String {
let command_output = Command::new("bash").arg("-c") let command_output = Command::new("bash").arg("-c")
.arg(command_string) .arg(command_text)
.output().unwrap(); .output().unwrap();
let to_string = |bytes| str::from_utf8(bytes).unwrap().to_string(); let to_string = |bytes| str::from_utf8(bytes).unwrap().to_string();
@ -29,9 +27,9 @@ fn com(print: &PrintType, command_string: &str) -> String {
let err = to_string(&command_output.stderr); let err = to_string(&command_output.stderr);
match print { match print {
PrintType::Out => { print!("{}", out); out }, PrintType::StdOut => { print!("{}", out); out },
PrintType::Err => { print!("{}", err); err }, PrintType::StdErr => { print!("{}", err); err },
PrintType::All => { PrintType::AllOutputs => {
print!("{}", out); print!("{}", out);
if err.trim().is_empty() { if err.trim().is_empty() {
print!("{}", err); print!("{}", err);
@ -68,7 +66,11 @@ fn output_filename(script_name: &str) -> String {
} }
fn run_compiled(script_name: &str) { fn run_compiled(script_name: &str) {
com(&PrintType::All, &output_filename(script_name)); com(&PrintType::AllOutputs, &output_filename(script_name));
}
fn contains<T: std::cmp::PartialEq>(opt: Option<T>, value: T) -> bool {
opt.is_some() && opt.unwrap() == value
} }
fn parse_bash_command( fn parse_bash_command(
@ -76,11 +78,27 @@ fn parse_bash_command(
code: &mut String, code: &mut String,
is_expr: bool is_expr: bool
) -> Result<()> { ) -> Result<()> {
code.push_str(&format!("com({}, \"", !is_expr)); let next = iter.peek().expect("Unexpected EOF");
code.push_str(&(match next {
'^' => {
iter.next();
format!("out_and_err_com({}, \"", !is_expr)
},
'?' => {
iter.next();
format!("stderr_com({}, \"", !is_expr)
},
_ => format!("stdout_com({}, \"", !is_expr)
}));
loop { loop {
if let Some(c) = iter.next() { if let Some(c) = iter.next() {
if c == '%' { if c == '%' {
break; // Double percents count as a single literal percent
if contains(iter.peek(), &'%') {
iter.next();
} else {
break;
}
} }
code.push(c); code.push(c);
} else { } else {
@ -89,7 +107,7 @@ fn parse_bash_command(
} }
code.push_str("\")"); code.push_str("\")");
let peek = iter.peek(); let peek = iter.peek();
if !is_expr && (peek.is_none() || peek.contains(&&' ') || peek.contains(&&'\n')) { if !is_expr && (peek.is_none() || contains(peek, &&' ') || contains(peek, &&'\n')) {
code.push(';'); code.push(';');
} }
Ok(()) Ok(())
@ -98,23 +116,45 @@ fn parse_bash_command(
fn collect_code(script_name: &str) -> Result<String> { fn collect_code(script_name: &str) -> Result<String> {
let script = fs::read_to_string(&script_name)?; let script = fs::read_to_string(&script_name)?;
let mut code = " let mut code =
"#![allow(dead_code)]
use std::str; use std::str;
use std::process::Command; use std::process::Command;
fn com(print: bool, comcom: &str) -> String { fn stdout_com(print: bool, command_text: &str) -> String {
let s = str::from_utf8(&Command::new(\"bash\").arg(\"-c\") let s = str::from_utf8(&Command::new(\"bash\").arg(\"-c\")
.arg(comcom).output().unwrap().stdout).unwrap().to_string(); .arg(command_text).output().unwrap().stdout).unwrap().to_string();
if print { if print {
print!(\"{}\", s) print!(\"{}\", s)
}; };
return s; return s;
} }
fn main() { fn stderr_com(print: bool, command_text: &str) -> String {
let s = str::from_utf8(&Command::new(\"bash\").arg(\"-c\")
.arg(command_text).output().unwrap().stderr).unwrap().to_string();
if print {
print!(\"{}\", s)
};
return s;
}
fn out_and_err_com(print: bool, command_text: &str) -> (String, String) {
let output = Command::new(\"bash\").arg(\"-c\")
.arg(command_text).output().unwrap();
let stdout = str::from_utf8(&output.stdout).unwrap().to_string();
let stderr = str::from_utf8(&output.stderr).unwrap().to_string();
if print {
print!(\"{}\", stdout);
eprint!(\"{}\", stderr);
};
return (stdout, stderr);
}
fn main() -> std::io::Result<()> {
".to_string(); ".to_string();
let mut is_expr = false;
let mut iter = script.chars().peekable(); let mut iter = script.chars().peekable();
if script.starts_with("#!/") { if script.starts_with("#!/") {
@ -125,10 +165,11 @@ fn main() {
} }
} }
let mut is_expr = false;
while let Some(c) = iter.next() { while let Some(c) = iter.next() {
if c == '%' { if c == '%' {
// Double percents count as a single literal percent // Double percents count as a single literal percent
if iter.peek().contains(&&'%') { if contains(iter.peek(), &&'%') {
code.push('%'); code.push('%');
iter.next(); iter.next();
} else { } else {
@ -136,13 +177,13 @@ fn main() {
} }
} else { } else {
code.push(c); code.push(c);
if (c == 'i' && iter.peek().contains(&&'f')) if (c == 'i' && contains(iter.peek(), &&'f'))
|| (c == '=' && !iter.peek().contains(&&'=')) { || (c == '=' && !contains(iter.peek(), &&'=')) {
is_expr = true; is_expr = true;
} else if c == 'f' && iter.peek().contains(&&'o') { } else if c == 'f' && contains(iter.peek(), &&'o') {
iter.next(); iter.next();
code.push('o'); code.push('o');
if iter.peek().contains(&&'r') { if contains(iter.peek(), &&'r') {
is_expr = true; is_expr = true;
} }
} else if (c == '{' || c == ';') && is_expr { } else if (c == '{' || c == ';') && is_expr {
@ -152,7 +193,7 @@ fn main() {
} }
} }
} }
code.push('}'); code.push_str(" Ok(())\n}");
return Ok(code); return Ok(code);
} }
@ -182,7 +223,7 @@ fn main() -> Result<()> {
code_filename, code_filename,
output_filename(&script_name) output_filename(&script_name)
); );
com(&PrintType::Err, &compile_command); com(&PrintType::StdErr, &compile_command);
println!("[Compiled in {}ms]\n", now.elapsed().as_millis()); println!("[Compiled in {}ms]\n", now.elapsed().as_millis());
run_compiled(&script_name); run_compiled(&script_name);
} }