tp_plus 0.0.73 → 0.0.74

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 (72) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +74 -65
  3. data/Rakefile +35 -21
  4. data/bin/tpp +73 -73
  5. data/lib/tp_plus/interpreter.rb +152 -129
  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/address_node.rb +22 -0
  9. data/lib/tp_plus/nodes/argument_node.rb +20 -19
  10. data/lib/tp_plus/nodes/assignment_node.rb +45 -45
  11. data/lib/tp_plus/nodes/call_node.rb +34 -34
  12. data/lib/tp_plus/nodes/case_condition_node.rb +26 -26
  13. data/lib/tp_plus/nodes/case_node.rb +33 -33
  14. data/lib/tp_plus/nodes/comment_node.rb +22 -22
  15. data/lib/tp_plus/nodes/conditional_node.rb +60 -54
  16. data/lib/tp_plus/nodes/definition_node.rb +22 -22
  17. data/lib/tp_plus/nodes/digit_node.rb +22 -21
  18. data/lib/tp_plus/nodes/empty_stmt_node.rb +9 -0
  19. data/lib/tp_plus/nodes/eval_node.rb +13 -13
  20. data/lib/tp_plus/nodes/expression_node.rb +68 -65
  21. data/lib/tp_plus/nodes/for_node.rb +20 -20
  22. data/lib/tp_plus/nodes/header_node.rb +27 -27
  23. data/lib/tp_plus/nodes/indirect_node.rb +51 -49
  24. data/lib/tp_plus/nodes/inline_conditional_node.rb +40 -40
  25. data/lib/tp_plus/nodes/io_method_node.rb +55 -55
  26. data/lib/tp_plus/nodes/io_node.rb +31 -30
  27. data/lib/tp_plus/nodes/jump_node.rb +23 -23
  28. data/lib/tp_plus/nodes/label_definition_node.rb +21 -21
  29. data/lib/tp_plus/nodes/motion_node.rb +62 -62
  30. data/lib/tp_plus/nodes/namespace_node.rb +16 -16
  31. data/lib/tp_plus/nodes/namespaced_var_node.rb +38 -38
  32. data/lib/tp_plus/nodes/numreg_node.rb +26 -25
  33. data/lib/tp_plus/nodes/offset_node.rb +27 -27
  34. data/lib/tp_plus/nodes/operator_node.rb +78 -72
  35. data/lib/tp_plus/nodes/paren_expression_node.rb +17 -0
  36. data/lib/tp_plus/nodes/pause_node.rb +9 -9
  37. data/lib/tp_plus/nodes/position_data_node.rb +64 -50
  38. data/lib/tp_plus/nodes/position_node.rb +26 -25
  39. data/lib/tp_plus/nodes/posreg_node.rb +64 -48
  40. data/lib/tp_plus/nodes/raise_node.rb +13 -13
  41. data/lib/tp_plus/nodes/real_node.rb +27 -27
  42. data/lib/tp_plus/nodes/set_skip_node.rb +14 -0
  43. data/lib/tp_plus/nodes/skip_node.rb +22 -22
  44. data/lib/tp_plus/nodes/speed_node.rb +29 -29
  45. data/lib/tp_plus/nodes/string_node.rb +13 -13
  46. data/lib/tp_plus/nodes/string_register_node.rb +26 -25
  47. data/lib/tp_plus/nodes/termination_node.rb +23 -18
  48. data/lib/tp_plus/nodes/terminator_node.rb +16 -16
  49. data/lib/tp_plus/nodes/time_node.rb +24 -24
  50. data/lib/tp_plus/nodes/timer_method_node.rb +37 -37
  51. data/lib/tp_plus/nodes/timer_node.rb +16 -21
  52. data/lib/tp_plus/nodes/units_node.rb +20 -20
  53. data/lib/tp_plus/nodes/use_node.rb +21 -21
  54. data/lib/tp_plus/nodes/user_alarm_node.rb +16 -15
  55. data/lib/tp_plus/nodes/var_method_node.rb +23 -23
  56. data/lib/tp_plus/nodes/var_node.rb +39 -39
  57. data/lib/tp_plus/nodes/vision_register_node.rb +22 -21
  58. data/lib/tp_plus/nodes/wait_for_node.rb +54 -54
  59. data/lib/tp_plus/nodes/wait_until_node.rb +65 -65
  60. data/lib/tp_plus/nodes/while_node.rb +42 -36
  61. data/lib/tp_plus/parser.rb +1682 -1592
  62. data/lib/tp_plus/scanner.rb +283 -383
  63. data/lib/tp_plus/token.rb +97 -0
  64. data/lib/tp_plus/version.rb +3 -3
  65. data/lib/tp_plus.rb +66 -62
  66. data/test/test_helper.rb +5 -5
  67. data/test/tp_plus/test_interpreter.rb +1309 -1168
  68. data/test/tp_plus/test_parser.rb +496 -489
  69. data/test/tp_plus/test_scanner.rb +577 -522
  70. data/tp_plus.gemspec +31 -28
  71. metadata +61 -14
  72. data/lib/tp_plus/nodes/set_node.rb +0 -22
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 67da7b5806591383225c3d09717b6df2951a3c39
4
- data.tar.gz: d11d8ac4716e5cccfc17b9913d587b2209a3438f
3
+ metadata.gz: 145a37ef7d63642c1a654a9e804c0dc8ce21f1b0
4
+ data.tar.gz: 3229bc8c6be259a54326b1d83ff5cc295a443b6c
5
5
  SHA512:
