# encoding: utf-8 # frozen_string_literal: true require "carbon/compiler/metanostic/defaults" require "carbon/compiler/metanostic/diagnostic" require "carbon/compiler/metanostic/list" require "carbon/compiler/metanostic/mode" require "carbon/compiler/metanostic/state" module Carbon module Compiler # A "Metanostic." A "metanostic" has information about what a diagnostic # could be about. For example, a `Diagnostic/Unknown` diagnostic has # metanostic information; the name itself, the default mode, and the # allowed modes are a part of that metanostic information. Diagnostics # are generated from both metanostic information and environmental # information. class Metanostic include Comparable # The name of the metanostic. This is used as a unique identifier for # the metanostic. This is a string, and is normally represented as a # path of CamelCase names. For example, a name might be # `Diagnostic/Unknown`. # # @return [::String] attr_reader :name # The allowed modes of a metanostic or diagnostic. This limits how a # diagnostic can be categorized. This is a bitfield of modes. If a # metanostic can be any mode, this is `Metanostic::Mode::ALL`; otherwise, # it is a union of all of the allowed modes of a diagnostic. # # @see Metanostic::Mode # @return [::Integer] attr_reader :allowed # The default mode of derived diagnostics. This is and must be an # allowed diagnostic. # # @see #allowed # @see Metanostic::Mode # @return [::Integer] attr_reader :default # The default message of derived diagnostics, if the diagnostic does not # provide one. This is used to provide a generic message. # # @return [::String] attr_reader :message # Initialize the diagnostic with the given data. # # @param data [::Hash{::Symbol,::String => ::Object}] # @option data [::String] :name The name of the metanostic. # @option data [::Integer] :allowed The allowed modes of derived # diagnostics. # @option data [::Integer] :default The default mode of derived # diagnostics. # @option data [::String] :message The default message of derived # diagnostics. # @raise [::KeyError] If one of the option keys is not present. def initialize(data) @name = data.fetch(:name) { data.fetch("name") } @allowed = data.fetch(:allowed) { data.fetch("allowed") } self.default = data.fetch(:default) { data.fetch("default") } @message = data.fetch(:message) { data.fetch("message") } end # Compares this with another metanostic. This should only be used for # equality; it makes no sense otherwise. # # @param other [Metanostic] The metanostic to compare to. # @return [::Numeric] def <=>(other) fail ArgumentError, "Expected Metanostic" unless other.is_a?(Metanostic) to_a <=> other.to_a end # Returns a numeric representation of this class for use of hashing. # # @return [::Numeric] def hash to_a.hash end # Returns an array representation of this class. This is frozen. # # @return [(::Class, ::String, ::Integer, ::Integer, ::String)] def to_a [self.class, @name, @allowed, @default, @message].freeze end # Pretty inspect. # # @return [::String] def inspect "#<#{self.class} #{@name}(#{Mode.to_s(@default)}) " \ "[#{Mode.to_s(@allowed)}]>" end # Sets the default mode of the metanostic to the given mode. The # metanostic must have the corresponding allowed mode bit set. # # @see Metanostic::Mode # @raise [DiagnosticError] If the metanostic cannot have the given mode # as the default. # @return [::Integer] The new mode. def default=(mode) unless can_be?(mode) fail DiagnosticError, "#{mode} is not allowed as a default" end @default = mode end # Checks to see if the metanostic or any derived diagnostics can have the # given mode. # # @see Metanostic::Mode # @param mode [Integer] The mode. # @return [Boolean] def can_be?(mode) Mode.singular?(mode) && Mode.is?(allowed, mode) end end end end