1mod atoms;
16mod forms;
17mod literals;
18mod strings;
19mod whitespace;
20
21#[cfg(test)]
22mod boolean_tests;
23#[cfg(test)]
24mod tests;
25
26use tracing::debug;
27use winnow::combinator::alt;
28use winnow::error::{AddContext, ContextError, ErrMode, ModalResult, StrContext, StrContextValue};
29use winnow::prelude::*;
30
31use crate::ast::{Annotation, Expr, Program};
32use crate::error::{Error, Result};
33
34pub struct Reader;
35
36impl Reader {
37 pub fn parse(input: &str) -> Result<Program> {
38 debug!(input_len = input.len(), "parse start");
39 let mut remaining = input;
40 let (exprs, annotations) = parse_program(&mut remaining).map_err(|e| {
41 let offset = input.len() - remaining.len();
42 Error::parse(format_parse_error(e), input, offset..offset + 1)
43 })?;
44 debug!(
45 expr_count = exprs.len(),
46 annotation_count = annotations.len(),
47 "parse complete"
48 );
49 Ok(Program::with_annotations(exprs, annotations))
50 }
51
52 #[must_use]
53 pub fn is_incomplete(input: &str) -> bool {
54 let mut remaining = input;
55 parse_program(&mut remaining).is_err() && remaining.is_empty()
56 }
57
58 pub fn parse_expr(input: &str) -> Result<Expr> {
59 let mut remaining = input;
60 parse_expr(&mut remaining).map_err(|e| {
61 let offset = input.len() - remaining.len();
62 Error::parse(format_parse_error(e), input, offset..offset + 1)
63 })
64 }
65}
66
67fn format_parse_error(err: ErrMode<ContextError>) -> String {
68 match err {
69 ErrMode::Backtrack(ctx) | ErrMode::Cut(ctx) => format_context_error(&ctx),
70 ErrMode::Incomplete(_) => "unexpected end of input".to_string(),
71 }
72}
73
74fn format_context_error(ctx: &ContextError) -> String {
75 if let Some(cause) = ctx.cause() {
76 return cause.to_string();
77 }
78 let mut parts = Vec::new();
79 for c in ctx.context() {
80 match c {
81 StrContext::Label(label) => parts.push(format!("in {label}")),
82 StrContext::Expected(StrContextValue::Description(desc)) => {
83 parts.push(format!("expected {desc}"));
84 }
85 StrContext::Expected(StrContextValue::CharLiteral(c)) => {
86 parts.push(format!("expected '{c}'"));
87 }
88 StrContext::Expected(StrContextValue::StringLiteral(s)) => {
89 parts.push(format!("expected \"{s}\""));
90 }
91 _ => {}
92 }
93 }
94 if parts.is_empty() {
95 "parse error".to_string()
96 } else {
97 parts.join(", ")
98 }
99}
100
101fn parse_program(input: &mut &str) -> ModalResult<(Vec<Expr>, Vec<Annotation>)> {
102 let mut annotations = Vec::new();
103 whitespace::skip_ws_and_comments(input, &mut annotations)?;
104
105 let mut exprs = Vec::new();
106 loop {
107 if input.is_empty() {
108 break;
109 }
110 let expr = parse_expr(input)?;
111 exprs.push(expr);
112 whitespace::skip_ws_and_comments(input, &mut annotations)?;
113 }
114
115 Ok((exprs, annotations))
116}
117
118const MAX_READ_DEPTH: usize = 64;
130
131thread_local! {
132 static READ_DEPTH: std::cell::Cell<usize> = const { std::cell::Cell::new(0) };
133}
134
135struct DepthGuard;
137
138impl Drop for DepthGuard {
139 fn drop(&mut self) {
140 READ_DEPTH.with(|d| d.set(d.get().saturating_sub(1)));
141 }
142}
143
144pub(super) fn parse_expr(input: &mut &str) -> ModalResult<Expr> {
145 let depth = READ_DEPTH.with(|d| {
146 let next = d.get() + 1;
147 d.set(next);
148 next
149 });
150 let _guard = DepthGuard;
151 if depth > MAX_READ_DEPTH {
152 return Err(ErrMode::Cut(
153 ContextError::new()
154 .add_context(input, &input.checkpoint(), StrContext::Label("expression"))
155 .add_context(
156 input,
157 &input.checkpoint(),
158 StrContext::Expected(StrContextValue::Description(
159 "nesting within the reader depth limit",
160 )),
161 ),
162 ));
163 }
164
165 let expr = alt((
166 alt((
167 literals::parse_hash_literal,
168 literals::parse_number,
169 strings::parse_string,
170 forms::parse_quote,
171 )),
172 alt((
173 forms::parse_quasiquote,
174 forms::parse_unquote,
175 forms::parse_list,
176 atoms::parse_keyword,
177 atoms::parse_symbol,
178 )),
179 ))
180 .parse_next(input)?;
181 debug!(expr = ?expr, "parsed expression");
182 Ok(expr)
183}