tealrb 0.11.0 → 0.12.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.
@@ -3,81 +3,104 @@
3
3
  module TEALrb
4
4
  class Contract
5
5
  include TEALrb
6
- include Opcodes
7
- include Opcodes::AllOpcodes
6
+ include Opcodes::TEALOpcodes
7
+ include MaybeOps
8
+ include ByteOpcodes
8
9
  include ABI
9
10
  include Rewriters
11
+ include Enums
10
12
 
11
- attr_reader :teal
13
+ alias global_opcode global
12
14
 
13
- class << self
14
- attr_accessor :subroutines, :version, :teal_methods, :abi_interface, :debug,
15
- :disable_abi_routing, :method_hashes
15
+ attr_reader :eval_location
16
+ attr_accessor :teal, :if_count
16
17
 
17
- private
18
+ class << self
19
+ attr_accessor :subroutines, :version, :abi_interface, :debug,
20
+ :disable_abi_routing, :method_hashes, :src_map
18
21
 
19
22
  def inherited(klass)
20
23
  klass.version = 6
21
24
  klass.subroutines = {}
22
- klass.teal_methods = {}
23
25
  klass.abi_interface = ABI::ABIDescription.new
24
26
  klass.abi_interface.name = klass.to_s
25
27
  klass.method_hashes = []
26
28
  klass.debug = false
27
29
  klass.disable_abi_routing = false
28
- parse(klass)
30
+ klass.src_map = true
29
31
  super
30
32
  end
31
33
 
32
34
  def parse(klass)
33
35
  YARD::Tags::Library.define_tag('ABI Method', :abi)
34
36
  YARD::Tags::Library.define_tag('Subroutine', :subroutine)
35
- YARD::Tags::Library.define_tag('TEAL Method', :teal)
37
+ YARD::Tags::Library.define_tag('OnCompletes', :on_completion)
38
+ YARD::Tags::Library.define_tag('Create', :create)
36
39
 
37
40
  YARD.parse Object.const_source_location(klass.to_s).first
38
41
 
42
+ parsed_methods = {}
39
43
  YARD::Registry.all.each do |y|
40
44
  next unless y.type == :method
41
- next unless y.parent.to_s == klass.to_s
45
+ next unless klass.instance_methods.include? y.name
46
+ next if y.parent.type == :class && y.parent.to_s != klass.to_s
42
47
 
43
- tags = y.tags.map(&:tag_name)
48
+ if parsed_methods.keys.include? y.name
49
+ raise "#{y.name} defined in two locations: \n #{parsed_methods[y.name]}\n #{y.file}:#{y.line}"
50
+ end
44
51
 
45
- if tags.include?('abi') || tags.include?('subroutine')
46
- method_hash = { name: y.name.to_s, desc: y.base_docstring, args: [], returns: { type: 'void' } }
52
+ parsed_methods[y.name] = "#{y.file}:#{y.line}"
47
53
 
48
- y.tags.each do |t|
49
- method_hash[:returns] = { type: t.types&.first } if t.tag_name == 'return'
54
+ tags = y.tags.map(&:tag_name)
55
+
56
+ next unless tags.include?('abi') || tags.include?('subroutine')
50
57
 
51
- next unless t.tag_name == 'param'
58
+ method_hash = { name: y.name.to_s, desc: y.base_docstring, args: [], returns: { type: 'void' },
59
+ on_completion: ['NoOp'], create: y.has_tag?('create') }
52
60
 
53
- method_hash[:args] << { name: t.name, type: t.types&.first, desc: t.text }
54
- end
61
+ y.tags.each do |t|
62
+ method_hash[:returns] = { type: t.types&.first&.downcase } if t.tag_name == 'return'
55
63
 
56
- klass.method_hashes << method_hash
64
+ method_hash[:on_completion] = t.text[1..-2].split(',').map(&:strip) if t.tag_name == 'on_completion'
65
+ next unless t.tag_name == 'param'
57
66
 
58
- klass.abi_interface.add_method(**method_hash) if tags.include? 'abi'
59
- elsif tags.include? 'teal'
60
- klass.teal_methods[y.name.to_s] = y
67
+ method_hash[:args] << { name: t.name, type: t.types&.first&.downcase, desc: t.text }
61
68
  end
69
+
70
+ klass.method_hashes << method_hash
71
+
72
+ klass.abi_interface.add_method(**method_hash) if tags.include? 'abi'
62
73
  end
63
74
  end
64
75
  end
