cairo_lang_parser/
utils.rs

1use std::path::PathBuf;
2
3use cairo_lang_diagnostics::{Diagnostics, DiagnosticsBuilder};
4use cairo_lang_filesystem::db::{ExternalFiles, FilesDatabase, FilesGroup, init_files_group};
5use cairo_lang_filesystem::ids::{FileId, FileKind, FileLongId, VirtualFile};
6use cairo_lang_syntax::node::ast::SyntaxFile;
7use cairo_lang_syntax::node::db::{SyntaxDatabase, SyntaxGroup};
8use cairo_lang_syntax::node::{SyntaxNode, TypedSyntaxNode};
9use cairo_lang_utils::{Intern, Upcast};
10
11use crate::ParserDiagnostic;
12use crate::db::ParserDatabase;
13use crate::parser::Parser;
14use crate::types::TokenStream;
15
16/// A salsa database for parsing only.
17#[salsa::database(ParserDatabase, SyntaxDatabase, FilesDatabase)]
18pub struct SimpleParserDatabase {
19    storage: salsa::Storage<SimpleParserDatabase>,
20}
21impl salsa::Database for SimpleParserDatabase {}
22impl ExternalFiles for SimpleParserDatabase {}
23impl Default for SimpleParserDatabase {
24    fn default() -> Self {
25        let mut res = Self { storage: Default::default() };
26        init_files_group(&mut res);
27        res
28    }
29}
30
31impl Upcast<dyn SyntaxGroup> for SimpleParserDatabase {
32    fn upcast(&self) -> &(dyn SyntaxGroup + 'static) {
33        self
34    }
35}
36impl Upcast<dyn FilesGroup> for SimpleParserDatabase {
37    fn upcast(&self) -> &(dyn FilesGroup + 'static) {
38        self
39    }
40}
41
42impl SimpleParserDatabase {
43    /// Parses new file and returns its syntax root.
44    ///
45    /// This is similar to [Self::parse_virtual_with_diagnostics], but is more ergonomic in cases
46    /// when exact diagnostics do not matter at the usage place. If the parser has emitted error
47    /// diagnostics, this function will return an error. If no error diagnostics has been
48    /// emitted, the syntax root will be returned.
49    pub fn parse_virtual(
50        &self,
51        content: impl ToString,
52    ) -> Result<SyntaxNode, Diagnostics<ParserDiagnostic>> {
53        let (node, diagnostics) = self.parse_virtual_with_diagnostics(content);
54        if diagnostics.check_error_free().is_ok() { Ok(node) } else { Err(diagnostics) }
55    }
56
57    /// Parses new file and return its syntax root with diagnostics.
58    ///
59    /// This function creates new virtual file with the given content and parses it.
60    /// Diagnostics gathered by the parser are returned alongside the result.
61    pub fn parse_virtual_with_diagnostics(
62        &self,
63        content: impl ToString,
64    ) -> (SyntaxNode, Diagnostics<ParserDiagnostic>) {
65        let file = FileLongId::Virtual(VirtualFile {
66            parent: None,
67            name: "parser_input".into(),
68            content: content.to_string().into(),
69            code_mappings: [].into(),
70            kind: FileKind::Module,
71        })
72        .intern(self);
73        get_syntax_root_and_diagnostics(self, file, content.to_string().as_str())
74    }
75
76    /// Parses a [TokenStream] (based on whole file) and returns its syntax root.
77    /// It's very similar to [Self::parse_virtual_with_diagnostics], but instead of taking a content
78    /// as a string, it takes a [TokenStream].
79    pub fn parse_token_stream(
80        &self,
81        token_stream: &dyn TokenStream,
82    ) -> (SyntaxNode, Diagnostics<ParserDiagnostic>) {
83        let file_id = FileLongId::Virtual(VirtualFile {
84            parent: Default::default(),
85            name: "token_stream_file_parser_input".into(),
86            content: token_stream.as_str().into(),
87            code_mappings: Default::default(),
88            kind: FileKind::Module,
89        })
90        .intern(self);
91        let mut diagnostics = DiagnosticsBuilder::default();
92
93        (
94            Parser::parse_token_stream(self, &mut diagnostics, file_id, token_stream)
95                .as_syntax_node(),
96            diagnostics.build(),
97        )
98    }
99
100    /// Parses a [TokenStream] (based on a single expression).
101    /// It's very similar to the [Self::parse_token_stream].
102    pub fn parse_token_stream_expr(
103        &self,
104        token_stream: &dyn TokenStream,
105    ) -> (SyntaxNode, Diagnostics<ParserDiagnostic>) {
106        let file_id = FileLongId::Virtual(VirtualFile {
107            parent: Default::default(),
108            name: "token_stream_expr_parser_input".into(),
109            content: Default::default(),
110            code_mappings: Default::default(),
111            kind: FileKind::Module,
112        })
113        .intern(self);
114        let mut diagnostics = DiagnosticsBuilder::default();
115
116        (
117            Parser::parse_token_stream_expr(self, &mut diagnostics, file_id, token_stream)
118                .as_syntax_node(),
119            diagnostics.build(),
120        )
121    }
122}
123
124/// Reads a cairo file to the db and return the syntax_root and diagnostic of its parsing.
125pub fn get_syntax_root_and_diagnostics_from_file(
126    db: &SimpleParserDatabase,
127    cairo_filepath: PathBuf,
128) -> (SyntaxNode, Diagnostics<ParserDiagnostic>) {
129    let file_id = FileId::new(db, cairo_filepath);
130    let contents = db.file_content(file_id).unwrap();
131    get_syntax_root_and_diagnostics(db, file_id, &contents)
132}
133
134/// Returns the syntax_root and diagnostic of a file in the db.
135pub fn get_syntax_root_and_diagnostics(
136    db: &SimpleParserDatabase,
137    file_id: FileId,
138    contents: &str,
139) -> (SyntaxNode, Diagnostics<ParserDiagnostic>) {
140    let (syntax_file, diagnostics) = get_syntax_file_and_diagnostics(db, file_id, contents);
141    (syntax_file.as_syntax_node(), diagnostics)
142}
143
144/// Returns the syntax_file and diagnostic of a file in the db.
145pub fn get_syntax_file_and_diagnostics(
146    db: &SimpleParserDatabase,
147    file_id: FileId,
148    contents: &str,
149) -> (SyntaxFile, Diagnostics<ParserDiagnostic>) {
150    let mut diagnostics = DiagnosticsBuilder::default();
151    let syntax_file = Parser::parse_file(db, &mut diagnostics, file_id, contents);
152    (syntax_file, diagnostics.build())
153}