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