···2424 background_jobs = [];
2525 last_background_process = "";
2626 argv = Array.of_list (pos_zero :: rest);
2727+ program = pos_zero;
2828+ functions = [];
2729 }
2830 in
2931 match (file, command) with
+4-4
src/lib/ast.ml
···148148 let rest = term a.value in
149149 let b = separator b.value in
150150 let c = and_or c.value in
151151- Nslist.cons b c rest
151151+ Nslist.append rest (Nslist.singleton b c)
152152 | Term_AndOr a ->
153153 let a = and_or a.value in
154154 Nslist.singleton Semicolon a
···321321 | FunctionDefinition_Fname_Lparen_Rparen_LineBreak_FunctionBody (a, _, c) ->
322322 let CST.(Fname_Name (Name a)) = a.value in
323323 let c = function_body c.value in
324324- Function_definition (a, c)
324324+ (a, c)
325325326326and function_body : CST.function_body -> function_body =
327327 fun x ->
328328 match x with
329329 | FunctionBody_CompoundCommand a ->
330330 let a = compound_command a.value in
331331- Function_body (a, [])
331331+ (a, [])
332332 | FunctionBody_CompoundCommand_RedirectList (a, b) ->
333333 let a = compound_command a.value in
334334 let b = redirect_list b.value in
335335- Function_body (a, b)
335335+ (a, b)
336336337337and brace_group : CST.brace_group -> brace_group =
338338 fun x ->
+79-68
src/lib/eval.ml
···3939 background_jobs : J.t list;
4040 last_background_process : string;
4141 async_switch : Eio.Switch.t;
4242+ program : string;
4243 argv : string array;
4444+ functions : (string * Ast.compound_command) list;
4345 }
44464547 let clear_local_state ctx = { ctx with local_state = [] }
···271273 (Ast.SimpleCommand (Named (executable, suffix)) :: rest)
272274 | Ast.SimpleCommand (Named (executable, None)) :: rest -> (
273275 let ctx, executable = expand_cst ctx executable in
274274- match
275275- Built_ins.of_args
276276- [ handle_word_components_to_string ctx executable ]
277277- with
276276+ let executable = handle_word_components_to_string ctx executable in
277277+ match Built_ins.of_args [ executable ] with
278278 | Some (Ok bi) ->
279279 let ctx = handle_built_in ctx bi in
280280 let built_in = ctx >|= fun _ -> () in
···283283 | Some (Error _) ->
284284 (ctx, handle_job ~pgid job (`Built_in (Exit.nonzero () 1)))
285285 | None -> (
286286- let some_read, some_write =
287287- stdout_for_pipeline ctx ~sw:pipeline_switch rest
288288- in
289289- match stdout_of_previous with
290290- | None ->
291291- let executable =
292292- handle_word_components_to_string ctx executable
293293- in
294294- let ctx, job =
295295- exec_process ctx job ?stdout:some_write ~pgid [ executable ]
296296- in
297297- Option.iter Eio.Flow.close some_write;
298298- loop ctx job (pgid, some_read) rest
299299- | Some stdout ->
300300- let executable =
301301- handle_word_components_to_string ctx executable
302302- in
303303- let ctx, job =
304304- exec_process ctx job ~stdin:stdout ?stdout:some_write ~pgid
305305- [ executable ]
286286+ match
287287+ handle_function_application ctx ~name:executable [ executable ]
288288+ with
289289+ | Some ctx ->
290290+ (* TODO: Proper job stuff and redirects etc. *)
291291+ loop (Exit.value ctx) job (pgid, stdout_of_previous) rest
292292+ | None -> (
293293+ let some_read, some_write =
294294+ stdout_for_pipeline ctx ~sw:pipeline_switch rest
306295 in
307307- Option.iter Eio.Flow.close some_write;
308308- loop ctx job (pgid, some_read) rest))
296296+ match stdout_of_previous with
297297+ | None ->
298298+ let ctx, job =
299299+ exec_process ctx job ?stdout:some_write ~pgid
300300+ [ executable ]
301301+ in
302302+ Option.iter Eio.Flow.close some_write;
303303+ loop ctx job (pgid, some_read) rest
304304+ | Some stdout ->
305305+ let ctx, job =
306306+ exec_process ctx job ~stdin:stdout ?stdout:some_write
307307+ ~pgid [ executable ]
308308+ in
309309+ Option.iter Eio.Flow.close some_write;
310310+ loop ctx job (pgid, some_read) rest)))
309311 | Ast.SimpleCommand (Named (executable, Some suffix)) :: rest -> (
310312 let ctx, executable = expand_cst ctx executable in
313313+ let executable = handle_word_components_to_string ctx executable in
311314 let ctx, suffix = expand_redirects (ctx, []) suffix in
312315 let args = args ctx suffix in
313313- match
314314- Built_ins.of_args
315315- (handle_word_components_to_string ctx executable :: args)
316316- with
316316+ match Built_ins.of_args (executable :: args) with
317317 | Some (Ok bi) ->
318318 let ctx = handle_built_in ctx bi in
319319 let built_in = ctx >|= fun _ -> () in
···322322 | Some (Error _) ->
323323 (ctx, handle_job ~pgid job (`Built_in (Exit.nonzero () 1)))
324324 | None -> (
325325- let redirect =
326326- List.fold_left
327327- (fun acc -> function
328328- | Ast.Suffix_word _ -> acc
329329- | Ast.Suffix_redirect rdr ->
330330- handle_one_redirection ~sw:pipeline_switch ctx rdr
331331- :: acc)
332332- [] suffix
333333- |> List.rev |> List.filter_map Fun.id
334334- in
335335- let some_read, some_write =
336336- stdout_for_pipeline ~sw:pipeline_switch ctx rest
337337- in
338338- match stdout_of_previous with
339339- | None ->
340340- let executable =
341341- handle_word_components_to_string ctx executable
325325+ match
326326+ handle_function_application ctx ~name:executable
327327+ (ctx.program :: args)
328328+ with
329329+ | Some ctx ->
330330+ (* TODO: Proper job stuff and redirects etc. *)
331331+ loop (Exit.value ctx) job (pgid, stdout_of_previous) rest
332332+ | None -> (
333333+ let redirect =
334334+ List.fold_left
335335+ (fun acc -> function
336336+ | Ast.Suffix_word _ -> acc
337337+ | Ast.Suffix_redirect rdr ->
338338+ handle_one_redirection ~sw:pipeline_switch ctx rdr
339339+ :: acc)
340340+ [] suffix
341341+ |> List.rev |> List.filter_map Fun.id
342342 in
343343- let ctx, job =
344344- exec_process ctx job ~fds:redirect ?stdout:some_write ~pgid
345345- (executable :: args)
343343+ let some_read, some_write =
344344+ stdout_for_pipeline ~sw:pipeline_switch ctx rest
346345 in
347347- Option.iter Eio.Flow.close some_write;
348348- loop ctx job (pgid, some_read) rest
349349- | Some stdout ->
350350- let executable =
351351- handle_word_components_to_string ctx executable
352352- in
353353- let ctx, job =
354354- exec_process ctx job ~fds:redirect ~stdin:stdout
355355- ?stdout:some_write ~pgid (executable :: args)
356356- in
357357- Option.iter Eio.Flow.close some_write;
358358- loop ctx job (pgid, some_read) rest))
359359- | CompoundCommand (c, rdrs) :: _rest ->
346346+ match stdout_of_previous with
347347+ | None ->
348348+ let ctx, job =
349349+ exec_process ctx job ~fds:redirect ?stdout:some_write
350350+ ~pgid (executable :: args)
351351+ in
352352+ Option.iter Eio.Flow.close some_write;
353353+ loop ctx job (pgid, some_read) rest
354354+ | Some stdout ->
355355+ let ctx, job =
356356+ exec_process ctx job ~fds:redirect ~stdin:stdout
357357+ ?stdout:some_write ~pgid (executable :: args)
358358+ in
359359+ Option.iter Eio.Flow.close some_write;
360360+ loop ctx job (pgid, some_read) rest)))
361361+ | CompoundCommand (c, rdrs) :: rest ->
360362 let _rdrs =
361363 List.map (handle_one_redirection ~sw:pipeline_switch ctx) rdrs
362364 in
363365 (* TODO: No way this is right *)
364364- (Exit.value @@ handle_compound_command ctx c, job)
365365- | v :: _ ->
366366- Fmt.epr "TODO: %a" Yojson.Safe.pp (Ast.command_to_yojson v);
367367- failwith "Err"
366366+ let ctx = handle_compound_command ctx c in
367367+ loop (Exit.value ctx) job (pgid, None) rest
368368+ | FunctionDefinition (name, (body, _rdrs)) :: rest ->
369369+ let ctx = { ctx with functions = (name, body) :: ctx.functions } in
370370+ loop ctx job (pgid, None) rest
368371 | [] -> (ctx, job)
369372 in
370373 (* HACK: when running the pipeline, we need a process group to
···393396 Exit.zero { ctx with background_jobs = job :: ctx.background_jobs }
394397 end
395398396396- and expand_cst (ctx : ctx) cst =
399399+ and expand_cst (ctx : ctx) cst : ctx * Ast.word_cst =
397400 let cst = tilde_expansion ctx cst in
398401 let _, o = parameter_expansion' ctx in
399402 (ctx, o cst)
···495498 match v with
496499 | Ast.ForClause fc -> handle_for_clause ctx fc
497500 | Ast.IfClause if_ -> handle_if_clause ctx if_
501501+ | Ast.BraceGroup (term, sep) -> exec ctx (term, Some sep)
498502 | _ as c ->
499503 Fmt.epr "Compound command not supported: %a\n%!" yojson_pp
500504 (Ast.compound_command_to_yojson c);
501505 exit 127
506506+507507+ and handle_function_application (ctx : ctx) ~name argv : ctx Exit.t option =
508508+ match List.assoc_opt name ctx.functions with
509509+ | None -> None
510510+ | Some commands ->
511511+ let ctx = { ctx with argv = Array.of_list argv } in
512512+ Option.some @@ (handle_compound_command ctx commands >|= fun _ -> ctx)
502513503514 and needs_subshelling = function
504515 | [] -> false
+2-2
src/lib/sast.ml
···67676868and while_clause = While of compound_list * do_group
6969and until_clause = Until of compound_list * do_group
7070-and function_definition = Function_definition of string * function_body
7171-and function_body = Function_body of compound_command * redirects
7070+and function_definition = string * function_body
7171+and function_body = compound_command * redirects
7272and brace_group = compound_list
7373and do_group = compound_list
7474
+18
test/functions.t
···11Testing functions and position arguments.
2233+First is simply that the original positional arguments are preserved.
44+35 $ cat > test.sh << EOF
46 > echo "\$1 from \$0"
57 > EOF
···911 $ msh test.sh hello
1012 hello from test.sh
11131414+Next, do they work inside function definitions!
1515+1616+ $ cat > test.sh << EOF
1717+ > shout () {
1818+ > echo \$0
1919+ > echo \$1 | tr a-z A-Z
2020+ > }
2121+ > shout "hi there"
2222+ > EOF
2323+2424+ $ sh test.sh
2525+ test.sh
2626+ HI THERE
2727+ $ msh test.sh
2828+ test.sh
2929+ HI THERE
+6-3
test/simple.t
···6464 $ msh -c "lssssss"
6565 msh: command not found: lssssss
6666 [127]
6767- $ sh -c "lssssss | echo 'all good'"
6767+ $ sh -c "lssssss 2>&1 > /dev/null | echo 'all good'"
6868 all good
6969- sh: line 1: lssssss: command not found
7070- $ msh -c "lssssss | echo 'all good'"
6969+7070+TODO: We need to also redirect error messages from the shell,
7171+for example the command not being found here :/
7272+7373+ $ msh -c "lssssss 2>&1 > /dev/null | echo 'all good'"
7174 msh: command not found: lssssss
7275 all good
7376