tealrb 0.10.1 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -4,9 +4,11 @@ module TEALrb
4
4
  module Rewriters
5
5
  class Rewriter < Parser::TreeRewriter
6
6
  include RuboCop::AST::Traversal
7
+ attr_reader :contract
7
8
 
8
- def rewrite(processed_source)
9
+ def rewrite(processed_source, contract)
9
10
  @comments = processed_source.comments
11
+ @contract = contract
10
12
  super(processed_source.buffer, processed_source.ast)
11
13
  end
12
14
  end
@@ -36,22 +38,11 @@ module TEALrb
36
38
  def on_block(node)
37
39
  replace node.source_range, node.body.source
38
40
  end
39
-
40
- def on_send(node)
41
- remove node.loc.selector if node.loc.selector.source == 'subroutine' || node.loc.selector.source == 'teal'
42
-
43
- # @teal_methods[:name] = ->(*args) { ... } becomes ->(*args) { ... }
44
- if ['@teal_methods', '@subroutines'].include? node.children[0]&.source
45
- replace node.source_range, node.children[3].body.source
46
- end
47
-
48
- super
49
- end
50
41
  end
51
42
 
52
43
  class AssignRewriter < Rewriter
53
44
  def on_lvasgn(node)
54
- wrap(node.children[1].source_range, '-> { ', ' }')
45
+ wrap(node.children[1].source_range, '-> {', '}')
55
46
  super
56
47
  end
57
48
 
@@ -61,12 +52,12 @@ module TEALrb
61
52
  end
62
53
 
63
54
  def on_ivasgn(node)
64
- wrap(node.children[1].source_range, '-> { ', ' }')
55
+ wrap(node.children[1].source_range, '-> {', ' }')
65
56
  super
66
57
  end
67
58
 
68
59
  def on_ivar(node)
69
- insert_after(node.loc.name, '.call') unless ['@teal_methods', '@subroutines', '@scratch'].include? node.source
60
+ insert_after(node.loc.name, '.call')
70
61
  super
71
62
  end
72
63
 
@@ -125,32 +116,32 @@ module TEALrb
125
116
  super
126
117
  end
127
118
 
128
- OPCODE_METHODS = TEALrb::Opcodes::AllOpcodes.instance_methods.freeze
119
+ def on_return(node)
120
+ replace node.loc.keyword, 'abi_return'
121
+
122
+ super
123
+ end
124
+
125
+ OPCODE_METHODS = TEALrb::Opcodes::TEALOpcodes.instance_methods.freeze
126
+ OPCODE_INSTANCE_METHODS = TEALrb::Opcodes::BINARY_OPCODE_METHOD_MAPPING.merge(
127
+ TEALrb::Opcodes::UNARY_OPCODE_METHOD_MAPPING
128
+ )
129
129
 
130
130
  def on_send(node)
131
131
  meth_name = node.children[1]
132
132
 
133
133
  if OPCODE_METHODS.include? meth_name
134
- if meth_name[/(byte|int)cblock/]
134
+ if %w[bytecblock intcblock pushints pushbytess switch match].include? meth_name.to_s
135
135
  @skips += node.children[2..]
136
136
  else
137
- params = TEALrb::Opcodes::AllOpcodes.instance_method(meth_name).parameters
137
+ params = TEALrb::Opcodes::TEALOpcodes.instance_method(meth_name).parameters
138
138
  req_params = params.count { |param| param[0] == :req }
139
139
  @skips += node.children[2..(1 + req_params.size)] unless req_params.zero?
140
140
  end
141
- elsif %i[comment placeholder rb].include?(meth_name) ||
142
- (%i[[] []=].include?(meth_name) &&
143
- (
144
- %i[@scratch @teal_methods Gtxn
145
- AppArgs].include?(node.children[0].children.last) ||
146
- node.children[0].children.first&.children&.last == :Txna
147
- ))
148
-
141
+ elsif %i[comment placeholder rb].include?(meth_name)
149
142
  @skips << node.children[2]
