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 definition: Def,
1140 }
1141
1142 impl State {
1143 fn new(definition: Def) -> Self {
1144 Self {
1145 depth: 0,
1146 definition,
1147 }
1148 }
1149 }
1150
1151 #[derive(Copy, Clone)]
1152 struct Parent {
1153 record: Record,
1154 field: Option<StrRef>,
1155 }
1156
1157 enum Def {
1158 User { parent: Parent, tpe: Type },
1159 System(Type),
1160 }
1161
1162 fn go<'global>(
1163 scope: &mut Scope,
1164 arena: &'global mut Arena,
1165 sys: &'global AnalysisOptions,
1166 expr: ExprRef,
1167 ) -> AnalysisResult<State> {
1168 let node = arena.exprs.get(expr);
1169 match node.value {
1170 Value::Id(id) => {
1171 if let Some(tpe) = sys.default_scope.get(id) {
1172 if matches!(tpe, Type::Record(_)) {
1173 Ok(State::new(Def::System(tpe)))
1174 } else {
1175 Err(AnalysisError::ExpectRecord(
1176 node.attrs.pos.line,
1177 node.attrs.pos.col,
1178 display_type(arena, tpe),
1179 ))
1180 }
1181 } else if let Some(tpe) = scope.get_mut(id) {
1182 if matches!(tpe, Type::Unspecified) {
1183 let record = arena.types.instantiate_open_record();
1184 *tpe = Type::Record(record);
1185
1186 Ok(State::new(Def::User {
1187 parent: Parent {
1188 record,
1189 field: None,
1190 },
1191 tpe: Type::Record(record),
1192 }))
1193 } else if let Type::Record(record) = *tpe {
1194 Ok(State::new(Def::User {
1195 parent: Parent {
1196 record,
1197 field: None,
1198 },
1199 tpe: *tpe,
1200 }))
1201 } else {
1202 Err(AnalysisError::ExpectRecord(
1203 node.attrs.pos.line,
1204 node.attrs.pos.col,
1205 display_type(arena, *tpe),
1206 ))
1207 }
1208 } else {
1209 Err(AnalysisError::VariableUndeclared(
1210 node.attrs.pos.line,
1211 node.attrs.pos.col,
1212 arena.strings.get(id).to_owned(),
1213 ))
1214 }
1215 }
1216 Value::Access(access) => {
1217 let state = go(scope, arena, sys, access.target)?;
1218
1219 match state.definition {
1220 Def::User { parent, tpe } => {
1221 if matches!(tpe, Type::Unspecified) {
1222 let record = arena.types.instantiate_open_record();
1223 arena
1224 .types
1225 .record_set(record, access.field, Type::Unspecified);
1226
1227 if let Some(field) = parent.field {
1229 arena.types.record_set(
1230 parent.record,
1231 field,
1232 Type::Record(record),
1233 );
1234 }
1235
1236 return Ok(State {
1237 depth: state.depth + 1,
1238 definition: Def::User {
1239 parent: Parent {
1240 record,
1241 field: Some(access.field),
1242 },
1243 tpe: Type::Unspecified,
1244 },
1245 });
1246 } else if let Type::Record(record) = tpe {
1247 return if let Some(tpe) =
1248 arena.types.record_get(record, access.field)
1249 {
1250 Ok(State {
1251 depth: state.depth + 1,
1252 definition: Def::User {
1253 parent: Parent {
1254 record,
1255 field: Some(access.field),
1256 },
1257 tpe,
1258 },
1259 })
1260 } else {
1261 if record.open {
1262 arena.types.record_set(
1263 record,
1264 access.field,
1265 Type::Unspecified,
1266 );
1267 return Ok(State {
1268 depth: state.depth + 1,
1269 definition: Def::User {
1270 parent: Parent {
1271 record,
1272 field: Some(access.field),
1273 },
1274 tpe: Type::Unspecified,
1275 },
1276 });
1277 }
1278
1279 Err(AnalysisError::FieldUndeclared(
1280 node.attrs.pos.line,
1281 node.attrs.pos.col,
1282 arena.strings.get(access.field).to_owned(),
1283 ))
1284 };
1285 }
1286
1287 Err(AnalysisError::ExpectRecord(
1288 node.attrs.pos.line,
1289 node.attrs.pos.col,
1290 display_type(arena, tpe),
1291 ))
1292 }
1293
1294 Def::System(tpe) => {
1295 if matches!(tpe, Type::Unspecified) {
1296 return Ok(State {
1297 depth: state.depth + 1,
1298 definition: Def::System(Type::Unspecified),
1299 });
1300 }
1301
1302 if let Type::Record(rec) = tpe {
1303 if let Some(field) = arena.types.record_get(rec, access.field) {
1304 return Ok(State {
1305 depth: state.depth + 1,
1306 definition: Def::System(field),
1307 });
1308 }
1309
1310 return Err(AnalysisError::FieldUndeclared(
1311 node.attrs.pos.line,
1312 node.attrs.pos.col,
1313 arena.strings.get(access.field).to_owned(),
1314 ));
1315 }
1316
1317 Err(AnalysisError::ExpectRecord(
1318 node.attrs.pos.line,
1319 node.attrs.pos.col,
1320 display_type(arena, tpe),
1321 ))
1322 }
1323 }
1324 }
1325 Value::Number(_)
1326 | Value::String(_)
1327 | Value::Bool(_)
1328 | Value::Array(_)
1329 | Value::Record(_)
1330 | Value::App(_)
1331 | Value::Binary(_)
1332 | Value::Unary(_)
1333 | Value::Group(_) => unreachable!(),
1334 }
1335 }
1336
1337 let state = go(&mut self.scope, self.arena, self.options, access)?;
1338
1339 match state.definition {
1340 Def::User { parent, tpe } => {
1341 let new_tpe = self.arena.type_check(attrs, tpe, expect)?;
1342
1343 if let Some(field) = parent.field {
1344 self.arena.types.record_set(parent.record, field, new_tpe);
1345 }
1346
1347 Ok(new_tpe)
1348 }
1349
1350 Def::System(tpe) => self.arena.type_check(attrs, tpe, expect),
1351 }
1352 }
1353
1354 fn projection_type(&mut self, query: &Query<Typed>) -> Type {
1355 self.project_type(query.projection)
1356 }
1357
1358 fn project_type(&mut self, node: ExprRef) -> Type {
1359 match self.arena.exprs.get(node).value {
1360 Value::Number(_) => Type::Number,
1361 Value::String(_) => Type::String,
1362 Value::Bool(_) => Type::Bool,
1363 Value::Id(id) => {
1364 if let Some(tpe) = self.options.default_scope.get(id) {
1365 tpe
1366 } else if let Some(tpe) = self.scope.get(id) {
1367 tpe
1368 } else {
1369 Type::Unspecified
1370 }
1371 }
1372 Value::Array(exprs) => {
1373 let mut project = Type::Unspecified;
1374
1375 for idx in self.arena.exprs.vec_idxes(exprs) {
1376 let expr = self.arena.exprs.vec_get(exprs, idx);
1377 let tmp = self.project_type(expr);
1378
1379 if !matches!(tmp, Type::Unspecified) {
1380 project = tmp;
1381 break;
1382 }
1383 }
1384
1385 self.arena.types.alloc_array_of(project)
1386 }
1387 Value::Record(fields) => {
1388 let mut props = FxHashMap::default();
1389
1390 for idx in self.arena.exprs.rec_idxes(fields) {
1391 let field = self.arena.exprs.rec_get(fields, idx);
1392 let tpe = self.project_type(field.expr);
1393 props.insert(field.name, tpe);
1394 }
1395
1396 Type::Record(self.arena.types.alloc_record(props))
1397 }
1398 Value::Access(access) => {
1399 let tpe = self.project_type(access.target);
1400 if let Type::Record(record) = tpe {
1401 self.arena
1402 .types
1403 .record_get(record, access.field)
1404 .unwrap_or_default()
1405 } else {
1406 Type::Unspecified
1407 }
1408 }
1409 Value::App(app) => self.options.default_scope.get(app.func).unwrap_or_default(),
1410 Value::Binary(binary) => match binary.operator {
1411 Operator::Add | Operator::Sub | Operator::Mul | Operator::Div => Type::Number,
1412 Operator::As => {
1413 if let Value::Id(n) = self.arena.exprs.get(binary.rhs).value
1414 && let Some(tpe) = resolve_type(self.arena, n)
1415 {
1416 tpe
1417 } else {
1418 Type::Unspecified
1419 }
1420 }
1421 Operator::Eq
1422 | Operator::Neq
1423 | Operator::Lt
1424 | Operator::Lte
1425 | Operator::Gt
1426 | Operator::Gte
1427 | Operator::And
1428 | Operator::Or
1429 | Operator::Xor
1430 | Operator::Not
1431 | Operator::Contains => Type::Bool,
1432 },
1433 Value::Unary(unary) => match unary.operator {
1434 Operator::Add | Operator::Sub => Type::Number,
1435 Operator::Mul
1436 | Operator::Div
1437 | Operator::Eq
1438 | Operator::Neq
1439 | Operator::Lt
1440 | Operator::Lte
1441 | Operator::Gt
1442 | Operator::Gte
1443 | Operator::And
1444 | Operator::Or
1445 | Operator::Xor
1446 | Operator::Not
1447 | Operator::Contains
1448 | Operator::As => unreachable!(),
1449 },
1450 Value::Group(expr) => self.project_type(expr),
1451 }
1452 }
1453}
1454
1455impl Arena {
1456 fn type_check(&mut self, attrs: Attrs, this: Type, other: Type) -> Result<Type, AnalysisError> {
1461 match (this, other) {
1462 (Type::Unspecified, other) => Ok(other),
1463 (this, Type::Unspecified) => Ok(this),
1464 (Type::Subject, Type::Subject) => Ok(Type::Subject),
1465
1466 (Type::Subject, Type::String) => Ok(Type::String),
1470 (Type::String, Type::Subject) => Ok(Type::String),
1471
1472 (Type::Number, Type::Number) => Ok(Type::Number),
1473 (Type::String, Type::String) => Ok(Type::String),
1474 (Type::Bool, Type::Bool) => Ok(Type::Bool),
1475 (Type::Date, Type::Date) => Ok(Type::Date),
1476 (Type::Time, Type::Time) => Ok(Type::Time),
1477 (Type::DateTime, Type::DateTime) => Ok(Type::DateTime),
1478
1479 (Type::DateTime, Type::Date) => Ok(Type::Date),
1481 (Type::Date, Type::DateTime) => Ok(Type::Date),
1482 (Type::DateTime, Type::Time) => Ok(Type::Time),
1483 (Type::Time, Type::DateTime) => Ok(Type::Time),
1484
1485 (Type::Array(a), Type::Array(b)) => {
1486 let a = self.types.get_type(a);
1487 let b = self.types.get_type(b);
1488 let tpe = self.type_check(attrs, a, b)?;
1489
1490 Ok(self.types.alloc_array_of(tpe))
1491 }
1492
1493 (Type::Record(a), Type::Record(b)) if self.types.records_have_same_keys(a, b) => {
1494 let mut map_a = mem::take(&mut self.types.records[a.id]);
1495 let mut map_b = mem::take(&mut self.types.records[b.id]);
1496
1497 for (bk, bv) in map_b.iter_mut() {
1498 let av = map_a.get_mut(bk).unwrap();
1499 let new_tpe = self.type_check(attrs, *av, *bv)?;
1500
1501 *av = new_tpe;
1502 *bv = new_tpe;
1503 }
1504
1505 self.types.records[a.id] = map_a;
1506 self.types.records[b.id] = map_b;
1507
1508 Ok(Type::Record(a))
1509 }
1510
1511 (
1512 Type::App {
1513 args: a_args,
1514 result: a_res,
1515 aggregate: a_agg,
1516 },
1517 Type::App {
1518 args: b_args,
1519 result: b_res,
1520 aggregate: b_agg,
1521 },
1522 ) if self.types.get_args(a_args.values).len()
1523 == self.types.get_args(b_args.values).len()
1524 && a_agg == b_agg =>
1525 {
1526 if self.types.get_args(a_args.values).is_empty() {
1527 let a = self.types.get_type(a_res);
1528 let b = self.types.get_type(b_res);
1529 let new_res = self.type_check(attrs, a, b)?;
1530
1531 return Ok(Type::App {
1532 args: a_args,
1533 result: self.types.register_type(new_res),
1534 aggregate: a_agg,
1535 });
1536 }
1537
1538 let mut vec_a = mem::take(&mut self.types.args[a_args.values.0]);
1539 let mut vec_b = mem::take(&mut self.types.args[b_args.values.0]);
1540
1541 for (a, b) in vec_a.iter_mut().zip(vec_b.iter_mut()) {
1542 let new_tpe = self.type_check(attrs, *a, *b)?;
1543 *a = new_tpe;
1544 *b = new_tpe;
1545 }
1546
1547 self.types.args[a_args.values.0] = vec_a;
1548 self.types.args[b_args.values.0] = vec_b;
1549
1550 let res_a = self.types.get_type(a_res);
1551 let res_b = self.types.get_type(b_res);
1552 let new_tpe = self.type_check(attrs, res_a, res_b)?;
1553
1554 Ok(Type::App {
1555 args: a_args,
1556 result: self.types.register_type(new_tpe),
1557 aggregate: a_agg,
1558 })
1559 }
1560
1561 (this, other) => Err(AnalysisError::TypeMismatch(
1562 attrs.pos.line,
1563 attrs.pos.col,
1564 display_type(self, this),
1565 display_type(self, other),
1566 )),
1567 }
1568 }
1569}
1570
1571pub(crate) fn resolve_type_from_str(name: &str) -> Option<Type> {
1590 if name.eq_ignore_ascii_case("string") {
1591 Some(Type::String)
1592 } else if name.eq_ignore_ascii_case("int")
1593 || name.eq_ignore_ascii_case("float64")
1594 || name.eq_ignore_ascii_case("number")
1595 {
1596 Some(Type::Number)
1597 } else if name.eq_ignore_ascii_case("boolean") || name.eq_ignore_ascii_case("bool") {
1598 Some(Type::Bool)
1599 } else if name.eq_ignore_ascii_case("date") {
1600 Some(Type::Date)
1601 } else if name.eq_ignore_ascii_case("time") {
1602 Some(Type::Time)
1603 } else if name.eq_ignore_ascii_case("datetime") {
1604 Some(Type::DateTime)
1605 } else {
1606 None
1607 }
1608}
1609
1610pub(crate) fn resolve_type(arena: &Arena, name_ref: StrRef) -> Option<Type> {
1611 let name = arena.strings.get(name_ref);
1612 resolve_type_from_str(name)
1613}
1614
1615pub(crate) fn display_type(arena: &Arena, tpe: Type) -> String {
1616 fn go(buffer: &mut String, arena: &Arena, tpe: Type) {
1617 match tpe {
1618 Type::Unspecified => buffer.push_str("Any"),
1619 Type::Number => buffer.push_str("Number"),
1620 Type::String => buffer.push_str("String"),
1621 Type::Bool => buffer.push_str("Bool"),
1622 Type::Subject => buffer.push_str("Subject"),
1623 Type::Date => buffer.push_str("Date"),
1624 Type::Time => buffer.push_str("Time"),
1625 Type::DateTime => buffer.push_str("DateTime"),
1626
1627 Type::Array(tpe) => {
1628 buffer.push_str("[]");
1629 go(buffer, arena, arena.types.get_type(tpe));
1630 }
1631
1632 Type::Record(map) => {
1633 let map = arena.types.get_record(map);
1634
1635 buffer.push_str("{ ");
1636
1637 for (idx, (name, value)) in map.iter().enumerate() {
1638 if idx != 0 {
1639 buffer.push_str(", ");
1640 }
1641
1642 buffer.push_str(arena.strings.get(*name));
1643 buffer.push_str(": ");
1644
1645 go(buffer, arena, *value);
1646 }
1647
1648 buffer.push_str(" }");
1649 }
1650
1651 Type::App {
1652 args,
1653 result,
1654 aggregate,
1655 } => {
1656 let fun_args = arena.types.get_args(args.values);
1657 buffer.push('(');
1658
1659 for (idx, arg) in fun_args.iter().copied().enumerate() {
1660 if idx != 0 {
1661 buffer.push_str(", ");
1662 }
1663
1664 go(buffer, arena, arg);
1665
1666 if idx + 1 > args.needed {
1667 buffer.push('?');
1668 }
1669 }
1670
1671 buffer.push(')');
1672
1673 if aggregate {
1674 buffer.push_str(" => ");
1675 } else {
1676 buffer.push_str(" -> ");
1677 }
1678
1679 go(buffer, arena, arena.types.get_type(result));
1680 }
1681 }
1682 }
1683
1684 let mut buffer = String::new();
1685 go(&mut buffer, arena, tpe);
1686
1687 buffer
1688}