Skip to main content

m2_syn/
parsed_file.rs

1//! Convenient ownership and projection of one parsed Macaulay2 source file.
2//!
3//! [`ParsedFile`] is the high-level entry point for applications that do not
4//! need to assemble the lexer and parser pipeline themselves. It owns the
5//! generated typed [`SourceFile`] and exposes fresh projections of that tree as
6//! tokens, cells, normalized source, or indented debug output. The typed CST is
7//! the sole mutable representation, so changing it cannot leave a cached token
8//! stream out of sync.
9
10use std::fmt::{Display, Formatter};
11#[cfg(feature = "pretty")]
12use std::io;
13
14#[cfg(feature = "pretty")]
15use crate::pretty::{
16    PrettyOptions, PrettyReport, PrettyTree, format_tokens, format_tree, print_report,
17};
18use crate::{
19    CellStream, Lex, NativeParseError, SourceFile, Span, Spanned, TokenStream, parse_native,
20};
21#[cfg(feature = "tree-sitter")]
22use crate::{ParseError, parse_file};
23
24/// An owned typed CST together with the original input.
25///
26/// With the `tree-sitter` feature, `from_source` selects that adapter;
27/// [`ParsedFile::from_source_native`] always selects the direct precedence
28/// parser. After parsing, callers may mutate [`Self::cst_mut`] or use
29/// [`Self::edit`], then project the resulting tree through
30/// [`Self::token_stream`] or [`Self::to_source`].
31///
32/// The original string is retained as an immutable snapshot for diagnostics.
33/// Cell and source-file comment ownership is retained by the typed graph;
34/// source-backed layout is recovered from spans during emission.
35#[derive(Debug)]
36pub struct ParsedFile {
37    /// The original source.
38    original_source: String,
39    /// The CST.
40    cst: SourceFile,
41}
42
43impl ParsedFile {
44    /// Parses source with the Tree-sitter adapter.
45    ///
46    /// # Errors
47    ///
48    /// Returns [`ParseError`] when Tree-sitter rejects the source or typed
49    /// migration fails.
50    #[cfg(feature = "tree-sitter")]
51    pub fn from_source(source: impl Into<String>) -> Result<Self, ParseError> {
52        let original_source = source.into();
53        let cst = parse_file(&original_source)?;
54        Ok(Self {
55            original_source,
56            cst,
57        })
58    }
59
60    /// Parses source with the native precedence parser.
61    ///
62    /// # Errors
63    ///
64    /// Returns [`NativeParseError`] when lexing or native parsing fails.
65    pub fn from_source_native(source: impl Into<String>) -> Result<Self, NativeParseError> {
66        let original_source = source.into();
67        let cst = parse_native(&original_source)?;
68        Ok(Self {
69            original_source,
70            cst,
71        })
72    }
73
74    /// Returns the input snapshot exactly as supplied to the parser.
75    pub fn original_source(&self) -> &str {
76        &self.original_source
77    }
78
79    /// Borrows the generated typed CST root.
80    pub fn cst(&self) -> &SourceFile {
81        &self.cst
82    }
83
84    /// Mutably borrows the generated typed CST root.
85    pub fn cst_mut(&mut self) -> &mut SourceFile {
86        &mut self.cst
87    }
88
89    /// Applies an edit and returns the file for further method chaining.
90    pub fn edit(mut self, edit: impl FnOnce(&mut SourceFile)) -> Self {
91        edit(&mut self.cst);
92        self
93    }
94
95    /// Consumes the facade and returns its generated typed CST.
96    pub fn into_cst(self) -> SourceFile {
97        self.cst
98    }
99
100    /// Flattens the current CST into one token stream.
101    pub fn token_stream(&self) -> TokenStream {
102        self.cst.to_token_stream()
103    }
104
105    /// Projects the current CST as independently parseable cells.
106    ///
107    /// This is the normal macro-facing representation after tokenisation. Use
108    /// [`Self::token_stream`] when a function-like transformation intentionally
109    /// needs the less structured lexical representation.
110    pub fn cell_stream(&self) -> CellStream {
111        CellStream::from_source_file(&self.cst)
112    }
113
114    /// Emits the current CST as normalized Macaulay2 source.
115    pub fn to_source(&self) -> String {
116        self.cell_stream().to_string()
117    }
118
119    /// Formats the current raw token representation as a recursively flattened
120    /// table with explicit group boundaries.
121    #[cfg(feature = "pretty")]
122    pub fn pretty_tokens(&self) -> String {
123        format_tokens(&self.token_stream(), PrettyOptions::PLAIN)
124    }
125
126    /// Formats the complete generated typed CST as a Unicode tree.
127    #[cfg(feature = "pretty")]
128    pub fn pretty_cst(&self) -> String {
129        format_tree(&self.cst.pretty_tree(), PrettyOptions::PLAIN)
130    }
131
132    /// Builds a combined source, flat-token, and typed-CST inspection report.
133    #[cfg(feature = "pretty")]
134    pub fn pretty_report(&self) -> PrettyReport<'_> {
135        PrettyReport::new(
136            &self.original_source,
137            self.token_stream(),
138            self.cst.pretty_tree(),
139        )
140    }
141
142    /// Prints the combined report to standard output, enabling ANSI styling
143    /// only when standard output is an interactive terminal.
144    ///
145    /// # Errors
146    ///
147    /// Returns an I/O error when standard output cannot be written.
148    #[cfg(feature = "pretty")]
149    pub fn print_pretty(&self) -> io::Result<()> {
150        print_report(self.pretty_report())
151    }
152}
153
154impl AsRef<SourceFile> for ParsedFile {
155    fn as_ref(&self) -> &SourceFile {
156        self.cst()
157    }
158}
159
160impl AsMut<SourceFile> for ParsedFile {
161    fn as_mut(&mut self) -> &mut SourceFile {
162        self.cst_mut()
163    }
164}
165
166impl Display for ParsedFile {
167    fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
168        self.cell_stream().fmt(formatter)
169    }
170}
171
172impl Spanned for ParsedFile {
173    fn span(&self) -> Span {
174        self.cst.span()
175    }
176}
177
178impl Lex for ParsedFile {
179    fn to_tokens(&self, output: &mut TokenStream) {
180        self.cst.to_tokens(output);
181    }
182}