]>
Witch of Git - ess/blob - src/parser.rs
1 //! Functions to parse s-expressions and expression atoms.
3 //! This module contains the core parsing machinery.
5 //! * If you're interested in getting a parsed s-expression that you can use,
6 //! then looking at [`parse`] and [`parse_one`] are your best bet.
7 //! * If you want to write your own parsers that contain s-expressions,
8 //! [`ParseResult`] and [`parse_expression`] will be the most useful to you.
10 //! [`parse`]: fn.parse.html
11 //! [`parse_one`]: fn.parse_one.html
12 //! [`ParseResult`]: enum.ParseResult.html
13 //! [`parse_expression`]: fn.parse_expression.html
16 use span
::{ByteSpan
, Span
};
18 // Parsing Types ///////////////////////////////////////////////////////////////
20 /// Represents what to do next in partially completed parsing.
22 /// `ParseResult` is returned from all intermediate parsers. If you just want to
23 /// get back parsed s-expressions, you won't need to worry about this type since
24 /// the top level parsers just return a `Result`.
26 /// If the parser failed to produce a result, it will return `Error`, and if it
27 /// succeeded we'll get the `Done` variant containing the value produced and the
28 /// rest of the text to work on.
29 #[derive(Debug, PartialEq, Eq, Clone)]
30 pub enum ParseResult
<'a
, T
, E
> {
31 /// The parser succeeded, this contains first the un-consumed portion of the
32 /// input then the result produced by parsing.
34 /// The parser failed, the `E` represents the reason for the failure.
38 /// Indicates how parsing failed.
40 /// Most `ParseError` variants contain a `Box<ParseError>` that represents the
41 /// cause of that error. Using this, `ParseError` variants can be chained to
42 /// produce a more complete picture of what exactly went wrong during parsing.
43 #[derive(Debug, PartialEq, Eq, Clone)]
44 pub enum ParseError
<Loc
= ByteSpan
>
48 /// Parsing reached the end of input where not expecting to, usually this
49 /// will be contained inside another `ParseError` like `String(box
50 /// UnexpectedEof, ...)` which indicates that the closing quote was never
53 /// Some problem occurred while parsing a list, along with the cause of that
55 List(Box
<ParseError
>, Loc
),
56 /// Some problem occurred while parsing an s-expression. This will only be
57 /// generated if EOF is reached unexpectedly at the beginning of
58 /// `parse_expression`, so it should probably be removed.
59 Sexp(Box
<ParseError
>, Loc
),
60 /// Some problem occurred while parsing a character literal, along with the
61 /// cause of the error.
62 Char(Box
<ParseError
>, Loc
),
63 /// Some problem occurred while parsing a string literal, along with the
64 /// cause of the error.
65 String(Box
<ParseError
>, Loc
),
66 /// Some problem occurred while parsing a symbol, along with the cause of
68 Symbol(Box
<ParseError
>, Loc
),
69 /// Some problem occurred while parsing a number literal, along with the
70 /// cause of the error.
71 Number(Box
<ParseError
>, Loc
),
72 /// An unexpected character was found. This will usually be the root cause
73 /// in some chain of `ParseError`s.
74 Unexpected(char, Loc
::Begin
),
76 use self::ParseResult
::*;
78 // Parsing Utilities ///////////////////////////////////////////////////////////
81 fn is_delimiter(&self) -> bool
;
84 impl IsDelimeter
for char {
85 fn is_delimiter(&self) -> bool
{
86 let delim_chars
= r
#";()[]{}"\`,"#;
87 self.is_whitespace() || delim_chars.contains(*self)
91 fn consume_whitespace<'a>(input: &'a str, start_loc: usize) -> (&'a str, usize) {
92 let mut buffer = input;
93 let mut loc = start_loc;
94 while !buffer.is_empty() && (buffer.starts_with(char::is_whitespace) || buffer.starts_with(';'))
96 if let Some(pos) = buffer.find(|c: char| !c.is_whitespace()) {
97 buffer = &buffer[pos..];
100 buffer = &buffer[buffer.len()..];
104 if buffer.starts_with(';') {
105 if let Some(pos) = buffer.find('\n') {
106 buffer = &buffer[pos + 1..];
109 buffer = &buffer[buffer.len()..];
117 macro_rules! consume_whitespace {
118 ($input:expr, $start_loc:expr, $ErrorFn:expr) => {{
119 let (text, loc) = consume_whitespace($input, $start_loc);
121 return Error($ErrorFn(Box::new(ParseError::UnexpectedEof), (loc, loc)));
128 // Top Level Parsers ///////////////////////////////////////////////////////////
130 /// Parse a sequence of s-expressions.
132 /// This function returns `(Vec<Sexp>, Option<ParseError>)` so that it can
133 /// return partial results, for when some component parses successfully and a
134 /// later part fails.
138 /// If the text contains an invalid s-expression (imbalanced parenthesis,
139 /// quotes, invalid numbers like 123q, etc.) then the parser will stop and
140 /// return an error. Every s-expression before that point that successfully
141 /// parsed will still be returned.
145 /// We can get useful partial results
148 /// # use ess::parser::parse;
149 /// let (exprs, err) = parse("1 2 3 ( 4");
150 /// assert_eq!(exprs.len(), 3);
151 /// assert!(err.is_some());
153 pub fn parse(mut input: &str) -> (Vec<Sexp>, Option<ParseError>) {
154 let mut start_loc = 0;
155 let mut results = Vec::new();
157 match parse_expression(input, start_loc) {
158 Done(rest, result) => {
159 let (rest, loc) = consume_whitespace(rest, result.get_loc().1);
162 results.push(result);
163 if rest.trim() == "" {
164 return (results, None);
168 return (results, Some(err));
174 /// Parses a single s-expression, ignoring any trailing text.
176 /// This function returns a pair of the parsed s-expression and the tail of the text.
180 /// If the text begins with an invalid s-expression (imbalanced parenthesis,
181 /// quotes, invalid numbers like 123q, etc.) then the parser will return an
182 /// error. Any text after the first s-expression doesn't affect the parsing.
188 /// # use ess::parser::parse_one;
189 /// let (expr, rest) = parse_one("1 (").unwrap();
190 /// assert_eq!(rest, " (");
192 pub fn parse_one(input: &str) -> Result<(Sexp, &str), ParseError> {
193 match parse_expression(input, 0) {
194 Done(rest, result) => Ok((result, rest)),
195 Error(err) => Err(err),
199 // Core Parsers ////////////////////////////////////////////////////////////////
201 // TODO: All of these parsers deserve docs, but since they're somewhat internal
202 // parsers, it's less critical than the rest of the API.
204 #[allow(missing_docs)]
205 pub fn parse_expression(input: &str, start_loc: usize) -> ParseResult<Sexp, ParseError> {
206 let (input, start_loc) = consume_whitespace!(input, start_loc, ParseError::Sexp);
208 match input.chars().next() {
209 Some('0'...'9') => parse_number(input, start_loc),
210 Some('(') | Some('{') | Some('[') => parse_list(input, start_loc),
211 Some('#') => parse_character(input, start_loc),
212 Some('"') => parse_string(input, start_loc),
213 Some('
\''
) => match parse_expression(&input
[1..], start_loc
+ 1) {
214 Done(rest
, result
) => {
215 let span
= *result
.get_loc();
216 let quote_span
= (0, 1).offset(start_loc
);
220 vec
![Sexp
::Sym("quote".into
(), quote_span
), result
],
221 quote_span
.un
ion
(&span
),
227 Some('`'
) => match parse_expression(&input
[1..], start_loc
+ 1) {
228 Done(rest
, result
) => {
229 let span
= *result
.get_loc();
230 let quote_span
= (0, 1).offset(start_loc
);
234 vec
![Sexp
::Sym("quasiquote".into
(), quote_span
), result
],
235 quote_span
.un
ion
(&span
),
242 if input
[1..].starts_with('@'
) {
243 match parse_expression(&input
[2..], start_loc
+ 2) {
244 Done(rest
, result
) => {
245 let span
= *result
.get_loc();
246 let quote_span
= (0, 2).offset(start_loc
);
250 vec
![Sexp
::Sym("unquote-splicing".into
(), quote_span
), result
],
251 quote_span
.un
ion
(&span
),
258 match parse_expression(&input
[1..], start_loc
+ 1) {
259 Done(rest
, result
) => {
260 let span
= *result
.get_loc();
261 let quote_span
= (0, 1).offset(start_loc
);
265 vec
![Sexp
::Sym("unquote".into
(), quote_span
), result
],
266 quote_span
.un
ion
(&span
),
274 Some(_
) => parse_symbol(input
, start_loc
),
275 None
=> unreachable
!(),
279 #[allow(missing_docs)]
280 pub fn parse_list(input
: &str, start_loc
: usize) -> ParseResult
<Sexp
, ParseError
> {
281 let (input
, start_loc
) = consume_whitespace
!(input
, start_loc
, ParseError
::List
);
283 let first_char
= match input
.chars().nth(0) {
284 Some(c @ '
('
) | Some(c @ '
{'
) | Some(c @ '
['
) => c
,
286 return Error(ParseError
::List(
287 Box
::new(ParseError
::Unexpected(c
, 0)),
288 (0, 0).offset(start_loc
),
291 None
=> unreachable
!(),
294 let mut input
= &input
[1..];
295 let mut loc
= start_loc
+ 1;
296 let mut members
= Vec
::new();
299 let (new_input
, new_loc
) = consume_whitespace
!(input
, loc
, ParseError
::List
);
304 match input
.chars().nth(0) {
305 Some(c @ '
)'
) | Some(c @ '
}'
) | Some(c @ '
]'
) => match (first_char
, c
) {
306 ('
('
, '
)'
) | ('
{'
, '
}'
) | ('
['
, '
]'
) => {
307 return Done(&input
[1..], Sexp
::List(members
, (start_loc
, loc
+ 1)))
310 return Error(ParseError
::List(
311 Box
::new(ParseError
::Unexpected(c
, loc
)),
317 None
=> unreachable
!(),
320 match parse_expression(input
, loc
) {
321 Done(new_input
, member
) => {
322 loc
= member
.get_loc().1;
323 members
.push(member
);
326 Error(err
) => return Error(ParseError
::List(Box
::new(err
), (0, 0).offset(loc
))),
331 #[allow(missing_docs)]
332 pub fn parse_number(input
: &str, start_loc
: usize) -> ParseResult
<Sexp
, ParseError
> {
333 let (input
, start_loc
) = consume_whitespace
!(input
, start_loc
, ParseError
::Number
);
335 match input
.chars().next() {
336 Some(c
) if !c
.is
_d
ig
it
(10) => {
337 return Error(ParseError
::Number(
338 Box
::new(ParseError
::Unexpected(c
, start_loc
)),
339 (0, c
.len_utf8()).offset(start_loc
),
343 return Error(ParseError
::Number(
344 Box
::new(ParseError
::UnexpectedEof
),
345 (0, 0).offset(start_loc
),
354 // Before the decimal point
355 for (i
, c
) in input
.char_indices() {
361 if c
.is
_del
im
iter
() {
365 input
[..i].parse().expect("Already matched digits"),
366 (0, i
).offset(start_loc
),
371 if !c
.is
_d
ig
it
(base
) {
372 return Error(ParseError
::Number(
373 Box
::new(ParseError
::Unexpected(c
, start_loc
+ i
)),
374 (i
, i
).offset(start_loc
),
378 end
= i
+ c
.len_utf8();
381 if input
[end
..].is
_empty
() {
385 input
.parse().expect("Already matched digits"),
386 (0, end
).offset(start_loc
),
391 // After the decimal point
392 for (i
, c
) in input
[end
..].char_indices() {
393 if c
.is
_del
im
iter
() {
399 .expect("Already matched digits.digits"),
400 (0, end
+ i
).offset(start_loc
),
405 if !c
.is
_d
ig
it
(base
) {
406 return Error(ParseError
::Number(
407 Box
::new(ParseError
::Unexpected(c
, start_loc
+ i
+ end
)),
408 (i
+ end
, i
+ end
).offset(start_loc
),
414 &input
[input
.len()..],
416 input
.parse().expect("Already matched digits.digits"),
417 (0, input
.len()).offset(start_loc
),
422 #[allow(missing_docs)]
423 pub fn parse_symbol(input
: &str, start_loc
: usize) -> ParseResult
<Sexp
, ParseError
> {
424 let (input
, start_loc
) = consume_whitespace
!(input
, start_loc
, ParseError
::Symbol
);
426 match input
.chars().next() {
427 Some(c @ '
#') | Some(c @ ':') | Some(c @ '0'...'9') => {
428 return Error(ParseError
::Symbol(
429 Box
::new(ParseError
::Unexpected(c
, start_loc
)),
430 (0, 0).offset(start_loc
),
433 Some(c
) if c
.is
_del
im
iter
() => {
434 return Error(ParseError
::Symbol(
435 Box
::new(ParseError
::Unexpected(c
, start_loc
)),
436 (0, 0).offset(start_loc
),
440 None
=> unreachable
!(),
443 for (i
, c
) in input
.char_indices() {
444 if c
.is
_del
im
iter
() {
447 Sexp
::Sym(input
[..i].into
(), (0, i
).offset(start_loc
)),
453 &input
[input
.len()..],
454 Sexp
::Sym(input
.into
(), (0, input
.len()).offset(start_loc
)),
458 #[allow(missing_docs)]
459 pub fn parse_string(input
: &str, start_loc
: usize) -> ParseResult
<Sexp
, ParseError
> {
460 let (input
, start_loc
) = consume_whitespace
!(input
, start_loc
, ParseError
::String
);
462 match input
.chars().next() {
465 return Error(ParseError::String(
466 Box::new(ParseError::Unexpected(c, start_loc)),
467 (0, 0).offset(start_loc),
470 None => unreachable!(),
473 for (i, c) in input[1..].char_indices() {
477 Sexp
::Str(input
[1..=i
].into
(), (0, i
+ 2).offset(start_loc
)),
482 Error(ParseError
::String(
483 Box
::new(ParseError
::UnexpectedEof
),
484 (0, input
.len()).offset(start_loc
),
488 #[allow(missing_docs)]
489 pub fn parse_character(input
: &str, start_loc
: usize) -> ParseResult
<Sexp
, ParseError
> {
490 let (input
, start_loc
) = consume_whitespace
!(input
, start_loc
, ParseError
::Char
);
492 match input
.chars().nth(0) {
495 return Error(ParseError
::Char(
496 Box
::new(ParseError
::Unexpected(c
, start_loc
)),
497 (0, 0).offset(start_loc
),
501 return Error(ParseError
::Char(
502 Box
::new(ParseError
::UnexpectedEof
),
503 (0, 0).offset(start_loc
),
508 match input
.chars().nth(1) {
511 return Error(ParseError
::Char(
512 Box
::new(ParseError
::Unexpected(c
, start_loc
+ 1)),
513 (1, 1).offset(start_loc
),
517 return Error(ParseError
::Char(
518 Box
::new(ParseError
::UnexpectedEof
),
519 (1, 1).offset(start_loc
),
524 match input
.chars().nth(2) {
525 Some(c
) => Done(&input
[3..], Sexp
::Char(c
, (0, 3).offset(start_loc
))),
526 None
=> Error(ParseError
::Char(
527 Box
::new(ParseError
::UnexpectedEof
),
528 (2, 2).offset(start_loc
),
533 // Tests ///////////////////////////////////////////////////////////////////////
537 use parser
::ParseResult
::*;
548 Sexp
::Int(1, (0, 1)),
549 Sexp
::Int(2, (2, 3)),
558 vec
![Sexp
::Int(1, (0, 1)), Sexp
::Int(2, (2, 3))],
559 Some(ParseError
::Symbol(
560 Box
::new(ParseError
::Unexpected('
)'
, 4)),
568 fn test_parse_one() {
569 assert_eq
!(parse_one("1 2"), Ok((Sexp
::Int(1, (0, 1)), " 2")));
573 fn test_parse_expression() {
574 assert_eq
!(parse_expression(" 1", 0), Done("", Sexp
::Int(1, (1, 2))));
576 parse_expression("2.2", 0),
577 Done("", Sexp
::Float(2.2, (0, 3)))
580 parse_expression(" a", 0),
581 Done("", Sexp
::Sym("a".into
(), (1, 2)))
584 parse_expression("#\\c", 0),
585 Done("", Sexp
::Char('c'
, (0, 3)))
588 parse_expression(r
#""hi""#, 0),
589 Done("", Sexp
::Str("hi".into
(), (0, 4)))
592 parse_expression("()", 0),
593 Done("", Sexp
::List(vec
![], (0, 2)))
596 parse_expression("( 1 2 3 )", 0),
601 Sexp
::Int(1, (2, 3)),
602 Sexp
::Int(2, (4, 5)),
603 Sexp
::Int(3, (6, 7)),
611 parse_expression("", 0),
612 Error(ParseError
::Sexp(
613 Box
::new(ParseError
::UnexpectedEof
),
620 fn test_parse_expr_quote() {
622 parse_expression("'a", 0),
627 Sexp
::Sym("quote".into
(), (0, 1)),
628 Sexp
::Sym("a".into
(), (1, 2)),
635 parse_expression("'1", 0),
639 vec
![Sexp
::Sym("quote".into
(), (0, 1)), Sexp
::Int(1, (1, 2)),],
645 parse_expression("' (1 2 3)", 0),
650 Sexp
::Sym("quote".into
(), (0, 1)),
653 Sexp
::Int(1, (3, 4)),
654 Sexp
::Int(2, (5, 6)),
655 Sexp
::Int(3, (7, 8)),
666 parse_expression("'", 0),
667 Error(ParseError
::Sexp(
668 Box
::new(ParseError
::UnexpectedEof
),
673 parse_expression("`'", 0),
674 Error(ParseError
::Sexp(
675 Box
::new(ParseError
::UnexpectedEof
),
682 fn test_parse_expr_quasiquote() {
684 parse_expression("`a", 0),
689 Sexp
::Sym("quasiquote".into
(), (0, 1)),
690 Sexp
::Sym("a".into
(), (1, 2)),
697 parse_expression("`1", 0),
701 vec
![Sexp
::Sym("quasiquote".into
(), (0, 1)), Sexp
::Int(1, (1, 2)),],
707 parse_expression("` (1 2 3)", 0),
712 Sexp
::Sym("quasiquote".into
(), (0, 1)),
715 Sexp
::Int(1, (3, 4)),
716 Sexp
::Int(2, (5, 6)),
717 Sexp
::Int(3, (7, 8)),
727 parse_expression("`'a", 0),
732 Sexp
::Sym("quasiquote".into
(), (0, 1)),
735 Sexp
::Sym("quote".into
(), (1, 2)),
736 Sexp
::Sym("a".into
(), (2, 3)),
747 parse_expression("`", 0),
748 Error(ParseError
::Sexp(
749 Box
::new(ParseError
::UnexpectedEof
),
756 fn test_parse_expr_unquote() {
758 parse_expression(",a", 0),
763 Sexp
::Sym("unquote".into
(), (0, 1)),
764 Sexp
::Sym("a".into
(), (1, 2)),
771 parse_expression(",1", 0),
775 vec
![Sexp
::Sym("unquote".into
(), (0, 1)), Sexp
::Int(1, (1, 2)),],
781 parse_expression(", (1 2 3)", 0),
786 Sexp
::Sym("unquote".into
(), (0, 1)),
789 Sexp
::Int(1, (3, 4)),
790 Sexp
::Int(2, (5, 6)),
791 Sexp
::Int(3, (7, 8)),
801 parse_expression("`,a", 0),
806 Sexp
::Sym("quasiquote".into
(), (0, 1)),
809 Sexp
::Sym("unquote".into
(), (1, 2)),
810 Sexp
::Sym("a".into
(), (2, 3)),
820 parse_expression("`(,@a)", 0),
825 Sexp
::Sym("quasiquote".into
(), (0, 1)),
829 Sexp
::Sym("unquote-splicing".into
(), (2, 4)),
830 Sexp
::Sym("a".into
(), (4, 5)),
843 parse_expression(",", 0),
844 Error(ParseError
::Sexp(
845 Box
::new(ParseError
::UnexpectedEof
),
850 parse_expression(",@", 0),
851 Error(ParseError
::Sexp(
852 Box
::new(ParseError
::UnexpectedEof
),
859 fn test_parse_list() {
860 assert_eq
!(parse_list("()", 0), Done("", Sexp
::List(vec
![], (0, 2))));
862 parse_list("(1)", 0),
863 Done("", Sexp
::List(vec
![Sexp
::Int(1, (1, 2))], (0, 3)))
866 parse_list(" ( 1 2 3 a )", 0),
871 Sexp
::Int(1, (4, 5)),
872 Sexp
::Int(2, (9, 10)),
873 Sexp
::Int(3, (12, 13)),
874 Sexp
::Sym("a".into
(), (14, 15)),
883 fn test_parse_number() {
884 assert_eq
!(parse_number("1", 0), Done("", Sexp
::Int(1, (0, 1))));
885 assert_eq
!(parse_number(" 13", 0), Done("", Sexp
::Int(13, (1, 3))));
886 assert_eq
!(parse_number("1.2", 0), Done("", Sexp
::Float(1.2, (0, 3))));
888 parse_number("\u{3000}4.2", 0),
889 Done("", Sexp
::Float(4.2, (0, 3).offset('
\u{3000}'
.len_utf8())))
891 assert_eq
!(parse_number(" 42 ", 0), Done(" ", Sexp
::Int(42, (2, 4))));
893 parse_number(" 4.2 ", 0),
894 Done(" ", Sexp
::Float(4.2, (1, 4)))
896 assert_eq
!(parse_number("1()", 0), Done("()", Sexp
::Int(1, (0, 1))));
898 parse_number("3.6()", 0),
899 Done("()", Sexp
::Float(3.6, (0, 3)))
904 Error(ParseError
::Number(
905 Box
::new(ParseError
::UnexpectedEof
),
910 parse_number("123a", 0),
911 Error(ParseError
::Number(
912 Box
::new(ParseError
::Unexpected('a'
, 3)),
917 parse_number("66.6+", 0),
918 Error(ParseError
::Number(
919 Box
::new(ParseError
::Unexpected('
+'
, 4)),
926 fn test_parse_ident() {
928 parse_symbol("+", 0),
929 Done("", Sexp
::Sym("+".into
(), (0, 1)))
932 parse_symbol(" nil?", 0),
933 Done("", Sexp
::Sym("nil?".into
(), (1, 5)))
936 parse_symbol(" ->socket", 0),
937 Done("", Sexp
::Sym("->socket".into
(), (1, 9)))
940 parse_symbol("fib(", 0),
941 Done("(", Sexp
::Sym("fib".into
(), (0, 3)))
944 parse_symbol("foo2", 0),
945 Done("", Sexp
::Sym("foo2".into
(), (0, 4)))
948 // We reserve #foo for the implementation to do as it wishes
950 parse_symbol("#hi", 0),
951 Error(ParseError
::Symbol(
952 Box
::new(ParseError
::Unexpected('
#', 0)),
956 // We reserve :foo for keywords
958 parse_symbol(":hi", 0),
959 Error(ParseError
::Symbol(
960 Box
::new(ParseError
::Unexpected('
:'
, 0)),
967 Error(ParseError
::Symbol(
968 Box
::new(ParseError
::UnexpectedEof
),
973 parse_symbol("0", 0),
974 Error(ParseError
::Symbol(
975 Box
::new(ParseError
::Unexpected('
0'
, 0)),
980 parse_symbol("()", 0),
981 Error(ParseError
::Symbol(
982 Box
::new(ParseError
::Unexpected('
('
, 0)),
989 fn test_parse_string() {
991 parse_string(r
#""""#, 0),
992 Done("", Sexp
::Str("".into
(), (0, 2)))
995 parse_string(r
#""hello""#, 0),
996 Done("", Sexp
::Str("hello".into
(), (0, 7)))
1000 r
#" "this is a nice string
1001 with
0123 things
in it
""#,
1007 "this is a nice string\nwith 0123 things in it".into
(),
1014 parse_string("", 0),
1015 Error(ParseError
::String(
1016 Box
::new(ParseError
::UnexpectedEof
),
1021 parse_string(r
#""hi"#, 0),
1022 Error(ParseError::String(
1023 Box::new(ParseError::UnexpectedEof),
1030 fn test_parse_char() {
1032 parse_character(r#"#\""#, 0),
1033 Done("", Sexp
::Char('
"', (0, 3)))
1036 parse_character(r#"#\ "#, 0),
1037 Done("", Sexp::Char(' ', (0, 3)))
1040 parse_character(r#" #\\"#, 0),
1041 Done("", Sexp::Char('\\', (2, 5)))
1045 parse_character("", 0),
1046 Error(ParseError::Char(
1047 Box::new(ParseError::UnexpectedEof),
1052 parse_character("#", 0),
1053 Error(ParseError::Char(
1054 Box::new(ParseError::UnexpectedEof),
1059 parse_character("#\\", 0),
1060 Error(ParseError::Char(
1061 Box::new(ParseError::UnexpectedEof),
1066 parse_character("a", 0),
1067 Error(ParseError::Char(
1068 Box::new(ParseError::Unexpected('a', 0)),
1075 fn test_parse_comments() {
1080 ;hey
;how are you doing?
1083 (vec![Sexp::List(vec![], (0, 42))], None)