nash_parse/declaration/
infix.rs1use nash_region::Located;
8use nash_source::{Associativity, Infix, Precedence};
9
10use crate::Parser;
11use crate::error::Module as ModuleErr;
12
13impl<'a> Parser<'a> {
14 pub fn infix_decl(&mut self) -> Result<&'a Located<Infix<'a>>, ModuleErr<'a>> {
42 let start = self.get_position();
43 let err = ModuleErr::Infix;
44
45 self.keyword_infix(err)?;
46 self.chomp_and_check_indent(|_, r, c| err(r, c), err)?;
47
48 let associativity = self.one_of(
50 err,
51 vec![
52 Box::new(|p: &mut Parser<'a>| {
53 p.keyword_left(err)?;
54 Ok(Associativity::Left)
55 }),
56 Box::new(|p| {
57 p.keyword_right(err)?;
58 Ok(Associativity::Right)
59 }),
60 Box::new(|p| {
61 p.keyword_non(err)?;
62 Ok(Associativity::None)
63 }),
64 ],
65 )?;
66
67 self.chomp_and_check_indent(|_, r, c| err(r, c), err)?;
68
69 let precedence = self.precedence(err)?;
71
72 self.chomp_and_check_indent(|_, r, c| err(r, c), err)?;
73
74 self.word1(b'(', err)?;
76 let op = self.operator(err, |_, r, c| err(r, c))?;
77 self.word1(b')', err)?;
78
79 self.chomp_and_check_indent(|_, r, c| err(r, c), err)?;
80
81 self.word1(b'=', err)?;
83
84 self.chomp_and_check_indent(|_, r, c| err(r, c), err)?;
85
86 let name = self.lower_name(err)?;
88
89 self.chomp(|_, r, c| err(r, c))?;
91 self.check_fresh_line(err)?;
92
93 let infix = Infix {
94 op,
95 associativity,
96 precedence,
97 name,
98 };
99
100 Ok(self.add_end(start, infix))
101 }
102
103 fn precedence<E>(&mut self, to_error: impl FnOnce(u16, u16) -> E) -> Result<Precedence, E> {
107 match self.peek() {
108 Some(b) if b.is_ascii_digit() => {
109 let value = (b - b'0') as u16;
110 self.advance();
111 Ok(Precedence(value))
112 }
113 _ => {
114 let (row, col) = self.position();
115 Err(to_error(row, col))
116 }
117 }
118 }
119}
120
121#[cfg(test)]
122mod tests {
123 use crate::Parser;
124
125 macro_rules! assert_infix_snapshot {
128 ($src:expr) => {{
129 let bump = bumpalo::Bump::new();
130 let src = concat!($src, "\n");
131 let src_in_arena = bump.alloc_str(src);
132 let mut parser = Parser::new(&bump, src_in_arena.as_bytes());
133 match parser.infix_decl() {
134 Ok(infix) => {
135 insta::with_settings!({
136 description => $src,
137 omit_expression => true,
138 }, {
139 insta::assert_debug_snapshot!(infix);
140 });
141 }
142 Err(e) => panic!("Expected Ok, got Err: {:?}\n\nSource:\n{}", e, src),
143 }
144 }};
145 }
146
147 #[test]
148 fn infix_left() {
149 assert_infix_snapshot!("infix left 6 (|>) = apR");
150 }
151
152 #[test]
153 fn infix_right() {
154 assert_infix_snapshot!("infix right 5 (::) = cons");
155 }
156
157 #[test]
158 fn infix_non() {
159 assert_infix_snapshot!("infix non 4 (==) = eq");
160 }
161}