openc3 5.5.2 → 5.6.0

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