Class: Statement

Inherits:
Object show all
Includes:
ActsAsCode, ActsAsStatement, PrintVariables
Defined in:
lib/core/statement/Statement.rb

Constant Summary collapse

@@statement_id =
0

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from ActsAsCode

#write_structure

Methods included from ActsAsStatement

#classes_match?, #statement_type

Methods included from PrintVariables

#display_name_for

Constructor Details

#initialize(*parameters) ⇒ Statement

&block [OPTIONAL] A block can also be provided to preview the statement from being

reviewed and therefore preventing changes to variables uniq_id.


22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/core/statement/Statement.rb', line 22

def initialize(*parameters)
  super()
  
  # 
  @overrides = nil
  @confirmed = false
  
  # Sets the flag that indicates the structure of the statement
  @structure = nil
  
  @nodes = []
  
  # Add the parameters to the array 
  parameters.each do |code|
  #  self.push(code.copy)
    @nodes.push(code.copy)
  end
  
  # TODO  I might change the statement_id to be determined by the structure
  #       of the statement.
  @statement_id = @@statement_id
  @@statement_id += 1
  
  # Review completed statement
  if block_given?
    values = yield
    review if (values[:review])
  else
    review
  end      
  
end

Instance Attribute Details

#overrides=(value) ⇒ Object (writeonly)

TODO scope and statement level are confusing - not sure of the difference (I think it’s legacy)



8
9
10
# File 'lib/core/statement/Statement.rb', line 8

def overrides=(value)
  @overrides = value
end

#scopeObject

Returns the value of attribute scope.



6
7
8
# File 'lib/core/statement/Statement.rb', line 6

def scope
  @scope
end

#statement_idObject

Returns the value of attribute statement_id.



6
7
8
# File 'lib/core/statement/Statement.rb', line 6

def statement_id
  @statement_id
end

#statement_levelObject

Returns the value of attribute statement_level.



6
7
8
# File 'lib/core/statement/Statement.rb', line 6

def statement_level
  @statement_level
end

Class Method Details

.reset_global_idObject

Resets the globablly used value for statements id



57
58
59
60
61
62
63
# File 'lib/core/statement/Statement.rb', line 57

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
  @@statement_id = 0
end

Instance Method Details

#[](index) ⇒ Object



310
311
312
# File 'lib/core/statement/Statement.rb', line 310

def [](index)
  return @nodes[index]
end

#[]=(index, value) ⇒ Object



306
307
308
# File 'lib/core/statement/Statement.rb', line 306

def []=(index,value)
  @nodes[index] = value
end

#add(element) ⇒ Object



284
285
286
# File 'lib/core/statement/Statement.rb', line 284

def add(element)
  push(element)
end

#assignment?Boolean

Indicates whether the statement is one that assigns a value to another. Essential “Does the statement contain an equals sign?”

Returns:



334
335
336
337
338
339
# File 'lib/core/statement/Statement.rb', line 334

def assignment?
  if detect_class(Equivalent).nil? 
    return false
  end
  return true
end

#cauldron_method_callsObject



913
914
915
# File 'lib/core/statement/Statement.rb', line 913

def cauldron_method_calls
  return ['.statement_id']
end

#clearObject



326
327
328
# File 'lib/core/statement/Statement.rb', line 326

def clear
  @nodes.clear
end

#confirmed?Boolean

Indicates whether the statement has been confirmed.

Returns:



354
355
356
# File 'lib/core/statement/Statement.rb', line 354

def confirmed?
  return @confirmed
end

#contains_method_call?Boolean

Returns true if the statement contains any calls to a runtime method.

Returns:



266
267
268
# File 'lib/core/statement/Statement.rb', line 266

def contains_method_call?
  return self.any? {|x| x.kind_of?(DefCall)}       
end

#context_variable(id) ⇒ Object

Returns a variable instance with the contextual requirements for the statement. TODO Don’t really need these contextual_variable calls



507
508
509
# File 'lib/core/statement/Statement.rb', line 507

def context_variable(id)
  return find_variable(id)
end

