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 original_source: String,
38 cst: SourceFile,
39}
40
41impl ParsedFile {
42 /// Parses source with the Tree-sitter adapter.
43 ///
44 /// # Errors
45 ///
46 /// Returns [`ParseError`] when Tree-sitter rejects the source or typed
47 /// migration fails.
48 #[cfg(feature = "tree-sitter")]
49 pub fn from_source(source: impl Into<String>) -> Result<Self, ParseError> {
50 let original_source = source.into();
51 let cst = parse_file(&original_source)?;
52 Ok(Self {
53 original_source,
54 cst,
55 })
56 }
57
58 /// Parses source with the native precedence parser.
59 ///
60 /// # Errors
61 ///
62 /// Returns [`NativeParseError`] when lexing or native parsing fails.
63 pub fn from_source_native(source: impl Into<String>) -> Result<Self, NativeParseError> {
64 let original_source = source.into();
65 let cst = parse_native(&original_source)?;
66 Ok(Self {
67 original_source,
68 cst,
69 })
70 }
71
72 /// Returns the input snapshot exactly as supplied to the parser.
73 pub fn original_source(&self) -> &str {
74 &self.original_source
75 }
76
77 /// Borrows the generated typed CST root.
78 pub fn cst(&self) -> &SourceFile {
79 &self.cst
80 }
81
82 /// Mutably borrows the generated typed CST root.
83 pub fn cst_mut(&mut self) -> &mut SourceFile {
84 &mut self.cst
85 }
86
87 /// Applies an edit and returns the file for further method chaining.
88 pub fn edit(mut self, edit: impl FnOnce(&mut SourceFile)) -> Self {
89 edit(&mut self.cst);
90 self
91 }
92
93 /// Consumes the facade and returns its generated typed CST.
94 pub fn into_cst(self) -> SourceFile {
95 self.cst
96 }
97
98 /// Flattens the current CST into one token stream.
99 pub fn token_stream(&self) -> TokenStream {
100 self.cst.to_token_stream()
101 }
102
103 /// Projects the current CST as independently parseable cells.
104 ///
105 /// This is the normal macro-facing representation after tokenisation. Use
106 /// [`Self::token_stream`] when a function-like transformation intentionally
107 /// needs the less structured lexical representation.
108 pub fn cell_stream(&self) -> CellStream {
109 CellStream::from_source_file(&self.cst)
110 }
111
112 /// Emits the current CST as normalized Macaulay2 source.
113 pub fn to_source(&self) -> String {
114 self.cell_stream().to_string()
115 }
116
117 /// Formats the current raw token representation as a recursively flattened
118 /// table with explicit group boundaries.
119 #[cfg(feature = "pretty")]
120 pub fn pretty_tokens(&self) -> String {
121 format_tokens(&self.token_stream(), PrettyOptions::PLAIN)
122 }
123
124 /// Formats the complete generated typed CST as a Unicode tree.
125 #[cfg(feature = "pretty")]
126 pub fn pretty_cst(&self) -> String {
127 format_tree(&self.cst.pretty_tree(), PrettyOptions::PLAIN)
128 }
129
130 /// Builds a combined source, flat-token, and typed-CST inspection report.
131 #[cfg(feature = "pretty")]
132 pub fn pretty_report(&self) -> PrettyReport<'_> {
133 PrettyReport::new(
134 &self.original_source,
135 self.token_stream(),
136 self.cst.pretty_tree(),
137 )
138 }
139
140 /// Prints the combined report to standard output, enabling ANSI styling
141 /// only when standard output is an interactive terminal.
142 ///
143 /// # Errors
144 ///
145 /// Returns an I/O error when standard output cannot be written.
146 #[cfg(feature = "pretty")]
147 pub fn print_pretty(&self) -> io::Result<()> {
148 print_report(self.pretty_report())
149 }
150}
151
152impl AsRef<SourceFile> for ParsedFile {
153 fn as_ref(&self) -> &SourceFile {
154 self.cst()
155 }
156}
157
158impl AsMut<SourceFile> for ParsedFile {
159 fn as_mut(&mut self) -> &mut SourceFile {
160 self.cst_mut()
161 }
162}
163
164impl Display for ParsedFile {
165 fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
166 self.cell_stream().fmt(formatter)
167 }
168}
169
170impl Spanned for ParsedFile {
171 fn span(&self) -> Span {
172 self.cst.span()
173 }
174}
175
176impl Lex for ParsedFile {
177 fn to_tokens(&self, output: &mut TokenStream) {
178 self.cst.to_tokens(output);
179 }
180}