tp_plus 0.0.77 → 0.0.87

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +87 -84
  3. data/Rakefile +35 -35
  4. data/bin/tpp +92 -92
  5. data/lib/tp_plus/interpreter.rb +152 -152
  6. data/lib/tp_plus/namespace.rb +66 -66
  7. data/lib/tp_plus/nodes/abort_node.rb +9 -9
  8. data/lib/tp_plus/nodes/acc_node.rb +19 -19
  9. data/lib/tp_plus/nodes/address_node.rb +22 -22
  10. data/lib/tp_plus/nodes/argument_node.rb +20 -20
  11. data/lib/tp_plus/nodes/assignment_node.rb +52 -45
  12. data/lib/tp_plus/nodes/base_node.rb +9 -0
  13. data/lib/tp_plus/nodes/call_node.rb +34 -34
  14. data/lib/tp_plus/nodes/case_condition_node.rb +26 -26
  15. data/lib/tp_plus/nodes/case_node.rb +33 -33
  16. data/lib/tp_plus/nodes/comment_node.rb +18 -22
  17. data/lib/tp_plus/nodes/conditional_node.rb +60 -60
  18. data/lib/tp_plus/nodes/definition_node.rb +22 -22
  19. data/lib/tp_plus/nodes/digit_node.rb +22 -22
  20. data/lib/tp_plus/nodes/empty_stmt_node.rb +9 -9
  21. data/lib/tp_plus/nodes/eval_node.rb +13 -13
  22. data/lib/tp_plus/nodes/expression_node.rb +68 -68
  23. data/lib/tp_plus/nodes/for_node.rb +20 -20
  24. data/lib/tp_plus/nodes/header_node.rb +27 -27
  25. data/lib/tp_plus/nodes/indirect_node.rb +27 -51
  26. data/lib/tp_plus/nodes/inline_conditional_node.rb +36 -40
  27. data/lib/tp_plus/nodes/io_method_node.rb +55 -55
  28. data/lib/tp_plus/nodes/io_node.rb +31 -31
  29. data/lib/tp_plus/nodes/jpos_node.rb +13 -0
  30. data/lib/tp_plus/nodes/jump_node.rb +23 -23
  31. data/lib/tp_plus/nodes/label_definition_node.rb +21 -21
  32. data/lib/tp_plus/nodes/lpos_node.rb +13 -0
  33. data/lib/tp_plus/nodes/motion_node.rb +62 -62
  34. data/lib/tp_plus/nodes/namespace_node.rb +16 -16
  35. data/lib/tp_plus/nodes/namespaced_var_node.rb +38 -38
  36. data/lib/tp_plus/nodes/numreg_node.rb +26 -26
  37. data/lib/tp_plus/nodes/offset_node.rb +27 -27
  38. data/lib/tp_plus/nodes/operator_node.rb +80 -78
  39. data/lib/tp_plus/nodes/paren_expression_node.rb +17 -17
  40. data/lib/tp_plus/nodes/pause_node.rb +9 -9
  41. data/lib/tp_plus/nodes/position_data_node.rb +66 -66
  42. data/lib/tp_plus/nodes/position_node.rb +26 -26
  43. data/lib/tp_plus/nodes/posreg_node.rb +64 -64
  44. data/lib/tp_plus/nodes/raise_node.rb +13 -13
  45. data/lib/tp_plus/nodes/real_node.rb +27 -27
  46. data/lib/tp_plus/nodes/return_node.rb +9 -0
  47. data/lib/tp_plus/nodes/set_skip_node.rb +14 -14
  48. data/lib/tp_plus/nodes/skip_node.rb +22 -22
  49. data/lib/tp_plus/nodes/speed_node.rb +29 -29
  50. data/lib/tp_plus/nodes/string_node.rb +13 -13
  51. data/lib/tp_plus/nodes/string_register_node.rb +26 -26
  52. data/lib/tp_plus/nodes/termination_node.rb +23 -23
  53. data/lib/tp_plus/nodes/terminator_node.rb +16 -16
  54. data/lib/tp_plus/nodes/time_node.rb +24 -24
  55. data/lib/tp_plus/nodes/timer_method_node.rb +33 -37
  56. data/lib/tp_plus/nodes/timer_node.rb +16 -16
  57. data/lib/tp_plus/nodes/unary_expression_node.rb +39 -0
  58. data/lib/tp_plus/nodes/units_node.rb +20 -20
  59. data/lib/tp_plus/nodes/use_node.rb +21 -21
  60. data/lib/tp_plus/nodes/user_alarm_node.rb +16 -16
  61. data/lib/tp_plus/nodes/var_method_node.rb +23 -23
  62. data/lib/tp_plus/nodes/var_node.rb +39 -39
  63. data/lib/tp_plus/nodes/vision_register_node.rb +22 -22
  64. data/lib/tp_plus/nodes/wait_for_node.rb +50 -54
  65. data/lib/tp_plus/nodes/wait_until_node.rb +61 -65
  66. data/lib/tp_plus/nodes/while_node.rb +40 -42
  67. data/lib/tp_plus/parser.rb +1749 -1697
  68. data/lib/tp_plus/scanner.rb +295 -295
  69. data/lib/tp_plus/token.rb +101 -98
  70. data/lib/tp_plus/version.rb +3 -3
  71. data/lib/tp_plus.rb +72 -67
  72. data/test/test_helper.rb +5 -5
  73. data/test/tp_plus/test_interpreter.rb +1372 -1329
  74. data/test/tp_plus/test_parser.rb +502 -502
  75. data/test/tp_plus/test_scanner.rb +591 -577
  76. data/tp_plus.gemspec +31 -31
  77. metadata +8 -3
