diff --git a/src/protocol/eval/value.rs b/src/protocol/eval/value.rs new file mode 100644 index 0000000000000000000000000000000000000000..8dc006f2f8ced17594cc867729b5e92486996f27 --- /dev/null +++ b/src/protocol/eval/value.rs @@ -0,0 +1,318 @@ + +use crate::PortId; +use crate::protocol::ast::{ + AssignmentOperator, + BinaryOperator, + UnaryOperator, +}; +use crate::protocol::eval::Store; + +pub type StackPos = u32; +pub type HeapPos = u32; + +#[derive(Copy, Clone)] +pub enum ValueId { + Stack(StackPos), // place on stack + Heap(HeapPos, u32), // allocated region + values within that region +} + +#[derive(Debug, Clone)] +pub enum Value { + // Special types, never encountered during evaluation if the compiler works correctly + Unassigned, // Marker when variables are first declared, immediately followed by assignment + PrevStackBoundary(isize), // Marker for stack frame beginning, so we can pop stack values + Ref(ValueId), // Reference to a value, used by expressions producing references + // Builtin types + Input(PortId), + Output(PortId), + Message(HeapPos), + Null, + Bool(bool), + Char(char), + String(HeapPos), + UInt8(u8), + UInt16(u16), + UInt32(u32), + UInt64(u64), + SInt8(i8), + SInt16(i16), + SInt32(i32), + SInt64(i64), + Array(HeapPos), + // Instances of user-defined types + Enum(i64), + Union(i64, HeapPos), + Struct(HeapPos), +} + +macro_rules! impl_union_unpack_as_value { + ($func_name:ident, $variant_name:path, $return_type:ty) => { + impl Value { + pub(crate) fn $func_name(&self) -> $return_type { + match self { + $variant_name(v) => *v, + _ => panic!(concat!("called ", stringify!($func_name()), " on {:?}"), self), + } + } + } + } +} + +impl_union_unpack_as_value!(as_stack_boundary, Value::PrevStackBoundary, isize); +impl_union_unpack_as_value!(as_ref, Value::Ref, ValueId); +impl_union_unpack_as_value!(as_input, Value::Input, PortId); +impl_union_unpack_as_value!(as_output, Value::Output, PortId); +impl_union_unpack_as_value!(as_message, Value::Message, HeapPos); +impl_union_unpack_as_value!(as_bool, Value::Bool, bool); +impl_union_unpack_as_value!(as_char, Value::Char, char); +impl_union_unpack_as_value!(as_string, Value::String, HeapPos); +impl_union_unpack_as_value!(as_uint8, Value::UInt8, u8); +impl_union_unpack_as_value!(as_uint16, Value::UInt16, u16); +impl_union_unpack_as_value!(as_uint32, Value::UInt32, u32); +impl_union_unpack_as_value!(as_uint64, Value::UInt64, u64); +impl_union_unpack_as_value!(as_sint8, Value::SInt8, i8); +impl_union_unpack_as_value!(as_sint16, Value::SInt16, i16); +impl_union_unpack_as_value!(as_sint32, Value::SInt32, i32); +impl_union_unpack_as_value!(as_sint64, Value::SInt64, i64); +impl_union_unpack_as_value!(as_array, Value::Array, HeapPos); +impl_union_unpack_as_value!(as_enum, Value::Enum, i64); +impl_union_unpack_as_value!(as_struct, Value::Struct, HeapPos); + +impl Value { + pub(crate) fn as_union(&self) -> (i64, HeapPos) { + match self { + Value::Union(tag, v) => (*tag, *v), + _ => panic!("called as_union on {:?}", self), + } + } + + pub(crate) fn is_integer(&self) -> bool { + match self { + Value::UInt8(_) | Value::UInt16(_) | Value::UInt32(_) | Value::UInt64(_) | + Value::SInt8(_) | Value::SInt16(_) | Value::SInt32(_) | Value::SInt64(_) => true, + _ => false + } + } + + pub(crate) fn is_unsigned_integer(&self) -> bool { + match self { + Value::UInt8(_) | Value::UInt16(_) | Value::UInt32(_) | Value::UInt64(_) => true, + _ => false + } + } + + pub(crate) fn is_signed_integer(&self) -> bool { + match self { + Value::SInt8(_) | Value::SInt16(_) | Value::SInt32(_) | Value::SInt64(_) => true, + _ => false + } + } + + pub(crate) fn as_unsigned_integer(&self) -> u64 { + match self { + Value::UInt8(v) => *v as u64, + Value::UInt16(v) => *v as u64, + Value::UInt32(v) => *v as u64, + Value::UInt64(v) => *v as u64, + _ => unreachable!("called as_unsigned_integer on {:?}", self), + } + } + + pub(crate) fn as_signed_integer(&self) -> i64 { + match self { + Value::SInt8(v) => *v as i64, + Value::SInt16(v) => *v as i64, + Value::SInt32(v) => *v as i64, + Value::SInt64(v) => *v as i64, + _ => unreachable!("called as_signed_integer on {:?}", self) + } + } + + /// Returns the heap position associated with the value. If the value + /// doesn't store anything in the heap then we return `None`. + pub(crate) fn get_heap_pos(&self) -> Option { + match self { + Value::Message(v) => Some(*v), + Value::Array(v) => Some(*v), + Value::Union(_, v) => Some(*v), + Value::Struct(v) => Some(*v), + _ => None + } + } +} + +/// Applies the assignment operator. If a heap position is returned then that +/// heap position should be cleared +pub(crate) fn apply_assignment_operator(store: &mut Store, lhs: ValueRef, op: AssignmentOperator, rhs: Value) { + use AssignmentOperator as AO; + + macro_rules! apply_int_op { + ($lhs:ident, $assignment_tokens:tt, $operator:ident, $rhs:ident) => { + match $lhs { + Value::UInt8(v) => { *v $assignment_tokens $rhs.as_uint8(); }, + Value::UInt16(v) => { *v $assignment_tokens $rhs.as_uint16(); }, + Value::UInt32(v) => { *v $assignment_tokens $rhs.as_uint32(); }, + Value::UInt64(v) => { *v $assignment_tokens $rhs.as_uint64(); }, + Value::SInt8(v) => { *v $assignment_tokens $rhs.as_sint8(); }, + Value::SInt16(v) => { *v $assignment_tokens $rhs.as_sint16(); }, + Value::SInt32(v) => { *v $assignment_tokens $rhs.as_sint32(); }, + Value::SInt64(v) => { *v $assignment_tokens $rhs.as_sint64(); }, + _ => unreachable!("apply_assignment_operator {:?} on lhs {:?} and rhs {:?}", $operator, $lhs, $rhs), + } + } + } + + let lhs = store.read_mut_ref(lhs); + + let mut to_dealloc = None; + match AO { + AO::Set => { + match lhs { + Value::Unassigned => { *lhs = rhs; }, + Value::Input(v) => { *v = rhs.as_input(); }, + Value::Output(v) => { *v = rhs.as_output(); }, + Value::Message(v) => { to_dealloc = Some(*v); *v = rhs.as_message(); }, + Value::Bool(v) => { *v = rhs.as_bool(); }, + Value::Char(v) => { *v = rhs.as_char(); }, + Value::String(v) => { *v = rhs.as_string().clone(); }, + Value::UInt8(v) => { *v = rhs.as_uint8(); }, + Value::UInt16(v) => { *v = rhs.as_uint16(); }, + Value::UInt32(v) => { *v = rhs.as_uint32(); }, + Value::UInt64(v) => { *v = rhs.as_uint64(); }, + Value::SInt8(v) => { *v = rhs.as_sint8(); }, + Value::SInt16(v) => { *v = rhs.as_sint16(); }, + Value::SInt32(v) => { *v = rhs.as_sint32(); }, + Value::SInt64(v) => { *v = rhs.as_sint64(); }, + Value::Array(v) => { to_dealloc = Some(*v); *v = rhs.as_array(); }, + Value::Enum(v) => { *v = rhs.as_enum(); }, + Value::Union(lhs_tag, lhs_heap_pos) => { + to_dealloc = Some(*lhs_heap_pos); + let (rhs_tag, rhs_heap_pos) = rhs.as_union(); + *lhs_tag = rhs_tag; + *lhs_heap_pos = rhs_heap_pos; + } + Value::Struct(v) => { to_dealloc = Some(*v); *v = rhs.as_struct(); }, + _ => unreachable!("apply_assignment_operator {:?} on lhs {:?} and rhs {:?}", op, lhs, rhs), + } + }, + AO::Multiplied => { apply_int_op!(lhs, *=, op, rhs) }, + AO::Divided => { apply_int_op!(lhs, /=, op, rhs) }, + AO::Remained => { apply_int_op!(lhs, %=, op, rhs) }, + AO::Added => { apply_int_op!(lhs, +=, op, rhs) }, + AO::Subtracted => { apply_int_op!(lhs, -=, op, rhs) }, + AO::ShiftedLeft => { apply_int_op!(lhs, <<=, op, rhs) }, + AO::ShiftedRight => { apply_int_op!(lhs, >>=, op, rhs) }, + AO::BitwiseAnded => { apply_int_op!(lhs, &=, op, rhs) }, + AO::BitwiseXored => { apply_int_op!(lhs, ^=, op, rhs) }, + AO::BitwiseOred => { apply_int_op!(lhs, |=, op, rhs) }, + } + + if let Some(heap_pos) = to_dealloc { + store.drop_heap_pos(heap_pos); + } +} + +pub(crate) fn apply_binary_operator(store: &mut Store, lhs: &Value, op: BinaryOperator, rhs: &Value) -> Value { + use BinaryOperator as BO; + + macro_rules! apply_int_op_and_return { + ($lhs:ident, $operator_tokens:tt, $operator:ident, $rhs:ident) => { + return match $lhs { + Value::UInt8(v) => { Value::UInt8( *v $operator_tokens $rhs.as_uint8() ); }, + Value::UInt16(v) => { Value::UInt16(*v $operator_tokens $rhs.as_uint16()); }, + Value::UInt32(v) => { Value::UInt32(*v $operator_tokens $rhs.as_uint32()); }, + Value::UInt64(v) => { Value::UInt64(*v $operator_tokens $rhs.as_uint64()); }, + Value::SInt8(v) => { Value::SInt8( *v $operator_tokens $rhs.as_sint8() ); }, + Value::SInt16(v) => { Value::SInt16(*v $operator_tokens $rhs.as_sint16()); }, + Value::SInt32(v) => { Value::SInt32(*v $operator_tokens $rhs.as_sint32()); }, + Value::SInt64(v) => { Value::SInt64(*v $operator_tokens $rhs.as_sint64()); }, + _ => unreachable!("apply_binary_operator {:?} on lhs {:?} and rhs {:?}", $operator, $lhs, $rhs) + }; + } + } + + match op { + BO::Concatenate => { + let lhs_heap_pos; + let rhs_heap_pos; + let construct_fn; + match lhs { + Value::Message(lhs_pos) => { + lhs_heap_pos = *lhs_pos; + rhs_heap_pos = rhs.as_message(); + construct_fn = |pos: HeapPos| Value::Message(pos); + }, + Value::String(lhs_pos) => { + lhs_heap_pos = *lhs_pos; + rhs_heap_pos = rhs.as_string(); + construct_fn = |pos: HeapPos| Value::String(pos); + }, + Value::Array(lhs_pos) => { + lhs_heap_pos = *lhs_pos; + rhs_heap_pos = *rhs.as_array(); + construct_fn = |pos: HeapPos| Value::Array(pos); + }, + _ => unreachable!("apply_binary_operator {:?} on lhs {:?} and rhs {:?}", op, lhs, rhs) + } + + let target_heap_pos = store.alloc_heap(); + let target = &mut store.heap_regions[target_heap_pos as usize].values; + target.extend(&store.heap_regions[lhs_heap_pos as usize].values); + target.extend(&store.heap_regions[rhs_heap_pos as usize].values); + return construct_fn(target_heap_pos); + }, + BO::LogicalOr => { + return Value::Bool(lhs.as_bool() || rhs.as_bool()); + }, + BO::LogicalAnd => { + return Value::Bool(lhs.as_bool() && rhs.as_bool()); + }, + BO::BitwiseOr => { apply_int_op_and_return!(lhs, |, op, rhs); }, + BO::BitwiseXor => { apply_int_op_and_return!(lhs, ^, op, rhs); }, + BO::BitwiseAnd => { apply_int_op_and_return!(lhs, &, op, rhs); }, + BO::Equality => { todo!("implement") }, + BO::Inequality => { todo!("implement") }, + BO::LessThan => { apply_int_op_and_return!(lhs, <, op, rhs); }, + BO::GreaterThan => { apply_int_op_and_return!(lhs, >, op, rhs); }, + BO::LessThanEqual => { apply_int_op_and_return!(lhs, <=, op, rhs); }, + BO::GreaterThanEqual => { apply_int_op_and_return!(lhs, >=, op, rhs); }, + BO::ShiftLeft => { apply_int_op_and_return!(lhs, <<, op, rhs); }, + BO::ShiftRight => { apply_int_op_and_return!(lhs, >>, op, rhs); }, + BO::Add => { apply_int_op_and_return!(lhs, +, op, rhs); }, + BO::Subtract => { apply_int_op_and_return!(lhs, -, op, rhs); }, + BO::Multiply => { apply_int_op_and_return!(lhs, *, op, rhs); }, + BO::Divide => { apply_int_op_and_return!(lhs, /, op, rhs); }, + BO::Remainder => { apply_int_op_and_return!(lhs, %, op, rhs); } + } +} + +pub(crate) fn apply_unary_operator(store: &mut Store, op: UnaryOperator, value: &Value) -> Value { + use UnaryOperator as UO; + + macro_rules! apply_int_expr_and_return { + ($value:ident, $apply:expr, $op:ident) => { + return match $value { + Value::UInt8(v) => Value::UInt8($apply), + Value::UInt16(v) => Value::UInt16($apply), + Value::UInt32(v) => Value::UInt32($apply), + Value::UInt64(v) => Value::UInt64($apply), + Value::SInt8(v) => Value::SInt8($apply), + Value::SInt16(v) => Value::SInt16($apply), + Value::SInt32(v) => Value::SInt32($apply), + Value::SInt64(v) => Value::SInt64($apply), + _ => unreachable!("apply_unary_operator {:?} on value {:?}", $op, $value), + }; + } + } + + match op { + UO::Positive => { apply_int_expr_and_return!(value, *v, op) }, + UO::Negative => { apply_int_expr_and_return!(value, *v, op) }, + UO::BitwiseNot => { apply_int_expr_and_return!(value, *v, op)}, + UO::LogicalNot => { return Value::Bool(!value.as_bool()); }, + UO::PreIncrement => { todo!("implement") }, + UO::PreDecrement => { todo!("implement") }, + UO::PostIncrement => { todo!("implement") }, + UO::PostDecrement => { todo!("implement") }, + } +} \ No newline at end of file