From 21deaee234f7b216b0cc3b7ea2a79e44514c443b 2021-11-12 19:38:16 From: MH Date: 2021-11-12 19:38:16 Subject: [PATCH] Add concept of fork statement to language --- diff --git a/src/protocol/ast.rs b/src/protocol/ast.rs index a633ce0c0c9ee19ebf048d71834b28bc23c58200..41106ef2a24594e50d98ab3af8a142076c69686a 100644 --- a/src/protocol/ast.rs +++ b/src/protocol/ast.rs @@ -138,6 +138,8 @@ define_new_ast_id!(BreakStatementId, StatementId, index(BreakStatement, Statemen define_new_ast_id!(ContinueStatementId, StatementId, index(ContinueStatement, Statement::Continue, statements), alloc(alloc_continue_statement)); define_new_ast_id!(SynchronousStatementId, StatementId, index(SynchronousStatement, Statement::Synchronous, statements), alloc(alloc_synchronous_statement)); define_new_ast_id!(EndSynchronousStatementId, StatementId, index(EndSynchronousStatement, Statement::EndSynchronous, statements), alloc(alloc_end_synchronous_statement)); +define_new_ast_id!(ForkStatementId, StatementId, index(ForkStatement, Statement::Fork, statements), alloc(alloc_fork_statement)); +define_new_ast_id!(EndForkStatementId, StatementId, index(EndForkStatement, Statement::EndFork, statements), alloc(alloc_end_fork_statement)); define_new_ast_id!(ReturnStatementId, StatementId, index(ReturnStatement, Statement::Return, statements), alloc(alloc_return_statement)); define_new_ast_id!(GotoStatementId, StatementId, index(GotoStatement, Statement::Goto, statements), alloc(alloc_goto_statement)); define_new_ast_id!(NewStatementId, StatementId, index(NewStatement, Statement::New, statements), alloc(alloc_new_statement)); @@ -1035,6 +1037,8 @@ pub enum Statement { Continue(ContinueStatement), Synchronous(SynchronousStatement), EndSynchronous(EndSynchronousStatement), + Fork(ForkStatement), + EndFork(EndForkStatement), Return(ReturnStatement), Goto(GotoStatement), New(NewStatement), @@ -1078,11 +1082,12 @@ impl Statement { Statement::Break(v) => v.span, Statement::Continue(v) => v.span, Statement::Synchronous(v) => v.span, + Statement::Fork(v) => v.span, Statement::Return(v) => v.span, Statement::Goto(v) => v.span, Statement::New(v) => v.span, Statement::Expression(v) => v.span, - Statement::EndBlock(_) | Statement::EndIf(_) | Statement::EndWhile(_) | Statement::EndSynchronous(_) => unreachable!(), + Statement::EndBlock(_) | Statement::EndIf(_) | Statement::EndWhile(_) | Statement::EndSynchronous(_) | Statement::EndFork(_) => unreachable!(), } } pub fn link_next(&mut self, next: StatementId) { @@ -1096,12 +1101,14 @@ impl Statement { Statement::EndIf(stmt) => stmt.next = next, Statement::EndWhile(stmt) => stmt.next = next, Statement::EndSynchronous(stmt) => stmt.next = next, + Statement::EndFork(stmt) => stmt.next = next, Statement::New(stmt) => stmt.next = next, Statement::Expression(stmt) => stmt.next = next, Statement::Return(_) | Statement::Break(_) | Statement::Continue(_) | Statement::Synchronous(_) + | Statement::Fork(_) | Statement::Goto(_) | Statement::While(_) | Statement::Labeled(_) @@ -1271,7 +1278,6 @@ pub struct SynchronousStatement { // Phase 1: parser pub span: InputSpan, // of the "sync" keyword pub body: BlockStatementId, - // Phase 2: linker pub end_sync: EndSynchronousStatementId, } @@ -1283,6 +1289,23 @@ pub struct EndSynchronousStatement { pub next: StatementId, } +#[derive(Debug, Clone)] +pub struct ForkStatement { + pub this: ForkStatementId, + // Phase 1: parser + pub span: InputSpan, // of the "fork" keyword + pub left_body: BlockStatementId, + pub right_body: Option, + pub end_fork: EndForkStatementId, +} + +#[derive(Debug, Clone)] +pub struct EndForkStatement { + pub this: EndForKStatementId, + pub start_fork: ForkStatementId, + pub next: StatementId, +} + #[derive(Debug, Clone)] pub struct ReturnStatement { pub this: ReturnStatementId, diff --git a/src/protocol/parser/pass_definitions.rs b/src/protocol/parser/pass_definitions.rs index 2f901cfd514e709e41823a10195bfae922b33871..a96d0f2df6f0cca3f62ea4f1017449ab885b0f2f 100644 --- a/src/protocol/parser/pass_definitions.rs +++ b/src/protocol/parser/pass_definitions.rs @@ -423,13 +423,28 @@ impl PassDefinitions { let id = self.consume_synchronous_statement(module, iter, ctx)?; section.push(id.upcast()); - let end_sync = ctx.heap.alloc_end_synchronous_statement(|this| EndSynchronousStatement{ - this, start_sync: id, next: StatementId::new_invalid() + let end_sync = ctx.heap.alloc_end_synchronous_statement(|this| EndSynchronousStatement { + this, + start_sync: id, + next: StatementId::new_invalid() }); section.push(end_sync.upcast()); let sync_stmt = &mut ctx.heap[id]; sync_stmt.end_sync = end_sync; + } else if ident == KW_STMT_FORK { + let id = self.consume_fork_statement(module, iter, ctx)?; + section.push(id.upcast()); + + let end_fork = ctx.heap.alloc_end_fork_statement(|this| EndForkStatement{ + this, + start_fork: id, + next: StatementId::new_invalid(), + }); + section.push(end_fork.upcast()); + + let fork_stmt = &mut ctx.heap[id]; + fork_stmt.end_fork = end_fork; } else if ident == KW_STMT_RETURN { let id = self.consume_return_statement(module, iter, ctx)?; section.push(id.upcast()); @@ -613,6 +628,29 @@ impl PassDefinitions { })) } + fn consume_fork_statement( + &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx + ) -> Result { + let fork_span = consume_exact_ident(&module.source, iter, KW_STMT_FORK)?; + let left_body = self.consume_block_or_wrapped_statement(module, iter, ctx)?; + + let right_body = if has_ident(&module.source, iter, KW_STMT_OR) { + iter.consume(); + let right_body = self.consume_block_or_wrapped_statement(module, iter, ctx)?; + Some(right_body) + } else { + None + }; + + Ok(ctx.heap.alloc_fork_statement(|this| ForkStatement{ + this, + span: fork_span, + left_body, + right_body, + end_fork: EndForkStatementId::new_invalid(), + })) + } + fn consume_return_statement( &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx ) -> Result { diff --git a/src/protocol/parser/pass_typing.rs b/src/protocol/parser/pass_typing.rs index 0b858460b6a7c1ad2e9bf19867c760b3a56c63fd..57d1fa8f05ffe09da37549dae45a3c87dbe2e662 100644 --- a/src/protocol/parser/pass_typing.rs +++ b/src/protocol/parser/pass_typing.rs @@ -1142,6 +1142,19 @@ impl Visitor for PassTyping { self.visit_block_stmt(ctx, body_id) } + fn visit_fork_stmt(&mut self, ctx: &mut Ctx, id: ForkStatementId) -> VisitorResult { + let fork_stmt = &ctx.heap[id]; + let left_body_id = fork_stmt.left_body; + let right_body_id = fork_stmt.right_body; + + self.visit_block_stmt(ctx, left_body_id)?; + if let Some(right_body_id) = right_body_id { + self.visit_block_stmt(ctx, right_body_id)?; + } + + Ok(()) + } + fn visit_return_stmt(&mut self, ctx: &mut Ctx, id: ReturnStatementId) -> VisitorResult { let return_stmt = &ctx.heap[id]; debug_assert_eq!(return_stmt.expressions.len(), 1); diff --git a/src/protocol/parser/pass_validation_linking.rs b/src/protocol/parser/pass_validation_linking.rs index f5bcae1bef2743e65581b156e5f61ddf92a70433..e6564ee6715297d1f90008489874a103bc566b45 100644 --- a/src/protocol/parser/pass_validation_linking.rs +++ b/src/protocol/parser/pass_validation_linking.rs @@ -1,3 +1,40 @@ +/* + * pass_validation_linking.rs + * + * The pass that will validate properties of the AST statements (one is not + * allowed to nest synchronous statements, instantiating components occurs in + * the right places, etc.) and expressions (assignments may not occur in + * arbitrary expressions). + * + * Furthermore, this pass will also perform "linking", in the sense of: some AST + * nodes have something to do with one another, so we link them up in this pass + * (e.g. setting the parents of expressions, linking the control flow statements + * like `continue` and `break` up to the respective loop statement, etc.). + * + * There are several "confusing" parts about this pass: + * + * Setting expression parents: this is the simplest one. The pass struct acts + * like a little state machine. When visiting an expression it will set the + * "parent expression" field of the pass to itself, then visit its child. The + * child will look at this "parent expression" field to determine its parent. + * + * Setting the `next` statement: the AST is a tree, but during execution we walk + * a linear path through all statements. So where appropriate a statement may + * set the "previous statement" field of the pass to itself. When visiting the + * subsequent statement it will check this "previous statement", and if set, it + * will link this previous statement up to itself. Not every statement has a + * previous statement. Hence there are two patterns that occur: assigning the + * `next` value, then clearing the "previous statement" field. And assigning the + * `next` value, and then putting the current statement's ID in the "previous + * statement" field. Because it is so common, this file contain two macros that + * perform that operation. + * + * To make storing types for polymorphic procedures simpler and more efficient, + * we assign to each expression in the procedure a unique ID. This is what the + * "next expression index" field achieves. Each expression simply takes the + * current value, and then increments this counter. + */ + use crate::collections::{ScopedBuffer}; use crate::protocol::ast::*; use crate::protocol::input_source::*; @@ -255,7 +292,8 @@ impl Visitor for PassValidationLinking { self.expr_parent = ExpressionParent::None; // Visit true and false branch. Executor chooses next statement based on - // test expression, not on if-statement itself. + // test expression, not on if-statement itself. Hence the if statement + // does not have a static subsequent statement. assign_then_erase_next_stmt!(self, ctx, id.upcast()); self.visit_block_stmt(ctx, true_stmt_id)?; assign_then_erase_next_stmt!(self, ctx, end_if_id.upcast()); @@ -370,6 +408,28 @@ impl Visitor for PassValidationLinking { Ok(()) } + fn visit_fork_stmt(&mut self, ctx: &mut Ctx, id: ForkStatementId) -> VisitorResult { + let fork_stmt = &ctx.heap[id]; + let end_fork_id = fork_stmt.end_fork; + let left_body_id = fork_stmt.left_body; + let right_body_id = fork_stmt.right_body; + + // Visit the respective bodies. Like the if statement, a fork statement + // does not have a single static subsequent statement. It forks and then + // each fork has a different next statement. + assign_then_erase_next_stmt!(self, ctx, id.upcast()); + self.visit_block_stmt(ctx, left_body_id)?; + assign_then_erase_next_stmt!(self, ctx, end_fork_id.upcast()); + + if let Some(right_body_id) = right_body_id { + self.visit_block_stmt(ctx, right_body_id)?; + assign_then_erase_next_stmt!(self, ctx, end_fork_id.upcast()); + } + + self.prev_stmt = end_fork_id.upcast(); + Ok(()) + } + fn visit_return_stmt(&mut self, ctx: &mut Ctx, id: ReturnStatementId) -> VisitorResult { // Check if "return" occurs within a function let stmt = &ctx.heap[id]; diff --git a/src/protocol/parser/token_parsing.rs b/src/protocol/parser/token_parsing.rs index 08cc187fafa331fcfc13633e7d50e3a3700f67e2..f9cc693182f025fbfb9491ebd2fc4fc60a59ebca 100644 --- a/src/protocol/parser/token_parsing.rs +++ b/src/protocol/parser/token_parsing.rs @@ -46,6 +46,8 @@ pub(crate) const KW_STMT_CONTINUE: &'static [u8] = b"continue"; pub(crate) const KW_STMT_GOTO: &'static [u8] = b"goto"; pub(crate) const KW_STMT_RETURN: &'static [u8] = b"return"; pub(crate) const KW_STMT_SYNC: &'static [u8] = b"sync"; +pub(crate) const KW_STMT_FORK: &'static [u8] = b"fork"; +pub(crate) const KW_STMT_OR: &'static [u8] = b"or"; pub(crate) const KW_STMT_NEW: &'static [u8] = b"new"; // Keywords - types @@ -527,7 +529,7 @@ fn is_reserved_statement_keyword(text: &[u8]) -> bool { KW_IMPORT | KW_AS | KW_STMT_CHANNEL | KW_STMT_IF | KW_STMT_WHILE | KW_STMT_BREAK | KW_STMT_CONTINUE | KW_STMT_GOTO | KW_STMT_RETURN | - KW_STMT_SYNC | KW_STMT_NEW => true, + KW_STMT_SYNC | KW_STMT_FORK | KW_STMT_NEW => true, _ => false, } } diff --git a/src/protocol/parser/visitor.rs b/src/protocol/parser/visitor.rs index c30b5129c9d77c333103e4eb3f96768645067890..0de39881d984f3aee776587344ba09e114f8f21c 100644 --- a/src/protocol/parser/visitor.rs +++ b/src/protocol/parser/visitor.rs @@ -133,6 +133,11 @@ pub(crate) trait Visitor { self.visit_synchronous_stmt(ctx, this) }, Statement::EndSynchronous(_stmt) => Ok(()), + Statement::Fork(stmt) => { + let this = stmt.this; + self.visit_fork_stmt(ctx, this) + }, + Statement::EndFork(_stmt) => Ok(()), Statement::Return(stmt) => { let this = stmt.this; self.visit_return_stmt(ctx, this) @@ -175,6 +180,7 @@ pub(crate) trait Visitor { 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_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(()) }