#contextual_variable(id) ⇒ Object



511
512
513
# File 'lib/core/statement/Statement.rb', line 511

def contextual_variable(id)
  return context_variable(id)
end

#copyObject

Creates a copy of the statement. The copy’s variables should have the same variable ids but should be different instances. When all the code elements are added the statement is ascessed and the the requirements updated approriately.



255
256
257
258
259
260
261
# File 'lib/core/statement/Statement.rb', line 255

def copy
#    tmp = self.collect{|x| x.copy}
#    result = self.class.new(*tmp) {{:review=>false}}
#    result.statement_level = statement_level
#    return result
  return Marshal.load(Marshal.dump(self))
end

#created_variableObject

Returns the variable created by this statement or raises an error if the statement doesn’t create a variable.



828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
# File 'lib/core/statement/Statement.rb', line 828

def created_variable

  # Check if the statement declares a new variable
  begin 
    return declared_variable
  rescue ImproperStatementUsageError => e
    if first.kind_of?(InstanceCallContainer)
      if first.subject.kind_of?(Variable) and first.method_call.destructive?
        return first.subject
      end
    end
    raise ImproperStatementUsageError.new(self.write+' statement does not create a new variable "'+e+"'")
  end
  
end

#created_variable_idObject

Returns the id of the created variable in this statement or raises an error if no variable is created.



821
822
823
# File 'lib/core/statement/Statement.rb', line 821

def created_variable_id
  return created_variable.variable_id
end

#creates_variable?Boolean

Returns true if this statement creates a new variable. A variable is considered created if it is declared in the variable or it modifies a variable. e.g. var_a.chop! or var_a.push(var_b)

Returns:



811
812
813
814
815
816
# File 'lib/core/statement/Statement.rb', line 811

def creates_variable?
  created_variable
  return true
rescue ImproperStatementUsageError
  return false
end

#decalares_variable?Boolean

Returns true if the statement declares a variable

Returns:



795
796
797
798
# File 'lib/core/statement/Statement.rb', line 795

def decalares_variable?
  return true if self[0].kind_of?(Variable) and self[1].kind_of?(Equal)
  return false
end

#declared_variableObject

Returns the variable declared in the statement. (if one exists)



802
803
804
805
806
# File 'lib/core/statement/Statement.rb', line 802

def declared_variable
  raise ImproperStatementUsageError.new(self.write+' does not declare a variable') unless decalares_variable?
  return nil if declared_variable_id.nil?  
  return self[0]
end

#declared_variable_idObject

Returns the id of the variable declared in this statement



613
614
615
616
# File 'lib/core/statement/Statement.rb', line 613

def declared_variable_id
  raise UnexpectedStatementTypeError.new('Should be declaration statement type '+self.write) unless decalares_variable?
  return self[0].variable_id 
end

#describe(tab = 0) ⇒ Object

Raises:

  • (StandardError)


168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/core/statement/Statement.rb', line 168

def describe(tab=0)
  # Check the file type of tab
  raise StandardError.new('Unexpexted class "'+tab.class.to_s+'" for Fixnum (number of tabs)') unless tab.kind_of? Fixnum

  line = ''
  tab.times {line += "\t" }
  self.each do |code|
    line += code.describe
    break if code.object_id == self.last.object_id
  end
  return line  
end

#eachObject



292
293
294
# File 'lib/core/statement/Statement.rb', line 292

def each
  @nodes.each {|x| yield x}
end

#each_unrealised_variableObject

Displays each of the unrealised variables within the statement



409
410
411
412
413
# File 'lib/core/statement/Statement.rb', line 409

def each_unrealised_variable
  unrealised_variables.each do |x|
    yield x
  end
end

#each_untyped_variableObject

yeilds each variable that is not declared in the statement and isn’t known e.g. cat be literalised in the statement.



373
374
375
376
377
# File 'lib/core/statement/Statement.rb', line 373

def each_untyped_variable
  untyped_variables.each do |x|  
    yield x
  end
end

#each_variableObject



117
118
119
# File 'lib/core/statement/Statement.rb', line 117

