ripper_ruby_parser 1.1.2 → 1.2.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.
Files changed (40) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +19 -0
  3. data/README.md +2 -2
  4. data/Rakefile +1 -1
  5. data/lib/ripper_ruby_parser.rb +0 -7
  6. data/lib/ripper_ruby_parser/commenting_ripper_parser.rb +112 -34
  7. data/lib/ripper_ruby_parser/parser.rb +26 -12
  8. data/lib/ripper_ruby_parser/sexp_handlers.rb +4 -1
  9. data/lib/ripper_ruby_parser/sexp_handlers/arguments.rb +7 -6
  10. data/lib/ripper_ruby_parser/sexp_handlers/arrays.rb +4 -2
  11. data/lib/ripper_ruby_parser/sexp_handlers/assignment.rb +39 -43
  12. data/lib/ripper_ruby_parser/sexp_handlers/blocks.rb +93 -69
  13. data/lib/ripper_ruby_parser/sexp_handlers/conditionals.rb +30 -24
  14. data/lib/ripper_ruby_parser/sexp_handlers/hashes.rb +7 -9
  15. data/lib/ripper_ruby_parser/sexp_handlers/helper_methods.rb +51 -71
  16. data/lib/ripper_ruby_parser/sexp_handlers/literals.rb +72 -56
  17. data/lib/ripper_ruby_parser/sexp_handlers/loops.rb +14 -13
  18. data/lib/ripper_ruby_parser/sexp_handlers/method_calls.rb +19 -13
  19. data/lib/ripper_ruby_parser/sexp_handlers/methods.rb +19 -22
  20. data/lib/ripper_ruby_parser/sexp_handlers/operators.rb +47 -35
  21. data/lib/ripper_ruby_parser/sexp_processor.rb +72 -85
  22. data/lib/ripper_ruby_parser/version.rb +1 -1
  23. data/test/end_to_end/line_numbering_test.rb +1 -1
  24. data/test/end_to_end/samples_comparison_test.rb +0 -1
  25. data/test/pt_testcase/pt_test.rb +4 -6
  26. data/test/{unit → ripper_ruby_parser}/commenting_ripper_parser_test.rb +82 -25
  27. data/test/{unit → ripper_ruby_parser}/parser_test.rb +37 -170
  28. data/test/{unit/parser_assignment_test.rb → ripper_ruby_parser/sexp_handlers/assignment_test.rb} +1 -1
  29. data/test/{unit/parser_blocks_test.rb → ripper_ruby_parser/sexp_handlers/blocks_test.rb} +267 -2
  30. data/test/{unit/parser_conditionals_test.rb → ripper_ruby_parser/sexp_handlers/conditionals_test.rb} +125 -17
  31. data/test/{unit/parser_literals_test.rb → ripper_ruby_parser/sexp_handlers/literals_test.rb} +10 -12
  32. data/test/{unit/parser_loops_test.rb → ripper_ruby_parser/sexp_handlers/loops_test.rb} +1 -1
  33. data/test/{unit/parser_method_calls_test.rb → ripper_ruby_parser/sexp_handlers/method_calls_test.rb} +10 -10
  34. data/test/{unit/parser_operators_test.rb → ripper_ruby_parser/sexp_handlers/operators_test.rb} +22 -2
  35. data/test/{unit → ripper_ruby_parser}/sexp_processor_test.rb +49 -48
  36. data/test/{unit → ripper_ruby_parser}/version_test.rb +0 -0
  37. data/test/samples/misc.rb +4 -0
  38. data/test/test_helper.rb +4 -4
  39. metadata +28 -42
  40. data/test/end_to_end/error_conditions_test.rb +0 -51
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 44bd9ec7c716649fbcf3698e39d9d7951b70a3d0
4
- data.tar.gz: 1b54fd14347e28a5c253b8f00be1a641dc7cbda5
2
+ SHA256:
3
+ metadata.gz: 57d07c4327035328992f94f2e0f49b36362743c84062414a6f09b8738aef709b
4
+ data.tar.gz: 9e19226ece2a88714fc2e2483e4accb0ee7aca7f31398d25396f72581f3c864f
5
5
  SHA512:
