Compare commits

...

2 Commits

Author SHA1 Message Date
Greg Shuflin
de741448e0 Accessors 2021-10-30 22:45:08 -07:00
Greg Shuflin
f0e4b50c99 Implement Access AST node
For name.value lookups
2021-10-30 21:22:15 -07:00
9 changed files with 1201 additions and 1091 deletions

View File

@ -196,6 +196,7 @@ pub enum ExpressionKind {
WhileExpression { condition: Option<Box<Expression>>, body: Block },
ForExpression { enumerators: Vec<Enumerator>, body: Box<ForBody> },
Lambda { params: Vec<FormalParam>, type_anno: Option<TypeIdentifier>, body: Block },
Access { name: Rc<String>, expr: Box<Expression> },
ListLiteral(Vec<Expression>),
}

View File

@ -128,6 +128,9 @@ pub fn walk_expression<V: ASTVisitor>(v: &mut V, expr: &Expression) {
Lambda { params: _, type_anno: _, body } => {
walk_block(v, body);
}
Access { name: _, expr } => {
walk_expression(v, expr);
}
ListLiteral(exprs) =>
for expr in exprs {
walk_expression(v, expr);

View File

@ -120,6 +120,7 @@ fn render_expression(expr: &Expression, indent: usize, buf: &mut String) {
newline(buf);
do_indent(indent, buf);
}
Access { .. } => buf.push_str("<access-expr>"),
ListLiteral(..) => buf.push_str("<list-literal>"),
}
buf.push(')');

File diff suppressed because it is too large Load Diff

View File

@ -203,11 +203,36 @@ fn operators() {
#[test]
fn accessors() {
/*
assert_expr!("a.b");
assert_expr!("a.b.c.d()");
assert_expr!("a.b().c.d()");
*/
use ExpressionKind::*;
assert_expr!("a.b", expr(Access { name: rc("b"), expr: bx(expr(Value(qn!(a)))) }));
assert_expr!(
"a.b.c",
expr(Access {
name: rc("c"),
expr: bx(expr(Access { name: rc("b"), expr: bx(expr(Value(qn!(a)))) }))
})
);
assert_expr!(
"a.b.c(3)",
expr(Call {
f: bx(expr(Access {
name: rc("c"),
expr: bx(expr(Access { name: rc("b"), expr: bx(expr(Value(qn!(a)))) }))
})),
arguments: vec![InvocationArgument::Positional(expr(NatLiteral(3)))],
})
);
assert_expr!(
"a.b().c",
expr(Access {
name: rc("c"),
expr: bx(expr(Call {
f: bx(expr(Access { name: rc("b"), expr: bx(expr(Value(qn!(a)))) })),
arguments: vec![]
}))
})
);
}
#[test]

View File

@ -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<String> = 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,6 +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 { name, expr } =>
Expression::Access { name: name.as_ref().to_string(), expr: Box::new(self.expression(expr)) },
}
}
@ -388,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<Vec<String>> {
// 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<String> =
record_members.iter().map(|(field, _type_id)| field).cloned().collect();
field_order.sort_unstable();
Some(field_order)
}

View File

@ -50,6 +50,7 @@ pub enum Expression {
Tuple(Vec<Expression>),
Lookup(Lookup),
Assign { lval: DefId, rval: Box<Expression> },
Access { name: String, expr: Box<Expression> },
Callable(Callable),
Call { f: Box<Expression>, args: Vec<Expression> },
Conditional { cond: Box<Expression>, then_clause: Vec<Statement>, else_clause: Vec<Statement> },

View File

@ -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()
),
}
}
})
}

View File

@ -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")"#);
}