def each_variable
  variables.each { |x| yield x }
end

#each_with_indexObject



300
301
302
303
304
# File 'lib/core/statement/Statement.rb', line 300

def each_with_index
  @nodes.each_with_index do |x,i|
    yield x, i
  end  
end

#equivalent?(statement) ⇒ Boolean

TODO Write tests for this Returns true if this statement is equivalent to the statement supplied. They are equalvalent if they have the same length, classes and values.

Parameters:

  • statement

    The statement that this statement is being compared to

Returns:



851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
# File 'lib/core/statement/Statement.rb', line 851

def equivalent?(statement)
  return false unless statement.length == self.length

  # TODO  It would be nice to come up with a method to itterate through
  #       groups in an array. 
  #       e.g.  [self,statement].each_pair do |x,y|
  #             end
  #       where x is the first element in self and y is the first element in statement
  # TODO  I think Unknowns should only be accepted if they are the first paramter in
  #       a declaration statement.  Current var_a = Unknown.chop is the same as var_b = var_c.chop
  self.each_with_index do |elem,i|
    return false unless elem.equivalent?(statement[i])
  end
  return true
  
#    # TODO  Tidy this up - pass the element to each other
#    #       e.g.
#    #       self.each_with_index do |elem,i|
#    #         return false unless eleme.equivalent?(statement[i])
#    #       end      
#    # 
#    self.each_with_index do |elem,i|
#      return false if statement[i].class == elem.class
#      if(elem.kind_of?(Variable))
#        return false if elem.variable_id != statement[i].variable_id
#      elsif(elem.kind_of?(InstanceCallContainer))
#        return false if elem.variable_id != statement[i].variable_id
#        return false if elem.method_call != statement[i].method_call
#      end      
#    end 
#    return true
end

#exchange_variables(conversions) ⇒ Object

Returns the statement with the any variables in the conversions table replaced with the supplied variables. This is used when a runtime method with parameters needs evaluated.

Parameters:

  • conversions

    A hash that identifies the uniq id’s of the variables that should be replaced. e.g. => <#Variable>,:8 => <#Variable> where variables with id 6 would changed to indicated variable.



900
901
902
903
904
905
906
907
908
909
910
911
# File 'lib/core/statement/Statement.rb', line 900

def exchange_variables(conversions)
  copied_statement = self.copy
  conversions.each do |x,y|
    begin 
      #copied_statement = copied_statement.subst(x.to_s.to_i,y.copy)
      copied_statement = copied_statement.replace_variable_if(y) {|a| a.uniq_id == x.to_s.to_i}
    rescue FailedToFindVariableError
      next
    end
  end
  return copied_statement
end

#find_actual_variable(uniq_id) ⇒ Object

Returns the variable in this statement with uniq_id specified. In the event the variable resides in a instance call then that is returned.



536
537
538
539
540
# File 'lib/core/statement/Statement.rb', line 536

def find_actual_variable(uniq_id)
  target = self.variables.select {|x| x.uniq_id == uniq_id}
  return target[0] unless target.empty?
  raise FailedToFindVariableError.new('Couldn\'t find a variable with the id '+uniq_id.to_s+' in "'+self.write+'"')    
end

#find_all_required_runtime_methodsObject

Returns an array of all the RuntimeMethods instances contained in this statement. In all likelyhood there will no more than one.



772
773
774
775
# File 'lib/core/statement/Statement.rb', line 772

def find_all_required_runtime_methods
  defcalls = @nodes.find_all {|x| x.kind_of?(DefCall)} 
  return defcalls.collect {|x| x.runtime_method}
end

#find_overriding_statements(overriding_statements = []) ⇒ Object

Adds this statement to array of overriding statements if it overrides an existing variable.

Parameters:

  • overriding_statements (defaults to: [])

    An array containing other overriding statements.



683
684
685
686
# File 'lib/core/statement/Statement.rb', line 683

def find_overriding_statements(overriding_statements=[])
  overriding_statements.push self if self.overrides? 
  return overriding_statements
end