6
- metadata.gz: 4fd9c3ffee9de0d9948a126ca5e44d3dc46b50b36b4fd7d9537263b84b123ebeac09118f3b0ae12f203f7c3ee1eb438bca92b2b630502b7bc83f6d3ea1a47668
7
- data.tar.gz: c24028ea9320a5fdba4eee182d257e9f3c5db7fc57f9b3e6ba15471e90eb96b48c291b7046e8ca52b83af4957a62e6b4321901179d53fe53ea549f431baf7f85
6
+ metadata.gz: 9f68e5ad8f58a6035ae16be08e5188a4f22f6809ce59705de877527ede90247125525a7be007b86b794ab046a8861ac4bd417ee3974e4d6fe60c877ff92c4230
7
+ data.tar.gz: 6f1b8cbd085e0249a56bf937d8806cba3dad3a07893f843361c8142321693e6c8e10225782f8fee672eb4ee3c148512778544648731b486f5977c142004f1271
data/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # Change log
2
2
 
3
+ ## 1.2.0 / 2018-01-12
4
+
5
+ * Improve code quality
6
+ * Document public API
7
+ * Speed improvements
8
+ - Process line numbers only once per parse run
9
+ - Reduce arbitrary conditionals
10
+ - Use deconstruction to split up block
11
+ * Improve intermediate s-expressions, reducing the number of typeless
12
+ expressions.
13
+ * Use SexpBuilder base class, giving more low-level access to the structure
14
+ created by Ripper.
15
+ * Support Ruby 2.5
16
+ * Improve handling of boolean operators with parenthes
17
+ * Improve compatibility for begin..end blocks used as method and operator
18
+ arguments.
19
+ * Drop support for Ruby 2.0 and 2.1
20
+ * Handle `__ENCODING__` constant.
21
+
3
22
  ## 1.1.2 / 2017-10-07
4
23
 
5
24
  * Fix support for newer Ruby syntax
data/README.md CHANGED
@@ -12,7 +12,7 @@ Parse with Ripper, produce sexps that are compatible with RubyParser.
12
12
 
13
13
  * Drop-in replacement for RubyParser.
14
14
  * Should handle 1.9 and later syntax gracefully.
15
- * Needs MRI 2.0 or higher
15
+ * Requires MRI 2.2 or higher
16
16
 
17
17
  ## Install
18
18
 
@@ -29,7 +29,7 @@ Parse with Ripper, produce sexps that are compatible with RubyParser.
29
29
 
30
30
  ## Requirements
31
31
 
32
- * Ruby 2.0 or higher
32
+ * Ruby 2.2 or higher
33
33
  * sexp_processor
34
34
 
35
35
  ## Hacking and contributing
data/Rakefile CHANGED
@@ -5,7 +5,7 @@ require 'rake/testtask'
5
5
  namespace :test do
6
6
  Rake::TestTask.new(:unit) do |t|
7
7
  t.libs = ['lib']
8
- t.test_files = FileList['test/unit/*_test.rb']
8
+ t.test_files = FileList['test/ripper_ruby_parser/**/*_test.rb']
9
9
  t.warning = true
10
10
  end
11
11
 
@@ -1,9 +1,2 @@
1
- if RUBY_VERSION < '1.9.3'
2
- raise LoadError, 'Only ruby version 1.9.3 and up are supported'
3
- end
4
-
5
1
  require 'ripper_ruby_parser/version'
6
2
  require 'ripper_ruby_parser/parser'
7
-
8
- module RipperRubyParser
9
- end
@@ -2,10 +2,12 @@ require 'ripper'
2
2
  require 'ripper_ruby_parser/syntax_error'
3
3
 
4
4
  module RipperRubyParser
5
- # Variant of Ripper's SexpBuilderPP parser class that inserts comments as
5
+ # Variant of Ripper's SexpBuilder parser class that inserts comments as
6
6
  # Sexps into the built parse tree.
7
- class CommentingRipperParser < Ripper::SexpBuilderPP
8
- def initialize *args
7
+ #
8
+ # @api private
9
+ class CommentingRipperParser < Ripper::SexpBuilder
10
+ def initialize(*args)
9
11
  super
10
12
  @comment = nil
11
13
  @comment_stack = []
@@ -19,13 +21,13 @@ module RipperRubyParser
19
21
  Sexp.from_array(result)
