cairo_lang_parser/
utils.rs1use 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 })
74 .intern(self);
75 get_syntax_root_and_diagnostics(self, file, content.to_string().as_str())
76 }
77
78 pub fn parse_token_stream(
82 &self,
83 token_stream: &dyn ToPrimitiveTokenStream<Iter = impl Iterator<Item = PrimitiveToken>>,
84 ) -> (SyntaxNode, Diagnostics<ParserDiagnostic>) {
85 let (content, _offset) = primitive_token_stream_content_and_offset(token_stream);
86 let file_id = FileLongId::Virtual(VirtualFile {
87 parent: Default::default(),
88 name: "token_stream_file_parser_input".into(),
89 content: content.into(),
90 code_mappings: Default::default(),
91 kind: FileKind::Module,
92 })
93 .intern(self);
94 let mut diagnostics = DiagnosticsBuilder::default();
95
96 (
97 Parser::parse_token_stream(self, &mut diagnostics, file_id, token_stream)
98 .as_syntax_node(),
99 diagnostics.build(),
100 )
101 }
102
103 pub fn parse_token_stream_expr(
106 &self,
107 token_stream: &dyn ToPrimitiveTokenStream<Iter = impl Iterator<Item = PrimitiveToken>>,
108 ) -> (SyntaxNode, Diagnostics<ParserDiagnostic>) {
109 let file_id = FileLongId::Virtual(VirtualFile {
110 parent: Default::default(),
111 name: "token_stream_expr_parser_input".into(),
112 content: Default::default(),
113 code_mappings: Default::default(),
114 kind: FileKind::Module,
115 })
116 .intern(self);
117 let mut diagnostics = DiagnosticsBuilder::default();
118
119 (
120 Parser::parse_token_stream_expr(self, &mut diagnostics, file_id, token_stream)
121 .as_syntax_node(),
122 diagnostics.build(),
123 )
124 }
125}
126
127pub fn get_syntax_root_and_diagnostics_from_file(
129 db: &SimpleParserDatabase,
130 cairo_filepath: PathBuf,
131) -> (SyntaxNode, Diagnostics<ParserDiagnostic>) {
132 let file_id = FileId::new(db, cairo_filepath);
133 let contents = db.file_content(file_id).unwrap();
134 get_syntax_root_and_diagnostics(db, file_id, &contents)
135}
136
137pub fn get_syntax_root_and_diagnostics(
139 db: &SimpleParserDatabase,
140 file_id: FileId,
141 contents: &str,
142) -> (SyntaxNode, Diagnostics<ParserDiagnostic>) {
143 let (syntax_file, diagnostics) = get_syntax_file_and_diagnostics(db, file_id, contents);
144 (syntax_file.as_syntax_node(), diagnostics)
145}
146
147pub fn get_syntax_file_and_diagnostics(
149 db: &SimpleParserDatabase,
150 file_id: FileId,
151 contents: &str,
152) -> (SyntaxFile, Diagnostics<ParserDiagnostic>) {
153 let mut diagnostics = DiagnosticsBuilder::default();
154 let syntax_file = Parser::parse_file(db, &mut diagnostics, file_id, contents);
155 (syntax_file, diagnostics.build())
156}
157
158pub(crate) fn primitive_token_stream_content_and_offset(
161 token_stream: &dyn ToPrimitiveTokenStream<Iter = impl Iterator<Item = PrimitiveToken>>,
162) -> (String, Option<TextOffset>) {
163 let mut primitive_stream = token_stream.to_primitive_token_stream();
164 let Some(first) = primitive_stream.next() else {
165 return ("".into(), None);
166 };
167 let start_offset = first
168 .span
169 .as_ref()
170 .map(|s| TextOffset::default().add_width(TextWidth::new_for_testing(s.start as u32)));
171 (chain!([first], primitive_stream).map(|t| t.content).collect(), start_offset)
172}