#find_statement(id) ⇒ Object

Returns this statement if it has the correct id

Parameters:

  • id

    The id of the statement should exist

Raises:



88
89
90
91
92
93
# File 'lib/core/statement/Statement.rb', line 88

def find_statement(id)
  if self.statement_id == id
    return self
  end
  raise FailedToFindStatementError.new('Couldn\'t find statement with id '+id.to_s)
end

#find_variable(id) ⇒ Object

Returns the variable or instance call that contains the variable with the specidied id. It doesn’t search the requirements.

TODO Write test to retrieve declarared variable in a statement with two variables

e.g. varA = varB-varC

Parameters:

  • id

    The id of the variable that should be returned



524
525
526
527
528
529
530
# File 'lib/core/statement/Statement.rb', line 524

def find_variable(id)
  results = variables.select() {|x| x.variable_id == id}
  if results.empty?
    raise FailedToFindVariableError.new('Couldn\'t find variable with id = '+id.to_s+' in "'+self.write+'"')
  end
  return results.first
end

#firstObject



322
323
324
# File 'lib/core/statement/Statement.rb', line 322

def first
  @nodes.first
end

#has?(&block) ⇒ Boolean

Returns:



884
885
886
887
888
889
# File 'lib/core/statement/Statement.rb', line 884

def has?(&block)
  self.each do |x|
    return true if block.call(x)
  end
  return false
end

#identify_overriding_statements(already_declared = []) ⇒ Object

If this statement declares a variable determine whether the the variable has been previously declared. If so mark this statement as an “overrides” statement otherwise flag it as not.



70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/core/statement/Statement.rb', line 70

def identify_overriding_statements(already_declared=[])
  [BaseVariable,Equal].zip(@nodes) do |x,y|
    return unless y.kind_of?(x)
  end
  already_declared.each do |x|
    if x[:variable_id] == declared_variable_id
      @overrides = true
      return
    end
  end
  already_declared.push({:variable_id => declared_variable_id})
  @overrides = false
end

#is_simple?Boolean

Returns true if the statement is simple. In that it just re-declares an existing variable e.g. var_a = var_b.

Returns:



629
630
631
632
633
634
635
636
# File 'lib/core/statement/Statement.rb', line 629

def is_simple?
  return false unless decalares_variable?
  # TODO  Look at what you call the right hand side of the equation
  if right_hand_side.length == 1 && right_hand_side[0].kind_of?(Variable)
    return true
  end
  return false
end

#lastObject



296
297
298
# File 'lib/core/statement/Statement.rb', line 296

def last
  return @nodes.last
end

#left_hand_sideObject



347
348
349
350
# File 'lib/core/statement/Statement.rb', line 347

def left_hand_side
  l, r = self.split(Equivalent)  
  return l
end

#lengthObject



314
315
316
# File 'lib/core/statement/Statement.rb', line 314

def length
  return @nodes.length
end

#literaliseObject

Attempt to convert the statement into a string without any references to variables where possible. So varA = varB.chop might become “varA = ‘test’chop”



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
# File 'lib/core/statement/Statement.rb', line 125

def literalise

  # Go through each element in the statement and attempt to literalise it
  line = ''
  self.each do |code|
    unless code.kind_of? Variable or code.kind_of? InstanceCallContainer
      line += code.write
      next
    end
    if code.kind_of?(Variable)
      if code.literalisable?
        literal = code.literalise
        line += literal.write
        next
      end
      line += code.write
    elsif code.kind_of?(InstanceCallContainer)
      if(code.subject.literalisable?)
        literal = code.subject.literalise
        line += literal.write+'.'+code.method_call.write
        next
      end
      line += code.write
    end
  end
  return line  
end

#map_to(mapping) ⇒ Object



917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
# File 'lib/core/statement/Statement.rb', line 917

