carbon-core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/.rspec +2 -0
- data/.rubocop.yml +38 -0
- data/.travis.yml +4 -0
- data/.yardopts +1 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +11 -0
- data/LICENSE.txt +21 -0
- data/README.md +41 -0
- data/Rakefile +14 -0
- data/carbon.gemspec +30 -0
- data/lib/carbon.rb +54 -0
- data/lib/carbon/concrete.rb +43 -0
- data/lib/carbon/concrete/build.rb +63 -0
- data/lib/carbon/concrete/index.rb +324 -0
- data/lib/carbon/concrete/item.rb +37 -0
- data/lib/carbon/concrete/item/base.rb +153 -0
- data/lib/carbon/concrete/item/data.rb +22 -0
- data/lib/carbon/concrete/item/function.rb +97 -0
- data/lib/carbon/concrete/item/internal.rb +83 -0
- data/lib/carbon/concrete/item/struct.rb +65 -0
- data/lib/carbon/concrete/item/struct/element.rb +42 -0
- data/lib/carbon/concrete/item/trait.rb +72 -0
- data/lib/carbon/concrete/item/trait/expectation.rb +55 -0
- data/lib/carbon/concrete/request.rb +137 -0
- data/lib/carbon/concrete/type.rb +260 -0
- data/lib/carbon/concrete/type/function.rb +91 -0
- data/lib/carbon/concrete/type/generic.rb +118 -0
- data/lib/carbon/concrete/type/name.rb +147 -0
- data/lib/carbon/concrete/type/parse.rb +172 -0
- data/lib/carbon/concrete/type/part.rb +100 -0
- data/lib/carbon/core.rb +61 -0
- data/lib/carbon/core/int.rb +87 -0
- data/lib/carbon/core/integer.rb +109 -0
- data/lib/carbon/core/integer/cast.rb +83 -0
- data/lib/carbon/core/integer/math.rb +198 -0
- data/lib/carbon/core/integer/misc.rb +145 -0
- data/lib/carbon/core/integer/pole.rb +133 -0
- data/lib/carbon/core/integer/ship.rb +71 -0
- data/lib/carbon/core/integer/sign.rb +52 -0
- data/lib/carbon/core/integer/type.rb +42 -0
- data/lib/carbon/core/integer/zero.rb +52 -0
- data/lib/carbon/core/pointer.rb +54 -0
- data/lib/carbon/core/pointer/access.rb +123 -0
- data/lib/carbon/core/pointer/cast.rb +55 -0
- data/lib/carbon/core/pointer/math.rb +187 -0
- data/lib/carbon/core/pointer/memory.rb +85 -0
- data/lib/carbon/core/pointer/type.rb +23 -0
- data/lib/carbon/tacky.rb +21 -0
- data/lib/carbon/tacky/block.rb +96 -0
- data/lib/carbon/tacky/builder.rb +310 -0
- data/lib/carbon/tacky/context.rb +66 -0
- data/lib/carbon/tacky/function.rb +137 -0
- data/lib/carbon/tacky/instruction.rb +170 -0
- data/lib/carbon/tacky/parameter.rb +23 -0
- data/lib/carbon/tacky/reference.rb +23 -0
- data/lib/carbon/tacky/value.rb +40 -0
- data/lib/carbon/version.rb +9 -0
- metadata +186 -0
@@ -0,0 +1,37 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "carbon/concrete/item/base"
|
5
|
+
require "carbon/concrete/item/data"
|
6
|
+
require "carbon/concrete/item/function"
|
7
|
+
require "carbon/concrete/item/internal"
|
8
|
+
require "carbon/concrete/item/struct"
|
9
|
+
require "carbon/concrete/item/trait"
|
10
|
+
|
11
|
+
module Carbon
|
12
|
+
module Concrete
|
13
|
+
# Items. These are buildable concepts that have dependencies. Each item
|
14
|
+
# includes {Item::Base} in order to respond to the item API. The main
|
15
|
+
# API for items are the following set of methods:
|
16
|
+
#
|
17
|
+
# - `#intern` (`String`) - Returns an "interned" name of the item. See
|
18
|
+
# {Base#intern}.
|
19
|
+
# - `#name` (`String`) - The full name of the item. See {Base#name}.
|
20
|
+
# - `#generics` (`<Type::Generic>`) - The generics of the item.
|
21
|
+
# This is normally something like `[T]`; these most likely have no
|
22
|
+
# correspondance to an actual type. See {Base#generics}.
|
23
|
+
# - `#dependencies` (`Set<Request>`) - A set of dependencies that
|
24
|
+
# the item has. This includes the generics that the item uses.
|
25
|
+
# See {Base#dependencies}.
|
26
|
+
# - `#==`, `#eql?` (`Boolean`) - Comparison. Normally uses the `#intern`
|
27
|
+
# name for a basis of comarison. See {Base#==}.
|
28
|
+
# - `#call` - Builds the item for compilation. This is the last stage.
|
29
|
+
# This should generate something for LLVM in some way. See {Base#call}.
|
30
|
+
# - `#corrected_dependencies` (`Enumerable`) - Corrects the depdenencies of
|
31
|
+
# the Item. This makes it so that the generics of the passed request
|
32
|
+
# are matched with the generics of the dependencies, essentially
|
33
|
+
# resolving them. See {Base#corrected_dependencies}.
|
34
|
+
module Item
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Carbon
|
5
|
+
module Concrete
|
6
|
+
module Item
|
7
|
+
# A "base" for other items. This just has some common methods that all
|
8
|
+
# items need to share to be compatible with the index. This module is
|
9
|
+
# the bases of the build system in the {Concrete} module.
|
10
|
+
module Base
|
11
|
+
# Returns the interned name of the item. This contains no generic
|
12
|
+
# information. Interned names are mostly used to match generic
|
13
|
+
# information laden types with the items that define them; for example,
|
14
|
+
# it is meant to be used to match the type
|
15
|
+
# `Carbon::Pointer<Carbon::String>` to the module `Carbon::Pointer<T>`.
|
16
|
+
# The common parts of both is the module name that the type uses - or,
|
17
|
+
# rather, everything but the generic information. Interned names
|
18
|
+
# discard the information that is unneeded for matching the two.
|
19
|
+
#
|
20
|
+
# This methodology has the downside that a module with the same name
|
21
|
+
# as the base of a generic module cannot exist; however, allowing such
|
22
|
+
# would ultimately be confusing.
|
23
|
+
#
|
24
|
+
# @api public
|
25
|
+
# @example For a module.
|
26
|
+
# item.name # => "Carbon::Pointer<T>"
|
27
|
+
# item.intern # => "Carbon::Pointer"
|
28
|
+
# @example For a function.
|
29
|
+
# item.name
|
30
|
+
# # => "Carbon::Pointer<T>.+(Carbon::Pointer<T>, Carbon::Int32)"
|
31
|
+
# item.intern
|
32
|
+
# # => "Carbon::Pointer.+(Carbon::Pointer, Carbon::Int32)"
|
33
|
+
# @return [::String] The interned name.
|
34
|
+
attr_reader :intern
|
35
|
+
|
36
|
+
# Returns the full name of the item. This can include generic
|
37
|
+
# information.
|
38
|
+
#
|
39
|
+
# @example
|
40
|
+
# item.name # => "Carbon::Pointer<T>"
|
41
|
+
# @return [::String]
|
42
|
+
attr_reader :name
|
43
|
+
|
44
|
+
# Returns the generic information associated with this item. This is
|
45
|
+
# used internally for generic substitution later on.
|
46
|
+
#
|
47
|
+
# @api semipublic
|
48
|
+
# @example
|
49
|
+
# item.generics # => [#<Carbon::Concrcete::Type::Generic T>]
|
50
|
+
# @return [<Type::Generic>]
|
51
|
+
attr_reader :generics
|
52
|
+
|
53
|
+
# The dependencies that this item is based on. These are requests
|
54
|
+
# because we _request_ the item from the index as a dependency.
|
55
|
+
# Requests contain the module name that it builds and the generics
|
56
|
+
# it requires.
|
57
|
+
#
|
58
|
+
# @api semipublic
|
59
|
+
# @example
|
60
|
+
# item.dependencies # => Set[#<Carbon::Concrete::Request T>]
|
61
|
+
# @return [Set<Request>]
|
62
|
+
attr_reader :dependencies
|
63
|
+
|
64
|
+
# Creates a hash from a given {Concrete::Type} that can be used to
|
65
|
+
# intialize an instance of this object. This is mostly used for
|
66
|
+
# {Index#define} and shouldn't be used anywhere else.
|
67
|
+
#
|
68
|
+
# @api private
|
69
|
+
# @param type [Concrete::Type] The type.
|
70
|
+
# @return [{::Symbol => ::Object}]
|
71
|
+
def self.from(type)
|
72
|
+
{ module: type }
|
73
|
+
end
|
74
|
+
|
75
|
+
# Compares this item to another object. If the other object _is_ this
|
76
|
+
# item, then it returns true; otherwise, if the other object is an
|
77
|
+
# item, and the other object's {#intern} is equal to this object's
|
78
|
+
# {#intern}, then it returns true; otherwise, it returns false.
|
79
|
+
#
|
80
|
+
# @api public
|
81
|
+
# @example
|
82
|
+
# first.intern # => "Carbon::Pointer"
|
83
|
+
# second.intern # => "Carbon::Pointer"
|
84
|
+
# first == first # => true
|
85
|
+
# first == second # => true
|
86
|
+
# second == second # => true
|
87
|
+
# second == first # => true
|
88
|
+
# @param other [Base, ::String, ::Object] The object to compare.
|
89
|
+
# @return [::Boolean] The result of comparison.
|
90
|
+
def ==(other)
|
91
|
+
equal?(other) || (other.is_a?(Base) && intern == other.intern)
|
92
|
+
end
|
93
|
+
alias_method :eql?, :==
|
94
|
+
|
95
|
+
# Creates a hash of the item. This is used mostly in the Hash
|
96
|
+
# class.
|
97
|
+
#
|
98
|
+
# @api private
|
99
|
+
# @return [Numeric]
|
100
|
+
def hash
|
101
|
+
intern.hash
|
102
|
+
end
|
103
|
+
|
104
|
+
# rubocop:disable Lint/UnusedMethodArgument
|
105
|
+
|
106
|
+
# Performs compilation for the item. This converts the item into an
|
107
|
+
# LLVM-based value or type, to be used for compiling. If
|
108
|
+
# unimplemented, it raises a `NotImplementedError`.
|
109
|
+
#
|
110
|
+
# @api private
|
111
|
+
# @param build [Concrete::Build] The build information for the item.
|
112
|
+
# @param generics [{Concrete::Type => Concrete::Type}] The generics
|
113
|
+
# to apply for the item.
|
114
|
+
# @return [void]
|
115
|
+
# @raise [NotImplementedError] If it is not implemented.
|
116
|
+
def call(build, generics)
|
117
|
+
fail NotImplementedError, "Could not build #{self.class}"
|
118
|
+
end
|
119
|
+
|
120
|
+
# Modifies the dependencies of this item so that they conform to the
|
121
|
+
# given request. This should resolve all of our dependencies so that
|
122
|
+
# they no longer hold any sort of generic class.
|
123
|
+
#
|
124
|
+
# @api private
|
125
|
+
# @param request [Request] The request.
|
126
|
+
# @yield [dep] The corrected dependencies.
|
127
|
+
# @yieldparam dep [Request] The corrected dependency.
|
128
|
+
# @return [::Enumerable] An enumerator of the corrected dependencies,
|
129
|
+
# if no block was given.
|
130
|
+
# @return [void] If a block was given.
|
131
|
+
def corrected_dependencies(request, &block)
|
132
|
+
return to_enum(:corrected_dependencies, request) unless block_given?
|
133
|
+
return dependencies.each(&block) if generics.empty?
|
134
|
+
|
135
|
+
forced_corrected_dependencies(request, &block)
|
136
|
+
end
|
137
|
+
|
138
|
+
private
|
139
|
+
|
140
|
+
def forced_corrected_dependencies(request, &block)
|
141
|
+
# Array<Type::Generic> -> Array<(Type::Generic, Numeric)> ->
|
142
|
+
# Array<(String, Type::Generic)> -> {String => Type::Generic}
|
143
|
+
mapping = generics
|
144
|
+
.each_with_index
|
145
|
+
.map { |gen, i| [gen.name.intern, request.generics[i]] }
|
146
|
+
.to_h
|
147
|
+
|
148
|
+
dependencies.map { |dep| dep.sub(mapping) }.each(&block)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Carbon
|
5
|
+
module Concrete
|
6
|
+
module Item
|
7
|
+
# A data definition. All data definitions include this module, which
|
8
|
+
# acts as a sort of hirearchy. It makes it easy to identify if an item
|
9
|
+
# is a data definition or a function definition.
|
10
|
+
module Data
|
11
|
+
include Base
|
12
|
+
|
13
|
+
# The traits that the data type implements. This is mostly used for
|
14
|
+
# validation and logic.
|
15
|
+
#
|
16
|
+
# @api public
|
17
|
+
# @return [<Type>]
|
18
|
+
attr_reader :implements
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Carbon
|
5
|
+
module Concrete
|
6
|
+
module Item
|
7
|
+
# A function definition. This contains all of the information needed to
|
8
|
+
# build a proper LLVM function. This is stored within an index and
|
9
|
+
# serialized as needed, in order to defer compilation. There are two
|
10
|
+
# main types of functions: "normal," or "extern." "Normal" functions
|
11
|
+
# have an actual definition (i.e. {Tacky::Function}), and are written
|
12
|
+
# in Ruby/Carbon. "Extern" functions do not have a definition; they
|
13
|
+
# map a Carbon function to a C/low-level function. Instead of a
|
14
|
+
# definition, they have a function name, that maps to the C/low-level
|
15
|
+
# function. This is normally specified with an `:extern` directive.
|
16
|
+
#
|
17
|
+
# @api private
|
18
|
+
# @note
|
19
|
+
# **This class is frozen upon initialization.** This means that any
|
20
|
+
# attempt to modify it will result in an error. In most cases, the
|
21
|
+
# attributes on this class will also be frozen, as well.
|
22
|
+
class Function
|
23
|
+
include Base
|
24
|
+
|
25
|
+
# (see Item::Base.from)
|
26
|
+
def self.from(type)
|
27
|
+
{
|
28
|
+
module: type.to_module,
|
29
|
+
internal: type.name.function.name,
|
30
|
+
arguments: type.name.function.parameters,
|
31
|
+
generics: type.generics,
|
32
|
+
definition: Tacky::Function.new(type.name.function.parameters)
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
def initialize(data)
|
37
|
+
@module = data.fetch(:module)
|
38
|
+
@internal = data.fetch(:internal)
|
39
|
+
@parameters = data.fetch(:arguments) { data.fetch(:parameters) }
|
40
|
+
@definition = data.fetch(:definition)
|
41
|
+
@generics = data.fetch(:generics) { @module.generics }
|
42
|
+
@return = data.fetch(:return)
|
43
|
+
|
44
|
+
derive_name
|
45
|
+
derive_dependencies
|
46
|
+
deep_freeze!
|
47
|
+
end
|
48
|
+
|
49
|
+
# (see Base#call)
|
50
|
+
def call(build, generics)
|
51
|
+
value = case @definition
|
52
|
+
when Tacky::Function
|
53
|
+
build_function_intern(build, generics)
|
54
|
+
when ::String, ::Symbol
|
55
|
+
build_function_extern(build, generics)
|
56
|
+
else fail ArgumentError,
|
57
|
+
"Unknown definition #{@definition.class}"
|
58
|
+
end
|
59
|
+
|
60
|
+
build.functions[@module.sub(generics)] = value
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
# rubocop:disable Metrics/AbcSize
|
66
|
+
def build_function_intern(build, generics)
|
67
|
+
full = @full.sub(generics)
|
68
|
+
params = @parameters
|
69
|
+
.map { |p| build.types.fetch(p.sub(generics)).last }
|
70
|
+
ret = build.types.fetch(@return.sub(generics)).last
|
71
|
+
func = build.module.functions.add(full, params, ret)
|
72
|
+
@definition.call(func, build, generics)
|
73
|
+
build.functions[full] = func
|
74
|
+
end
|
75
|
+
# rubocop:enable Metrics/AbcSize
|
76
|
+
|
77
|
+
# @todo TODO: finish.
|
78
|
+
def build_function_extern(build, generics)
|
79
|
+
end
|
80
|
+
|
81
|
+
def derive_name
|
82
|
+
@full = @module.call(@internal, @parameters)
|
83
|
+
@intern = @full.intern
|
84
|
+
@name = @full.to_s
|
85
|
+
end
|
86
|
+
|
87
|
+
def derive_dependencies
|
88
|
+
@dependencies = Set.new
|
89
|
+
@dependencies << @return.to_request
|
90
|
+
@dependencies.merge(@parameters.map(&:to_request))
|
91
|
+
@dependencies.merge(@definition.dependencies.map(&:to_request)) if \
|
92
|
+
@definition.is_a?(Tacky::Function)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Carbon
|
5
|
+
module Concrete
|
6
|
+
module Item
|
7
|
+
# An internal data type. In most cases, this is just integers and
|
8
|
+
# pointers that can't be easily serialized because of links to LLVM and
|
9
|
+
# FFI.
|
10
|
+
#
|
11
|
+
# @api private
|
12
|
+
# @note
|
13
|
+
# **This class is frozen upon initialization.** This means that any
|
14
|
+
# attempt to modify it will result in an error. In most cases, the
|
15
|
+
# attributes on this class will also be frozen, as well.
|
16
|
+
class Internal
|
17
|
+
include Data
|
18
|
+
|
19
|
+
# Retreives the LLVM type information for the given integer size.
|
20
|
+
# This cannot be serialized by marshal or any other serialization
|
21
|
+
# library.
|
22
|
+
#
|
23
|
+
# @api private
|
24
|
+
# @return [Proc<Numeric, LLVM::Type>]
|
25
|
+
TYPE = proc { |size| LLVM.const_get("Int#{size}").type }
|
26
|
+
|
27
|
+
# (see Item::Base.from)
|
28
|
+
def self.from(type)
|
29
|
+
{ module: type, implements: [] }
|
30
|
+
end
|
31
|
+
|
32
|
+
# Initialize the internal data type. Internal data types may not have
|
33
|
+
# generic information, and have no dependencies, but can still
|
34
|
+
# implement traits.
|
35
|
+
#
|
36
|
+
# @param data [::Hash] The options for the internal data type.
|
37
|
+
# @option data [Type] :module The module of the internal data type.
|
38
|
+
# @option data [::String, ::Symbol] :kind The kind of the internal data
|
39
|
+
# type. Valid values are `:integer`, `:pointer`, `:int_pointer`,
|
40
|
+
# `:ary_pointer`, or the string version of any of those. This
|
41
|
+
# controls the underlying LLVM type of the internal data type.
|
42
|
+
# @option data [::String, ::Numeric, #to_i] :size The size of the
|
43
|
+
# internal data type. For integers, it is the number of bits in the
|
44
|
+
# integer; for pointers, it is the number of bits of the element it
|
45
|
+
# points to.
|
46
|
+
# @option data [<Type>] :implements ([]) The types that the
|
47
|
+
# internal data type implements.
|
48
|
+
def initialize(data)
|
49
|
+
@module = data.fetch(:module)
|
50
|
+
@kind = data.fetch(:kind) { data.fetch(:type) }.to_s
|
51
|
+
@size = data.fetch(:size, 8)
|
52
|
+
|
53
|
+
@generics = @module.generics
|
54
|
+
@implements = Set.new(data.fetch(:implements, []))
|
55
|
+
@dependencies = Set.new
|
56
|
+
|
57
|
+
derive_name
|
58
|
+
deep_freeze!
|
59
|
+
end
|
60
|
+
|
61
|
+
# (see Base#call)
|
62
|
+
def call(build, generics)
|
63
|
+
value = case @kind.to_s
|
64
|
+
when "integer" then TYPE.call(@size)
|
65
|
+
when "pointer"
|
66
|
+
type = build.types.fetch(generics.fetch("T"))
|
67
|
+
type.last.pointer
|
68
|
+
else fail ArgumentError, "Unknown kind #{@kind}"
|
69
|
+
end
|
70
|
+
|
71
|
+
build.types[@module.sub(generics)] = [self, value]
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def derive_name
|
77
|
+
@intern = @module.intern
|
78
|
+
@name = @module.to_s
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "carbon/concrete/item/struct/element"
|
5
|
+
|
6
|
+
module Carbon
|
7
|
+
module Concrete
|
8
|
+
module Item
|
9
|
+
# A struct data type. This is normally a sequence of elements that are
|
10
|
+
# stored sequentially in memory. Each element has a name, to reference
|
11
|
+
# which position, and a type.
|
12
|
+
#
|
13
|
+
# @api private
|
14
|
+
# @note
|
15
|
+
# **This class is frozen upon initialization.** This means that any
|
16
|
+
# attempt to modify it will result in an error. In most cases, the
|
17
|
+
# attributes on this class will also be frozen, as well.
|
18
|
+
class Struct
|
19
|
+
include Data
|
20
|
+
|
21
|
+
# (see Item::Base.from)
|
22
|
+
def self.from(type)
|
23
|
+
{ module: type, implements: [], elements: [] }
|
24
|
+
end
|
25
|
+
|
26
|
+
# Initialize the struct with the given data.
|
27
|
+
#
|
28
|
+
# @param data [::Hash] The data to initialize with.
|
29
|
+
# @option data [Type] :module The name of the struct.
|
30
|
+
# @option data [<::String, Type>] :elements The elements of
|
31
|
+
# the struct.
|
32
|
+
# @option data [<Type>] :implements The traits that this
|
33
|
+
# data type implements.
|
34
|
+
def initialize(data)
|
35
|
+
@module = data.fetch(:module)
|
36
|
+
@generics = @module.generics
|
37
|
+
@intern = @module.intern
|
38
|
+
@name = @module.to_s
|
39
|
+
|
40
|
+
@implements = Set.new(data.fetch(:implements))
|
41
|
+
@dependencies = Set.new
|
42
|
+
|
43
|
+
derive_elements(data.fetch(:elements))
|
44
|
+
derive_dependencies
|
45
|
+
deep_freeze!
|
46
|
+
end
|
47
|
+
|
48
|
+
# (see Base#call)
|
49
|
+
def call(build, generics)
|
50
|
+
super # TODO: fixme.
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def derive_elements(elements)
|
56
|
+
@elements = elements.map { |e| Element.new(*e) }
|
57
|
+
end
|
58
|
+
|
59
|
+
def derive_dependencies
|
60
|
+
@dependencies.merge(@elements.map { |e| e.type.to_request })
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|