Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Structs: Condenser & Evaluator #1911

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 22 additions & 4 deletions pil-analyzer/src/condenser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,17 @@ use powdr_ast::{
types::{ArrayType, Type},
visitor::{AllChildren, ExpressionVisitable},
ArrayLiteral, BinaryOperation, BlockExpression, FunctionCall, FunctionKind,
LambdaExpression, LetStatementInsideBlock, Number, Pattern, SourceReference,
TraitImplementation, TypedExpression, UnaryOperation,
LambdaExpression, LetStatementInsideBlock, NamedExpression, Number, Pattern,
SourceReference, StructExpression, TraitImplementation, TypedExpression, UnaryOperation,
},
};
use powdr_number::{BigUint, FieldElement};
use powdr_parser_util::SourceRef;

use crate::{
evaluator::{
self, evaluate_function_call, Closure, Definitions, EnumValue, EvalError, SymbolLookup,
Value,
self, evaluate_function_call, Closure, Definitions, EnumValue, EvalError, StructValue,
SymbolLookup, Value,
},
statement_processor::Counters,
};
Expand Down Expand Up @@ -1102,6 +1102,24 @@ fn try_value_to_expression<T: FieldElement>(value: &Value<'_, T>) -> Result<Expr
"Converting builtin functions to expressions not supported.".to_string(),
))
}
Value::Struct(StructValue { name, fields }) => {
let name = Reference::Poly(PolynomialReference {
name: name.to_string(),
// We do not know the type args here.
type_args: None,
});
let fields = fields
.iter()
.map(|(name, value)| {
Ok(NamedExpression {
name: name.to_string(),
body: Box::new(try_value_to_expression(value)?),
})
})
.collect::<Result<Vec<_>, _>>()?;

StructExpression { name, fields }.into()
}
Value::Expression(e) => try_algebraic_expression_to_expression(e)?,
})
}
69 changes: 65 additions & 4 deletions pil-analyzer/src/evaluator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ use powdr_ast::{
types::{ArrayType, Type, TypeScheme},
ArrayLiteral, BinaryOperation, BinaryOperator, BlockExpression, EnumDeclaration,
FunctionCall, IfExpression, IndexAccess, LambdaExpression, LetStatementInsideBlock,
MatchArm, MatchExpression, Number, Pattern, StatementInsideBlock, UnaryOperation,
UnaryOperator,
MatchArm, MatchExpression, Number, Pattern, StatementInsideBlock, StructExpression,
UnaryOperation, UnaryOperator,
},
};
use powdr_number::{BigInt, BigUint, FieldElement, LargeInt};
Expand Down Expand Up @@ -147,6 +147,7 @@ pub enum Value<'a, T> {
Closure(Closure<'a, T>),
TypeConstructor(TypeConstructorValue<'a>),
Enum(EnumValue<'a, T>),
Struct(StructValue<'a, T>),
BuiltinFunction(BuiltinFunction),
Expression(AlgebraicExpression<T>),
}
Expand Down Expand Up @@ -224,6 +225,7 @@ impl<'a, T: FieldElement> Value<'a, T> {
Value::Closure(c) => c.type_formatted(),
Value::TypeConstructor(tc) => tc.type_formatted(),
Value::Enum(enum_val) => enum_val.type_formatted(),
Value::Struct(struct_val) => struct_val.type_formatted(),
Value::BuiltinFunction(b) => format!("builtin_{b:?}"),
Value::Expression(_) => "expr".to_string(),
}
Expand Down Expand Up @@ -344,6 +346,32 @@ impl<'a, T: Display> Display for EnumValue<'a, T> {
}
}

#[derive(Clone, Debug)]
pub struct StructValue<'a, T> {
pub name: &'a str,
pub fields: HashMap<&'a str, Arc<Value<'a, T>>>,
}

impl<'a, T: Display> StructValue<'a, T> {
pub fn type_formatted(&self) -> String {
self.name.to_string()
}
}

impl<'a, T: Display> Display for StructValue<'a, T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.name)?;
if !self.fields.is_empty() {
write!(f, "{{")?;
for (name, value) in &self.fields {
write!(f, "{name}: {value}, ")?;
}
write!(f, "}}")?;
}
Ok(())
}
}

/// An enum type constructor value, i.e. the value arising from referencing an
/// enum variant that takes data.
#[derive(Clone, Debug)]
Expand Down Expand Up @@ -495,6 +523,7 @@ impl<'a, T: Display> Display for Value<'a, T> {
Value::Closure(closure) => write!(f, "{closure}"),
Value::TypeConstructor(tc) => write!(f, "{tc}"),
Value::Enum(enum_value) => write!(f, "{enum_value}"),
Value::Struct(struct_value) => write!(f, "{struct_value}"),
Value::BuiltinFunction(b) => write!(f, "{b:?}"),
Value::Expression(e) => write!(f, "{e}"),
}
Expand Down Expand Up @@ -976,8 +1005,16 @@ impl<'a, 'b, T: FieldElement, S: SymbolLookup<'a, T>> Evaluator<'a, 'b, T, S> {
Expression::FreeInput(_, _) => Err(EvalError::Unsupported(
"Cannot evaluate free input.".to_string(),
))?,
Expression::StructExpression(_, _) => {
unimplemented!("Struct expressions are not yet supported.")
Expression::StructExpression(_, StructExpression { name: _, fields }) => {
self.op_stack.push(Operation::Combine(expr));
self.op_stack.extend(
fields
.iter()
.skip(1)
.rev()
.map(|named_expr| Operation::Expand(named_expr.body.as_ref())),
);
self.expand(fields[0].body.as_ref())?;
}
};
Ok(())
Expand Down Expand Up @@ -1145,7 +1182,31 @@ impl<'a, 'b, T: FieldElement, S: SymbolLookup<'a, T>> Evaluator<'a, 'b, T, S> {
let body = if *condition { body } else { else_body };
return self.expand(body);
}
Expression::StructExpression(_, StructExpression { name, fields }) => {
let fields = fields
.iter()
.rev()
.map(|named_expr| {
let value = self.value_stack.pop().unwrap_or_else(|| {
panic!(
"Value for field {} not found in struct {name}",
named_expr.name
)
});
(named_expr.name.as_str(), value)
})
.collect::<HashMap<_, _>>();

let Reference::Poly(poly) = name else {
unreachable!();
};

Value::Struct(StructValue {
name: &poly.name,
fields,
})
.into()
}
_ => unreachable!(),
};
self.value_stack.push(value);
Expand Down
33 changes: 33 additions & 0 deletions pil-analyzer/tests/evaluator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -647,3 +647,36 @@ fn test_trait_function_call_cross_impl() {

assert_eq!(parse_and_evaluate_symbol(input, "F::r"), "6".to_string());
}

#[test]
fn test_struct() {
// TODO: This test needs to be updated when we add field accessors.
let input = " struct Point {
x: int,
y: int,
}
let f: int -> Point = |i| Point{ x: 0, y: i };
let equal_one: Point, int -> bool = |p1, p2| 1 == p2;
let result = equal_one(f(3), 3);
";

assert_eq!(
parse_and_evaluate_symbol(input, "result"),
"false".to_string()
);
}

#[test]
fn test_struct_unordered_fields() {
let input = " struct Point {
x: int,
y: int,
}
let p: Point = Point{ y: 0, x: 2 };
";

assert_eq!(
parse_and_evaluate_symbol(input, "p"),
"Point{x: 2, y: 0, }".to_string()
);
}
Loading