openc3 5.5.2 → 5.6.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.

Potentially problematic release.


This version of openc3 might be problematic. Click here for more details.

Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/bin/openc3cli +167 -69
  3. data/data/config/_interfaces.yaml +1 -6
  4. data/data/config/interface_modifiers.yaml +55 -4
  5. data/data/config/microservice.yaml +30 -3
  6. data/ext/openc3/ext/crc/crc.c +82 -1
  7. data/lib/openc3/api/cmd_api.rb +19 -7
  8. data/lib/openc3/api/tlm_api.rb +13 -12
  9. data/lib/openc3/bridge/bridge_config.rb +4 -4
  10. data/lib/openc3/config/config_parser.rb +1 -0
  11. data/lib/openc3/conversions/unix_time_conversion.rb +3 -1
  12. data/lib/openc3/ext/.keep +0 -0
  13. data/lib/openc3/interfaces/interface.rb +54 -26
  14. data/lib/openc3/interfaces/serial_interface.rb +4 -5
  15. data/lib/openc3/interfaces/simulated_target_interface.rb +4 -4
  16. data/lib/openc3/interfaces/stream_interface.rb +2 -2
  17. data/lib/openc3/interfaces/tcpip_client_interface.rb +4 -3
  18. data/lib/openc3/interfaces/tcpip_server_interface.rb +18 -19
  19. data/lib/openc3/interfaces/udp_interface.rb +10 -4
  20. data/lib/openc3/io/json_api.rb +72 -0
  21. data/lib/openc3/io/serial_driver.rb +4 -5
  22. data/lib/openc3/logs/buffered_packet_log_writer.rb +2 -4
  23. data/lib/openc3/logs/log_writer.rb +9 -8
  24. data/lib/openc3/logs/packet_log_reader.rb +8 -1
  25. data/lib/openc3/logs/packet_log_writer.rb +3 -4
  26. data/lib/openc3/logs/stream_log.rb +116 -0
  27. data/lib/openc3/logs/stream_log_pair.rb +70 -0
  28. data/lib/openc3/microservices/cleanup_microservice.rb +1 -1
  29. data/lib/openc3/microservices/decom_microservice.rb +17 -2
  30. data/lib/openc3/microservices/interface_decom_common.rb +42 -0
  31. data/lib/openc3/microservices/interface_microservice.rb +24 -17
  32. data/lib/openc3/microservices/router_microservice.rb +46 -4
  33. data/lib/openc3/migrations/20221202214600_add_target_names.rb +1 -1
  34. data/lib/openc3/migrations/20230319154100_log_stream.rb +40 -0
  35. data/lib/openc3/migrations/20230413101100_remove_log.rb +30 -0
  36. data/lib/openc3/models/gem_model.rb +2 -2
  37. data/lib/openc3/models/interface_model.rb +13 -14
  38. data/lib/openc3/models/metadata_model.rb +1 -1
  39. data/lib/openc3/models/note_model.rb +1 -1
  40. data/lib/openc3/models/plugin_model.rb +3 -2
  41. data/lib/openc3/operators/operator.rb +2 -0
  42. data/lib/openc3/packets/commands.rb +2 -0
  43. data/lib/openc3/packets/packet_config.rb +3 -2
  44. data/lib/openc3/packets/parsers/xtce_converter.rb +2 -1
  45. data/lib/openc3/script/gems.rb +125 -0
  46. data/lib/openc3/script/plugins.rb +186 -0
  47. data/lib/openc3/script/screen.rb +119 -0
  48. data/lib/openc3/script/script.rb +3 -0
  49. data/lib/openc3/script/script_runner.rb +19 -8
  50. data/lib/openc3/script/suite_results.rb +2 -2
  51. data/lib/openc3/script/web_socket_api.rb +5 -1
  52. data/lib/openc3/streams/serial_stream.rb +14 -11
  53. data/lib/openc3/streams/tcpip_client_stream.rb +5 -2
  54. data/lib/openc3/streams/tcpip_socket_stream.rb +37 -71
  55. data/lib/openc3/streams/web_socket_client_stream.rb +5 -3
  56. data/lib/openc3/system/system.rb +2 -0
  57. data/lib/openc3/topics/interface_topic.rb +13 -4
  58. data/lib/openc3/topics/router_topic.rb +6 -6
  59. data/lib/openc3/topics/telemetry_decom_topic.rb +10 -1
  60. data/lib/openc3/utilities/bucket_utilities.rb +12 -5
  61. data/lib/openc3/utilities/cli_generator.rb +56 -4
  62. data/lib/openc3/utilities/crc.rb +42 -7
  63. data/lib/openc3/utilities/process_manager.rb +3 -1
  64. data/lib/openc3/utilities/ruby_lex_utils.rb +265 -504
  65. data/lib/openc3/version.rb +6 -6
  66. data/templates/conversion/conversion.rb +10 -2
  67. data/templates/microservice/microservices/TEMPLATE/microservice.rb +1 -1
  68. data/templates/plugin/Rakefile +8 -1
  69. data/templates/widget/.browserslistrc +16 -0
  70. data/templates/widget/.eslintrc.js +43 -0
  71. data/templates/widget/.nycrc +3 -0
  72. data/templates/widget/.prettierrc.js +5 -0
  73. data/templates/widget/LICENSE.txt +20 -0
  74. data/templates/widget/Rakefile +24 -0
  75. data/templates/widget/babel.config.json +11 -0
  76. data/templates/widget/package.json +35 -0
  77. data/templates/widget/src/Widget.vue +46 -0
  78. data/templates/widget/vue.config.js +25 -0
  79. data/templates/widget/yarn.lock +8938 -0
  80. metadata +23 -4
  81. data/lib/openc3/io/raw_logger.rb +0 -170
  82. data/lib/openc3/io/raw_logger_pair.rb +0 -80
