microcad_lang_parse/parser/
parsers.rs1use crate::ast;
7
8use crate::parser::{ParserInput, RichError, helpers::ParserExt};
9use crate::tokens::Token;
10use chumsky::extra::Full;
11use chumsky::inspector::Inspector;
12use chumsky::{IterParser, Parser, select_ref};
13
14pub type PInput<'a> = ParserInput<'a, 'a>;
16
17pub type PError<'a, S, Ctx> = Full<RichError<'a>, S, Ctx>;
19
20pub trait PInspector<'a>: Inspector<'a, PInput<'a>> + Default + Clone + 'static {}
22
23impl<'a, T> PInspector<'a> for T where T: Inspector<'a, PInput<'a>> + Default + Clone + 'static {}
24
25pub fn unit<'tokens, S, Ctx>()
27-> impl Parser<'tokens, PInput<'tokens>, ast::Unit, PError<'tokens, S, Ctx>>
28where
29 S: PInspector<'tokens>,
30 Ctx: 'tokens,
31{
32 select_ref! {
33 Token::Identifier(ident) = e => ast::Unit {
34 span: e.span(),
35 name: ident.as_ref().into()
36 },
37 Token::Unit(unit) = e => ast::Unit {
38 span: e.span(),
39 name: unit.as_ref().into()
40 },
41 Token::SigilQuote = e => ast::Unit {
42 span: e.span(),
43 name: r#"""#.into()
44 },
45 }
46 .labelled("unit")
47}
48
49pub fn literal<'tokens, S, Ctx>()
51-> impl Parser<'tokens, PInput<'tokens>, ast::Literal, PError<'tokens, S, Ctx>>
52where
53 S: PInspector<'tokens>,
54 Ctx: 'tokens,
55{
56 use microcad_lang_base::ToCompactString;
57 use std::str::FromStr;
58
59 {
60 let single_value = select_ref! {
61 Token::LiteralFloat(x) = e => {
62 match f64::from_str(x) {
63 Ok(value) => ast::LiteralKind::Float(ast::FloatLiteral {
64 value,
65 raw: x.to_compact_string(),
66 span: e.span(),
67 }),
68 Err(err) => ast::LiteralKind::Error(ast::LiteralError {
69 span: e.span(),
70 kind: err.into(),
71 })
72 }
73 },
74 Token::LiteralInt(x) = e => {
75 match i64::from_str(x) {
76 Ok(value) => ast::LiteralKind::Integer(ast::IntegerLiteral {
77 value,
78 raw: x.to_compact_string(),
79 span: e.span(),
80 }),
81 Err(err) => ast::LiteralKind::Error(ast::LiteralError {
82 span: e.span(),
83 kind: err.into(),
84 })
85 }
86 },
87 Token::LiteralString(content) = e => {
88 ast::LiteralKind::String(ast::StringLiteral {
89 span: e.span(),
90 content: content.as_ref().into(),
91 })
92 },
93 Token::LiteralBool(value) = e => {
94 ast::LiteralKind::Bool(ast::BoolLiteral {
95 span: e.span(),
96 value: *value,
97 })
98 },
99 }
100 .boxed();
101
102 single_value
103 .then(unit().or_not())
104 .with_extras()
105 .try_map_with(|((literal, ty), extras), e| {
106 let literal = match (literal, ty) {
107 (ast::LiteralKind::Float(float), Some(unit)) => {
108 ast::LiteralKind::Quantity(ast::QuantityLiteral {
109 span: e.span(),
110 value: float.value,
111 raw: float.raw,
112 unit,
113 })
114 }
115 (ast::LiteralKind::Integer(int), Some(unit)) => {
116 ast::LiteralKind::Quantity(ast::QuantityLiteral {
117 span: e.span(),
118 value: int.value as f64,
119 raw: int.raw,
120 unit,
121 })
122 }
123 (_, Some(_)) => ast::LiteralKind::Error(ast::LiteralError {
124 span: e.span(),
125 kind: ast::LiteralErrorKind::Untypable,
126 }),
127 (literal, None) => literal,
128 };
129 Ok(ast::Literal {
130 span: e.span(),
131 literal,
132 extras,
133 })
134 })
135 .labelled("literal")
136 .boxed()
137 }
138}
139
140pub fn comment<'tokens, S, Ctx>()
142-> impl Parser<'tokens, PInput<'tokens>, ast::Comment, PError<'tokens, S, Ctx>>
143where
144 S: PInspector<'tokens>,
145 Ctx: 'tokens,
146{
147 let single_line_comments = select_ref! {
148 Token::SingleLineComment(comment) => comment
149 }
150 .map_with(|line, e| ast::Comment {
151 span: e.span(),
152 inner: ast::CommentInner::SingleLine(line.to_string()),
153 })
154 .boxed();
155 let multi_line = select_ref! {
156 Token::MultiLineComment(comment) = e => ast::Comment {
157 span: e.span(),
158 inner: ast::CommentInner::MultiLine(comment.to_string())
159 }
160 };
161
162 single_line_comments
163 .or(multi_line)
164 .labelled("comment")
165 .boxed()
166}
167
168pub fn whitespace<'tokens, S, Ctx>()
170-> impl Parser<'tokens, PInput<'tokens>, String, PError<'tokens, S, Ctx>> + 'tokens + Clone
171where
172 S: PInspector<'tokens>,
173 Ctx: 'tokens,
174{
175 select_ref! {
176 Token::Whitespace(s) => s.to_string(),
177 }
178 .labelled("whitespace")
179 .boxed()
180}
181
182pub fn leading_extras<'tokens, S, Ctx>()
184-> impl Parser<'tokens, PInput<'tokens>, ast::LeadingExtras, PError<'tokens, S, Ctx>>
185where
186 S: PInspector<'tokens>,
187 Ctx: 'tokens,
188{
189 let whitespace =
191 select_ref! { Token::Whitespace(s) => ast::ItemExtra::Whitespace(s.to_string()) };
192 let comment = comment().map(ast::ItemExtra::Comment);
193
194 comment
195 .or(whitespace)
196 .repeated()
197 .collect::<Vec<_>>()
198 .map(ast::LeadingExtras)
199 .boxed()
200}
201
202pub fn trailing_extras<'tokens, S, Ctx>()
204-> impl Parser<'tokens, PInput<'tokens>, ast::TrailingExtras, PError<'tokens, S, Ctx>>
205where
206 S: PInspector<'tokens>,
207 Ctx: 'tokens,
208{
209 let whitespace =
211 select_ref! { Token::Whitespace(s) => ast::ItemExtra::Whitespace(s.to_string()) };
212 let comment = comment().map(ast::ItemExtra::Comment);
213
214 whitespace
215 .or(comment)
216 .repeated()
217 .collect::<Vec<_>>()
218 .map(ast::TrailingExtras)
219 .boxed()
220}