1use std::cell::RefCell;
17use std::collections::BTreeMap;
18use std::path::PathBuf;
19use std::rc::Rc;
20
21use crate::ast::walk::{Visitor, Walker};
22use crate::ast::{
23 Expression, FailDef, FuncShapeDef, ImportDef, IncludeDef, Shape, Statement, Value,
24};
25use crate::error::{BuildError, ErrorType};
26use crate::iter::OffsetStrIter;
27use crate::parse::parse;
28
29use super::{
30 BinaryExprType, BinaryOpDef, CallDef, CastType, CopyDef, FuncDef, FuncOpDef, ImportShape,
31 MapFilterOpDef, ModuleDef, ModuleShape, NarrowedShape, NarrowingShape, NotDef, Position,
32 PositionedItem, ReduceOpDef, SelectDef, TupleShape,
33};
34
35pub trait DeriveShape {
37 fn derive_shape(&self, symbol_table: &mut BTreeMap<Rc<str>, Shape>) -> Shape;
39}
40
41impl DeriveShape for FuncDef {
42 fn derive_shape(&self, symbol_table: &mut BTreeMap<Rc<str>, Shape>) -> Shape {
43 let mut sym_table = self
45 .argdefs
46 .iter()
47 .map(|(sym, constraint)| {
48 let shape = if let Some(c) = constraint {
49 c.derive_shape(symbol_table)
50 } else {
51 Shape::Hole(sym.clone())
52 };
53 (sym.val.clone(), shape)
54 })
55 .collect::<BTreeMap<Rc<str>, Shape>>();
56 sym_table.append(&mut symbol_table.clone());
57 let shape = self.fields.derive_shape(&mut sym_table);
59 let table = self
62 .argdefs
63 .iter()
64 .map(|(sym, _constraint)| {
65 (
66 sym.val.clone(),
67 sym_table
68 .get(&sym.val)
69 .unwrap()
70 .clone()
71 .with_pos(sym.pos.clone()),
72 )
73 })
74 .collect::<BTreeMap<Rc<str>, Shape>>();
75 Shape::Func(FuncShapeDef {
76 args: table,
77 ret: shape.with_pos(self.pos.clone()).into(),
78 })
79 }
80}
81
82impl DeriveShape for ModuleDef {
83 fn derive_shape(&self, symbol_table: &mut BTreeMap<Rc<str>, Shape>) -> Shape {
84 let mut sym_table: BTreeMap<Rc<str>, Shape> = BTreeMap::new();
85 let mod_key: Rc<str> = "mod".into();
86 let mut mod_shape: TupleShape = Vec::new();
87 if let Some(pos) = self.arg_set.first().map(|(t, _, _)| t.pos.clone()) {
88 mod_shape = self
89 .arg_set
90 .iter()
91 .map(|(tok, _constraint, expr)| {
92 let default_shape = expr.derive_shape(symbol_table);
93 let shape = match &default_shape {
94 Shape::Tuple(pi) if pi.val.is_empty() => Shape::Narrowed(NarrowedShape {
96 pos: tok.pos.clone(),
97 types: NarrowingShape::Any,
98 }),
99 _ => default_shape,
100 };
101 (tok.into(), shape)
102 })
103 .collect();
104 sym_table.insert(
105 mod_key.clone(),
106 Shape::Tuple(PositionedItem::new(mod_shape.clone(), pos)),
107 );
108 }
109 let mut checker = Checker::new().with_symbol_table(sym_table);
111 checker.walk_statement_list(self.statements.clone().iter_mut().collect());
112 let mut ret = if let Some(out_expr) = &self.out_expr {
115 Box::new(out_expr.derive_shape(&mut checker.symbol_table))
116 } else {
117 Box::new(
118 checker
119 .pop_shape()
120 .unwrap_or(Shape::Narrowed(NarrowedShape {
121 pos: self.pos.clone(),
122 types: NarrowingShape::Any,
123 })),
124 )
125 };
126 if let Some(ref constraint_expr) = self.out_constraint {
128 let constraint_shape = constraint_expr.derive_shape(symbol_table);
129 let narrowed = ret.narrow(&constraint_shape, symbol_table);
130 if let Shape::TypeErr(_, _) = &narrowed {
131 ret = Box::new(narrowed);
132 } else {
133 ret = Box::new(narrowed);
134 }
135 }
136 if let Some(Shape::Tuple(mod_tuple)) = checker.symbol_table.get(&mod_key) {
138 mod_shape = mod_tuple.val.clone();
139 }
140 Shape::Module(ModuleShape {
141 items: mod_shape,
142 ret,
143 })
144 }
145}
146
147impl DeriveShape for SelectDef {
148 fn derive_shape(&self, symbol_table: &mut BTreeMap<Rc<str>, Shape>) -> Shape {
149 let SelectDef {
150 val: _,
151 default: _,
152 tuple,
153 pos: _,
154 } = self;
155 let mut narrowed_shape =
156 NarrowedShape::new_with_pos(Vec::with_capacity(tuple.len()), self.pos.clone());
157 for (_, _constraint, expr) in tuple {
158 let shape = expr.derive_shape(symbol_table);
159 narrowed_shape.merge_in_shape(shape, symbol_table);
160 }
161 Shape::Narrowed(narrowed_shape)
162 }
163}
164
165fn derive_include_shape(
166 IncludeDef {
167 pos,
168 path: _path,
169 typ: _typ,
170 }: &IncludeDef,
171) -> Shape {
172 Shape::Narrowed(NarrowedShape::new_with_pos(
173 vec![
174 Shape::Tuple(PositionedItem::new(vec![], pos.clone())),
175 Shape::List(NarrowedShape::new_with_pos(vec![], pos.clone())),
176 ],
177 pos.clone(),
178 ))
179}
180
181fn derive_not_shape(def: &NotDef, symbol_table: &mut BTreeMap<Rc<str>, Shape>) -> Shape {
182 let shape = def.expr.as_ref().derive_shape(symbol_table);
183 match &shape {
184 Shape::Boolean(_) => {
185 return Shape::Boolean(def.pos.clone());
186 }
187 Shape::Hole(_) => {
188 return Shape::Boolean(def.pos.clone());
189 }
190 Shape::Narrowed(NarrowedShape {
191 pos: _,
192 types: NarrowingShape::Any,
193 }) => {
194 return Shape::Boolean(def.pos.clone());
195 }
196 Shape::Narrowed(NarrowedShape {
197 pos: _,
198 types: NarrowingShape::Narrowed(shape_list),
199 }) => {
200 for s in shape_list.iter() {
201 if let Shape::Boolean(_) = s {
202 return Shape::Boolean(def.pos.clone());
203 }
204 }
205 }
206 _ => {
207 }
209 }
210 return Shape::TypeErr(
211 def.pos.clone(),
212 format!(
213 "Expected Boolean value in Not expression but got: {:?}",
214 shape
215 ),
216 );
217}
218
219fn derive_copy_shape(def: &CopyDef, symbol_table: &mut BTreeMap<Rc<str>, Shape>) -> Shape {
220 let base_shape = def.selector.derive_shape(symbol_table);
221 match &base_shape {
222 Shape::TypeErr(_, _) => base_shape,
224 Shape::Boolean(_)
225 | Shape::Int(_)
226 | Shape::Float(_)
227 | Shape::Str(_)
228 | Shape::List(_)
229 | Shape::Func(_) => Shape::TypeErr(
230 def.pos.clone(),
231 format!("Not a Copyable type {}", base_shape.type_name()),
232 ),
233 Shape::Hole(pi) => Shape::Narrowed(NarrowedShape::new_with_pos(
235 vec![
236 Shape::Tuple(PositionedItem::new(vec![], pi.pos.clone())),
237 Shape::Module(ModuleShape {
238 items: vec![],
239 ret: Box::new(Shape::Narrowed(NarrowedShape::new_with_pos(
240 vec![],
241 pi.pos.clone(),
242 ))),
243 }),
244 Shape::Import(ImportShape::Unresolved(pi.clone())),
245 ],
246 pi.pos.clone(),
247 )),
248 Shape::Narrowed(NarrowedShape {
249 pos: _,
250 types: NarrowingShape::Any,
251 }) => Shape::Narrowed(NarrowedShape::new_with_pos(
252 vec![
253 Shape::Tuple(PositionedItem {
254 pos: def.pos.clone(),
255 val: Vec::new(),
256 }),
257 Shape::Module(ModuleShape {
258 items: vec![],
259 ret: Box::new(Shape::Narrowed(NarrowedShape {
260 pos: def.pos.clone(),
261 types: NarrowingShape::Any,
262 })),
263 }),
264 ],
265 def.pos.clone(),
266 )),
267 Shape::Narrowed(NarrowedShape {
268 pos: _,
269 types: NarrowingShape::Narrowed(potentials),
270 }) => {
271 let filtered = potentials
273 .iter()
274 .filter_map(|v| match v {
275 Shape::Tuple(_) | Shape::Module(_) | Shape::Import(_) | Shape::Hole(_) => {
276 Some(v.clone())
277 }
278 _ => None,
279 })
280 .collect::<Vec<Shape>>();
281 if !filtered.is_empty() {
282 Shape::Narrowed(NarrowedShape::new_with_pos(filtered, def.pos.clone()))
284 } else {
285 Shape::TypeErr(
287 def.pos.clone(),
288 format!("Not a Copyable type {}", base_shape.type_name()),
289 )
290 }
291 }
292 Shape::Module(mdef) => {
294 let arg_fields = def
295 .fields
296 .iter()
297 .map(|(tok, _constraint, expr)| {
298 (tok.fragment.clone(), expr.derive_shape(symbol_table))
299 })
300 .collect::<BTreeMap<Rc<str>, Shape>>();
301 for (sym, shape) in mdef.items.iter() {
303 if let Some(s) = arg_fields.get(&sym.val) {
304 if let Shape::TypeErr(pos, msg) = shape.narrow(s, symbol_table) {
305 return Shape::TypeErr(pos, msg);
306 }
307 }
308 }
309 mdef.ret.as_ref().clone()
311 }
312 Shape::Tuple(t_def) => {
313 let mut base_fields = t_def.clone();
314 base_fields.val.extend(
315 def.fields
316 .iter()
317 .map(|(tok, _constraint, expr)| (tok.into(), expr.derive_shape(symbol_table))),
318 );
319 Shape::Tuple(base_fields).with_pos(def.pos.clone())
320 }
321 Shape::Import(ImportShape::Unresolved(_)) => Shape::Narrowed(NarrowedShape::new_with_pos(
322 vec![Shape::Tuple(PositionedItem::new(vec![], def.pos.clone()))],
323 def.pos.clone(),
324 )),
325 Shape::Import(ImportShape::Resolved(_, tuple_shape)) => {
326 let mut base_fields = tuple_shape.clone();
327 base_fields.extend(
328 def.fields
329 .iter()
330 .map(|(tok, _constraint, expr)| (tok.into(), expr.derive_shape(symbol_table))),
331 );
332 Shape::Tuple(PositionedItem::new(base_fields, def.pos.clone()))
333 }
334 }
335}
336
337fn derive_call_shape(def: &CallDef, symbol_table: &mut BTreeMap<Rc<str>, Shape>) -> Shape {
338 let func_shape = def.funcref.derive_shape(symbol_table);
339 match &func_shape {
340 Shape::Func(fdef) => {
341 if fdef.args.len() != def.arglist.len() {
343 return Shape::TypeErr(
344 def.pos.clone(),
345 format!(
346 "Function expects {} arguments but got {}",
347 fdef.args.len(),
348 def.arglist.len()
349 ),
350 );
351 }
352 for arg_expr in def.arglist.iter() {
357 arg_expr.derive_shape(symbol_table);
358 }
359 fdef.ret.as_ref().clone()
361 }
362 Shape::Hole(_) => {
363 for arg_expr in def.arglist.iter() {
365 arg_expr.derive_shape(symbol_table);
366 }
367 Shape::Narrowed(NarrowedShape {
368 pos: def.pos.clone(),
369 types: NarrowingShape::Any,
370 })
371 }
372 Shape::Narrowed(nshape) => {
373 match &nshape.types {
374 NarrowingShape::Any => {
375 for arg_expr in def.arglist.iter() {
377 arg_expr.derive_shape(symbol_table);
378 }
379 Shape::Narrowed(NarrowedShape {
380 pos: def.pos.clone(),
381 types: NarrowingShape::Any,
382 })
383 }
384 NarrowingShape::Narrowed(types) => {
385 let func_types: Vec<&FuncShapeDef> = types
387 .iter()
388 .filter_map(|t| {
389 if let Shape::Func(fdef) = t {
390 Some(fdef)
391 } else {
392 None
393 }
394 })
395 .collect();
396 if func_types.is_empty() {
397 return Shape::TypeErr(def.pos.clone(), "Not a callable type".to_owned());
398 }
399 let arg_shapes: Vec<Shape> = def
401 .arglist
402 .iter()
403 .map(|e| e.derive_shape(symbol_table))
404 .collect();
405 let mut ret_shapes = Vec::new();
407 for fdef in func_types {
408 if fdef.args.len() == arg_shapes.len() {
409 ret_shapes.push(fdef.ret.as_ref().clone());
410 }
411 }
412 if ret_shapes.is_empty() {
413 Shape::TypeErr(
414 def.pos.clone(),
415 "No callable candidate matches argument count".to_owned(),
416 )
417 } else if ret_shapes.len() == 1 {
418 ret_shapes.pop().unwrap()
419 } else {
420 Shape::Narrowed(NarrowedShape::new_with_pos(ret_shapes, def.pos.clone()))
421 }
422 }
423 }
424 }
425 _ => Shape::TypeErr(
426 def.pos.clone(),
427 format!("Not a callable type: {}", func_shape.type_name()),
428 ),
429 }
430}
431
432fn derive_func_op_shape(def: &FuncOpDef, symbol_table: &mut BTreeMap<Rc<str>, Shape>) -> Shape {
433 match def {
434 FuncOpDef::Map(MapFilterOpDef { func, target, pos }) => {
435 let target_shape = target.derive_shape(symbol_table);
436 let func_shape = func.derive_shape(symbol_table);
437 match &target_shape {
439 Shape::List(_) | Shape::Hole(_) => {}
440 Shape::Narrowed(NarrowedShape {
441 types: NarrowingShape::Any,
442 ..
443 }) => {}
444 _ => {
445 return Shape::TypeErr(
446 pos.clone(),
447 format!(
448 "map target must be a list, got {}",
449 target_shape.type_name()
450 ),
451 );
452 }
453 }
454 match &func_shape {
456 Shape::Func(fdef) => Shape::List(NarrowedShape::new_with_pos(
457 vec![fdef.ret.as_ref().clone()],
458 pos.clone(),
459 )),
460 _ => Shape::List(NarrowedShape {
461 pos: pos.clone(),
462 types: NarrowingShape::Any,
463 }),
464 }
465 }
466 FuncOpDef::Filter(MapFilterOpDef { func, target, pos }) => {
467 let target_shape = target.derive_shape(symbol_table);
468 let _func_shape = func.derive_shape(symbol_table);
469 match &target_shape {
471 Shape::List(_) => target_shape,
472 Shape::Hole(_) => Shape::List(NarrowedShape {
473 pos: pos.clone(),
474 types: NarrowingShape::Any,
475 }),
476 Shape::Narrowed(NarrowedShape {
477 types: NarrowingShape::Any,
478 ..
479 }) => Shape::List(NarrowedShape {
480 pos: pos.clone(),
481 types: NarrowingShape::Any,
482 }),
483 _ => Shape::TypeErr(
484 pos.clone(),
485 format!(
486 "filter target must be a list, got {}",
487 target_shape.type_name()
488 ),
489 ),
490 }
491 }
492 FuncOpDef::Reduce(ReduceOpDef {
493 func,
494 acc,
495 target,
496 pos,
497 }) => {
498 let target_shape = target.derive_shape(symbol_table);
499 let acc_shape = acc.derive_shape(symbol_table);
500 let func_shape = func.derive_shape(symbol_table);
501 match &target_shape {
503 Shape::List(_) | Shape::Hole(_) => {}
504 Shape::Narrowed(NarrowedShape {
505 types: NarrowingShape::Any,
506 ..
507 }) => {}
508 _ => {
509 return Shape::TypeErr(
510 pos.clone(),
511 format!(
512 "reduce target must be a list, got {}",
513 target_shape.type_name()
514 ),
515 );
516 }
517 }
518 match &func_shape {
520 Shape::Func(fdef) => {
521 let narrowed = acc_shape.narrow(&fdef.ret, symbol_table);
522 match narrowed {
523 Shape::TypeErr(_, _) => acc_shape,
524 other => other,
525 }
526 }
527 _ => acc_shape,
528 }
529 }
530 }
531}
532
533impl DeriveShape for Expression {
534 fn derive_shape(&self, symbol_table: &mut BTreeMap<Rc<str>, Shape>) -> Shape {
535 match self {
536 Expression::Simple(v) => v.derive_shape(symbol_table),
537 Expression::Format(def) => Shape::Str(def.pos.clone()),
538 Expression::Not(def) => derive_not_shape(def, symbol_table),
539 Expression::Grouped(v, _pos) => v.as_ref().derive_shape(symbol_table),
540 Expression::Range(def) => Shape::List(NarrowedShape::new_with_pos(
541 vec![Shape::Int(def.start.pos().clone())],
542 def.pos.clone(),
543 )),
544 Expression::Cast(def) => match def.cast_type {
545 CastType::Int => Shape::Int(def.pos.clone()),
546 CastType::Str => Shape::Str(def.pos.clone()),
547 CastType::Float => Shape::Float(def.pos.clone()),
548 CastType::Bool => Shape::Boolean(def.pos.clone()),
549 },
550 Expression::Import(def) => Shape::Import(ImportShape::Unresolved(PositionedItem::new(
551 def.path.fragment.clone(),
552 def.path.pos.clone(),
553 ))),
554 Expression::Binary(def) => {
555 let left_shape = def.left.derive_shape(symbol_table);
556 if def.kind == BinaryExprType::DOT {
557 let shape =
558 derive_dot_expression(&def.pos, &left_shape, &def.right, symbol_table);
559 if let Expression::Simple(Value::Symbol(pi)) = def.left.as_ref() {
561 if let Shape::TypeErr(_, _) = &shape {
562 } else {
564 match &left_shape {
565 Shape::Hole(_) => {
566 let inferred = infer_container_shape_from_dot(
567 &left_shape,
568 &def.right,
569 &shape,
570 &def.pos,
571 symbol_table,
572 );
573 symbol_table.insert(pi.val.clone(), inferred);
574 }
575 _ => {}
576 }
577 }
578 }
579 shape
580 } else {
581 let right_shape = def.right.derive_shape(symbol_table);
582 match &def.kind {
583 BinaryExprType::Equal
585 | BinaryExprType::NotEqual
586 | BinaryExprType::GT
587 | BinaryExprType::LT
588 | BinaryExprType::GTEqual
589 | BinaryExprType::LTEqual
590 | BinaryExprType::REMatch
591 | BinaryExprType::NotREMatch
592 | BinaryExprType::IN
593 | BinaryExprType::IS => {
594 Shape::Boolean(def.pos.clone())
597 }
598 BinaryExprType::AND | BinaryExprType::OR => {
600 let narrowed = left_shape.narrow(&right_shape, symbol_table);
602 if let Shape::TypeErr(_, _) = &narrowed {
603 narrowed
604 } else {
605 Shape::Boolean(def.pos.clone())
606 }
607 }
608 _ => left_shape.narrow(&right_shape, symbol_table),
610 }
611 }
612 }
613 Expression::Copy(def) => derive_copy_shape(def, symbol_table),
614 Expression::Include(def) => derive_include_shape(def),
615 Expression::Call(def) => derive_call_shape(def, symbol_table),
616 Expression::Func(def) => def.derive_shape(symbol_table),
617 Expression::Select(def) => def.derive_shape(symbol_table),
618 Expression::FuncOp(def) => derive_func_op_shape(def, symbol_table),
619 Expression::Module(def) => def.derive_shape(symbol_table),
620 Expression::Fail(def) => {
621 let msg_shape = def.message.derive_shape(symbol_table);
622 match &msg_shape {
623 Shape::Str(_) | Shape::Hole(_) => {}
624 Shape::Narrowed(NarrowedShape {
625 types: NarrowingShape::Any,
626 ..
627 }) => {}
628 _ => {
629 return Shape::TypeErr(
630 def.pos.clone(),
631 format!(
632 "fail message must be a string, got {}",
633 msg_shape.type_name()
634 ),
635 );
636 }
637 }
638 Shape::Narrowed(NarrowedShape {
640 pos: def.pos.clone(),
641 types: NarrowingShape::Any,
642 })
643 }
644 Expression::Debug(def) => {
645 def.expr.derive_shape(symbol_table)
647 }
648 }
649 }
650}
651
652fn infer_container_shape_from_dot(
656 left_shape: &Shape,
657 right_expr: &Expression,
658 _field_shape: &Shape,
659 _pos: &Position,
660 _symbol_table: &mut BTreeMap<Rc<str>, Shape>,
661) -> Shape {
662 match (left_shape, right_expr) {
663 (Shape::Hole(hole_pi), Expression::Simple(Value::Symbol(pi)))
664 | (Shape::Hole(hole_pi), Expression::Simple(Value::Str(pi))) => {
665 Shape::Tuple(PositionedItem::new(
666 vec![(
667 PositionedItem::new(pi.val.clone(), pi.pos.clone()),
668 Shape::Narrowed(NarrowedShape {
669 pos: hole_pi.pos.clone(),
670 types: NarrowingShape::Any,
671 }),
672 )],
673 hole_pi.pos.clone(),
674 ))
675 }
676 (Shape::Hole(hole_pi), Expression::Simple(Value::Int(_))) => Shape::List(NarrowedShape {
677 pos: hole_pi.pos.clone(),
678 types: NarrowingShape::Any,
679 }),
680 _ => left_shape.clone(),
681 }
682}
683
684fn derive_dot_expression(
685 pos: &Position,
686 left_shape: &Shape,
687 right_expr: &Expression,
688 symbol_table: &mut BTreeMap<Rc<str>, Shape>,
689) -> Shape {
690 match (left_shape, right_expr) {
693 (
695 Shape::Tuple(tshape),
696 Expression::Binary(BinaryOpDef {
697 kind: BinaryExprType::DOT,
698 left,
699 right,
700 pos,
701 }),
702 ) => {
703 let accessor_shape = left.derive_shape(symbol_table);
705 let resolved = resolve_tuple_field(tshape, &accessor_shape, left, pos);
707 match resolved {
708 Shape::TypeErr(_, _) => resolved,
709 field_shape => {
710 derive_dot_expression(pos, &field_shape, right, symbol_table)
712 }
713 }
714 }
715 (
716 Shape::List(lshape),
717 Expression::Binary(BinaryOpDef {
718 kind: BinaryExprType::DOT,
719 left,
720 right,
721 pos,
722 }),
723 ) => {
724 let accessor_shape = left.derive_shape(symbol_table);
725 match &accessor_shape {
726 Shape::Int(_) => {
727 let elem_shape = Shape::Narrowed(lshape.clone());
729 derive_dot_expression(pos, &elem_shape, right, symbol_table)
730 }
731 Shape::Hole(_pi) => {
732 let elem_shape = Shape::Narrowed(lshape.clone());
734 derive_dot_expression(pos, &elem_shape, right, symbol_table)
735 }
736 _ => Shape::TypeErr(
737 pos.clone(),
738 format!(
739 "Lists can only be indexed by integer, got {}",
740 accessor_shape.type_name()
741 ),
742 ),
743 }
744 }
745 (
746 Shape::Narrowed(narrowed_shape),
747 Expression::Binary(BinaryOpDef {
748 kind: BinaryExprType::DOT,
749 left: _,
750 right: _,
751 pos,
752 }),
753 ) => {
754 match &narrowed_shape.types {
756 NarrowingShape::Any => {
757 Shape::Narrowed(NarrowedShape {
759 pos: pos.clone(),
760 types: NarrowingShape::Any,
761 })
762 }
763 NarrowingShape::Narrowed(types) => {
764 let mut results = Vec::new();
766 for t in types {
767 let inner_result = derive_dot_expression(pos, t, right_expr, symbol_table);
768 if let Shape::TypeErr(_, _) = &inner_result {
769 } else {
771 results.push(inner_result);
772 }
773 }
774 if results.is_empty() {
775 Shape::TypeErr(
776 pos.clone(),
777 "No candidate type is compatible with field access".to_owned(),
778 )
779 } else if results.len() == 1 {
780 results.pop().unwrap()
781 } else {
782 Shape::Narrowed(NarrowedShape::new_with_pos(results, pos.clone()))
783 }
784 }
785 }
786 }
787 (
788 Shape::Hole(_hole_pi),
789 Expression::Binary(BinaryOpDef {
790 kind: BinaryExprType::DOT,
791 left,
792 right,
793 pos,
794 }),
795 ) => {
796 let accessor_shape = left.derive_shape(symbol_table);
799 match &accessor_shape {
800 Shape::Hole(_) | Shape::Str(_) => {
802 let field_shape = Shape::Narrowed(NarrowedShape {
804 pos: pos.clone(),
805 types: NarrowingShape::Any,
806 });
807 derive_dot_expression(pos, &field_shape, right, symbol_table)
808 }
809 Shape::Int(_) => {
810 let elem_shape = Shape::Narrowed(NarrowedShape {
812 pos: pos.clone(),
813 types: NarrowingShape::Any,
814 });
815 derive_dot_expression(pos, &elem_shape, right, symbol_table)
816 }
817 _ => Shape::Narrowed(NarrowedShape {
818 pos: pos.clone(),
819 types: NarrowingShape::Any,
820 }),
821 }
822 }
823
824 (Shape::Tuple(tshape), Expression::Simple(Value::Str(pi)))
828 | (Shape::Tuple(tshape), Expression::Simple(Value::Symbol(pi))) => {
829 for (field_name, field_shape) in tshape.val.iter() {
830 if field_name.val == pi.val {
831 return field_shape.clone();
832 }
833 }
834 Shape::TypeErr(
835 pi.pos.clone(),
836 format!("Field '{}' not found in tuple", pi.val),
837 )
838 }
839
840 (Shape::Tuple(_), Expression::Simple(Value::Int(pi))) => Shape::TypeErr(
842 pi.pos.clone(),
843 "Tuples cannot be indexed by integer".to_owned(),
844 ),
845
846 (Shape::List(lshape), Expression::Simple(Value::Int(_pi))) => {
848 Shape::Narrowed(lshape.clone())
849 }
850
851 (Shape::List(_), Expression::Simple(Value::Symbol(pi)))
853 | (Shape::List(_), Expression::Simple(Value::Str(pi))) => Shape::TypeErr(
854 pi.pos.clone(),
855 "Lists cannot be accessed by field name".to_owned(),
856 ),
857
858 (Shape::Hole(hole_pi), Expression::Simple(Value::Symbol(_pi)))
860 | (Shape::Hole(hole_pi), Expression::Simple(Value::Str(_pi))) => {
861 Shape::Narrowed(NarrowedShape {
862 pos: hole_pi.pos.clone(),
863 types: NarrowingShape::Any,
864 })
865 }
866
867 (Shape::Hole(hole_pi), Expression::Simple(Value::Int(_pi))) => {
869 Shape::Narrowed(NarrowedShape {
870 pos: hole_pi.pos.clone(),
871 types: NarrowingShape::Any,
872 })
873 }
874
875 (Shape::Narrowed(nshape), Expression::Simple(Value::Symbol(pi)))
877 | (Shape::Narrowed(nshape), Expression::Simple(Value::Str(pi))) => {
878 match &nshape.types {
879 NarrowingShape::Any => Shape::Narrowed(NarrowedShape {
880 pos: pi.pos.clone(),
881 types: NarrowingShape::Any,
882 }),
883 NarrowingShape::Narrowed(types) if types.is_empty() => {
884 Shape::Narrowed(NarrowedShape {
886 pos: pi.pos.clone(),
887 types: NarrowingShape::Any,
888 })
889 }
890 NarrowingShape::Narrowed(types) => {
891 let mut results = Vec::new();
892 for t in types {
893 match t {
894 Shape::Tuple(tshape) => {
895 for (field_name, field_shape) in tshape.val.iter() {
896 if field_name.val == pi.val {
897 results.push(field_shape.clone());
898 }
899 }
900 }
901 Shape::Hole(_) => {
902 results.push(Shape::Narrowed(NarrowedShape {
903 pos: pi.pos.clone(),
904 types: NarrowingShape::Any,
905 }));
906 }
907 _ => { }
908 }
909 }
910 if results.is_empty() {
911 Shape::TypeErr(
912 pi.pos.clone(),
913 format!("No candidate type has field '{}'", pi.val),
914 )
915 } else if results.len() == 1 {
916 results.pop().unwrap()
917 } else {
918 Shape::Narrowed(NarrowedShape::new_with_pos(results, pi.pos.clone()))
919 }
920 }
921 }
922 }
923
924 (Shape::Narrowed(nshape), Expression::Simple(Value::Int(pi))) => {
926 match &nshape.types {
927 NarrowingShape::Any => Shape::Narrowed(NarrowedShape {
928 pos: pi.pos.clone(),
929 types: NarrowingShape::Any,
930 }),
931 NarrowingShape::Narrowed(types) if types.is_empty() => {
932 Shape::Narrowed(NarrowedShape {
934 pos: pi.pos.clone(),
935 types: NarrowingShape::Any,
936 })
937 }
938 NarrowingShape::Narrowed(types) => {
939 let mut results = Vec::new();
940 for t in types {
941 match t {
942 Shape::List(lshape) => {
943 results.push(Shape::Narrowed(lshape.clone()));
944 }
945 Shape::Hole(_) => {
946 results.push(Shape::Narrowed(NarrowedShape {
947 pos: pi.pos.clone(),
948 types: NarrowingShape::Any,
949 }));
950 }
951 _ => { }
952 }
953 }
954 if results.is_empty() {
955 Shape::TypeErr(
956 pi.pos.clone(),
957 "No candidate type supports integer indexing".to_owned(),
958 )
959 } else if results.len() == 1 {
960 results.pop().unwrap()
961 } else {
962 Shape::Narrowed(NarrowedShape::new_with_pos(results, pi.pos.clone()))
963 }
964 }
965 }
966 }
967
968 (_, Expression::Grouped(expr, _)) => {
970 derive_dot_expression(pos, left_shape, expr.as_ref(), symbol_table)
971 }
972
973 (Shape::Import(ImportShape::Resolved(_, tuple_fields)), _) => {
975 let tshape = PositionedItem::new(tuple_fields.clone(), pos.clone());
976 derive_dot_expression(pos, &Shape::Tuple(tshape), right_expr, symbol_table)
977 }
978
979 (Shape::Import(ImportShape::Unresolved(_)), _) => Shape::Narrowed(NarrowedShape {
981 pos: pos.clone(),
982 types: NarrowingShape::Any,
983 }),
984
985 (Shape::TypeErr(_, _), _) => left_shape.clone(),
987
988 (_, _) => Shape::TypeErr(pos.clone(), "Invalid field selector".to_owned()),
990 }
991}
992
993fn resolve_tuple_field(
995 tshape: &PositionedItem<TupleShape>,
996 _accessor_shape: &Shape,
997 accessor_expr: &Expression,
998 pos: &Position,
999) -> Shape {
1000 match accessor_expr {
1001 Expression::Simple(Value::Symbol(pi)) | Expression::Simple(Value::Str(pi)) => {
1002 for (field_name, field_shape) in tshape.val.iter() {
1003 if field_name.val == pi.val {
1004 return field_shape.clone();
1005 }
1006 }
1007 Shape::TypeErr(
1008 pos.clone(),
1009 format!("Field '{}' not found in tuple", pi.val),
1010 )
1011 }
1012 _ => {
1013 Shape::Narrowed(NarrowedShape {
1015 pos: pos.clone(),
1016 types: NarrowingShape::Any,
1017 })
1018 }
1019 }
1020}
1021
1022impl DeriveShape for Value {
1023 fn derive_shape(&self, symbol_table: &mut BTreeMap<Rc<str>, Shape>) -> Shape {
1024 match self {
1025 Value::Empty(p) => Shape::Narrowed(NarrowedShape {
1026 pos: p.clone(),
1027 types: NarrowingShape::Any,
1028 }),
1029 Value::Boolean(p) => Shape::Boolean(p.pos.clone()),
1030 Value::Int(p) => Shape::Int(p.pos.clone()),
1031 Value::Float(p) => Shape::Float(p.pos.clone()),
1032 Value::Str(p) => Shape::Str(p.pos.clone()),
1033 Value::Symbol(p) => {
1034 if let Some(s) = symbol_table.get(&p.val) {
1035 s.clone()
1036 } else {
1037 Shape::Hole(p.clone())
1038 }
1039 }
1040 Value::Tuple(flds) => derive_field_list_shape(&flds.val, &flds.pos, symbol_table),
1041 Value::List(flds) => {
1042 let mut field_shapes = Vec::new();
1043 for f in &flds.elems {
1044 field_shapes.push(f.derive_shape(symbol_table));
1045 }
1046 Shape::List(NarrowedShape::new_with_pos(field_shapes, flds.pos.clone()))
1047 }
1048 }
1049 }
1050}
1051
1052fn derive_field_list_shape(
1053 flds: &Vec<(super::Token, Option<Expression>, Expression)>,
1054 pos: &Position,
1055 symbol_table: &mut BTreeMap<Rc<str>, Shape>,
1056) -> Shape {
1057 let mut field_shapes = Vec::new();
1058 for (ref tok, ref constraint, ref expr) in flds {
1059 let value_shape = expr.derive_shape(symbol_table);
1060 let shape = if let Some(c) = constraint {
1061 let constraint_shape = c.derive_shape(symbol_table);
1062 let narrowed = value_shape.narrow(&constraint_shape, symbol_table);
1063 if let Shape::TypeErr(_, _) = &narrowed {
1064 return narrowed;
1065 }
1066 narrowed
1067 } else {
1068 value_shape
1069 };
1070 field_shapes.push((
1071 PositionedItem::new(tok.fragment.clone(), tok.pos.clone()),
1072 shape,
1073 ));
1074 }
1075 Shape::Tuple(PositionedItem::new(field_shapes, pos.clone()))
1076}
1077
1078pub struct Checker {
1079 symbol_table: BTreeMap<Rc<str>, Shape>,
1080 err_stack: Vec<BuildError>,
1081 shape_stack: Vec<Shape>,
1082 nested_depth: usize,
1086 strict: bool,
1088 working_dir: Option<PathBuf>,
1090 shape_cache: Rc<RefCell<BTreeMap<PathBuf, Shape>>>,
1092 import_stack: Vec<PathBuf>,
1094}
1095
1096impl Checker {
1097 pub fn new() -> Self {
1098 return Self {
1099 symbol_table: BTreeMap::new(),
1100 err_stack: Vec::new(),
1101 shape_stack: Vec::new(),
1102 nested_depth: 0,
1103 strict: true,
1104 working_dir: None,
1105 shape_cache: Rc::new(RefCell::new(BTreeMap::new())),
1106 import_stack: Vec::new(),
1107 };
1108 }
1109
1110 pub fn with_strict(mut self, strict: bool) -> Self {
1111 self.strict = strict;
1112 self
1113 }
1114
1115 pub fn with_working_dir<P: Into<PathBuf>>(mut self, dir: P) -> Self {
1116 self.working_dir = Some(dir.into());
1117 self
1118 }
1119
1120 pub fn with_shape_cache(mut self, cache: Rc<RefCell<BTreeMap<PathBuf, Shape>>>) -> Self {
1121 self.shape_cache = cache;
1122 self
1123 }
1124
1125 pub fn with_import_stack(mut self, stack: Vec<PathBuf>) -> Self {
1126 self.import_stack = stack;
1127 self
1128 }
1129
1130 pub fn with_symbol_table(mut self, symbol_table: BTreeMap<Rc<str>, Shape>) -> Self {
1131 self.symbol_table = symbol_table;
1132 self
1133 }
1134
1135 pub fn pop_shape(&mut self) -> Option<Shape> {
1136 self.shape_stack.pop()
1137 }
1138
1139 fn push_shape_or_err(&mut self, shape: Shape) {
1140 if let Shape::TypeErr(pos, msg) = &shape {
1141 self.err_stack.push(BuildError::with_pos(
1142 msg.clone(),
1143 ErrorType::TypeFail,
1144 pos.clone(),
1145 ));
1146 } else {
1147 self.shape_stack.push(shape);
1148 }
1149 }
1150
1151 fn resolve_import(&mut self, path: &str, pos: &Position) -> Shape {
1155 let working_dir = match &self.working_dir {
1156 Some(dir) => dir.clone(),
1157 None => {
1158 return Shape::Import(ImportShape::Unresolved(PositionedItem::new(
1159 path.into(),
1160 pos.clone(),
1161 )));
1162 }
1163 };
1164
1165 let resolved_path = working_dir.join(path);
1166
1167 if let Some(cached) = self.shape_cache.borrow().get(&resolved_path) {
1169 return cached.clone();
1170 }
1171
1172 if self.import_stack.contains(&resolved_path) {
1174 return Shape::TypeErr(
1175 pos.clone(),
1176 format!("Import cycle detected: {}", resolved_path.display()),
1177 );
1178 }
1179
1180 let contents = match std::fs::read_to_string(&resolved_path) {
1182 Ok(c) => c,
1183 Err(_) => {
1184 return Shape::Import(ImportShape::Unresolved(PositionedItem::new(
1187 path.into(),
1188 pos.clone(),
1189 )));
1190 }
1191 };
1192
1193 let iter = OffsetStrIter::new(&contents).with_src_file(&resolved_path);
1195 let mut stmts = match parse(iter, None) {
1196 Ok(stmts) => stmts,
1197 Err(_) => {
1198 return Shape::TypeErr(
1199 pos.clone(),
1200 format!("Failed to parse imported file: {}", resolved_path.display()),
1201 );
1202 }
1203 };
1204
1205 let import_dir = resolved_path.parent().map(|p| p.to_path_buf());
1207 let mut import_stack = self.import_stack.clone();
1208 import_stack.push(resolved_path.clone());
1209
1210 let mut child_checker = Checker::new()
1211 .with_shape_cache(self.shape_cache.clone())
1212 .with_import_stack(import_stack)
1213 .with_strict(self.strict);
1214 if let Some(dir) = import_dir {
1215 child_checker = child_checker.with_working_dir(dir);
1216 }
1217
1218 child_checker.walk_statement_list(stmts.iter_mut().collect());
1219
1220 let symbol_table = match child_checker.result() {
1222 Ok(syms) => syms,
1223 Err(err) => {
1224 return Shape::TypeErr(
1226 err.pos.unwrap_or_else(|| pos.clone()),
1227 format!(
1228 "Type error in imported file {}: {}",
1229 resolved_path.display(),
1230 err.msg
1231 ),
1232 );
1233 }
1234 };
1235
1236 let tuple_fields: TupleShape = symbol_table
1238 .iter()
1239 .map(|(name, shape)| {
1240 (
1241 PositionedItem::new(name.clone(), pos.clone()),
1242 shape.clone(),
1243 )
1244 })
1245 .collect();
1246
1247 let resolved = Shape::Import(ImportShape::Resolved(pos.clone(), tuple_fields));
1248
1249 self.shape_cache
1251 .borrow_mut()
1252 .insert(resolved_path, resolved.clone());
1253
1254 resolved
1255 }
1256
1257 pub fn result(self) -> Result<BTreeMap<Rc<str>, Shape>, BuildError> {
1260 if self.err_stack.is_empty() {
1261 Ok(self.symbol_table)
1262 } else {
1263 Err(self.err_stack.into_iter().next().unwrap())
1266 }
1267 }
1268}
1269
1270impl Visitor for Checker {
1271 fn visit_import(&mut self, _i: &mut ImportDef) {
1272 }
1274
1275 fn leave_import(&mut self) {
1276 }
1278
1279 fn visit_include(&mut self, _i: &mut IncludeDef) {
1280 }
1282
1283 fn leave_include(&mut self) {
1284 }
1286
1287 fn visit_fail(&mut self, _f: &mut FailDef) {
1288 }
1290
1291 fn leave_fail(&mut self) {
1292 }
1294
1295 fn visit_expression(&mut self, expr: &mut Expression) {
1298 if matches!(expr, Expression::Module(_)) {
1301 self.nested_depth += 1;
1302 }
1303 }
1304
1305 fn leave_expression(&mut self, expr: &Expression) {
1306 if matches!(expr, Expression::Module(_)) {
1307 self.nested_depth -= 1;
1308 }
1309 }
1310
1311 fn visit_statement(&mut self, stmt: &mut Statement) {
1312 if self.nested_depth > 0 {
1315 return;
1316 }
1317 match stmt {
1318 Statement::Let(def) => {
1319 let name = def.name.fragment.clone();
1320 let mut shape = def.value.derive_shape(&mut self.symbol_table);
1321 if let Shape::Import(ImportShape::Unresolved(pi)) = &shape {
1323 shape = self.resolve_import(&pi.val, &pi.pos);
1324 }
1325 if let Some(ref constraint_expr) = def.constraint {
1327 let constraint_shape = constraint_expr.derive_shape(&mut self.symbol_table);
1328 let narrowed = shape.narrow(&constraint_shape, &mut self.symbol_table);
1329 if let Shape::TypeErr(pos, msg) = &narrowed {
1330 self.err_stack.push(BuildError::with_pos(
1331 msg.clone(),
1332 ErrorType::TypeFail,
1333 pos.clone(),
1334 ));
1335 return;
1336 }
1337 shape = narrowed;
1338 }
1339 if let Shape::TypeErr(pos, msg) = &shape {
1340 self.err_stack.push(BuildError::with_pos(
1341 msg.clone(),
1342 ErrorType::TypeFail,
1343 pos.clone(),
1344 ));
1345 } else {
1346 self.symbol_table.insert(name.clone(), shape.clone());
1347 self.shape_stack.push(shape);
1348 }
1349 }
1350 Statement::Assert(_pos, ref expr) => {
1351 let shape = expr.derive_shape(&mut self.symbol_table);
1352 self.push_shape_or_err(shape);
1355 }
1356 Statement::Output(_pos, _tok, ref expr) => {
1357 let shape = expr.derive_shape(&mut self.symbol_table);
1358 self.push_shape_or_err(shape);
1359 }
1360 Statement::Print(_pos, _tok, ref expr) => {
1361 let shape = expr.derive_shape(&mut self.symbol_table);
1362 self.push_shape_or_err(shape);
1363 }
1364 Statement::Expression(ref expr) => {
1365 let shape = expr.derive_shape(&mut self.symbol_table);
1366 self.push_shape_or_err(shape);
1367 }
1368 }
1369 }
1370
1371 fn leave_statement(&mut self, _stmt: &Statement) {
1372 }
1374}
1375
1376#[cfg(test)]
1377mod test;