@@ -17,555 +17,316 @@
17
17
  # All changes Copyright 2022, OpenC3, Inc.
18
18
  # All Rights Reserved
19
19
  #
20
- # This file may also be used under the terms of a commercial license
20
+ # This file may also be used under the terms of a commercial license
21
21
  # if purchased from OpenC3, Inc.
22
22
 
23
23
  require 'irb/ruby-lex'
24
24
  require 'stringio'
25
25
 
26
- if RUBY_VERSION >= "2.7"
27
-
28
- # Clear the $VERBOSE global since we're overriding methods
29
- old_verbose = $VERBOSE; $VERBOSE = nil
30
- class RubyLex
31
- attr_accessor :indent
32
- attr_accessor :line_no
33
- attr_accessor :exp_line_no
34
- attr_accessor :tokens
35
- attr_accessor :code_block_open
36
- attr_accessor :ltype
37
- attr_accessor :line
38
- attr_accessor :continue
26
+ # Clear the $VERBOSE global since we're overriding methods
27
+ old_verbose = $VERBOSE; $VERBOSE = nil
28
+ class RubyLex
29
+ attr_accessor :indent
30
+ attr_accessor :line_no
31
+ attr_accessor :exp_line_no
32
+ attr_accessor :tokens
33
+ attr_accessor :code_block_open
34
+ attr_accessor :ltype
35
+ attr_accessor :line
36
+ attr_accessor :continue
37
+
38
+ def reinitialize
39
+ @line_no = 1
40
+ @prompt = nil
41
+ initialize_input()
42
+ end
43
+ end
44
+ $VERBOSE = old_verbose
45
+
46
+ class RubyLexUtils
47
+ # Regular expression to detect blank lines
48
+ BLANK_LINE_REGEX = /^\s*$/
49
+ # Regular expression to detect lines containing only 'else'
50
+ LONELY_ELSE_REGEX = /^\s*else\s*$/
51
+
52
+ KEY_KEYWORDS = [
53
+ 'class'.freeze,
54
+ 'module'.freeze,
55
+ 'def'.freeze,
56
+ 'undef'.freeze,
57
+ 'begin'.freeze,
58
+ 'rescue'.freeze,
59
+ 'ensure'.freeze,
60
+ 'end'.freeze,
61
+ 'if'.freeze,
62
+ 'unless'.freeze,
63
+ 'then'.freeze,
64
+ 'elsif'.freeze,
65
+ 'else'.freeze,
66
+ 'case'.freeze,
67
+ 'when'.freeze,
68
+ 'while'.freeze,
69
+ 'until'.freeze,
70
+ 'for'.freeze,
71
+ 'break'.freeze,
72
+ 'next'.freeze,
73
+ 'redo'.freeze,
74
+ 'retry'.freeze,
75
+ 'in'.freeze,
76
+ 'do'.freeze,
77
+ 'return'.freeze,
78
+ 'alias'.freeze
79
+ ]
80
+
81
+ # Create a new RubyLex and StringIO to hold the text to operate on
82
+ def initialize
83
+ @lex = RubyLex.new
84
+ @lex_io = StringIO.new('')
85
+ end
39
86
 
