Skip to content

Commit 36361a7

Browse files
committed
Add basic parameters and substs
1 parent 97dd38f commit 36361a7

File tree

3 files changed

+121
-25
lines changed

3 files changed

+121
-25
lines changed

Cargo.lock

+10
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,4 @@ linked-hash-map = "0.5"
3232
crossbeam-utils = "0.8"
3333

3434
trace = { version = "0.1", optional = true }
35+
itertools = "0.10.1"

src/exec.rs

+110-25
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ use std::io::Write;
33
use std::path::Path;
44
use std::process::{ExitStatus, Output, Stdio};
55
use std::sync::atomic::AtomicBool;
6-
use std::sync::{Arc, mpsc};
76
use std::sync::mpsc::{Receiver, Sender};
7+
use std::sync::{mpsc, Arc};
88

99
use conch_parser::ast::{
1010
Arithmetic, AtomicCommandList, AtomicShellPipeableCommand, AtomicTopLevelCommand,
@@ -14,6 +14,7 @@ use conch_parser::ast::{
1414
};
1515
use either::{Either, Left, Right};
1616
use filenamegen::Glob;
17+
use itertools::Itertools;
1718
use termcolor::ColorSpec;
1819

1920
use crate::parser::RunscriptLocation;
@@ -152,14 +153,20 @@ struct Interrupter(Sender<Interrupt>);
152153

153154
impl Interruptable {
154155
fn was_interrupted(&self) -> bool {
155-
self.1.fetch_or(self.0.try_recv().is_ok(), std::sync::atomic::Ordering::AcqRel)
156+
self.1.fetch_or(
157+
self.0.try_recv().is_ok(),
158+
std::sync::atomic::Ordering::AcqRel,
159+
)
156160
}
157161
}
158162

159163
impl Interrupter {
160164
fn new() -> (Interrupter, Interruptable) {
161165
let (sender, receiver) = mpsc::channel();
162-
(Interrupter(sender), Interruptable(receiver, AtomicBool::new(false)))
166+
(
167+
Interrupter(sender),
168+
Interruptable(receiver, AtomicBool::new(false)),
169+
)
163170
}
164171

165172
fn interrupt(&self) {
@@ -205,7 +212,8 @@ fn exec_script_entries(
205212
for job in jobs {
206213
job.interrupt();
207214
}
208-
}).unwrap();
215+
})
216+
.unwrap();
209217

210218
Ok(ProcessOutput::new(true))
211219
}
@@ -235,7 +243,9 @@ fn exec_listable_command(
235243
) -> Result<ProcessOutput, CommandExecError> {
236244
match command {
237245
ListableCommand::Pipe(negate, commands) => todo!(),
238-
ListableCommand::Single(command) => exec_pipeable_command(&command, config, env, interrupter),
246+
ListableCommand::Single(command) => {
247+
exec_pipeable_command(&command, config, env, interrupter)
248+
}
239249
}
240250
}
241251