65
76
 
77
+ TEALrb::Opcodes::BINARY_OPCODE_METHOD_MAPPING.each do |meth, opcode|
78
+ define_method(meth) do |other|
79
+ @contract.send(opcode, self, other)
80
+ end
81
+ end
82
+
83
+ TEALrb::Opcodes::UNARY_OPCODE_METHOD_MAPPING.each do |meth, opcode|
84
+ define_method(meth) do
85
+ @contract.send(opcode, self)
86
+ end
87
+ end
88
+
66
89
  # sets the `#pragma version`, defines teal methods, and defines subroutines
67
90
  def initialize
68
- @teal = TEAL.new ["#pragma version #{self.class.version}"]
69
- IfBlock.id = 0
70
- @scratch = Scratch.new
91
+ self.class.parse(self.class)
92
+
93
+ @teal = TEAL.new ["#pragma version #{self.class.version}"], self
94
+ @scratch = Scratch.new self
71
95
 
72
- @@active_contract = self # rubocop:disable Style/ClassVars
96
+ @contract = self
97
+ @if_count = 0
73
98
 
74
99
  self.class.method_hashes.each do |mh|
75
100
  define_subroutine(mh[:name], method(mh[:name]))
76
101
  end
77
102
 
78
- self.class.teal_methods.each do |name, definition|
79
- define_teal_method(name, definition)
80
- end
103
+ compile
81
104
  end
82
105
 
83
106
  VOID_OPS = %w[assert err return app_global_put b bnz bz store
@@ -85,47 +108,209 @@ module TEALrb
85
108
  log itxn_submit itxn_next].freeze
86
109
 
87
110
  def teal_source
88
- teal_lines = []
111
+ @teal.compact.join("\n")
112
+ end
89
113
 
90
- @teal.each_with_index do |line, i|
91
- ln = line.strip
114
+ def account(_account = nil)
115
+ @account ||= Account.new self
116
+ end
92
117
 
93
- teal_lines << '' if i != 0 && VOID_OPS.include?(@teal[i - 1][/\S+/])
118
+ alias accounts account
94
119
 
