1use std::ops::{Deref, DerefMut};
2
3use indexmap::IndexMap;
4
5use plotnik_core::{Interner, NodeFieldId, NodeTypeId, Symbol};
6use plotnik_langs::Lang;
7
8use super::{SourceId, SourceMap};
9use crate::Diagnostics;
10use crate::analyze::link;
11use crate::analyze::symbol_table::{SymbolTable, resolve_names};
12use crate::analyze::type_check::{self, Arity, TypeContext};
13use crate::analyze::validation::{
14 validate_alt_kinds, validate_anchors, validate_empty_constructs, validate_predicates,
15};
16use crate::analyze::{dependencies, validate_recursion};
17use crate::parser::{Parser, Root, SyntaxNode, lex};
18
19const DEFAULT_QUERY_PARSE_FUEL: u32 = 1_000_000;
20const DEFAULT_QUERY_PARSE_MAX_DEPTH: u32 = 4096;
21
22pub type AstMap = IndexMap<SourceId, Root>;
23
24pub struct QueryConfig {
25 pub query_parse_fuel: u32,
26 pub query_parse_max_depth: u32,
27}
28
29pub struct QueryBuilder {
30 source_map: SourceMap,
31 config: QueryConfig,
32}
33
34impl QueryBuilder {
35 pub fn new(source_map: SourceMap) -> Self {
36 let config = QueryConfig {
37 query_parse_fuel: DEFAULT_QUERY_PARSE_FUEL,
38 query_parse_max_depth: DEFAULT_QUERY_PARSE_MAX_DEPTH,
39 };
40
41 Self { source_map, config }
42 }
43
44 pub fn one_liner(src: &str) -> Self {
45 let source_map = SourceMap::one_liner(src);
46 Self::new(source_map)
47 }
48
49 pub fn with_query_parse_fuel(mut self, fuel: u32) -> Self {
50 self.config.query_parse_fuel = fuel;
51 self
52 }
53
54 pub fn with_query_parse_recursion_limit(mut self, limit: u32) -> Self {
55 self.config.query_parse_max_depth = limit;
56 self
57 }
58
59 pub fn parse(self) -> crate::Result<QueryParsed> {
60 let mut ast = IndexMap::new();
61 let mut diag = Diagnostics::new();
62 let mut total_fuel_consumed = 0u32;
63
64 for source in self.source_map.iter() {
65 let tokens = lex(source.content);
66 let parser = Parser::new(
67 source.content,
68 source.id,
69 tokens,
70 &mut diag,
71 self.config.query_parse_fuel,
72 self.config.query_parse_max_depth,
73 );
74
75 let res = parser.parse()?;
76
77 validate_alt_kinds(source.id, &res.ast, &mut diag);
78 validate_anchors(source.id, &res.ast, &mut diag);
79 validate_empty_constructs(source.id, &res.ast, &mut diag);
80 validate_predicates(source.id, source.content, &res.ast, &mut diag);
81 total_fuel_consumed = total_fuel_consumed.saturating_add(res.fuel_consumed);
82 ast.insert(source.id, res.ast);
83 }
84
85 Ok(QueryParsed {
86 source_map: self.source_map,
87 diag,
88 ast_map: ast,
89 fuel_consumed: total_fuel_consumed,
90 })
91 }
92}
93
94#[derive(Debug)]
95pub struct QueryParsed {
96 source_map: SourceMap,
97 ast_map: AstMap,
98 diag: Diagnostics,
99 fuel_consumed: u32,
100}
101
102impl QueryParsed {
103 pub fn query_parser_fuel_consumed(&self) -> u32 {
104 self.fuel_consumed
105 }
106}
107
108impl QueryParsed {
109 pub fn analyze(mut self) -> QueryAnalyzed {
110 let mut interner = Interner::new();
112
113 let symbol_table = resolve_names(&self.source_map, &self.ast_map, &mut self.diag);
115
116 let dependency_analysis = dependencies::analyze_dependencies(&symbol_table, &mut interner);
117 validate_recursion(
118 &dependency_analysis,
119 &self.ast_map,
120 &symbol_table,
121 &mut self.diag,
122 );
123
124 let type_context = type_check::infer_types(
126 &mut interner,
127 &self.ast_map,
128 &symbol_table,
129 &dependency_analysis,
130 &mut self.diag,
131 );
132
133 QueryAnalyzed {
134 query_parsed: self,
135 interner,
136 symbol_table,
137 type_context,
138 }
139 }
140
141 pub fn source_map(&self) -> &SourceMap {
142 &self.source_map
143 }
144
145 pub fn diagnostics(&self) -> Diagnostics {
146 self.diag.clone()
147 }
148
149 pub fn asts(&self) -> &AstMap {
150 &self.ast_map
151 }
152}
153
154pub type Query = QueryAnalyzed;
155
156#[derive(Clone, Copy)]
161pub struct QueryContext<'q> {
162 pub interner: &'q Interner,
163 pub type_ctx: &'q TypeContext,
164 pub symbol_table: &'q SymbolTable,
165}
166
167pub struct QueryAnalyzed {
168 query_parsed: QueryParsed,
169 interner: Interner,
170 pub symbol_table: SymbolTable,
171 type_context: TypeContext,
172}
173
174impl QueryAnalyzed {
175 pub fn is_valid(&self) -> bool {
176 !self.diag.has_errors()
177 }
178
179 pub fn context(&self) -> QueryContext<'_> {
181 QueryContext {
182 interner: &self.interner,
183 type_ctx: &self.type_context,
184 symbol_table: &self.symbol_table,
185 }
186 }
187
188 pub fn get_arity(&self, node: &SyntaxNode) -> Option<Arity> {
189 use crate::parser::ast;
190
191 if let Some(expr) = ast::Expr::cast(node.clone()) {
193 return self.type_context.get_arity(&expr);
194 }
195
196 if let Some(root) = ast::Root::cast(node.clone()) {
198 return Some(if root.defs().nth(1).is_some() {
199 Arity::Many
200 } else {
201 Arity::One
202 });
203 }
204
205 if let Some(def) = ast::Def::cast(node.clone()) {
207 return def.body().and_then(|b| self.type_context.get_arity(&b));
208 }
209
210 if let Some(branch) = ast::Branch::cast(node.clone()) {
212 return branch.body().and_then(|b| self.type_context.get_arity(&b));
213 }
214
215 None
216 }
217
218 pub fn type_context(&self) -> &TypeContext {
219 &self.type_context
220 }
221
222 pub fn interner(&self) -> &Interner {
223 &self.interner
224 }
225
226 pub fn link(mut self, lang: &Lang) -> LinkedQuery {
227 let mut output = link::LinkOutput::default();
228
229 link::link(
230 &mut self.interner,
231 lang,
232 &self.query_parsed.source_map,
233 &self.query_parsed.ast_map,
234 &self.symbol_table,
235 &mut output,
236 &mut self.query_parsed.diag,
237 );
238
239 LinkedQuery {
240 inner: self,
241 linking: output,
242 }
243 }
244}
245
246impl Deref for QueryAnalyzed {
247 type Target = QueryParsed;
248
249 fn deref(&self) -> &Self::Target {
250 &self.query_parsed
251 }
252}
253
254impl DerefMut for QueryAnalyzed {
255 fn deref_mut(&mut self) -> &mut Self::Target {
256 &mut self.query_parsed
257 }
258}
259
260impl TryFrom<&str> for QueryAnalyzed {
261 type Error = crate::Error;
262
263 fn try_from(src: &str) -> crate::Result<Self> {
264 Ok(QueryBuilder::new(SourceMap::one_liner(src))
265 .parse()?
266 .analyze())
267 }
268}
269
270pub struct LinkedQuery {
271 inner: QueryAnalyzed,
272 linking: link::LinkOutput,
273}
274
275impl LinkedQuery {
276 pub fn interner(&self) -> &Interner {
277 &self.inner.interner
278 }
279
280 pub fn node_type_ids(&self) -> &IndexMap<Symbol, NodeTypeId> {
281 &self.linking.node_type_ids
282 }
283
284 pub fn node_field_ids(&self) -> &IndexMap<Symbol, NodeFieldId> {
285 &self.linking.node_field_ids
286 }
287
288 pub fn emit(&self) -> Result<Vec<u8>, crate::emit::EmitError> {
290 if !self.is_valid() {
291 return Err(crate::emit::EmitError::InvalidQuery);
292 }
293 crate::emit::emit(self)
294 }
295}
296
297impl Deref for LinkedQuery {
298 type Target = QueryAnalyzed;
299
300 fn deref(&self) -> &Self::Target {
301 &self.inner
302 }
303}
304
305impl DerefMut for LinkedQuery {
306 fn deref_mut(&mut self) -> &mut Self::Target {
307 &mut self.inner
308 }
309}