40
- def reinitialize
41
- @line_no = 1
42
- @prompt = nil
43
- initialize_input()
87
+ if RUBY_VERSION >= "3.0"
88
+ def ripper_lex_without_warning(code)
89
+ RubyLex.ripper_lex_without_warning(code)
90
+ end
91
+ else
92
+ def ripper_lex_without_warning(code)
93
+ @lex.ripper_lex_without_warning(code)
44
94
  end
45
95
  end
46
- $VERBOSE = old_verbose
47
96
 
48
- class RubyLexUtils
49
- # Regular expression to detect blank lines
50
- BLANK_LINE_REGEX = /^\s*$/
51
- # Regular expression to detect lines containing only 'else'
52
- LONELY_ELSE_REGEX = /^\s*else\s*$/
53
-
54
- KEY_KEYWORDS = [
55
- 'class'.freeze,
56
- 'module'.freeze,
57
- 'def'.freeze,
58
- 'undef'.freeze,
59
- 'begin'.freeze,
60
- 'rescue'.freeze,
61
- 'ensure'.freeze,
62
- 'end'.freeze,
63
- 'if'.freeze,
64
- 'unless'.freeze,
65
- 'then'.freeze,
66
- 'elsif'.freeze,
67
- 'else'.freeze,
68
- 'case'.freeze,
69
- 'when'.freeze,
70
- 'while'.freeze,
71
- 'until'.freeze,
72
- 'for'.freeze,
73
- 'break'.freeze,
74
- 'next'.freeze,
75
- 'redo'.freeze,
76
- 'retry'.freeze,
77
- 'in'.freeze,
78
- 'do'.freeze,
79
- 'return'.freeze,
80
- 'alias'.freeze
81
- ]
82
-
83
- # Create a new RubyLex and StringIO to hold the text to operate on
84
- def initialize
85
- @lex = RubyLex.new
86
- @lex_io = StringIO.new('')
97
+ # @param text [String]
98
+ # @return [Boolean] Whether the text contains the 'begin' keyword
99
+ def contains_begin?(text)
100
+ @lex.reinitialize
101
+ @lex_io.string = text
102
+ @lex.set_input(@lex_io)
103
+ tokens = ripper_lex_without_warning(text)
104
+ tokens.each do |token|
105
+ if token[1] == :on_kw and token[2] == 'begin'
106
+ return true
107
+ end
87
108
  end
109
+ return false
110
+ end
88
111
 
89
- if RUBY_VERSION >= "3.0"
90
- def ripper_lex_without_warning(code)
91
- RubyLex.ripper_lex_without_warning(code)
92
- end
93
- else
94
- def ripper_lex_without_warning(code)
95
- @lex.ripper_lex_without_warning(code)
112
+ # @param text [String]
113
+ # @return [Boolean] Whether the text contains the 'end' keyword
114
+ def contains_end?(text)
115
+ @lex.reinitialize
116
+ @lex_io.string = text
117
+ @lex.set_input(@lex_io)
118
+ tokens = ripper_lex_without_warning(text)
119
+ tokens.each do |token|
120
+ if token[1] == :on_kw and token[2] == 'end'
121
+ return true
96
122
  end
97
123
  end
124
+ return false
125
+ end
98
126
 
99
- # @param text [String]
100
- # @return [Boolean] Whether the text contains the 'begin' keyword
101
- def contains_begin?(text)
102
- @lex.reinitialize
103
- @lex_io.string = text
104
- @lex.set_input(@lex_io)
105
- tokens = ripper_lex_without_warning(text)
106
- tokens.each do |token|
107
- if token[1] == :on_kw and token[2] == 'begin'
127
+ # @param text [String]
128
+ # @return [Boolean] Whether the text contains a Ruby keyword
129
+ def contains_keyword?(text)
130
+ @lex.reinitialize
131
+ @lex_io.string = text
132
+ @lex.set_input(@lex_io)
133
+ tokens = ripper_lex_without_warning(text)
134
+ tokens.each do |token|
135
+ if token[1] == :on_kw
136
+ if KEY_KEYWORDS.include?(token[2])
108
137
  return true
109
138
  end
139
+ elsif token[1] == :on_lbrace and !token[3].allbits?(Ripper::EXPR_BEG | Ripper::EXPR_LABEL)
140
+ return true
110
141
  end
111
- return false
112
142
  end
143
+ return false
144
+ end
113
145
 