def map_to(mapping)
  
  # Duplicate the current statement before it is rewritten
  rewritten_statement = self.copy
  
  # Find all the containers that contain TheoryVariables
  # NOTE  The statement is put in an array because select all doesn't include the array itself
  containers = [rewritten_statement].select_all {|x| x.respond_to?(:has?)}
  theory_variable_containers = containers.select {|x| x.has? {|y| y.kind_of?(Variable)}}
  
  # Rewrite the statement replacing the values
  theory_variable_containers.each do |z|
    z.replace_variables!(mapping)
  end     
  
  return rewritten_statement
  #return TheoryDependent.new(rewritten_statement,@theory_component_id)
end

#not_declared_variablesObject

Returns an array of all the undeclared variables in the statement. An undeclared variable is simply a variable not declared in the statement but used in it. e.g. var_c = var_a+var_b var_a and var_b are the undeclared variable.



421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
# File 'lib/core/statement/Statement.rb', line 421

def not_declared_variables()

  # The statement shouldn't be valid if there are undeclared variables
  return [] if valid_syntax?
  
  # Find any excluded variables(ones that are delcared within the statement)
  # e.g. var = 'test'.chop
  declared_variables = []
  if( (self[0].kind_of? Variable)&&(self[1].class == Equal) )  
    declared_variables.push(self[0].copy)
  end

  # Attempt to find all the undeclared variables
  undeclared = []
  variables.each do |x|
    next if declared_variables.any? {|y| y.uniq_id == x.uniq_id}
    undeclared.push(x.copy)
  end
  return undeclared
  
end

#overrides?Boolean

Returns true if the statement overrides an existing variable. So if a = 6 but a was already set to 4 then this would be an overriding statement.

Returns:



99
100
101
# File 'lib/core/statement/Statement.rb', line 99

def overrides?
  return @overrides    
end

#push(code) ⇒ Object

Adds the new piece of code to the statement and update the variables requirements to accomodate any change.



273
274
275
276
277
278
279
280
281
282
# File 'lib/core/statement/Statement.rb', line 273

def push(code)
  
  # Add the new piece of code to the statement
  #array_push(code)
  @nodes << code   
  
  # Update the requirements to reflect the change
  update
  
end

#realise2(method_map) ⇒ Object

Returns an updated statement where all the variables within the statement have been realised. If the statement resides inside a nested statement or multiple nested statements the variables will be changed into a Dynamic Variable. These dynamic variables contain multiple variables with different values but the same variable_id.

For example:

3.times do |var_a|

var_b = var_a+1

end

In the above case var_b would be a dynamic variable would contain 3 FixnumVariable with the values 1,2,3. The statement itself doesn’t indicate that it is nested, it is still just a regular statement.

In a more complex example where we have two nested statements we might have something like the following:

2.times do |var_a|

2.times do |var_b|
  var_c = var_b+1
end

end

We would end up with another dynamic variable with the values 1,2,1,2.

The final tricky situation is when variables are overwritten. As a rule block variables are never overwritten - I should also include the subject of a loop as well.

For example:

var_a = 0 3.times do |var_b|

var_a += var_b

end

In this situation there will be 3 versions of var_a the first one, the dynamic internal one and a post statement variable. The post statement variable will have a statement dependencey on the above nested statement. The internal variable will have a scope depenencey on the nested statement.

TODO I suspect my approach to this is wrong. I pass in the history instance with

all the information and then try to work out the realised value for this statement
but it would probably be easier if the history instance itself to determine this.

Raises:

  • (StandardError)


735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
# File 'lib/core/statement/Statement.rb', line 735

def realise2(method_map)
  return copy if realised?

  raise StandardError.new('Unexpected data type ') unless method_map.kind_of?(History)
  # Create a duplcate statement to be modified 
  result = self.copy
  
  # Find value(s) for each unrealised variable.  
  y = []
  each_unrealised_variable do |x|
    if x.kind_of?(BlockVariable)
      y.push(method_map.find_realised_variable(x.variable_id,x.uniq_id,'BlockVariable'))
    else
      y.push(method_map.find_realised_variable(x.variable_id,x.uniq_id))
    end
    # TODO  Change to elsif x.kind_of?(Variable)
  end

  # Substitue the realised variables for the unrealised ones
  self.each_unrealised_variable do |x|
    catch(:variable_substituted) do
      y.each do |z|
        if z.uniq_id == x.uniq_id
          result = result.replace_variable_if(z) {|a| a.uniq_id == x.uniq_id}
          throw :variable_substituted
        end
      end
      raise StandardError.new('Couldn\'t find realised value for variable with id '+x.variable_id.to_s+' in "'+self.write+'"')
    end
  end
  return result
  
