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