114
- # @param text [String]
115
- # @return [Boolean] Whether the text contains a Ruby keyword
116
- def contains_keyword?(text)
117
- @lex.reinitialize
118
- @lex_io.string = text
119
- @lex.set_input(@lex_io)
120
- tokens = ripper_lex_without_warning(text)
121
- tokens.each do |token|
122
- if token[1] == :on_kw
123
- if KEY_KEYWORDS.include?(token[2])
124
- return true
125
- end
126
- elsif token[1] == :on_lbrace and !token[3].allbits?(Ripper::EXPR_BEG | Ripper::EXPR_LABEL)
146
+ # @param text [String]
147
+ # @return [Boolean] Whether the text contains a keyword which starts a block.
148
+ # i.e. 'do', '{', or 'begin'
149
+ def contains_block_beginning?(text)
150
+ @lex.reinitialize
151
+ @lex_io.string = text
152
+ @lex.set_input(@lex_io)
153
+ tokens = ripper_lex_without_warning(text)
154
+ tokens.each do |token|
155
+ if token[1] == :on_kw
156
+ if token[2] == 'begin' || token[2] == 'do'
127
157
  return true
128
158
  end
159
+ elsif token[1] == :on_lbrace
160
+ return true
129
161
  end
130
- return false
131
162
  end
163
+ return false
164
+ end
132
165
 
133
- # @param text [String]
134
- # @return [Boolean] Whether the text contains a keyword which starts a block.
135
- # i.e. 'do', '{', or 'begin'
136
- def contains_block_beginning?(text)
137
- @lex.reinitialize
138
- @lex_io.string = text
139
- @lex.set_input(@lex_io)
140
- tokens = ripper_lex_without_warning(text)
141
- tokens.each do |token|
142
- if token[1] == :on_kw
143
- if token[2] == 'begin' || token[2] == 'do'
144
- return true
145
- end
146
- elsif token[1] == :on_lbrace
147
- return true
148
- end
149
- end
150
- return false
166
+ def continue_block?(text)
167
+ @lex.reinitialize
168
+ @lex_io.string = text
169
+ @lex.set_input(@lex_io)
170
+ tokens = RubyLex.ripper_lex_without_warning(text)
171
+ index = tokens.length - 1
172
+ while index > 0
173
+ token = tokens[index]
174
+ return true if token[1] == :on_kw and token[2] == "do"
175
+ index -= 1
151
176
  end
177
+ return false
178
+ end
152
179
 
153
- # @param text [String]
154
- # @param progress_dialog [OpenC3::ProgressDialog] If this is set, the overall
155
- # progress will be set as the processing progresses
156
- # @return [String] The text with all comments removed
157
- def remove_comments(text, progress_dialog = nil)
158
- @lex.reinitialize
159
- @lex_io.string = text
160
- @lex.set_input(@lex_io)
161
- comments_removed = ""
162
- token_count = 0
163
- progress = 0.0
164
- tokens = ripper_lex_without_warning(text)
165
- tokens.each do |token|
166
- token_count += 1
167
- if token[1] != :on_comment
168
- comments_removed << token[2]
169
- else
170
- newline_count = token[2].count("\n")
171
- comments_removed << ("\n" * newline_count)
172
- end
173
- if progress_dialog and token_count % 10000 == 0
174
- progress += 0.01
175
- progress = 0.0 if progress >= 0.99
176
- progress_dialog.set_overall_progress(progress)
177
- end
180
+ # @param text [String]
181
+ # @param progress_dialog [OpenC3::ProgressDialog] If this is set, the overall
182
+ # progress will be set as the processing progresses
183
+ # @return [String] The text with all comments removed
184
+ def remove_comments(text, progress_dialog = nil)
185
+ @lex.reinitialize
186
+ @lex_io.string = text
187
+ @lex.set_input(@lex_io)
188
+ comments_removed = ""
189
+ token_count = 0
190
+ progress = 0.0
191
+ tokens = ripper_lex_without_warning(text)
192
+ tokens.each do |token|
193
+ token_count += 1
194
+ if token[1] != :on_comment
195
+ comments_removed << token[2]
196
+ else
197
+ newline_count = token[2].count("\n")
198
+ comments_removed << ("\n" * newline_count)
199
+ end
200
+ if progress_dialog and token_count % 10000 == 0
201
+ progress += 0.01
202
+ progress = 0.0 if progress >= 0.99
203
+ progress_dialog.set_overall_progress(progress)
178
204
  end
179
-
180
- return comments_removed
181
205
  end
182
206
 