@@ -278,25 +288,41 @@ fn exec_simple_command(
278288
// TODO: Env variables
279289
// TODO: Redirects
280290

281-
let command_words = command.redirects_or_cmd_words.iter().filter_map(|r| {
282-
if let RedirectOrCmdWord::CmdWord(w) = r {
283-
Some(evaluate_tl_word(w, config, env))
284-
} else {
285-
None
286-
}
287-
}).collect::<Vec<_>>();
291+
let command_words = command
292+
.redirects_or_cmd_words
293+
.iter()
294+
.filter_map(|r| {
295+
if let RedirectOrCmdWord::CmdWord(w) = r {
296+
Some(evaluate_tl_word(w, config, env))
297+
} else {
298+
None
299+
}
300+
})
301+
.flatten_ok()
302+
.collect::<Result<Vec<_>, _>>()?;
288303

289304
// TODO: Print pre-evaluated command words
290-
eprintln!(">{}", command_words.iter().fold("".to_owned(), |acc, w| { acc + " " + w }));
305+
eprintln!(
306+
">{}",
307+
command_words.join(" ")
308+
);
291309

292310
// TODO: "Builtin" commands
293311
// TODO: Interruption
294312

295313
let output = Command::new(&command_words[0])
296314
.args(&command_words[1..])
297315
.stdin(Stdio::inherit())
298-
.stdout(if config.capture_stdout { Stdio::piped() } else { Stdio::inherit() })
299-
.stderr(if config.capture_stdout { Stdio::piped() } else { Stdio::inherit() })
316+
.stdout(if config.capture_stdout {
317+
Stdio::piped()
318+
} else {
319+
Stdio::inherit()
320+
})
321+
.stderr(if config.capture_stdout {
322+
Stdio::piped()
323+
} else {
324+
Stdio::inherit()
325+
})
300326
.output()
301327
.unwrap();
302328

@@ -307,11 +333,12 @@ fn evaluate_tl_word(
307333
AtomicTopLevelWord(word): &AtomicTopLevelWord<String>,
308334
config: &ExecConfig,
309335
env: &HashMap<String, String>,
310-
) -> String {
336+
) -> Result<Vec<String>, CommandExecError> {
311337
match word {
312338
ComplexWord::Concat(words) => words
313339
.iter()
314340
.map(|w| evaluate_word(w, config, env))
341+
.flatten_ok()
315342
.collect(),
316343
ComplexWord::Single(word) => evaluate_word(word, config, env),
317344
}
@@ -335,13 +362,14 @@ fn evaluate_word(
335362
>,
336363
config: &ExecConfig,
337364
env: &HashMap<String, String>,
338-
) -> String {
365+
) -> Result<Vec<String>, CommandExecError> {
339366
match word {
340-
Word::SingleQuoted(literal) => literal.clone(),
341-
Word::DoubleQuoted(words) => words
367+
Word::SingleQuoted(literal) => Ok(vec![literal.clone()]),
368+
Word::DoubleQuoted(words) => Ok(vec![words
342369
.iter()
343370
.map(|w| evaluate_simple_word(w, config, env))
344-
.collect(),
371+
.flatten_ok()
372+
.collect::<Result<String, _>>()?]),
345373
Word::Simple(word) => evaluate_simple_word(word, config, env),
346374
}
347375
}
@@ -361,12 +389,12 @@ fn evaluate_simple_word(
361389
>,
362390
config: &ExecConfig,
363391
env: &HashMap<String, String>,
364-
) -> String {
392+
) -> Result<Vec<String>, CommandExecError> {
365393
match word {
366-
SimpleWord::Literal(s) => s.clone(),
367-
SimpleWord::Escaped(s) => s.clone(),
368-
SimpleWord::Param(_) => todo!(),
369-
SimpleWord::Subst(_) => todo!(),
394+
SimpleWord::Literal(s) => Ok(vec![s.clone()]),
395+
SimpleWord::Escaped(s) => Ok(vec![s.clone()]),
396+
SimpleWord::Param(p) => Ok(evaluate_parameter(p, config, env)),
397+
SimpleWord::Subst(p) => evaluate_param_subst(p, config, env),
370398
SimpleWord::Star => todo!(),
371399
SimpleWord::Question => todo!(),
372400
SimpleWord::SquareOpen => todo!(),
@@ -376,6 +404,63 @@ fn evaluate_simple_word(
376404
}
377405
}
378406

407+
fn evaluate_param_subst(
408+
param: &ParameterSubstitution<
409+
Parameter<String>,
410+
AtomicTopLevelWord<String>,
411+
AtomicTopLevelCommand<String>,
412+
Arithmetic<String>,
413+
>,
414+
config: &ExecConfig,
415+
env: &HashMap<String, String>,
416+
) -> Result<Vec<String>, CommandExecError> {
417+
match param {
418+
ParameterSubstitution::Command(commands) => exec_script_entries(commands, config, env).map(|output| vec![String::from_utf8(output.stdout).unwrap()]),
419+
ParameterSubstitution::Len(p) => Ok(vec![format!("{}", match p {
420+
Parameter::At | Parameter::Star => config.positional_args.len(),
421+
p => evaluate_parameter(p, config, env).into_iter().map(|s| s.len()).reduce(|acc, s| acc + s + 1).unwrap_or(0),
422+
})]),
423+
ParameterSubstitution::Arith(_) => todo!(),
424+
ParameterSubstitution::Default(_, _, _) => todo!(),
425+
ParameterSubstitution::Assign(_, _, _) => todo!(),
426+
ParameterSubstitution::Error(_, _, _) => todo!(),
427+
ParameterSubstitution::Alternative(_, _, _) => todo!(),
428+
ParameterSubstitution::RemoveSmallestSuffix(_, _) => todo!(),
429+
ParameterSubstitution::RemoveLargestSuffix(_, _) => todo!(),
430+
ParameterSubstitution::RemoveSmallestPrefix(_, _) => todo!(),
431+
ParameterSubstitution::RemoveLargestPrefix(_, _) => todo!(),
432+
}
433+
}
434+
435+
fn evaluate_parameter(
436+
parameter: &Parameter<String>,
437+
config: &ExecConfig,
438+
env: &HashMap<String, String>,
439+
) -> Vec<String> {
440+
match parameter {
441+
Parameter::Positional(n) => config
442+
.positional_args
443+
.get(*n as usize)
444+
.cloned()
445+
.into_iter()
446+
.collect(),
447+
Parameter::Var(name) => env
448+
.get(name)
449+
.cloned()
450+
.or_else(|| std::env::var(name).ok())
451+
.into_iter()
452+
.collect(),
453+
Parameter::At => config.positional_args.clone(),
454+
Parameter::Pound => vec![format!("{}", config.positional_args.len())],
455+
Parameter::Dollar => vec![format!("{}", std::process::id())],
456+
457+
Parameter::Star => todo!(), // Like @ but runs evaluate_simple_word on each word
458+
Parameter::Question => todo!(), // Exit code of previous top-level command
459+
Parameter::Dash => todo!(), // Options of current run invocation. Perhaps could be useful?
460+
Parameter::Bang => todo!(), // PID of most recent job
461+
}
462+
}
463+
379464
/*
380465
381466
fn exec_script_entries(entries: &[ScriptEntry], config: &ExecConfig, env_remap: &HashMap<String, String>) -> Result<ProcessOutput, (CommandExecError, ScriptEntry)> {

0 commit comments

Comments
 (0)