1use super::lexer::Token;
2
3#[derive(Debug, Clone, PartialEq, Eq)]
4pub enum Ast {
5 Pipeline(Vec<ShellCommand>),
6 Sequence(Vec<Ast>),
7 Assignment {
8 var: String,
9 value: String,
10 },
11 LocalAssignment {
12 var: String,
13 value: String,
14 },
15 If {
16 branches: Vec<(Box<Ast>, Box<Ast>)>, else_branch: Option<Box<Ast>>,
18 },
19 Case {
20 word: String,
21 cases: Vec<(Vec<String>, Ast)>,
22 default: Option<Box<Ast>>,
23 },
24 For {
25 variable: String,
26 items: Vec<String>,
27 body: Box<Ast>,
28 },
29 While {
30 condition: Box<Ast>,
31 body: Box<Ast>,
32 },
33 FunctionDefinition {
34 name: String,
35 body: Box<Ast>,
36 },
37 FunctionCall {
38 name: String,
39 args: Vec<String>,
40 },
41 Return {
42 value: Option<String>,
43 },
44 And {
45 left: Box<Ast>,
46 right: Box<Ast>,
47 },
48 Or {
49 left: Box<Ast>,
50 right: Box<Ast>,
51 },
52}
53
54#[derive(Debug, Clone, PartialEq, Eq)]
55pub struct ShellCommand {
56 pub args: Vec<String>,
57 pub input: Option<String>,
58 pub output: Option<String>,
59 pub append: Option<String>,
60}
61
62pub fn parse(tokens: Vec<Token>) -> Result<Ast, String> {
63 if tokens.len() >= 4
65 && let (Token::Word(_), Token::LeftParen, Token::RightParen, Token::LeftBrace) =
66 (&tokens[0], &tokens[1], &tokens[2], &tokens[3])
67 {
68 let mut brace_depth = 1; let mut function_end = tokens.len();
72 let mut j = 4; while j < tokens.len() {
75 match &tokens[j] {
76 Token::LeftBrace => {
77 brace_depth += 1;
78 j += 1;
79 },
80 Token::RightBrace => {
81 brace_depth -= 1;
82 if brace_depth == 0 {
83 function_end = j + 1; break;
85 }
86 j += 1;
87 }
88 Token::If => {
89 let mut if_depth = 1;
91 j += 1;
92 while j < tokens.len() && if_depth > 0 {
93 match tokens[j] {
94 Token::If => if_depth += 1,
95 Token::Fi => if_depth -= 1,
96 _ => {}
97 }
98 j += 1;
99 }
100 }
101 Token::For | Token::While => {
102 let mut for_depth = 1;
104 j += 1;
105 while j < tokens.len() && for_depth > 0 {
106 match tokens[j] {
107 Token::For | Token::While => for_depth += 1,
108 Token::Done => for_depth -= 1,
109 _ => {}
110 }
111 j += 1;
112 }
113 }
114 Token::Case => {
115 j += 1;
117 while j < tokens.len() {
118 if tokens[j] == Token::Esac {
119 j += 1;
120 break;
121 }
122 j += 1;
123 }
124 }
125 _ => {
126 j += 1;
127 }
128 }
129 }
130
131 if brace_depth == 0 && function_end <= tokens.len() {
132 let function_tokens = &tokens[0..function_end];
134 let remaining_tokens = &tokens[function_end..];
135
136 let function_ast = parse_function_definition(function_tokens)?;
137
138 if remaining_tokens.is_empty() {
139 return Ok(function_ast);
140 } else {
141 let remaining_ast = parse_commands_sequentially(remaining_tokens)?;
143 return Ok(Ast::Sequence(vec![function_ast, remaining_ast]));
144 }
145 }
146 }
147
148 if tokens.len() >= 2
150 && let Token::Word(ref word) = tokens[0]
151 && let Some(paren_pos) = word.find('(')
152 && word.ends_with(')') && paren_pos > 0
153 && tokens[1] == Token::LeftBrace {
154 return parse_function_definition(&tokens);
155 }
156
157 parse_commands_sequentially(&tokens)
159}
160
161fn parse_slice(tokens: &[Token]) -> Result<Ast, String> {
162 if tokens.is_empty() {
163 return Err("No commands found".to_string());
164 }
165
166 if tokens.len() == 2 {
168 if let (Token::Word(var_eq), Token::Word(value)) = (&tokens[0], &tokens[1])
170 && let Some(eq_pos) = var_eq.find('=')
171 && eq_pos > 0 && eq_pos < var_eq.len() - 1 {
172 let var = var_eq[..eq_pos].to_string();
173 let full_value = format!("{}{}", &var_eq[eq_pos + 1..], value);
174 if var.chars().next().unwrap().is_alphabetic() || var.starts_with('_') {
176 return Ok(Ast::Assignment {
177 var,
178 value: full_value,
179 });
180 }
181 }
182 }
183
184 if tokens.len() == 2
186 && let (Token::Word(var_eq), Token::Word(value)) = (&tokens[0], &tokens[1])
187 && let Some(eq_pos) = var_eq.find('=')
188 && eq_pos > 0 && eq_pos == var_eq.len() - 1 {
189 let var = var_eq[..eq_pos].to_string();
190 if var.chars().next().unwrap().is_alphabetic() || var.starts_with('_') {
192 return Ok(Ast::Assignment {
193 var,
194 value: value.clone(),
195 });
196 }
197 }
198
199 if tokens.len() == 3
201 && let (Token::Local, Token::Word(var), Token::Word(value)) = (&tokens[0], &tokens[1], &tokens[2]) {
202 let clean_var = if var.ends_with('=') {
204 &var[..var.len() - 1]
205 } else {
206 var
207 };
208 if clean_var.chars().next().unwrap().is_alphabetic() || clean_var.starts_with('_') {
210 return Ok(Ast::LocalAssignment {
211 var: clean_var.to_string(),
212 value: value.clone(),
213 });
214 }
215 }
216
217 if !tokens.is_empty() && tokens.len() <= 2
219 && let Token::Return = &tokens[0] {
220 if tokens.len() == 1 {
221 return Ok(Ast::Return { value: None });
223 } else if let Token::Word(word) = &tokens[1] {
224 return Ok(Ast::Return {
226 value: Some(word.clone()),
227 });
228 }
229 }
230
231 if tokens.len() == 2
233 && let (Token::Local, Token::Word(var_eq)) = (&tokens[0], &tokens[1])
234 && let Some(eq_pos) = var_eq.find('=')
235 && eq_pos > 0 && eq_pos < var_eq.len() - 1 {
236 let var = var_eq[..eq_pos].to_string();
237 let value = var_eq[eq_pos + 1..].to_string();
238 if var.chars().next().unwrap().is_alphabetic() || var.starts_with('_') {
240 return Ok(Ast::LocalAssignment { var, value });
241 }
242 }
243
244 if tokens.len() == 1
246 && let Token::Word(ref word) = tokens[0]
247 && let Some(eq_pos) = word.find('=')
248 && eq_pos > 0 && eq_pos < word.len() - 1 {
249 let var = word[..eq_pos].to_string();
250 let value = word[eq_pos + 1..].to_string();
251 if var.chars().next().unwrap().is_alphabetic() || var.starts_with('_') {
253 return Ok(Ast::Assignment { var, value });
254 }
255 }
256
257 if let Token::If = tokens[0] {
259 return parse_if(tokens);
260 }
261
262 if let Token::Case = tokens[0] {
264 return parse_case(tokens);
265 }
266
267 if let Token::For = tokens[0] {
269 return parse_for(tokens);
270 }
271
272 if let Token::While = tokens[0] {
274 return parse_while(tokens);
275 }
276
277 if tokens.len() >= 4
280 && let (Token::Word(word), Token::LeftParen, Token::RightParen, Token::LeftBrace) =
281 (&tokens[0], &tokens[1], &tokens[2], &tokens[3])
282 && (word.chars().next().unwrap().is_alphabetic() || word.starts_with('_')) {
283 return parse_function_definition(tokens);
284 }
285
286 if tokens.len() >= 2
288 && let Token::Word(ref word) = tokens[0]
289 && let Some(paren_pos) = word.find('(')
290 && word.ends_with(')') && paren_pos > 0 {
291 let func_name = &word[..paren_pos];
292 if (func_name.chars().next().unwrap().is_alphabetic() || func_name.starts_with('_'))
293 && tokens[1] == Token::LeftBrace {
294 return parse_function_definition(tokens);
295 }
296 }
297
298 parse_pipeline(tokens)
303}
304
305fn parse_commands_sequentially(tokens: &[Token]) -> Result<Ast, String> {
306 let mut i = 0;
307 let mut commands = Vec::new();
308
309 while i < tokens.len() {
310 while i < tokens.len() {
312 match &tokens[i] {
313 Token::Newline => {
314 i += 1;
315 }
316 Token::Word(word) if word.starts_with('#') => {
317 while i < tokens.len() && tokens[i] != Token::Newline {
319 i += 1;
320 }
321 if i < tokens.len() {
322 i += 1; }
324 }
325 _ => break,
326 }
327 }
328
329 if i >= tokens.len() {
330 break;
331 }
332
333 let start = i;
335
336 if tokens[i] == Token::If {
338 let mut depth = 0;
340 while i < tokens.len() {
341 match tokens[i] {
342 Token::If => depth += 1,
343 Token::Fi => {
344 depth -= 1;
345 if depth == 0 {
346 i += 1; break;
348 }
349 }
350 _ => {}
351 }
352 i += 1;
353 }
354
355 } else if tokens[i] == Token::For {
358 let mut depth = 1; i += 1; while i < tokens.len() {
362 match tokens[i] {
363 Token::For | Token::While => depth += 1,
364 Token::Done => {
365 depth -= 1;
366 if depth == 0 {
367 i += 1; break;
369 }
370 }
371 _ => {}
372 }
373 i += 1;
374 }
375 } else if tokens[i] == Token::While {
376 let mut depth = 1; i += 1; while i < tokens.len() {
380 match tokens[i] {
381 Token::While | Token::For => depth += 1,
382 Token::Done => {
383 depth -= 1;
384 if depth == 0 {
385 i += 1; break;
387 }
388 }
389 _ => {}
390 }
391 i += 1;
392 }
393 } else if tokens[i] == Token::Case {
394 while i < tokens.len() {
396 if tokens[i] == Token::Esac {
397 i += 1; break;
399 }
400 i += 1;
401 }
402 } else if i + 3 < tokens.len()
403 && matches!(tokens[i], Token::Word(_))
404 && tokens[i + 1] == Token::LeftParen
405 && tokens[i + 2] == Token::RightParen
406 && tokens[i + 3] == Token::LeftBrace
407 {
408 let mut brace_depth = 1;
410 i += 4; while i < tokens.len() && brace_depth > 0 {
412 match tokens[i] {
413 Token::LeftBrace => brace_depth += 1,
414 Token::RightBrace => brace_depth -= 1,
415 _ => {}
416 }
417 i += 1;
418 }
419 } else {
420 while i < tokens.len() {
423 if tokens[i] == Token::Newline || tokens[i] == Token::Semicolon
424 || tokens[i] == Token::And || tokens[i] == Token::Or {
425 let mut j = i + 1;
427 while j < tokens.len() && tokens[j] == Token::Newline {
428 j += 1;
429 }
430 if j < tokens.len()
432 && (tokens[j] == Token::Else
433 || tokens[j] == Token::Elif
434 || tokens[j] == Token::Fi)
435 {
436 i = j + 1;
438 continue;
439 }
440 break;
441 }
442 i += 1;
443 }
444 }
445
446 let command_tokens = &tokens[start..i];
447 if !command_tokens.is_empty() {
448 if command_tokens.len() == 1 {
450 match command_tokens[0] {
451 Token::Else | Token::Elif | Token::Fi => {
452 if i < tokens.len()
454 && (tokens[i] == Token::Newline || tokens[i] == Token::Semicolon)
455 {
456 i += 1;
457 }
458 continue;
459 }
460 _ => {}
461 }
462 }
463
464 let ast = parse_slice(command_tokens)?;
465
466 if i < tokens.len() && (tokens[i] == Token::And || tokens[i] == Token::Or) {
468 let operator = tokens[i].clone();
469 i += 1; while i < tokens.len() && tokens[i] == Token::Newline {
473 i += 1;
474 }
475
476 let remaining_tokens = &tokens[i..];
478 let right_ast = parse_commands_sequentially(remaining_tokens)?;
479
480 let combined_ast = match operator {
482 Token::And => Ast::And {
483 left: Box::new(ast),
484 right: Box::new(right_ast),
485 },
486 Token::Or => Ast::Or {
487 left: Box::new(ast),
488 right: Box::new(right_ast),
489 },
490 _ => unreachable!(),
491 };
492
493 commands.push(combined_ast);
494 break; } else {
496 commands.push(ast);
497 }
498 }
499
500 if i < tokens.len() && (tokens[i] == Token::Newline || tokens[i] == Token::Semicolon) {
501 i += 1;
502 }
503 }
504
505 if commands.is_empty() {
506 return Err("No commands found".to_string());
507 }
508
509 if commands.len() == 1 {
510 Ok(commands.into_iter().next().unwrap())
511 } else {
512 Ok(Ast::Sequence(commands))
513 }
514}
515
516fn parse_pipeline(tokens: &[Token]) -> Result<Ast, String> {
517 let mut commands = Vec::new();
518 let mut current_cmd = ShellCommand {
519 args: Vec::new(),
520 input: None,
521 output: None,
522 append: None,
523 };
524
525 let mut i = 0;
526 while i < tokens.len() {
527 let token = &tokens[i];
528 match token {
529 Token::Word(word) => {
530 current_cmd.args.push(word.clone());
531 }
532 Token::Pipe => {
533 if !current_cmd.args.is_empty() {
534 commands.push(current_cmd.clone());
535 current_cmd = ShellCommand {
536 args: Vec::new(),
537 input: None,
538 output: None,
539 append: None,
540 };
541 }
542 }
543 Token::RedirIn => {
544 i += 1;
545 if i < tokens.len()
546 && let Token::Word(ref file) = tokens[i] {
547 current_cmd.input = Some(file.clone());
548 }
549 }
550 Token::RedirOut => {
551 i += 1;
552 if i < tokens.len()
553 && let Token::Word(ref file) = tokens[i] {
554 current_cmd.output = Some(file.clone());
555 }
556 }
557 Token::RedirAppend => {
558 i += 1;
559 if i < tokens.len()
560 && let Token::Word(ref file) = tokens[i] {
561 current_cmd.append = Some(file.clone());
562 }
563 }
564 Token::RightParen => {
565 if !current_cmd.args.is_empty() && i > 0
568 && let Token::LeftParen = tokens[i-1] {
569 break;
573 }
574 return Err("Unexpected ) in pipeline".to_string());
575 }
576 Token::Newline => {
577 i += 1;
579 continue;
580 }
581 Token::Do | Token::Done | Token::Then | Token::Else | Token::Elif | Token::Fi | Token::Esac => {
582 break;
585 }
586 _ => {
587 return Err(format!("Unexpected token in pipeline: {:?}", token));
588 }
589 }
590 i += 1;
591 }
592
593 if !current_cmd.args.is_empty() {
594 commands.push(current_cmd);
595 }
596
597 if commands.is_empty() {
598 return Err("No commands found".to_string());
599 }
600
601 Ok(Ast::Pipeline(commands))
602}
603
604fn parse_if(tokens: &[Token]) -> Result<Ast, String> {
605 let mut i = 1; let mut branches = Vec::new();
607
608 loop {
609 let mut cond_tokens = Vec::new();
611 while i < tokens.len()
612 && tokens[i] != Token::Semicolon
613 && tokens[i] != Token::Newline
614 && tokens[i] != Token::Then
615 {
616 cond_tokens.push(tokens[i].clone());
617 i += 1;
618 }
619
620 if i < tokens.len() && (tokens[i] == Token::Semicolon || tokens[i] == Token::Newline) {
622 i += 1;
623 }
624
625 while i < tokens.len() && tokens[i] == Token::Newline {
627 i += 1;
628 }
629
630 if i >= tokens.len() || tokens[i] != Token::Then {
631 return Err("Expected then after if/elif condition".to_string());
632 }
633 i += 1; while i < tokens.len() && tokens[i] == Token::Newline {
637 i += 1;
638 }
639
640 let mut then_tokens = Vec::new();
643 let mut depth = 0;
644 while i < tokens.len() {
645 match &tokens[i] {
646 Token::If => {
647 depth += 1;
648 then_tokens.push(tokens[i].clone());
649 }
650 Token::Fi => {
651 if depth > 0 {
652 depth -= 1;
653 then_tokens.push(tokens[i].clone());
654 } else {
655 break; }
657 }
658 Token::Else | Token::Elif if depth == 0 => {
659 break; }
661 Token::Newline => {
662 let mut j = i + 1;
664 while j < tokens.len() && tokens[j] == Token::Newline {
665 j += 1;
666 }
667 if j < tokens.len()
668 && depth == 0
669 && (tokens[j] == Token::Else
670 || tokens[j] == Token::Elif
671 || tokens[j] == Token::Fi)
672 {
673 i = j; break;
675 }
676 then_tokens.push(tokens[i].clone());
678 }
679 _ => {
680 then_tokens.push(tokens[i].clone());
681 }
682 }
683 i += 1;
684 }
685
686 while i < tokens.len() && tokens[i] == Token::Newline {
688 i += 1;
689 }
690
691 let then_ast = if then_tokens.is_empty() {
692 Ast::Pipeline(vec![ShellCommand {
694 args: vec!["true".to_string()],
695 input: None,
696 output: None,
697 append: None,
698 }])
699 } else {
700 parse_commands_sequentially(&then_tokens)?
701 };
702
703 let condition = parse_slice(&cond_tokens)?;
704 branches.push((Box::new(condition), Box::new(then_ast)));
705
706 if i < tokens.len() && tokens[i] == Token::Elif {
708 i += 1; } else {
710 break;
711 }
712 }
713
714 let else_ast = if i < tokens.len() && tokens[i] == Token::Else {
715 i += 1; while i < tokens.len() && tokens[i] == Token::Newline {
719 i += 1;
720 }
721
722 let mut else_tokens = Vec::new();
723 let mut depth = 0;
724 while i < tokens.len() {
725 match &tokens[i] {
726 Token::If => {
727 depth += 1;
728 else_tokens.push(tokens[i].clone());
729 }
730 Token::Fi => {
731 if depth > 0 {
732 depth -= 1;
733 else_tokens.push(tokens[i].clone());
734 } else {
735 break; }
737 }
738 Token::Newline => {
739 let mut j = i + 1;
741 while j < tokens.len() && tokens[j] == Token::Newline {
742 j += 1;
743 }
744 if j < tokens.len() && depth == 0 && tokens[j] == Token::Fi {
745 i = j; break;
747 }
748 else_tokens.push(tokens[i].clone());
750 }
751 _ => {
752 else_tokens.push(tokens[i].clone());
753 }
754 }
755 i += 1;
756 }
757
758 let else_ast = if else_tokens.is_empty() {
759 Ast::Pipeline(vec![ShellCommand {
761 args: vec!["true".to_string()],
762 input: None,
763 output: None,
764 append: None,
765 }])
766 } else {
767 parse_commands_sequentially(&else_tokens)?
768 };
769
770 Some(Box::new(else_ast))
771 } else {
772 None
773 };
774
775 if i >= tokens.len() || tokens[i] != Token::Fi {
776 return Err("Expected fi".to_string());
777 }
778
779 Ok(Ast::If {
780 branches,
781 else_branch: else_ast,
782 })
783}
784
785fn parse_case(tokens: &[Token]) -> Result<Ast, String> {
786 let mut i = 1; if i >= tokens.len() || !matches!(tokens[i], Token::Word(_)) {
790 return Err("Expected word after case".to_string());
791 }
792 let word = if let Token::Word(ref w) = tokens[i] {
793 w.clone()
794 } else {
795 unreachable!()
796 };
797 i += 1;
798
799 if i >= tokens.len() || tokens[i] != Token::In {
800 return Err("Expected in after case word".to_string());
801 }
802 i += 1;
803
804 let mut cases = Vec::new();
805 let mut default = None;
806
807 loop {
808 while i < tokens.len() && tokens[i] == Token::Newline {
810 i += 1;
811 }
812
813 if i >= tokens.len() {
814 return Err("Unexpected end in case statement".to_string());
815 }
816
817 if tokens[i] == Token::Esac {
818 break;
819 }
820
821 let mut patterns = Vec::new();
823 while i < tokens.len() && tokens[i] != Token::RightParen {
824 if let Token::Word(ref p) = tokens[i] {
825 for pat in p.split('|') {
827 patterns.push(pat.to_string());
828 }
829 } else if tokens[i] == Token::Pipe {
830 } else if tokens[i] == Token::Newline {
832 } else {
834 return Err(format!("Expected pattern, found {:?}", tokens[i]));
835 }
836 i += 1;
837 }
838
839 if i >= tokens.len() || tokens[i] != Token::RightParen {
840 return Err("Expected ) after patterns".to_string());
841 }
842 i += 1;
843
844 let mut commands_tokens = Vec::new();
846 while i < tokens.len() && tokens[i] != Token::DoubleSemicolon && tokens[i] != Token::Esac {
847 commands_tokens.push(tokens[i].clone());
848 i += 1;
849 }
850
851 let commands_ast = parse_slice(&commands_tokens)?;
852
853 if i >= tokens.len() {
854 return Err("Unexpected end in case statement".to_string());
855 }
856
857 if tokens[i] == Token::DoubleSemicolon {
858 i += 1;
859 if patterns.len() == 1 && patterns[0] == "*" {
861 default = Some(Box::new(commands_ast));
862 } else {
863 cases.push((patterns, commands_ast));
864 }
865 } else if tokens[i] == Token::Esac {
866 if patterns.len() == 1 && patterns[0] == "*" {
868 default = Some(Box::new(commands_ast));
869 } else {
870 cases.push((patterns, commands_ast));
871 }
872 break;
873 } else {
874 return Err("Expected ;; or esac after commands".to_string());
875 }
876 }
877
878 Ok(Ast::Case {
879 word,
880 cases,
881 default,
882 })
883}
884
885fn parse_for(tokens: &[Token]) -> Result<Ast, String> {
886 let mut i = 1; if i >= tokens.len() || !matches!(tokens[i], Token::Word(_)) {
890 return Err("Expected variable name after for".to_string());
891 }
892 let variable = if let Token::Word(ref v) = tokens[i] {
893 v.clone()
894 } else {
895 unreachable!()
896 };
897 i += 1;
898
899 if i >= tokens.len() || tokens[i] != Token::In {
901 return Err("Expected 'in' after for variable".to_string());
902 }
903 i += 1;
904
905 let mut items = Vec::new();
907 while i < tokens.len() {
908 match &tokens[i] {
909 Token::Do => break,
910 Token::Semicolon | Token::Newline => {
911 i += 1;
912 if i < tokens.len() && tokens[i] == Token::Do {
914 break;
915 }
916 }
917 Token::Word(word) => {
918 items.push(word.clone());
919 i += 1;
920 }
921 _ => {
922 return Err(format!("Unexpected token in for items: {:?}", tokens[i]));
923 }
924 }
925 }
926
927 while i < tokens.len() && tokens[i] == Token::Newline {
929 i += 1;
930 }
931
932 if i >= tokens.len() || tokens[i] != Token::Do {
934 return Err("Expected 'do' in for loop".to_string());
935 }
936 i += 1;
937
938 while i < tokens.len() && tokens[i] == Token::Newline {
940 i += 1;
941 }
942
943 let mut body_tokens = Vec::new();
945 let mut depth = 0;
946 while i < tokens.len() {
947 match &tokens[i] {
948 Token::For => {
949 depth += 1;
950 body_tokens.push(tokens[i].clone());
951 }
952 Token::Done => {
953 if depth > 0 {
954 depth -= 1;
955 body_tokens.push(tokens[i].clone());
956 } else {
957 break; }
959 }
960 Token::Newline => {
961 let mut j = i + 1;
963 while j < tokens.len() && tokens[j] == Token::Newline {
964 j += 1;
965 }
966 if j < tokens.len() && depth == 0 && tokens[j] == Token::Done {
967 i = j; break;
969 }
970 body_tokens.push(tokens[i].clone());
972 }
973 _ => {
974 body_tokens.push(tokens[i].clone());
975 }
976 }
977 i += 1;
978 }
979
980 if i >= tokens.len() || tokens[i] != Token::Done {
981 return Err("Expected 'done' to close for loop".to_string());
982 }
983
984 let body_ast = if body_tokens.is_empty() {
986 Ast::Pipeline(vec![ShellCommand {
988 args: vec!["true".to_string()],
989 input: None,
990 output: None,
991 append: None,
992 }])
993 } else {
994 parse_commands_sequentially(&body_tokens)?
995 };
996
997 Ok(Ast::For {
998 variable,
999 items,
1000 body: Box::new(body_ast),
1001 })
1002}
1003
1004fn parse_while(tokens: &[Token]) -> Result<Ast, String> {
1005 let mut i = 1; let mut cond_tokens = Vec::new();
1009 while i < tokens.len() {
1010 match &tokens[i] {
1011 Token::Do => break,
1012 Token::Semicolon | Token::Newline => {
1013 i += 1;
1014 if i < tokens.len() && tokens[i] == Token::Do {
1016 break;
1017 }
1018 }
1019 _ => {
1020 cond_tokens.push(tokens[i].clone());
1021 i += 1;
1022 }
1023 }
1024 }
1025
1026 if cond_tokens.is_empty() {
1027 return Err("Expected condition after while".to_string());
1028 }
1029
1030 while i < tokens.len() && tokens[i] == Token::Newline {
1032 i += 1;
1033 }
1034
1035 if i >= tokens.len() || tokens[i] != Token::Do {
1037 return Err("Expected 'do' in while loop".to_string());
1038 }
1039 i += 1;
1040
1041 while i < tokens.len() && tokens[i] == Token::Newline {
1043 i += 1;
1044 }
1045
1046 let mut body_tokens = Vec::new();
1048 let mut depth = 0;
1049 while i < tokens.len() {
1050 match &tokens[i] {
1051 Token::While | Token::For => {
1052 depth += 1;
1053 body_tokens.push(tokens[i].clone());
1054 }
1055 Token::Done => {
1056 if depth > 0 {
1057 depth -= 1;
1058 body_tokens.push(tokens[i].clone());
1059 } else {
1060 break; }
1062 }
1063 Token::Newline => {
1064 let mut j = i + 1;
1066 while j < tokens.len() && tokens[j] == Token::Newline {
1067 j += 1;
1068 }
1069 if j < tokens.len() && depth == 0 && tokens[j] == Token::Done {
1070 i = j; break;
1072 }
1073 body_tokens.push(tokens[i].clone());
1075 }
1076 _ => {
1077 body_tokens.push(tokens[i].clone());
1078 }
1079 }
1080 i += 1;
1081 }
1082
1083 if i >= tokens.len() || tokens[i] != Token::Done {
1084 return Err("Expected 'done' to close while loop".to_string());
1085 }
1086
1087 let condition_ast = parse_slice(&cond_tokens)?;
1089
1090 let body_ast = if body_tokens.is_empty() {
1092 Ast::Pipeline(vec![ShellCommand {
1094 args: vec!["true".to_string()],
1095 input: None,
1096 output: None,
1097 append: None,
1098 }])
1099 } else {
1100 parse_commands_sequentially(&body_tokens)?
1101 };
1102
1103 Ok(Ast::While {
1104 condition: Box::new(condition_ast),
1105 body: Box::new(body_ast),
1106 })
1107}
1108
1109fn parse_function_definition(tokens: &[Token]) -> Result<Ast, String> {
1110 if tokens.len() < 2 {
1111 return Err("Function definition too short".to_string());
1112 }
1113
1114 let func_name = if let Token::Word(word) = &tokens[0] {
1116 if let Some(paren_pos) = word.find('(') {
1118 if word.ends_with(')') && paren_pos > 0 {
1119 word[..paren_pos].to_string()
1120 } else {
1121 word.clone()
1122 }
1123 } else {
1124 word.clone()
1125 }
1126 } else {
1127 return Err("Function name must be a word".to_string());
1128 };
1129
1130 let brace_pos = if tokens.len() >= 4 && tokens[1] == Token::LeftParen && tokens[2] == Token::RightParen {
1132 if tokens[3] != Token::LeftBrace {
1134 return Err("Expected { after function name".to_string());
1135 }
1136 3
1137 } else if tokens.len() >= 2 && tokens[1] == Token::LeftBrace {
1138 1
1140 } else {
1141 return Err("Expected ( after function name or { for legacy format".to_string());
1142 };
1143
1144 let mut brace_depth = 0;
1146 let mut body_end = 0;
1147 let mut found_closing = false;
1148 let mut i = brace_pos + 1;
1149
1150 while i < tokens.len() {
1151 if i + 3 < tokens.len()
1154 && matches!(&tokens[i], Token::Word(_))
1155 && tokens[i + 1] == Token::LeftParen
1156 && tokens[i + 2] == Token::RightParen
1157 && tokens[i + 3] == Token::LeftBrace
1158 {
1159 i += 4;
1162 let mut nested_depth = 1;
1163 while i < tokens.len() && nested_depth > 0 {
1164 match tokens[i] {
1165 Token::LeftBrace => nested_depth += 1,
1166 Token::RightBrace => nested_depth -= 1,
1167 _ => {}
1168 }
1169 i += 1;
1170 }
1171 continue;
1173 }
1174
1175 match &tokens[i] {
1176 Token::LeftBrace => {
1177 brace_depth += 1;
1178 i += 1;
1179 }
1180 Token::RightBrace => {
1181 if brace_depth == 0 {
1182 body_end = i;
1184 found_closing = true;
1185 break;
1186 } else {
1187 brace_depth -= 1;
1188 i += 1;
1189 }
1190 }
1191 Token::If => {
1192 let mut if_depth = 1;
1194 i += 1;
1195 while i < tokens.len() && if_depth > 0 {
1196 match tokens[i] {
1197 Token::If => if_depth += 1,
1198 Token::Fi => if_depth -= 1,
1199 _ => {}
1200 }
1201 i += 1;
1202 }
1203 }
1204 Token::For | Token::While => {
1205 let mut for_depth = 1;
1207 i += 1;
1208 while i < tokens.len() && for_depth > 0 {
1209 match tokens[i] {
1210 Token::For | Token::While => for_depth += 1,
1211 Token::Done => for_depth -= 1,
1212 _ => {}
1213 }
1214 i += 1;
1215 }
1216 }
1217 Token::Case => {
1218 i += 1;
1220 while i < tokens.len() {
1221 if tokens[i] == Token::Esac {
1222 i += 1;
1223 break;
1224 }
1225 i += 1;
1226 }
1227 }
1228 _ => {
1229 i += 1;
1230 }
1231 }
1232 }
1233
1234 if !found_closing {
1235 return Err("Missing closing } for function definition".to_string());
1236 }
1237
1238 let body_tokens = &tokens[brace_pos + 1..body_end];
1240
1241 let body_ast = if body_tokens.is_empty() {
1243 Ast::Pipeline(vec![ShellCommand {
1245 args: vec!["true".to_string()],
1246 input: None,
1247 output: None,
1248 append: None,
1249 }])
1250 } else {
1251 parse_commands_sequentially(body_tokens)?
1252 };
1253
1254 Ok(Ast::FunctionDefinition {
1255 name: func_name,
1256 body: Box::new(body_ast),
1257 })
1258}
1259
1260#[cfg(test)]
1261mod tests {
1262 use super::super::lexer::Token;
1263 use super::*;
1264
1265 #[test]
1266 fn test_single_command() {
1267 let tokens = vec![Token::Word("ls".to_string())];
1268 let result = parse(tokens).unwrap();
1269 assert_eq!(
1270 result,
1271 Ast::Pipeline(vec![ShellCommand {
1272 args: vec!["ls".to_string()],
1273 input: None,
1274 output: None,
1275 append: None,
1276 }])
1277 );
1278 }
1279
1280 #[test]
1281 fn test_command_with_args() {
1282 let tokens = vec![
1283 Token::Word("ls".to_string()),
1284 Token::Word("-la".to_string()),
1285 ];
1286 let result = parse(tokens).unwrap();
1287 assert_eq!(
1288 result,
1289 Ast::Pipeline(vec![ShellCommand {
1290 args: vec!["ls".to_string(), "-la".to_string()],
1291 input: None,
1292 output: None,
1293 append: None,
1294 }])
1295 );
1296 }
1297
1298 #[test]
1299 fn test_pipeline() {
1300 let tokens = vec![
1301 Token::Word("ls".to_string()),
1302 Token::Pipe,
1303 Token::Word("grep".to_string()),
1304 Token::Word("txt".to_string()),
1305 ];
1306 let result = parse(tokens).unwrap();
1307 assert_eq!(
1308 result,
1309 Ast::Pipeline(vec![
1310 ShellCommand {
1311 args: vec!["ls".to_string()],
1312 input: None,
1313 output: None,
1314 append: None,
1315 },
1316 ShellCommand {
1317 args: vec!["grep".to_string(), "txt".to_string()],
1318 input: None,
1319 output: None,
1320 append: None,
1321 }
1322 ])
1323 );
1324 }
1325
1326 #[test]
1327 fn test_input_redirection() {
1328 let tokens = vec![
1329 Token::Word("cat".to_string()),
1330 Token::RedirIn,
1331 Token::Word("input.txt".to_string()),
1332 ];
1333 let result = parse(tokens).unwrap();
1334 assert_eq!(
1335 result,
1336 Ast::Pipeline(vec![ShellCommand {
1337 args: vec!["cat".to_string()],
1338 input: Some("input.txt".to_string()),
1339 output: None,
1340 append: None,
1341 }])
1342 );
1343 }
1344
1345 #[test]
1346 fn test_output_redirection() {
1347 let tokens = vec![
1348 Token::Word("printf".to_string()),
1349 Token::Word("hello".to_string()),
1350 Token::RedirOut,
1351 Token::Word("output.txt".to_string()),
1352 ];
1353 let result = parse(tokens).unwrap();
1354 assert_eq!(
1355 result,
1356 Ast::Pipeline(vec![ShellCommand {
1357 args: vec!["printf".to_string(), "hello".to_string()],
1358 input: None,
1359 output: Some("output.txt".to_string()),
1360 append: None,
1361 }])
1362 );
1363 }
1364
1365 #[test]
1366 fn test_append_redirection() {
1367 let tokens = vec![
1368 Token::Word("printf".to_string()),
1369 Token::Word("hello".to_string()),
1370 Token::RedirAppend,
1371 Token::Word("output.txt".to_string()),
1372 ];
1373 let result = parse(tokens).unwrap();
1374 assert_eq!(
1375 result,
1376 Ast::Pipeline(vec![ShellCommand {
1377 args: vec!["printf".to_string(), "hello".to_string()],
1378 input: None,
1379 output: None,
1380 append: Some("output.txt".to_string()),
1381 }])
1382 );
1383 }
1384
1385 #[test]
1386 fn test_complex_pipeline_with_redirections() {
1387 let tokens = vec![
1388 Token::Word("cat".to_string()),
1389 Token::RedirIn,
1390 Token::Word("input.txt".to_string()),
1391 Token::Pipe,
1392 Token::Word("grep".to_string()),
1393 Token::Word("pattern".to_string()),
1394 Token::Pipe,
1395 Token::Word("sort".to_string()),
1396 Token::RedirOut,
1397 Token::Word("output.txt".to_string()),
1398 ];
1399 let result = parse(tokens).unwrap();
1400 assert_eq!(
1401 result,
1402 Ast::Pipeline(vec![
1403 ShellCommand {
1404 args: vec!["cat".to_string()],
1405 input: Some("input.txt".to_string()),
1406 output: None,
1407 append: None,
1408 },
1409 ShellCommand {
1410 args: vec!["grep".to_string(), "pattern".to_string()],
1411 input: None,
1412 output: None,
1413 append: None,
1414 },
1415 ShellCommand {
1416 args: vec!["sort".to_string()],
1417 input: None,
1418 output: Some("output.txt".to_string()),
1419 append: None,
1420 }
1421 ])
1422 );
1423 }
1424
1425 #[test]
1426 fn test_empty_tokens() {
1427 let tokens = vec![];
1428 let result = parse(tokens);
1429 assert!(result.is_err());
1430 assert_eq!(result.unwrap_err(), "No commands found");
1431 }
1432
1433 #[test]
1434 fn test_only_pipe() {
1435 let tokens = vec![Token::Pipe];
1436 let result = parse(tokens);
1437 assert!(result.is_err());
1438 assert_eq!(result.unwrap_err(), "No commands found");
1439 }
1440
1441 #[test]
1442 fn test_redirection_without_file() {
1443 let tokens = vec![Token::Word("cat".to_string()), Token::RedirIn];
1445 let result = parse(tokens).unwrap();
1446 assert_eq!(
1447 result,
1448 Ast::Pipeline(vec![ShellCommand {
1449 args: vec!["cat".to_string()],
1450 input: None,
1451 output: None,
1452 append: None,
1453 }])
1454 );
1455 }
1456
1457 #[test]
1458 fn test_multiple_redirections() {
1459 let tokens = vec![
1460 Token::Word("cat".to_string()),
1461 Token::RedirIn,
1462 Token::Word("file1.txt".to_string()),
1463 Token::RedirOut,
1464 Token::Word("file2.txt".to_string()),
1465 ];
1466 let result = parse(tokens).unwrap();
1467 assert_eq!(
1468 result,
1469 Ast::Pipeline(vec![ShellCommand {
1470 args: vec!["cat".to_string()],
1471 input: Some("file1.txt".to_string()),
1472 output: Some("file2.txt".to_string()),
1473 append: None,
1474 }])
1475 );
1476 }
1477
1478 #[test]
1479 fn test_parse_if() {
1480 let tokens = vec![
1481 Token::If,
1482 Token::Word("true".to_string()),
1483 Token::Semicolon,
1484 Token::Then,
1485 Token::Word("printf".to_string()),
1486 Token::Word("yes".to_string()),
1487 Token::Semicolon,
1488 Token::Fi,
1489 ];
1490 let result = parse(tokens).unwrap();
1491 if let Ast::If {
1492 branches,
1493 else_branch,
1494 } = result
1495 {
1496 assert_eq!(branches.len(), 1);
1497 let (condition, then_branch) = &branches[0];
1498 if let Ast::Pipeline(cmds) = &**condition {
1499 assert_eq!(cmds[0].args, vec!["true"]);
1500 } else {
1501 panic!("condition not pipeline");
1502 }
1503 if let Ast::Pipeline(cmds) = &**then_branch {
1504 assert_eq!(cmds[0].args, vec!["printf", "yes"]);
1505 } else {
1506 panic!("then_branch not pipeline");
1507 }
1508 assert!(else_branch.is_none());
1509 } else {
1510 panic!("not if");
1511 }
1512 }
1513
1514 #[test]
1515 fn test_parse_if_elif() {
1516 let tokens = vec![
1517 Token::If,
1518 Token::Word("false".to_string()),
1519 Token::Semicolon,
1520 Token::Then,
1521 Token::Word("printf".to_string()),
1522 Token::Word("no".to_string()),
1523 Token::Semicolon,
1524 Token::Elif,
1525 Token::Word("true".to_string()),
1526 Token::Semicolon,
1527 Token::Then,
1528 Token::Word("printf".to_string()),
1529 Token::Word("yes".to_string()),
1530 Token::Semicolon,
1531 Token::Fi,
1532 ];
1533 let result = parse(tokens).unwrap();
1534 if let Ast::If {
1535 branches,
1536 else_branch,
1537 } = result
1538 {
1539 assert_eq!(branches.len(), 2);
1540 let (condition1, then1) = &branches[0];
1542 if let Ast::Pipeline(cmds) = &**condition1 {
1543 assert_eq!(cmds[0].args, vec!["false"]);
1544 }
1545 if let Ast::Pipeline(cmds) = &**then1 {
1546 assert_eq!(cmds[0].args, vec!["printf", "no"]);
1547 }
1548 let (condition2, then2) = &branches[1];
1550 if let Ast::Pipeline(cmds) = &**condition2 {
1551 assert_eq!(cmds[0].args, vec!["true"]);
1552 }
1553 if let Ast::Pipeline(cmds) = &**then2 {
1554 assert_eq!(cmds[0].args, vec!["printf", "yes"]);
1555 }
1556 assert!(else_branch.is_none());
1557 } else {
1558 panic!("not if");
1559 }
1560 }
1561
1562 #[test]
1563 fn test_parse_assignment() {
1564 let tokens = vec![Token::Word("MY_VAR=test_value".to_string())];
1565 let result = parse(tokens).unwrap();
1566 if let Ast::Assignment { var, value } = result {
1567 assert_eq!(var, "MY_VAR");
1568 assert_eq!(value, "test_value");
1569 } else {
1570 panic!("not assignment");
1571 }
1572 }
1573
1574 #[test]
1575 fn test_parse_assignment_quoted() {
1576 let tokens = vec![Token::Word("MY_VAR=hello world".to_string())];
1577 let result = parse(tokens).unwrap();
1578 if let Ast::Assignment { var, value } = result {
1579 assert_eq!(var, "MY_VAR");
1580 assert_eq!(value, "hello world");
1581 } else {
1582 panic!("not assignment");
1583 }
1584 }
1585
1586 #[test]
1587 fn test_parse_assignment_invalid() {
1588 let tokens = vec![Token::Word("123VAR=value".to_string())];
1590 let result = parse(tokens).unwrap();
1591 if let Ast::Pipeline(cmds) = result {
1592 assert_eq!(cmds[0].args, vec!["123VAR=value"]);
1593 } else {
1594 panic!("should be parsed as pipeline");
1595 }
1596 }
1597
1598 #[test]
1599 fn test_parse_function_definition() {
1600 let tokens = vec![
1601 Token::Word("myfunc".to_string()),
1602 Token::LeftParen,
1603 Token::RightParen,
1604 Token::LeftBrace,
1605 Token::Word("echo".to_string()),
1606 Token::Word("hello".to_string()),
1607 Token::RightBrace,
1608 ];
1609 let result = parse(tokens).unwrap();
1610 if let Ast::FunctionDefinition { name, body } = result {
1611 assert_eq!(name, "myfunc");
1612 if let Ast::Pipeline(cmds) = *body {
1614 assert_eq!(cmds[0].args, vec!["echo", "hello"]);
1615 } else {
1616 panic!("function body should be a pipeline");
1617 }
1618 } else {
1619 panic!("should be parsed as function definition");
1620 }
1621 }
1622
1623 #[test]
1624 fn test_parse_function_definition_empty() {
1625 let tokens = vec![
1626 Token::Word("emptyfunc".to_string()),
1627 Token::LeftParen,
1628 Token::RightParen,
1629 Token::LeftBrace,
1630 Token::RightBrace,
1631 ];
1632 let result = parse(tokens).unwrap();
1633 if let Ast::FunctionDefinition { name, body } = result {
1634 assert_eq!(name, "emptyfunc");
1635 if let Ast::Pipeline(cmds) = *body {
1637 assert_eq!(cmds[0].args, vec!["true"]);
1638 } else {
1639 panic!("function body should be a pipeline");
1640 }
1641 } else {
1642 panic!("should be parsed as function definition");
1643 }
1644 }
1645
1646 #[test]
1647 fn test_parse_function_definition_legacy_format() {
1648 let tokens = vec![
1650 Token::Word("legacyfunc()".to_string()),
1651 Token::LeftBrace,
1652 Token::Word("echo".to_string()),
1653 Token::Word("hello".to_string()),
1654 Token::RightBrace,
1655 ];
1656 let result = parse(tokens).unwrap();
1657 if let Ast::FunctionDefinition { name, body } = result {
1658 assert_eq!(name, "legacyfunc");
1659 if let Ast::Pipeline(cmds) = *body {
1661 assert_eq!(cmds[0].args, vec!["echo", "hello"]);
1662 } else {
1663 panic!("function body should be a pipeline");
1664 }
1665 } else {
1666 panic!("should be parsed as function definition");
1667 }
1668 }
1669
1670 #[test]
1671 fn test_parse_local_assignment() {
1672 let tokens = vec![
1673 Token::Local,
1674 Token::Word("MY_VAR=test_value".to_string()),
1675 ];
1676 let result = parse(tokens).unwrap();
1677 if let Ast::LocalAssignment { var, value } = result {
1678 assert_eq!(var, "MY_VAR");
1679 assert_eq!(value, "test_value");
1680 } else {
1681 panic!("should be parsed as local assignment");
1682 }
1683 }
1684
1685 #[test]
1686 fn test_parse_local_assignment_separate_tokens() {
1687 let tokens = vec![
1688 Token::Local,
1689 Token::Word("MY_VAR".to_string()),
1690 Token::Word("test_value".to_string()),
1691 ];
1692 let result = parse(tokens).unwrap();
1693 if let Ast::LocalAssignment { var, value } = result {
1694 assert_eq!(var, "MY_VAR");
1695 assert_eq!(value, "test_value");
1696 } else {
1697 panic!("should be parsed as local assignment");
1698 }
1699 }
1700
1701 #[test]
1702 fn test_parse_local_assignment_invalid_var_name() {
1703 let tokens = vec![
1705 Token::Local,
1706 Token::Word("123VAR=value".to_string()),
1707 ];
1708 let result = parse(tokens);
1709 assert!(result.is_err());
1711 }
1712}