Shells in OCaml
3
fork

Configure Feed

Select the types of activity you want to include in your feed.

For loops

Basic for loop support. Lightly tested but already useful.

+87 -46
+6 -15
src/lib/ast.ml
··· 160 160 let a = name a.value in 161 161 let b = do_group b.value in 162 162 For_Name_DoGroup (a, b) 163 - | ForClause_For_Name_SequentialSep_DoGroup (a, b, c) -> 163 + | ForClause_For_Name_SequentialSep_DoGroup (a, _, c) -> 164 164 let a = name a.value in 165 - let b = sequential_sep b.value in 166 165 let c = do_group c.value in 167 - For_Name_SequentialSep_DoGroup (a, b, c) 168 - | ForClause_For_Name_LineBreak_In_SequentialSep_DoGroup (a, _, c, d) -> 166 + For_Name_DoGroup (a, c) 167 + | ForClause_For_Name_LineBreak_In_SequentialSep_DoGroup (a, _, _, d) -> 169 168 let a = name a.value in 170 - let c = sequential_sep c.value in 171 169 let d = do_group d.value in 172 - For_Name_SequentialSep_DoGroup (a, c, d) 170 + For_Name_DoGroup (a, d) 173 171 | ForClause_For_Name_LineBreak_In_WordList_SequentialSep_DoGroup 174 - (a, _, c, d, e) -> 172 + (a, _, c, _, e) -> 175 173 let a = name a.value in 176 174 let c = wordlist c.value in 177 - let d = sequential_sep d.value in 178 175 let e = do_group e.value in 179 - For_Name_In_WordList_SequentialSep_DoGroup (a, c, d, e) 176 + For_Name_In_WordList_DoGroup (a, c, e) 180 177 181 178 and wordlist : CST.wordlist -> wordlist = 182 179 fun x -> ··· 512 509 match x with 513 510 | Separator_SeparatorOp_LineBreak (a, _) -> separator_op a.value 514 511 | Separator_NewLineList _ -> Nosep 515 - 516 - and sequential_sep : CST.sequential_sep -> sequential_sep = 517 - fun x -> 518 - match x with 519 - | SequentialSep_Semicolon_LineBreak _ -> Semicolon 520 - | SequentialSep_NewLineList _ -> Newline 521 512 522 513 and word : CST.word -> word = fun x -> match x with Word (_, b) -> word_cst b 523 514 and word_cst : CST.word_cst -> word_cst = fun v -> List.map word_component v
+51 -27
src/lib/eval.ml
··· 66 66 in 67 67 o#complete_command ast 68 68 69 - let map_words f ast = 69 + let map_words ?(skip_for_clauses = true) f = 70 70 let o = 71 71 object (_) 72 - inherit default_map 72 + inherit default_map as super 73 73 method! word cst = f cst 74 + 75 + method! for_clause cst = 76 + if skip_for_clauses then cst else super#for_clause cst 74 77 end 75 78 in 76 - o#complete_command ast 79 + o 77 80 78 81 let tilde_expansion ctx ast = 79 82 ( ctx, ··· 83 86 | s -> s) 84 87 ast ) 85 88 86 - let parameter_expansion ctx ast = 89 + let parameter_expansion' ?skip_for_clauses ctx = 87 90 ( ctx, 88 - map_words 91 + map_words ?skip_for_clauses 89 92 (List.concat_map (function 90 93 | Ast.WordVariable v -> ( 91 94 match v with ··· 94 97 | None -> [ Ast.WordName "" ] 95 98 | Some cst -> cst) 96 99 | _ -> Fmt.failwith "No support for variable attributes yet!") 97 - | s -> [ s ])) 98 - ast ) 100 + | s -> [ s ])) ) 101 + 102 + let parameter_expansion ctx ast = 103 + let ctx, o = parameter_expansion' ctx in 104 + (ctx, o#complete_command ast) 99 105 100 106 let assignments ctx ast = 101 107 let o = ··· 210 216 let glob_expand wc = 211 217 Ast.word_components_to_string wc |> Globlon.glob |> Array.to_list 212 218 219 + let word_glob_expand wc = 220 + if List.exists needs_glob_expansion wc then glob_expand wc 221 + else [ Ast.word_components_to_string wc ] 222 + 213 223 let args swc = 214 224 List.concat_map 215 225 (function 216 - | Ast.Suffix_redirect _ -> [] 217 - | Suffix_word wc -> 218 - if List.exists needs_glob_expansion wc then glob_expand wc 219 - else [ Ast.word_components_to_string wc ]) 226 + | Ast.Suffix_redirect _ -> [] | Suffix_word wc -> word_glob_expand wc) 220 227 swc 221 228 222 - let execute_commands initial_ctx local_switch p = 229 + let rec execute_commands initial_ctx local_switch p = 223 230 let rec loop ctx 224 231 ((status_of_previous, stdout_of_previous) : 225 232 ctx Exit.t * Eio_unix.source_ty Eio_unix.source option) : ··· 252 259 Option.iter Eio.Flow.close some_write; 253 260 loop ctx (res, some_read) rest)) 254 261 | Ast.SimpleCommand (Named (executable, Some suffix)) :: rest -> ( 255 - let args = 256 - args suffix 257 - (* List.filter_map *) 258 - (* (function *) 259 - (* | Ast.Suffix_word w -> *) 260 - (* Some *) 261 - (* (String.concat "" *) 262 - (* @@ List.map Ast.word_component_to_string w) *) 263 - (* | Ast.Suffix_redirect _ -> None) *) 264 - (* suffix *) 265 - in 262 + let args = args suffix in 266 263 match 267 264 Built_ins.of_args (Ast.word_components_to_string executable :: args) 268 265 with ··· 299 296 in 300 297 Option.iter Eio.Flow.close some_write; 301 298 loop ctx (res, some_read) rest)) 299 + | CompoundCommand (c, rdrs) :: _rest -> 300 + let _rdrs = 301 + List.map (handle_one_redirection ~sw:local_switch ctx) rdrs 302 + in 303 + let ctx = handle_compound_command ~sw:local_switch ctx c in 304 + ctx 302 305 | v :: _ -> 303 306 Fmt.epr "TODO: %a" Yojson.Safe.pp (Ast.command_to_yojson v); 304 307 failwith "Err" ··· 306 309 in 307 310 loop initial_ctx (Exit.zero initial_ctx, None) p 308 311 309 - let handle_single_pipeline ~sw ctx c = 312 + and handle_single_pipeline ~sw ctx c = 310 313 let pipeline = function 311 314 | Ast.Pipeline p -> (Fun.id, p) 312 315 | Ast.Pipeline_Bang p -> (Exit.not, p) ··· 349 352 in 350 353 fold (Noand_or, Exit.zero ctx) c 351 354 352 - let exec initial_ctx (ast : Ast.complete_command) = 355 + and handle_for_clause ctx = function 356 + | Ast.For_Name_DoGroup (_, (term, sep)) -> exec ctx (term, Some sep) 357 + | Ast.For_Name_In_WordList_DoGroup (Name name, wdlist, (term, sep)) -> 358 + let wdlist = Nlist.flatten @@ Nlist.map word_glob_expand wdlist in 359 + (* Fmt.pr "List [%a]\n%!" Fmt.(list (list ~sep:Fmt.comma string)) (Nlist.to_list wdlist); *) 360 + Nlist.fold_left 361 + (fun _ word -> 362 + (* let words = List.map (fun s -> Ast.WordLiteral s) words in *) 363 + let s = S.update ctx.state ~param:name [ Ast.WordLiteral word ] in 364 + let ctx = { ctx with state = s } in 365 + let ctx, o = parameter_expansion' ~skip_for_clauses:false ctx in 366 + let term = o#term term in 367 + exec ctx (term, Some sep)) 368 + (Exit.zero ctx) wdlist 369 + 370 + and handle_compound_command ~sw:_ ctx = function 371 + | Ast.ForClause fc -> handle_for_clause ctx fc 372 + | _ as c -> 373 + Fmt.failwith "Compound command not supported: %a" yojson_pp 374 + (Ast.compound_command_to_yojson c) 375 + 376 + and exec initial_ctx (ast : Ast.complete_command) = 353 377 let command, _ = ast in 354 378 let rec loop : Eio.Switch.t -> ctx -> Ast.clist -> ctx Exit.t = 355 379 fun sw ctx -> function ··· 360 384 | v -> v) 361 385 | _ -> Fmt.failwith "Background tasks not implemented yet!" 362 386 in 363 - Eio.Switch.run @@ fun sw -> (loop sw initial_ctx command, ast) 387 + Eio.Switch.run @@ fun sw -> loop sw initial_ctx command 364 388 365 389 let apply_pair (a, b) f = f a b 366 390 let ( ||> ) = apply_pair ··· 378 402 List.fold_left 379 403 (fun (ctx, cs) command -> 380 404 let ctx = Exit.value ctx in 381 - let exit, ast = expand ctx command ||> redirect ||> execute in 405 + let exit = expand ctx command ||> redirect ||> execute in 382 406 match exit with 383 407 | Exit.Nonzero { exit_code; message; should_exit; _ } -> ( 384 408 Option.iter (Fmt.epr "%s\n%!") message;
+1
src/lib/import.ml
··· 34 34 `List (List.map (fun i -> f i) lst) 35 35 36 36 let pp elt ppf v = to_list v |> Fmt.(list elt) ppf 37 + let flatten v = to_list v |> List.flatten |> of_list 37 38 end 38 39 39 40 (* Nonempty, separated lists *)
+1 -4
src/lib/sast.ml
··· 44 44 45 45 and for_clause = 46 46 | For_Name_DoGroup of name * do_group 47 - | For_Name_SequentialSep_DoGroup of name * sequential_sep * do_group 48 - | For_Name_In_WordList_SequentialSep_DoGroup of 49 - name * wordlist * sequential_sep * do_group 47 + | For_Name_In_WordList_DoGroup of name * wordlist * do_group 50 48 51 49 and wordlist = word Nlist.t 52 50 and case_clause = Cases of word * case_list | Case of word ··· 112 110 113 111 and here_end = HereEnd_Word of word 114 112 and separator = Ampersand | Semicolon | Nosep 115 - and sequential_sep = Semicolon | Newline 116 113 and word = word_cst 117 114 and word_cst = word_component list 118 115
+28
test/forloops.t
··· 1 + For loops 2 + 1. Simple examples 3 + 4 + 1.1 Simple loop without needing to expand 5 + 6 + $ osh -c "for i in a b c; do echo hello; done" 7 + hello 8 + hello 9 + hello 10 + 11 + 12 + 1.2. Simple word list to echo 13 + 14 + $ osh -c "for foo in a b c; do echo \$foo; done" 15 + a 16 + b 17 + c 18 + 19 + 1.3 In conjunction with path globbing 20 + 21 + $ touch hello.txt world.txt 22 + $ sh -c "for foo in *.txt; do echo \$foo; done" 23 + hello.txt 24 + world.txt 25 + $ osh -c "for foo in *.txt; do echo \$foo; done" 26 + hello.txt 27 + world.txt 28 +