rltk 3.0.0 → 3.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +21 -22
- data/lib/rltk/ast.rb +185 -118
- data/lib/rltk/cfg.rb +157 -103
- data/lib/rltk/cg/basic_block.rb +19 -19
- data/lib/rltk/cg/bindings.rb +16 -16
- data/lib/rltk/cg/builder.rb +129 -129
- data/lib/rltk/cg/context.rb +7 -7
- data/lib/rltk/cg/contractor.rb +7 -7
- data/lib/rltk/cg/execution_engine.rb +30 -30
- data/lib/rltk/cg/function.rb +37 -37
- data/lib/rltk/cg/generated_bindings.rb +3932 -3932
- data/lib/rltk/cg/generic_value.rb +17 -17
- data/lib/rltk/cg/instruction.rb +116 -116
- data/lib/rltk/cg/llvm.rb +22 -22
- data/lib/rltk/cg/memory_buffer.rb +7 -7
- data/lib/rltk/cg/module.rb +73 -73
- data/lib/rltk/cg/pass_manager.rb +35 -35
- data/lib/rltk/cg/target.rb +41 -41
- data/lib/rltk/cg/triple.rb +7 -7
- data/lib/rltk/cg/type.rb +75 -75
- data/lib/rltk/cg/value.rb +161 -161
- data/lib/rltk/lexer.rb +57 -57
- data/lib/rltk/lexers/calculator.rb +7 -7
- data/lib/rltk/lexers/ebnf.rb +5 -5
- data/lib/rltk/parser.rb +338 -295
- data/lib/rltk/parsers/infix_calc.rb +7 -7
- data/lib/rltk/parsers/postfix_calc.rb +3 -3
- data/lib/rltk/parsers/prefix_calc.rb +3 -3
- data/lib/rltk/token.rb +13 -13
- data/lib/rltk/version.rb +6 -6
- data/test/cg/tc_basic_block.rb +17 -17
- data/test/cg/tc_control_flow.rb +41 -41
- data/test/cg/tc_function.rb +4 -4
- data/test/cg/tc_generic_value.rb +3 -3
- data/test/cg/tc_instruction.rb +53 -53
- data/test/cg/tc_math.rb +12 -12
- data/test/cg/tc_module.rb +14 -14
- data/test/cg/tc_transforms.rb +11 -11
- data/test/cg/tc_type.rb +12 -12
- data/test/cg/tc_value.rb +35 -35
- data/test/cg/ts_cg.rb +5 -5
- data/test/tc_ast.rb +137 -60
- data/test/tc_cfg.rb +34 -34
- data/test/tc_lexer.rb +42 -42
- data/test/tc_parser.rb +250 -173
- data/test/tc_token.rb +2 -2
- data/test/ts_rltk.rb +8 -8
- metadata +84 -85
- data/lib/rltk/cg/old_generated_bindings.rb +0 -6152
data/lib/rltk/cg/type.rb
CHANGED
@@ -16,13 +16,13 @@ require 'filigree/abstract_class'
|
|
16
16
|
# Ruby Language Toolkit
|
17
17
|
require 'rltk/cg/bindings'
|
18
18
|
require 'rltk/cg/context'
|
19
|
-
|
19
|
+
|
20
20
|
#######################
|
21
21
|
# Classes and Modules #
|
22
22
|
#######################
|
23
23
|
|
24
24
|
module RLTK::CG
|
25
|
-
|
25
|
+
|
26
26
|
# The Type class and its sub-classes are used to describe the size and
|
27
27
|
# structure of various data objects inside LLVM and how different
|
28
28
|
# operations interact with them. When instantiating objects of the
|
@@ -32,7 +32,7 @@ module RLTK::CG
|
|
32
32
|
class Type
|
33
33
|
include BindingClass
|
34
34
|
include Filigree::AbstractClass
|
35
|
-
|
35
|
+
|
36
36
|
# Instantiate a Type object from a pointer. This function is used
|
37
37
|
# internally, and as a library user you should never have to call it.
|
38
38
|
#
|
@@ -59,13 +59,13 @@ module RLTK::CG
|
|
59
59
|
when :x86_mmx then X86MMXType.new
|
60
60
|
end
|
61
61
|
end
|
62
|
-
|
62
|
+
|
63
63
|
# The default constructor for Type objects.
|
64
64
|
#
|
65
65
|
# @param [Context, nil] context An optional context in which to create the type.
|
66
66
|
def initialize(context = nil)
|
67
67
|
bname = Bindings.get_bname(self.class.short_name)
|
68
|
-
|
68
|
+
|
69
69
|
@ptr =
|
70
70
|
if context
|
71
71
|
Bindings.send((bname.to_s + '_in_context').to_sym, check_type(context, Context, 'context'))
|
@@ -73,82 +73,82 @@ module RLTK::CG
|
|
73
73
|
Bindings.send(bname)
|
74
74
|
end
|
75
75
|
end
|
76
|
-
|
76
|
+
|
77
77
|
# @return [NativeInt] Alignment of the type.
|
78
78
|
def allignment
|
79
79
|
NativeInt.new(Bindings.align_of(@ptr))
|
80
80
|
end
|
81
|
-
|
81
|
+
|
82
82
|
# @return [Context] Context in which this type was created.
|
83
83
|
def context
|
84
84
|
Context.new(Bindings.get_type_context(@ptr))
|
85
85
|
end
|
86
|
-
|
86
|
+
|
87
87
|
# Dump a string representation of the type to stdout.
|
88
88
|
#
|
89
89
|
# @return [void]
|
90
90
|
def dump
|
91
91
|
Bindings.dump_type(@ptr)
|
92
92
|
end
|
93
|
-
|
93
|
+
|
94
94
|
# @return [Fixnum] Hashed value of the pointer representing this type.
|
95
95
|
def hash
|
96
96
|
@ptr.address.hash
|
97
97
|
end
|
98
|
-
|
98
|
+
|
99
99
|
# @see Bindings._enum_type_kind_
|
100
100
|
#
|
101
101
|
# @return [Symbol] The *kind* of this type.
|
102
102
|
def kind
|
103
103
|
Bindings.get_type_kind(@ptr)
|
104
104
|
end
|
105
|
-
|
105
|
+
|
106
106
|
# @return [NativeInt] Size of objects of this type.
|
107
107
|
def size
|
108
108
|
Int64.new(Bindings.size_of(@ptr))
|
109
109
|
end
|
110
|
-
|
110
|
+
|
111
111
|
# @return [String] LLVM IR representation of the type
|
112
112
|
def to_s
|
113
113
|
Bindings.print_type_to_string(@ptr)
|
114
114
|
end
|
115
115
|
end
|
116
|
-
|
116
|
+
|
117
117
|
# All types that are used to represent numbers inherit from this class.
|
118
118
|
#
|
119
119
|
# @abstract
|
120
120
|
class NumberType < Type
|
121
121
|
include Filigree::AbstractClass
|
122
|
-
|
122
|
+
|
123
123
|
# @return [Value] The corresponding Value sub-class that is used to represent values of this type.
|
124
124
|
def self.value_class
|
125
125
|
begin
|
126
126
|
@value_class ||=
|
127
127
|
RLTK::CG.const_get(self.name.match(/::(.+)Type$/).captures.last.to_sym)
|
128
|
-
|
128
|
+
|
129
129
|
rescue
|
130
130
|
raise "#{self.name} has no value class."
|
131
131
|
end
|
132
132
|
end
|
133
|
-
|
133
|
+
|
134
134
|
# @return [Value] The corresponding Value sub-class that is used to represent values of this type.
|
135
135
|
def value_class
|
136
136
|
self.class.value_class
|
137
137
|
end
|
138
138
|
end
|
139
|
-
|
139
|
+
|
140
140
|
# All types that represent integers of a given width inherit from this class.
|
141
141
|
#
|
142
142
|
# @abstract
|
143
143
|
class BasicIntType < NumberType
|
144
144
|
include Filigree::AbstractClass
|
145
|
-
|
145
|
+
|
146
146
|
# @return [Integer] Number of bits used to represent an integer type.
|
147
147
|
def width
|
148
148
|
@width ||= Bindings.get_int_type_width(@ptr)
|
149
149
|
end
|
150
150
|
end
|
151
|
-
|
151
|
+
|
152
152
|
# An integer of an arbitrary width.
|
153
153
|
class IntType < BasicIntType
|
154
154
|
# @param [Integer] width Width of new integer type.
|
@@ -167,7 +167,7 @@ module RLTK::CG
|
|
167
167
|
raise 'The width parameter must be greater then 0.'
|
168
168
|
end
|
169
169
|
end
|
170
|
-
|
170
|
+
|
171
171
|
# Overrides {NumberType#value_class}.
|
172
172
|
#
|
173
173
|
# @raise [RuntimeError] This function has no meaning in this class.
|
@@ -175,7 +175,7 @@ module RLTK::CG
|
|
175
175
|
raise 'The RLKT::CG::IntType class has no value class.'
|
176
176
|
end
|
177
177
|
end
|
178
|
-
|
178
|
+
|
179
179
|
# A class inherited by singleton integer type classes.
|
180
180
|
#
|
181
181
|
# @abstract
|
@@ -183,7 +183,7 @@ module RLTK::CG
|
|
183
183
|
include Filigree::AbstractClass
|
184
184
|
include Singleton
|
185
185
|
end
|
186
|
-
|
186
|
+
|
187
187
|
# A class inherited by all types representing floats.
|
188
188
|
#
|
189
189
|
# @abstract
|
@@ -191,7 +191,7 @@ module RLTK::CG
|
|
191
191
|
include Filigree::AbstractClass
|
192
192
|
include Singleton
|
193
193
|
end
|
194
|
-
|
194
|
+
|
195
195
|
# A class inherited by non-number singleton type classes.
|
196
196
|
#
|
197
197
|
# @abstract
|
@@ -199,7 +199,7 @@ module RLTK::CG
|
|
199
199
|
include Filigree::AbstractClass
|
200
200
|
include Singleton
|
201
201
|
end
|
202
|
-
|
202
|
+
|
203
203
|
# 1 bit integer type. Often used to represent Boolean values.
|
204
204
|
class Int1Type < SimpleIntType; end
|
205
205
|
# 8 bit (1 byte) integer type.
|
@@ -210,7 +210,7 @@ module RLTK::CG
|
|
210
210
|
class Int32Type < SimpleIntType; end
|
211
211
|
# 64 bit (8 byte) integer type.
|
212
212
|
class Int64Type < SimpleIntType; end
|
213
|
-
|
213
|
+
|
214
214
|
# Integer the same size as a native pointer.
|
215
215
|
class IntPtr < SimpleIntType
|
216
216
|
# Create an integer that is the same size as a pointer on the target
|
@@ -223,26 +223,26 @@ module RLTK::CG
|
|
223
223
|
def initialize(target_data, addr_space = nil, context = nil)
|
224
224
|
call = 'int_type'
|
225
225
|
args = [target_data]
|
226
|
-
|
226
|
+
|
227
227
|
if addr_space
|
228
228
|
call += '_for_as'
|
229
229
|
args << addr_space
|
230
230
|
end
|
231
|
-
|
231
|
+
|
232
232
|
if context
|
233
233
|
call += '_in_context'
|
234
234
|
args << context
|
235
235
|
end
|
236
|
-
|
236
|
+
|
237
237
|
Bindings.send(call.to_s, *args)
|
238
238
|
end
|
239
239
|
end
|
240
|
-
|
240
|
+
|
241
241
|
# The native integer type on the current (not the target) platform.
|
242
242
|
NativeIntType = RLTK::CG.const_get("Int#{FFI.type_size(:int) * 8}Type")
|
243
|
-
|
243
|
+
|
244
244
|
# A 16-bit floating point number type.
|
245
|
-
class HalfType < RealType; end
|
245
|
+
class HalfType < RealType; end
|
246
246
|
# A double precision floating point number type.
|
247
247
|
class DoubleType < RealType; end
|
248
248
|
# A single precision floating point number type.
|
@@ -253,29 +253,29 @@ module RLTK::CG
|
|
253
253
|
class PPCFP128Type < RealType; end
|
254
254
|
# A 80 bit (10 byte) floating point number type for the x86 architecture.
|
255
255
|
class X86FP80Type < RealType; end
|
256
|
-
|
256
|
+
|
257
257
|
# A type for x86 MMX instructions.
|
258
258
|
class X86MMXType < SimpleType; end
|
259
|
-
|
259
|
+
|
260
260
|
# A type used in representing void pointers and functions that return no values.
|
261
261
|
class VoidType < SimpleType; end
|
262
262
|
# A type used to represent labels in LLVM IR.
|
263
263
|
class LabelType < SimpleType; end
|
264
|
-
|
264
|
+
|
265
265
|
# The common ancestor for array, pointer, and struct types.
|
266
266
|
#
|
267
267
|
# @abstract
|
268
268
|
class AggregateType < Type
|
269
269
|
include Filigree::AbstractClass
|
270
270
|
end
|
271
|
-
|
271
|
+
|
272
272
|
# {ArrayType} and {PointerType} inherit from this class so they can share
|
273
273
|
# a constructor.
|
274
274
|
#
|
275
275
|
# @abstract
|
276
276
|
class SimpleAggregateType < AggregateType
|
277
277
|
include Filigree::AbstractClass
|
278
|
-
|
278
|
+
|
279
279
|
# Used to initialize {ArrayType ArrayTypes} and {PointerType PointerTypes}.
|
280
280
|
#
|
281
281
|
# @param [FFI::Pointer, Type] overloaded Pointer to an existing aggregate type or a Type object that
|
@@ -288,17 +288,17 @@ module RLTK::CG
|
|
288
288
|
else
|
289
289
|
@element_type = check_cg_type(overloaded, Type, 'overloaded')
|
290
290
|
bname = Bindings.get_bname(self.class.short_name)
|
291
|
-
|
291
|
+
|
292
292
|
Bindings.send(bname, @element_type, size_or_address_space)
|
293
293
|
end
|
294
294
|
end
|
295
|
-
|
295
|
+
|
296
296
|
# @return [Type] Type of objects stored inside this aggregate.
|
297
297
|
def element_type
|
298
298
|
@element_type ||= Type.from_ptr(Bindings.get_element_type(@ptr))
|
299
299
|
end
|
300
300
|
end
|
301
|
-
|
301
|
+
|
302
302
|
# A Type describing an array that holds objects of a single given type.
|
303
303
|
class ArrayType < SimpleAggregateType
|
304
304
|
# @return [Integer] Number of elements in this array type.
|
@@ -307,7 +307,7 @@ module RLTK::CG
|
|
307
307
|
end
|
308
308
|
alias :length :size
|
309
309
|
end
|
310
|
-
|
310
|
+
|
311
311
|
# A Type describing a pointer to another type.
|
312
312
|
class PointerType < SimpleAggregateType
|
313
313
|
# @return [Integer] Address space of this pointer.
|
@@ -315,7 +315,7 @@ module RLTK::CG
|
|
315
315
|
@address_space ||= Bindings.get_pointer_address_space(@ptr)
|
316
316
|
end
|
317
317
|
end
|
318
|
-
|
318
|
+
|
319
319
|
# A type used to represent vector operations (SIMD). This is NOT an
|
320
320
|
# aggregate type.
|
321
321
|
class VectorType < Type
|
@@ -331,28 +331,28 @@ module RLTK::CG
|
|
331
331
|
else
|
332
332
|
@element_type = check_cg_type(overloaded, Type, 'overloaded')
|
333
333
|
bname = Bindings.get_bname(self.class.short_name)
|
334
|
-
|
334
|
+
|
335
335
|
Bindings.send(bname, @element_type, size)
|
336
336
|
end
|
337
337
|
end
|
338
|
-
|
338
|
+
|
339
339
|
# @return [Type] Type of object stored inside this vector.
|
340
340
|
def element_type
|
341
341
|
@element_type ||= Type.from_ptr(Bindings.get_element_type(@ptr))
|
342
342
|
end
|
343
|
-
|
343
|
+
|
344
344
|
# @return [Integer] Number of objects in this vector type.
|
345
345
|
def size
|
346
346
|
Bindings.get_vector_size(@ptr)
|
347
347
|
end
|
348
348
|
alias :length :size
|
349
349
|
end
|
350
|
-
|
350
|
+
|
351
351
|
# A type representing the return an argument types for a function.
|
352
352
|
class FunctionType < Type
|
353
353
|
# @return [Array<Type>] Types of this function type's arguments.
|
354
354
|
attr_reader :arg_types
|
355
|
-
|
355
|
+
|
356
356
|
# Create a new function type from a pointer or description of the
|
357
357
|
# return type and argument types.
|
358
358
|
#
|
@@ -367,36 +367,36 @@ module RLTK::CG
|
|
367
367
|
else
|
368
368
|
@return_type = check_cg_type(overloaded, Type, 'return_type')
|
369
369
|
@arg_types = check_cg_array_type(arg_types, Type, 'arg_types').freeze
|
370
|
-
|
370
|
+
|
371
371
|
arg_types_ptr = FFI::MemoryPointer.new(:pointer, @arg_types.length)
|
372
372
|
arg_types_ptr.write_array_of_pointer(@arg_types)
|
373
|
-
|
373
|
+
|
374
374
|
Bindings.function_type(@return_type, arg_types_ptr, @arg_types.length, varargs.to_i)
|
375
375
|
end
|
376
376
|
end
|
377
|
-
|
377
|
+
|
378
378
|
# @return [Array<Type>] Types of this function type's arguments.
|
379
379
|
def argument_types
|
380
380
|
@arg_types ||=
|
381
381
|
begin
|
382
382
|
num_elements = Bindings.count_param_types(@ptr)
|
383
|
-
|
383
|
+
|
384
384
|
ret_ptr = FFI::MemoryPointer.new(:pointer)
|
385
385
|
Bindings.get_param_types(@ptr, ret_ptr)
|
386
|
-
|
386
|
+
|
387
387
|
types_ptr = ret_ptr.get_pointer(0)
|
388
|
-
|
388
|
+
|
389
389
|
types_ptr.get_array_of_pointer(0, num_elements).map { |ptr| Type.from_ptr(ptr) }
|
390
390
|
end
|
391
391
|
end
|
392
392
|
alias :arg_types :argument_types
|
393
|
-
|
393
|
+
|
394
394
|
# @return [Type] The return type of this function type.
|
395
395
|
def return_type
|
396
396
|
@return_type ||= Type.from_ptr(Bindings.get_return_type(@ptr))
|
397
397
|
end
|
398
398
|
end
|
399
|
-
|
399
|
+
|
400
400
|
# A type for representing an arbitrary collection of types.
|
401
401
|
class StructType < AggregateType
|
402
402
|
# Create a new struct type.
|
@@ -413,43 +413,43 @@ module RLTK::CG
|
|
413
413
|
else
|
414
414
|
# Check the types of the elements of the overloaded parameter.
|
415
415
|
@element_types = check_cg_array_type(overloaded, Type, 'overloaded')
|
416
|
-
|
416
|
+
|
417
417
|
el_types_ptr = FFI::MemoryPointer.new(:pointer, @element_types.length)
|
418
418
|
el_types_ptr.write_array_of_pointer(@element_types)
|
419
|
-
|
419
|
+
|
420
420
|
if name
|
421
421
|
@name = check_type(name, String, 'name')
|
422
|
-
|
422
|
+
|
423
423
|
Bindings.struct_create_named(Context.global, @name).tap do |ptr|
|
424
424
|
Bindings.struct_set_body(ptr, el_types_ptr, @element_types.length, packed.to_i) unless @element_types.empty?
|
425
425
|
end
|
426
|
-
|
426
|
+
|
427
427
|
elsif context
|
428
428
|
check_type(context, Context, 'context')
|
429
|
-
|
429
|
+
|
430
430
|
Bindings.struct_type_in_context(context, el_types_ptr, @element_types.length, is_packed.to_i)
|
431
|
-
|
431
|
+
|
432
432
|
else
|
433
433
|
Bindings.struct_type(el_types_ptr, @element_types.length, packed.to_i)
|
434
434
|
end
|
435
435
|
end
|
436
436
|
end
|
437
|
-
|
437
|
+
|
438
438
|
# @return [Array<Type>] Array of the types in this struct type.
|
439
439
|
def element_types
|
440
440
|
@element_types ||=
|
441
441
|
begin
|
442
442
|
num_elements = Bindings.count_struct_element_types(@ptr)
|
443
|
-
|
443
|
+
|
444
444
|
ret_ptr = FFI::MemoryPointer.new(:pointer)
|
445
445
|
Bindings.get_struct_element_types(@ptr, ret_ptr)
|
446
|
-
|
446
|
+
|
447
447
|
types_ptr = ret_ptr.get_pointer(0)
|
448
|
-
|
448
|
+
|
449
449
|
types_ptr.get_array_of_pointer(0, num_elements).map { |ptr| Type.from_ptr(ptr) }
|
450
450
|
end
|
451
451
|
end
|
452
|
-
|
452
|
+
|
453
453
|
# Set the types in the body of this struct type.
|
454
454
|
#
|
455
455
|
# @param [Array<Type>] el_types Array of types in the struct.
|
@@ -458,13 +458,13 @@ module RLTK::CG
|
|
458
458
|
# @return [void]
|
459
459
|
def element_types=(el_types, packed = false)
|
460
460
|
@element_types = check_cg_array_type(el_types, Type, 'el_types')
|
461
|
-
|
461
|
+
|
462
462
|
el_types_ptr = FFI::MemoryPointer.new(:pointer, @element_types.length)
|
463
463
|
el_types_ptr.write_array_of_pointer(@element_types)
|
464
|
-
|
464
|
+
|
465
465
|
Bindings.struct_set_body(@ptr, el_types_ptr, @element_types.length, packed.to_i)
|
466
466
|
end
|
467
|
-
|
467
|
+
|
468
468
|
# @return [String] Name of the struct type in LLVM IR.
|
469
469
|
def name
|
470
470
|
@name ||= Bindings.get_struct_name(@ptr)
|
@@ -494,8 +494,8 @@ end
|
|
494
494
|
# @return [Type] The object *o* or an instance of the class passed in parameter *o*.
|
495
495
|
def check_cg_type(o, type = RLTK::CG::Type, blame = 'type', strict = false)
|
496
496
|
if o.is_a?(Class)
|
497
|
-
type_ok = if strict then o == type else o.subclass_of?(type) end
|
498
|
-
|
497
|
+
type_ok = if strict then o == type else o.subclass_of?(type) end
|
498
|
+
|
499
499
|
if type_ok
|
500
500
|
if o.includes_module?(Singleton)
|
501
501
|
o.instance
|
@@ -503,7 +503,7 @@ def check_cg_type(o, type = RLTK::CG::Type, blame = 'type', strict = false)
|
|
503
503
|
raise ArgumentError, "The #{o.name} class (passed as parameter #{blame}) must be instantiated directly."
|
504
504
|
end
|
505
505
|
else
|
506
|
-
raise ArgumentError, "The #{o.name} class (passed as parameter #{blame} does not inherit from the #{type.name} class."
|
506
|
+
raise ArgumentError, "The #{o.name} class (passed as parameter #{blame} does not inherit from the #{type.name} class."
|
507
507
|
end
|
508
508
|
else
|
509
509
|
check_type(o, type, blame, strict)
|
@@ -512,7 +512,7 @@ end
|
|
512
512
|
|
513
513
|
# This helper function checks to make sure that an array of objects are all
|
514
514
|
# sub-classses of {RLTK::CG::Type Type} or instances of a sub-class of Type.
|
515
|
-
# If a class is present in the *array* parameter it is expected to be a
|
515
|
+
# If a class is present in the *array* parameter it is expected to be a
|
516
516
|
# singleton class and will be instantiated via the *instance* method.
|
517
517
|
#
|
518
518
|
# @param [Array<Type, Class>] array Array of objects to type check for code generation type.
|
@@ -530,7 +530,7 @@ def check_cg_array_type(array, type = RLTK::CG::Type, blame = 'el_types', strict
|
|
530
530
|
array.map do |o|
|
531
531
|
if o.is_a?(Class)
|
532
532
|
type_ok = if strict then o == type else o.subclass_of?(type) end
|
533
|
-
|
533
|
+
|
534
534
|
if type_ok
|
535
535
|
if o.includes_module?(Singleton)
|
536
536
|
o.instance
|
@@ -540,10 +540,10 @@ def check_cg_array_type(array, type = RLTK::CG::Type, blame = 'el_types', strict
|
|
540
540
|
else
|
541
541
|
raise ArgumentError, "The #{o.name} class (passed in parameter #{blame}) does not inherit from the #{type.name} class."
|
542
542
|
end
|
543
|
-
|
543
|
+
|
544
544
|
else
|
545
545
|
type_ok = if strict then o.instance_of(type) else o.is_a?(type) end
|
546
|
-
|
546
|
+
|
547
547
|
if type_ok
|
548
548
|
o
|
549
549
|
else
|