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.
- checksums.yaml +4 -4
- data/README.md +87 -84
- data/Rakefile +35 -35
- data/bin/tpp +92 -92
- data/lib/tp_plus/interpreter.rb +152 -152
- data/lib/tp_plus/namespace.rb +66 -66
- data/lib/tp_plus/nodes/abort_node.rb +9 -9
- data/lib/tp_plus/nodes/acc_node.rb +19 -19
- data/lib/tp_plus/nodes/address_node.rb +22 -22
- data/lib/tp_plus/nodes/argument_node.rb +20 -20
- data/lib/tp_plus/nodes/assignment_node.rb +52 -45
- data/lib/tp_plus/nodes/base_node.rb +9 -0
- data/lib/tp_plus/nodes/call_node.rb +34 -34
- data/lib/tp_plus/nodes/case_condition_node.rb +26 -26
- data/lib/tp_plus/nodes/case_node.rb +33 -33
- data/lib/tp_plus/nodes/comment_node.rb +18 -22
- data/lib/tp_plus/nodes/conditional_node.rb +60 -60
- data/lib/tp_plus/nodes/definition_node.rb +22 -22
- data/lib/tp_plus/nodes/digit_node.rb +22 -22
- data/lib/tp_plus/nodes/empty_stmt_node.rb +9 -9
- data/lib/tp_plus/nodes/eval_node.rb +13 -13
- data/lib/tp_plus/nodes/expression_node.rb +68 -68
- data/lib/tp_plus/nodes/for_node.rb +20 -20
- data/lib/tp_plus/nodes/header_node.rb +27 -27
- data/lib/tp_plus/nodes/indirect_node.rb +27 -51
- data/lib/tp_plus/nodes/inline_conditional_node.rb +36 -40
- data/lib/tp_plus/nodes/io_method_node.rb +55 -55
- data/lib/tp_plus/nodes/io_node.rb +31 -31
- data/lib/tp_plus/nodes/jpos_node.rb +13 -0
- data/lib/tp_plus/nodes/jump_node.rb +23 -23
- data/lib/tp_plus/nodes/label_definition_node.rb +21 -21
- data/lib/tp_plus/nodes/lpos_node.rb +13 -0
- data/lib/tp_plus/nodes/motion_node.rb +62 -62
- data/lib/tp_plus/nodes/namespace_node.rb +16 -16
- data/lib/tp_plus/nodes/namespaced_var_node.rb +38 -38
- data/lib/tp_plus/nodes/numreg_node.rb +26 -26
- data/lib/tp_plus/nodes/offset_node.rb +27 -27
- data/lib/tp_plus/nodes/operator_node.rb +80 -78
- data/lib/tp_plus/nodes/paren_expression_node.rb +17 -17
- data/lib/tp_plus/nodes/pause_node.rb +9 -9
- data/lib/tp_plus/nodes/position_data_node.rb +66 -66
- data/lib/tp_plus/nodes/position_node.rb +26 -26
- data/lib/tp_plus/nodes/posreg_node.rb +64 -64
- data/lib/tp_plus/nodes/raise_node.rb +13 -13
- data/lib/tp_plus/nodes/real_node.rb +27 -27
- data/lib/tp_plus/nodes/return_node.rb +9 -0
- data/lib/tp_plus/nodes/set_skip_node.rb +14 -14
- data/lib/tp_plus/nodes/skip_node.rb +22 -22
- data/lib/tp_plus/nodes/speed_node.rb +29 -29
- data/lib/tp_plus/nodes/string_node.rb +13 -13
- data/lib/tp_plus/nodes/string_register_node.rb +26 -26
- data/lib/tp_plus/nodes/termination_node.rb +23 -23
- data/lib/tp_plus/nodes/terminator_node.rb +16 -16
- data/lib/tp_plus/nodes/time_node.rb +24 -24
- data/lib/tp_plus/nodes/timer_method_node.rb +33 -37
- data/lib/tp_plus/nodes/timer_node.rb +16 -16
- data/lib/tp_plus/nodes/unary_expression_node.rb +39 -0
- data/lib/tp_plus/nodes/units_node.rb +20 -20
- data/lib/tp_plus/nodes/use_node.rb +21 -21
- data/lib/tp_plus/nodes/user_alarm_node.rb +16 -16
- data/lib/tp_plus/nodes/var_method_node.rb +23 -23
- data/lib/tp_plus/nodes/var_node.rb +39 -39
- data/lib/tp_plus/nodes/vision_register_node.rb +22 -22
- data/lib/tp_plus/nodes/wait_for_node.rb +50 -54
- data/lib/tp_plus/nodes/wait_until_node.rb +61 -65
- data/lib/tp_plus/nodes/while_node.rb +40 -42
- data/lib/tp_plus/parser.rb +1749 -1697
- data/lib/tp_plus/scanner.rb +295 -295
- data/lib/tp_plus/token.rb +101 -98
- data/lib/tp_plus/version.rb +3 -3
- data/lib/tp_plus.rb +72 -67
- data/test/test_helper.rb +5 -5
- data/test/tp_plus/test_interpreter.rb +1372 -1329
- data/test/tp_plus/test_parser.rb +502 -502
- data/test/tp_plus/test_scanner.rb +591 -577
- data/tp_plus.gemspec +31 -31
- metadata +8 -3
@@ -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
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
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
|