183
- # Yields each lexed segment and if the segment is instrumentable
184
- #
185
- # @param text [String]
186
- # @yieldparam line [String] The entire line
187
- # @yieldparam instrumentable [Boolean] Whether the line is instrumentable
188
- # @yieldparam inside_begin [Integer] The level of indentation
189
- # @yieldparam line_no [Integer] The current line number
190
- def each_lexed_segment(text)
191
- inside_begin = false
192
- indent = 0
193
- lex = RubyLex.new
194
- lex_io = StringIO.new(text)
195
- lex.set_input(lex_io)
196
- lex.line = ''
197
- while lexed = lex.lex
198
- lex.line_no += lexed.count("\n")
199
- lex.line.concat lexed
200
- next if lex.ltype or lex.continue
201
-
202
- # Detect the beginning and end of begin blocks so we can not catch exceptions there
203
- if indent == 0 and contains_begin?(lex.line)
204
- inside_begin = true
205
- indent = lex.indent
206
- else
207
- indent += lex.indent if indent > 0
208
- end
209
-
210
- if inside_begin and indent <= 0
211
- indent = 0
212
- inside_begin = false
213
- end
214
-
215
- loop do # loop to allow restarting for nested conditions
216
- # Yield blank lines and lonely else lines before the actual line
217
- while (index = lex.line.index("\n"))
218
- line = lex.line[0..index]
219
- if BLANK_LINE_REGEX.match?(line)
220
- yield line, true, inside_begin, lex.exp_line_no
221
- lex.exp_line_no += 1
222
- lex.line = lex.line[(index + 1)..-1]
223
- elsif LONELY_ELSE_REGEX.match?(line)
224
- yield line, false, inside_begin, lex.exp_line_no
225
- lex.exp_line_no += 1
226
- lex.line = lex.line[(index + 1)..-1]
227
- else
228
- break
229
- end
230
- end
207
+ return comments_removed
208
+ end
231
209
 
232
- if contains_keyword?(lex.line)
233
- if contains_block_beginning?(lex.line)
234
- section = ''
235
- lex.line.each_line do |lexed_part|
236
- section << lexed_part
237
- if contains_block_beginning?(section)
238
- yield section, false, inside_begin, lex.exp_line_no
239
- break
240
- end
241
- lex.exp_line_no += 1
242
- end
243
- lex.exp_line_no += 1
244
- remainder = lex.line[(section.length)..-1]
245
- lex.line = remainder
246
- next unless remainder.empty?
247
- else
248
- yield lex.line, false, inside_begin, lex.exp_line_no
249
- end
250
- elsif !lex.line.empty?
251
- num_left_brackets = lex.line.count('{')
252
- num_right_brackets = lex.line.count('}')
253
- if num_left_brackets != num_right_brackets
254
- # Don't instrument lines with unequal numbers of { and } brackets
255
- yield lex.line, false, inside_begin, lex.exp_line_no
210
+ # Yields each lexed segment and if the segment is instrumentable
211
+ #
212
+ # @param text [String]
213
+ # @yieldparam line [String] The entire line
214
+ # @yieldparam instrumentable [Boolean] Whether the line is instrumentable
215
+ # @yieldparam inside_begin [Integer] The level of indentation
216
+ # @yieldparam line_no [Integer] The current line number
217
+ def each_lexed_segment(text)
218
+ inside_begin = false
219
+ lex = RubyLex.new
220
+ lex_io = StringIO.new(text)
221
+ lex.set_input(lex_io)
222
+ lex.line = ''
223
+ line = ''
224
+ continue_indent = nil
225
+ begin_indent = nil
226
+ previous_indent = 0
227
+
228
+ while lexed = lex.lex
229
+ #puts "lexed = #{lexed.chomp}, indent = #{lex.indent}, continue = #{lex.continue}"
230
+ lex.line_no += lexed.count("\n")
231
+ lex.line.concat lexed
232
+ line.concat lexed
233
+ if lex.continue
234
+ if not continue_block?(lexed)
235
+ unless continue_indent
236
+ if (lex.indent - previous_indent) > 1
237
+ continue_indent = lex.indent - 1
256
238
  else
257
- yield lex.line, true, inside_begin, lex.exp_line_no
239
+ continue_indent = previous_indent
258
240
  end
259
241
  end
