diff --git a/src/protocol/parser/mod.rs b/src/protocol/parser/mod.rs index 2a7a85099282418fbec1e42f3a71425cfc766919..73fdd6dfcd496c0ec23042d8c6bb0c6272a57d99 100644 --- a/src/protocol/parser/mod.rs +++ b/src/protocol/parser/mod.rs @@ -1,3 +1,4 @@ +#[macro_use] mod visitor; pub(crate) mod symbol_table; pub(crate) mod type_table; pub(crate) mod tokens; @@ -11,7 +12,6 @@ pub(crate) mod pass_validation_linking; pub(crate) mod pass_rewriting; pub(crate) mod pass_typing; pub(crate) mod pass_stack_size; -mod visitor; use tokens::*; use crate::collections::*; diff --git a/src/protocol/parser/pass_typing.rs b/src/protocol/parser/pass_typing.rs index c4e82df63dfaff2f8ec920b893f1c9c5a3a98c45..17686469649a7f39d1834d2ebef80e4824512e2e 100644 --- a/src/protocol/parser/pass_typing.rs +++ b/src/protocol/parser/pass_typing.rs @@ -26,13 +26,8 @@ /// instead of with HashMaps is there, but it is not really used because of /// time constraints. When time is available, rewrite the system such that /// AST IDs are not needed, and only indices into arrays are used. -/// 2. We're doing a lot of extra work. It seems better to apply the initial -/// type based on expression parents, and immediately apply forced -/// constraints (arg to a fires() call must be port-like). All of the \ -/// progress_xxx calls should then only be concerned with "transmitting" -/// type inference across their parent/child expressions. -/// 3. Remove the `msg` type? -/// 4. Disallow certain types in certain operations (e.g. `Void`). +/// 2. Remove the `msg` type? +/// 3. Disallow certain types in certain operations (e.g. `Void`). macro_rules! debug_log_enabled { () => { false }; @@ -59,10 +54,12 @@ use super::visitor::{ BUFFER_INIT_CAP_LARGE, BUFFER_INIT_CAP_SMALL, Ctx, - Visitor, - VisitorResult }; +// ----------------------------------------------------------------------------- +// Inference type +// ----------------------------------------------------------------------------- + const VOID_TEMPLATE: [InferenceTypePart; 1] = [ InferenceTypePart::Void ]; const MESSAGE_TEMPLATE: [InferenceTypePart; 2] = [ InferenceTypePart::Message, InferenceTypePart::UInt8 ]; const BOOL_TEMPLATE: [InferenceTypePart; 1] = [ InferenceTypePart::Bool ]; @@ -807,6 +804,13 @@ enum SingleInferenceResult { Incompatible } +// ----------------------------------------------------------------------------- +// PassTyping - Public Interface +// ----------------------------------------------------------------------------- + +type InferIndex = usize; +type ExtraIndex = usize; + enum DefinitionType{ Component(ComponentDefinitionId), Function(FunctionDefinitionId), @@ -1008,9 +1012,24 @@ impl PassTyping { } } -impl Visitor for PassTyping { +// ----------------------------------------------------------------------------- +// PassTyping - Visitor-like implementation +// ----------------------------------------------------------------------------- + +type VisitorResult = Result<(), ParseError>; +type VisitStmtResult = Result<> + +impl PassTyping { // Definitions + fn visit_definition(&mut self, ctx: &mut Ctx, id: DefinitionId) -> VisitorResult { + return visitor_recursive_definition_impl!(self, &ctx.heap[id], ctx); + } + + fn visit_enum_definition(&mut self, _: &mut Ctx, _: EnumDefinitionId) -> VisitorResult { return Ok(()) } + fn visit_struct_definition(&mut self, _: &mut Ctx, _: StructDefinitionId) -> VisitorResult { return Ok(()) } + fn visit_union_definition(&mut self, _: &mut Ctx, _: UnionDefinitionId) -> VisitorResult { return Ok(()) } + fn visit_component_definition(&mut self, ctx: &mut Ctx, id: ComponentDefinitionId) -> VisitorResult { self.definition_type = DefinitionType::Component(id); @@ -1082,6 +1101,10 @@ impl Visitor for PassTyping { // Statements + fn visit_stmt(&mut self, ctx: &mut Ctx, id: StatementId) -> VisitorResult { + return visitor_recursive_statement_impl!(self, &ctx.heap[id], ctx, Ok(())); + } + fn visit_block_stmt(&mut self, ctx: &mut Ctx, id: BlockStatementId) -> VisitorResult { // Transfer statements for traversal let block = &ctx.heap[id]; @@ -1095,6 +1118,10 @@ impl Visitor for PassTyping { Ok(()) } + fn visit_local_stmt(&mut self, ctx: &mut Ctx, id: LocalStatementId) -> VisitorResult { + return visitor_recursive_local_impl!(self, &ctx.heap[id], ctx); + } + fn visit_local_memory_stmt(&mut self, ctx: &mut Ctx, id: MemoryStatementId) -> VisitorResult { let memory_stmt = &ctx.heap[id]; let initial_expr_id = memory_stmt.initial_expr; @@ -1158,6 +1185,9 @@ impl Visitor for PassTyping { Ok(()) } + fn visit_break_stmt(&mut self, _: &mut Ctx, _: BreakStatementId) -> VisitorResult { return Ok(()) } + fn visit_continue_stmt(&mut self, _: &mut Ctx, _: ContinueStatementId) -> VisitorResult { return Ok(()) } + fn visit_synchronous_stmt(&mut self, ctx: &mut Ctx, id: SynchronousStatementId) -> VisitorResult { let sync_stmt = &ctx.heap[id]; let body_id = sync_stmt.body; @@ -1210,6 +1240,8 @@ impl Visitor for PassTyping { self.visit_expr(ctx, expr_id) } + fn visit_goto_stmt(&mut self, _: &mut Ctx, _: GotoStatementId) -> VisitorResult { return Ok(()) } + fn visit_new_stmt(&mut self, ctx: &mut Ctx, id: NewStatementId) -> VisitorResult { let new_stmt = &ctx.heap[id]; let call_expr_id = new_stmt.expression; @@ -1226,6 +1258,10 @@ impl Visitor for PassTyping { // Expressions + fn visit_expr(&mut self, ctx: &mut Ctx, id: ExpressionId) -> VisitorResult { + return visitor_recursive_expression_impl!(self, &ctx.heap[id], ctx); + } + fn visit_assignment_expr(&mut self, ctx: &mut Ctx, id: AssignmentExpressionId) -> VisitorResult { let upcast_id = id.upcast(); self.insert_initial_expr_inference_type(ctx, upcast_id)?; @@ -1451,6 +1487,10 @@ impl Visitor for PassTyping { } } +// ----------------------------------------------------------------------------- +// PassTyping - Type-inference progression +// ----------------------------------------------------------------------------- + impl PassTyping { #[allow(dead_code)] // used when debug flag at the top of this file is true. fn debug_get_display_name(&self, ctx: &Ctx, expr_id: ExpressionId) -> String { diff --git a/src/protocol/parser/visitor.rs b/src/protocol/parser/visitor.rs index ad92f4dd13b1a946e00f40c5aabed836c76e5938..a94fb28b94113846eb558566b6bcc7b35f5c958b 100644 --- a/src/protocol/parser/visitor.rs +++ b/src/protocol/parser/visitor.rs @@ -32,216 +32,246 @@ impl<'p> Ctx<'p> { } } -/// Visitor is a generic trait that will fully walk the AST. The default -/// implementation of the visitors is to not recurse. The exception is the -/// top-level `visit_definition`, `visit_stmt` and `visit_expr` methods, which -/// call the appropriate visitor function. -pub(crate) trait Visitor { - // Entry point - fn visit_module(&mut self, ctx: &mut Ctx) -> VisitorResult { - let mut def_index = 0; - let module_root_id = ctx.modules[ctx.module_idx].root_id; - loop { - let definition_id = { - let root = &ctx.heap[module_root_id]; - if def_index >= root.definitions.len() { - return Ok(()) - } - - root.definitions[def_index] - }; - - self.visit_definition(ctx, definition_id)?; - def_index += 1; - } - } - - // Definitions - // --- enum matching - fn visit_definition(&mut self, ctx: &mut Ctx, id: DefinitionId) -> VisitorResult { - match &ctx.heap[id] { - Definition::Enum(def) => { - let def = def.this; - self.visit_enum_definition(ctx, def) - }, - Definition::Union(def) => { - let def = def.this; - self.visit_union_definition(ctx, def) - } - Definition::Struct(def) => { - let def = def.this; - self.visit_struct_definition(ctx, def) - }, - Definition::Component(def) => { - let def = def.this; - self.visit_component_definition(ctx, def) - }, - Definition::Function(def) => { - let def = def.this; - self.visit_function_definition(ctx, def) - } - } - } - - // --- enum variant handling - fn visit_enum_definition(&mut self, _ctx: &mut Ctx, _id: EnumDefinitionId) -> VisitorResult { Ok(()) } - fn visit_union_definition(&mut self, _ctx: &mut Ctx, _id: UnionDefinitionId) -> VisitorResult{ Ok(()) } - fn visit_struct_definition(&mut self, _ctx: &mut Ctx, _id: StructDefinitionId) -> VisitorResult { Ok(()) } - fn visit_component_definition(&mut self, _ctx: &mut Ctx, _id: ComponentDefinitionId) -> VisitorResult { Ok(()) } - fn visit_function_definition(&mut self, _ctx: &mut Ctx, _id: FunctionDefinitionId) -> VisitorResult { Ok(()) } - - // Statements - // --- enum matching - fn visit_stmt(&mut self, ctx: &mut Ctx, id: StatementId) -> VisitorResult { - match &ctx.heap[id] { +/// Implements the logic that checks the statement union retrieved from the +/// AST and calls the appropriate visit function. This entire macro assumes that +/// `$this` points to `self`, `$stmt` is the statement of type `Statement`, +/// `$ctx` is the context passed to all the visitor calls (of the form +/// `visit_x_stmt(context, id)`) and `$default_return` is the default return +/// value for the statements that will not be visited. +macro_rules! visitor_recursive_statement_impl { + ($this:expr, $stmt:expr, $ctx:expr, $default_return:expr) => { + match $stmt { Statement::Block(stmt) => { let this = stmt.this; - self.visit_block_stmt(ctx, this) + $this.visit_block_stmt($ctx, this) }, - Statement::EndBlock(_stmt) => Ok(()), + Statement::EndBlock(_stmt) => $default_return, Statement::Local(stmt) => { let this = stmt.this(); - self.visit_local_stmt(ctx, this) + $this.visit_local_stmt($ctx, this) }, Statement::Labeled(stmt) => { let this = stmt.this; - self.visit_labeled_stmt(ctx, this) + $this.visit_labeled_stmt($ctx, this) }, Statement::If(stmt) => { let this = stmt.this; - self.visit_if_stmt(ctx, this) + $this.visit_if_stmt($ctx, this) }, - Statement::EndIf(_stmt) => Ok(()), + Statement::EndIf(_stmt) => $default_return, Statement::While(stmt) => { let this = stmt.this; - self.visit_while_stmt(ctx, this) + $this.visit_while_stmt($ctx, this) }, - Statement::EndWhile(_stmt) => Ok(()), + Statement::EndWhile(_stmt) => $default_return, Statement::Break(stmt) => { let this = stmt.this; - self.visit_break_stmt(ctx, this) + $this.visit_break_stmt($ctx, this) }, Statement::Continue(stmt) => { let this = stmt.this; - self.visit_continue_stmt(ctx, this) + $this.visit_continue_stmt($ctx, this) }, Statement::Synchronous(stmt) => { let this = stmt.this; - self.visit_synchronous_stmt(ctx, this) + $this.visit_synchronous_stmt($ctx, this) }, - Statement::EndSynchronous(_stmt) => Ok(()), + Statement::EndSynchronous(_stmt) => $default_return, Statement::Fork(stmt) => { let this = stmt.this; - self.visit_fork_stmt(ctx, this) + $this.visit_fork_stmt($ctx, this) }, - Statement::EndFork(_stmt) => Ok(()), + Statement::EndFork(_stmt) => $default_return, Statement::Select(stmt) => { let this = stmt.this; - self.visit_select_stmt(ctx, this) + $this.visit_select_stmt($ctx, this) }, - Statement::EndSelect(_stmt) => Ok(()), + Statement::EndSelect(_stmt) => $default_return, Statement::Return(stmt) => { let this = stmt.this; - self.visit_return_stmt(ctx, this) + $this.visit_return_stmt($ctx, this) }, Statement::Goto(stmt) => { let this = stmt.this; - self.visit_goto_stmt(ctx, this) + $this.visit_goto_stmt($ctx, this) }, Statement::New(stmt) => { let this = stmt.this; - self.visit_new_stmt(ctx, this) + $this.visit_new_stmt($ctx, this) }, Statement::Expression(stmt) => { let this = stmt.this; - self.visit_expr_stmt(ctx, this) + $this.visit_expr_stmt($ctx, this) + } + } + }; +} + +macro_rules! visitor_recursive_local_impl { + ($this:expr, $local:expr, $ctx:expr) => { + match $local { + LocalStatement::Channel(local) => { + let this = local.this; + $this.visit_local_channel_stmt($ctx, this) + }, + LocalStatement::Memory(local) => { + let this = local.this; + $this.visit_local_memory_stmt($ctx, this) } } } +} - fn visit_local_stmt(&mut self, ctx: &mut Ctx, id: LocalStatementId) -> VisitorResult { - match &ctx.heap[id] { - LocalStatement::Channel(stmt) => { - let this = stmt.this; - self.visit_local_channel_stmt(ctx, this) +macro_rules! visitor_recursive_definition_impl { + ($this:expr, $definition:expr, $ctx:expr) => { + match $definition { + Definition::Enum(def) => { + let def = def.this; + $this.visit_enum_definition($ctx, def) }, - LocalStatement::Memory(stmt) => { - let this = stmt.this; - self.visit_local_memory_stmt(ctx, this) + Definition::Union(def) => { + let def = def.this; + $this.visit_union_definition($ctx, def) + }, + Definition::Struct(def) => { + let def = def.this; + $this.visit_struct_definition($ctx, def) + }, + Definition::Component(def) => { + let def = def.this; + $this.visit_component_definition($ctx, def) + }, + Definition::Function(def) => { + let def = def.this; + $this.visit_function_definition($ctx, def) }, } } +} - // --- enum variant handling - fn visit_block_stmt(&mut self, _ctx: &mut Ctx, _id: BlockStatementId) -> VisitorResult { Ok(()) } - fn visit_local_memory_stmt(&mut self, _ctx: &mut Ctx, _id: MemoryStatementId) -> VisitorResult { Ok(()) } - fn visit_local_channel_stmt(&mut self, _ctx: &mut Ctx, _id: ChannelStatementId) -> VisitorResult { Ok(()) } - fn visit_labeled_stmt(&mut self, _ctx: &mut Ctx, _id: LabeledStatementId) -> VisitorResult { Ok(()) } - fn visit_if_stmt(&mut self, _ctx: &mut Ctx, _id: IfStatementId) -> VisitorResult { Ok(()) } - fn visit_while_stmt(&mut self, _ctx: &mut Ctx, _id: WhileStatementId) -> VisitorResult { Ok(()) } - fn visit_break_stmt(&mut self, _ctx: &mut Ctx, _id: BreakStatementId) -> VisitorResult { Ok(()) } - fn visit_continue_stmt(&mut self, _ctx: &mut Ctx, _id: ContinueStatementId) -> VisitorResult { Ok(()) } - fn visit_synchronous_stmt(&mut self, _ctx: &mut Ctx, _id: SynchronousStatementId) -> VisitorResult { Ok(()) } - fn visit_fork_stmt(&mut self, _ctx: &mut Ctx, _id: ForkStatementId) -> VisitorResult { Ok(()) } - fn visit_select_stmt(&mut self, _ctx: &mut Ctx, _id: SelectStatementId) -> VisitorResult { Ok(()) } - fn visit_return_stmt(&mut self, _ctx: &mut Ctx, _id: ReturnStatementId) -> VisitorResult { Ok(()) } - fn visit_goto_stmt(&mut self, _ctx: &mut Ctx, _id: GotoStatementId) -> VisitorResult { Ok(()) } - fn visit_new_stmt(&mut self, _ctx: &mut Ctx, _id: NewStatementId) -> VisitorResult { Ok(()) } - fn visit_expr_stmt(&mut self, _ctx: &mut Ctx, _id: ExpressionStatementId) -> VisitorResult { Ok(()) } - - // Expressions - // --- enum matching - fn visit_expr(&mut self, ctx: &mut Ctx, id: ExpressionId) -> VisitorResult { - match &ctx.heap[id] { +macro_rules! visitor_recursive_expression_impl { + ($this:expr, $expression:expr, $ctx:expr) => { + match $expression { Expression::Assignment(expr) => { let this = expr.this; - self.visit_assignment_expr(ctx, this) + $this.visit_assignment_expr($ctx, this) }, Expression::Binding(expr) => { let this = expr.this; - self.visit_binding_expr(ctx, this) - } + $this.visit_binding_expr($ctx, this) + }, Expression::Conditional(expr) => { let this = expr.this; - self.visit_conditional_expr(ctx, this) - } + $this.visit_conditional_expr($ctx, this) + }, Expression::Binary(expr) => { let this = expr.this; - self.visit_binary_expr(ctx, this) - } + $this.visit_binary_expr($ctx, this) + }, Expression::Unary(expr) => { let this = expr.this; - self.visit_unary_expr(ctx, this) - } + $this.visit_unary_expr($ctx, this) + }, Expression::Indexing(expr) => { let this = expr.this; - self.visit_indexing_expr(ctx, this) - } + $this.visit_indexing_expr($ctx, this) + }, Expression::Slicing(expr) => { let this = expr.this; - self.visit_slicing_expr(ctx, this) - } + $this.visit_slicing_expr($ctx, this) + }, Expression::Select(expr) => { let this = expr.this; - self.visit_select_expr(ctx, this) - } + $this.visit_select_expr($ctx, this) + }, Expression::Literal(expr) => { let this = expr.this; - self.visit_literal_expr(ctx, this) - } + $this.visit_literal_expr($ctx, this) + }, Expression::Cast(expr) => { let this = expr.this; - self.visit_cast_expr(ctx, this) - } + $this.visit_cast_expr($ctx, this) + }, Expression::Call(expr) => { let this = expr.this; - self.visit_call_expr(ctx, this) - } + $this.visit_call_expr($ctx, this) + }, Expression::Variable(expr) => { let this = expr.this; - self.visit_variable_expr(ctx, this) - } + $this.visit_variable_expr($ctx, this) + }, } + }; +} + +/// Visitor is a generic trait that will fully walk the AST. The default +/// implementation of the visitors is to not recurse. The exception is the +/// top-level `visit_definition`, `visit_stmt` and `visit_expr` methods, which +/// call the appropriate visitor function. +pub(crate) trait Visitor { + // Entry point + fn visit_module(&mut self, ctx: &mut Ctx) -> VisitorResult { + let mut def_index = 0; + let module_root_id = ctx.modules[ctx.module_idx].root_id; + loop { + let definition_id = { + let root = &ctx.heap[module_root_id]; + if def_index >= root.definitions.len() { + return Ok(()) + } + + root.definitions[def_index] + }; + + self.visit_definition(ctx, definition_id)?; + def_index += 1; + } + } + + // Definitions + // --- enum matching + fn visit_definition(&mut self, ctx: &mut Ctx, id: DefinitionId) -> VisitorResult { + return visitor_recursive_definition_impl!(self, &ctx.heap[id], ctx); + } + + // --- enum variant handling + fn visit_enum_definition(&mut self, _ctx: &mut Ctx, _id: EnumDefinitionId) -> VisitorResult { Ok(()) } + fn visit_union_definition(&mut self, _ctx: &mut Ctx, _id: UnionDefinitionId) -> VisitorResult{ Ok(()) } + fn visit_struct_definition(&mut self, _ctx: &mut Ctx, _id: StructDefinitionId) -> VisitorResult { Ok(()) } + fn visit_component_definition(&mut self, _ctx: &mut Ctx, _id: ComponentDefinitionId) -> VisitorResult { Ok(()) } + fn visit_function_definition(&mut self, _ctx: &mut Ctx, _id: FunctionDefinitionId) -> VisitorResult { Ok(()) } + + // Statements + // --- enum matching + fn visit_stmt(&mut self, ctx: &mut Ctx, id: StatementId) -> VisitorResult { + return visitor_recursive_statement_impl!(self, &ctx.heap[id], ctx, Ok(())); + } + + fn visit_local_stmt(&mut self, ctx: &mut Ctx, id: LocalStatementId) -> VisitorResult { + return visitor_recursive_local_impl!(self, &ctx.heap[id], ctx); + } + + // --- enum variant handling + fn visit_block_stmt(&mut self, _ctx: &mut Ctx, _id: BlockStatementId) -> VisitorResult { Ok(()) } + fn visit_local_memory_stmt(&mut self, _ctx: &mut Ctx, _id: MemoryStatementId) -> VisitorResult { Ok(()) } + fn visit_local_channel_stmt(&mut self, _ctx: &mut Ctx, _id: ChannelStatementId) -> VisitorResult { Ok(()) } + fn visit_labeled_stmt(&mut self, _ctx: &mut Ctx, _id: LabeledStatementId) -> VisitorResult { Ok(()) } + fn visit_if_stmt(&mut self, _ctx: &mut Ctx, _id: IfStatementId) -> VisitorResult { Ok(()) } + fn visit_while_stmt(&mut self, _ctx: &mut Ctx, _id: WhileStatementId) -> VisitorResult { Ok(()) } + fn visit_break_stmt(&mut self, _ctx: &mut Ctx, _id: BreakStatementId) -> VisitorResult { Ok(()) } + fn visit_continue_stmt(&mut self, _ctx: &mut Ctx, _id: ContinueStatementId) -> VisitorResult { Ok(()) } + fn visit_synchronous_stmt(&mut self, _ctx: &mut Ctx, _id: SynchronousStatementId) -> VisitorResult { Ok(()) } + fn visit_fork_stmt(&mut self, _ctx: &mut Ctx, _id: ForkStatementId) -> VisitorResult { Ok(()) } + fn visit_select_stmt(&mut self, _ctx: &mut Ctx, _id: SelectStatementId) -> VisitorResult { Ok(()) } + fn visit_return_stmt(&mut self, _ctx: &mut Ctx, _id: ReturnStatementId) -> VisitorResult { Ok(()) } + fn visit_goto_stmt(&mut self, _ctx: &mut Ctx, _id: GotoStatementId) -> VisitorResult { Ok(()) } + fn visit_new_stmt(&mut self, _ctx: &mut Ctx, _id: NewStatementId) -> VisitorResult { Ok(()) } + fn visit_expr_stmt(&mut self, _ctx: &mut Ctx, _id: ExpressionStatementId) -> VisitorResult { Ok(()) } + + // Expressions + // --- enum matching + fn visit_expr(&mut self, ctx: &mut Ctx, id: ExpressionId) -> VisitorResult { + return visitor_recursive_expression_impl!(self, &ctx.heap[id], ctx); } fn visit_assignment_expr(&mut self, _ctx: &mut Ctx, _id: AssignmentExpressionId) -> VisitorResult { Ok(()) }