1use rustc_hash::FxHashMap;
2use serde::Serialize;
3use std::mem;
4
5use crate::arena::Arena;
6use crate::typing::{Record, Type};
7use crate::{
8 App, Attrs, Binary, ExprRef, Field, Query, Raw, RecRef, Source, SourceKind, StrRef, Value,
9 error::AnalysisError, token::Operator,
10};
11
12#[derive(Debug, Clone, Serialize)]
22pub struct Typed {
23 pub project: Type,
28
29 #[serde(skip)]
34 pub scope: Scope,
35
36 pub aggregate: bool,
38}
39
40pub type AnalysisResult<A> = Result<A, AnalysisError>;
45
46#[derive(Default)]
52pub struct AnalysisOptions {
53 pub default_scope: Scope,
55 pub default_event_type: Type,
57 pub data_sources: FxHashMap<StrRef, Type>,
63}
64
65#[derive(Default, Clone, Serialize, Debug)]
71pub struct Scope {
72 #[serde(skip_serializing)]
73 entries: FxHashMap<StrRef, Type>,
74}
75
76impl Scope {
77 pub fn is_empty(&self) -> bool {
79 self.entries.is_empty()
80 }
81
82 pub fn declare(&mut self, name: StrRef, tpe: Type) -> bool {
87 self.entries.insert(name, tpe).is_none()
88 }
89
90 pub fn get(&self, name: StrRef) -> Option<Type> {
94 self.entries.get(&name).copied()
95 }
96
97 pub fn get_mut(&mut self, name: StrRef) -> Option<&mut Type> {
101 self.entries.get_mut(&name)
102 }
103
104 pub fn exists(&self, name: StrRef) -> bool {
106 self.entries.contains_key(&name)
107 }
108}
109
110#[derive(Default)]
111struct CheckContext {
112 use_agg_func: bool,
113 use_source_based: bool,
114}
115
116#[derive(Default)]
121pub struct AnalysisContext {
122 pub allow_agg_func: bool,
128
129 pub use_agg_funcs: bool,
131}
132
133pub struct Analysis<'a> {
138 arena: &'a mut Arena,
139 options: &'a AnalysisOptions,
141 prev_scopes: Vec<Scope>,
143 scope: Scope,
145}
146
147impl<'a> Analysis<'a> {
148 pub fn new(arena: &'a mut Arena, options: &'a AnalysisOptions) -> Self {
150 Self {
151 arena,
152 options,
153 prev_scopes: Default::default(),
154 scope: Scope::default(),
155 }
156 }
157
158 pub fn scope(&self) -> &Scope {
166 &self.scope
167 }
168
169 fn enter_scope(&mut self) {
170 if self.scope.is_empty() {
171 return;
172 }
173
174 let prev = mem::take(&mut self.scope);
175 self.prev_scopes.push(prev);
176 }
177
178 fn exit_scope(&mut self) -> Scope {
179 if let Some(prev) = self.prev_scopes.pop() {
180 mem::replace(&mut self.scope, prev)
181 } else {
182 mem::take(&mut self.scope)
183 }
184 }
185
186 #[cfg(test)]
187 pub fn test_declare(&mut self, name: &str, tpe: Type) -> bool {
188 let name = self.arena.strings.alloc_no_case(name);
189 self.scope.declare(name, tpe)
190 }
191
192 pub fn analyze_query(&mut self, query: Query<Raw>) -> AnalysisResult<Query<Typed>> {
219 self.enter_scope();
220
221 let mut sources = Vec::with_capacity(query.sources.len());
222 let mut ctx = AnalysisContext::default();
223
224 for source in query.sources {
225 sources.push(self.analyze_source(source)?);
226 }
227
228 if let Some(expr) = query.predicate.as_ref().copied() {
229 self.analyze_expr(&mut ctx, expr, Type::Bool)?;
230 }
231
232 if let Some(group_by) = &query.group_by {
233 let node = self.arena.exprs.get(group_by.expr);
234 if !matches!(node.value, Value::Access(_) | Value::Id(_)) {
235 return Err(AnalysisError::ExpectFieldLiteral(
236 node.attrs.pos.line,
237 node.attrs.pos.col,
238 ));
239 }
240
241 self.analyze_expr(&mut ctx, group_by.expr, Type::Unspecified)?;
242
243 if let Some(expr) = group_by.predicate {
244 ctx.allow_agg_func = true;
245 ctx.use_agg_funcs = true;
246
247 self.analyze_expr(&mut ctx, expr, Type::Bool)?;
248
249 let node = self.arena.exprs.get(expr);
250 if !self.expect_agg_expr(expr)? {
251 return Err(AnalysisError::ExpectAggExpr(
252 node.attrs.pos.line,
253 node.attrs.pos.col,
254 ));
255 }
256 }
257
258 ctx.allow_agg_func = true;
259 ctx.use_agg_funcs = true;
260 }
261
262 let project = self.analyze_projection(&mut ctx, query.projection)?;
263
264 if let Some(order_by) = &query.order_by {
265 self.analyze_expr(&mut ctx, order_by.expr, Type::Unspecified)?;
266 let node = self.arena.exprs.get(order_by.expr);
267 if query.group_by.is_none() && !matches!(node.value, Value::Access(_) | Value::Id(_)) {
268 return Err(AnalysisError::ExpectFieldLiteral(
269 node.attrs.pos.line,
270 node.attrs.pos.col,
271 ));
272 } else if query.group_by.is_some() {
273 self.expect_agg_func(order_by.expr)?;
274 }
275 }
276
277 let scope = self.exit_scope();
278
279 Ok(Query {
280 attrs: query.attrs,
281 sources,
282 predicate: query.predicate,
283 group_by: query.group_by,
284 order_by: query.order_by,
285 limit: query.limit,
286 projection: query.projection,
287 distinct: query.distinct,
288 meta: Typed {
289 project,
290 scope,
291 aggregate: ctx.use_agg_funcs,
292 },
293 })
294 }
295
296 fn analyze_source(&mut self, source: Source<Raw>) -> AnalysisResult<Source<Typed>> {
297 let kind = self.analyze_source_kind(source.kind)?;
298 let tpe = match &kind {
299 SourceKind::Name(name) => {
300 let tpe = if let Some(tpe) = self.options.data_sources.get(name).copied() {
301 tpe
302 } else {
303 self.options.default_event_type
304 };
305
306 self.arena.types.alloc_type(tpe)
307 }
308
309 SourceKind::Subject(_) => self.arena.types.alloc_type(self.options.default_event_type),
310
311 SourceKind::Subquery(query) => self.projection_type(query),
312 };
313
314 if !self.scope.declare(source.binding.name, tpe) {
315 return Err(AnalysisError::BindingAlreadyExists(
316 source.binding.pos.line,
317 source.binding.pos.col,
318 self.arena.strings.get(source.binding.name).to_owned(),
319 ));
320 }
321
322 Ok(Source {
323 binding: source.binding,
324 kind,
325 })
326 }
327
328 fn analyze_source_kind(&mut self, kind: SourceKind<Raw>) -> AnalysisResult<SourceKind<Typed>> {
329 match kind {
330 SourceKind::Name(n) => Ok(SourceKind::Name(n)),
331 SourceKind::Subject(s) => Ok(SourceKind::Subject(s)),
332 SourceKind::Subquery(query) => {
333 let query = self.analyze_query(*query)?;
334 Ok(SourceKind::Subquery(Box::new(query)))
335 }
336 }
337 }
338
339 fn analyze_projection(
340 &mut self,
341 ctx: &mut AnalysisContext,
342 expr: ExprRef,
343 ) -> AnalysisResult<Type> {
344 let node = self.arena.exprs.get(expr);
345 match node.value {
346 Value::Record(record) => {
347 if self.arena.exprs.rec(record).is_empty() {
348 return Err(AnalysisError::EmptyRecord(
349 node.attrs.pos.line,
350 node.attrs.pos.col,
351 ));
352 }
353
354 ctx.allow_agg_func = true;
355 let tpe = self.analyze_expr(ctx, expr, Type::Unspecified)?;
356 let mut chk_ctx = CheckContext {
357 use_agg_func: ctx.use_agg_funcs,
358 ..Default::default()
359 };
360
361 self.check_projection_on_record(&mut chk_ctx, record)?;
362 Ok(tpe)
363 }
364
365 Value::App(app) => {
366 ctx.allow_agg_func = true;
367
368 let tpe = self.analyze_expr(ctx, expr, Type::Unspecified)?;
369
370 if ctx.use_agg_funcs {
371 let mut chk_ctx = CheckContext {
372 use_agg_func: ctx.use_agg_funcs,
373 ..Default::default()
374 };
375
376 self.check_projection_on_field_expr(&mut chk_ctx, expr)?;
377 } else {
378 self.reject_constant_func(node.attrs, &app)?;
379 }
380
381 Ok(tpe)
382 }
383
384 Value::Id(_) if ctx.use_agg_funcs => Err(AnalysisError::ExpectAggExpr(
385 node.attrs.pos.line,
386 node.attrs.pos.col,
387 )),
388
389 Value::Id(id) => {
390 if let Some(tpe) = self.scope.get(id) {
391 Ok(tpe)
392 } else {
393 Err(AnalysisError::VariableUndeclared(
394 node.attrs.pos.line,
395 node.attrs.pos.col,
396 self.arena.strings.get(id).to_owned(),
397 ))
398 }
399 }
400
401 Value::Access(_) if ctx.use_agg_funcs => Err(AnalysisError::ExpectAggExpr(
402 node.attrs.pos.line,
403 node.attrs.pos.col,
404 )),
405
406 Value::Access(access) => {
407 let mut current = self.arena.exprs.get(access.target);
408
409 loop {
410 match current.value {
411 Value::Id(name) => {
412 if !self.scope.exists(name) {
413 return Err(AnalysisError::VariableUndeclared(
414 current.attrs.pos.line,
415 current.attrs.pos.col,
416 self.arena.strings.get(name).to_owned(),
417 ));
418 }
419
420 break;
421 }
422
423 Value::Access(next) => current = self.arena.exprs.get(next.target),
424 _ => unreachable!(),
425 }
426 }
427
428 self.analyze_expr(ctx, expr, Type::Unspecified)
429 }
430
431 _ => {
432 let tpe = self.project_type(expr);
433
434 Err(AnalysisError::ExpectRecordOrSourcedProperty(
435 node.attrs.pos.line,
436 node.attrs.pos.col,
437 display_type(self.arena, tpe),
438 ))
439 }
440 }
441 }
442
443 fn check_projection_on_record(
444 &mut self,
445 ctx: &mut CheckContext,
446 record: RecRef,
447 ) -> AnalysisResult<()> {
448 for idx in 0..self.arena.exprs.rec(record).len() {
449 let field = self.arena.exprs.rec_get(record, idx);
450
451 self.check_projection_on_field(ctx, &field)?;
452 }
453
454 Ok(())
455 }
456
457 fn check_projection_on_field(
458 &mut self,
459 ctx: &mut CheckContext,
460 field: &Field,
461 ) -> AnalysisResult<()> {
462 self.check_projection_on_field_expr(ctx, field.expr)
463 }
464
465 fn check_projection_on_field_expr(
466 &mut self,
467 ctx: &mut CheckContext,
468 expr: ExprRef,
469 ) -> AnalysisResult<()> {
470 let node = self.arena.exprs.get(expr);
471 match node.value {
472 Value::Number(_) | Value::String(_) | Value::Bool(_) => Ok(()),
473
474 Value::Id(id) => {
475 if self.scope.exists(id) {
476 if ctx.use_agg_func {
477 return Err(AnalysisError::UnallowedAggFuncUsageWithSrcField(
478 node.attrs.pos.line,
479 node.attrs.pos.col,
480 ));
481 }
482
483 ctx.use_source_based = true;
484 }
485
486 Ok(())
487 }
488
489 Value::Array(exprs) => {
490 for idx in self.arena.exprs.vec_idxes(exprs) {
491 let expr = self.arena.exprs.vec_get(exprs, idx);
492
493 self.check_projection_on_field_expr(ctx, expr)?;
494 }
495
496 Ok(())
497 }
498
499 Value::Record(fields) => {
500 for idx in self.arena.exprs.rec_idxes(fields) {
501 let field = self.arena.exprs.rec_get(fields, idx);
502
503 self.check_projection_on_field(ctx, &field)?;
504 }
505
506 Ok(())
507 }
508
509 Value::Access(access) => self.check_projection_on_field_expr(ctx, access.target),
510
511 Value::App(app) => {
512 if let Some(Type::App { aggregate, .. }) = self.options.default_scope.get(app.func)
513 {
514 ctx.use_agg_func |= aggregate;
515
516 if ctx.use_agg_func && ctx.use_source_based {
517 return Err(AnalysisError::UnallowedAggFuncUsageWithSrcField(
518 node.attrs.pos.line,
519 node.attrs.pos.col,
520 ));
521 }
522
523 if aggregate {
524 return self.expect_agg_func(expr);
525 }
526
527 for idx in self.arena.exprs.vec_idxes(app.args) {
528 let arg = self.arena.exprs.vec_get(app.args, idx);
529
530 self.invalidate_agg_func_usage(arg)?;
531 }
532 }
533
534 Ok(())
535 }
536
537 Value::Binary(binary) => {
538 self.check_projection_on_field_expr(ctx, binary.lhs)?;
539 self.check_projection_on_field_expr(ctx, binary.rhs)
540 }
541
542 Value::Unary(unary) => self.check_projection_on_field_expr(ctx, unary.expr),
543 Value::Group(expr) => self.check_projection_on_field_expr(ctx, expr),
544 }
545 }
546
547 fn expect_agg_func(&self, expr: ExprRef) -> AnalysisResult<()> {
548 let node = self.arena.exprs.get(expr);
549 if let Value::App(app) = node.value
550 && let Some(Type::App {
551 aggregate: true, ..
552 }) = self.options.default_scope.get(app.func)
553 {
554 for idx in 0..self.arena.exprs.vec(app.args).len() {
555 let arg = self.arena.exprs.vec_get(app.args, idx);
556
557 self.ensure_agg_param_is_source_bound(arg)?;
558 self.invalidate_agg_func_usage(arg)?;
559 }
560
561 return Ok(());
562 }
563
564 Err(AnalysisError::ExpectAggExpr(
565 node.attrs.pos.line,
566 node.attrs.pos.col,
567 ))
568 }
569
570 fn expect_agg_expr(&self, expr: ExprRef) -> AnalysisResult<bool> {
571 let node = self.arena.exprs.get(expr);
572 match node.value {
573 Value::Id(id) => {
574 if self.scope.exists(id) {
575 return Err(AnalysisError::UnallowedAggFuncUsageWithSrcField(
576 node.attrs.pos.line,
577 node.attrs.pos.col,
578 ));
579 }
580
581 Ok(false)
582 }
583 Value::Group(expr) => self.expect_agg_expr(expr),
584 Value::Binary(binary) => {
585 let lhs = self.expect_agg_expr(binary.lhs)?;
586 let rhs = self.expect_agg_expr(binary.rhs)?;
587
588 if !lhs && !rhs {
589 return Err(AnalysisError::ExpectAggExpr(
590 node.attrs.pos.line,
591 node.attrs.pos.col,
592 ));
593 }
594
595 Ok(true)
596 }
597 Value::Unary(unary) => self.expect_agg_expr(unary.expr),
598 Value::App(_) => {
599 self.expect_agg_func(expr)?;
600 Ok(true)
601 }
602
603 _ => Ok(false),
604 }
605 }
606
607 fn ensure_agg_param_is_source_bound(&self, expr: ExprRef) -> AnalysisResult<()> {
608 let node = self.arena.exprs.get(expr);
609 match node.value {
610 Value::Id(id) if !self.options.default_scope.exists(id) => Ok(()),
611 Value::Access(access) => self.ensure_agg_param_is_source_bound(access.target),
612 Value::Binary(binary) => self.ensure_agg_binary_op_is_source_bound(node.attrs, binary),
613 Value::Unary(unary) => self.ensure_agg_param_is_source_bound(unary.expr),
614
615 _ => Err(AnalysisError::ExpectSourceBoundProperty(
616 node.attrs.pos.line,
617 node.attrs.pos.col,
618 )),
619 }
620 }
621
622 fn ensure_agg_binary_op_is_source_bound(
623 &self,
624 attrs: Attrs,
625 binary: Binary,
626 ) -> AnalysisResult<()> {
627 if !self.ensure_agg_binary_op_branch_is_source_bound(binary.lhs)
628 && !self.ensure_agg_binary_op_branch_is_source_bound(binary.rhs)
629 {
630 return Err(AnalysisError::ExpectSourceBoundProperty(
631 attrs.pos.line,
632 attrs.pos.col,
633 ));
634 }
635
636 Ok(())
637 }
638
639 fn ensure_agg_binary_op_branch_is_source_bound(&self, expr: ExprRef) -> bool {
640 let node = self.arena.exprs.get(expr);
641 match node.value {
642 Value::Id(id) => !self.options.default_scope.exists(id),
643 Value::Array(exprs) => {
644 if self.arena.exprs.vec(exprs).is_empty() {
645 return false;
646 }
647
648 for idx in 0..self.arena.exprs.vec(exprs).len() {
649 let expr = self.arena.exprs.vec_get(exprs, idx);
650
651 if !self.ensure_agg_binary_op_branch_is_source_bound(expr) {
652 return false;
653 }
654 }
655
656 true
657 }
658 Value::Record(fields) => {
659 if self.arena.exprs.rec(fields).is_empty() {
660 return false;
661 }
662
663 for idx in 0..self.arena.exprs.rec(fields).len() {
664 let field = self.arena.exprs.rec_get(fields, idx);
665
666 if !self.ensure_agg_binary_op_branch_is_source_bound(field.expr) {
667 return false;
668 }
669 }
670
671 true
672 }
673
674 Value::Access(access) => {
675 self.ensure_agg_binary_op_branch_is_source_bound(access.target)
676 }
677
678 Value::Binary(binary) => self
679 .ensure_agg_binary_op_is_source_bound(node.attrs, binary)
680 .is_ok(),
681 Value::Unary(unary) => self.ensure_agg_binary_op_branch_is_source_bound(unary.expr),
682 Value::Group(expr) => self.ensure_agg_binary_op_branch_is_source_bound(expr),
683
684 Value::Number(_) | Value::String(_) | Value::Bool(_) | Value::App(_) => false,
685 }
686 }
687
688 fn invalidate_agg_func_usage(&self, expr: ExprRef) -> AnalysisResult<()> {
689 let node = self.arena.exprs.get(expr);
690 match node.value {
691 Value::Number(_)
692 | Value::String(_)
693 | Value::Bool(_)
694 | Value::Id(_)
695 | Value::Access(_) => Ok(()),
696
697 Value::Array(exprs) => {
698 for idx in 0..self.arena.exprs.vec(exprs).len() {
699 let expr = self.arena.exprs.vec_get(exprs, idx);
700
701 self.invalidate_agg_func_usage(expr)?;
702 }
703
704 Ok(())
705 }
706
707 Value::Record(fields) => {
708 for idx in 0..self.arena.exprs.rec(fields).len() {
709 let field = self.arena.exprs.rec_get(fields, idx);
710
711 self.invalidate_agg_func_usage(field.expr)?;
712 }
713
714 Ok(())
715 }
716
717 Value::App(app) => {
718 if let Some(Type::App { aggregate, .. }) = self.options.default_scope.get(app.func)
719 && aggregate
720 {
721 return Err(AnalysisError::WrongAggFunUsage(
722 node.attrs.pos.line,
723 node.attrs.pos.col,
724 self.arena.strings.get(app.func).to_owned(),
725 ));
726 }
727
728 for idx in 0..self.arena.exprs.vec(app.args).len() {
729 let arg = self.arena.exprs.vec_get(app.args, idx);
730 self.invalidate_agg_func_usage(arg)?;
731 }
732
733 Ok(())
734 }
735
736 Value::Binary(binary) => {
737 self.invalidate_agg_func_usage(binary.lhs)?;
738 self.invalidate_agg_func_usage(binary.rhs)
739 }
740
741 Value::Unary(unary) => self.invalidate_agg_func_usage(unary.expr),
742 Value::Group(expr) => self.invalidate_agg_func_usage(expr),
743 }
744 }
745
746 fn reject_constant_func(&self, attrs: Attrs, app: &App) -> AnalysisResult<()> {
747 if self.arena.exprs.vec(app.args).is_empty() {
748 return Err(AnalysisError::ConstantExprInProjectIntoClause(
749 attrs.pos.line,
750 attrs.pos.col,
751 ));
752 }
753
754 let mut errored = None;
755 for idx in 0..self.arena.exprs.vec(app.args).len() {
756 let arg = self.arena.exprs.vec_get(app.args, idx);
757
758 if let Err(e) = self.reject_constant_expr(arg) {
759 if errored.is_none() {
760 errored = Some(e);
761 }
762
763 continue;
764 }
765
766 return Ok(());
768 }
769
770 Err(errored.expect("to be defined at that point"))
771 }
772
773 fn reject_constant_expr(&self, expr: ExprRef) -> AnalysisResult<()> {
774 let node = self.arena.exprs.get(expr);
775 match node.value {
776 Value::Id(id) if self.scope.exists(id) => Ok(()),
777 Value::Array(exprs) => {
778 let mut errored = None;
779 for idx in 0..self.arena.exprs.vec(exprs).len() {
780 let expr = self.arena.exprs.vec_get(exprs, idx);
781
782 if let Err(e) = self.reject_constant_expr(expr) {
783 if errored.is_none() {
784 errored = Some(e);
785 }
786
787 continue;
788 }
789
790 return Ok(());
792 }
793
794 Err(errored.expect("to be defined at that point"))
795 }
796
797 Value::Record(fields) => {
798 let mut errored = None;
799 for idx in 0..self.arena.exprs.rec(fields).len() {
800 let field = self.arena.exprs.rec_get(fields, idx);
801
802 if let Err(e) = self.reject_constant_expr(field.expr) {
803 if errored.is_none() {
804 errored = Some(e);
805 }
806
807 continue;
808 }
809
810 return Ok(());
812 }
813
814 Err(errored.expect("to be defined at that point"))
815 }
816
817 Value::Binary(binary) => self
818 .reject_constant_expr(binary.lhs)
819 .or_else(|e| self.reject_constant_expr(binary.rhs).map_err(|_| e)),
820
821 Value::Access(access) => self.reject_constant_expr(access.target),
822 Value::App(app) => self.reject_constant_func(node.attrs, &app),
823 Value::Unary(unary) => self.reject_constant_expr(unary.expr),
824 Value::Group(expr) => self.reject_constant_expr(expr),
825
826 _ => Err(AnalysisError::ConstantExprInProjectIntoClause(
827 node.attrs.pos.line,
828 node.attrs.pos.col,
829 )),
830 }
831 }
832
833 pub fn analyze_expr(
861 &mut self,
862 ctx: &mut AnalysisContext,
863 expr: ExprRef,
864 mut expect: Type,
865 ) -> AnalysisResult<Type> {
866 let node = self.arena.exprs.get(expr);
867 match node.value {
868 Value::Number(_) => self.arena.type_check(node.attrs, expect, Type::Number),
869 Value::String(_) => self.arena.type_check(node.attrs, expect, Type::String),
870 Value::Bool(_) => self.arena.type_check(node.attrs, expect, Type::Bool),
871
872 Value::Id(id) => {
873 if let Some(tpe) = self.options.default_scope.get(id) {
874 self.arena.type_check(node.attrs, expect, tpe)
875 } else if let Some(tpe) = self.scope.get_mut(id) {
876 *tpe = self.arena.type_check(node.attrs, mem::take(tpe), expect)?;
877
878 Ok(*tpe)
879 } else {
880 Err(AnalysisError::VariableUndeclared(
881 node.attrs.pos.line,
882 node.attrs.pos.col,
883 self.arena.strings.get(id).to_owned(),
884 ))
885 }
886 }
887
888 Value::Array(exprs) => {
889 if matches!(expect, Type::Unspecified) {
890 for idx in self.arena.exprs.vec_idxes(exprs) {
891 let expr = self.arena.exprs.vec_get(exprs, idx);
892
893 expect = self.analyze_expr(ctx, expr, expect)?;
894 }
895
896 return Ok(self.arena.types.alloc_array_of(expect));
897 }
898
899 match expect {
900 Type::Array(expect) => {
901 let mut expect = self.arena.types.get_type(expect);
902 for idx in 0..self.arena.exprs.vec(exprs).len() {
903 let expr = self.arena.exprs.vec_get(exprs, idx);
904 expect = self.analyze_expr(ctx, expr, expect)?;
905 }
906
907 Ok(self.arena.types.alloc_array_of(expect))
908 }
909
910 expect => {
911 let tpe = self.project_type(expr);
912
913 Err(AnalysisError::TypeMismatch(
914 node.attrs.pos.line,
915 node.attrs.pos.col,
916 display_type(self.arena, expect),
917 display_type(self.arena, tpe),
918 ))
919 }
920 }
921 }
922
923 Value::Record(fields) => {
924 if matches!(expect, Type::Unspecified) {
925 let mut record = FxHashMap::default();
926
927 for idx in 0..self.arena.exprs.rec(fields).len() {
928 let field = self.arena.exprs.rec_get(fields, idx);
929
930 record.insert(
931 field.name,
932 self.analyze_expr(ctx, field.expr, Type::Unspecified)?,
933 );
934 }
935
936 return Ok(Type::Record(self.arena.types.alloc_record(record)));
937 }
938
939 if let Type::Record(rec) = expect
940 && self.arena.types.record_len(rec) == self.arena.exprs.rec(fields).len()
941 {
942 for idx in self.arena.exprs.rec_idxes(fields) {
943 let field = self.arena.exprs.rec_get(fields, idx);
944
945 if let Some(tpe) = self.arena.types.record_get(rec, field.name) {
946 let new_tpe = self.analyze_expr(ctx, field.expr, tpe)?;
947 self.arena.types.record_set(rec, field.name, new_tpe);
948 continue;
949 }
950
951 return Err(AnalysisError::FieldUndeclared(
952 field.attrs.pos.line,
953 field.attrs.pos.col,
954 self.arena.strings.get(field.name).to_owned(),
955 ));
956 }
957
958 return Ok(expect);
959 }
960
961 let tpe = self.project_type(expr);
962
963 Err(AnalysisError::TypeMismatch(
964 node.attrs.pos.line,
965 node.attrs.pos.col,
966 display_type(self.arena, expect),
967 display_type(self.arena, tpe),
968 ))
969 }
970
971 Value::Access(_) => Ok(self.analyze_access(node.attrs, expr, expect)?),
972
973 Value::App(app) => {
974 if let Some(tpe) = self.options.default_scope.get(app.func)
975 && let Type::App {
976 args,
977 result,
978 aggregate,
979 } = tpe
980 {
981 let args_actual_len = self.arena.exprs.vec(app.args).len();
982 let args_decl_len = self.arena.types.get_args(args.values).len();
983
984 if !(args_actual_len >= args.needed && args_actual_len <= args_decl_len) {
985 return Err(AnalysisError::FunWrongArgumentCount(
986 node.attrs.pos.line,
987 node.attrs.pos.col,
988 self.arena.strings.get(app.func).to_owned(),
989 ));
990 }
991
992 if aggregate && !ctx.allow_agg_func {
993 return Err(AnalysisError::WrongAggFunUsage(
994 node.attrs.pos.line,
995 node.attrs.pos.col,
996 self.arena.strings.get(app.func).to_owned(),
997 ));
998 }
999
1000 if aggregate && ctx.allow_agg_func {
1001 ctx.use_agg_funcs = true;
1002 }
1003
1004 let arg_types = self.arena.types.args_idxes(args.values);
1005 let args_idxes = self.arena.exprs.vec_idxes(app.args);
1006 for (val_idx, tpe_idx) in args_idxes.zip(arg_types) {
1007 let arg = self.arena.exprs.vec_get(app.args, val_idx);
1008 let tpe = self.arena.types.args_get(args.values, tpe_idx);
1009
1010 self.analyze_expr(ctx, arg, tpe)?;
1011 }
1012
1013 if matches!(expect, Type::Unspecified) {
1014 Ok(self.arena.types.get_type(result))
1015 } else {
1016 self.arena
1017 .type_check(node.attrs, expect, self.arena.types.get_type(result))
1018 }
1019 } else {
1020 Err(AnalysisError::FuncUndeclared(
1021 node.attrs.pos.line,
1022 node.attrs.pos.col,
1023 self.arena.strings.get(app.func).to_owned(),
1024 ))
1025 }
1026 }
1027
1028 Value::Binary(binary) => match binary.operator {
1029 Operator::Add | Operator::Sub | Operator::Mul | Operator::Div => {
1030 self.analyze_expr(ctx, binary.lhs, Type::Number)?;
1031 self.analyze_expr(ctx, binary.rhs, Type::Number)?;
1032 self.arena.type_check(node.attrs, expect, Type::Number)
1033 }
1034
1035 Operator::Eq
1036 | Operator::Neq
1037 | Operator::Lt
1038 | Operator::Lte
1039 | Operator::Gt
1040 | Operator::Gte => {
1041 let lhs_expect = self.analyze_expr(ctx, binary.lhs, Type::Unspecified)?;
1042 let rhs_expect = self.analyze_expr(ctx, binary.rhs, lhs_expect)?;
1043
1044 if matches!(lhs_expect, Type::Unspecified)
1047 && !matches!(rhs_expect, Type::Unspecified)
1048 {
1049 self.analyze_expr(ctx, binary.lhs, rhs_expect)?;
1050 }
1051
1052 self.arena.type_check(node.attrs, expect, Type::Bool)
1053 }
1054
1055 Operator::Contains => {
1056 let new_expect = self.arena.types.alloc_array_of(Type::Unspecified);
1057 let lhs_expect = self.analyze_expr(ctx, binary.lhs, new_expect)?;
1058
1059 let lhs_assumption = match lhs_expect {
1060 Type::Array(inner) => self.arena.types.get_type(inner),
1061 other => {
1062 return Err(AnalysisError::ExpectArray(
1063 node.attrs.pos.line,
1064 node.attrs.pos.col,
1065 display_type(self.arena, other),
1066 ));
1067 }
1068 };
1069
1070 let rhs_expect = self.analyze_expr(ctx, binary.rhs, lhs_assumption)?;
1071
1072 if matches!(lhs_assumption, Type::Unspecified)
1075 && !matches!(rhs_expect, Type::Unspecified)
1076 {
1077 let new_expect = self.arena.types.alloc_array_of(rhs_expect);
1078 self.analyze_expr(ctx, binary.lhs, new_expect)?;
1079 }
1080
1081 self.arena.type_check(node.attrs, expect, Type::Bool)
1082 }
1083
1084 Operator::And | Operator::Or | Operator::Xor => {
1085 self.analyze_expr(ctx, binary.lhs, Type::Bool)?;
1086 self.analyze_expr(ctx, binary.rhs, Type::Bool)?;
1087 self.arena.type_check(node.attrs, expect, Type::Bool)
1088 }
1089
1090 Operator::As => {
1091 let rhs = self.arena.exprs.get(binary.rhs);
1092 if let Value::Id(name) = rhs.value {
1093 return if let Some(tpe) = resolve_type(self.arena, name) {
1094 Ok(tpe)
1096 } else {
1097 Err(AnalysisError::UnknownType(
1098 rhs.attrs.pos.line,
1099 rhs.attrs.pos.col,
1100 self.arena.strings.get(name).to_owned(),
1101 ))
1102 };
1103 }
1104
1105 unreachable!(
1106 "we already made sure during parsing that we can only have an ID symbol at this point"
1107 )
1108 }
1109
1110 Operator::Not => unreachable!(),
1111 },
1112
1113 Value::Unary(unary) => match unary.operator {
1114 Operator::Add | Operator::Sub => {
1115 self.analyze_expr(ctx, unary.expr, Type::Number)?;
1116 self.arena.type_check(node.attrs, expect, Type::Number)
1117 }
1118
1119 Operator::Not => {
1120 self.analyze_expr(ctx, unary.expr, Type::Bool)?;
1121 self.arena.type_check(node.attrs, expect, Type::Bool)
1122 }
1123
1124 _ => unreachable!(),
1125 },
1126
1127 Value::Group(expr) => Ok(self.analyze_expr(ctx, expr, expect)?),
1128 }
1129 }
1130
1131 fn analyze_access(
1132 &mut self,
1133 attrs: Attrs,
1134 access: ExprRef,
1135 expect: Type,
1136 ) -> AnalysisResult<Type> {
1137 struct State {
1138 depth: u8,
1139 dynamic: bool,
1141 definition: Def,
1142 }
1143
1144 impl State {
1145 fn new(definition: Def) -> Self {
1146 Self {
1147 depth: 0,
1148 dynamic: false,
1149 definition,
1150 }
1151 }
1152 }
1153
1154 #[derive(Copy, Clone)]
1155 struct Parent {
1156 record: Record,
1157 field: Option<StrRef>,
1158 }
1159
1160 enum Def {
1161 User { parent: Parent, tpe: Type },
1162 System(Type),
1163 }
1164
1165 fn go<'global>(
1166 scope: &mut Scope,
1167 arena: &'global mut Arena,
1168 sys: &'global AnalysisOptions,
1169 expr: ExprRef,
1170 ) -> AnalysisResult<State> {
1171 let node = arena.exprs.get(expr);
1172 match node.value {
1173 Value::Id(id) => {
1174 if let Some(tpe) = sys.default_scope.get(id) {
1175 if matches!(tpe, Type::Record(_)) {
1176 Ok(State::new(Def::System(tpe)))
1177 } else {
1178 Err(AnalysisError::ExpectRecord(
1179 node.attrs.pos.line,
1180 node.attrs.pos.col,
1181 display_type(arena, tpe),
1182 ))
1183 }
1184 } else if let Some(tpe) = scope.get_mut(id) {
1185 if matches!(tpe, Type::Unspecified) {
1186 let record = arena.types.instantiate_record();
1187 *tpe = Type::Record(record);
1188
1189 Ok(State::new(Def::User {
1190 parent: Parent {
1191 record,
1192 field: None,
1193 },
1194 tpe: Type::Record(record),
1195 }))
1196 } else if let Type::Record(record) = *tpe {
1197 Ok(State::new(Def::User {
1198 parent: Parent {
1199 record,
1200 field: None,
1201 },
1202 tpe: *tpe,
1203 }))
1204 } else {
1205 Err(AnalysisError::ExpectRecord(
1206 node.attrs.pos.line,
1207 node.attrs.pos.col,
1208 display_type(arena, *tpe),
1209 ))
1210 }
1211 } else {
1212 Err(AnalysisError::VariableUndeclared(
1213 node.attrs.pos.line,
1214 node.attrs.pos.col,
1215 arena.strings.get(id).to_owned(),
1216 ))
1217 }
1218 }
1219 Value::Access(access) => {
1220 let mut state = go(scope, arena, sys, access.target)?;
1221
1222 let is_data_field =
1224 state.depth == 0 && arena.strings.get(access.field) == "data";
1225
1226 if !state.dynamic && is_data_field {
1230 state.dynamic = true;
1231 }
1232
1233 match state.definition {
1234 Def::User { parent, tpe } => {
1235 if matches!(tpe, Type::Unspecified) && state.dynamic {
1236 let record = arena.types.instantiate_record();
1237 arena
1238 .types
1239 .record_set(record, access.field, Type::Unspecified);
1240
1241 if let Some(field) = parent.field {
1243 arena.types.record_set(
1244 parent.record,
1245 field,
1246 Type::Record(record),
1247 );
1248 }
1249
1250 return Ok(State {
1251 depth: state.depth + 1,
1252 definition: Def::User {
1253 parent: Parent {
1254 record,
1255 field: Some(access.field),
1256 },
1257 tpe: Type::Unspecified,
1258 },
1259 ..state
1260 });
1261 } else if let Type::Record(record) = tpe {
1262 return if let Some(tpe) =
1263 arena.types.record_get(record, access.field)
1264 {
1265 Ok(State {
1266 depth: state.depth + 1,
1267 definition: Def::User {
1268 parent: Parent {
1269 record,
1270 field: Some(access.field),
1271 },
1272 tpe,
1273 },
1274 ..state
1275 })
1276 } else {
1277 if state.dynamic || is_data_field {
1279 arena.types.record_set(
1280 record,
1281 access.field,
1282 Type::Unspecified,
1283 );
1284 return Ok(State {
1285 depth: state.depth + 1,
1286 definition: Def::User {
1287 parent: Parent {
1288 record,
1289 field: Some(access.field),
1290 },
1291 tpe: Type::Unspecified,
1292 },
1293 ..state
1294 });
1295 }
1296
1297 Err(AnalysisError::FieldUndeclared(
1298 node.attrs.pos.line,
1299 node.attrs.pos.col,
1300 arena.strings.get(access.field).to_owned(),
1301 ))
1302 };
1303 }
1304
1305 Err(AnalysisError::ExpectRecord(
1306 node.attrs.pos.line,
1307 node.attrs.pos.col,
1308 display_type(arena, tpe),
1309 ))
1310 }
1311
1312 Def::System(tpe) => {
1313 if matches!(tpe, Type::Unspecified) && state.dynamic {
1314 return Ok(State {
1315 depth: state.depth + 1,
1316 definition: Def::System(Type::Unspecified),
1317 ..state
1318 });
1319 }
1320
1321 if let Type::Record(rec) = tpe {
1322 if let Some(field) = arena.types.record_get(rec, access.field) {
1323 return Ok(State {
1324 depth: state.depth + 1,
1325 definition: Def::System(field),
1326 ..state
1327 });
1328 }
1329
1330 return Err(AnalysisError::FieldUndeclared(
1331 node.attrs.pos.line,
1332 node.attrs.pos.col,
1333 arena.strings.get(access.field).to_owned(),
1334 ));
1335 }
1336
1337 Err(AnalysisError::ExpectRecord(
1338 node.attrs.pos.line,
1339 node.attrs.pos.col,
1340 display_type(arena, tpe),
1341 ))
1342 }
1343 }
1344 }
1345 Value::Number(_)
1346 | Value::String(_)
1347 | Value::Bool(_)
1348 | Value::Array(_)
1349 | Value::Record(_)
1350 | Value::App(_)
1351 | Value::Binary(_)
1352 | Value::Unary(_)
1353 | Value::Group(_) => unreachable!(),
1354 }
1355 }
1356
1357 let state = go(&mut self.scope, self.arena, self.options, access)?;
1358
1359 match state.definition {
1360 Def::User { parent, tpe } => {
1361 let new_tpe = self.arena.type_check(attrs, tpe, expect)?;
1362
1363 if let Some(field) = parent.field {
1364 self.arena.types.record_set(parent.record, field, new_tpe);
1365 }
1366
1367 Ok(new_tpe)
1368 }
1369
1370 Def::System(tpe) => self.arena.type_check(attrs, tpe, expect),
1371 }
1372 }
1373
1374 fn projection_type(&mut self, query: &Query<Typed>) -> Type {
1375 self.project_type(query.projection)
1376 }
1377
1378 fn project_type(&mut self, node: ExprRef) -> Type {
1379 match self.arena.exprs.get(node).value {
1380 Value::Number(_) => Type::Number,
1381 Value::String(_) => Type::String,
1382 Value::Bool(_) => Type::Bool,
1383 Value::Id(id) => {
1384 if let Some(tpe) = self.options.default_scope.get(id) {
1385 tpe
1386 } else if let Some(tpe) = self.scope.get(id) {
1387 tpe
1388 } else {
1389 Type::Unspecified
1390 }
1391 }
1392 Value::Array(exprs) => {
1393 let mut project = Type::Unspecified;
1394
1395 for idx in self.arena.exprs.vec_idxes(exprs) {
1396 let expr = self.arena.exprs.vec_get(exprs, idx);
1397 let tmp = self.project_type(expr);
1398
1399 if !matches!(tmp, Type::Unspecified) {
1400 project = tmp;
1401 break;
1402 }
1403 }
1404
1405 self.arena.types.alloc_array_of(project)
1406 }
1407 Value::Record(fields) => {
1408 let mut props = FxHashMap::default();
1409
1410 for idx in self.arena.exprs.rec_idxes(fields) {
1411 let field = self.arena.exprs.rec_get(fields, idx);
1412 let tpe = self.project_type(field.expr);
1413 props.insert(field.name, tpe);
1414 }
1415
1416 Type::Record(self.arena.types.alloc_record(props))
1417 }
1418 Value::Access(access) => {
1419 let tpe = self.project_type(access.target);
1420 if let Type::Record(record) = tpe {
1421 self.arena
1422 .types
1423 .record_get(record, access.field)
1424 .unwrap_or_default()
1425 } else {
1426 Type::Unspecified
1427 }
1428 }
1429 Value::App(app) => self.options.default_scope.get(app.func).unwrap_or_default(),
1430 Value::Binary(binary) => match binary.operator {
1431 Operator::Add | Operator::Sub | Operator::Mul | Operator::Div => Type::Number,
1432 Operator::As => {
1433 if let Value::Id(n) = self.arena.exprs.get(binary.rhs).value
1434 && let Some(tpe) = resolve_type(self.arena, n)
1435 {
1436 tpe
1437 } else {
1438 Type::Unspecified
1439 }
1440 }
1441 Operator::Eq
1442 | Operator::Neq
1443 | Operator::Lt
1444 | Operator::Lte
1445 | Operator::Gt
1446 | Operator::Gte
1447 | Operator::And
1448 | Operator::Or
1449 | Operator::Xor
1450 | Operator::Not
1451 | Operator::Contains => Type::Bool,
1452 },
1453 Value::Unary(unary) => match unary.operator {
1454 Operator::Add | Operator::Sub => Type::Number,
1455 Operator::Mul
1456 | Operator::Div
1457 | Operator::Eq
1458 | Operator::Neq
1459 | Operator::Lt
1460 | Operator::Lte
1461 | Operator::Gt
1462 | Operator::Gte
1463 | Operator::And
1464 | Operator::Or
1465 | Operator::Xor
1466 | Operator::Not
1467 | Operator::Contains
1468 | Operator::As => unreachable!(),
1469 },
1470 Value::Group(expr) => self.project_type(expr),
1471 }
1472 }
1473}
1474
1475impl Arena {
1476 fn type_check(&mut self, attrs: Attrs, this: Type, other: Type) -> Result<Type, AnalysisError> {
1481 match (this, other) {
1482 (Type::Unspecified, other) => Ok(other),
1483 (this, Type::Unspecified) => Ok(this),
1484 (Type::Subject, Type::Subject) => Ok(Type::Subject),
1485
1486 (Type::Subject, Type::String) => Ok(Type::String),
1490 (Type::String, Type::Subject) => Ok(Type::String),
1491
1492 (Type::Number, Type::Number) => Ok(Type::Number),
1493 (Type::String, Type::String) => Ok(Type::String),
1494 (Type::Bool, Type::Bool) => Ok(Type::Bool),
1495 (Type::Date, Type::Date) => Ok(Type::Date),
1496 (Type::Time, Type::Time) => Ok(Type::Time),
1497 (Type::DateTime, Type::DateTime) => Ok(Type::DateTime),
1498
1499 (Type::DateTime, Type::Date) => Ok(Type::Date),
1501 (Type::Date, Type::DateTime) => Ok(Type::Date),
1502 (Type::DateTime, Type::Time) => Ok(Type::Time),
1503 (Type::Time, Type::DateTime) => Ok(Type::Time),
1504
1505 (Type::Array(a), Type::Array(b)) => {
1506 let a = self.types.get_type(a);
1507 let b = self.types.get_type(b);
1508 let tpe = self.type_check(attrs, a, b)?;
1509
1510 Ok(self.types.alloc_array_of(tpe))
1511 }
1512
1513 (Type::Record(a), Type::Record(b)) if self.types.records_have_same_keys(a, b) => {
1514 let mut map_a = mem::take(&mut self.types.records[a.0]);
1515 let mut map_b = mem::take(&mut self.types.records[b.0]);
1516
1517 for (bk, bv) in map_b.iter_mut() {
1518 let av = map_a.get_mut(bk).unwrap();
1519 let new_tpe = self.type_check(attrs, *av, *bv)?;
1520
1521 *av = new_tpe;
1522 *bv = new_tpe;
1523 }
1524
1525 self.types.records[a.0] = map_a;
1526 self.types.records[b.0] = map_b;
1527
1528 Ok(Type::Record(a))
1529 }
1530
1531 (
1532 Type::App {
1533 args: a_args,
1534 result: a_res,
1535 aggregate: a_agg,
1536 },
1537 Type::App {
1538 args: b_args,
1539 result: b_res,
1540 aggregate: b_agg,
1541 },
1542 ) if self.types.get_args(a_args.values).len()
1543 == self.types.get_args(b_args.values).len()
1544 && a_agg == b_agg =>
1545 {
1546 if self.types.get_args(a_args.values).is_empty() {
1547 let a = self.types.get_type(a_res);
1548 let b = self.types.get_type(b_res);
1549 let new_res = self.type_check(attrs, a, b)?;
1550
1551 return Ok(Type::App {
1552 args: a_args,
1553 result: self.types.register_type(new_res),
1554 aggregate: a_agg,
1555 });
1556 }
1557
1558 let mut vec_a = mem::take(&mut self.types.args[a_args.values.0]);
1559 let mut vec_b = mem::take(&mut self.types.args[b_args.values.0]);
1560
1561 for (a, b) in vec_a.iter_mut().zip(vec_b.iter_mut()) {
1562 let new_tpe = self.type_check(attrs, *a, *b)?;
1563 *a = new_tpe;
1564 *b = new_tpe;
1565 }
1566
1567 self.types.args[a_args.values.0] = vec_a;
1568 self.types.args[b_args.values.0] = vec_b;
1569
1570 let res_a = self.types.get_type(a_res);
1571 let res_b = self.types.get_type(b_res);
1572 let new_tpe = self.type_check(attrs, res_a, res_b)?;
1573
1574 Ok(Type::App {
1575 args: a_args,
1576 result: self.types.register_type(new_tpe),
1577 aggregate: a_agg,
1578 })
1579 }
1580
1581 (this, other) => Err(AnalysisError::TypeMismatch(
1582 attrs.pos.line,
1583 attrs.pos.col,
1584 display_type(self, this),
1585 display_type(self, other),
1586 )),
1587 }
1588 }
1589}
1590
1591pub(crate) fn resolve_type_from_str(name: &str) -> Option<Type> {
1610 if name.eq_ignore_ascii_case("string") {
1611 Some(Type::String)
1612 } else if name.eq_ignore_ascii_case("int")
1613 || name.eq_ignore_ascii_case("float64")
1614 || name.eq_ignore_ascii_case("number")
1615 {
1616 Some(Type::Number)
1617 } else if name.eq_ignore_ascii_case("boolean") || name.eq_ignore_ascii_case("bool") {
1618 Some(Type::Bool)
1619 } else if name.eq_ignore_ascii_case("date") {
1620 Some(Type::Date)
1621 } else if name.eq_ignore_ascii_case("time") {
1622 Some(Type::Time)
1623 } else if name.eq_ignore_ascii_case("datetime") {
1624 Some(Type::DateTime)
1625 } else {
1626 None
1627 }
1628}
1629
1630pub(crate) fn resolve_type(arena: &Arena, name_ref: StrRef) -> Option<Type> {
1631 let name = arena.strings.get(name_ref);
1632 resolve_type_from_str(name)
1633}
1634
1635pub(crate) fn display_type(arena: &Arena, tpe: Type) -> String {
1636 fn go(buffer: &mut String, arena: &Arena, tpe: Type) {
1637 match tpe {
1638 Type::Unspecified => buffer.push_str("Any"),
1639 Type::Number => buffer.push_str("Number"),
1640 Type::String => buffer.push_str("String"),
1641 Type::Bool => buffer.push_str("Bool"),
1642 Type::Subject => buffer.push_str("Subject"),
1643 Type::Date => buffer.push_str("Date"),
1644 Type::Time => buffer.push_str("Time"),
1645 Type::DateTime => buffer.push_str("DateTime"),
1646
1647 Type::Array(tpe) => {
1648 buffer.push_str("[]");
1649 go(buffer, arena, arena.types.get_type(tpe));
1650 }
1651
1652 Type::Record(map) => {
1653 let map = arena.types.get_record(map);
1654
1655 buffer.push_str("{ ");
1656
1657 for (idx, (name, value)) in map.iter().enumerate() {
1658 if idx != 0 {
1659 buffer.push_str(", ");
1660 }
1661
1662 buffer.push_str(arena.strings.get(*name));
1663 buffer.push_str(": ");
1664
1665 go(buffer, arena, *value);
1666 }
1667
1668 buffer.push_str(" }");
1669 }
1670
1671 Type::App {
1672 args,
1673 result,
1674 aggregate,
1675 } => {
1676 let fun_args = arena.types.get_args(args.values);
1677 buffer.push('(');
1678
1679 for (idx, arg) in fun_args.iter().copied().enumerate() {
1680 if idx != 0 {
1681 buffer.push_str(", ");
1682 }
1683
1684 go(buffer, arena, arg);
1685
1686 if idx + 1 > args.needed {
1687 buffer.push('?');
1688 }
1689 }
1690
1691 buffer.push(')');
1692
1693 if aggregate {
1694 buffer.push_str(" => ");
1695 } else {
1696 buffer.push_str(" -> ");
1697 }
1698
1699 go(buffer, arena, arena.types.get_type(result));
1700 }
1701 }
1702 }
1703
1704 let mut buffer = String::new();
1705 go(&mut buffer, arena, tpe);
1706
1707 buffer
1708}