260
- lex.line = ''
261
- lex.exp_line_no = lex.line_no
262
- lex.indent = 0
263
- break
264
- end # loop do
265
- end # while lexed
266
- end # def each_lexed_segment
267
- end
268
-
269
- else
270
-
271
- # Clear the $VERBOSE global since we're overriding methods
272
- old_verbose = $VERBOSE; $VERBOSE = nil
273
- class RubyLex
274
- if self.method_defined?(:indent)
275
- attr_writer :indent
276
- else
277
- attr_accessor :indent
278
- end
279
- # @return [Integer] The expression line number. This can differ from the
280
- # actual line number due to white space and Ruby control keywords.
281
- attr_accessor :exp_line_no
282
-
283
- # Resets the RubyLex in preparation of parsing a line
284
- def reinitialize
285
- @seek = 0
286
- @exp_line_no = 1
287
- @line_no = 1
288
- @base_char_no = 0
289
- @char_no = 0
290
- @rests.clear
291
- @readed.clear
292
- @here_readed.clear
293
- @indent = 0
294
- @indent_stack.clear
295
- @lex_state = EXPR_BEG
296
- @space_seen = false
297
- @here_header = false
298
- @continue = false
299
- @line = ''
300
- @skip_space = false
301
- @readed_auto_clean_up = false
302
- @exception_on_syntax_error = true
303
- @prompt = nil
304
- end
305
-
306
- # Monkey patch to keep this from looping forever if the string never is closed with a right brace
307
- def identify_string_dvar
308
- getc
309
-
310
- reserve_continue = @continue
311
- reserve_ltype = @ltype
312
- reserve_indent = @indent
313
- reserve_indent_stack = @indent_stack
314
- reserve_state = @lex_state
315
- reserve_quoted = @quoted
316
-
317
- @ltype = nil
318
- @quoted = nil
319
- @indent = 0
320
- @indent_stack = []
321
- @lex_state = EXPR_BEG
322
-
323
- loop do
324
- @continue = false
325
- prompt
326
- tk = token
327
- break if tk.nil? # This is the patch
328
- if @ltype or @continue or @indent >= 0
242
+ #puts "continue_indent = #{continue_indent}"
329
243
  next
330
244
  end
331
- break if tk.kind_of?(TkRBRACE)
332
- end
333
- ensure
334
- @continue = reserve_continue
335
- @ltype = reserve_ltype
336
- @indent = reserve_indent
337
- @indent_stack = reserve_indent_stack
338
- @lex_state = reserve_state
339
- @quoted = reserve_quoted
340
- end
341
- end
342
- $VERBOSE = old_verbose
343
-
344
- class RubyLexUtils
345
- # Regular expression to detect blank lines
346
- BLANK_LINE_REGEX = /^\s*$/
347
- # Regular expression to detect lines containing only 'else'
348
- LONELY_ELSE_REGEX = /^\s*else\s*$/
349
-
350
- # Ruby keywords
351
- KEYWORD_TOKENS = [RubyToken::TkCLASS,
352
- RubyToken::TkMODULE,
353
- RubyToken::TkDEF,
354
- RubyToken::TkUNDEF,
355
- RubyToken::TkBEGIN,
356
- RubyToken::TkRESCUE,
357
- RubyToken::TkENSURE,
358
- RubyToken::TkEND,
359
- RubyToken::TkIF,
360
- RubyToken::TkUNLESS,
361
- RubyToken::TkTHEN,
362
- RubyToken::TkELSIF,
363
- RubyToken::TkELSE,
364
- RubyToken::TkCASE,
365
- RubyToken::TkWHEN,
366
- RubyToken::TkWHILE,
367
- RubyToken::TkUNTIL,
368
- RubyToken::TkFOR,
369
- RubyToken::TkBREAK,
370
- RubyToken::TkNEXT,
371
- RubyToken::TkREDO,
372
- RubyToken::TkRETRY,
373
- RubyToken::TkIN,
374
- RubyToken::TkDO,
375
- RubyToken::TkRETURN,
376
- RubyToken::TkIF_MOD,
377
- RubyToken::TkUNLESS_MOD,
378
- RubyToken::TkWHILE_MOD,
379
- RubyToken::TkUNTIL_MOD,
380
- RubyToken::TkALIAS,
381
- RubyToken::TklBEGIN,
382
- RubyToken::TklEND,
383
- RubyToken::TkfLBRACE]
384
-
385
- # Ruby keywords which define the beginning of a block: do, {, begin
386
- BLOCK_BEGINNING_TOKENS = [RubyToken::TkDO,
387
- RubyToken::TkfLBRACE,
388
- RubyToken::TkBEGIN]
389
-
390
- # Create a new RubyLex and StringIO to hold the text to operate on
391
- def initialize
392
- @lex = RubyLex.new
393
- @lex_io = StringIO.new('')
394
- end
395
-
396
- # @param text [String]
397
- # @return [Boolean] Whether the text contains the 'begin' keyword
398
- def contains_begin?(text)
399
- @lex.reinitialize
400
- @lex.exception_on_syntax_error = false
401
- @lex_io.string = text
402
- @lex.set_input(@lex_io)
403
- while token = @lex.token
404
- if token.class == RubyToken::TkBEGIN
405
- return true
406
- end
407
- end
408
- return false
409
- end
410
-
411
- # @param text [String]
412
- # @return [Boolean] Whether the text contains a Ruby keyword
413
- def contains_keyword?(text)
414
- @lex.reinitialize
415
- @lex.exception_on_syntax_error = false
416
- @lex_io.string = text
417
- @lex.set_input(@lex_io)
418
- while token = @lex.token
419
- if KEYWORD_TOKENS.include?(token.class)
420
- return true
421
- end
422
- end
423
- return false
424
- end
425
-
426
- # @param text [String]
427
- # @return [Boolean] Whether the text contains a keyword which starts a block.
428
- # i.e. 'do', '{', or 'begin'
429
- def contains_block_beginning?(text)
430
- @lex.reinitialize
431
- @lex.exception_on_syntax_error = false
432
- @lex_io.string = text
433
- @lex.set_input(@lex_io)
434
- while token = @lex.token
435
- if BLOCK_BEGINNING_TOKENS.include?(token.class)
436
- return true
245
+ elsif continue_indent
246
+ if lex.indent > continue_indent
247
+ next
248
+ else
249
+ yield line, !contains_keyword?(line), inside_begin, lex.exp_line_no
250
+ line = ''
251
+ lex.exp_line_no = lex.line_no
252
+ if lex.indent == 0
253
+ lex.line = ''
254
+ end
255
+ next
437
256
  end
