emfrp 0.1.2 → 0.1.3
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 +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,32 +1,32 @@
|
|
1
|
-
require 'emfrp/pre_convert/make_name_dict'
|
2
|
-
require 'emfrp/pre_convert/alpha_convert'
|
3
|
-
require 'emfrp/pre_convert/node_check'
|
4
|
-
|
5
|
-
module Emfrp
|
6
|
-
module PreConvert
|
7
|
-
extend self
|
8
|
-
|
9
|
-
PreConvertError = Class.new(CompileError)
|
10
|
-
|
11
|
-
def convert(top)
|
12
|
-
MakeNameDict.make_name_dict(top)
|
13
|
-
AlphaConvert.alpha_convert(top, top)
|
14
|
-
NodeCheck.node_check(top)
|
15
|
-
#FuncCheck - check-circular-def
|
16
|
-
#TypeCheck - check-circular-def
|
17
|
-
end
|
18
|
-
|
19
|
-
def additional_convert(top, definition)
|
20
|
-
MakeNameDict.set_dict(top[:dict], definition)
|
21
|
-
AlphaConvert.alpha_convert(top, definition)
|
22
|
-
end
|
23
|
-
|
24
|
-
def cancel(top, definition)
|
25
|
-
MakeNameDict.remove_dict(top[:dict], definition)
|
26
|
-
end
|
27
|
-
|
28
|
-
def err(code, msg, *facts)
|
29
|
-
raise PreConvertError.new(msg, code, *facts)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
1
|
+
require 'emfrp/pre_convert/make_name_dict'
|
2
|
+
require 'emfrp/pre_convert/alpha_convert'
|
3
|
+
require 'emfrp/pre_convert/node_check'
|
4
|
+
|
5
|
+
module Emfrp
|
6
|
+
module PreConvert
|
7
|
+
extend self
|
8
|
+
|
9
|
+
PreConvertError = Class.new(CompileError)
|
10
|
+
|
11
|
+
def convert(top)
|
12
|
+
MakeNameDict.make_name_dict(top)
|
13
|
+
AlphaConvert.alpha_convert(top, top)
|
14
|
+
NodeCheck.node_check(top)
|
15
|
+
#FuncCheck - check-circular-def
|
16
|
+
#TypeCheck - check-circular-def
|
17
|
+
end
|
18
|
+
|
19
|
+
def additional_convert(top, definition)
|
20
|
+
MakeNameDict.set_dict(top[:dict], definition)
|
21
|
+
AlphaConvert.alpha_convert(top, definition)
|
22
|
+
end
|
23
|
+
|
24
|
+
def cancel(top, definition)
|
25
|
+
MakeNameDict.remove_dict(top[:dict], definition)
|
26
|
+
end
|
27
|
+
|
28
|
+
def err(code, msg, *facts)
|
29
|
+
raise PreConvertError.new(msg, code, *facts)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/emfrp/syntax.rb
CHANGED
@@ -1,171 +1,171 @@
|
|
1
|
-
module Emfrp
|
2
|
-
class Syntax < Hash
|
3
|
-
def initialize(hash, hash2 = {})
|
4
|
-
self[:class] = self.class
|
5
|
-
self.merge!(hash)
|
6
|
-
self.merge!(hash2)
|
7
|
-
self[:class] = self.class
|
8
|
-
end
|
9
|
-
|
10
|
-
def [](key)
|
11
|
-
if self.has_key?(key)
|
12
|
-
self.fetch(key)
|
13
|
-
else
|
14
|
-
pp self
|
15
|
-
raise "unexist key #{key}"
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
def traverse_all_syntax(target=self, &block)
|
20
|
-
case target
|
21
|
-
when Syntax
|
22
|
-
block.call(target)
|
23
|
-
traverse_all_syntax(target.values, &block)
|
24
|
-
when Array
|
25
|
-
target.each{|e| traverse_all_syntax(e, &block)}
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
def deep_copy(x=self)
|
30
|
-
case x
|
31
|
-
when Syntax
|
32
|
-
x.class.new(Hash[x.map{|k, v| [k, deep_copy(v)]}])
|
33
|
-
when Array
|
34
|
-
x.map{|x| deep_copy(x)}
|
35
|
-
else
|
36
|
-
x
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
class Link
|
42
|
-
def initialize(syntax, name=nil)
|
43
|
-
@link = syntax
|
44
|
-
@name = name
|
45
|
-
end
|
46
|
-
|
47
|
-
def get
|
48
|
-
@link
|
49
|
-
end
|
50
|
-
|
51
|
-
def hash
|
52
|
-
@link.object_id
|
53
|
-
end
|
54
|
-
|
55
|
-
def eql?(other)
|
56
|
-
self.hash == other.hash
|
57
|
-
end
|
58
|
-
|
59
|
-
def inspect
|
60
|
-
if @name || @link.has_key?(:name)
|
61
|
-
"Link(#{@name || @link[:name][:desc]} : #{@link.class})"
|
62
|
-
else
|
63
|
-
"Link(#{@link.class})"
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
def to_s
|
68
|
-
inspect
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
class SSymbol < Syntax
|
73
|
-
def ==(other)
|
74
|
-
if other.is_a?(SSymbol)
|
75
|
-
self[:desc] == other[:desc]
|
76
|
-
else
|
77
|
-
super
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
def hash
|
82
|
-
self[:desc].hash
|
83
|
-
end
|
84
|
-
|
85
|
-
def eql?(other)
|
86
|
-
if other.is_a?(SSymbol)
|
87
|
-
self.hash == other.hash
|
88
|
-
else
|
89
|
-
super
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
def pretty_print(q)
|
94
|
-
q.text 'SSymbol(' + self[:desc] + ')'
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
class Top < Syntax
|
99
|
-
ATTRS = [
|
100
|
-
:inputs,
|
101
|
-
:outputs,
|
102
|
-
:uses,
|
103
|
-
:datas,
|
104
|
-
:funcs,
|
105
|
-
:nodes,
|
106
|
-
:types,
|
107
|
-
:infixes,
|
108
|
-
:ptypes,
|
109
|
-
:pfuncs,
|
110
|
-
:itypes,
|
111
|
-
:ifuncs,
|
112
|
-
:commands,
|
113
|
-
:newnodes,
|
114
|
-
]
|
115
|
-
|
116
|
-
def initialize(*tops)
|
117
|
-
ATTRS.each do |a|
|
118
|
-
self[a] = []
|
119
|
-
tops.each do |h|
|
120
|
-
self[a] += h[a] if h[a]
|
121
|
-
end
|
122
|
-
end
|
123
|
-
self[:module_name] = tops.map{|x| x[:module_name]}.find{|x| x}
|
124
|
-
end
|
125
|
-
|
126
|
-
def add(d)
|
127
|
-
case d
|
128
|
-
when DataDef then self[:datas] << d
|
129
|
-
when FuncDef then self[:funcs] << d
|
130
|
-
when TypeDef then self[:types] << d
|
131
|
-
else raise "assertion error: unsupported Def-type"
|
132
|
-
end
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
class Pattern < Syntax
|
137
|
-
def find_refs # -> [SSymbol]
|
138
|
-
res = []
|
139
|
-
if self[:ref]
|
140
|
-
res << self[:ref]
|
141
|
-
end
|
142
|
-
if self.has_key?(:args)
|
143
|
-
res = res + self[:args].map{|a| a.find_refs}.flatten
|
144
|
-
end
|
145
|
-
return res
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
149
|
-
AnyPattern = Class.new(Pattern)
|
150
|
-
ValuePattern = Class.new(Pattern)
|
151
|
-
IntegralPattern = Class.new(Pattern)
|
152
|
-
|
153
|
-
Types = [
|
154
|
-
:InputDef, :OutputDef, :DataDef, :FuncDef, :NodeDef, :TypeDef, :InfixDef,
|
155
|
-
:PrimTypeDef, :PrimFuncDef, :CommandDef,
|
156
|
-
:NewNodeDef,
|
157
|
-
|
158
|
-
:ParamDef, :Type, :TypeVar, :TValue, :TValueParam, :NodeConst, :ForeignExp,
|
159
|
-
|
160
|
-
:NodeRef,
|
161
|
-
|
162
|
-
# Expression
|
163
|
-
:MatchExp, :Case,
|
164
|
-
:OperatorSeq, :ParenthExp,
|
165
|
-
:FuncCall, :ValueConst, :SkipExp, :VarRef,
|
166
|
-
:LiteralChar, :LiteralIntegral, :LiteralFloating,
|
167
|
-
]
|
168
|
-
Types.each do |t|
|
169
|
-
const_set(t, Class.new(Syntax))
|
170
|
-
end
|
171
|
-
end
|
1
|
+
module Emfrp
|
2
|
+
class Syntax < Hash
|
3
|
+
def initialize(hash, hash2 = {})
|
4
|
+
self[:class] = self.class
|
5
|
+
self.merge!(hash)
|
6
|
+
self.merge!(hash2)
|
7
|
+
self[:class] = self.class
|
8
|
+
end
|
9
|
+
|
10
|
+
def [](key)
|
11
|
+
if self.has_key?(key)
|
12
|
+
self.fetch(key)
|
13
|
+
else
|
14
|
+
pp self
|
15
|
+
raise "unexist key #{key}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def traverse_all_syntax(target=self, &block)
|
20
|
+
case target
|
21
|
+
when Syntax
|
22
|
+
block.call(target)
|
23
|
+
traverse_all_syntax(target.values, &block)
|
24
|
+
when Array
|
25
|
+
target.each{|e| traverse_all_syntax(e, &block)}
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def deep_copy(x=self)
|
30
|
+
case x
|
31
|
+
when Syntax
|
32
|
+
x.class.new(Hash[x.map{|k, v| [k, deep_copy(v)]}])
|
33
|
+
when Array
|
34
|
+
x.map{|x| deep_copy(x)}
|
35
|
+
else
|
36
|
+
x
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class Link
|
42
|
+
def initialize(syntax, name=nil)
|
43
|
+
@link = syntax
|
44
|
+
@name = name
|
45
|
+
end
|
46
|
+
|
47
|
+
def get
|
48
|
+
@link
|
49
|
+
end
|
50
|
+
|
51
|
+
def hash
|
52
|
+
@link.object_id
|
53
|
+
end
|
54
|
+
|
55
|
+
def eql?(other)
|
56
|
+
self.hash == other.hash
|
57
|
+
end
|
58
|
+
|
59
|
+
def inspect
|
60
|
+
if @name || @link.has_key?(:name)
|
61
|
+
"Link(#{@name || @link[:name][:desc]} : #{@link.class})"
|
62
|
+
else
|
63
|
+
"Link(#{@link.class})"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def to_s
|
68
|
+
inspect
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
class SSymbol < Syntax
|
73
|
+
def ==(other)
|
74
|
+
if other.is_a?(SSymbol)
|
75
|
+
self[:desc] == other[:desc]
|
76
|
+
else
|
77
|
+
super
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def hash
|
82
|
+
self[:desc].hash
|
83
|
+
end
|
84
|
+
|
85
|
+
def eql?(other)
|
86
|
+
if other.is_a?(SSymbol)
|
87
|
+
self.hash == other.hash
|
88
|
+
else
|
89
|
+
super
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def pretty_print(q)
|
94
|
+
q.text 'SSymbol(' + self[:desc] + ')'
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
class Top < Syntax
|
99
|
+
ATTRS = [
|
100
|
+
:inputs,
|
101
|
+
:outputs,
|
102
|
+
:uses,
|
103
|
+
:datas,
|
104
|
+
:funcs,
|
105
|
+
:nodes,
|
106
|
+
:types,
|
107
|
+
:infixes,
|
108
|
+
:ptypes,
|
109
|
+
:pfuncs,
|
110
|
+
:itypes,
|
111
|
+
:ifuncs,
|
112
|
+
:commands,
|
113
|
+
:newnodes,
|
114
|
+
]
|
115
|
+
|
116
|
+
def initialize(*tops)
|
117
|
+
ATTRS.each do |a|
|
118
|
+
self[a] = []
|
119
|
+
tops.each do |h|
|
120
|
+
self[a] += h[a] if h[a]
|
121
|
+
end
|
122
|
+
end
|
123
|
+
self[:module_name] = tops.map{|x| x[:module_name]}.find{|x| x}
|
124
|
+
end
|
125
|
+
|
126
|
+
def add(d)
|
127
|
+
case d
|
128
|
+
when DataDef then self[:datas] << d
|
129
|
+
when FuncDef then self[:funcs] << d
|
130
|
+
when TypeDef then self[:types] << d
|
131
|
+
else raise "assertion error: unsupported Def-type"
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
class Pattern < Syntax
|
137
|
+
def find_refs # -> [SSymbol]
|
138
|
+
res = []
|
139
|
+
if self[:ref]
|
140
|
+
res << self[:ref]
|
141
|
+
end
|
142
|
+
if self.has_key?(:args)
|
143
|
+
res = res + self[:args].map{|a| a.find_refs}.flatten
|
144
|
+
end
|
145
|
+
return res
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
AnyPattern = Class.new(Pattern)
|
150
|
+
ValuePattern = Class.new(Pattern)
|
151
|
+
IntegralPattern = Class.new(Pattern)
|
152
|
+
|
153
|
+
Types = [
|
154
|
+
:InputDef, :OutputDef, :DataDef, :FuncDef, :NodeDef, :TypeDef, :InfixDef,
|
155
|
+
:PrimTypeDef, :PrimFuncDef, :CommandDef,
|
156
|
+
:NewNodeDef,
|
157
|
+
|
158
|
+
:ParamDef, :Type, :TypeVar, :TValue, :TValueParam, :NodeConst, :ForeignExp,
|
159
|
+
|
160
|
+
:NodeRef,
|
161
|
+
|
162
|
+
# Expression
|
163
|
+
:MatchExp, :Case,
|
164
|
+
:OperatorSeq, :ParenthExp,
|
165
|
+
:FuncCall, :ValueConst, :SkipExp, :VarRef,
|
166
|
+
:LiteralChar, :LiteralIntegral, :LiteralFloating,
|
167
|
+
]
|
168
|
+
Types.each do |t|
|
169
|
+
const_set(t, Class.new(Syntax))
|
170
|
+
end
|
171
|
+
end
|
@@ -1,47 +1,47 @@
|
|
1
|
-
require 'colorize'
|
2
|
-
require 'emfrp/compile_error'
|
3
|
-
|
4
|
-
module Emfrp
|
5
|
-
module Typing
|
6
|
-
class TypeMatchingError < CompileError
|
7
|
-
def initialize(code, expected_utype, real_utype, place, *factors)
|
8
|
-
@code = code
|
9
|
-
@expected_utype = expected_utype
|
10
|
-
@real_utype = real_utype
|
11
|
-
@place = place
|
12
|
-
@factors = factors
|
13
|
-
end
|
14
|
-
|
15
|
-
def code
|
16
|
-
@code
|
17
|
-
end
|
18
|
-
|
19
|
-
def print_error(output_io, file_loader)
|
20
|
-
output_io << "[Type Matching Error]".colorize(:red) + ": For #{@place}:\n"
|
21
|
-
output_io << "Expected: " + "#{@expected_utype.inspect}".colorize(:green) + "\n"
|
22
|
-
output_io << "Real: " + "#{@real_utype.inspect}".colorize(:green) + "\n"
|
23
|
-
@factors.each do |factor|
|
24
|
-
print_lexical_factor(factor, output_io, file_loader)
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
class TypeDetermineError < CompileError
|
30
|
-
def initialize(code, undetermined_utype, factor)
|
31
|
-
@code = code
|
32
|
-
@utype = undetermined_utype
|
33
|
-
@factor = factor
|
34
|
-
end
|
35
|
-
|
36
|
-
def code
|
37
|
-
@code
|
38
|
-
end
|
39
|
-
|
40
|
-
def print_error(output_io, file_loader)
|
41
|
-
output_io << "[Undetermined Type Error]".colorize(:red) + ":\n"
|
42
|
-
output_io << "Undetermined: " + "#{@utype.inspect}".colorize(:green) + "\n"
|
43
|
-
print_lexical_factor(@factor, output_io, file_loader)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
1
|
+
require 'colorize'
|
2
|
+
require 'emfrp/compile_error'
|
3
|
+
|
4
|
+
module Emfrp
|
5
|
+
module Typing
|
6
|
+
class TypeMatchingError < CompileError
|
7
|
+
def initialize(code, expected_utype, real_utype, place, *factors)
|
8
|
+
@code = code
|
9
|
+
@expected_utype = expected_utype
|
10
|
+
@real_utype = real_utype
|
11
|
+
@place = place
|
12
|
+
@factors = factors
|
13
|
+
end
|
14
|
+
|
15
|
+
def code
|
16
|
+
@code
|
17
|
+
end
|
18
|
+
|
19
|
+
def print_error(output_io, file_loader)
|
20
|
+
output_io << "[Type Matching Error]".colorize(:red) + ": For #{@place}:\n"
|
21
|
+
output_io << "Expected: " + "#{@expected_utype.inspect}".colorize(:green) + "\n"
|
22
|
+
output_io << "Real: " + "#{@real_utype.inspect}".colorize(:green) + "\n"
|
23
|
+
@factors.each do |factor|
|
24
|
+
print_lexical_factor(factor, output_io, file_loader)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class TypeDetermineError < CompileError
|
30
|
+
def initialize(code, undetermined_utype, factor)
|
31
|
+
@code = code
|
32
|
+
@utype = undetermined_utype
|
33
|
+
@factor = factor
|
34
|
+
end
|
35
|
+
|
36
|
+
def code
|
37
|
+
@code
|
38
|
+
end
|
39
|
+
|
40
|
+
def print_error(output_io, file_loader)
|
41
|
+
output_io << "[Undetermined Type Error]".colorize(:red) + ":\n"
|
42
|
+
output_io << "Undetermined: " + "#{@utype.inspect}".colorize(:green) + "\n"
|
43
|
+
print_lexical_factor(@factor, output_io, file_loader)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|