tp_plus 0.0.73

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +65 -0
  3. data/Rakefile +21 -0
  4. data/bin/tpp +73 -0
  5. data/lib/tp_plus/interpreter.rb +129 -0
  6. data/lib/tp_plus/namespace.rb +66 -0
  7. data/lib/tp_plus/nodes/abort_node.rb +9 -0
  8. data/lib/tp_plus/nodes/argument_node.rb +19 -0
  9. data/lib/tp_plus/nodes/assignment_node.rb +45 -0
  10. data/lib/tp_plus/nodes/call_node.rb +34 -0
  11. data/lib/tp_plus/nodes/case_condition_node.rb +26 -0
  12. data/lib/tp_plus/nodes/case_node.rb +33 -0
  13. data/lib/tp_plus/nodes/comment_node.rb +22 -0
  14. data/lib/tp_plus/nodes/conditional_node.rb +54 -0
  15. data/lib/tp_plus/nodes/definition_node.rb +22 -0
  16. data/lib/tp_plus/nodes/digit_node.rb +21 -0
  17. data/lib/tp_plus/nodes/eval_node.rb +13 -0
  18. data/lib/tp_plus/nodes/expression_node.rb +65 -0
  19. data/lib/tp_plus/nodes/for_node.rb +20 -0
  20. data/lib/tp_plus/nodes/header_node.rb +27 -0
  21. data/lib/tp_plus/nodes/indirect_node.rb +49 -0
  22. data/lib/tp_plus/nodes/inline_conditional_node.rb +40 -0
  23. data/lib/tp_plus/nodes/io_method_node.rb +55 -0
  24. data/lib/tp_plus/nodes/io_node.rb +30 -0
  25. data/lib/tp_plus/nodes/jump_node.rb +23 -0
  26. data/lib/tp_plus/nodes/label_definition_node.rb +21 -0
  27. data/lib/tp_plus/nodes/motion_node.rb +62 -0
  28. data/lib/tp_plus/nodes/namespace_node.rb +16 -0
  29. data/lib/tp_plus/nodes/namespaced_var_node.rb +38 -0
  30. data/lib/tp_plus/nodes/numreg_node.rb +25 -0
  31. data/lib/tp_plus/nodes/offset_node.rb +27 -0
  32. data/lib/tp_plus/nodes/operator_node.rb +72 -0
  33. data/lib/tp_plus/nodes/pause_node.rb +9 -0
  34. data/lib/tp_plus/nodes/position_data_node.rb +50 -0
  35. data/lib/tp_plus/nodes/position_node.rb +25 -0
  36. data/lib/tp_plus/nodes/posreg_node.rb +48 -0
  37. data/lib/tp_plus/nodes/raise_node.rb +13 -0
  38. data/lib/tp_plus/nodes/real_node.rb +27 -0
  39. data/lib/tp_plus/nodes/set_node.rb +22 -0
  40. data/lib/tp_plus/nodes/skip_node.rb +22 -0
  41. data/lib/tp_plus/nodes/speed_node.rb +29 -0
  42. data/lib/tp_plus/nodes/string_node.rb +13 -0
  43. data/lib/tp_plus/nodes/string_register_node.rb +25 -0
  44. data/lib/tp_plus/nodes/termination_node.rb +18 -0
  45. data/lib/tp_plus/nodes/terminator_node.rb +16 -0
  46. data/lib/tp_plus/nodes/time_node.rb +24 -0
  47. data/lib/tp_plus/nodes/timer_method_node.rb +37 -0
  48. data/lib/tp_plus/nodes/timer_node.rb +21 -0
  49. data/lib/tp_plus/nodes/units_node.rb +20 -0
  50. data/lib/tp_plus/nodes/use_node.rb +21 -0
  51. data/lib/tp_plus/nodes/user_alarm_node.rb +15 -0
  52. data/lib/tp_plus/nodes/var_method_node.rb +23 -0
  53. data/lib/tp_plus/nodes/var_node.rb +39 -0
  54. data/lib/tp_plus/nodes/vision_register_node.rb +21 -0
  55. data/lib/tp_plus/nodes/wait_for_node.rb +54 -0
  56. data/lib/tp_plus/nodes/wait_until_node.rb +65 -0
  57. data/lib/tp_plus/nodes/while_node.rb +36 -0
  58. data/lib/tp_plus/parser.rb +1592 -0
  59. data/lib/tp_plus/scanner.rb +383 -0
  60. data/lib/tp_plus/version.rb +3 -0
  61. data/lib/tp_plus.rb +62 -0
  62. data/test/test_helper.rb +5 -0
  63. data/test/tp_plus/test_interpreter.rb +1168 -0
  64. data/test/tp_plus/test_parser.rb +489 -0
  65. data/test/tp_plus/test_scanner.rb +522 -0
  66. data/tp_plus.gemspec +28 -0
  67. metadata +156 -0
