Class: Udb::NonIsaSpecification

Inherits:
Object
  • Object
show all
Extended by:
T::Sig
Includes:
Kernel
Defined in:
lib/udb/obj/non_isa_specification.rb

Overview

Clean non-ISA specification object that handles YAML-based specifications

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, data) ⇒ NonIsaSpecification

Returns a new instance of NonIsaSpecification.



40
41
42
43
44
45
46
# File 'lib/udb/obj/non_isa_specification.rb', line 40

def initialize(name, data)
  @name = name
  @data = data.dup
  @spec_data = nil
  @spec_path = nil
  load_spec_data
end

Instance Attribute Details

#dataObject (readonly)

Returns the value of attribute data.



31
32
33
# File 'lib/udb/obj/non_isa_specification.rb', line 31

def data
  @data
end

#nameObject (readonly)

Returns the value of attribute name.



28
29
30
# File 'lib/udb/obj/non_isa_specification.rb', line 28

def name
  @name
end

#spec_dataObject (readonly)

Returns the value of attribute spec_data.



37
38
39
# File 'lib/udb/obj/non_isa_specification.rb', line 37

def spec_data
  @spec_data
end

#spec_pathObject (readonly)

Returns the value of attribute spec_path.



34
35
36
# File 'lib/udb/obj/non_isa_specification.rb', line 34

def spec_path
  @spec_path
end

Instance Method Details

#defined_by_conditionObject

Return configuration conditions that enable this spec



136
137
138
139
140
141
142
143
144
# File 'lib/udb/obj/non_isa_specification.rb', line 136

def defined_by_condition
  when_condition = @data['when()'] || @spec_data&.dig('when()')
  return OpenStruct.new(to_asciidoc: "Always included") if when_condition.nil?

  # Convert condition to human-readable description
  OpenStruct.new(
    to_asciidoc: "When #{when_condition}"
  )
end

#exists_in_cfg?(cfg_arch) ⇒ Boolean

Check if this non-ISA spec should be included in the given configuration

Returns:

  • (Boolean)


109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/udb/obj/non_isa_specification.rb', line 109

def exists_in_cfg?(cfg_arch)
  return false unless valid?

  # Check configuration conditions if present
  when_condition = @data['when()'] || @spec_data&.dig('when()')
  return true if when_condition.nil?

  # Evaluate condition against cfg_arch
  # For now, simple string matching - could be enhanced with expression evaluation
  case when_condition
  when String
    # Basic string matching for now
    cfg_arch.param_values.any? { |k, v| when_condition.include?(k.to_s) }
  else
    true
  end
end

#extract_prose_statements(include_sections: true) ⇒ Object

Extract all prose statements from description and sections, tagging their source.



177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/udb/obj/non_isa_specification.rb', line 177

def extract_prose_statements(include_sections: true)
  statements = []
  # Extract from description
  if spec_description.is_a?(Array)
    spec_description.each_with_index { |stmt, i| statements << stmt.merge(source: "description[#{i}]") if stmt.is_a?(Hash) }
  end
  # Extract from sections
  if include_sections
    sections.each_with_index do |section, section_idx|
      next unless section['content'].is_a?(Array)
      section['content'].each_with_index do |stmt, stmt_idx|
        statements << stmt.merge(source: "sections[#{section_idx}].content[#{stmt_idx}]") if stmt.is_a?(Hash)
      end
    end
  end
  statements
end

#load_spec_dataObject



50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/udb/obj/non_isa_specification.rb', line 50

def load_spec_data
  base_path = Pathname.new(__dir__).parent.parent.parent.parent.parent.parent / "spec/custom/non_isa"
  @spec_path = base_path / "#{name}.yaml"
  return unless @spec_path.exist?

  begin
    @spec_data = YAML.safe_load_file(@spec_path, permitted_classes: [])
  rescue Psych::SyntaxError => e
    raise NonIsaSpecificationLoadError, "YAML syntax error in #{@spec_path}: #{e.message}"
  rescue => e
    raise NonIsaSpecificationLoadError, "Failed to load #{@spec_path}: #{e.message}"
  end
end

#long_nameObject

Get the long name for display



