tp_plus 0.0.77 → 0.0.87

Sign up to get free protection for your applications and to get access to all the features.
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