Class: CodeHandler

Inherits:
Object show all
Defined in:
lib/CodeHandler.rb

Overview

A code handler continualy considers how to extend the complexity of a method in a custom class given the intial starting methods already availble within the class.

It also can refactor the code into separate methods thereby reusing the same functionality.

Essentially it just tries to create as many uniquely new variables as it can. Note they are consider unique if the process to create them is new, NOT whether or not there value is new.

Constant Summary collapse

@@CODE_HANDLER_ID =
0

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(runtime_methods = [], structures = nil) ⇒ CodeHandler

Returns a new instance of CodeHandler.



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/CodeHandler.rb', line 21

def initialize(runtime_methods=[],structures=nil)
  @runtime_methods = runtime_methods
  @entrance_method = create_entrance_method
  @code_handler_id = @@CODE_HANDLER_ID
  @@CODE_HANDLER_ID += 1
  
  # These are the default statement structures if non-are supplied
  @default_structures = [
    DeclareVariableAsVariableStructure.new,
    StatementStructure2.new(StatementStructure2.declared_runtime_method),
    StatementStructure2.new(StatementStructure2.declared_array),      
    StatementStructure2.new(StatementStructure2.declared_string_addition),            
    DeclareNewInstanceStructure.new(MethodUsageClass.new,RuntimeMethodClass.new)
   ]
   
    # If structures are supplied the overwrite the default structures
    @default_structures = structures unless structures.nil? 
end

Instance Attribute Details

#default_structures=(value) ⇒ Object (writeonly)

Sets the attribute default_structures

Parameters:

  • value

    the value to set the attribute default_structures to.



17
18
19
# File 'lib/CodeHandler.rb', line 17

def default_structures=(value)
  @default_structures = value
end

#runtime_methodsObject (readonly)

Returns the value of attribute runtime_methods.



16
17
18
# File 'lib/CodeHandler.rb', line 16

def runtime_methods
  @runtime_methods
end

Class Method Details

.reset_global_idObject



232
233
234
235
236
237
238
# File 'lib/CodeHandler.rb', line 232

def self.reset_global_id
  #http://www.zenspider.com/Languages/Ruby/QuickRef.html
  unless $".include?('test/unit.rb')
    StandardLogger.log 'WARNING: Resetting variable id, this should only be done for tests'
  end
  @@CODE_HANDLER_ID = 0
end

Instance Method Details

#class_nameObject



240
241
242
# File 'lib/CodeHandler.rb', line 240

def class_name
  return "RuntimeCodeHandler"+@code_handler_id.to_s
end

#itterate(count = 1, additional_available = []) ⇒ Object

Returns an array of method instances containing various numbers of statements.

  1. At this point we are creating a statement to go straight into a method or into a nested statement. First we find all the available variables in the context. For this to work the variables should be saved with a scope. At this point it isn’t concerning it’s self with the dependencies for the variable. It does however have to take into consideration scope. As such every nested statement maintains an array of it’s level e.g. [5,7] for a two level nested statement. This would mean that varibles with a scope level of 5 or 7 would be valid consideration.

  2. Next using the variables available to it it constructs a number of different statements.

  3. From this statement it needs to work backwards to construct the full method. It does this by finding all the dependencies that have the same scope level. After that has been done, if finds and adds all the dependcies on the other scope level.

  4. With the method constructed the value for the new variable can be asscertained. The asscertained value gets added to available array. Also if a variable is overwritten within the nested statement then its value is included with a scope value outside the nested statement. Specifically a scope level the same as the variable that was overridden. The variable simple has a depencey on the nested statement. This allows it to rebuild the statements in the correct order. It can then check the scopes and wrap the nested statement accordingly.

Parameters:

  • count (defaults to: 1)

    The number of itterations to go through when generating new runtime method arrangements.

  • additional_available (defaults to: [])

    Any additional values that should be made available when generating statements.



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
# File 'lib/CodeHandler.rb', line 91