75
76
77
78
79
80
# File 'lib/udb/obj/non_isa_specification.rb', line 75

def long_name
  return @data['long_name'] if @data['long_name']
  return @spec_data['long_name'] if @spec_data&.dig('long_name')
  return @spec_data['name'] if @spec_data&.dig('name')
  name.split('_').map(&:capitalize).join(' ')
end

#optional_in_cfg?(cfg_arch) ⇒ Boolean

Check if this spec is optional in the given configuration

Returns:

  • (Boolean)


129
130
131
132
# File 'lib/udb/obj/non_isa_specification.rb', line 129

def optional_in_cfg?(cfg_arch)
  # Non-ISA specs are generally optional unless marked as mandatory
  !(@data['mandatory'] == true || @spec_data&.dig('mandatory') == true)
end

#referencesObject

Get the array of reference hashes.



102
103
104
# File 'lib/udb/obj/non_isa_specification.rb', line 102

def references
  @spec_data&.dig('references') || []
end

#render_for_cfg(cfg_arch, base_level: 3, normative: true, non_normative: true) ⇒ Object

Configuration-aware rendering



205
206
207
208
209
210
211
212
213
214
# File 'lib/udb/obj/non_isa_specification.rb', line 205

def render_for_cfg(cfg_arch, base_level: 3, normative: true, non_normative: true)
  return "" unless exists_in_cfg?(cfg_arch)

  to_asciidoc(
    base_level: base_level,
    normative: normative,
    non_normative: non_normative,
    when_callback: create_when_callback(cfg_arch)
  )
end

#sectionsObject

Get the array of section hashes.



96
97
98
# File 'lib/udb/obj/non_isa_specification.rb', line 96

def sections
  @spec_data&.dig('sections') || []
end

#spec_descriptionObject

Get the main description prose array or string.



90
91
92
# File 'lib/udb/obj/non_isa_specification.rb', line 90

def spec_description
  @spec_data&.dig('description')
end

#to_asciidoc(base_level: 3, normative: true, non_normative: true, when_callback: nil) ⇒ Object

Render the full specification as AsciiDoc, including description, sections, and references.



225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/udb/obj/non_isa_specification.rb', line 225

def to_asciidoc(base_level: 3, normative: true, non_normative: true, when_callback: nil)
  return create_fallback_content(base_level) unless valid?

  content = []

  # Add main description prose
  desc_content = render_structured_prose(spec_description, normative: normative, non_normative: non_normative, when_callback: when_callback)
  content << desc_content if desc_content && !desc_content.empty?
  content << ""

  # Process all sections
  content.concat(render_sections(base_level, normative, non_normative, when_callback))

  # Add references section if present
  content.concat(render_references(base_level)) unless references.empty?

  content.join("\n")
end

#valid?Boolean

Returns true if the spec is valid and has required fields.

Returns:

  • (Boolean)


66
67
68
69
70
71
# File 'lib/udb/obj/non_isa_specification.rb', line 66

def valid?
  !@spec_data.nil? &&
    @spec_data['kind'] == 'non-isa specification' &&
    !@spec_data['name'].nil? &&
    !@spec_data['description'].nil?
end

#validate_prose_idsObject

Validate all prose statement IDs and conventions for this spec.



149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/udb/obj/non_isa_specification.rb', line 149

def validate_prose_ids
  return [] unless valid?

  issues = []
  statements = extract_prose_statements
  statements.each do |stmt|
    next unless stmt['id']
    id = stmt['id']
    source = stmt[:source] || 'unknown'

    # Check ID format according to prose-schema conventions
    unless valid_id_format?(id)
      issues << "Invalid ID format '#{id}' in #{source}: must be lowercase with underscores/hyphens only"
    end

    # Check for non-ISA specification naming convention
    unless valid_id_naming?(id)
      issues << "ID '#{id}' in #{source} should start with '#{name.downcase}-' for non-ISA specifications"
    end
  end

  # Check for duplicate IDs
  issues.concat(find_duplicate_ids(statements))
  issues
end

#versionObject

Get the version if available



84
85
86
# File 'lib/udb/obj/non_isa_specification.rb', line 84

def version
  @spec_data&.dig('version')
end