diff --git a/examples/records.myc b/examples/records.myc index 5aae83e..17ff166 100644 --- a/examples/records.myc +++ b/examples/records.myc @@ -1,5 +1,5 @@ -;; Benchmark: 1.9us -;; Benchmark-Repeat: 1211 +;; Benchmark: 1.3us +;; Benchmark-Repeat: 1599 ;; Output: ["Alice" 101 :admin "Zürich" ["Alice" "Bob"] true] ; --------------------------------------------------------- diff --git a/src/ast/compiler/analyzer.rs b/src/ast/compiler/analyzer.rs index 9a2d0e1..f9be2a4 100644 --- a/src/ast/compiler/analyzer.rs +++ b/src/ast/compiler/analyzer.rs @@ -265,16 +265,15 @@ impl<'a> Analyzer<'a> { ) } - BoundKind::Record { fields } => { - let mut new_fields = Vec::with_capacity(fields.len()); + BoundKind::Record { layout, values } => { + let mut new_values = Vec::with_capacity(values.len()); let mut p = Purity::Pure; - for (k, v) in fields { - let km = self.visit(Rc::new(k.clone())); + for v in values { let vm = self.visit(Rc::new(v.clone())); - p = p.min(km.ty.purity).min(vm.ty.purity); - new_fields.push((km, vm)); + p = p.min(vm.ty.purity); + new_values.push(vm); } - (BoundKind::Record { fields: new_fields }, p) + (BoundKind::Record { layout: layout.clone(), values: new_values }, p) } BoundKind::Expansion { @@ -348,9 +347,8 @@ impl NodeExt for BoundKind { f(e); } } - BoundKind::Record { fields } => { - for (k, v) in fields { - f(k); + BoundKind::Record { values, .. } => { + for v in values { f(v); } } diff --git a/src/ast/compiler/binder.rs b/src/ast/compiler/binder.rs index 9a55ba1..119db35 100644 --- a/src/ast/compiler/binder.rs +++ b/src/ast/compiler/binder.rs @@ -374,14 +374,28 @@ impl Binder { } UntypedKind::Record { fields } => { - let mut bound_fields = Vec::new(); + let mut bound_values = Vec::new(); + let mut layout_fields = Vec::new(); + for (k, v) in fields { - bound_fields.push((self.bind(k)?, self.bind(v)?)); + let key_node = self.bind(k)?; + let val_node = self.bind(v)?; + + if let BoundKind::Constant(crate::ast::types::Value::Keyword(kw)) = key_node.kind { + layout_fields.push((kw, crate::ast::types::StaticType::Any)); + } else { + return Err(format!("Record keys must be keywords, found at {:?}", key_node.identity.location)); + } + bound_values.push(val_node); } + + let layout = crate::ast::types::RecordLayout::get_or_create(layout_fields); + Ok(self.make_node( node.identity.clone(), BoundKind::Record { - fields: bound_fields, + layout, + values: bound_values, }, )) } diff --git a/src/ast/compiler/bound_nodes.rs b/src/ast/compiler/bound_nodes.rs index b5b1766..6d0103a 100644 --- a/src/ast/compiler/bound_nodes.rs +++ b/src/ast/compiler/bound_nodes.rs @@ -145,7 +145,8 @@ pub enum BoundKind { }, Record { - fields: Vec>, + layout: std::sync::Arc, + values: Vec>, }, /// An expanded macro call, preserving the original call for debugging and UI. @@ -250,7 +251,9 @@ where (BoundKind::Again { args: aa }, BoundKind::Again { args: ab }) => aa == ab, (BoundKind::Block { exprs: ea }, BoundKind::Block { exprs: eb }) => ea == eb, (BoundKind::Tuple { elements: ea }, BoundKind::Tuple { elements: eb }) => ea == eb, - (BoundKind::Record { fields: fa }, BoundKind::Record { fields: fb }) => fa == fb, + (BoundKind::Record { layout: la, values: va }, BoundKind::Record { layout: lb, values: vb }) => { + std::sync::Arc::ptr_eq(la, lb) && va == vb + } ( BoundKind::Expansion { original_call: oa, @@ -303,7 +306,7 @@ impl BoundKind { BoundKind::Again { .. } => "AGAIN".to_string(), BoundKind::Block { .. } => "BLOCK".to_string(), BoundKind::Tuple { elements } => format!("TUPLE({})", elements.len()), - BoundKind::Record { fields } => format!("RECORD({})", fields.len()), + BoundKind::Record { values, .. } => format!("RECORD({})", values.len()), BoundKind::Expansion { .. } => "EXPANSION".to_string(), BoundKind::Extension(ext) => ext.display_name(), } diff --git a/src/ast/compiler/captures.rs b/src/ast/compiler/captures.rs index 18ce6c6..44c9fdc 100644 --- a/src/ast/compiler/captures.rs +++ b/src/ast/compiler/captures.rs @@ -108,16 +108,12 @@ impl CapturePass { }; } - BoundKind::Record { fields } => { + BoundKind::Record { layout, values } => { node.kind = BoundKind::Record { - fields: fields + layout, + values: values .into_iter() - .map(|(k, v)| { - ( - Self::transform(k, capture_map), - Self::transform(v, capture_map), - ) - }) + .map(|v| Self::transform(v, capture_map)) .collect(), }; } diff --git a/src/ast/compiler/dumper.rs b/src/ast/compiler/dumper.rs index 8b79885..ee550df 100644 --- a/src/ast/compiler/dumper.rs +++ b/src/ast/compiler/dumper.rs @@ -222,11 +222,10 @@ impl Dumper { self.indent -= 1; } - BoundKind::Record { fields } => { - self.log("Record", node); + BoundKind::Record { layout, values } => { + self.log(&format!("Record (Layout: {} fields)", layout.fields.len()), node); self.indent += 1; - for (k, v) in fields { - self.visit(k); + for v in values { self.visit(v); } self.indent -= 1; diff --git a/src/ast/compiler/lambda_collector.rs b/src/ast/compiler/lambda_collector.rs index 80e5ef1..ab97a16 100644 --- a/src/ast/compiler/lambda_collector.rs +++ b/src/ast/compiler/lambda_collector.rs @@ -82,9 +82,8 @@ impl<'a, T: Clone> LambdaCollector<'a, T> { } } - BoundKind::Record { fields } => { - for (k, v) in fields { - self.visit(k); + BoundKind::Record { values, .. } => { + for v in values { self.visit(v); } } diff --git a/src/ast/compiler/optimizer/engine.rs b/src/ast/compiler/optimizer/engine.rs index cb55a04..cca23c3 100644 --- a/src/ast/compiler/optimizer/engine.rs +++ b/src/ast/compiler/optimizer/engine.rs @@ -543,17 +543,17 @@ impl Optimizer { .collect(); (BoundKind::Tuple { elements }, node.ty.clone()) } - BoundKind::Record { ref fields } => { - let mapped_fields: Vec<_> = fields + BoundKind::Record { ref layout, ref values } => { + let mapped_values: Vec<_> = values .iter() - .map(|(k, v)| (self.visit_node(k.clone(), sub, path), self.visit_node(v.clone(), sub, path))) + .map(|v| self.visit_node(v.clone(), sub, path)) .collect(); - if self.enabled && let Some(folded) = folder.try_fold_record(&mapped_fields, &node) { + if self.enabled && let Some(folded) = folder.try_fold_record(layout, &mapped_values, &node) { return folded; } - (BoundKind::Record { fields: mapped_fields }, node.ty.clone()) + (BoundKind::Record { layout: layout.clone(), values: mapped_values }, node.ty.clone()) } BoundKind::Expansion { ref original_call, diff --git a/src/ast/compiler/optimizer/folder.rs b/src/ast/compiler/optimizer/folder.rs index d346f04..d126032 100644 --- a/src/ast/compiler/optimizer/folder.rs +++ b/src/ast/compiler/optimizer/folder.rs @@ -50,27 +50,21 @@ impl<'a> Folder<'a> { pub fn try_fold_record( &self, - fields: &[(AnalyzedNode, AnalyzedNode)], + layout: &std::sync::Arc, + values: &[AnalyzedNode], template: &AnalyzedNode, ) -> Option { - let mut layout_fields = Vec::with_capacity(fields.len()); - let mut values = Vec::with_capacity(fields.len()); + let mut constant_values = Vec::with_capacity(values.len()); - for (k_node, v_node) in fields { - if let BoundKind::Constant(Value::Keyword(kw)) = &k_node.kind { - if let BoundKind::Constant(val) = &v_node.kind { - layout_fields.push((*kw, val.static_type())); - values.push(val.clone()); - } else { - return None; - } + for v_node in values { + if let BoundKind::Constant(val) = &v_node.kind { + constant_values.push(val.clone()); } else { return None; } } - let layout = RecordLayout::get_or_create(layout_fields); - let record_val = Value::Record(layout, Rc::new(values)); + let record_val = Value::Record(layout.clone(), Rc::new(constant_values)); Some(self.make_constant_node(record_val, template)) } diff --git a/src/ast/compiler/optimizer/substitution_map.rs b/src/ast/compiler/optimizer/substitution_map.rs index e8034ae..e6575a7 100644 --- a/src/ast/compiler/optimizer/substitution_map.rs +++ b/src/ast/compiler/optimizer/substitution_map.rs @@ -169,17 +169,12 @@ impl SubstitutionMap { .collect(); (BoundKind::Tuple { elements }, node.ty.clone()) } - BoundKind::Record { fields } => { - let fields = fields + BoundKind::Record { layout, values } => { + let values = values .into_iter() - .map(|(k, v)| { - ( - self.reindex_upvalues(k, mapping), - self.reindex_upvalues(v, mapping), - ) - }) + .map(|v| self.reindex_upvalues(v, mapping)) .collect(); - (BoundKind::Record { fields }, node.ty.clone()) + (BoundKind::Record { layout, values }, node.ty.clone()) } k => (k, node.ty.clone()), }; diff --git a/src/ast/compiler/optimizer/utils.rs b/src/ast/compiler/optimizer/utils.rs index ec1dc96..e883b39 100644 --- a/src/ast/compiler/optimizer/utils.rs +++ b/src/ast/compiler/optimizer/utils.rs @@ -138,9 +138,8 @@ impl UsageInfo { self.collect(e); } } - BoundKind::Record { fields } => { - for (k, v) in fields { - self.collect(k); + BoundKind::Record { values, .. } => { + for v in values { self.collect(v); } } diff --git a/src/ast/compiler/specializer.rs b/src/ast/compiler/specializer.rs index 82ae0d2..75c7f7a 100644 --- a/src/ast/compiler/specializer.rs +++ b/src/ast/compiler/specializer.rs @@ -131,12 +131,12 @@ impl Specializer { let elements = elements.into_iter().map(|e| self.visit_node(e)).collect(); (BoundKind::Tuple { elements }, node.ty.clone()) } - BoundKind::Record { fields } => { - let fields = fields + BoundKind::Record { layout, values } => { + let values = values .into_iter() - .map(|(k, v)| (self.visit_node(k), self.visit_node(v))) + .map(|v| self.visit_node(v)) .collect(); - (BoundKind::Record { fields }, node.ty.clone()) + (BoundKind::Record { layout, values }, node.ty.clone()) } BoundKind::Expansion { original_call, diff --git a/src/ast/compiler/tco.rs b/src/ast/compiler/tco.rs index d7f55e9..7bcc2ff 100644 --- a/src/ast/compiler/tco.rs +++ b/src/ast/compiler/tco.rs @@ -115,17 +115,15 @@ impl TCO { pattern: Box::new(Self::transform(Rc::new((**pattern).clone()), false)), value: Box::new(Self::transform(Rc::new((**value).clone()), false)), }, - BoundKind::Record { fields } => { - let new_fields = fields + BoundKind::Record { layout, values } => { + let new_values = values .iter() - .map(|(k, v)| { - ( - Self::transform(Rc::new(k.clone()), false), - Self::transform(Rc::new(v.clone()), false), - ) - }) + .map(|v| Self::transform(Rc::new(v.clone()), false)) .collect(); - BoundKind::Record { fields: new_fields } + BoundKind::Record { + layout: layout.clone(), + values: new_values, + } } BoundKind::Tuple { elements } => { let new_elements = elements diff --git a/src/ast/compiler/type_checker.rs b/src/ast/compiler/type_checker.rs index a8a9c48..13d0332 100644 --- a/src/ast/compiler/type_checker.rs +++ b/src/ast/compiler/type_checker.rs @@ -563,25 +563,23 @@ impl TypeChecker { ) } - BoundKind::Record { fields } => { - let mut typed_fields = Vec::with_capacity(fields.len()); - let mut fields_ty = Vec::with_capacity(fields.len()); - for (k, v) in fields { - let kt = self.check_node(k, ctx)?; + BoundKind::Record { layout, values } => { + let mut typed_values = Vec::with_capacity(values.len()); + let mut fields_ty = Vec::with_capacity(values.len()); + + for (i, v) in values.into_iter().enumerate() { let vt = self.check_node(v, ctx)?; - - if let BoundKind::Constant(crate::ast::types::Value::Keyword(kw)) = &kt.kind { - fields_ty.push((*kw, vt.ty.clone())); - } - - typed_fields.push((kt, vt)); + fields_ty.push((layout.fields[i].0, vt.ty.clone())); + typed_values.push(vt); } - let layout = crate::ast::types::RecordLayout::get_or_create(fields_ty); + + let new_layout = crate::ast::types::RecordLayout::get_or_create(fields_ty); ( BoundKind::Record { - fields: typed_fields, + layout: new_layout.clone(), + values: typed_values, }, - StaticType::Record(layout), + StaticType::Record(new_layout), ) } diff --git a/src/ast/vm.rs b/src/ast/vm.rs index 0da56ad..c1b0a3c 100644 --- a/src/ast/vm.rs +++ b/src/ast/vm.rs @@ -526,20 +526,12 @@ impl VM { } Ok(Value::make_tuple(vals)) } - BoundKind::Record { fields } => { - let mut keys = Vec::with_capacity(fields.len()); - let mut values = Vec::with_capacity(fields.len()); - for (k, v) in fields { - let key = self.eval_internal(obs, k)?; - let val = self.eval_internal(obs, v)?; - if let Value::Keyword(kw) = key { - keys.push(kw); - values.push(val); - } else { - return Err(format!("Record key must be keyword, got {}", key)); - } + BoundKind::Record { layout, values } => { + let mut evaluated_values = Vec::with_capacity(values.len()); + for v in values { + evaluated_values.push(self.eval_internal(obs, v)?); } - Ok(Value::make_record(keys, values)) + Ok(Value::Record(layout.clone(), std::rc::Rc::new(evaluated_values))) } BoundKind::Expansion { bound_expanded, .. } => self.eval_internal(obs, bound_expanded), BoundKind::Extension(ext) => Err(format!(