@@ -0,0 +1,65 @@
1
+ module TPPlus
2
+ module Nodes
3
+ class ExpressionNode
4
+ attr_reader :left_op, :op, :right_op
5
+ attr_accessor :grouped
6
+ def initialize(left_op, op_string, right_op)
7
+ @left_op = left_op
8
+ @op = OperatorNode.new(op_string)
9
+ @right_op = right_op
10
+ end
11
+
12
+ def requires_mixed_logic?(context)
13
+ contains_expression? ||
14
+ @grouped ||
15
+ [@op, @left_op, @right_op].map { |op|
16
+ next if op.nil?
17
+ op.requires_mixed_logic?(context)
18
+ }.any?
19
+ end
20
+
21
+ def contains_expression?
22
+ [@left_op, @right_op].map {|op| op.is_a? ExpressionNode }.any?
23
+ end
24
+
25
+ def boolean_result?
26
+ case @op.string
27
+ when "&&","||","!","==","<>",">",">=","<","<="
28
+ true
29
+ else
30
+ false
31
+ end
32
+ end
33
+
34
+ def with_parens(string, context, options={})
35
+ return string unless options[:force_parens] || options[:as_condition] && requires_mixed_logic?(context)
36
+
37
+ "(#{string})"
38
+ end
39
+
40
+ def string_val(context, options={})
41
+ if @op.bang?
42
+ # this is for skip conditions, which do not
43
+ # support mixed logic
44
+ if options[:disable_mixed_logic]
45
+ "#{@left_op.eval(context)}=OFF"
46
+ else
47
+ "#{@op.eval(context,options)}#{@left_op.eval(context)}"
48
+ end
49
+ else
50
+ if @op.boolean? && options[:opposite]
51
+ "!#{@left_op.eval(context)}#{@op.eval(context,options)}!#{@right_op.eval(context)}"
52
+ else
53
+ "#{@left_op.eval(context)}#{@op.eval(context,options)}#{@right_op.eval(context)}"
54
+ end
55
+ end
56
+ end
57
+
58
+ def eval(context,options={})
59
+ options[:force_parens] = true if @grouped
60
+
61
+ with_parens(string_val(context, options), context, options)
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,20 @@
1
+ module TPPlus
2
+ module Nodes
3
+ class ForNode
4
+ def initialize(var_node, initial_value_node, final_value_node, block)
5
+ @var_node = var_node
6
+ @initial_value_node = initial_value_node
7
+ @final_value_node = final_value_node
8
+ @block = block.flatten.reject {|n| n.is_a?(TerminatorNode) }
9
+ end
10
+
11
+ def block(context)
12
+ @s ||= @block.inject("") {|s,n| s << "#{n.eval(context)} ;\n" }
13
+ end
14
+
15
+ def eval(context)
16
+ "FOR #{@var_node.eval(context)}=#{@initial_value_node.eval(context)} TO #{@final_value_node.eval(context)} ;\n#{block(context)}ENDFOR"
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,27 @@
1
+ module TPPlus
2
+ module Nodes
3
+ class HeaderNode
4
+ def initialize(type, value)
5
+ @type = type
6
+ @value = value
7
+ end
8
+
9
+ def eval(context, options={})
10
+ case @type
11
+ when "TP_IGNORE_PAUSE"
12
+ context.header_data[:ignore_pause] = @value
13
+ when "TP_COMMENT"
14
+ context.header_data[:comment] = @value
15
+ when "TP_GROUPMASK"
16
+ context.header_data[:group_mask] = @value
17
+ when "TP_SUBTYPE"
18
+ context.header_data[:subtype] = @value.to_sym
19
+ else
20
+ raise "Unsupported TP Header value (#{@type})"
21
+ end
22
+
23
+ nil
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,49 @@
1
+ module TPPlus
2
+ module Nodes
3
+ class IndirectNode
4
+ def initialize(type, target)
5
+ @type = type
6
+ @target = target
7
+ end
8
+
9
+ def string
10
+ case @type
11
+ when :position
12
+ "P"
13
+ when :position_register
14
+ "PR"
15
+ when :flag
16
+ "F"
17
+ when :timer
18
+ "TIMER"
19
+ when :register
20
+ "R"
21
+ when :user_alarm
22
+ "UALM"
23
+ else
24
+ raise "Invalid indirect type"
25
+ end
26
+ end
27
+
28
+ def requires_mixed_logic?(context)
29
+ case @type
30
+ when :flag#, :timer
31
+ true
32
+ else
33
+ false
34
+ end
35
+ end
36
+
37
+ def eval(context,options={})
38
+ s = "#{string}[#{@target.eval(context)}]"
39
+ if options[:opposite]
40
+ s = "!#{s}"
41
+ end
42
+ if options[:as_condition]
43
+ s = "(#{s})"
44
+ end
45
+ s
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,40 @@
1
+ module TPPlus
2
+ module Nodes
3
+ class InlineConditionalNode
4
+ attr_reader :condition_node
5
+ def initialize(type, condition, block)
6
+ @type = type
7
+ @condition_node = condition
8
+ @block = block
9
+ end
10
+
11
+ def condition_requires_mixed_logic?(context)
12
+ @condition_node.is_a?(VarNode) ||
13
+ @condition_node.is_a?(NamespacedVarNode) ||
14
+ @condition_node.requires_mixed_logic?(context)
15
+ end
16
+
17
+ def block_requires_mixed_logic?(context)
18
+ @block.requires_mixed_logic?(context)
19
+ end
20
+
21
+ def can_be_inlined?
22
+ false
23
+ end
24
+
25
+ def condition(context,options={})
26
+ options[:opposite] ||= @type == "unless"
27
+
28
+ if condition_requires_mixed_logic?(context) || block_requires_mixed_logic?(context)
29
+ "(#{@condition_node.eval(context, options)})"
30
+ else
31
+ @condition_node.eval(context, options)
32
+ end
33
+ end
34
+
35
+ def eval(context)
36
+ "IF #{condition(context)},#{@block.eval(context,mixed_logic:true)}"
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,55 @@
1
+ module TPPlus
2
+ module Nodes
3
+ class IOMethodNode
4
+ def initialize(method, target,options={})
5
+ @method = method
6
+ @target = target
7
+ @init_options = options
8
+ end
9
+
10
+ def requires_mixed_logic?(context)
11
+ true
12
+ end
13
+
14
+ def can_be_inlined?
15
+ true
16
+ end
17
+
18
+ def on_off(value,options={})
19
+ options[:mixed_logic] ? "(#{value})" : value
20
+ end
21
+
22
+ def pulse_time(context)
23
+ "%.1f" % case @init_options[:pulse_units]
24
+ when "s"
25
+ @init_options[:pulse_time].eval(context)
26
+ when "ms"
27
+ @init_options[:pulse_time].eval(context).to_f / 1000
28
+ else
29
+ raise "Invalid pulse units"
30
+ end
31
+ end
32
+
33
+ def pulse_extra(context)
34
+ return "" if @init_options[:pulse_time].nil?
35
+
36
+ ",#{pulse_time(context)}sec"
37
+ end
38
+
39
+ def eval(context,options={})
40
+ options[:mixed_logic] = true if @target.requires_mixed_logic?(context)
41
+
42
+ case @method
43
+ when "turn_on"
44
+ "#{@target.eval(context)}=#{on_off("ON",options)}"
45
+ when "turn_off"
46
+ "#{@target.eval(context)}=#{on_off("OFF",options)}"
47
+ when "toggle"
48
+ "#{@target.eval(context)}=(!#{@target.eval(context)})"
49
+ when "pulse"
50
+ "#{@target.eval(context)}=PULSE#{pulse_extra(context)}"
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,30 @@
1
+ module TPPlus
2
+ module Nodes
3
+ class IONode
4
+ attr_accessor :comment
5
+ def initialize(type, id)
6
+ @type = type
7
+ @id = id.to_i
8
+ @comment = ""
9
+ end
10
+
11
+ def requires_mixed_logic?(context)
12
+ ["F","SO","SI","DI"].include?(@type) ? true : false
13
+ end
14
+
15
+ def result
16
+ "#{@type}[#{@id}:#{@comment}]"
17
+ end
18
+
19
+ def eval(context, options={})
20
+ s = result
21
+
22
+ if options[:disable_mixed_logic]
23
+ s = "#{s}=ON"
24
+ end
25
+
26
+ options[:force_parens] ? "(#{s})" : s
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,23 @@
1
+ module TPPlus
2
+ module Nodes
3
+ class JumpNode
4
+ def initialize(identifier)
5
+ @identifier = identifier
6
+ end
7
+
8
+ def requires_mixed_logic?(context)
9
+ false
10
+ end
11
+
12
+ def can_be_inlined?
13
+ true
14
+ end
15
+
16
+ def eval(context,options={})
17
+ raise "Label (#{@identifier}) not found" if context.labels[@identifier.to_sym].nil?
18
+
19
+ "JMP LBL[#{context.labels[@identifier.to_sym]}]"
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,21 @@
1
+ module TPPlus
2
+ module Nodes
3
+ class LabelDefinitionNode
4
+ attr_reader :identifier
5
+ def initialize(identifier)
6
+ @identifier = identifier
7
+ end
8
+
9
+ def long_identifier_comment(context)
10
+ return "" unless @identifier.length > 16
11
+
12
+ " ;\n! #{@identifier}"
13
+ end
14
+
15
+ def eval(context)
16
+ #context.add_label(@identifier)
17
+ "LBL[#{context.labels[@identifier.to_sym]}:#{@identifier[0,16]}]#{long_identifier_comment(context)}"
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,62 @@
1
+ module TPPlus
2
+ module Nodes
3
+ class MotionNode
4
+ def initialize(type, destination, modifiers)
5
+ @type = type
6
+ @destination = destination
7
+ @modifiers = modifiers
8
+ end
9
+
10
+ def prefix
11
+ case @type
12
+ when "linear_move"
13
+ "L"
14
+ when "joint_move"
15
+ "J"
16
+ when "circular_move"
17
+ "C"
18
+ else
19
+ raise "Unsupported motion"
20
+ end
21
+ end
22
+
23
+ def speed_node
24
+ @speed_node ||= @modifiers.select {|m| m.is_a? SpeedNode }.first
25
+ end
26
+
27
+ def termination_node
28
+ @termination_node ||= @modifiers.select {|m| m.is_a? TerminationNode }.first
29
+ end
30
+
31
+ def actual_modifiers
32
+ @actual_modifiers ||= @modifiers.reject {|m| m.is_a? SpeedNode}.reject {|m| m.is_a? TerminationNode }
33
+ end
34
+
35
+ def modifiers_string(context)
36
+ return "" unless actual_modifiers.any?
37
+
38
+ strings_array = [""] << actual_modifiers.map { |m| m.eval(context) }
39
+ @modifiers_string = strings_array.join(" ")
40
+ end
41
+
42
+ def speed_valid?(context)
43
+ case @type
44
+ when "linear_move"
45
+ return true if speed_node.eval(context) == "max_speed"
46
+
47
+ ["mm/sec"].include? speed_node.units
48
+ when "joint_move"
49
+ return false if speed_node.eval(context) == "max_speed"
50
+
51
+ ["%"].include? speed_node.units
52
+ end
53
+ end
54
+
55
+ def eval(context)
56
+ raise "Speed is invalid for motion type" unless speed_valid?(context)
57
+
58
+ "#{prefix} #{@destination.eval(context)} #{speed_node.eval(context)} #{termination_node.eval(context)}#{modifiers_string(context)}"
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,16 @@
1
+ module TPPlus
2
+ module Nodes
3
+ class NamespaceNode
4
+ attr_reader :block
5
+ def initialize(name, block)
6
+ @name = name
7
+ @block = block
8
+ end
9
+
10
+ def eval(context)
11
+ context.add_namespace(@name, @block)
12
+ nil
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,38 @@
1
+ module TPPlus
2
+ module Nodes
3
+ class NamespacedVarNode
4
+ attr_reader :namespaces
5
+ def initialize(namespaces, var_node)
6
+ @namespaces = namespaces
7
+ @var_node = var_node
8
+ end
9
+
10
+ def namespace(context)
11
+ @context = context
12
+ @namespaces.each do |ns|
13
+ if @context.get_namespace(ns)
14
+ @context = @context.get_namespace(ns)
15
+ end
16
+ end
17
+
18
+ @context
19
+ end
20
+
21
+ def identifier
22
+ @var_node.identifier
23
+ end
24
+
25
+ def target_node(context)
26
+ @var_node.target_node(namespace(context))
27
+ end
28
+
29
+ def requires_mixed_logic?(context)
30
+ @var_node.requires_mixed_logic?(namespace(context))
31
+ end
32
+
33
+ def eval(context,options={})
34
+ @var_node.eval(namespace(context), options)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,25 @@
1
+ module TPPlus
2
+ module Nodes
3
+ class NumregNode
4
+ attr_accessor :comment
5
+ def initialize(id)
6
+ @id = id
7
+ @comment = ""
8
+ end
9
+
10
+ def requires_mixed_logic?(context)
11
+ false
12
+ end
13
+
14
+ def comment_string
15
+ return "" if @comment == ""
16
+
17
+ ":#{@comment}"
18
+ end
19
+
20
+ def eval(context,options={})
21
+ "R[#{@id}#{comment_string}]"
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,27 @@
1
+ module TPPlus
2
+ module Nodes
3
+ class OffsetNode
4
+ def initialize(type, var)
5
+ @type = type
6
+ @var = var
7
+ end
8
+
9
+ def name
10
+ case @type.downcase
11
+ when "offset"
12
+ "Offset"
13
+ when "tool_offset"
14
+ "Tool_Offset"
15
+ when "vision_offset"
16
+ "VOFFSET"
17
+ else
18
+ raise "Invalid type"
19
+ end
20
+ end
21
+
22
+ def eval(context)
23
+ "#{name},#{@var.eval(context)}"
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,72 @@
1
+ module TPPlus
2
+ module Nodes
3
+ class OperatorNode
4
+ attr_reader :string
5
+ def initialize(string)
6
+ @string = string
7
+ end
8
+
9
+ def bang?
10
+ @string == "!"
11
+ end
12
+
13
+ def requires_mixed_logic?(context)
14
+ case @string
15
+ when "&&", "||", "!"
16
+ true
17
+ else
18
+ false
19
+ end
20
+ end
21
+
22
+ def boolean?
23
+ case @string
24
+ when "&&", "||", "!"#, "==", "<>", ">", ">=", "<", "<="
25
+ true
26
+ else
27
+ false
28
+ end
29
+ end
30
+
31
+ def eval(context,options={})
32
+ if options[:opposite]
33
+ case @string
34
+ when "=="
35
+ "<>"
36
+ when "!=", "<>"
37
+ "="
38
+ when ">"
39
+ "<="
40
+ when "<"
41
+ ">="
42
+ when ">="
43
+ "<"
44
+ when "<="
45
+ ">"
46
+ when "!"
47
+ ""
48
+ when "||"
49
+ " AND "
50
+ when "&&"
51
+ " OR "
52
+ end
53
+ else
54
+ case @string
55
+ when "=="
56
+ "="
57
+ when "!="
58
+ "<>"
59
+ when "&&"
60
+ " AND "
61
+ when "||"
62
+ " OR "
63
+ when "%"
64
+ " MOD "
65
+ else
66
+ "#{@string}"
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,9 @@
1
+ module TPPlus
2
+ module Nodes
3
+ class PauseNode
4
+ def eval(context)
5
+ "PAUSE"
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,50 @@
1
+ module TPPlus
2
+ module Nodes
3
+ class PositionDataNode
4
+ attr_reader :hash
5
+ def initialize(hash)
6
+ @hash = hash
7
+ @ids = []
8
+ end
9
+
10
+ def valid?
11
+ return false unless @hash[:positions].is_a?(Array)
12
+ return false if @hash[:positions].map {|p| position_valid?(p) == false }.any?
13
+
14
+ true
15
+ end
16
+
17
+ def position_valid?(position_hash)
18
+ return false if @ids.include?(position_hash[:id])
19
+ @ids.push(position_hash[:id])
20
+
21
+ return false unless position_hash[:group].is_a?(Fixnum)
22
+ return false unless position_hash[:uframe].is_a?(Fixnum)
23
+ return false unless position_hash[:utool].is_a?(Fixnum)
24
+ return false unless position_hash[:config].is_a?(Hash)
25
+ return false unless boolean?(position_hash[:config][:flip])
26
+ return false unless boolean?(position_hash[:config][:up])
27
+ return false unless boolean?(position_hash[:config][:top])
28
+ return false unless position_hash[:config][:turn_counts].is_a?(Array)
29
+ return false unless position_hash[:config][:turn_counts].length == 3
30
+ return false if position_hash[:config][:turn_counts].map {|tc| tc.is_a?(Fixnum) == false }.any?
31
+ return false unless position_hash[:components].is_a?(Hash)
32
+ [:x,:y,:z,:w,:p,:r].each do |component|
33
+ return false unless position_hash[:components][component].is_a?(Float)
34
+ end
35
+
36
+ true
37
+ end
38
+
39
+ def boolean?(thing)
40
+ thing.is_a?(TrueClass) || thing.is_a?(FalseClass)
41
+ end
42
+
43
+ def eval(context, options={})
44
+ raise "Invalid position data" unless valid?
45
+ context.position_data = @hash
46
+ nil
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,25 @@
1
+ module TPPlus
2
+ module Nodes
3
+ class PositionNode
4
+ attr_accessor :comment
5
+ def initialize(id)
6
+ @id = id
7
+ @comment = ""
8
+ end
9
+
10
+ def requires_mixed_logic?(context)
11
+ false
12
+ end
13
+
14
+ def comment_string
15
+ return "" if @comment == ""
16
+
17
+ ":#{@comment}"
18
+ end
19
+
20
+ def eval(context,options={})
21
+ "P[#{@id}#{comment_string}]"
22
+ end
23
+ end
24
+ end
25
+ end