From 622cf123a4e69d560124884f5406d7e70a4831a1 2021-05-27 18:56:55 From: MH Date: 2021-05-27 18:56:55 Subject: [PATCH] Add concatenation assignment --- diff --git a/src/protocol/ast.rs b/src/protocol/ast.rs index 2ec91950cccab65a5a367f7f582b1ee9bb61a25a..ccb79434b3832c10b0220d8d4286afba8a6d9af3 100644 --- a/src/protocol/ast.rs +++ b/src/protocol/ast.rs @@ -1279,6 +1279,7 @@ impl Expression { #[derive(Debug, Clone, Copy)] pub enum AssignmentOperator { Set, + Concatenated, Multiplied, Divided, Remained, diff --git a/src/protocol/eval/executor.rs b/src/protocol/eval/executor.rs index bece26924d9c6faf15c3b1a2ca1aefb44f8d28ed..3f2a19ea3aee42b7afbd63a876e059320273a491 100644 --- a/src/protocol/eval/executor.rs +++ b/src/protocol/eval/executor.rs @@ -729,7 +729,7 @@ impl Prompt { debug_log!("Heap:"); for (_heap_idx, _heap_region) in self.store.heap_regions.iter().enumerate() { let _is_free = self.store.free_regions.iter().any(|idx| *idx as usize == _heap_idx); - debug_log!(" [{:03}] in_use: {}, len: {}, vals: {:?}", _heap_idx, !_is_free, heap_region.values.len(), &_heap_region.values); + debug_log!(" [{:03}] in_use: {}, len: {}, vals: {:?}", _heap_idx, !_is_free, _heap_region.values.len(), &_heap_region.values); } } // No (more) expressions to evaluate. So evaluate statement (that may diff --git a/src/protocol/eval/value.rs b/src/protocol/eval/value.rs index f5e06c522cbafce1755cac8be4eccad043a6257d..6b8295c4f7537c0ed15d1d82341b525408e67c2d 100644 --- a/src/protocol/eval/value.rs +++ b/src/protocol/eval/value.rs @@ -261,6 +261,8 @@ impl Default for ValueGroup { } } +enum ValueKind { Message, String, Array } + pub(crate) fn apply_assignment_operator(store: &mut Store, lhs: ValueId, op: AssignmentOperator, rhs: Value) { use AssignmentOperator as AO; @@ -280,7 +282,6 @@ pub(crate) fn apply_assignment_operator(store: &mut Store, lhs: ValueId, op: Ass } } - // let rhs = store.maybe_read_ref(&rhs).clone(); // we don't own this thing, so don't drop it let lhs = store.read_mut_ref(lhs); let mut to_dealloc = None; @@ -314,6 +315,28 @@ pub(crate) fn apply_assignment_operator(store: &mut Store, lhs: ValueId, op: Ass _ => unreachable!("apply_assignment_operator {:?} on lhs {:?} and rhs {:?}", op, lhs, rhs), } }, + AO::Concatenated => { + let lhs_heap_pos = lhs.get_heap_pos().unwrap() as usize; + let rhs_heap_pos = rhs.get_heap_pos().unwrap() as usize; + + // To prevent borrowing crap, swap out heap region with a temp empty array + let mut total = Vec::new(); + std::mem::swap(&mut total, &mut store.heap_regions[lhs_heap_pos].values); + + // Push everything onto the swapped vector + let rhs_len = store.heap_regions[rhs_heap_pos].values.len(); + total.reserve(rhs_len); + for value_idx in 0..rhs_len { + total.push(store.clone_value(store.heap_regions[rhs_heap_pos].values[value_idx].clone())); + } + + // Swap back in place + std::mem::swap(&mut total, &mut store.heap_regions[lhs_heap_pos].values); + + // We took ownership of the RHS, but we copied it into the LHS, so + // different form assignment we need to drop the RHS heap pos. + to_dealloc = Some(rhs_heap_pos as u32); + }, AO::Multiplied => { apply_int_op!(lhs, *=, op, rhs) }, AO::Divided => { apply_int_op!(lhs, /=, op, rhs) }, AO::Remained => { apply_int_op!(lhs, %=, op, rhs) }, @@ -376,7 +399,6 @@ pub(crate) fn apply_binary_operator(store: &mut Store, lhs: &Value, op: BinaryOp let lhs = store.maybe_read_ref(lhs); let rhs = store.maybe_read_ref(rhs); - enum ValueKind { Message, String, Array } let value_kind; match lhs { diff --git a/src/protocol/parser/mod.rs b/src/protocol/parser/mod.rs index 9e85d0599835b082b977ad5e9cab6f38286e9ff5..198815685054992b13d10dfcf4b6dd4545f7c51c 100644 --- a/src/protocol/parser/mod.rs +++ b/src/protocol/parser/mod.rs @@ -187,9 +187,6 @@ impl Parser { // Continue compilation with the remaining phases now that the types // are all in the type table for module_idx in 0..self.modules.len() { - // TODO: Remove the entire Visitor abstraction. It really doesn't - // make sense considering the amount of special handling we do - // in each pass. let mut ctx = visitor::Ctx{ heap: &mut self.heap, module: &mut self.modules[module_idx], diff --git a/src/protocol/parser/pass_definitions.rs b/src/protocol/parser/pass_definitions.rs index 4214c9c89221f9ae45203396b927e1e5d205a2cd..8a4a30886030ca001e78c0ed0293b0e882adb062 100644 --- a/src/protocol/parser/pass_definitions.rs +++ b/src/protocol/parser/pass_definitions.rs @@ -916,6 +916,7 @@ impl PassDefinitions { match token.unwrap() { TK::Equal => Some(AO::Set), + TK::AtEquals => Some(AO::Concatenated), TK::StarEquals => Some(AO::Multiplied), TK::SlashEquals => Some(AO::Divided), TK::PercentEquals => Some(AO::Remained), diff --git a/src/protocol/parser/pass_tokenizer.rs b/src/protocol/parser/pass_tokenizer.rs index 23537ca2aea79b2119ac7ad10eddca89a7ce271c..a1e29aabfa77e1c29d0fd3b0d9711ba67a30c6ec 100644 --- a/src/protocol/parser/pass_tokenizer.rs +++ b/src/protocol/parser/pass_tokenizer.rs @@ -355,7 +355,12 @@ impl PassTokenizer { token_kind = TokenKind::Question; } else if first_char == b'@' { source.consume(); - token_kind = TokenKind::At; + if let Some(b'=') = source.next() { + source.consume(); + token_kind = TokenKind::AtEquals; + } else { + token_kind = TokenKind::At; + } } else if first_char == b'[' { source.consume(); token_kind = TokenKind::OpenSquare; diff --git a/src/protocol/parser/pass_typing.rs b/src/protocol/parser/pass_typing.rs index 98447a5af22fccfc472f6774102b6ed0d051fced..008a6139295be5afe14ede32c33eea4b116bae88 100644 --- a/src/protocol/parser/pass_typing.rs +++ b/src/protocol/parser/pass_typing.rs @@ -1631,6 +1631,8 @@ impl PassTyping { let progress_forced = match expr.operation { AO::Set => false, + AO::Concatenated => + self.apply_template_constraint(ctx, arg1_expr_id, &ARRAYLIKE_TEMPLATE)?, AO::Multiplied | AO::Divided | AO::Added | AO::Subtracted => self.apply_template_constraint(ctx, arg1_expr_id, &NUMBERLIKE_TEMPLATE)?, AO::Remained | AO::ShiftedLeft | AO::ShiftedRight | @@ -1641,7 +1643,6 @@ impl PassTyping { let (progress_arg1, progress_arg2) = self.apply_equal2_constraint( ctx, upcast_id, arg1_expr_id, 0, arg2_expr_id, 0 )?; - debug_assert!(if progress_forced { progress_arg2 } else { true }); debug_log!(" * After:"); debug_log!(" - Arg1 type [{}]: {}", progress_forced || progress_arg1, self.debug_get_display_name(ctx, arg1_expr_id)); diff --git a/src/protocol/parser/tokens.rs b/src/protocol/parser/tokens.rs index 7b273223f931c88ea68e0e327cd9d3b2e049f5b8..5b0e42b581f0bc867a38d59c27ebb4df34984482 100644 --- a/src/protocol/parser/tokens.rs +++ b/src/protocol/parser/tokens.rs @@ -49,6 +49,7 @@ pub enum TokenKind { DotDot, // .. ArrowRight, // -> // Operator-like (two characters) + AtEquals, // @= PlusPlus, // ++ PlusEquals, // += MinusMinus, // -- @@ -87,7 +88,7 @@ impl TokenKind { debug_assert!(!self.has_span_end() && *self != TokenKind::SpanEnd); if *self <= TokenKind::Equal { 1 - } else if *self <= TokenKind::ShiftRight { + } else if *self <= TokenKind::GreaterEquals { 2 } else { 3 @@ -129,6 +130,7 @@ impl TokenKind { TK::ColonColon => "::", TK::DotDot => "..", TK::ArrowRight => "->", + TK::AtEquals => "@=", TK::PlusPlus => "++", TK::PlusEquals => "+=", TK::MinusMinus => "--", diff --git a/src/protocol/tests/eval_binding.rs b/src/protocol/tests/eval_binding.rs index acdeb70a9569f6b744905e408e4f673f3c565902..31ef8934ce3472c6a7d53c7fffd9306d7bfd88c7 100644 --- a/src/protocol/tests/eval_binding.rs +++ b/src/protocol/tests/eval_binding.rs @@ -134,7 +134,7 @@ fn test_binding_fizz_buzz() { Tester::new_single_source_expect_ok("am I employable?", " union Fizzable { Number(u32), FizzBuzz, Fizz, Buzz } - func construct_fizz_buzz(u32 num) -> Fizzable[] { + func construct_fizz_buzz_very_slow(u32 num) -> Fizzable[] { u32 counter = 1; auto result = {}; while (counter <= num) { @@ -156,6 +156,28 @@ fn test_binding_fizz_buzz() { return result; } + func construct_fizz_buzz_slightly_less_slow(u32 num) -> Fizzable[] { + u32 counter = 1; + auto result = {}; + while (counter <= num) { + auto value = Fizzable::Number(counter); + if (counter % 5 == 0) { + if (counter % 3 == 0) { + value = Fizzable::FizzBuzz; + } else { + value = Fizzable::Buzz; + } + } else if (counter % 3 == 0) { + value = Fizzable::Fizz; + } + + result @= { value }; // woohoo, no array builtins! + counter += 1; + } + + return result; + } + func test_fizz_buzz(Fizzable[] fizzoid) -> bool { u32 idx = 0; bool valid = true; @@ -184,8 +206,14 @@ fn test_binding_fizz_buzz() { } func fizz_buzz() -> bool { - auto fizz = construct_fizz_buzz(100); - return test_fizz_buzz(fizz); + // auto fizz_more_slow = construct_fizz_buzz_very_slow(100); + // return test_fizz_buzz(fizz_more_slow); + // auto fizz_less_slow = construct_fizz_buzz_slightly_less_slow(100); + // return test_fizz_buzz(fizz_less_slow); + + auto fizz_more_slow2 = construct_fizz_buzz_very_slow(100); + auto fizz_less_slow2 = construct_fizz_buzz_slightly_less_slow(100); + return test_fizz_buzz(fizz_more_slow2) && test_fizz_buzz(fizz_less_slow2); } ").for_function("fizz_buzz", |f| { f .call_ok(Some(Value::Bool(true)));