diff --git a/tests/irdl/test_attr_declarative_assembly_format.py b/tests/irdl/test_attr_declarative_assembly_format.py index c313f467a6..d752fed0bc 100644 --- a/tests/irdl/test_attr_declarative_assembly_format.py +++ b/tests/irdl/test_attr_declarative_assembly_format.py @@ -44,12 +44,19 @@ def test_empty_format_produces_empty_program(): @pytest.mark.parametrize( - "format_str, printed", - [("", ""), ("` `", " "), ("``", ""), ("`\\n`", "\n")], + "format_str, body, printed", + [ + ("", "", ""), + ("` `", "", " "), + ("``", "", ""), + ("`\\n`", "", "\n"), + ("`hello`", "hello", "hello"), + ("`foo``bar`", "foo bar", "foo bar"), + ], ) -def test_prints_and_parses_nothing(format_str: str, printed: str): +def test_print_and_parse(format_str: str, body: str, printed: str): assert _print(format_str) == printed - assert _parse(format_str, "") == [] + assert _parse(format_str, body) == [] def test_parse_attribute_end_to_end(): @@ -63,7 +70,7 @@ def test_parse_attribute_end_to_end(): [ ("$foo", "unexpected token"), ("` `", "unexpected whitespace in directive"), - ("`+`", "expected a whitespace directive"), + ("`+`", "punctuation or identifier expected"), ], ) def test_error(format_str: str, error: str): diff --git a/xdsl/irdl/declarative_assembly_format.py b/xdsl/irdl/declarative_assembly_format.py index ee70c481ec..3f897eb146 100644 --- a/xdsl/irdl/declarative_assembly_format.py +++ b/xdsl/irdl/declarative_assembly_format.py @@ -1371,6 +1371,14 @@ def _print_whitespace(printer: Printer, state: PrintingState, whitespace: str) - state.should_emit_space = False +def _print_keyword(printer: Printer, state: PrintingState, keyword: str) -> None: + if state.should_emit_space: + printer.print_string(" ") + state.should_emit_space = True + state.last_was_punctuation = False + printer.print_string(keyword) + + @dataclass(frozen=True) class WhitespaceDirective(FormatDirective): """ @@ -1446,12 +1454,7 @@ def parse(self, parser: Parser, state: ParsingState) -> bool: return parser.parse_optional_keyword(self.keyword) is not None def print(self, printer: Printer, state: PrintingState, op: IRDLOperation) -> None: - if state.should_emit_space: - printer.print_string(" ") - state.should_emit_space = True - state.last_was_punctuation = False - - printer.print_string(self.keyword) + _print_keyword(printer, state, self.keyword) def is_optional_like(self) -> bool: return True @@ -1570,6 +1573,27 @@ def print( _print_whitespace(printer, state, self.whitespace) +@dataclass(frozen=True) +class AttrKeywordDirective(AttrFormatDirective): + """ + A keyword directive for attribute/type assembly format. + + Mirrors the op-side KeywordDirective: expects a specific identifier and + requests a space to be printed after. + """ + + keyword: str + """The identifier that should be printed.""" + + def parse(self, parser: AttrParser, state: AttrParsingState) -> bool: + return parser.parse_optional_keyword(self.keyword) is not None + + def print( + self, printer: Printer, state: PrintingState, attr: ParametrizedAttribute, / + ) -> None: + _print_keyword(printer, state, self.keyword) + + @dataclass(frozen=True) class AttrFormatProgram: """ diff --git a/xdsl/irdl/declarative_assembly_format_parser.py b/xdsl/irdl/declarative_assembly_format_parser.py index ded3eccbb4..aa8570b8bf 100644 --- a/xdsl/irdl/declarative_assembly_format_parser.py +++ b/xdsl/irdl/declarative_assembly_format_parser.py @@ -49,6 +49,7 @@ AttrFormatDirective, AttrFormatProgram, AttributeVariable, + AttrKeywordDirective, AttrWhitespaceDirective, DenseArrayAttributeVariable, Directive, @@ -949,4 +950,10 @@ def parse_keyword_or_punctuation(self) -> AttrFormatDirective: ) return AttrWhitespaceDirective(whitespace) - self.raise_error("expected a whitespace directive") + # Identifier case + ident = self.parse_optional_identifier() + if ident is None or ident == "`": + self.raise_error("punctuation or identifier expected") + + self.parse_characters("`") + return AttrKeywordDirective(ident)