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_filesystem::span::{TextOffset, TextWidth};
7use cairo_lang_primitive_token::{PrimitiveToken, ToPrimitiveTokenStream};
8use cairo_lang_syntax::node::ast::SyntaxFile;
9use cairo_lang_syntax::node::db::{SyntaxDatabase, SyntaxGroup};
10use cairo_lang_syntax::node::{SyntaxNode, TypedSyntaxNode};
11use cairo_lang_utils::{Intern, Upcast};
12use itertools::chain;
13
14use crate::ParserDiagnostic;
15use crate::db::ParserDatabase;
16use crate::parser::Parser;
17
18#[salsa::database(ParserDatabase, SyntaxDatabase, FilesDatabase)]
20pub struct SimpleParserDatabase {
21 storage: salsa::Storage<SimpleParserDatabase>,
22}
23impl salsa::Database for SimpleParserDatabase {}
24impl ExternalFiles for SimpleParserDatabase {}
25impl Default for SimpleParserDatabase {
26 fn default() -> Self {
27 let mut res = Self { storage: Default::default() };
28 init_files_group(&mut res);
29 res
30 }
31}
32
33impl Upcast<dyn SyntaxGroup> for SimpleParserDatabase {
34 fn upcast(&self) -> &(dyn SyntaxGroup + 'static) {
35 self
36 }
37}
38impl Upcast<dyn FilesGroup> for SimpleParserDatabase {
39 fn upcast(&self) -> &(dyn FilesGroup + 'static) {
40 self
41 }
42}
43
44impl SimpleParserDatabase {
45 pub fn parse_virtual(
52 &self,
53 content: impl ToString,
54 ) -> Result<SyntaxNode, Diagnostics<ParserDiagnostic>> {
55 let (node, diagnostics) = self.parse_virtual_with_diagnostics(content);
56 if diagnostics.check_error_free().is_ok() { Ok(node) } else { Err(diagnostics) }
57 }
58
59 pub fn parse_virtual_with_diagnostics(
64 &self,
65 content: impl ToString,
66 ) -> (SyntaxNode, Diagnostics<ParserDiagnostic>) {
67 let file = FileLongId::Virtual(VirtualFile {
68 parent: None,
69 name: "parser_input".into(),
70 content: content.to_string().into(),
71 code_mappings: [].into(),
72 kind: FileKind::Module,
73 original_item_removed: false,
74 })
75 .intern(self);
76 get_syntax_root_and_diagnostics(self, file, content.to_string().as_str())
77 }
78
79 pub fn parse_token_stream(
83 &self,
84 token_stream: &dyn ToPrimitiveTokenStream<Iter = impl Iterator<Item = PrimitiveToken>>,
85 ) -> (SyntaxNode, Diagnostics<ParserDiagnostic>) {
86 let (content, _offset) = primitive_token_stream_content_and_offset(token_stream);
87 let file_id = FileLongId::Virtual(VirtualFile {
88 parent: Default::default(),
89 name: "token_stream_file_parser_input".into(),
90 content: content.into(),
91 code_mappings: Default::default(),
92 kind: FileKind::Module,
93 original_item_removed: false,
94 })
95 .intern(self);
96 let mut diagnostics = DiagnosticsBuilder::default();
97
98 (
99 Parser::parse_token_stream(self, &mut diagnostics, file_id, token_stream)
100 .as_syntax_node(),
101 diagnostics.build(),
102 )
103 }
104
105 pub fn parse_token_stream_expr(
108 &self,
109 token_stream: &dyn ToPrimitiveTokenStream<Iter = impl Iterator<Item = PrimitiveToken>>,
110 ) -> (SyntaxNode, Diagnostics<ParserDiagnostic>) {
111 let (content, offset) = primitive_token_stream_content_and_offset(token_stream);
112 let vfs = VirtualFile {
113 parent: Default::default(),
114 name: "token_stream_expr_parser_input".into(),
115 content: content.into(),
116 code_mappings: Default::default(),
117 kind: FileKind::Module,
118 original_item_removed: false,
119 };
120 let content = vfs.content.clone();
121 let file_id = FileLongId::Virtual(vfs).intern(self);
122 let mut diagnostics = DiagnosticsBuilder::default();
123
124 (
125 Parser::parse_token_stream_expr(self, &mut diagnostics, file_id, &content, offset)
126 .as_syntax_node(),
127 diagnostics.build(),
128 )
129 }
130}
131
132pub fn get_syntax_root_and_diagnostics_from_file(
134 db: &SimpleParserDatabase,
135 cairo_filepath: PathBuf,
136) -> (SyntaxNode, Diagnostics<ParserDiagnostic>) {
137 let file_id = FileId::new(db, cairo_filepath);
138 let contents = db.file_content(file_id).unwrap();
139 get_syntax_root_and_diagnostics(db, file_id, &contents)
140}
141
142pub fn get_syntax_root_and_diagnostics(
144 db: &SimpleParserDatabase,
145 file_id: FileId,
146 contents: &str,
147) -> (SyntaxNode, Diagnostics<ParserDiagnostic>) {
148 let (syntax_file, diagnostics) = get_syntax_file_and_diagnostics(db, file_id, contents);
149 (syntax_file.as_syntax_node(), diagnostics)
150}
151
152pub fn get_syntax_file_and_diagnostics(
154 db: &SimpleParserDatabase,
155 file_id: FileId,
156 contents: &str,
157) -> (SyntaxFile, Diagnostics<ParserDiagnostic>) {
158 let mut diagnostics = DiagnosticsBuilder::default();
159 let syntax_file = Parser::parse_file(db, &mut diagnostics, file_id, contents);
160 (syntax_file, diagnostics.build())
161}
162
163pub(crate) fn primitive_token_stream_content_and_offset(
166 token_stream: &dyn ToPrimitiveTokenStream<Iter = impl Iterator<Item = PrimitiveToken>>,
167) -> (String, Option<TextOffset>) {
168 let mut primitive_stream = token_stream.to_primitive_token_stream();
169 let Some(first) = primitive_stream.next() else {
170 return ("".into(), None);
171 };
172 let start_offset = first
173 .span
174 .as_ref()
175 .map(|s| TextOffset::default().add_width(TextWidth::new_for_testing(s.start as u32)));
176 (chain!([first], primitive_stream).map(|t| t.content).collect(), start_offset)
177}