20
22
  end
21
23
 
22
- def on_comment tok
24
+ def on_comment(tok)
23
25
  @comment ||= ''
24
26
  @comment += tok
25
27
  super
26
28
  end
27
29
 
28
- def on_kw tok
30
+ def on_kw(tok)
29
31
  case tok
30
32
  when 'class', 'def', 'module'
31
33
  unless @in_symbol
@@ -36,42 +38,122 @@ module RipperRubyParser
36
38
  super
37
39
  end
38
40
 
39
- def on_module *args
41
+ def on_module(*args)
40
42
  commentize(:module, super)
41
43
  end
42
44
 
43
- def on_class *args
45
+ def on_class(*args)
44
46
  commentize(:class, super)
45
47
  end
46
48
 
47
- def on_sclass *args
49
+ def on_sclass(*args)
48
50
  commentize(:class, super)
49
51
  end
50
52
 
51
- def on_def *args
53
+ def on_def(*args)
52
54
  commentize(:def, super)
53
55
  end
54
56
 
55
- def on_defs *args
57
+ def on_defs(*args)
56
58
  commentize(:def, super)
57
59
  end
58
60
 
59
- def on_qsymbols_add list, elem
60
- super list, [:dyna_symbol, [elem]]
61
+ def on_args_new
62
+ [:args]
61
63
  end
62
64
 
63
- def on_symbols_add list, elem
64
- super list, [:dyna_symbol, elem]
65
+ def on_args_add(list, elem)
66
+ list << elem
65
67
  end
66
68
 
67
- def on_words_add list, elem
68
- if elem.count == 1
69
- super
69
+ def on_mlhs_new
70
+ [:mlhs]
71
+ end
72
+
73
+ def on_mlhs_add(list, elem)
74
+ if list.first == :mlhs
75
+ list << elem
70
76
  else
71
- super list, [:string_content, *elem]
77
+ [:mlhs_add_post, list, elem]
72
78
  end
73
79
  end
74
80
 
81
+ def on_mrhs_new
82
+ [:mrhs]
83
+ end
84
+
85
+ def on_mrhs_add(list, elem)
86
+ list << elem
87
+ end
88
+
89
+ def on_qsymbols_new
90
+ [:qsymbols]
91
+ end
92
+
93
+ def on_qsymbols_add(list, elem)
94
+ list << elem
95
+ end
96
+
97
+ def on_qwords_new
98
+ [:qwords]
99
+ end
100
+
101
+ def on_qwords_add(list, elem)
102
+ list << elem
103
+ end
104
+
105
+ def on_regexp_new
106
+ [:regexp]
107
+ end
108
+
109
+ def on_regexp_add(list, elem)
110
+ list << elem
111
+ end
112
+
113
+ def on_stmts_new
114
+ [:stmts]
115
+ end
116
+
117
+ def on_stmts_add(list, elem)
118
+ list << elem
119
+ end
120
+
121
+ def on_string_add(list, elem)
122
+ list << elem
123
+ end
124
+
125
+ def on_symbols_new
126
+ [:symbols]
127
+ end
128
+
129
+ def on_symbols_add(list, elem)
130
+ list << elem
131
+ end
132
+
133
+ def on_word_new
134
+ [:word]
135
+ end
136
+
137
+ def on_word_add(list, elem)
138
+ list << elem
139
+ end
140
+
141
+ def on_words_new
142
+ [:words]
143
+ end
144
+
145
+ def on_words_add(list, elem)
146
+ list << elem
147
+ end
148
+
149
+ def on_xstring_new
150
+ [:xstring]
151
+ end
152
+
153
+ def on_xstring_add(list, elem)
154
+ list << elem
155
+ end
156
+
75
157
  def on_op(token)
76
158
  @seen_space = false
77
159
  super
@@ -92,7 +174,7 @@ module RipperRubyParser
92
174
  super
93
175
  end
94
176
 
95
- NUMBER_LITERAL_TYPES = [:@int, :@float]
177
+ NUMBER_LITERAL_TYPES = [:@int, :@float].freeze
96
178
 
97
179
  def on_unary(op, value)
98
180
  if !@space_before && op == :-@ && NUMBER_LITERAL_TYPES.include?(value.first)
