emfrp 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +45 -12
- data/bin/emfrp +4 -1
- data/examples/LCDClock/LCDClock.mfrp +93 -93
- data/examples/LCDClock/LCDClock_LPC1768.bin +0 -0
- data/examples/LCDClock/README.md +24 -24
- data/examples/LCDPositioner/LCDPositioner.mfrp +30 -30
- data/examples/LCDPositioner/LCDPositionerMain.c +15 -15
- data/examples/MostDistantPoint/MostDistantPoint.mfrp +25 -25
- data/examples/MostDistantPoint/MostDistantPointMain.c +14 -14
- data/lib/emfrp/compile/c/alloc.rb +200 -200
- data/lib/emfrp/compile/c/codegen.rb +18 -18
- data/lib/emfrp/compile/c/codegen_context.rb +218 -218
- data/lib/emfrp/compile/c/monofy.rb +185 -185
- data/lib/emfrp/compile/c/syntax_codegen.rb +364 -364
- data/lib/emfrp/compile/c/syntax_exp_codegen.rb +119 -119
- data/lib/emfrp/compile/graphviz/graphviz.rb +53 -53
- data/lib/emfrp/compile_error.rb +95 -95
- data/lib/emfrp/interpreter/command_manager.rb +367 -367
- data/lib/emfrp/interpreter/evaluater.rb +146 -146
- data/lib/emfrp/interpreter/file_loader.rb +52 -52
- data/lib/emfrp/interpreter/interpreter.rb +200 -195
- data/lib/emfrp/parser/expression.rb +386 -386
- data/lib/emfrp/parser/misc.rb +184 -184
- data/lib/emfrp/parser/newnode_convert.rb +72 -72
- data/lib/emfrp/parser/operator.rb +25 -25
- data/lib/emfrp/parser/parser.rb +150 -150
- data/lib/emfrp/parser/parsing_error.rb +49 -49
- data/lib/emfrp/parser/toplevel.rb +555 -555
- data/lib/emfrp/pre_convert/pre_convert.rb +32 -32
- data/lib/emfrp/syntax.rb +171 -171
- data/lib/emfrp/typing/typing_error.rb +47 -47
- data/lib/emfrp/typing/union_type.rb +197 -197
- data/lib/emfrp/version.rb +1 -1
- data/mfrp_include/Std.mfrp +122 -122
- data/tests/Rakefile +8 -8
- data/tests/Rakefile.common +27 -27
- data/tests/command/Rakefile +2 -2
- data/tests/command/ReplaceNode.mfrp +39 -39
- data/tests/compiler/ComplexDataType/ComplexDataType.mfrp +14 -14
- data/tests/compiler/ComplexDataType/ComplexDataTypeMain.c +15 -15
- data/tests/compiler/ComplexDataType/Rakefile +2 -2
- data/tests/compiler/ComplexDataType/expected_out.txt +0 -0
- data/tests/compiler/ComplexDataType/in.txt +5 -5
- data/tests/compiler/LCDClock/LCDClock.mfrp +90 -90
- data/tests/compiler/LCDClock/LCDClockMain.c +0 -0
- data/tests/compiler/LCDClock/Rakefile +2 -2
- data/tests/compiler/LCDClock/expected_out.txt +0 -0
- data/tests/compiler/LCDClock/in.txt +0 -0
- data/tests/compiler/LCDPositioner/LCDPositioner.mfrp +30 -30
- data/tests/compiler/LCDPositioner/LCDPositionerMain.c +15 -15
- data/tests/compiler/LCDPositioner/Rakefile +2 -2
- data/tests/compiler/LCDPositioner/graph.dot +0 -0
- data/tests/compiler/LCDPositioner/graph.png +0 -0
- data/tests/compiler/Rakefile +8 -8
- data/tests/compiler/Rakefile.common +23 -23
- data/tests/compiler/UseData/Rakefile +2 -2
- data/tests/compiler/UseData/UseData.mfrp +8 -8
- data/tests/compiler/UseSubModule/Rakefile +2 -2
- data/tests/compiler/UseSubModule/SubModule.mfrp +8 -8
- data/tests/compiler/UseSubModule/SubModule2.mfrp +5 -5
- data/tests/compiler/UseSubModule/UseSubModule.mfrp +11 -11
- data/tests/core/FromAnnotation.mfrp +18 -18
- data/tests/core/Last.mfrp +10 -10
- data/tests/core/Rakefile +2 -2
- data/tests/core/TypingTest.mfrp +11 -11
- data/tests/core/WithoutInputs.mfrp +19 -19
- data/tests/load_time_error/Rakefile +32 -32
- data/tests/load_time_error/TypeMismatch.mfrp +4 -4
- metadata +3 -3
@@ -1,218 +1,218 @@
|
|
1
|
-
module Emfrp
|
2
|
-
class CodegenContext
|
3
|
-
SymbolToStr = {
|
4
|
-
"!" => "_exclamation_",
|
5
|
-
"#" => "_hash_",
|
6
|
-
"$" => "_dollar_",
|
7
|
-
"%" => "_parcent_",
|
8
|
-
"&" => "_anpersand",
|
9
|
-
"*" => "_asterisk_",
|
10
|
-
"+" => "_plus_",
|
11
|
-
"." => "_dot_",
|
12
|
-
"/" => "_slash_",
|
13
|
-
"<" => "_lt_",
|
14
|
-
"=" => "_eq_",
|
15
|
-
">" => "_gt_",
|
16
|
-
"?" => "_question_",
|
17
|
-
"@" => "_at_",
|
18
|
-
"\\" => "_backslash_",
|
19
|
-
"^" => "_caret_",
|
20
|
-
"|" => "_vertial_",
|
21
|
-
"-" => "_minus_",
|
22
|
-
"~" => "_tilde_",
|
23
|
-
"(" => "_cpbegin_",
|
24
|
-
")" => "_cpend_",
|
25
|
-
"," => "_comma_"
|
26
|
-
}
|
27
|
-
|
28
|
-
def initialize(top)
|
29
|
-
@top = top
|
30
|
-
@global_vars = []
|
31
|
-
@funcs = []
|
32
|
-
@structs = []
|
33
|
-
@protos = []
|
34
|
-
@static_protos = []
|
35
|
-
@macros = []
|
36
|
-
@init_stmts = []
|
37
|
-
@templates = []
|
38
|
-
end
|
39
|
-
|
40
|
-
def code_generate(c_output, h_output, main_output, name)
|
41
|
-
# generate header-file
|
42
|
-
h_output << "#ifndef #{name.upcase}_H\n"
|
43
|
-
h_output << "#define #{name.upcase}_H\n\n"
|
44
|
-
@protos.each do |x|
|
45
|
-
h_output.puts x.to_s
|
46
|
-
end
|
47
|
-
h_output << "\n#endif /* end of include guard */\n"
|
48
|
-
# generate library-file
|
49
|
-
c_output.puts "#include \"#{name}.h\""
|
50
|
-
c_output.puts "/* Primitive functions (Macros) */"
|
51
|
-
@macros.each do |x|
|
52
|
-
c_output.puts x.to_s
|
53
|
-
end
|
54
|
-
c_output.puts "/* Data types */"
|
55
|
-
@structs.each do |x|
|
56
|
-
c_output.puts x.to_s
|
57
|
-
end
|
58
|
-
c_output.puts "/* Global variables */"
|
59
|
-
@global_vars.each do |x|
|
60
|
-
c_output.puts x.to_s
|
61
|
-
end
|
62
|
-
c_output.puts "/* Static prototypes */"
|
63
|
-
@static_protos.each do |x|
|
64
|
-
c_output.puts x.to_s
|
65
|
-
end
|
66
|
-
c_output.puts "/* Functions, Constructors, GCMarkers, etc... */"
|
67
|
-
@funcs.each do |x|
|
68
|
-
c_output.puts x.to_s
|
69
|
-
end
|
70
|
-
# generate main-file
|
71
|
-
main_output << "#include \"#{name}.h\"\n\n"
|
72
|
-
main_output << "void Input(#{@top[:inputs].map{|x| "#{tref(x)}* #{x[:name][:desc]}"}.join(", ")}) {\n /* Your code goes here... */\n}\n"
|
73
|
-
main_output << "void Output(#{@top[:outputs].map{|x| "#{tref(x)}* #{x[:name][:desc]}"}.join(", ")}) {\n /* Your code goes here... */\n}\n"
|
74
|
-
main_output << "int main() {\n Activate#{@top[:module_name][:desc]}();\n}\n"
|
75
|
-
end
|
76
|
-
|
77
|
-
def init_stmts
|
78
|
-
@init_stmts
|
79
|
-
end
|
80
|
-
|
81
|
-
def func_name(name, ret_utype, arg_utypes)
|
82
|
-
case f = @top[:dict][:func_space][name].get
|
83
|
-
when PrimFuncDef
|
84
|
-
f.func_name(self)
|
85
|
-
when FuncDef
|
86
|
-
key = [ret_utype, *arg_utypes].map(&:to_uniq_str) + [name]
|
87
|
-
@top[:dict][:ifunc_space][key].get.func_name(self)
|
88
|
-
else
|
89
|
-
raise "Assertion error: unexpected func type #{f.class}"
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
def constructor_name(name, utype)
|
94
|
-
@top[:dict][:itype_space][utype.to_uniq_str].get[:tvalues].each do |tval|
|
95
|
-
if tval[:name][:desc] == name
|
96
|
-
return tval.constructor_name(self)
|
97
|
-
end
|
98
|
-
end
|
99
|
-
raise "Assertion error: #{name} is not found"
|
100
|
-
end
|
101
|
-
|
102
|
-
def escape_name(name)
|
103
|
-
rexp = Regexp.new("[" + Regexp.escape(SymbolToStr.keys.join) + "]")
|
104
|
-
name.gsub(rexp, SymbolToStr)
|
105
|
-
end
|
106
|
-
|
107
|
-
def tdef(x)
|
108
|
-
case x
|
109
|
-
when Typing::UnionType
|
110
|
-
key = x.to_uniq_str
|
111
|
-
if @top[:dict][:type_space][key] && @top[:dict][:type_space][key].get.is_a?(PrimTypeDef)
|
112
|
-
@top[:dict][:type_space][key].get
|
113
|
-
elsif @top[:dict][:itype_space][key]
|
114
|
-
@top[:dict][:itype_space][key].get
|
115
|
-
else
|
116
|
-
raise "Assertion error: itype #{x.to_uniq_str} is undefined"
|
117
|
-
end
|
118
|
-
when Syntax
|
119
|
-
tdef(x[:typing])
|
120
|
-
else
|
121
|
-
raise "Assertion error"
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
def tref(x)
|
126
|
-
tdef(x).ref_name(self)
|
127
|
-
end
|
128
|
-
|
129
|
-
def serial(key, id)
|
130
|
-
@serials ||= Hash.new{|h, k| h[k] = []}
|
131
|
-
@serials[key] << id unless @serials[key].find{|x| x == id}
|
132
|
-
return @serials[key].index{|x| x == id}
|
133
|
-
end
|
134
|
-
|
135
|
-
def uniq_id_gen
|
136
|
-
@uniq_ids ||= (0..1000).to_a
|
137
|
-
@uniq_ids.shift
|
138
|
-
end
|
139
|
-
|
140
|
-
def define_global_var(type_str, name_str, initial_value_str=nil)
|
141
|
-
@global_vars << "#{type_str} #{name_str}" + (initial_value_str ? " = #{initial_value_str}" : "") + ";"
|
142
|
-
end
|
143
|
-
|
144
|
-
def define_macro(name_str, params, body_str)
|
145
|
-
@macros << "#define #{name_str}(#{params.join(", ")}) (#{body_str})"
|
146
|
-
end
|
147
|
-
|
148
|
-
def define_func(type_str, name_str, params, accessor=:static, with_proto=true, &block)
|
149
|
-
elements = []
|
150
|
-
proc.call(elements)
|
151
|
-
case accessor
|
152
|
-
when :none then deco = ""
|
153
|
-
when :static then deco = "static "
|
154
|
-
end
|
155
|
-
define_proto(type_str, name_str, params.map(&:first), accessor) if with_proto
|
156
|
-
@funcs << Block.new("#{deco}#{type_str} #{name_str}(#{params.map{|a, b| "#{a} #{b}"}.join(", ")}) {", elements, "}")
|
157
|
-
return nil
|
158
|
-
end
|
159
|
-
|
160
|
-
def define_proto(type_str, name_str, param_types, accessor=:static)
|
161
|
-
case accessor
|
162
|
-
when :none then deco = ""
|
163
|
-
when :static then deco = "static "
|
164
|
-
when :extern then deco = "extern "
|
165
|
-
end
|
166
|
-
proto = "#{deco}#{type_str} #{name_str}(#{param_types.join(", ")});"
|
167
|
-
if accessor == :static || accessor == :extern
|
168
|
-
@static_protos << proto
|
169
|
-
else
|
170
|
-
@protos << proto
|
171
|
-
end
|
172
|
-
return nil
|
173
|
-
end
|
174
|
-
|
175
|
-
def define_init_stmt(stmt)
|
176
|
-
@init_stmts << stmt
|
177
|
-
end
|
178
|
-
|
179
|
-
def define_struct(kind_str, name_str, var_name_str)
|
180
|
-
elements = []
|
181
|
-
proc.call(elements)
|
182
|
-
x = Block.new("#{kind_str} #{name_str}{", elements, "}#{var_name_str};")
|
183
|
-
if name_str
|
184
|
-
@structs << x
|
185
|
-
return nil
|
186
|
-
else
|
187
|
-
return x
|
188
|
-
end
|
189
|
-
end
|
190
|
-
|
191
|
-
def make_block(head_str, elements, foot_str)
|
192
|
-
Block.new(head_str, elements, foot_str)
|
193
|
-
end
|
194
|
-
|
195
|
-
class Block
|
196
|
-
T = (0..20).map{|i| " " * i}
|
197
|
-
def initialize(head_str, elements, foot_str)
|
198
|
-
@head_str = head_str
|
199
|
-
@elements = elements
|
200
|
-
@foot_str = foot_str
|
201
|
-
end
|
202
|
-
|
203
|
-
def to_s(t=0)
|
204
|
-
res = ""
|
205
|
-
res << T[t] + @head_str + "\n"
|
206
|
-
@elements.each do |e|
|
207
|
-
case e
|
208
|
-
when Block
|
209
|
-
res << e.to_s(t+1) + "\n"
|
210
|
-
when String
|
211
|
-
res << T[t+1] + e + "\n"
|
212
|
-
end
|
213
|
-
end
|
214
|
-
res << T[t] + @foot_str
|
215
|
-
end
|
216
|
-
end
|
217
|
-
end
|
218
|
-
end
|
1
|
+
module Emfrp
|
2
|
+
class CodegenContext
|
3
|
+
SymbolToStr = {
|
4
|
+
"!" => "_exclamation_",
|
5
|
+
"#" => "_hash_",
|
6
|
+
"$" => "_dollar_",
|
7
|
+
"%" => "_parcent_",
|
8
|
+
"&" => "_anpersand",
|
9
|
+
"*" => "_asterisk_",
|
10
|
+
"+" => "_plus_",
|
11
|
+
"." => "_dot_",
|
12
|
+
"/" => "_slash_",
|
13
|
+
"<" => "_lt_",
|
14
|
+
"=" => "_eq_",
|
15
|
+
">" => "_gt_",
|
16
|
+
"?" => "_question_",
|
17
|
+
"@" => "_at_",
|
18
|
+
"\\" => "_backslash_",
|
19
|
+
"^" => "_caret_",
|
20
|
+
"|" => "_vertial_",
|
21
|
+
"-" => "_minus_",
|
22
|
+
"~" => "_tilde_",
|
23
|
+
"(" => "_cpbegin_",
|
24
|
+
")" => "_cpend_",
|
25
|
+
"," => "_comma_"
|
26
|
+
}
|
27
|
+
|
28
|
+
def initialize(top)
|
29
|
+
@top = top
|
30
|
+
@global_vars = []
|
31
|
+
@funcs = []
|
32
|
+
@structs = []
|
33
|
+
@protos = []
|
34
|
+
@static_protos = []
|
35
|
+
@macros = []
|
36
|
+
@init_stmts = []
|
37
|
+
@templates = []
|
38
|
+
end
|
39
|
+
|
40
|
+
def code_generate(c_output, h_output, main_output, name)
|
41
|
+
# generate header-file
|
42
|
+
h_output << "#ifndef #{name.upcase}_H\n"
|
43
|
+
h_output << "#define #{name.upcase}_H\n\n"
|
44
|
+
@protos.each do |x|
|
45
|
+
h_output.puts x.to_s
|
46
|
+
end
|
47
|
+
h_output << "\n#endif /* end of include guard */\n"
|
48
|
+
# generate library-file
|
49
|
+
c_output.puts "#include \"#{name}.h\""
|
50
|
+
c_output.puts "/* Primitive functions (Macros) */"
|
51
|
+
@macros.each do |x|
|
52
|
+
c_output.puts x.to_s
|
53
|
+
end
|
54
|
+
c_output.puts "/* Data types */"
|
55
|
+
@structs.each do |x|
|
56
|
+
c_output.puts x.to_s
|
57
|
+
end
|
58
|
+
c_output.puts "/* Global variables */"
|
59
|
+
@global_vars.each do |x|
|
60
|
+
c_output.puts x.to_s
|
61
|
+
end
|
62
|
+
c_output.puts "/* Static prototypes */"
|
63
|
+
@static_protos.each do |x|
|
64
|
+
c_output.puts x.to_s
|
65
|
+
end
|
66
|
+
c_output.puts "/* Functions, Constructors, GCMarkers, etc... */"
|
67
|
+
@funcs.each do |x|
|
68
|
+
c_output.puts x.to_s
|
69
|
+
end
|
70
|
+
# generate main-file
|
71
|
+
main_output << "#include \"#{name}.h\"\n\n"
|
72
|
+
main_output << "void Input(#{@top[:inputs].map{|x| "#{tref(x)}* #{x[:name][:desc]}"}.join(", ")}) {\n /* Your code goes here... */\n}\n"
|
73
|
+
main_output << "void Output(#{@top[:outputs].map{|x| "#{tref(x)}* #{x[:name][:desc]}"}.join(", ")}) {\n /* Your code goes here... */\n}\n"
|
74
|
+
main_output << "int main() {\n Activate#{@top[:module_name][:desc]}();\n}\n"
|
75
|
+
end
|
76
|
+
|
77
|
+
def init_stmts
|
78
|
+
@init_stmts
|
79
|
+
end
|
80
|
+
|
81
|
+
def func_name(name, ret_utype, arg_utypes)
|
82
|
+
case f = @top[:dict][:func_space][name].get
|
83
|
+
when PrimFuncDef
|
84
|
+
f.func_name(self)
|
85
|
+
when FuncDef
|
86
|
+
key = [ret_utype, *arg_utypes].map(&:to_uniq_str) + [name]
|
87
|
+
@top[:dict][:ifunc_space][key].get.func_name(self)
|
88
|
+
else
|
89
|
+
raise "Assertion error: unexpected func type #{f.class}"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def constructor_name(name, utype)
|
94
|
+
@top[:dict][:itype_space][utype.to_uniq_str].get[:tvalues].each do |tval|
|
95
|
+
if tval[:name][:desc] == name
|
96
|
+
return tval.constructor_name(self)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
raise "Assertion error: #{name} is not found"
|
100
|
+
end
|
101
|
+
|
102
|
+
def escape_name(name)
|
103
|
+
rexp = Regexp.new("[" + Regexp.escape(SymbolToStr.keys.join) + "]")
|
104
|
+
name.gsub(rexp, SymbolToStr)
|
105
|
+
end
|
106
|
+
|
107
|
+
def tdef(x)
|
108
|
+
case x
|
109
|
+
when Typing::UnionType
|
110
|
+
key = x.to_uniq_str
|
111
|
+
if @top[:dict][:type_space][key] && @top[:dict][:type_space][key].get.is_a?(PrimTypeDef)
|
112
|
+
@top[:dict][:type_space][key].get
|
113
|
+
elsif @top[:dict][:itype_space][key]
|
114
|
+
@top[:dict][:itype_space][key].get
|
115
|
+
else
|
116
|
+
raise "Assertion error: itype #{x.to_uniq_str} is undefined"
|
117
|
+
end
|
118
|
+
when Syntax
|
119
|
+
tdef(x[:typing])
|
120
|
+
else
|
121
|
+
raise "Assertion error"
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def tref(x)
|
126
|
+
tdef(x).ref_name(self)
|
127
|
+
end
|
128
|
+
|
129
|
+
def serial(key, id)
|
130
|
+
@serials ||= Hash.new{|h, k| h[k] = []}
|
131
|
+
@serials[key] << id unless @serials[key].find{|x| x == id}
|
132
|
+
return @serials[key].index{|x| x == id}
|
133
|
+
end
|
134
|
+
|
135
|
+
def uniq_id_gen
|
136
|
+
@uniq_ids ||= (0..1000).to_a
|
137
|
+
@uniq_ids.shift
|
138
|
+
end
|
139
|
+
|
140
|
+
def define_global_var(type_str, name_str, initial_value_str=nil)
|
141
|
+
@global_vars << "#{type_str} #{name_str}" + (initial_value_str ? " = #{initial_value_str}" : "") + ";"
|
142
|
+
end
|
143
|
+
|
144
|
+
def define_macro(name_str, params, body_str)
|
145
|
+
@macros << "#define #{name_str}(#{params.join(", ")}) (#{body_str})"
|
146
|
+
end
|
147
|
+
|
148
|
+
def define_func(type_str, name_str, params, accessor=:static, with_proto=true, &block)
|
149
|
+
elements = []
|
150
|
+
proc.call(elements)
|
151
|
+
case accessor
|
152
|
+
when :none then deco = ""
|
153
|
+
when :static then deco = "static "
|
154
|
+
end
|
155
|
+
define_proto(type_str, name_str, params.map(&:first), accessor) if with_proto
|
156
|
+
@funcs << Block.new("#{deco}#{type_str} #{name_str}(#{params.map{|a, b| "#{a} #{b}"}.join(", ")}) {", elements, "}")
|
157
|
+
return nil
|
158
|
+
end
|
159
|
+
|
160
|
+
def define_proto(type_str, name_str, param_types, accessor=:static)
|
161
|
+
case accessor
|
162
|
+
when :none then deco = ""
|
163
|
+
when :static then deco = "static "
|
164
|
+
when :extern then deco = "extern "
|
165
|
+
end
|
166
|
+
proto = "#{deco}#{type_str} #{name_str}(#{param_types.join(", ")});"
|
167
|
+
if accessor == :static || accessor == :extern
|
168
|
+
@static_protos << proto
|
169
|
+
else
|
170
|
+
@protos << proto
|
171
|
+
end
|
172
|
+
return nil
|
173
|
+
end
|
174
|
+
|
175
|
+
def define_init_stmt(stmt)
|
176
|
+
@init_stmts << stmt
|
177
|
+
end
|
178
|
+
|
179
|
+
def define_struct(kind_str, name_str, var_name_str)
|
180
|
+
elements = []
|
181
|
+
proc.call(elements)
|
182
|
+
x = Block.new("#{kind_str} #{name_str}{", elements, "}#{var_name_str};")
|
183
|
+
if name_str
|
184
|
+
@structs << x
|
185
|
+
return nil
|
186
|
+
else
|
187
|
+
return x
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def make_block(head_str, elements, foot_str)
|
192
|
+
Block.new(head_str, elements, foot_str)
|
193
|
+
end
|
194
|
+
|
195
|
+
class Block
|
196
|
+
T = (0..20).map{|i| " " * i}
|
197
|
+
def initialize(head_str, elements, foot_str)
|
198
|
+
@head_str = head_str
|
199
|
+
@elements = elements
|
200
|
+
@foot_str = foot_str
|
201
|
+
end
|
202
|
+
|
203
|
+
def to_s(t=0)
|
204
|
+
res = ""
|
205
|
+
res << T[t] + @head_str + "\n"
|
206
|
+
@elements.each do |e|
|
207
|
+
case e
|
208
|
+
when Block
|
209
|
+
res << e.to_s(t+1) + "\n"
|
210
|
+
when String
|
211
|
+
res << T[t+1] + e + "\n"
|
212
|
+
end
|
213
|
+
end
|
214
|
+
res << T[t] + @foot_str
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|