Shells in OCaml
3
fork

Configure Feed

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

Simple sequences

Commands separated by newlines or semicolons will now be executed
sequentially. The logic for commands that exit with non-zero exit codes
will still need some close work.

+139 -114
+6 -6
src/lib/ast.ml
··· 37 37 | CST.SeparatorOp_Uppersand -> Ampersand 38 38 | CST.SeparatorOp_Semicolon -> Semicolon 39 39 40 - and clist : CST.clist -> clist = 41 - fun x -> 40 + and clist : ?sep:separator -> CST.clist -> clist = 41 + fun ?(sep = Nosep) x -> 42 42 match x with 43 43 | CList_CList_SeparatorOp_AndOr (a, b, c) -> 44 - let rest = clist a.value in 45 - let sep = separator_op b.value in 44 + let next_sep = separator_op b.value in 45 + let rest = clist ~sep:next_sep a.value in 46 46 let command = and_or c.value in 47 - Nslist.cons sep command rest 47 + Nslist.append rest (Nlist.Singleton (command, sep)) 48 48 | CList_AndOr a -> 49 49 let a = and_or a.value in 50 - Nslist.singleton Nosep a 50 + Nslist.singleton sep a 51 51 52 52 and and_or : ?sep:and_or -> CST.and_or -> pipeline and_or_list = 53 53 fun ?(sep = Noand_or) x ->
+113 -108
src/lib/eval.ml
··· 188 188 | Ast.IoRedirect_IoHere _ -> 189 189 Fmt.failwith "HERE documents not yet implemented!" 190 190 191 - let exec ctx (ast : Ast.complete_command) = 192 - let command, _ = ast in 193 - let execute_commands local_switch p = 194 - let rec loop (status_of_previous, stdout_of_previous) = function 195 - | Ast.SimpleCommand (Prefixed _) :: next -> 196 - loop (status_of_previous, stdout_of_previous) next 197 - | Ast.SimpleCommand (Named (executable, None)) :: rest -> ( 198 - let some_read, some_write = 199 - stdout_for_pipeline ~sw:local_switch rest 200 - in 201 - match stdout_of_previous with 202 - | None -> 203 - E.exec ctx.executor ?stdout:some_write 191 + let execute_commands ctx local_switch p = 192 + let rec loop (status_of_previous, stdout_of_previous) = function 193 + | Ast.SimpleCommand (Prefixed _) :: next -> 194 + loop (status_of_previous, stdout_of_previous) next 195 + | Ast.SimpleCommand (Named (executable, None)) :: rest -> ( 196 + let some_read, some_write = 197 + stdout_for_pipeline ~sw:local_switch rest 198 + in 199 + match stdout_of_previous with 200 + | None -> 201 + E.exec ctx.executor ?stdout:some_write 202 + (List.map word_component_to_string executable) 203 + |> Option.some 204 + | Some stdout -> 205 + let res = 206 + E.exec ctx.executor ~stdin:stdout ?stdout:some_write 204 207 (List.map word_component_to_string executable) 208 + in 209 + Option.iter Eio.Flow.close some_write; 210 + loop (Some res, some_read) rest) 211 + | Ast.SimpleCommand (Named (executable, Some suffix)) :: rest -> ( 212 + let args = 213 + List.filter_map 214 + (function 215 + | Ast.Suffix_word w -> 216 + Some (List.map word_component_to_string w) 217 + | Ast.Suffix_redirect _ -> None) 218 + suffix 219 + in 220 + let redirect = 221 + List.fold_left 222 + (fun acc -> function 223 + | Ast.Suffix_word _ -> acc 224 + | Ast.Suffix_redirect rdr -> 225 + handle_one_redirection ~sw:local_switch ctx rdr :: acc) 226 + [] suffix 227 + |> List.rev |> List.filter_map Fun.id 228 + in 229 + let some_read, some_write = 230 + stdout_for_pipeline ~sw:local_switch rest 231 + in 232 + match stdout_of_previous with 233 + | None -> 234 + let res = 235 + E.exec ~fds:redirect ctx.executor ?stdout:some_write 236 + (List.map word_component_to_string executable 237 + @ List.concat args) 205 238 |> Option.some 206 - | Some stdout -> 207 - let res = 208 - E.exec ctx.executor ~stdin:stdout ?stdout:some_write 209 - (List.map word_component_to_string executable) 210 - in 211 - Option.iter Eio.Flow.close some_write; 212 - loop (Some res, some_read) rest) 213 - | Ast.SimpleCommand (Named (executable, Some suffix)) :: rest -> ( 214 - let args = 215 - List.filter_map 216 - (function 217 - | Ast.Suffix_word w -> 218 - Some (List.map word_component_to_string w) 219 - | Ast.Suffix_redirect _ -> None) 220 - suffix 221 - in 222 - let redirect = 223 - List.fold_left 224 - (fun acc -> function 225 - | Ast.Suffix_word _ -> acc 226 - | Ast.Suffix_redirect rdr -> 227 - handle_one_redirection ~sw:local_switch ctx rdr :: acc) 228 - [] suffix 229 - |> List.rev |> List.filter_map Fun.id 230 - in 231 - let some_read, some_write = 232 - stdout_for_pipeline ~sw:local_switch rest 233 - in 234 - match stdout_of_previous with 235 - | None -> 236 - let res = 237 - E.exec ~fds:redirect ctx.executor ?stdout:some_write 238 - (List.map word_component_to_string executable 239 - @ List.concat args) 240 - |> Option.some 241 - in 242 - Option.iter Eio.Flow.close some_write; 243 - loop (res, some_read) rest 244 - | Some stdout -> 245 - let res = 246 - E.exec ~fds:redirect ctx.executor ~stdin:stdout 247 - ?stdout:some_write 248 - (List.map word_component_to_string executable 249 - @ List.concat args) 250 - |> Option.some 251 - in 252 - Option.iter Eio.Flow.close some_write; 253 - loop (res, some_read) rest) 254 - | v :: _ -> 255 - Fmt.epr "TODO: %a" Yojson.Safe.pp (Ast.command_to_yojson v); 256 - failwith "Err" 257 - | [] -> status_of_previous 258 - in 259 - loop (None, None) p 239 + in 240 + Option.iter Eio.Flow.close some_write; 241 + loop (res, some_read) rest 242 + | Some stdout -> 243 + let res = 244 + E.exec ~fds:redirect ctx.executor ~stdin:stdout 245 + ?stdout:some_write 246 + (List.map word_component_to_string executable 247 + @ List.concat args) 248 + |> Option.some 249 + in 250 + Option.iter Eio.Flow.close some_write; 251 + loop (res, some_read) rest) 252 + | v :: _ -> 253 + Fmt.epr "TODO: %a" Yojson.Safe.pp (Ast.command_to_yojson v); 254 + failwith "Err" 255 + | [] -> status_of_previous 260 256 in 257 + loop (None, None) p 258 + 259 + let handle_single_pipeline ~sw ctx c = 261 260 let pipeline = function 262 261 | Ast.Pipeline p -> (Fun.id, p) 263 262 | Ast.Pipeline_Bang p -> 264 263 (Option.map (fun i -> if Int.equal i 0 then -1 else 0), p) 265 264 in 266 - let loop : Eio.Switch.t -> Ast.clist -> int option = 265 + 266 + let rec fold : 267 + Ast.and_or * int option -> Ast.pipeline Ast.and_or_list -> int option = 268 + fun (sep, exit_so_far) pipe -> 269 + match (sep, pipe) with 270 + | And, Nlist.Singleton (p, _) -> ( 271 + match exit_so_far with 272 + | Some 0 -> 273 + let f, p = pipeline p in 274 + f @@ execute_commands ctx sw p 275 + | v -> v) 276 + | Or, Nlist.Singleton (p, _) -> ( 277 + match exit_so_far with 278 + | Some 0 -> Some 0 279 + | _ -> 280 + let f, p = pipeline p in 281 + f @@ execute_commands ctx sw p) 282 + | Noand_or, Nlist.Singleton (p, _) -> 283 + let f, p = pipeline p in 284 + f @@ execute_commands ctx sw p 285 + | Noand_or, Nlist.Cons ((p, next_sep), rest) -> 286 + let f, p = pipeline p in 287 + fold (next_sep, f (execute_commands ctx sw p)) rest 288 + | And, Nlist.Cons ((p, next_sep), rest) -> ( 289 + match exit_so_far with 290 + | Some 0 -> 291 + let f, p = pipeline p in 292 + fold (next_sep, f (execute_commands ctx sw p)) rest 293 + | (None | Some _) as v -> v) 294 + | Or, Nlist.Cons ((p, next_sep), rest) -> ( 295 + match exit_so_far with 296 + | Some 0 -> fold (next_sep, exit_so_far) rest 297 + | None | Some _ -> 298 + let f, p = pipeline p in 299 + fold (next_sep, f (execute_commands ctx sw p)) rest) 300 + in 301 + fold (Noand_or, None) c 302 + 303 + let exec ctx (ast : Ast.complete_command) = 304 + let command, _ = ast in 305 + let rec loop : Eio.Switch.t -> Ast.clist -> int option = 267 306 fun sw -> function 268 - | Nlist.Singleton (c, _) -> 269 - let rec fold : 270 - Ast.and_or * int option -> 271 - Ast.pipeline Ast.and_or_list -> 272 - int option = 273 - fun (sep, exit_so_far) pipe -> 274 - match (sep, pipe) with 275 - | And, Nlist.Singleton (p, _) -> ( 276 - match exit_so_far with 277 - | Some 0 -> 278 - let f, p = pipeline p in 279 - f @@ execute_commands sw p 280 - | v -> v) 281 - | Or, Nlist.Singleton (p, _) -> ( 282 - match exit_so_far with 283 - | Some 0 -> Some 0 284 - | _ -> 285 - let f, p = pipeline p in 286 - f @@ execute_commands sw p) 287 - | Noand_or, Nlist.Singleton (p, _) -> 288 - let f, p = pipeline p in 289 - f @@ execute_commands sw p 290 - | Noand_or, Nlist.Cons ((p, next_sep), rest) -> 291 - let f, p = pipeline p in 292 - fold (next_sep, f (execute_commands sw p)) rest 293 - | And, Nlist.Cons ((p, next_sep), rest) -> ( 294 - match exit_so_far with 295 - | Some 0 -> 296 - let f, p = pipeline p in 297 - fold (next_sep, f (execute_commands sw p)) rest 298 - | (None | Some _) as v -> v) 299 - | Or, Nlist.Cons ((p, next_sep), rest) -> ( 300 - match exit_so_far with 301 - | Some 0 -> fold (next_sep, exit_so_far) rest 302 - | None | Some _ -> 303 - let f, p = pipeline p in 304 - fold (next_sep, f (execute_commands sw p)) rest) 305 - in 306 - fold (Noand_or, None) c 307 - | _ -> Fmt.failwith "TODO!!!" 307 + | Nlist.Singleton (c, _) -> handle_single_pipeline ~sw ctx c 308 + | Nlist.Cons ((c, (Semicolon | Nosep)), cs) -> ( 309 + match handle_single_pipeline ~sw ctx c with 310 + | Some 0 -> loop sw cs 311 + | v -> v) 312 + | _ -> Fmt.failwith "Background tasks not implemented yet!" 308 313 in 309 314 Eio.Switch.run @@ fun sw -> (loop sw command, ctx, ast) 310 315
+20
test/simple.t
··· 104 104 $ osh -c "echo hello 3>out.txt >&3" 105 105 $ cat out.txt 106 106 hello 107 + 108 + 2.7 Sequences 109 + 110 + A simple, semicolon sequence. 111 + 112 + $ osh -c "echo hello; echo world; echo 'that is all'" 113 + hello 114 + world 115 + that is all 116 + 117 + $ cat > test.sh <<EOF 118 + > echo hello 119 + > echo world 120 + > echo 'that is all' 121 + > EOF 122 + 123 + $ osh test.sh 124 + hello 125 + world 126 + that is all