438
257
  end
439
- return false
440
- end
441
-
442
- # @param text [String]
443
- # @param progress_dialog [OpenC3::ProgressDialog] If this is set, the overall
444
- # progress will be set as the processing progresses
445
- # @return [String] The text with all comments removed
446
- def remove_comments(text, progress_dialog = nil)
447
- comments_removed = text.clone
448
- @lex.reinitialize
449
- @lex.exception_on_syntax_error = false
450
- @lex_io.string = text
451
- @lex.set_input(@lex_io)
452
- need_remove = nil
453
- delete_ranges = []
454
- token_count = 0
455
- progress = 0.0
456
- while token = @lex.token
457
- token_count += 1
458
- if need_remove
459
- delete_ranges << (need_remove..(token.seek - 1))
460
- need_remove = nil
461
- end
462
- if token.class == RubyToken::TkCOMMENT
463
- need_remove = token.seek
464
- end
465
- if progress_dialog and token_count % 10000 == 0
466
- progress += 0.01
467
- progress = 0.0 if progress >= 0.99
468
- progress_dialog.set_overall_progress(progress)
258
+ previous_indent = lex.indent
259
+ continue_indent = nil
260
+
261
+ # Detect the beginning and end of begin blocks so we can not catch exceptions there
262
+ if contains_begin?(line)
263
+ if contains_end?(line)
264
+ # Assume the user is being fancy with a single line begin; end;
265
+ # Ignore
266
+ else
267
+ inside_begin = true
268
+ begin_indent = lex.indent unless begin_indent # Don't restart for nested begins
469
269
  end
470
270
  end
471
271
 
472
- if need_remove
473
- delete_ranges << (need_remove..(text.length - 1))
474
- need_remove = nil
272
+ if inside_begin and lex.indent < begin_indent
273
+ begin_indent = nil
274
+ inside_begin = false
475
275
  end
476
276
 