6
- metadata.gz: 82ef1ac3fbfa26321b17e679613c69b7bb43ccb5997f927fe2c8a87677600996a93e3f42708b2f997aaab7e83caefbc6e8ddbc82ef786c1a6209d928387fc9ef
7
- data.tar.gz: fc6b978abb2f2952d3622f165549b0c7e700c0e60b78ebc2b6496201216f7a666cf62835be5ca9605692a5e7fb98488a957382ac5fdf9fe6086bf7592d62bd02
6
+ metadata.gz: 33068559319eff301cd865cef17e9db384ab30b50b7f59fa44659b4ba4ad87289a195ac500ce9105a7355a5561e9f483f6c9b22946c59e89458d5b85c395df9b
7
+ data.tar.gz: 0c4db71fa16f12d8fa0d244c0a1b59a25a492f9911049a3697c8fa33a5b46ee6387848928dcf0daaa8ce912b461d589e353c5214d4255f0ca2eec29f3a32a2cf
data/README.md CHANGED
@@ -1,65 +1,74 @@
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).
1
+ TP+
2
+ ===
3
+
4
+ [![Build Status](https://travis-ci.org/onerobotics/tp_plus.svg?branch=master)](https://travis-ci.org/onerobotics/tp_plus)
5
+
6
+ TP+ is a higher-level language abstraction that translates into FANUC
7
+ TP. It features many useful utilities that makes creating TP programs
8
+ easier:
9
+
10
+ * Identifiers for registers, position registers, IO, etc.
11
+ * Re-usable methods
12
+ * If-else blocks
13
+ * Readable motion statements
14
+ * Automatic label numbering
15
+
16
+ Of course adding another layer of abstraction takes you a step further
17
+ away from the code the robot is running. However, it's hoped that the
18
+ increased productivity and rigid syntax requirements will actually
19
+ improve your TP code.
20
+
21
+ Examples
22
+ --------
23
+
24
+ example_1.tpp
25
+
26
+ foo := R[1]
27
+ bar := DO[1]
28
+ baz := DO[2]
29
+
30
+ home := PR[1]
31
+
32
+ foo = 1
33
+
34
+ @loop
35
+ foo += 1
36
+
37
+ jump_to @loop 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[100:loop] ;
52
+ 1: R[1:foo]=R[1:foo]+1 ;
53
+ 1: IF R[1:foo]<10,JMP LBL[100] ;
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
+ For a more extensive example and test environment, visit http://tp-plus.herokuapp.com/.
62
+
63
+ Usage
64
+ -----
65
+
66
+ 1. `gem install tp_plus`
67
+ 2. `tpp filename.tpp > filename.ls`
68
+
69
+ See `tpp --help` for options.
70
+
71
+ License
72
+ -------
73
+
74
+ TP+ is released under the [MIT License](http://www.opensource.org/licenses/MIT).
data/Rakefile CHANGED
@@ -1,21 +1,35 @@
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
1
+ file "lib/tp_plus/parser.rb" => ["generators/parser.y"] do |t|
2
+ sh "racc -l -t -v -o lib/tp_plus/parser.rb generators/parser.y"
3
+ end
4
+
5
+ namespace :compile do
6
+ task :parser do
7
+ Rake::Task["lib/tp_plus/parser.rb"].invoke
8
+ end
9
+ end
10
+
11
+ task compile: ["compile:parser"]
12
+
13
+ require 'rake/testtask'
14
+
15
+ Rake::TestTask.new do |t|
16
+ # build the parser if necessary
17
+ Rake::Task["lib/tp_plus/parser.rb"].invoke
18
+
19
+ t.libs << "test"
20
+ t.test_files = FileList['test/**/test_*.rb']
21
+ t.verbose = true
22
+ end
23
+
24
+ task default: :test
25
+
26
+
27
+ desc "Run the TP+ benchmark"
28
+ task :benchmark do
29
+ ruby "./performance/benchmark.rb"
30
+ end
31
+
32
+ desc "Run the TP+ profiler"
33
+ task :profile do
34
+ ruby "./performance/profile.rb"
35
+ end
data/bin/tpp CHANGED
@@ -1,73 +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
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
@@ -1,129 +1,152 @@
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
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]}"]{\n)
91
+
92
+ p[:mask].each do |q|
93
+ s << pos_return(q)
94
+ end
95
+
96
+ s << %(\n};\n)
97
+ end
98
+ end
99
+
100
+ def pos_return(position_hash)
101
+ s = ""
102
+ if position_hash[:config].is_a?(Hash)
103
+ s << %( GP#{position_hash[:group]}:
104
+ UF : #{position_hash[:uframe]}, UT : #{position_hash[:utool]}, CONFIG : '#{position_hash[:config][:flip] ? 'F' : 'N'} #{position_hash[:config][:up] ? 'U' : 'D'} #{position_hash[:config][:top] ? 'T' : 'B'}, #{position_hash[:config][:turn_counts].join(', ')}',
105
+ X = #{position_hash[:components][:x]} mm, Y = #{position_hash[:components][:y]} mm, Z = #{position_hash[:components][:z]} mm,
106
+ W = #{position_hash[:components][:w]} deg, P = #{position_hash[:components][:p]} deg, R = #{position_hash[:components][:r]} deg)
107
+ else
108
+ s << %( GP#{position_hash[:group]}:
109
+ UF : #{position_hash[:uframe]}, UT : #{position_hash[:utool]})
110
+ if position_hash[:components].is_a?(Hash)
111
+ position_hash[:components].each_with_index do |key, joint|
112
+ s << %(, \n)
113
+ s << %(\tJ#{key} = #{joint[1]} deg)
114
+ end
115
+ s << %(\n)
116
+ end
117
+ end
118
+
119
+ return s
120
+ end
121
+
122
+ def eval
123
+ s = ""
124
+ last_node = nil
125
+
126
+ define_labels
127
+
128
+ @source_line_count = 0
129
+
130
+ @nodes.each do |n|
131
+ @source_line_count += 1 unless n.is_a?(Nodes::TerminatorNode) && !last_node.is_a?(Nodes::TerminatorNode)
132
+ raise if n.is_a?(String)
133
+
134
+ res = n.eval(self)
135
+
136
+ # preserve whitespace
137
+ if n.is_a?(Nodes::TerminatorNode) && last_node.is_a?(Nodes::TerminatorNode)
138
+ s += " ;\n"
139
+ end
140
+ last_node = n
141
+ # end preserve whitespace
142
+
143
+ next if res.nil?
144
+
145
+ s += "#{res} ;\n"
146
+ end
147
+ s
148
+ rescue RuntimeError => e
149
+ raise "Runtime error on line #{@source_line_count}:\n#{e}"
150
+ end
151
+ end
152
+ end