95
- if (ln[%r{\S+:($| //)}] && !ln[/^if\d+/]) || ln == 'b main' || ln[/^#/]
96
- teal_lines << ln
97
- elsif ln == 'retsub'
98
- teal_lines << " #{ln}"
99
- teal_lines << ''
100
- else
101
- teal_lines << " #{ln}"
120
+ def app(_app = nil)
121
+ @app ||= App.new self
122
+ end
123
+
124
+ alias apps app
125
+
126
+ def asset(_asset = nil)
127
+ @asset ||= Asset.new self
128
+ end
129
+
130
+ alias assets asset
131
+
132
+ def group_txn(_group_txn = nil)
133
+ @group_txn ||= GroupTxn.new self
134
+ end
135
+
136
+ alias group_txns group_txn
137
+
138
+ def global(field = nil, *_args)
139
+ if field
140
+ global_opcode(field)
141
+ else
142
+ @global ||= Global.new self
143
+ end
144
+ end
145
+
146
+ def this_txn
147
+ ThisTxn.new self
148
+ end
149
+
150
+ def box
151
+ Box.new self
152
+ end
153
+
154
+ def inner_txn
155
+ InnerTxn.new self
156
+ end
157
+
158
+ def logs
159
+ Logs.new self
160
+ end
161
+
162
+ def app_args
163
+ AppArgs.new self
164
+ end
165
+
166
+ def local
167
+ Local.new self
168
+ end
169
+
170
+ def txn_type
171
+ TxnType.new self
172
+ end
173
+
174
+ def generate_source_map(src)
175
+ last_location = nil
176
+ src_map_hash = {}
177
+
178
+ src.each_line.with_index do |ln, i|
179
+ if ln[/src_map:/]
180
+ last_location = ln[/(?<=src_map:)\S+/]
181
+ next
102
182
  end
183
+
184
+ src_map_hash[i + 1] ||= { location: last_location } if last_location
103
185
  end
104
186
 
105
- teal_lines.delete_if.with_index do |t, i|
106
- t.empty? && teal_lines[i - 1].empty?
187
+ compile_response = Algod.new.compile(src)
188
+
189
+ case compile_response.status
190
+ when 400
191
+ msg = JSON.parse(compile_response.body)['message']
192
+ e_msg = StringIO.new
193
+ e_msg.puts 'Error(s) while attempting to compile TEAL'
194
+ msg.each_line do |ln|
195
+ teal_line = ln.split(':').first.to_i
196
+ ln_msg = ln.split(':')[1..].join(':').strip
197
+ next if ln_msg == '"0"'
198
+
199
+ if src_map_hash[teal_line]
200
+ e_msg.puts " #{teal_line} - #{src_map_hash[teal_line][:location]}: #{ln_msg}"
201
+ else
202
+ e_msg.puts " #{ln}"
203
+ end
204
+ end
205
+ raise e_msg.string
206
+ when 200
207
+ json_body = JSON.parse(compile_response.body)
208
+ pc_mapping = json_body['sourcemap']['mapping']
209
+
210
+ pc_array = pc_mapping.split(';').map do |v|
211
+ SourceMap::VLQ.decode_array(v)[2]
212
+ end
213
+
214
+ last_line = 1
215
+
216
+ line_to_pc = {}
217
+ pc_to_line = {}
218
+ pc_array.each_with_index do |line_delta, pc|
219
+ last_line += line_delta.to_i
220
+
221
+ next if last_line == 1
222
+
223
+ line_to_pc[last_line] ||= []
224
+ line_to_pc[last_line] << pc
225
+ pc_to_line[pc] = last_line
226
+ end
227
+
228
+ line_to_pc.each do |line, pcs|
229
+ src_map_hash[line][:pcs] = pcs if src_map_hash[line]
230
+ end
107
231
  end
108
232
 
109
- teal_lines.join("\n")
233
+ src_map_hash
110
234
  end
111
235
 
112
- # return the input without transpiling to TEAL
113
- def rb(input)
114
- input
236
+ def dump(directory = Dir.pwd, name: self.class.to_s.downcase, abi: true, src_map: true)
237
+ src = formatted_teal
238
+
239
+ File.write(File.join(directory, "#{name}.teal"), src)
240
+ File.write(File.join(directory, "#{name}.abi.json"), JSON.pretty_generate(abi_hash)) if abi
241
+
242
+ return unless src_map
243
+
244
+ src_map_json = JSON.pretty_generate(generate_source_map(src))
245
+ File.write(File.join(directory, "#{name}.src_map.json"), src_map_json)
115
246
  end
116
247
 
117
- # defines a method that is transpiled to TEAL
118
- # @param name [Symbol] name of the method
119
- # @param definition [Lambda, Proc, UnboundMethod] the method definition
120
- # @return [nil]
121
- def define_teal_method(name, definition)
122
- new_source = generate_method_source(name, definition)
248
+ def formatted_teal
249
+ new_lines = []
250
+ comments = []
123
251
 
124
- define_singleton_method(name) do |*_args|
125
- eval_tealrb(new_source, debug_context: "teal method: #{name}")
252
+ @teal.compact.each do |ln|
253
+ ln = ln.strip
254
+
255
+ next if ln.empty?
256
+
257
+ if ln[/^#pragma/]
258
+ new_lines << { text: ln, void: true, comments: comments }
259
+ comments = []
260
+ elsif ln[%r{^//}]
261
+ comments << ln
262
+ else
263
+ op = ln.split.first
264
+ label_regex = %r{\S+:($| //)}
265
+
266
+ label_type = label_number = label_end = nil
267
+
268
+ if ln[label_regex]
269
+ label_type = ln[/^if/] || ln[/^while/]
270
+ label_number = ln[/(?<=^if)\d+/] || ln[/(?<=^while)\d+/]
271
+ label_end = ln[/_end:/]
272
+ end
273
+
274
+ new_lines << { text: ln, void: VOID_OPS.include?(op), comments: comments, label: ln[label_regex],
275
+ label_type: label_type, label_number: label_number, label_end: label_end }
276
+ comments = []
277
+ end
126
278
  end
127
279
 
128
- nil
280
+ output = []
281
+ indent_level = 0
282
+ current_labels = { 'while' => [], 'if' => [] }
283
+
284
+ new_lines.each do |ln|
285
+ indent_level = 1 if ln[:label] && indent_level.zero? && !ln[:label_type]
286
+
287
+ if ln[:label_type]
288
+ indent_level += 1 unless current_labels[ln[:label_type]].include?(ln[:label_number])
289
+ current_labels[ln[:label_type]] << ln[:label_number]
290
+ end
291
+
292
+ ln_indent_level = indent_level
293
+ ln_indent_level -= 1 if ln[:label]
294
+
295
+ output << '' if !output.last&.empty? && (ln[:label] || ln[:comments].any?)
296
+
297
+ ln[:comments].each { output << (("\t" * ln_indent_level) + _1) }
298
+ output << (("\t" * ln_indent_level) + ln[:text])
299
+
300
+ output << '' if ln[:void] && !output.last.empty?
301
+
302
+ next unless ln[:label_end]
303
+
304
+ indent_level -= 1
305
+ current_labels[ln[:label_type]].delete ln[:label_number]
306
+ end
307
+
308
+ output.join("\n")
309
+ end
310
+
311
+ # return the input without transpiling to TEAL
312
+ def rb(input)
313
+ input
129
314
  end
130
315
 
131
316
  # defines a TEAL subroutine
@@ -133,11 +318,15 @@ module TEALrb
133
318
  # @param definition [Lambda, Proc, UnboundMethod] the method definition
134
319
  # @return [nil]
135
320
  def define_subroutine(name, definition)
321
+ return if method(name).source_location.first == __FILE__
322
+
323
+ @eval_location = method(name).source_location
324
+
136
325
  define_singleton_method(name) do |*_args|
137
326
  callsub(name)
138
327
  end
139
328
 
140
- TEAL.instance << 'b main' unless TEAL.instance.include? 'b main'
329
+ @teal << 'b main' unless @teal.include? 'b main'
141
330
 
142
331
  label(name) # add teal label
143
332
 
@@ -161,18 +350,17 @@ module TEALrb
161
350
  def comment(content, inline: false)
162
351
  content = " #{content}" unless content[0] == ' '
163
352
  if inline
164
- last_line = TEAL.instance.pop
165
- TEAL.instance << "#{last_line} //#{content}"
353
+ last_line = @teal.pop
354
+ @teal << "#{last_line} //#{content}"
166
355
  else
167
- TEAL.instance << '' unless TEAL.instance.last[%r{^//}]
168
- TEAL.instance << "//#{content}"
356
+ @teal << "//#{content}"
169
357
  end
170
358
  end
171
359
 
172
360
  # inserts a string into TEAL source
173
361
  # @param string [String] the string to insert
174
362
  def placeholder(string)
175
- TEAL.instance << string
363
+ @teal << string
176
364
  end
177
365
 
178
366
  # the hash of the abi description
@@ -191,29 +379,52 @@ module TEALrb
191
379
  # transpiles #main and routes abi methods. To disable abi routing, set `@disable_abi_routing` to true in your
192
380
  # Contract subclass
193
381
  def compile
194
- TEAL.instance << 'main:' if TEAL.instance.include? 'b main'
382
+ @teal << 'main:' if @teal.include? 'b main'
195
383
  route_abi_methods unless self.class.disable_abi_routing
196
- eval_tealrb(rewrite(method(:main).source, method_rewriter: true), debug_context: 'main') if respond_to? :main
384
+ return unless respond_to? :main
385
+
386
+ @eval_location = method(:main).source_location
387
+
388
+ eval_tealrb(
389
+ rewrite(
390
+ method(:main).source,
391
+ method_rewriter: true
392
+ ),
393
+ debug_context: 'main'
394
+ )
395
+ end
396
+
397
+ def main
398
+ nil
399
+ end
400
+
401
+ def compiled_program
402
+ compile_response = Algod.new.compile(formatted_teal)
403
+
404
+ generate_source_map(formatted_teal) if compile_response.status != 200
405
+
406
+ JSON.parse(compile_response.body)['result']
197
407
  end
198
408
 
199
409
  private
200
410
 
201
411
  def route_abi_methods
202
- self.class.abi_interface.methods.each do |meth|
412
+ self.class.abi_interface.methods.each_with_index do |meth, i|
203
413
  signature = "#{meth[:name]}(#{meth[:args].map { _1[:type] }.join(',')})#{meth[:returns][:type]}"
204
414
  selector = OpenSSL::Digest.new('SHA512-256').hexdigest(signature)[..7]
205
415
 
206
- IfBlock.new(AppArgs[0] == byte(selector)) do
207
- callsub(meth[:name])
208
- approve
209
- end
416
+ app_args[int(0)] == byte(selector) # rubocop:disable Lint/Void
417
+ bz("abi_routing#{i}")
418
+ callsub(meth[:name])
419
+ approve
420
+ label("abi_routing#{i}")
210
421
  end
211
422
  end
212
423
 
213
424
  def generate_method_source(name, definition)
214
425
  new_source = rewrite(definition.source, method_rewriter: true)
215
426
 
216
- pre_string = StringIO.new
427
+ pre_string = []
217
428
 
218
429
  scratch_names = []
219
430
  definition.parameters.reverse.each_with_index do |param, _i|
@@ -221,17 +432,17 @@ module TEALrb
221
432
  scratch_name = [name, param_name].map(&:to_s).join(': ')
222
433
  scratch_names << scratch_name
223
434
 
224
- pre_string.puts "@scratch.store('#{scratch_name}')"
225
- pre_string.puts "#{param_name} = -> { @scratch['#{scratch_name}'] }"
435
+ pre_string << "@scratch.store('#{scratch_name}')"
436
+ pre_string << "#{param_name} = -> { @scratch['#{scratch_name}'] }"
226
437
  end
227
438
 
228
- "#{pre_string.string}#{new_source}"
439
+ "#{pre_string.join(';')};#{new_source}"
229
440
  end
230
441
 
231
442
  def generate_subroutine_source(definition, method_hash)
232
443
  new_source = rewrite(definition.source, method_rewriter: true)
233
444
 
234
- pre_string = StringIO.new
445
+ pre_string = []
235
446
 
236
447
  scratch_names = []
237
448
 
@@ -245,29 +456,42 @@ module TEALrb
245
456
  account_param_index = 0
246
457
  args_index = 0
247
458
 
459
+ method_hash[:on_completion].each_with_index do |oc, i|
460
+ this_txn.on_completion
461
+ int(oc)
462
+ equal
463
+ boolean_or unless i.zero?
464
+ end
465
+ assert
466
+
467
+ assert(this_txn.application_id == (int(0))) if method_hash[:create]
468
+
248
469
  if abi_hash['methods'].find { _1[:name] == method_hash[:name].to_s }
249
470
  definition.parameters.each_with_index do |param, i|
250
471
  param_name = param.last
251
472
 
252
- scratch_name = "#{definition.original_name}: #{param_name} [#{arg_types[i] || 'any'}] #{if args[i]
253
- args[i][:desc]
254
- end}"
473
+ scratch_name = "#{definition.original_name}: #{param_name} [#{arg_types[i] || 'any'}] #{
474
+ args[i][:desc] if args[i]}"
255
475
  scratch_names << scratch_name
256
476
 
257
- if txn_types.include? arg_types[i]
258
- pre_string.puts "@scratch['#{scratch_name}'] = Gtxns[Txn.group_index - int(#{txn_params})]"
477
+ type = arg_types[i]&.downcase
478
+
479
+ if txn_types.include? type
480
+ @scratch[scratch_name] = group_txns[this_txn.group_index.subtract int(txn_params)]
259
481
  txn_params -= 1
260
- elsif arg_types[i] == 'application'
261
- pre_string.puts "@scratch['#{scratch_name}'] = Apps[#{app_param_index += 1}]"
262
- elsif arg_types[i] == 'asset'
263
- pre_string.puts "@scratch['#{scratch_name}'] = Assets[#{asset_param_index += 1}]"
264
- elsif arg_types[i] == 'account'
265
- pre_string.puts "@scratch['#{scratch_name}'] = Accounts[#{account_param_index += 1}]"
482
+ elsif type == 'application'
483
+ @scratch[scratch_name] = apps[app_param_index += 1]
484
+ elsif type == 'asset'
485
+ @scratch[scratch_name] = assets[asset_param_index += 1]
486
+ elsif type == 'account'
487
+ @scratch[scratch_name] = accounts[account_param_index += 1]
488
+ elsif type == 'uint64'
489
+ @scratch[scratch_name] = btoi(app_args[args_index += 1])
266
490
  else
267
- pre_string.puts "@scratch['#{scratch_name}'] = AppArgs[#{args_index += 1}]"
491
+ @scratch[scratch_name] = app_args[args_index += 1]
268
492
  end
269
493
 
270
- pre_string.puts "#{param_name} = -> { @scratch['#{scratch_name}'] }"
494
+ pre_string << "#{param_name} = -> {@scratch['#{scratch_name}'] }"
271
495
  end
272
496
 
273
497
  else
@@ -277,34 +501,35 @@ module TEALrb
277
501
  definition.parameters.reverse.each_with_index do |param, i|
278
502
  param_name = param.last
279
503
 
280
- scratch_name = "#{definition.original_name}: #{param_name} [#{arg_types[i] || 'any'}] #{if args[i]
281
- args[i][:desc]
282
- end}"
504
+ scratch_name = "#{definition.original_name}: #{param_name} [#{arg_types[i] || 'any'}] #{
505
+ args[i][:desc] if args[i]}"
283
506
  scratch_names << scratch_name
284
507
 
285
- if txn_types.include? arg_types[i]
286
- pre_string.puts "@scratch['#{scratch_name}'] = Gtxns"
287
- elsif arg_types[i] == 'application'
288
- pre_string.puts "@scratch['#{scratch_name}'] = Applications.new"
289
- elsif arg_types[i] == 'asset'
290
- pre_string.puts "@scratch['#{scratch_name}'] = Assets.new"
291
- elsif arg_types[i] == 'account'
292
- pre_string.puts "@scratch['#{scratch_name}'] = Accounts.new"
508
+ type = arg_types[i]&.downcase
509
+
510
+ if txn_types.include? type
511
+ @scratch[scratch_name] = group_txn
512
+ elsif type == 'application'
513
+ @scratch[scratch_name] = application
514
+ elsif type == 'asset'
515
+ @scratch[scratch_name] = asset
516
+ elsif type == 'account'
517
+ @scratch[scratch_name] = account
293
518
  else
294
- pre_string.puts "@scratch.store('#{scratch_name}')"
519
+ @scratch.store(scratch_name)
295
520
  end
296
521
 
297
- pre_string.puts "#{param_name} = -> { @scratch['#{scratch_name}'] }"
522
+ pre_string << "#{param_name} = -> { @scratch['#{scratch_name}'] }"
298
523
  end
299
524
 
300
525
  end
301
526
 
302
- "#{pre_string.string}#{new_source}"
527
+ "#{pre_string.join(';')};#{new_source}"
303
528
  end
304
529
 
305
530
  def rewrite_with_rewriter(string, rewriter)
306
531
  process_source = RuboCop::ProcessedSource.new(string, RUBY_VERSION[/\d\.\d/].to_f)
307
- rewriter.new.rewrite(process_source)
532
+ rewriter.new.rewrite(process_source, self)
308
533
  end
309
534
 
310
535
  def rewrite(string, method_rewriter: false)
@@ -315,7 +540,7 @@ module TEALrb
315
540
  end
316
541
 
317
542
  [CommentRewriter, ComparisonRewriter, WhileRewriter, InlineIfRewriter, IfRewriter, OpRewriter,
318
- AssignRewriter, InternalMethodRewriter].each do |rw|
543
+ AssignRewriter].each do |rw|
319
544
  string = rewrite_with_rewriter(string, rw)
320
545
  end
321
546
 
@@ -331,7 +556,7 @@ module TEALrb
331
556
  end
332
557
 
333
558
  def eval_tealrb(s, debug_context:)
334
- pre_teal = Array.new TEAL.instance
559
+ pre_teal = Array.new @teal
335
560
 
336
561
  if self.class.debug
337
562
  puts "DEBUG: Evaluating the following code (#{debug_context}):"
@@ -343,7 +568,7 @@ module TEALrb
343
568
 
344
569
  if self.class.debug
345
570
  puts "DEBUG: Resulting TEAL (#{debug_context}):"
346
- puts Array.new(TEAL.instance) - pre_teal
571
+ puts Array.new(@teal) - pre_teal
347
572
  puts ''
348
573
  end
349
574
  rescue SyntaxError, StandardError => e
@@ -354,23 +579,9 @@ module TEALrb
354
579
  error_line = eval_locations[@eval_tealrb_rescue_count].split(':')[1].to_i
355
580
  end
356
581
 
357
- warn "'#{e}' when evaluating transpiled TEALrb source" if @eval_tealrb_rescue_count.zero?
358
-
359
- warn "Backtrace location (#{@eval_tealrb_rescue_count + 1} / #{eval_locations.size}):"
360
-
361
- @eval_tealrb_rescue_count += 1
362
-
363
- s.lines.each_with_index do |line, i|
364
- line_num = i + 1
365
- if error_line == line_num
366
- warn "=> #{line_num}: #{line}"
367
- else
368
- warn " #{line_num}: #{line}"
369
- end
370
- end
582
+ msg = "#{@eval_location.first}:#{error_line + @eval_location.last}"
371
583
 
372
- warn ''
373
- raise e
584
+ raise e, "#{msg}: #{e}"
374
585
  end
375
586
  end
376
587
  end