150
- elsif node.children.first&.children&.last == :@scratch && meth_name[/=$/]
151
- nil
152
- elsif %i[@scratch Gtxn Accounts ApplicationArgs Assets Apps Logs].include? node.children.first&.children&.last
153
- @skips << node.children.last
143
+ elsif meth_name == :[]
144
+ @skips << node.children[2] if node.children[2].type == :int
154
145
  end
155
146
 
156
147
  super
@@ -191,9 +182,15 @@ module TEALrb
191
182
  def on_if(node)
192
183
  unless node.loc.respond_to? :else
193
184
  conditional = node.children[0].source
194
- if_blk = node.children[1].source
195
185
 
196
- replace(node.loc.expression, "if(#{conditional})\n#{if_blk}\nend")
186
+ if_blk = if node.keyword == 'unless'
187
+ node.children[2].source
188
+ conditional = "!(#{conditional})"
189
+ else
190
+ node.children[1].source
191
+ end
192
+
193
+ replace(node.loc.expression, "if(#{conditional});#{if_blk};end")
197
194
  end
198
195
 
199
196
  super
@@ -202,17 +199,53 @@ module TEALrb
202
199
 
203
200
  class IfRewriter < Rewriter
204
201
  def on_if(node)
202
+ condition = node.children.first
203
+ block = node.children[1]
204
+
205
205
  case node.loc.keyword.source
206
- when 'if'
207
- replace(node.loc.keyword, 'IfBlock.new(')
206
+ when 'if', 'unless'
207
+ @contract.if_count += 1
208
+ @elsif_count ||= 0
209
+
210
+ if node.loc.keyword.source == 'unless'
211
+ replace(node.loc.keyword, ":if#{@contract.if_count}_condition;!")
212
+ else
213
+ replace(node.loc.keyword, ":if#{@contract.if_count}_condition;")
214
+ end
215
+ insert_before(block.source_range, ":if#{@contract.if_count}_logic;")
216
+
217
+ case node.loc.else&.source
218
+ when 'else'
219
+ insert_after(condition.source_range, "; bz :if#{@contract.if_count}_else")
220
+ insert_after(block.source_range, "; b :if#{@contract.if_count}_end")
221
+ replace(node.loc.else, ":if#{@contract.if_count}_else;")
222
+ when 'elsif'
223
+ insert_after(condition.source_range, "; bz :if#{@contract.if_count}_elsif#{@elsif_count + 1}_condition")
224
+ insert_after(block.source_range, "; b :if#{@contract.if_count}_end")
225
+ replace(node.loc.else, ":if#{@contract.if_count}_elsif#{@elsif_count + 1}_condition;")
226
+ else
227
+ insert_after(condition.source_range, "; bz :if#{@contract.if_count}_end")
228
+ end
229
+ replace(node.loc.end, ":if#{@contract.if_count}_end")
208
230
  when 'elsif'
209
- replace(node.loc.keyword, 'end.elsif(')
210
- end
231
+ @elsif_count += 1
232
+
233
+ case node.loc.else&.source
234
+ when 'else'
235
+ insert_after(condition.source_range, "; bz :if#{@contract.if_count}_else")
236
+ insert_after(block.source_range, "; b :if#{@contract.if_count}_end")
237
+ replace(node.loc.else, ":if#{@contract.if_count}_else;")
238
+ when 'elsif'
239
+ insert_after(condition.source_range, "; bz :if#{@contract.if_count}_elsif#{@elsif_count + 1}_condition")
240
+ insert_after(block.source_range, "; b :if#{@contract.if_count}_end")
241
+ replace(node.loc.else, ":if#{@contract.if_count}_elsif#{@elsif_count + 1}_condition;")
242
+ else
243
+ insert_after(condition.source_range, "; bz :if#{@contract.if_count}_end")
244
+ end
211
245
 
212
- cond_expr = node.children.first.source_range
213
- replace(cond_expr, "#{cond_expr.source} ) do")
246
+ insert_before(block.source_range, ":if#{@contract.if_count}_elsif#{@elsif_count}_logic;")
247
+ end
214
248
 
