tp_plus 0.0.73
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +65 -0
- data/Rakefile +21 -0
- data/bin/tpp +73 -0
- data/lib/tp_plus/interpreter.rb +129 -0
- data/lib/tp_plus/namespace.rb +66 -0
- data/lib/tp_plus/nodes/abort_node.rb +9 -0
- data/lib/tp_plus/nodes/argument_node.rb +19 -0
- data/lib/tp_plus/nodes/assignment_node.rb +45 -0
- data/lib/tp_plus/nodes/call_node.rb +34 -0
- data/lib/tp_plus/nodes/case_condition_node.rb +26 -0
- data/lib/tp_plus/nodes/case_node.rb +33 -0
- data/lib/tp_plus/nodes/comment_node.rb +22 -0
- data/lib/tp_plus/nodes/conditional_node.rb +54 -0
- data/lib/tp_plus/nodes/definition_node.rb +22 -0
- data/lib/tp_plus/nodes/digit_node.rb +21 -0
- data/lib/tp_plus/nodes/eval_node.rb +13 -0
- data/lib/tp_plus/nodes/expression_node.rb +65 -0
- data/lib/tp_plus/nodes/for_node.rb +20 -0
- data/lib/tp_plus/nodes/header_node.rb +27 -0
- data/lib/tp_plus/nodes/indirect_node.rb +49 -0
- data/lib/tp_plus/nodes/inline_conditional_node.rb +40 -0
- data/lib/tp_plus/nodes/io_method_node.rb +55 -0
- data/lib/tp_plus/nodes/io_node.rb +30 -0
- data/lib/tp_plus/nodes/jump_node.rb +23 -0
- data/lib/tp_plus/nodes/label_definition_node.rb +21 -0
- data/lib/tp_plus/nodes/motion_node.rb +62 -0
- data/lib/tp_plus/nodes/namespace_node.rb +16 -0
- data/lib/tp_plus/nodes/namespaced_var_node.rb +38 -0
- data/lib/tp_plus/nodes/numreg_node.rb +25 -0
- data/lib/tp_plus/nodes/offset_node.rb +27 -0
- data/lib/tp_plus/nodes/operator_node.rb +72 -0
- data/lib/tp_plus/nodes/pause_node.rb +9 -0
- data/lib/tp_plus/nodes/position_data_node.rb +50 -0
- data/lib/tp_plus/nodes/position_node.rb +25 -0
- data/lib/tp_plus/nodes/posreg_node.rb +48 -0
- data/lib/tp_plus/nodes/raise_node.rb +13 -0
- data/lib/tp_plus/nodes/real_node.rb +27 -0
- data/lib/tp_plus/nodes/set_node.rb +22 -0
- data/lib/tp_plus/nodes/skip_node.rb +22 -0
- data/lib/tp_plus/nodes/speed_node.rb +29 -0
- data/lib/tp_plus/nodes/string_node.rb +13 -0
- data/lib/tp_plus/nodes/string_register_node.rb +25 -0
- data/lib/tp_plus/nodes/termination_node.rb +18 -0
- data/lib/tp_plus/nodes/terminator_node.rb +16 -0
- data/lib/tp_plus/nodes/time_node.rb +24 -0
- data/lib/tp_plus/nodes/timer_method_node.rb +37 -0
- data/lib/tp_plus/nodes/timer_node.rb +21 -0
- data/lib/tp_plus/nodes/units_node.rb +20 -0
- data/lib/tp_plus/nodes/use_node.rb +21 -0
- data/lib/tp_plus/nodes/user_alarm_node.rb +15 -0
- data/lib/tp_plus/nodes/var_method_node.rb +23 -0
- data/lib/tp_plus/nodes/var_node.rb +39 -0
- data/lib/tp_plus/nodes/vision_register_node.rb +21 -0
- data/lib/tp_plus/nodes/wait_for_node.rb +54 -0
- data/lib/tp_plus/nodes/wait_until_node.rb +65 -0
- data/lib/tp_plus/nodes/while_node.rb +36 -0
- data/lib/tp_plus/parser.rb +1592 -0
- data/lib/tp_plus/scanner.rb +383 -0
- data/lib/tp_plus/version.rb +3 -0
- data/lib/tp_plus.rb +62 -0
- data/test/test_helper.rb +5 -0
- data/test/tp_plus/test_interpreter.rb +1168 -0
- data/test/tp_plus/test_parser.rb +489 -0
- data/test/tp_plus/test_scanner.rb +522 -0
- data/tp_plus.gemspec +28 -0
- 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,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
|