Shells in OCaml
1(* Our AST is essentially Morbig's CST without linebreaks, newlines
2 etc. *)
3open Morbig
4open Import
5include Sast
6
7type t = complete_commands
8
9let rec program : CST.program -> complete_commands =
10 fun x ->
11 match x with
12 | Program_LineBreak_CompleteCommands_LineBreak (_, b, _) ->
13 complete_commands b.value
14 | Program_LineBreak _ -> []
15
16and complete_commands : CST.complete_commands -> complete_commands =
17 fun x ->
18 match x with
19 | CompleteCommands_CompleteCommands_NewlineList_CompleteCommand (a, _, c) ->
20 let a = complete_commands a.value in
21 let c = complete_command c.value in
22 a @ [ c ]
23 | CompleteCommands_CompleteCommand a ->
24 let a = complete_command a.value in
25 [ a ]
26
27and complete_command : CST.complete_command -> complete_command =
28 fun x ->
29 match x with
30 | CompleteCommand_CList_SeparatorOp (a, b) ->
31 (clist a.value, Some (separator_op b.value))
32 | CompleteCommand_CList a ->
33 let a = clist a.value in
34 (a, None)
35
36and separator_op : CST.separator_op -> separator = function
37 | CST.SeparatorOp_Uppersand -> Ampersand
38 | CST.SeparatorOp_Semicolon -> Semicolon
39
40and clist : ?sep:separator -> CST.clist -> clist =
41 fun ?(sep = Semicolon) x ->
42 match x with
43 | CList_CList_SeparatorOp_AndOr (a, b, c) ->
44 let next_sep = separator_op b.value in
45 let rest = clist ~sep:next_sep a.value in
46 let command = and_or c.value in
47 Nslist.append rest (Nlist.Singleton (command, sep))
48 | CList_AndOr a ->
49 let a = and_or a.value in
50 Nslist.singleton sep a
51
52and and_or : ?sep:and_or -> CST.and_or -> pipeline and_or_list =
53 fun ?(sep = Noand_or) x ->
54 match x with
55 | AndOr_Pipeline a ->
56 let a = pipeline a.value in
57 Nslist.singleton sep a
58 | AndOr_AndOr_AndIf_LineBreak_Pipeline (a, _, c) ->
59 let rest = and_or ~sep:And a.value in
60 let c = pipeline c.value in
61 Nslist.append rest (Nlist.Singleton (c, sep))
62 | AndOr_AndOr_OrIf_LineBreak_Pipeline (a, _, c) ->
63 let rest = and_or ~sep:Or a.value in
64 let c = pipeline c.value in
65 Nslist.append rest (Nlist.Singleton (c, sep))
66
67and pipeline : CST.pipeline -> pipeline =
68 fun x ->
69 match x with
70 | Pipeline_PipeSequence a ->
71 let a = pipe_sequence a.value in
72 Pipeline a
73 | Pipeline_Bang_PipeSequence a ->
74 let a = pipe_sequence a.value in
75 Pipeline_Bang a
76
77and pipe_sequence : CST.pipe_sequence -> pipe_sequence =
78 fun x ->
79 match x with
80 | PipeSequence_Command a ->
81 let a = command a.value in
82 [ a ]
83 | PipeSequence_PipeSequence_Pipe_LineBreak_Command (a, _, c) ->
84 let rest = pipe_sequence a.value in
85 let c = command c.value in
86 rest @ [ c ]
87
88and command : CST.command -> command =
89 fun x ->
90 match x with
91 | Command_SimpleCommand a ->
92 let a = simple_command a.value in
93 SimpleCommand a
94 | Command_CompoundCommand a ->
95 let a = compound_command a.value in
96 CompoundCommand (a, [])
97 | Command_CompoundCommand_RedirectList (a, rdrs) ->
98 let a = compound_command a.value in
99 let b = redirect_list rdrs.value in
100 CompoundCommand (a, b)
101 | Command_FunctionDefinition a ->
102 let a = function_definition a.value in
103 FunctionDefinition a
104
105and compound_command : CST.compound_command -> compound_command =
106 fun x ->
107 match x with
108 | CompoundCommand_BraceGroup a ->
109 let a = brace_group a.value in
110 BraceGroup a
111 | CompoundCommand_Subshell a ->
112 let a = subshell a.value in
113 Subshell a
114 | CompoundCommand_ForClause a ->
115 let a = for_clause a.value in
116 ForClause a
117 | CompoundCommand_CaseClause a ->
118 let a = case_clause a.value in
119 CaseClause a
120 | CompoundCommand_IfClause a ->
121 let a = if_clause a.value in
122 IfClause a
123 | CompoundCommand_WhileClause a ->
124 let a = while_clause a.value in
125 WhileClause a
126 | CompoundCommand_UntilClause a ->
127 let a = until_clause a.value in
128 UntilClause a
129
130and subshell : CST.subshell -> subshell =
131 fun x ->
132 match x with Subshell_Lparen_CompoundList_Rparen a -> compound_list a.value
133
134and compound_list : CST.compound_list -> compound_list =
135 fun x ->
136 match x with
137 | CompoundList_LineBreak_Term (_, b) ->
138 let b = term b.value in
139 (b, Semicolon)
140 | CompoundList_LineBreak_Term_Separator (_, b, c) ->
141 let b = term b.value in
142 let c = separator c.value in
143 (b, c)
144
145and term : CST.term -> term =
146 fun x ->
147 match x with
148 | Term_Term_Separator_AndOr (a, b, c) ->
149 let rest = term a.value in
150 let b = separator b.value in
151 let c = and_or c.value in
152 Nslist.append rest (Nslist.singleton b c)
153 | Term_AndOr a ->
154 let a = and_or a.value in
155 Nslist.singleton Semicolon a
156
157and for_clause : CST.for_clause -> for_clause =
158 fun x ->
159 match x with
160 | ForClause_For_Name_DoGroup (a, b) ->
161 let a = name a.value in
162 let b = do_group b.value in
163 For_Name_DoGroup (a, b)
164 | ForClause_For_Name_SequentialSep_DoGroup (a, _, c) ->
165 let a = name a.value in
166 let c = do_group c.value in
167 For_Name_DoGroup (a, c)
168 | ForClause_For_Name_LineBreak_In_SequentialSep_DoGroup (a, _, _, d) ->
169 let a = name a.value in
170 let d = do_group d.value in
171 For_Name_DoGroup (a, d)
172 | ForClause_For_Name_LineBreak_In_WordList_SequentialSep_DoGroup
173 (a, _, c, _, e) ->
174 let a = name a.value in
175 let c = wordlist c.value in
176 let e = do_group e.value in
177 For_Name_In_WordList_DoGroup (a, c, e)
178
179and wordlist : CST.wordlist -> wordlist =
180 fun x ->
181 match x with
182 | WordList_WordList_Word (a, b) ->
183 let a = wordlist a.value in
184 let b = word b.value in
185 Nlist.append a (Nlist.Singleton b)
186 | WordList_Word a ->
187 let a = word a.value in
188 Nlist.Singleton a
189
190and case_clause : CST.case_clause -> case_clause =
191 fun x ->
192 match x with
193 | CaseClause_Case_Word_LineBreak_In_LineBreak_CaseList_Esac (a, _, _, d) ->
194 let a = word a.value in
195 let d = case_list d.value in
196 Cases (a, d)
197 | CaseClause_Case_Word_LineBreak_In_LineBreak_CaseListNS_Esac (a, _, _, d) ->
198 let a = word a.value in
199 let d = case_list_ns d.value in
200 Cases (a, d)
201 | CaseClause_Case_Word_LineBreak_In_LineBreak_Esac (a, _, _) ->
202 let a = word a.value in
203 Case a
204
205and case_list_ns : CST.case_list_ns -> case_list =
206 fun x ->
207 match x with
208 | CaseListNS_CaseList_CaseItemNS (a, b) ->
209 let a = case_list a.value in
210 let b = case_item_ns b.value in
211 Nlist.(a @ Singleton b)
212 | CaseListNS_CaseItemNS a ->
213 let a = case_item_ns a.value in
214 Nlist.Singleton a
215
216and case_list : CST.case_list -> case_list =
217 fun x ->
218 match x with
219 | CaseList_CaseList_CaseItem (a, b) ->
220 let a = case_list a.value in
221 let b = case_item b.value in
222 Nlist.(a @ Singleton b)
223 | CaseList_CaseItem a ->
224 let a = case_item a.value in
225 Nlist.Singleton a
226
227and case_item_ns : CST.case_item_ns -> case_item =
228 fun x ->
229 match x with
230 | CaseItemNS_Pattern_Rparen_LineBreak (a, _) ->
231 let a = pattern a.value in
232 Case_pattern (a, None)
233 | CaseItemNS_Pattern_Rparen_CompoundList (a, b) ->
234 let a = pattern a.value in
235 let b = compound_list b.value in
236 Case_pattern (a, Some b)
237 | CaseItemNS_Lparen_Pattern_Rparen_LineBreak (a, _) ->
238 let a = pattern a.value in
239 Case_pattern (a, None)
240 | CaseItemNS_Lparen_Pattern_Rparen_CompoundList (a, b) ->
241 let a = pattern a.value in
242 let b = compound_list b.value in
243 Case_pattern (a, Some b)
244
245and case_item : CST.case_item -> case_item =
246 fun x ->
247 match x with
248 | CaseItem_Pattern_Rparen_LineBreak_Dsemi_LineBreak (a, _, _) ->
249 let a = pattern a.value in
250 Case_pattern (a, None)
251 | CaseItem_Pattern_Rparen_CompoundList_Dsemi_LineBreak (a, b, _) ->
252 let a = pattern a.value in
253 let b = compound_list b.value in
254 Case_pattern (a, Some b)
255 | CaseItem_Lparen_Pattern_Rparen_LineBreak_Dsemi_LineBreak (a, _, _) ->
256 let a = pattern a.value in
257 Case_pattern (a, None)
258 | CaseItem_Lparen_Pattern_Rparen_CompoundList_Dsemi_LineBreak (a, b, _) ->
259 let a = pattern a.value in
260 let b = compound_list b.value in
261 Case_pattern (a, Some b)
262
263and pattern : CST.pattern -> pattern =
264 fun x ->
265 match x with
266 | Pattern_Word a ->
267 let a = word a.value in
268 Nlist.Singleton a
269 | Pattern_Pattern_Pipe_Word (a, b) ->
270 let a = pattern a.value in
271 let b = word b.value in
272 Nlist.(a @ Singleton b)
273
274and if_clause : CST.if_clause -> if_clause =
275 fun x ->
276 match x with
277 | IfClause_If_CompoundList_Then_CompoundList_ElsePart_Fi (a, b, c) ->
278 let a = compound_list a.value in
279 let b = compound_list b.value in
280 let c = else_part c.value in
281 If_then_else (a, b, c)
282 | IfClause_If_CompoundList_Then_CompoundList_Fi (a, b) ->
283 let a = compound_list a.value in
284 let b = compound_list b.value in
285 If_then (a, b)
286
287and else_part : CST.else_part -> else_part =
288 fun x ->
289 match x with
290 | ElsePart_Elif_CompoundList_Then_CompoundList (a, b) ->
291 let a = compound_list a.value in
292 let b = compound_list b.value in
293 Elif_then (a, b)
294 | ElsePart_Elif_CompoundList_Then_CompoundList_ElsePart (a, b, c) ->
295 let a = compound_list a.value in
296 let b = compound_list b.value in
297 let c = else_part c.value in
298 Elif_then_else (a, b, c)
299 | ElsePart_Else_CompoundList a ->
300 let a = compound_list a.value in
301 Else a
302
303and while_clause : CST.while_clause -> while_clause =
304 fun x ->
305 match x with
306 | WhileClause_While_CompoundList_DoGroup (a, b) ->
307 let a = compound_list a.value in
308 let b = do_group b.value in
309 While (a, b)
310
311and until_clause : CST.until_clause -> until_clause =
312 fun x ->
313 match x with
314 | UntilClause_Until_CompoundList_DoGroup (a, b) ->
315 let a = compound_list a.value in
316 let b = do_group b.value in
317 Until (a, b)
318
319and function_definition : CST.function_definition -> function_definition =
320 fun x ->
321 match x with
322 | FunctionDefinition_Fname_Lparen_Rparen_LineBreak_FunctionBody (a, _, c) ->
323 let CST.(Fname_Name (Name a)) = a.value in
324 let c = function_body c.value in
325 (a, c)
326
327and function_body : CST.function_body -> function_body =
328 fun x ->
329 match x with
330 | FunctionBody_CompoundCommand a ->
331 let a = compound_command a.value in
332 (a, [])
333 | FunctionBody_CompoundCommand_RedirectList (a, b) ->
334 let a = compound_command a.value in
335 let b = redirect_list b.value in
336 (a, b)
337
338and brace_group : CST.brace_group -> brace_group =
339 fun x ->
340 match x with
341 | BraceGroup_LBrace_CompoundList_RBrace a ->
342 let a = compound_list a.value in
343 a
344
345and do_group : CST.do_group -> do_group =
346 fun x ->
347 match x with
348 | DoGroup_Do_CompoundList_Done a ->
349 let a = compound_list a.value in
350 a
351
352and simple_command : CST.simple_command -> simple_command =
353 fun x ->
354 match x with
355 | SimpleCommand_CmdPrefix_CmdWord_CmdSuffix (a, b, c) ->
356 let a = cmd_prefix a.value in
357 let b = cmd_word b.value in
358 let c = cmd_suffix c.value in
359 Prefixed (a, Some b, Some c)
360 | SimpleCommand_CmdPrefix_CmdWord (a, b) ->
361 let a = cmd_prefix a.value in
362 let b = cmd_word b.value in
363 Prefixed (a, Some b, None)
364 | SimpleCommand_CmdPrefix a ->
365 let a = cmd_prefix a.value in
366 Prefixed (a, None, None)
367 | SimpleCommand_CmdName_CmdSuffix (a, b) ->
368 let a = cmd_name a.value in
369 let b = cmd_suffix b.value in
370 Named (a, Some b)
371 | SimpleCommand_CmdName a ->
372 let a = cmd_name a.value in
373 Named (a, None)
374
375and cmd_name : CST.cmd_name -> cmd_name =
376 fun x ->
377 match x with
378 | CmdName_Word a ->
379 let a = word a.value in
380 a
381
382and cmd_word : CST.cmd_word -> cmd_word =
383 fun x ->
384 match x with
385 | CmdWord_Word a ->
386 let a = word a.value in
387 a
388
389and cmd_prefix : CST.cmd_prefix -> cmd_prefix =
390 fun x ->
391 match x with
392 | CmdPrefix_IoRedirect a ->
393 let a = io_redirect a.value in
394 [ Prefix_redirect a ]
395 | CmdPrefix_CmdPrefix_IoRedirect (a, b) ->
396 let a = cmd_prefix a.value in
397 let b = io_redirect b.value in
398 a @ [ Prefix_redirect b ]
399 | CmdPrefix_AssignmentWord a ->
400 let a = assignment_word a.value in
401 [ Prefix_assignment a ]
402 | CmdPrefix_CmdPrefix_AssignmentWord (a, b) ->
403 let a = cmd_prefix a.value in
404 let b = assignment_word b.value in
405 a @ [ Prefix_assignment b ]
406
407and cmd_suffix : CST.cmd_suffix -> cmd_suffix =
408 fun x ->
409 match x with
410 | CmdSuffix_IoRedirect a ->
411 let a = io_redirect a.value in
412 [ Suffix_redirect a ]
413 | CmdSuffix_CmdSuffix_IoRedirect (a, b) ->
414 let a = cmd_suffix a.value in
415 let b = io_redirect b.value in
416 a @ [ Suffix_redirect b ]
417 | CmdSuffix_Word a ->
418 let a = word a.value in
419 [ Suffix_word a ]
420 | CmdSuffix_CmdSuffix_Word (a, b) ->
421 let a = cmd_suffix a.value in
422 let b = word b.value in
423 a @ [ Suffix_word b ]
424
425and redirect_list : CST.redirect_list -> redirects =
426 fun x ->
427 match x with
428 | RedirectList_IoRedirect a ->
429 let a = io_redirect a.value in
430 [ a ]
431 | RedirectList_RedirectList_IoRedirect (a, b) ->
432 let a = redirect_list a.value in
433 let b = io_redirect b.value in
434 a @ [ b ]
435
436and io_op_default_fd : io_op -> io_number = function
437 | Io_op_less -> 0
438 | Io_op_lessand -> 0
439 | _ -> 1
440
441and io_redirect : CST.io_redirect -> io_redirect =
442 fun x ->
443 match x with
444 | IoRedirect_IoFile a ->
445 let a = io_file a.value in
446 IoRedirect_IoFile (io_op_default_fd (fst a), a)
447 | IoRedirect_IoNumber_IoFile (a, b) ->
448 let a = io_number a in
449 let b = io_file b.value in
450 IoRedirect_IoFile (a, b)
451 | IoRedirect_IoHere a ->
452 let a = io_here a.value in
453 IoRedirect_IoHere (0, a)
454 | IoRedirect_IoNumber_IoHere (a, b) ->
455 let a = io_number a in
456 let b = io_here b.value in
457 IoRedirect_IoHere (a, b)
458
459and io_file : CST.io_file -> io_file =
460 fun x ->
461 match x with
462 | IoFile_Less_FileName a ->
463 let a = filename a.value in
464 (Io_op_less, a)
465 | IoFile_LessAnd_FileName a ->
466 let a = filename a.value in
467 (Io_op_lessand, a)
468 | IoFile_Great_FileName a ->
469 let a = filename a.value in
470 (Io_op_great, a)
471 | IoFile_GreatAnd_FileName a ->
472 let a = filename a.value in
473 (Io_op_greatand, a)
474 | IoFile_DGreat_FileName a ->
475 let a = filename a.value in
476 (Io_op_dgreat, a)
477 | IoFile_LessGreat_FileName a ->
478 let a = filename a.value in
479 (Io_op_lessgreat, a)
480 | IoFile_AndGreat_FileName a ->
481 let a = filename a.value in
482 (Io_op_andgreat, a)
483 | IoFile_Clobber_FileName a ->
484 let a = filename a.value in
485 (Io_op_clobber, a)
486
487and filename : CST.filename -> filename =
488 fun x ->
489 match x with
490 | Filename_Word a ->
491 let a = word a.value in
492 a
493
494and io_here : CST.io_here -> io_here =
495 fun x ->
496 match x with
497 | IoHere_DLess_HereEnd (a, b) ->
498 let a = here_end a.value in
499 IoHere (a, word !b.value)
500 | IoHere_DLessDash_HereEnd (a, b) ->
501 let a = here_end a.value in
502 IoHere_Dash (a, word !b.value)
503
504and here_end : CST.here_end -> here_end =
505 fun x ->
506 match x with
507 | HereEnd_Word a ->
508 let a = word a.value in
509 a
510
511and separator : CST.separator -> separator =
512 fun x ->
513 match x with
514 | Separator_SeparatorOp_LineBreak (a, _) -> separator_op a.value
515 | Separator_NewLineList _ -> Semicolon
516
517and word : CST.word -> word = fun x -> match x with Word (_, b) -> word_cst b
518and word_cst : CST.word_cst -> word_cst = fun v -> List.map word_component v
519
520and need_escaped = function
521 | '(' | '`' | '$' | ')' | '0' .. '9' -> true
522 | _ -> false
523
524and remove_double_escape s =
525 let len = String.length s in
526 let i = ref 0 in
527 let r = ref "" in
528 while !i <= len - 1 do
529 if
530 Char.equal (String.unsafe_get s !i) '\\'
531 && need_escaped (String.unsafe_get s (!i + 1))
532 then begin
533 r := !r ^ String.make 1 @@ String.unsafe_get s (!i + 1);
534 i := !i + 2
535 end
536 else begin
537 r := !r ^ String.make 1 @@ String.unsafe_get s !i;
538 incr i
539 end
540 done;
541 !r
542
543and word_component : CST.word_component -> word_component =
544 fun x ->
545 match x with
546 | WordSubshell (_a, b) ->
547 let b = program b.value in
548 WordSubshell b
549 | WordName a -> WordName (remove_double_escape a)
550 | WordAssignmentWord a ->
551 let a = assignment_word a in
552 WordAssignmentWord a
553 | WordDoubleQuoted a ->
554 let a = word a in
555 WordDoubleQuoted a
556 | WordSingleQuoted a ->
557 let a = word a in
558 WordSingleQuoted a
559 | WordTildePrefix a -> WordTildePrefix a
560 | WordLiteral a -> WordLiteral (remove_double_escape a)
561 | WordVariable a ->
562 let a = variable a in
563 WordVariable a
564 | WordGlobAll -> WordGlobAll
565 | WordGlobAny -> WordGlobAny
566 | WordArithmeticExpression s -> WordArithmeticExpression (word s)
567 | WordReBracketExpression a ->
568 let a = bracket_expression a in
569 WordReBracketExpression a
570 | WordEmpty -> WordEmpty
571
572and bracket_expression : CST.bracket_expression -> bracket_expression =
573 fun x ->
574 match x with
575 | BracketExpression_LBRACKET_MatchingList_RBRACKET a ->
576 let a = matching_list a in
577 BracketExpression_LBRACKET_MatchingList_RBRACKET a
578 | BracketExpression_LBRACKET_NonMatchingList_RBRACKET a ->
579 let a = nonmatching_list a in
580 BracketExpression_LBRACKET_NonMatchingList_RBRACKET a
581
582and matching_list : CST.matching_list -> matching_list =
583 fun x ->
584 match x with
585 | MatchingList_BracketList a ->
586 let a = bracket_list a in
587 MatchingList_BracketList a
588
589and nonmatching_list : CST.nonmatching_list -> nonmatching_list =
590 fun x ->
591 match x with
592 | NonMatchingList_BracketList a ->
593 let a = bracket_list a in
594 NonMatchingList_BracketList a
595
596and bracket_list : CST.bracket_list -> bracket_list =
597 fun x ->
598 match x with
599 | BracketList_FollowList a ->
600 let a = follow_list a in
601 BracketList_FollowList a
602 | BracketList_FollowList_MINUS a ->
603 let a = follow_list a in
604 BracketList_FollowList_MINUS a
605
606and follow_list : CST.follow_list -> follow_list =
607 fun x ->
608 match x with
609 | FollowList_ExpressionTerm a ->
610 let a = expression_term a in
611 FollowList_ExpressionTerm a
612 | FollowList_FollowList_ExpressionTerm (a, b) ->
613 let a = follow_list a in
614 let b = expression_term b in
615 FollowList_FollowList_ExpressionTerm (a, b)
616
617and expression_term : CST.expression_term -> expression_term =
618 fun x ->
619 match x with
620 | ExpressionTerm_SingleExpression a ->
621 let a = single_expression a in
622 ExpressionTerm_SingleExpression a
623 | ExpressionTerm_RangeExpression a ->
624 let a = range_expression a in
625 ExpressionTerm_RangeExpression a
626
627and single_expression : CST.single_expression -> single_expression =
628 fun x ->
629 match x with
630 | SingleExpression_EndRange a ->
631 let a = end_range a in
632 SingleExpression_EndRange a
633 | SingleExpression_CharacterClass a ->
634 let a = character_class a in
635 SingleExpression_CharacterClass a
636 | SingleExpression_EquivalenceClass a ->
637 let a = equivalence_class a in
638 SingleExpression_EquivalenceClass a
639
640and range_expression : CST.range_expression -> range_expression =
641 fun x ->
642 match x with
643 | RangeExpression_StartRange_EndRange (a, b) ->
644 let a = start_range a in
645 let b = end_range b in
646 RangeExpression_StartRange_EndRange (a, b)
647 | RangeExpression_StartRange_MINUS a ->
648 let a = start_range a in
649 RangeExpression_StartRange_MINUS a
650
651and start_range : CST.start_range -> start_range =
652 fun x ->
653 match x with
654 | StartRange_EndRange_MINUS a ->
655 let a = end_range a in
656 StartRange_EndRange_MINUS a
657
658and end_range : CST.end_range -> end_range =
659 fun x ->
660 match x with
661 | EndRange_COLLELEMSINGLE a -> EndRange_COLLELEMSINGLE a
662 | EndRangeCollatingSymbol a ->
663 let a = collating_symbol a in
664 EndRangeCollatingSymbol a
665
666and collating_symbol : CST.collating_symbol -> collating_symbol =
667 fun x ->
668 match x with
669 | CollatingSymbol_OpenDot_COLLELEMSINGLE_DotClose a ->
670 CollatingSymbol_OpenDot_COLLELEMSINGLE_DotClose a
671 | CollatingSymbol_OpenDot_COLLELEMMULTI_DotClose a ->
672 CollatingSymbol_OpenDot_COLLELEMMULTI_DotClose a
673 | CollatingSymbol_OpenDot_METACHAR_DotClose a ->
674 CollatingSymbol_OpenDot_METACHAR_DotClose a
675
676and equivalence_class : CST.equivalence_class -> equivalence_class =
677 fun x ->
678 match x with
679 | EquivalenceClass_OpenEqual_COLLELEMSINGLE_EqualClose a ->
680 EquivalenceClass_OpenEqual_COLLELEMSINGLE_EqualClose a
681 | EquivalenceClass_OpenEqual_COLLELEMMULTI_EqualClose a ->
682 EquivalenceClass_OpenEqual_COLLELEMMULTI_EqualClose a
683
684and character_class : CST.character_class -> character_class =
685 fun x ->
686 match x with
687 | CharacterClass_OpenColon_CLASSNAME_ColonClose a ->
688 let a = class_name a in
689 CharacterClass_OpenColon_CLASSNAME_ColonClose a
690
691and class_name : CST.class_name -> class_name =
692 fun x -> match x with ClassName a -> ClassName a
693
694and variable : CST.variable -> variable =
695 fun x ->
696 match x with
697 | VariableAtom (a, b) ->
698 let b = variable_attribute b in
699 VariableAtom (a, b)
700
701and variable_attribute : CST.variable_attribute -> variable_attribute =
702 fun x ->
703 match x with
704 | NoAttribute -> NoAttribute
705 | ParameterLength -> ParameterLength
706 | UseDefaultValues (a, b) ->
707 let b = word b in
708 UseDefaultValues (a, b)
709 | AssignDefaultValues (a, b) ->
710 let b = word b in
711 AssignDefaultValues (a, b)
712 | IndicateErrorifNullorUnset (a, b) ->
713 let b = word b in
714 IndicateErrorifNullorUnset (a, b)
715 | UseAlternativeValue (a, b) ->
716 let b = word b in
717 UseAlternativeValue (a, b)
718 | RemoveSmallestSuffixPattern a ->
719 let a = word a in
720 RemoveSmallestSuffixPattern a
721 | RemoveLargestSuffixPattern a ->
722 let a = word a in
723 RemoveLargestSuffixPattern a
724 | RemoveSmallestPrefixPattern a ->
725 let a = word a in
726 RemoveSmallestPrefixPattern a
727 | RemoveLargestPrefixPattern a ->
728 let a = word a in
729 RemoveLargestPrefixPattern a
730
731and name : CST.name -> name = fun x -> match x with Name a -> Name a
732
733and assignment_word : CST.assignment_word -> assignment_word =
734 fun (a, b) ->
735 let a = name a in
736 let b = word b in
737 (a, b)
738
739and io_number : CST.io_number -> io_number =
740 fun x -> match x with IONumber a -> int_of_string a
741
742let of_program = program
743
744module Dump = struct
745 let pp ppf v =
746 let yjs = complete_commands_to_yojson v in
747 Yojson.Safe.pretty_print ppf yjs
748end
749
750let () =
751 Printexc.register_printer (function
752 | Morbig.Errors.DuringParsing pos ->
753 Some
754 (Fmt.str "Error during parsing (potentially non-POSIX): %s"
755 (Morbig.string_of_lexing_position pos))
756 | Morbig.Errors.DuringLexing (pos, s) ->
757 Some
758 (Fmt.str "Error during lexing of \"%s\": %s" s
759 (Morbig.string_of_lexing_position pos))
760 | Morbig.Errors.DuringAliasing (pos, s) ->
761 Some
762 (Fmt.str "Error during aliasing of \"%s\": %s" s
763 (Morbig.string_of_lexing_position pos))
764 | _ -> None)
765
766let of_string ?(filename = "-") s =
767 let f = Morbig.parse_string filename s in
768 of_program f
769
770let of_file path =
771 let fname = Eio.Path.native_exn path in
772 Eio.Path.load path |> of_string ~filename:fname
773
774let rec word_component_to_string :
775 ?field_splitting:bool -> word_component -> string list =
776 fun ?(field_splitting = true) -> function
777 | WordName s -> [ s ]
778 | WordLiteral s -> [ s ]
779 | WordDoubleQuoted s -> word_components_to_strings ~field_splitting:false s
780 | WordSingleQuoted s -> word_components_to_strings ~field_splitting:false s
781 | WordGlobAll -> [ "*" ]
782 | WordGlobAny -> [ "?" ]
783 | WordEmpty -> [ "" ]
784 | WordAssignmentWord (Name p, v) ->
785 p :: "=" :: word_components_to_strings ~field_splitting v
786 | WordSubshell _ ->
787 Fmt.failwith
788 "This is an error in Merry, subshells should already have been \
789 expanded by now!"
790 | v ->
791 Fmt.failwith "conversion of %a" Yojson.Safe.pp
792 (word_component_to_yojson v)
793
794and word_components_to_strings ?(field_splitting = true) ws =
795 if field_splitting then
796 List.concat_map (word_component_to_string ~field_splitting) ws
797 else
798 [
799 String.concat ""
800 (List.concat_map (word_component_to_string ~field_splitting) ws);
801 ]
802
803class check_ast =
804 object (_)
805 inherit [bool] Sast.fold
806 method int _ ctx = ctx
807 method bool _ ctx = ctx
808 method string _ ctx = ctx
809 method char _ ctx = ctx
810 method option f v ctx = Option.fold ~none:ctx ~some:(fun i -> f i ctx) v
811 method nlist__t f v ctx = Nlist.fold_left (fun acc i -> f i acc) ctx v
812
813 method nslist__t f g v ctx =
814 Nslist.fold_left (fun acc a b -> f a acc |> g b) ctx v
815
816 method list f v ctx = List.fold_left (fun acc i -> f i acc) ctx v
817 end
818
819let has_async ast =
820 let o =
821 object
822 inherit check_ast as super
823
824 method! complete_command v ctx =
825 match v with
826 | _, Some Ampersand -> true
827 | _ -> super#complete_command v ctx
828
829 method! clist l ctx =
830 let v =
831 Nlist.to_list l
832 |> List.exists (function _, Ampersand -> true | _ -> false)
833 in
834 if v then v else super#clist l ctx
835
836 method! term t ctx =
837 let v =
838 Nlist.to_list t
839 |> List.exists (function _, Ampersand -> true | _ -> false)
840 in
841 if v then v else super#term t ctx
842 end
843 in
844 o#complete_command ast false
845
846let has_glob ast =
847 let o =
848 object
849 inherit check_ast as super
850
851 method! word_component v ctx =
852 match v with
853 | WordGlobAll | WordGlobAny -> true
854 | _ -> super#word_component v ctx
855 end
856 in
857 o#word_cst ast false
858
859module Fragment = struct
860 let make ?(splittable = false) ?(globbable = false) ?(tilde_expansion = false)
861 ?(join = `No) txt =
862 { txt; splittable; join; globbable; tilde_expansion }
863
864 let empty = make ""
865 let to_string { txt; _ } = txt
866
867 let join ~sep f1 f2 =
868 {
869 f1 with
870 txt = f1.txt ^ sep ^ f2.txt;
871 globbable = f1.globbable || f2.globbable;
872 }
873
874 let join_list ~sep fs = List.fold_left (join ~sep) empty fs |> to_string
875
876 let pp_join ppf = function
877 | `No -> Fmt.pf ppf "no"
878 | `With_previous -> Fmt.pf ppf "with-previous"
879 | `With_next -> Fmt.pf ppf "with-next"
880
881 let pp ppf { txt; join; splittable; globbable; tilde_expansion } =
882 Fmt.pf ppf
883 "{ txt = %s; join = %a; splittable = %b; globbable = %b; tilde_expansion \
884 = %b }"
885 txt pp_join join splittable globbable tilde_expansion
886
887 let handle_joins cst =
888 let rec loop = function
889 | [] -> []
890 | [ x ] -> [ { x with join = `No } ]
891 | x :: { txt; join = `With_previous; globbable; _ } :: rest ->
892 loop
893 ({
894 x with
895 join = `No;
896 txt = x.txt ^ txt;
897 globbable = x.globbable || globbable;
898 }
899 :: rest)
900 | { txt; join = `With_next; globbable; _ } :: y :: rest ->
901 { y with txt = txt ^ y.txt; globbable = globbable || y.globbable }
902 :: loop rest
903 | x :: xs -> x :: loop xs
904 in
905 let v = loop cst in
906 let has_a_tilde = List.exists (fun f -> f.tilde_expansion) v in
907 let v =
908 if has_a_tilde then [ List.fold_left (join ~sep:"") empty v ] else v
909 in
910 (* TODO: Blergh, this is horrible, surely there is a better way? Maybe morbig
911 should not parse these separately... *)
912 let rec recombine_equals = function
913 | [] -> []
914 | [ x ] -> [ x ]
915 | ({ txt; _ } as x) :: y :: rest -> (
916 let s = String.length txt in
917 match String.get txt (s - 1) with
918 | '=' -> { y with txt = txt ^ y.txt } :: recombine_equals rest
919 | (exception Invalid_argument _) | _ ->
920 x :: recombine_equals (y :: rest))
921 in
922 recombine_equals v
923end