Class: Carbon::Concrete::Index

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Includes:
Enumerable, TSort
Defined in:
lib/carbon/concrete/index.rb

Overview

A list of all of the "items" in the global context of Carbon. Items are mostly functions and data definitions. This keeps track of them and their dependencies, so that they can be built later. Indexes are also designed to be easily serialized if needed (which is likely the case, for libraries).

Constant Summary collapse

ITEMS =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Used only for #define. This maps item "names" to the classes that represent them.

Returns:

{
  internal: Concrete::Item::Internal,
  function: Concrete::Item::Function,
  struct: Concrete::Item::Struct,
  trait: Concrete::Item::Trait
}.freeze

Instance Method Summary collapse

Constructor Details

#initialize(data = {}) ⇒ Index

Initialize the index with the given data.

Parameters:

  • data ({::Symbol => ::Object}) (defaults to: {})

    The data to initialize with.

Options Hash (data):

  • :items (::Hash) — default: {}

    The definitions.

  • :id (::String) — default: #id

    The ID of the index. This is completely derived from the index itself; or, rather, what's defined on the index. See #id.



41
42
43
44
45
# File 'lib/carbon/concrete/index.rb', line 41

def initialize(data = {})
  @items = ::Set.new.merge(data.fetch(:items, []))
  @links = ::Set.new
  @id = data.fetch(:id) { id }
end

Instance Method Details

#add(item) ⇒ self Also known as: <<

Adds a defined item to the index. The item just has to respond to the item API (see Carbon::Concrete::Item). This clears the index's cache, such that the ID (#id) and item list (#items) are reset and have to be recalculated.

Examples:

Adding an item.

index << item
index.key?(item.intern) # => true

Parameters:

Returns:

  • (self)


110
111
112
113
# File 'lib/carbon/concrete/index.rb', line 110

def add(item)
  @items << item
  clear!
end

#build(item) {|dep| ... } ⇒ ::Enumerable, void

Yields the builds that need to be built for the given item. This uses the TSort module in order to determine all of the dependencies the given item has, and yields each; and finally, yields the last item. The item may not have any generics, even if they are fully formed generics (e.g. have a definition). If any items have a cyclic depdendency, it fails.

Examples:

Build request.

request.intern # => "Main"
request.generics # => []
index.build(request).to_a # => [...]

Parameters:

Yields:

  • (dep)

    Multiple times, each with a dependency.

Yield Parameters:

  • dep (Request)

    A request.

Returns:

  • (::Enumerable)

    The build items, if no block is given.

  • (void)

    If a block is given.



204
205
206
207
208
209
210
# File 'lib/carbon/concrete/index.rb', line 204

def build(item)
  fail ArgumentError, "Passed item cannot be generic" if \
    item.generics.any?
  return to_enum(:build, item) unless block_given?

  build_from_request(item, &Proc.new)
end

#define(data) {|data| ... } ⇒ void

This method returns an undefined value.

Defines a type. This is mostly a shorthand method. The purpose of this is to make it easy to define items on the index. The only parameter, data, is a hash. The key is the name of the type of the item; this corresponds to the keys in the ITEMS hash. The value is the Type that is being defined. The method then yields a hash that is later used to define the item.

Examples:

index.class # => Carbon::Concrete::Index
index.define(internal: Carbon::Boolean) do |bool|
  bool[:size] = 1
  bool[:kind] = :integer
  bool[:implements] << Carbon::Type("Carbon::Numeric")
  bool[:implements] << Carbon::Type("Carbon::Boolean")
end
internal = index[Carbon::Boolean]
internal.name # => "Carbon::Boolean"

Parameters:

  • data ({::Symbol => Concrete::Type})

    The name and item type information. There should only be one key pair; any more will cause an error.

Yields:

  • (data)

    To construct the data. At the end of the block, the data should contain all the information needed to create the item.

Yield Parameters:

  • data ({::Symbol => ::Object})

    The data to be passed to the item's intializer.

Raises:

  • (ArgumentError)

    if more than one key-value pair is provided for the data argument.



177
178
179
180
181
182
183
184
185
# File 'lib/carbon/concrete/index.rb', line 177

def define(data)
  fail ArgumentError, "Expected only one pair" if data.size > 1
  data.each do |itype, name|
    item = ITEMS.fetch(itype)
    opts = item.from(Carbon::Type(name))
    yield opts
    self << item.new(opts)
  end
end

#fetch(type, default = @canary) ⇒ Object



48
49
50
51
52
53
54
55
56
57
58
# File 'lib/carbon/concrete/index.rb', line 48

def fetch(type, default = @canary)
  matching = items.map { |i| [i, i.type.match?(type)] }.select(&:last)
  fail TooManyItemsError, "Multiple items match #{type}" if
    matching.size > 1
  match = matching.first

  return match if match
  return yield if block_given?
  return default if default != @canary
  fail ItemNotFoundError, "Could not find an item named #{type}"
end

#finalizeself

Note:

This freezes the item list for the index. This prevents modification for this instance of the index.

Finalizes the index. This should only be used when all of the items defined on the index are defined. For libraries, this is the culmination of everything in the library.

Returns:

  • (self)


130
131
132
133
134
135
# File 'lib/carbon/concrete/index.rb', line 130

def finalize
  current = @items.dup
  items = ::Set.new(current.map { |item| item.define(self) })
  @items = items.freeze
  clear!
end

#id::String

The digest of the ID. This uses the SHA256 base58 digest of the items defined on the current index. This includes both defined and merged items.

Examples:

index.items # => []
index.id # => "gjNT5GbSC-81KmUPncw-hzQAGV3GU-eAXafmkMP-Bw2GMHWM"

Returns:

  • (::String)


92
93
94
95
96
97
# File 'lib/carbon/concrete/index.rb', line 92

def id
  @_id ||= begin
    body = (@items.map(&:type).map(&:to_s) + @links.map(&:id)).join("\n")
    Carbon.hash(body)
  end
end

#item?(type) ⇒ Boolean

Returns:



60
61
62
# File 'lib/carbon/concrete/index.rb', line 60

def item?(type)
  items.any? { |item| item.type.match?(type) }
end

#itemsObject



64
65
66
67
68
69
70
71
72
73
# File 'lib/carbon/concrete/index.rb', line 64

def items
  @_current ||= begin
    current = @items.dup
    @links.each do |link|
      current.merge(link.items)
    end

    current.freeze
  end
end


117
118
119
120
# File 'lib/carbon/concrete/index.rb', line 117

def link(index)
  @links << index
  clear!
end

#to_jsonObject



75
76
77
78
79
80
81
# File 'lib/carbon/concrete/index.rb', line 75

def to_json
  links, @links = @links, nil
  clear! && id
  value = Oj.dump(self, Concrete::OPTIONS)
  @links = links
  value
end