···160160 let a = name a.value in
161161 let b = do_group b.value in
162162 For_Name_DoGroup (a, b)
163163- | ForClause_For_Name_SequentialSep_DoGroup (a, b, c) ->
163163+ | ForClause_For_Name_SequentialSep_DoGroup (a, _, c) ->
164164 let a = name a.value in
165165- let b = sequential_sep b.value in
166165 let c = do_group c.value in
167167- For_Name_SequentialSep_DoGroup (a, b, c)
168168- | ForClause_For_Name_LineBreak_In_SequentialSep_DoGroup (a, _, c, d) ->
166166+ For_Name_DoGroup (a, c)
167167+ | ForClause_For_Name_LineBreak_In_SequentialSep_DoGroup (a, _, _, d) ->
169168 let a = name a.value in
170170- let c = sequential_sep c.value in
171169 let d = do_group d.value in
172172- For_Name_SequentialSep_DoGroup (a, c, d)
170170+ For_Name_DoGroup (a, d)
173171 | ForClause_For_Name_LineBreak_In_WordList_SequentialSep_DoGroup
174174- (a, _, c, d, e) ->
172172+ (a, _, c, _, e) ->
175173 let a = name a.value in
176174 let c = wordlist c.value in
177177- let d = sequential_sep d.value in
178175 let e = do_group e.value in
179179- For_Name_In_WordList_SequentialSep_DoGroup (a, c, d, e)
176176+ For_Name_In_WordList_DoGroup (a, c, e)
180177181178and wordlist : CST.wordlist -> wordlist =
182179 fun x ->
···512509 match x with
513510 | Separator_SeparatorOp_LineBreak (a, _) -> separator_op a.value
514511 | Separator_NewLineList _ -> Nosep
515515-516516-and sequential_sep : CST.sequential_sep -> sequential_sep =
517517- fun x ->
518518- match x with
519519- | SequentialSep_Semicolon_LineBreak _ -> Semicolon
520520- | SequentialSep_NewLineList _ -> Newline
521512522513and word : CST.word -> word = fun x -> match x with Word (_, b) -> word_cst b
523514and word_cst : CST.word_cst -> word_cst = fun v -> List.map word_component v
+51-27
src/lib/eval.ml
···6666 in
6767 o#complete_command ast
68686969- let map_words f ast =
6969+ let map_words ?(skip_for_clauses = true) f =
7070 let o =
7171 object (_)
7272- inherit default_map
7272+ inherit default_map as super
7373 method! word cst = f cst
7474+7575+ method! for_clause cst =
7676+ if skip_for_clauses then cst else super#for_clause cst
7477 end
7578 in
7676- o#complete_command ast
7979+ o
77807881 let tilde_expansion ctx ast =
7982 ( ctx,
···8386 | s -> s)
8487 ast )
85888686- let parameter_expansion ctx ast =
8989+ let parameter_expansion' ?skip_for_clauses ctx =
8790 ( ctx,
8888- map_words
9191+ map_words ?skip_for_clauses
8992 (List.concat_map (function
9093 | Ast.WordVariable v -> (
9194 match v with
···9497 | None -> [ Ast.WordName "" ]
9598 | Some cst -> cst)
9699 | _ -> Fmt.failwith "No support for variable attributes yet!")
9797- | s -> [ s ]))
9898- ast )
100100+ | s -> [ s ])) )
101101+102102+ let parameter_expansion ctx ast =
103103+ let ctx, o = parameter_expansion' ctx in
104104+ (ctx, o#complete_command ast)
99105100106 let assignments ctx ast =
101107 let o =
···210216 let glob_expand wc =
211217 Ast.word_components_to_string wc |> Globlon.glob |> Array.to_list
212218219219+ let word_glob_expand wc =
220220+ if List.exists needs_glob_expansion wc then glob_expand wc
221221+ else [ Ast.word_components_to_string wc ]
222222+213223 let args swc =
214224 List.concat_map
215225 (function
216216- | Ast.Suffix_redirect _ -> []
217217- | Suffix_word wc ->
218218- if List.exists needs_glob_expansion wc then glob_expand wc
219219- else [ Ast.word_components_to_string wc ])
226226+ | Ast.Suffix_redirect _ -> [] | Suffix_word wc -> word_glob_expand wc)
220227 swc
221228222222- let execute_commands initial_ctx local_switch p =
229229+ let rec execute_commands initial_ctx local_switch p =
223230 let rec loop ctx
224231 ((status_of_previous, stdout_of_previous) :
225232 ctx Exit.t * Eio_unix.source_ty Eio_unix.source option) :
···252259 Option.iter Eio.Flow.close some_write;
253260 loop ctx (res, some_read) rest))
254261 | Ast.SimpleCommand (Named (executable, Some suffix)) :: rest -> (
255255- let args =
256256- args suffix
257257- (* List.filter_map *)
258258- (* (function *)
259259- (* | Ast.Suffix_word w -> *)
260260- (* Some *)
261261- (* (String.concat "" *)
262262- (* @@ List.map Ast.word_component_to_string w) *)
263263- (* | Ast.Suffix_redirect _ -> None) *)
264264- (* suffix *)
265265- in
262262+ let args = args suffix in
266263 match
267264 Built_ins.of_args (Ast.word_components_to_string executable :: args)
268265 with
···299296 in
300297 Option.iter Eio.Flow.close some_write;
301298 loop ctx (res, some_read) rest))
299299+ | CompoundCommand (c, rdrs) :: _rest ->
300300+ let _rdrs =
301301+ List.map (handle_one_redirection ~sw:local_switch ctx) rdrs
302302+ in
303303+ let ctx = handle_compound_command ~sw:local_switch ctx c in
304304+ ctx
302305 | v :: _ ->
303306 Fmt.epr "TODO: %a" Yojson.Safe.pp (Ast.command_to_yojson v);
304307 failwith "Err"
···306309 in
307310 loop initial_ctx (Exit.zero initial_ctx, None) p
308311309309- let handle_single_pipeline ~sw ctx c =
312312+ and handle_single_pipeline ~sw ctx c =
310313 let pipeline = function
311314 | Ast.Pipeline p -> (Fun.id, p)
312315 | Ast.Pipeline_Bang p -> (Exit.not, p)
···349352 in
350353 fold (Noand_or, Exit.zero ctx) c
351354352352- let exec initial_ctx (ast : Ast.complete_command) =
355355+ and handle_for_clause ctx = function
356356+ | Ast.For_Name_DoGroup (_, (term, sep)) -> exec ctx (term, Some sep)
357357+ | Ast.For_Name_In_WordList_DoGroup (Name name, wdlist, (term, sep)) ->
358358+ let wdlist = Nlist.flatten @@ Nlist.map word_glob_expand wdlist in
359359+ (* Fmt.pr "List [%a]\n%!" Fmt.(list (list ~sep:Fmt.comma string)) (Nlist.to_list wdlist); *)
360360+ Nlist.fold_left
361361+ (fun _ word ->
362362+ (* let words = List.map (fun s -> Ast.WordLiteral s) words in *)
363363+ let s = S.update ctx.state ~param:name [ Ast.WordLiteral word ] in
364364+ let ctx = { ctx with state = s } in
365365+ let ctx, o = parameter_expansion' ~skip_for_clauses:false ctx in
366366+ let term = o#term term in
367367+ exec ctx (term, Some sep))
368368+ (Exit.zero ctx) wdlist
369369+370370+ and handle_compound_command ~sw:_ ctx = function
371371+ | Ast.ForClause fc -> handle_for_clause ctx fc
372372+ | _ as c ->
373373+ Fmt.failwith "Compound command not supported: %a" yojson_pp
374374+ (Ast.compound_command_to_yojson c)
375375+376376+ and exec initial_ctx (ast : Ast.complete_command) =
353377 let command, _ = ast in
354378 let rec loop : Eio.Switch.t -> ctx -> Ast.clist -> ctx Exit.t =
355379 fun sw ctx -> function
···360384 | v -> v)
361385 | _ -> Fmt.failwith "Background tasks not implemented yet!"
362386 in
363363- Eio.Switch.run @@ fun sw -> (loop sw initial_ctx command, ast)
387387+ Eio.Switch.run @@ fun sw -> loop sw initial_ctx command
364388365389 let apply_pair (a, b) f = f a b
366390 let ( ||> ) = apply_pair
···378402 List.fold_left
379403 (fun (ctx, cs) command ->
380404 let ctx = Exit.value ctx in
381381- let exit, ast = expand ctx command ||> redirect ||> execute in
405405+ let exit = expand ctx command ||> redirect ||> execute in
382406 match exit with
383407 | Exit.Nonzero { exit_code; message; should_exit; _ } -> (
384408 Option.iter (Fmt.epr "%s\n%!") message;
+1
src/lib/import.ml
···3434 `List (List.map (fun i -> f i) lst)
35353636 let pp elt ppf v = to_list v |> Fmt.(list elt) ppf
3737+ let flatten v = to_list v |> List.flatten |> of_list
3738end
38393940(* Nonempty, separated lists *)
+1-4
src/lib/sast.ml
···44444545and for_clause =
4646 | For_Name_DoGroup of name * do_group
4747- | For_Name_SequentialSep_DoGroup of name * sequential_sep * do_group
4848- | For_Name_In_WordList_SequentialSep_DoGroup of
4949- name * wordlist * sequential_sep * do_group
4747+ | For_Name_In_WordList_DoGroup of name * wordlist * do_group
50485149and wordlist = word Nlist.t
5250and case_clause = Cases of word * case_list | Case of word
···112110113111and here_end = HereEnd_Word of word
114112and separator = Ampersand | Semicolon | Nosep
115115-and sequential_sep = Semicolon | Newline
116113and word = word_cst
117114and word_cst = word_component list
118115
+28
test/forloops.t
···11+For loops
22+1. Simple examples
33+44+1.1 Simple loop without needing to expand
55+66+ $ osh -c "for i in a b c; do echo hello; done"
77+ hello
88+ hello
99+ hello
1010+1111+1212+1.2. Simple word list to echo
1313+1414+ $ osh -c "for foo in a b c; do echo \$foo; done"
1515+ a
1616+ b
1717+ c
1818+1919+1.3 In conjunction with path globbing
2020+2121+ $ touch hello.txt world.txt
2222+ $ sh -c "for foo in *.txt; do echo \$foo; done"
2323+ hello.txt
2424+ world.txt
2525+ $ osh -c "for foo in *.txt; do echo \$foo; done"
2626+ hello.txt
2727+ world.txt
2828+