@@ -107,55 +189,51 @@ module RipperRubyParser
107
189
  end
108
190
  end
109
191
 
110
- def on_symbeg *args
192
+ def on_symbeg(*args)
111
193
  @in_symbol = true
112
194
  super
113
195
  end
114
196
 
115
- def on_symbol *args
197
+ def on_symbol(*args)
116
198
  @in_symbol = false
117
199
  super
118
200
  end
119
201
 
120
- def on_embexpr_beg *args
202
+ def on_embexpr_beg(*args)
121
203
  @in_symbol = false
122
204
  super
123
205
  end
124
206
 
125
- def on_dyna_symbol *args
207
+ def on_dyna_symbol(*args)
126
208
  @in_symbol = false
127
209
  super
128
210
  end
129
211
 
130
- def on_parse_error *args
212
+ def on_parse_error(*args)
131
213
  raise SyntaxError, *args
132
214
  end
133
215
 
134
- def on_class_name_error *args
216
+ def on_class_name_error(*args)
135
217
  raise SyntaxError, *args
136
218
  end
137
219
 
138
- def on_alias_error *args
220
+ def on_alias_error(*args)
139
221
  raise SyntaxError, *args
140
222
  end
141
223
 
142
- def on_assign_error *args
224
+ def on_assign_error(*args)
143
225
  raise SyntaxError, *args
144
226
  end
145
227
 
146
- def on_param_error *args
228
+ def on_param_error(*args)
147
229
  raise SyntaxError, *args
148
230
  end
149
231
 
150
232
  private
151
233
 
152
- def commentize name, exp
153
- raise "Comment stack empty in #{name} event" if @comment_stack.empty?
154
- tok, comment = @comment_stack.pop
234
+ def commentize(_name, exp)
235
+ _tok, comment = @comment_stack.pop
155
236
  @comment = nil
156
- unless tok == name
157
- raise "Expected on_#{tok} event, got on_#{name}"
158
- end
159
237
  [:comment, comment || '', exp]
160
238
  end
161
239
 
@@ -5,26 +5,40 @@ module RipperRubyParser
5
5
  # Main parser class. Brings together Ripper and our
6
6
  # RipperRubyParser::SexpProcessor.
7
7
  class Parser
8
- attr_accessor :extra_compatible
9
-
10
- def initialize processor = SexpProcessor.new
11
- @processor = processor
12
- @extra_compatible = false
13
- end
14
-
15
- def parse source, filename = '(string)', lineno = 1
8
+ def parse(source, filename = '(string)', lineno = 1)
16
9
  parser = CommentingRipperParser.new(source, filename, lineno)
17
10
  exp = parser.parse
18
11
 
19
- @processor.filename = filename
20
- @processor.extra_compatible = extra_compatible
21
- result = @processor.process exp
12
+ processor = SexpProcessor.new(filename: filename)
13
+ result = processor.process exp
22
14
 
23
- if result == s(:void_stmt)
15
+ if result.sexp_type == :void_stmt
24
16
  nil
25
17
  else
18
+ trickle_up_line_numbers result
19
+ trickle_down_line_numbers result
26
20
  result
27
21
  end
28
22
  end
23
+
24
+ private
25
+
26
+ def trickle_up_line_numbers(exp)
27
+ exp.each do |sub_exp|
28
+ if sub_exp.is_a? Sexp
29
+ trickle_up_line_numbers sub_exp
30
+ exp.line ||= sub_exp.line
31
+ end
32
+ end
33
+ end
34
+
35
+ def trickle_down_line_numbers(exp)
36
+ exp.each do |sub_exp|
37
+ if sub_exp.is_a? Sexp
38
+ sub_exp.line ||= exp.line
39
+ trickle_down_line_numbers sub_exp
40
+ end
41
+ end
42
+ end
29
43
  end
30
44
  end
@@ -13,8 +13,11 @@ require 'ripper_ruby_parser/sexp_handlers/methods'
13
13
  require 'ripper_ruby_parser/sexp_handlers/operators'
14
14
 
15
15
  module RipperRubyParser
16
+ # Umbrella module for handlers of particular sexp types
17
+ #
18
+ # @api private
16
19
  module SexpHandlers
