carbon-core 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|