carbon-core 0.1.0 → 0.1.1
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 +4 -4
- data/.gitignore +1 -2
- data/.rspec +0 -0
- data/.rubocop.yml +0 -0
- data/.travis.yml +0 -0
- data/.yardopts +0 -0
- data/CODE_OF_CONDUCT.md +0 -0
- data/Gemfile +0 -0
- data/LICENSE.txt +0 -0
- data/README.md +0 -0
- data/Rakefile +0 -0
- data/Vagrantfile +83 -0
- data/carbon.gemspec +1 -1
- data/lib/carbon.rb +3 -2
- data/lib/carbon/concrete.rb +0 -0
- data/lib/carbon/concrete/build.rb +1 -1
- data/lib/carbon/concrete/index.rb +37 -13
- data/lib/carbon/concrete/item.rb +0 -0
- data/lib/carbon/concrete/item/base.rb +11 -0
- data/lib/carbon/concrete/item/data.rb +0 -0
- data/lib/carbon/concrete/item/function.rb +13 -1
- data/lib/carbon/concrete/item/internal.rb +0 -0
- data/lib/carbon/concrete/item/struct.rb +0 -0
- data/lib/carbon/concrete/item/struct/element.rb +0 -0
- data/lib/carbon/concrete/item/trait.rb +0 -0
- data/lib/carbon/concrete/item/trait/expectation.rb +0 -0
- data/lib/carbon/concrete/request.rb +0 -0
- data/lib/carbon/concrete/type.rb +0 -0
- data/lib/carbon/concrete/type/function.rb +0 -0
- data/lib/carbon/concrete/type/generic.rb +0 -0
- data/lib/carbon/concrete/type/name.rb +0 -0
- data/lib/carbon/concrete/type/parse.rb +0 -0
- data/lib/carbon/concrete/type/part.rb +0 -0
- data/lib/carbon/core.rb +1 -1
- data/lib/carbon/core/int.rb +0 -0
- data/lib/carbon/core/integer.rb +0 -0
- data/lib/carbon/core/integer/cast.rb +4 -4
- data/lib/carbon/core/integer/math.rb +11 -10
- data/lib/carbon/core/integer/misc.rb +6 -5
- data/lib/carbon/core/integer/pole.rb +9 -7
- data/lib/carbon/core/integer/ship.rb +4 -2
- data/lib/carbon/core/integer/sign.rb +0 -0
- data/lib/carbon/core/integer/type.rb +0 -0
- data/lib/carbon/core/integer/zero.rb +2 -2
- data/lib/carbon/core/pointer.rb +0 -0
- data/lib/carbon/core/pointer/access.rb +7 -4
- data/lib/carbon/core/pointer/cast.rb +1 -1
- data/lib/carbon/core/pointer/math.rb +12 -10
- data/lib/carbon/core/pointer/memory.rb +4 -4
- data/lib/carbon/core/pointer/type.rb +0 -0
- data/lib/carbon/tacky.rb +1 -0
- data/lib/carbon/tacky/block.rb +0 -0
- data/lib/carbon/tacky/builder.rb +14 -2
- data/lib/carbon/tacky/context.rb +12 -1
- data/lib/carbon/tacky/function.rb +1 -1
- data/lib/carbon/tacky/instruction.rb +9 -54
- data/lib/carbon/tacky/instruction/dependencies.rb +33 -0
- data/lib/carbon/tacky/instruction/generation.rb +90 -0
- data/lib/carbon/tacky/instruction/typeof.rb +25 -0
- data/lib/carbon/tacky/parameter.rb +0 -0
- data/lib/carbon/tacky/reference.rb +4 -0
- data/lib/carbon/tacky/typed.rb +25 -0
- data/lib/carbon/tacky/value.rb +0 -0
- data/lib/carbon/version.rb +1 -1
- data/scripts/core.rb +9 -0
- data/scripts/test.rb +19 -0
- metadata +12 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d19855dccc09339b3aab63ae8189dd7d2c8be42d
|
4
|
+
data.tar.gz: e83a47d93719cea99bc46e972ee5cc2be8d23ab9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d167ad893b767cbe85fbece99c690f71b824c3847070122c162bb64559ccbc567ebd4c7ebdb4596c3a211f069cfe14caf8e60428f328736c2eaae631ebd5a1b0
|
7
|
+
data.tar.gz: d01280b2758d932ded3fb9399f009fbaa968ae115cbf69b415a2aaadb59926f809f96105cf876fbec474aa131ed814a5cc7d6fdccb1e1dc748b02fc936257bde
|
data/.gitignore
CHANGED
data/.rspec
CHANGED
File without changes
|
data/.rubocop.yml
CHANGED
File without changes
|
data/.travis.yml
CHANGED
File without changes
|
data/.yardopts
CHANGED
File without changes
|
data/CODE_OF_CONDUCT.md
CHANGED
File without changes
|
data/Gemfile
CHANGED
File without changes
|
data/LICENSE.txt
CHANGED
File without changes
|
data/README.md
CHANGED
File without changes
|
data/Rakefile
CHANGED
File without changes
|
data/Vagrantfile
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
# -*- mode: ruby -*-
|
4
|
+
# vi: set ft=ruby :
|
5
|
+
|
6
|
+
# All Vagrant configuration is done below. The "2" in Vagrant.configure
|
7
|
+
# configures the configuration version (we support older styles for
|
8
|
+
# backwards compatibility). Please don't change it unless you know what
|
9
|
+
# you're doing.
|
10
|
+
Vagrant.configure(2) do |config|
|
11
|
+
# The most common configuration options are documented and commented below.
|
12
|
+
# For a complete reference, please see the online documentation at
|
13
|
+
# https://docs.vagrantup.com.
|
14
|
+
|
15
|
+
# Every Vagrant development environment requires a box. You can search for
|
16
|
+
# boxes at https://atlas.hashicorp.com/search.
|
17
|
+
config.vm.box = "terrywang/archlinux"
|
18
|
+
|
19
|
+
# Disable automatic box update checking. If you disable this, then
|
20
|
+
# boxes will only be checked for updates when the user runs
|
21
|
+
# `vagrant box outdated`. This is not recommended.
|
22
|
+
# config.vm.box_check_update = false
|
23
|
+
|
24
|
+
# Create a forwarded port mapping which allows access to a specific port
|
25
|
+
# within the machine from a port on the host machine. In the example below,
|
26
|
+
# accessing "localhost:8080" will access port 80 on the guest machine.
|
27
|
+
# config.vm.network "forwarded_port", guest: 3000, host: 3000
|
28
|
+
|
29
|
+
# Create a private network, which allows host-only access to the machine
|
30
|
+
# using a specific IP.
|
31
|
+
# config.vm.network "private_network", ip: "192.168.33.10"
|
32
|
+
|
33
|
+
# Create a public network, which generally matched to bridged network.
|
34
|
+
# Bridged networks make the machine appear as another physical device on
|
35
|
+
# your network.
|
36
|
+
# config.vm.network "public_network"
|
37
|
+
|
38
|
+
# Share an additional folder to the guest VM. The first argument is
|
39
|
+
# the path on the host to the actual folder. The second argument is
|
40
|
+
# the path on the guest to mount the folder. And the optional third
|
41
|
+
# argument is a set of non-required options.
|
42
|
+
# config.vm.synced_folder "../data", "/vagrant_data"
|
43
|
+
|
44
|
+
# Provider-specific configuration so you can fine-tune various
|
45
|
+
# backing providers for Vagrant. These expose provider-specific options.
|
46
|
+
# Example for VirtualBox:
|
47
|
+
#
|
48
|
+
config.vm.provider "virtualbox" do |vb|
|
49
|
+
# Display the VirtualBox GUI when booting the machine
|
50
|
+
# vb.gui = true
|
51
|
+
|
52
|
+
# Customize the amount of memory on the VM:
|
53
|
+
vb.memory = "1024"
|
54
|
+
end
|
55
|
+
#
|
56
|
+
# View the documentation for the provider you are using for more
|
57
|
+
# information on available options.
|
58
|
+
|
59
|
+
# Define a Vagrant Push strategy for pushing to Atlas. Other push strategies
|
60
|
+
# such as FTP and Heroku are also available. See the documentation at
|
61
|
+
# https://docs.vagrantup.com/v2/push/atlas.html for more information.
|
62
|
+
# config.push.define "atlas" do |push|
|
63
|
+
# push.app = "YOUR_ATLAS_USERNAME/YOUR_APPLICATION_NAME"
|
64
|
+
# end
|
65
|
+
|
66
|
+
# Enable provisioning with a shell script. Additional provisioners such as
|
67
|
+
# Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the
|
68
|
+
# documentation for more information about their specific syntax and use.
|
69
|
+
# config.vm.provision "shell", inline: <<-SHELL
|
70
|
+
# sudo apt-get update
|
71
|
+
# sudo apt-get install -y apache2
|
72
|
+
# SHELL
|
73
|
+
|
74
|
+
config.vm.provision "shell", inline: <<-SHELL
|
75
|
+
sudo pacman --noconfirm -Syyu git fish base-devel libyaml openssl zlib llvm35
|
76
|
+
sudo usermod -s /usr/bin/fish vagrant
|
77
|
+
SHELL
|
78
|
+
|
79
|
+
config.vm.provision "shell", privileged: false, name: "ruby", inline: <<-SHELL
|
80
|
+
mkdir -p ~/.config/fish
|
81
|
+
echo "set -x PATH ~/.gem/ruby/2.3.0/bin \\\$PATH" >> ~/.config/fish/config.fish
|
82
|
+
SHELL
|
83
|
+
end
|
data/carbon.gemspec
CHANGED
@@ -25,6 +25,6 @@ Gem::Specification.new do |spec|
|
|
25
25
|
spec.add_development_dependency "rake", "~> 10.0"
|
26
26
|
spec.add_development_dependency "rspec", "~> 3.0"
|
27
27
|
spec.add_dependency "base58", "~> 0.1"
|
28
|
-
spec.add_dependency "
|
28
|
+
spec.add_dependency "oj", "~> 2.17"
|
29
29
|
spec.add_dependency "ice_nine", "~> 0.11"
|
30
30
|
end
|
data/lib/carbon.rb
CHANGED
@@ -9,8 +9,6 @@ require "concurrent"
|
|
9
9
|
require "llvm"
|
10
10
|
require "llvm/core"
|
11
11
|
require "carbon/version"
|
12
|
-
require "carbon/tacky"
|
13
|
-
require "carbon/concrete"
|
14
12
|
|
15
13
|
# Carbon. The language.
|
16
14
|
module Carbon
|
@@ -44,11 +42,14 @@ module Carbon
|
|
44
42
|
encoded.scan(/.{1,9}/).join("-").freeze
|
45
43
|
end
|
46
44
|
|
45
|
+
require "carbon/concrete"
|
46
|
+
|
47
47
|
# A Boolean type for Carbon. This is used as a handy shortcut.
|
48
48
|
#
|
49
49
|
# @api private
|
50
50
|
# @return [Carbon::Concrete::Type]
|
51
51
|
Boolean = Carbon::Type("Carbon::Boolean")
|
52
52
|
|
53
|
+
require "carbon/tacky"
|
53
54
|
require "carbon/core"
|
54
55
|
end
|
data/lib/carbon/concrete.rb
CHANGED
File without changes
|
@@ -20,7 +20,7 @@ module Carbon
|
|
20
20
|
# The functions that have been defined. This is a mapping of the
|
21
21
|
# {Concrete::Type} to the `LLVM::Function` that was defined.
|
22
22
|
#
|
23
|
-
# @return [{Concrete::Type => ::LLVM::Function}]
|
23
|
+
# @return [{Concrete::Type => (Concrete::Item, ::LLVM::Function)}]
|
24
24
|
attr_reader :functions
|
25
25
|
|
26
26
|
# The LLVM module that is being used for compilation.
|
@@ -37,8 +37,8 @@ module Carbon
|
|
37
37
|
# on the index. See {#id}.
|
38
38
|
def initialize(data = {})
|
39
39
|
@items = Concurrent::Hash.new.merge(data.fetch(:items, {}))
|
40
|
-
@mutex = Mutex.new
|
41
40
|
@id = data.fetch(:id) { id }
|
41
|
+
mutex
|
42
42
|
end
|
43
43
|
|
44
44
|
# @overload fetch(name, default = CANARY)
|
@@ -71,7 +71,7 @@ module Carbon
|
|
71
71
|
# @raise [KeyError] If the key doesn't match an item and neither a
|
72
72
|
# block nor the second argument were passed.
|
73
73
|
def fetch(*options, &block)
|
74
|
-
|
74
|
+
mutex.synchronize { @items.fetch(*options, &block) }
|
75
75
|
end
|
76
76
|
|
77
77
|
# @overload [](key)
|
@@ -87,7 +87,7 @@ module Carbon
|
|
87
87
|
# @return [::Object] The item, if it exists;
|
88
88
|
# @return [nil] Otherwise.
|
89
89
|
def [](*options)
|
90
|
-
|
90
|
+
mutex.synchronize { @items[*options] }
|
91
91
|
end
|
92
92
|
|
93
93
|
# @overload key?(key)
|
@@ -102,7 +102,7 @@ module Carbon
|
|
102
102
|
# @param key [::Object] The key of the item to check.
|
103
103
|
# @return [::Boolean] Whether the key exists or not.
|
104
104
|
def key?(*options)
|
105
|
-
|
105
|
+
mutex.synchronize { @items.key?(*options) }
|
106
106
|
end
|
107
107
|
|
108
108
|
# Returns all of the keys in the index. The keys and the array itself
|
@@ -113,7 +113,7 @@ module Carbon
|
|
113
113
|
# index.keys # => ["Carbon::Boolean", "Carbon::Integer"]
|
114
114
|
# @return [<::String>] The keys in the index.
|
115
115
|
def keys
|
116
|
-
|
116
|
+
mutex.synchronize { @items.keys.deep_freeze! }
|
117
117
|
end
|
118
118
|
|
119
119
|
# Returns all of the values (actual items) in the index. The items, and
|
@@ -124,7 +124,7 @@ module Carbon
|
|
124
124
|
# index.values.map(&:class) # => [Carbon::Concrete::Item::Internal, ...]
|
125
125
|
# @return [<Item::Base>] The values in the index.
|
126
126
|
def values
|
127
|
-
|
127
|
+
mutex.synchornize { @items.values.freeze }
|
128
128
|
end
|
129
129
|
|
130
130
|
# The digest of the ID. This uses the SHA256 base58 digest of the items
|
@@ -149,7 +149,7 @@ module Carbon
|
|
149
149
|
# # => {"Carbon::Integer" => #<Carbon::Concrete::Item::Base>}
|
150
150
|
# @return [{::String => Item::Base}] The items of the index.
|
151
151
|
def items
|
152
|
-
|
152
|
+
mutex.synchronize { @items.dup.deep_freeze! }
|
153
153
|
end
|
154
154
|
|
155
155
|
# Adds a defined item to the index. The item just has to respond to
|
@@ -173,7 +173,7 @@ module Carbon
|
|
173
173
|
# @return [self]
|
174
174
|
def add(item)
|
175
175
|
return merge(item) if item.is_a?(Index)
|
176
|
-
|
176
|
+
mutex.synchronize do
|
177
177
|
@items[item.intern] = item
|
178
178
|
clear!
|
179
179
|
end
|
@@ -192,12 +192,32 @@ module Carbon
|
|
192
192
|
# @param index [Index] The index to merge.
|
193
193
|
# @return [self]
|
194
194
|
def merge(index)
|
195
|
-
|
195
|
+
mutex.synchronize do
|
196
196
|
@items.merge(index.items)
|
197
197
|
clear!
|
198
198
|
end
|
199
199
|
end
|
200
200
|
|
201
|
+
# Finalizes the index. This should only be used when all of the items
|
202
|
+
# defined on the index are defined. For libraries, this is the
|
203
|
+
# culmination of everything in the library.
|
204
|
+
#
|
205
|
+
# @note
|
206
|
+
# This freezes the item list for the index. This prevents modification
|
207
|
+
# for this instance of the index.
|
208
|
+
# @return [self]
|
209
|
+
def finalize
|
210
|
+
mutex.synchronize do
|
211
|
+
items = @items.map do |key, value|
|
212
|
+
[key, value.define(self)]
|
213
|
+
end.to_h
|
214
|
+
|
215
|
+
@items = items.freeze
|
216
|
+
end
|
217
|
+
|
218
|
+
self
|
219
|
+
end
|
220
|
+
|
201
221
|
# Used only for {#define}. This maps item "names" to the classes that
|
202
222
|
# represent them.
|
203
223
|
#
|
@@ -269,7 +289,7 @@ module Carbon
|
|
269
289
|
# @yield [dep] Multiple times, each with a dependency.
|
270
290
|
# @yieldparam dep [Request] A request.
|
271
291
|
def build(item)
|
272
|
-
|
292
|
+
mutex.lock
|
273
293
|
fail ArgumentError, "Passed item cannot be generic" if \
|
274
294
|
item.generics.any?
|
275
295
|
return to_enum(:build, item) unless block_given?
|
@@ -277,11 +297,15 @@ module Carbon
|
|
277
297
|
|
278
298
|
build_from_request(request, &Proc.new)
|
279
299
|
ensure
|
280
|
-
|
300
|
+
mutex.unlock
|
281
301
|
end
|
282
302
|
|
283
303
|
private
|
284
304
|
|
305
|
+
def mutex
|
306
|
+
@_mutex ||= ::Mutex.new
|
307
|
+
end
|
308
|
+
|
285
309
|
# rubocop:disable Metrics/MethodLength
|
286
310
|
def build_from_request(request)
|
287
311
|
components = to_enum(:each_strongly_connected_component_from, request)
|
@@ -289,10 +313,10 @@ module Carbon
|
|
289
313
|
fail TSort::Cyclic, "cyclic dependencies: #{group.join(', ')}" if \
|
290
314
|
group.size > 1
|
291
315
|
begin
|
292
|
-
|
316
|
+
mutex.unlock
|
293
317
|
yield group.first
|
294
318
|
ensure
|
295
|
-
|
319
|
+
mutex.lock
|
296
320
|
end
|
297
321
|
end
|
298
322
|
end
|
data/lib/carbon/concrete/item.rb
CHANGED
File without changes
|
@@ -117,6 +117,17 @@ module Carbon
|
|
117
117
|
fail NotImplementedError, "Could not build #{self.class}"
|
118
118
|
end
|
119
119
|
|
120
|
+
# Performs modification to the item. This is done after _all_ items
|
121
|
+
# are loaded into the index, so that an item can load outside
|
122
|
+
# information if need be.
|
123
|
+
#
|
124
|
+
# @param index [Concrete::Index] The index.
|
125
|
+
# @return [Concrete::Item::Base] A modified version of the item, or
|
126
|
+
# self.
|
127
|
+
def define(index)
|
128
|
+
self
|
129
|
+
end
|
130
|
+
|
120
131
|
# Modifies the dependencies of this item so that they conform to the
|
121
132
|
# given request. This should resolve all of our dependencies so that
|
122
133
|
# they no longer hold any sort of generic class.
|
File without changes
|
@@ -21,6 +21,9 @@ module Carbon
|
|
21
21
|
# attributes on this class will also be frozen, as well.
|
22
22
|
class Function
|
23
23
|
include Base
|
24
|
+
attr_reader :return
|
25
|
+
attr_reader :definition
|
26
|
+
|
24
27
|
|
25
28
|
# (see Item::Base.from)
|
26
29
|
def self.from(type)
|
@@ -46,6 +49,15 @@ module Carbon
|
|
46
49
|
deep_freeze!
|
47
50
|
end
|
48
51
|
|
52
|
+
def define(index)
|
53
|
+
if @definition.is_a?(::Proc)
|
54
|
+
Function.from(func)
|
55
|
+
.merge(definition: @definition.call(index), return: @return)
|
56
|
+
else
|
57
|
+
self
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
49
61
|
# (see Base#call)
|
50
62
|
def call(build, generics)
|
51
63
|
value = case @definition
|
@@ -70,7 +82,7 @@ module Carbon
|
|
70
82
|
ret = build.types.fetch(@return.sub(generics)).last
|
71
83
|
func = build.module.functions.add(full, params, ret)
|
72
84
|
@definition.call(func, build, generics)
|
73
|
-
build.functions[full] = func
|
85
|
+
build.functions[full] = [self, func]
|
74
86
|
end
|
75
87
|
# rubocop:enable Metrics/AbcSize
|
76
88
|
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
data/lib/carbon/concrete/type.rb
CHANGED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
data/lib/carbon/core.rb
CHANGED
data/lib/carbon/core/int.rb
CHANGED
File without changes
|
data/lib/carbon/core/integer.rb
CHANGED
File without changes
|
@@ -63,19 +63,19 @@ module Carbon
|
|
63
63
|
end
|
64
64
|
|
65
65
|
def upcast_signed(entry, this, to)
|
66
|
-
entry.ret(entry.sext(this, to))
|
66
|
+
entry.ret(entry.sext(this, to).as(this.type))
|
67
67
|
end
|
68
68
|
|
69
69
|
def upcast_unsigned(entry, this, to)
|
70
|
-
entry.ret(entry.zext(this, to))
|
70
|
+
entry.ret(entry.zext(this, to).as(this.type))
|
71
71
|
end
|
72
72
|
|
73
73
|
def downcast(entry, this, to)
|
74
|
-
entry.ret(entry.trunc(this, to))
|
74
|
+
entry.ret(entry.trunc(this, to).as(this.type))
|
75
75
|
end
|
76
76
|
|
77
77
|
def sidecast(entry, this, to)
|
78
|
-
entry.ret(entry.int_cast(this, to))
|
78
|
+
entry.ret(entry.int_cast(this, to).as(this.type))
|
79
79
|
end
|
80
80
|
end
|
81
81
|
end
|
@@ -121,7 +121,7 @@ module Carbon
|
|
121
121
|
params[0].name, params[1].name = %w(self other)
|
122
122
|
this, other = force_same_cast(params, entry, left, right)
|
123
123
|
icmp = COMP_OPERATIONS_MAP.fetch(left.sign, op)
|
124
|
-
entry.ret(entry.icmp(icmp, this, other))
|
124
|
+
entry.ret(entry.icmp(icmp, this, other).as(Carbon::Boolean))
|
125
125
|
end
|
126
126
|
|
127
127
|
def define_math_definition(left, right, op, definition)
|
@@ -130,6 +130,7 @@ module Carbon
|
|
130
130
|
params[0].name, params[1].name = %w(self other)
|
131
131
|
this, other = force_same_cast(params, entry, left, right)
|
132
132
|
result = perform_operation(entry, [this, other], left, op)
|
133
|
+
.as(this.type)
|
133
134
|
return_original_size(entry, result, left, right)
|
134
135
|
end
|
135
136
|
|
@@ -179,18 +180,18 @@ module Carbon
|
|
179
180
|
end
|
180
181
|
|
181
182
|
def force_same_cast(params, entry, left, right)
|
182
|
-
|
183
|
-
|
184
|
-
|
183
|
+
dest = destination_size([left, right])
|
184
|
+
lcall, rcall =
|
185
|
+
[left, right].map { |p| p.name.call(dest.cast, [p.name]) }
|
186
|
+
[entry.call(lcall, params[0]).as(dest.name),
|
187
|
+
entry.call(rcall, params[1]).as(dest.name)]
|
185
188
|
end
|
186
189
|
|
187
|
-
def
|
188
|
-
sign =
|
189
|
-
size =
|
190
|
-
fname = sign == :unsigned ? "to-uint#{size}" : "to-int#{size}"
|
190
|
+
def destination_size(params)
|
191
|
+
sign = params[0].sign
|
192
|
+
size = params.map(&:size).max
|
191
193
|
|
192
|
-
|
193
|
-
right.name.call(fname, [right.name])]
|
194
|
+
Int.find(size: size, sign: sign)
|
194
195
|
end
|
195
196
|
end
|
196
197
|
end
|