1use nash_region::Region;
12use nash_source::{Exposed, Exposing, Privacy};
13
14use crate::Parser;
15use crate::error;
16
17impl<'a> Parser<'a> {
18 pub fn exposing(&mut self) -> Result<Exposing<'a>, error::Exposing> {
25 self.word1(b'(', error::Exposing::Start)?;
27 self.chomp_and_check_indent(error::Exposing::Space, error::Exposing::IndentValue)?;
28
29 self.one_of(
31 error::Exposing::Value,
32 vec![
33 Box::new(|p: &mut Parser<'a>| {
35 p.word2(b'.', b'.', error::Exposing::Value)?;
36 p.chomp_and_check_indent(error::Exposing::Space, error::Exposing::IndentEnd)?;
37 p.word1(b')', error::Exposing::End)?;
38 Ok(Exposing::Open)
39 }),
40 Box::new(|p: &mut Parser<'a>| {
42 let exposed = p.chomp_exposed()?;
43 p.chomp_and_check_indent(error::Exposing::Space, error::Exposing::IndentEnd)?;
44 p.exposing_help(vec![exposed])
45 }),
46 ],
47 )
48 }
49
50 fn exposing_help(
54 &mut self,
55 mut rev_exposed: Vec<&'a Exposed<'a>>,
56 ) -> Result<Exposing<'a>, error::Exposing> {
57 loop {
58 let rev_exposed_for_fallback = rev_exposed.clone();
59
60 let result = self.one_of(
61 error::Exposing::End,
62 vec![
63 Box::new(|p: &mut Parser<'a>| {
65 p.word1(b',', error::Exposing::End)?;
66 p.chomp_and_check_indent(
67 error::Exposing::Space,
68 error::Exposing::IndentValue,
69 )?;
70 let exposed = p.chomp_exposed()?;
71 p.chomp_and_check_indent(
72 error::Exposing::Space,
73 error::Exposing::IndentEnd,
74 )?;
75 rev_exposed.push(exposed);
76 Ok(ExposingHelpState::Continue)
77 }),
78 Box::new(|p: &mut Parser<'a>| {
80 p.word1(b')', error::Exposing::End)?;
81 let exposed_slice = p.alloc_slice_copy(&rev_exposed_for_fallback);
82 Ok(ExposingHelpState::Done(Exposing::Explicit(exposed_slice)))
83 }),
84 ],
85 )?;
86
87 match result {
88 ExposingHelpState::Continue => continue,
89 ExposingHelpState::Done(exposing) => return Ok(exposing),
90 }
91 }
92 }
93
94 fn chomp_exposed(&mut self) -> Result<&'a Exposed<'a>, error::Exposing> {
101 let start = self.get_position();
102
103 self.one_of(
104 error::Exposing::Value,
105 vec![
106 Box::new(|p: &mut Parser<'a>| {
108 let name = p.lower_name(error::Exposing::Value)?;
109 let located = p.add_end(start, name);
110 Ok(p.alloc(Exposed::Lower(located)))
111 }),
112 Box::new(|p: &mut Parser<'a>| {
114 p.word1(b'(', error::Exposing::Value)?;
115 let op = p.operator(error::Exposing::Operator, |bad_op, row, col| {
116 error::Exposing::OperatorReserved(bad_op, row, col)
117 })?;
118 p.word1(b')', error::Exposing::OperatorRightParen)?;
119 let end = p.get_position();
120 Ok(p.alloc(Exposed::Operator {
121 region: Region::new(start, end),
122 op,
123 }))
124 }),
125 Box::new(|p: &mut Parser<'a>| {
127 let name = p.upper_name(error::Exposing::Value)?;
128 let located = p.add_end(start, name);
129 p.chomp_and_check_indent(error::Exposing::Space, error::Exposing::IndentEnd)?;
130 let privacy = p.privacy()?;
131 Ok(p.alloc(Exposed::Upper {
132 name: located,
133 privacy,
134 }))
135 }),
136 ],
137 )
138 }
139
140 fn privacy(&mut self) -> Result<Privacy, error::Exposing> {
144 self.one_of_with_fallback(
145 vec![Box::new(|p: &mut Parser<'a>| {
146 p.word1(b'(', error::Exposing::TypePrivacy)?;
147 p.chomp_and_check_indent(error::Exposing::Space, error::Exposing::TypePrivacy)?;
148 let start = p.get_position();
149 p.word2(b'.', b'.', error::Exposing::TypePrivacy)?;
150 let end = p.get_position();
151 p.chomp_and_check_indent(error::Exposing::Space, error::Exposing::TypePrivacy)?;
152 p.word1(b')', error::Exposing::TypePrivacy)?;
153 Ok(Privacy::Public(Region::new(start, end)))
154 })],
155 Privacy::Private,
156 )
157 }
158}
159
160enum ExposingHelpState<'a> {
162 Continue,
163 Done(Exposing<'a>),
164}
165
166#[cfg(test)]
167mod tests {
168 use super::*;
169 use bumpalo::Bump;
170 use indoc::indoc;
171
172 macro_rules! assert_exposing_snapshot {
173 ($input:expr) => {{
174 let input = indoc!($input);
175 let bump = Bump::new();
176 let src = bump.alloc_str(input);
177 let mut parser = Parser::new(&bump, src.as_bytes());
178 let result = parser.exposing();
179 match result {
180 Ok(ref exposing) => {
181 insta::with_settings!({
182 description => format!("Code:\n\n{}", input),
183 omit_expression => true,
184 }, {
185 insta::assert_debug_snapshot!(exposing);
186 });
187 }
188 Err(e) => {
189 panic!("Expected successful parse, got error: {:?}", e);
190 }
191 }
192 }};
193 }
194
195 #[test]
196 fn exposing_open() {
197 assert_exposing_snapshot!("(..)");
198 }
199
200 #[test]
201 fn exposing_single_value() {
202 assert_exposing_snapshot!("(foo)");
203 }
204
205 #[test]
206 fn exposing_multiple_values() {
207 assert_exposing_snapshot!("(foo, bar, baz)");
208 }
209
210 #[test]
211 fn exposing_type_private() {
212 assert_exposing_snapshot!("(Foo)");
213 }
214
215 #[test]
216 fn exposing_type_public() {
217 assert_exposing_snapshot!("(Foo(..))");
218 }
219
220 #[test]
221 fn exposing_operator() {
222 assert_exposing_snapshot!("((+))");
223 }
224
225 #[test]
226 fn exposing_multiple_operators() {
227 assert_exposing_snapshot!("((+), (-), (++))");
228 }
229
230 #[test]
231 fn exposing_mixed() {
232 assert_exposing_snapshot!("(foo, Bar, Baz(..), (+))");
233 }
234
235 #[test]
236 fn exposing_with_spaces() {
237 assert_exposing_snapshot!("( foo , bar )");
238 }
239
240 }