From 0a028748b8467d26b0050ec68176349e4f8d6ce8 Mon Sep 17 00:00:00 2001 From: Afonso Oliveira Date: Mon, 9 Dec 2024 10:46:44 +0000 Subject: [PATCH] Add definedBy validation Signed-off-by: Afonso Oliveira --- Rakefile | 26 ++++++++++++++++++++++++-- lib/validate.rb | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 2 deletions(-) diff --git a/Rakefile b/Rakefile index bddeefd9d..b02e3fd7f 100644 --- a/Rakefile +++ b/Rakefile @@ -90,15 +90,37 @@ namespace :test do end task schema: "gen:arch" do validator = Validator.instance - puts "Checking arch files against schema.." + puts "Checking arch files against schema..." arch_files = Dir.glob("#{$root}/arch/**/*.yaml") progressbar = ProgressBar.create(total: arch_files.size) + + incorrect_defined_by_files = [] + arch_files.each do |f| progressbar.increment validator.validate(f) + + # Only check definedBy for instructions + if f.include?("/inst/") + yaml_content = YamlLoader.load(f, permitted_classes: [Date]) + if defined_by = yaml_content['definedBy'] + unless validator.valid_defined_by?(defined_by) + incorrect_defined_by_files << f + end + end + end end + + if incorrect_defined_by_files.any? + puts "\nThe following instruction files have incorrectly formatted 'definedBy' fields:" + incorrect_defined_by_files.each do |file| + puts " #{Pathname.new(file).relative_path_from($root)}" + end + exit 1 + end + Rake::Task["test:insts"].invoke - puts "All files validate against their schema" + puts "All files validate against their schema" end task idl_model: ["gen:arch", "#{$root}/.stamps/arch-gen-_32.stamp", "#{$root}/.stamps/arch-gen-_64.stamp"] do print "Parsing IDL code for RV32..." diff --git a/lib/validate.rb b/lib/validate.rb index c2215b433..28b1dbe9f 100644 --- a/lib/validate.rb +++ b/lib/validate.rb @@ -275,4 +275,42 @@ def validate_instruction(path) validate_instruction_encoding(inst_name, obj["encoding"]["RV64"]) end end + # Validate the format of a definedBy field in an instruction file + # + # @param defined_by [String, Hash] The definedBy field to validate + # @return [Boolean] true if the field format is valid, false otherwise + # - String must be non-empty and without commas + # - Hash must have single key (anyOf/oneOf/allOf) with array or comma-separated list values + def valid_defined_by?(defined_by) + return false if defined_by.nil? + + case defined_by + when String + # Simple string case - shouldn't contain commas + !defined_by.include?(',') && !defined_by.strip.empty? + when Hash + # Only allow one key which must be anyOf, oneOf, or allOf + return false unless defined_by.size == 1 + key = defined_by.keys.first + return false unless ['anyOf', 'oneOf', 'allOf'].include?(key) + + value = defined_by[key] + case value + when Array + # Each array element should be a simple string without commas + value.all? { |v| v.is_a?(String) && !v.include?(',') && !v.strip.empty? } + when String + # Also accept the compact form: anyOf: [C,Zca] + # Split by comma and/or brackets, strip whitespace and validate + elements = value.gsub(/[\[\]]/, '').split(',').map(&:strip) + elements.all? { |v| !v.empty? } + else + false + end + else + false + end + end + end +