From 9b32fa307ceb67302d9bb9caa8599a8f654c22f4 2021-05-11 13:04:29 From: MH Date: 2021-05-11 13:04:29 Subject: [PATCH] WIP on finishing evaluator --- diff --git a/src/protocol/ast.rs b/src/protocol/ast.rs index 56a0149fed527e40c7493e409b86c5dc78feadbc..71ea38e2e1768e374b9d1fb2b5726386ca1482ab 100644 --- a/src/protocol/ast.rs +++ b/src/protocol/ast.rs @@ -357,6 +357,12 @@ impl Display for Identifier { #[derive(Debug, Clone, PartialEq, Eq)] pub enum ParserTypeVariant { + // Special builtin, only usable by the compiler and not constructable by the + // programmer + Void, + InputOrOutput, + ArrayLike, + IntegerLike, // Basic builtin Message, Bool, @@ -381,13 +387,14 @@ impl ParserTypeVariant { use ParserTypeVariant::*; match self { + Void | IntegerLike | Message | Bool | UInt8 | UInt16 | UInt32 | UInt64 | SInt8 | SInt16 | SInt32 | SInt64 | Character | String | IntegerLiteral | Inferred | PolymorphicArgument(_, _) => 0, - Array | Input | Output => + ArrayLike | InputOrOutput | Array | Input | Output => 1, Definition(_, num) => *num, } @@ -1209,31 +1216,6 @@ pub struct BlockStatement { pub labels: Vec, } -impl BlockStatement { - pub fn parent_block(&self, h: &Heap) -> Option { - let parent = self.parent_scope.unwrap(); - match parent { - Scope::Definition(_) => { - // If the parent scope is a definition, then there is no - // parent block. - None - } - Scope::Synchronous((parent, _)) => { - // It is always the case that when this function is called, - // the parent of a synchronous statement is a block statement: - // nested synchronous statements are flagged illegal, - // and that happens before resolving variables that - // creates the parent_scope references in the first place. - Some(h[parent].parent_scope.unwrap().to_block()) - } - Scope::Regular(parent) => { - // A variable scope is either a definition, sync, or block. - Some(parent) - } - } - } -} - #[derive(Debug, Clone)] pub struct EndBlockStatement { pub this: EndBlockStatementId, diff --git a/src/protocol/ast_printer.rs b/src/protocol/ast_printer.rs index c1c80fa9a39a3388c55b0b95158cd39055d18145..058a2ace98a89cc1189f5e21ab8d0e2c9dfd01a5 100644 --- a/src/protocol/ast_printer.rs +++ b/src/protocol/ast_printer.rs @@ -423,8 +423,7 @@ impl ASTWriter { self.write_variable(heap, stmt.from, indent3); self.kv(indent2).with_s_key("To"); self.write_variable(heap, stmt.to, indent3); - self.kv(indent2).with_s_key("Next") - .with_opt_disp_val(stmt.next.as_ref().map(|v| &v.index)); + self.kv(indent2).with_s_key("Next").with_disp_val(&stmt.next.index); }, LocalStatement::Memory(stmt) => { self.kv(indent).with_id(PREFIX_MEM_STMT_ID, stmt.this.0.0.index) @@ -432,8 +431,7 @@ impl ASTWriter { self.kv(indent2).with_s_key("Variable"); self.write_variable(heap, stmt.variable, indent3); - self.kv(indent2).with_s_key("Next") - .with_opt_disp_val(stmt.next.as_ref().map(|v| &v.index)); + self.kv(indent2).with_s_key("Next").with_disp_val(&stmt.next.index); } } }, @@ -449,8 +447,7 @@ impl ASTWriter { self.kv(indent).with_id(PREFIX_IF_STMT_ID, stmt.this.0.index) .with_s_key("If"); - self.kv(indent2).with_s_key("EndIf") - .with_opt_disp_val(stmt.end_if.as_ref().map(|v| &v.0.index)); + self.kv(indent2).with_s_key("EndIf").with_disp_val(&stmt.end_if.0.index); self.kv(indent2).with_s_key("Condition"); self.write_expr(heap, stmt.test, indent3); @@ -467,15 +464,13 @@ impl ASTWriter { self.kv(indent).with_id(PREFIX_ENDIF_STMT_ID, stmt.this.0.index) .with_s_key("EndIf"); self.kv(indent2).with_s_key("StartIf").with_disp_val(&stmt.start_if.0.index); - self.kv(indent2).with_s_key("Next") - .with_opt_disp_val(stmt.next.as_ref().map(|v| &v.index)); + self.kv(indent2).with_s_key("Next").with_disp_val(&stmt.next.index); }, Statement::While(stmt) => { self.kv(indent).with_id(PREFIX_WHILE_STMT_ID, stmt.this.0.index) .with_s_key("While"); - self.kv(indent2).with_s_key("EndWhile") - .with_opt_disp_val(stmt.end_while.as_ref().map(|v| &v.0.index)); + self.kv(indent2).with_s_key("EndWhile").with_disp_val(&stmt.end_while.0.index); self.kv(indent2).with_s_key("InSync") .with_opt_disp_val(stmt.in_sync.as_ref().map(|v| &v.0.index)); self.kv(indent2).with_s_key("Condition"); @@ -487,8 +482,7 @@ impl ASTWriter { self.kv(indent).with_id(PREFIX_ENDWHILE_STMT_ID, stmt.this.0.index) .with_s_key("EndWhile"); self.kv(indent2).with_s_key("StartWhile").with_disp_val(&stmt.start_while.0.index); - self.kv(indent2).with_s_key("Next") - .with_opt_disp_val(stmt.next.as_ref().map(|v| &v.index)); + self.kv(indent2).with_s_key("Next").with_disp_val(&stmt.next.index); }, Statement::Break(stmt) => { self.kv(indent).with_id(PREFIX_BREAK_STMT_ID, stmt.this.0.index) @@ -509,8 +503,7 @@ impl ASTWriter { Statement::Synchronous(stmt) => { self.kv(indent).with_id(PREFIX_SYNC_STMT_ID, stmt.this.0.index) .with_s_key("Synchronous"); - self.kv(indent2).with_s_key("EndSync") - .with_opt_disp_val(stmt.end_sync.as_ref().map(|v| &v.0.index)); + self.kv(indent2).with_s_key("EndSync").with_disp_val(&stmt.end_sync.0.index); self.kv(indent2).with_s_key("Body"); self.write_stmt(heap, stmt.body.upcast(), indent3); }, @@ -518,8 +511,7 @@ impl ASTWriter { self.kv(indent).with_id(PREFIX_ENDSYNC_STMT_ID, stmt.this.0.index) .with_s_key("EndSynchronous"); self.kv(indent2).with_s_key("StartSync").with_disp_val(&stmt.start_sync.0.index); - self.kv(indent2).with_s_key("Next") - .with_opt_disp_val(stmt.next.as_ref().map(|v| &v.index)); + self.kv(indent2).with_s_key("Next").with_disp_val(&stmt.next.index); }, Statement::Return(stmt) => { self.kv(indent).with_id(PREFIX_RETURN_STMT_ID, stmt.this.0.index) @@ -541,15 +533,13 @@ impl ASTWriter { .with_s_key("New"); self.kv(indent2).with_s_key("Expression"); self.write_expr(heap, stmt.expression.upcast(), indent3); - self.kv(indent2).with_s_key("Next") - .with_opt_disp_val(stmt.next.as_ref().map(|v| &v.index)); + self.kv(indent2).with_s_key("Next").with_disp_val(&stmt.next.index); }, Statement::Expression(stmt) => { self.kv(indent).with_id(PREFIX_EXPR_STMT_ID, stmt.this.0.index) .with_s_key("ExpressionStatement"); self.write_expr(heap, stmt.expression, indent2); - self.kv(indent2).with_s_key("Next") - .with_opt_disp_val(stmt.next.as_ref().map(|v| &v.index)); + self.kv(indent2).with_s_key("Next").with_disp_val(&stmt.next.index); } } } @@ -791,7 +781,7 @@ impl ASTWriter { let var = &heap[variable_id]; let indent2 = indent + 1; - self.kv(indent).with_id(PREFIX_VARIABLE_ID, variable_id.0.index) + self.kv(indent).with_id(PREFIX_VARIABLE_ID, variable_id.index) .with_s_key("Variable"); self.kv(indent2).with_s_key("Name").with_identifier_val(&var.identifier); @@ -827,39 +817,46 @@ fn write_option(target: &mut String, value: Option) { fn write_parser_type(target: &mut String, heap: &Heap, t: &ParserType) { use ParserTypeVariant as PTV; - fn push_bytes(target: &mut String, msg: &[u8]) { - target.push_str(&String::from_utf8_lossy(msg)); - } - fn write_element(target: &mut String, heap: &Heap, t: &ParserType, mut element_idx: usize) -> usize { let element = &t.elements[element_idx]; match &element.variant { - PTV::Message => { push_bytes(target, KW_TYPE_MESSAGE); }, - PTV::Bool => { push_bytes(target, KW_TYPE_BOOL); }, - PTV::UInt8 => { push_bytes(target, KW_TYPE_UINT8); }, - PTV::UInt16 => { push_bytes(target, KW_TYPE_UINT16); }, - PTV::UInt32 => { push_bytes(target, KW_TYPE_UINT32); }, - PTV::UInt64 => { push_bytes(target, KW_TYPE_UINT64); }, - PTV::SInt8 => { push_bytes(target, KW_TYPE_SINT8); }, - PTV::SInt16 => { push_bytes(target, KW_TYPE_SINT16); }, - PTV::SInt32 => { push_bytes(target, KW_TYPE_SINT32); }, - PTV::SInt64 => { push_bytes(target, KW_TYPE_SINT64); }, - PTV::Character => { push_bytes(target, KW_TYPE_CHAR); }, - PTV::String => { push_bytes(target, KW_TYPE_STRING); }, + PTV::Void => target.push_str("void"), + PTV::InputOrOutput => { + target.push_str("portlike<"); + element_idx = write_element(target, heap, t, element_idx + 1); + target.push('>'); + }, + PTV::ArrayLike => { + element_idx = write_element(target, heap, t, element_idx + 1); + target.push_str("[???]"); + }, + PTV::IntegerLike => target.push_str("integerlike"), + PTV::Message => { target.push_str(KW_TYPE_MESSAGE_STR); }, + PTV::Bool => { target.push_str(KW_TYPE_BOOL_STR); }, + PTV::UInt8 => { target.push_str(KW_TYPE_UINT8_STR); }, + PTV::UInt16 => { target.push_str(KW_TYPE_UINT16_STR); }, + PTV::UInt32 => { target.push_str(KW_TYPE_UINT32_STR); }, + PTV::UInt64 => { target.push_str(KW_TYPE_UINT64_STR); }, + PTV::SInt8 => { target.push_str(KW_TYPE_SINT8_STR); }, + PTV::SInt16 => { target.push_str(KW_TYPE_SINT16_STR); }, + PTV::SInt32 => { target.push_str(KW_TYPE_SINT32_STR); }, + PTV::SInt64 => { target.push_str(KW_TYPE_SINT64_STR); }, + PTV::Character => { target.push_str(KW_TYPE_CHAR_STR); }, + PTV::String => { target.push_str(KW_TYPE_STRING_STR); }, PTV::IntegerLiteral => { target.push_str("int_literal"); }, - PTV::Inferred => { push_bytes(target, KW_TYPE_INFERRED); }, + PTV::Inferred => { target.push_str(KW_TYPE_INFERRED_STR); }, PTV::Array => { element_idx = write_element(target, heap, t, element_idx + 1); target.push_str("[]"); }, PTV::Input => { - push_bytes(target, KW_TYPE_IN_PORT); + target.push_str(KW_TYPE_IN_PORT_STR); target.push('<'); element_idx = write_element(target, heap, t, element_idx + 1); target.push('>'); }, PTV::Output => { - push_bytes(target, KW_TYPE_OUT_PORT); + target.push_str(KW_TYPE_OUT_PORT_STR); target.push('<'); element_idx = write_element(target, heap, t, element_idx + 1); target.push('>'); diff --git a/src/protocol/eval/executor.rs b/src/protocol/eval/executor.rs index f9ce1d8964fcd9e63a44f2442b77683e47c7f3fd..357193c6e57e241286b52eb9658e678a33e2484d 100644 --- a/src/protocol/eval/executor.rs +++ b/src/protocol/eval/executor.rs @@ -6,12 +6,14 @@ use super::store::*; use crate::protocol::*; use crate::protocol::ast::*; -enum ExprInstruction { +#[derive(Debug, Clone)] +pub(crate) enum ExprInstruction { EvalExpr(ExpressionId), PushValToFront, } -struct Frame { +#[derive(Debug, Clone)] +pub(crate) struct Frame { definition: DefinitionId, position: StatementId, expr_stack: VecDeque, // hack for expression evaluation, evaluated by popping from back @@ -138,7 +140,7 @@ impl Frame { } } -type EvalResult = Result<(), EvalContinuation>; +type EvalResult = Result; pub enum EvalContinuation { Stepping, Inconsistent, @@ -151,6 +153,9 @@ pub enum EvalContinuation { Put(Value, Value), } +// Note: cloning is fine, methinks. cloning all values and the heap regions then +// we end up with valid "pointers" to heap regions. +#[derive(Debug, Clone)] pub struct Prompt { pub(crate) frames: Vec, pub(crate) store: Store, @@ -169,13 +174,13 @@ impl Prompt { prompt } - pub fn step(&mut self, heap: &Heap, ctx: &mut EvalContext) -> EvalResult { + pub(crate) fn step(&mut self, heap: &Heap, ctx: &mut EvalContext) -> EvalResult { let cur_frame = self.frames.last_mut().unwrap(); if cur_frame.position.is_invalid() { if heap[cur_frame.definition].is_function() { todo!("End of function without return, return an evaluation error"); } - return Err(EvalContinuation::Terminal); + return Ok(EvalContinuation::Terminal); } while !cur_frame.expr_stack.is_empty() { @@ -190,9 +195,10 @@ impl Prompt { Expression::Assignment(expr) => { let to = cur_frame.expr_values.pop_back().unwrap().as_ref(); let rhs = cur_frame.expr_values.pop_back().unwrap(); + let rhs_heap_pos = rhs.get_heap_pos(); apply_assignment_operator(&mut self.store, to, expr.operation, rhs); cur_frame.expr_values.push_back(self.store.read_copy(to)); - self.store.drop_value(&rhs); + self.store.drop_value(rhs_heap_pos); }, Expression::Binding(_expr) => { todo!("Binding expression"); @@ -212,24 +218,22 @@ impl Prompt { let rhs = cur_frame.expr_values.pop_back().unwrap(); let result = apply_binary_operator(&mut self.store, &lhs, expr.operation, &rhs); cur_frame.expr_values.push_back(result); - self.store.drop_value(&lhs); - self.store.drop_value(&rhs); + self.store.drop_value(lhs.get_heap_pos()); + self.store.drop_value(rhs.get_heap_pos()); }, Expression::Unary(expr) => { let val = cur_frame.expr_values.pop_back().unwrap(); let result = apply_unary_operator(&mut self.store, expr.operation, &val); cur_frame.expr_values.push_back(result); - self.store.drop_value(&val); + self.store.drop_value(val.get_heap_pos()); }, Expression::Indexing(expr) => { // TODO: Out of bounds checking // Evaluate index. Never heap allocated so we do // not have to drop it. let index = cur_frame.expr_values.pop_back().unwrap(); - let index = match &index { - Value::Ref(value_ref) => self.store.read_ref(*value_ref), - index => index, - }; + let index = self.store.maybe_read_ref(&index); + debug_assert!(index.is_integer()); let index = if index.is_signed_integer() { index.as_signed_integer() as u32 @@ -237,14 +241,17 @@ impl Prompt { index.as_unsigned_integer() as u32 }; + // TODO: This is probably wrong, we're dropping the + // heap while refering to an element... let subject = cur_frame.expr_values.pop_back().unwrap(); + let subject_heap_pos = subject.get_heap_pos(); let heap_pos = match subject { Value::Ref(value_ref) => self.store.read_ref(value_ref).as_array(), val => val.as_array(), }; cur_frame.expr_values.push_back(Value::Ref(ValueId::Heap(heap_pos, index))); - self.store.drop_value(&subject); + self.store.drop_value(subject_heap_pos); }, Expression::Slicing(expr) => { // TODO: Out of bounds checking @@ -258,7 +265,7 @@ impl Prompt { }; cur_frame.expr_values.push_back(Value::Ref(ValueId::Heap(heap_pos, expr.field.as_symbolic().field_idx as u32))); - self.store.drop_value(&subject); + self.store.drop_value(subject.get_heap_pos()); }, Expression::Literal(expr) => { let value = match &expr.value { @@ -305,7 +312,7 @@ impl Prompt { Value::Struct(heap_pos) } Literal::Enum(lit_value) => { - Value::Enum(lit_value.variant_idx as i64); + Value::Enum(lit_value.variant_idx as i64) } Literal::Union(lit_value) => { let heap_pos = self.store.alloc_heap(); @@ -327,6 +334,7 @@ impl Prompt { for _ in 0..num_values { values.push(cur_frame.expr_values.pop_front().unwrap()) } + Value::Array(heap_pos) } }; @@ -351,7 +359,7 @@ impl Prompt { // To simplify the logic a little bit we will now // return and ask our caller to call us again - return Err(EvalContinuation::Stepping); + return Ok(EvalContinuation::Stepping); }, Expression::Variable(expr) => { let variable = &heap[expr.declaration.unwrap()]; @@ -373,14 +381,14 @@ impl Prompt { self.store.reserve_stack(stmt.next_unique_id_in_scope as usize); cur_frame.position = stmt.statements[0]; - Ok(()) + Ok(EvalContinuation::Stepping) }, Statement::EndBlock(stmt) => { let block = &heap[stmt.start_block]; - self.store.clear_stack(stmt.first_unique_id_in_scope as usize); + self.store.clear_stack(block.first_unique_id_in_scope as usize); cur_frame.position = stmt.next; - Ok(()) + Ok(EvalContinuation::Stepping) }, Statement::Local(stmt) => { match stmt { @@ -399,12 +407,12 @@ impl Prompt { } } - Ok(()) + Ok(EvalContinuation::Stepping) }, Statement::Labeled(stmt) => { cur_frame.position = stmt.body; - Ok(()) + Ok(EvalContinuation::Stepping) }, Statement::If(stmt) => { debug_assert_eq!(cur_frame.expr_values.len(), 1, "expected one expr value for if statement"); @@ -415,14 +423,14 @@ impl Prompt { cur_frame.position = false_body.upcast(); } else { // Not true, and no false body - cur_frame.position = stmt.end_if.unwrap().upcast(); + cur_frame.position = stmt.end_if.upcast(); } - Ok(()) + Ok(EvalContinuation::Stepping) }, Statement::EndIf(stmt) => { cur_frame.position = stmt.next; - Ok(()) + Ok(EvalContinuation::Stepping) }, Statement::While(stmt) => { debug_assert_eq!(cur_frame.expr_values.len(), 1, "expected one expr value for while statement"); @@ -430,61 +438,75 @@ impl Prompt { if test_value { cur_frame.position = stmt.body.upcast(); } else { - cur_frame.position = stmt.end_while.unwrap().upcast(); + cur_frame.position = stmt.end_while.upcast(); } - Ok(()) + Ok(EvalContinuation::Stepping) }, Statement::EndWhile(stmt) => { cur_frame.position = stmt.next; - Ok(()) + Ok(EvalContinuation::Stepping) }, Statement::Break(stmt) => { cur_frame.position = stmt.target.unwrap().upcast(); - Ok(()) + Ok(EvalContinuation::Stepping) }, Statement::Continue(stmt) => { cur_frame.position = stmt.target.unwrap().upcast(); - Ok(()) + Ok(EvalContinuation::Stepping) }, Statement::Synchronous(stmt) => { cur_frame.position = stmt.body.upcast(); - Err(EvalContinuation::SyncBlockStart) + Ok(EvalContinuation::SyncBlockStart) }, Statement::EndSynchronous(stmt) => { cur_frame.position = stmt.next; - Err(EvalContinuation::SyncBlockEnd) + Ok(EvalContinuation::SyncBlockEnd) }, Statement::Return(stmt) => { debug_assert!(heap[cur_frame.definition].is_function()); debug_assert_eq!(cur_frame.expr_values.len(), 1, "expected one expr value for return statement"); - // Clear any values in the current stack frame - self.store.clear_stack(0); // The preceding frame has executed a call, so is expecting the - // return expression on its expression value stack. + // return expression on its expression value stack. Note that + // we may be returning a reference to something on our stack, + // so we need to read that value and clone it. let return_value = cur_frame.expr_values.pop_back().unwrap(); + let return_value = match return_value { + Value::Ref(value_id) => self.store.read_copy(value_id), + _ => return_value, + }; + let prev_stack_idx = self.store.stack[self.store.cur_stack_boundary].as_stack_boundary(); + self.frames.pop(); + self.store.clear_stack(0); + + // TODO: Temporary hack for testing, remove at some point + if self.frames.is_empty() { + debug_assert!(prev_stack_idx == -1); + self.store.stack[0] = return_value; + return Ok(EvalContinuation::Terminal); + } + debug_assert!(prev_stack_idx >= 0); self.store.cur_stack_boundary = prev_stack_idx as usize; - self.frames.pop(); let cur_frame = self.frames.last_mut().unwrap(); cur_frame.expr_values.push_back(return_value); // Immediately return, we don't care about the current frame // anymore and there is nothing left to evaluate - return Ok(()); + return Ok(EvalContinuation::Stepping); }, Statement::Goto(stmt) => { cur_frame.position = stmt.target.unwrap().upcast(); - Ok(()) + Ok(EvalContinuation::Stepping) }, Statement::New(stmt) => { let call_expr = &heap[stmt.expression]; @@ -502,10 +524,18 @@ impl Prompt { args.push(value); } + // Construct argument group, thereby copying heap regions + let argument_group = ValueGroup::from_store(&self.store, &args); + + // Clear any heap regions + for arg in &args { + self.store.drop_value(arg.get_heap_pos()); + } + cur_frame.position = stmt.next; todo!("Make sure this is handled correctly, transfer 'heap' values to another Prompt"); - Err(EvalContinuation::NewComponent(call_expr.definition, args)) + Ok(EvalContinuation::NewComponent(call_expr.definition, argument_group)) }, Statement::Expression(stmt) => { // The expression has just been completely evaluated. Some @@ -513,7 +543,7 @@ impl Prompt { cur_frame.expr_values.clear(); cur_frame.position = stmt.next; - Ok(()) + Ok(EvalContinuation::Stepping) }, }; diff --git a/src/protocol/eval/mod.rs b/src/protocol/eval/mod.rs index d234f378e8c6e78306a6287808fbe9ffa844756c..d8dd58ddadf2283671e42215f56c632ac76c98d1 100644 --- a/src/protocol/eval/mod.rs +++ b/src/protocol/eval/mod.rs @@ -25,5 +25,6 @@ mod store; mod executor; pub use value::{Value, ValueGroup}; +pub(crate) use store::{Store}; pub use executor::{EvalContinuation, Prompt}; diff --git a/src/protocol/eval/store.rs b/src/protocol/eval/store.rs index ac6a01d709149e186b655189a7a31194c1f25b6a..e0bed260e87ce5062e3b6cd498b005585946f7b8 100644 --- a/src/protocol/eval/store.rs +++ b/src/protocol/eval/store.rs @@ -3,10 +3,12 @@ use std::collections::VecDeque; use super::value::{Value, ValueId, HeapPos}; +#[derive(Debug, Clone)] pub(crate) struct HeapAllocation { pub values: Vec, } +#[derive(Debug, Clone)] pub(crate) struct Store { // The stack where variables/parameters are stored. Note that this is a // non-shrinking stack. So it may be filled with garbage. @@ -50,7 +52,7 @@ impl Store { pub(crate) fn clear_stack(&mut self, unique_stack_idx: usize) { let new_size = self.cur_stack_boundary + unique_stack_idx + 1; for idx in new_size..self.stack.len() { - self.drop_value(&self.stack[idx]); + self.drop_value(self.stack[idx].get_heap_pos()); self.stack[idx] = Value::Unassigned; } } @@ -62,16 +64,27 @@ impl Store { match address { ValueId::Stack(pos) => { let cur_pos = self.cur_stack_boundary + 1 + pos as usize; - return self.clone_value(&self.stack[cur_pos]); + return self.clone_value(self.stack[cur_pos].clone()); }, ValueId::Heap(heap_pos, region_idx) => { - return self.clone_value(&self.heap_regions[heap_pos as usize].values[region_idx as usize]) + return self.clone_value(self.heap_regions[heap_pos as usize].values[region_idx as usize].clone()) } } } + /// Potentially reads a reference value. The supplied `Value` might not + /// actually live in the store's stack or heap, but live on the expression + /// stack. Generally speaking you only want to call this if the value comes + /// from the expression stack due to borrowing issues. + pub(crate) fn maybe_read_ref<'a>(&'a self, value: &'a Value) -> &'a Value { + match value { + Value::Ref(value_id) => self.read_ref(*value_id), + _ => value, + } + } + /// Returns an immutable reference to the value pointed to by an address - pub(crate) fn read_ref(&mut self, address: ValueId) -> &Value { + pub(crate) fn read_ref(&self, address: ValueId) -> &Value { match address { ValueId::Stack(pos) => { let cur_pos = self.cur_stack_boundary + 1 + pos as usize; @@ -101,24 +114,28 @@ impl Store { match address { ValueId::Stack(pos) => { let cur_pos = self.cur_stack_boundary + 1 + pos as usize; - self.drop_value(&self.stack[cur_pos]); + self.drop_value(self.stack[cur_pos].get_heap_pos()); self.stack[cur_pos] = value; }, ValueId::Heap(heap_pos, region_idx) => { let heap_pos = heap_pos as usize; let region_idx = region_idx as usize; - self.drop_value(&self.heap_regions[heap_pos].values[region_idx]); + self.drop_value(self.heap_regions[heap_pos].values[region_idx].get_heap_pos()); self.heap_regions[heap_pos].values[region_idx] = value } } } - fn clone_value(&mut self, value: &Value) -> Value { + /// This thing takes a cloned Value, because of borrowing issues (which is + /// either a direct value, or might contain an index to a heap value), but + /// should be treated by the programmer as a reference (i.e. don't call + /// `drop_value(thing)` after calling `clone_value(thing.clone())`. + fn clone_value(&mut self, value: Value) -> Value { // Quickly check if the value is not on the heap let source_heap_pos = value.get_heap_pos(); if source_heap_pos.is_none() { // We can do a trivial copy - return value.clone(); + return value; } // Value does live on heap, copy it @@ -128,21 +145,21 @@ impl Store { let num_values = self.heap_regions[source_heap_pos].values.len(); for value_idx in 0..num_values { - let cloned = self.clone_value(&self.heap_regions[source_heap_pos].values[value_idx]); + let cloned = self.clone_value(self.heap_regions[source_heap_pos].values[value_idx].clone()); self.heap_regions[target_heap_pos_usize].values.push(cloned); } match value { Value::Message(_) => Value::Message(target_heap_pos), Value::Array(_) => Value::Array(target_heap_pos), - Value::Union(tag, _) => Value::Union(*tag, target_heap_pos), + Value::Union(tag, _) => Value::Union(tag, target_heap_pos), Value::Struct(_) => Value::Struct(target_heap_pos), _ => unreachable!("performed clone_value on heap, but {:?} is not a heap value", value), } } - pub(crate) fn drop_value(&mut self, value: &Value) { - if let Some(heap_pos) = value.get_heap_pos() { + pub(crate) fn drop_value(&mut self, value: Option) { + if let Some(heap_pos) = value { self.drop_heap_pos(heap_pos); } } diff --git a/src/protocol/eval/value.rs b/src/protocol/eval/value.rs index af3c0d9a009887cb98bb27d38caed6747d99b305..ee08ad1323fe931b22e1adef58449793c1eab082 100644 --- a/src/protocol/eval/value.rs +++ b/src/protocol/eval/value.rs @@ -10,7 +10,7 @@ use crate::protocol::ast::{ pub type StackPos = u32; pub type HeapPos = u32; -#[derive(Copy, Clone)] +#[derive(Debug, Copy, Clone)] pub enum ValueId { Stack(StackPos), // place on stack Heap(HeapPos, u32), // allocated region + values within that region @@ -192,7 +192,7 @@ impl ValueGroup { let from_region = &from_store.heap_regions[heap_pos as usize].values; let mut new_region = Vec::with_capacity(from_region.len()); for value in from_region { - let transferred = self.transfer_value(value, from_store); + let transferred = self.retrieve_value(value, from_store); new_region.push(transferred); } @@ -216,7 +216,7 @@ impl ValueGroup { /// Transfers the heap values and the stack values into the store. Stack /// values are pushed onto the Store's stack in the order in which they /// appear in the value group. - pub fn into_store(self, store: &mut Store) { + pub(crate) fn into_store(self, store: &mut Store) { for value in &self.values { let transferred = self.provide_value(value, store); store.stack.push(transferred); @@ -328,74 +328,120 @@ pub(crate) fn apply_assignment_operator(store: &mut Store, lhs: ValueId, op: Ass 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 { + macro_rules! apply_int_op_and_return_self { ($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()); }, + 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) - } + macro_rules! apply_int_op_and_return_bool { + ($lhs:ident, $operator_tokens:tt, $operator:ident, $rhs:ident) => { + return match $lhs { + Value::UInt8(v) => { Value::Bool(*v $operator_tokens $rhs.as_uint8() ) }, + Value::UInt16(v) => { Value::Bool(*v $operator_tokens $rhs.as_uint16()) }, + Value::UInt32(v) => { Value::Bool(*v $operator_tokens $rhs.as_uint32()) }, + Value::UInt64(v) => { Value::Bool(*v $operator_tokens $rhs.as_uint64()) }, + Value::SInt8(v) => { Value::Bool(*v $operator_tokens $rhs.as_sint8() ) }, + Value::SInt16(v) => { Value::Bool(*v $operator_tokens $rhs.as_sint16()) }, + Value::SInt32(v) => { Value::Bool(*v $operator_tokens $rhs.as_sint32()) }, + Value::SInt64(v) => { Value::Bool(*v $operator_tokens $rhs.as_sint64()) }, + _ => unreachable!("apply_binary_operator {:?} on lhs {:?} and rhs {:?}", $operator, $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); - }, + // We need to handle concatenate in a special way because it needs the store + // mutably. + if op == BO::Concatenate { + let target_heap_pos = store.alloc_heap(); + let lhs_heap_pos; + let rhs_heap_pos; + + let lhs = store.maybe_read_ref(lhs); + let rhs = store.maybe_read_ref(rhs); + + enum ValueKind { Message, String, Array }; + let value_kind; + + match lhs { + Value::Message(lhs_pos) => { + lhs_heap_pos = *lhs_pos; + rhs_heap_pos = rhs.as_message(); + value_kind = ValueKind::Message; + }, + Value::String(lhs_pos) => { + lhs_heap_pos = *lhs_pos; + rhs_heap_pos = rhs.as_string(); + value_kind = ValueKind::String; + }, + Value::Array(lhs_pos) => { + lhs_heap_pos = *lhs_pos; + rhs_heap_pos = rhs.as_array(); + value_kind = ValueKind::Array; + }, + _ => unreachable!("apply_binary_operator {:?} on lhs {:?} and rhs {:?}", op, lhs, rhs) + } + + // TODO: I hate this, but fine... + let mut concatenated = Vec::new(); + concatenated.extend_from_slice(&store.heap_regions[lhs_heap_pos as usize].values); + concatenated.extend_from_slice(&store.heap_regions[rhs_heap_pos as usize].values); + + store.heap_regions[target_heap_pos as usize].values = concatenated; + + return match value_kind{ + ValueKind::Message => Value::Message(target_heap_pos), + ValueKind::String => Value::String(target_heap_pos), + ValueKind::Array => Value::Array(target_heap_pos), + }; + } + + // If any of the values are references, retrieve the thing they're referring + // to. + let lhs = match lhs { + Value::Ref(value_id) => store.read_ref(*value_id), + _ => lhs, + }; + + let rhs = match rhs { + Value::Ref(value_id) => store.read_ref(*value_id), + _ => rhs, + }; + + match op { + BO::Concatenate => unreachable!(), 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::BitwiseOr => { apply_int_op_and_return_self!(lhs, |, op, rhs); }, + BO::BitwiseXor => { apply_int_op_and_return_self!(lhs, ^, op, rhs); }, + BO::BitwiseAnd => { apply_int_op_and_return_self!(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); } + BO::LessThan => { apply_int_op_and_return_bool!(lhs, <, op, rhs); }, + BO::GreaterThan => { apply_int_op_and_return_bool!(lhs, >, op, rhs); }, + BO::LessThanEqual => { apply_int_op_and_return_bool!(lhs, <=, op, rhs); }, + BO::GreaterThanEqual => { apply_int_op_and_return_bool!(lhs, >=, op, rhs); }, + BO::ShiftLeft => { apply_int_op_and_return_self!(lhs, <<, op, rhs); }, + BO::ShiftRight => { apply_int_op_and_return_self!(lhs, >>, op, rhs); }, + BO::Add => { apply_int_op_and_return_self!(lhs, +, op, rhs); }, + BO::Subtract => { apply_int_op_and_return_self!(lhs, -, op, rhs); }, + BO::Multiply => { apply_int_op_and_return_self!(lhs, *, op, rhs); }, + BO::Divide => { apply_int_op_and_return_self!(lhs, /, op, rhs); }, + BO::Remainder => { apply_int_op_and_return_self!(lhs, %, op, rhs); } } } @@ -418,12 +464,24 @@ pub(crate) fn apply_unary_operator(store: &mut Store, op: UnaryOperator, value: } } + // If the value is a reference, retrieve the thing it is referring to + let value = store.maybe_read_ref(value); + match op { - UO::Positive => { + UO::Positive => { debug_assert!(value.is_integer()); return value.clone(); }, - UO::Negative => { apply_int_expr_and_return!(value, -, op) }, + UO::Negative => { + // TODO: Error on negating unsigned integers + return match value { + Value::SInt8(v) => Value::SInt8(-*v), + Value::SInt16(v) => Value::SInt16(-*v), + Value::SInt32(v) => Value::SInt32(-*v), + Value::SInt64(v) => Value::SInt64(-*v), + _ => unreachable!("apply_unary_operator {:?} on value {:?}", op, value), + } + }, UO::BitwiseNot => { apply_int_expr_and_return!(value, !, op)}, UO::LogicalNot => { return Value::Bool(!value.as_bool()); }, UO::PreIncrement => { todo!("implement") }, @@ -431,4 +489,8 @@ pub(crate) fn apply_unary_operator(store: &mut Store, op: UnaryOperator, value: UO::PostIncrement => { todo!("implement") }, UO::PostDecrement => { todo!("implement") }, } +} + +pub(crate) fn apply_equality_operator(store: &Store, lhs: &Value, rhs: &Value) -> bool { + } \ No newline at end of file diff --git a/src/protocol/input_source.rs b/src/protocol/input_source.rs index 0a5304926c32cb91d49059c198624c10c6bd0e59..b3eb530d5c0239c2995b518292930027916e8df5 100644 --- a/src/protocol/input_source.rs +++ b/src/protocol/input_source.rs @@ -21,6 +21,12 @@ pub struct InputSpan { } impl InputSpan { + // This will only be used for builtin functions + #[inline] + pub fn new() -> InputSpan { + InputSpan{ begin: InputPosition{ line: 0, offset: 0 }, end: InputPosition{ line: 0, offset: 0 }} + } + #[inline] pub fn from_positions(begin: InputPosition, end: InputPosition) -> Self { Self { begin, end } diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs index 1ed2c1ced4360b98636228500547e97b607f1d3c..2747d065b40721b46e1792c71f730f561f766aaf 100644 --- a/src/protocol/mod.rs +++ b/src/protocol/mod.rs @@ -20,14 +20,14 @@ pub struct ProtocolDescription { source: InputSource, root: RootId, } -// #[derive(Debug, Clone)] +#[derive(Debug, Clone)] pub(crate) struct ComponentState { prompt: Prompt, } pub(crate) enum EvalContext<'a> { Nonsync(&'a mut NonsyncProtoContext<'a>), Sync(&'a mut SyncProtoContext<'a>), - // None, + None, } ////////////////////////////////////////////// @@ -122,9 +122,8 @@ impl ComponentState { loop { let result = self.prompt.step(&pd.heap, &mut context); match result { - // In component definitions, there are no return statements - Ok(_) => unreachable!(), - Err(cont) => match cont { + Err(_) => todo!("error handling"), + Ok(cont) => match cont { EvalContinuation::Stepping => continue, EvalContinuation::Inconsistent => return NonsyncBlocker::Inconsistent, EvalContinuation::Terminal => return NonsyncBlocker::ComponentExit, @@ -178,9 +177,8 @@ impl ComponentState { loop { let result = self.prompt.step(&pd.heap, &mut context); match result { - // Inside synchronous blocks, there are no return statements - Ok(_) => unreachable!(), - Err(cont) => match cont { + Err(_) => todo!("error handling"), + Ok(cont) => match cont { EvalContinuation::Stepping => continue, EvalContinuation::Inconsistent => return SyncBlocker::Inconsistent, // First need to exit synchronous block before definition may end @@ -252,7 +250,7 @@ impl EvalContext<'_> { // } fn new_component(&mut self, moved_ports: HashSet, init_state: ComponentState) -> () { match self { - // EvalContext::None => unreachable!(), + EvalContext::None => unreachable!(), EvalContext::Nonsync(context) => { context.new_component(moved_ports, init_state) } @@ -261,7 +259,7 @@ impl EvalContext<'_> { } fn new_channel(&mut self) -> [Value; 2] { match self { - // EvalContext::None => unreachable!(), + EvalContext::None => unreachable!(), EvalContext::Nonsync(context) => { let [from, to] = context.new_port_pair(); let from = Value::Output(from); @@ -273,7 +271,7 @@ impl EvalContext<'_> { } fn fires(&mut self, port: Value) -> Option { match self { - // EvalContext::None => unreachable!(), + EvalContext::None => unreachable!(), EvalContext::Nonsync(_) => unreachable!(), EvalContext::Sync(context) => match port { Value::Output(port) => context.is_firing(port).map(Value::Bool), @@ -282,17 +280,29 @@ impl EvalContext<'_> { }, } } - fn get(&mut self, port: Value) -> Option { + fn get(&mut self, port: Value, store: &mut Store) -> Option { match self { - // EvalContext::None => unreachable!(), + EvalContext::None => unreachable!(), EvalContext::Nonsync(_) => unreachable!(), EvalContext::Sync(context) => match port { Value::Output(port) => { debug_assert!(false, "Getting from an output port? Am I mad?"); - context.read_msg(port).map(Value::receive_message) + unreachable!(); } Value::Input(port) => { - context.read_msg(port).map(Value::receive_message) + 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 payload = payload.unwrap(); + store.heap_regions[heap_pos_usize].values.reserve(payload.0.len()); + for value in payload.0.iter() { + store.heap_regions[heap_pos_usize].values.push(Value::UInt8(*value)); + } + + return Some(Value::Message(heap_pos)); } _ => unreachable!(), }, @@ -300,6 +310,7 @@ impl EvalContext<'_> { } fn did_put(&mut self, port: Value) -> bool { match self { + EvalContext::None => unreachable!("did_put in None context"), EvalContext::Nonsync(_) => unreachable!("did_put in nonsync context"), EvalContext::Sync(context) => match port { Value::Output(port) => { diff --git a/src/protocol/parser/depth_visitor.rs b/src/protocol/parser/depth_visitor.rs index 870aeb48e4df9a32cb5c417e156053b5260d0a24..acc0fb04531d6f6aeab674504c6b229660785df1 100644 --- a/src/protocol/parser/depth_visitor.rs +++ b/src/protocol/parser/depth_visitor.rs @@ -540,8 +540,7 @@ impl Visitor for LinkStatements { fn visit_if_statement(&mut self, h: &mut Heap, stmt: IfStatementId) -> VisitorResult { // Link the two branches to the corresponding EndIf pseudo-statement let end_if_id = h[stmt].end_if; - assert!(end_if_id.is_some()); - let end_if_id = end_if_id.unwrap(); + assert!(!end_if_id.is_invalid()); assert!(self.prev.is_none()); self.visit_block_statement(h, h[stmt].true_body)?; @@ -570,8 +569,7 @@ impl Visitor for LinkStatements { // We allocate a pseudo-statement, to which the break statement finds its target // Update the while's next statement to point to the pseudo-statement let end_while_id = h[stmt].end_while; - assert!(end_while_id.is_some()); - // let end_while_id = end_while_id.unwrap(); + assert!(!end_while_id.is_invalid()); assert!(self.prev.is_none()); self.visit_block_statement(h, h[stmt].body)?; @@ -608,8 +606,7 @@ impl Visitor for LinkStatements { // that marks the end of the synchronous block. Every evaluation has to pause at this // point, only to resume later when the thread is selected as unique thread to continue. let end_sync_id = h[stmt].end_sync; - assert!(end_sync_id.is_some()); - let end_sync_id = end_sync_id.unwrap(); + assert!(!end_sync_id.is_invalid()); assert!(self.prev.is_none()); self.visit_block_statement(h, h[stmt].body)?; diff --git a/src/protocol/parser/mod.rs b/src/protocol/parser/mod.rs index 1043e598de85fe3febf0e85280c49ae68f0ca389..181ec59b4d4b96beaf2f551da2d390fd65ae38fc 100644 --- a/src/protocol/parser/mod.rs +++ b/src/protocol/parser/mod.rs @@ -14,7 +14,6 @@ mod visitor; use depth_visitor::*; use tokens::*; use crate::collections::*; -use symbol_table::SymbolTable; use visitor::Visitor2; use pass_tokenizer::PassTokenizer; use pass_symbols::PassSymbols; @@ -22,12 +21,14 @@ use pass_imports::PassImport; use pass_definitions::PassDefinitions; use pass_validation_linking::PassValidationLinking; use pass_typing::{PassTyping, ResolveQueue}; +use symbol_table::*; use type_table::TypeTable; use crate::protocol::ast::*; use crate::protocol::input_source::*; use crate::protocol::ast_printer::ASTWriter; +use crate::protocol::parser::type_table::PolymorphicVariable; #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum ModuleCompilationPhase { @@ -75,7 +76,7 @@ pub struct Parser { impl Parser { pub fn new() -> Self { - Parser{ + let mut parser = Parser{ heap: Heap::new(), string_pool: StringPool::new(), modules: Vec::new(), @@ -87,7 +88,58 @@ impl Parser { pass_definitions: PassDefinitions::new(), pass_validation: PassValidationLinking::new(), pass_typing: PassTyping::new(), + }; + + parser.symbol_table.insert_scope(None, SymbolScope::Global); + + fn quick_type(variants: &[ParserTypeVariant]) -> ParserType { + let mut t = ParserType{ elements: Vec::with_capacity(variants.len()) }; + for variant in variants { + t.elements.push(ParserTypeElement{ full_span: InputSpan::new(), variant: variant.clone() }); + } + t } + + use ParserTypeVariant as PTV; + insert_builtin_function(&mut parser, "get", &["T"], |id| ( + vec![ + ("input", quick_type(&[PTV::Input, PTV::PolymorphicArgument(id.upcast(), 0)])) + ], + quick_type(&[PTV::PolymorphicArgument(id.upcast(), 0)]) + )); + insert_builtin_function(&mut parser, "put", &["T"], |id| ( + vec![ + ("output", quick_type(&[PTV::Output, PTV::PolymorphicArgument(id.upcast(), 0)])), + ("value", quick_type(&[PTV::PolymorphicArgument(id.upcast(), 0)])), + ], + quick_type(&[PTV::Void]) + )); + insert_builtin_function(&mut parser, "fires", &["T"], |id| ( + vec![ + ("port", quick_type(&[PTV::InputOrOutput, PTV::PolymorphicArgument(id.upcast(), 0)])) + ], + quick_type(&[PTV::Bool]) + )); + insert_builtin_function(&mut parser, "create", &["T"], |id| ( + vec![ + ("length", quick_type(&[PTV::IntegerLike])) + ], + quick_type(&[PTV::ArrayLike, PTV::PolymorphicArgument(id.upcast(), 0)]) + )); + insert_builtin_function(&mut parser, "length", &["T"], |id| ( + vec![ + ("array", quick_type(&[PTV::ArrayLike, PTV::PolymorphicArgument(id.upcast(), 0)])) + ], + quick_type(&[PTV::IntegerLike]) + )); + insert_builtin_function(&mut parser, "assert", &[], |id| ( + vec![ + ("condition", quick_type(&[PTV::Bool])), + ], + quick_type(&[PTV::Void]) + )); + + parser } pub fn feed(&mut self, mut source: InputSource) -> Result<(), ParseError> { @@ -176,9 +228,9 @@ impl Parser { } } - // let mut writer = ASTWriter::new(); - // let mut file = std::fs::File::create(std::path::Path::new("ast.txt")).unwrap(); - // writer.write_ast(&mut file, &self.heap); + let mut writer = ASTWriter::new(); + let mut file = std::fs::File::create(std::path::Path::new("ast.txt")).unwrap(); + writer.write_ast(&mut file, &self.heap); Ok(()) } @@ -203,4 +255,60 @@ impl Parser { Ok(()) } +} + +// Note: args and return type need to be a function because we need to know the function ID. +fn insert_builtin_function (Vec<(&'static str, ParserType)>, ParserType)> ( + p: &mut Parser, func_name: &str, polymorphic: &[&str], arg_and_return_fn: T) { + + let mut poly_vars = Vec::with_capacity(polymorphic.len()); + for poly_var in polymorphic { + poly_vars.push(Identifier{ span: InputSpan::new(), value: p.string_pool.intern(poly_var.as_bytes()) }); + } + + let func_ident_ref = p.string_pool.intern(func_name.as_bytes()); + let func_id = p.heap.alloc_function_definition(|this| FunctionDefinition{ + this, + defined_in: RootId::new_invalid(), + builtin: true, + span: InputSpan::new(), + identifier: Identifier{ span: InputSpan::new(), value: func_ident_ref.clone() }, + poly_vars, + return_types: Vec::new(), + parameters: Vec::new(), + body: BlockStatementId::new_invalid(), + }); + + let (args, ret) = arg_and_return_fn(func_id); + + let mut parameters = Vec::with_capacity(args.len()); + for (arg_name, arg_type) in args { + let identifier = Identifier{ span: InputSpan::new(), value: p.string_pool.intern(arg_name.as_bytes()) }; + let param_id = p.heap.alloc_variable(|this| Variable{ + this, + kind: VariableKind::Parameter, + parser_type: arg_type.clone(), + identifier, + relative_pos_in_block: 0, + unique_id_in_scope: 0 + }); + parameters.push(param_id); + } + + let func = &mut p.heap[func_id]; + func.parameters = parameters; + func.return_types.push(ret); + + p.symbol_table.insert_symbol(SymbolScope::Global, Symbol{ + name: func_ident_ref, + variant: SymbolVariant::Definition(SymbolDefinition{ + defined_in_module: RootId::new_invalid(), + defined_in_scope: SymbolScope::Global, + definition_span: InputSpan::new(), + identifier_span: InputSpan::new(), + imported_at: None, + class: DefinitionClass::Function, + definition_id: func_id.upcast(), + }) + }).unwrap(); } \ No newline at end of file diff --git a/src/protocol/parser/pass_definitions.rs b/src/protocol/parser/pass_definitions.rs index 4fb43dc88f693d922d1c4226ab3c4435a0282b2b..4c2a8f46d2e9d4ca369bfa5b84f6bd5066d71073 100644 --- a/src/protocol/parser/pass_definitions.rs +++ b/src/protocol/parser/pass_definitions.rs @@ -453,7 +453,10 @@ impl PassDefinitions { section.push(id.upcast()); } } - }; + } else { + let id = self.consume_expression_statement(module, iter, ctx)?; + section.push(id.upcast()); + } return Ok(()); } diff --git a/src/protocol/parser/pass_symbols.rs b/src/protocol/parser/pass_symbols.rs index 76a9dfcc71347a02f5a5d161cb183154c8c2468b..f24b8dade79eafdaac69877d885643a652015269 100644 --- a/src/protocol/parser/pass_symbols.rs +++ b/src/protocol/parser/pass_symbols.rs @@ -89,7 +89,7 @@ impl PassSymbols { // Add the module's symbol scope and the symbols we just parsed let module_scope = SymbolScope::Module(root_id); - ctx.symbols.insert_scope(None, module_scope); + ctx.symbols.insert_scope(Some(SymbolScope::Global), module_scope); for symbol in self.symbols.drain(..) { ctx.symbols.insert_scope(Some(module_scope), SymbolScope::Definition(symbol.variant.as_definition().definition_id)); if let Err((new_symbol, old_symbol)) = ctx.symbols.insert_symbol(module_scope, symbol) { diff --git a/src/protocol/parser/pass_typing.rs b/src/protocol/parser/pass_typing.rs index 3d36bb225909edee013eedec754192c53d04f00e..09d5717e678e4a079590eb3adb24b3dfe272c1de 100644 --- a/src/protocol/parser/pass_typing.rs +++ b/src/protocol/parser/pass_typing.rs @@ -992,7 +992,7 @@ impl Visitor2 for PassTyping { let param = &ctx.heap[param_id]; let var_type = self.determine_inference_type_from_parser_type_elements(¶m.parser_type.elements, true); debug_assert!(var_type.is_done, "expected component arguments to be concrete types"); - self.var_types.insert(param_id.upcast(), VarData::new_local(var_type)); + self.var_types.insert(param_id, VarData::new_local(var_type)); } let body_stmt_id = ctx.heap[id].body; @@ -1013,7 +1013,7 @@ impl Visitor2 for PassTyping { let param = &ctx.heap[param_id]; let var_type = self.determine_inference_type_from_parser_type_elements(¶m.parser_type.elements, true); debug_assert!(var_type.is_done, "expected function arguments to be concrete types"); - self.var_types.insert(param_id.upcast(), VarData::new_local(var_type)); + self.var_types.insert(param_id, VarData::new_local(var_type)); } let body_stmt_id = ctx.heap[id].body; @@ -1038,7 +1038,7 @@ impl Visitor2 for PassTyping { let local = &ctx.heap[memory_stmt.variable]; let var_type = self.determine_inference_type_from_parser_type_elements(&local.parser_type.elements, true); - self.var_types.insert(memory_stmt.variable.upcast(), VarData::new_local(var_type)); + self.var_types.insert(memory_stmt.variable, VarData::new_local(var_type)); Ok(()) } @@ -1048,11 +1048,11 @@ impl Visitor2 for PassTyping { let from_local = &ctx.heap[channel_stmt.from]; let from_var_type = self.determine_inference_type_from_parser_type_elements(&from_local.parser_type.elements, true); - self.var_types.insert(from_local.this.upcast(), VarData::new_channel(from_var_type, channel_stmt.to.upcast())); + self.var_types.insert(from_local.this, VarData::new_channel(from_var_type, channel_stmt.to)); let to_local = &ctx.heap[channel_stmt.to]; let to_var_type = self.determine_inference_type_from_parser_type_elements(&to_local.parser_type.elements, true); - self.var_types.insert(to_local.this.upcast(), VarData::new_channel(to_var_type, channel_stmt.from.upcast())); + self.var_types.insert(to_local.this, VarData::new_channel(to_var_type, channel_stmt.from)); Ok(()) } @@ -2373,7 +2373,7 @@ impl PassTyping { if infer_res == DualInferenceResult::Incompatible { let var_decl = &ctx.heap[var_id]; return Err(ParseError::new_error_at_span( - &ctx.module.source, var_decl.identifier().span, format!( + &ctx.module.source, var_decl.identifier.span, format!( "Conflicting types for this variable, previously assigned the type '{}'", var_data.var_type.display_name(&ctx.heap) ) @@ -2425,12 +2425,12 @@ impl PassTyping { let link_decl = &ctx.heap[linked_id]; return Err(ParseError::new_error_at_span( - &ctx.module.source, var_decl.identifier().span, format!( + &ctx.module.source, var_decl.identifier.span, format!( "Conflicting types for this variable, assigned the type '{}'", var_data.var_type.display_name(&ctx.heap) ) ).with_info_at_span( - &ctx.module.source, link_decl.identifier().span, format!( + &ctx.module.source, link_decl.identifier.span, format!( "Because it is incompatible with this variable, assigned the type '{}'", link_data.var_type.display_name(&ctx.heap) ) @@ -3099,6 +3099,12 @@ impl PassTyping { for element in elements { match &element.variant { + // Compiler-only types + PTV::Void => { infer_type.push(ITP::Void); }, + PTV::InputOrOutput => { infer_type.push(ITP::PortLike); }, + PTV::ArrayLike => { infer_type.push(ITP::ArrayLike); }, + PTV::IntegerLike => { infer_type.push(ITP::IntegerLike); }, + // Builtins PTV::Message => { // TODO: @types Remove the Message -> Byte hack at some point... infer_type.push(ITP::Message); @@ -3115,11 +3121,13 @@ impl PassTyping { PTV::SInt64 => { infer_type.push(ITP::SInt64); }, PTV::Character => { infer_type.push(ITP::Character); }, PTV::String => { infer_type.push(ITP::String); }, + // Special markers PTV::IntegerLiteral => { unreachable!("integer literal type on variable type"); }, PTV::Inferred => { infer_type.push(ITP::Unknown); has_inferred = true; }, + // With nested types PTV::Array => { infer_type.push(ITP::Array); }, PTV::Input => { infer_type.push(ITP::Input); }, PTV::Output => { infer_type.push(ITP::Output); }, diff --git a/src/protocol/parser/pass_validation_linking.rs b/src/protocol/parser/pass_validation_linking.rs index 0e5cdfbafe161912a1ae689afa86f1f040e34f92..590c7b31db58ce0ee791873a2a82697be6641b4d 100644 --- a/src/protocol/parser/pass_validation_linking.rs +++ b/src/protocol/parser/pass_validation_linking.rs @@ -239,8 +239,9 @@ impl Visitor2 for PassValidationLinking { let stmt = &ctx.heap[id]; let target_while_id = self.resolve_break_or_continue_target(ctx, stmt.span, &stmt.label)?; let target_while = &ctx.heap[target_while_id]; - debug_assert!(target_while.end_while.is_some()); - target_while.end_while.unwrap() + debug_assert!(!target_while.end_while.is_invalid()); + + target_while.end_while }; let stmt = &mut ctx.heap[id]; @@ -980,8 +981,7 @@ impl PassValidationLinking { } // Current scope is fine, move to parent scope if any - debug_assert!(block.parent_scope.is_some(), "block scope does not have a parent"); - scope = block.parent_scope.as_ref().unwrap(); + scope = &block.parent_scope; if let Scope::Definition(definition_id) = scope { // At outer scope, check parameters of function/component for parameter_id in ctx.heap[*definition_id].parameters() { @@ -1034,12 +1034,11 @@ impl PassValidationLinking { let local = &ctx.heap[*local_id]; if local.relative_pos_in_block < relative_pos && identifier == &local.identifier { - return Ok(local_id.upcast()); + return Ok(*local_id); } } - debug_assert!(block.parent_scope.is_some()); - scope = block.parent_scope.as_ref().unwrap(); + scope = &block.parent_scope; if !scope.is_block() { // Definition scope, need to check arguments to definition match scope { @@ -1048,7 +1047,7 @@ impl PassValidationLinking { for parameter_id in definition.parameters() { let parameter = &ctx.heap[*parameter_id]; if identifier == ¶meter.identifier { - return Ok(parameter_id.upcast()); + return Ok(*parameter_id); } } }, @@ -1094,15 +1093,14 @@ impl PassValidationLinking { } } - debug_assert!(block.parent_scope.is_some(), "block scope does not have a parent"); - scope = block.parent_scope.as_ref().unwrap(); + scope = &block.parent_scope; if !scope.is_block() { break; } } // No collisions - let block = &mut ctx.heap[self.cur_scope.as_ref().unwrap().to_block()]; + let block = &mut ctx.heap[self.cur_scope.to_block()]; block.labels.push(id); Ok(()) @@ -1141,8 +1139,7 @@ impl PassValidationLinking { } } - debug_assert!(block.parent_scope.is_some(), "block scope does not have a parent"); - scope = block.parent_scope.as_ref().unwrap(); + scope = &block.parent_scope; if !scope.is_block() { return Err(ParseError::new_error_str_at_span( &ctx.module.source, identifier.span, "could not find this label" @@ -1155,7 +1152,6 @@ impl PassValidationLinking { /// This function will check if the provided while statement ID has a block /// statement that is one of our current parents. fn has_parent_while_scope(&self, ctx: &Ctx, id: WhileStatementId) -> bool { - debug_assert!(self.cur_scope.is_some()); let mut scope = &self.cur_scope; let while_stmt = &ctx.heap[id]; loop { @@ -1166,7 +1162,6 @@ impl PassValidationLinking { } let block = &ctx.heap[block]; - debug_assert!(block.parent_scope.is_some(), "block scope does not have a parent"); scope = &block.parent_scope; if !scope.is_block() { return false; diff --git a/src/protocol/parser/tokens.rs b/src/protocol/parser/tokens.rs index be1b418a77912e021844104de739f1e40ac3be5e..7b273223f931c88ea68e0e327cd9d3b2e049f5b8 100644 --- a/src/protocol/parser/tokens.rs +++ b/src/protocol/parser/tokens.rs @@ -320,7 +320,7 @@ impl<'a> TokenIter<'a> { /// Advances the iterator to the next (meaningful) token. pub(crate) fn consume(&mut self) { - if let Some(kind) = self.next() { + if let Some(kind) = self.next_including_comments() { if kind.has_span_end() { self.cur += 2; } else { diff --git a/src/protocol/parser/type_table.rs b/src/protocol/parser/type_table.rs index 9fc2f5e87b61020ef6da904e2b4c7f8a7b9aaa4d..cdcee8542c0118e8bac44be0ab5a4851d51db122 100644 --- a/src/protocol/parser/type_table.rs +++ b/src/protocol/parser/type_table.rs @@ -275,7 +275,7 @@ impl TypeTable { } } - debug_assert_eq!(self.lookup.len(), reserve_size, "mismatch in reserved size of type table"); + debug_assert_eq!(self.lookup.len() + 6, reserve_size, "mismatch in reserved size of type table"); // NOTE: Temp fix for builtin functions for module in modules { module.phase = ModuleCompilationPhase::TypesAddedToTable; } @@ -727,6 +727,9 @@ impl TypeTable { for element in parser_type.elements.iter() { match element.variant { + PTV::Void | PTV::InputOrOutput | PTV::ArrayLike | PTV::IntegerLike => { + unreachable!("compiler-only ParserTypeVariant within type definition"); + }, PTV::Message | PTV::Bool | PTV::UInt8 | PTV::UInt16 | PTV::UInt32 | PTV::UInt64 | PTV::SInt8 | PTV::SInt16 | PTV::SInt32 | PTV::SInt64 | diff --git a/src/protocol/parser/visitor.rs b/src/protocol/parser/visitor.rs index b046c0662a0ed5872cb21777968605c4a02ac42c..f6cfec81afb14332309fb4d0cb46e686a8c806ea 100644 --- a/src/protocol/parser/visitor.rs +++ b/src/protocol/parser/visitor.rs @@ -86,6 +86,7 @@ pub(crate) trait Visitor2 { let this = stmt.this; self.visit_block_stmt(ctx, this) }, + Statement::EndBlock(_stmt) => Ok(()), Statement::Local(stmt) => { let this = stmt.this(); self.visit_local_stmt(ctx, this) diff --git a/src/protocol/tests/eval_operators.rs b/src/protocol/tests/eval_operators.rs new file mode 100644 index 0000000000000000000000000000000000000000..c0607d3a1523913f9529582e9ec576e37ce03413 --- /dev/null +++ b/src/protocol/tests/eval_operators.rs @@ -0,0 +1,70 @@ +use super::*; + +#[test] +fn test_assignment() { + fn construct_source(value_type: &str, value_initial: &str, value_op: &str) -> String { + return format!( + "func foo() -> {} {{ + {} value = {}; + value {}; + return value; + }}", + value_type, value_type, value_initial, value_op + ); + } + Tester::new_single_source_expect_ok( + "set", construct_source("u32", "1", "= 5") + ).for_function("foo", |f| { f.call(); }); + + Tester::new_single_source_expect_ok( + "multiplied", construct_source("u32", "2", "*= 4") + ).for_function("foo", |f| { f.call(); }); + + Tester::new_single_source_expect_ok( + "divided", construct_source("u32", "8", "/= 4") + ).for_function("foo", |f| { f.call(); }); + + Tester::new_single_source_expect_ok( + "remained", construct_source("u32", "8", "%= 3") + ).for_function("foo", |f| { f.call(); }); + + Tester::new_single_source_expect_ok( + "added", construct_source("u32", "2", "+= 4") + ).for_function("foo", |f| { f.call(); }); + + Tester::new_single_source_expect_ok( + "subtracted", construct_source("u32", "6", "-= 4") + ).for_function("foo", |f| { f.call(); }); + + Tester::new_single_source_expect_ok( + "shifted left", construct_source("u32", "2", "<<= 2") + ).for_function("foo", |f| { f.call(); }); + + Tester::new_single_source_expect_ok( + "shifted right", construct_source("u32", "8", ">>= 2") + ).for_function("foo", |f| { f.call(); }); + + Tester::new_single_source_expect_ok( + "bitwise and", construct_source("u32", "3", "&= 2") + ).for_function("foo", |f| { f.call(); }); + + Tester::new_single_source_expect_ok( + "bitwise xor", construct_source("u32", "3", "^= 7") + ).for_function("foo", |f| { f.call(); }); + + Tester::new_single_source_expect_ok( + "bitwise or", construct_source("u32", "12", "|= 3") + ).for_function("foo", |f| { f.call(); }); +} + +#[test] +fn test_function_call() { + Tester::new_single_source_expect_ok("calling", " + func add_two(u32 value) -> u32 { + return value + 2; + } + func foo() -> u32 { + return add_two(5); + } + ").for_function("foo", |f| { f.call(); }); +} \ No newline at end of file diff --git a/src/protocol/tests/lexer.rs b/src/protocol/tests/lexer.rs index dcc200336d1929477428880c13c0e9e1b63538e3..2c7f4ef5d81bdcba5631a065803ca7cd1d10ca7c 100644 --- a/src/protocol/tests/lexer.rs +++ b/src/protocol/tests/lexer.rs @@ -9,7 +9,7 @@ use super::*; fn test_disallowed_inference() { Tester::new_single_source_expect_err( "argument auto inference", - "s32 func(auto arg) { return 0; }" + "func thing(auto arg) -> s32 { return 0; }" ).error(|e| { e .assert_msg_has(0, "inference is not allowed") .assert_occurs_at(0, "auto arg"); @@ -17,15 +17,15 @@ fn test_disallowed_inference() { Tester::new_single_source_expect_err( "return type auto inference", - "auto func(s32 arg) { return 0; }" + "func thing(s32 arg) -> auto { return 0; }" ).error(|e| { e .assert_msg_has(0, "inference is not allowed") - .assert_occurs_at(0, "auto func"); + .assert_occurs_at(0, "auto {"); }); Tester::new_single_source_expect_err( "implicit polymorph argument auto inference", - "s32 func(in port) { return port; }" + "func thing(in port) -> s32 { return port; }" ).error(|e| { e .assert_msg_has(0, "inference is not allowed") .assert_occurs_at(0, "in port"); @@ -33,7 +33,7 @@ fn test_disallowed_inference() { Tester::new_single_source_expect_err( "explicit polymorph argument auto inference", - "s32 func(in port) { return port; }" + "func thing(in port) -> s32 { return port; }" ).error(|e| { e .assert_msg_has(0, "inference is not allowed") .assert_occurs_at(0, "auto> port"); @@ -41,18 +41,18 @@ fn test_disallowed_inference() { Tester::new_single_source_expect_err( "implicit polymorph return type auto inference", - "in func(in a, in b) { return a; }" + "func thing(in a, in b) -> in { return a; }" ).error(|e| { e .assert_msg_has(0, "inference is not allowed") - .assert_occurs_at(0, "in func"); + .assert_occurs_at(0, "in {"); }); Tester::new_single_source_expect_err( "explicit polymorph return type auto inference", - "in func(in a) { return a; }" + "func thing(in a) -> in { return a; }" ).error(|e| { e .assert_msg_has(0, "inference is not allowed") - .assert_occurs_at(0, "auto> func"); + .assert_occurs_at(0, "auto> {"); }); } diff --git a/src/protocol/tests/mod.rs b/src/protocol/tests/mod.rs index fe6791df28f790a5c8859202d916a18cd7874d18..ac50bdb9a135321bc7ad893e9071c99b7051fb09 100644 --- a/src/protocol/tests/mod.rs +++ b/src/protocol/tests/mod.rs @@ -4,5 +4,6 @@ mod parser_validation; mod parser_inference; mod parser_monomorphs; mod parser_imports; +mod eval_operators; pub(crate) use utils::{Tester}; \ No newline at end of file diff --git a/src/protocol/tests/utils.rs b/src/protocol/tests/utils.rs index 7b08b933e661ab2493b5e9dcd350a6044fb53c7b..d81dd664fcd85b39b51c893af83aa53649e2300c 100644 --- a/src/protocol/tests/utils.rs +++ b/src/protocol/tests/utils.rs @@ -8,6 +8,7 @@ use crate::protocol::{ symbol_table::SymbolTable, token_parsing::*, }, + eval::*, }; // Carries information about the test into utility structures for builder-like @@ -572,6 +573,24 @@ impl<'a> FunctionTester<'a> { self } + pub(crate) fn call(self, expected_result: Option) -> Self { + use crate::protocol::*; + use crate::runtime::*; + + let mut prompt = Prompt::new(&self.ctx.heap, self.def.this.upcast(), ValueGroup::new_stack(Vec::new())); + let mut call_context = EvalContext::None; + loop { + let result = prompt.step(&self.ctx.heap, &mut call_context).unwrap(); + match result { + EvalContinuation::Stepping => {}, + _ => break, + } + } + + + self + } + fn assert_postfix(&self) -> String { format!("Function{{ name: {} }}", self.def.identifier.value.as_str()) } @@ -580,20 +599,20 @@ impl<'a> FunctionTester<'a> { pub(crate) struct VariableTester<'a> { ctx: TestCtx<'a>, definition_id: DefinitionId, - local: &'a Local, + variable: &'a Variable, assignment: &'a AssignmentExpression, } impl<'a> VariableTester<'a> { fn new( - ctx: TestCtx<'a>, definition_id: DefinitionId, local: &'a Local, assignment: &'a AssignmentExpression + ctx: TestCtx<'a>, definition_id: DefinitionId, variable: &'a Variable, assignment: &'a AssignmentExpression ) -> Self { - Self{ ctx, definition_id, local, assignment } + Self{ ctx, definition_id, variable, assignment } } pub(crate) fn assert_parser_type(self, expected: &str) -> Self { let mut serialized = String::new(); - serialize_parser_type(&mut serialized, self.ctx.heap, &self.local.parser_type); + serialize_parser_type(&mut serialized, self.ctx.heap, &self.variable.parser_type); assert_eq!( expected, &serialized, @@ -619,7 +638,7 @@ impl<'a> VariableTester<'a> { } fn assert_postfix(&self) -> String { - format!("Variable{{ name: {} }}", self.local.identifier.value.as_str()) + format!("Variable{{ name: {} }}", self.variable.identifier.value.as_str()) } } @@ -826,32 +845,43 @@ fn serialize_parser_type(buffer: &mut String, heap: &Heap, parser_type: &ParserT fn serialize_variant(buffer: &mut String, heap: &Heap, parser_type: &ParserType, mut idx: usize) -> usize { match &parser_type.elements[idx].variant { - PTV::Message => write_bytes(buffer, KW_TYPE_MESSAGE), - PTV::Bool => write_bytes(buffer, KW_TYPE_BOOL), - PTV::UInt8 => write_bytes(buffer, KW_TYPE_UINT8), - PTV::UInt16 => write_bytes(buffer, KW_TYPE_UINT16), - PTV::UInt32 => write_bytes(buffer, KW_TYPE_UINT32), - PTV::UInt64 => write_bytes(buffer, KW_TYPE_UINT64), - PTV::SInt8 => write_bytes(buffer, KW_TYPE_SINT8), - PTV::SInt16 => write_bytes(buffer, KW_TYPE_SINT16), - PTV::SInt32 => write_bytes(buffer, KW_TYPE_SINT32), - PTV::SInt64 => write_bytes(buffer, KW_TYPE_SINT64), - PTV::Character => write_bytes(buffer, KW_TYPE_CHAR), - PTV::String => write_bytes(buffer, KW_TYPE_STRING), + PTV::Void => buffer.push_str("void"), + PTV::InputOrOutput => { + buffer.push_str("portlike<"); + idx = serialize_variant(buffer, heap, parser_type, idx + 1); + buffer.push('>'); + }, + PTV::ArrayLike => { + idx = serialize_variant(buffer, heap, parser_type, idx + 1); + buffer.push_str("[???]"); + }, + PTV::IntegerLike => buffer.push_str("integerlike"), + PTV::Message => buffer.push_str(KW_TYPE_MESSAGE_STR), + PTV::Bool => buffer.push_str(KW_TYPE_BOOL_STR), + PTV::UInt8 => buffer.push_str(KW_TYPE_UINT8_STR), + PTV::UInt16 => buffer.push_str(KW_TYPE_UINT16_STR), + PTV::UInt32 => buffer.push_str(KW_TYPE_UINT32_STR), + PTV::UInt64 => buffer.push_str(KW_TYPE_UINT64_STR), + PTV::SInt8 => buffer.push_str(KW_TYPE_SINT8_STR), + PTV::SInt16 => buffer.push_str(KW_TYPE_SINT16_STR), + PTV::SInt32 => buffer.push_str(KW_TYPE_SINT32_STR), + PTV::SInt64 => buffer.push_str(KW_TYPE_SINT64_STR), + PTV::Character => buffer.push_str(KW_TYPE_CHAR_STR), + PTV::String => buffer.push_str(KW_TYPE_STRING_STR), PTV::IntegerLiteral => buffer.push_str("int_literal"), - PTV::Inferred => write_bytes(buffer, KW_TYPE_INFERRED), + PTV::Inferred => buffer.push_str(KW_TYPE_INFERRED_STR), PTV::Array => { idx = serialize_variant(buffer, heap, parser_type, idx + 1); buffer.push_str("[]"); }, PTV::Input => { - write_bytes(buffer, KW_TYPE_IN_PORT); + buffer.push_str(KW_TYPE_IN_PORT_STR); buffer.push('<'); idx = serialize_variant(buffer, heap, parser_type, idx + 1); buffer.push('>'); }, PTV::Output => { - write_bytes(buffer, KW_TYPE_OUT_PORT); + buffer.push_str(KW_TYPE_OUT_PORT_STR); buffer.push('<'); idx = serialize_variant(buffer, heap, parser_type, idx + 1); buffer.push('>');