1mod atoms;
16mod forms;
17mod literals;
18mod strings;
19mod whitespace;
20
21#[cfg(test)]
22mod tests;
23
24use tracing::debug;
25use winnow::combinator::alt;
26use winnow::error::{ContextError, ErrMode, ModalResult, StrContext, StrContextValue};
27use winnow::prelude::*;
28
29use crate::ast::{Annotation, Expr, Program};
30use crate::error::{Error, Result};
31
32pub struct Reader;
33
34impl Reader {
35 pub fn parse(input: &str) -> Result<Program> {
36 debug!(input_len = input.len(), "parse start");
37 let mut remaining = input;
38 let (exprs, annotations) = parse_program(&mut remaining).map_err(|e| {
39 let offset = input.len() - remaining.len();
40 Error::parse(format_parse_error(e), input, offset..offset + 1)
41 })?;
42 debug!(
43 expr_count = exprs.len(),
44 annotation_count = annotations.len(),
45 "parse complete"
46 );
47 Ok(Program::with_annotations(exprs, annotations))
48 }
49
50 #[must_use]
51 pub fn is_incomplete(input: &str) -> bool {
52 let mut remaining = input;
53 parse_program(&mut remaining).is_err() && remaining.is_empty()
54 }
55
56 pub fn parse_expr(input: &str) -> Result<Expr> {
57 let mut remaining = input;
58 parse_expr(&mut remaining).map_err(|e| {
59 let offset = input.len() - remaining.len();
60 Error::parse(format_parse_error(e), input, offset..offset + 1)
61 })
62 }
63}
64
65fn format_parse_error(err: ErrMode<ContextError>) -> String {
66 match err {
67 ErrMode::Backtrack(ctx) | ErrMode::Cut(ctx) => format_context_error(&ctx),
68 ErrMode::Incomplete(_) => "unexpected end of input".to_string(),
69 }
70}
71
72fn format_context_error(ctx: &ContextError) -> String {
73 let mut parts = Vec::new();
74 for c in ctx.context() {
75 match c {
76 StrContext::Label(label) => parts.push(format!("in {label}")),
77 StrContext::Expected(StrContextValue::Description(desc)) => {
78 parts.push(format!("expected {desc}"));
79 }
80 StrContext::Expected(StrContextValue::CharLiteral(c)) => {
81 parts.push(format!("expected '{c}'"));
82 }
83 StrContext::Expected(StrContextValue::StringLiteral(s)) => {
84 parts.push(format!("expected \"{s}\""));
85 }
86 _ => {}
87 }
88 }
89 if parts.is_empty() {
90 "parse error".to_string()
91 } else {
92 parts.join(", ")
93 }
94}
95
96fn parse_program(input: &mut &str) -> ModalResult<(Vec<Expr>, Vec<Annotation>)> {
97 let mut annotations = Vec::new();
98 whitespace::skip_ws_and_comments(input, &mut annotations)?;
99
100 let mut exprs = Vec::new();
101 loop {
102 if input.is_empty() {
103 break;
104 }
105 let expr = parse_expr(input)?;
106 exprs.push(expr);
107 whitespace::skip_ws_and_comments(input, &mut annotations)?;
108 }
109
110 Ok((exprs, annotations))
111}
112
113pub(super) fn parse_expr(input: &mut &str) -> ModalResult<Expr> {
114 let expr = alt((
115 alt((
116 literals::parse_nil,
117 literals::parse_hash_literal,
118 literals::parse_number,
119 strings::parse_string,
120 forms::parse_quote,
121 )),
122 alt((
123 forms::parse_quasiquote,
124 forms::parse_unquote,
125 forms::parse_list,
126 atoms::parse_keyword,
127 atoms::parse_symbol,
128 )),
129 ))
130 .parse_next(input)?;
131 debug!(expr = ?expr, "parsed expression");
132 Ok(expr)
133}