yash_syntax/parser/lex/
raw_param.rs1use super::core::Lexer;
20use crate::parser::core::Result;
21use crate::syntax::Param;
22use crate::syntax::ParamType;
23use crate::syntax::SpecialParam;
24use crate::syntax::TextUnit;
25
26pub const fn is_portable_name_char(c: char) -> bool {
32 matches!(c, '0'..='9' | 'A'..='Z' | '_' | 'a'..='z')
33}
34
35pub const fn is_special_parameter_char(c: char) -> bool {
39 SpecialParam::from_char(c).is_some()
40}
41
42pub const fn is_single_char_name(c: char) -> bool {
50 c.is_ascii_digit() || is_special_parameter_char(c)
51}
52
53impl Lexer<'_> {
54 pub async fn raw_param(&mut self, start_index: usize) -> Result<Option<TextUnit>> {
65 let param = if let Some(c) = self.consume_char_if(is_special_parameter_char).await? {
66 Param {
67 id: c.value.to_string(),
68 r#type: SpecialParam::from_char(c.value).unwrap().into(),
69 }
70 } else if let Some(c) = self.consume_char_if(|c| c.is_ascii_digit()).await? {
71 Param {
72 id: c.value.to_string(),
73 r#type: ParamType::Positional(c.value.to_digit(10).unwrap() as usize),
74 }
75 } else if let Some(c) = self.consume_char_if(is_portable_name_char).await? {
76 let mut name = c.value.to_string();
77 while let Some(c) = self.consume_char_if(is_portable_name_char).await? {
78 name.push(c.value);
79 }
80 Param::variable(name)
81 } else {
82 return Ok(None);
83 };
84
85 let location = self.location_range(start_index..self.index());
86
87 Ok(Some(TextUnit::RawParam { param, location }))
88 }
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94 use crate::source::Source;
95 use crate::syntax::Param;
96 use assert_matches::assert_matches;
97 use futures_util::FutureExt;
98
99 #[test]
100 fn lexer_raw_param_special_parameter() {
101 let mut lexer = Lexer::with_code("$@;");
102 lexer.peek_char().now_or_never().unwrap().unwrap();
103 lexer.consume_char();
104
105 let result = lexer.raw_param(0).now_or_never().unwrap().unwrap().unwrap();
106 assert_matches!(result, TextUnit::RawParam { param, location } => {
107 assert_eq!(param, Param::from(SpecialParam::At));
108 assert_eq!(*location.code.value.borrow(), "$@;");
109 assert_eq!(location.code.start_line_number.get(), 1);
110 assert_eq!(*location.code.source, Source::Unknown);
111 assert_eq!(location.range, 0..2);
112 });
113
114 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some(';')));
115 }
116
117 #[test]
118 fn lexer_raw_param_digit() {
119 let mut lexer = Lexer::with_code("$12");
120 lexer.peek_char().now_or_never().unwrap().unwrap();
121 lexer.consume_char();
122
123 let result = lexer.raw_param(0).now_or_never().unwrap().unwrap().unwrap();
124 assert_matches!(result, TextUnit::RawParam { param, location } => {
125 assert_eq!(param, Param::from(1));
126 assert_eq!(*location.code.value.borrow(), "$12");
127 assert_eq!(location.code.start_line_number.get(), 1);
128 assert_eq!(*location.code.source, Source::Unknown);
129 assert_eq!(location.range, 0..2);
130 });
131
132 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('2')));
133 }
134
135 #[test]
136 fn lexer_raw_param_posix_name() {
137 let mut lexer = Lexer::with_code("$az_AZ_019<");
138 lexer.peek_char().now_or_never().unwrap().unwrap();
139 lexer.consume_char();
140
141 let result = lexer.raw_param(0).now_or_never().unwrap().unwrap().unwrap();
142 assert_matches!(result, TextUnit::RawParam { param, location } => {
143 assert_eq!(param, Param::variable("az_AZ_019"));
144 assert_eq!(*location.code.value.borrow(), "$az_AZ_019<");
145 assert_eq!(location.code.start_line_number.get(), 1);
146 assert_eq!(*location.code.source, Source::Unknown);
147 assert_eq!(location.range, 0..10);
148 });
149
150 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('<')));
151 }
152
153 #[test]
154 fn lexer_raw_param_posix_name_line_continuations() {
155 let mut lexer = Lexer::with_code("$a\\\n\\\nb\\\n\\\nc\\\n>");
156 lexer.peek_char().now_or_never().unwrap().unwrap();
157 lexer.consume_char();
158
159 let result = lexer.raw_param(0).now_or_never().unwrap().unwrap().unwrap();
160 assert_matches!(result, TextUnit::RawParam { param, location } => {
161 assert_eq!(param, Param::variable("abc"));
162 assert_eq!(*location.code.value.borrow(), "$a\\\n\\\nb\\\n\\\nc\\\n>");
163 assert_eq!(location.code.start_line_number.get(), 1);
164 assert_eq!(*location.code.source, Source::Unknown);
165 assert_eq!(location.range, 0..14);
166 });
167
168 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('>')));
169 }
170
171 #[test]
172 fn lexer_raw_param_not_parameter() {
173 let mut lexer = Lexer::with_code("$;");
174 lexer.peek_char().now_or_never().unwrap().unwrap();
175 lexer.consume_char();
176 assert_eq!(lexer.raw_param(0).now_or_never().unwrap(), Ok(None));
177 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some(';')));
178 }
179}