diff --git a/src/protocol/ast.rs b/src/protocol/ast.rs index 95195aef473b46c535fb2b034a712da10bf953ca..a8b18a801eef40f745879bd48630af59c8159dc3 100644 --- a/src/protocol/ast.rs +++ b/src/protocol/ast.rs @@ -137,6 +137,8 @@ define_new_ast_id!(SynchronousStatementId, StatementId, index(SynchronousStateme 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!(SelectStatementId, StatementId, index(SelectStatement, Statement::Select, statements), alloc(alloc_select_statement)); +define_new_ast_id!(EndSelectStatementId, StatementId, index(EndSelectStatement, Statement::EndSelect, statements), alloc(alloc_end_select_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)); @@ -1068,6 +1070,8 @@ pub enum Statement { EndSynchronous(EndSynchronousStatement), Fork(ForkStatement), EndFork(EndForkStatement), + Select(SelectStatement), + EndSelect(EndSelectStatement), Return(ReturnStatement), Goto(GotoStatement), New(NewStatement), @@ -1112,11 +1116,17 @@ impl Statement { Statement::Continue(v) => v.span, Statement::Synchronous(v) => v.span, Statement::Fork(v) => v.span, + Statement::Select(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(_) | Statement::EndFork(_) => unreachable!(), + Statement::EndBlock(_) + | Statement::EndIf(_) + | Statement::EndWhile(_) + | Statement::EndSynchronous(_) + | Statement::EndFork(_) + | Statement::EndSelect(_) => unreachable!(), } } pub fn link_next(&mut self, next: StatementId) { @@ -1131,6 +1141,7 @@ impl Statement { Statement::EndWhile(stmt) => stmt.next = next, Statement::EndSynchronous(stmt) => stmt.next = next, Statement::EndFork(stmt) => stmt.next = next, + Statement::EndSelect(stmt) => stmt.next = next, Statement::New(stmt) => stmt.next = next, Statement::Expression(stmt) => stmt.next = next, Statement::Return(_) @@ -1138,6 +1149,7 @@ impl Statement { | Statement::Continue(_) | Statement::Synchronous(_) | Statement::Fork(_) + | Statement::Select(_) | Statement::Goto(_) | Statement::While(_) | Statement::Labeled(_) @@ -1335,6 +1347,28 @@ pub struct EndForkStatement { pub next: StatementId, } +#[derive(Debug, Clone)] +pub struct SelectStatement { + pub this: SelectStatementId, + pub span: InputSpan, // of the "select" keyword + pub cases: Vec, + pub end_select: EndSelectStatementId, +} + +#[derive(Debug, Clone)] +pub struct SelectCase { + pub guard_var: Option, // optional memory declaration + pub guard_expr: ExpressionStatementId, // if `guard_var.is_some()`, then always assignment expression + pub block: BlockStatementId, +} + +#[derive(Debug, Clone)] +pub struct EndSelectStatement { + pub this: EndSelectStatementId, + pub start_select: SelectStatementId, + pub next: StatementId, +} + #[derive(Debug, Clone)] pub struct ReturnStatement { pub this: ReturnStatementId, diff --git a/src/protocol/ast_printer.rs b/src/protocol/ast_printer.rs index 6bdc0dd1171d61344c781fe57b0ee3e296f35098..8a059052ecad78939c48f82b234610db38ebccbd 100644 --- a/src/protocol/ast_printer.rs +++ b/src/protocol/ast_printer.rs @@ -38,6 +38,8 @@ const PREFIX_SYNC_STMT_ID: &'static str = "SSyn"; const PREFIX_ENDSYNC_STMT_ID: &'static str = "SESy"; const PREFIX_FORK_STMT_ID: &'static str = "SFrk"; const PREFIX_END_FORK_STMT_ID: &'static str = "SEFk"; +const PREFIX_SELECT_STMT_ID: &'static str = "SSel"; +const PREFIX_END_SELECT_STMT_ID: &'static str = "SESl"; const PREFIX_RETURN_STMT_ID: &'static str = "SRet"; const PREFIX_ASSERT_STMT_ID: &'static str = "SAsr"; const PREFIX_GOTO_STMT_ID: &'static str = "SGot"; @@ -530,6 +532,34 @@ impl ASTWriter { .with_s_key("EndFork"); self.kv(indent2).with_s_key("StartFork").with_disp_val(&stmt.start_fork.0.index); self.kv(indent2).with_s_key("Next").with_disp_val(&stmt.next.index); + }, + Statement::Select(stmt) => { + self.kv(indent).with_id(PREFIX_SELECT_STMT_ID, stmt.this.0.index) + .with_s_key("Select"); + self.kv(indent2).with_s_key("EndSelect").with_disp_val(&stmt.end_select.0.index); + self.kv(indent2).with_s_key("Cases"); + let indent3 = indent2 + 1; + let indent4 = indent3 + 1; + for case in &stmt.cases { + if let Some(guard_var_id) = case.guard_var { + self.kv(indent3).with_s_key("GuardStatement"); + self.write_stmt(heap, guard_var_id.upcast().upcast(), indent4); + } else { + self.kv(indent3).with_s_key("GuardStatement").with_s_val("None"); + } + + self.kv(indent3).with_s_key("GuardExpression"); + self.write_stmt(heap, case.guard_expr.upcast(), indent4); + + self.kv(indent3).with_s_key("Block"); + self.write_stmt(heap, case.block.upcast(), indent4); + } + }, + Statement::EndSelect(stmt) => { + self.kv(indent).with_id(PREFIX_END_SELECT_STMT_ID, stmt.this.0.index) + .with_s_key("EndSelect"); + self.kv(indent2).with_s_key("StartSelect").with_disp_val(&stmt.start_select.0.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) diff --git a/src/protocol/eval/executor.rs b/src/protocol/eval/executor.rs index 130de897237dfa1854d68ef1225d452e527db3b2..75c41c08a68920931792d388ed51c4c5cdb16cb6 100644 --- a/src/protocol/eval/executor.rs +++ b/src/protocol/eval/executor.rs @@ -936,7 +936,15 @@ impl Prompt { cur_frame.position = stmt.next; Ok(EvalContinuation::Stepping) - } + }, + Statement::Select(_stmt) => { + todo!("implement select evaluation") + }, + Statement::EndSelect(stmt) => { + cur_frame.position = stmt.next; + + Ok(EvalContinuation::Stepping) + }, 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"); diff --git a/src/protocol/parser/pass_definitions.rs b/src/protocol/parser/pass_definitions.rs index c6b2f994762f6f61637e05f7c3fd29e8198a074b..af7df9289e33d4f14b041012aed4b6d3581b76fe 100644 --- a/src/protocol/parser/pass_definitions.rs +++ b/src/protocol/parser/pass_definitions.rs @@ -456,6 +456,19 @@ impl PassDefinitions { let fork_stmt = &mut ctx.heap[id]; fork_stmt.end_fork = end_fork; + } else if ident == KW_STMT_SELECT { + let id = self.consume_select_statement(module, iter, ctx)?; + section.push(id.upcast()); + + let end_select = ctx.heap.alloc_end_select_statement(|this| EndSelectStatement{ + this, + start_select: id, + next: StatementId::new_invalid(), + }); + section.push(end_select.upcast()); + + let select_stmt = &mut ctx.heap[id]; + select_stmt.end_select = end_select; } else if ident == KW_STMT_RETURN { let id = self.consume_return_statement(module, iter, ctx)?; section.push(id.upcast()); @@ -671,6 +684,44 @@ impl PassDefinitions { })) } + fn consume_select_statement( + &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx + ) -> Result { + let select_span = consume_exact_ident(&module.source, iter, KW_STMT_SELECT)?; + consume_token(&module.source, iter, TokenKind::OpenCurly)?; + + let mut cases = Vec::new(); + consume_comma_separated_until( + TokenKind::CloseCurly, &module.source, iter, ctx, + |source, iter, ctx| { + // A select arm starts with a guard, being something of the form + // `defined_var = get(port)`, `get(port)` or + // `Type var = get(port)`. So: + let (guard_var, guard_expr) = match self.maybe_consume_memory_statement(module, iter, ctx)? { + Some((guard_var, guard_expr)) => { + (Some(guard_var), guard_expr) + }, + None => { + let guard_expr = self.consume_expression_statement(module, iter, ctx)?; + (None, guard_expr) + }, + }; + consume_token(source, iter, TokenKind::ArrowRight)?; + let block = self.consume_block_or_wrapped_statement(module, iter, ctx)?; + + Ok(SelectCase{ guard_var, guard_expr, block }) + }, + &mut cases, "select arm", None + )?; + + Ok(ctx.heap.alloc_select_statement(|this| SelectStatement{ + this, + span: select_span, + cases, + end_select: EndSelectStatementId::new_invalid(), + })) + } + fn consume_return_statement( &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx ) -> Result { diff --git a/src/protocol/parser/token_parsing.rs b/src/protocol/parser/token_parsing.rs index 3e265270f8f0375198d1a606c0cdf7c51fe4bcce..47f02e7f6c2367f467a348b4363af2e28a590014 100644 --- a/src/protocol/parser/token_parsing.rs +++ b/src/protocol/parser/token_parsing.rs @@ -47,6 +47,7 @@ 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_SELECT: &'static [u8] = b"select"; pub(crate) const KW_STMT_OR: &'static [u8] = b"or"; pub(crate) const KW_STMT_NEW: &'static [u8] = b"new"; diff --git a/src/protocol/parser/visitor.rs b/src/protocol/parser/visitor.rs index edd9bf5e3989728d794ca63b514600e7b50953f8..3d62f1c603b4b29b8d640dee5b355d8186fc3383 100644 --- a/src/protocol/parser/visitor.rs +++ b/src/protocol/parser/visitor.rs @@ -133,6 +133,11 @@ pub(crate) trait Visitor { self.visit_fork_stmt(ctx, this) }, Statement::EndFork(_stmt) => Ok(()), + Statement::Select(stmt) => { + let this = stmt.this; + self.visit_select_stmt(ctx, this) + }, + Statement::EndSelect(_stmt) => Ok(()), Statement::Return(stmt) => { let this = stmt.this; self.visit_return_stmt(ctx, this) @@ -176,6 +181,7 @@ pub(crate) trait Visitor { 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(()) }