+6
-6
src/lib/ast.ml
+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
+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
+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