477
- delete_count = 0
478
- delete_ranges.reverse_each do |range|
479
- delete_count += 1
480
- comments_removed[range] = ''
481
- if progress_dialog and delete_count % 10000 == 0
482
- progress += 0.01
483
- progress = 0.0 if progress >= 0.99
484
- progress_dialog.set_overall_progress(progress)
485
- end
486
- end
487
-
488
- return comments_removed
489
- end
490
-
491
- # Yields each lexed segment and if the segment is instrumentable
492
- #
493
- # @param text [String]
494
- # @yieldparam line [String] The entire line
495
- # @yieldparam instrumentable [Boolean] Whether the line is instrumentable
496
- # @yieldparam inside_begin [Integer] The level of indentation
497
- # @yieldparam line_no [Integer] The current line number
498
- def each_lexed_segment(text)
499
- inside_begin = false
500
- inside_indent = nil
501
- lex = RubyLex.new
502
- lex.exception_on_syntax_error = false
503
- lex_io = StringIO.new(text)
504
- lex.set_input(lex_io)
505
-
506
- while lexed = lex.lex
507
- line_no = lex.exp_line_no
508
-
509
- if inside_indent.nil? and contains_begin?(lexed)
510
- inside_indent = lex.indent - 1
511
- inside_begin = true
512
- end
513
-
514
- if lex.indent == inside_indent
515
- inside_indent = nil
516
- inside_begin = false
517
- end
518
-
519
- loop do # loop to allow restarting for nested conditions
520
- # Yield blank lines and lonely else lines before the actual line
521
- while (index = lexed.index("\n"))
522
- line = lexed[0..index]
523
- if BLANK_LINE_REGEX.match?(line)
524
- yield line, true, inside_begin, line_no
525
- line_no += 1
526
- lexed = lexed[(index + 1)..-1]
527
- elsif LONELY_ELSE_REGEX.match?(line)
528
- yield line, false, inside_begin, line_no
529
- line_no += 1
530
- lexed = lexed[(index + 1)..-1]
531
- else
532
- break
533
- end
277
+ loop do # loop to allow restarting for nested conditions
278
+ # Yield blank lines and lonely else lines before the actual line
279
+ while (index = line.index("\n"))
280
+ one_line = line[0..index]
281
+ if BLANK_LINE_REGEX.match?(one_line)
282
+ yield one_line, true, inside_begin, lex.exp_line_no
283
+ lex.exp_line_no += 1
284
+ line = line[(index + 1)..-1]
285
+ elsif LONELY_ELSE_REGEX.match?(one_line)
286
+ yield one_line, false, inside_begin, lex.exp_line_no
287
+ lex.exp_line_no += 1
288
+ line = line[(index + 1)..-1]
289
+ else
290
+ break
534
291
  end
292
+ end
535
293
 
536
- if contains_keyword?(lexed)
537
- if contains_block_beginning?(lexed)
538
- section = ''
539
- lexed.each_line do |lexed_part|
540
- section << lexed_part
541
- if contains_block_beginning?(section)
542
- yield section, false, inside_begin, line_no
543
- break
544
- end
545
- line_no += 1
294
+ if contains_keyword?(line)
295
+ if contains_block_beginning?(line)
296
+ section = ''
297
+ line.each_line do |lexed_part|
298
+ section << lexed_part
299
+ if contains_block_beginning?(section)
300
+ yield section, false, inside_begin, lex.exp_line_no
301
+ break
546
302
  end
547
- line_no += 1
548
- remainder = lexed[(section.length)..-1]
549
- lexed = remainder
550
- next unless remainder.empty?
551
- else
552
- yield lexed, false, inside_begin, line_no
553
- end
554
- elsif !lexed.empty?
555
- num_left_brackets = lexed.count('{')
556
- num_right_brackets = lexed.count('}')
557
- if num_left_brackets != num_right_brackets
558
- # Don't instrument lines with unequal numbers of { and } brackets
559
- yield lexed, false, inside_begin, line_no
560
- else
561
- yield lexed, true, inside_begin, line_no
303
+ lex.exp_line_no += 1
562
304
  end
305
+ lex.exp_line_no += 1
306
+ remainder = line[(section.length)..-1]
307
+ line = remainder
308
+ next unless remainder.empty?
309
+ else
310
+ yield line, false, inside_begin, lex.exp_line_no
563
311
  end
564
- lex.exp_line_no = lex.line_no
565
- break
566
- end # loop do
567
- end # while lexed
568
- end # def each_lexed_segment
569
- end
312
+ elsif !line.empty?
313
+ num_left_brackets = line.count('{')
314
+ num_right_brackets = line.count('}')
315
+ if num_left_brackets != num_right_brackets
316
+ # Don't instrument lines with unequal numbers of { and } brackets
317
+ yield line, false, inside_begin, lex.exp_line_no
318
+ else
319
+ yield line, true, inside_begin, lex.exp_line_no
320
+ end
321
+ end
322
+ line = ''
323
+ lex.exp_line_no = lex.line_no
324
+ break
325
+ end # loop do
570
326
 
571
- end # RUBY_VERSION < "2.7"
327
+ if lex.indent == 0
328
+ lex.line = ''
329
+ end
330
+ end # while lexed
331
+ end # def each_lexed_segment
332
+ end