end

#realised?Boolean

Returns true if the statement has been fully realsied - in that all the variables contained within it are realised. Otherwise it returns false.

Returns:



780
781
782
# File 'lib/core/statement/Statement.rb', line 780

def realised?
  return self.variables.all? {|x| x.realised?}
end

#replace_variable!(id, var) ⇒ Object



542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
# File 'lib/core/statement/Statement.rb', line 542

def replace_variable!(id,var)

  # Find the variable to be replaced
  target = self.find_actual_variable(id)  
 
  #raise StandardError.new('Both target and variable should be the same class('+target.class.to_s+' != '+var.class.to_s+')') unless(target.kind_of?(var.class))
  # TODO  Should check for two variable kinds to two instance call kinds
  self.each_with_index do |code,i|

    # TODO Need to test with changing instance calls
    next unless code.kind_of?(Variable) or code.kind_of?(InstanceCallContainer)

    if(code.variable_id==target.variable_id)
      
      if code.kind_of?(Variable)
        self[i] = var
      elsif code.kind_of?(InstanceCallContainer)
        self[i].subject = var
      end
      return self
    end
  end
  
end

#replace_variable_if(var, &block) ⇒ Object

TODO I should have a realised statement class Finds each element that satisfy the

Parameters:

  • var

    The variable to replace any elements that match the block.



482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
# File 'lib/core/statement/Statement.rb', line 482

def replace_variable_if(var,&block)
  container = []
  @nodes.each do |x|
    if x.kind_of?(Variable)
      if block.call(x)
        container.push(var)
        next          
      end
    end
    if x.kind_of?(InstanceCallContainer)
      container.push(x.replace_variable_if(var,&block))
      next
    end
    container.push(x.copy)
  end
  #return self.copy(*container)
  copied = self.copy().clear
  container.each {|x| copied.push(x) }
  return copied
    
end

#required_variable_idsObject

Retuns an array of all the variable ids required for the statement.



621
622
623
624
# File 'lib/core/statement/Statement.rb', line 621

def required_variable_ids
  results = self.variables.collect {|x| x.variable_id}
  return (results-[declared_variable_id])
end

#right_hand_sideObject



342
343
344
345
# File 'lib/core/statement/Statement.rb', line 342

def right_hand_side
  l, r = self.split(Equivalent)  
  return r
end

#same_not_declared_variables?(statement) ⇒ Boolean

Returns true if the statement contains the same undeclared variables as the statement provided as well as the same number of elements.

Parameters:

  • statement

    The statement thats undeclared variables are being compared.

Returns:



670
671
672
673
674
675
676
# File 'lib/core/statement/Statement.rb', line 670

def same_not_declared_variables?(statement)
  return false if statement.length != self.length
  statement.not_declared_variables.each do |x|
    return false unless self.not_declared_variables.any? {|y| y.variable_id  == x.variable_id} 
  end
  return true
end

#select_all(results = [], &block) ⇒ Object



288
289
290
# File 'lib/core/statement/Statement.rb', line 288

def select_all(results=[],&block)
  return @nodes.select_all(results,&block)
end

#statement_countObject

Returns the number of statments in the statement. This will always be one but nested statements may have more.



661
662
663
# File 'lib/core/statement/Statement.rb', line 661

def statement_count
  return 1
end

#subst(id, var, context = self) ⇒ Object

TODO It would be nicer to a subst that allows a block passed though

e.g. a.subst(sub) {|x| x.variable_id == 9}

Returns a new statement that is a duplicate of this statement but with the specified variable replaced.

TODO Use the name nest for more than one statement TODO Write allot!! of tests for this

