diff --git a/src/protocol/eval/executor.rs b/src/protocol/eval/executor.rs index c50e075e365c569cdda3cb2c95e6e5995a128ae2..9bcb1c55e0fa8754e1cfd8d6372692502a435bf6 100644 --- a/src/protocol/eval/executor.rs +++ b/src/protocol/eval/executor.rs @@ -359,7 +359,12 @@ impl Prompt { // exists in the normal stack/heap. We don't want to deallocate // this thing. Rather we want to return a reference to it. let subject = self.store.read_ref(value_ref); - let subject_heap_pos = subject.as_array(); + let subject_heap_pos = match subject { + Value::String(v) => *v, + Value::Array(v) => *v, + Value::Message(v) => *v, + _ => unreachable!(), + }; if array_inclusive_index_is_invalid(&self.store, subject_heap_pos, index) { return Err(construct_array_error(self, modules, heap, expr_id, subject_heap_pos, index)); @@ -370,7 +375,12 @@ impl Prompt { _ => { // Our value lives on the expression stack, hence we need to // clone whatever we're referring to. Then drop the subject. - let subject_heap_pos = subject.as_array(); + let subject_heap_pos = match &subject { + Value::String(v) => *v, + Value::Array(v) => *v, + Value::Message(v) => *v, + _ => unreachable!(), + }; if array_inclusive_index_is_invalid(&self.store, subject_heap_pos, index) { return Err(construct_array_error(self, modules, heap, expr_id, subject_heap_pos, index)); @@ -409,7 +419,14 @@ impl Prompt { // Slicing needs to produce a copy anyway (with the // current evaluator implementation) - let array_heap_pos = deref_subject.as_array(); + enum ValueKind{ Array, String, Message } + let (value_kind, array_heap_pos) = match deref_subject { + Value::Array(v) => (ValueKind::Array, *v), + Value::String(v) => (ValueKind::String, *v), + Value::Message(v) => (ValueKind::Message, *v), + _ => unreachable!() + }; + if array_inclusive_index_is_invalid(&self.store, array_heap_pos, from_index) { return Err(construct_array_error(self, modules, heap, expr.from_index, array_heap_pos, from_index)); } @@ -433,7 +450,11 @@ impl Prompt { } // else: empty range - cur_frame.expr_values.push_back(Value::Array(new_heap_pos)); + cur_frame.expr_values.push_back(match value_kind { + ValueKind::Array => Value::Array(new_heap_pos), + ValueKind::String => Value::String(new_heap_pos), + ValueKind::Message => Value::Message(new_heap_pos), + }); // Dropping the original subject, because we don't // want to drop something on the stack @@ -545,6 +566,76 @@ impl Prompt { // If we're dealing with a builtin we don't do any // fancy shenanigans at all, just push the result. match expr.method { + Method::Get => { + let value = cur_frame.expr_values.pop_front().unwrap(); + let value = self.store.maybe_read_ref(&value).clone(); + + match ctx.get(value.clone(), &mut self.store) { + Some(result) => { + cur_frame.expr_values.push_back(result) + }, + None => { + cur_frame.expr_values.push_front(value.clone()); + cur_frame.expr_stack.push_back(ExprInstruction::EvalExpr(expr_id)); + return Ok(EvalContinuation::BlockGet(value)); + } + } + }, + Method::Put => { + let port_value = cur_frame.expr_values.pop_front().unwrap(); + let deref_port_value = self.store.maybe_read_ref(&port_value).clone(); + let msg_value = cur_frame.expr_values.pop_front().unwrap(); + let deref_msg_value = self.store.maybe_read_ref(&msg_value).clone(); + + if ctx.did_put(deref_port_value.clone()) { + // We're fine, deallocate in case the expression value stack + // held an owned value + self.store.drop_value(msg_value.get_heap_pos()); + } else { + cur_frame.expr_values.push_front(msg_value); + cur_frame.expr_values.push_front(port_value); + cur_frame.expr_stack.push_back(ExprInstruction::EvalExpr(expr_id)); + return Ok(EvalContinuation::Put(deref_port_value, deref_msg_value)); + } + }, + Method::Fires => { + let port_value = cur_frame.expr_values.pop_front().unwrap(); + let port_value_deref = self.store.maybe_read_ref(&port_value).clone(); + match ctx.fires(port_value_deref.clone()) { + None => { + cur_frame.expr_values.push_front(port_value); + cur_frame.expr_stack.push_back(ExprInstruction::EvalExpr(expr_id)); + return Ok(EvalContinuation::BlockFires(port_value_deref)); + }, + Some(value) => { + cur_frame.expr_values.push_back(value); + } + } + }, + Method::Create => { + let length_value = cur_frame.expr_values.pop_front().unwrap(); + let length_value = self.store.maybe_read_ref(&length_value); + let length = if length_value.is_signed_integer() { + let length_value = length_value.as_signed_integer(); + if length_value < 0 { + return Err(EvalError::new_error_at_expr( + self, modules, heap, expr_id, + format!("got length '{}', can only create a message with a non-negative length", length_value) + )); + } + + length_value as u64 + } else { + debug_assert!(length_value.is_unsigned_integer()); + length_value.as_unsigned_integer() + }; + + let heap_pos = self.store.alloc_heap(); + let values = &mut self.store.heap_regions[heap_pos as usize].values; + debug_assert!(values.is_empty()); + values.resize(length as usize, Value::UInt8(0)); + cur_frame.expr_values.push_back(Value::Message(heap_pos)); + }, Method::Length => { let value = cur_frame.expr_values.pop_front().unwrap(); let value_heap_pos = value.get_heap_pos(); @@ -562,7 +653,19 @@ impl Prompt { cur_frame.expr_values.push_back(Value::UInt32(len as u32)); self.store.drop_value(value_heap_pos); }, - Method::UserComponent => todo!("component creation"), + Method::Assert => { + let value = cur_frame.expr_values.pop_front().unwrap(); + let value = self.store.maybe_read_ref(&value).clone(); + if !value.as_bool() { + return Ok(EvalContinuation::Inconsistent) + } + }, + Method::UserComponent => { + // This is actually handled by the evaluation + // of the statement. + debug_assert_eq!(heap[expr.definition].parameters().len(), cur_frame.expr_values.len()); + debug_assert_eq!(heap[cur_frame.position].as_new().expression, expr.this) + }, Method::UserFunction => { // Push a new frame. Note that all expressions have // been pushed to the front, so they're in the order @@ -595,7 +698,6 @@ impl Prompt { // return and ask our caller to call us again return Ok(EvalContinuation::Stepping); }, - _ => todo!("other builtins"), } }, Expression::Variable(expr) => { @@ -671,7 +773,8 @@ impl Prompt { }, Statement::If(stmt) => { debug_assert_eq!(cur_frame.expr_values.len(), 1, "expected one expr value for if statement"); - let test_value = cur_frame.expr_values.pop_back().unwrap().as_bool(); + let test_value = cur_frame.expr_values.pop_back().unwrap(); + let test_value = self.store.maybe_read_ref(&test_value).as_bool(); if test_value { cur_frame.position = stmt.true_body.upcast(); } else if let Some(false_body) = stmt.false_body { @@ -689,7 +792,8 @@ impl Prompt { }, Statement::While(stmt) => { debug_assert_eq!(cur_frame.expr_values.len(), 1, "expected one expr value for while statement"); - let test_value = cur_frame.expr_values.pop_back().unwrap().as_bool(); + let test_value = cur_frame.expr_values.pop_back().unwrap(); + let test_value = self.store.maybe_read_ref(&test_value).as_bool(); if test_value { cur_frame.position = stmt.body.upcast(); } else { @@ -798,13 +902,12 @@ impl Prompt { cur_frame.position = stmt.next; - todo!("Make sure this is handled correctly, transfer 'heap' values to another Prompt"); Ok(EvalContinuation::NewComponent(call_expr.definition, expr_data.field_or_monomorph_idx, argument_group)) }, Statement::Expression(stmt) => { // The expression has just been completely evaluated. Some // values might have remained on the expression value stack. - cur_frame.expr_values.clear(); + // cur_frame.expr_values.clear(); PROPER CLEARING cur_frame.position = stmt.next; Ok(EvalContinuation::Stepping) diff --git a/src/protocol/eval/value.rs b/src/protocol/eval/value.rs index 3c25330e5ceae1b8ecdf5879ed2480ea3ad8eb59..efbba361c6c6e4f6f9de09d26edf2b80cfaeaa5e 100644 --- a/src/protocol/eval/value.rs +++ b/src/protocol/eval/value.rs @@ -190,6 +190,7 @@ impl ValueGroup { /// heap allocations (if any) stored in the ValueGroup. Calling this /// function will not store the returned value in the `values` member. fn retrieve_value(&mut self, value: &Value, from_store: &Store) -> Value { + let value = from_store.maybe_read_ref(value); if let Some(heap_pos) = value.get_heap_pos() { // Value points to a heap allocation, so transfer the heap values // internally. diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs index c52236eeb8caad4696d3a66ec68d878d32918c8a..87fa277bad3bfc9d4ed0b3c8ea381e10d7033a43 100644 --- a/src/protocol/mod.rs +++ b/src/protocol/mod.rs @@ -342,12 +342,11 @@ impl EvalContext<'_> { unreachable!(); } Value::Input(port) => { - let heap_pos = store.alloc_heap(); - let heap_pos_usize = heap_pos as usize; - let payload = context.read_msg(port); if payload.is_none() { return None; } + let heap_pos = store.alloc_heap(); + let heap_pos_usize = heap_pos as usize; let payload = payload.unwrap(); store.heap_regions[heap_pos_usize].values.reserve(payload.0.len()); for value in payload.0.iter() { diff --git a/src/protocol/parser/pass_definitions.rs b/src/protocol/parser/pass_definitions.rs index a7449130ff3b2e46ec03f93f4dbcbe5867e14476..ba7a121d98236cff29efc65b55ad3e9b09b9ae12 100644 --- a/src/protocol/parser/pass_definitions.rs +++ b/src/protocol/parser/pass_definitions.rs @@ -693,7 +693,7 @@ impl PassDefinitions { ) -> Result { // Consume channel specification let channel_span = consume_exact_ident(&module.source, iter, KW_STMT_CHANNEL)?; - let channel_type = if Some(TokenKind::OpenAngle) == iter.next() { + let inner_port_type = if Some(TokenKind::OpenAngle) == iter.next() { // Retrieve the type of the channel, we're cheating a bit here by // consuming the first '<' and setting the initial angle depth to 1 // such that our final '>' will be consumed as well. @@ -719,19 +719,27 @@ impl PassDefinitions { consume_token(&module.source, iter, TokenKind::SemiColon)?; // Construct ports + let port_type_len = inner_port_type.elements.len() + 1; + let mut from_port_type = ParserType{ elements: Vec::with_capacity(port_type_len) }; + from_port_type.elements.push(ParserTypeElement{ full_span: channel_span, variant: ParserTypeVariant::Output }); + from_port_type.elements.extend_from_slice(&inner_port_type.elements); let from = ctx.heap.alloc_variable(|this| Variable{ this, kind: VariableKind::Local, identifier: from_identifier, - parser_type: channel_type.clone(), + parser_type: from_port_type, relative_pos_in_block: 0, unique_id_in_scope: -1, }); + + let mut to_port_type = ParserType{ elements: Vec::with_capacity(port_type_len) }; + to_port_type.elements.push(ParserTypeElement{ full_span: channel_span, variant: ParserTypeVariant::Input }); + to_port_type.elements.extend_from_slice(&inner_port_type.elements); let to = ctx.heap.alloc_variable(|this|Variable{ this, kind: VariableKind::Local, identifier: to_identifier, - parser_type: channel_type, + parser_type: to_port_type, relative_pos_in_block: 0, unique_id_in_scope: -1, }); diff --git a/src/protocol/parser/pass_validation_linking.rs b/src/protocol/parser/pass_validation_linking.rs index 72ade83a91fd22fd3c6069b522aafb867b586819..cfd28c4f3ab906c4d244ccb119ba7adc77f74ec5 100644 --- a/src/protocol/parser/pass_validation_linking.rs +++ b/src/protocol/parser/pass_validation_linking.rs @@ -403,6 +403,7 @@ impl Visitor2 for PassValidationLinking { fn visit_assignment_expr(&mut self, ctx: &mut Ctx, id: AssignmentExpressionId) -> VisitorResult { let upcast_id = id.upcast(); + let assignment_expr = &mut ctx.heap[id]; let left_expr_id = assignment_expr.left; @@ -586,8 +587,12 @@ impl Visitor2 for PassValidationLinking { self.expr_parent = ExpressionParent::Expression(upcast_id, 0); self.visit_expr(ctx, subject_expr_id)?; + + let old_assignable = self.must_be_assignable.take(); self.expr_parent = ExpressionParent::Expression(upcast_id, 1); self.visit_expr(ctx, index_expr_id)?; + + self.must_be_assignable = old_assignable; self.expr_parent = old_expr_parent; Ok(()) @@ -608,10 +613,14 @@ impl Visitor2 for PassValidationLinking { self.expr_parent = ExpressionParent::Expression(upcast_id, 0); self.visit_expr(ctx, subject_expr_id)?; + + let old_assignable = self.must_be_assignable.take(); self.expr_parent = ExpressionParent::Expression(upcast_id, 1); self.visit_expr(ctx, from_expr_id)?; self.expr_parent = ExpressionParent::Expression(upcast_id, 2); self.visit_expr(ctx, to_expr_id)?; + + self.must_be_assignable = old_assignable; self.expr_parent = old_expr_parent; Ok(()) @@ -865,7 +874,7 @@ impl Visitor2 for PassValidationLinking { "a call to 'get' may only occur in primitive component definitions" )); } - if !self.in_sync.is_invalid() { + if self.in_sync.is_invalid() { return Err(ParseError::new_error_str_at_span( &ctx.module.source, call_expr.span, "a call to 'get' may only occur inside synchronous blocks" @@ -879,7 +888,7 @@ impl Visitor2 for PassValidationLinking { "a call to 'put' may only occur in primitive component definitions" )); } - if !self.in_sync.is_invalid() { + if self.in_sync.is_invalid() { return Err(ParseError::new_error_str_at_span( &ctx.module.source, call_expr.span, "a call to 'put' may only occur inside synchronous blocks" @@ -893,7 +902,7 @@ impl Visitor2 for PassValidationLinking { "a call to 'fires' may only occur in primitive component definitions" )); } - if !self.in_sync.is_invalid() { + if self.in_sync.is_invalid() { return Err(ParseError::new_error_str_at_span( &ctx.module.source, call_expr.span, "a call to 'fires' may only occur inside synchronous blocks" @@ -909,7 +918,7 @@ impl Visitor2 for PassValidationLinking { "assert statement may only occur in components" )); } - if !self.in_sync.is_invalid() { + if self.in_sync.is_invalid() { return Err(ParseError::new_error_str_at_span( &ctx.module.source, call_expr.span, "assert statements may only occur inside synchronous blocks" diff --git a/src/protocol/tests/eval_binding.rs b/src/protocol/tests/eval_binding.rs index 4a44447ff65c2fef8dacaaa212a9bb63aaf71d88..acdeb70a9569f6b744905e408e4f673f3c565902 100644 --- a/src/protocol/tests/eval_binding.rs +++ b/src/protocol/tests/eval_binding.rs @@ -82,6 +82,53 @@ fn test_binding_from_array() { }); } +#[test] +fn test_binding_from_union() { + Tester::new_single_source_expect_ok("option type", " + union Option { Some(T), None } + + func is_some(Option opt) -> bool { + if (let Option::Some(throwaway) = opt) return true; + else return false; + } + func is_none(Option opt) -> bool { + if (let Option::Some(throwaway) = opt) return false; + else return true; + } + + func foo() -> u32 { + // Hey look, we're so modern, we have algebraic discriminated sum datauniontypes + auto something = Option::Some(5); + auto nonething = Option::None; + + bool success1 = false; + if (let Option::Some(value) = something && let Option::None = nonething) { + success1 = value == 5; + } + + bool success2 = false; + if (is_some(something) && is_none(nonething)) { + success2 = true; + } + + bool success3 = true; + if (let Option::None = something) success3 = false; + if (let Option::Some(value) = nonething) success3 = false; + + bool success4 = true; + if (is_none(something) || is_some(nonething)) success4 = false; + + if (success1 && success2 && success3 && success4) { + if (let Option::Some(value) = something) return value; + } + + return 0; + } + ").for_function("foo", |f| { f + .call_ok(Some(Value::UInt32(5))); + }); +} + #[test] fn test_binding_fizz_buzz() { Tester::new_single_source_expect_ok("am I employable?", " diff --git a/src/protocol/tests/eval_silly.rs b/src/protocol/tests/eval_silly.rs index ef7478190e07155df12dfaa841bb57bfe359848f..62cb0ca3ae138c85373515dfe4c54f0428c30b3d 100644 --- a/src/protocol/tests/eval_silly.rs +++ b/src/protocol/tests/eval_silly.rs @@ -210,7 +210,7 @@ fn test_field_selection_polymorphism() { mod_yzx.x == 1337 && mod_yzx.y == 2 && mod_yzx.z == 3 && mod_zxy.x == 1337 && mod_zxy.y == 2 && mod_zxy.z == 3; } -").for_function("foo", |f| { + ").for_function("foo", |f| { f.call_ok(Some(Value::Bool(true))); }); } diff --git a/src/runtime/tests.rs b/src/runtime/tests.rs index dca5f781d734bc85e5d1d00f946c05c7b69240a6..17a6aac7bc715623c08fbdf995688b6dfc39c7e2 100644 --- a/src/runtime/tests.rs +++ b/src/runtime/tests.rs @@ -37,6 +37,19 @@ fn file_logged_configured_connector( Connector::new(file_logger, pd, connector_id) } static MINIMAL_PDL: &'static [u8] = b" +primitive sync(in a, out b) { + while (true) { + synchronous { + if (fires(a) && fires(b)) { + msg x = get(a); + put(b, x); + } else { + assert(!fires(a) && !fires(b)); + } + } + } +} + primitive together(in ia, in ib, out oa, out ob){ while(true) synchronous { if(fires(ia)) { @@ -970,13 +983,15 @@ fn pdl_reo_fifo1full() { let test_log_path = Path::new("./logs/pdl_reo_fifo1full"); let pdl = b" primitive fifo1full(in a, out b) { + bool is_set = true; msg m = create(0); while(true) synchronous { - if(m == null) { + if(!is_set) { if(fires(a)) m=get(a); + is_set = false; } else { if(fires(b)) put(b, m); - m = null; + is_set = true; } } } @@ -1024,7 +1039,7 @@ fn sequencer3_prim() { let test_log_path = Path::new("./logs/sequencer3_prim"); let pdl = b" primitive sequencer3(out a, out b, out c) { - int i = 0; + u32 i = 0; while(true) synchronous { out to = a; if (i==1) to = b; @@ -1070,21 +1085,35 @@ fn sequencer3_prim() { fn sequencer3_comp() { let test_log_path = Path::new("./logs/sequencer3_comp"); let pdl = b" - primitive fifo1_init(T m, in a, out b) { + primitive replicator(in a, out b, out c) { + while (true) { + synchronous { + if (fires(a) && fires(b) && fires(c)) { + msg x = get(a); + put(b, x); + put(c, x); + } else { + assert(!fires(a) && !fires(b) && !fires(c)); + } + } + } + } + primitive fifo1_init(bool has_value, T m, in a, out b) { while(true) synchronous { - if(m != null && fires(b)) { + if(has_value && fires(b)) { put(b, m); - m = null; - } else if (m == null && fires(a)) { + has_value = false; + } else if (!has_value && fires(a)) { m = get(a); + has_value = true; } } } composite fifo1_full(in a, out b) { - new fifo1_init(create(0), a, b); + new fifo1_init(true, create(0), a, b); } composite fifo1(in a, out b) { - new fifo1_init(null, a, b); + new fifo1_init(false, create(0), a, b); } composite sequencer3(out a, out b, out c) { channel d -> e; @@ -1191,6 +1220,34 @@ fn xrouter_prim() { fn xrouter_comp() { let test_log_path = Path::new("./logs/xrouter_comp"); let pdl = b" + primitive replicator(in a, out b, out c) { + while (true) { + synchronous { + if (fires(a) && fires(b) && fires(c)) { + msg x = get(a); + put(b, x); + put(c, x); + } else { + assert(!fires(a) && !fires(b) && !fires(c)); + } + } + } + } + + primitive merger(in a, in b, out c) { + while (true) { + synchronous { + if (fires(a) && !fires(b) && fires(c)) { + put(c, get(a)); + } else if (!fires(a) && fires(b) && fires(c)) { + put(c, get(b)); + } else { + assert(!fires(a) && !fires(b) && !fires(c)); + } + } + } + } + primitive lossy(in a, out b) { while(true) synchronous { if(fires(a)) { @@ -1202,8 +1259,8 @@ fn xrouter_comp() { primitive sync_drain(in a, in b) { while(true) synchronous { if(fires(a)) { - get(a); - get(b); + msg drop_it = get(a); + msg on_the_floor = get(b); } } } @@ -1289,13 +1346,13 @@ fn for_msg_byte() { let test_log_path = Path::new("./logs/for_msg_byte"); let pdl = b" primitive for_msg_byte(out o) { - byte i = 0; - int idx = 0; + u8 i = 0; + u32 idx = 0; while(i<8) { msg m = create(1); m[idx] = i; synchronous put(o, m); - i++; + i += 1; } } "; @@ -1320,8 +1377,8 @@ fn eq_causality() { let test_log_path = Path::new("./logs/eq_causality"); let pdl = b" primitive eq(in a, in b, out c) { - msg ma = null; - msg mb = null; + msg ma = create(0); + msg mb = create(0); while(true) synchronous { if(fires(a)) { // b and c also fire! @@ -1376,8 +1433,8 @@ fn eq_no_causality() { new eqinner(a, b, c, leftfirsto, leftfirsti); } primitive eqinner(in a, in b, out c, out leftfirsto, in leftfirsti) { - msg ma = null; - msg mb = null; + msg ma = create(0); + msg mb = create(0); while(true) synchronous { if(fires(a)) { // b and c also fire! @@ -1389,7 +1446,7 @@ fn eq_no_causality() { // using dummy! put(leftfirsto, ma); - get(leftfirsti); + auto drop_it = get(leftfirsti); } else { // right first! DON'T USE DUMMY mb = get(b); @@ -1400,27 +1457,6 @@ fn eq_no_causality() { } } } - T some_function(int a, int b) { - T something = a; - return something; - } - primitive quick_test(in a, in b) { - // msg ma = null; - auto test1 = 0; - auto test2 = 0; - auto ma = some_function(test1, test2); - while(true) synchronous { - if (fires(a)) { - ma = get(a); - } - if (fires(b)) { - ma = get(b); - } - if (fires(a) && fires(b)) { - ma = get(a) + get(b); - } - } - } "; let pd = reowolf::ProtocolDescription::parse(pdl).unwrap(); let mut c = file_logged_configured_connector(0, test_log_path, Arc::new(pd));