tp_plus 0.0.73

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 (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
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 67da7b5806591383225c3d09717b6df2951a3c39
4
+ data.tar.gz: d11d8ac4716e5cccfc17b9913d587b2209a3438f
5
+ SHA512:
6
+ metadata.gz: 82ef1ac3fbfa26321b17e679613c69b7bb43ccb5997f927fe2c8a87677600996a93e3f42708b2f997aaab7e83caefbc6e8ddbc82ef786c1a6209d928387fc9ef
7
+ data.tar.gz: fc6b978abb2f2952d3622f165549b0c7e700c0e60b78ebc2b6496201216f7a666cf62835be5ca9605692a5e7fb98488a957382ac5fdf9fe6086bf7592d62bd02
data/README.md ADDED
@@ -0,0 +1,65 @@
1
+ TP+
2
+ ===
3
+
4
+ TP+ is a higher-level language abstraction that translates into FANUC
5
+ TP. It features many useful utilities that makes creating TP programs
6
+ easier:
7
+
8
+ * Identifiers for registers, position registers, IO, etc.
9
+ * Re-usable methods
10
+ * If-else blocks
11
+ * Readable motion statements
12
+ * Automatic label numbering
13
+
14
+ Of course adding another layer of abstraction takes you a step further
15
+ away from the code the robot is running. However, it's hoped that the
16
+ increased productivity and rigid syntax requirements will actually
17
+ improve your TP code.
18
+
19
+ Examples
20
+ --------
21
+
22
+ example_1.tpp
23
+
24
+ foo := R[1]
25
+
26
+ bar := DO[1]
27
+ baz := DO[2]
28
+
29
+ home := PR[1]
30
+
31
+
32
+ foo = 1
33
+
34
+ @loop
35
+ foo += 1
36
+
37
+ jump_to @foo if foo < 10
38
+
39
+ turn_on bar if foo == 5
40
+ toggle baz
41
+
42
+ linear_move.to(home).at(2000mm/s).term(0)
43
+
44
+
45
+ example_1.ls
46
+
47
+ /PROG example_1
48
+ /MN
49
+ 1: R[1:foo] = 1 ;
50
+ 1: ;
51
+ 1: LBL[1:loop] ;
52
+ 1: R[1:foo]=R[1:foo]+1 ;
53
+ 1: IF R[1:foo]<10,JMP LBL[1] ;
54
+ 1: ;
55
+ 1: IF (R[1:foo]=5),DO[1:bar]=(ON) ;
56
+ 1: DO[2:baz]=(!DO[2:baz]) ;
57
+ 1: ;
58
+ 1: L PR[1:home] 2000mm/sec CNT0 ;
59
+ /END
60
+
61
+
62
+ License
63
+ -------
64
+
65
+ TP+ is released under the [MIT License](http://www.opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,21 @@
1
+ namespace :compile do
2
+ task :parser do
3
+ sh "racc -l -o lib/tp_plus/parser.rb generators/parser.y"
4
+ end
5
+
6
+ task :scanner do
7
+ sh "rex generators/scanner.rex -o lib/tp_plus/scanner.rb --stub"
8
+ end
9
+ end
10
+
11
+ task compile: ["compile:parser","compile:scanner"]
12
+
13
+ require 'rake/testtask'
14
+
15
+ Rake::TestTask.new do |t|
16
+ t.libs << "test"
17
+ t.test_files = FileList['test/**/test_*.rb']
18
+ t.verbose = true
19
+ end
20
+
21
+ task default: :test
data/bin/tpp ADDED
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env ruby
2
+ require 'tp_plus'
3
+ require 'optparse'
4
+
5
+ options = {}
6
+ OptionParser.new do |opts|
7
+ opts.banner = "Usage: tpp [options] filename"
8
+
9
+ opts.on("-e", "--environment env_file.tpp", "Require an environment file before parsing files") do |e|
10
+ options[:environment] = e
11
+ end
12
+
13
+ opts.on_tail("-h", "--help", "Show this message") do
14
+ puts opts
15
+ exit
16
+ end
17
+ end.parse!
18
+
19
+ def contents(filename)
20
+ if !File.exist?(filename)
21
+ puts "File (#{filename}) does not exist"
22
+ exit
23
+ end
24
+ f = File.open(filename,'rb')
25
+ src = f.read
26
+ f.close
27
+ return src
28
+ end
29
+
30
+ scanner = TPPlus::Scanner.new
31
+ parser = TPPlus::Parser.new(scanner)
32
+ interpreter = parser.interpreter
33
+ if options[:environment]
34
+ interpreter.load_environment(contents(options[:environment]))
35
+ end
36
+
37
+ if ARGV.length != 1
38
+ puts "Must provide filename argument. See tpp --help for details"
39
+ exit
40
+ end
41
+
42
+ src = contents(ARGV[0])
43
+
44
+ scanner.scan_setup(src)
45
+ parser.parse
46
+
47
+ lines = interpreter.eval
48
+
49
+ tpp_filename = File.basename(ARGV[0],".*")
50
+ output = %(/PROG #{tpp_filename.upcase}
51
+ /ATTR
52
+ COMMENT = "#{interpreter.header_data[:comment] || tpp_filename.upcase}";
53
+ TCD: STACK_SIZE = 0,
54
+ TASK_PRIORITY = 50,
55
+ TIME_SLICE = 0,
56
+ BUSY_LAMP_OFF = 0,
57
+ ABORT_REQUEST = 0,
58
+ PAUSE_REQUEST = #{interpreter.header_data[:ignore_pause] ? "7" : "0"};
59
+ DEFAULT_GROUP = #{interpreter.header_data[:group_mask] || "1,*,*,*,*"};
60
+ /MN\n)
61
+
62
+ lines.each_line do |line|
63
+ output += " : " + line
64
+ end
65
+
66
+ if interpreter.pos_section != ""
67
+ output += "/POS\n"
68
+ output += interpreter.pos_section
69
+ end
70
+
71
+ output += %(/END\n)
72
+
73
+ print output
@@ -0,0 +1,129 @@
1
+ require 'tp_plus/parser'
2
+
3
+ module TPPlus
4
+ class Interpreter
5
+ attr_accessor :line_count, :nodes, :position_data, :header_data
6
+ attr_reader :labels, :variables, :constants, :namespaces, :source_line_count
7
+ def initialize
8
+ @line_count = 0
9
+ @nodes = []
10
+ @labels = {}
11
+ @namespaces = {}
12
+ @variables = {}
13
+ @constants = {}
14
+ @position_data = {}
15
+ @header_data = {}
16
+ @current_label = 99
17
+ end
18
+
19
+ def load_environment(string)
20
+ scanner = TPPlus::Scanner.new
21
+ parser = TPPlus::Parser.new(scanner, self)
22
+ scanner.scan_setup(string)
23
+ parser.parse
24
+ eval
25
+ rescue RuntimeError => e
26
+ raise "Runtime error in environment on line #{@source_line_count}:\n#{e}"
27
+ end
28
+
29
+ def next_label
30
+ @current_label += 1
31
+ end
32
+
33
+ def add_namespace(name, block)
34
+ if @namespaces[name.to_sym].nil?
35
+ @namespaces[name.to_sym] = TPPlus::Namespace.new(name, block)
36
+ else
37
+ @namespaces[name.to_sym].reopen!(block)
38
+ end
39
+ end
40
+
41
+ def add_label(identifier)
42
+ raise "Label @#{identifier} already defined" if @labels[identifier.to_sym]
43
+ @labels[identifier.to_sym] = next_label
44
+ end
45
+
46
+ def add_var(identifier, node)
47
+ raise "Variable #{identifier} already defined" if @variables[identifier.to_sym]
48
+
49
+ @variables[identifier.to_sym] = node
50
+ node.comment = identifier
51
+ end
52
+
53
+ def add_constant(identifier, node)
54
+ raise "Constant #{identifier} already defined" if @constants[identifier.to_sym]
55
+
56
+ @constants[identifier.to_sym] = node
57
+ end
58
+
59
+ def get_namespace(identifier)
60
+ if ns = @namespaces[identifier.to_sym]
61
+ return ns
62
+ end
63
+
64
+ false
65
+ end
66
+
67
+ def get_var(identifier)
68
+ raise "Variable (#{identifier}) not defined" if @variables[identifier.to_sym].nil?
69
+
70
+ @variables[identifier.to_sym]
71
+ end
72
+
73
+ def get_constant(identifier)
74
+ raise "Constant (#{identifier}) not defined" if @constants[identifier.to_sym].nil?
75
+
76
+ @constants[identifier.to_sym]
77
+ end
78
+
79
+ def define_labels
80
+ @nodes.select {|n| n.is_a? Nodes::LabelDefinitionNode}.each do |n|
81
+ add_label(n.identifier)
82
+ end
83
+ end
84
+
85
+ def pos_section
86
+ return "" if @position_data.empty?
87
+ return "" if @position_data[:positions].empty?
88
+
89
+ @position_data[:positions].inject("") do |s,p|
90
+ s << %(P[#{p[:id]}:"#{p[:comment]}"]{
91
+ GP#{p[:group]}:
92
+ UF : #{p[:uframe]}, UT : #{p[:utool]}, CONFIG : '#{p[:config][:flip] ? 'F' : 'N'} #{p[:config][:up] ? 'U' : 'D'} #{p[:config][:top] ? 'T' : 'B'}, #{p[:config][:turn_counts].join(', ')}',
93
+ X = #{p[:components][:x]} mm, Y = #{p[:components][:y]} mm, Z = #{p[:components][:z]} mm,
94
+ W = #{p[:components][:w]} deg, P = #{p[:components][:p]} deg, R = #{p[:components][:r]} deg
95
+ };\n)
96
+ end
97
+ end
98
+
99
+ def eval
100
+ s = ""
101
+ last_node = nil
102
+
103
+ define_labels
104
+
105
+ @source_line_count = 0
106
+
107
+ @nodes.each do |n|
108
+ @source_line_count += 1 unless n.is_a?(Nodes::TerminatorNode) && !last_node.is_a?(Nodes::TerminatorNode)
109
+ raise if n.is_a?(String)
110
+
111
+ res = n.eval(self)
112
+
113
+ # preserve whitespace
114
+ if n.is_a?(Nodes::TerminatorNode) && last_node.is_a?(Nodes::TerminatorNode)
115
+ s += " ;\n"
116
+ end
117
+ last_node = n
118
+ # end preserve whitespace
119
+
120
+ next if res.nil?
121
+
122
+ s += "#{res} ;\n"
123
+ end
124
+ s
125
+ rescue RuntimeError => e
126
+ raise "Runtime error on line #{@source_line_count}:\n#{e}"
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,66 @@
1
+ module TPPlus
2
+ class Namespace
3
+ def initialize(name, block)
4
+ @name = name
5
+ @block = block
6
+ @namespaces = {}
7
+ @variables = {}
8
+ @constants = {}
9
+
10
+ define!
11
+ end
12
+
13
+ def define!
14
+ @block.flatten.select {|n| [TPPlus::Nodes::DefinitionNode, TPPlus::Nodes::NamespaceNode].include? n.class }.each do |node|
15
+ node.eval(self)
16
+ end
17
+ end
18
+
19
+ def reopen!(block)
20
+ @block = block
21
+ define!
22
+ end
23
+
24
+ def add_constant(identifier, node)
25
+ raise "Constant (#{identifier}) already defined within namespace #{@name}" unless @constants[identifier.to_sym].nil?
26
+
27
+ @constants[identifier.to_sym] = node
28
+ end
29
+
30
+ def add_namespace(identifier, block)
31
+ if @namespaces[identifier.to_sym].nil?
32
+ @namespaces[identifier.to_sym] = TPPlus::Namespace.new("#{@name} #{identifier}", block)
33
+ else
34
+ @namespaces[identifier.to_sym].reopen!(block)
35
+ end
36
+ end
37
+
38
+ def add_var(identifier, node)
39
+ raise "Variable (#{identifier}) already defined within namespace #{@name}" unless @variables[identifier.to_sym].nil?
40
+
41
+ @variables[identifier.to_sym] = node
42
+ node.comment = "#{@name} #{identifier}"
43
+ end
44
+
45
+ def get_constant(identifier)
46
+ raise "Constant (#{identifier}) not defined within namespace #{@name}" if @constants[identifier.to_sym].nil?
47
+
48
+ @constants[identifier.to_sym]
49
+ end
50
+
51
+ def get_var(identifier)
52
+ return get_constant(identifier) if identifier.upcase == identifier
53
+ raise "Variable (#{identifier}) not defined within namespace #{@name}" if @variables[identifier.to_sym].nil?
54
+
55
+ @variables[identifier.to_sym]
56
+ end
57
+
58
+ def get_namespace(identifier)
59
+ if ns = @namespaces[identifier.to_sym]
60
+ return ns
61
+ end
62
+
63
+ false
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,9 @@
1
+ module TPPlus
2
+ module Nodes
3
+ class AbortNode
4
+ def eval(context)
5
+ "ABORT"
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,19 @@
1
+ module TPPlus
2
+ module Nodes
3
+ class ArgumentNode
4
+ attr_accessor :comment
5
+ def initialize(id)
6
+ @id = id
7
+ @comment = comment
8
+ end
9
+
10
+ def requires_mixed_logic?(context)
11
+ true
12
+ end
13
+
14
+ def eval(context,options={})
15
+ "AR[#{@id}]"
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,45 @@
1
+ module TPPlus
2
+ module Nodes
3
+ class AssignmentNode
4
+ attr_reader :identifier, :assignable
5
+ def initialize(identifier,assignable)
6
+ @identifier = identifier
7
+ @assignable = assignable
8
+ end
9
+
10
+ def assignable_string(context,options={})
11
+ if @assignable.is_a?(ExpressionNode)
12
+ options[:mixed_logic] = true if @assignable.contains_expression?
13
+ options[:mixed_logic] = true if @assignable.op.requires_mixed_logic?(context)
14
+ options[:mixed_logic] = true if @assignable.op.boolean?
15
+ options[:mixed_logic] = true if @assignable.boolean_result?
16
+ else
17
+ options[:mixed_logic] = true if @assignable.requires_mixed_logic?(context)
18
+ options[:mixed_logic] = true if @identifier.requires_mixed_logic?(context)
19
+ end
20
+
21
+ if options[:mixed_logic]
22
+ "(#{@assignable.eval(context)})"
23
+ else
24
+ @assignable.eval(context)
25
+ end
26
+ end
27
+
28
+ def requires_mixed_logic?(context)
29
+ true
30
+ end
31
+
32
+ def can_be_inlined?
33
+ true
34
+ end
35
+
36
+ def identifier_string(context)
37
+ @identifier.eval(context)
38
+ end
39
+
40
+ def eval(context,options={})
41
+ "#{identifier_string(context)}=#{assignable_string(context,options)}"
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +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
@@ -0,0 +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
@@ -0,0 +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
@@ -0,0 +1,22 @@
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
@@ -0,0 +1,54 @@
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 eval(context, options={})
43
+ return InlineConditionalNode.new(@type,@condition,@true_block.first).eval(context) if can_be_inlined?
44
+
45
+ if @false_block.empty?
46
+ "IF #{@condition.eval(context,opposite: opposite?, as_condition: true)},JMP LBL[#{true_label(context)}] ;\n#{true_block(context)}LBL[#{true_label(context)}]"
47
+ else
48
+ # could be if-else or unless-else
49
+ "IF #{@condition.eval(context,opposite: opposite?, as_condition: true)},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)}]"
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +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
@@ -0,0 +1,21 @@
1
+ module TPPlus
2
+ module Nodes
3
+ class DigitNode
4
+ def initialize(value)
5
+ @value = value
6
+ end
7
+
8
+ def requires_mixed_logic?(context)
9
+ false
10
+ end
11
+
12
+ def eval(context, options={})
13
+ if @value < 0
14
+ "(#{@value})"
15
+ else
16
+ @value
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +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