Skip to content

Commit d729038

Browse files
author
Derek Hower
committed
Add $mref to inst variables
Also enhances arch_gen to expand references. Supports $ref (JSON Reference) and $mref (merged ref, doesn't ignore other keys in the object) Difference: (in something.yaml) key: ref_extra: Hello! obj: $ref: path/to/something.yaml#/key extra: Hi! => obj = { ref_extra: "Hello!" } obj: $mref: path/to/something.yaml#/key extra: Hi! => obj = { extra: "Hi!", ref_extra: "Hello!" }
1 parent e437ad2 commit d729038

File tree

7 files changed

+169
-42
lines changed

7 files changed

+169
-42
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# yaml-language-server: $schema=../../schemas/inst_variable_metadatas.json
2+
---
3+
4+
itype_imm:
5+
location: 31-20

arch/inst/I/addi.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ addi:
99
match: -----------------000-----0010011
1010
variables:
1111
- name: imm
12-
location: 31-20
12+
$mref: ../../common/inst_variable_types.yaml#/itype_imm
1313
- name: rs1
1414
location: 19-15
1515
- name: rd

backends/arch_gen/lib/arch_gen.rb

Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
require "rake/application"
77
require "rubygems/requirement"
88
require "tilt"
9-
require "yaml"
109

1110
require_relative "#{$lib}/validate"
1211
require_relative "#{$lib}/arch_def"
@@ -53,7 +52,7 @@ def gen_params_schema
5352
@implemented_extensions.each do |ext|
5453
ext_name = ext["name"]
5554
gen_ext_path = @gen_dir / "arch" / "ext" / "#{ext_name}.yaml"
56-
ext_yaml = YAML.safe_load gen_ext_path.read
55+
ext_yaml = YamlLoader.load gen_ext_path.to_s
5756
unless ext_yaml[ext_name]["params"].nil?
5857
ext_yaml[ext_name]["params"].each do |param_name, param_data|
5958
schema["properties"]["params"]["required"] << param_name
@@ -92,7 +91,7 @@ def initialize(config_name)
9291
raise "Validation failed" if @cfg_impl_ext.nil?
9392

9493
cfg_opts_path = @cfg_dir / "cfg.yaml"
95-
@cfg_opts = YAML.load_file(cfg_opts_path)
94+
@cfg_opts = YamlLoader.load(cfg_opts_path)
9695
raise "Validation failed" if @cfg_opts.nil?
9796
raise "Validation failed: bad type" unless ["partially configured", "fully configured"].include?(@cfg_opts["type"])
9897

@@ -147,7 +146,7 @@ def params_extra_validation
147146
@implemented_extensions.each do |ext|
148147
ext_name = ext["name"]
149148
gen_ext_path = @gen_dir / "arch" / "ext" / "#{ext_name}.yaml"
150-
ext_yaml = YAML.safe_load gen_ext_path.read
149+
ext_yaml = YamlLoader.load gen_ext_path.read
151150
unless ext_yaml[ext_name]["params"].nil?
152151
ext_yaml[ext_name]["params"].each do |param_name, param_data|
153152
next unless param_data.key?("extra_validation")
@@ -275,63 +274,63 @@ def implemented_extensions
275274
#
276275
def gen_arch_def
277276
csr_hash = Dir.glob(@gen_dir / "arch" / "csr" / "**" / "*.yaml").map do |f|
278-
csr_obj = YAML.load_file(f)
277+
csr_obj = YamlLoader.load(f)
279278
csr_name = csr_obj.keys[0]
280279
[csr_name, csr_obj[csr_name]]
281280
end.to_h
282281
inst_hash = Dir.glob(@gen_dir / "arch" / "inst" / "**" / "*.yaml").map do |f|
283-
inst_obj = YAML.load_file(f)
282+
inst_obj = YamlLoader.load(f)
284283
inst_name = inst_obj.keys[0]
285284
[inst_name, inst_obj[inst_name]]
286285
end.to_h
287286
ext_hash = Dir.glob(@gen_dir / "arch" / "ext" / "**" / "*.yaml").map do |f|
288-
ext_obj = YAML.load_file(f)
287+
ext_obj = YamlLoader.load(f)
289288
ext_name = ext_obj.keys[0]
290289
[ext_name, ext_obj[ext_name]]
291290
end.to_h
292291
profile_family_hash = Dir.glob($root / "arch" / "profile_family" / "**" / "*.yaml").map do |f|
293-
profile_obj = YAML.load_file(f)
292+
profile_obj = YamlLoader.load(f)
294293
profile_name = profile_obj.keys[0]
295294
profile_obj[profile_name]["name"] = profile_name
296295
profile_obj[profile_name]["__source"] = f
297296
[profile_name, profile_obj[profile_name]]
298297
end.to_h
299298
profile_hash = Dir.glob($root / "arch" / "profile" / "**" / "*.yaml").map do |f|
300-
profile_obj = YAML.load_file(f)
299+
profile_obj = YamlLoader.load(f)
301300
profile_name = profile_obj.keys[0]
302301
profile_obj[profile_name]["name"] = profile_name
303302
profile_obj[profile_name]["__source"] = f
304303
[profile_name, profile_obj[profile_name]]
305304
end.to_h
306305
manual_hash = {}
307306
Dir.glob($root / "arch" / "manual" / "**" / "contents.yaml").map do |f|
308-
manual_version = YAML.load_file(f)
307+
manual_version = YamlLoader.load(f)
309308
manual_id = manual_version["manual"]
310309
unless manual_hash.key?(manual_id)
311310
manual_info_files = Dir.glob($root / "arch" / "manual" / "**" / "#{manual_id}.yaml")
312311
raise "Could not find manual info '#{manual_id}'.yaml, needed by #{f}" if manual_info_files.empty?
313312
raise "Found multiple manual infos '#{manual_id}'.yaml, needed by #{f}" if manual_info_files.size > 1
314313

315314
manual_info_file = manual_info_files.first
316-
manual_hash[manual_id] = YAML.load_file(manual_info_file)
315+
manual_hash[manual_id] = YamlLoader.load(manual_info_file)
317316
manual_hash[manual_id]["__source"] = manual_info_file
318317
# TODO: schema validation
319318
end
320319

321320
manual_hash[manual_id]["versions"] ||= []
322-
manual_hash[manual_id]["versions"] << YAML.load_file(f)
321+
manual_hash[manual_id]["versions"] << YamlLoader.load(f)
323322
# TODO: schema validation
324323
manual_hash[manual_id]["versions"].last["__source"] = f
325324
end
326325
crd_family_hash = Dir.glob($root / "arch" / "crd_family" / "**" / "*.yaml").map do |f|
327-
family_obj = YAML.load_file(f, permitted_classes: [Date])
326+
family_obj = YamlLoader.load(f, permitted_classes: [Date])
328327
family_name = family_obj.keys[0]
329328
family_obj[family_name]["name"] = family_name
330329
family_obj[family_name]["__source"] = f
331330
[family_name, family_obj[family_name]]
332331
end.to_h
333332
crd_hash = Dir.glob($root / "arch" / "crd" / "**" / "*.yaml").map do |f|
334-
crd_obj = YAML.load_file(f, permitted_classes: [Date])
333+
crd_obj = YamlLoader.load(f, permitted_classes: [Date])
335334
crd_name = crd_obj.keys[0]
336335
crd_obj[crd_name]["name"] = crd_name
337336
crd_obj[crd_name]["__source"] = f
@@ -590,7 +589,7 @@ def gen_rendered_arch_def(type, name, extra_env = {})
590589
raise e
591590
end
592591

593-
def_obj = YAML.safe_load(rendered_def)
592+
def_obj = YamlLoader.load(rendered_def)
594593

595594
raise "#{type} name ('#{name}') must match key in defintion ('#{def_obj.keys[0]}')" if name.to_s != def_obj.keys[0]
596595

@@ -626,7 +625,7 @@ def gen_rendered_arch_overlay_def(type, name, extra_env = {})
626625
extra_env&.each { |k, v| current_env.define_singleton_method(k) { v } }
627626
rendered_def = Tilt["erb"].new(source_path, trim: "-").render(current_env)
628627

629-
def_obj = YAML.safe_load(rendered_def)
628+
def_obj = YamlLoader.load(rendered_def)
630629

631630
raise "#{type} name (#{name}) must match key in defintion (#{def_obj.keys[0]})" if name.to_s != def_obj.keys[0]
632631

@@ -672,8 +671,8 @@ def gen_merged_def(type, arch_path, overlay_path)
672671
FileUtils.cp overlay_path, merged_path
673672
else
674673
# arch and overlay, do the merge
675-
arch_obj = YAML.load_file(arch_path)
676-
overlay_obj = YAML.load_file(overlay_path)
674+
arch_obj = YamlLoader.load(arch_path)
675+
overlay_obj = YamlLoader.load(overlay_path)
677676

678677
merge(arch_obj, overlay_obj)
679678
merged_path.write YAML.dump(arch_obj)
@@ -722,7 +721,7 @@ def maybe_add_csr(csr_name, extra_env = {})
722721
end
723722

724723
# get the csr data (not including the name key), which is redundant at this point
725-
csr_data = YAML.load_file(merged_path)[csr_name]
724+
csr_data = YamlLoader.load(merged_path)[csr_name]
726725
csr_data["name"] = csr_name
727726
csr_data["fields"].each { |n, f| f["name"] = n }
728727
csr_data["__source"] = og_path.to_s
@@ -805,7 +804,7 @@ def maybe_add_ext(ext_name)
805804

806805
merged_path = gen_merged_def(:ext, arch_path, arch_overlay_path)
807806

808-
yaml_contents = YAML.load_file(merged_path)
807+
yaml_contents = YamlLoader.load(merged_path)
809808
raise "In #{merged_path}, key does not match file name" unless yaml_contents.key?(ext_name)
810809

811810
ext_obj = yaml_contents[ext_name]
@@ -884,7 +883,7 @@ def exception_codes
884883

885884
@exception_codes = {}
886885
Dir.glob(@gen_dir / "arch" / "ext" / "*.yaml") do |ext_path|
887-
ext_obj = YAML.load_file(ext_path)
886+
ext_obj = YamlLoader.load(ext_path)
888887
ext_obj = ext_obj[ext_obj.keys[0]]
889888
if ext_obj.key?("exception_codes")
890889
ext_obj["exception_codes"].each do |exception_code|
@@ -905,7 +904,7 @@ def interrupt_codes
905904

906905
@interrupt_codes = {}
907906
Dir.glob(@gen_dir / "arch" / "ext" / "*.yaml") do |ext_path|
908-
ext_obj = YAML.load_file(ext_path)
907+
ext_obj = YamlLoader.load(ext_path)
909908
ext_obj = ext_obj[ext_obj.keys[0]]
910909
if ext_obj.key?("interrupt_codes")
911910
ext_obj["interrupt_codes"].each do |interrupt_code|
@@ -967,7 +966,7 @@ def maybe_add_inst(inst_name, extra_env = {})
967966
end
968967

969968
# get the inst data (not including the name key), which is redundant at this point
970-
inst_data = YAML.load_file(merged_path)
969+
inst_data = YamlLoader.load(merged_path)
971970
raise "The first and only key of #{arch_path} must be '#{inst_name}" unless inst_data.key?(inst_name)
972971
inst_data = inst_data[inst_name]
973972

backends/arch_gen/tasks.rake

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
# This file contains tasks related to the generation of a configured architecture specification
44

5+
require_relative "../../lib/yaml_loader"
56
require_relative "lib/arch_gen"
67

78
ARCH_GEN_DIR = Pathname.new(__FILE__).dirname
@@ -33,7 +34,7 @@ file "#{$root}/.stamps/arch-gen.stamp" => (
3334
] + Dir.glob($root / "arch" / "**" / "*.yaml")
3435
) do |t|
3536
csr_hash = Dir.glob($root / "arch" / "csr" / "**" / "*.yaml").map do |f|
36-
csr_obj = YAML.load_file(f)
37+
csr_obj = YamlLoader.load(f)
3738
csr_name = csr_obj.keys[0]
3839
csr_obj[csr_name]["name"] = csr_name
3940
csr_obj[csr_name]["fields"].map do |k, v|
@@ -44,62 +45,62 @@ file "#{$root}/.stamps/arch-gen.stamp" => (
4445
[csr_name, csr_obj[csr_name]]
4546
end.to_h
4647
inst_hash = Dir.glob($root / "arch" / "inst" / "**" / "*.yaml").map do |f|
47-
inst_obj = YAML.load_file(f)
48+
inst_obj = YamlLoader.load(f)
4849
inst_name = inst_obj.keys[0]
4950
inst_obj[inst_name]["name"] = inst_name
5051
inst_obj[inst_name]["__source"] = f
5152
[inst_name, inst_obj[inst_name]]
5253
end.to_h
5354
ext_hash = Dir.glob($root / "arch" / "ext" / "**" / "*.yaml").map do |f|
54-
ext_obj = YAML.load_file(f)
55+
ext_obj = YamlLoader.load(f)
5556
ext_name = ext_obj.keys[0]
5657
ext_obj[ext_name]["name"] = ext_name
5758
ext_obj[ext_name]["__source"] = f
5859
[ext_name, ext_obj[ext_name]]
5960
end.to_h
6061
profile_family_hash = Dir.glob($root / "arch" / "profile_family" / "**" / "*.yaml").map do |f|
61-
profile_obj = YAML.load_file(f)
62+
profile_obj = YamlLoader.load(f)
6263
profile_name = profile_obj.keys[0]
6364
profile_obj[profile_name]["name"] = profile_name
6465
profile_obj[profile_name]["__source"] = f
6566
[profile_name, profile_obj[profile_name]]
6667
end.to_h
6768
profile_hash = Dir.glob($root / "arch" / "profile" / "**" / "*.yaml").map do |f|
68-
profile_obj = YAML.load_file(f)
69+
profile_obj = YamlLoader.load(f)
6970
profile_name = profile_obj.keys[0]
7071
profile_obj[profile_name]["name"] = profile_name
7172
profile_obj[profile_name]["__source"] = f
7273
[profile_name, profile_obj[profile_name]]
7374
end.to_h
7475
manual_hash = {}
7576
Dir.glob($root / "arch" / "manual" / "**" / "contents.yaml").map do |f|
76-
manual_version = YAML.load_file(f)
77+
manual_version = YamlLoader.load(f)
7778
manual_id = manual_version["manual"]
7879
unless manual_hash.key?(manual_id)
7980
manual_info_files = Dir.glob($root / "arch" / "manual" / "**" / "#{manual_id}.yaml")
8081
raise "Could not find manual info '#{manual_id}'.yaml, needed by #{f}" if manual_info_files.empty?
8182
raise "Found multiple manual infos '#{manual_id}'.yaml, needed by #{f}" if manual_info_files.size > 1
8283

8384
manual_info_file = manual_info_files.first
84-
manual_hash[manual_id] = YAML.load_file(manual_info_file)
85+
manual_hash[manual_id] = YamlLoader.load(manual_info_file)
8586
manual_hash[manual_id]["__source"] = manual_info_file
8687
# TODO: schema validation
8788
end
8889

8990
manual_hash[manual_id]["versions"] ||= []
90-
manual_hash[manual_id]["versions"] << YAML.load_file(f)
91+
manual_hash[manual_id]["versions"] << YamlLoader.load(f)
9192
# TODO: schema validation
9293
manual_hash[manual_id]["versions"].last["__source"] = f
9394
end
9495
crd_family_hash = Dir.glob($root / "arch" / "crd_family" / "**" / "*.yaml").map do |f|
95-
family_obj = YAML.load_file(f, permitted_classes: [Date])
96+
family_obj = YamlLoader.load(f, permitted_classes: [Date])
9697
family_name = family_obj.keys[0]
9798
family_obj[family_name]["name"] = family_name
9899
family_obj[family_name]["__source"] = f
99100
[family_name, family_obj[family_name]]
100101
end.to_h
101102
crd_hash = Dir.glob($root / "arch" / "crd" / "**" / "*.yaml").map do |f|
102-
crd_obj = YAML.load_file(f, permitted_classes: [Date])
103+
crd_obj = YamlLoader.load(f, permitted_classes: [Date])
103104
crd_name = crd_obj.keys[0]
104105
crd_obj[crd_name]["name"] = crd_name
105106
crd_obj[crd_name]["__source"] = f

lib/yaml_loader.rb

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# frozen_string_literal: true
2+
3+
require "yaml"
4+
5+
# loads a YAML file and expands any $ref/$mref references
6+
class YamlLoader
7+
@cache = {}
8+
9+
def self.expand(filename, obj, yaml_opts = {})
10+
return obj unless obj.is_a?(Hash) || obj.is_a?(Array)
11+
12+
return obj.map { |v| expand(filename, v, yaml_opts) } if obj.is_a?(Array)
13+
14+
if obj.keys.include?("$ref")
15+
# according JSON Reference, all keys except $ref are ignored
16+
target_filename = File.dirname(obj["$ref"]) + value.split("#")[0]
17+
obj = YamlLoader.load(target_filename, yaml_opts).dig(*value.spilt("#")[1].split("/"))
18+
else
19+
obj_keys = obj.keys
20+
obj_keys.each do |key|
21+
value = obj[key]
22+
23+
if key == "$mref"
24+
relative_path = value.split("#")[0]
25+
target_filename = File.realpath(File.join(File.dirname(filename), relative_path))
26+
raise "#{target_filename} does not exist" unless File.exist?(target_filename)
27+
28+
target_obj = YamlLoader.load(target_filename, yaml_opts)
29+
target_obj = target_obj.dig(*value.split("#/")[1].split("/"))
30+
target_obj.each { |target_key, target_value|
31+
obj[target_key] = expand(filename, target_value, yaml_opts)
32+
}
33+
obj.delete("$mref")
34+
else
35+
obj[key] = expand(filename, value, yaml_opts)
36+
end
37+
end
38+
end
39+
obj
40+
end
41+
42+
# load a YAML file and expand any $ref/$mref references
43+
# @param filename [String,Pathname] path to the YAML file
44+
# @param yaml_opts [Hash] options to pass to YAML.load_file
45+
# @return [Object] the loaded YAML file
46+
def self.load(filename, yaml_opts = {})
47+
raise ArgumentError, "Cannot find file #{filename}" unless File.exist?(filename)
48+
49+
filename = File.realpath(filename)
50+
return @cache[filename] if @cache.key?(filename)
51+
52+
obj = YAML.load_file(filename, **yaml_opts)
53+
obj = expand(filename, obj, yaml_opts) if obj.is_a?(Hash)
54+
55+
@cache[filename] = obj
56+
end
57+
end

0 commit comments

Comments
 (0)