215
- replace(node.loc.else, 'end.else do') if node.loc.else && node.loc.else.source == 'else'
216
249
  super
217
250
  end
218
251
  end
@@ -234,9 +267,12 @@ module TEALrb
234
267
 
235
268
  def on_while(node)
236
269
  cond_node = node.children.first
237
- replace(node.loc.keyword, ":while#{while_count}\n#{cond_node.source}\nbz :end_while#{while_count}")
270
+ replace(
271
+ node.loc.keyword,
272
+ ":while#{while_count}_condition;#{cond_node.source};bz :while#{while_count}_end; :while#{while_count}_logic;"
273
+ )
238
274
  replace(node.loc.begin, '') if node.loc.begin
239
- replace(node.loc.end, "b :while#{while_count}\n:end_while#{while_count}")
275
+ replace(node.loc.end, "b :while#{while_count}_condition;:while#{while_count}_end")
240
276
  replace(cond_node.loc.expression, '')
241
277
  super
242
278
  end
@@ -2,14 +2,15 @@
2
2
 
3
3
  module TEALrb
4
4
  class Scratch
5
- def initialize
5
+ def initialize(contract)
6
6
  @open_slots = (0..256).to_a
7
7
  @named_slots = {}
8
8
  @values = {}
9
+ @contract = contract
9
10
  end
10
11
 
11
12
  def [](key)
12
- TEAL.instance << "load #{@named_slots[key]} // #{key}"
13
+ @contract.teal << "load #{@named_slots[key]} // #{key}"
13
14
  @values[key]
14
15
  end
15
16
 
@@ -17,9 +18,9 @@ module TEALrb
17
18
  store(key, value)
18
19
  end
19
20
 
20
- def store(key, value = TEAL.instance)
21
+ def store(key, value = @contract.teal)
21
22
  @values[key] = value
22
- TEAL.instance << "store #{@named_slots[key] ||= @open_slots.shift} // #{key}"
23
+ @contract.teal << "store #{@named_slots[key] ||= @open_slots.shift} // #{key}"
23
24
  end
24
25
 
