sbuilder-eth 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,122 @@
1
+ require 'sexp_processor'
2
+
3
+ module Sbuilder
4
+ module Eth
5
+ class SexpProcessorResolve < SexpProcessorEth
6
+ # ------------------------------------------------------------------
7
+ # @!group Constructor
8
+
9
+ def initialize( options={} )
10
+ super( options )
11
+ # Allow Sexp nodes to remain intact, see 'process_rest'
12
+ self.require_empty = false
13
+
14
+ # visitors just recurses - do not shift sexp (as sexp_processor default does)
15
+ self.default_method = :process_rest
16
+
17
+ end
18
+
19
+ # @!endgroup
20
+
21
+ # ------------------------------------------------------------------
22
+ # @!group Resolvers for reference node types
23
+
24
+ # Resolve 'reference' on 'NewExpression' to point to constructor
25
+ # in contract pointed by 'classConstructed'
26
+ def process_NewExpression( sexp )
27
+
28
+ # resolve child particulary 'UserDefinedTypeName'
29
+ process_rest( sexp )
30
+
31
+ # Navigate to resolved contract
32
+ contract = sexp.classConstructed.reference
33
+
34
+ # resolve for constructor method
35
+ sexp.reference = contract.resolve( sexp.classConstructed["name"])
36
+
37
+ s()
38
+ end
39
+
40
+ def process_UserDefinedTypeName( sexp )
41
+ logger.debug "#{__method__}: sexp:#{sexp}" if logger.debug?
42
+
43
+ raise "Unscoped identifier '#{sexp['name']}' for #{sexp}" if sexp.enclosingScope.nil?
44
+ # Resolve user defined type name in 'enclosingScope'
45
+ sexp.reference = sexp.enclosingScope.resolve(sexp['name'])
46
+
47
+ s()
48
+ end
49
+
50
+ def process_MemberAccess( sexp )
51
+ logger.debug "#{__method__}: sexp:#{sexp}" if logger.debug?
52
+
53
+ # resolve childs (ie. :Identifier, :MemberAccess) NB: can also
54
+ # be FunctionCall as is case for f.gas(1).value(2)(3,5,6)
55
+ process_rest( sexp )
56
+
57
+ # 'struct' should have been resolved by process_rest.
58
+ # No need to resolve function call modifiers
59
+
60
+ if sexp.struct.sexp_type == :FunctionCall
61
+
62
+ # sexp.reference = scope.resolve( sexp["member_name"] ) unless sexp["member_name"] == Constants::FIELD_NAME_VALUE ||
63
+ # # sexp["member_name"] == Constants::FIELD_NAME_GAS
64
+
65
+ else
66
+ # scope is pointed by reference
67
+ scope = sexp.struct.reference
68
+ # Indirection for :VariableDeclaration: scope is actually the
69
+ # type of the :VariableDeclaration
70
+ scope = scope.variableType.reference if scope.sexp_type == :VariableDeclaration
71
+
72
+ if scope.sexp_type == :FunctionDefinition
73
+ #
74
+ # Case f.value(1).gas(1)(3,4) - value and gas modifiers of
75
+ # the function CALL are NOT defined in 'FunctionDefinition'
76
+ #
77
+ # Gas, value - No need to resolve - raise exception for surprieses if
78
+ # 'member_name' is something else that 'value' or 'gas'.
79
+ #
80
+ # raise "Unexpected sexp #{sexp["member_name"]} of in #{sexp}" unless sexp["member_name"] == Constants::FIELD_NAME_VALUE ||
81
+ # sexp["member_name"] == Constants::FIELD_NAME_GAS
82
+ # sexp.reference = sexp.functionCalled.reference
83
+ if sexp["member_name"] == Constants::FIELD_NAME_VALUE || sexp["member_name"] == Constants::FIELD_NAME_GAS
84
+ # point to called function
85
+ sexp.reference = sexp.struct.reference
86
+ else
87
+ sexp.reference = scope.resolve( sexp["member_name"] )
88
+ end
89
+
90
+ else
91
+ # easy part
92
+ sexp.reference = scope.resolve( sexp["member_name"] )
93
+ end
94
+
95
+ end
96
+
97
+ s()
98
+
99
+ end
100
+
101
+ def process_Identifier( sexp )
102
+ logger.debug "#{__method__}: sexp:#{sexp}" if logger.debug?
103
+
104
+ raise "Unscoped identifier '#{sexp['value']}' for #{sexp}" if sexp.enclosingScope.nil?
105
+ # Resolve identifier 'value' in 'enclosingScope' of 'sexp'
106
+ sexp.reference = sexp.enclosingScope.resolve(sexp['value'])
107
+
108
+ # continue with tree traversal
109
+ # process_rest( sexp )
110
+ s()
111
+
112
+ end
113
+
114
+
115
+ # @!endgroup
116
+
117
+
118
+
119
+ end
120
+ end
121
+ end
122
+
@@ -0,0 +1,355 @@
1
+ # coding: utf-8
2
+ require 'sexp_processor'
3
+
4
+ module Sbuilder
5
+ module Eth
6
+
7
+ # Visit AstSexp, 1) create hierarchical symbol tables 2) define
8
+ # symbols, and 3) add pointer to encosing scope to expression
9
+ # needed to be resolved.
10
+ #
11
+ # Techinically:
12
+ # - 1) Scope#enclosingScope : build scope hierarchy
13
+ # - 2) Scope#define( name, sexp ): build symbol table defines
14
+ # - 3) Reference#enclosingScope : set scope for use for Scope#resolve
15
+ #
16
+ # Scope hierarchy built:
17
+ # - contract
18
+ # - function
19
+ # - input parameter = request message (key name "msg"), scope where expression
20
+ # references are resolved
21
+ # - return parameters = response message ( key name "return-")
22
+ #
23
+ # TODO:
24
+ # - global scope
25
+ # - structure types
26
+ # - enums
27
+ #
28
+ #
29
+ #
30
+
31
+ class SexpProcessorScope < SexpProcessorEth
32
+
33
+ # ------------------------------------------------------------------
34
+ # @!group Object state
35
+
36
+ # @!attribute [Scope] currentScope which currently opened
37
+ attr_accessor :currentScope
38
+
39
+ # @!attribute [Hash] options
40
+ attr_accessor :options
41
+
42
+ # @!endgroup
43
+
44
+
45
+ # ------------------------------------------------------------------
46
+ # @!group Constructor
47
+
48
+ def initialize( options={} )
49
+ super( options )
50
+
51
+ self.options = options
52
+
53
+ initGlobalScope
54
+ end
55
+
56
+ def initGlobalScope
57
+ self.currentScope = GlobalScope.start( options )
58
+ end
59
+
60
+ # @!endgroup
61
+
62
+ # ------------------------------------------------------------------
63
+ # @!group Recursion helpers
64
+
65
+ def scoped( ethScope )
66
+ raise "Scope support missing in #{ethScope}" unless ethScope.respond_to?( :isScope ) && ethScope.isScope
67
+
68
+ # remember 'prevScope', update enclosing scope, switch '@currentScope',
69
+ prevScope = currentScope
70
+ ethScope.enclosingScope = currentScope
71
+ self.currentScope = ethScope
72
+
73
+ begin
74
+ yield
75
+ ensure
76
+ # logger.debug "#{__method__}: pop scope for '#{ethScope.sexp_type}' - resume #{prevScope ? prevScope.sexp_type : 'global scope'}" if logger.debug?
77
+ self.currentScope = prevScope
78
+ end
79
+ end
80
+
81
+ def getScopeExpressionProcessor( scope )
82
+ proc = ScopeExpressionProcessor.new( options )
83
+ proc.currentScope = scope
84
+ proc
85
+
86
+ end
87
+
88
+ # @!endgroup
89
+
90
+
91
+ # ------------------------------------------------------------------
92
+ # @!group Node rules to recurse down
93
+
94
+ def process_ContractDefinition( sexp )
95
+
96
+ # add contract to global scope
97
+ currentScope.define( sexp['name'], sexp )
98
+
99
+ scoped( sexp ) do
100
+ getScopeExpressionProcessor( currentScope ).process( sexp )
101
+ process_rest( sexp )
102
+ end
103
+ s()
104
+ end
105
+
106
+ # Scope ModifierDefinitionReferences
107
+ # to 'msg' and :ParameterList parameters in
108
+ # :ModifierDefinition must be resolved dynamically during
109
+ # :ModifierInvocation. Global references can be resolved
110
+ # "normally" (=statically by sexp_processor_scope and
111
+ # sexp_processor_resolve), as well as :Idenfier references in
112
+ # sexp_processor_resolve.
113
+ #
114
+ #
115
+ # Solution: we define 'msg' scope as :ParameterListWrapper
116
+ # encapsulating :ParameterList of :ModifierDefinition. Method
117
+ # 'scopeDefining', which would otherwise return :ParameterList
118
+ # or :ModifierDefinition, is changed to return :ParameterList of
119
+ # :FunctionDefintion from a value set dynamically into
120
+ # :ParameterListWrapper, when scope of modifier is known (=when
121
+ # processing :ModifierInvocation in :FunctionDefinition
122
+ # statements).
123
+ #
124
+ def process_ModifierDefinition( sexp )
125
+ logger.debug "#{__method__}: sexp=#{sexp}" if logger.debug?
126
+
127
+ currentScope.define( sexp['name'], sexp )
128
+
129
+ # Wrap :ModifierDefinition 'requestParameters' ParameterListWrapper
130
+ # sexp.wrapRequestParameters
131
+
132
+ blockScope = sexp
133
+ scoped( sexp ) do
134
+
135
+
136
+ # Define msg
137
+ currentScope.define( Constants::SOL_MSG, sexp.requestParameters)
138
+
139
+ # Add common params
140
+ addCommonRequestParameters( sexp.requestParameters, sexp)
141
+
142
+ # Scope 'normal' request paramers in () ModifierDefinition scope
143
+ sexp.requestParameters.parameters.each { |paramDecl| process( paramDecl ) }
144
+ scoped( sexp.requestParameters ) do
145
+ # scope request params within contract:func:input:output
146
+ process( sexp.requestParameters )
147
+ end
148
+
149
+
150
+ # currentScope.define( Constants::SYMBOL_RESPONSE_MSG, sexp.responseParameters )
151
+ # process( sexp.responseParameters )
152
+ # local variables
153
+ process( sexp.block )
154
+ end
155
+
156
+ # process expression on block with requestParameteres in scope context
157
+ getScopeExpressionProcessor( blockScope ).process( sexp.block )
158
+
159
+ s()
160
+
161
+ end
162
+
163
+ # Create scope hierarchy for :FunctionDefintion, define symbols
164
+ # for declarations in a scope, and add pointers to scope for
165
+ # identfiers.
166
+ #
167
+ # Implementation:
168
+ #
169
+ # - assumes preprocessing ensures return parameters have name
170
+ #
171
+ # - scope hieararchy: :FunctionDefintion, requestParameters,
172
+ # responseParameters
173
+ #
174
+ # -symbol definitions:
175
+ # - FunctionDefintion
176
+ # - msg --> requestParameters
177
+ # - local variables
178
+ # - requestParameters
179
+ # - request parameter name -> :VarDecl
180
+ # - responseParameters
181
+ # - response parameter name -> :VarDecl
182
+ #
183
+ # - scope identifiers
184
+ # - requestParameters
185
+ # -- modifierInvocations
186
+ # - responseParameters
187
+ # -- function body block
188
+ #
189
+ #
190
+
191
+ def process_FunctionDefinition( sexp )
192
+ logger.debug "#{__method__}: sexp=#{sexp}" if logger.debug?
193
+
194
+
195
+ currentScope.define( sexp['name'], sexp )
196
+ currentScope.define( Constants::SOL_THIS, sexp )
197
+
198
+ scoped( sexp ) do
199
+
200
+ # validate existing names for return value
201
+ logger.debug "#{__method__}: sexp.responseParameters=#{sexp.responseParameters}" if logger.debug?
202
+ # assume preprocessing ensures return parameters have name
203
+ sexp.responseParameters.parameters.each { |parameter| raise "Unnamed return parameters are not supported yet #{parameter} in #{sexp}" if parameter["name"].empty? }
204
+
205
+ # Support msg.X, where X=sender, value, ...
206
+ currentScope.define( Constants::SOL_MSG, sexp.requestParameters )
207
+
208
+ # TODO: remove, currently used to create response data types
209
+ # for this function
210
+ currentScope.define( Constants::SYMBOL_RESPONSE_MSG, sexp.responseParameters )
211
+
212
+ # add msg.sender, msg.recipient, msg.value
213
+ addCommonRequestParameters( sexp.requestParameters, sexp)
214
+
215
+ scoped( sexp.requestParameters ) do
216
+
217
+ # scope request params within contract:func:input:output
218
+ process( sexp.requestParameters )
219
+
220
+ # scope for modfifiers: contract:func:input
221
+ sexp.modifierInvocations.each do |modifier|
222
+ logger.debug "#{__method__}: scope-modifierInvocation:#{modifier}" if logger.debug?
223
+ # getScopeExpressionProcessor( sexp.requestParameters ).process( modifier )
224
+ getScopeExpressionProcessor( currentScope ).process( modifier )
225
+
226
+ end
227
+
228
+ # declare return parametes within 'responseParameters' scope
229
+ scoped( sexp.responseParameters ) do
230
+ process( sexp.responseParameters )
231
+ # add scope for identifier references
232
+ getScopeExpressionProcessor( currentScope ).process( sexp.block )
233
+ end # scoped( sexp.responseParameters )
234
+
235
+ end # request parameters
236
+
237
+ # local variables
238
+ process( sexp.block )
239
+
240
+ end # scoped(sexp): function
241
+
242
+ s()
243
+ end
244
+
245
+ # def process_ParameterList( sexp )
246
+ # # logger.info "#{__method__}: #{sexp} entered"
247
+ # scoped( sexp ) do
248
+ # process_rest( sexp )
249
+ # end
250
+ # end
251
+
252
+ # @!endgroup
253
+
254
+ # ------------------------------------------------------------------
255
+ # @!group Node rules for scoped ie. nodes using scope
256
+
257
+ def process_VariableDeclaration( sexp )
258
+ logger.debug "#{__method__}: sexp=#{sexp}" if logger.debug?
259
+ logger.info "#{__method__}: define variable name=#{sexp['name']}, type=#{sexp.sexp_type} within #{currentScope}"
260
+
261
+ currentScope.define( sexp['name'], sexp )
262
+ process_rest( sexp )
263
+ end
264
+
265
+ def process_VariableDeclarationWithDomain( sexp )
266
+ logger.debug "#{__method__}: sexp=#{sexp}" if logger.debug?
267
+ logger.info "#{__method__}: define variable name=#{sexp['name']}, type=#{sexp.sexp_type} within #{currentScope}"
268
+
269
+ currentScope.define( sexp['name'], sexp )
270
+
271
+ # No need to go deeper
272
+ # process_rest( sexp )
273
+
274
+ s()
275
+ end
276
+
277
+
278
+
279
+ # @!endgroup
280
+
281
+ # ------------------------------------------------------------------
282
+ # @!group AST modifiers
283
+
284
+ # Add common request parameters (e.g. sender, value, recipient)
285
+ # to 'sexpRequestParameters' of a constructor or a normal
286
+ # contract function 'sexpFunctionDefinition'.
287
+ #
288
+ # @param sexpFuncOrModifier
289
+ # [:FunctionDefinition,:ModifierDefinition] to add common
290
+ # parametes rto
291
+ #
292
+ #
293
+ def addCommonRequestParameters( sexpRequestParameters, sexpFuncOrModifier )
294
+ (sexpFuncOrModifier.sexp_type != :ModifierDefinition && sexpFuncOrModifier["isConstructor"] ?
295
+ Constants::COMMON_REQ_CONSTRUCTOR :
296
+ Constants::COMMON_REQ_MSG).each do |name_domain|
297
+ sexpRequestParameters.addChild( VariableDeclarationWithDomain.create( name_domain))
298
+ end
299
+
300
+ end
301
+
302
+ # @!endgroup
303
+
304
+
305
+ end
306
+
307
+ class ScopeExpressionProcessor < SexpProcessorEth
308
+
309
+ # @!attribute [Scope] currentScope which currently opened
310
+ attr_accessor :currentScope
311
+
312
+
313
+ def initialize( options={} )
314
+ super( options )
315
+
316
+ # self.options = options
317
+
318
+ end
319
+
320
+ # ------------------------------------------------------------------
321
+ # @!group Individual expressions - set enclosingScope
322
+
323
+ def process_UserDefinedTypeName( sexp )
324
+
325
+ logger.debug "#{__method__}: sexp==#{sexp}" if logger.debug?
326
+ raise "No current scope for #{sexp}" if currentScope.nil?
327
+
328
+ # save 'currentScope' to use in resolve declaration reference
329
+ sexp.enclosingScope = currentScope
330
+
331
+ process_rest(sexp)
332
+
333
+ end
334
+
335
+
336
+ def process_Identifier( sexp )
337
+
338
+ logger.debug "#{__method__}: sexp==#{sexp}" if logger.debug?
339
+ logger.info "#{__method__}: sexp=#{sexp['value']}"
340
+
341
+ # save 'currentScope' to use in resolve declaration reference
342
+ sexp.enclosingScope = currentScope
343
+
344
+ process_rest(sexp)
345
+ end
346
+
347
+ # @!endgroup
348
+
349
+
350
+
351
+ end
352
+ end
353
+
354
+ end
355
+