phase_rs/
text.rs

1//! Helpers for parsing and pretty printing.
2
3use std::{
4    fmt::{Debug, Display},
5    ops::Range,
6};
7
8use miette::SourceSpan;
9use pretty::RcDoc;
10use winnow::{
11    LocatingSlice, ModalResult, Parser,
12    ascii::{alphanumeric1, multispace0},
13    combinator::repeat,
14    error::{StrContext, StrContextValue},
15    token::take_until,
16};
17
18/// Trait for types which can be pretty-printed
19pub trait ToDoc {
20    /// Produce an `RcDoc` for pretty-printing.
21    fn to_doc(&self) -> RcDoc<'_>;
22}
23
24/// Trait for types which can be parsed
25pub trait HasParser: Sized {
26    /// Parse an element of this type.
27    fn parser(input: &mut LocatingSlice<&str>) -> ModalResult<Self>;
28}
29
30pub trait Span: Clone + Debug + Into<SourceSpan> {}
31
32impl Span for Range<usize> {}
33
34#[derive(Clone, Debug)]
35pub struct NoSpan;
36
37impl From<NoSpan> for SourceSpan {
38    fn from(_: NoSpan) -> Self {
39        0.into()
40    }
41}
42
43impl Span for NoSpan {}
44
45/// Wraps data of type `T` in a span of type `S`, locating it in the source text.
46/// The span is ignored when printing.
47#[derive(Clone, Debug, PartialEq)]
48pub struct Spanned<S, T> {
49    /// Wrapped data
50    pub inner: T,
51    /// Text span
52    pub span: S,
53}
54
55impl<S, T: ToDoc> ToDoc for Spanned<S, T> {
56    fn to_doc(&self) -> RcDoc<'_> {
57        self.inner.to_doc()
58    }
59}
60
61impl<T> From<T> for Spanned<(), T> {
62    fn from(value: T) -> Self {
63        Spanned {
64            inner: value,
65            span: (),
66        }
67    }
68}
69
70impl<S: Span, T> From<Spanned<S, T>> for SourceSpan {
71    fn from(value: Spanned<S, T>) -> Self {
72        value.span.into()
73    }
74}
75impl<S: Span, T: Clone + Debug> Span for Spanned<S, T> {}
76
77impl<T: HasParser> HasParser for Spanned<Range<usize>, T> {
78    fn parser(input: &mut LocatingSlice<&str>) -> ModalResult<Self> {
79        T::parser
80            .with_span()
81            .map(|(inner, span)| Spanned { inner, span })
82            .parse_next(input)
83    }
84}
85
86/// Parse a comment
87pub fn comment_parser(input: &mut LocatingSlice<&str>) -> ModalResult<()> {
88    (
89        multispace0,
90        repeat::<_, _, (), _, _>(0.., ("//", take_until(0.., "\n"), multispace0).value(())),
91    )
92        .parse_next(input)?;
93    Ok(())
94}
95
96#[derive(Clone, Debug, PartialEq, Eq, Hash)]
97/// An identifier
98pub struct Name(String);
99
100impl Display for Name {
101    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
102        std::fmt::Display::fmt(&self.0, f)
103    }
104}
105
106impl ToDoc for Name {
107    fn to_doc(&self) -> RcDoc<'_> {
108        RcDoc::text(&self.0)
109    }
110}
111
112impl HasParser for Name {
113    fn parser(input: &mut LocatingSlice<&str>) -> ModalResult<Self> {
114        alphanumeric1
115            .map(|s: &str| Name(s.to_owned()))
116            .context(StrContext::Label("identifier"))
117            .context(StrContext::Expected(StrContextValue::Description(
118                "alphanumeric string",
119            )))
120            .parse_next(input)
121    }
122}