1use crate::location::{Location, Span, Spanned};
2
3pub type Tok<'a, 'f> = Spanned<'f, TokInfo<'a>>;
5
6#[derive(Clone, Debug, PartialEq)]
7pub enum TokInfo<'a> {
9 EOF,
10 Extern,
11 Unsafe,
12 And,
13 Arrow,
14 Assert,
15 Bar,
16 Bool,
17 CDots,
18 CloseBrace,
19 CloseBracket,
20 ClosePar,
21 CloseStaticPar,
22 Colon,
23 Coma,
24 Const,
25 Current,
26 Sharp,
27 Div,
28 DoubleColon,
29 Dot,
30 Equal,
31 Else,
32 Enum,
33 False,
34 Function,
35 Gt,
36 Gte,
37 Hat,
38 IConst(i64),
39 Ident(&'a str),
40 If,
41 Impl,
42 Int,
43 Let,
44 Lt,
45 Lte,
46 Merge,
47 Minus,
48 Mod,
49 Neq,
50 Node,
51 Nor,
52 Not,
53 OpenBrace,
54 OpenBracket,
55 OpenPar,
56 OpenStaticPar,
57 Operator,
58 Or,
59 Percent,
60 Plus,
61 Power,
62 Pre,
63 FBy,
64 RConst(f64),
65 Real,
66 Returns,
67 Semicolon,
68 Slash,
69 Star,
70 Step,
71 Struct,
72 Tel,
73 Then,
74 True,
75 Type,
76 Var,
77 When,
78 With,
79 Xor,
80 Model,
81 Package,
82 Needs,
83 Provides,
84 Uses,
85 Is,
86 Body,
87 End,
88 Include,
89 Str(&'a str),
90}
91
92#[derive(Copy, Clone, Eq, PartialEq)]
93enum Grammar {
94 Main,
95 Str,
96 InlineComment,
97 Comment(char),
98}
99
100#[cfg_attr(test, derive(PartialEq))]
101#[derive(Debug)]
102pub enum Error {
104 UnclosedStr,
105 UnclosedComment,
106}
107
108pub struct Lexer<'a, 'f> {
110 file: &'f str,
111 src: &'a str,
112}
113
114#[derive(PartialEq, Debug)]
115enum MatchResult<'a> {
116 NotYetMatched,
117 NoMatches,
118 Ambiguous(Vec<TokInfo<'a>>),
119 Match(LexMatch<'a>),
120 ManyMatches(Vec<TokInfo<'a>>),
121}
122
123#[derive(PartialEq, Debug)]
124enum LexMatch<'a> {
125 Tok(TokInfo<'a>),
126 Comment(&'a str),
127}
128
129impl<'a, 'f> Lexer<'a, 'f> {
130 pub fn new(file: &'f str, src: &'a str) -> Self {
137 Lexer { file, src }
138 }
139
140 pub fn lex(&mut self) -> Result<Vec<Tok>, Error> {
142 let total_len = self.src.len();
143 let mut res = Vec::with_capacity(total_len / 4);
144 let mut start = 0;
145 let mut end = 0;
146 let mut grammar = Grammar::Main;
147 let mut line = 1;
148 let mut col = 0;
149 while end < total_len {
150 let mut last_span = Span::default();
151 let mut last_match = MatchResult::NotYetMatched;
152 let mut curr_match = MatchResult::NotYetMatched;
153 let mut last_end = end;
154 while end <= total_len && curr_match != MatchResult::NoMatches {
155 let src_slice = &self.src[start..end];
156 match src_slice {
157 "" => {}
158 "--" => grammar = Grammar::InlineComment,
159 "/*" => grammar = Grammar::Comment('/'),
160 "(*" => grammar = Grammar::Comment(')'),
161 "\"" => grammar = Grammar::Str,
162 _ => {
163 last_span = self.span(line, col, start, last_end - start);
164 last_match = curr_match;
165 curr_match = match grammar {
166 Grammar::InlineComment => self.match_inline_comm(src_slice),
167 Grammar::Comment(delim) => self.match_comm(src_slice, delim),
168 Grammar::Str => self.match_str(src_slice),
169 Grammar::Main => self.match_tokens(src_slice),
170 };
171 if curr_match != MatchResult::NotYetMatched {
172 grammar = Grammar::Main;
173 }
174 }
175 }
176 if curr_match != MatchResult::NoMatches {
177 last_end = end;
178 end = self.next_char(end, 1);
179 }
180 }
181
182 let added_lines = self.src[last_span.start.pos as usize..last_span.end.pos as usize]
183 .chars()
184 .filter(|c| *c == '\n')
185 .count();
186 if added_lines != 0 {
187 line += added_lines;
188 col = 0;
189 }
190 let added_cols = self.src[last_span.start.pos as usize..last_span.end.pos as usize]
191 .lines()
192 .last()
193 .unwrap_or_default()
194 .len();
195 col += added_cols;
196
197 if end == total_len + 1 {
198 last_match = curr_match;
199 }
200
201 match last_match {
202 MatchResult::Match(LexMatch::Tok(m)) => {
203 res.push(Spanned {
204 span: last_span.clone(),
205 item: m,
206 });
207 end = last_end;
208 }
209 MatchResult::ManyMatches(matches) | MatchResult::Ambiguous(matches) => {
210 for m in matches {
211 res.push(Spanned {
212 span: last_span.clone(),
213 item: m,
214 });
215 }
216 end = last_end;
217 }
218 _ => {}
219 }
220 start = last_end;
221 }
222 if grammar == Grammar::Str {
223 Err(Error::UnclosedStr)
224 } else if let Grammar::Comment(_) = grammar {
225 Err(Error::UnclosedComment)
226 } else {
227 res.push(Spanned {
228 span: self.span(line, col, start, 0),
229 item: TokInfo::EOF,
230 });
231 Ok(res)
232 }
233 }
234
235 fn next_char(&self, from: usize, add: isize) -> usize {
236 Self::next_char_in(self.src, from, add)
237 }
238
239 fn next_char_in(src: &str, from: usize, add: isize) -> usize {
240 let mut new_pos = (from as isize) + add;
241 while !src.is_char_boundary(new_pos as usize) && (new_pos as usize) < src.len() {
242 new_pos += add.signum();
243 }
244 new_pos as usize
245 }
246
247 fn match_inline_comm(&self, src_slice: &'a str) -> MatchResult<'a> {
248 let len = src_slice.len();
249 let start = Self::next_char_in(src_slice, len, -1);
250 if &src_slice[start..] == "\n" {
251 MatchResult::Match(LexMatch::Comment(&src_slice[2..]))
252 } else {
253 MatchResult::NotYetMatched
254 }
255 }
256
257 fn match_comm(&self, src_slice: &'a str, delim: char) -> MatchResult<'a> {
258 let end = format!("*{}", delim);
259 let len = src_slice.len();
260 let start = Self::next_char_in(src_slice, len, -2);
261 if &src_slice[start..] == &end {
262 MatchResult::Match(LexMatch::Comment(&src_slice[2..start]))
263 } else {
264 MatchResult::NotYetMatched
265 }
266 }
267
268 fn match_str(&self, src_slice: &'a str) -> MatchResult<'a> {
269 let len = src_slice.len();
270 let start = Self::next_char_in(src_slice, len, -1);
271 if &src_slice[start..] == "\"" {
272 MatchResult::Match(LexMatch::Tok(TokInfo::Str(&src_slice[1..start])))
273 } else {
274 MatchResult::NotYetMatched
275 }
276 }
277
278 fn match_tokens(&self, src_slice: &'a str) -> MatchResult<'a> {
279 match src_slice {
280 " " | "\t" | "\n" | "\r" => MatchResult::NoMatches,
281 "," => MatchResult::Match(LexMatch::Tok(TokInfo::Coma)),
282 ";" => MatchResult::Match(LexMatch::Tok(TokInfo::Semicolon)),
283 "::" => MatchResult::Match(LexMatch::Tok(TokInfo::DoubleColon)),
284 ":" => MatchResult::Match(LexMatch::Tok(TokInfo::Colon)),
285 "->" => MatchResult::Match(LexMatch::Tok(TokInfo::Arrow)),
286 "=>" => MatchResult::Match(LexMatch::Tok(TokInfo::Impl)),
287 "<=" => MatchResult::Match(LexMatch::Tok(TokInfo::Lte)),
288 "<>" => MatchResult::Match(LexMatch::Tok(TokInfo::Neq)),
289 ">=" => MatchResult::Match(LexMatch::Tok(TokInfo::Gte)),
290 ".." => MatchResult::Match(LexMatch::Tok(TokInfo::CDots)),
291 "**" => MatchResult::Match(LexMatch::Tok(TokInfo::Power)),
292 "<<" => MatchResult::Match(LexMatch::Tok(TokInfo::OpenStaticPar)),
293 ">>" => MatchResult::Match(LexMatch::Tok(TokInfo::CloseStaticPar)),
294 "+" => MatchResult::Match(LexMatch::Tok(TokInfo::Plus)),
295 "^" => MatchResult::Match(LexMatch::Tok(TokInfo::Hat)),
296 "#" => MatchResult::Match(LexMatch::Tok(TokInfo::Sharp)),
297 "-" => MatchResult::Ambiguous(vec![TokInfo::Minus]),
298 "/" => MatchResult::Match(LexMatch::Tok(TokInfo::Slash)),
299 "%" => MatchResult::Match(LexMatch::Tok(TokInfo::Percent)),
300 "*" => MatchResult::Match(LexMatch::Tok(TokInfo::Star)),
301 "|" => MatchResult::Match(LexMatch::Tok(TokInfo::Bar)),
302 "=" => MatchResult::Match(LexMatch::Tok(TokInfo::Equal)),
303 "." => MatchResult::Match(LexMatch::Tok(TokInfo::Dot)),
304 "(" => MatchResult::Match(LexMatch::Tok(TokInfo::OpenPar)),
305 ")" => MatchResult::Match(LexMatch::Tok(TokInfo::ClosePar)),
306 "{" => MatchResult::Match(LexMatch::Tok(TokInfo::OpenBrace)),
307 "}" => MatchResult::Match(LexMatch::Tok(TokInfo::CloseBrace)),
308 "[" => MatchResult::Match(LexMatch::Tok(TokInfo::OpenBracket)),
309 "]" => MatchResult::Match(LexMatch::Tok(TokInfo::CloseBracket)),
310 "<" => MatchResult::Match(LexMatch::Tok(TokInfo::Lt)),
311 ">" => MatchResult::Match(LexMatch::Tok(TokInfo::Gt)),
312 "extern" => MatchResult::Match(LexMatch::Tok(TokInfo::Extern)),
313 "unsafe" => MatchResult::Match(LexMatch::Tok(TokInfo::Unsafe)),
314 "and" => MatchResult::Match(LexMatch::Tok(TokInfo::And)),
315 "assert" => MatchResult::Match(LexMatch::Tok(TokInfo::Assert)),
316 "bool" => MatchResult::Match(LexMatch::Tok(TokInfo::Bool)),
317 "const" => MatchResult::Match(LexMatch::Tok(TokInfo::Const)),
318 "current" => MatchResult::Match(LexMatch::Tok(TokInfo::Current)),
319 "div" => MatchResult::Match(LexMatch::Tok(TokInfo::Div)),
320 "else" => MatchResult::Match(LexMatch::Tok(TokInfo::Else)),
321 "enum" => MatchResult::Match(LexMatch::Tok(TokInfo::Enum)),
322 "function" => MatchResult::Match(LexMatch::Tok(TokInfo::Function)),
323 "false" => MatchResult::Match(LexMatch::Tok(TokInfo::False)),
324 "if" => MatchResult::Match(LexMatch::Tok(TokInfo::If)),
325 "int" => MatchResult::Match(LexMatch::Tok(TokInfo::Int)),
326 "let" => MatchResult::Match(LexMatch::Tok(TokInfo::Let)),
327 "mod" => MatchResult::Match(LexMatch::Tok(TokInfo::Mod)),
328 "node" => MatchResult::Match(LexMatch::Tok(TokInfo::Node)),
329 "not" => MatchResult::Match(LexMatch::Tok(TokInfo::Not)),
330 "operator" => MatchResult::Match(LexMatch::Tok(TokInfo::Operator)),
331 "or" => MatchResult::Match(LexMatch::Tok(TokInfo::Or)),
332 "nor" => MatchResult::Match(LexMatch::Tok(TokInfo::Nor)),
333 "fby" => MatchResult::Match(LexMatch::Tok(TokInfo::FBy)),
334 "pre" => MatchResult::Match(LexMatch::Tok(TokInfo::Pre)),
335 "real" => MatchResult::Match(LexMatch::Tok(TokInfo::Real)),
336 "returns" => MatchResult::Match(LexMatch::Tok(TokInfo::Returns)),
337 "step" => MatchResult::Match(LexMatch::Tok(TokInfo::Step)),
338 "struct" => MatchResult::Match(LexMatch::Tok(TokInfo::Struct)),
339 "tel" => MatchResult::Match(LexMatch::Tok(TokInfo::Tel)),
340 "type" => MatchResult::Match(LexMatch::Tok(TokInfo::Type)),
341 "then" => MatchResult::Match(LexMatch::Tok(TokInfo::Then)),
342 "true" => MatchResult::Match(LexMatch::Tok(TokInfo::True)),
343 "var" => MatchResult::Match(LexMatch::Tok(TokInfo::Var)),
344 "when" => MatchResult::Match(LexMatch::Tok(TokInfo::When)),
345 "with" => MatchResult::Match(LexMatch::Tok(TokInfo::With)),
346 "xor" => MatchResult::Match(LexMatch::Tok(TokInfo::Xor)),
347 "model" => MatchResult::Match(LexMatch::Tok(TokInfo::Model)),
348 "package" => MatchResult::Match(LexMatch::Tok(TokInfo::Package)),
349 "needs" => MatchResult::Match(LexMatch::Tok(TokInfo::Needs)),
350 "provides" => MatchResult::Match(LexMatch::Tok(TokInfo::Provides)),
351 "uses" => MatchResult::Match(LexMatch::Tok(TokInfo::Uses)),
352 "is" => MatchResult::Match(LexMatch::Tok(TokInfo::Is)),
353 "body" => MatchResult::Match(LexMatch::Tok(TokInfo::Body)),
354 "end" => MatchResult::Match(LexMatch::Tok(TokInfo::End)),
355 "include" => MatchResult::Match(LexMatch::Tok(TokInfo::Include)),
356 "merge" => MatchResult::Match(LexMatch::Tok(TokInfo::Merge)),
357 x if x.chars().all(char::is_numeric) => {
358 MatchResult::Match(LexMatch::Tok(TokInfo::IConst(x.parse::<i64>().unwrap())))
359 }
360 x if x.chars().all(|c| {
361 c.is_numeric() || c == '.' || c == 'e' || c == 'E' || c == '+' || c == '-'
362 }) && x.chars().any(char::is_numeric)
363 && (!(x.contains('+') || x.contains('-'))
364 || x.contains("e-")
365 || x.contains("e+")
366 || x.contains("E+")
367 || x.contains("E-")) =>
368 {
369 if x.ends_with('.') {
371 if x.ends_with("..") {
372 MatchResult::ManyMatches(vec![
373 TokInfo::IConst(x[..x.len() - 2].parse::<i64>().unwrap()),
374 TokInfo::CDots,
375 ])
376 } else {
377 MatchResult::Ambiguous(vec![TokInfo::RConst(x.parse::<f64>().unwrap())])
378 }
379 } else if x.contains("..") {
380 MatchResult::NoMatches
381 } else if x.starts_with('e') || x.starts_with('E') {
382 MatchResult::Match(LexMatch::Tok(TokInfo::Ident(x)))
383 } else if x.ends_with('e') || x.ends_with('E') {
384 MatchResult::NotYetMatched
385 } else if x.ends_with('-') {
386 let mut tokens = Vec::with_capacity(2);
387 match self.match_tokens(&x[..x.len() - 1]) {
388 MatchResult::Ambiguous(ref mut t) | MatchResult::ManyMatches(ref mut t) => {
389 tokens.append(t)
390 }
391 MatchResult::Match(LexMatch::Tok(m)) => tokens.push(m),
392 _ => {}
393 }
394 tokens.push(TokInfo::Minus);
395 MatchResult::Ambiguous(tokens)
396 } else if x.ends_with('+') {
397 let mut tokens = Vec::with_capacity(2);
398 match self.match_tokens(&x[..x.len() - 1]) {
399 MatchResult::Ambiguous(ref mut t) | MatchResult::ManyMatches(ref mut t) => {
400 tokens.append(t)
401 }
402 MatchResult::Match(LexMatch::Tok(m)) => tokens.push(m),
403 _ => {}
404 }
405 tokens.push(TokInfo::Plus);
406 MatchResult::Ambiguous(tokens)
407 } else if x.starts_with('-') {
408 let mut tokens = Vec::with_capacity(2);
409 tokens.push(TokInfo::Minus);
410 match self.match_tokens(&x[1..]) {
411 MatchResult::Ambiguous(ref mut t) | MatchResult::ManyMatches(ref mut t) => {
412 tokens.append(t)
413 }
414 MatchResult::Match(LexMatch::Tok(m)) => tokens.push(m),
415 _ => {}
416 }
417 MatchResult::ManyMatches(tokens)
418 } else if x.starts_with('+') {
419 let mut tokens = Vec::with_capacity(2);
420 tokens.push(TokInfo::Plus);
421 match self.match_tokens(&x[1..]) {
422 MatchResult::Ambiguous(ref mut t) | MatchResult::ManyMatches(ref mut t) => {
423 tokens.append(t)
424 }
425 MatchResult::Match(LexMatch::Tok(m)) => tokens.push(m),
426 _ => {}
427 }
428 MatchResult::ManyMatches(tokens)
429 } else {
430 MatchResult::Match(LexMatch::Tok(TokInfo::RConst(x.parse::<f64>().unwrap())))
431 }
432 }
433 x if x.chars().all(|c| c.is_alphanumeric() || c == '_') => {
434 MatchResult::Match(LexMatch::Tok(TokInfo::Ident(x)))
435 }
436 x if x.ends_with("->") => {
439 let mut tokens = Vec::with_capacity(2);
440 match self.match_tokens(&x[..x.len() - 2]) {
441 MatchResult::Ambiguous(ref mut t) | MatchResult::ManyMatches(ref mut t) => {
442 tokens.append(t)
443 }
444 MatchResult::Match(LexMatch::Tok(m)) => tokens.push(m),
445 _ => {}
446 }
447 tokens.push(TokInfo::Arrow);
448 MatchResult::ManyMatches(tokens)
449 }
450 _ => MatchResult::NoMatches,
451 }
452 }
453
454 fn span(&self, line: usize, col: usize, pos: usize, len: usize) -> Span {
455 Span {
456 start: Location {
457 line: line as u64,
458 col: col as u64,
459 pos: pos as u64,
460 },
461 end: Location {
462 line: line as u64, col: (col + len) as u64,
464 pos: (pos + len) as u64,
465 },
466 file: self.file,
467 }
468 }
469}
470
471#[cfg(test)]
472mod tests {
473 use super::*;
474
475 fn check_tok_info<'a, 'f>(actual: Vec<Tok<'a, 'f>>, expected: Vec<TokInfo<'a>>) {
476 dbg!(&actual);
477 assert_eq!(actual.len(), expected.len());
478
479 for (ref a, ref e) in actual.iter().zip(expected.iter()) {
480 assert_eq!(a.item, **e);
481 }
482 }
483
484 fn test_lexer<'a>(src: &'a str, expected: Vec<TokInfo<'a>>) {
485 let mut lex = Lexer::new("main.lus", src);
486 let toks = lex.lex().unwrap();
487 check_tok_info(toks, expected);
488 }
489
490 #[test]
491 fn test_empty() {
492 test_lexer("", vec![TokInfo::EOF])
493 }
494
495 #[test]
496 fn test_keyword() {
497 test_lexer("function", vec![TokInfo::Function, TokInfo::EOF])
498 }
499
500 #[test]
501 fn test_arrow() {
502 test_lexer(
503 "y=0->pre",
504 vec![
505 TokInfo::Ident("y"),
506 TokInfo::Equal,
507 TokInfo::IConst(0),
508 TokInfo::Arrow,
509 TokInfo::Pre,
510 TokInfo::EOF,
511 ],
512 )
513 }
514
515 #[test]
516 fn test_keywords() {
517 test_lexer(
518 "extern function",
519 vec![TokInfo::Extern, TokInfo::Function, TokInfo::EOF],
520 );
521 test_lexer(
522 "functional",
523 vec![TokInfo::Ident("functional"), TokInfo::EOF],
524 );
525 }
526
527 #[test]
528 fn test_spaces() {
529 test_lexer(
530 "extern\n \t\r\nfunction",
531 vec![TokInfo::Extern, TokInfo::Function, TokInfo::EOF],
532 );
533 test_lexer(
534 "\n \t\r\nextern function",
535 vec![TokInfo::Extern, TokInfo::Function, TokInfo::EOF],
536 );
537 test_lexer(
538 "extern function\n \t\r\n",
539 vec![TokInfo::Extern, TokInfo::Function, TokInfo::EOF],
540 );
541 }
542
543 #[test]
544 fn test_iconst() {
545 test_lexer(
546 "42 -12",
547 vec![
548 TokInfo::IConst(42),
549 TokInfo::Minus,
550 TokInfo::IConst(12),
551 TokInfo::EOF,
552 ],
553 )
554 }
555
556 #[test]
557 fn test_rconst() {
558 test_lexer("33.3", vec![TokInfo::RConst(33.3), TokInfo::EOF])
559 }
560
561 #[test]
562 fn test_str() {
563 test_lexer(
564 "include \"memoire.lus\"",
565 vec![TokInfo::Include, TokInfo::Str("memoire.lus"), TokInfo::EOF],
566 );
567 }
568
569 #[test]
570 fn test_comments() {
571 test_lexer(
572 "-- comment\nfunction\nfunction --comment",
573 vec![TokInfo::Function, TokInfo::Function, TokInfo::EOF],
574 );
575 test_lexer(
576 "include (* hello *) extern /* world */ function",
577 vec![
578 TokInfo::Include,
579 TokInfo::Extern,
580 TokInfo::Function,
581 TokInfo::EOF,
582 ],
583 )
584 }
585
586 #[test]
587 fn test_ops() {
588 test_lexer(
589 "12 + 3",
590 vec![
591 TokInfo::IConst(12),
592 TokInfo::Plus,
593 TokInfo::IConst(3),
594 TokInfo::EOF,
595 ],
596 );
597 test_lexer(
598 "42*7",
599 vec![
600 TokInfo::IConst(42),
601 TokInfo::Star,
602 TokInfo::IConst(7),
603 TokInfo::EOF,
604 ],
605 );
606 }
607
608 #[test]
609 fn test_const_slice() {
610 test_lexer(
611 "a[1..2]",
612 vec![
613 TokInfo::Ident("a"),
614 TokInfo::OpenBracket,
615 TokInfo::IConst(1),
616 TokInfo::CDots,
617 TokInfo::IConst(2),
618 TokInfo::CloseBracket,
619 TokInfo::EOF,
620 ],
621 )
622 }
623
624 #[test]
625 fn test_ident() {
626 test_lexer(
627 "a aaaa",
628 vec![TokInfo::Ident("a"), TokInfo::Ident("aaaa"), TokInfo::EOF],
629 )
630 }
631
632 #[test]
633 fn test_static_pars() {
634 test_lexer(
635 "function a<<const n : int>>()",
636 vec![
637 TokInfo::Function,
638 TokInfo::Ident("a"),
639 TokInfo::OpenStaticPar,
640 TokInfo::Const,
641 TokInfo::Ident("n"),
642 TokInfo::Colon,
643 TokInfo::Int,
644 TokInfo::CloseStaticPar,
645 TokInfo::OpenPar,
646 TokInfo::ClosePar,
647 TokInfo::EOF,
648 ],
649 );
650 test_lexer(
651 "x + amaury_n<<n-1>>(x);",
652 vec![
653 TokInfo::Ident("x"),
654 TokInfo::Plus,
655 TokInfo::Ident("amaury_n"),
656 TokInfo::OpenStaticPar,
657 TokInfo::Ident("n"),
658 TokInfo::Minus,
659 TokInfo::IConst(1),
660 TokInfo::CloseStaticPar,
661 TokInfo::OpenPar,
662 TokInfo::Ident("x"),
663 TokInfo::ClosePar,
664 TokInfo::Semicolon,
665 TokInfo::EOF,
666 ],
667 )
668 }
669
670 #[test]
671 fn test_unclosed_str() {
672 let mut lexer = Lexer::new("main.lus", "\"hello ");
673 assert_eq!(lexer.lex(), Err(Error::UnclosedStr));
674 }
675
676 #[test]
677 fn test_unclosed_comment() {
678 let mut lexer = Lexer::new("main.lus", "/* hello ");
679 assert_eq!(lexer.lex(), Err(Error::UnclosedComment));
680 }
681
682 #[test]
683 fn test_amaury() {
684 use TokInfo::*;
685 let amaury1 = r#"y = with n = 0 then 0 else
686 x + amaury_n<<n-1>>(x);"#;
687 test_lexer(
688 amaury1,
689 vec![
690 Ident("y"),
691 Equal,
692 With,
693 Ident("n"),
694 Equal,
695 IConst(0),
696 Then,
697 IConst(0),
698 Else,
699 Ident("x"),
700 Plus,
701 Ident("amaury_n"),
702 OpenStaticPar,
703 Ident("n"),
704 Minus,
705 IConst(1),
706 CloseStaticPar,
707 OpenPar,
708 Ident("x"),
709 ClosePar,
710 Semicolon,
711 EOF,
712 ],
713 );
714
715 let amaury = r#"
716node amaury_n<<const n:int>>(x: int) returns (y:int);
717let
718 y = with n = 0 then 0 else
719 x + amaury_n<<n-1>>(x);
720tel
721
722node amaury = amaury_n<<4>>;
723"#;
724 test_lexer(
725 amaury,
726 vec![
727 Node,
728 Ident("amaury_n"),
729 OpenStaticPar,
730 Const,
731 Ident("n"),
732 Colon,
733 Int,
734 CloseStaticPar,
735 OpenPar,
736 Ident("x"),
737 Colon,
738 Int,
739 ClosePar,
740 Returns,
741 OpenPar,
742 Ident("y"),
743 Colon,
744 Int,
745 ClosePar,
746 Semicolon,
747 Let,
748 Ident("y"),
749 Equal,
750 With,
751 Ident("n"),
752 Equal,
753 IConst(0),
754 Then,
755 IConst(0),
756 Else,
757 Ident("x"),
758 Plus,
759 Ident("amaury_n"),
760 OpenStaticPar,
761 Ident("n"),
762 Minus,
763 IConst(1),
764 CloseStaticPar,
765 OpenPar,
766 Ident("x"),
767 ClosePar,
768 Semicolon,
769 Tel,
770 Node,
771 Ident("amaury"),
772 Equal,
773 Ident("amaury_n"),
774 OpenStaticPar,
775 IConst(4),
776 CloseStaticPar,
777 Semicolon,
778 EOF,
779 ],
780 );
781 }
782}