17
- def self.included base
20
+ def self.included(base)
18
21
  base.class_eval do
19
22
  include HelperMethods
20
23
 
@@ -1,25 +1,26 @@
1
1
  module RipperRubyParser
2
2
  module SexpHandlers
3
+ # Sexp handlers for argument lists
3
4
  module Arguments
4
- def process_args_add_block exp
5
+ def process_args_add_block(exp)
5
6
  _, regular, block = exp.shift 3
6
- args = handle_potentially_typeless_sexp(regular)
7
+ args = process(regular)
7
8
  args << s(:block_pass, process(block)) if block
8
- s(:arglist, *args)
9
+ s(:arglist, *args.sexp_body)
9
10
  end
10
11
 
11
- def process_args_add_star exp
12
+ def process_args_add_star(exp)
12
13
  generic_add_star exp
13
14
  end
14
15
 
15
- def process_arg_paren exp
16
+ def process_arg_paren(exp)
16
17
  _, args = exp.shift 2
17
18
  args = s() if args.nil?
18
19
  args.unshift :arglist unless args.first.is_a? Symbol
19
20
  process(args)
20
21
  end
21
22
 
22
- def process_rest_param exp
23
+ def process_rest_param(exp)
23
24
  _, ident = exp.shift 2
24
25
  s(:splat, process(ident))
25
26
  end
@@ -1,12 +1,14 @@
1
1
  module RipperRubyParser
2
2
  module SexpHandlers
3
+ # Sexp handlers for array literals
3
4
  module Arrays
4
- def process_array exp
5
+ def process_array(exp)
5
6
  _, elems = exp.shift 2
7
+ return s(:array) if elems.nil?
6
8
  s(:array, *handle_array_elements(elems))
7
9
  end
8
10
 
9
- def process_aref exp
11
+ def process_aref(exp)
10
12
  _, coll, idx = exp.shift 3
11
13
 
12
14
  coll = process(coll)
@@ -1,14 +1,15 @@
1
1
  module RipperRubyParser
2
2
  module SexpHandlers
3
+ # Sexp handlers for assignments
3
4
  module Assignment
4
- def process_assign exp
5
+ def process_assign(exp)
5
6
  _, lvalue, value = exp.shift 3
6
7
  lvalue = process(lvalue)
7
8
  value = process(value)
8
9
 
9
10
  case value.sexp_type
10
- when :splat
11
- value = s(:svalue, value)
11
+ when :mrhs
12
+ value.sexp_type = :svalue
12
13
  when :fake_array
13
14
  value = s(:svalue, s(:array, *value.sexp_body))
14
15
  end
@@ -17,25 +18,21 @@ module RipperRubyParser
17
18
  create_regular_assignment_sub_type(lvalue, value))
18
19
  end
19
20
 
20
- def process_massign exp
21
+ def process_massign(exp)
21
22
  _, left, right = exp.shift 3
22
23
 
23
- left = handle_potentially_typeless_sexp left
24
+ left = process left
24
25
 
25
- if left.first == :masgn
26
- left = left[1]
27
- left.shift
28
- end
29
-
30
- left = create_multiple_assignment_sub_types left
26
+ left = left[1] if left.sexp_type == :masgn
27
+ left = create_multiple_assignment_sub_types left.sexp_body
31
28
 
32
29
  right = process(right)
33
30
 
34
31
  case right.sexp_type
35
32
  when :fake_array
36
33
  right[0] = :array
37
- when :splat
38
- nil # Do nothing
34
+ when :mrhs
35
+ right = right[1]
39
36
  else
40
37
  right = s(:to_ary, right)
41
38
  end
@@ -43,42 +40,41 @@ module RipperRubyParser
43
40
  s(:masgn, s(:array, *left), right)
44
41
  end
45
42
 
46
- def process_mrhs_new_from_args exp
43
+ def process_mrhs_new_from_args(exp)
47
44
  _, inner, last = exp.shift 3
48
- inner.map! { |item| process(item) }
49
- inner.push process(last) unless last.nil?
45
+ inner = map_process_sexp_body_compact(inner)
46
+ inner.push process(last) if last
50
47
  s(:fake_array, *inner)
51
48
  end
52
49
 
