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.

Changed files
+139 -114
src
test
+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