25
26
  def delete(key)
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TEALrb
4
+ class ThisTxn < OpcodeType
5
+ include TxnFields
6
+
7
+ private
8
+
9
+ def txnfield_opcode(field, *_args)
10
+ @contract.txn field
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,333 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TEALrb
4
+ module TxnFields
5
+ def txnfield_opcode(*args)
6
+ raise NotImplementedError
7
+ end
8
+
9
+ # @return [[]byte] 32 byte address (v1)
10
+ def sender(*args)
11
+ @contract.account txnfield_opcode('Sender', *args)
12
+ end
13
+
14
+ # @return [uint64] microalgos (v1)
15
+ def fee(*args)
16
+ txnfield_opcode('Fee', *args)
17
+ end
18
+
19
+ # @return [uint64] round number (v1)
20
+ def first_valid(*args)
21
+ txnfield_opcode('FirstValid', *args)
22
+ end
23
+
24
+ # @return [uint64] Causes program to fail; reserved for future use (v1)
25
+ def first_valid_time(*args)
26
+ txnfield_opcode('FirstValidTime', *args)
27
+ end
28
+
29
+ # @return [uint64] round number (v1)
30
+ def last_valid(*args)
31
+ txnfield_opcode('LastValid', *args)
32
+ end
33
+
34
+ # @return [[]byte] Any data up to 1024 bytes (v1)
35
+ def note(*args)
36
+ txnfield_opcode('Note', *args)
37
+ end
38
+
39
+ # @return [[]byte] 32 byte lease value (v1)
40
+ def lease(*args)
41
+ txnfield_opcode('Lease', *args)
42
+ end
43
+
44
+ # @return [[]byte] 32 byte address (v1)
45
+ def receiver(*args)
46
+ @contract.account txnfield_opcode('Receiver', *args)
47
+ end
48
+
49
+ # @return [uint64] microalgos (v1)
50
+ def amount(*args)
51
+ txnfield_opcode('Amount', *args)
52
+ end
53
+
54
+ # @return [[]byte] 32 byte address (v1)
55
+ def close_remainder_to(*args)
56
+ @contract.account txnfield_opcode('CloseRemainderTo', *args)
57
+ end
58
+
59
+ # @return [[]byte] 32 byte address (v1)
60
+ def vote_pk(*args)
61
+ txnfield_opcode('VotePK', *args)
62
+ end
63
+
64
+ # @return [[]byte] 32 byte address (v1)
65
+ def selection_pk(*args)
66
+ txnfield_opcode('SelectionPK', *args)
67
+ end
68
+
69
+ # @return [uint64] The first round that the participation key is valid. (v1)
70
+ def vote_first(*args)
71
+ txnfield_opcode('VoteFirst', *args)
72
+ end
73
+
74
+ # @return [uint64] The last round that the participation key is valid. (v1)
75
+ def vote_last(*args)
76
+ txnfield_opcode('VoteLast', *args)
77
+ end
78
+
79
+ # @return [uint64] Dilution for the 2-level participation key (v1)
80
+ def vote_key_dilution(*args)
81
+ txnfield_opcode('VoteKeyDilution', *args)
82
+ end
83
+
84
+ # @return [[]byte] Transaction type as bytes (v1)
85
+ def type(*args)
86
+ txnfield_opcode('Type', *args)
87
+ end
88
+
89
+ # @return [uint64] See table below (v1)
90
+ def type_enum(*args)
91
+ txnfield_opcode('TypeEnum', *args)
92
+ end
93
+
94
+ # @return [uint64] Asset ID (v1)
95
+ def xfer_asset(*args)
96
+ @contract.asset txnfield_opcode('XferAsset', *args)
97
+ end
98
+
99
+ # @return [uint64] value in Asset's units (v1)
100
+ def asset_amount(*args)
101
+ txnfield_opcode('AssetAmount', *args)
102
+ end
103
+
104
+ # @return [[]byte] 32 byte address. Causes clawback of all value of asset from AssetSender if
105
+ # Sender is the Clawback address of the asset. (v1)
106
+ def asset_sender(*args)
107
+ @contract.account txnfield_opcode('AssetSender', *args)
108
+ end
109
+
110
+ # @return [[]byte] 32 byte address (v1)
111
+ def asset_receiver(*args)
112
+ @contract.account txnfield_opcode('AssetReceiver', *args)
113
+ end
114
+
115
+ # @return [[]byte] 32 byte address (v1)
116
+ def asset_close_to(*args)
117
+ @contract.account txnfield_opcode('AssetCloseTo', *args)
118
+ end
119
+
120
+ # @return [uint64] Position of this transaction within an atomic transaction group.
121
+ # A stand-alone transaction is implicitly element 0 in a group of 1 (v1)
122
+ def group_index(*args)
123
+ txnfield_opcode('GroupIndex', *args)
124
+ end
125
+
126
+ # @return [[]byte] The computed ID for this transaction. 32 bytes. (v1)
127
+ def tx_id(*args)
128
+ txnfield_opcode('TxID', *args)
129
+ end
130
+
131
+ # @return [uint64] ApplicationID from ApplicationCall transaction (v2)
132
+ def application_id(*args)
133
+ @contract.app txnfield_opcode('ApplicationID', *args)
134
+ end
135
+
136
+ # @return [uint64] ApplicationCall transaction on completion action (v2)
137
+ def on_completion(*args)
138
+ txnfield_opcode('OnCompletion', *args)
139
+ end
140
+
141
+ # @return [[]byte] Arguments passed to the application in the ApplicationCall transaction (v2)
142
+ def application_args(*args)
143
+ txnfield_opcode('ApplicationArgs', *args)
144
+ end
145
+
146
+ # @return [uint64] Number of ApplicationArgs (v2)
147
+ def num_app_args(*args)
148
+ txnfield_opcode('NumAppArgs', *args)
149
+ end
150
+
151
+ # @return [[]byte] Accounts listed in the ApplicationCall transaction (v2)
152
+ def accounts(*args)
153
+ txnfield_opcode('Accounts', *args)
154
+ end
155
+
156
+ # @return [uint64] Number of Accounts (v2)
157
+ def num_accounts(*args)
158
+ txnfield_opcode('NumAccounts', *args)
159
+ end
160
+
161
+ # @return [[]byte] Approval program (v2)
162
+ def approval_program(*args)
163
+ txnfield_opcode('ApprovalProgram', *args)
164
+ end
165
+
166
+ # @return [[]byte] Clear state program (v2)
167
+ def clear_state_program(*args)
168
+ txnfield_opcode('ClearStateProgram', *args)
169
+ end
170
+
171
+ # @return [[]byte] 32 byte Sender's new AuthAddr (v2)
172
+ def rekey_to(*args)
173
+ @contract.account txnfield_opcode('RekeyTo', *args)
174
+ end
175
+
176
+ # @return [uint64] Asset ID in asset config transaction (v2)
177
+ def config_asset(*args)
178
+ @contract.asset txnfield_opcode('ConfigAsset', *args)
179
+ end
180
+
181
+ # @return [uint64] Total number of units of this asset created (v2)
182
+ def config_asset_total(*args)
183
+ txnfield_opcode('ConfigAssetTotal', *args)
184
+ end
185
+
186
+ # @return [uint64] Number of digits to display after the decimal place when displaying the asset (v2)
187
+ def config_asset_decimals(*args)
188
+ txnfield_opcode('ConfigAssetDecimals', *args)
189
+ end
190
+
191
+ # @return [uint64] Whether the asset's slots are frozen by default or not, 0 or 1 (v2)
192
+ def config_asset_default_frozen(*args)
193
+ txnfield_opcode('ConfigAssetDefaultFrozen', *args)
194
+ end
195
+
196
+ # @return [[]byte] Unit name of the asset (v2)
197
+ def config_asset_unit_name(*args)
198
+ txnfield_opcode('ConfigAssetUnitName', *args)
199
+ end
200
+
201
+ # @return [[]byte] The asset name (v2)
202
+ def config_asset_name(*args)
203
+ txnfield_opcode('ConfigAssetName', *args)
204
+ end
205
+
206
+ # @return [[]byte] URL (v2)
207
+ def config_asset_url(*args)
208
+ txnfield_opcode('ConfigAssetURL', *args)
209
+ end
210
+
211
+ # @return [[]byte] 32 byte commitment to some unspecified asset metadata (v2)
212
+ def config_asset_metadata_hash(*args)
213
+ txnfield_opcode('ConfigAssetMetadataHash', *args)
214
+ end
215
+
216
+ # @return [[]byte] 32 byte address (v2)
217
+ def config_asset_manager(*args)
218
+ @contract.account txnfield_opcode('ConfigAssetManager', *args)
219
+ end
220
+
221
+ # @return [[]byte] 32 byte address (v2)
222
+ def config_asset_reserve(*args)
223
+ @contract.account txnfield_opcode('ConfigAssetReserve', *args)
224
+ end
225
+
226
+ # @return [[]byte] 32 byte address (v2)
227
+ def config_asset_freeze(*args)
228
+ @contract.account txnfield_opcode('ConfigAssetFreeze', *args)
229
+ end
230
+
231
+ # @return [[]byte] 32 byte address (v2)
232
+ def config_asset_clawback(*args)
233
+ @contract.account txnfield_opcode('ConfigAssetClawback', *args)
234
+ end
235
+
236
+ # @return [uint64] Asset ID being frozen or un-frozen (v2)
237
+ def freeze_asset(*args)
238
+ @contract.asset txnfield_opcode('FreezeAsset', *args)
239
+ end
240
+
241
+ # @return [[]byte] 32 byte address of the account whose asset slot is being frozen or un-frozen (v2)
242
+ def freeze_asset_account(*args)
243
+ @contract.account txnfield_opcode('FreezeAssetAccount', *args)
244
+ end
245
+
246
+ # @return [uint64] The new frozen value, 0 or 1 (v2)
247
+ def freeze_asset_frozen(*args)
248
+ txnfield_opcode('FreezeAssetFrozen', *args)
249
+ end
250
+
251
+ # @return [uint64] Foreign Assets listed in the ApplicationCall transaction (v3)
252
+ def assets(*args)
253
+ txnfield_opcode('Assets', *args)
254
+ end
255
+
256
+ # @return [uint64] Number of Assets (v3)
257
+ def num_assets(*args)
258
+ txnfield_opcode('NumAssets', *args)
259
+ end
260
+
261
+ # @return [uint64] Foreign Apps listed in the ApplicationCall transaction (v3)
262
+ def applications(*args)
263
+ txnfield_opcode('Applications', *args)
264
+ end
265
+
266
+ # @return [uint64] Number of Applications (v3)
267
+ def num_applications(*args)
268
+ txnfield_opcode('NumApplications', *args)
269
+ end
270
+
271
+ # @return [uint64] Number of global state integers in ApplicationCall (v3)
272
+ def global_num_uint(*args)
273
+ txnfield_opcode('GlobalNumUint', *args)
274
+ end
275
+
276
+ # @return [uint64] Number of global state byteslices in ApplicationCall (v3)
277
+ def global_num_byte_slice(*args)
278
+ txnfield_opcode('GlobalNumByteSlice', *args)
279
+ end
280
+
281
+ # @return [uint64] Number of local state integers in ApplicationCall (v3)
282
+ def local_num_uint(*args)
283
+ txnfield_opcode('LocalNumUint', *args)
284
+ end
285
+
286
+ # @return [uint64] Number of local state byteslices in ApplicationCall (v3)
287
+ def local_num_byte_slice(*args)
288
+ txnfield_opcode('LocalNumByteSlice', *args)
289
+ end
290
+
291
+ # @return [uint64] Number of additional pages for each of the application's approval and clear state programs.
292
+ # An ExtraProgramPages of 1 means 2048 more total bytes, or 1024 for each program. (v4)
293
+ def extra_program_pages(*args)
294
+ txnfield_opcode('ExtraProgramPages', *args)
295
+ end
296
+
297
+ # @return [uint64] Marks an account nonparticipating for rewards (v5)
298
+ def nonparticipation(*args)
299
+ txnfield_opcode('Nonparticipation', *args)
300
+ end
301
+
302
+ # @return [[]byte] Log messages emitted by an application call (only with itxn in v5). Application mode only (v5)
303
+ def logs(*args)
304
+ txnfield_opcode('Logs', *args)
305
+ end
306
+
307
+ # @return [uint64] Number of Logs (only with itxn in v5). Application mode only (v5)
308
+ def num_logs(*args)
309
+ txnfield_opcode('NumLogs', *args)
310
+ end
311
+
312
+ # @return [uint64] Asset ID allocated by the creation of an ASA (only with itxn in v5). Application mode only (v5)
313
+ def created_asset_id(*args)
314
+ @contract.asset txnfield_opcode('CreatedAssetID', *args)
315
+ end
316
+
317
+ # @return [uint64] ApplicationID allocated by the creation of an application (only with itxn in v5).
318
+ # Application mode only (v5)
319
+ def created_application_id(*args)
320
+ @contract.app txnfield_opcode('CreatedApplicationID', *args)
321
+ end
322
+
323
+ # @return [[]byte] The last message emitted. Empty bytes if none were emitted. Application mode only (v6)
324
+ def last_log(*args)
325
+ txnfield_opcode('LastLog', *args)
326
+ end
327
+
328
+ # @return [[]byte] 64 byte state proof public key commitment (v6)
329
+ def state_proof_pk(*args)
330
+ txnfield_opcode('StateProofPK', *args)
331
+ end
332
+ end
333
+ end
data/lib/tealrb.rb CHANGED
@@ -2,32 +2,40 @@
2
2
 
