From ac550c6b42b55d2dd75a7a6314168f5eef1d2765 Mon Sep 17 00:00:00 2001 From: Kevin Broch Date: Fri, 10 Jan 2025 10:11:36 -0800 Subject: [PATCH] autocorrected changes from running pre-commit rubocop relates to #408 command run: `pre-commit run --all-files rubocop` --- Rakefile | 21 +- backends/certificate_doc/tasks.rake | 8 +- backends/cfg_html_doc/adoc_gen.rake | 2 +- backends/cfg_html_doc/html_gen.rake | 7 +- backends/ext_pdf_doc/idl_lexer.rb | 20 +- backends/ext_pdf_doc/tasks.rake | 6 +- backends/indexer/tasks.rake | 1 - backends/manual/tasks.rake | 28 +- .../arch_overlay/csr/Xqci/gen_mcliciX.rb | 7 +- lib/arch_obj_models/certificate.rb | 12 +- lib/arch_obj_models/csr.rb | 51 +- lib/arch_obj_models/csr_field.rb | 49 +- lib/arch_obj_models/extension.rb | 87 +- lib/arch_obj_models/instruction.rb | 64 +- lib/arch_obj_models/manual.rb | 4 +- lib/arch_obj_models/obj.rb | 29 +- lib/arch_obj_models/portfolio.rb | 83 +- lib/arch_obj_models/profile.rb | 16 +- lib/arch_obj_models/schema.rb | 274 +++--- lib/architecture.rb | 10 +- lib/cfg_arch.rb | 73 +- lib/config.rb | 4 +- lib/idl.rb | 8 +- lib/idl/ast.rb | 859 ++++++++---------- lib/idl/passes/find_return_values.rb | 12 +- lib/idl/passes/gen_adoc.rb | 189 ++-- lib/idl/passes/gen_option_adoc.rb | 74 +- lib/idl/passes/prune.rb | 80 +- lib/idl/passes/reachable_exceptions.rb | 22 +- lib/idl/passes/reachable_functions.rb | 24 +- lib/idl/symbol_table.rb | 35 +- lib/idl/tests/helpers.rb | 6 +- lib/idl/tests/test_expressions.rb | 4 +- lib/idl/tests/test_lexer.rb | 1 - lib/idl/type.rb | 224 ++--- lib/template_helpers.rb | 12 +- lib/test/test_yaml_loader.rb | 150 +-- lib/version.rb | 35 +- 38 files changed, 1324 insertions(+), 1267 deletions(-) diff --git a/Rakefile b/Rakefile index f9a3d858c..d2779396b 100644 --- a/Rakefile +++ b/Rakefile @@ -120,7 +120,7 @@ namespace :test do Architecture.new("#{$root}/resolved_arch").validate(show_progress: true) puts "All files validate against their schema" end - task idl: ["gen:resolved_arch", "#{$root}/.stamps/resolve-rv32.stamp", "#{$root}/.stamps/resolve-rv64.stamp"] do + task idl: ["gen:resolved_arch", "#{$root}/.stamps/resolve-rv32.stamp", "#{$root}/.stamps/resolve-rv64.stamp"] do print "Parsing IDL code for RV32..." cfg_arch32 = cfg_arch_for("rv32") puts "done" @@ -141,7 +141,8 @@ def insert_warning(str, from) # insert a warning on the second line lines = str.lines first_line = lines.shift - lines.unshift(first_line, "\n# WARNING: This file is auto-generated from #{Pathname.new(from).relative_path_from($root)}").join("") + lines.unshift(first_line, + "\n# WARNING: This file is auto-generated from #{Pathname.new(from).relative_path_from($root)}").join("") end private :insert_warning @@ -149,7 +150,7 @@ private :insert_warning file "#{$root}/arch/csr/Zihpm/mhpmcounter#{hpm_num}.yaml" => [ "#{$root}/arch/csr/Zihpm/mhpmcounterN.layout", __FILE__ - ] do |t| + ] do |t| erb = ERB.new(File.read($root / "arch/csr/Zihpm/mhpmcounterN.layout"), trim_mode: "-") erb.filename = "#{$root}/arch/csr/Zihpm/mhpmcounterN.layout" File.write(t.name, insert_warning(erb.result(binding), t.prerequisites.first)) @@ -157,7 +158,7 @@ private :insert_warning file "#{$root}/arch/csr/Zihpm/mhpmcounter#{hpm_num}h.yaml" => [ "#{$root}/arch/csr/Zihpm/mhpmcounterNh.layout", __FILE__ - ] do |t| + ] do |t| erb = ERB.new(File.read($root / "arch/csr/Zihpm/mhpmcounterNh.layout"), trim_mode: "-") erb.filename = "#{$root}/arch/csr/Zihpm/mhpmcounterNh.layout" File.write(t.name, insert_warning(erb.result(binding), t.prerequisites.first)) @@ -165,7 +166,7 @@ private :insert_warning file "#{$root}/arch/csr/Zihpm/mhpmevent#{hpm_num}.yaml" => [ "#{$root}/arch/csr/Zihpm/mhpmeventN.layout", __FILE__ - ] do |t| + ] do |t| erb = ERB.new(File.read($root / "arch/csr/Zihpm/mhpmeventN.layout"), trim_mode: "-") erb.filename = "#{$root}/arch/csr/Zihpm/mhpmeventN.layout" File.write(t.name, insert_warning(erb.result(binding), t.prerequisites.first)) @@ -173,7 +174,7 @@ private :insert_warning file "#{$root}/arch/csr/Zihpm/mhpmevent#{hpm_num}h.yaml" => [ "#{$root}/arch/csr/Zihpm/mhpmeventNh.layout", __FILE__ - ] do |t| + ] do |t| erb = ERB.new(File.read($root / "arch/csr/Zihpm/mhpmeventNh.layout"), trim_mode: "-") erb.filename = "#{$root}/arch/csr/Zihpm/mhpmeventNh.layout" File.write(t.name, insert_warning(erb.result(binding), t.prerequisites.first)) @@ -181,7 +182,7 @@ private :insert_warning file "#{$root}/arch/csr/Zihpm/hpmcounter#{hpm_num}.yaml" => [ "#{$root}/arch/csr/Zihpm/hpmcounterN.layout", __FILE__ - ] do |t| + ] do |t| erb = ERB.new(File.read($root / "arch/csr/Zihpm/hpmcounterN.layout"), trim_mode: "-") erb.filename = "#{$root}/arch/csr/Zihpm/hpmcounterN.layout" File.write(t.name, insert_warning(erb.result(binding), t.prerequisites.first)) @@ -189,7 +190,7 @@ private :insert_warning file "#{$root}/arch/csr/Zihpm/hpmcounter#{hpm_num}h.yaml" => [ "#{$root}/arch/csr/Zihpm/hpmcounterNh.layout", __FILE__ - ] do |t| + ] do |t| erb = ERB.new(File.read($root / "arch/csr/Zihpm/hpmcounterNh.layout"), trim_mode: "-") erb.filename = "#{$root}/arch/csr/Zihpm/hpmcounterNh.layout" File.write(t.name, insert_warning(erb.result(binding), t.prerequisites.first)) @@ -200,7 +201,7 @@ end file "#{$root}/arch/csr/I/pmpaddr#{pmpaddr_num}.yaml" => [ "#{$root}/arch/csr/I/pmpaddrN.layout", __FILE__ - ] do |t| + ] do |t| erb = ERB.new(File.read($root / "arch/csr/I/pmpaddrN.layout"), trim_mode: "-") erb.filename = "#{$root}/arch/csr/I/pmpaddrN.layout" File.write(t.name, insert_warning(erb.result(binding), t.prerequisites.first)) @@ -211,7 +212,7 @@ end file "#{$root}/arch/csr/I/pmpcfg#{pmpcfg_num}.yaml" => [ "#{$root}/arch/csr/I/pmpcfgN.layout", __FILE__ - ] do |t| + ] do |t| erb = ERB.new(File.read($root / "arch/csr/I/pmpcfgN.layout"), trim_mode: "-") erb.filename = "#{$root}/arch/csr/I/pmpcfgN.layout" File.write(t.name, insert_warning(erb.result(binding), t.prerequisites.first)) diff --git a/backends/certificate_doc/tasks.rake b/backends/certificate_doc/tasks.rake index 31766c0cd..0e9220360 100644 --- a/backends/certificate_doc/tasks.rake +++ b/backends/certificate_doc/tasks.rake @@ -12,8 +12,8 @@ CERT_DOC_DIR = Pathname.new "#{$root}/backends/certificate_doc" Dir.glob("#{$root}/arch/certificate_model/*.yaml") do |f| cert_model_name = File.basename(f, ".yaml") cert_model_obj = YAML.load_file(f, permitted_classes: [Date]) - cert_class_name = File.basename(cert_model_obj['class']['$ref'].split("#")[0], ".yaml") - raise "Ill-formed certificate model file #{f}: missing 'class' field" if cert_model_obj['class'].nil? + cert_class_name = File.basename(cert_model_obj["class"]["$ref"].split("#")[0], ".yaml") + raise "Ill-formed certificate model file #{f}: missing 'class' field" if cert_model_obj["class"].nil? base = cert_model_obj["base"] raise "Missing certificate model base" if base.nil? @@ -46,12 +46,11 @@ Dir.glob("#{$root}/arch/certificate_model/*.yaml") do |f| cert_model = cfg_arch.cert_model(cert_model_name) # Set globals for ERB template. - portfolio = cert_model cert_class = cert_model.cert_class portfolio = cert_model portfolio_class = cert_class - version = File.basename(t.name, '.adoc').split('-')[1..].join('-') + version = File.basename(t.name, ".adoc").split("-")[1..].join("-") erb = ERB.new(File.read("#{CERT_DOC_DIR}/templates/certificate.adoc.erb"), trim_mode: "-") erb.filename = "#{CERT_DOC_DIR}/templates/certificate.adoc.erb" @@ -108,7 +107,6 @@ Dir.glob("#{$root}/arch/certificate_model/*.yaml") do |f| adoc_file ].join(" ") end - end namespace :gen do diff --git a/backends/cfg_html_doc/adoc_gen.rake b/backends/cfg_html_doc/adoc_gen.rake index 527927182..ed9541c3c 100644 --- a/backends/cfg_html_doc/adoc_gen.rake +++ b/backends/cfg_html_doc/adoc_gen.rake @@ -4,7 +4,7 @@ require "ruby-prof" # fill out templates for every csr, inst, ext, and func ["csr", "inst", "ext", "func"].each do |type| - rule %r{#{$root}/\.stamps/adoc-gen-#{type}s-.*\.stamp} => proc { |tname| + rule %r{#{$root}/\.stamps/adoc-gen-#{type}s-.*\.stamp} => proc { |_tname| [ "#{CFG_HTML_DOC_DIR}/templates/#{type}.adoc.erb", "#{$root}/lib/cfg_arch.rb", diff --git a/backends/cfg_html_doc/html_gen.rake b/backends/cfg_html_doc/html_gen.rake index 1a2534fa2..b124ec131 100644 --- a/backends/cfg_html_doc/html_gen.rake +++ b/backends/cfg_html_doc/html_gen.rake @@ -21,7 +21,7 @@ module AntoraUtils when "csr" "xref:csrs:#{name}.adoc##{name}-def[#{link_text.gsub(']', '\]')}]" when "csr_field" - csr_name, field_name = name.split('.') + csr_name, field_name = name.split(".") "xref:csrs:#{csr_name}.adoc##{csr_name}-#{field_name}-def[#{link_text.gsub(']', '\]')}]" when "ext" "xref:exts:#{name}.adoc##{name}-def[#{link_text.gsub(']', '\]')}]" @@ -72,7 +72,7 @@ rule %r{#{$root}/gen/cfg_html_doc/.*/antora/modules/nav.adoc} => proc { |tname| end rule %r{#{$root}/gen/cfg_html_doc/.*/antora/modules/ROOT/pages/config.adoc} => proc { |tname| - config_name = Pathname.new(tname).relative_path_from("#{$root}/gen/cfg_html_doc").to_s.split("/")[0] + Pathname.new(tname).relative_path_from("#{$root}/gen/cfg_html_doc").to_s.split("/")[0] [ "#{CFG_HTML_DOC_DIR}/templates/config.adoc.erb", __FILE__ @@ -226,7 +226,8 @@ rule %r{#{$root}/\.stamps/html-gen-prose-.*\.stamp} => FileList[$root / "arch" / config_name = Pathname.new(t.name).basename(".stamp").sub("html-gen-prose-", "") FileUtils.rm_rf $root / "gen" / "cfg_html_doc" / config_name / "antora" / "modules" / "prose" FileUtils.mkdir_p $root / "gen" / "cfg_html_doc" / config_name / "antora" / "modules" / "prose" - FileUtils.cp_r $root / "arch" / "prose", $root / "gen" / "cfg_html_doc" / config_name / "antora" / "modules" / "prose" / "pages" + FileUtils.cp_r $root / "arch" / "prose", + $root / "gen" / "cfg_html_doc" / config_name / "antora" / "modules" / "prose" / "pages" Rake::Task["#{$root}/.stamps"].invoke diff --git a/backends/ext_pdf_doc/idl_lexer.rb b/backends/ext_pdf_doc/idl_lexer.rb index c89af9fe0..a5fa52d81 100644 --- a/backends/ext_pdf_doc/idl_lexer.rb +++ b/backends/ext_pdf_doc/idl_lexer.rb @@ -34,16 +34,16 @@ def self.keywords_type state :root do rule ws, Text::Whitespace - rule %r{#.*}, Comment::Single - rule %r{"[^"]*"}, Str::Double - rule %r{[A-Z][a-zA-Z0-9]*}, Name::Constant - rule %r{(?:(?:[0-9]+)|(?:XLEN))?'s?[bodh]?[0-9_a-fA-F]+}, Num - rule %r/0x[0-9a-f]+[lu]*/i, Num::Hex - rule %r/0[0-7]+[lu]*/i, Num::Oct - rule %r{\d+}, Num::Integer - rule %r{(?:true|false|\$encoding|\$pc|\$signed|\$bits)}, Name::Builtin - rule %r{[.,;:\[\]\(\)\}\{]}, Punctuation - rule %r([~!%^&*+=\|?:<>/-]), Operator + rule(/#.*/, Comment::Single) + rule(/"[^"]*"/, Str::Double) + rule(/[A-Z][a-zA-Z0-9]*/, Name::Constant) + rule(/(?:(?:[0-9]+)|(?:XLEN))?'s?[bodh]?[0-9_a-fA-F]+/, Num) + rule(/0x[0-9a-f]+[lu]*/i, Num::Hex) + rule(/0[0-7]+[lu]*/i, Num::Oct) + rule(/\d+/, Num::Integer) + rule(/(?:true|false|\$encoding|\$pc|\$signed|\$bits)/, Name::Builtin) + rule(/[.,;:\[\]()}{]/, Punctuation) + rule %r{[~!%^&*+=|?:<>/-]}, Operator rule id do |m| name = m[0] diff --git a/backends/ext_pdf_doc/tasks.rake b/backends/ext_pdf_doc/tasks.rake index bb8a0343c..72cb38bde 100644 --- a/backends/ext_pdf_doc/tasks.rake +++ b/backends/ext_pdf_doc/tasks.rake @@ -30,7 +30,7 @@ module AsciidocUtils when "csr" "xref:#csr-#{name}-def[#{link_text.gsub(']', '\]')}]" when "csr_field" - csr_name, field_name = name.split('.') + name.split(".") # "xref:csrs:#{csr_name}.adoc##{csr_name}-#{field_name}-def[#{link_text.gsub(']', '\]')}]" link_text when "ext" @@ -47,7 +47,7 @@ module AsciidocUtils end end -file "#{$root}/ext/docs-resources/themes/riscv-pdf.yml" => "#{$root}/.gitmodules" do |t| +file "#{$root}/ext/docs-resources/themes/riscv-pdf.yml" => "#{$root}/.gitmodules" do |_t| system "git submodule update --init ext/docs-resources" end @@ -111,7 +111,7 @@ rule %r{#{$root}/gen/ext_pdf_doc/.*/html/.*_extension\.html} => proc { |tname| end rule %r{#{$root}/gen/ext_pdf_doc/.*/adoc/.*_extension\.adoc} => proc { |tname| - config_name = Pathname.new(tname).relative_path_from("#{$root}/gen/ext_pdf_doc").to_s.split("/")[0] + Pathname.new(tname).relative_path_from("#{$root}/gen/ext_pdf_doc").to_s.split("/")[0] ext_name = Pathname.new(tname).basename(".adoc").to_s.split("_")[0..-2].join("_") arch_yaml_paths = if File.exist?("#{$root}/arch/ext/#{ext_name}.yaml") diff --git a/backends/indexer/tasks.rake b/backends/indexer/tasks.rake index fac759c65..ddbc69f90 100644 --- a/backends/indexer/tasks.rake +++ b/backends/indexer/tasks.rake @@ -1,4 +1,3 @@ - require "pathname" namespace :gen do diff --git a/backends/manual/tasks.rake b/backends/manual/tasks.rake index d7573071e..8bd04dbc8 100644 --- a/backends/manual/tasks.rake +++ b/backends/manual/tasks.rake @@ -50,8 +50,8 @@ end # Rule to create a chapter page in antora hierarchy rule %r{#{MANUAL_GEN_DIR}/.*/.*/antora/modules/chapters/pages/.*\.adoc} do |t| parts = t.name.sub("#{MANUAL_GEN_DIR}/", "").split("/") - manual_name = parts[0] - version_name = parts[1] + parts[0] + parts[1] manual = cfg_arch_for("_").manual(parts[0]) manual_version = manual.version(parts[1]) chapter_name = File.basename(t.name, ".adoc") @@ -128,9 +128,7 @@ rule %r{#{MANUAL_GEN_DIR}/.*/.*/antora/nav.adoc} => proc { |tname| raise "Can't find any manual version for '#{parts[0]}' '#{parts[1]}'" if manual_version.nil? nav_template_path = $root / "backends" / "manual" / "templates" / "#{parts[0]}_nav.adoc.erb" - unless nav_template_path.exist? - raise "There is no navigation file for manual '#{parts[0]}' at '#{nav_template_path}'" - end + raise "There is no navigation file for manual '#{parts[0]}' at '#{nav_template_path}'" unless nav_template_path.exist? raise "no cfg_arch" if manual_version.cfg_arch.nil? @@ -298,8 +296,10 @@ end rule %r{#{MANUAL_GEN_DIR}/.*/top/.*/antora/landing/modules/ROOT/pages/index.adoc} => proc { |tname| parts = tname.sub("#{MANUAL_GEN_DIR}/", "").split("/") manual_name = parts[0] - versions, _ = versions_from_env(ENV["MANUAL_NAME"]) - version_files = Dir.glob($root / "arch" / "manual_version" / "**" / "*.yaml").select { |f| versions.include?(File.basename(f, ".yaml"))} + versions, = versions_from_env(ENV["MANUAL_NAME"]) + version_files = Dir.glob($root / "arch" / "manual_version" / "**" / "*.yaml").select do |f| + versions.include?(File.basename(f, ".yaml")) + end FileList[ __FILE__, ($root / "arch" / "manual" / "#{manual_name}.yaml").to_s, @@ -325,8 +325,10 @@ end rule %r{#{MANUAL_GEN_DIR}/.*/top/.*/antora/playbook/playbook.yml} => proc { |tname| parts = tname.sub("#{MANUAL_GEN_DIR}/", "").split("/") manual_name = parts[0] - versions, _ = versions_from_env(ENV["MANUAL_NAME"]) - version_files = Dir.glob($root / "arch" / "manual_version" / "**" / "*.yaml").select { |f| versions.include?(File.basename(f, ".yaml"))} + versions, = versions_from_env(ENV["MANUAL_NAME"]) + version_files = Dir.glob($root / "arch" / "manual_version" / "**" / "*.yaml").select do |f| + versions.include?(File.basename(f, ".yaml")) + end FileList[ __FILE__, ($root / "arch" / "manual" / "#{manual_name}.yaml").to_s, @@ -355,7 +357,7 @@ file $root / "ext" / "riscv-isa-manual" / "README.md" do end rule %r{#{MANUAL_GEN_DIR}/[^/]+/[^/]+/riscv-isa-manual/README.md} => ["#{$root}/ext/riscv-isa-manual/README.md"] do |t| - parts = t.name.sub("#{MANUAL_GEN_DIR}/","").split("/") + parts = t.name.sub("#{MANUAL_GEN_DIR}/", "").split("/") manual_version_name = parts[1] version_paths = Dir.glob("#{$root}/arch/manual_version/**/#{manual_version_name}.yaml") @@ -396,7 +398,10 @@ namespace :gen do DESC desc html_manual_desc task :html_manual do - raise ArgumentError, "Missing required environment variable MANUAL_NAME\n\n#{html_manual_desc}" if ENV["MANUAL_NAME"].nil? + if ENV["MANUAL_NAME"].nil? + raise ArgumentError, + "Missing required environment variable MANUAL_NAME\n\n#{html_manual_desc}" + end raise ArgumentError, "Missing required environment variable VERSIONS\n\n#{html_manual_desc}" if ENV["VERSIONS"].nil? versions, output_hash = versions_from_env(ENV["MANUAL_NAME"]) @@ -405,7 +410,6 @@ namespace :gen do manual = cfg_arch.manual(ENV["MANUAL_NAME"]) raise "No manual named '#{ENV['MANUAL_NAME']}" if manual.nil? - # check out the correct version of riscv-isa-manual, if needed versions.each do |version| version_obj = cfg_arch.manual_version(version) diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/gen_mcliciX.rb b/cfgs/qc_iu/arch_overlay/csr/Xqci/gen_mcliciX.rb index 11dd988a7..933406a4f 100644 --- a/cfgs/qc_iu/arch_overlay/csr/Xqci/gen_mcliciX.rb +++ b/cfgs/qc_iu/arch_overlay/csr/Xqci/gen_mcliciX.rb @@ -1,5 +1,4 @@ - -require 'erb' +require "erb" pend_template = <<~YAML # yaml-language-server: $schema=../../../../../schemas/csr_schema.json @@ -47,12 +46,12 @@ root = File.dirname(__FILE__) -erb = ERB.new(pend_template, trim_mode: '-') +erb = ERB.new(pend_template, trim_mode: "-") 8.times do |num| File.write("#{root}/qc_mclicip#{num}.yaml", erb.result(binding)) end -erb = ERB.new(en_template, trim_mode: '-') +erb = ERB.new(en_template, trim_mode: "-") 8.times do |num| File.write("#{root}/qc_mclicie#{num}.yaml", erb.result(binding)) end diff --git a/lib/arch_obj_models/certificate.rb b/lib/arch_obj_models/certificate.rb index 68d8a47cf..b667b3d73 100644 --- a/lib/arch_obj_models/certificate.rb +++ b/lib/arch_obj_models/certificate.rb @@ -34,7 +34,7 @@ def initialize(obj_yaml, yaml_path, arch: nil) # TODO: XXX: Add back in arch.name. # See https://github.com/riscv-software-src/riscv-unified-db/pull/371 - #puts "UPDATE: Creating CertModel object for #{name} using cfg #{cfg_arch.name}" + # puts "UPDATE: Creating CertModel object for #{name} using cfg #{cfg_arch.name}" puts "UPDATE: Creating CertModel object for #{name}" end @@ -47,15 +47,15 @@ def tsc_profile profile = cfg_arch.profile(@data["tsc_profile"]) - raise "No profile '#{@data["tsc_profile"]}'" if profile.nil? + raise "No profile '#{@data['tsc_profile']}'" if profile.nil? profile end # @return [CertClass] The certification class that this model belongs to. def cert_class - cert_class = @cfg_arch.ref(@data["class"]['$ref']) - raise "No certificate class named '#{@data["class"]}'" if cert_class.nil? + cert_class = @cfg_arch.ref(@data["class"]["$ref"]) + raise "No certificate class named '#{@data['class']}'" if cert_class.nil? cert_class end @@ -81,7 +81,7 @@ def when_pretty @data["when"].keys.map do |key| case key when "xlen" - "XLEN == #{@data["when"]["xlen"]}" + "XLEN == #{@data['when']['xlen']}" when "param" @data["when"]["param"].map do |param_name, param_value| "Parameter #{param_name} == #{param_value}" @@ -115,7 +115,7 @@ def when_pretty @data["when"].keys.map do |key| case key when "xlen" - "XLEN == #{@data["when"]["xlen"]}" + "XLEN == #{@data['when']['xlen']}" when "param" @data["when"]["param"].map do |param_name, param_value| "Parameter #{param_name} == #{param_value}" diff --git a/lib/arch_obj_models/csr.rb b/lib/arch_obj_models/csr.rb index 38bbc99e5..7ff31acc3 100644 --- a/lib/arch_obj_models/csr.rb +++ b/lib/arch_obj_models/csr.rb @@ -5,11 +5,9 @@ # CSR definition class Csr < DatabaseObject def ==(other) - if other.is_a?(Csr) - name == other.name - else - raise ArgumentError, "Csr is not comparable to #{other.class.name}" - end + raise ArgumentError, "Csr is not comparable to #{other.class.name}" unless other.is_a?(Csr) + + name == other.name end # @return [Integer] CSR address (the value passed as an immediate to csrrw, etc.) @@ -131,7 +129,7 @@ def dynamic_length?(cfg_arch) # @param cfg_arch [ConfiguredArchitecture] Architecture definition # @return [Integer] Smallest length of the CSR in any mode - def min_length(cfg_arch) + def min_length(_cfg_arch) case @data["length"] when "MXLEN", "SXLEN", "VSXLEN" 32 @@ -256,7 +254,7 @@ def length_cond64 # @param cfg_arch [ConfiguredArchitecture] A configuration # @return [String] Pretty-printed length string - def length_pretty(cfg_arch, effective_xlen=nil) + def length_pretty(cfg_arch, effective_xlen = nil) if dynamic_length?(cfg_arch) cond = case @data["length"] @@ -327,15 +325,14 @@ def implemented_fields_for(cfg_arch, effective_xlen) def implemented_fields(cfg_arch) return @implemented_fields unless @implemented_fields.nil? - implemented_bases = - if cfg_arch.param_values["SXLEN"] == 3264 || - cfg_arch.param_values["UXLEN"] == 3264 || - cfg_arch.param_values["VSXLEN"] == 3264 || - cfg_arch.param_values["VUXLEN"] == 3264 - [32, 64] - else - [cfg_arch.param_values["XLEN"]] - end + if cfg_arch.param_values["SXLEN"] == 3264 || + cfg_arch.param_values["UXLEN"] == 3264 || + cfg_arch.param_values["VSXLEN"] == 3264 || + cfg_arch.param_values["VUXLEN"] == 3264 + [32, 64] + else + [cfg_arch.param_values["XLEN"]] + end @implemented_fields = fields.select do |f| f.exists_in_cfg?(cfg_arch) @@ -411,7 +408,7 @@ def type_checked_sw_read_ast(symtab) symtab.add( "__expected_return_type", Idl::Type.new(:bits, width: 128) - ) + ) ast = sw_read_ast(symtab) symtab.cfg_arch.idl_compiler.type_check( @@ -513,22 +510,26 @@ def wavedrom_desc(cfg_arch, effective_xlen, exclude_unimplemented: false, option field_list.sort! { |a, b| a.location(cfg_arch, effective_xlen).min <=> b.location(cfg_arch, effective_xlen).min } field_list.each do |field| - if field.location(cfg_arch, effective_xlen).min != last_idx + 1 # have some reserved space n = field.location(cfg_arch, effective_xlen).min - last_idx - 1 - raise "negative reserved space? #{n} #{name} #{field.location(cfg_arch, effective_xlen).min} #{last_idx + 1}" if n <= 0 + if n <= 0 + raise "negative reserved space? #{n} #{name} #{field.location(cfg_arch, + effective_xlen).min} #{last_idx + 1}" + end desc["reg"] << { "bits" => n, type: 1 } end - if cfg_arch.partially_configured? && field.optional_in_cfg?(cfg_arch) - desc["reg"] << { "bits" => field.location(cfg_arch, effective_xlen).size, "name" => field.name, type: optional_type } - else - desc["reg"] << { "bits" => field.location(cfg_arch, effective_xlen).size, "name" => field.name, type: 3 } - end + desc["reg"] << if cfg_arch.partially_configured? && field.optional_in_cfg?(cfg_arch) + { "bits" => field.location(cfg_arch, effective_xlen).size, "name" => field.name, + type: optional_type } + else + { "bits" => field.location(cfg_arch, effective_xlen).size, "name" => field.name, type: 3 } + end last_idx = field.location(cfg_arch, effective_xlen).max end - if !field_list.empty? && (field_list.last.location(cfg_arch, effective_xlen).max != (length(cfg_arch, effective_xlen) - 1)) + if !field_list.empty? && (field_list.last.location(cfg_arch, + effective_xlen).max != (length(cfg_arch, effective_xlen) - 1)) # reserved space at the end desc["reg"] << { "bits" => (length(cfg_arch, effective_xlen) - 1 - last_idx), type: 1 } # desc['reg'] << { 'bits' => 1, type: 1 } diff --git a/lib/arch_obj_models/csr_field.rb b/lib/arch_obj_models/csr_field.rb index df4b53261..ba0a1e460 100644 --- a/lib/arch_obj_models/csr_field.rb +++ b/lib/arch_obj_models/csr_field.rb @@ -42,13 +42,19 @@ def exists_in_cfg?(cfg_arch) parent.exists_in_cfg?(cfg_arch) && (@data["base"].nil? || cfg_arch.possible_xlens.include?(@data["base"])) && - (@data["definedBy"].nil? || cfg_arch.prohibited_extensions.none? { |ext_req| ext_req.satisfying_versions.any? { |ext_ver| defined_by?(ext_ver) } }) + (@data["definedBy"].nil? || cfg_arch.prohibited_extensions.none? do |ext_req| + ext_req.satisfying_versions.any? do |ext_ver| + defined_by?(ext_ver) + end + end) end end # @return [Boolean] For a partially configured cfg_arch, whether or not the field is optional (not mandatory or prohibited) def optional_in_cfg?(cfg_arch) - raise "optional_in_cfg? should only be called on a partially configured cfg_arch" unless cfg_arch.partially_configured? + unless cfg_arch.partially_configured? + raise "optional_in_cfg? should only be called on a partially configured cfg_arch" + end exists_in_cfg?(cfg_arch) && ( @@ -162,7 +168,9 @@ def type(symtab) raise ArgumentError, "Argument 1 should be a symtab" unless symtab.is_a?(Idl::SymbolTable) unless @type_cache.nil? - raise "Different cfg_arch for type #{@type_cache.keys}, #{symtab.cfg_arch}" unless @type_cache.key?(symtab.cfg_arch) + unless @type_cache.key?(symtab.cfg_arch) + raise "Different cfg_arch for type #{@type_cache.keys}, #{symtab.cfg_arch}" + end return @type_cache[symtab.cfg_arch] end @@ -263,7 +271,7 @@ def reachable_functions(cfg_arch, effective_xlen) return @reachable_functions unless @reachable_functions.nil? symtab = - if (cfg_arch.configured?) + if cfg_arch.configured? cfg_arch.symtab else raise ArgumentError, "Must supply effective_xlen for generic ConfiguredArchitecture" if effective_xlen.nil? @@ -287,15 +295,11 @@ def reachable_functions(cfg_arch, effective_xlen) end if @data.key?("type()") ast = pruned_type_ast(symtab.deep_clone) - unless ast.nil? - fns.concat ast.reachable_functions(symtab.deep_clone.push(ast)) - end + fns.concat ast.reachable_functions(symtab.deep_clone.push(ast)) unless ast.nil? end if @data.key?("reset_value()") ast = pruned_reset_value_ast(symtab.deep_clone) - unless ast.nil? - fns.concat ast.reachable_functions(symtab.deep_clone.push(ast)) - end + fns.concat ast.reachable_functions(symtab.deep_clone.push(ast)) unless ast.nil? end @reachable_functions = fns.uniq @@ -311,21 +315,15 @@ def reachable_functions_unevaluated(symtab) fns = [] if has_custom_sw_write? ast = sw_write_ast(symtab) - unless ast.nil? - fns.concat ast.reachable_functions_unevaluated(symtab) - end + fns.concat ast.reachable_functions_unevaluated(symtab) unless ast.nil? end if @data.key?("type()") ast = type_ast(symtab) - unless ast.nil? - fns.concat ast.reachable_functions_unevaluated(symtab) - end + fns.concat ast.reachable_functions_unevaluated(symtab) unless ast.nil? end if @data.key?("reset_value()") ast = reset_value_ast(symtab) - unless ast.nil? - fns.concat ast.reachable_functions_unevalutated(symtab) - end + fns.concat ast.reachable_functions_unevalutated(symtab) unless ast.nil? end @reachable_functions_unevaluated = fns.uniq @@ -449,7 +447,7 @@ def reset_value(cfg_arch) def dynamic_reset_value?(cfg_arch) return false unless @data["reset_value"].nil? - value_result = Idl::AstNode.value_try do + Idl::AstNode.value_try do reset_value(cfg_arch) false end || true @@ -570,7 +568,6 @@ def pruned_sw_write_ast(cfg_arch, effective_xlen) ast.freeze_tree(cfg_arch.symtab) - cfg_arch.idl_compiler.type_check( ast, symtab, @@ -591,7 +588,12 @@ def location(cfg_arch, effective_xlen = nil) if @data.key?("location") "location" else - raise ArgumentError, "The location of #{csr.name}.#{name} changes with XLEN, so effective_xlen must be provided" unless [32, 64].include?(effective_xlen) + unless [ + 32, 64 + ].include?(effective_xlen) + raise ArgumentError, + "The location of #{csr.name}.#{name} changes with XLEN, so effective_xlen must be provided" + end "location_rv#{effective_xlen}" end @@ -606,7 +608,8 @@ def location(cfg_arch, effective_xlen = nil) raise "Location (#{key} = #{@data[key]}) is past the max csr length (#{csr.max_length(cfg_arch)}) in #{csr.name}.#{name}" end elsif @data[key] > csr_length - raise "Location (#{key} = #{@data[key]}) is past the csr length (#{csr.length(cfg_arch, effective_xlen)}) in #{csr.name}.#{name}" + raise "Location (#{key} = #{@data[key]}) is past the csr length (#{csr.length(cfg_arch, + effective_xlen)}) in #{csr.name}.#{name}" end @data[key]..@data[key] diff --git a/lib/arch_obj_models/extension.rb b/lib/arch_obj_models/extension.rb index 120154da7..6988ca075 100644 --- a/lib/arch_obj_models/extension.rb +++ b/lib/arch_obj_models/extension.rb @@ -50,7 +50,9 @@ def initialize(ext, name, data) unless data["also_defined_in"].nil? if data["also_defined_in"].is_a?(String) other_ext = @arch.extension(data["also_defined_in"]) - raise "Definition error in #{ext.name}.#{name}: #{data['also_defined_in']} is not a known extension" if other_ext.nil? + if other_ext.nil? + raise "Definition error in #{ext.name}.#{name}: #{data['also_defined_in']} is not a known extension" + end also_defined_in << other_ext else @@ -60,7 +62,9 @@ def initialize(ext, name, data) data["also_defined_in"].each do |other_ext_name| other_ext = @arch.extension(other_ext_name) - raise "Definition error in #{ext.name}.#{name}: #{data['also_defined_in']} is not a known extension" if other_ext.nil? + if other_ext.nil? + raise "Definition error in #{ext.name}.#{name}: #{data['also_defined_in']} is not a known extension" + end also_defined_in << other_ext end @@ -95,7 +99,10 @@ def name_potentially_with_link(exts) # sorts by name def <=>(other) - raise ArgumentError, "ExtensionParameters are only comparable to other extension parameters" unless other.is_a?(ExtensionParameter) + unless other.is_a?(ExtensionParameter) + raise ArgumentError, + "ExtensionParameters are only comparable to other extension parameters" + end @name <=> other.name end @@ -197,7 +204,8 @@ def implies(version_requirement = nil) if version_requirement.nil? return [] unless ExtensionRequirement.new(@new, arch: @arch).satisfied_by?(max_version.version) else - return [] unless ExtensionRequirement.new(@new, version_requirement, arch: @arch).satisfied_by?(max_version.version) + return [] unless ExtensionRequirement.new(@new, version_requirement, + arch: @arch).satisfied_by?(max_version.version) end max_version.implications @@ -222,7 +230,7 @@ def conflicts end end else - raise "Invalid conflicts data: #{@data["conflicts"].inspect}" + raise "Invalid conflicts data: #{@data['conflicts'].inspect}" end end @@ -230,7 +238,7 @@ def conflicts def instructions return @instructions unless @instructions.nil? - @instructions = arch.instructions.select { |i| versions.any? { |v| i.defined_by?(v) }} + @instructions = arch.instructions.select { |i| versions.any? { |v| i.defined_by?(v) } } end # @return [Array] the list of CSRs implemented by *any version* of this extension (may be empty) @@ -260,6 +268,7 @@ def reachable_functions(symtab) # The one place in this file that needs a ConfiguredArchitecture object instead of just Architecture. raise "In #{name}, need to provide ConfiguredArchitecture" if cfg_arch.nil? + csrs.each do |csr| funcs += csr.reachable_functions(cfg_arch) end @@ -291,6 +300,7 @@ def initialize(name, version_str, arch, fail_if_version_does_not_exist: false) @version_spec = VersionSpec.new(version_str) raise ArgumentError, "Must supply arch" if arch.nil? + @arch = arch @ext = @arch.extension(@name) @@ -527,28 +537,24 @@ def implemented_instructions # Example => presence: # optional: development class ExtensionPresence - attr_reader :presence - attr_reader :optional_type + attr_reader :presence, :optional_type # @param data [Hash, String] The presence data from the architecture spec def initialize(data) if data.is_a?(String) - raise "Unknown extension presence of #{data}" unless ["mandatory","optional"].include?(data) + raise "Unknown extension presence of #{data}" unless ["mandatory", "optional"].include?(data) @presence = data @optional_type = nil elsif data.is_a?(Hash) data.each do |key, value| - if key == "optional" - raise ArgumentError, "Extension presence hash #{data} missing type of optional" if value.nil? - raise ArgumentError, "Unknown extension presence optional #{value} for type of optional" unless - ["localized", "development", "expansion", "transitory"].include?(value) + raise ArgumentError, "Extension presence hash #{data} has unsupported key of #{key}" unless key == "optional" + raise ArgumentError, "Extension presence hash #{data} missing type of optional" if value.nil? + raise ArgumentError, "Unknown extension presence optional #{value} for type of optional" unless + ["localized", "development", "expansion", "transitory"].include?(value) - @presence = key - @optional_type = value - else - raise ArgumentError, "Extension presence hash #{data} has unsupported key of #{key}" - end + @presence = key + @optional_type = value end else raise ArgumentError, "Extension presence is a #{data.class} but only String or Hash are supported" @@ -567,11 +573,13 @@ def self.optional_type_expansion = "expansion" def self.optional_type_transitory = "transitory" def self.presence_types = [mandatory, optional] + def self.optional_types = [ - optional_type_localized, - optional_type_development, - optional_type_expansion, - optional_type_transitory] + optional_type_localized, + optional_type_development, + optional_type_expansion, + optional_type_transitory + ] def self.presence_types_obj return @presence_types_obj unless @presence_types_obj.nil? @@ -591,7 +599,7 @@ def self.optional_types_obj @optional_types_obj = [] optional_types.each do |optional_type| - @optional_types_obj << ExtensionPresence.new({ self.optional => optional_type }) + @optional_types_obj << ExtensionPresence.new({ optional => optional_type }) end @optional_types_obj @@ -633,15 +641,23 @@ def ==(other) # @param other [ExtensionPresence] An extension presence object # @return [Boolean] Whether or not this ExtensionPresence is greater-than the other def >(other) - raise ArgumentError, "ExtensionPresence is only comparable to other ExtensionPresence classes" unless other.is_a?(ExtensionPresence) - (self.mandatory? && other.optional?) + unless other.is_a?(ExtensionPresence) + raise ArgumentError, + "ExtensionPresence is only comparable to other ExtensionPresence classes" + end + + mandatory? && other.optional? end # @overload >=(other) # @param other [ExtensionPresence] An extension presence object # @return [Boolean] Whether or not this ExtensionPresence is greater-than or equal to the other def >=(other) - raise ArgumentError, "ExtensionPresence is only comparable to other ExtensionPresence classes" unless other.is_a?(ExtensionPresence) + unless other.is_a?(ExtensionPresence) + raise ArgumentError, + "ExtensionPresence is only comparable to other ExtensionPresence classes" + end + (self > other) || (self == other) end @@ -649,15 +665,23 @@ def >=(other) # @param other [ExtensionPresence] An extension presence object # @return [Boolean] Whether or not this ExtensionPresence is less-than the other def <(other) - raise ArgumentError, "ExtensionPresence is only comparable to other ExtensionPresence classes" unless other.is_a?(ExtensionPresence) - (self.optional? && other.mandatory?) + unless other.is_a?(ExtensionPresence) + raise ArgumentError, + "ExtensionPresence is only comparable to other ExtensionPresence classes" + end + + optional? && other.mandatory? end # @overload <=(other) # @param other [ExtensionPresence] An extension presence object # @return [Boolean] Whether or not this ExtensionPresence is less-than or equal to the other def <=(other) - raise ArgumentError, "ExtensionPresence is only comparable to other ExtensionPresence classes" unless other.is_a?(ExtensionPresence) + unless other.is_a?(ExtensionPresence) + raise ArgumentError, + "ExtensionPresence is only comparable to other ExtensionPresence classes" + end + (self < other) || (self == other) end end @@ -780,7 +804,10 @@ def csrs # sorts by name def <=>(other) - raise ArgumentError, "ExtensionRequirements are only comparable to other extension requirements" unless other.is_a?(ExtensionRequirement) + unless other.is_a?(ExtensionRequirement) + raise ArgumentError, + "ExtensionRequirements are only comparable to other extension requirements" + end @name <=> other.name end diff --git a/lib/arch_obj_models/instruction.rb b/lib/arch_obj_models/instruction.rb index 077a60f6c..e1b5961c0 100644 --- a/lib/arch_obj_models/instruction.rb +++ b/lib/arch_obj_models/instruction.rb @@ -1,10 +1,9 @@ # frozen_string_literal: true -require 'ruby-prof-flamegraph' +require "ruby-prof-flamegraph" require_relative "obj" - # model of a specific instruction in a specific base (RV32/RV64) class Instruction < DatabaseObject def self.ary_from_location(location_str_or_int) @@ -37,13 +36,10 @@ def self.validate_encoding(encoding, inst_name) elsif vars_match != 1 raise ValidationError, "In instruction, #{inst_name}, bit #{i} is covered by more than one variable" end - else + elsif !variables.nil? && !variables.none? { |variable| ary_from_location(variable["location"]).include?(i) } # make sure no variable covers this bit - unless variables.nil? - unless variables.none? { |variable| ary_from_location(variable["location"]).include?(i) } - raise ValidationError, "In instruction, #{inst_name}, bit #{i} is covered by both a variable and the match string" - end - end + raise ValidationError, + "In instruction, #{inst_name}, bit #{i} is covered by both a variable and the match string" end end end @@ -60,21 +56,17 @@ def validate end def ==(other) - if other.is_a?(Instruction) - name == other.name - else - raise ArgumentError, "Instruction is not comparable to a #{other.class.name}" - end + raise ArgumentError, "Instruction is not comparable to a #{other.class.name}" unless other.is_a?(Instruction) + + name == other.name end alias eql? == def <=>(other) - if other.is_a?(Instruction) - name <=> other.name - else - raise ArgumentError, "Instruction is not comparable to a #{other.class.name}" - end + raise ArgumentError, "Instruction is not comparable to a #{other.class.name}" unless other.is_a?(Instruction) + + name <=> other.name end # @return [Hash] Hash of access permissions for each mode. The key is the lowercase name of a privilege mode, and the value is one of ['never', 'sometimes', 'always'] @@ -113,7 +105,8 @@ def fill_symtab(global_symtab, effective_xlen, ast) symtab.push(ast) symtab.add( "__instruction_encoding_size", - Idl::Var.new("__instruction_encoding_size", Idl::Type.new(:bits, width: encoding_width.bit_length), encoding_width) + Idl::Var.new("__instruction_encoding_size", Idl::Type.new(:bits, width: encoding_width.bit_length), + encoding_width) ) symtab.add( "__effective_xlen", @@ -196,9 +189,7 @@ def mask_to_array(int) elems = [] idx = 0 while int != 0 - if (int & (1 << idx)) != 0 - elems << idx - end + elems << idx if (int & (1 << idx)) != 0 int &= ~(1 << idx) idx += 1 end @@ -208,7 +199,7 @@ def mask_to_array(int) # @param symtab [Idl::SymbolTable] Symbol table with global scope populated # @param effective_xlen [Integer] Effective XLEN to evaluate against. If nil, evaluate against all valid XLENs # @return [Array] List of all exceptions that can be reached from operation() - def reachable_exceptions_str(symtab, effective_xlen=nil) + def reachable_exceptions_str(symtab, effective_xlen = nil) if @data["operation()"].nil? [] else @@ -219,24 +210,24 @@ def reachable_exceptions_str(symtab, effective_xlen=nil) ( pruned_ast = pruned_operation_ast(symtab, 32) print "Determining reachable exceptions from #{name}#RV32..." - e32 = mask_to_array(pruned_ast.reachable_exceptions(fill_symtab(symtab, 32, pruned_ast))).map { |code| + e32 = mask_to_array(pruned_ast.reachable_exceptions(fill_symtab(symtab, 32, pruned_ast))).map do |code| etype.element_name(code) - } + end puts "done" pruned_ast = pruned_operation_ast(symtab, 64) print "Determining reachable exceptions from #{name}#RV64..." - e64 = mask_to_array(pruned_ast.reachable_exceptions(fill_symtab(symtab, 64, pruned_ast))).map { |code| + e64 = mask_to_array(pruned_ast.reachable_exceptions(fill_symtab(symtab, 64, pruned_ast))).map do |code| etype.element_name(code) - } + end puts "done" e32 + e64 ).uniq else pruned_ast = pruned_operation_ast(symtab, base) print "Determining reachable exceptions from #{name}..." - e = mask_to_array(pruned_ast.reachable_exceptions(fill_symtab(symtab, base, pruned_ast))).map { |code| + e = mask_to_array(pruned_ast.reachable_exceptions(fill_symtab(symtab, base, pruned_ast))).map do |code| etype.element_name(code) - } + end puts "done" e end @@ -244,9 +235,10 @@ def reachable_exceptions_str(symtab, effective_xlen=nil) effective_xlen = symtab.cfg_arch.mxlen pruned_ast = pruned_operation_ast(symtab, effective_xlen) print "Determining reachable exceptions from #{name}..." - e = mask_to_array(pruned_ast.reachable_exceptions(fill_symtab(symtab, effective_xlen, pruned_ast))).map { |code| + e = mask_to_array(pruned_ast.reachable_exceptions(fill_symtab(symtab, effective_xlen, + pruned_ast))).map do |code| etype.element_name(code) - } + end puts "done" e end @@ -254,9 +246,10 @@ def reachable_exceptions_str(symtab, effective_xlen=nil) pruned_ast = pruned_operation_ast(symtab, effective_xlen) print "Determining reachable exceptions from #{name}..." - e = mask_to_array(pruned_ast.reachable_exceptions(fill_symtab(symtab, effective_xlen, pruned_ast))).map { |code| + e = mask_to_array(pruned_ast.reachable_exceptions(fill_symtab(symtab, effective_xlen, + pruned_ast))).map do |code| etype.element_name(code) - } + end puts "done" e end @@ -284,7 +277,6 @@ def opcode? name.match?(/^[01]+$/) end - def eql?(other) @name == other.name && @range == other.range end @@ -383,17 +375,19 @@ def inst_range_to_var_range(r) var_bits = inst_pos_to_var_pos raise "?" if var_bits[r.last].nil? + parts = [var_bits[r.last]..var_bits[r.last]] r.to_a.reverse[1..].each do |i| if var_bits[i] == (parts.last.min - 1) raise "??" if parts.last.max.nil? + parts[-1] = var_bits[i]..parts.last.max else parts << Range.new(var_bits[i], var_bits[i]) end end - parts.map { |p| p.size == 1 ? p.first.to_s : "#{p.last}:#{p.first}"}.join("|") + parts.map { |p| p.size == 1 ? p.first.to_s : "#{p.last}:#{p.first}" }.join("|") end private :inst_range_to_var_range diff --git a/lib/arch_obj_models/manual.rb b/lib/arch_obj_models/manual.rb index 396cb041d..59677c59c 100644 --- a/lib/arch_obj_models/manual.rb +++ b/lib/arch_obj_models/manual.rb @@ -49,9 +49,7 @@ def fullpath @repo_path / @path end - def repo_path=(path) - @repo_path = path - end + attr_writer :repo_path # @return [Pathname] The relative path to the chapter attr_reader :path diff --git a/lib/arch_obj_models/obj.rb b/lib/arch_obj_models/obj.rb index d26cff70d..f966051d3 100644 --- a/lib/arch_obj_models/obj.rb +++ b/lib/arch_obj_models/obj.rb @@ -42,7 +42,6 @@ def initialize(result) # exception raised when an object does not validate against its schema class SchemaValidationError < ::StandardError - # result from JsonSchemer.validate attr_reader :result @@ -95,11 +94,11 @@ def initialize(path, result) # @return [Architecture] If only a specification (no config) is known # @return [ConfiguredArchitecture] If a specification and config is known # @return [nil] If neither is known - attr_reader :arch # Use when Architecture class is sufficient + attr_reader :arch # Use when Architecture class is sufficient # @return [ConfiguredArchitecture] If a specification and config is known # @return [nil] Otherwise - attr_reader :cfg_arch # Use when extra stuff provided by ConfiguredArchitecture is required + attr_reader :cfg_arch # Use when extra stuff provided by ConfiguredArchitecture is required def kind = @data["kind"] @@ -191,9 +190,7 @@ def initialize(data, data_path, arch: nil) @data = data @data_path = data_path - if arch.is_a?(ConfiguredArchitecture) - @cfg_arch = arch - end + @cfg_arch = arch if arch.is_a?(ConfiguredArchitecture) @arch = arch @name = data["name"] @long_name = data["long_name"] @@ -276,10 +273,10 @@ def primary_defined_by # misa_csr.source_line("sw_read()") #=> 2 # mis_csr.source_line("fields", "A", "type()") #=> 5 def source_line(*path) - # find the line number of this operation() in the *original* file yaml_filename = @data["$source"] raise "No $source for #{name}" if yaml_filename.nil? + line = nil path_idx = 0 Psych.parse_stream(File.read(yaml_filename), filename: yaml_filename) do |doc| @@ -490,8 +487,8 @@ def first_requirement(req = @hsh) # combine all conds into one using AND def self.all_of(*conds, arch:) cond = SchemaCondition.new({ - "allOf" => conds - }, arch) + "allOf" => conds + }, arch) SchemaCondition.new(cond.minimize, arch) end @@ -531,27 +528,29 @@ def to_rb_helper(hsh) if hsh.key?("name") if hsh.key?("version") if hsh["version"].is_a?(String) - "(yield ExtensionRequirement.new('#{hsh["name"]}', '#{hsh["version"]}', arch: @arch))" + "(yield ExtensionRequirement.new('#{hsh['name']}', '#{hsh['version']}', arch: @arch))" elsif hsh["version"].is_a?(Array) - "(yield ExtensionRequirement.new('#{hsh["name"]}', #{hsh["version"].map { |v| "'#{v}'" }.join(', ')}, arch: @arch))" + "(yield ExtensionRequirement.new('#{hsh['name']}', #{hsh['version'].map do |v| + "'#{v}'" + end.join(', ')}, arch: @arch))" else raise "unexpected" end else - "(yield ExtensionRequirement.new('#{hsh["name"]}', arch: @arch))" + "(yield ExtensionRequirement.new('#{hsh['name']}', arch: @arch))" end else key = hsh.keys[0] case key when "allOf" - rb_str = hsh[key].map { |element| to_rb_helper(element) }.join(' && ') + rb_str = hsh[key].map { |element| to_rb_helper(element) }.join(" && ") "(#{rb_str})" when "anyOf" - rb_str = hsh[key].map { |element| to_rb_helper(element) }.join(' || ') + rb_str = hsh[key].map { |element| to_rb_helper(element) }.join(" || ") "(#{rb_str})" when "oneOf" - rb_str = hsh[key].map { |element| to_rb_helper(element) }.join(', ') + rb_str = hsh[key].map { |element| to_rb_helper(element) }.join(", ") "([#{rb_str}].count(true) == 1)" else raise "Unexpected" diff --git a/lib/arch_obj_models/portfolio.rb b/lib/arch_obj_models/portfolio.rb index 5c5747f20..86bfb8f38 100644 --- a/lib/arch_obj_models/portfolio.rb +++ b/lib/arch_obj_models/portfolio.rb @@ -37,8 +37,9 @@ def eql?(other) # @return [Array greatest_presence - greatest_presence = presence - end + next unless ext_req.satisfied_by?(v) + + presence = extension_presence_obj(ext_name) + + unless presence.nil? + if greatest_presence.nil? + greatest_presence = presence + elsif presence > greatest_presence + greatest_presence = presence end end end @@ -120,7 +121,7 @@ def extension_note(ext_name) ext_data = @data["extensions"][ext_name] raise "Cannot find extension named #{ext_name}" if ext_data.nil? - return ext_data["note"] unless ext_data.nil? + ext_data["note"] unless ext_data.nil? end def mandatory_ext_reqs = in_scope_ext_reqs(ExtensionPresence.mandatory) @@ -136,10 +137,15 @@ def in_scope_ext_reqs(desired_presence = nil) # Convert desired_present argument to ExtensionPresence object if not nil. desired_presence_converted = - desired_presence.nil? ? nil : - desired_presence.is_a?(String) ? desired_presence : - desired_presence.is_a?(ExtensionPresence) ? desired_presence : - ExtensionPresence.new(desired_presence) + if desired_presence.nil? + nil + elsif desired_presence.is_a?(String) + desired_presence + elsif desired_presence.is_a?(ExtensionPresence) + desired_presence + else + ExtensionPresence.new(desired_presence) + end missing_ext = false @@ -153,7 +159,7 @@ def in_scope_ext_reqs(desired_presence = nil) puts "Extension #{ext_name} for #{name} not found in database" missing_ext = true else - actual_presence = ext_data["presence"] # Could be a String or Hash + actual_presence = ext_data["presence"] # Could be a String or Hash raise "Missing extension presence for extension #{ext_name}" if actual_presence.nil? # Convert presence String or Hash to object. @@ -171,11 +177,13 @@ def in_scope_ext_reqs(desired_presence = nil) if ext_data.key?("version") ExtensionRequirement.new( ext_name, ext_data["version"], arch: @arch, - presence: actual_presence_obj, note: ext_data["note"], req_id: "REQ-EXT-#{ext_name}") + presence: actual_presence_obj, note: ext_data["note"], req_id: "REQ-EXT-#{ext_name}" + ) else ExtensionRequirement.new( ext_name, arch: @arch, - presence: actual_presence_obj, note: ext_data["note"], req_id: "REQ-EXT-#{ext_name}") + presence: actual_presence_obj, note: ext_data["note"], req_id: "REQ-EXT-#{ext_name}" + ) end end end @@ -203,7 +211,7 @@ def in_scope_extensions @in_scope_extensions = in_scope_ext_reqs.map do |ext_req| arch.extension(ext_req.name) - end.reject(&:nil?) # Filter out extensions that don't exist yet. + end.reject(&:nil?) # Filter out extensions that don't exist yet. @in_scope_extensions end @@ -217,9 +225,7 @@ def uses_optional_types? # Iterate through different kinds of optional using the "object" version (not the string version). ExtensionPresence.optional_types_obj.each do |optional_type_obj| # See if any extension reqs have this type of optional. - unless in_scope_ext_reqs(optional_type_obj).empty? - @uses_optional_types = true - end + @uses_optional_types = true unless in_scope_ext_reqs(optional_type_obj).empty? end @uses_optional_types @@ -264,8 +270,7 @@ def to_cfg_arch # Holds extension parameter information from the portfolio. class InScopeExtensionParameter - attr_reader :param # ExtensionParameter object (from the architecture database) - attr_reader :note + attr_reader :param, :note # ExtensionParameter object (from the architecture database) def initialize(param, schema_hash, note) raise ArgumentError, "Expecting ExtensionParameter" unless param.is_a?(ExtensionParameter) @@ -293,7 +298,7 @@ def value # @return [String] - # What parameter values are allowed by the portfolio. def allowed_values - if (@schema_portfolio.empty?) + if @schema_portfolio.empty? # Portfolio doesn't add any constraints on parameter's value. return "Any" end @@ -308,8 +313,10 @@ def allowed_values # sorts by name def <=>(other) - raise ArgumentError, - "InScopeExtensionParameter are only comparable to other parameter constraints" unless other.is_a?(InScopeExtensionParameter) + unless other.is_a?(InScopeExtensionParameter) + raise ArgumentError, + "InScopeExtensionParameter are only comparable to other parameter constraints" + end @param.name <=> other.param.name end end # class InScopeExtensionParameter @@ -356,7 +363,7 @@ def all_in_scope_ext_params def in_scope_ext_params(ext_req) raise ArgumentError, "Expecting ExtensionRequirement" unless ext_req.is_a?(ExtensionRequirement) - ext_params = [] # Local variable, no caching + ext_params = [] # Local variable, no caching # Get extension information from portfolio YAML for passed in extension requirement. ext_data = @data["extensions"][ext_req.name] @@ -408,7 +415,7 @@ def all_out_of_scope_params # @return [Array] Parameters that are out of scope for named extension. def out_of_scope_params(ext_name) - all_out_of_scope_params.select{ |param| param.exts.any? { |ext| ext.name == ext_name } } + all_out_of_scope_params.select { |param| param.exts.any? { |ext| ext.name == ext_name } } end # @return [Array] @@ -446,7 +453,7 @@ def all_in_scope_exts_with_param(param) def all_in_scope_exts_without_param(param) raise ArgumentError, "Expecting ExtensionParameter" unless param.is_a?(ExtensionParameter) - exts = [] # Local variable, no caching + exts = [] # Local variable, no caching # Iterate through all the extensions in the architecture database that define this parameter. param.exts.each do |ext| @@ -460,8 +467,8 @@ def all_in_scope_exts_without_param(param) end if found - # Only add extensions that are in-scope (i.e., exist in this portfolio). - exts << ext + # Only add extensions that are in-scope (i.e., exist in this portfolio). + exts << ext end end @@ -507,7 +514,8 @@ def initialize(data) @presence_obj = ExtensionPresence.new(@data["presence"]) end - def presence_obj = @presence_obj + attr_reader :presence_obj + def text = @data["text"] end @@ -525,9 +533,12 @@ def extra_notes # @return [String] Note for desired_presence # @return [nil] No note for desired_presence def extra_notes_for_presence(desired_presence_obj) - raise ArgumentError, "Expecting ExtensionPresence but got a #{desired_presence_obj.class}" unless desired_presence_obj.is_a?(ExtensionPresence) + unless desired_presence_obj.is_a?(ExtensionPresence) + raise ArgumentError, + "Expecting ExtensionPresence but got a #{desired_presence_obj.class}" + end - extra_notes.select {|extra_note| extra_note.presence_obj == desired_presence_obj} + extra_notes.select { |extra_note| extra_note.presence_obj == desired_presence_obj } end ########################### diff --git a/lib/arch_obj_models/profile.rb b/lib/arch_obj_models/profile.rb index 05acb3433..dedde6001 100644 --- a/lib/arch_obj_models/profile.rb +++ b/lib/arch_obj_models/profile.rb @@ -37,9 +37,9 @@ def profile_releases_matching_processor_kind matching_classes = portfolio_classes_matching_portfolio_kind_and_processor_kind # Look for all profile releases that are from any of the matching classes. - @profile_releases_matching_processor_kind = @cfg_arch.profile_releases.select { |pr| + @profile_releases_matching_processor_kind = @cfg_arch.profile_releases.select do |pr| matching_classes.any? { |matching_class| matching_class.name == pr.profile_class.name } - } + end @profile_releases_matching_processor_kind end @@ -48,14 +48,16 @@ def profile_releases_matching_processor_kind def profiles return @profiles unless @profiles.nil? - @profiles = @cfg_arch.profiles.select {|profile| profile.profile_class.name == name} + @profiles = @cfg_arch.profiles.select { |profile| profile.profile_class.name == name } end # @return [Array] All profiles in database matching my processor kind def profiles_matching_processor_kind return @profiles_matching_processor_kind unless @profiles_matching_processor_kind.nil? - @profiles_matching_processor_kind = @cfg_arch.profiles.select {|profile| profile.profile_class.processor_kind == processor_kind} + @profiles_matching_processor_kind = @cfg_arch.profiles.select do |profile| + profile.profile_class.processor_kind == processor_kind + end end # @return [Array] List of all extensions referenced by the profile class @@ -117,7 +119,7 @@ def contributors # @return [ProfileClass] Profile Class that this ProfileRelease belongs to def profile_class profile_class = @cfg_arch.profile_class(@data["class"]) - raise "No profile class named '#{@data["class"]}'" if profile_class.nil? + raise "No profile class named '#{@data['class']}'" if profile_class.nil? profile_class end @@ -177,7 +179,7 @@ def marketing_name = @data["marketing_name"] # @return [ProfileRelease] The profile release this profile belongs to def profile_release profile_release = @cfg_arch.ref(@data["release"]["$ref"]) - raise "No profile release named '#{@data["release"]["$ref"]}'" if profile_release.nil? + raise "No profile release named '#{@data['release']['$ref']}'" if profile_release.nil? profile_release end @@ -206,7 +208,7 @@ def extensions_to_adoc(presence_type, heading_level) ret = [] presence_ext_reqs = in_scope_ext_reqs(presence_type) - plural = (presence_ext_reqs.size == 1) ? "" : "s" + plural = presence_ext_reqs.size == 1 ? "" : "s" ret << "The #{marketing_name} Profile has #{presence_ext_reqs.size} #{presence_type} extension#{plural}." ret << "" diff --git a/lib/arch_obj_models/schema.rb b/lib/arch_obj_models/schema.rb index ff11d6b94..5de19b6c6 100644 --- a/lib/arch_obj_models/schema.rb +++ b/lib/arch_obj_models/schema.rb @@ -1,4 +1,4 @@ -#frozen_string_literal: true +# frozen_string_literal: true require_relative "../idl/type" @@ -7,157 +7,155 @@ # Used when an object in the database specifies a constraint using JSON schema # For example, extension parameters class Schema - def initialize(schema_hash) - raise ArgumentError, "Expecting hash" unless schema_hash.is_a?(Hash) - - @schema_hash = schema_hash - end - - # @return [Hash] Hash representation of the JSON Schema - def to_h = @schema_hash - - # @return [String] Human-readable type of the schema (e.g., array, string, integer) - def type_pretty - @schema_hash["type"] - end - - # @return [String] A human-readable description of the schema - def to_pretty_s(schema_hash = @schema_hash) - raise ArgumentError, "Expecting hash" unless schema_hash.is_a?(Hash) - raise ArgumentError, "Expecting non-empty hash" if schema_hash.empty? - - if schema_hash.key?("const") - large2hex(schema_hash["const"]) - elsif schema_hash.key?("enum") - "[#{schema_hash["enum"].join(', ')}]" - elsif schema_hash.key?("type") - case schema_hash["type"] - when "integer" - min = schema_hash["minimum"] - minstr = large2hex(min) - max = schema_hash["maximum"] - maxstr = large2hex(max) - if min && max - sz = num_bits(min, max) - (sz > 0) ? "#{sz}-bit integer" : "#{minstr} to #{maxstr}" - elsif min - "≥ #{minstr}" - elsif max - "≤ #{maxstr}" - else - "integer" - end - when "string" - format = schema_hash["format"] - pattern = schema_hash["pattern"] - if format - format - elsif pattern - "string matching #{pattern}" - else - "string" - end - when "boolean" - "boolean" - when "array" - items = schema_hash["items"] - min_items = schema_hash["minItems"] - max_items = schema_hash["maxItems"] - size_str = if min_items && max_items - if min_items == max_items - "#{min_items}-element " - else - "#{min_items}-element to #{max_items}-element " - end - elsif min_items - "at least #{min_items}-element " - elsif max_items - "at most #{max_items}-element " - else - "" - end - - array_str = if items.nil? - size_str + "array" - else - if items.is_a?(Hash) - "#{size_str}array of #{to_pretty_s(items)}" - elsif items.is_a?(Array) - str = size_str + "array where: +\n" - items.each_with_index do |item,index| - str = str + "  [#{index}] is #{to_pretty_s(item)} +\n" - end - additional_items = schema_hash["additionalItems"] - if additional_items - str = str + "additional items are: +\n  " + - to_pretty_s(additional_items) - end - str - else - raise "to_pretty_s unknown array items #{items} in #{schema_hash}" - end - end - - if schema_hash.key?("contains") - array_str = array_str + " Contains : [#{to_pretty_s(schema_hash["contains"])}]" - end - - array_str + def initialize(schema_hash) + raise ArgumentError, "Expecting hash" unless schema_hash.is_a?(Hash) + + @schema_hash = schema_hash + end + + # @return [Hash] Hash representation of the JSON Schema + def to_h = @schema_hash + + # @return [String] Human-readable type of the schema (e.g., array, string, integer) + def type_pretty + @schema_hash["type"] + end + + # @return [String] A human-readable description of the schema + def to_pretty_s(schema_hash = @schema_hash) + raise ArgumentError, "Expecting hash" unless schema_hash.is_a?(Hash) + raise ArgumentError, "Expecting non-empty hash" if schema_hash.empty? + + if schema_hash.key?("const") + large2hex(schema_hash["const"]) + elsif schema_hash.key?("enum") + "[#{schema_hash['enum'].join(', ')}]" + elsif schema_hash.key?("type") + case schema_hash["type"] + when "integer" + min = schema_hash["minimum"] + minstr = large2hex(min) + max = schema_hash["maximum"] + maxstr = large2hex(max) + if min && max + sz = num_bits(min, max) + sz > 0 ? "#{sz}-bit integer" : "#{minstr} to #{maxstr}" + elsif min + "≥ #{minstr}" + elsif max + "≤ #{maxstr}" + else + "integer" + end + when "string" + format = schema_hash["format"] + pattern = schema_hash["pattern"] + if format + format + elsif pattern + "string matching #{pattern}" else - raise "to_pretty_s unknown type #{schema_hash["type"]} in #{schema_hash}" + "string" end + when "boolean" + "boolean" + when "array" + items = schema_hash["items"] + min_items = schema_hash["minItems"] + max_items = schema_hash["maxItems"] + size_str = if min_items && max_items + if min_items == max_items + "#{min_items}-element " + else + "#{min_items}-element to #{max_items}-element " + end + elsif min_items + "at least #{min_items}-element " + elsif max_items + "at most #{max_items}-element " + else + "" + end + + array_str = if items.nil? + size_str + "array" + elsif items.is_a?(Hash) + "#{size_str}array of #{to_pretty_s(items)}" + elsif items.is_a?(Array) + str = size_str + "array where: +\n" + items.each_with_index do |item, index| + str += "  [#{index}] is #{to_pretty_s(item)} +\n" + end + additional_items = schema_hash["additionalItems"] + if additional_items + str = str + "additional items are: +\n  " + + to_pretty_s(additional_items) + end + str + else + raise "to_pretty_s unknown array items #{items} in #{schema_hash}" + end + + array_str += " Contains : [#{to_pretty_s(schema_hash['contains'])}]" if schema_hash.key?("contains") + + array_str else - raise "Unsupported schema for #{schema_hash}" + raise "to_pretty_s unknown type #{schema_hash['type']} in #{schema_hash}" end + else + raise "Unsupported schema for #{schema_hash}" end - - # Convert large integers to hex str. - def large2hex(value) - if value.nil? - "" - elsif value.is_a?(Integer) - (value > 999) ? "0x" + value.to_s(16) : value.to_s - else - value.to_s - end + end + + # Convert large integers to hex str. + def large2hex(value) + if value.nil? + "" + elsif value.is_a?(Integer) + value > 999 ? "0x" + value.to_s(16) : value.to_s + else + value.to_s end + end - def merge(other_schema) - raise ArgumentError, "Expecting Schema" unless (other_schema.is_a?(Schema) || other_schema.is_a?(Hash)) + def merge(other_schema) + raise ArgumentError, "Expecting Schema" unless other_schema.is_a?(Schema) || other_schema.is_a?(Hash) - other_hash = other_schema.is_a?(Schema) ? other_schema.instance_variable_get(:@schema_hash) : other_schema + other_hash = other_schema.is_a?(Schema) ? other_schema.instance_variable_get(:@schema_hash) : other_schema - Schema.new(@schema_hash.merge(other_hash)) - end + Schema.new(@schema_hash.merge(other_hash)) + end - def empty? - @schema_hash.empty? - end + def empty? + @schema_hash.empty? + end - def single_value? - @schema_hash.key?("const") - end + def single_value? + @schema_hash.key?("const") + end - def value - raise "Schema is not a single value" unless single_value? + def value + raise "Schema is not a single value" unless single_value? - @schema_hash["const"] - end + @schema_hash["const"] + end - def is_power_of_two?(num) - return false if num < 1 - return (num & (num-1)) == 0 - end + def is_power_of_two?(num) + return false if num < 1 - # If min to max range represents an unsigned number of bits, return the number of bits. - # Otherwise return 0 - def num_bits(min, max) - return 0 unless min == 0 - is_power_of_two?(max+1) ? max.bit_length : 0 - end + (num & (num - 1)) == 0 + end - # @return [Idl::Type] THe IDL-equivalent type for this schema object - def to_idl_type - Idl::Type.from_json_schema(@schema_hash) - end + # If min to max range represents an unsigned number of bits, return the number of bits. + # Otherwise return 0 + def num_bits(min, max) + return 0 unless min == 0 + + is_power_of_two?(max + 1) ? max.bit_length : 0 + end + + # @return [Idl::Type] THe IDL-equivalent type for this schema object + def to_idl_type + Idl::Type.from_json_schema(@schema_hash) + end end diff --git a/lib/architecture.rb b/lib/architecture.rb index 0ff77ba80..9cfeecbfb 100644 --- a/lib/architecture.rb +++ b/lib/architecture.rb @@ -264,17 +264,18 @@ def exception_codes return @exception_codes unless @exception_codes.nil? @exception_codes = - extensions.reduce([]) do |list, ext_version| + extensions.each_with_object([]) do |ext_version, list| ecodes = extension(ext_version.name)["exception_codes"] next list if ecodes.nil? ecodes.each do |ecode| # double check that all the codes are unique - raise "Duplicate exception code" if list.any? { |e| e.num == ecode["num"] || e.name == ecode["name"] || e.var == ecode["var"] } + raise "Duplicate exception code" if list.any? do |e| + e.num == ecode["num"] || e.name == ecode["name"] || e.var == ecode["var"] + end list << ExceptionCode.new(ecode["name"], ecode["var"], ecode["num"], self) end - list end end @@ -283,7 +284,7 @@ def interrupt_codes return @interrupt_codes unless @interrupt_codes.nil? @interupt_codes = - extensions.reduce([]) do |list, ext_version| + extensions.each_with_object([]) do |ext_version, list| icodes = extension(ext_version.name)["interrupt_codes"] next list if icodes.nil? @@ -295,7 +296,6 @@ def interrupt_codes list << InterruptCode.new(icode["name"], icode["var"], icode["num"], self) end - list end end diff --git a/lib/cfg_arch.rb b/lib/cfg_arch.rb index 01c70ad94..15c622376 100644 --- a/lib/cfg_arch.rb +++ b/lib/cfg_arch.rb @@ -204,9 +204,7 @@ def inspect = "ConfiguredArchitecture##{name}" def type_check(show_progress: true, io: $stdout) io.puts "Type checking IDL code for #{@config.name}..." progressbar = - if show_progress - ProgressBar.create(title: "Instructions", total: instructions.size) - end + (ProgressBar.create(title: "Instructions", total: instructions.size) if show_progress) instructions.each do |inst| progressbar.increment if show_progress @@ -219,41 +217,37 @@ def type_check(show_progress: true, io: $stdout) end progressbar = - if show_progress - ProgressBar.create(title: "CSRs", total: csrs.size) - end + (ProgressBar.create(title: "CSRs", total: csrs.size) if show_progress) csrs.each do |csr| progressbar.increment if show_progress - if csr.has_custom_sw_read? - if (possible_xlens.include?(32) && csr.defined_in_base32?) || (possible_xlens.include?(64) && csr.defined_in_base64?) - csr.type_checked_sw_read_ast(@symtab) - end + if csr.has_custom_sw_read? && ((possible_xlens.include?(32) && csr.defined_in_base32?) || (possible_xlens.include?(64) && csr.defined_in_base64?)) + csr.type_checked_sw_read_ast(@symtab) end csr.fields.each do |field| - unless field.type_ast(@symtab).nil? - if ((possible_xlens.include?(32) && csr.defined_in_base32? && field.defined_in_base32?) || - (possible_xlens.include?(64) && csr.defined_in_base64? && field.defined_in_base64?)) - field.type_checked_type_ast(@symtab) - end + if !field.type_ast(@symtab).nil? && ((possible_xlens.include?(32) && csr.defined_in_base32? && field.defined_in_base32?) || + (possible_xlens.include?(64) && csr.defined_in_base64? && field.defined_in_base64?)) + field.type_checked_type_ast(@symtab) end - unless field.reset_value_ast(@symtab).nil? - if ((possible_xlens.include?(32) && csr.defined_in_base32? && field.defined_in_base32?) || - (possible_xlens.include?(64) && csr.defined_in_base64? && field.defined_in_base64?)) - field.type_checked_reset_value_ast(@symtab) if csr.defined_in_base32? && field.defined_in_base32? - end + if !field.reset_value_ast(@symtab).nil? && ((possible_xlens.include?(32) && csr.defined_in_base32? && field.defined_in_base32?) || + (possible_xlens.include?(64) && csr.defined_in_base64? && field.defined_in_base64?)) && (csr.defined_in_base32? && field.defined_in_base32?) + field.type_checked_reset_value_ast(@symtab) + end + next if field.sw_write_ast(@symtab).nil? + + if possible_xlens.include?(32) && csr.defined_in_base32? && field.defined_in_base32? + field.type_checked_sw_write_ast(@symtab, + 32) end - unless field.sw_write_ast(@symtab).nil? - field.type_checked_sw_write_ast(@symtab, 32) if possible_xlens.include?(32) && csr.defined_in_base32? && field.defined_in_base32? - field.type_checked_sw_write_ast(@symtab, 64) if possible_xlens.include?(64) && csr.defined_in_base64? && field.defined_in_base64? + if possible_xlens.include?(64) && csr.defined_in_base64? && field.defined_in_base64? + field.type_checked_sw_write_ast(@symtab, + 64) end end end progressbar = - if show_progress - ProgressBar.create(title: "Functions", total: functions.size) - end + (ProgressBar.create(title: "Functions", total: functions.size) if show_progress) functions.each do |func| progressbar.increment if show_progress func.type_check(@symtab) @@ -489,21 +483,23 @@ def implemented_exception_codes return @implemented_exception_codes unless @implemented_exception_codes.nil? @implemented_exception_codes = - implemented_extensions.reduce([]) do |list, ext_version| + implemented_extensions.each_with_object([]) do |ext_version, list| ecodes = extension(ext_version.name)["exception_codes"] next list if ecodes.nil? ecodes.each do |ecode| # double check that all the codes are unique - raise "Duplicate exception code" if list.any? { |e| e.num == ecode["num"] || e.name == ecode["name"] || e.var == ecode["var"] } + raise "Duplicate exception code" if list.any? do |e| + e.num == ecode["num"] || e.name == ecode["name"] || e.var == ecode["var"] + end - unless ecode.dig("when", "version").nil? + if !ecode.dig("when", "version").nil? && !ext?(ext_version.name.to_sym, ecode["when"]["version"]) # check version - next unless ext?(ext_version.name.to_sym, ecode["when"]["version"]) + next end + list << ExceptionCode.new(ecode["name"], ecode["var"], ecode["num"], self) end - list end end @@ -512,21 +508,23 @@ def implemented_interrupt_codes return @implemented_interrupt_codes unless @implemented_interrupt_codes.nil? @implemented_interupt_codes = - implemented_extensions.reduce([]) do |list, ext_version| + implemented_extensions.each_with_object([]) do |ext_version, list| icodes = extension(ext_version.name)["interrupt_codes"] next list if icodes.nil? icodes.each do |icode| # double check that all the codes are unique - raise "Duplicate interrupt code" if list.any? { |i| i.num == icode["num"] || i.name == icode["name"] || i.var == icode["var"] } + raise "Duplicate interrupt code" if list.any? do |i| + i.num == icode["num"] || i.name == icode["name"] || i.var == icode["var"] + end - unless ecode.dig("when", "version").nil? + if !ecode.dig("when", "version").nil? && !ext?(ext_version.name.to_sym, ecode["when"]["version"]) # check version - next unless ext?(ext_version.name.to_sym, ecode["when"]["version"]) + next end + list << InterruptCode.new(icode["name"], icode["var"], icode["num"], self) end - list end end @@ -571,6 +569,7 @@ def implemented_functions end end raise "?" unless @implemented_functions.is_a?(Array) + @implemented_functions = @implemented_functions.flatten @implemented_functions.uniq!(&:name) @@ -694,7 +693,7 @@ def render_erb(erb_template, what = "") t.flush begin Tilt["erb"].new(t.path, trim: "-").render(erb_env) - rescue + rescue StandardError warn "While rendering ERB template: #{what}" raise ensure diff --git a/lib/config.rb b/lib/config.rb index 5efe7b684..9f661d2f4 100644 --- a/lib/config.rb +++ b/lib/config.rb @@ -118,7 +118,7 @@ def mandatory_extensions else @data["mandatory_extensions"].map do |e| # convert the requirement to always be an array - { "name" => e["name"], "version" => e["version"].is_a?(String) ? [e["version"]] : e["version"]} + { "name" => e["name"], "version" => e["version"].is_a?(String) ? [e["version"]] : e["version"] } end end end @@ -138,7 +138,7 @@ def prohibited_extensions else @data["prohibited_extensions"].map do |e| # convert the requirement to always be an array - { "name" => e["name"], "version" => e["version"].is_a?(String) ? [e["version"]] : e["version"]} + { "name" => e["name"], "version" => e["version"].is_a?(String) ? [e["version"]] : e["version"] } end end end diff --git a/lib/idl.rb b/lib/idl.rb index 287761e22..55cc90330 100644 --- a/lib/idl.rb +++ b/lib/idl.rb @@ -110,7 +110,8 @@ def compile_file(path) # @param input_line [Integer] Starting line in the input file that this source comes from # @param no_rescue [Boolean] Whether or not to automatically catch any errors # @return [Ast] The root of the abstract syntax tree - def compile_func_body(body, return_type: nil, symtab: nil, name: nil, input_file: nil, input_line: 0, no_rescue: false, extra_syms: {}, type_check: true) + def compile_func_body(body, return_type: nil, symtab: nil, name: nil, input_file: nil, input_line: 0, + no_rescue: false, extra_syms: {}, type_check: true) @parser.set_input_file(input_file, input_line) m = @parser.parse(body, root: :function_body) @@ -134,9 +135,9 @@ def compile_func_body(body, return_type: nil, symtab: nil, name: nil, input_file cloned_symtab.push(ast) cloned_symtab.add("__expected_return_type", return_type) unless return_type.nil? - extra_syms.each { |k, v| + extra_syms.each do |k, v| cloned_symtab.add(k, v) - } + end begin ast.statements.each do |s| @@ -274,7 +275,6 @@ def compile_expression(expression, symtab, pass_error: false) exit 1 end - ast end end diff --git a/lib/idl/ast.rb b/lib/idl/ast.rb index d77aaa27c..21e05de43 100644 --- a/lib/idl/ast.rb +++ b/lib/idl/ast.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'benchmark' +require "benchmark" require_relative "type" require_relative "symbol_table" @@ -15,9 +15,9 @@ class SyntaxNode # @param [String] filename The name of the input file. # @param [Integer] starting_line The starting line number in the input file. def set_input_file_unless_already_set(filename, starting_line = 0) - if @input_file.nil? - set_input(filename, starting_line) - end + return unless @input_file.nil? + + set_input(filename, starting_line) end # remember where the code comes from @@ -154,13 +154,15 @@ def self.value_try(&block) yield block end end - def value_try(&block) = return self.class.value_try(&block) + + def value_try(&block) = self.class.value_try(&block) def self.value_else(value_result, &block) return unless value_result == :unknown_value yield block end + def value_else(value_result, &block) = self.class.value_else(value_result, &block) # @param input [String] The source being compiled @@ -172,7 +174,12 @@ def initialize(input, interval, children) @starting_line = 0 @interval = interval @text_value = input[interval] - children.each { |child| raise ArgumentError, "Children of #{self.class.name} must be AstNodes (found a #{child.class.name})" unless child.is_a?(AstNode)} + children.each do |child| + unless child.is_a?(AstNode) + raise ArgumentError, + "Children of #{self.class.name} must be AstNodes (found a #{child.class.name})" + end + end @children = children @parent = nil # will be set later unless this is the root @children.each { |child| child.instance_variable_set(:@parent, self) } @@ -327,8 +334,9 @@ def self.value_error(reason, ast = nil) # warn reason # warn "At #{ast.input_file}:#{ast.lineno}" unless ast.nil? throw(:value_error, :unknown_value) - #raise AstNode::ValueError.new(lineno, input_file, reason), reason, [] + # raise AstNode::ValueError.new(lineno, input_file, reason), reason, [] end + def value_error(reason) = self.class.value_error(reason, self) # unindent a multiline string, getting rid of all common leading whitespace (like <<~ heredocs) @@ -360,7 +368,6 @@ def print_ast(indent = 0, indent_size: 2, io: $stdout) # # @param global_symtab [SymbolTable] Symbol table with global scope populated - # @!macro freeze_tree def freeze_tree(global_symtab) return if frozen? @@ -572,13 +579,13 @@ def type(symtab) sym = symtab.get(name) # @type = - if sym.is_a?(Type) - sym - elsif sym.is_a?(Var) - sym.type - else - internal_error "Unexpected object on the symbol table" - end + if sym.is_a?(Type) + sym + elsif sym.is_a?(Var) + sym.type + else + internal_error "Unexpected object on the symbol table" + end end # @return [Boolean] whether or not the Id represents a const @@ -705,7 +712,6 @@ def add_symbol(symtab) declaration.add_symbol(symtab) end - end # @api private @@ -793,9 +799,9 @@ def type_check(symtab) type_error "#{expression.text_value} is not an array" unless expression_type.kind == :array type_error "#{expression.text_value} must be a constant" unless expression_type.const? - if symtab.cfg_arch.fully_configured? && (expression_type.width == :unknown) - type_error "#{expression.text_value} must have a known value at compile time" - end + return unless symtab.cfg_arch.fully_configured? && (expression_type.width == :unknown) + + type_error "#{expression.text_value} must have a known value at compile time" end def type(symtab) @@ -813,7 +819,6 @@ def value(symtab) def to_idl = "$array_size(#{expression.to_idl})" end - class EnumSizeSyntaxNode < Treetop::Runtime::SyntaxNode def to_ast EnumSizeAst.new(input, interval, user_type_name.to_ast) @@ -903,9 +908,7 @@ def type_check(symtab) enum_name.type_check(symtab) expression.type_check(symtab) - if expression.type(symtab).kind != :bits - type_error "Can only cast from Bits to enum" - end + type_error "Can only cast from Bits to enum" if expression.type(symtab).kind != :bits enum_def_type = symtab.get(enum_name.text_value) type_error "No enum named #{enum_name.text_value}" if enum_def_type.nil? @@ -968,11 +971,11 @@ def to_ast values = [] e.elements.each do |e| - if e.i.empty? - values << nil - else - values << e.i.int.to_ast - end + values << if e.i.empty? + nil + else + e.i.int.to_ast + end end EnumDefinitionAst.new( @@ -1000,7 +1003,7 @@ class EnumDefinitionAst < AstNode include Declaration def initialize(input, interval, user_type, element_names, element_values) - super(input, interval, [user_type] + element_names + element_values.reject{ |e| e.nil? }) + super(input, interval, [user_type] + element_names + element_values.reject { |e| e.nil? }) @user_type = user_type @element_name_asts = element_names @element_value_asts = element_values @@ -1040,9 +1043,7 @@ def element_values # @!macro type_check def type_check(symtab) @element_value_asts.each do |e| - unless e.nil? - e.type_check(symtab) - end + e.type_check(symtab) unless e.nil? end add_symbol(symtab) @@ -1058,7 +1059,7 @@ def add_symbol(symtab) end # @!macro type_no_args - def type(symtab) + def type(_symtab) @type end @@ -1223,7 +1224,8 @@ class BitfieldDefinitionSyntaxNode < Treetop::Runtime::SyntaxNode def to_ast fields = [] e.elements.each do |f| - fields << BitfieldFieldDefinitionAst.new(f.input, f.interval, f.field_name.text_value, f.range.int.to_ast, f.range.lsb.empty? ? nil : f.range.lsb.int.to_ast) + fields << BitfieldFieldDefinitionAst.new(f.input, f.interval, f.field_name.text_value, f.range.int.to_ast, + f.range.lsb.empty? ? nil : f.range.lsb.int.to_ast) end BitfieldDefinitionAst.new(input, interval, user_type_name.to_ast, int.to_ast, fields) end @@ -1286,7 +1288,7 @@ def element_names def element_ranges(symtab) return @element_ranges unless @element_ranges.nil? - @element_ranges = @fields.map{ |f| f.range(symtab) } + @element_ranges = @fields.map { |f| f.range(symtab) } end # @!macro type_check @@ -1295,7 +1297,9 @@ def type_check(symtab) @fields.each do |f| f.type_check(symtab) r = f.range(symtab) - type_error "Field position (#{r}) is larger than the bitfield width (#{@size.value(symtab)} #{@size.text_value})" if r.first >= @size.value(symtab) + if r.first >= @size.value(symtab) + type_error "Field position (#{r}) is larger than the bitfield width (#{@size.value(symtab)} #{@size.text_value})" + end end add_symbol(symtab) @@ -1416,6 +1420,7 @@ def add_symbol(symtab) def member_type(name, symtab) idx = member_names.index(name) return nil if idx.nil? + member_types[idx].type(symtab) end @@ -1427,7 +1432,7 @@ def to_idl num_members.times do |i| member_decls << "#{member_types[i].to_idl} #{member_names[i]}" end - "struct #{name} { #{member_decls.join("; ")}; }" + "struct #{name} { #{member_decls.join('; ')}; }" end end @@ -1505,16 +1510,14 @@ def type_check(symtab) var_type = var.type(symtab) if var_type.kind == :array - value_result = value_try do + value_try do index_value = index.value(symtab) - if var_type.width != :unknown - type_error "Array index out of range" if index_value >= var_type.width - end + type_error "Array index out of range" if var_type.width != :unknown && index_value >= (var_type.width) end # Ok, doesn't need to be known elsif var_type.integral? if var_type.kind == :bits - value_result = value_try do + value_try do index_value = index.value(symtab) if (var_type.width != :unknown) && (index_value >= var_type.width) type_error "Bits element index (#{index_value}) out of range (max #{var_type.width - 1}) in access '#{text_value}'" @@ -1577,12 +1580,14 @@ def type_check(symtab) msb.type_check(symtab) lsb.type_check(symtab) - type_error "Range operator only defined for integral types (found #{var.type(symtab)})" unless var.type(symtab).integral? + unless var.type(symtab).integral? + type_error "Range operator only defined for integral types (found #{var.type(symtab)})" + end type_error "Range MSB must be an integral type" unless msb.type(symtab).integral? type_error "Range LSB must be an integral type" unless lsb.type(symtab).integral? - value_result = value_try do + value_try do msb_value = msb.value(symtab) lsb_value = lsb.value(symtab) @@ -1593,7 +1598,7 @@ def type_check(symtab) range_size = msb_value - lsb_value + 1 type_error "zero/negative range (#{msb_value}:#{lsb_value})" if range_size <= 0 - end # OK, don't have to know + end # OK, don't have to know end # @!macro type @@ -1616,7 +1621,6 @@ def value(symtab) # @!macro to_idl def to_idl = "#{var.to_idl}[#{msb.to_idl}:#{lsb.to_idl}]" - end class PcAssignmentSyntaxNode < Treetop::Runtime::SyntaxNode @@ -1636,17 +1640,16 @@ def initialize(input, interval, rval) end # @macro execute - def execute(symtab) = value_error "$pc is never statically known" + def execute(_symtab) = value_error "$pc is never statically known" # @macro execute_unknown def execute_unknown(symtab); end # @!macro type_check - def type_check(symtab) - end + def type_check(symtab); end # @!macro value - def value(symtab) = value_error "$pc is never statically known" + def value(_symtab) = value_error "$pc is never statically known" # @!macro type def type(symtab) = symtab.xreg_type @@ -1683,9 +1686,9 @@ def type_check(symtab) type_error "Cannot assign to a const" if lhs.type(symtab).const? rhs.type_check(symtab) - unless rhs.type(symtab).convertable_to?(lhs.type(symtab)) - type_error "Incompatible type in assignment (#{lhs.type(symtab)}, #{rhs.type(symtab)})" - end + return if rhs.type(symtab).convertable_to?(lhs.type(symtab)) + + type_error "Incompatible type in assignment (#{lhs.type(symtab)}, #{rhs.type(symtab)})" end def var(symtab) @@ -1766,9 +1769,11 @@ def type_check(symtab) type_error "Index must be integral" unless idx.type(symtab).integral? - value_result = value_try do + value_try do idx_value = idx.value(symtab) - type_error "Array index (#{idx.text_value} = #{idx_value}) out of range (< #{var.type(symtab).width})" if idx_value >= lhs.type(symtab).width + if idx_value >= lhs.type(symtab).width + type_error "Array index (#{idx.text_value} = #{idx_value}) out of range (< #{var.type(symtab).width})" + end end # OK, doesn't need to be known @@ -1780,9 +1785,7 @@ def type_check(symtab) type_error "Incompatible type in array assignment" end when :bits - unless rhs.type(symtab).convertable_to?(Bits1Type) - type_error "Incompatible type in integer slice assignment" - end + type_error "Incompatible type in integer slice assignment" unless rhs.type(symtab).convertable_to?(Bits1Type) else internal_error "Unexpected type on array element assignment" end @@ -1889,7 +1892,7 @@ def type_check(symtab) type_error "MSB must be integral" unless msb.type(symtab).integral? type_error "LSB must be integral" unless lsb.type(symtab).integral? - value_result = value_try do + value_try do msb_value = msb.value(symtab) lsb_value = lsb.value(symtab) @@ -1900,9 +1903,9 @@ def type_check(symtab) write_value.type_check(symtab) - unless write_value.type(symtab).integral? - type_error "Incompatible type in range assignment" - end + return if write_value.type(symtab).integral? + + type_error "Incompatible type in range assignment" end def rhs @@ -2024,7 +2027,9 @@ def initialize(input, interval, csr_field, write_value) def type(symtab) if field(symtab).defined_in_all_bases? if symtab.cfg_arch.mxlen == 64 && symtab.cfg_arch.multi_xlen? - Type.new(:bits, width: [field(symtab).location(symtab.cfg_arch, 32).size, field(symtab).location(symtab.cfg_arch, 64).size].max) + Type.new(:bits, + width: [field(symtab).location(symtab.cfg_arch, 32).size, + field(symtab).location(symtab.cfg_arch, 64).size].max) else Type.new(:bits, width: field(symtab).location(symtab.cfg_arch, symtab.cfg_arch.mxlen).size) end @@ -2055,7 +2060,7 @@ def type_check(symtab) end # @!macro execute - def execute(symtab) + def execute(_symtab) value_error "CSR field writes are never compile-time-executable" end @@ -2067,7 +2072,9 @@ def to_idl = "#{csr_field.to_idl} = #{write_value.to_idl}" class MultiVariableAssignmentSyntaxNode < Treetop::Runtime::SyntaxNode def to_ast - MultiVariableAssignmentAst.new(input, interval, [first.to_ast] + rest.elements.map { |r| r.var.to_ast }, function_call.to_ast) + MultiVariableAssignmentAst.new(input, interval, [first.to_ast] + rest.elements.map do |r| + r.var.to_ast + end, function_call.to_ast) end end @@ -2162,7 +2169,9 @@ def to_idl = "(#{variables.map(&:to_idl).join(', ')}) = #{function_call.to_idl}" class MultiVariableDeclarationSyntaxNode < Treetop::Runtime::SyntaxNode def to_ast - MultiVariableDeclarationAst.new(input, interval, type_name.to_ast, [first.to_ast] + rest.elements.map { |r| r.id.to_ast }) + MultiVariableDeclarationAst.new(input, interval, type_name.to_ast, [first.to_ast] + rest.elements.map do |r| + r.id.to_ast + end) end end @@ -2225,7 +2234,8 @@ def to_idl = "#{type_name.to_idl} #{var_name_nodes.map(&:to_idl).join(', ')}" class VariableDeclarationSyntaxNode < Treetop::Runtime::SyntaxNode def to_ast - VariableDeclarationAst.new(input, interval, type_name.to_ast, id.to_ast, ary_size.empty? ? nil : ary_size.ary_size_decl.expression.to_ast) + VariableDeclarationAst.new(input, interval, type_name.to_ast, id.to_ast, + ary_size.empty? ? nil : ary_size.ary_size_decl.expression.to_ast) end end @@ -2415,9 +2425,7 @@ def type_check(symtab) symtab.add(lhs.text_value, Var.new(lhs.text_value, decl_type.clone, rhs.value(symtab))) end value_else(value_result) do - unless rhs.type(symtab).const? - type_error "Declaring constant with a non-constant value (#{rhs.text_value})" - end + type_error "Declaring constant with a non-constant value (#{rhs.text_value})" unless rhs.type(symtab).const? symtab.add(lhs.text_value, Var.new(lhs.text_value, decl_type.clone)) end else @@ -2489,7 +2497,6 @@ def to_idl end class BinaryExpressionRightSyntaxNode < Treetop::Runtime::SyntaxNode - # fix up left recursion # i.e., xlen() - 1 - i => (xlen() - 1) - i def to_ast @@ -2576,9 +2583,9 @@ def initialize(input, interval, exp) = super(input, interval, [exp]) def type_check(symtab) expression.type_check(symtab) - unless [:bits, :enum_ref, :csr].include?(expression.type(symtab).kind) - type_error "#{expression.type(symtab)} Cannot be cast to bits" - end + return if [:bits, :enum_ref, :csr].include?(expression.type(symtab).kind) + + type_error "#{expression.type(symtab)} Cannot be cast to bits" end # @!macro type @@ -2645,9 +2652,7 @@ def initialize(input, interval, lhs, op, rhs) # @return [BinaryExpressionAst] this expression, but with an inverted condition def invert(symtab) - unless symtab.nil? - type_error "Not a boolean operator" unless type(symtab).kind == :boolean - end + type_error "Not a boolean operator" if !symtab.nil? && !type(symtab).kind == (:boolean) inverted_op_map = { "==" => "!=", @@ -2661,7 +2666,7 @@ def invert(symtab) if inverted_op_map.key?(op) BinaryExpressionAst.new(input, interval, lhs.dup, inverted_op_map[op], rhs.dup) else - UnaryOperatorExpressionAst.new(input, interval, "!", self.dup) + UnaryOperatorExpressionAst.new(input, interval, "!", dup) end # else # # harder case of && / || @@ -2685,9 +2690,7 @@ def type(symtab) value_result = value_try do lhs_value = lhs.value(symtab) - if (lhs_value == true && op == "||") || (lhs_value == false && op == "&&") - short_circuit = true - end + short_circuit = true if (lhs_value == true && op == "||") || (lhs_value == false && op == "&&") end value_else(value_result) { short_circuit = false } @@ -2712,7 +2715,7 @@ def type(symtab) value_else(value_result) do Type.new(:bits, width: lhs_type.width, qualifiers:) end - #elsif ["+", "-", "*", "/", "%"].include?(op) + # elsif ["+", "-", "*", "/", "%"].include?(op) elsif lhs_type.const? && rhs_type.const? # if both types are const and the operator results in a Bits type, # then the result type is the largest of: @@ -2762,9 +2765,7 @@ def type_check(symtab) short_circuit = false value_result = value_try do lhs_value = lhs.value(symtab) - if (lhs_value == true && op == "||") || (lhs_value == false && op == "&&") - short_circuit = true - end + short_circuit = true if (lhs_value == true && op == "||") || (lhs_value == false && op == "&&") end value_else(value_result) do short_circuit = false @@ -2797,7 +2798,7 @@ def type_check(symtab) lhs_type = lhs.type(symtab) type_error "Unsupported type for left shift: #{lhs_type}" unless lhs_type.kind == :bits type_error "Unsupported shift for left shift: #{rhs_type}" unless rhs_type.kind == :bits - elsif op == ">>" || op == ">>>" + elsif [">>", ">>>"].include?(op) rhs_type = rhs.type(symtab) lhs_type = lhs.type(symtab) type_error "Unsupported type for right shift: #{lhs_type(symtab)}" unless lhs_type.kind == :bits @@ -2830,190 +2831,186 @@ def value(symtab) # cached_value = @value_cache[symtab] # return cached_value unless cached_value.nil? - value = - if op == ">>>" - lhs_value = lhs.value(symtab) - if lhs_value & (1 << (lhs.type(symtab).width - 1)).zero? - lhs_value >> rhs.value(symtab) - else - # need to shift in ones - shift_amount = rhs.value(symtab) - shifted_value = lhs_value >> shift_amount - mask_len = lhs.type(symtab).width - shift_amount - mask = ((1 << mask_len) - 1) << shift_amount + if op == ">>>" + lhs_value = lhs.value(symtab) + if lhs_value & (1 << (lhs.type(symtab).width - 1)).zero? + lhs_value >> rhs.value(symtab) + else + # need to shift in ones + shift_amount = rhs.value(symtab) + shifted_value = lhs_value >> shift_amount + mask_len = lhs.type(symtab).width - shift_amount + mask = ((1 << mask_len) - 1) << shift_amount - shifted_value | mask - end - elsif ["&&", "||"].include?(op) - # these can short circuit, so we might only need to check the lhs - lhs_value = lhs.value(symtab) - if (op == "&&") && lhs_value == false + shifted_value | mask + end + elsif ["&&", "||"].include?(op) + # these can short circuit, so we might only need to check the lhs + lhs_value = lhs.value(symtab) + if (op == "&&") && lhs_value == false + false + elsif (op == "||") && lhs_value == true + true + elsif op == "&&" + lhs_value && rhs.value(symtab) + else + lhs_value || rhs.value(symtab) + end + elsif op == "==" + value_result = value_try do + return lhs.value(symtab) == rhs.value(symtab) + end + value_else(value_result) do + # even if we don't know the exact value of @lhs and @rhs, we can still + # know that == is false if the possible values of each do not overlap + if lhs.values(symtab).intersection(rhs.values(symtab)).empty? false - elsif (op == "||") && lhs_value == true - true else - if op == "&&" - lhs_value && rhs.value(symtab) - else - lhs_value || rhs.value(symtab) - end - end - elsif op == "==" - value_result = value_try do - return lhs.value(symtab) == rhs.value(symtab) - end - value_else(value_result) do - # even if we don't know the exact value of @lhs and @rhs, we can still - # know that == is false if the possible values of each do not overlap - if lhs.values(symtab).intersection(rhs.values(symtab)).empty? - false - else - value_error "There is overlap in the lhs/rhs return values" - end - end - elsif op == "!=" - value_result = value_try do - return lhs.value(symtab) != rhs.value(symtab) - end - value_else(value_result) do - # even if we don't know the exact value of @lhs and @rhs, we can still - # know that != is true if the possible values of each do not overlap - if lhs.values(symtab).intersection(rhs.values(symtab)).empty? - true - else - value_error "There is overlap in the lhs/rhs return values" - end - end - elsif op == "<=" - value_result = value_try do - return lhs.value(symtab) <= rhs.value(symtab) - end - value_else(value_result) do - # even if we don't know the exact value of @lhs and @rhs, we can still - # know that != is true if the possible values of lhs are all <= the possible values of rhs - rhs_values = rhs.values(symtab) - if lhs.values(symtab).all? { |lhs_value| rhs_values.all? { |rhs_value| lhs_value <= rhs_value} } - true - else - value_error "Some value of lhs is not <= some value of rhs" - end - end - elsif op == ">=" - value_result = value_try do - return lhs.value(symtab) >= rhs.value(symtab) + value_error "There is overlap in the lhs/rhs return values" end - value_else(value_result) do - # even if we don't know the exact value of @lhs and @rhs, we can still - # know that != is true if the possible values of lhs are all >= the possible values of rhs - rhs_values = rhs.values(symtab) - if lhs.values(symtab).all? { |lhs_value| rhs_values.all? { |rhs_value| lhs_value >= rhs_value} } - true - else - value_error "Some value of lhs is not >= some value of rhs" - end - end - elsif op == "<" - value_result = value_try do - return lhs.value(symtab) < rhs.value(symtab) + end + elsif op == "!=" + value_result = value_try do + return lhs.value(symtab) != rhs.value(symtab) + end + value_else(value_result) do + # even if we don't know the exact value of @lhs and @rhs, we can still + # know that != is true if the possible values of each do not overlap + if lhs.values(symtab).intersection(rhs.values(symtab)).empty? + true + else + value_error "There is overlap in the lhs/rhs return values" end - value_else(value_result) do - # even if we don't know the exact value of @lhs and @rhs, we can still - # know that != is true if the possible values of lhs are all < the possible values of rhs - rhs_values = rhs.values(symtab) - if lhs.values(symtab).all? { |lhs_value| rhs_values.all? { |rhs_value| lhs_value < rhs_value} } - true - else - value_error "Some value of lhs is not < some value of rhs" - end + end + elsif op == "<=" + value_result = value_try do + return lhs.value(symtab) <= rhs.value(symtab) + end + value_else(value_result) do + # even if we don't know the exact value of @lhs and @rhs, we can still + # know that != is true if the possible values of lhs are all <= the possible values of rhs + rhs_values = rhs.values(symtab) + if lhs.values(symtab).all? { |lhs_value| rhs_values.all? { |rhs_value| lhs_value <= rhs_value } } + true + else + value_error "Some value of lhs is not <= some value of rhs" end - elsif op == ">" - value_result = value_try do - return lhs.value(symtab) > rhs.value(symtab) + end + elsif op == ">=" + value_result = value_try do + return lhs.value(symtab) >= rhs.value(symtab) + end + value_else(value_result) do + # even if we don't know the exact value of @lhs and @rhs, we can still + # know that != is true if the possible values of lhs are all >= the possible values of rhs + rhs_values = rhs.values(symtab) + if lhs.values(symtab).all? { |lhs_value| rhs_values.all? { |rhs_value| lhs_value >= rhs_value } } + true + else + value_error "Some value of lhs is not >= some value of rhs" end - value_else(value_result) do - # even if we don't know the exact value of @lhs and @rhs, we can still - # know that != is true if the possible values of lhs are all > the possible values of rhs - rhs_values = rhs.values(symtab) - if lhs.values(symtab).all? { |lhs_value| rhs_values.all? { |rhs_value| lhs_value > rhs_value} } - true - else - value_error "Some value of lhs is not > some value of rhs" - end + end + elsif op == "<" + value_result = value_try do + return lhs.value(symtab) < rhs.value(symtab) + end + value_else(value_result) do + # even if we don't know the exact value of @lhs and @rhs, we can still + # know that != is true if the possible values of lhs are all < the possible values of rhs + rhs_values = rhs.values(symtab) + if lhs.values(symtab).all? { |lhs_value| rhs_values.all? { |rhs_value| lhs_value < rhs_value } } + true + else + value_error "Some value of lhs is not < some value of rhs" end - elsif op == "&" - # if one side is zero, we don't need to know the other side - value_result = value_try do - return 0 if lhs.value(symtab).zero? + end + elsif op == ">" + value_result = value_try do + return lhs.value(symtab) > rhs.value(symtab) + end + value_else(value_result) do + # even if we don't know the exact value of @lhs and @rhs, we can still + # know that != is true if the possible values of lhs are all > the possible values of rhs + rhs_values = rhs.values(symtab) + if lhs.values(symtab).all? { |lhs_value| rhs_values.all? { |rhs_value| lhs_value > rhs_value } } + true + else + value_error "Some value of lhs is not > some value of rhs" end - # ok, try rhs + end + elsif op == "&" + # if one side is zero, we don't need to know the other side + value_try do + return 0 if lhs.value(symtab).zero? + end + # ok, try rhs - return 0 if rhs.value(symtab).zero? + return 0 if rhs.value(symtab).zero? - lhs.value(symtab) & rhs.value(symtab) + lhs.value(symtab) & rhs.value(symtab) - elsif op == "|" - # if one side is all ones, we don't need to know the other side - rhs_type = rhs.type(symtab) - value_error("Unknown width") if rhs_type.width == :unknown - lhs_type = lhs.type(symtab) - value_error("unknown width") if lhs_type.width == :unknown + elsif op == "|" + # if one side is all ones, we don't need to know the other side + rhs_type = rhs.type(symtab) + value_error("Unknown width") if rhs_type.width == :unknown + lhs_type = lhs.type(symtab) + value_error("unknown width") if lhs_type.width == :unknown - value_result = value_try do - rhs_mask = ((1 << rhs_type.width) - 1) - return rhs_mask if (rhs.value(symtab) == rhs_mask) && (lhs_type.width <= rhs_type.width) - end - # ok, try rhs + value_try do + rhs_mask = ((1 << rhs_type.width) - 1) + return rhs_mask if (rhs.value(symtab) == rhs_mask) && (lhs_type.width <= rhs_type.width) + end + # ok, try rhs - lhs_mask = ((1 << lhs_type.width) - 1) - return lhs_mask if (lhs.value(symtab) == lhs_mask) && (rhs_type.width <= lhs_type.width) + lhs_mask = ((1 << lhs_type.width) - 1) + return lhs_mask if (lhs.value(symtab) == lhs_mask) && (rhs_type.width <= lhs_type.width) - lhs.value(symtab) | rhs.value(symtab) + lhs.value(symtab) | rhs.value(symtab) - else - v = - case op - when "+" - lhs.value(symtab) + rhs.value(symtab) - when "-" - lhs.value(symtab) - rhs.value(symtab) - when "*" - lhs.value(symtab) * rhs.value(symtab) - when "/" - lhs.value(symtab) / rhs.value(symtab) - when "%" - lhs.value(symtab) % rhs.value(symtab) - when "^" - lhs.value(symtab) ^ rhs.value(symtab) - when "|" - lhs.value(symtab) | rhs.value(symtab) - when "&" - lhs.value(symtab) & rhs.value(symtab) - when ">>" - lhs.value(symtab) >> rhs.value(symtab) - when "<<" - lhs.value(symtab) << rhs.value(symtab) - else - internal_error "Unhandled binary op #{op}" - end + else + v = + case op + when "+" + lhs.value(symtab) + rhs.value(symtab) + when "-" + lhs.value(symtab) - rhs.value(symtab) + when "*" + lhs.value(symtab) * rhs.value(symtab) + when "/" + lhs.value(symtab) / rhs.value(symtab) + when "%" + lhs.value(symtab) % rhs.value(symtab) + when "^" + lhs.value(symtab) ^ rhs.value(symtab) + when "|" + lhs.value(symtab) | rhs.value(symtab) + when "&" + lhs.value(symtab) & rhs.value(symtab) + when ">>" + lhs.value(symtab) >> rhs.value(symtab) + when "<<" + lhs.value(symtab) << rhs.value(symtab) + else + internal_error "Unhandled binary op #{op}" + end - v_trunc = - if !lhs.type(symtab).const? || !rhs.type(symtab).const? - # when both sides are constant, the value is not truncated - width = type(symtab).width - if width == :unknown - value_error("unknown width in op that possibly truncates") - end - v & ((1 << type(symtab).width) - 1) - else - v - end + v_trunc = + if !lhs.type(symtab).const? || !rhs.type(symtab).const? + # when both sides are constant, the value is not truncated + width = type(symtab).width + value_error("unknown width in op that possibly truncates") if width == :unknown + v & ((1 << type(symtab).width) - 1) + else + v + end - warn "WARNING: The value of '#{text_value}' (#{lhs.type(symtab).const?}, #{rhs.type(symtab).const?}) is truncated from #{v} to #{v_trunc} because the result is only #{type(symtab).width} bits" if v != v_trunc - v_trunc + if v != v_trunc + warn "WARNING: The value of '#{text_value}' (#{lhs.type(symtab).const?}, #{rhs.type(symtab).const?}) is truncated from #{v} to #{v_trunc} because the result is only #{type(symtab).width} bits" end + v_trunc + end # @value_cache[symtab] = value - value end # returns the operator as a string @@ -3073,9 +3070,9 @@ def type_check(symtab) node.type_check(symtab) end - unless element_nodes.all? { |e| e.type(symtab).equal_to?(element_nodes[0].type(symtab)) } - type_error "Array elements must be identical" - end + return if element_nodes.all? { |e| e.type(symtab).equal_to?(element_nodes[0].type(symtab)) } + + type_error "Array elements must be identical" end def type(symtab) @@ -3091,7 +3088,7 @@ def to_idl = "[#{element_nodes.map(&:to_idl).join(',')}]" class ConcatenationExpressionSyntaxNode < Treetop::Runtime::SyntaxNode def to_ast - ConcatenationExpressionAst.new(input, interval, [first.to_ast] + rest.elements.map{ |e| e.expression.to_ast }) + ConcatenationExpressionAst.new(input, interval, [first.to_ast] + rest.elements.map { |e| e.expression.to_ast }) end end @@ -3113,7 +3110,9 @@ def type_check(symtab) e_type = exp.type(symtab) type_error "Concatenation only supports Bits<> types" unless e_type.kind == :bits - internal_error "Negative width for element #{exp.text_value}" if (e_type.width != :unknown) && (e_type.width <= 0) + if (e_type.width != :unknown) && (e_type.width <= 0) + internal_error "Negative width for element #{exp.text_value}" + end end end @@ -3259,14 +3258,13 @@ def to_ast end class BuiltinVariableAst < AstNode - def name = text_value def initialize(input, interval) super(input, interval, EMPTY_ARRAY) end - def type_check(symtab) + def type_check(_symtab) type_error "Not a builtin variable" unless ["$pc", "$encoding"].include?(name) end @@ -3285,7 +3283,7 @@ def type(symtab) end end - def value(symtab) + def value(_symtab) value_error "Cannot know the value of pc or encoding" end @@ -3393,10 +3391,10 @@ def type_check(symtab) obj_type = obj.type(symtab) if obj_type.kind == :bitfield - internal_error "#{bitfield.text_value} Not a BitfieldType (is a #{obj_type.class.name})" unless obj_type.respond_to?(:field_names) - unless obj_type.field_names.include?(@field_name) - type_error "#{@field_name} is not a member of #{obj_type}" + unless obj_type.respond_to?(:field_names) + internal_error "#{bitfield.text_value} Not a BitfieldType (is a #{obj_type.class.name})" end + type_error "#{@field_name} is not a member of #{obj_type}" unless obj_type.field_names.include?(@field_name) elsif obj_type.kind == :struct type_error "#{@field_name} is not a member of #{obj_type}" unless obj_type.member?(@field_name) else @@ -3435,7 +3433,7 @@ class EnumRefAst < AstNode include Rvalue def class_name = @enum_class_name - def member_name = @member_name + attr_reader :member_name def initialize(input, interval, class_name, member_name) super(input, interval, EMPTY_ARRAY) @@ -3462,7 +3460,7 @@ def freeze_tree(global_symtab) end # @!macro type_check - def type_check(symtab) + def type_check(_symtab) enum_def_type = @enum_def_type type_error "No symbol #{@enum_class_name} has been defined" if enum_def_type.nil? @@ -3472,7 +3470,7 @@ def type_check(symtab) end # @!macro type_no_cfg_arch - def type(symtab) + def type(_symtab) internal_error "Not frozen?" unless frozen? type_error "No enum named #{@enum_class_name}" if @enum_def_type.nil? @@ -3480,7 +3478,7 @@ def type(symtab) end # @!macro value_no_cfg_arch - def value(symtab) + def value(_symtab) internal_error "Must call type_check first" if @enum_def_type.nil? @enum_def_type.value(@member_name) @@ -3514,9 +3512,7 @@ def initialize(input, interval, op, expression) end def invert(symtab) - unless symtab.nil? - type_error "Not a boolean operator" unless type(symtab).kind == :boolean - end + type_error "Not a boolean operator" if !symtab.nil? && !type(symtab).kind == (:boolean) type_error "Invert only works with !" unless op == "!" @@ -3582,9 +3578,7 @@ def value(symtab) end t = type(symtab) if t.integral? - if t.width == :unknown - value_error("Unknown width for truncation") - end + value_error("Unknown width for truncation") if t.width == :unknown val_trunc = val & ((1 << t.width) - 1) if t.signed? && ((((val_trunc >> (t.width - 1))) & 1) == 1) # need to make this negative! @@ -3593,8 +3587,8 @@ def value(symtab) end end - if op != "~" - warn "#{text_value} is truncated due to insufficient bit width (from #{val} to #{val_trunc} on line #{lineno})" if val_trunc != val + if op != "~" && val_trunc != (val) + warn "#{text_value} is truncated due to insufficient bit width (from #{val} to #{val_trunc} on line #{lineno})" end val_trunc @@ -3606,9 +3600,7 @@ def exp end # @return [String] The operator - def op - @op - end + attr_reader :op # @!macro to_idl def to_idl = "#{op}#{expression.to_idl}" @@ -3654,11 +3646,9 @@ def type_check(symtab) true_expression.type_check(symtab) false_expression.type_check(symtab) - unless true_expression.type(symtab).equal_to?(false_expression.type(symtab)) - # we'll allow dissimilar if they are both bits type - unless true_expression.type(symtab).kind == :bits && false_expression.type(symtab).kind == :bits - type_error "True and false options must be same type (have #{true_expression.type(symtab)} and #{false_expression.type(symtab)})" - end + # we'll allow dissimilar if they are both bits type + if !true_expression.type(symtab).equal_to?(false_expression.type(symtab)) && !(true_expression.type(symtab).kind == :bits && false_expression.type(symtab).kind == :bits) + type_error "True and false options must be same type (have #{true_expression.type(symtab)} and #{false_expression.type(symtab)})" end end end @@ -3669,11 +3659,9 @@ def type(symtab) value_result = value_try do cond = condition.value(symtab) # if the condition is compile-time-known, only check the used field - if cond - return true_expression.type(symtab) - else - return false_expression.type(symtab) - end + return true_expression.type(symtab) if cond + + return false_expression.type(symtab) end value_else(value_result) do t = @@ -3760,22 +3748,18 @@ def type_check(symtab) # @!macro execute def execute(symtab) - if action.is_a?(Declaration) - action.add_symbol(symtab) - end - if action.is_a?(Executable) - action.execute(symtab) - end + action.add_symbol(symtab) if action.is_a?(Declaration) + return unless action.is_a?(Executable) + + action.execute(symtab) end # @!macro execute_unknown def execute_unknown(symtab) - if action.is_a?(Declaration) - action.add_symbol(symtab) - end - if action.is_a?(Executable) - action.execute_unknown(symtab) - end + action.add_symbol(symtab) if action.is_a?(Declaration) + return unless action.is_a?(Executable) + + action.execute_unknown(symtab) end # @!macro to_idl @@ -3812,9 +3796,7 @@ def execute(symtab) value_result = value_try do cond = condition.value(symtab) - if (cond) - action.execute(symtab) - end + action.execute(symtab) if cond end value_else(value_result) do # force action to set any values to nil @@ -3823,10 +3805,10 @@ def execute(symtab) end end - # @!macro execute - def execute_unknown(symtab) - action.execute_unknown(symtab) - end + # @!macro execute + def execute_unknown(symtab) + action.execute_unknown(symtab) + end # @!macro to_idl def to_idl @@ -3990,7 +3972,7 @@ def return_types(symtab) if return_value_nodes[0].type(symtab).kind == :tuple return_value_nodes[0].type(symtab).tuple_types else - return_value_nodes.map{ |v| v.type(symtab) } + return_value_nodes.map { |v| v.type(symtab) } end end @@ -4032,9 +4014,11 @@ def expected_return_type(symtab) unless template_values.size == func_type.template_names.size internal_error "Did not find correct number of template arguments (found #{template_values.size}, need #{func_type.template_names.size}) #{symtab.keys_pretty}" end - func_type.return_type(template_values.sort { |a, b| a.template_index <=> b.template_index }.map(&:value), self) + func_type.return_type(template_values.sort do |a, b| + a.template_index <=> b.template_index + end.map(&:value), self) else - @func_type_cache[symtab.cfg_arch]= func_type + @func_type_cache[symtab.cfg_arch] = func_type func_type.return_type(EMPTY_ARRAY, self) end end @@ -4047,13 +4031,13 @@ def type_check(symtab) type_error "Unknown type for #{v.text_value}" if v.type(symtab).nil? end - if return_value_nodes[0].type(symtab).kind == :tuple - type_error("Can't combine tuple types in return") unless return_value_nodes.size == 1 + if return_value_nodes[0].type(symtab).kind == :tuple && !return_value_nodes.size == (1) + type_error("Can't combine tuple types in return") end - unless return_type(symtab).convertable_to?(expected_return_type(symtab)) - type_error "Return type (#{return_type(symtab)}) not convertable to expected return type (#{expected_return_type(symtab)})" - end + return if return_type(symtab).convertable_to?(expected_return_type(symtab)) + + type_error "Return type (#{return_type(symtab)}) not convertable to expected return type (#{expected_return_type(symtab)})" end def enclosing_function @@ -4114,16 +4098,13 @@ def return_types(symtab) return_expression.return_types(symtab) end - # @!macro return_value def return_value(symtab) cond = condition.value(symtab) - if cond - return_expression.return_value(symtab) - else - nil - end + return unless cond + + return_expression.return_value(symtab) end # @!macro return_values @@ -4132,7 +4113,6 @@ def return_values(symtab) cond = condition.value(symtab) return cond ? return_expression.return_values(symtab) : EMPTY_ARRAY - end value_else(value_result) do # condition isn't known, so the return value is always possible @@ -4186,7 +4166,6 @@ def to_ast # * U32 (Bits<32>) # * U64 (Bits<64>) class BuiltinTypeNameAst < AstNode - def bits_expression = @children[0] def initialize(input, interval, type_name, bits_expression) @@ -4211,9 +4190,9 @@ def type_check(symtab) type_error "Bit width must be known at compile time" if symtab.cfg_arch.fully_configured? end end - unless ["Bits", "String", "XReg", "Boolean", "U32", "U64"].include?(@type_name) - type_error "Unimplemented builtin type #{text_value}" - end + return if ["Bits", "String", "XReg", "Boolean", "U32", "U64"].include?(@type_name) + + type_error "Unimplemented builtin type #{text_value}" end def freeze_tree(symtab) @@ -4296,7 +4275,7 @@ def initialize(input, interval) # @!macro type_check def type_check(_symtab); end - def type(symtab) + def type(_symtab) @type end @@ -4333,18 +4312,18 @@ def freeze_tree(global_symtab) # @!macro type_check def type_check(symtab) - if text_value.delete("_") =~ /^((XLEN)|([0-9]+))?'(s?)([bodh]?)(.*)$/ - # verilog-style literal - width = ::Regexp.last_match(1) - value_text = ::Regexp.last_match(6) + return unless text_value.delete("_") =~ /^((XLEN)|([0-9]+))?'(s?)([bodh]?)(.*)$/ - if width.nil? || width == "XLEN" - width = symtab.mxlen.nil? ? 32 : symtab.mxlen # 32 is the min width, which is what we care about here - end + # verilog-style literal + width = ::Regexp.last_match(1) + value_text = ::Regexp.last_match(6) - # ensure we actually have enough bits to represent the value - type_error("#{value_text} cannot be represented in #{width} bits") if unsigned_value.bit_length > width.to_i + if width.nil? || width == "XLEN" + width = symtab.mxlen.nil? ? 32 : symtab.mxlen # 32 is the min width, which is what we care about here end + + # ensure we actually have enough bits to represent the value + type_error("#{value_text} cannot be represented in #{width} bits") if unsigned_value.bit_length > width.to_i end # @!macro type @@ -4357,8 +4336,8 @@ def type(symtab) signed = ::Regexp.last_match(4) width = width(symtab) - unless width == :unknown - type_error("integer width must be positive (is #{width})") unless width.is_a?(Integer) && width.positive? + if !(width == :unknown) && !(width.is_a?(Integer) && width.positive?) + type_error("integer width must be positive (is #{width})") end qualifiers = signed == "s" ? [:signed, :const] : [:const] @@ -4389,11 +4368,11 @@ def width(symtab) when /^((XLEN)|([0-9]+))?'(s?)([bodh]?)(.*)$/ # verilog-style literal width = ::Regexp.last_match(1) - if width.nil? || width == "XLEN" - width = symtab.mxlen.nil? ? :unknown : symtab.mxlen - else - width = width.to_i - end + width = if width.nil? || width == "XLEN" + symtab.mxlen.nil? ? :unknown : symtab.mxlen + else + width.to_i + end @width = width when /^0([bdx]?)([0-9a-fA-F]*)(s?)$/ signed = ::Regexp.last_match(3) @@ -4429,25 +4408,17 @@ def value(symtab) if unsigned_value > 0x7fff_ffff value_error("Don't know if value will be negative") else - if unsigned_value > 0xffff_ffff - value_error("Don't know if value will fit in literal") - end + value_error("Don't know if value will fit in literal") if unsigned_value > 0xffff_ffff unsigned_value end else - if unsigned_value > 0xffff_ffff - value_error("Don't know if value will fit in literal") - end + value_error("Don't know if value will fit in literal") if unsigned_value > 0xffff_ffff unsigned_value end else - if unsigned_value.bit_length > width - value_error("Value does not fit in literal") - end + value_error("Value does not fit in literal") if unsigned_value.bit_length > width if !signed.empty? && ((unsigned_value >> (width - 1)) == 1) - if unsigned_value.bit_length > (width - 1) - value_error("Value does not fit in literal") - end + value_error("Value does not fit in literal") if unsigned_value.bit_length > (width - 1) -(2**width.to_i - unsigned_value) else unsigned_value @@ -4460,7 +4431,6 @@ def value(symtab) end end - # @return [Integer] the unsigned value of this literal (i.e., treating it as unsigned even if the signed specifier is present) def unsigned_value return @unsigned_value unless @unsigned_value.nil? @@ -4492,16 +4462,16 @@ def unsigned_value radix_id = "o" if radix_id.empty? # @unsigned_value = - case radix_id - when "b" - value.to_i(2) - when "o" - value.to_i(8) - when "d" - value.to_i(10) - when "x" - value.to_i(16) - end + case radix_id + when "b" + value.to_i(2) + when "o" + value.to_i(8) + when "d" + value.to_i(10) + when "x" + value.to_i(16) + end when /^([0-9]*)(s?)$/ # basic decimal @@ -4605,7 +4575,9 @@ def type_check(symtab) func_def_type = func_type(symtab) - type_error "Template arguments provided in call to non-template function #{@name}" if template? && func_def_type.template_names.empty? + if template? && func_def_type.template_names.empty? + type_error "Template arguments provided in call to non-template function #{@name}" + end type_error "Missing template arguments in call to #{@name}" if !template? && !func_def_type.template_names.empty? @@ -4637,15 +4609,16 @@ def type_check(symtab) end arg_nodes.each_with_index do |a, idx| unless a.type(symtab).convertable_to?(func_def_type.argument_type(idx, tvals, arg_nodes, symtab, self)) - type_error "Wrong type for argument number #{idx + 1}. Expecting #{func_def_type.argument_type(idx, tvals, arg_nodes, symtab, self)}, got #{a.type(symtab)}" + type_error "Wrong type for argument number #{idx + 1}. Expecting #{func_def_type.argument_type(idx, tvals, + arg_nodes, symtab, self)}, got #{a.type(symtab)}" end end - if func_def_type.return_type(tvals, self).nil? - internal_error "No type determined for function" - end + internal_error "No type determined for function" if func_def_type.return_type(tvals, self).nil? - internal_error "Function call symtab not at same level post type check (#{symtab.levels} #{level})" unless symtab.levels == level + return if symtab.levels == level + + internal_error "Function call symtab not at same level post type check (#{symtab.levels} #{level})" end # @!macro type @@ -4658,16 +4631,16 @@ def type(symtab) # @!macro value def value(symtab) # sometimes we want to evaluate for a specific XLEN - if name == "xlen" && !symtab.get("__effective_xlen").nil? - return symtab.get("__effective_xlen").value - end + return symtab.get("__effective_xlen").value if name == "xlen" && !symtab.get("__effective_xlen").nil? func_def_type = func_type(symtab) type_error "#{name} is not a function" unless func_def_type.is_a?(FunctionType) if func_def_type.builtin? if name == "implemented?" extname_ref = arg_nodes[0] - type_error "First argument should be a ExtensionName" unless extname_ref.type(symtab).kind == :enum_ref && extname_ref.class_name == "ExtensionName" + unless extname_ref.type(symtab).kind == :enum_ref && extname_ref.class_name == "ExtensionName" + type_error "First argument should be a ExtensionName" + end return symtab.cfg_arch.ext?(arg_nodes[0].member_name) if symtab.cfg_arch.fully_configured? @@ -4675,6 +4648,7 @@ def value(symtab) # we can know if it is implemented, but not if it's not implemented for a partially configured return true end + value_error "implemented? is only known when evaluating in the context of a fully-configured arch def" else value_error "value of builtin function cannot be known" @@ -4694,9 +4668,7 @@ def value(symtab) end alias execute value - def name - @name - end + attr_reader :name # @!macro execute_unknown # nothing to do for a function call @@ -4712,7 +4684,6 @@ def to_idl end end - class UserTypeNameSyntaxNode < Treetop::Runtime::SyntaxNode def to_ast UserTypeNameAst.new(input, interval) @@ -4773,8 +4744,6 @@ def stmts = @children def type_check(symtab) internal_error "Function bodies should be at global + 1 scope (at #{symtab.levels})" unless symtab.levels == 2 - return_value_might_be_known = true - stmts.each do |s| s.type_check(symtab) # next unless return_value_might_be_known @@ -4861,7 +4830,7 @@ def to_ast function_name.text_value, targs.empty? ? [] : [targs.first.to_ast] + targs.rest.elements.map { |r| r.single_declaration.to_ast }, ret.empty? ? [] : [ret.first.to_ast] + 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.empty? ? [] : [args.first.to_ast] + args.rest.elements.map { |r| r.single_declaration.to_ast }, desc.text_value, respond_to?(:body_block) ? body_block.function_body.to_ast : nil ) @@ -4897,15 +4866,13 @@ def initialize(input, interval, name, targs, return_types, arguments, desc, body @reachable_functions_cache ||= {} end - attr_reader :reachable_functions_cache + attr_reader :reachable_functions_cache, :name # @!macro freeze_tree def freeze_tree(global_symtab) return if frozen? - unless templated? - arguments(global_symtab) - end + arguments(global_symtab) unless templated? @children.each { |child| child.freeze_tree(global_symtab) } freeze @@ -4950,9 +4917,7 @@ def arguments(symtab) end arglist.freeze - unless templated? - @arglist = arglist - end + @arglist = arglist unless templated? arglist end @@ -5004,8 +4969,6 @@ def return_type(symtab) end end - - if @return_type_nodes.size == 1 rtype = @return_type_nodes[0].type(symtab) rtype = rtype.ref_type if rtype.kind == :enum @@ -5032,10 +4995,6 @@ def return_type_list_str end end - def name - @name - end - # @param [Array] template values to apply def type_check_template_instance(symtab) internal_error "Function definitions should be at global + 1 scope" unless symtab.levels == 2 @@ -5090,11 +5049,8 @@ def type_check(symtab) end type_check_args(symtab) - # template functions are checked as they are called - unless templated? - type_check_body(symtab) - end + type_check_body(symtab) unless templated? symtab.pop end @@ -5132,7 +5088,9 @@ def template_types(symtab) end def type_check_targs(symtab) - @targs.each { |t| type_error "Template arguments must be uppercase" unless t.text_value[0] == t.text_value[0].upcase } + @targs.each do |t| + type_error "Template arguments must be uppercase" unless t.text_value[0] == t.text_value[0].upcase + end @targs.each { |t| type_error "Template types must be integral" unless t.type(symtab).integral? } end @@ -5211,9 +5169,7 @@ def return_value(symtab) stmts.each do |s| if s.is_a?(Returns) v = s.return_value(symtab) - unless v.nil? - return v - end + return v unless v.nil? else s.execute(symtab) end @@ -5250,9 +5206,7 @@ def return_values(symtab) if s.is_a?(Returns) value_result = value_try do v = s.return_value(symtab) - unless v.nil? - return values.push(v).uniq - end + return values.push(v).uniq unless v.nil? end value_else(value_result) do values += s.return_values(symtab) @@ -5321,9 +5275,7 @@ def return_value(symtab) stmts.each do |s| if s.is_a?(Returns) v = s.return_value(symtab) - unless v.nil? - return v - end + return v unless v.nil? else s.execute(symtab) end @@ -5345,9 +5297,7 @@ def return_values(symtab) if s.is_a?(Returns) value_result = value_try do v = s.return_value(symtab) - unless v.nil? - return values.push(v).uniq - end + return values.push(v).uniq unless v.nil? end value_else(value_result) do values += s.return_values(symtab) @@ -5373,7 +5323,6 @@ def execute(symtab) value_result = value_try do v = s.return_value(symtab) break unless v.nil? # nil means this is a conditional return and the condition is false - end value_else(value_result) do # not known, keep going @@ -5402,7 +5351,6 @@ def execute_unknown(symtab) def to_idl stmts.map(&:to_idl).join("") end - end class ElseIfAst < AstNode @@ -5424,9 +5372,7 @@ def type_check(symtab) cond_value = cond.value(symtab) end - unless cond.type(symtab).convertable_to?(:boolean) - type_error "'#{cond.text_value}' is not boolean" - end + type_error "'#{cond.text_value}' is not boolean" unless cond.type(symtab).convertable_to?(:boolean) body.type_check(symtab) unless cond_value == false end @@ -5477,8 +5423,7 @@ def to_ast else IfBodyAst.new(input, final_else.body.interval, final_else_stmts) end - ast = IfAst.new(input, interval, if_cond.to_ast, if_body_ast, eifs, final_else_ast) - ast + IfAst.new(input, interval, if_cond.to_ast, if_body_ast, eifs, final_else_ast) end end @@ -5504,7 +5449,6 @@ def type_check(symtab) level = symtab.levels if_cond.type_check(symtab) - unless if_cond.type(symtab).convertable_to?(:boolean) if if_cond.type(symtab).kind == :bits type_error "'#{if_cond.text_value}' is not boolean. Maybe you meant 'if ((#{if_cond.text_value}) != 0)'?" @@ -5552,7 +5496,6 @@ def taken_body(symtab) # @!macro return_value def return_value(symtab) - body = taken_body(symtab) return nil if body.nil? @@ -5569,12 +5512,11 @@ def return_values_after_if(symtab) values += eif.return_values(symtab) value_result = value_try do elseif_cond_value = eif.value(symtab) - if elseif_cond_value - # this else if is defintately taken, so we are done - return (values + eif.return_values(symtab)).uniq - else - next :ok # we know the else if isn't taken, so we can just go to the next - end + return (values + eif.return_values(symtab)).uniq if elseif_cond_value + + # this else if is defintately taken, so we are done + + next :ok # we know the else if isn't taken, so we can just go to the next end value_else(value_result) do # else if path not known; body return paths are possible @@ -5596,13 +5538,12 @@ def return_values_after_if(symtab) def return_values(symtab) value_result = value_try do if_cond_value = if_cond.value(symtab) - if if_cond_value - # if is taken, so the only possible return values are those in the if body - return if_body.return_values(symtab) - else - # if cond not taken; check else ifs and possibly final else - return return_values_after_if(symtab) - end + return if_body.return_values(symtab) if if_cond_value + + # if is taken, so the only possible return values are those in the if body + + # if cond not taken; check else ifs and possibly final else + return return_values_after_if(symtab) end value_else(value_result) do # if condition not known; both paths are possible @@ -5617,13 +5558,13 @@ def execute_after_if(symtab) elseifs.each do |eif| value_result = value_try do elseif_cond_value = eif.cond.value(symtab) - if elseif_cond_value - # this else if is defintately taken, so we are done - eif.body.execute(symtab) - return - else - next :ok # we know the else if isn't taken, so we can just go to the next - end + next :ok unless elseif_cond_value + + # this else if is defintately taken, so we are done + eif.body.execute(symtab) + return + + # we know the else if isn't taken, so we can just go to the next end value_else(value_result) do # else if path not known; body return paths are possible @@ -5756,8 +5697,12 @@ def type_check(symtab) type_error "No CSR named #{csr_name}" if csr_def(symtab).nil? end type_error "CSR[#{csr_name(symtab)}] has no field named #{@field_name}" if field_def(symtab).nil? - type_error "CSR[#{csr_name(symtab)}].#{@field_name} is not defined in RV32" if symtab.cfg_arch.mxlen == 32 && !field_def(symtab).defined_in_base32? - type_error "CSR[#{csr_name(symtab)}].#{@field_name} is not defined in RV64" if symtab.cfg_arch.mxlen == 64 && !field_def(symtab).defined_in_base64? + if symtab.cfg_arch.mxlen == 32 && !field_def(symtab).defined_in_base32? + type_error "CSR[#{csr_name(symtab)}].#{@field_name} is not defined in RV32" + end + return unless symtab.cfg_arch.mxlen == 64 && !field_def(symtab).defined_in_base64? + + type_error "CSR[#{csr_name(symtab)}].#{@field_name} is not defined in RV64" end def csr_def(symtab) @@ -5792,7 +5737,7 @@ def to_idl end # @!macro type - def type(symtab) + def type(_symtab) @type end @@ -5806,15 +5751,11 @@ def calc_type(symtab) end end if fd.defined_in_all_bases? - Type.new(:bits, width: symtab.cfg_arch.possible_xlens.map{ |xlen| fd.width(symtab.cfg_arch, xlen) }.max) + Type.new(:bits, width: symtab.cfg_arch.possible_xlens.map { |xlen| fd.width(symtab.cfg_arch, xlen) }.max) elsif fd.base64_only? - if symtab.cfg_arch.possible_xlens.include?(64) - Type.new(:bits, width: fd.width(symtab.cfg_arch, 64)) - end + Type.new(:bits, width: fd.width(symtab.cfg_arch, 64)) if symtab.cfg_arch.possible_xlens.include?(64) elsif fd.base32_only? - if symtab.cfg_arch.possible_xlens.include?(32) - Type.new(:bits, width: fd.width(symtab.cfg_arch, 32)) - end + Type.new(:bits, width: fd.width(symtab.cfg_arch, 32)) if symtab.cfg_arch.possible_xlens.include?(32) else internal_error "unexpected field base" end @@ -5833,9 +5774,7 @@ def calc_value(symtab) # field isn't implemented, so it must be zero return 0 if field_def(symtab).nil? - unless field_def(symtab).type(symtab) == "RO" - value_error "'#{csr_name(symtab)}.#{field_name(symtab)}' is not RO" - end + value_error "'#{csr_name(symtab)}.#{field_name(symtab)}' is not RO" unless field_def(symtab).type(symtab) == "RO" field_def(symtab).reset_value(symtab.cfg_arch) end @@ -5914,7 +5853,7 @@ def type_check(symtab) @idx.type_check(symtab) type_error "Csr index must be integral" unless @idx.type(symtab).integral? - value_result = value_try do + value_try do idx_value = @idx.value(symtab) csr_index = cfg_arch.csrs.index { |csr| csr.address == idx_value } type_error "No csr number '#{idx_value}' was found" if csr_index.nil? @@ -5933,7 +5872,7 @@ def csr_def(symtab) csr else # this is an expression - value_result = value_try do + value_try do idx_value = @idx.value(symtab) return cfg_arch.csrs.find { |csr| csr.address == idx_value } end @@ -5957,7 +5896,9 @@ def value(symtab) cd = csr_def(symtab) value_error "CSR number not knowable" if cd.nil? if symtab.cfg_arch.fully_configured? - value_error "CSR is not implemented" unless symtab.cfg_arch.transitive_implemented_csrs.any? { |icsr| icsr.name == cd.name } + value_error "CSR is not implemented" unless symtab.cfg_arch.transitive_implemented_csrs.any? do |icsr| + icsr.name == cd.name + end else value_error "CSR is not defined" unless symtab.cfg_arch.csrs.any? { |icsr| icsr.name == cd.name } end @@ -5987,13 +5928,15 @@ def initialize(input, interval, csr, expression) end def type_check(symtab) - cfg_arch = symtab.cfg_arch + symtab.cfg_arch csr.type_check(symtab) expression.type_check(symtab) e_type = expression.type(symtab) - return if e_type.kind == :bits && ((e_type.width == :unknown || symtab.mxlen.nil?) || (e_type.width == symtab.mxlen)) + if e_type.kind == :bits && ((e_type.width == :unknown || symtab.mxlen.nil?) || (e_type.width == symtab.mxlen)) + return + end type_error "CSR value must be an XReg" end @@ -6149,7 +6092,7 @@ def name(symtab) end # @!macro execute - def execute(symtab) + def execute(_symtab) value_error "CSR write" end diff --git a/lib/idl/passes/find_return_values.rb b/lib/idl/passes/find_return_values.rb index e1a54678b..bbadf5b8d 100644 --- a/lib/idl/passes/find_return_values.rb +++ b/lib/idl/passes/find_return_values.rb @@ -47,14 +47,14 @@ def pass_find_return_values(values, current_conditions, symtab) end end - unless final_else.empty? - current_conditions.push if_cond.invert(symtab) + return if final_else.empty? - final_else.body.elements.each do |e| - e.e.pass_find_return_values(values, current_conditions, symtab) - end - current_conditions.pop + current_conditions.push if_cond.invert(symtab) + + final_else.body.elements.each do |e| + e.e.pass_find_return_values(values, current_conditions, symtab) end + current_conditions.pop end end diff --git a/lib/idl/passes/gen_adoc.rb b/lib/idl/passes/gen_adoc.rb index 6213411fd..bf590108e 100644 --- a/lib/idl/passes/gen_adoc.rb +++ b/lib/idl/passes/gen_adoc.rb @@ -1,30 +1,40 @@ require_relative "../ast" class Idl::AstNode - def gen_adoc(indent = 0, indent_spaces: 2) + def gen_adoc(_indent = 0, indent_spaces: 2) internal_error "must implement gen_adoc for #{self.class.name}" end end module Idl class NoopAst - def gen_adoc(indent = 0, indent_spaces: 2) = "" + def gen_adoc(_indent = 0, indent_spaces: 2) = "" end + class AryRangeAssignmentAst def gen_adoc(indent = 0, indent_spaces: 2) - "#{' '*indent}#{variable.gen_adoc(indent, indent_spaces: )}[#{msb.gen_adoc(0, indent_spaces:)}:#{lsb.gen_adoc(0, indent_spaces:)}] = #{write_value.gen_adoc(0, indent_spaces:)}" + "#{' ' * indent}#{variable.gen_adoc(indent, + indent_spaces:)}[#{msb.gen_adoc(0, + indent_spaces:)}:#{lsb.gen_adoc(0, + indent_spaces:)}] = #{write_value.gen_adoc( + 0, indent_spaces: + )}" end end + class ConditionalReturnStatementAst def gen_adoc(indent = 0, indent_spaces: 2) - "#{' '*indent}#{return_expression.gen_adoc(indent, indent_spaces: )} if (#{condition.gen_adoc(0, indent_spaces:)});" + "#{' ' * indent}#{return_expression.gen_adoc(indent, + indent_spaces:)} if (#{condition.gen_adoc(0, indent_spaces:)});" end end + class ReturnExpressionAst def gen_adoc(indent = 0, indent_spaces: 2) - "#{' '*indent}return #{return_value_nodes.map{ |r| r.gen_adoc(0, indent_spaces: )}.join(', ')}" + "#{' ' * indent}return #{return_value_nodes.map { |r| r.gen_adoc(0, indent_spaces:) }.join(', ')}" end end + class IfBodyAst def gen_adoc(indent = 0, indent_spaces: 2) adoc = [] @@ -37,119 +47,147 @@ def gen_adoc(indent = 0, indent_spaces: 2) class PostIncrementExpressionAst def gen_adoc(indent, indent_spaces: 2) - "#{' '*indent}#{rval.gen_adoc(indent, indent_spaces: )}++" + "#{' ' * indent}#{rval.gen_adoc(indent, indent_spaces:)}++" end end + class PostDecrementExpressionAst def gen_adoc(indent, indent_spaces: 2) - "#{' '*indent}#{rval.gen_adoc(indent, indent_spaces: )}--" + "#{' ' * indent}#{rval.gen_adoc(indent, indent_spaces:)}--" end end + class StringLiteralAst def gen_adoc(indent, indent_spaces: 2) # text_value will include leading and trailing quotes - "#{' '*indent}#{text_value}" + "#{' ' * indent}#{text_value}" end end + class DontCareReturnAst def gen_adoc(indent, indent_spaces: 2) - "#{' '*indent}-" + "#{' ' * indent}-" end end + class UserTypeNameAst def gen_adoc(indent, indent_spaces: 2) - "#{' '*indent}#{text_value}" + "#{' ' * indent}#{text_value}" end end + class MultiVariableAssignmentAst def gen_adoc(indent, indent_spaces: 2) - "#{' '*indent}(#{variables.map { |v| v.gen_adoc(0, indent_spaces: )}.join(', ')} = #{function_call.gen_adoc(0, indent_spaces:)})" + "#{' ' * indent}(#{variables.map do |v| + v.gen_adoc(0, indent_spaces:) + end.join(', ')} = #{function_call.gen_adoc(0, indent_spaces:)})" end end + class CsrFunctionCallAst def gen_adoc(indent, indent_spaces: 2) - "#{' '*indent}#{csr.gen_adoc(indent, indent_spaces:)}.#{function_name}()" + "#{' ' * indent}#{csr.gen_adoc(indent, indent_spaces:)}.#{function_name}()" end end + class CsrSoftwareWriteAst def gen_adoc(indent, indent_spaces: 2) - "#{' '*indent}#{csr.gen_adoc(indent, indent_spaces:)}.sw_write(#{expression.gen_adoc(0, indent_spaces:)})" + "#{' ' * indent}#{csr.gen_adoc(indent, indent_spaces:)}.sw_write(#{expression.gen_adoc(0, indent_spaces:)})" end end + class FieldAccessExpressionAst def gen_adoc(indent, indent_spaces: 2) - "#{' '*indent}#{obj.gen_adoc(indent, indent_spaces: )}.#{@field_name}" + "#{' ' * indent}#{obj.gen_adoc(indent, indent_spaces:)}.#{@field_name}" end end + class FieldAssignmentAst - def gen_adoc(indent, indent_spaces: 2) + def gen_adoc(_indent, indent_spaces: 2) "#{field_access.gen_adoc(0, indent_spaces:)} = #{write_value.gen_adoc(0, indent_spaces:)}" end end + class ConcatenationExpressionAst def gen_adoc(indent, indent_spaces: 2) - "#{' '*indent}{#{expressions.map { |e| e.gen_adoc(0, indent_spaces: )}.join(', ')}}" + "#{' ' * indent}{#{expressions.map { |e| e.gen_adoc(0, indent_spaces:) }.join(', ')}}" end end + class BitsCastAst def gen_adoc(indent, indent_spaces: 2) - "#{' '*indent}$bits(#{expression.gen_adoc(0, indent_spaces: )})" + "#{' ' * indent}$bits(#{expression.gen_adoc(0, indent_spaces:)})" end end + class EnumCastAst def gen_adoc(indent, indent_spaces: 2) - "#{' '*indent}$enum(#{enum_name.gen_adoc(0, indent_spaces:)}, #{expression.gen_adoc(0, indent_spaces: )})" + "#{' ' * indent}$enum(#{enum_name.gen_adoc(0, indent_spaces:)}, #{expression.gen_adoc(0, indent_spaces:)})" end end + class CsrFieldAssignmentAst def gen_adoc(indent, indent_spaces: 2) - "#{' '*indent}#{csr_field.gen_adoc(indent, indent_spaces:)} = #{write_value.gen_adoc(0, indent_spaces:)}" + "#{' ' * indent}#{csr_field.gen_adoc(indent, indent_spaces:)} = #{write_value.gen_adoc(0, indent_spaces:)}" end end + class EnumRefAst def gen_adoc(indent, indent_spaces: 2) - "#{' '*indent}#{class_name}::#{member_name}" + "#{' ' * indent}#{class_name}::#{member_name}" end end + class EnumSizeAst def gen_adoc(indent, indent_spaces: 2) - "#{' '*indent}$enum_size(#{enum_class.gen_adoc(0, indent_spaces:)})" + "#{' ' * indent}$enum_size(#{enum_class.gen_adoc(0, indent_spaces:)})" end end + class EnumElementSizeAst def gen_adoc(indent, indent_spaces: 2) - "#{' '*indent}$enum_element_size(#{enum_class.gen_adoc(0, indent_spaces:)})" + "#{' ' * indent}$enum_element_size(#{enum_class.gen_adoc(0, indent_spaces:)})" end end + class EnumArrayCastAst def gen_adoc(indent, indent_spaces: 2) - "#{' '*indent}$enum_to_a(#{enum_class.gen_adoc(0, indent_spaces:)})" + "#{' ' * indent}$enum_to_a(#{enum_class.gen_adoc(0, indent_spaces:)})" end end + class ParenExpressionAst def gen_adoc(indent = 0, indent_spaces: 2) - "#{' '*indent}(#{expression.gen_adoc(indent, indent_spaces:)})" + "#{' ' * indent}(#{expression.gen_adoc(indent, indent_spaces:)})" end end + class IntLiteralAst def gen_adoc(indent = 0, indent_spaces: 2) raise "?" if text_value.empty? - "#{' '*indent}#{text_value}" + + "#{' ' * indent}#{text_value}" end end + class IdAst def gen_adoc(indent = 0, indent_spaces: 2) - "#{' '*indent}#{text_value}" + "#{' ' * indent}#{text_value}" end end + class SignCastAst def gen_adoc(indent = 0, indent_spaces: 2) - "#{' '*indent}$signed+++(+++#{expression.gen_adoc(0, indent_spaces:)})" + "#{' ' * indent}$signed+++(+++#{expression.gen_adoc(0, indent_spaces:)})" end end + class AryRangeAccessAst def gen_adoc(indent = 0, indent_spaces: 2) - "#{' '*indent}#{var.gen_adoc(indent, indent_spaces:)}[#{msb.gen_adoc(0, indent_spaces:)}:#{lsb.gen_adoc(0, indent_spaces:)}]" + "#{' ' * indent}#{var.gen_adoc(indent, + indent_spaces:)}[#{msb.gen_adoc(0, + indent_spaces:)}:#{lsb.gen_adoc(0, + indent_spaces:)}]" end end @@ -161,20 +199,26 @@ def gen_adoc(indent = 0, indent_spaces: 2) class MultiVariableDeclarationAst def gen_adoc(indent = 0, indent_spaces: 2) - "#{' ' * indent}#{type_name.gen_adoc(0, indent_spaces:)} #{var_name_nodes.map { |var| var.gen_adoc(0, indent_spaces:) }.join(', ')}" + "#{' ' * indent}#{type_name.gen_adoc(0, indent_spaces:)} #{var_name_nodes.map do |var| + var.gen_adoc(0, indent_spaces:) + end.join(', ')}" end end class TernaryOperatorExpressionAst def gen_adoc(indent = 0, indent_spaces: 2) - "#{' ' * indent}#{condition.gen_adoc(0, indent_spaces:)} ? #{true_expression.gen_adoc(0, indent_spaces:)} : #{false_expression.gen_adoc(0, indent_spaces:)}" + "#{' ' * indent}#{condition.gen_adoc(0, + indent_spaces:)} ? #{true_expression.gen_adoc(0, + indent_spaces:)} : #{false_expression.gen_adoc( + 0, indent_spaces: + )}" end end class BuiltinTypeNameAst def gen_adoc(indent = 0, indent_spaces: 2) if @type_name == "Bits" - "#{' '*indent}Bits<#{bits_expression.gen_adoc(0, indent_spaces:)}>" + "#{' ' * indent}Bits<#{bits_expression.gen_adoc(0, indent_spaces:)}>" else to_idl end @@ -183,17 +227,21 @@ def gen_adoc(indent = 0, indent_spaces: 2) class ForLoopAst def gen_adoc(indent = 0, indent_spaces: 2) - lines = ["#{' '*indent}for pass:[(]#{init.gen_adoc(0, indent_spaces:)}; #{condition.gen_adoc(0, indent_spaces:)}; #{update.gen_adoc(0, indent_spaces:)}) {"] + lines = ["#{' ' * indent}for pass:[(]#{init.gen_adoc(0, + indent_spaces:)}; #{condition.gen_adoc(0, + indent_spaces:)}; #{update.gen_adoc( + 0, indent_spaces: + )}) {"] stmts.each do |s| lines << s.gen_adoc(indent + indent_spaces, indent_spaces:) end - lines << "#{' '*indent}}" + lines << "#{' ' * indent}}" lines.join("\n") end end class BuiltinVariableAst - def gen_adoc(indent = 0, indent_spaces: 2) + def gen_adoc(_indent = 0, indent_spaces: 2) name end end @@ -201,40 +249,51 @@ def gen_adoc(indent = 0, indent_spaces: 2) class VariableDeclarationWithInitializationAst def gen_adoc(indent = 0, indent_spaces: 2) if ary_size.nil? - "#{' ' * indent}#{type_name.gen_adoc(0, indent_spaces:)} #{lhs.gen_adoc(0, indent_spaces:)} = #{rhs.gen_adoc(0, indent_spaces:)}" + "#{' ' * indent}#{type_name.gen_adoc(0, + indent_spaces:)} #{lhs.gen_adoc(0, + indent_spaces:)} = #{rhs.gen_adoc(0, + indent_spaces:)}" else - "#{' ' * indent}#{type_name.gen_adoc(0, indent_spaces:)} #{lhs.gen_adoc(0, indent_spaces:)}[#{ary_size.gen_adoc(0, indent_spaces:)}] = #{rhs.gen_adoc(0, indent_spaces:)}" + "#{' ' * indent}#{type_name.gen_adoc(0, + indent_spaces:)} #{lhs.gen_adoc(0, + indent_spaces:)}[#{ary_size.gen_adoc(0, + indent_spaces:)}] = #{rhs.gen_adoc( + 0, indent_spaces: + )}" end end end class AryElementAccessAst def gen_adoc(indent = 0, indent_spaces: 2) - "#{' '*indent}#{var.gen_adoc(indent, indent_spaces:)}[#{index.gen_adoc(0, indent_spaces:)}]" + "#{' ' * indent}#{var.gen_adoc(indent, indent_spaces:)}[#{index.gen_adoc(0, indent_spaces:)}]" end end class BinaryExpressionAst def gen_adoc(indent = 0, indent_spaces: 2) - "#{' '*indent}#{lhs.gen_adoc(0, indent_spaces:)} #{op.sub("+", "pass:[+]")} #{rhs.gen_adoc(0, indent_spaces:)}" + "#{' ' * indent}#{lhs.gen_adoc(0, indent_spaces:)} #{op.sub('+', 'pass:[+]')} #{rhs.gen_adoc(0, indent_spaces:)}" end end class VariableAssignmentAst def gen_adoc(indent = 0, indent_spaces: 2) - "#{' '*indent}#{lhs.gen_adoc(0, indent_spaces:)} = #{rhs.gen_adoc(0, indent_spaces:)}" + "#{' ' * indent}#{lhs.gen_adoc(0, indent_spaces:)} = #{rhs.gen_adoc(0, indent_spaces:)}" end end class PcAssignmentAst def gen_adoc(indent = 0, indent_spaces: 2) - "#{' '*indent}$pc = #{rhs.gen_adoc(0, indent_spaces:)}" + "#{' ' * indent}$pc = #{rhs.gen_adoc(0, indent_spaces:)}" end end class AryElementAssignmentAst def gen_adoc(indent = 0, indent_spaces: 2) - "#{' '*indent}#{lhs.gen_adoc(0, indent_spaces:)}[#{idx.gen_adoc(0, indent_spaces:)}] = #{rhs.gen_adoc(0, indent_spaces:)}" + "#{' ' * indent}#{lhs.gen_adoc(0, + indent_spaces:)}[#{idx.gen_adoc(0, + indent_spaces:)}] = #{rhs.gen_adoc(0, + indent_spaces:)}" end end @@ -246,7 +305,7 @@ def gen_adoc(indent = 0, indent_spaces: 2) class UnaryOperatorExpressionAst def gen_adoc(indent = 0, indent_spaces: 2) - "#{' '*indent}#{op}#{exp.gen_adoc(0, indent_spaces:)}" + "#{' ' * indent}#{op}#{exp.gen_adoc(0, indent_spaces:)}" end end @@ -258,34 +317,38 @@ def gen_adoc(indent = 0, indent_spaces: 2) class ReplicationExpressionAst def gen_adoc(indent = 0, indent_spaces: 2) - "#{' '*indent}{#{n.gen_adoc(0, indent_spaces:)}{#{v.gen_adoc(indent, indent_spaces:)}}}" + "#{' ' * indent}{#{n.gen_adoc(0, indent_spaces:)}{#{v.gen_adoc(indent, indent_spaces:)}}}" end end class ConditionalStatementAst def gen_adoc(indent = 0, indent_spaces: 2) - "#{' '*indent}#{action.gen_adoc(0, indent_spaces:)} if (#{condition.gen_adoc(0, indent_spaces:)});" + "#{' ' * indent}#{action.gen_adoc(0, indent_spaces:)} if (#{condition.gen_adoc(0, indent_spaces:)});" end end class FunctionCallExpressionAst def gen_adoc(indent = 0, indent_spaces: 2) after_name = [] - after_name << "<#{template_arg_nodes.map { |t| t.gen_adoc(0, indent_spaces:)}.join(', ')}>" unless template_arg_nodes.empty? - after_name << "pass:[(]#{arg_nodes.map { |a| a.gen_adoc(0, indent_spaces: ) }.join(', ')})" - "#{' '*indent}%%LINK%func;#{name};#{name}%%#{after_name.join ''}" + unless template_arg_nodes.empty? + after_name << "<#{template_arg_nodes.map do |t| + t.gen_adoc(0, indent_spaces:) + end.join(', ')}>" + end + after_name << "pass:[(]#{arg_nodes.map { |a| a.gen_adoc(0, indent_spaces:) }.join(', ')})" + "#{' ' * indent}%%LINK%func;#{name};#{name}%%#{after_name.join ''}" end end class ArraySizeAst def gen_adoc(indent = 0, indent_spaces: 2) - "#{' '*indent}$array_size(#{expression.gen_adoc(0, indent_spaces:)})" + "#{' ' * indent}$array_size(#{expression.gen_adoc(0, indent_spaces:)})" end end class FunctionBodyAst def gen_adoc(indent = 0, indent_spaces: 2) - statements.map{ |s| "#{' ' * indent}#{s.gen_adoc(0, indent_spaces:)}" }.join("\n") + statements.map { |s| "#{' ' * indent}#{s.gen_adoc(0, indent_spaces:)}" }.join("\n") end end @@ -299,13 +362,11 @@ def gen_adoc(indent = 0, indent_spaces: 2) end csr_text = "CSR[#{idx_text}].#{@field_name}" if idx_text =~ /[0-9]+/ - "#{' '*indent}#{csr_text}" + "#{' ' * indent}#{csr_text}" + elsif @cfg_arch.csr(csr_text).nil? + "#{' ' * indent}#{csr_text}" else - if @cfg_arch.csr(csr_text).nil? - "#{' '*indent}#{csr_text}" - else - "#{' '*indent}%%LINK%csr_field;#{idx_text}.#{@field_name};#{csr_text}%%" - end + "#{' ' * indent}%%LINK%csr_field;#{idx_text}.#{@field_name};#{csr_text}%%" end end end @@ -322,36 +383,34 @@ def gen_adoc(indent = 0, indent_spaces: 2) csr_text = "CSR[#{idx_text}]" if idx_text =~ /[0-9]+/ # we don't have the symtab to map this to a csr name - "#{' '*indent}#{csr_text}" + "#{' ' * indent}#{csr_text}" + elsif @cfg_arch.csr(csr_text).nil? + "#{' ' * indent}#{csr_text}" else - if @cfg_arch.csr(csr_text).nil? - "#{' '*indent}#{csr_text}" - else - "#{' '*indent}%%LINK%csr;#{idx_text};#{csr_text}%%" - end + "#{' ' * indent}%%LINK%csr;#{idx_text};#{csr_text}%%" end end end class IfAst def gen_adoc(indent = 0, indent_spaces: 2) - lines = ["#{' '*indent}if pass:[(]#{if_cond.gen_adoc(0, indent_spaces:)}) {"] + lines = ["#{' ' * indent}if pass:[(]#{if_cond.gen_adoc(0, indent_spaces:)}) {"] if_body.stmts.each do |s| lines << s.gen_adoc(indent + indent_spaces, indent_spaces:) end elseifs.each do |eif| - lines << "#{' '*indent}} else if pass:[(]#{eif.cond.gen_adoc(0, indent_spaces:)}) {" + lines << "#{' ' * indent}} else if pass:[(]#{eif.cond.gen_adoc(0, indent_spaces:)}) {" eif.body.stmts.each do |s| lines << s.gen_adoc(indent + indent_spaces, indent_spaces:) end end unless final_else_body.stmts.empty? - lines << "#{' '*indent}} else {" + lines << "#{' ' * indent}} else {" final_else_body.stmts.each do |s| lines << s.gen_adoc(indent + indent_spaces, indent_spaces:) end end - lines << "#{' '*indent}}" + lines << "#{' ' * indent}}" lines.join("\n") end diff --git a/lib/idl/passes/gen_option_adoc.rb b/lib/idl/passes/gen_option_adoc.rb index 059530ce7..0dc368c74 100644 --- a/lib/idl/passes/gen_option_adoc.rb +++ b/lib/idl/passes/gen_option_adoc.rb @@ -38,24 +38,24 @@ def gen_option_adoc ADOC end unless final_else_body.nil? - if elseifs.empty? - if if_cond.is_a?(BinaryExpressionAst) || if_cond.is_a?(UnaryOperatorExpressionAst) - adoc << <<~ADOC - [when,"#{if_cond.invert(nil).to_idl}"] - #{final_else_body.gen_option_adoc} - ADOC - else - adoc << <<~ADOC - [when,"!(#{if_cond.to_idl})"] - #{final_else_body.gen_option_adoc} - ADOC - end - else - adoc << <<~ADOC - [when,"else"] - #{final_else_body.gen_option_adoc} - ADOC - end + adoc << if elseifs.empty? + if if_cond.is_a?(BinaryExpressionAst) || if_cond.is_a?(UnaryOperatorExpressionAst) + <<~ADOC + [when,"#{if_cond.invert(nil).to_idl}"] + #{final_else_body.gen_option_adoc} + ADOC + else + <<~ADOC + [when,"!(#{if_cond.to_idl})"] + #{final_else_body.gen_option_adoc} + ADOC + end + else + <<~ADOC + [when,"else"] + #{final_else_body.gen_option_adoc} + ADOC + end end adoc end @@ -110,19 +110,19 @@ def gen_option_adoc cond = condition.is_a?(ParenExpressionAst) ? condition.expression : condition if cond.is_a?(BinaryExpressionAst) || cond.is_a?(UnaryOperatorExpressionAst) <<~ADOC - [when,"#{cond.gen_adoc.gsub('"', """)}"] + [when,"#{cond.gen_adoc.gsub('"', '"')}"] #{true_expression.gen_option_adoc} - [when,"#{cond.invert(nil).gen_adoc.gsub('"', """)}"] + [when,"#{cond.invert(nil).gen_adoc.gsub('"', '"')}"] #{false_expression.gen_option_adoc} ADOC else <<~ADOC - [when,"#{cond.gen_adoc.gsub('"', """)}"] + [when,"#{cond.gen_adoc.gsub('"', '"')}"] #{true_expression.gen_option_adoc} - [when,"!(#{cond.gen_adoc.gsub('"', """)})"] + [when,"!(#{cond.gen_adoc.gsub('"', '"')})"] #{false_expression.gen_option_adoc} ADOC @@ -132,23 +132,21 @@ def gen_option_adoc class EnumRefAst def gen_option_adoc - if class_name == "CsrFieldType" - case member_name - when "RW", "RO" - member_name - when "ROH" - "RO-H" - when "RWR" - "RW-R" - when "RWH" - "RW-H" - when "RWRH" - "RW-RH" - else - raise "unexpected" - end + raise "Unexpected" unless class_name == "CsrFieldType" + + case member_name + when "RW", "RO" + member_name + when "ROH" + "RO-H" + when "RWR" + "RW-R" + when "RWH" + "RW-H" + when "RWRH" + "RW-RH" else - raise "Unexpected" + raise "unexpected" end end end diff --git a/lib/idl/passes/prune.rb b/lib/idl/passes/prune.rb index ca5bedde0..c926ec7f5 100644 --- a/lib/idl/passes/prune.rb +++ b/lib/idl/passes/prune.rb @@ -52,6 +52,7 @@ def prune(symtab) new_node end end + class FunctionCallExpressionAst def prune(symtab) value_result = value_try do @@ -59,10 +60,13 @@ def prune(symtab) return create_literal(v) end value_else(value_result) do - FunctionCallExpressionAst.new(input, interval, name, targs.map { |t| t.prune(symtab) }, args.map { |a| a.prune(symtab)} ) + FunctionCallExpressionAst.new(input, interval, name, targs.map { |t| t.prune(symtab) }, args.map do |a| + a.prune(symtab) + end) end end end + class VariableDeclarationWithInitializationAst def prune(symtab) VariableDeclarationWithInitializationAst.new( @@ -74,6 +78,7 @@ def prune(symtab) ) end end + class ForLoopAst def prune(symtab) symtab.push(self) @@ -93,6 +98,7 @@ def prune(symtab) new_loop end end + class FunctionBodyAst def prune(symtab, args_already_applied: false) symtab.push(self) @@ -100,9 +106,7 @@ def prune(symtab, args_already_applied: false) begin func_def = find_ancestor(FunctionDefAst) unless args_already_applied || func_def.nil? - if func_def.templated? # can't prune a template because we don't have all types - return dup - end + return dup if func_def.templated? # can't prune a template because we don't have all types # push template values func_def.template_names.each_with_index do |tname, idx| @@ -154,6 +158,7 @@ def prune(symtab, args_already_applied: false) pruned_body end end + class StatementAst def prune(symtab) pruned_action = action.prune(symtab) @@ -170,6 +175,7 @@ def prune(symtab) new_stmt end end + class BinaryExpressionAst # @!macro prune def prune(symtab) @@ -251,7 +257,9 @@ def prune(symtab) stmts.each do |s| pruned_stmts << s.prune(symtab) - break if pruned_stmts.last.is_a?(StatementAst) && pruned_stmts.last.action.is_a?(FunctionCallExpressionAst) && pruned_stmts.last.action.name == "raise" + if pruned_stmts.last.is_a?(StatementAst) && pruned_stmts.last.action.is_a?(FunctionCallExpressionAst) && pruned_stmts.last.action.name == "raise" + break + end end IfBodyAst.new(input, interval, pruned_stmts) end @@ -282,7 +290,8 @@ def prune(symtab) elseifs[0].cond.dup, elseifs[0].body.dup, elseifs[1..].map(&:dup), - final_else_body.dup).prune(symtab) + final_else_body.dup + ).prune(symtab) elsif !final_else_body.stmts.empty? # the if is false, and there are no else ifs, so the result of the prune is just the pruned else body return final_else_body.prune(symtab) @@ -297,19 +306,18 @@ def prune(symtab) unknown_elsifs = [] elseifs.each do |eif| value_result = value_try do - if eif.cond.value(symtab) - # this elseif is true, so turn it into an else and then we are done - return IfAst.new( - input, interval, - if_cond.dup, - if_body.dup, - unknown_elsifs.map(&:dup), - eif.body.dup - ).prune(symtab) - else - # this elseif is false, so we can remove it - next :ok - end + next :ok unless eif.cond.value(symtab) + + # this elseif is true, so turn it into an else and then we are done + return IfAst.new( + input, interval, + if_cond.dup, + if_body.dup, + unknown_elsifs.map(&:dup), + eif.body.dup + ).prune(symtab) + + # this elseif is false, so we can remove it end value_else(value_result) do unknown_elsifs << eif @@ -330,11 +338,9 @@ def prune(symtab) class ConditionalReturnStatementAst def prune(symtab) value_result = value_try do - if condition.value(symtab) - return return_expression.prune(symtab) - else - return NoopAst.new - end + return return_expression.prune(symtab) if condition.value(symtab) + + return NoopAst.new end value_else(value_result) do ConditionalReturnStatementAst.new(input, interval, return_expression.prune(symtab), condition.prune(symtab)) @@ -345,17 +351,15 @@ def prune(symtab) class ConditionalStatementAst def prune(symtab) value_result = value_try do - if condition.value(symtab) - pruned_action = action.prune(symtab) - pruned_action.add_symbol(symtab) if pruned_action.is_a?(Declaration) - value_result = value_try do - pruned_action.execute(symtab) if pruned_action.is_a?(Executable) - end + return NoopAst.new unless condition.value(symtab) - return StatementAst.new(input, interval, pruned_action) - else - return NoopAst.new + pruned_action = action.prune(symtab) + pruned_action.add_symbol(symtab) if pruned_action.is_a?(Declaration) + value_result = value_try do + pruned_action.execute(symtab) if pruned_action.is_a?(Executable) end + + return StatementAst.new(input, interval, pruned_action) end value_else(value_result) do # condition not known @@ -364,7 +368,7 @@ def prune(symtab) value_result = value_try do pruned_action.execute(symtab) if pruned_action.is_a?(Executable) end - # ok + # ok ConditionalStatementAst.new(input, interval, pruned_action, condition.prune(symtab)) end end @@ -373,11 +377,9 @@ def prune(symtab) class TernaryOperatorExpressionAst def prune(symtab) value_result = value_try do - if condition.value(symtab) - return true_expression.prune(symtab) - else - return false_expression.prune(symtab) - end + return true_expression.prune(symtab) if condition.value(symtab) + + return false_expression.prune(symtab) end value_else(value_result) do TernaryOperatorExpressionAst.new( diff --git a/lib/idl/passes/reachable_exceptions.rb b/lib/idl/passes/reachable_exceptions.rb index 3daad890b..f085b13f6 100644 --- a/lib/idl/passes/reachable_exceptions.rb +++ b/lib/idl/passes/reachable_exceptions.rb @@ -20,7 +20,7 @@ def reachable_exceptions(symtab) class FunctionCallExpressionAst def reachable_exceptions(symtab) - if name == "raise" || name == "raise_precise" + if ["raise", "raise_precise"].include?(name) # first argument is the exception code_ast = arg_nodes[0] value_result = value_try do @@ -69,17 +69,17 @@ class StatementAst def reachable_exceptions(symtab) mask = # if action.is_a?(FunctionCallExpressionAst) - action.reachable_exceptions(symtab) - # else - # 0 - # end + action.reachable_exceptions(symtab) + # else + # 0 + # end action.add_symbol(symtab) if action.is_a?(Declaration) if action.is_a?(Executable) value_try do action.execute(symtab) end end - # ok + # ok mask end end @@ -90,14 +90,14 @@ def reachable_exceptions(symtab) value_try do mask = if_cond.reachable_exceptions(symtab) if if_cond.is_a?(FunctionCallExpressionAst) value_result = value_try do - if (if_cond.value(symtab)) + if if_cond.value(symtab) mask |= if_body.reachable_exceptions(symtab) return mask # no need to continue else elseifs.each do |eif| mask |= eif.cond.reachable_exceptions(symtab) if eif.cond.is_a?(FunctionCallExpressionAst) value_result = value_try do - if (eif.cond.value(symtab)) + if eif.cond.value(symtab) mask |= eif.body.reachable_exceptions(symtab) return mask # no need to keep going end @@ -116,7 +116,7 @@ def reachable_exceptions(symtab) elseifs.each do |eif| mask |= eif.cond.reachable_exceptions(symtab) if eif.cond.is_a?(FunctionCallExpressionAst) value_result = value_try do - if (eif.cond.value(symtab)) + if eif.cond.value(symtab) mask |= eif.body.reachable_exceptions(symtab) return mask # no need to keep going end @@ -129,7 +129,7 @@ def reachable_exceptions(symtab) mask |= final_else_body.reachable_exceptions(symtab) end end - return mask + mask end end @@ -139,7 +139,7 @@ def reachable_exceptions(symtab) value_result = value_try do if condition.value(symtab) mask |= return_expression.is_a?(FunctionCallExpressionAst) ? return_expression.reachable_exceptions(symtab) : 0 - # ok + # ok end end value_else(value_result) do diff --git a/lib/idl/passes/reachable_functions.rb b/lib/idl/passes/reachable_functions.rb index ca4379501..16091be92 100644 --- a/lib/idl/passes/reachable_functions.rb +++ b/lib/idl/passes/reachable_functions.rb @@ -36,9 +36,7 @@ def reachable_functions(symtab) func_def_type.apply_arguments(body_symtab, arg_nodes, symtab, self) - unless func_def_type.builtin? - fns.concat(func_def_type.body.reachable_functions(body_symtab)) - end + fns.concat(func_def_type.body.reachable_functions(body_symtab)) unless func_def_type.builtin? fns = fns.push(func_def_type.func_def_ast).uniq(&:name) ensure @@ -58,27 +56,26 @@ def reachable_functions(symtab) value_try do action.execute(symtab) if action.is_a?(Executable) end - # ok + # ok fns end end - class IfAst def reachable_functions(symtab) fns = [] value_try do fns.concat if_cond.reachable_functions(symtab) if if_cond.is_a?(FunctionCallExpressionAst) value_result = value_try do - if (if_cond.value(symtab)) + if if_cond.value(symtab) fns.concat if_body.reachable_functions(symtab) return fns # no need to continue else elseifs.each do |eif| fns.concat eif.cond.reachable_functions(symtab) if eif.cond.is_a?(FunctionCallExpressionAst) value_result = value_try do - if (eif.cond.value(symtab)) + if eif.cond.value(symtab) fns.concat eif.body.reachable_functions(symtab) return fns # no need to keep going end @@ -97,7 +94,7 @@ def reachable_functions(symtab) elseifs.each do |eif| fns.concat eif.cond.reachable_functions(symtab) if eif.cond.is_a?(FunctionCallExpressionAst) value_result = value_try do - if (eif.cond.value(symtab)) + if eif.cond.value(symtab) fns.concat eif.body.reachable_functions(symtab) return fns # no need to keep going end @@ -110,7 +107,7 @@ def reachable_functions(symtab) fns.concat final_else_body.reachable_functions(symtab) end end - return fns + fns end end @@ -119,8 +116,8 @@ def reachable_functions(symtab) fns = condition.is_a?(FunctionCallExpressionAst) ? condition.reachable_functions(symtab) : [] value_result = value_try do cv = condition.value(symtab) - if cv - fns.concat return_expression.reachable_functions(symtab) if return_expression.is_a?(FunctionCallExpressionAst) + if cv && return_expression.is_a?(FunctionCallExpressionAst) + fns.concat return_expression.reachable_functions(symtab) end end value_else(value_result) do @@ -133,12 +130,11 @@ def reachable_functions(symtab) class ConditionalStatementAst def reachable_functions(symtab) - fns = condition.is_a?(FunctionCallExpressionAst) ? condition.reachable_functions(symtab) : [] value_result = value_try do - if condition.value(symtab) - fns.concat action.reachable_functions(symtab) if action.is_a?(FunctionCallExpressionAst) + if condition.value(symtab) && action.is_a?(FunctionCallExpressionAst) + fns.concat action.reachable_functions(symtab) # no need to execute action (return) end end diff --git a/lib/idl/symbol_table.rb b/lib/idl/symbol_table.rb index 466183f9f..762d744e6 100644 --- a/lib/idl/symbol_table.rb +++ b/lib/idl/symbol_table.rb @@ -5,7 +5,8 @@ module Idl # Objects to represent variables in the ISA def class Var - attr_reader :name, :type, :value + attr_accessor :value + attr_reader :name, :type def initialize(name, type, value = nil, decode_var: false, template_index: nil, function_name: nil) @name = name @@ -67,15 +68,11 @@ def template_val? def to_cxx @name end - - def value=(new_value) - @value = new_value - end end # scoped symbol table holding known symbols at a current point in parsing class SymbolTable - def cfg_arch = @cfg_arch + attr_reader :cfg_arch # @return [Integer] 32 or 64, the XLEN in M-mode attr_reader :mxlen @@ -91,9 +88,10 @@ def hash def initialize(cfg_arch) raise "Must provide cfg_arch" if cfg_arch.nil? + # TODO: XXX: Put this check back in when replaced by Design class. # See https://github.com/riscv-software-src/riscv-unified-db/pull/371 - #raise "The cfg_arch must be a ConfiguredArchitecture but is a #{cfg_arch.class}" unless (cfg_arch.is_a?(ConfiguredArchitecture) || cfg_arch.is_a?(MockConfiguredArchitecture)) + # raise "The cfg_arch must be a ConfiguredArchitecture but is a #{cfg_arch.class}" unless (cfg_arch.is_a?(ConfiguredArchitecture) || cfg_arch.is_a?(MockConfiguredArchitecture)) @cfg_arch = cfg_arch @mxlen = cfg_arch.unconfigured? ? nil : cfg_arch.mxlen @@ -129,7 +127,8 @@ def initialize(cfg_arch) add!(param_with_value.name, Var.new(param_with_value.name, type, param_with_value.value)) else unless existing_sym.type.equal_to?(type) && existing_sym.value == param_with_value.value - raise DuplicateSymError, "Definition error: Param #{param.name} is defined by multiple extensions and is not the same definition in each" + raise DuplicateSymError, + "Definition error: Param #{param.name} is defined by multiple extensions and is not the same definition in each" end end end @@ -193,6 +192,7 @@ def push(ast) # @scope_caller ||= [] # @scope_caller.push caller[0] raise "#{@scopes.size} #{@callstack.size}" unless @scopes.size == @callstack.size + @scopes << {} @callstack << ast @frozen_hash = nil @@ -206,6 +206,7 @@ def pop raise "Error: popping the symbol table would remove global scope" if @scopes.size == 1 raise "?" unless @scopes.size == @callstack.size + @scopes.pop @callstack.pop end @@ -220,7 +221,7 @@ def key?(name) end def keys_pretty - @scopes.map { |s| s.map { |k, v| v.is_a?(Var) && v.template_val? ? "#{k} (template)" : k }} + @scopes.map { |s| s.map { |k, v| v.is_a?(Var) && v.template_val? ? "#{k} (template)" : k } } end # searches the symbol table scope-by-scope to find 'name' @@ -286,7 +287,9 @@ def add(name, var) # @param var [Object] Symbol object (usually a Var or a Type) # @raise [DuplicationSymError] if 'name' is already in the symbol table def add!(name, var) - raise DuplicateSymError, "Symbol #{name} already defined as #{get(name)}" unless @scopes.select { |h| h.key? name }.empty? + raise DuplicateSymError, "Symbol #{name} already defined as #{get(name)}" unless @scopes.select do |h| + h.key? name + end.empty? @scopes.last[name] = var end @@ -302,7 +305,7 @@ def add_above!(name, var) # add to the scope at level, and make sure name is unique at that scope def add_at!(level, name, var) - raise "Level #{level} is too large #{@scopes.size}" if level >= @scopes.size + raise "Level #{level} is too large #{@scopes.size}" if level >= @scopes.size raise "Symbol #{name} already defined" unless @scopes[0...level].select { |h| h.key? name }.empty? @@ -391,11 +394,11 @@ def deep_clone(clone_values: false, freeze_global: true) @scopes[1..].each do |scope| c_scopes << {} scope.each do |k, v| - if clone_values - c_scopes.last[k] = v.dup - else - c_scopes.last[k] = v - end + c_scopes.last[k] = if clone_values + v.dup + else + v + end end end copy diff --git a/lib/idl/tests/helpers.rb b/lib/idl/tests/helpers.rb index 2d694e5b3..4cf813781 100644 --- a/lib/idl/tests/helpers.rb +++ b/lib/idl/tests/helpers.rb @@ -5,6 +5,7 @@ # Extension mock that returns an extension name class Xmockension attr_reader :name + def initialize(name) @name = name end @@ -16,7 +17,10 @@ def initialize(name) # ConfiguredArchitecture mock that knows about XLEN and extensions class MockConfiguredArchitecture def param_values = { "XLEN" => 32 } - def params_with_value = [XmockensionParameterWithValue.new("XLEN", "mxlen", {"type" => "integer", "enum" => [32, 64]}, nil, nil, 32)] + + def params_with_value = [XmockensionParameterWithValue.new("XLEN", "mxlen", + { "type" => "integer", "enum" => [32, 64] }, nil, nil, 32)] + def params_without_value = [] def params = [] def extensions = [Xmockension.new("I")] diff --git a/lib/idl/tests/test_expressions.rb b/lib/idl/tests/test_expressions.rb index cb16a7d92..892a9577b 100644 --- a/lib/idl/tests/test_expressions.rb +++ b/lib/idl/tests/test_expressions.rb @@ -23,8 +23,8 @@ def test_that_values_are_tuncated idl = <<~IDL.strip a + b IDL - @symtab.add("a", Idl::Var.new("a", Idl::Type.new(:bits, width:4), 0xf)) - @symtab.add("b", Idl::Var.new("b", Idl::Type.new(:bits, width:4), 0x1)) + @symtab.add("a", Idl::Var.new("a", Idl::Type.new(:bits, width: 4), 0xf)) + @symtab.add("b", Idl::Var.new("b", Idl::Type.new(:bits, width: 4), 0x1)) ast = @compiler.compile_expression(idl, @symtab) assert_equal 0, ast.value(@symtab) diff --git a/lib/idl/tests/test_lexer.rb b/lib/idl/tests/test_lexer.rb index a279b6cde..f5428b434 100644 --- a/lib/idl/tests/test_lexer.rb +++ b/lib/idl/tests/test_lexer.rb @@ -29,6 +29,5 @@ def test_function puts token puts chunk end - end end diff --git a/lib/idl/type.rb b/lib/idl/type.rb index 07d2f46a9..fbae6f7a8 100644 --- a/lib/idl/type.rb +++ b/lib/idl/type.rb @@ -38,7 +38,7 @@ def default false when :array if @width == :unknown - Array.new + [] else Array.new(@width, sub_type.default) end @@ -63,18 +63,19 @@ def qualify(qualifier) def self.from_typename(type_name, cfg_arch) case type_name - when 'XReg' - return Type.new(:bits, width: cfg_arch.param_values['XLEN']) - when 'FReg' - return Type.new(:freg, width: 32) - when 'DReg' - return Type.new(:dreg, width: 64) + when "XReg" + Type.new(:bits, width: cfg_arch.param_values["XLEN"]) + when "FReg" + Type.new(:freg, width: 32) + when "DReg" + Type.new(:dreg, width: 64) when /Bits<((?:0x)?[0-9a-fA-F]+)>/ - Type.new(:bits, width: $1.to_i) + Type.new(:bits, width: ::Regexp.last_match(1).to_i) end end - def initialize(kind, qualifiers: [], width: nil, sub_type: nil, name: nil, tuple_types: nil, return_type: nil, arguments: nil, enum_class: nil, csr: nil) + def initialize(kind, qualifiers: [], width: nil, sub_type: nil, name: nil, tuple_types: nil, return_type: nil, + arguments: nil, enum_class: nil, csr: nil) raise "Invalid kind '#{kind}'" unless KINDS.include?(kind) @kind = kind @@ -83,12 +84,16 @@ def initialize(kind, qualifiers: [], width: nil, sub_type: nil, name: nil, tuple @qualifiers = qualifiers # raise "#{width.class.name}" if (kind == :bits && !width.is_a?(Integer)) - raise "Should be a FunctionType" if kind == :function && !self.is_a?(FunctionType) + raise "Should be a FunctionType" if kind == :function && !is_a?(FunctionType) + + unless width.nil? || width.is_a?(Integer) || width == :unknown + raise "Width must be an Integer, is a #{width.class}" + end - raise "Width must be an Integer, is a #{width.class}" unless width.nil? || width.is_a?(Integer) || width == :unknown @width = width @sub_type = sub_type raise "Tuples need a type list" if kind == :tuple && tuple_types.nil? + @tuple_types = tuple_types @return_type = return_type @arguments = arguments @@ -98,20 +103,15 @@ def initialize(kind, qualifiers: [], width: nil, sub_type: nil, name: nil, tuple raise "Bits type must have width" unless @width raise "Bits type must have positive width" unless @width == :unknown || @width.positive? end - if kind == :enum - raise "Enum type must have width" unless @width - end - if kind == :array - raise "Array must have a subtype" unless @sub_type - end - if kind == :csr - raise 'CSR type must have a csr argument' if csr.nil? + raise "Enum type must have width" if kind == :enum && !@width + raise "Array must have a subtype" if kind == :array && !@sub_type + return unless kind == :csr + raise "CSR type must have a csr argument" if csr.nil? - @csr = csr - raise "CSR types must have a width" if width.nil? + @csr = csr + raise "CSR types must have a width" if width.nil? - @width = width - end + @width = width end TYPE_FROM_KIND = [:boolean, :void, :dontcare].map { |k| [k, Type.new(k)] }.to_h.freeze @@ -141,23 +141,23 @@ def comparable_to?(type) case @kind when :boolean - return type.kind == :boolean + type.kind == :boolean when :enum_ref - return \ - (type.kind == :enum_ref && type.enum_class.name == @enum_class.name) \ - || (type.kind == :enum && type.name == @enum_class.name) + \ + (type.kind == :enum_ref && type.enum_class.name == @enum_class.name) \ + || (type.kind == :enum && type.name == @enum_class.name) when :bits - return type.convertable_to?(self) + type.convertable_to?(self) when :enum - return type.convertable_to?(:bits) + type.convertable_to?(:bits) when :function # functions are not comparable to anything - return false + false when :csr - return ((type.kind == :csr) && (type.csr.name == @csr.name)) || - type.convertable_to?(Type.new(:bits, width: type.csr.width)) + ((type.kind == :csr) && (type.csr.name == @csr.name)) || + type.convertable_to?(Type.new(:bits, width: type.csr.width)) when :string - return type.kind == :string + type.kind == :string else raise "unimplemented #{@kind}" end @@ -211,64 +211,61 @@ def convertable_to?(type) case @kind when :boolean - return type.kind == :boolean + type.kind == :boolean when :enum_ref - return \ - (type.kind == :enum && type.name == @enum_class.name) || \ + \ + (type.kind == :enum && type.name == @enum_class.name) || \ (type.kind == :enum_ref && type.enum_class.name == @enum_class.name) when :dontcare - return true + true when :bits - return type.kind != :boolean + type.kind != :boolean when :function - return @return_type.convertable_to?(type) + @return_type.convertable_to?(type) when :enum if type.kind == :bits - return false + false # return (type.width == :unknown) || (width <= type.width) elsif type.kind == :enum - return type.enum_class == enum_class + type.enum_class == enum_class else - return false + false end when :tuple is_tuple_of_same_size = (type.kind == :tuple) && (@tuple_types.size == type.tuple_types.size) - if is_tuple_of_same_size - @tuple_types.each_index do |i| - unless @tuple_types[i].convertable_to?(type.tuple_types[i]) - return false - end - end - return true - else - return false + return false unless is_tuple_of_same_size + + @tuple_types.each_index do |i| + return false unless @tuple_types[i].convertable_to?(type.tuple_types[i]) end + true + when :csr - return (type.kind == :csr && type.csr.name == @csr.name) || type.convertable_to?(Type.new(:bits, width:)) + (type.kind == :csr && type.csr.name == @csr.name) || type.convertable_to?(Type.new(:bits, width:)) when :bitfield - if (type.kind == :bitfield && name == type.name) - return true - elsif (type.kind == :bits && type.width == @width) - return true + if type.kind == :bitfield && name == type.name + true + elsif type.kind == :bits && type.width == @width + true else # be strict with bitfields -- only accept integrals that are exact width Bit types - return false + false end when :array - return type.kind == :array && type.sub_type.convertable_to?(sub_type) && type.width == @width + type.kind == :array && type.sub_type.convertable_to?(sub_type) && type.width == @width when :string - return type.kind == :string + type.kind == :string when :void - return false + false when :struct - return type.kind == :struct && (type.type_name == type_name) + type.kind == :struct && (type.type_name == type_name) else raise "unimplemented type '#{@kind}'" end end def to_s - ((@qualifiers.nil? || @qualifiers.empty?) ? '' : "#{@qualifiers.map(&:to_s).join(' ')} ") + \ + (@qualifiers.nil? || @qualifiers.empty? ? "" : "#{@qualifiers.map(&:to_s).join(' ')} ") + \ if @kind == :bits "Bits<#{@width}>" elsif @kind == :enum @@ -280,7 +277,7 @@ def to_s elsif @kind == :enum_ref "enum #{@enum_class.name}" elsif @kind == :tuple - "(#{@tuple_types.map{ |t| t.to_s }.join(',')})" + "(#{@tuple_types.map { |t| t.to_s }.join(',')})" elsif @kind == :bitfield "bitfield #{@name}" elsif @kind == :array @@ -300,41 +297,45 @@ def to_s alias fully_qualified_name to_s def to_cxx_no_qualifiers - if @kind == :bits - raise "@width is unknown" if @width == :unknown - raise "@width is a #{@width.class}" unless @width.is_a?(Integer) + if @kind == :bits + raise "@width is unknown" if @width == :unknown + raise "@width is a #{@width.class}" unless @width.is_a?(Integer) - if signed? - "SignedBits<#{@width.is_a?(Integer) ? @width : @width.to_cxx}>" - else - "Bits<#{@width.is_a?(Integer) ? @width : @width.to_cxx}>" - end - elsif @kind == :enum - "#{@name}" - elsif @kind == :boolean - "bool" - elsif @kind == :function - "std::function<#{@return_type.to_cxx}(...)>" - elsif @kind == :enum_ref - "#{@enum_class.name}" - elsif @kind == :tuple - "std::tuple<#{@tuple_types.map{ |t| t.to_cxx }.join(',')}>" - elsif @kind == :bitfield - "#{@name}" - elsif @kind == :array - "#{@sub_type}[]" - elsif @kind == :csr - "#{@csr.downcase.capitalize}Csr" - elsif @kind == :string - "std::string" + if signed? + "SignedBits<#{@width.is_a?(Integer) ? @width : @width.to_cxx}>" else - raise @kind.to_s + "Bits<#{@width.is_a?(Integer) ? @width : @width.to_cxx}>" end + elsif @kind == :enum + "#{@name}" + elsif @kind == :boolean + "bool" + elsif @kind == :function + "std::function<#{@return_type.to_cxx}(...)>" + elsif @kind == :enum_ref + "#{@enum_class.name}" + elsif @kind == :tuple + "std::tuple<#{@tuple_types.map { |t| t.to_cxx }.join(',')}>" + elsif @kind == :bitfield + "#{@name}" + elsif @kind == :array + "#{@sub_type}[]" + elsif @kind == :csr + "#{@csr.downcase.capitalize}Csr" + elsif @kind == :string + "std::string" + else + raise @kind.to_s + end end def to_cxx - ((@qualifiers.nil? || @qualifiers.empty?) ? '' : "#{@qualifiers.include?(:const) ? 'const' : ''} ") + \ - to_cxx_no_qualifiers + (if @qualifiers.nil? || @qualifiers.empty? + "" + else + "#{@qualifiers.include?(:const) ? 'const' : ''} " + end) + \ + to_cxx_no_qualifiers end def name @@ -454,7 +455,7 @@ def self.from_json_schema_array_type(schema) sub_type = from_json_schema_scalar_type(item_schema) else unless sub_type.equal_to?(from_json_schema_scalar_type(item_schema)) - raise "Schema error: Array elements must be the same type (#{sub_type} #{from_json_schema_scalar_type(item_schema)}) \n#{schema["items"]}" + raise "Schema error: Array elements must be the same type (#{sub_type} #{from_json_schema_scalar_type(item_schema)}) \n#{schema['items']}" end end end @@ -492,9 +493,17 @@ def initialize(type_name, member_types, member_names) raise ArgumentError, "Argument 2 should be an array of types" unless member_types.is_a?(Array) - raise ArgumentError, "Argument 3 should be an array of names" unless member_names.is_a?(Array) && member_names.all? { |m| m.is_a?(String) } + unless member_names.is_a?(Array) && member_names.all? do |m| + m.is_a?(String) + end + raise ArgumentError, + "Argument 3 should be an array of names" + end - raise ArgumentError, "member_types and member_names must be the same size" unless member_names.size == member_types.size + unless member_names.size == member_types.size + raise ArgumentError, + "member_types and member_names must be the same size" + end super(:struct) @type_name = type_name @@ -590,9 +599,7 @@ def range(field_name) @field_ranges[i] end - def field_names - @field_names - end + attr_reader :field_names def clone BitfieldType.new( @@ -602,7 +609,6 @@ def clone @field_ranges ) end - end # represents a CSR register @@ -670,7 +676,9 @@ def templated? = @func_def_ast.templated? def apply_template_values(template_values, func_call_ast) func_call_ast.type_error "Missing template values" if templated? && template_values.empty? - func_call_ast.type_error "wrong number of template values in call to #{name}" unless template_names.size == template_values.size + unless template_names.size == template_values.size + func_call_ast.type_error "wrong number of template values in call to #{name}" + end symtab = @symtab.global_clone @@ -679,9 +687,13 @@ def apply_template_values(template_values, func_call_ast) symtab.push(func_call_ast) template_values.each_with_index do |value, idx| - func_call_ast.type_error "template value should be an Integer (found #{value.class.name})" unless value == :unknown || value.is_a?(Integer) + unless value == :unknown || value.is_a?(Integer) + func_call_ast.type_error "template value should be an Integer (found #{value.class.name})" + end - symtab.add!(template_names[idx], Var.new(template_names[idx], template_types(symtab)[idx], value, template_index: idx, function_name: @func_def_ast.name)) + symtab.add!(template_names[idx], + Var.new(template_names[idx], template_types(symtab)[idx], value, template_index: idx, + function_name: @func_def_ast.name)) end symtab end @@ -707,7 +719,7 @@ def apply_arguments(symtab, argument_nodes, call_site_symtab, func_call_ast) def argument_values(symtab, argument_nodes, call_site_symtab, func_call_ast) idx = 0 values = [] - @func_def_ast.arguments(symtab).each do |atype, aname| + @func_def_ast.arguments(symtab).each do |_atype, _aname| func_call_ast.type_error "Missing argument #{idx}" if idx >= argument_nodes.size value_result = Idl::AstNode.value_try do values << argument_nodes[idx].value(call_site_symtab) @@ -764,7 +776,7 @@ def return_types(template_values, argument_nodes, call_site_symtab, func_call_as types end - def argument_type(index, template_values, argument_nodes, call_site_symtab, func_call_ast) + def argument_type(index, template_values, _argument_nodes, _call_site_symtab, func_call_ast) return nil if index >= @func_def_ast.num_args symtab = apply_template_values(template_values, func_call_ast) @@ -817,11 +829,11 @@ def initialize(xlen) end def to_s - 'XReg' + "XReg" end def to_cxx - 'XReg' + "XReg" end end end diff --git a/lib/template_helpers.rb b/lib/template_helpers.rb index f42ef9603..87d485c2e 100644 --- a/lib/template_helpers.rb +++ b/lib/template_helpers.rb @@ -18,7 +18,7 @@ def link_to_ext(name) # @param ext_name [#to_s] Name of the extension # @param param_name [#to_s] Name of the parameter def link_to_ext_param(ext_name, param_name) - "<>" + "<>" end # Insert a hyperlink to an instruction. @@ -43,33 +43,33 @@ def link_to_csr_field(csr_name, field_name) # Insert anchor to an extension. # @param name [#to_s] Name of the extension def anchor_for_ext(name) - "[[ext-#{name.gsub(".", "_")}-def]]" + "[[ext-#{name.gsub('.', '_')}-def]]" end # Insert anchor to an extension parameter. # @param ext_name [#to_s] Name of the extension # @param param_name [#to_s] Name of the parameter def anchor_for_ext_param(ext_name, param_name) - "[[ext-#{ext_name.gsub(".", "_")}-param-#{param_name}-def]]" + "[[ext-#{ext_name.gsub('.', '_')}-param-#{param_name}-def]]" end # Insert anchor to an instruction. # @param name [#to_s] Name of the instruction def anchor_for_inst(name) - "[[inst-#{name.gsub(".", "_")}-def]]" + "[[inst-#{name.gsub('.', '_')}-def]]" end # Insert anchor to a CSR. # @param name [#to_s] Name of the CSR def anchor_for_csr(name) - "[[csr-#{name.gsub(".", "_")}-def]]" + "[[csr-#{name.gsub('.', '_')}-def]]" end # Insert anchor to a CSR field. # @param csr_name [#to_s] Name of the CSR # @param field_name [#to_s] Name of the CSR field def anchor_for_csr_field(csr_name, field_name) - "[[csr_field-#{csr_name.gsub(".", "_")}-#{field_name.gsub(".", "_")}-def]]" + "[[csr_field-#{csr_name.gsub('.', '_')}-#{field_name.gsub('.', '_')}-def]]" end def partial(template_path, locals = {}) diff --git a/lib/test/test_yaml_loader.rb b/lib/test/test_yaml_loader.rb index c5831736a..dbba0c134 100644 --- a/lib/test/test_yaml_loader.rb +++ b/lib/test/test_yaml_loader.rb @@ -19,7 +19,7 @@ def resolve_yaml(yaml) File.write(test_dir / "test.yaml", yaml) - stdout, stderr, status = + _, _, status = Dir.chdir($root) do Open3.capture3("/bin/bash -c \"source #{$root}/.home/.venv/bin/activate && #{$root}/.home/.venv/bin/python3 #{$root}/lib/yaml_resolver.py resolve --no-progress --no-checks #{arch_dir} #{resolved_dir}\"") end @@ -27,9 +27,7 @@ def resolve_yaml(yaml) # puts stderr # puts status - if status.to_i.zero? - YAML.load_file(resolved_dir / "test" / "test.yaml") - end + YAML.load_file(resolved_dir / "test" / "test.yaml") if status.to_i.zero? end end @@ -51,9 +49,7 @@ def resolve_multi_yaml(*yamls) system "/bin/bash -c \"source #{$root}/.home/.venv/bin/activate && #{$root}/.home/.venv/bin/python3 #{$root}/lib/yaml_resolver.py resolve --no-checks #{arch_dir} #{resolved_dir}\"" # `source #{$root}/.home/.venv/bin/activate && python3 #{$root}/lib/yaml_resolver.py resolve #{arch_dir} #{resolved_dir}` - if $CHILD_STATUS == 0 - YAML.load_file(resolved_dir / "test" / "test1.yaml") - end + YAML.load_file(resolved_dir / "test" / "test1.yaml") if $CHILD_STATUS == 0 end end @@ -115,7 +111,10 @@ def test_that_inherits_with_nested_replace_works YAML doc = resolve_yaml(yaml) - assert_equal({ "$child_of" => ["#/middle"], "key1" => { "sub_key1" => "value1", "sub_key6" => "value6" }, "key2" => "value2_new", "key3" => "value3", "key4" => "value4_new", "key5" => "value5" }, doc["bottom"]) + assert_equal( + { "$child_of" => ["#/middle"], "key1" => { "sub_key1" => "value1", "sub_key6" => "value6" }, "key2" => "value2_new", + "key3" => "value3", "key4" => "value4_new", "key5" => "value5" }, doc["bottom"] + ) end def test_that_recursive_inherits_works @@ -137,8 +136,14 @@ def test_that_recursive_inherits_works YAML doc = resolve_yaml(yaml) - assert_equal({ "$child_of" => "#/base", "$parent_of" => "test/test.yaml#/bottom", "key1" => "value1", "key2" => "value2", "key3" => "value3", "key4" => "value4" }, doc["middle"]) - assert_equal({ "$child_of" => "#/middle", "key1" => "value1", "key2" => "value2_new", "key3" => "value3", "key4" => "value4_new", "key5" => "value5" }, doc["bottom"]) + assert_equal( + { "$child_of" => "#/base", "$parent_of" => "test/test.yaml#/bottom", "key1" => "value1", "key2" => "value2", + "key3" => "value3", "key4" => "value4" }, doc["middle"] + ) + assert_equal( + { "$child_of" => "#/middle", "key1" => "value1", "key2" => "value2_new", "key3" => "value3", "key4" => "value4_new", + "key5" => "value5" }, doc["bottom"] + ) end def test_that_nested_inherits_works @@ -156,7 +161,8 @@ def test_that_nested_inherits_works YAML doc = resolve_yaml(yaml) - assert_equal({ "$child_of" => "#/top/base", "key1" => "value1", "key2" => "value2", "key3" => "value3_new" }, doc["bottom"]["child"]) + assert_equal({ "$child_of" => "#/top/base", "key1" => "value1", "key2" => "value2", "key3" => "value3_new" }, + doc["bottom"]["child"]) end def test_that_inherits_doesnt_delete_keys @@ -172,7 +178,8 @@ def test_that_inherits_doesnt_delete_keys YAML doc = resolve_yaml(yaml) - assert_equal({ "$child_of" => "#/base", "key1" => "value1", "key2" => "value2", "key3" => "value3_new" }, doc["child"]) + assert_equal({ "$child_of" => "#/base", "key1" => "value1", "key2" => "value2", "key3" => "value3_new" }, + doc["child"]) end def test_that_double_inherits_doesnt_delete_keys @@ -196,7 +203,10 @@ def test_that_double_inherits_doesnt_delete_keys YAML doc = resolve_yaml(yaml) - assert_equal({ "$child_of" => ["#/base1", "#/base2"], "key1" => "value1", "key2" => "value2", "key3" => "value3_new", "key4" => "value4", "key5" => "value5", "key6" => "value6_new" }, doc["child"]) + assert_equal( + { "$child_of" => ["#/base1", "#/base2"], "key1" => "value1", "key2" => "value2", "key3" => "value3_new", + "key4" => "value4", "key5" => "value5", "key6" => "value6_new" }, doc["child"] + ) end def test_inherits_in_the_same_document @@ -272,7 +282,7 @@ def test_inherits_entire_object doc = resolve_multi_yaml(yaml1, yaml2) assert_equal("test/test2.yaml#", doc["$child_of"]) assert_equal("Should take precedence", doc["target1"]) - assert_equal({ "a" => "hash", "sub1" => { "key_a" => "new_value_a", "key_b" => "old_value_b" }}, doc["target2"]) + assert_equal({ "a" => "hash", "sub1" => { "key_a" => "new_value_a", "key_b" => "old_value_b" } }, doc["target2"]) end def test_multi_inherits_in_the_same_document @@ -312,60 +322,60 @@ def test_that_invalid_inherits_raise end # Commented out until https://github.com/riscv-software-src/riscv-unified-db/issues/369 is fixed. -# def test_copy_in_the_same_document -# yaml = <<~YAML -# $defs: -# target1: A string -# target2: -# a: hash -# target3: Another string -# -# obj1: -# target10: abc -# target11: -# $copy: "#/$defs/target1" -# target12: def -# target13: -# $copy: "#/$defs/target3" -# -# YAML -# -# doc = resolve_yaml(yaml) -# assert_equal({ -# "$child_of" => "#/$defs", -# "target10" => "abc", -# "target11" => "A string", -# "target12" => "def", -# "target13" => "Another string" -# }, doc["obj1"]) -# end -# -# def test_copy_in_the_different_document -# yaml2 = <<~YAML -# $defs: -# target1: A string -# target2: -# a: hash -# target3: Another string -# YAML -# -# yaml1 = <<~YAML -# obj1: -# target10: abc -# target11: -# $copy: "YAML2_REL_PATH#/$defs/target1" -# target12: def -# target13: -# $copy: "YAML2_REL_PATH#/$defs/target3" -# YAML -# -# doc = resolve_multi_yaml(yaml1, yaml2) -# assert_equal({ -# "$child_of" => "test/test2.yaml#/$defs", -# "target10" => "abc", -# "target11" => "A string", -# "target12" => "def", -# "target13" => "Another string" -# }, doc["obj1"]) -# end + # def test_copy_in_the_same_document + # yaml = <<~YAML + # $defs: + # target1: A string + # target2: + # a: hash + # target3: Another string + # + # obj1: + # target10: abc + # target11: + # $copy: "#/$defs/target1" + # target12: def + # target13: + # $copy: "#/$defs/target3" + # + # YAML + # + # doc = resolve_yaml(yaml) + # assert_equal({ + # "$child_of" => "#/$defs", + # "target10" => "abc", + # "target11" => "A string", + # "target12" => "def", + # "target13" => "Another string" + # }, doc["obj1"]) + # end + # + # def test_copy_in_the_different_document + # yaml2 = <<~YAML + # $defs: + # target1: A string + # target2: + # a: hash + # target3: Another string + # YAML + # + # yaml1 = <<~YAML + # obj1: + # target10: abc + # target11: + # $copy: "YAML2_REL_PATH#/$defs/target1" + # target12: def + # target13: + # $copy: "YAML2_REL_PATH#/$defs/target3" + # YAML + # + # doc = resolve_multi_yaml(yaml1, yaml2) + # assert_equal({ + # "$child_of" => "test/test2.yaml#/$defs", + # "target10" => "abc", + # "target11" => "A string", + # "target12" => "def", + # "target13" => "Another string" + # }, doc["obj1"]) + # end end diff --git a/lib/version.rb b/lib/version.rb index 6151c810a..9fbb2a0fd 100644 --- a/lib/version.rb +++ b/lib/version.rb @@ -43,17 +43,16 @@ class VersionSpec attr_reader :pre def initialize(version_str) - if version_str =~ /^\s*#{VERSION_REGEX}\s*$/ - m = ::Regexp.last_match - @major = m[1].to_i - @minor_given = !m[2].nil? - @minor = @minor_given ? m[2].to_i : 0 - @patch_given = !m[3].nil? - @patch = @patch_given ? m[3].to_i : 0 - @pre = !m[4].nil? - else - raise ArgumentError, "#{version_str} is not a valid Version spec" - end + raise ArgumentError, "#{version_str} is not a valid Version spec" unless version_str =~ /^\s*#{VERSION_REGEX}\s*$/ + + m = ::Regexp.last_match + @major = m[1].to_i + @minor_given = !m[2].nil? + @minor = @minor_given ? m[2].to_i : 0 + @patch_given = !m[3].nil? + @patch = @patch_given ? m[3].to_i : 0 + @pre = !m[4].nil? + @version_str = version_str end @@ -142,14 +141,12 @@ def initialize(requirement) raise ArgumentError, "requirement must be a string (is a #{requirement.class.name})" end - if requirement =~ /^\s*#{REQUIREMENT_REGEX}\s*$/ - m = ::Regexp.last_match - @op = m[1] - @version_str = m[2] - @version_spec = VersionSpec.new(@version_str) - else - raise ArgumentError, "Bad requirement string '#{requirement}'" - end + raise ArgumentError, "Bad requirement string '#{requirement}'" unless requirement =~ /^\s*#{REQUIREMENT_REGEX}\s*$/ + + m = ::Regexp.last_match + @op = m[1] + @version_str = m[2] + @version_spec = VersionSpec.new(@version_str) end def to_s