Parameters:

  • id

    The id of the variable that will be replaced from the statement



454
455
456
# File 'lib/core/statement/Statement.rb', line 454

def subst(id,var,context=self)    
  return self.copy.subst!(var){|x| x.uniq_id == id}
end

#subst!(var, &block) ⇒ Object



458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
# File 'lib/core/statement/Statement.rb', line 458

def subst!(var,&block)
  
  # TODO  I should probably check whether anything was substituted or or not              
  self.each_with_index do |x,i|
    next unless x.kind_of?(Variable) or x.kind_of?(InstanceCallContainer)    
    
    # Essentially checks whether element is an InstanceCallContainer
    if x.respond_to?(:subst!)
      self[i] = x.subst!(var,&block)
      next
    end   
    
    if block.call(x)
      self[i] = var.copy
    end
  end    
  return self
end

#subst_variable!(id, var) ⇒ Object

TODO Not sure whether both replace_variable! and subst_variable! are both needed - either ways

their names aren't descriptive enough to distinguish that one uses the uniq_id and one uses
the variable.


571
572
573
574
575
576
577
578
579
580
581
582
583
# File 'lib/core/statement/Statement.rb', line 571

def subst_variable!(id,var)
  # => TODO Use replace_variable_if?
  self.each_with_index do |token,i|
    if token.kind_of?(Variable) && id == token.variable_id
      self[i] = var
      next
    end
    if token.kind_of?(Container)
      self[i] = token.subst_variable!(id,var)
    end
  end
  self
end

#to_declaration(except = []) ⇒ Object

Returns a declaration for this statement. So it will look something like - Statement.new(StringVariable.new(‘test’),Equal.new,‘test’)

Parameters:

  • except (defaults to: [])

    An array of variable ids indicating what variables not to convert to new declarations. This is useful when you don’t want to redclare an exisitng variable.

    e.g. Statement.new(Unknown.new,Equal.new,var_12,Addition.new,Literal.new(9))



596
597
598
599
600
601
602
603
604
605
606
607
608
609
# File 'lib/core/statement/Statement.rb', line 596

def to_declaration(except=[])
  if except.empty? then return VariableDeclaration.new('Statement',*self.collect {|x| x.to_declaration}) end
  results = []
  self.each do |x|
    if x.kind_of?(Variable)
      if except.any? {|y| x.variable_id == y}
        results.push Declaration.new(x.write)
        next
      end
    end
    results.push x.to_declaration
  end
  return VariableDeclaration.new('Statement',*results)
end

#to_literal_stringObject

TODO Write tests This method writes the statement out as a literal string. In the sense that any of the variables used in the statement are converted to literals and written. Unknown variables are not written yet though.

This method is called during tracking to give an indication what the statement being tracked is doing.

TODO I am treating unknown variables as a special case that is the same

value e.g. 'var' - but for determining equivalent processes it isn't
ideal becuase you loose track of what variables is used in each 
statement.  Although I'll wait unitl I can come up with an example
and come up with a solution then.


652
653
654
655
656
# File 'lib/core/statement/Statement.rb', line 652

def to_literal_string
  return @nodes.inject('') do |complete,part|
    complete += part.to_literal_string
  end
end

#to_var(id = nil, uniq_id = nil) ⇒ Object

Returns the statement as a StatementVariable

Parameters:

  • id (defaults to: nil)

    The id of the variable to give to the returned statement variable.



789
790
791
792
# File 'lib/core/statement/Statement.rb', line 789

def to_var(id=nil,uniq_id=nil)
  var = StatementVariable.new(self.copy)  {{:variable_id => id,:uniq_id=>uniq_id}}
  return var
end

#tokensObject

Returns an array of all the tokens in the statement. A token is any literal, variable, intrinsic runtime or intrinsic testcase



112
113
114
# File 'lib/core/statement/Statement.rb', line 112

def tokens
  return self.select_all([]){|x| x.kind_of?(Token)}
end

#unrealised_variablesObject

Returns each of the variables that aren’t realised. In that they aren’t TypeVariables and don’t have a literal value.



