From e3bfcc6a64bb9766b7d12c4a6449d2be25d5aad8 Mon Sep 17 00:00:00 2001 From: Reto Achermann Date: Thu, 1 Feb 2024 09:48:25 -0800 Subject: [PATCH] adding support do declare 'instr' interface fields Signed-off-by: Reto Achermann --- lib/velosilexer/src/tokens.rs | 10 ++- src/parser/interface/field.rs | 115 +++++++++++++++++++++++++++++++++- src/parser/terminals.rs | 1 + src/parsetree/interface.rs | 65 +++++++++++++++++++ src/parsetree/mod.rs | 7 ++- 5 files changed, 191 insertions(+), 7 deletions(-) diff --git a/lib/velosilexer/src/tokens.rs b/lib/velosilexer/src/tokens.rs index 925bc80..39b381a 100644 --- a/lib/velosilexer/src/tokens.rs +++ b/lib/velosilexer/src/tokens.rs @@ -82,8 +82,10 @@ pub enum VelosiKeyword { Mem, /// field that is a register Reg, - /// field that is a memory-mapped regsiter + /// field that is a memory-mapped registers Mmio, + /// a special instruction that is being executed + Instr, // // interface descriptions @@ -108,7 +110,7 @@ pub enum VelosiKeyword { // // control flow and expressions // - /// conditional statemt + /// conditional statement If, /// conditional else branch Else, @@ -189,6 +191,7 @@ impl VelosiKeyword { VelosiKeyword::Mem => "mem", VelosiKeyword::Reg => "reg", VelosiKeyword::Mmio => "mmio", + VelosiKeyword::Instr => "instr", // interface descriptions VelosiKeyword::ReadActions => "ReadActions", @@ -256,6 +259,7 @@ impl<'a> TryFrom<&'a str> for VelosiKeyword { "reg" => Ok(VelosiKeyword::Reg), "mem" => Ok(VelosiKeyword::Mem), "mmio" => Ok(VelosiKeyword::Mmio), + "instr" => Ok(VelosiKeyword::Instr), // interface descriptions "ReadActions" => Ok(VelosiKeyword::ReadActions), "WriteActions" => Ok(VelosiKeyword::WriteActions), @@ -702,6 +706,8 @@ fn test_enum_str() { assert_eq!(VelosiKeyword::Reg.as_str(), "reg"); assert_eq!("mmio".try_into(), Ok(VelosiKeyword::Mmio)); assert_eq!(VelosiKeyword::Mmio.as_str(), "mmio"); + assert_eq!("instr".try_into(), Ok(VelosiKeyword::Instr)); + assert_eq!(VelosiKeyword::Instr.as_str(), "instr"); assert_eq!("ReadActions".try_into(), Ok(VelosiKeyword::ReadActions)); assert_eq!(VelosiKeyword::ReadActions.as_str(), "ReadActions"); diff --git a/src/parser/interface/field.rs b/src/parser/interface/field.rs index 572a88c..12c2cc4 100644 --- a/src/parser/interface/field.rs +++ b/src/parser/interface/field.rs @@ -43,7 +43,8 @@ use crate::error::{IResult, VelosiParserErrBuilder}; /// library internal includes use crate::parser::terminals::*; use crate::parsetree::{ - VelosiParseTreeIdentifier, VelosiParseTreeInterfaceField, VelosiParseTreeInterfaceFieldMemory, + VelosiParseTreeIdentifier, VelosiParseTreeInterfaceField, + VelosiParseTreeInterfaceFieldInstruction, VelosiParseTreeInterfaceFieldMemory, VelosiParseTreeInterfaceFieldMmio, VelosiParseTreeInterfaceFieldNode, VelosiParseTreeInterfaceFieldRegister, }; @@ -77,7 +78,7 @@ use crate::{VelosiParserErr, VelosiTokenStream}; pub fn ifacefield( input: VelosiTokenStream, ) -> IResult { - alt((memoryfield, registerfield, mmiofield))(input) + alt((memoryfield, registerfield, mmiofield, instrfield))(input) } /// Parses and consumes a register interface field definition @@ -244,6 +245,58 @@ pub fn mmiofield( Ok((i3, VelosiParseTreeInterfaceField::Mmio(res))) } +/// Parses and consumes a instr interface field definition +/// +/// # Arguments +/// +/// * `input` - input token stream to be parsed +/// +/// # Results +/// +/// * Ok: The parser succeeded. The return value is a tuple of the remaining input and the +/// recognized mmio field as a parse tree node. +/// * Err: The parser did not succeed. The return value indicates whether this is: +/// +/// * Error: a recoverable error indicating that the parser did not recognize the input but +/// another parser might, or +/// * Failure: a fatal failure indicating the parser recognized the input but failed to parse it +/// and that another parser would fail. +/// +/// # Grammar +/// +/// `INSTRFIELD := KW_INSTR LBRAK NUM RBRACK LBRACE SEPLIST(COMMA, IFACEFIELDBODY) RBRACE` +/// +pub fn instrfield( + input: VelosiTokenStream, +) -> IResult { + let mut loc = input.clone(); + + // if we have the instr keyword + let (i1, _) = kw_instr(input)?; + + let (i2, name) = cut(ident)(i1)?; + + let (i3, nodes) = opt(delimited( + lbrace, + terminated(separated_list0(comma, interfacefieldbody), opt(comma)), + cut(rbrace), + ))(i2)?; + + loc.span_until_start(&i3); + + let nodes = if let Some(nodes) = nodes { + if nodes.is_empty() { + return Err(empty_field_err(i3)); + } + nodes + } else { + Vec::new() + }; + + let res = VelosiParseTreeInterfaceFieldInstruction::with_loc(name, nodes, loc); + Ok((i3, VelosiParseTreeInterfaceField::Instruction(res))) +} + fn empty_field_err(loc: VelosiTokenStream) -> Err { let errmsg = "empty interface field block"; let hint = "remove the block or fill it in explicitly"; @@ -568,3 +621,61 @@ fn test_mmio_field_fail_error_messages() { test_parse_and_compare_file_fail!("interface/parts/mmiofield_01_field_info_wrong", mmiofield); test_parse_and_compare_file_fail!("interface/parts/mmiofield_02_field_info_wrong_2", mmiofield); } + +#[test] +fn test_instr_field_ok() { + test_parse_and_compare_ok!( + "instr foo { Layout {} }", + instrfield, + "instr foo {\n Layout {\n },\n}" + ); + // trailing comma is ok + test_parse_and_compare_ok!( + "instr foo { Layout {}, }", + instrfield, + "instr foo {\n Layout {\n },\n}" + ); + // it's ok to have the elements twice + test_parse_and_compare_ok!( + "instr foo { Layout {}, Layout {}, }", + instrfield, + "instr foo {\n Layout {\n },\n Layout {\n },\n}" + ); + + test_parse_and_compare_ok!( + "instr foo { WriteActions {}, WriteActions {}, }", + instrfield, + "instr foo {\n WriteActions {\n },\n WriteActions {\n },\n}" + ); + + test_parse_and_compare_ok!( + "instr foo { ReadActions {}, ReadActions {}, }", + instrfield, + "instr foo {\n ReadActions {\n },\n ReadActions {\n },\n}" + ); + // different orders of the body elements are ok + test_parse_and_compare_ok!( + "instr foo { Layout {}, WriteActions{}, ReadActions{} }", + instrfield, + "instr foo {\n Layout {\n },\n WriteActions {\n },\n ReadActions {\n },\n}" + ); + test_parse_and_compare_ok!( + "instr foo { ReadActions {}, Layout {}, WriteActions{}, }", + instrfield, + "instr foo {\n ReadActions {\n },\n Layout {\n },\n WriteActions {\n },\n}" + ); + test_parse_and_compare_ok!( + "instr foo { WriteActions {}, Layout {}, ReadActions{} }", + instrfield, + "instr foo {\n WriteActions {\n },\n Layout {\n },\n ReadActions {\n },\n}" + ); +} + +#[test] +fn test_instr_field_fail() { + // empty field definition + test_parse_and_check_fail!("instr foo {}", instrfield); + + // parameters + test_parse_and_check_fail!("instr foo [8]", instrfield); +} diff --git a/src/parser/terminals.rs b/src/parser/terminals.rs index 2514c54..214286b 100644 --- a/src/parser/terminals.rs +++ b/src/parser/terminals.rs @@ -238,6 +238,7 @@ keywordparser!(pub kw_mapdef, VelosiKeyword::Map); keywordparser!(pub kw_mem, VelosiKeyword::Mem); keywordparser!(pub kw_reg, VelosiKeyword::Reg); keywordparser!(pub kw_mmio, VelosiKeyword::Mmio); +keywordparser!(pub kw_instr, VelosiKeyword::Instr); keywordparser!(pub kw_readaction, VelosiKeyword::ReadActions); keywordparser!(pub kw_writeaction, VelosiKeyword::WriteActions); diff --git a/src/parsetree/interface.rs b/src/parsetree/interface.rs index 9d03bb3..5241c49 100644 --- a/src/parsetree/interface.rs +++ b/src/parsetree/interface.rs @@ -485,6 +485,69 @@ impl Debug for VelosiParseTreeInterfaceFieldRegister { } } +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Interface Instruction Field +/////////////////////////////////////////////////////////////////////////////////////////////////// + +/// Represents a instruction interface field +#[derive(PartialEq, Eq, Clone)] +pub struct VelosiParseTreeInterfaceFieldInstruction { + /// the identifer of the field + pub name: VelosiParseTreeIdentifier, + /// nodes of the field + pub nodes: Vec, + /// location of the field in the source file + pub loc: VelosiTokenStream, +} + +impl VelosiParseTreeInterfaceFieldInstruction { + /// constructs a new register interface field + pub fn with_loc( + name: VelosiParseTreeIdentifier, + nodes: Vec, + loc: VelosiTokenStream, + ) -> Self { + Self { name, nodes, loc } + } + + /// constructs a new register interface field with default location + pub fn new( + name: VelosiParseTreeIdentifier, + nodes: Vec, + ) -> Self { + Self::with_loc(name, nodes, VelosiTokenStream::default()) + } +} + +/// Implementation of [Display] for [VelosiParseTreeInterfaceFieldInstruction] +impl Display for VelosiParseTreeInterfaceFieldInstruction { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, "instr {}", self.name)?; + if !self.nodes.is_empty() { + writeln!(f, " {{")?; + for node in &self.nodes { + let formatted = format!("{node}"); + for (i, l) in formatted.lines().enumerate() { + if i > 0 { + writeln!(f)?; + } + write!(f, " {l}")?; + } + writeln!(f, ",")?; + } + write!(f, "}}")?; + } + Ok(()) + } +} + +/// Implementation of [Debug] for [VelosiParseTreeInterfaceFieldInstruction] +impl Debug for VelosiParseTreeInterfaceFieldInstruction { + fn fmt(&self, format: &mut Formatter) -> FmtResult { + Display::fmt(&self, format) + } +} + /////////////////////////////////////////////////////////////////////////////////////////////////// // Interface Fields /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -495,6 +558,7 @@ pub enum VelosiParseTreeInterfaceField { Memory(VelosiParseTreeInterfaceFieldMemory), Mmio(VelosiParseTreeInterfaceFieldMmio), Register(VelosiParseTreeInterfaceFieldRegister), + Instruction(VelosiParseTreeInterfaceFieldInstruction), } /// Implementation of [Display] for [VelosiParseTreeInterfaceField] @@ -505,6 +569,7 @@ impl Display for VelosiParseTreeInterfaceField { Memory(field) => Display::fmt(field, f), Mmio(field) => Display::fmt(field, f), Register(field) => Display::fmt(field, f), + Instruction(field) => Display::fmt(field, f), } } } diff --git a/src/parsetree/mod.rs b/src/parsetree/mod.rs index 1e188af..4352fa8 100644 --- a/src/parsetree/mod.rs +++ b/src/parsetree/mod.rs @@ -60,9 +60,10 @@ pub use identifier::VelosiParseTreeIdentifier; pub use import::VelosiParseTreeImport; pub use interface::{ VelosiParseTreeInterface, VelosiParseTreeInterfaceAction, VelosiParseTreeInterfaceActions, - VelosiParseTreeInterfaceField, VelosiParseTreeInterfaceFieldMemory, - VelosiParseTreeInterfaceFieldMmio, VelosiParseTreeInterfaceFieldNode, - VelosiParseTreeInterfaceFieldRegister, VelosiParseTreeInterfaceLayout, + VelosiParseTreeInterfaceField, VelosiParseTreeInterfaceFieldInstruction, + VelosiParseTreeInterfaceFieldMemory, VelosiParseTreeInterfaceFieldMmio, + VelosiParseTreeInterfaceFieldNode, VelosiParseTreeInterfaceFieldRegister, + VelosiParseTreeInterfaceLayout, }; pub use map::{ VelosiParseTreeMap, VelosiParseTreeMapElement, VelosiParseTreeMapExplicit,