shrub_parser/
test_command.rs1use crate::{ast, error};
4
5pub fn parse<S: AsRef<str>>(input: &[S]) -> Result<ast::TestExpr, error::TestCommandParseError> {
17 let expr =
18 test_command::full_expression(&input.iter().map(AsRef::as_ref).collect::<Vec<&str>>())?;
19
20 Ok(expr)
21}
22
23peg::parser! {
24 grammar test_command<'a>() for [&'a str] {
25 pub(crate) rule full_expression() -> ast::TestExpr =
26 end() { ast::TestExpr::False } /
27 e:one_arg_expr() end() { e } /
28 e:two_arg_expr() end() { e } /
29 e:three_arg_expr() end() { e } /
30 e:four_arg_expr() end() { e } /
31 expression()
32
33 rule one_arg_expr() -> ast::TestExpr =
34 [s] { ast::TestExpr::Literal(s.to_owned()) }
35
36 rule two_arg_expr() -> ast::TestExpr =
37 ["!"] e:one_arg_expr() { ast::TestExpr::Not(Box::from(e)) } /
38 op:unary_op() [s] { ast::TestExpr::UnaryTest(op, s.to_owned()) }
39
40 rule three_arg_expr() -> ast::TestExpr =
41 [left] ["-a"] [right] { ast::TestExpr::And(Box::from(ast::TestExpr::Literal(left.to_owned())), Box::from(ast::TestExpr::Literal(right.to_owned()))) } /
42 [left] ["-o"] [right] { ast::TestExpr::Or(Box::from(ast::TestExpr::Literal(left.to_owned())), Box::from(ast::TestExpr::Literal(right.to_owned()))) } /
43 [left] op:binary_op() [right] { ast::TestExpr::BinaryTest(op, left.to_owned(), right.to_owned()) } /
44 ["!"] e:two_arg_expr() { ast::TestExpr::Not(Box::from(e)) } /
45 ["("] e:one_arg_expr() [")"] { e }
46
47 rule four_arg_expr() -> ast::TestExpr =
48 ["!"] e:three_arg_expr() { ast::TestExpr::Not(Box::from(e)) }
49
50 rule expression() -> ast::TestExpr = precedence! {
51 left:(@) ["-a"] right:@ { ast::TestExpr::And(Box::from(left), Box::from(right)) }
52 left:(@) ["-o"] right:@ { ast::TestExpr::Or(Box::from(left), Box::from(right)) }
53 --
54 ["("] e:expression() [")"] { ast::TestExpr::Parenthesized(Box::from(e)) }
55 --
56 ["!"] e:@ { ast::TestExpr::Not(Box::from(e)) }
57 --
58 [left] op:binary_op() [right] { ast::TestExpr::BinaryTest(op, left.to_owned(), right.to_owned()) }
59 --
60 op:unary_op() [operand] { ast::TestExpr::UnaryTest(op, operand.to_owned()) }
61 --
62 [s] { ast::TestExpr::Literal(s.to_owned()) }
63 }
64
65 rule unary_op() -> ast::UnaryPredicate =
66 ["-a"] { ast::UnaryPredicate::FileExists } /
67 ["-b"] { ast::UnaryPredicate::FileExistsAndIsBlockSpecialFile } /
68 ["-c"] { ast::UnaryPredicate::FileExistsAndIsCharSpecialFile } /
69 ["-d"] { ast::UnaryPredicate::FileExistsAndIsDir } /
70 ["-e"] { ast::UnaryPredicate::FileExists } /
71 ["-f"] { ast::UnaryPredicate::FileExistsAndIsRegularFile } /
72 ["-g"] { ast::UnaryPredicate::FileExistsAndIsSetgid } /
73 ["-h"] { ast::UnaryPredicate::FileExistsAndIsSymlink } /
74 ["-k"] { ast::UnaryPredicate::FileExistsAndHasStickyBit } /
75 ["-n"] { ast::UnaryPredicate::StringHasNonZeroLength } /
76 ["-o"] { ast::UnaryPredicate::ShellOptionEnabled } /
77 ["-p"] { ast::UnaryPredicate::FileExistsAndIsFifo } /
78 ["-r"] { ast::UnaryPredicate::FileExistsAndIsReadable } /
79 ["-s"] { ast::UnaryPredicate::FileExistsAndIsNotZeroLength } /
80 ["-t"] { ast::UnaryPredicate::FdIsOpenTerminal } /
81 ["-u"] { ast::UnaryPredicate::FileExistsAndIsSetuid } /
82 ["-v"] { ast::UnaryPredicate::ShellVariableIsSetAndAssigned } /
83 ["-w"] { ast::UnaryPredicate::FileExistsAndIsWritable } /
84 ["-x"] { ast::UnaryPredicate::FileExistsAndIsExecutable } /
85 ["-z"] { ast::UnaryPredicate::StringHasZeroLength } /
86 ["-G"] { ast::UnaryPredicate::FileExistsAndOwnedByEffectiveGroupId } /
87 ["-L"] { ast::UnaryPredicate::FileExistsAndIsSymlink } /
88 ["-N"] { ast::UnaryPredicate::FileExistsAndModifiedSinceLastRead } /
89 ["-O"] { ast::UnaryPredicate::FileExistsAndOwnedByEffectiveUserId } /
90 ["-R"] { ast::UnaryPredicate::ShellVariableIsSetAndNameRef } /
91 ["-S"] { ast::UnaryPredicate::FileExistsAndIsSocket }
92
93 rule binary_op() -> ast::BinaryPredicate =
94 ["=="] { ast::BinaryPredicate::StringExactlyMatchesString } /
95 ["-ef"] { ast::BinaryPredicate::FilesReferToSameDeviceAndInodeNumbers } /
96 ["-eq"] { ast::BinaryPredicate::ArithmeticEqualTo } /
97 ["-ge"] { ast::BinaryPredicate::ArithmeticGreaterThanOrEqualTo } /
98 ["-gt"] { ast::BinaryPredicate::ArithmeticGreaterThan } /
99 ["-le"] { ast::BinaryPredicate::ArithmeticLessThanOrEqualTo } /
100 ["-lt"] { ast::BinaryPredicate::ArithmeticLessThan } /
101 ["-ne"] { ast::BinaryPredicate::ArithmeticNotEqualTo } /
102 ["-nt"] { ast::BinaryPredicate::LeftFileIsNewerOrExistsWhenRightDoesNot } /
103 ["-ot"] { ast::BinaryPredicate::LeftFileIsOlderOrDoesNotExistWhenRightDoes } /
104 ["="] { ast::BinaryPredicate::StringExactlyMatchesString } /
105 ["!="] { ast::BinaryPredicate::StringDoesNotExactlyMatchString } /
106 ["<"] { ast::BinaryPredicate::LeftSortsBeforeRight } /
107 [">"] { ast::BinaryPredicate::LeftSortsAfterRight }
108
109 rule end() = ![_]
110 }
111}