393
394
395
396
397
398
399
400
401
402
403
404
405
# File 'lib/core/statement/Statement.rb', line 393

def unrealised_variables
  results = []  
  variables.each do |x|
    unless x.kind_of?(TypeVariable) 
      results.push(x) 
      next
    end
    if x.value.nil? and !x.kind_of?(NilVariable)
      results.push(x)
    end
  end
  return results
end

#untyped_variablesObject

Returns an array of all the untyped variables in the statement. Excluding any declared variables in the statement.



382
383
384
385
386
387
388
# File 'lib/core/statement/Statement.rb', line 382

def untyped_variables
  x = []
  not_declared_variables.each do |y|  
    x.push(y) unless y.kind_of?(TypeVariable)
  end
  return x
end

#valid_syntax?Boolean

This writes and evalutes the syntax of a statement to determine whether it can be used by it self.

So something like var1 = var0.chop would fail since var0 doesn’t exist. It needs to write to a different class to avoid the situation where the statement ‘return false’ would perceived as invalid syntax.

Returns:



366
367
368
# File 'lib/core/statement/Statement.rb', line 366

def valid_syntax?
  return StatementCheck.new.valid_syntax?(self.write)
end

#variablesObject

Returns an array of variables in this statement



105
106
107
# File 'lib/core/statement/Statement.rb', line 105

def variables
  return self.select_all([]){|x| x.kind_of?(Variable)}
end

#write(tab = 0) ⇒ Object

TODO Maybe use a opject to handle the output of the statement.

Raises:

  • (StandardError)


154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/core/statement/Statement.rb', line 154

def write(tab=0)

  # Check the file type of tab
  raise StandardError.new('Unexpexted class "'+tab.class.to_s+'" for Fixnum (number of tabs)') unless tab.kind_of? Fixnum

  line = ''
  tab.times {line += "\t" }
  self.each do |code|
    line += code.write
    break if code.object_id == self.last.object_id
  end
  return line
end

#write_with_uniq_id(tab = 0) ⇒ Object

Raises:

  • (StandardError)


181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'lib/core/statement/Statement.rb', line 181

def write_with_uniq_id(tab=0)
  # Check the file type of tab
  raise StandardError.new('Unexpexted class "'+tab.class.to_s+'" for Fixnum (number of tabs)') unless tab.kind_of? Fixnum

  line = ''
  tab.times {line += "\t" }
  self.each do |code|
    # TODO  Doesn't need a space on the last code element
    #line += code.write_with_uniq_id+' '
    if code.respond_to?('write_with_uniq_id')
      line += code.write_with_uniq_id+' '
      next
    end
    line += code.write+' '
  end
  return line    
end

#write_with_value(tab = 0) ⇒ Object

NOTE This method should only be used on realised statements

Raises:

  • (StandardError)


200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
# File 'lib/core/statement/Statement.rb', line 200

def write_with_value(tab=0)
  
  # Check the file type of tab
  raise StandardError.new('Unexpexted class "'+tab.class.to_s+'" for Fixnum (number of tabs)') unless tab.kind_of? Fixnum

  line = ''
  tab.times {line += "\t" }
  self.each do |code|

    if code.kind_of?(Variable)

      line += code.write+'('+code.value.write+')'
      # unless code == self.last
        # line += ' '
      # end
      next
    end

    line += code.write
    
  end
  return line    
  
end

#write_with_variable_id(tab = 0, context = self) ⇒ Object

Returns a string containing the written version of the statement but any variables will have there id in brackets after them.

TODO This method is duplicated with requirements



230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
# File 'lib/core/statement/Statement.rb', line 230

def write_with_variable_id(tab=0,context=self)

  line = ''
  tab.times {line += "\t" }    
  self.each do |x|
    if x.kind_of? Variable
      line += x.write+'('+x.variable_id.to_s+') '  
      next
    end
    if x.kind_of? InstanceCallContainer
      line += x.write+'('+x.subject.variable_id.to_s+')'
      next
    end
    line += x.write
  end
  return line
  
end