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
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