@@ -3,8 +3,8 @@ use std::io::Write;
3
3
use std:: path:: Path ;
4
4
use std:: process:: { ExitStatus , Output , Stdio } ;
5
5
use std:: sync:: atomic:: AtomicBool ;
6
- use std:: sync:: { Arc , mpsc} ;
7
6
use std:: sync:: mpsc:: { Receiver , Sender } ;
7
+ use std:: sync:: { mpsc, Arc } ;
8
8
9
9
use conch_parser:: ast:: {
10
10
Arithmetic , AtomicCommandList , AtomicShellPipeableCommand , AtomicTopLevelCommand ,
@@ -14,6 +14,7 @@ use conch_parser::ast::{
14
14
} ;
15
15
use either:: { Either , Left , Right } ;
16
16
use filenamegen:: Glob ;
17
+ use itertools:: Itertools ;
17
18
use termcolor:: ColorSpec ;
18
19
19
20
use crate :: parser:: RunscriptLocation ;
@@ -152,14 +153,20 @@ struct Interrupter(Sender<Interrupt>);
152
153
153
154
impl Interruptable {
154
155
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
+ )
156
160
}
157
161
}
158
162
159
163
impl Interrupter {
160
164
fn new ( ) -> ( Interrupter , Interruptable ) {
161
165
let ( sender, receiver) = mpsc:: channel ( ) ;
162
- ( Interrupter ( sender) , Interruptable ( receiver, AtomicBool :: new ( false ) ) )
166
+ (
167
+ Interrupter ( sender) ,
168
+ Interruptable ( receiver, AtomicBool :: new ( false ) ) ,
169
+ )
163
170
}
164
171
165
172
fn interrupt ( & self ) {
@@ -205,7 +212,8 @@ fn exec_script_entries(
205
212
for job in jobs {
206
213
job. interrupt ( ) ;
207
214
}
208
- } ) . unwrap ( ) ;
215
+ } )
216
+ . unwrap ( ) ;
209
217
210
218
Ok ( ProcessOutput :: new ( true ) )
211
219
}
@@ -235,7 +243,9 @@ fn exec_listable_command(
235
243
) -> Result < ProcessOutput , CommandExecError > {
236
244
match command {
237
245
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
+ }
239
249
}
240
250
}
241
251
@@ -278,25 +288,41 @@ fn exec_simple_command(
278
288
// TODO: Env variables
279
289
// TODO: Redirects
280
290
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 < _ > , _ > > ( ) ?;
288
303
289
304
// 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
+ ) ;
291
309
292
310
// TODO: "Builtin" commands
293
311
// TODO: Interruption
294
312
295
313
let output = Command :: new ( & command_words[ 0 ] )
296
314
. args ( & command_words[ 1 ..] )
297
315
. 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
+ } )
300
326
. output ( )
301
327
. unwrap ( ) ;
302
328
@@ -307,11 +333,12 @@ fn evaluate_tl_word(
307
333
AtomicTopLevelWord ( word) : & AtomicTopLevelWord < String > ,
308
334
config : & ExecConfig ,
309
335
env : & HashMap < String , String > ,
310
- ) -> String {
336
+ ) -> Result < Vec < String > , CommandExecError > {
311
337
match word {
312
338
ComplexWord :: Concat ( words) => words
313
339
. iter ( )
314
340
. map ( |w| evaluate_word ( w, config, env) )
341
+ . flatten_ok ( )
315
342
. collect ( ) ,
316
343
ComplexWord :: Single ( word) => evaluate_word ( word, config, env) ,
317
344
}
@@ -335,13 +362,14 @@ fn evaluate_word(
335
362
> ,
336
363
config : & ExecConfig ,
337
364
env : & HashMap < String , String > ,
338
- ) -> String {
365
+ ) -> Result < Vec < String > , CommandExecError > {
339
366
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
342
369
. iter( )
343
370
. map( |w| evaluate_simple_word( w, config, env) )
344
- . collect ( ) ,
371
+ . flatten_ok( )
372
+ . collect:: <Result <String , _>>( ) ?] ) ,
345
373
Word :: Simple ( word) => evaluate_simple_word ( word, config, env) ,
346
374
}
347
375
}
@@ -361,12 +389,12 @@ fn evaluate_simple_word(
361
389
> ,
362
390
config : & ExecConfig ,
363
391
env : & HashMap < String , String > ,
364
- ) -> String {
392
+ ) -> Result < Vec < String > , CommandExecError > {
365
393
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 ) ,
370
398
SimpleWord :: Star => todo ! ( ) ,
371
399
SimpleWord :: Question => todo ! ( ) ,
372
400
SimpleWord :: SquareOpen => todo ! ( ) ,
@@ -376,6 +404,63 @@ fn evaluate_simple_word(
376
404
}
377
405
}
378
406
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
+
379
464
/*
380
465
381
466
fn exec_script_entries(entries: &[ScriptEntry], config: &ExecConfig, env_remap: &HashMap<String, String>) -> Result<ProcessOutput, (CommandExecError, ScriptEntry)> {
0 commit comments