diff --git a/schala-lang/language/src/reduced_ir/mod.rs b/schala-lang/language/src/reduced_ir/mod.rs index ca61c74..de1ae45 100644 --- a/schala-lang/language/src/reduced_ir/mod.rs +++ b/schala-lang/language/src/reduced_ir/mod.rs @@ -4,7 +4,7 @@ use crate::{ ast, builtin::Builtin, symbol_table::{DefId, SymbolSpec, SymbolTable}, - type_inference::TypeContext, + type_inference::{TypeContext, TypeId}, }; mod test; @@ -135,23 +135,14 @@ impl<'a, 'b> Reducer<'a, 'b> { }), NamedStruct { name, fields } => { self.symbol_table.debug(); + println!("Namedstruct name {} id: {}", name, name.id); let symbol = self.symbol_table.lookup_symbol(&name.id).unwrap(); let (tag, type_id) = match symbol.spec() { SymbolSpec::RecordConstructor { tag, members: _, type_id } => (tag, type_id), e => return Expression::ReductionError(format!("Bad symbol for NamedStruct: {:?}", e)), }; - // Eventually, the ReducedIR should decide what field ordering is optimal. - // For now, just do it alphabetically. - let mut field_order: Vec = self - .type_context - .lookup_record_members(&type_id, tag) - .unwrap() - .iter() - .map(|(field, _type_id)| field) - .cloned() - .collect(); - field_order.sort_unstable(); + let field_order = compute_field_orderings(&self.type_context, &type_id, tag).unwrap(); let mut field_map = HashMap::new(); for (name, expr) in fields.iter() { @@ -179,7 +170,8 @@ impl<'a, 'b> Reducer<'a, 'b> { WhileExpression { .. } => Expression::ReductionError("While expr not implemented".to_string()), ForExpression { .. } => Expression::ReductionError("For expr not implemented".to_string()), ListLiteral { .. } => Expression::ReductionError("ListLiteral expr not implemented".to_string()), - Access { .. } => unimplemented!(), + Access { name, expr } => + Expression::Access { name: name.as_ref().to_string(), expr: Box::new(self.expression(expr)) }, } } @@ -389,3 +381,21 @@ impl ast::Pattern { }) } } + +/// Given the type context and a variant, compute what order the fields on it were stored. +/// This needs to be public until type-checking is fully implemented because the type information +/// is only available at runtime. +pub fn compute_field_orderings( + type_context: &TypeContext, + type_id: &TypeId, + tag: u32, +) -> Option> { + // Eventually, the ReducedIR should decide what field ordering is optimal. + // For now, just do it alphabetically. + + let record_members = type_context.lookup_record_members(type_id, tag)?; + let mut field_order: Vec = + record_members.iter().map(|(field, _type_id)| field).cloned().collect(); + field_order.sort_unstable(); + Some(field_order) +} diff --git a/schala-lang/language/src/reduced_ir/types.rs b/schala-lang/language/src/reduced_ir/types.rs index 47fd4b7..d803c5b 100644 --- a/schala-lang/language/src/reduced_ir/types.rs +++ b/schala-lang/language/src/reduced_ir/types.rs @@ -50,6 +50,7 @@ pub enum Expression { Tuple(Vec), Lookup(Lookup), Assign { lval: DefId, rval: Box }, + Access { name: String, expr: Box }, Callable(Callable), Call { f: Box, args: Vec }, Conditional { cond: Box, then_clause: Vec, else_clause: Vec }, diff --git a/schala-lang/language/src/tree_walk_eval/evaluator.rs b/schala-lang/language/src/tree_walk_eval/evaluator.rs index 8beabc5..67b9953 100644 --- a/schala-lang/language/src/tree_walk_eval/evaluator.rs +++ b/schala-lang/language/src/tree_walk_eval/evaluator.rs @@ -126,6 +126,28 @@ impl<'a, 'b> Evaluator<'a, 'b> { Expression::CaseMatch { box cond, alternatives } => self.case_match_expression(cond, alternatives)?, Expression::ReductionError(e) => return Err(e.into()), + Expression::Access { name, box expr } => { + let expr = self.expression(expr)?; + match expr { + Primitive::Object { items, ordered_fields: Some(ordered_fields), .. } => { + let idx = match ordered_fields.iter().position(|s| s == &name) { + Some(idx) => idx, + None => return Err(format!("Field `{}` not found", name).into()), + }; + + let item = match items.get(idx) { + Some(item) => item, + None => return Err(format!("Field lookup `{}` failed", name).into()), + }; + + item.clone() + } + e => + return Err( + format!("Trying to do a field lookup on a non-object value: {:?}", e).into() + ), + } + } }) } diff --git a/schala-lang/language/src/tree_walk_eval/test.rs b/schala-lang/language/src/tree_walk_eval/test.rs index eee4e0d..7b86229 100644 --- a/schala-lang/language/src/tree_walk_eval/test.rs +++ b/schala-lang/language/src/tree_walk_eval/test.rs @@ -1,6 +1,6 @@ #![cfg(test)] -use test_case::test_case; use pretty_assertions::assert_eq; +use test_case::test_case; use crate::{ symbol_table::SymbolTable, @@ -367,3 +367,14 @@ import Option::* let x = Some(9); if x is Some(q) then { q } else { 0 }"#; eval_assert(source, "9"); } + +#[test] +fn accessors() { + let source = r#" +type Klewos = Klewos { a: Int, b: String } +let value = Klewos::Klewos { a: 50, b: "nah" } +(value.a, value.b) + "#; + + eval_assert(source, r#"(50, "nah")"#); +}