1use chumsky::Parser;
4use ordered_float::OrderedFloat;
5use smol_str::SmolStr;
6use std::borrow::Borrow;
7use std::cmp::Ordering;
8use std::fmt::{self, Debug, Display, Formatter, Write};
9use std::hash::Hash;
10use std::ops::Deref;
11use std::sync::OnceLock;
12use unicode_ident::{is_xid_continue, is_xid_start};
13
14pub use crate::ast::Span;
15use crate::lexer::{Keyword, ShortTypeSpec, Token, TokenParser};
16use crate::parse::{Parse, impl_fromstr_via_parse};
17
18#[derive(Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
20pub struct Spanned<T> {
21 pub value: T,
22 pub span: Span,
23}
24impl<T> Spanned<T> {
25 #[inline]
26 pub fn unspanned(value: T) -> Self {
27 Spanned {
28 value,
29 span: Span::MISSING,
30 }
31 }
32 #[inline]
33 pub fn map<U>(this: Self, func: impl FnOnce(T) -> U) -> Spanned<U> {
34 Spanned {
35 value: func(this.value),
36 span: this.span,
37 }
38 }
39}
40impl<T: Debug> Debug for Spanned<T> {
41 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
42 f.debug_tuple("Spanned")
43 .field(&self.value)
44 .field(&self.span)
45 .finish()
46 }
47}
48impl<T: Display> Display for Spanned<T> {
49 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
50 Display::fmt(&self.value, f)
51 }
52}
53impl<T> From<T> for Spanned<T> {
54 fn from(value: T) -> Self {
55 Spanned {
56 value,
57 span: Span::MISSING,
58 }
59 }
60}
61impl<T> From<(T, Span)> for Spanned<T> {
62 fn from(value: (T, Span)) -> Self {
63 Spanned {
64 value: value.0,
65 span: value.1,
66 }
67 }
68}
69impl<T> Deref for Spanned<T> {
70 type Target = T;
71
72 #[inline]
73 fn deref(&self) -> &Self::Target {
74 &self.value
75 }
76}
77
78macro_rules! opaque_string_wrapper {
82 ($target:ident) => {
83 impl $target {
84 #[inline]
85 pub fn text(&self) -> &'_ str {
86 &self.text
87 }
88 #[inline]
89 pub fn span(&self) -> Span {
90 self.span
91 }
92 }
93 impl From<AstString> for $target {
94 fn from(s: AstString) -> Self {
95 $target::new(s, Span::MISSING)
96 }
97 }
98 impl From<String> for $target {
99 fn from(value: String) -> Self {
100 $target::new(value, Span::MISSING)
101 }
102 }
103 impl From<&str> for $target {
104 fn from(value: &str) -> Self {
105 $target::new(value, Span::MISSING)
106 }
107 }
108 impl From<(AstString, Span)> for $target {
109 #[inline]
110 fn from(value: (AstString, Span)) -> Self {
111 $target::new(value.0, value.1)
112 }
113 }
114 impl<T: Into<AstString>> From<Spanned<T>> for $target {
115 fn from(value: Spanned<T>) -> Self {
116 $target::new(value.value, value.span)
117 }
118 }
119 impl_string_like!($target);
120 };
121}
122#[derive(Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
124pub struct Ident {
125 text: AstString,
126 span: Span,
127}
128impl Ident {
129 #[track_caller]
130 #[inline]
131 pub fn unspanned(text: impl Into<AstString>) -> Self {
132 Self::new(text, Span::MISSING)
133 }
134 #[inline]
143 #[track_caller]
144 pub fn new(text: impl Into<AstString>, span: Span) -> Self {
145 let text = text.into();
146 let mut chars = text.chars();
147 let first = chars
148 .next()
149 .unwrap_or_else(|| panic!("Identifier is empty at {span:?}"));
150 assert!(
151 is_xid_start(first),
152 "Invalid start char {first:?} for ident at {span:?}"
153 );
154 for other in chars {
155 assert!(
156 is_xid_continue(other),
157 "Invalid char {other:?} for ident at {span:?}"
158 );
159 }
160 if let Ok(byte_len) = span.byte_len() {
161 assert_eq!(
162 byte_len,
163 text.len() as u64,
164 "Length of span {span} doesn't match {text:?}"
165 );
166 }
167 Ident { text, span }
168 }
169 #[inline]
170 pub fn is_keyword(&self) -> bool {
171 self.to_keyword().is_some()
172 }
173 #[inline]
174 pub fn to_keyword(&self) -> Option<Keyword> {
175 self.as_str().parse::<Keyword>().ok()
176 }
177 #[inline]
178 pub fn to_short_type_spec(&self) -> Option<ShortTypeSpec> {
179 self.as_str().parse::<ShortTypeSpec>().ok()
180 }
181 #[inline]
187 pub fn as_str(&self) -> &'_ str {
188 &self.text
189 }
190}
191impl From<Spanned<Keyword>> for Ident {
192 fn from(value: Spanned<Keyword>) -> Self {
193 Ident::new(value.text(), value.span)
194 }
195}
196impl From<Keyword> for Ident {
197 fn from(value: Keyword) -> Self {
198 Spanned::unspanned(value).into()
199 }
200}
201opaque_string_wrapper!(Ident);
202impl Display for Ident {
203 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
204 f.write_str(&self.text)
205 }
206}
207impl Parse for Ident {
208 const DESC: &'static str = "identifier";
209 fn parser<'a>() -> impl TokenParser<'a, Self> {
210 chumsky::select!(Token::Ident(ref name) => name.clone()).labelled(Self::DESC)
211 }
212}
213
214#[derive(Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
216pub struct StringLiteral {
217 text: AstString,
218 span: Span,
219}
220impl StringLiteral {
221 #[inline]
222 pub fn unspanned(text: impl Into<AstString>) -> Self {
223 StringLiteral::new(text, Span::MISSING)
224 }
225 #[inline]
226 pub fn new(text: impl Into<AstString>, span: Span) -> Self {
227 StringLiteral {
228 text: text.into(),
229 span,
230 }
231 }
232}
233opaque_string_wrapper!(StringLiteral);
234impl Parse for StringLiteral {
235 const DESC: &'static str = "string literal";
236 fn parser<'a>() -> impl TokenParser<'a, Self> {
237 chumsky::select!(Token::StringLiteral(str) => str).labelled(Self::DESC)
238 }
239}
240impl Display for StringLiteral {
241 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
242 f.write_char('"')?;
243 for c in self.text.chars().flat_map(char::escape_default) {
244 f.write_char(c)?;
245 }
246 f.write_char('"')
247 }
248}
249
250#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Ord, PartialOrd)]
251pub struct NumericLiteral<T: Number> {
252 pub value: T,
253 pub span: Span,
254}
255impl<T: Number> NumericLiteral<T> {
256 #[inline]
257 pub fn unspanned(value: T) -> Self {
258 NumericLiteral {
259 value,
260 span: Span::MISSING,
261 }
262 }
263 #[inline]
264 pub fn span(&self) -> Span {
265 self.span
266 }
267 #[inline]
268 pub fn map_value<U: Number>(self, func: impl FnOnce(T) -> U) -> NumericLiteral<U> {
269 NumericLiteral {
270 value: func(self.value),
271 span: self.span,
272 }
273 }
274}
275impl<T: Number> Display for NumericLiteral<T> {
276 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
277 Display::fmt(&self.value, f)
278 }
279}
280impl<T: Number> From<T> for NumericLiteral<T> {
281 #[inline]
282 fn from(value: T) -> Self {
283 Self::unspanned(value)
284 }
285}
286impl From<f64> for NumericLiteral<OrderedFloat<f64>> {
287 #[inline]
288 fn from(value: f64) -> Self {
289 Self::unspanned(value.into())
290 }
291}
292impl From<f32> for NumericLiteral<OrderedFloat<f32>> {
293 #[inline]
294 fn from(value: f32) -> Self {
295 Self::unspanned(value.into())
296 }
297}
298
299#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
302pub enum FloatPrefix {
303 SinglePrecision,
304 DoublePrecision,
305}
306impl FloatPrefix {
307 pub fn text(&self) -> &'static str {
308 match self {
309 FloatPrefix::SinglePrecision => "s_",
310 FloatPrefix::DoublePrecision => "d_",
311 }
312 }
313}
314impl Display for FloatPrefix {
315 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
316 f.write_str(self.text())
317 }
318}
319type FloatValue = NumericLiteral<OrderedFloat<f64>>;
320#[derive(Clone, Eq, PartialEq, Hash, Debug, Ord, PartialOrd)]
321pub struct FloatLiteral {
322 pub span: Span,
323 pub prefix: Spanned<FloatPrefix>,
324 pub value: FloatValue,
325}
326impl FloatLiteral {
327 #[inline]
328 pub fn span(&self) -> Span {
329 self.span
330 }
331 pub fn single_unspanned(value: impl Into<FloatValue>) -> Self {
333 FloatLiteral {
334 value: value.into(),
335 span: Span::MISSING,
336 prefix: Spanned::from(FloatPrefix::SinglePrecision),
337 }
338 }
339 pub fn double_unspanned(value: impl Into<FloatValue>) -> Self {
341 FloatLiteral {
342 value: value.into(),
343 span: Span::MISSING,
344 prefix: Spanned::from(FloatPrefix::DoublePrecision),
345 }
346 }
347}
348impl Display for FloatLiteral {
349 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
350 write!(f, "{}{}", self.prefix, self.value)
351 }
352}
353impl Parse for FloatLiteral {
354 const DESC: &'static str = "float literal";
355 fn parser<'a>() -> impl TokenParser<'a, Self> {
356 chumsky::select!(Token::Float(literal) => literal).labelled(Self::DESC)
357 }
358}
359impl_fromstr_via_parse!(FloatLiteral);
360
361pub trait Number: Debug + Display + num_traits::Num + Clone {}
363macro_rules! impl_numtype {
364 ($($target:ty),+ $(,)?) => {
365 $(impl Number for $target {})*
366 };
367}
368impl_numtype!(
369 u32,
370 u64,
371 usize,
372 i32,
373 i64,
374 isize,
375 f64,
376 ordered_float::OrderedFloat<f32>,
377 ordered_float::OrderedFloat<f64>,
378 ordered_float::NotNan<f64>,
379 u128,
380 i128,
381);
382
383macro_rules! prefixed_ident_type {
384 ($target:ident, PREFIX = $prefix:literal, DESC = $desc:literal) => {
385 #[derive(Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
386 pub struct $target {
387 ident: Ident,
388 span: Span,
389 }
390 impl $target {
391 pub const PREFIX: char = $prefix;
392 pub(crate) fn label() -> &'static str {
394 static LABEL: OnceLock<Box<str>> = OnceLock::new();
395 &*LABEL.get_or_init(|| {
396 let snake_name: &str = paste3::paste!(stringify!());
397 snake_name.replace('_', " ").into_boxed_str()
398 })
399 }
400 #[track_caller]
401 pub fn unspanned(text: &str) -> Self {
402 Self {
403 ident: Ident::new(text, Span::MISSING),
404 span: Span::MISSING,
405 }
406 }
407 #[inline]
408 pub fn text(&self) -> &'_ str {
409 self.ident.text()
410 }
411 #[inline]
412 pub fn ident(&self) -> &'_ Ident {
413 &self.ident
414 }
415 #[inline]
416 pub fn span(&self) -> Span {
417 self.span
418 }
419 #[inline]
420 #[track_caller]
421 pub fn new(ident: Ident, span: Span) -> Self {
422 let res = Self { ident, span };
423 assert_eq!(res.ident.span().is_missing(), span.is_missing());
424 if !span.is_missing() {
425 assert_eq!(
426 res.ident.span().byte_range().unwrap(),
427 span.slice_byte_indexes(1..).byte_range().unwrap(),
428 "Span for {ident:?} doesn't correspond to {res:?}",
429 ident = res.ident
430 );
431 }
432 res
433 }
434 }
435 impl Parse for $target {
436 const DESC: &'static str = $desc;
437 fn parser<'a>() -> impl TokenParser<'a, Self> {
438 use chumsky::Parser;
439 chumsky::select!(Token::$target(val) => val).labelled(Self::DESC)
440 }
441 }
442 impl_ident_like!($target);
443 impl Display for $target {
444 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
445 f.write_char(Self::PREFIX)?;
446 f.write_str(self.text())
447 }
448 }
449 };
450}
451prefixed_ident_type!(TypeName, PREFIX = ':', DESC = "type name");
452prefixed_ident_type!(GlobalName, PREFIX = '$', DESC = "global name");
453prefixed_ident_type!(TemporaryName, PREFIX = '%', DESC = "temporary name");
454prefixed_ident_type!(BlockName, PREFIX = '@', DESC = "block name");
455
456#[derive(Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
461pub struct AstString(SmolStr);
462impl AstString {
463 #[inline]
468 pub fn from_static(s: &'static str) -> AstString {
469 AstString(SmolStr::new_static(s))
470 }
471 #[inline]
472 pub fn as_str(&self) -> &'_ str {
473 self.0.as_str()
474 }
475}
476impl From<String> for AstString {
477 #[inline]
478 fn from(s: String) -> Self {
479 AstString(s.into())
480 }
481}
482impl From<&str> for AstString {
483 #[inline]
484 fn from(s: &str) -> Self {
485 AstString(s.into())
486 }
487}
488impl From<AstString> for String {
489 #[inline]
490 fn from(value: AstString) -> Self {
491 value.0.into()
492 }
493}
494impl Debug for AstString {
495 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
496 <str as Debug>::fmt(&self.0, f)
497 }
498}
499impl Display for AstString {
500 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
501 f.write_str(self.as_str())
502 }
503}
504impl Deref for AstString {
505 type Target = str;
506 #[inline]
507 fn deref(&self) -> &Self::Target {
508 self.0.as_str()
509 }
510}
511impl Borrow<str> for AstString {
512 #[inline]
513 fn borrow(&self) -> &str {
514 self.0.as_str()
515 }
516}
517impl equivalent::Equivalent<String> for AstString {
518 fn equivalent(&self, other: &String) -> bool {
519 self.as_str() == other.as_str()
520 }
521}
522impl equivalent::Comparable<String> for AstString {
523 fn compare(&self, key: &String) -> Ordering {
524 self.as_str().cmp(key.as_str())
525 }
526}