From 7acf48cefe218a4acb4fcc03653c912684307305 Mon Sep 17 00:00:00 2001 From: Kevin Broch <86068473+kbroch-rivosinc@users.noreply.github.com> Date: Mon, 12 May 2025 18:00:04 -0700 Subject: [PATCH 1/5] feat(idl): add pretty_idl methods and initial formatter tool --- .pre-commit-config.yaml | 10 +- .../manual/templates/instruction.adoc.erb | 10 +- lib/idl/ast.rb | 376 ++++++++++++++++++ tools/format_idl.rb | 34 ++ 4 files changed, 423 insertions(+), 7 deletions(-) create mode 100755 tools/format_idl.rb diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 924485816..f37a34636 100755 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -96,7 +96,6 @@ repos: hooks: - id: pyupgrade stages: [pre-commit] - - repo: local hooks: - id: shellcheck @@ -106,6 +105,15 @@ repos: entry: shellcheck args: ["--severity=error"] + - repo: local + hooks: + - id: format-idl + name: Format IDL files + entry: tools/format_idl.rb + language: system + types: [file] + files: \.idl$ + - repo: https://github.com/scop/pre-commit-shfmt rev: v3.11.0-1 hooks: diff --git a/backends/manual/templates/instruction.adoc.erb b/backends/manual/templates/instruction.adoc.erb index b28f11d87..30ba0fc07 100644 --- a/backends/manual/templates/instruction.adoc.erb +++ b/backends/manual/templates/instruction.adoc.erb @@ -5,18 +5,16 @@ *<%= inst.long_name %>* -== Assembly format - -`<%= inst.name %> <%= inst.assembly.gsub('x', 'r') %>` - -== Synopsis +<%= inst.fix_entities(inst.description) %> <%- if inst.data_independent_timing? -%> [IMPORTANT] This instruction must have data-independent timing when extension `Zkt` is enabled. <%- end -%> -<%= inst.fix_entities(inst.description) %> +== Assembly format + +`<%= inst.name %> <%= inst.assembly.gsub('x', 'r') %>` == Decode Variables diff --git a/lib/idl/ast.rb b/lib/idl/ast.rb index d15504642..d60335d8d 100644 --- a/lib/idl/ast.rb +++ b/lib/idl/ast.rb @@ -402,6 +402,10 @@ def type_check(symtab) = raise NotImplementedError, "#{self.class.name} must imp # @!macro to_idl # @abstract def to_idl = raise NotImplementedError, "#{self.class.name} must implement to_idl" + def pretty_idl(indent = 0) + (' ' * indent) + to_idl + "\n" + end + def inspect = self.class.name.to_s end @@ -667,6 +671,11 @@ def min_value(symtab) # @!macro to_idl def to_idl = name + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end + end class GlobalWithInitializationSyntaxNode < Treetop::Runtime::SyntaxNode @@ -727,6 +736,11 @@ def add_symbol(symtab) def to_idl var_decl_with_init.to_idl end + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end + end class GlobalSyntaxNode < Treetop::Runtime::SyntaxNode @@ -880,9 +894,15 @@ def value(symtab) end def to_idl = "$array_size(#{expression.to_idl})" + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end + end + class EnumSizeSyntaxNode < Treetop::Runtime::SyntaxNode def to_ast EnumSizeAst.new(input, interval, user_type_name.to_ast) @@ -916,6 +936,11 @@ def value(symtab) end def to_idl = "$enum_size(#{enum_class.to_idl})" + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end + end class EnumElementSizeSyntaxNode < Treetop::Runtime::SyntaxNode @@ -947,6 +972,11 @@ def value(symtab) end def to_idl = "$enum_element_size(#{enum_class.to_idl})" + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end + end class EnumCastSyntaxNode < Treetop::Runtime::SyntaxNode @@ -994,6 +1024,11 @@ def type(symtab) def value(symtab) = expression.value(symtab) def to_idl = "$enum(#{enum_name.to_idl}, #{expression.to_idl})" + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end + end class EnumArrayCastSyntaxNode < Treetop::Runtime::SyntaxNode @@ -1030,6 +1065,11 @@ def value(symtab) end def to_idl = "$enum_to_a(#{enum_class.to_idl})" + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end + end class EnumDefinitionSyntaxNode < Treetop::Runtime::SyntaxNode @@ -1146,6 +1186,17 @@ def to_idl idl << "}" idl end + def pretty_idl(indent = 0) + indent_str = ' ' * indent + result = "#{indent_str}enum #{name} {\n" + elements.each do |element| + result += element.pretty_idl(indent + 1) + end + result += "#{indent_str}}\n" + result + end + + end class BuiltinEnumDefinitionSyntaxNode < Treetop::Runtime::SyntaxNode @@ -1231,6 +1282,11 @@ def name = @user_type.text_value # @!macro to_idl def to_idl = "generated enum #{@user_type.text_value}" + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end + end class BitfieldFieldDefinitionAst < AstNode @@ -1287,6 +1343,11 @@ def to_idl "#{@name} #{@msb.to_idl}-#{@lsb.to_idl}" end end + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end + end class BitfieldDefinitionSyntaxNode < Treetop::Runtime::SyntaxNode @@ -1412,6 +1473,16 @@ def to_idl idl << "}" idl.join("\n") end + def pretty_idl(indent = 0) + indent_str = ' ' * indent + result = "#{indent_str}bitfield #{name} {\n" + fields.each do |field| + result += field.pretty_idl(indent + 1) + end + result += "#{indent_str}}\n" + result + end + end class StructDefinitionSyntaxNode < Treetop::Runtime::SyntaxNode @@ -1502,6 +1573,16 @@ def to_idl end "struct #{name} { #{member_decls.join("; ")}; }" end + def pretty_idl(indent = 0) + indent_str = ' ' * indent + result = "#{indent_str}struct #{name} {\n" + fields.each do |field| + result += field.pretty_idl(indent + 1) + end + result += "#{indent_str}}\n" + result + end + end # class VariableAccessAst < Ast @@ -1557,6 +1638,11 @@ def to_idl "#{a.to_idl}[#{brackets.msb.to_idl}:#{brackets.lsb.to_idl}]" end end + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end + end class AryElementAccessAst < AstNode @@ -1631,6 +1717,11 @@ def value(symtab) # @!macro to_idl def to_idl = "#{var.to_idl}[#{index.to_idl}]" + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end + end class AryRangeAccessAst < AstNode @@ -1689,6 +1780,11 @@ def value(symtab) # @!macro to_idl def to_idl = "#{var.to_idl}[#{msb.to_idl}:#{lsb.to_idl}]" + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end + end @@ -1726,6 +1822,11 @@ def type(symtab) = symtab.xreg_type # @!macro to_idl def to_idl = "$pc = #{rhs.to_idl}" + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end + end class VariableAssignmentSyntaxNode < Treetop::Runtime::SyntaxNode @@ -1806,6 +1907,11 @@ def execute_unknown(symtab) # @!macro to_idl def to_idl = "#{lhs.to_idl} = #{rhs.to_idl}" + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end + end class AryElementAssignmentSyntaxNode < Treetop::Runtime::SyntaxNode @@ -1928,6 +2034,11 @@ def execute_unknown(symtab) # @!macro to_idl def to_idl = "#{lhs.to_idl}[#{idx.to_idl}] = #{rhs.to_idl}" + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end + end class AryRangeAssignmentSyntaxNode < Treetop::Runtime::SyntaxNode @@ -2019,6 +2130,11 @@ def execute_unknown(symtab) # @!macro to_idl def to_idl = "#{variable.to_idl}[#{msb.to_idl}:#{lsb.to_idl}] = #{write_value.to_idl}" + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end + end class FieldAssignmentSyntaxNode < Treetop::Runtime::SyntaxNode @@ -2078,6 +2194,11 @@ def execute_unknown(symtab) # @!macro to_idl def to_idl = "#{field_access.to_idl} = #{write_value.to_idl}" + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end + end class CsrFieldAssignmentSyntaxNode < Treetop::Runtime::SyntaxNode @@ -2138,6 +2259,11 @@ def execute(symtab) def execute_unknown(symtab); end def to_idl = "#{csr_field.to_idl} = #{write_value.to_idl}" + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end + end class MultiVariableAssignmentSyntaxNode < Treetop::Runtime::SyntaxNode @@ -2233,6 +2359,11 @@ def execute_unknown(symtab) # @!macro to_idl def to_idl = "(#{variables.map(&:to_idl).join(', ')}) = #{function_call.to_idl}" + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end + end class MultiVariableDeclarationSyntaxNode < Treetop::Runtime::SyntaxNode @@ -2296,6 +2427,11 @@ def add_symbol(symtab) # @!macro to_idl def to_idl = "#{type_name.to_idl} #{var_name_nodes.map(&:to_idl).join(', ')}" + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end + end class VariableDeclarationSyntaxNode < Treetop::Runtime::SyntaxNode @@ -2411,6 +2547,11 @@ def to_idl "#{type_name.to_idl} #{id.to_idl}[#{ary_size.to_idl}]" end end + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end + end class VariableDeclarationWithInitializationSyntaxNode < Treetop::Runtime::SyntaxNode @@ -2565,6 +2706,11 @@ def to_idl "#{type_name.to_idl} #{lhs.to_idl}[#{ary_size.to_idl}] = #{rhs.to_idl}" end end + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end + end class BinaryExpressionRightSyntaxNode < Treetop::Runtime::SyntaxNode @@ -2630,6 +2776,11 @@ def value(symtab) # @!macro to_idl def to_idl = "$signed(#{expression.to_idl})" + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end + end class BitsCastSyntaxNode < Treetop::Runtime::SyntaxNode @@ -2704,6 +2855,11 @@ def value(symtab) # @!macro to_idl def to_idl = "$signed(#{expr.to_idl})" + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end + end class BinaryExpressionAst < AstNode @@ -2758,6 +2914,11 @@ def invert(symtab) def to_idl "(#{lhs.to_idl} #{op} #{rhs.to_idl})" end + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end + # @!macro type def type(symtab) @@ -3447,6 +3608,11 @@ def value(symtab) = expression.value(symtab) # @!macro to_idl def to_idl = "(#{expression.to_idl})" + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end + end class ArrayLiteralSyntaxNode < Treetop::Runtime::SyntaxNode @@ -3484,6 +3650,11 @@ def value(symtab) end def to_idl = "[#{element_nodes.map(&:to_idl).join(',')}]" + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end + end class ConcatenationExpressionSyntaxNode < Treetop::Runtime::SyntaxNode @@ -3539,6 +3710,11 @@ def value(symtab) # @!macro to_idl def to_idl = "{#{expressions.map { |exp| exp.to_idl }.join(',')}}" + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end + end class ReplicationExpressionSyntaxNode < Treetop::Runtime::SyntaxNode @@ -3595,6 +3771,11 @@ def type(symtab) # @!macro to_idl def to_idl = "{#{n.to_idl}{#{v.to_idl}}}" + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end + end class PostDecrementExpressionSyntaxNode < Treetop::Runtime::SyntaxNode @@ -3647,6 +3828,11 @@ def execute_unknown(symtab) end def to_idl = "#{rval.to_idl}--" + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end + end class BuiltinVariableSyntaxNode < Treetop::Runtime::SyntaxNode @@ -3687,6 +3873,11 @@ def value(symtab) end def to_idl = name + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end + end class PostIncrementExpressionSyntaxNode < Treetop::Runtime::SyntaxNode @@ -3744,6 +3935,11 @@ def execute_unknown(symtab) # @!macro to_idl def to_idl = "#{rval.to_idl}++" + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end + end class FieldAccessExpressionSyntaxNode < Treetop::Runtime::SyntaxNode @@ -3815,6 +4011,11 @@ def value(symtab) # @!macro to_idl def to_idl = "#{obj.to_idl}.#{@field_name}" + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end + end class EnumRefSyntaxNode < Treetop::Runtime::SyntaxNode @@ -3893,6 +4094,11 @@ def value(symtab) # @!macro to_idl def to_idl = "#{@enum_class_name}::#{@member_name}" + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end + end class UnaryOperatorExpressionSyntaxNode < Treetop::Runtime::SyntaxNode @@ -4017,6 +4223,11 @@ def op # @!macro to_idl def to_idl = "#{op}#{expression.to_idl}" + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end + end class TernaryOperatorExpressionSyntaxNode < Treetop::Runtime::SyntaxNode @@ -4117,6 +4328,11 @@ def values(symtab) # @!macro to_idl def to_idl = "#{condition.to_idl} ? #{true_expression.to_idl} : #{false_expression.to_idl}" + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end + end class StatementSyntaxNode < Treetop::Runtime::SyntaxNode @@ -4141,6 +4357,11 @@ def execute_unknown(symtab); end # @1macro to_idl def to_idl = "" + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end + end # represents a simple, one-line statement @@ -4185,6 +4406,11 @@ def execute_unknown(symtab) # @!macro to_idl def to_idl = "#{action.to_idl};" + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end + end class ConditionalStatementSyntaxNode < Treetop::Runtime::SyntaxNode @@ -4237,6 +4463,24 @@ def execute_unknown(symtab) def to_idl "#{action.to_idl} if (#{condition.to_idl});" end + def pretty_idl(indent = 0) + indent_str = ' ' * indent + result = "#{indent_str}if #{condition.to_idl} {\n" + then_branch.each do |stmt| + result += stmt.pretty_idl(indent + 1) + end + if else_branch + result += "#{indent_str}else {\n" + else_branch.each do |stmt| + result += stmt.pretty_idl(indent + 1) + end + result += "#{indent_str}}\n" + else + result += "#{indent_str}}\n" + end + result + end + end class DontCareReturnSyntaxNode < Treetop::Runtime::SyntaxNode @@ -4285,6 +4529,11 @@ def set_expected_type(t) end def to_idl = "-" + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end + end class DontCareLvalueSyntaxNode < Treetop::Runtime::SyntaxNode @@ -4310,6 +4559,11 @@ def type(_symtab) def value(_symtab, _cfg_arch) = internal_error "Why are you calling value for an lval?" def to_idl = "-" + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end + end class ReturnStatementSyntaxNode < Treetop::Runtime::SyntaxNode @@ -4374,6 +4628,11 @@ def return_values(symtab) end def to_idl = "#{return_expression.to_idl};" + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end + end class ReturnExpressionSyntaxNode < Treetop::Runtime::SyntaxNode @@ -4499,6 +4758,11 @@ def return_values(symtab) end def to_idl = "return #{return_value_nodes.map(&:to_idl).join(',')}" + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end + end class ConditionalReturnStatementSyntaxNode < Treetop::Runtime::SyntaxNode @@ -4561,6 +4825,12 @@ def return_values(symtab) end def to_idl = "#{return_expression.to_idl} if (#{condition.to_idl});" + def pretty_idl(indent = 0) + indent_str = ' ' * indent + result = "#{indent_str}if #{condition.to_idl} return #{expression.to_idl};\n" + result + end + end # @api private @@ -4696,6 +4966,11 @@ def to_idl @type_name end end + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end + end module StringLiteralSyntaxNode @@ -4730,6 +5005,11 @@ def value(_symtab) end def to_idl = text_value + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end + end module IntLiteralSyntaxNode @@ -4974,6 +5254,11 @@ def unsigned_value # @!macro to_idl def to_idl = text_value + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end + end class FunctionCallExpressionSyntaxNode < Treetop::Runtime::SyntaxNode @@ -5218,6 +5503,11 @@ def to_idl "#{name}(#{arg_nodes.map(&:to_idl).join(',')})" end end + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end + end @@ -5250,6 +5540,13 @@ def type(symtab) # @!macro to_idl def to_idl = text_value + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end + + + end class InstructionOperationSyntaxNode < Treetop::Runtime::SyntaxNode @@ -5368,6 +5665,16 @@ def to_idl end result end + def pretty_idl(indent = 0) + indent_str = ' ' * indent + result = "#{indent_str}{\n" + statements.each do |stmt| + result += stmt.pretty_idl(indent + 1) + end + result += "#{indent_str}}\n" + result + end + end class FetchSyntaxNode < Treetop::Runtime::SyntaxNode @@ -5850,6 +6157,16 @@ def to_idl idl << "}" idl end + def pretty_idl(indent = 0) + indent_str = ' ' * indent + result = "#{indent_str}for (#{init.to_idl}; #{condition.to_idl}; #{update.to_idl}) {\n" + body.each do |stmt| + result += stmt.pretty_idl(indent + 1) + end + result += "#{indent_str}}\n" + result + end + end class IfBodyAst < AstNode @@ -5967,6 +6284,15 @@ def execute_unknown(symtab) def to_idl stmts.map(&:to_idl).join("") end + def pretty_idl(indent = 0) + indent_str = ' ' * indent + result = "" + statements.each do |stmt| + result += stmt.pretty_idl(indent) + end + result + end + end @@ -6011,6 +6337,16 @@ def return_values(symtab) def to_idl " else if (#{cond.to_idl}) { #{body.to_idl} }" end + def pretty_idl(indent = 0) + indent_str = ' ' * indent + result = "#{indent_str}else if #{condition.to_idl} {\n" + body.each do |stmt| + result += stmt.pretty_idl(indent + 1) + end + result += "#{indent_str}}\n" + result + end + end class IfSyntaxNode < Treetop::Runtime::SyntaxNode @@ -6281,6 +6617,26 @@ def to_idl end result end + def pretty_idl(indent = 0) + indent_str = ' ' * indent + result = "#{indent_str}if #{condition.to_idl} {\n" + then_body.each do |stmt| + result += stmt.pretty_idl(indent + 1) + end + result += "#{indent_str}}" + + if else_body + result += " else {\n" + else_body.each do |stmt| + result += stmt.pretty_idl(indent + 1) + end + result += "#{indent_str}}" + end + + result += "\n" + result + end + end class CsrFieldReadExpressionAst < AstNode @@ -6341,6 +6697,10 @@ def field_name(symtab) def to_idl "CSR[#{@csr_obj.name}].#{@field_name}" end + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end # @!macro type def type(symtab) @@ -6457,6 +6817,10 @@ def value(symtab) # @!macro to_idl def to_idl = "CSR[#{@csr_name}]" + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end end class CsrSoftwareWriteSyntaxNode < Treetop::Runtime::SyntaxNode @@ -6506,6 +6870,10 @@ def execute_unknown(_symtab); end # @!macro to_idl def to_idl = "#{csr.to_idl}.sw_write(#{expression.to_idl})" + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end end # @api private @@ -6610,6 +6978,10 @@ def value(symtab) # @!macro to_idl def to_idl = "#{csr.to_idl}.#{function_call}()" + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end end class CsrWriteSyntaxNode < Treetop::Runtime::SyntaxNode @@ -6665,5 +7037,9 @@ def execute_unknown(symtab); end # @!macro to_idl def to_idl = "CSR[#{idx.text_value}]" + def pretty_idl(indent = 0) + indent_str = ' ' * indent + "#{indent_str}#{type} #{name};\n" + end end end diff --git a/tools/format_idl.rb b/tools/format_idl.rb new file mode 100755 index 000000000..a45dbbb70 --- /dev/null +++ b/tools/format_idl.rb @@ -0,0 +1,34 @@ +# SPDX-FileCopyrightText: 2025 syedowaisalishah alishahowais@gmail.com +# SPDX-License-Identifier: BSD-2-Clause + +#!/usr/bin/env ruby + +require_relative '../idl/parser' +require 'optparse' + +options = {} +OptionParser.new do |opts| + opts.banner = "Usage: format_idl.rb [options] file.idl" + + opts.on("-o", "--output FILE", "Write formatted output to a file") do |v| + options[:output] = v + end +end.parse! + +input_file = ARGV[0] + +unless input_file && File.exist?(input_file) + puts "Error: Please provide a valid IDL file." + exit 1 +end + +source = File.read(input_file) +ast = IDL.parse(source) + +formatted = ast.pretty_idl(0) + +if options[:output] + File.write(options[:output], formatted) +else + puts formatted +end From 17cd5488329a7079987e4ec047dc6e1ed9758310 Mon Sep 17 00:00:00 2001 From: syedowaisalishah Date: Fri, 23 May 2025 15:23:10 +0000 Subject: [PATCH 2/5] feat(idl): add pretty_idl methods and initial formatter tool --- lib/idl.rb | 2 + lib/idl/ast.rb | 92 ++++++++++++++++++++++++++------------------- tools/format_idl.rb | 34 ++++++++++++++--- 3 files changed, 84 insertions(+), 44 deletions(-) diff --git a/lib/idl.rb b/lib/idl.rb index c2212c15b..9f13fa52e 100644 --- a/lib/idl.rb +++ b/lib/idl.rb @@ -2,6 +2,8 @@ require "treetop" +$root ||= Pathname.new(File.expand_path("..", __dir__)) + module Treetop module Runtime # open up Treetop::Runtime::CompiledParser and add a few utility functions diff --git a/lib/idl/ast.rb b/lib/idl/ast.rb index d60335d8d..ef67f2232 100644 --- a/lib/idl/ast.rb +++ b/lib/idl/ast.rb @@ -401,7 +401,17 @@ def type_check(symtab) = raise NotImplementedError, "#{self.class.name} must imp # @!macro to_idl # @abstract - def to_idl = raise NotImplementedError, "#{self.class.name} must implement to_idl" + def to_idl(indent = 0) + # Example implementation — adjust based on actual node structure + result = "" + + # Assuming `children` returns subnodes: + children.each do |child| + result += child.pretty_idl(indent + 1) + end + + result + end def pretty_idl(indent = 0) (' ' * indent) + to_idl + "\n" end @@ -736,10 +746,6 @@ def add_symbol(symtab) def to_idl var_decl_with_init.to_idl end - def pretty_idl(indent = 0) - indent_str = ' ' * indent - "#{indent_str}#{type} #{name};\n" - end end @@ -1189,14 +1195,18 @@ def to_idl def pretty_idl(indent = 0) indent_str = ' ' * indent result = "#{indent_str}enum #{name} {\n" - elements.each do |element| - result += element.pretty_idl(indent + 1) + + element_names.each_with_index do |ename, idx| + val = element_values[idx] + result += "#{indent_str} #{ename} = #{val},\n" end + result += "#{indent_str}}\n" result end + end class BuiltinEnumDefinitionSyntaxNode < Treetop::Runtime::SyntaxNode @@ -1345,9 +1355,12 @@ def to_idl end def pretty_idl(indent = 0) indent_str = ' ' * indent - "#{indent_str}#{type} #{name};\n" + type_str = @type.respond_to?(:to_idl) ? @type.to_idl : @type.to_s + name_str = @name.respond_to?(:to_idl) ? @name.to_idl : @name.to_s + "#{indent_str}#{type_str} #{name_str};\n" end + end class BitfieldDefinitionSyntaxNode < Treetop::Runtime::SyntaxNode @@ -1391,6 +1404,9 @@ def initialize(input, interval, name, size, fields) @size = size @fields = fields end + def fields + @fields + end # @!macro freeze_tree def freeze_tree(global_symtab) @@ -1476,13 +1492,14 @@ def to_idl def pretty_idl(indent = 0) indent_str = ' ' * indent result = "#{indent_str}bitfield #{name} {\n" - fields.each do |field| + @fields.each do |field| result += field.pretty_idl(indent + 1) end result += "#{indent_str}}\n" result end + end class StructDefinitionSyntaxNode < Treetop::Runtime::SyntaxNode @@ -1576,8 +1593,8 @@ def to_idl def pretty_idl(indent = 0) indent_str = ' ' * indent result = "#{indent_str}struct #{name} {\n" - fields.each do |field| - result += field.pretty_idl(indent + 1) + num_members.times do |i| + result += "#{indent_str} #{member_types[i].to_idl} #{member_names[i]};\n" end result += "#{indent_str}}\n" result @@ -2547,10 +2564,8 @@ def to_idl "#{type_name.to_idl} #{id.to_idl}[#{ary_size.to_idl}]" end end - def pretty_idl(indent = 0) - indent_str = ' ' * indent - "#{indent_str}#{type} #{name};\n" - end + + end @@ -4406,10 +4421,7 @@ def execute_unknown(symtab) # @!macro to_idl def to_idl = "#{action.to_idl};" - def pretty_idl(indent = 0) - indent_str = ' ' * indent - "#{indent_str}#{type} #{name};\n" - end + end @@ -4628,10 +4640,7 @@ def return_values(symtab) end def to_idl = "#{return_expression.to_idl};" - def pretty_idl(indent = 0) - indent_str = ' ' * indent - "#{indent_str}#{type} #{name};\n" - end + end @@ -4827,7 +4836,7 @@ def return_values(symtab) def to_idl = "#{return_expression.to_idl} if (#{condition.to_idl});" def pretty_idl(indent = 0) indent_str = ' ' * indent - result = "#{indent_str}if #{condition.to_idl} return #{expression.to_idl};\n" + result = "#{indent_str}if (#{condition.to_idl}) return #{return_expression.to_idl};\n" result end @@ -4966,10 +4975,7 @@ def to_idl @type_name end end - def pretty_idl(indent = 0) - indent_str = ' ' * indent - "#{indent_str}#{type} #{name};\n" - end + end @@ -5540,10 +5546,7 @@ def type(symtab) # @!macro to_idl def to_idl = text_value - def pretty_idl(indent = 0) - indent_str = ' ' * indent - "#{indent_str}#{type} #{name};\n" - end + @@ -6160,13 +6163,14 @@ def to_idl def pretty_idl(indent = 0) indent_str = ' ' * indent result = "#{indent_str}for (#{init.to_idl}; #{condition.to_idl}; #{update.to_idl}) {\n" - body.each do |stmt| + stmts.each do |stmt| result += stmt.pretty_idl(indent + 1) end result += "#{indent_str}}\n" result end + end class IfBodyAst < AstNode @@ -6182,6 +6186,10 @@ def initialize(input, interval, body_stmts) super(input, interval, body_stmts) end end + def each(&block) + @children.each(&block) + end + # @!macro type_check def type_check(symtab) @@ -6339,14 +6347,15 @@ def to_idl end def pretty_idl(indent = 0) indent_str = ' ' * indent - result = "#{indent_str}else if #{condition.to_idl} {\n" - body.each do |stmt| + result = "#{indent_str}else if #{cond.to_idl} {\n" + body.children.each do |stmt| # or just body.each if you define it result += stmt.pretty_idl(indent + 1) end result += "#{indent_str}}\n" result end + end class IfSyntaxNode < Treetop::Runtime::SyntaxNode @@ -6619,15 +6628,19 @@ def to_idl end def pretty_idl(indent = 0) indent_str = ' ' * indent - result = "#{indent_str}if #{condition.to_idl} {\n" - then_body.each do |stmt| + result = "#{indent_str}if (#{if_cond.to_idl}) {\n" + if_body.stmts.each do |stmt| result += stmt.pretty_idl(indent + 1) end result += "#{indent_str}}" - if else_body + elseifs.each do |eif| + result += "\n" + eif.pretty_idl(indent) + end + + unless final_else_body.stmts.empty? result += " else {\n" - else_body.each do |stmt| + final_else_body.stmts.each do |stmt| result += stmt.pretty_idl(indent + 1) end result += "#{indent_str}}" @@ -6637,6 +6650,7 @@ def pretty_idl(indent = 0) result end + end class CsrFieldReadExpressionAst < AstNode diff --git a/tools/format_idl.rb b/tools/format_idl.rb index a45dbbb70..8fdd6c8f6 100755 --- a/tools/format_idl.rb +++ b/tools/format_idl.rb @@ -3,7 +3,7 @@ #!/usr/bin/env ruby -require_relative '../idl/parser' +require_relative '../lib/idl' # This loads the IDL compiler and AST require 'optparse' options = {} @@ -13,6 +13,10 @@ opts.on("-o", "--output FILE", "Write formatted output to a file") do |v| options[:output] = v end + + opts.on("-c", "--check", "Check if file is already properly formatted") do + options[:check] = true + end end.parse! input_file = ARGV[0] @@ -22,11 +26,31 @@ exit 1 end -source = File.read(input_file) -ast = IDL.parse(source) - -formatted = ast.pretty_idl(0) +# Parse the IDL file into an AST +ast = Idl::Compiler.compile_file(input_file) + +# Generate pretty-printed output (based on your own `to_pretty_idl(indent)` methods) +# If your root AST node has no to_pretty_idl method, use definitions.map... +formatted = if ast.respond_to?(:to_pretty_idl) + ast.to_pretty_idl(0) + elsif ast.respond_to?(:definitions) + ast.definitions.map { |d| d.to_pretty_idl(0) }.join("\n") + else + raise "AST does not support pretty printing" + end + +# Optional check mode for pre-commit hooks +if options[:check] + original = File.read(input_file) + if original != formatted + puts "File is not properly formatted: #{input_file}" + exit 1 + else + exit 0 + end +end +# Write or print the output if options[:output] File.write(options[:output], formatted) else From e7b921370b1d2e0e166b83c1769b8f0f4fca19cf Mon Sep 17 00:00:00 2001 From: syedowaisalishah Date: Thu, 29 May 2025 07:42:33 +0000 Subject: [PATCH 3/5] feat(idl): add pretty_idl methods and initial formatter tool --- lib/idl/ast.rb | 31 +++++++++++++++++ lib/idl/idl.treetop | 85 ++++++++++++++++++++++++++++++++------------- 2 files changed, 92 insertions(+), 24 deletions(-) diff --git a/lib/idl/ast.rb b/lib/idl/ast.rb index ef67f2232..1b6d9559e 100644 --- a/lib/idl/ast.rb +++ b/lib/idl/ast.rb @@ -7056,4 +7056,35 @@ def pretty_idl(indent = 0) "#{indent_str}#{type} #{name};\n" end end + class CommentAst < AstNode + attr_reader :text + + def initialize(input, interval, text) + super(input, interval, []) + @text = text + end + + def format_idl(indent = 0) + "#{' ' * indent}# #{@text}".rstrip + "\n" + end + + def to_idl(indent = 0) + format_idl(indent) + end + end + + class WhitespaceAst < AstNode + def initialize(input, interval) + super(input, interval, []) + end + + def format_idl(indent = 0) + "\n" + end + + def to_idl(indent = 0) + "\n" + end + end + end diff --git a/lib/idl/idl.treetop b/lib/idl/idl.treetop index 59bbf6937..a8deeed78 100644 --- a/lib/idl/idl.treetop +++ b/lib/idl/idl.treetop @@ -523,6 +523,15 @@ grammar Idl 'return' vals:(first:(space+ e:(expression/dontcare_return))? rest:(space* ',' space* e:(expression/dontcare_return))*) end + rule statement_list + (statement / comment / blank_line)* + end + + rule block_item + (statement / return_statement / function_if_block / for_loop / comment / blank_line) + end + + rule return_statement return_expression space* 'if' space* expression space* ';' / @@ -530,42 +539,57 @@ grammar Idl end rule function_if_block - 'if' space* '(' space* if_cond:expression space* ')' space* '{' space* - if_body:(e:(statement / return_statement / function_if_block / for_loop) space*)+ + 'if' space* '(' space* if_cond:expression space* ')' space* '{' space* + if_body:(e:(statement / comment / blank_line / return_statement / function_if_block / for_loop) space*)+ + '}' + elseifs:( + space* 'else' space+ 'if' space* '(' space* expression space* ')' space* '{' space* + body:(e:(statement / comment / blank_line / return_statement / function_if_block / for_loop) space*)+ '}' - elseifs:( - space* 'else' space+ 'if' space* '(' space* expression space* ')' space* '{' space* - body:(e:(statement / return_statement / function_if_block / for_loop) space*)+ - '}' - )* - final_else:( - space* 'else' space* '{' space* - body:(e:(statement / return_statement / function_if_block / for_loop) space*)+ - '}' - )? + )* + final_else:( + space* 'else' space* '{' space* + body:(e:(statement / comment / blank_line / return_statement / function_if_block / for_loop) space*)+ + '}' + )? + end - rule execute_if_block - 'if' space* '(' space* if_cond:expression space* ')' space* '{' space* - if_body:(e:(statement / execute_if_block / for_loop) space*)+ +rule function_if_block + 'if' space* '(' space* if_cond:expression space* ')' space* '{' space* + if_body:(e:(statement / comment / blank_line / return_statement / function_if_block / for_loop) space*)+ + '}' + elseifs:( + space* 'else' space+ 'if' space* '(' space* expression space* ')' space* '{' space* + body:(e:(statement / comment / blank_line / return_statement / function_if_block / for_loop) space*)+ + '}' + )* + final_else:( + space* 'else' space* '{' space* + body:(e:(statement / comment / blank_line / return_statement / function_if_block / for_loop) space*)+ + '}' + )? + +end +e:(statement / execute_if_block / for_loop / comment / blank_line) space*)+ '}' elseifs:( space* 'else' space+ 'if' space* '(' space* expression space* ')' space* '{' space* - body:(e:(statement / execute_if_block / for_loop) space*)+ + body:(e:(statement / execute_if_block / for_loop / comment / blank_line) space*)+ '}' )* final_else:( space* 'else' space* '{' space* - body:(e:(statement / execute_if_block / for_loop) space*)+ + body:(e:(statement / execute_if_block / for_loop / comment / blank_line) space*)+ '}' )? - end + end - rule for_loop - 'for' space* '(' space* single_declaration_with_initialization space* ';' space* condition:expression space* ';' space* action:(assignment / post_inc / post_dec) space* ')' space* '{' space* - stmts:(s:(statement / return_statement / function_if_block / for_loop) space*)+ - '}' - end + rule for_loop + 'for' space* '(' space* single_declaration_with_initialization space* ';' space* condition:expression space* ';' space* action:(assignment / post_inc / post_dec) space* ')' space* '{' space* + stmts:(s:(statement / return_statement / function_if_block / for_loop) space*)+ + '}' + end rule builtin_type_name # alias for Bits @@ -618,9 +642,22 @@ grammar Idl end rule comment - '#' content:(!"\n" .)* "\n" # + space* "#" (!"\n" .)* "\n" { + def to_ast + CommentAst.new(input, interval, text_value.strip) + end + } + end + + rule blank_line + space* "\n" { + def to_ast + WhitespaceAst.new(input, interval) + end + } end + rule function_statement (return_statement/statement/function_if_block/for_loop) end From a9cd21b6ab5ce9e7f00f4264ca7738bc544cbab8 Mon Sep 17 00:00:00 2001 From: syedowaisalishah Date: Mon, 2 Jun 2025 22:05:38 +0000 Subject: [PATCH 4/5] feat(idl): add pretty_idl methods and initial formatter tool --- lib/idl/ast.rb | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/lib/idl/ast.rb b/lib/idl/ast.rb index 1b6d9559e..b1c75f245 100644 --- a/lib/idl/ast.rb +++ b/lib/idl/ast.rb @@ -796,7 +796,8 @@ def to_ast interval, definitions.elements.reject do |e| e.elements.all?(&:space?) - end.map(&:to_ast) + end.map { |e| e.respond_to?(:to_ast) ? e.to_ast : nil }.compact + ) end end @@ -1094,7 +1095,9 @@ def to_ast input, interval, user_type_name.to_ast, - e.elements.map { |entry| entry.user_type_name.to_ast }, + e.elements.filter_map { |entry| + entry.respond_to?(:user_type_name) && entry.user_type_name.respond_to?(:to_ast) ? entry.user_type_name.to_ast : nil + } values ) end @@ -1507,6 +1510,7 @@ def to_ast member_types = [] member_names = [] member.elements.each do |m| + next unless m.respond_to?(:type_name) && m.respond_to?(:id) member_types << m.type_name.to_ast member_names << m.id.text_value end @@ -5710,7 +5714,7 @@ def to_ast function_name.text_value, (!respond_to?(:targs) || targs.empty?) ? [] : [targs.first.to_ast] + targs.rest.elements.map { |r| r.single_declaration.to_ast }, ret.empty? ? [] : [ret.first.to_ast] + (ret.respond_to?(:rest) ? ret.rest.elements.map { |r| r.type_name.to_ast } : []), - args.empty? ? [] : [args.first.to_ast] + args.rest.elements.map { |r| r.single_declaration.to_ast}, + args.rest.elements.map { |r| r.respond_to?(:to_ast) ? r.single_declaration.to_ast : nil }.compact desc.text_value, respond_to?(:type) ? type.text_value.strip.to_sym : :normal, respond_to?(:body_block) ? body_block.function_body.to_ast : nil @@ -6043,7 +6047,7 @@ def to_ast single_declaration_with_initialization.to_ast, condition.to_ast, action.to_ast, - stmts.elements.map(&:s).map(&:to_ast) + stmts.elements.map(&:s).filter_map { |e| e.respond_to?(:to_ast) ? e.to_ast : nil } ) end end @@ -6362,14 +6366,18 @@ class IfSyntaxNode < Treetop::Runtime::SyntaxNode def to_ast if_body_stmts = [] if_body.elements.each do |e| + next unless e.respond_to?(:e) && e.e.respond_to?(:to_ast) if_body_stmts << e.e.to_ast + end eifs = [] unless elseifs.empty? elseifs.elements.each do |eif| stmts = [] eif.body.elements.each do |e| - stmts << e.e.to_ast + next unless e.respond_to?(:e) && e.e.respond_to?(:to_ast) + if_body_stmts << e.e.to_ast + end eifs << ElseIfAst.new(input, eif.interval, eif.body.interval, eif.expression.to_ast, stmts) end @@ -6377,6 +6385,7 @@ def to_ast final_else_stmts = [] unless final_else.empty? final_else.body.elements.each do |e| + next unless e.respond_to?(:e) && e.e.respond_to?(:to_ast) final_else_stmts << e.e.to_ast end end From b1e3e7e2481b2523282531bc1eebb166d70b64a7 Mon Sep 17 00:00:00 2001 From: syedowaisalishah Date: Tue, 3 Jun 2025 15:01:52 +0000 Subject: [PATCH 5/5] feat(idl): add pretty_idl methods and initial formatter tool --- lib/idl/ast.rb | 21 +++++++++++++++------ lib/idl/idl.treetop | 25 +++++-------------------- 2 files changed, 20 insertions(+), 26 deletions(-) diff --git a/lib/idl/ast.rb b/lib/idl/ast.rb index b1c75f245..fdf5ed47a 100644 --- a/lib/idl/ast.rb +++ b/lib/idl/ast.rb @@ -1092,14 +1092,13 @@ def to_ast end EnumDefinitionAst.new( - input, + input, interval, user_type_name.to_ast, - e.elements.filter_map { |entry| - entry.respond_to?(:user_type_name) && entry.user_type_name.respond_to?(:to_ast) ? entry.user_type_name.to_ast : nil - } + e.elements.map { |entry| entry.user_type_name.to_ast }, values ) + end end @@ -5714,11 +5713,12 @@ def to_ast function_name.text_value, (!respond_to?(:targs) || targs.empty?) ? [] : [targs.first.to_ast] + targs.rest.elements.map { |r| r.single_declaration.to_ast }, ret.empty? ? [] : [ret.first.to_ast] + (ret.respond_to?(:rest) ? ret.rest.elements.map { |r| r.type_name.to_ast } : []), - args.rest.elements.map { |r| r.respond_to?(:to_ast) ? r.single_declaration.to_ast : nil }.compact + args.empty? ? [] : [args.first.to_ast] + args.rest.elements.map { |r| r.single_declaration.to_ast}, desc.text_value, respond_to?(:type) ? type.text_value.strip.to_sym : :normal, respond_to?(:body_block) ? body_block.function_body.to_ast : nil - ) + ) + end end @@ -7080,6 +7080,10 @@ def format_idl(indent = 0) def to_idl(indent = 0) format_idl(indent) end + + def pretty_idl(indent = 0) + "#{' ' * indent}# #{@text}\n" + end end class WhitespaceAst < AstNode @@ -7094,6 +7098,11 @@ def format_idl(indent = 0) def to_idl(indent = 0) "\n" end + + def pretty_idl(indent = 0) + "\n" + end end + end diff --git a/lib/idl/idl.treetop b/lib/idl/idl.treetop index a8deeed78..039c9cef9 100644 --- a/lib/idl/idl.treetop +++ b/lib/idl/idl.treetop @@ -540,37 +540,22 @@ grammar Idl rule function_if_block 'if' space* '(' space* if_cond:expression space* ')' space* '{' space* - if_body:(e:(statement / comment / blank_line / return_statement / function_if_block / for_loop) space*)+ + if_body:(e:(statement / execute_if_block / for_loop / comment / blank_line) space*)+ '}' elseifs:( space* 'else' space+ 'if' space* '(' space* expression space* ')' space* '{' space* - body:(e:(statement / comment / blank_line / return_statement / function_if_block / for_loop) space*)+ + body:(e:(statement / execute_if_block / for_loop / comment / blank_line) space*)+ '}' )* final_else:( space* 'else' space* '{' space* - body:(e:(statement / comment / blank_line / return_statement / function_if_block / for_loop) space*)+ - '}' - )? - - end - -rule function_if_block - 'if' space* '(' space* if_cond:expression space* ')' space* '{' space* - if_body:(e:(statement / comment / blank_line / return_statement / function_if_block / for_loop) space*)+ - '}' - elseifs:( - space* 'else' space+ 'if' space* '(' space* expression space* ')' space* '{' space* - body:(e:(statement / comment / blank_line / return_statement / function_if_block / for_loop) space*)+ - '}' - )* - final_else:( - space* 'else' space* '{' space* - body:(e:(statement / comment / blank_line / return_statement / function_if_block / for_loop) space*)+ + body:(e:(statement / execute_if_block / for_loop / comment / blank_line) space*)+ '}' )? end + + e:(statement / execute_if_block / for_loop / comment / blank_line) space*)+ '}' elseifs:(