3
3
  require 'method_source'
4
4
  require 'rubocop'
5
+ require 'yard'
6
+ require 'faraday'
7
+ require 'source_map'
5
8
 
6
9
  require_relative 'tealrb/constants'
7
10
  require_relative 'tealrb/abi'
8
11
  require_relative 'tealrb/opcodes'
9
- require_relative 'tealrb/opcode_modules'
10
- require_relative 'tealrb/rewriters'
11
- require_relative 'tealrb/if_block'
12
+ require_relative 'tealrb/maybe_ops'
13
+ require_relative 'tealrb/byte_opcodes'
14
+ require_relative 'tealrb/txn_fields'
15
+ require_relative 'tealrb/opcode_type'
16
+ require_relative 'tealrb/account'
17
+ require_relative 'tealrb/this_txn'
18
+ require_relative 'tealrb/box'
19
+ require_relative 'tealrb/inner_txn'
20
+ require_relative 'tealrb/group_txn'
21
+ require_relative 'tealrb/global'
22
+ require_relative 'tealrb/app'
23
+ require_relative 'tealrb/asset'
24
+ require_relative 'tealrb/logs'
25
+ require_relative 'tealrb/app_args'
26
+ require_relative 'tealrb/local'
27
+ require_relative 'tealrb/enums'
12
28
  require_relative 'tealrb/scratch'