def itterate(count=1,additional_available=[])

  # Create an array to contain the vessels currently available
  initial_copied_method = @entrance_method.copy
  initial_copied_method.clear              
  vessels = [initial_copied_method]
  
  # Create the array to contain the available variables and the initial runtime method calls
  available = []
  available += additional_available
  @runtime_methods.each {|x| available += x.callable_combinations}  
  
  # Set the scope id for all the method calls
  available.each {|x| x.scope_id = initial_copied_method.scope_id}
  
  # Maintains all the statements and their dependencies
  dependencies = StatementDependencies.new
  
  # Completed methods
  results = []
      
  # Itterate over the number of times specified
  count.times do |i|        

    # Keep track of the newly created variables
    newly_created_variable = []
    
    # Keeps track of newly created vessels
    newly_created_vessels = []
    
    # For each vessel create appropriate statements
    vessels.each do |vessel|
      
      # Find potential new statements available to the current vessel
      new_statement_results = find_possible_new_statements(vessel,available,dependencies)

      # Find the statement dependencies for each statement
      new_statement_results.each do |y|      
      
        # Create the vessel for the new statement
        new_vessel = vessel.copy                    

        # Create a runtime method with the statement and all the dependencies 
        copied_method = build_minimalist_method(@entrance_method,y,dependencies)

        # Realise the generated method
        begin
          realised_method = copied_method.realise2(ParametersContainer.new,@runtime_methods)
        rescue ImproperStatementUsageError => e
          
          # TODO  I have rejected statements "var = <#statement_variable>.declare_variable" if the statement
          # variable doesn't represent a declaration statement.  I don't know if this is the right way to
          # do things.  It might be better to return a NilVariable or prevent it from ever being created.
          
          # TODO  I should check this is logged
          StandardLogger.instance.warning('Method rejeceted')       
          StandardLogger.instance.warning(e)                   
          next
        end       
        
        results.push(realised_method)       
        
        # TODO  I should determine the nature of the statement and then 
        #       determine the new variables available.
        # - I should maybe save structures with statements.    
        
        raise StandardError.new('Statement type has not defined for this statement') if y.statement_type.nil?

        # Determine the id of the statement that declares the value
        #case(y.statement_type)
        #  when StatementStructure::DECLARATION_STATEMENT
        if((y.statement_type == StatementStructure::MODIFYING_STATEMENT)or(y.statement_type == StatementStructure::DECLARATION_STATEMENT))

            #declared_variable_id = y.created_variable.variable_id                       
            variable_uniq_id = y.created_variable.uniq_id                       

            # Find the value of the variable within the realised method and set it's scope
            #resulting_variable = realised_method.find_variable(declared_variable_id)   
            resulting_variable = realised_method.find_variable(variable_uniq_id)     

            if y.kind_of?(BlockStatement)
              resulting_variable.scope_id = y.scope_id
              resulting_variable.clear
            else
              resulting_variable.scope_id = vessel.scope_id
            end
            newly_created_variable.push(resulting_variable)          
        
            #new_vessel = realised_method.find_statement_that_declares_variable(declared_variable_id)
            # TODO  Check this works with uniq_id          
            new_vessel = realised_method.find_statement_that_created_variable(variable_uniq_id)
            
            # Find the statement from the typed method that declares the variable
            # NOTE  I wonder if it would be quicker and safe to add the realised statement here
            #declaring_statement = realised_method.find_statement_that_declares_variable(declared_variable_id)
            # TODO  Check this works with uniq_id
            declaring_statement = realised_method.find_statement_that_created_variable(variable_uniq_id)
            dependencies.push(declaring_statement)          
        
            # If the new statement was a nested statement then add as vessel
            if y.kind_of?(BlockStatement)
              new_vessels_scope = [new_vessel.scope_id]
              new_vessels_scope += vessel.scope
              new_vessel.scope = new_vessels_scope
              new_vessel.clear
              newly_created_vessels.push(new_vessel)
            end          
        
#          when StatementStructure::MODIFYING_STATEMENT:
  
#             # exit
#              # Need to save modified statement;
#              StandardLogger.instance.warning('I think I need to treat modifying statements as if they declare new variables')
#              
#              # TODO  I'm not certain this isn't going to add loads of statement
#              StandardLogger.instance.info('Statement ID: '+y.statement_id.to_s)              
#              StandardLogger.instance.info(': '+y.write)              
#              
#              # Adding the statement the newly modified statement
#              dependencies.push(realised_method.select {|stment| stment.statement_id == y.statement_id}.first)
#            elsif(StatementStructure::USAGE_STATEMENT)  
          else
            raise StandardError.new('Unknown statement_type "'+y.statement_type+'" "'+y.write+'"')
        end
        
      end
        
    end
    
    # Add the new variables to available list
    # TODO  newly_created_variable is an array - it should be newly_created_variables
    available += newly_created_variable                         
    
    # Add the new vessel to the list
    vessels += newly_created_vessels      
       
  end  
  return results
  
end

#writeObject

Returns a string that prints out how the dynamic code for the code handler currently looks.



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/CodeHandler.rb', line 43

def write

  dynamic_code = "class #{class_name}"+"\n"
  
  # Include each of the avaialble runtime methods
  @runtime_methods.each do |x|
    dynamic_code += x.write(ParametersContainer.new,1)
  end
  
  # Now add the entrance method
  dynamic_code += @entrance_method.write(ParametersContainer.new,1)
  
  dynamic_code += "end"
  return dynamic_code
end