1use std::fmt::{self};
2
3use combine::easy::{Error, Errors, Info};
4use combine::error::StreamError;
5use combine::stream::ResetStream;
6use combine::{Positioned, StreamOnce};
7
8use super::position::Pos;
9
10#[derive(Debug, PartialEq, Eq, Clone, Copy)]
11pub enum Kind {
12 Punctuator,
13 Name,
14 IntValue,
15 FloatValue,
16 StringValue,
17 BlockString,
18}
19
20#[derive(Debug, PartialEq, Eq, Clone, Copy)]
21pub struct Token<'a> {
22 pub kind: Kind,
23 pub value: &'a str,
24}
25
26#[derive(Debug, PartialEq)]
27pub struct TokenStream<'a> {
28 buf: &'a str,
29 position: Pos,
30 off: usize,
31 next_state: Option<(usize, Token<'a>, usize, Pos)>,
32 recursion_limit: usize,
33 token_limit: Option<usize>,
34 token_count: usize,
35}
36
37impl TokenStream<'_> {
38 pub(crate) fn offset(&self) -> usize {
39 self.off
40 }
41}
42
43pub fn spanned_tokens(source: &str) -> Result<Vec<(Kind, std::ops::Range<usize>)>, String> {
46 let mut stream = TokenStream::new(source);
47 let mut tokens = Vec::new();
48 loop {
49 match stream.uncons() {
50 Ok(token) => {
51 let start = token.value.as_ptr() as usize - source.as_ptr() as usize;
53 tokens.push((token.kind, start..start + token.value.len()));
54 }
55 Err(ref error) if error == &Error::end_of_input() => return Ok(tokens),
56 Err(error) => return Err(error.to_string()),
57 }
58 }
59}
60
61#[derive(Clone, Debug, PartialEq)]
62pub struct Checkpoint {
63 position: Pos,
64 off: usize,
65}
66
67impl<'a> StreamOnce for TokenStream<'a> {
68 type Token = Token<'a>;
69 type Range = Token<'a>;
70 type Position = Pos;
71 type Error = Errors<Token<'a>, Token<'a>, Pos>;
72
73 fn uncons(&mut self) -> Result<Self::Token, Error<Token<'a>, Token<'a>>> {
74 if let Some((at, tok, off, pos)) = self.next_state {
75 if at == self.off {
76 self.off = off;
77 self.position = pos;
78 return Ok(tok);
79 }
80 }
81 let old_pos = self.off;
82 let (kind, len) = self.take_token()?;
83 let value = &self.buf[self.off - len..self.off];
84 self.skip_whitespace();
85 let token = Token { kind, value };
86 self.next_state = Some((old_pos, token, self.off, self.position));
87 Ok(token)
88 }
89}
90
91impl<'a> Positioned for TokenStream<'a> {
92 fn position(&self) -> Self::Position {
93 self.position
94 }
95}
96
97impl<'a> ResetStream for TokenStream<'a> {
98 type Checkpoint = Checkpoint;
99 fn checkpoint(&self) -> Self::Checkpoint {
100 Checkpoint {
101 position: self.position,
102 off: self.off,
103 }
104 }
105 fn reset(&mut self, checkpoint: Checkpoint) -> Result<(), Self::Error> {
106 self.position = checkpoint.position;
107 self.off = checkpoint.off;
108 Ok(())
109 }
110}
111
112fn check_int(value: &str) -> bool {
115 value == "0"
116 || value == "-0"
117 || (!value.starts_with('0')
118 && value != "-"
119 && !value.starts_with("-0")
120 && value[1..].chars().all(|x| x.is_ascii_digit()))
121}
122
123fn check_dec(value: &str) -> bool {
124 !value.is_empty() && value.chars().all(|x| x.is_ascii_digit())
125}
126
127fn check_exp(value: &str) -> bool {
128 if value.is_empty() {
129 return false;
130 }
131 let first = value.chars().next().unwrap();
132 if first != '-' && first != '+' && (first <= '0' || first >= '9') {
133 return false;
134 }
135
136 value[1..].chars().all(|x| x.is_ascii_digit())
137}
138
139fn check_float(value: &str, exponent: Option<usize>, real: Option<usize>) -> bool {
140 match (exponent, real) {
141 (Some(e), Some(r)) if e < r => false,
142 (Some(e), Some(r)) => {
143 check_int(&value[..r]) && check_dec(&value[r + 1..e]) && check_exp(&value[e + 1..])
144 }
145 (Some(e), None) => check_int(&value[..e]) && check_exp(&value[e + 1..]),
146 (None, Some(r)) => check_int(&value[..r]) && check_dec(&value[r + 1..]),
147 (None, None) => unreachable!(),
148 }
149}
150
151impl<'a> TokenStream<'a> {
152 pub fn new(s: &'a str) -> TokenStream<'a> {
153 Self::with_recursion_limit(s, 50, None)
154 }
155
156 pub fn new_with_token_limit(s: &'a str, token_limit: usize) -> TokenStream<'a> {
157 Self::with_recursion_limit(s, 50, Some(token_limit))
158 }
159
160 pub(crate) fn with_recursion_limit(
164 s: &'a str,
165 recursion_limit: usize,
166 token_limit: Option<usize>,
167 ) -> TokenStream<'a> {
168 let mut me = TokenStream {
169 buf: s,
170 position: Pos { line: 1, column: 1 },
171 off: 0,
172 next_state: None,
173 recursion_limit,
174 token_limit,
175 token_count: 0,
176 };
177 me.skip_whitespace();
178 me
179 }
180
181 #[inline]
184 fn advance_token<T>(&mut self, kind: Kind, size: usize) -> Result<(Kind, usize), T> {
185 self.position.column += size;
186 self.off += size;
187 if self.token_limit.is_some() {
189 self.token_count += 1;
190 }
191 Ok((kind, size))
192 }
193
194 fn take_token(&mut self) -> Result<(Kind, usize), Error<Token<'a>, Token<'a>>> {
195 if let Some(limit) = self.token_limit {
196 if self.token_count >= limit {
197 return Err(Error::message_static_message("Token limit exceeded"));
198 }
199 }
200 use self::Kind::*;
201 let mut iter = self.buf[self.off..].char_indices();
202 let cur_char = match iter.next() {
203 Some((_, x)) => x,
204 None => return Err(Error::end_of_input()),
205 };
206
207 match cur_char {
208 '(' | '[' | '{' => {
209 self.recursion_limit = self
211 .recursion_limit
212 .checked_sub(1)
213 .ok_or_else(|| Error::message_static_message("Recursion limit exceeded"))?;
214
215 self.advance_token(Punctuator, 1)
216 }
217 ')' | ']' | '}' => {
218 self.recursion_limit = self.recursion_limit.saturating_add(1);
229 self.advance_token(Punctuator, 1)
230 }
231 '!' | '$' | ':' | '=' | '@' | '|' | '&' => self.advance_token(Punctuator, 1),
232 '.' => {
233 if iter.as_str().starts_with("..") {
234 self.advance_token(Punctuator, 3)
235 } else {
236 Err(Error::Unexpected(Info::Owned(
237 format_args!(
238 "bare dot {:?} is not supported, \
239 only \"...\"",
240 cur_char
241 )
242 .to_string(),
243 )))
244 }
245 }
246 '_' | 'a'..='z' | 'A'..='Z' => {
247 for (idx, cur_char) in iter.by_ref() {
248 match cur_char {
249 '_' | 'a'..='z' | 'A'..='Z' | '0'..='9' => continue,
250 _ => return self.advance_token(Name, idx),
251 }
252 }
253 let len = self.buf.len() - self.off;
254 self.position.column += len;
255 self.off += len;
256
257 Ok((Name, len))
258 }
259 '-' | '0'..='9' => {
260 let mut exponent = None;
261 let mut real = None;
262 let len = loop {
263 let (idx, cur_char) = match iter.next() {
264 Some(pair) => pair,
265 None => break self.buf.len() - self.off,
266 };
267 match cur_char {
268 ' ' | '\n' | '\r' | '\t' | ',' | '#' | '!' | '$' | ':' | '=' | '@'
270 | '|' | '&' | '(' | ')' | '[' | ']' | '{' | '}' => break idx,
271 '.' => real = Some(idx),
272 'e' | 'E' => exponent = Some(idx),
273 _ => {}
274 }
275 };
276
277 if exponent.is_some() || real.is_some() {
278 let value = &self.buf[self.off..][..len];
279 if !check_float(value, exponent, real) {
280 return Err(Error::Unexpected(Info::Owned(
281 format_args!("unsupported float {:?}", value).to_string(),
282 )));
283 }
284 self.position.column += len;
285 self.off += len;
286
287 Ok((FloatValue, len))
288 } else {
289 let value = &self.buf[self.off..][..len];
290 if !check_int(value) {
291 return Err(Error::Unexpected(Info::Owned(
292 format_args!("unsupported integer {:?}", value).to_string(),
293 )));
294 }
295 self.advance_token(IntValue, len)
296 }
297 }
298 '"' => {
299 if iter.as_str().starts_with("\"\"") {
300 let tail = &iter.as_str()[2..];
301 for (end_idx, _) in tail.match_indices("\"\"\"") {
302 if !tail[..end_idx].ends_with('\\') {
303 self.update_position(end_idx + 6);
304 return Ok((BlockString, end_idx + 6));
305 }
306 }
307
308 Err(Error::Unexpected(Info::Owned(
309 "unterminated block string value".to_string(),
310 )))
311 } else {
312 let mut nchars = 1;
313 let mut escaped = false;
314 for (idx, cur_char) in iter {
315 nchars += 1;
316 match cur_char {
317 '"' if escaped => {}
318 '"' => {
319 self.position.column += nchars;
320 self.off += idx + 1;
321 return Ok((StringValue, idx + 1));
322 }
323 '\n' => {
324 return Err(Error::Unexpected(Info::Owned(
325 "unterminated string value".to_string(),
326 )));
327 }
328
329 _ => {}
330 }
331
332 escaped = !escaped && cur_char == '\\';
334 }
335 Err(Error::Unexpected(Info::Owned(
336 "unterminated string value".to_string(),
337 )))
338 }
339 }
340 _ => Err(Error::Unexpected(Info::Owned(
341 format_args!("unexpected character {:?}", cur_char).to_string(),
342 ))),
343 }
344 }
345
346 fn skip_whitespace(&mut self) {
347 let mut iter = self.buf[self.off..].char_indices();
348 let idx = loop {
349 let (idx, cur_char) = match iter.next() {
350 Some(pair) => pair,
351 None => break self.buf.len() - self.off,
352 };
353 match cur_char {
354 '\u{feff}' | '\r' => continue,
355 '\t' => self.position.column += 8,
356 '\n' => {
357 self.position.column = 1;
358 self.position.line += 1;
359 }
360 ' ' | ',' => {
362 self.position.column += 1;
363 continue;
364 }
365 '#' => {
367 for (_, cur_char) in iter.by_ref() {
368 if cur_char == '\r' || cur_char == '\n' {
370 self.position.column = 1;
371 self.position.line += 1;
372 break;
373 }
374 }
375 continue;
376 }
377 _ => break idx,
378 }
379 };
380 self.off += idx;
381 }
382
383 fn update_position(&mut self, len: usize) {
384 let val = &self.buf[self.off..][..len];
385 self.off += len;
386 let lines = val.as_bytes().iter().filter(|&&x| x == b'\n').count();
387 self.position.line += lines;
388 if lines > 0 {
389 let line_offset = val.rfind('\n').unwrap() + 1;
390 let num = val[line_offset..].chars().count();
391 self.position.column = num + 1;
392 } else {
393 let num = val.chars().count();
394 self.position.column += num;
395 }
396 }
397}
398
399impl<'a> fmt::Display for Token<'a> {
400 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
401 write!(f, "{}[{:?}]", self.value, self.kind)
402 }
403}
404
405#[cfg(test)]
406mod test {
407 #[test]
408 fn spanned_tokens_cover_whole_tokens() {
409 let source = "query($a:ID){f(s:\"\"\"\n x$a\n\"\"\" t:\"$a\")}";
410 let tokens = super::spanned_tokens(source).unwrap();
411 let names: Vec<&str> = tokens
412 .iter()
413 .filter(|(kind, _)| *kind == super::Kind::Name)
414 .map(|(_, span)| &source[span.clone()])
415 .collect();
416 assert_eq!(names, ["query", "a", "ID", "f", "s", "t"]);
418 let strings: Vec<&str> = tokens
419 .iter()
420 .filter(|(kind, _)| matches!(kind, super::Kind::StringValue | super::Kind::BlockString))
421 .map(|(_, span)| &source[span.clone()])
422 .collect();
423 assert_eq!(strings, ["\"\"\"\n x$a\n\"\"\"", "\"$a\""]);
424 }
425
426 use super::Kind::*;
427 use super::{Kind, TokenStream};
428 use combine::easy::Error;
429
430 use combine::{Positioned, StreamOnce};
431
432 fn tok_str(s: &str) -> Vec<&str> {
433 let mut r = Vec::new();
434 let mut s = TokenStream::new(s);
435 loop {
436 match s.uncons() {
437 Ok(x) => r.push(x.value),
438 Err(ref e) if e == &Error::end_of_input() => break,
439 Err(e) => panic!("Parse error at {}: {}", s.position(), e),
440 }
441 }
442 r
443 }
444 fn tok_typ(s: &str) -> Vec<Kind> {
445 let mut r = Vec::new();
446 let mut s = TokenStream::new(s);
447 loop {
448 match s.uncons() {
449 Ok(x) => r.push(x.kind),
450 Err(ref e) if e == &Error::end_of_input() => break,
451 Err(e) => panic!("Parse error at {}: {}", s.position(), e),
452 }
453 }
454 r
455 }
456
457 #[test]
458 fn comments_and_commas() {
459 assert_eq!(tok_str("# hello { world }"), &[] as &[&str]);
460 assert_eq!(tok_str("# x\n,,,"), &[] as &[&str]);
461 assert_eq!(tok_str(", ,, ,,, # x"), &[] as &[&str]);
462 }
463
464 #[test]
465 fn simple() {
466 assert_eq!(tok_str("a { b }"), ["a", "{", "b", "}"]);
467 assert_eq!(tok_typ("a { b }"), [Name, Punctuator, Name, Punctuator]);
468 }
469
470 #[test]
471 fn query() {
472 assert_eq!(
473 tok_str(
474 "query Query {
475 object { field }
476 }"
477 ),
478 ["query", "Query", "{", "object", "{", "field", "}", "}"]
479 );
480 }
481
482 #[test]
483 fn fragment() {
484 assert_eq!(tok_str("a { ...b }"), ["a", "{", "...", "b", "}"]);
485 }
486
487 #[test]
488 fn int() {
489 assert_eq!(tok_str("0"), ["0"]);
490 assert_eq!(tok_str("0,"), ["0"]);
491 assert_eq!(tok_str("0# x"), ["0"]);
492 assert_eq!(tok_typ("0"), [IntValue]);
493 assert_eq!(tok_str("-0"), ["-0"]);
494 assert_eq!(tok_typ("-0"), [IntValue]);
495 assert_eq!(tok_str("-1"), ["-1"]);
496 assert_eq!(tok_typ("-1"), [IntValue]);
497 assert_eq!(tok_str("-132"), ["-132"]);
498 assert_eq!(tok_typ("-132"), [IntValue]);
499 assert_eq!(tok_str("132"), ["132"]);
500 assert_eq!(tok_typ("132"), [IntValue]);
501 assert_eq!(
502 tok_str("a(x: 10) { b }"),
503 ["a", "(", "x", ":", "10", ")", "{", "b", "}"]
504 );
505 assert_eq!(
506 tok_typ("a(x: 10) { b }"),
507 [
508 Name, Punctuator, Name, Punctuator, IntValue, Punctuator, Punctuator, Name,
509 Punctuator
510 ]
511 );
512 }
513
514 #[test]
516 #[should_panic]
517 fn zero_int() {
518 tok_str("01");
519 }
520 #[test]
521 #[should_panic]
522 fn zero_int4() {
523 tok_str("00001");
524 }
525 #[test]
526 #[should_panic]
527 fn minus_int() {
528 tok_str("-");
529 }
530 #[test]
531 #[should_panic]
532 fn minus_zero_int() {
533 tok_str("-01");
534 }
535 #[test]
536 #[should_panic]
537 fn minus_zero_int4() {
538 tok_str("-00001");
539 }
540 #[test]
541 #[should_panic]
542 fn letters_int() {
543 tok_str("0bbc");
544 }
545
546 #[test]
547 fn float() {
548 assert_eq!(tok_str("0.0"), ["0.0"]);
549 assert_eq!(tok_typ("0.0"), [FloatValue]);
550 assert_eq!(tok_str("-0.0"), ["-0.0"]);
551 assert_eq!(tok_typ("-0.0"), [FloatValue]);
552 assert_eq!(tok_str("-1.0"), ["-1.0"]);
553 assert_eq!(tok_typ("-1.0"), [FloatValue]);
554 assert_eq!(tok_str("-1.023"), ["-1.023"]);
555 assert_eq!(tok_typ("-1.023"), [FloatValue]);
556 assert_eq!(tok_str("-132.0"), ["-132.0"]);
557 assert_eq!(tok_typ("-132.0"), [FloatValue]);
558 assert_eq!(tok_str("132.0"), ["132.0"]);
559 assert_eq!(tok_typ("132.0"), [FloatValue]);
560 assert_eq!(tok_str("0e+0"), ["0e+0"]);
561 assert_eq!(tok_typ("0e+0"), [FloatValue]);
562 assert_eq!(tok_str("0.0e+0"), ["0.0e+0"]);
563 assert_eq!(tok_typ("0.0e+0"), [FloatValue]);
564 assert_eq!(tok_str("-0e+0"), ["-0e+0"]);
565 assert_eq!(tok_typ("-0e+0"), [FloatValue]);
566 assert_eq!(tok_str("-1e+0"), ["-1e+0"]);
567 assert_eq!(tok_typ("-1e+0"), [FloatValue]);
568 assert_eq!(tok_str("-132e+0"), ["-132e+0"]);
569 assert_eq!(tok_typ("-132e+0"), [FloatValue]);
570 assert_eq!(tok_str("132e+0"), ["132e+0"]);
571 assert_eq!(tok_typ("132e+0"), [FloatValue]);
572 assert_eq!(
573 tok_str("a(x: 10.0) { b }"),
574 ["a", "(", "x", ":", "10.0", ")", "{", "b", "}"]
575 );
576 assert_eq!(
577 tok_typ("a(x: 10.0) { b }"),
578 [
579 Name, Punctuator, Name, Punctuator, FloatValue, Punctuator, Punctuator, Name,
580 Punctuator
581 ]
582 );
583 assert_eq!(tok_str("1.23e4"), ["1.23e4"]);
584 assert_eq!(tok_typ("1.23e4"), [FloatValue]);
585 }
586
587 #[test]
589 #[should_panic]
590 fn no_int_float() {
591 tok_str(".0");
592 }
593 #[test]
594 #[should_panic]
595 fn no_int_float1() {
596 tok_str(".1");
597 }
598 #[test]
599 #[should_panic]
600 fn zero_float() {
601 tok_str("01.0");
602 }
603 #[test]
604 #[should_panic]
605 fn zero_float4() {
606 tok_str("00001.0");
607 }
608 #[test]
609 #[should_panic]
610 fn minus_float() {
611 tok_str("-.0");
612 }
613 #[test]
614 #[should_panic]
615 fn minus_zero_float() {
616 tok_str("-01.0");
617 }
618 #[test]
619 #[should_panic]
620 fn minus_zero_float4() {
621 tok_str("-00001.0");
622 }
623 #[test]
624 #[should_panic]
625 fn letters_float() {
626 tok_str("0bbc.0");
627 }
628 #[test]
629 #[should_panic]
630 fn letters_float2() {
631 tok_str("0.bbc");
632 }
633 #[test]
634 #[should_panic]
635 fn letters_float3() {
636 tok_str("0.bbce0");
637 }
638 #[test]
639 #[should_panic]
640 fn no_exp_sign_float() {
641 tok_str("0e0");
642 }
643 #[test]
644 #[should_panic]
645 fn unterminated_string() {
646 tok_str(r#""hello\""#);
647 }
648 #[test]
649 #[should_panic]
650 fn extra_unterminated_string() {
651 tok_str(r#""hello\\\""#);
652 }
653
654 #[test]
655 fn string() {
656 assert_eq!(tok_str(r#""""#), [r#""""#]);
657 assert_eq!(tok_typ(r#""""#), [StringValue]);
658 assert_eq!(tok_str(r#""hello""#), [r#""hello""#]);
659 assert_eq!(tok_str(r#""hello\\""#), [r#""hello\\""#]);
660 assert_eq!(tok_str(r#""hello\\\\""#), [r#""hello\\\\""#]);
661 assert_eq!(tok_str(r#""he\\llo""#), [r#""he\\llo""#]);
662 assert_eq!(tok_typ(r#""hello""#), [StringValue]);
663 assert_eq!(tok_str(r#""my\"quote""#), [r#""my\"quote""#]);
664 assert_eq!(tok_typ(r#""my\"quote""#), [StringValue]);
665 }
666
667 #[test]
668 fn block_string() {
669 assert_eq!(tok_str(r#""""""""#), [r#""""""""#]);
670 assert_eq!(tok_typ(r#""""""""#), [BlockString]);
671 assert_eq!(tok_str(r#""""hello""""#), [r#""""hello""""#]);
672 assert_eq!(tok_typ(r#""""hello""""#), [BlockString]);
673 assert_eq!(tok_str(r#""""my "quote" """"#), [r#""""my "quote" """"#]);
674 assert_eq!(tok_typ(r#""""my "quote" """"#), [BlockString]);
675 assert_eq!(tok_str(r#""""\"""quote" """"#), [r#""""\"""quote" """"#]);
676 assert_eq!(tok_typ(r#""""\"""quote" """"#), [BlockString]);
677 }
678}