29
+ require_relative 'tealrb/algod'
30
+ require_relative 'tealrb/rewriters'
13
31
  require_relative 'tealrb/contract'
14
32
  require_relative 'tealrb/cmd_line/teal2tealrb'
15
33
 
16
34
  module TEALrb
17
35
  class TEAL < Array
18
- class << self
19
- attr_writer :instance
20
-
21
- def instance
22
- raise 'TEALrb does not support multi-threading' if Thread.current != Thread.main
23
-
24
- @instance
25
- end
26
- end
27
-
28
- def initialize(teal_array)
29
- self.class.instance = self
30
- super
36
+ def initialize(teal_array, contract)
37
+ @contract = contract
38
+ super(teal_array)
31
39
  end
32
40
 
33
41
  def teal
@@ -38,19 +46,41 @@ module TEALrb
38
46
  self << '&&'
39
47
  end
40
48
 
49
+ def <<(value)
50
+ return super unless @contract.class.src_map
51
+ return super if caller.join['src_map']
52
+ return super unless @contract.eval_location
53
+
54
+ eval_location = caller.reverse.find { _1[/^\(eval/] }&.split(':')
55
+ return super unless eval_location
56
+
57
+ eval_line = eval_location[1].to_i
58
+ src_map(@contract.eval_location.first, @contract.eval_location.last + eval_line)
59
+ super
60
+ end
61
+
62
+ def src_map(file, line_number)
63
+ ln = "// src_map:#{File.basename(file)}:#{line_number}"
64
+ return if ln == @last_src_map
65
+
66
+ @last_src_map = ln
67
+
68
+ self << ln
69
+ end
70
+
41
71
  def boolean_or(_other)
42
72
  self << '||'
43
73
  end
44
74
 
45
75
  TEALrb::Opcodes::BINARY_OPCODE_METHOD_MAPPING.each do |meth, opcode|
46
76
  define_method(meth) do |other|
47
- TEALrb::Opcodes::ExtendedOpcodes.send(opcode, self, other)
77
+ @contract.send(opcode, self, other)
48
78
  end
49
79
  end
50
80
 
51
81
  TEALrb::Opcodes::UNARY_OPCODE_METHOD_MAPPING.each do |meth, opcode|
52
82
  define_method(meth) do
53
- TEALrb::Opcodes::ExtendedOpcodes.send(opcode, self)
83
+ @contract.send(opcode, self)
54
84
  end
55
85
  end
56
86
  end