1use super::keyword::Keyword;
20use super::op::Operator;
21use crate::alias::Alias;
22use crate::input::Context;
23use crate::input::InputObject;
24use crate::input::Memory;
25use crate::parser::core::Result;
26use crate::parser::error::Error;
27use crate::source::Code;
28use crate::source::Location;
29use crate::source::Source;
30use crate::source::SourceChar;
31use crate::source::source_chars;
32use crate::syntax::Word;
33use std::cell::RefCell;
34use std::fmt;
35use std::num::NonZeroU64;
36use std::ops::Deref;
37use std::ops::DerefMut;
38use std::ops::Range;
39use std::pin::Pin;
40use std::rc::Rc;
41
42pub fn is_blank(c: char) -> bool {
44 c != '\n' && c.is_whitespace()
46}
47
48#[derive(Clone, Copy, Debug, Eq, PartialEq)]
50enum PeekChar<'a> {
51 Char(&'a SourceChar),
52 EndOfInput(&'a Location),
53}
54
55impl<'a> PeekChar<'a> {
56 #[must_use]
58 fn location<'b>(self: &'b PeekChar<'a>) -> &'a Location {
59 match self {
60 PeekChar::Char(c) => &c.location,
61 PeekChar::EndOfInput(l) => l,
62 }
63 }
64}
65
66#[derive(Clone, Copy, Debug, Eq, PartialEq)]
74pub enum TokenId {
75 Token(Option<Keyword>),
83 Operator(Operator),
85 IoNumber,
87 IoLocation,
89 EndOfInput,
91}
92
93impl TokenId {
94 pub fn is_clause_delimiter(self) -> bool {
101 use TokenId::*;
102 match self {
103 Token(Some(keyword)) => keyword.is_clause_delimiter(),
104 Token(None) => false,
105 Operator(operator) => operator.is_clause_delimiter(),
106 IoNumber => false,
107 IoLocation => false,
108 EndOfInput => true,
109 }
110 }
111}
112
113#[derive(Debug)]
115pub struct Token {
116 pub word: Word,
122 pub id: TokenId,
124 pub index: usize,
126}
127
128impl fmt::Display for Token {
129 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
130 write!(f, "{}", self.word)
131 }
132}
133
134#[derive(Clone, Debug)]
136enum InputState {
137 Alive,
138 EndOfInput(Location),
139 Error(Error),
140}
141
142#[derive(Clone, Debug, Eq, PartialEq)]
144struct SourceCharEx {
145 value: SourceChar,
146 is_line_continuation: bool,
147}
148
149fn ex<I: IntoIterator<Item = SourceChar>>(i: I) -> impl Iterator<Item = SourceCharEx> {
150 i.into_iter().map(|sc| SourceCharEx {
151 value: sc,
152 is_line_continuation: false,
153 })
154}
155
156struct LexerCore<'a> {
158 input: Box<dyn InputObject + 'a>,
163 state: InputState,
164 raw_code: Rc<Code>,
165 source: Vec<SourceCharEx>,
166 index: usize,
167 mode: yash_env::parser::Mode,
168}
169
170impl<'a> LexerCore<'a> {
171 #[must_use]
173 fn new(
174 input: Box<dyn InputObject + 'a>,
175 start_line_number: NonZeroU64,
176 source: Rc<Source>,
177 ) -> LexerCore<'a> {
178 LexerCore {
179 input,
180 raw_code: Rc::new(Code {
181 value: RefCell::new(String::new()),
182 start_line_number,
183 source,
184 }),
185 state: InputState::Alive,
186 source: Vec::new(),
187 index: 0,
188 mode: yash_env::parser::Mode::default(),
189 }
190 }
191
192 #[must_use]
194 fn next_index(&self) -> usize {
195 let Some(last) = self.source.last() else {
196 return 0;
197 };
198
199 let mut location = &last.value.location;
200 while let Source::Alias { original, .. } = &*location.code.source {
201 location = original;
202 }
203 location.range.end
204 }
205
206 async fn peek_char(&mut self) -> Result<PeekChar<'_>> {
208 loop {
209 if self.index < self.source.len() {
212 return Ok(PeekChar::Char(&self.source[self.index].value));
213 }
214
215 match self.state {
216 InputState::Alive => (),
217 InputState::EndOfInput(ref location) => return Ok(PeekChar::EndOfInput(location)),
218 InputState::Error(ref error) => return Err(error.clone()),
219 }
220
221 let index = self.next_index();
223 match self.input.next_line(&self.input_context()).await {
224 Ok(line) => {
225 if line.is_empty() {
226 self.state = InputState::EndOfInput(Location {
228 code: Rc::clone(&self.raw_code),
229 range: index..index,
230 });
231 } else {
232 self.raw_code.value.borrow_mut().push_str(&line);
234 self.source
235 .extend(ex(source_chars(&line, &self.raw_code, index)));
236 }
237 }
238 Err(io_error) => {
239 self.state = InputState::Error(Error {
240 cause: io_error.into(),
241 location: Location {
242 code: Rc::clone(&self.raw_code),
243 range: index..index,
244 },
245 });
246 }
247 }
248 }
249 }
250
251 fn input_context(&self) -> Context {
253 let mut context = Context::default();
254 context.set_is_first_line(self.raw_code.value.borrow().is_empty());
255 context
256 }
257
258 fn consume_char(&mut self) {
264 assert!(
265 self.index < self.source.len(),
266 "A character must have been peeked before being consumed: index={}",
267 self.index
268 );
269 self.index += 1;
270 }
271
272 #[must_use]
274 fn peek_char_at(&self, index: usize) -> &SourceChar {
275 assert!(
276 index <= self.index,
277 "The index {} must not be larger than the current index {}",
278 index,
279 self.index
280 );
281 &self.source[index].value
282 }
283
284 #[must_use]
286 fn index(&self) -> usize {
287 self.index
288 }
289
290 fn rewind(&mut self, index: usize) {
292 assert!(
293 index <= self.index,
294 "The new index {} must not be larger than the current index {}",
295 index,
296 self.index
297 );
298 self.index = index;
299 }
300
301 #[must_use]
304 fn pending(&self) -> bool {
305 self.index < self.source.len()
306 }
307
308 fn flush(&mut self) {
310 let start_line_number = self.raw_code.line_number(usize::MAX);
311 self.raw_code = Rc::new(Code {
312 value: RefCell::new(String::new()),
313 start_line_number,
314 source: self.raw_code.source.clone(),
315 });
316 self.source.clear();
317 self.index = 0;
318 }
319
320 fn reset(&mut self) {
323 self.state = InputState::Alive;
324 self.flush();
325 }
326
327 fn source_string(&self, range: Range<usize>) -> String {
329 self.source[range].iter().map(|c| c.value.value).collect()
330 }
331
332 #[must_use]
334 fn location_range(&self, range: Range<usize>) -> Location {
335 if range.start == self.source.len()
336 && let InputState::EndOfInput(ref location) = self.state
337 {
338 return location.clone();
339 }
340 let start = &self.peek_char_at(range.start).location;
341 let code = start.code.clone();
342 let end = range
343 .map(|index| &self.peek_char_at(index).location)
344 .take_while(|location| location.code == code)
345 .last()
346 .map(|location| location.range.end)
347 .unwrap_or(start.range.start);
348 let range = start.range.start..end;
349 Location { code, range }
350 }
351
352 fn mark_line_continuation(&mut self, range: Range<usize>) {
358 assert!(
359 range.end <= self.index,
360 "characters must have been read (range = {:?}, current index = {})",
361 range,
362 self.index
363 );
364 for sc in &mut self.source[range] {
365 sc.is_line_continuation = true;
366 }
367 }
368
369 fn substitute_alias(&mut self, begin: usize, alias: &Rc<Alias>) {
375 let end = self.index;
376 assert!(
377 begin < end,
378 "begin index {begin} should be less than end index {end}"
379 );
380
381 let source = Rc::new(Source::Alias {
382 original: self.location_range(begin..end),
383 alias: alias.clone(),
384 });
385 let code = Rc::new(Code {
386 value: RefCell::new(alias.replacement.clone()),
387 start_line_number: NonZeroU64::new(1).unwrap(),
388 source,
389 });
390 let repl = ex(source_chars(&alias.replacement, &code, 0));
391
392 self.source.splice(begin..end, repl);
393 self.index = begin;
394 }
395
396 fn is_after_blank_ending_alias(&self, index: usize) -> bool {
403 fn ends_with_blank(s: &str) -> bool {
404 s.chars().next_back().is_some_and(is_blank)
405 }
406 fn is_same_alias(alias: &Alias, sc: Option<&SourceCharEx>) -> bool {
407 sc.is_some_and(|sc| sc.value.location.code.source.is_alias_for(&alias.name))
408 }
409
410 for index in (0..index).rev() {
411 let sc = &self.source[index];
412
413 if !sc.is_line_continuation && !is_blank(sc.value.value) {
414 return false;
415 }
416
417 if let Source::Alias { ref alias, .. } = *sc.value.location.code.source
418 && ends_with_blank(&alias.replacement)
419 && !is_same_alias(alias, self.source.get(index + 1))
420 {
421 return true;
422 }
423 }
424
425 false
426 }
427}
428
429impl fmt::Debug for LexerCore<'_> {
430 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
431 f.debug_struct("LexerCore")
432 .field("state", &self.state)
433 .field("source", &self.source)
434 .field("index", &self.index)
435 .finish_non_exhaustive()
436 }
437}
438
439#[deprecated(since = "0.17.0", note = "use `yash_env::parser::Config` instead")]
450#[derive(Debug)]
451#[must_use = "you must call `input` to create a lexer"]
452#[non_exhaustive]
453pub struct Config {
454 pub start_line_number: NonZeroU64,
463
464 pub source: Option<Rc<Source>>,
477}
478
479#[allow(deprecated, reason = "for backward compatible API")]
480impl Config {
481 #[deprecated(since = "0.17.0", note = "use `yash_env::parser::Config` instead")]
487 pub fn new() -> Self {
488 Config {
489 start_line_number: NonZeroU64::MIN,
490 source: None,
491 }
492 }
493
494 pub fn input<'a>(self, input: Box<dyn InputObject + 'a>) -> Lexer<'a> {
496 let mut config = yash_env::parser::Config::with_input(input);
497 config.start_line_number = self.start_line_number;
498 config.source = self.source;
499 config.into()
500 }
501}
502
503#[allow(deprecated, reason = "for backward compatible API")]
504impl Default for Config {
505 fn default() -> Self {
506 Self::new()
507 }
508}
509
510#[derive(Debug)]
543#[must_use]
544pub struct Lexer<'a> {
545 core: LexerCore<'a>,
549 line_continuation_enabled: bool,
550}
551
552impl<'a> From<yash_env::parser::Config<'a>> for Lexer<'a> {
555 fn from(config: yash_env::parser::Config<'a>) -> Self {
556 let input = config.input;
557 let start_line_number = config.start_line_number;
558 let source = config.source.unwrap_or_else(|| Rc::new(Source::Unknown));
559 let mut core = LexerCore::new(input, start_line_number, source);
560 core.mode = config.mode;
561 Lexer {
562 core,
563 line_continuation_enabled: true,
564 }
565 }
566}
567
568impl<'a> Lexer<'a> {
569 #[allow(deprecated, reason = "for backward compatible API")]
579 #[deprecated(since = "0.17.0", note = "use `yash_env::parser::Config` instead")]
580 #[inline(always)]
581 pub fn config() -> Config {
582 Config::new()
583 }
584
585 pub fn new(input: Box<dyn InputObject + 'a>) -> Lexer<'a> {
596 yash_env::parser::Config::with_input(input).into()
597 }
598
599 pub fn with_code(code: &'a str) -> Lexer<'a> {
609 Self::new(Box::new(Memory::new(code)))
610 }
611
612 pub fn from_memory<S: Into<Rc<Source>>>(code: &'a str, source: S) -> Lexer<'a> {
623 fn inner(code: &str, source: Rc<Source>) -> Lexer<'_> {
624 let mut config = yash_env::parser::Config::with_input(Box::new(Memory::new(code)));
625 config.source = Some(source);
626 config.into()
627 }
628 inner(code, source.into())
629 }
630
631 #[must_use]
638 pub fn mode(&self) -> yash_env::parser::Mode {
639 self.core.mode
640 }
641
642 pub fn set_mode(&mut self, mode: yash_env::parser::Mode) {
648 self.core.mode = mode;
649 }
650
651 pub fn disable_line_continuation<'b>(&'b mut self) -> PlainLexer<'b, 'a> {
662 assert!(
663 self.line_continuation_enabled,
664 "line continuation already disabled"
665 );
666 self.line_continuation_enabled = false;
667 PlainLexer { lexer: self }
668 }
669
670 pub fn enable_line_continuation<'b>(_: PlainLexer<'a, 'b>) {}
677
678 async fn line_continuation(&mut self) -> Result<bool> {
690 if !self.line_continuation_enabled {
691 return Ok(false);
692 }
693
694 let index = self.core.index();
695 match self.core.peek_char().await? {
696 PeekChar::Char(c) if c.value == '\\' => self.core.consume_char(),
697 _ => return Ok(false),
698 }
699
700 match self.core.peek_char().await? {
701 PeekChar::Char(c) if c.value == '\n' => self.core.consume_char(),
702 _ => {
703 self.core.rewind(index);
704 return Ok(false);
705 }
706 }
707
708 self.core.mark_line_continuation(index..index + 2);
709
710 Ok(true)
711 }
712
713 pub async fn peek_char(&mut self) -> Result<Option<char>> {
726 while self.line_continuation().await? {}
727
728 match self.core.peek_char().await? {
729 PeekChar::Char(source_char) => Ok(Some(source_char.value)),
730 PeekChar::EndOfInput(_) => Ok(None),
731 }
732 }
733
734 pub async fn location(&mut self) -> Result<&Location> {
742 self.core.peek_char().await.map(|p| p.location())
743 }
744
745 pub fn consume_char(&mut self) {
751 self.core.consume_char()
752 }
753
754 #[must_use]
768 pub fn index(&self) -> usize {
769 self.core.index()
770 }
771
772 pub fn rewind(&mut self, index: usize) {
791 self.core.rewind(index)
792 }
793
794 #[must_use]
797 pub fn pending(&self) -> bool {
798 self.core.pending()
799 }
800
801 pub fn flush(&mut self) {
810 self.core.flush()
811 }
812
813 pub fn reset(&mut self) {
820 self.core.reset()
821 }
822
823 pub async fn consume_char_if<F>(&mut self, mut f: F) -> Result<Option<&SourceChar>>
829 where
830 F: FnMut(char) -> bool,
831 {
832 self.consume_char_if_dyn(&mut f).await
833 }
834
835 pub(crate) async fn consume_char_if_dyn(
837 &mut self,
838 f: &mut dyn FnMut(char) -> bool,
839 ) -> Result<Option<&SourceChar>> {
840 match self.peek_char().await? {
841 Some(c) if f(c) => {
842 let index = self.index();
843 self.consume_char();
844 Ok(Some(self.core.peek_char_at(index)))
845 }
846 _ => Ok(None),
847 }
848 }
849
850 #[inline]
861 pub fn source_string(&self, range: Range<usize>) -> String {
862 self.core.source_string(range)
863 }
864
865 #[must_use]
882 pub fn location_range(&self, range: Range<usize>) -> Location {
883 self.core.location_range(range)
884 }
885
886 pub fn substitute_alias(&mut self, begin: usize, alias: &Rc<Alias>) {
901 self.core.substitute_alias(begin, alias)
902 }
903
904 pub fn is_after_blank_ending_alias(&self, index: usize) -> bool {
911 self.core.is_after_blank_ending_alias(index)
912 }
913
914 pub async fn inner_program(&mut self) -> Result<String> {
921 let begin = self.index();
922
923 let mut parser = super::super::Parser::new(self);
924 parser.maybe_compound_list().await?;
925
926 let end = parser.peek_token().await?.index;
927 self.rewind(end);
928
929 Ok(self.core.source_string(begin..end))
930 }
931
932 pub fn inner_program_boxed(&mut self) -> Pin<Box<dyn Future<Output = Result<String>> + '_>> {
934 Box::pin(self.inner_program())
935 }
936}
937
938#[derive(Debug)]
946#[must_use = "You must retain the PlainLexer to keep line continuation disabled"]
947pub struct PlainLexer<'a, 'b> {
948 lexer: &'a mut Lexer<'b>,
949}
950
951impl<'b> Deref for PlainLexer<'_, 'b> {
952 type Target = Lexer<'b>;
953 fn deref(&self) -> &Lexer<'b> {
954 self.lexer
955 }
956}
957
958impl<'b> DerefMut for PlainLexer<'_, 'b> {
959 fn deref_mut(&mut self) -> &mut Lexer<'b> {
960 self.lexer
961 }
962}
963
964impl Drop for PlainLexer<'_, '_> {
965 fn drop(&mut self) {
966 self.lexer.line_continuation_enabled = true;
967 }
968}
969
970#[derive(Clone, Copy, Debug, Eq, PartialEq)]
979pub enum WordContext {
980 Text,
982 Word,
984}
985
986#[derive(Debug)]
989pub struct WordLexer<'a, 'b> {
990 pub lexer: &'a mut Lexer<'b>,
991 pub context: WordContext,
992}
993
994impl<'b> Deref for WordLexer<'_, 'b> {
995 type Target = Lexer<'b>;
996 fn deref(&self) -> &Lexer<'b> {
997 self.lexer
998 }
999}
1000
1001impl<'b> DerefMut for WordLexer<'_, 'b> {
1002 fn deref_mut(&mut self) -> &mut Lexer<'b> {
1003 self.lexer
1004 }
1005}
1006
1007#[cfg(test)]
1008mod tests {
1009 use super::*;
1010 use crate::input::Input;
1011 use crate::parser::error::ErrorCause;
1012 use crate::parser::error::SyntaxError;
1013 use assert_matches::assert_matches;
1014 use futures_util::FutureExt as _;
1015
1016 #[test]
1017 fn lexer_mode_defaults_to_permissive() {
1018 let lexer = Lexer::with_code("");
1019 assert_eq!(lexer.mode(), yash_env::parser::Mode::default());
1020 }
1021
1022 #[test]
1023 fn lexer_mode_round_trips() {
1024 let mut lexer = Lexer::with_code("");
1025 let mut mode = yash_env::parser::Mode::default();
1026 mode.portable = true;
1027 lexer.set_mode(mode);
1028 assert_eq!(lexer.mode(), mode);
1029 }
1030
1031 #[test]
1032 fn lexer_from_config_carries_mode() {
1033 let mut config = yash_env::parser::Config::with_input(Box::new(Memory::new("")));
1034 config.mode.portable = true;
1035 let lexer = Lexer::from(config);
1036 assert!(lexer.mode().portable);
1037 }
1038
1039 #[test]
1040 fn lexer_core_peek_char_empty_source() {
1041 let input = Memory::new("");
1042 let line = NonZeroU64::new(32).unwrap();
1043 let mut lexer = LexerCore::new(Box::new(input), line, Rc::new(Source::Unknown));
1044 let result = lexer.peek_char().now_or_never().unwrap();
1045 assert_matches!(result, Ok(PeekChar::EndOfInput(location)) => {
1046 assert_eq!(*location.code.value.borrow(), "");
1047 assert_eq!(location.code.start_line_number, line);
1048 assert_eq!(*location.code.source, Source::Unknown);
1049 assert_eq!(location.range, 0..0);
1050 });
1051 }
1052
1053 #[test]
1054 fn lexer_core_peek_char_io_error() {
1055 #[derive(Debug)]
1056 struct Failing;
1057 impl fmt::Display for Failing {
1058 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1059 write!(f, "Failing")
1060 }
1061 }
1062 impl std::error::Error for Failing {}
1063 impl Input for Failing {
1064 async fn next_line(&mut self, _: &Context) -> crate::input::Result {
1065 Err(std::io::Error::other(Failing))
1066 }
1067 }
1068 let line = NonZeroU64::new(42).unwrap();
1069 let mut lexer = LexerCore::new(Box::new(Failing), line, Rc::new(Source::Unknown));
1070
1071 let e = lexer.peek_char().now_or_never().unwrap().unwrap_err();
1072 assert_matches!(e.cause, ErrorCause::Io(io_error) => {
1073 assert_eq!(io_error.kind(), std::io::ErrorKind::Other);
1074 });
1075 assert_eq!(*e.location.code.value.borrow(), "");
1076 assert_eq!(e.location.code.start_line_number, line);
1077 assert_eq!(*e.location.code.source, Source::Unknown);
1078 assert_eq!(e.location.range, 0..0);
1079 }
1080
1081 #[test]
1082 fn lexer_core_peek_char_context_is_first_line() {
1083 struct InputMock {
1085 first: bool,
1086 }
1087 impl Input for InputMock {
1088 async fn next_line(&mut self, context: &Context) -> crate::input::Result {
1089 assert_eq!(context.is_first_line(), self.first);
1090 self.first = false;
1091 Ok("\n".to_owned())
1092 }
1093 }
1094
1095 let input = InputMock { first: true };
1096 let line = NonZeroU64::new(42).unwrap();
1097 let mut lexer = LexerCore::new(Box::new(input), line, Rc::new(Source::Unknown));
1098
1099 let peek = lexer.peek_char().now_or_never().unwrap();
1100 assert_matches!(peek, Ok(PeekChar::Char(_)));
1101 lexer.consume_char();
1102
1103 let peek = lexer.peek_char().now_or_never().unwrap();
1104 assert_matches!(peek, Ok(PeekChar::Char(_)));
1105 lexer.consume_char();
1106 }
1107
1108 #[test]
1109 fn lexer_core_consume_char_success() {
1110 let input = Memory::new("a\nb");
1111 let line = NonZeroU64::new(1).unwrap();
1112 let mut lexer = LexerCore::new(Box::new(input), line, Rc::new(Source::Unknown));
1113
1114 let result = lexer.peek_char().now_or_never().unwrap();
1115 assert_matches!(result, Ok(PeekChar::Char(c)) => {
1116 assert_eq!(c.value, 'a');
1117 assert_eq!(*c.location.code.value.borrow(), "a\n");
1118 assert_eq!(c.location.code.start_line_number, line);
1119 assert_eq!(*c.location.code.source, Source::Unknown);
1120 assert_eq!(c.location.range, 0..1);
1121 });
1122 assert_matches!(result, Ok(PeekChar::Char(c)) => {
1123 assert_eq!(c.value, 'a');
1124 assert_eq!(*c.location.code.value.borrow(), "a\n");
1125 assert_eq!(c.location.code.start_line_number, line);
1126 assert_eq!(*c.location.code.source, Source::Unknown);
1127 assert_eq!(c.location.range, 0..1);
1128 });
1129 lexer.consume_char();
1130
1131 let result = lexer.peek_char().now_or_never().unwrap();
1132 assert_matches!(result, Ok(PeekChar::Char(c)) => {
1133 assert_eq!(c.value, '\n');
1134 assert_eq!(*c.location.code.value.borrow(), "a\n");
1135 assert_eq!(c.location.code.start_line_number, line);
1136 assert_eq!(*c.location.code.source, Source::Unknown);
1137 assert_eq!(c.location.range, 1..2);
1138 });
1139 lexer.consume_char();
1140
1141 let result = lexer.peek_char().now_or_never().unwrap();
1142 assert_matches!(result, Ok(PeekChar::Char(c)) => {
1143 assert_eq!(c.value, 'b');
1144 assert_eq!(*c.location.code.value.borrow(), "a\nb");
1145 assert_eq!(c.location.code.start_line_number.get(), 1);
1146 assert_eq!(*c.location.code.source, Source::Unknown);
1147 assert_eq!(c.location.range, 2..3);
1148 });
1149 lexer.consume_char();
1150
1151 let result = lexer.peek_char().now_or_never().unwrap();
1152 assert_matches!(result, Ok(PeekChar::EndOfInput(location)) => {
1153 assert_eq!(*location.code.value.borrow(), "a\nb");
1154 assert_eq!(location.code.start_line_number.get(), 1);
1155 assert_eq!(*location.code.source, Source::Unknown);
1156 assert_eq!(location.range, 3..3);
1157 });
1158 }
1159
1160 #[test]
1161 #[should_panic(expected = "A character must have been peeked before being consumed: index=0")]
1162 fn lexer_core_consume_char_panic() {
1163 let input = Memory::new("a");
1164 let line = NonZeroU64::new(1).unwrap();
1165 let mut lexer = LexerCore::new(Box::new(input), line, Rc::new(Source::Unknown));
1166 lexer.consume_char();
1167 }
1168
1169 #[test]
1170 fn lexer_core_peek_char_at() {
1171 let input = Memory::new("a\nb");
1172 let line = NonZeroU64::new(1).unwrap();
1173 let mut lexer = LexerCore::new(Box::new(input), line, Rc::new(Source::Unknown));
1174
1175 let c0 = assert_matches!(
1176 lexer.peek_char().now_or_never().unwrap(),
1177 Ok(PeekChar::Char(c)) => c.clone()
1178 );
1179 lexer.consume_char();
1180
1181 let c1 = assert_matches!(
1182 lexer.peek_char().now_or_never().unwrap(),
1183 Ok(PeekChar::Char(c)) => c.clone()
1184 );
1185 lexer.consume_char();
1186
1187 let c2 = assert_matches!(
1188 lexer.peek_char().now_or_never().unwrap(),
1189 Ok(PeekChar::Char(c)) => c.clone()
1190 );
1191
1192 assert_eq!(lexer.peek_char_at(0), &c0);
1193 assert_eq!(lexer.peek_char_at(1), &c1);
1194 assert_eq!(lexer.peek_char_at(2), &c2);
1195 }
1196
1197 #[test]
1198 fn lexer_core_index() {
1199 let input = Memory::new("a\nb");
1200 let line = NonZeroU64::new(1).unwrap();
1201 let mut lexer = LexerCore::new(Box::new(input), line, Rc::new(Source::Unknown));
1202
1203 assert_eq!(lexer.index(), 0);
1204 lexer.peek_char().now_or_never().unwrap().unwrap();
1205 assert_eq!(lexer.index(), 0);
1206 lexer.consume_char();
1207
1208 assert_eq!(lexer.index(), 1);
1209 lexer.peek_char().now_or_never().unwrap().unwrap();
1210 lexer.consume_char();
1211
1212 assert_eq!(lexer.index(), 2);
1213 lexer.peek_char().now_or_never().unwrap().unwrap();
1214 lexer.consume_char();
1215
1216 assert_eq!(lexer.index(), 3);
1217 }
1218
1219 #[test]
1220 fn lexer_core_rewind_success() {
1221 let input = Memory::new("abc");
1222 let line = NonZeroU64::new(1).unwrap();
1223 let mut lexer = LexerCore::new(Box::new(input), line, Rc::new(Source::Unknown));
1224 lexer.rewind(0);
1225 assert_eq!(lexer.index(), 0);
1226
1227 let _ = lexer.peek_char().now_or_never().unwrap();
1228 lexer.consume_char();
1229 let _ = lexer.peek_char().now_or_never().unwrap();
1230 lexer.consume_char();
1231 lexer.rewind(0);
1232
1233 let result = lexer.peek_char().now_or_never().unwrap();
1234 assert_matches!(result, Ok(PeekChar::Char(c)) => {
1235 assert_eq!(c.value, 'a');
1236 assert_eq!(*c.location.code.value.borrow(), "abc");
1237 assert_eq!(c.location.code.start_line_number, line);
1238 assert_eq!(*c.location.code.source, Source::Unknown);
1239 assert_eq!(c.location.range, 0..1);
1240 });
1241 }
1242
1243 #[test]
1244 #[should_panic(expected = "The new index 1 must not be larger than the current index 0")]
1245 fn lexer_core_rewind_invalid_index() {
1246 let input = Memory::new("abc");
1247 let line = NonZeroU64::new(1).unwrap();
1248 let mut lexer = LexerCore::new(Box::new(input), line, Rc::new(Source::Unknown));
1249 lexer.rewind(1);
1250 }
1251
1252 #[test]
1253 fn lexer_core_source_string() {
1254 let input = Memory::new("ab\ncd");
1255 let line = NonZeroU64::new(1).unwrap();
1256 let mut lexer = LexerCore::new(Box::new(input), line, Rc::new(Source::Unknown));
1257 for _ in 0..4 {
1258 let _ = lexer.peek_char().now_or_never().unwrap();
1259 lexer.consume_char();
1260 }
1261
1262 let result = lexer.source_string(1..4);
1263 assert_eq!(result, "b\nc");
1264 }
1265
1266 #[test]
1267 #[should_panic(expected = "begin index 0 should be less than end index 0")]
1268 fn lexer_core_substitute_alias_with_invalid_index() {
1269 let input = Memory::new("a b");
1270 let line = NonZeroU64::new(1).unwrap();
1271 let mut lexer = LexerCore::new(Box::new(input), line, Rc::new(Source::Unknown));
1272 let alias = Rc::new(Alias {
1273 name: "a".to_string(),
1274 replacement: "".to_string(),
1275 global: false,
1276 origin: Location::dummy("dummy"),
1277 });
1278 lexer.substitute_alias(0, &alias);
1279 }
1280
1281 #[test]
1282 fn lexer_core_substitute_alias_single_line_replacement() {
1283 let input = Memory::new("a b");
1284 let line = NonZeroU64::new(1).unwrap();
1285 let mut lexer = LexerCore::new(Box::new(input), line, Rc::new(Source::Unknown));
1286 let alias = Rc::new(Alias {
1287 name: "a".to_string(),
1288 replacement: "lex".to_string(),
1289 global: false,
1290 origin: Location::dummy("dummy"),
1291 });
1292
1293 let _ = lexer.peek_char().now_or_never().unwrap();
1294 lexer.consume_char();
1295
1296 lexer.substitute_alias(0, &alias);
1297
1298 assert_matches!(lexer.peek_char().now_or_never().unwrap(), Ok(PeekChar::Char(c)) => {
1299 assert_eq!(c.value, 'l');
1300 assert_eq!(*c.location.code.value.borrow(), "lex");
1301 assert_eq!(c.location.code.start_line_number.get(), 1);
1302 assert_matches!(&*c.location.code.source,
1303 Source::Alias { original, alias: alias2 } => {
1304 assert_eq!(*original.code.value.borrow(), "a b");
1305 assert_eq!(original.code.start_line_number, line);
1306 assert_eq!(*original.code.source, Source::Unknown);
1307 assert_eq!(original.range, 0..1);
1308 assert_eq!(alias2, &alias);
1309 });
1310 assert_eq!(c.location.range, 0..1);
1311 });
1312 lexer.consume_char();
1313
1314 assert_matches!(lexer.peek_char().now_or_never().unwrap(), Ok(PeekChar::Char(c)) => {
1315 assert_eq!(c.value, 'e');
1316 assert_eq!(*c.location.code.value.borrow(), "lex");
1317 assert_eq!(c.location.code.start_line_number, line);
1318 assert_matches!(&*c.location.code.source,
1319 Source::Alias { original, alias: alias2 } => {
1320 assert_eq!(*original.code.value.borrow(), "a b");
1321 assert_eq!(original.code.start_line_number, line);
1322 assert_eq!(*original.code.source, Source::Unknown);
1323 assert_eq!(original.range, 0..1);
1324 assert_eq!(alias2, &alias);
1325 });
1326 assert_eq!(c.location.range, 1..2);
1327 });
1328 lexer.consume_char();
1329
1330 assert_matches!(lexer.peek_char().now_or_never().unwrap(), Ok(PeekChar::Char(c)) => {
1331 assert_eq!(c.value, 'x');
1332 assert_eq!(*c.location.code.value.borrow(), "lex");
1333 assert_eq!(c.location.code.start_line_number, line);
1334 assert_matches!(&*c.location.code.source,
1335 Source::Alias { original, alias: alias2 } => {
1336 assert_eq!(*original.code.value.borrow(), "a b");
1337 assert_eq!(original.code.start_line_number, line);
1338 assert_eq!(*original.code.source, Source::Unknown);
1339 assert_eq!(original.range, 0..1);
1340 assert_eq!(alias2, &alias);
1341 });
1342 assert_eq!(c.location.range, 2..3);
1343 });
1344 lexer.consume_char();
1345
1346 assert_matches!(lexer.peek_char().now_or_never().unwrap(), Ok(PeekChar::Char(c)) => {
1347 assert_eq!(c.value, ' ');
1348 assert_eq!(*c.location.code.value.borrow(), "a b");
1349 assert_eq!(c.location.code.start_line_number, line);
1350 assert_eq!(*c.location.code.source, Source::Unknown);
1351 assert_eq!(c.location.range, 1..2);
1352 });
1353 lexer.consume_char();
1354 }
1355
1356 #[test]
1357 fn lexer_core_substitute_alias_multi_line_replacement() {
1358 let input = Memory::new(" foo b");
1359 let line = NonZeroU64::new(1).unwrap();
1360 let mut lexer = LexerCore::new(Box::new(input), line, Rc::new(Source::Unknown));
1361 let alias = Rc::new(Alias {
1362 name: "foo".to_string(),
1363 replacement: "x\ny".to_string(),
1364 global: true,
1365 origin: Location::dummy("loc"),
1366 });
1367
1368 for _ in 0..4 {
1369 let _ = lexer.peek_char().now_or_never().unwrap();
1370 lexer.consume_char();
1371 }
1372
1373 lexer.substitute_alias(1, &alias);
1374
1375 assert_matches!(lexer.peek_char().now_or_never().unwrap(), Ok(PeekChar::Char(c)) => {
1376 assert_eq!(c.value, 'x');
1377 assert_eq!(*c.location.code.value.borrow(), "x\ny");
1378 assert_eq!(c.location.code.start_line_number, line);
1379 assert_matches!(&*c.location.code.source,
1380 Source::Alias { original, alias: alias2 } => {
1381 assert_eq!(*original.code.value.borrow(), " foo b");
1382 assert_eq!(original.code.start_line_number, line);
1383 assert_eq!(*original.code.source, Source::Unknown);
1384 assert_eq!(original.range, 1..4);
1385 assert_eq!(alias2, &alias);
1386 });
1387 assert_eq!(c.location.range, 0..1);
1388 });
1389 lexer.consume_char();
1390
1391 assert_matches!(lexer.peek_char().now_or_never().unwrap(), Ok(PeekChar::Char(c)) => {
1392 assert_eq!(c.value, '\n');
1393 assert_eq!(*c.location.code.value.borrow(), "x\ny");
1394 assert_eq!(c.location.code.start_line_number, line);
1395 assert_matches!(&*c.location.code.source,
1396 Source::Alias { original, alias: alias2 } => {
1397 assert_eq!(*original.code.value.borrow(), " foo b");
1398 assert_eq!(original.code.start_line_number, line);
1399 assert_eq!(*original.code.source, Source::Unknown);
1400 assert_eq!(original.range, 1..4);
1401 assert_eq!(alias2, &alias);
1402 });
1403 assert_eq!(c.location.range, 1..2);
1404 });
1405 lexer.consume_char();
1406
1407 assert_matches!(lexer.peek_char().now_or_never().unwrap(), Ok(PeekChar::Char(c)) => {
1408 assert_eq!(c.value, 'y');
1409 assert_eq!(*c.location.code.value.borrow(), "x\ny");
1410 assert_eq!(c.location.code.start_line_number, line);
1411 assert_matches!(&*c.location.code.source, Source::Alias { original, alias: alias2 } => {
1412 assert_eq!(*original.code.value.borrow(), " foo b");
1413 assert_eq!(original.code.start_line_number, line);
1414 assert_eq!(*original.code.source, Source::Unknown);
1415 assert_eq!(original.range, 1..4);
1416 assert_eq!(alias2, &alias);
1417 });
1418 assert_eq!(c.location.range, 2..3);
1419 });
1420 lexer.consume_char();
1421
1422 assert_matches!(lexer.peek_char().now_or_never().unwrap(), Ok(PeekChar::Char(c)) => {
1423 assert_eq!(c.value, ' ');
1424 assert_eq!(*c.location.code.value.borrow(), " foo b");
1425 assert_eq!(c.location.code.start_line_number, line);
1426 assert_eq!(*c.location.code.source, Source::Unknown);
1427 assert_eq!(c.location.range, 4..5);
1428 });
1429 lexer.consume_char();
1430 }
1431
1432 #[test]
1433 fn lexer_core_substitute_alias_empty_replacement() {
1434 let input = Memory::new("x ");
1435 let line = NonZeroU64::new(1).unwrap();
1436 let mut lexer = LexerCore::new(Box::new(input), line, Rc::new(Source::Unknown));
1437 let alias = Rc::new(Alias {
1438 name: "x".to_string(),
1439 replacement: "".to_string(),
1440 global: false,
1441 origin: Location::dummy("dummy"),
1442 });
1443
1444 let _ = lexer.peek_char().now_or_never().unwrap();
1445 lexer.consume_char();
1446
1447 lexer.substitute_alias(0, &alias);
1448
1449 assert_matches!(lexer.peek_char().now_or_never().unwrap(), Ok(PeekChar::Char(c)) => {
1450 assert_eq!(c.value, ' ');
1451 assert_eq!(*c.location.code.value.borrow(), "x ");
1452 assert_eq!(c.location.code.start_line_number, line);
1453 assert_eq!(*c.location.code.source, Source::Unknown);
1454 assert_eq!(c.location.range, 1..2);
1455 });
1456 }
1457
1458 #[test]
1459 fn lexer_core_peek_char_after_alias_substitution() {
1460 let input = Memory::new("a\nb");
1461 let line = NonZeroU64::new(1).unwrap();
1462 let mut lexer = LexerCore::new(Box::new(input), line, Rc::new(Source::Unknown));
1463
1464 lexer.peek_char().now_or_never().unwrap().unwrap();
1465 lexer.consume_char();
1466
1467 let alias = Rc::new(Alias {
1468 name: "a".to_string(),
1469 replacement: "".to_string(),
1470 global: false,
1471 origin: Location::dummy("dummy"),
1472 });
1473 lexer.substitute_alias(0, &alias);
1474
1475 let result = lexer.peek_char().now_or_never().unwrap();
1476 assert_matches!(result, Ok(PeekChar::Char(c)) => {
1477 assert_eq!(c.value, '\n');
1478 assert_eq!(*c.location.code.value.borrow(), "a\n");
1479 assert_eq!(c.location.code.start_line_number, line);
1480 assert_eq!(*c.location.code.source, Source::Unknown);
1481 assert_eq!(c.location.range, 1..2);
1482 });
1483 lexer.consume_char();
1484
1485 let result = lexer.peek_char().now_or_never().unwrap();
1486 assert_matches!(result, Ok(PeekChar::Char(c)) => {
1487 assert_eq!(c.value, 'b');
1488 assert_eq!(*c.location.code.value.borrow(), "a\nb");
1489 assert_eq!(c.location.code.start_line_number.get(), 1);
1490 assert_eq!(*c.location.code.source, Source::Unknown);
1491 assert_eq!(c.location.range, 2..3);
1492 });
1493 lexer.consume_char();
1494
1495 let result = lexer.peek_char().now_or_never().unwrap();
1496 assert_matches!(result, Ok(PeekChar::EndOfInput(location)) => {
1497 assert_eq!(*location.code.value.borrow(), "a\nb");
1498 assert_eq!(location.code.start_line_number.get(), 1);
1499 assert_eq!(*location.code.source, Source::Unknown);
1500 assert_eq!(location.range, 3..3);
1501 });
1502 }
1503
1504 #[test]
1505 fn lexer_core_is_after_blank_ending_alias_index_0() {
1506 let original = Location::dummy("original");
1507 let alias = Rc::new(Alias {
1508 name: "a".to_string(),
1509 replacement: " ".to_string(),
1510 global: false,
1511 origin: Location::dummy("origin"),
1512 });
1513 let source = Source::Alias { original, alias };
1514 let input = Memory::new("a");
1515 let line = NonZeroU64::new(1).unwrap();
1516 let lexer = LexerCore::new(Box::new(input), line, Rc::new(source));
1517 assert!(!lexer.is_after_blank_ending_alias(0));
1518 }
1519
1520 #[test]
1521 fn lexer_core_is_after_blank_ending_alias_not_blank_ending() {
1522 let input = Memory::new("a x");
1523 let line = NonZeroU64::new(1).unwrap();
1524 let mut lexer = LexerCore::new(Box::new(input), line, Rc::new(Source::Unknown));
1525 let alias = Rc::new(Alias {
1526 name: "a".to_string(),
1527 replacement: " b".to_string(),
1528 global: false,
1529 origin: Location::dummy("dummy"),
1530 });
1531
1532 lexer.peek_char().now_or_never().unwrap().unwrap();
1533 lexer.consume_char();
1534
1535 lexer.substitute_alias(0, &alias);
1536
1537 assert!(!lexer.is_after_blank_ending_alias(0));
1538 assert!(!lexer.is_after_blank_ending_alias(1));
1539 assert!(!lexer.is_after_blank_ending_alias(2));
1540 assert!(!lexer.is_after_blank_ending_alias(3));
1541 }
1542
1543 #[test]
1544 fn lexer_core_is_after_blank_ending_alias_blank_ending() {
1545 let input = Memory::new("a x");
1546 let line = NonZeroU64::new(1).unwrap();
1547 let mut lexer = LexerCore::new(Box::new(input), line, Rc::new(Source::Unknown));
1548 let alias = Rc::new(Alias {
1549 name: "a".to_string(),
1550 replacement: " b ".to_string(),
1551 global: false,
1552 origin: Location::dummy("dummy"),
1553 });
1554
1555 lexer.peek_char().now_or_never().unwrap().unwrap();
1556 lexer.consume_char();
1557
1558 lexer.substitute_alias(0, &alias);
1559
1560 assert!(!lexer.is_after_blank_ending_alias(0));
1561 assert!(!lexer.is_after_blank_ending_alias(1));
1562 assert!(!lexer.is_after_blank_ending_alias(2));
1563 assert!(lexer.is_after_blank_ending_alias(3));
1564 assert!(lexer.is_after_blank_ending_alias(4));
1565 }
1566
1567 #[test]
1568 fn lexer_core_is_after_blank_ending_alias_after_line_continuation() {
1569 let input = Memory::new("a\\\n x");
1570 let line = NonZeroU64::new(1).unwrap();
1571 let mut lexer = LexerCore::new(Box::new(input), line, Rc::new(Source::Unknown));
1572 let alias = Rc::new(Alias {
1573 name: "a".to_string(),
1574 replacement: " b ".to_string(),
1575 global: false,
1576 origin: Location::dummy("dummy"),
1577 });
1578
1579 lexer.peek_char().now_or_never().unwrap().unwrap();
1580 lexer.consume_char();
1581 lexer.substitute_alias(0, &alias);
1582
1583 while let Ok(PeekChar::Char(_)) = lexer.peek_char().now_or_never().unwrap() {
1584 lexer.consume_char();
1585 }
1586 lexer.mark_line_continuation(3..5);
1587
1588 assert!(!lexer.is_after_blank_ending_alias(0));
1589 assert!(!lexer.is_after_blank_ending_alias(1));
1590 assert!(!lexer.is_after_blank_ending_alias(2));
1591 assert!(lexer.is_after_blank_ending_alias(5));
1592 assert!(lexer.is_after_blank_ending_alias(6));
1593 }
1594
1595 #[test]
1596 fn lexer_with_empty_source() {
1597 let mut lexer = Lexer::with_code("");
1598 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(None));
1599 }
1600
1601 #[test]
1602 fn lexer_peek_char_with_line_continuation_enabled_stopping_on_non_backslash() {
1603 let mut lexer = Lexer::with_code("\\\n\n\\");
1604 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('\n')));
1605 assert_eq!(lexer.index(), 2);
1606 }
1607
1608 #[test]
1609 fn lexer_peek_char_with_line_continuation_enabled_stopping_on_non_newline() {
1610 let mut lexer = Lexer::with_code("\\\n\\\n\\\n\\\\");
1611 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('\\')));
1612 assert_eq!(lexer.index(), 6);
1613 }
1614
1615 #[test]
1616 fn lexer_peek_char_with_line_continuation_disabled() {
1617 let mut lexer = Lexer::with_code("\\\n\\\n\\\\");
1618 let mut lexer = lexer.disable_line_continuation();
1619 assert_eq!(lexer.peek_char().now_or_never().unwrap(), Ok(Some('\\')));
1620 assert_eq!(lexer.index(), 0);
1621 }
1622
1623 #[test]
1624 fn lexer_flush() {
1625 let mut lexer = Lexer::with_code(" \n\n\t\n");
1626 let location_1 = lexer.location().now_or_never().unwrap().unwrap().clone();
1627 assert_eq!(*location_1.code.value.borrow(), " \n");
1628
1629 lexer.consume_char();
1630 lexer.peek_char().now_or_never().unwrap().unwrap();
1631 lexer.consume_char();
1632 lexer.peek_char().now_or_never().unwrap().unwrap();
1633 lexer.consume_char();
1634 lexer.flush();
1635 lexer.peek_char().now_or_never().unwrap().unwrap();
1636 lexer.consume_char();
1637
1638 let location_2 = lexer.location().now_or_never().unwrap().unwrap().clone();
1639
1640 assert_eq!(*location_1.code.value.borrow(), " \n\n");
1641 assert_eq!(location_1.code.start_line_number.get(), 1);
1642 assert_eq!(*location_1.code.source, Source::Unknown);
1643 assert_eq!(location_1.range, 0..1);
1644 assert_eq!(*location_2.code.value.borrow(), "\t\n");
1645 assert_eq!(location_2.code.start_line_number.get(), 3);
1646 assert_eq!(*location_2.code.source, Source::Unknown);
1647 assert_eq!(location_2.range, 1..2);
1648 }
1649
1650 #[test]
1651 fn lexer_consume_char_if() {
1652 let mut lexer = Lexer::with_code("word\n");
1653
1654 let mut called = 0;
1655 let c = lexer
1656 .consume_char_if(|c| {
1657 assert_eq!(c, 'w');
1658 called += 1;
1659 true
1660 })
1661 .now_or_never()
1662 .unwrap()
1663 .unwrap()
1664 .unwrap();
1665 assert_eq!(called, 1);
1666 assert_eq!(c.value, 'w');
1667 assert_eq!(*c.location.code.value.borrow(), "word\n");
1668 assert_eq!(c.location.code.start_line_number.get(), 1);
1669 assert_eq!(*c.location.code.source, Source::Unknown);
1670 assert_eq!(c.location.range, 0..1);
1671
1672 let mut called = 0;
1673 let r = lexer
1674 .consume_char_if(|c| {
1675 assert_eq!(c, 'o');
1676 called += 1;
1677 false
1678 })
1679 .now_or_never()
1680 .unwrap();
1681 assert_eq!(called, 1);
1682 assert_eq!(r, Ok(None));
1683
1684 let mut called = 0;
1685 let r = lexer
1686 .consume_char_if(|c| {
1687 assert_eq!(c, 'o');
1688 called += 1;
1689 false
1690 })
1691 .now_or_never()
1692 .unwrap();
1693 assert_eq!(called, 1);
1694 assert_eq!(r, Ok(None));
1695
1696 let mut called = 0;
1697 let c = lexer
1698 .consume_char_if(|c| {
1699 assert_eq!(c, 'o');
1700 called += 1;
1701 true
1702 })
1703 .now_or_never()
1704 .unwrap()
1705 .unwrap()
1706 .unwrap();
1707 assert_eq!(called, 1);
1708 assert_eq!(c.value, 'o');
1709 assert_eq!(*c.location.code.value.borrow(), "word\n");
1710 assert_eq!(c.location.code.start_line_number.get(), 1);
1711 assert_eq!(*c.location.code.source, Source::Unknown);
1712 assert_eq!(c.location.range, 1..2);
1713
1714 lexer
1715 .consume_char_if(|c| {
1716 assert_eq!(c, 'r');
1717 true
1718 })
1719 .now_or_never()
1720 .unwrap()
1721 .unwrap()
1722 .unwrap();
1723 lexer
1724 .consume_char_if(|c| {
1725 assert_eq!(c, 'd');
1726 true
1727 })
1728 .now_or_never()
1729 .unwrap()
1730 .unwrap()
1731 .unwrap();
1732 lexer
1733 .consume_char_if(|c| {
1734 assert_eq!(c, '\n');
1735 true
1736 })
1737 .now_or_never()
1738 .unwrap()
1739 .unwrap()
1740 .unwrap();
1741
1742 let r = lexer
1744 .consume_char_if(|c| {
1745 unreachable!("unexpected call to the decider function: argument={}", c)
1746 })
1747 .now_or_never()
1748 .unwrap();
1749 assert_eq!(r, Ok(None));
1750 }
1751
1752 #[test]
1753 fn lexer_location_range_with_empty_range() {
1754 let mut lexer = Lexer::with_code("");
1755 lexer.peek_char().now_or_never().unwrap().unwrap();
1756 let location = lexer.location_range(0..0);
1757 assert_eq!(*location.code.value.borrow(), "");
1758 assert_eq!(location.code.start_line_number.get(), 1);
1759 assert_eq!(*location.code.source, Source::Unknown);
1760 assert_eq!(location.range, 0..0);
1761 }
1762
1763 #[test]
1764 fn lexer_location_range_with_nonempty_range() {
1765 let mut lexer = Lexer::from_memory("cat foo", Source::Stdin);
1766 for _ in 0..4 {
1767 lexer.peek_char().now_or_never().unwrap().unwrap();
1768 lexer.consume_char();
1769 }
1770 lexer.peek_char().now_or_never().unwrap().unwrap();
1771
1772 let location = lexer.location_range(1..4);
1773 assert_eq!(*location.code.value.borrow(), "cat foo");
1774 assert_eq!(location.code.start_line_number.get(), 1);
1775 assert_eq!(*location.code.source, Source::Stdin);
1776 assert_eq!(location.range, 1..4);
1777 }
1778
1779 #[test]
1780 fn lexer_location_range_with_range_starting_at_end() {
1781 let mut lexer = Lexer::from_memory("cat", Source::Stdin);
1782 for _ in 0..3 {
1783 lexer.peek_char().now_or_never().unwrap().unwrap();
1784 lexer.consume_char();
1785 }
1786 lexer.peek_char().now_or_never().unwrap().unwrap();
1787
1788 let location = lexer.location_range(3..3);
1789 assert_eq!(*location.code.value.borrow(), "cat");
1790 assert_eq!(location.code.start_line_number.get(), 1);
1791 assert_eq!(*location.code.source, Source::Stdin);
1792 assert_eq!(location.range, 3..3);
1793 }
1794
1795 #[test]
1796 #[should_panic]
1797 fn lexer_location_range_with_unconsumed_code() {
1798 let lexer = Lexer::with_code("echo ok");
1799 let _ = lexer.location_range(0..0);
1800 }
1801
1802 #[test]
1803 #[should_panic(expected = "The index 1 must not be larger than the current index 0")]
1804 fn lexer_location_range_with_range_out_of_bounds() {
1805 let lexer = Lexer::with_code("");
1806 let _ = lexer.location_range(1..2);
1807 }
1808
1809 #[test]
1810 fn lexer_location_range_with_alias_substitution() {
1811 let mut lexer = Lexer::with_code(" a;");
1812 let alias_def = Rc::new(Alias {
1813 name: "a".to_string(),
1814 replacement: "abc".to_string(),
1815 global: false,
1816 origin: Location::dummy("dummy"),
1817 });
1818 for _ in 0..2 {
1819 lexer.peek_char().now_or_never().unwrap().unwrap();
1820 lexer.consume_char();
1821 }
1822 lexer.substitute_alias(1, &alias_def);
1823 for _ in 1..5 {
1824 lexer.peek_char().now_or_never().unwrap().unwrap();
1825 lexer.consume_char();
1826 }
1827
1828 let location = lexer.location_range(2..5);
1829 assert_eq!(*location.code.value.borrow(), "abc");
1830 assert_eq!(location.code.start_line_number.get(), 1);
1831 assert_matches!(&*location.code.source, Source::Alias { original, alias } => {
1832 assert_eq!(*original.code.value.borrow(), " a;");
1833 assert_eq!(original.code.start_line_number.get(), 1);
1834 assert_eq!(*original.code.source, Source::Unknown);
1835 assert_eq!(original.range, 1..2);
1836 assert_eq!(alias, &alias_def);
1837 });
1838 assert_eq!(location.range, 1..3);
1839 }
1840
1841 #[test]
1842 fn lexer_inner_program_success() {
1843 let mut lexer = Lexer::with_code("x y )");
1844 let source = lexer.inner_program().now_or_never().unwrap().unwrap();
1845 assert_eq!(source, "x y ");
1846 }
1847
1848 #[test]
1849 fn lexer_inner_program_failure() {
1850 let mut lexer = Lexer::with_code("<< )");
1851 let e = lexer.inner_program().now_or_never().unwrap().unwrap_err();
1852 assert_eq!(
1853 e.cause,
1854 ErrorCause::Syntax(SyntaxError::MissingHereDocDelimiter)
1855 );
1856 assert_eq!(*e.location.code.value.borrow(), "<< )");
1857 assert_eq!(e.location.code.start_line_number.get(), 1);
1858 assert_eq!(*e.location.code.source, Source::Unknown);
1859 assert_eq!(e.location.range, 3..4);
1860 }
1861}