@@ -0,0 +1,9 @@
1
+ module TPPlus
2
+ module Nodes
3
+ class BaseNode
4
+ def can_be_inlined?
5
+ false
6
+ end
7
+ end
8
+ end
9
+ end
@@ -1,34 +1,34 @@
1
- module TPPlus
2
- module Nodes
3
- class CallNode
4
- attr_reader :args
5
- def initialize(program_name, args, options={})
6
- @program_name = program_name
7
- @args = args
8
- @async = options[:async]
9
- end
10
-
11
- def requires_mixed_logic?(context)
12
- false
13
- end
14
-
15
- def async?
16
- @async
17
- end
18
-
19
- def args_string(context)
20
- return "" unless @args.any?
21
-
22
- "(" + @args.map {|a| a.eval(context) }.join(",") + ")"
23
- end
24
-
25
- def can_be_inlined?
26
- true
27
- end
28
-
29
- def eval(context,options={})
30
- "#{async? ? "RUN" : "CALL"} #{@program_name.upcase}#{args_string(context)}"
31
- end
32
- end
33
- end
34
- end
1
+ module TPPlus
2
+ module Nodes
3
+ class CallNode < BaseNode
4
+ attr_reader :args
5
+ def initialize(program_name, args, options={})
6
+ @program_name = program_name
7
+ @args = args
8
+ @async = options[:async]
9
+ end
10
+
11
+ def requires_mixed_logic?(context)
12
+ false
13
+ end
14
+
15
+ def async?
16
+ @async
17
+ end
18
+
19
+ def args_string(context)
20
+ return "" unless @args.any?
21
+
22
+ "(" + @args.map {|a| a.eval(context) }.join(",") + ")"
23
+ end
24
+
25
+ def can_be_inlined?
26
+ true
27
+ end
28
+
29
+ def eval(context,options={})
30
+ "#{async? ? "RUN" : "CALL"} #{@program_name.upcase}#{args_string(context)}"
31
+ end
32
+ end
33
+ end
34
+ end
@@ -1,26 +1,26 @@
1
- module TPPlus
2
- module Nodes
3
- class CaseConditionNode
4
- def initialize(condition, block)
5
- @condition = condition
6
- @block = block
7
- end
8
-
9
- def eval(context, options={})
10
- options[:no_indent] ||= false
11
-
12
- s = ""
13
- if !options[:no_indent]
14
- s += " "
15
- end
16
-
17
- if @condition
18
- s += "=#{@condition.eval(context)},#{@block.eval(context)}"
19
- else
20
- s += "ELSE,#{@block.eval(context)}"
21
- end
22
- s
23
- end
24
- end
25
- end
26
- end
1
+ module TPPlus
2
+ module Nodes
3
+ class CaseConditionNode < BaseNode
4
+ def initialize(condition, block)
5
+ @condition = condition
6
+ @block = block
7
+ end
8
+
9
+ def eval(context, options={})
10
+ options[:no_indent] ||= false
11
+
12
+ s = ""
13
+ if !options[:no_indent]
14
+ s += " "
15
+ end
16
+
17
+ if @condition
18
+ s += "=#{@condition.eval(context)},#{@block.eval(context)}"
19
+ else
20
+ s += "ELSE,#{@block.eval(context)}"
21
+ end
22
+ s
23
+ end
24
+ end
25
+ end
26
+ end
@@ -1,33 +1,33 @@
1
- module TPPlus
2
- module Nodes
3
- class CaseNode
4
- def initialize(var, conditions, else_condition)
5
- @var = var
6
- @conditions = conditions
7
- @else_condition = else_condition
8
- end
9
-
10
- def else_condition(context)
11
- return "" if @else_condition.nil?
12
-
13
- " ;\n#{@else_condition.eval(context)}"
14
- end
15
-
16
- def other_conditions(context)
17
- return "" if @conditions.empty?
18
-
19
- s = " ;\n"
20
- @conditions.reject! {|c| c.nil? }.each do |c|
21
- s += c.eval(context)
22
- s += " ;\n" unless c == @conditions.last
23
- end
24
-
25
- s
26
- end
27
-
28
- def eval(context)
29
- "SELECT #{@var.eval(context)}#{@conditions.shift.eval(context, no_indent: true)}#{other_conditions(context)}#{else_condition(context)}"
30
- end
31
- end
32
- end
33
- end
1
+ module TPPlus
2
+ module Nodes
3
+ class CaseNode < BaseNode
4
+ def initialize(var, conditions, else_condition)
5
+ @var = var
6
+ @conditions = conditions
7
+ @else_condition = else_condition
8
+ end
9
+
10
+ def else_condition(context)
11
+ return "" if @else_condition.nil?
12
+
13
+ " ;\n#{@else_condition.eval(context)}"
14
+ end
15
+
16
+ def other_conditions(context)
17
+ return "" if @conditions.empty?
18
+
19
+ s = " ;\n"
20
+ @conditions.reject! {|c| c.nil? }.each do |c|
21
+ s += c.eval(context)
22
+ s += " ;\n" unless c == @conditions.last
23
+ end
24
+
25
+ s
26
+ end
27
+
28
+ def eval(context)
29
+ "SELECT #{@var.eval(context)}#{@conditions.shift.eval(context, no_indent: true)}#{other_conditions(context)}#{else_condition(context)}"
30
+ end
31
+ end
32
+ end
33
+ end
@@ -1,22 +1,18 @@
1
- module TPPlus
2
- module Nodes
3
- class CommentNode
4
- def initialize(text)
5
- @text = text[1,text.length]
6
- end
7
-
8
- def can_be_inlined?
9
- false
10
- end
11
-
12
- def eval(context)
13
- s = ""
14
- width = 29
15
- @text.scan(/\S.{0,#{width}}\S(?=\s|$)|\S+/).each do |piece|
16
- s += "! #{piece} ;\n"
17
- end
18
- s[0,s.length-3]
19
- end
20
- end
21
- end
22
- end
1
+ module TPPlus
2
+ module Nodes
3
+ class CommentNode < BaseNode
4
+ def initialize(text)
5
+ @text = text[1,text.length]
6
+ end
7
+
8
+ def eval(context)
9
+ s = ""
10
+ width = 29
11
+ @text.scan(/\S.{0,#{width}}\S(?=\s|$)|\S+/).each do |piece|
12
+ s += "! #{piece} ;\n"
13
+ end
14
+ s[0,s.length-3]
15
+ end
16
+ end
17
+ end
18
+ end
@@ -1,60 +1,60 @@
1
- module TPPlus
2
- module Nodes
3
- class ConditionalNode
4
- def initialize(type,condition,true_block,false_block)
5
- @type = type
6
- @condition = condition
7
- @true_block = true_block.flatten.reject {|n| n.is_a? TerminatorNode }
8
- @false_block = false_block.flatten.reject {|n| n.is_a? TerminatorNode }
9
- end
10
-
11
- def true_label(context)
12
- @true_label ||= context.next_label
13
- end
14
-
15
- def end_label(context)
16
- @end_label ||= context.next_label
17
- end
18
-
19
- def true_block(context)
20
- @t ||= string_for(@true_block,context)
21
- end
22
-
23
- def false_block(context)
24
- @f ||= string_for(@false_block,context)
25
- end
26
-
27
- def string_for(block,context)
28
- block.inject("") {|s,n| s << "#{n.eval(context)} ;\n" }
29
- end
30
-
31
- def can_be_inlined?
32
- return false unless @false_block.empty?
33
- return false unless @true_block.length == 1
34
-
35
- @true_block.first.can_be_inlined?
36
- end
37
-
38
- def opposite?
39
- @type == "if"
40
- end
41
-
42
- def parens(s, context)
43
- return s unless @condition.requires_mixed_logic?(context) || !@condition.is_a?(ExpressionNode)
44
-
45
- "(#{s})"
46
- end
47
-
48
- def eval(context, options={})
49
- return InlineConditionalNode.new(@type,@condition,@true_block.first).eval(context) if can_be_inlined?
50
-
51
- if @false_block.empty?
52
- "IF #{parens(@condition.eval(context,opposite: opposite?), context)},JMP LBL[#{true_label(context)}] ;\n#{true_block(context)}LBL[#{true_label(context)}]"
53
- else
54
- # could be if-else or unless-else
55
- "IF #{parens(@condition.eval(context,opposite: opposite?), context)},JMP LBL[#{true_label(context)}] ;\n#{true_block(context)}JMP LBL[#{end_label(context)}] ;\nLBL[#{true_label(context)}] ;\n#{false_block(context)}LBL[#{end_label(context)}]"
56
- end
57
- end
58
- end
59
- end
60
- end
1
+ module TPPlus
2
+ module Nodes
3
+ class ConditionalNode < BaseNode
4
+ def initialize(type,condition,true_block,false_block)
5
+ @type = type
6
+ @condition = condition
7
+ @true_block = true_block.flatten.reject {|n| n.is_a? TerminatorNode }
8
+ @false_block = false_block.flatten.reject {|n| n.is_a? TerminatorNode }
9
+ end
10
+
11
+ def true_label(context)
12
+ @true_label ||= context.next_label
13
+ end
14
+
15
+ def end_label(context)
16
+ @end_label ||= context.next_label
17
+ end
18
+
19
+ def true_block(context)
20
+ @t ||= string_for(@true_block,context)
21
+ end
22
+
23
+ def false_block(context)
24
+ @f ||= string_for(@false_block,context)
25
+ end
26
+
27
+ def string_for(block,context)
28
+ block.inject("") {|s,n| s << "#{n.eval(context)} ;\n" }
29
+ end
30
+
31
+ def can_be_inlined?
32
+ return false unless @false_block.empty?
33
+ return false unless @true_block.length == 1
34
+
35
+ @true_block.first.can_be_inlined?
36
+ end
37
+
38
+ def opposite?
39
+ @type == "if"
40
+ end
41
+
42
+ def parens(s, context)
43
+ return s unless @condition.requires_mixed_logic?(context) || !@condition.is_a?(ExpressionNode)
44
+
45
+ "(#{s})"
46
+ end
47
+
48
+ def eval(context, options={})
49
+ return InlineConditionalNode.new(@type,@condition,@true_block.first).eval(context) if can_be_inlined?
50
+
51
+ if @false_block.empty?
52
+ "IF #{parens(@condition.eval(context,opposite: opposite?), context)},JMP LBL[#{true_label(context)}] ;\n#{true_block(context)}LBL[#{true_label(context)}]"
53
+ else
54
+ # could be if-else or unless-else
55
+ "IF #{parens(@condition.eval(context,opposite: opposite?), context)},JMP LBL[#{true_label(context)}] ;\n#{true_block(context)}JMP LBL[#{end_label(context)}] ;\nLBL[#{true_label(context)}] ;\n#{false_block(context)}LBL[#{end_label(context)}]"
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -1,22 +1,22 @@
1
- module TPPlus
2
- module Nodes
3
- class DefinitionNode
4
- attr_reader :identifier, :assignable
5
- def initialize(identifier,assignable)
6
- @identifier = identifier
7
- @assignable = assignable
8
- end
9
-
10
- def eval(context)
11
- if @assignable.is_a?(DigitNode) || @assignable.is_a?(RealNode)
12
- raise "Constants must be defined with all CAPS" unless @identifier.upcase == @identifier
13
-
14
- context.add_constant(@identifier, @assignable)
15
- else
16
- context.add_var(@identifier, @assignable)
17
- end
18
- nil
19
- end
20
- end
21
- end
22
- end
1
+ module TPPlus
2
+ module Nodes
3
+ class DefinitionNode < BaseNode
4
+ attr_reader :identifier, :assignable
5
+ def initialize(identifier,assignable)
6
+ @identifier = identifier
7
+ @assignable = assignable
8
+ end
9
+
10
+ def eval(context)
11
+ if @assignable.is_a?(DigitNode) || @assignable.is_a?(RealNode)
12
+ raise "Constants must be defined with all CAPS" unless @identifier.upcase == @identifier
13
+
14
+ context.add_constant(@identifier, @assignable)
15
+ else
16
+ context.add_var(@identifier, @assignable)
17
+ end
18
+ nil
19
+ end
20
+ end
21
+ end
22
+ end
@@ -1,22 +1,22 @@
1
- module TPPlus
2
- module Nodes
3
- class DigitNode
4
- attr_reader :value
5
- def initialize(value)
6
- @value = value
7
- end
8
-
9
- def requires_mixed_logic?(context)
10
- false
11
- end
12
-
13
- def eval(context, options={})
14
- if @value < 0
15
- "(#{@value})"
16
- else
17
- @value
18
- end
19
- end
20
- end
21
- end
22
- end
1
+ module TPPlus
2
+ module Nodes
3
+ class DigitNode < BaseNode
4
+ attr_reader :value
5
+ def initialize(value)
6
+ @value = value
7
+ end
8
+
9
+ def requires_mixed_logic?(context)
10
+ false
11
+ end
12
+
13
+ def eval(context, options={})
14
+ if @value < 0
15
+ "(#{@value})"
16
+ else
17
+ @value
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -1,9 +1,9 @@
1
- module TPPlus
2
- module Nodes
3
- class EmptyStmtNode
4
- def eval(context, options={})
5
- ""
6
- end
7
- end
8
- end
9
- end
1
+ module TPPlus
2
+ module Nodes
3
+ class EmptyStmtNode < BaseNode
4
+ def eval(context, options={})
5
+ ""
6
+ end
7
+ end
8
+ end
9
+ end
@@ -1,13 +1,13 @@
1
- module TPPlus
2
- module Nodes
3
- class EvalNode
4
- def initialize(string)
5
- @string = string
6
- end
7
-
8
- def eval(context)
9
- @string
10
- end
11
- end
12
- end
13
- end
1
+ module TPPlus
2
+ module Nodes
3
+ class EvalNode < BaseNode
4
+ def initialize(string)
5
+ @string = string
6
+ end
7
+
8
+ def eval(context)
9
+ @string
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,68 +1,68 @@
1
- module TPPlus
2
- module Nodes
3
- class ExpressionNode
4
- attr_reader :left_op, :op, :right_op
5
- def initialize(left_op, op_string, right_op)
6
- @left_op = left_op
7
- @op = OperatorNode.new(op_string)
8
- @right_op = right_op
9
- end
10
-
11
- def grouped?
12
- return false if @right_op.nil? # this is for NOT (!) operator
13
-
14
- @left_op.is_a?(ParenExpressionNode) || @right_op.is_a?(ParenExpressionNode)
15
- end
16
-
17
- def requires_mixed_logic?(context)
18
- contains_expression? ||
19
- grouped? ||
20
- [@op, @left_op, @right_op].map { |op|
21
- next if op.nil?
22
- op.requires_mixed_logic?(context)
23
- }.any?
24
- end
25
-
26
- def contains_expression?
27
- [@left_op, @right_op].map {|op| op.is_a? ExpressionNode }.any? ||
28
- [@left_op, @right_op].map { |op| op.is_a? ParenExpressionNode }.any?
29
- end
30
-
31
- def boolean_result?
32
- case @op.string
33
- when "&&","||","!","==","<>",">",">=","<","<="
34
- true
35
- else
36
- false
37
- end
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?
51
- # if operator is &&, || or !, we flip the operator and the operands
52
- "#{@left_op.eval(context,options)}#{@op.eval(context, options)}#{@right_op.eval(context, options)}"
53
- else
54
- # flip the operator if options[:opposite]
55
- # flip the operands only if opposite and the operand is an expression
56
- "#{@left_op.eval(context, opposite: (@left_op.is_a?(ExpressionNode) && options[:opposite]))}#{@op.eval(context, options)}#{@right_op.eval(context, opposite: (@right_op.is_a?(ExpressionNode) && options[:opposite]))}"
57
- end
58
- end
59
- end
60
-
61
- def eval(context,options={})
62
- options[:force_parens] = true if grouped?
63
-
64
- string_val(context, options)
65
- end
66
- end
67
- end
68
- end
1
+ module TPPlus
2
+ module Nodes
3
+ class ExpressionNode < BaseNode
4
+ attr_reader :left_op, :op, :right_op
5
+ def initialize(left_op, op_string, right_op)
6
+ @left_op = left_op
7
+ @op = OperatorNode.new(op_string)
8
+ @right_op = right_op
9
+ end
10
+
11
+ def grouped?
12
+ return false if @right_op.nil? # this is for NOT (!) operator
13
+
14
+ @left_op.is_a?(ParenExpressionNode) || @right_op.is_a?(ParenExpressionNode)
15
+ end
16
+
17
+ def requires_mixed_logic?(context)
18
+ contains_expression? ||
19
+ grouped? ||
20
+ [@op, @left_op, @right_op].map { |op|
21
+ next if op.nil?
22
+ op.requires_mixed_logic?(context)
23
+ }.any?
24
+ end
25
+
26
+ def contains_expression?
27
+ [@left_op, @right_op].map {|op| op.is_a? ExpressionNode }.any? ||
28
+ [@left_op, @right_op].map { |op| op.is_a? ParenExpressionNode }.any?
29
+ end
30
+
31
+ def boolean_result?
32
+ case @op.string
33
+ when "&&","||","!","==","<>",">",">=","<","<="
34
+ true
35
+ else
36
+ false
37
+ end
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?
51
+ # if operator is &&, || or !, we flip the operator and the operands
52
+ "#{@left_op.eval(context,options)}#{@op.eval(context, options)}#{@right_op.eval(context, options)}"
53
+ else
54
+ # flip the operator if options[:opposite]
55
+ # flip the operands only if opposite and the operand is an expression
56
+ "#{@left_op.eval(context, opposite: (@left_op.is_a?(ExpressionNode) && options[:opposite]))}#{@op.eval(context, options)}#{@right_op.eval(context, opposite: (@right_op.is_a?(ExpressionNode) && options[:opposite]))}"
57
+ end
58
+ end
59
+ end
60
+
61
+ def eval(context,options={})
62
+ options[:force_parens] = true if grouped?
63
+
64
+ string_val(context, options)
65
+ end
66
+ end
67
+ end
68
+ end