53
- def process_mrhs_add_star exp
54
- exp = generic_add_star exp
55
-
56
- if exp.first.is_a? Symbol
57
- exp
58
- else
59
- exp.first
60
- end
50
+ def process_mrhs_add_star(exp)
51
+ generic_add_star exp
61
52
  end
62
53
 
63
- def process_mlhs_add_star exp
64
- _, args, splatarg, rest = exp.shift 4
65
- items = handle_potentially_typeless_sexp args
54
+ def process_mlhs_add_star(exp)
55
+ _, args, splatarg = exp.shift 3
56
+ items = process args
66
57
  items << s(:splat, process(splatarg))
67
- rest.each { |arg| items << process(arg) } if rest
68
- items
69
58
  end
70
59
 
71
- def process_mlhs_paren exp
72
- _, contents = exp.shift 2
60
+ def process_mlhs_add_post(exp)
61
+ _, base, rest = exp.shift 3
62
+ process(base).push(*process(rest).sexp_body)
63
+ end
73
64
 
74
- items = handle_potentially_typeless_sexp(contents)
65
+ def process_mlhs_paren(exp)
66
+ _, contents = exp.shift 2
75
67
 
76
- return items if items.first.is_a? Symbol
68
+ items = process(contents)
77
69
 
78
- s(:masgn, s(:array, *create_multiple_assignment_sub_types(items)))
70
+ if items.sexp_type == :mlhs
71
+ s(:masgn, s(:array, *create_multiple_assignment_sub_types(items.sexp_body)))
72
+ else
73
+ items
74
+ end
79
75
  end
80
76
 
81
- def process_opassign exp
77
+ def process_opassign(exp)
82
78
  _, lvalue, operator, value = exp.shift 4
83
79
 
84
80
  lvalue = process(lvalue)
@@ -90,7 +86,7 @@ module RipperRubyParser
90
86
 
91
87
  private
92
88
 
93
- def create_multiple_assignment_sub_types sexp_list
89
+ def create_multiple_assignment_sub_types(sexp_list)
94
90
  sexp_list.map! do |item|
95
91
  if item.sexp_type == :splat
96
92
  if item[1].nil?
@@ -104,7 +100,7 @@ module RipperRubyParser
104
100
  end
105
101
  end
106
102
 
107
- def create_valueless_assignment_sub_type item
103
+ def create_valueless_assignment_sub_type(item)
108
104
  item = with_line_number(item.line,
109
105
  create_regular_assignment_sub_type(item, nil))
110
106
  item.pop
@@ -112,11 +108,11 @@ module RipperRubyParser
112
108
  end
113
109
 
114
110
  OPERATOR_ASSIGNMENT_MAP = {
115
- :"||" => :op_asgn_or,
116
- :"&&" => :op_asgn_and
111
+ '||': :op_asgn_or,
112
+ '&&': :op_asgn_and
117
113
  }.freeze
118
114
 
119
- def create_operator_assignment_sub_type lvalue, value, operator
115
+ def create_operator_assignment_sub_type(lvalue, value, operator)
120
116
  case lvalue.sexp_type
121
117
  when :aref_field
122
118
  _, arr, arglist = lvalue
@@ -134,7 +130,7 @@ module RipperRubyParser
134
130
  end
135
131
  end
136
132
 
137
- def create_regular_assignment_sub_type lvalue, value
133
+ def create_regular_assignment_sub_type(lvalue, value)
138
134
  case lvalue.sexp_type
139
135
  when :aref_field
140
136
  _, arr, arglist = lvalue
@@ -161,11 +157,11 @@ module RipperRubyParser
161
157
  cvar: :cvasgn
162
158
  }.freeze
163
159
 
164
- def create_assignment_sub_type lvalue, value
160
+ def create_assignment_sub_type(lvalue, value)
165
161
  s(map_assignment_lvalue_type(lvalue.sexp_type), lvalue[1], value)
166
162
  end
167
163
 
168
- def map_assignment_lvalue_type type
164
+ def map_assignment_lvalue_type(type)
169
165
  @in_method_body && ASSIGNMENT_IN_METHOD_SUB_TYPE_MAP[type] ||
170
166
  ASSIGNMENT_SUB_TYPE_MAP[type] ||
171
167
  type