emfrp 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +45 -12
  3. data/bin/emfrp +4 -1
  4. data/examples/LCDClock/LCDClock.mfrp +93 -93
  5. data/examples/LCDClock/LCDClock_LPC1768.bin +0 -0
  6. data/examples/LCDClock/README.md +24 -24
  7. data/examples/LCDPositioner/LCDPositioner.mfrp +30 -30
  8. data/examples/LCDPositioner/LCDPositionerMain.c +15 -15
  9. data/examples/MostDistantPoint/MostDistantPoint.mfrp +25 -25
  10. data/examples/MostDistantPoint/MostDistantPointMain.c +14 -14
  11. data/lib/emfrp/compile/c/alloc.rb +200 -200
  12. data/lib/emfrp/compile/c/codegen.rb +18 -18
  13. data/lib/emfrp/compile/c/codegen_context.rb +218 -218
  14. data/lib/emfrp/compile/c/monofy.rb +185 -185
  15. data/lib/emfrp/compile/c/syntax_codegen.rb +364 -364
  16. data/lib/emfrp/compile/c/syntax_exp_codegen.rb +119 -119
  17. data/lib/emfrp/compile/graphviz/graphviz.rb +53 -53
  18. data/lib/emfrp/compile_error.rb +95 -95
  19. data/lib/emfrp/interpreter/command_manager.rb +367 -367
  20. data/lib/emfrp/interpreter/evaluater.rb +146 -146
  21. data/lib/emfrp/interpreter/file_loader.rb +52 -52
  22. data/lib/emfrp/interpreter/interpreter.rb +200 -195
  23. data/lib/emfrp/parser/expression.rb +386 -386
  24. data/lib/emfrp/parser/misc.rb +184 -184
  25. data/lib/emfrp/parser/newnode_convert.rb +72 -72
  26. data/lib/emfrp/parser/operator.rb +25 -25
  27. data/lib/emfrp/parser/parser.rb +150 -150
  28. data/lib/emfrp/parser/parsing_error.rb +49 -49
  29. data/lib/emfrp/parser/toplevel.rb +555 -555
  30. data/lib/emfrp/pre_convert/pre_convert.rb +32 -32
  31. data/lib/emfrp/syntax.rb +171 -171
  32. data/lib/emfrp/typing/typing_error.rb +47 -47
  33. data/lib/emfrp/typing/union_type.rb +197 -197
  34. data/lib/emfrp/version.rb +1 -1
  35. data/mfrp_include/Std.mfrp +122 -122
  36. data/tests/Rakefile +8 -8
  37. data/tests/Rakefile.common +27 -27
  38. data/tests/command/Rakefile +2 -2
  39. data/tests/command/ReplaceNode.mfrp +39 -39
  40. data/tests/compiler/ComplexDataType/ComplexDataType.mfrp +14 -14
  41. data/tests/compiler/ComplexDataType/ComplexDataTypeMain.c +15 -15
  42. data/tests/compiler/ComplexDataType/Rakefile +2 -2
  43. data/tests/compiler/ComplexDataType/expected_out.txt +0 -0
  44. data/tests/compiler/ComplexDataType/in.txt +5 -5
  45. data/tests/compiler/LCDClock/LCDClock.mfrp +90 -90
  46. data/tests/compiler/LCDClock/LCDClockMain.c +0 -0
  47. data/tests/compiler/LCDClock/Rakefile +2 -2
  48. data/tests/compiler/LCDClock/expected_out.txt +0 -0
  49. data/tests/compiler/LCDClock/in.txt +0 -0
  50. data/tests/compiler/LCDPositioner/LCDPositioner.mfrp +30 -30
  51. data/tests/compiler/LCDPositioner/LCDPositionerMain.c +15 -15
  52. data/tests/compiler/LCDPositioner/Rakefile +2 -2
  53. data/tests/compiler/LCDPositioner/graph.dot +0 -0
  54. data/tests/compiler/LCDPositioner/graph.png +0 -0
  55. data/tests/compiler/Rakefile +8 -8
  56. data/tests/compiler/Rakefile.common +23 -23
  57. data/tests/compiler/UseData/Rakefile +2 -2
  58. data/tests/compiler/UseData/UseData.mfrp +8 -8
  59. data/tests/compiler/UseSubModule/Rakefile +2 -2
  60. data/tests/compiler/UseSubModule/SubModule.mfrp +8 -8
  61. data/tests/compiler/UseSubModule/SubModule2.mfrp +5 -5
  62. data/tests/compiler/UseSubModule/UseSubModule.mfrp +11 -11
  63. data/tests/core/FromAnnotation.mfrp +18 -18
  64. data/tests/core/Last.mfrp +10 -10
  65. data/tests/core/Rakefile +2 -2
  66. data/tests/core/TypingTest.mfrp +11 -11
  67. data/tests/core/WithoutInputs.mfrp +19 -19
  68. data/tests/load_time_error/Rakefile +32 -32
  69. data/tests/load_time_error/TypeMismatch.mfrp +4 -4
  70. metadata +3 -3
@@ -1,119 +1,119 @@
1
- require 'emfrp/syntax'
2
-
3
- module Emfrp
4
- class FuncCall
5
- def codegen(ct, stmts)
6
- name = ct.func_name(self[:name][:desc], self[:typing], self[:args].map{|x| x[:typing]})
7
- "#{name}(#{self[:args].map{|x| x.codegen(ct, stmts)}.join(", ")})"
8
- end
9
- end
10
-
11
- class ValueConst
12
- def codegen(ct, stmts)
13
- if ct.tdef(self[:typing]).enum?(ct)
14
- ct.tdef(self[:typing])[:tvalues].index{|t| t[:name] == self[:name]}.to_s
15
- else
16
- name = ct.constructor_name(self[:name][:desc], self[:typing])
17
- "#{name}(#{self[:args].map{|x| x.codegen(ct, stmts)}.join(", ")})"
18
- end
19
- end
20
- end
21
-
22
- class ParenthExp
23
- def codegen(ct, stmts)
24
- self[:exp].codegen(ct, stmts)
25
- end
26
- end
27
-
28
- class LiteralIntegral
29
- def codegen(ct, stmts)
30
- self[:entity][:desc]
31
- end
32
- end
33
-
34
- class LiteralFloating
35
- def codegen(ct, stmts)
36
- self[:entity][:desc]
37
- end
38
- end
39
-
40
- class LitaralChar
41
- def codegen(ct, stmts)
42
- self[:entity][:desc]
43
- end
44
- end
45
-
46
- class VarRef
47
- def codegen(ct, stmts)
48
- self[:binder].get.var_name(ct, self[:name][:desc])
49
- end
50
- end
51
-
52
- class MatchExp
53
- def codegen(ct, stmts)
54
- vname = "_tmp%03d" % ct.uniq_id_gen
55
- stmts << "#{ct.tref(self)} #{vname};"
56
- left = self[:exp]
57
- if left.is_a?(VarRef)
58
- left_vname = left[:name][:desc]
59
- else
60
- left_vname = "_tmp%03d" % ct.uniq_id_gen
61
- stmts.unshift "#{ct.tref(left)} #{left_vname};"
62
- stmts.push "#{left_vname} = #{left.codegen(ct, stmts)};"
63
- end
64
- self[:cases].each_with_index do |c, i|
65
- then_stmts = []
66
- cond_exps = pattern_to_cond_exps(ct, left_vname, then_stmts, c, c[:pattern])
67
- cond_exp = cond_exps.size == 0 ? "1" : cond_exps.join(" && ")
68
- if c[:exp].is_a?(SkipExp)
69
- then_stmts << "return 0;"
70
- else
71
- then_stmts << "#{vname} = #{c[:exp].codegen(ct, then_stmts)};"
72
- end
73
- if i == 0
74
- stmts << ct.make_block("if (#{cond_exp}) {", then_stmts, "}")
75
- else
76
- stmts << ct.make_block("else if (#{cond_exp}) {", then_stmts, "}")
77
- end
78
- end
79
- return vname
80
- end
81
-
82
- def pattern_to_cond_exps(ct, receiver, stmts, case_def, pattern)
83
- if pattern[:ref]
84
- vname = case_def.var_name(ct, pattern[:ref][:desc])
85
- stmts << "#{ct.tref(pattern)} #{vname} = #{receiver};"
86
- end
87
- case pattern
88
- when ValuePattern
89
- conds = []
90
- type_def = ct.tdef(pattern)
91
- accessor = type_def[:static] ? "." : "->"
92
- if type_def[:tvalues].size > 1
93
- tvalue_id = type_def[:tvalues].index{|x| x[:name] == pattern[:name]}
94
- if type_def.enum?(ct)
95
- conds << "#{receiver} == #{tvalue_id}"
96
- else
97
- conds << "#{receiver}" + accessor + "tvalue_type == " + tvalue_id.to_s
98
- end
99
- end
100
- new_receiver = "#{receiver}" + accessor + "value." + pattern[:name][:desc]
101
- pattern[:args].each_with_index do |x, i|
102
- conds += pattern_to_cond_exps(ct, new_receiver + ".member#{i}", stmts, case_def, x)
103
- end
104
- return conds
105
- when IntegralPattern
106
- return ["#{receiver} == #{pattern[:val][:entity][:desc]}"]
107
- else
108
- return []
109
- end
110
- end
111
- end
112
-
113
- class Case
114
- def var_name(ct, name)
115
- "pvar#{ct.serial(nil, self)}_" + ct.escape_name(name)
116
- end
117
- end
118
-
119
- end
1
+ require 'emfrp/syntax'
2
+
3
+ module Emfrp
4
+ class FuncCall
5
+ def codegen(ct, stmts)
6
+ name = ct.func_name(self[:name][:desc], self[:typing], self[:args].map{|x| x[:typing]})
7
+ "#{name}(#{self[:args].map{|x| x.codegen(ct, stmts)}.join(", ")})"
8
+ end
9
+ end
10
+
11
+ class ValueConst
12
+ def codegen(ct, stmts)
13
+ if ct.tdef(self[:typing]).enum?(ct)
14
+ ct.tdef(self[:typing])[:tvalues].index{|t| t[:name] == self[:name]}.to_s
15
+ else
16
+ name = ct.constructor_name(self[:name][:desc], self[:typing])
17
+ "#{name}(#{self[:args].map{|x| x.codegen(ct, stmts)}.join(", ")})"
18
+ end
19
+ end
20
+ end
21
+
22
+ class ParenthExp
23
+ def codegen(ct, stmts)
24
+ self[:exp].codegen(ct, stmts)
25
+ end
26
+ end
27
+
28
+ class LiteralIntegral
29
+ def codegen(ct, stmts)
30
+ self[:entity][:desc]
31
+ end
32
+ end
33
+
34
+ class LiteralFloating
35
+ def codegen(ct, stmts)
36
+ self[:entity][:desc]
37
+ end
38
+ end
39
+
40
+ class LitaralChar
41
+ def codegen(ct, stmts)
42
+ self[:entity][:desc]
43
+ end
44
+ end
45
+
46
+ class VarRef
47
+ def codegen(ct, stmts)
48
+ self[:binder].get.var_name(ct, self[:name][:desc])
49
+ end
50
+ end
51
+
52
+ class MatchExp
53
+ def codegen(ct, stmts)
54
+ vname = "_tmp%03d" % ct.uniq_id_gen
55
+ stmts << "#{ct.tref(self)} #{vname};"
56
+ left = self[:exp]
57
+ if left.is_a?(VarRef)
58
+ left_vname = ct.escape_name(left[:name][:desc])
59
+ else
60
+ left_vname = "_tmp%03d" % ct.uniq_id_gen
61
+ stmts.unshift "#{ct.tref(left)} #{left_vname};"
62
+ stmts.push "#{left_vname} = #{left.codegen(ct, stmts)};"
63
+ end
64
+ self[:cases].each_with_index do |c, i|
65
+ then_stmts = []
66
+ cond_exps = pattern_to_cond_exps(ct, left_vname, then_stmts, c, c[:pattern])
67
+ cond_exp = cond_exps.size == 0 ? "1" : cond_exps.join(" && ")
68
+ if c[:exp].is_a?(SkipExp)
69
+ then_stmts << "return 0;"
70
+ else
71
+ then_stmts << "#{vname} = #{c[:exp].codegen(ct, then_stmts)};"
72
+ end
73
+ if i == 0
74
+ stmts << ct.make_block("if (#{cond_exp}) {", then_stmts, "}")
75
+ else
76
+ stmts << ct.make_block("else if (#{cond_exp}) {", then_stmts, "}")
77
+ end
78
+ end
79
+ return vname
80
+ end
81
+
82
+ def pattern_to_cond_exps(ct, receiver, stmts, case_def, pattern)
83
+ if pattern[:ref]
84
+ vname = case_def.var_name(ct, pattern[:ref][:desc])
85
+ stmts << "#{ct.tref(pattern)} #{vname} = #{receiver};"
86
+ end
87
+ case pattern
88
+ when ValuePattern
89
+ conds = []
90
+ type_def = ct.tdef(pattern)
91
+ accessor = type_def[:static] ? "." : "->"
92
+ if type_def[:tvalues].size > 1
93
+ tvalue_id = type_def[:tvalues].index{|x| x[:name] == pattern[:name]}
94
+ if type_def.enum?(ct)
95
+ conds << "#{receiver} == #{tvalue_id}"
96
+ else
97
+ conds << "#{receiver}" + accessor + "tvalue_type == " + tvalue_id.to_s
98
+ end
99
+ end
100
+ new_receiver = "#{receiver}" + accessor + "value." + pattern[:name][:desc]
101
+ pattern[:args].each_with_index do |x, i|
102
+ conds += pattern_to_cond_exps(ct, new_receiver + ".member#{i}", stmts, case_def, x)
103
+ end
104
+ return conds
105
+ when IntegralPattern
106
+ return ["#{receiver} == #{pattern[:val][:entity][:desc]}"]
107
+ else
108
+ return []
109
+ end
110
+ end
111
+ end
112
+
113
+ class Case
114
+ def var_name(ct, name)
115
+ "pvar#{ct.serial(nil, self)}_" + ct.escape_name(name)
116
+ end
117
+ end
118
+
119
+ end
@@ -1,53 +1,53 @@
1
- module Emfrp
2
- module Graphviz
3
- extend self
4
-
5
- def compile(top, output_io)
6
- node_stmts, edge_stmts = [], []
7
- visited = {}
8
- top[:outputs].each do |n|
9
- node = top[:dict][:node_space][n[:name][:desc]].get
10
- traverse(top, node, node_stmts, edge_stmts, visited)
11
- end
12
- output_io << "digraph #{top[:module_name][:desc]} {\n"
13
- node_stmts.each do |s|
14
- output_io << " #{s}\n"
15
- end
16
- edge_stmts.each do |s|
17
- output_io << " #{s}\n"
18
- end
19
- output_io << "}\n"
20
- end
21
-
22
- def traverse(top, node, node_stmts, edge_stmts, visited)
23
- return if visited[node]
24
- visited[node] = true
25
- name = node[:name][:desc]
26
- type = node[:typing].to_uniq_str
27
- node_attrs = ["label = \"#{name} : #{type}\""]
28
- case node
29
- when NodeDef
30
- if top[:outputs].find{|o| o[:name] == node[:name]}
31
- node_attrs += ["style = filled", "fillcolor = \"#e4e4e4\""]
32
- end
33
- node[:params].each do |n|
34
- ch_name = n[:name][:desc]
35
- if n[:last]
36
- edge_stmts << "#{escape_name(ch_name)} -> #{escape_name(name)} [style = dashed];"
37
- else
38
- edge_stmts << "#{escape_name(ch_name)} -> #{escape_name(name)};"
39
- end
40
- ch_node = top[:dict][:node_space][ch_name].get
41
- traverse(top, ch_node, node_stmts, edge_stmts, visited)
42
- end
43
- when InputDef
44
- node_attrs << "shape = \"invhouse\""
45
- end
46
- node_stmts << "#{escape_name(name)} [#{node_attrs.join(", ")}];"
47
- end
48
-
49
- def escape_name(name)
50
- CodegenContext.new(nil).escape_name(name)
51
- end
52
- end
53
- end
1
+ module Emfrp
2
+ module Graphviz
3
+ extend self
4
+
5
+ def compile(top, output_io)
6
+ node_stmts, edge_stmts = [], []
7
+ visited = {}
8
+ top[:outputs].each do |n|
9
+ node = top[:dict][:node_space][n[:name][:desc]].get
10
+ traverse(top, node, node_stmts, edge_stmts, visited)
11
+ end
12
+ output_io << "digraph #{top[:module_name][:desc]} {\n"
13
+ node_stmts.each do |s|
14
+ output_io << " #{s}\n"
15
+ end
16
+ edge_stmts.each do |s|
17
+ output_io << " #{s}\n"
18
+ end
19
+ output_io << "}\n"
20
+ end
21
+
22
+ def traverse(top, node, node_stmts, edge_stmts, visited)
23
+ return if visited[node]
24
+ visited[node] = true
25
+ name = node[:name][:desc]
26
+ type = node[:typing].to_uniq_str
27
+ node_attrs = ["label = \"#{name} : #{type}\""]
28
+ case node
29
+ when NodeDef
30
+ if top[:outputs].find{|o| o[:name] == node[:name]}
31
+ node_attrs += ["style = filled", "fillcolor = \"#e4e4e4\""]
32
+ end
33
+ node[:params].each do |n|
34
+ ch_name = n[:name][:desc]
35
+ if n[:last]
36
+ edge_stmts << "#{escape_name(ch_name)} -> #{escape_name(name)} [style = dashed];"
37
+ else
38
+ edge_stmts << "#{escape_name(ch_name)} -> #{escape_name(name)};"
39
+ end
40
+ ch_node = top[:dict][:node_space][ch_name].get
41
+ traverse(top, ch_node, node_stmts, edge_stmts, visited)
42
+ end
43
+ when InputDef
44
+ node_attrs << "shape = \"invhouse\""
45
+ end
46
+ node_stmts << "#{escape_name(name)} [#{node_attrs.join(", ")}];"
47
+ end
48
+
49
+ def escape_name(name)
50
+ CodegenContext.new(nil).escape_name(name)
51
+ end
52
+ end
53
+ end
@@ -1,95 +1,95 @@
1
- require 'colorize'
2
-
3
- module Emfrp
4
- class CompileError < RuntimeError
5
- attr_reader :message, :factors
6
-
7
- def initialize(message, code, *factors)
8
- @message = message
9
- @code = message
10
- @factors = factors
11
- end
12
-
13
- def code
14
- @code
15
- end
16
-
17
- def tag_comp(a, b)
18
- [a[:line_number], a[:column_number]] <=> [b[:line_number], b[:column_number]]
19
- end
20
-
21
- def collect_factor_tags(factor)
22
- case factor
23
- when Syntax
24
- if factor.has_key?(:start_pos)
25
- collect_factor_tags(factor.values) + [[factor[:start_pos], factor[:end_pos]]]
26
- else
27
- collect_factor_tags(factor.values)
28
- end
29
- when Array
30
- factor.flat_map{|x| collect_factor_tags(x)}
31
- else
32
- []
33
- end
34
- end
35
-
36
- def find_factor_file_name(factor)
37
- case factor
38
- when Syntax
39
- if factor.has_key?(:start_pos)
40
- return factor[:start_pos][:document_name]
41
- else
42
- return find_factor_file_name(factor.values)
43
- end
44
- when Array
45
- factor.each do |x|
46
- if res = find_factor_file_name(x)
47
- return res
48
- end
49
- end
50
- end
51
- nil
52
- end
53
-
54
- def factor_name(factor)
55
- klass = factor.class.name.split("::").last
56
- if factor.is_a?(Syntax)
57
- name = factor.has_key?(:name) ? "`#{factor[:name][:desc]}`" : ""
58
- else
59
- name = factor.inspect
60
- end
61
- klass + " " + name
62
- end
63
-
64
- def print_lexical_factor(factor, output_io, file_loader)
65
- src_lines = nil
66
- unless factor_file_name = find_factor_file_name(factor)
67
- raise "assertion error: cannot detect file_name"
68
- end
69
- src_str = file_loader.get_src_from_full_path(factor_file_name)
70
- src_lines = src_str.each_line.to_a
71
- tags = collect_factor_tags(factor)
72
- spos = tags.map{|x| x[0]}.min{|a, b| tag_comp(a, b)}
73
- epos = tags.map{|x| x[1]}.max{|a, b| tag_comp(a, b)}
74
- line_nums = (spos[:line_number]..epos[:line_number]).to_a
75
- if line_nums.size == 1
76
- output_io << "#{factor_file_name}:#{line_nums[0]}:\n"
77
- output_io << "> " + src_lines[line_nums[0] - 1].chomp + "\n"
78
- output_io << " " + " " * (spos[:column_number] - 1)
79
- output_io << ("^" * (epos[:column_number] - spos[:column_number] + 1)).colorize(:green) + "\n"
80
- else
81
- output_io << "#{factor_file_name}:#{line_nums.first}-#{line_nums.last}:\n"
82
- line_nums.each do |line_num|
83
- output_io << "> " + src_lines[line_num - 1]
84
- end
85
- end
86
- end
87
-
88
- def print_error(output_io, file_loader)
89
- output_io << "\e[31m[Error]\e[m: #{@message}"
90
- @factors.each do |factor|
91
- print_lexical_factor(factor, output_io, file_loader)
92
- end
93
- end
94
- end
95
- end
1
+ require 'colorize'
2
+
3
+ module Emfrp
4
+ class CompileError < RuntimeError
5
+ attr_reader :message, :factors
6
+
7
+ def initialize(message, code, *factors)
8
+ @message = message
9
+ @code = message
10
+ @factors = factors
11
+ end
12
+
13
+ def code
14
+ @code
15
+ end
16
+
17
+ def tag_comp(a, b)
18
+ [a[:line_number], a[:column_number]] <=> [b[:line_number], b[:column_number]]
19
+ end
20
+
21
+ def collect_factor_tags(factor)
22
+ case factor
23
+ when Syntax
24
+ if factor.has_key?(:start_pos)
25
+ collect_factor_tags(factor.values) + [[factor[:start_pos], factor[:end_pos]]]
26
+ else
27
+ collect_factor_tags(factor.values)
28
+ end
29
+ when Array
30
+ factor.flat_map{|x| collect_factor_tags(x)}
31
+ else
32
+ []
33
+ end
34
+ end
35
+
36
+ def find_factor_file_name(factor)
37
+ case factor
38
+ when Syntax
39
+ if factor.has_key?(:start_pos)
40
+ return factor[:start_pos][:document_name]
41
+ else
42
+ return find_factor_file_name(factor.values)
43
+ end
44
+ when Array
45
+ factor.each do |x|
46
+ if res = find_factor_file_name(x)
47
+ return res
48
+ end
49
+ end
50
+ end
51
+ nil
52
+ end
53
+
54
+ def factor_name(factor)
55
+ klass = factor.class.name.split("::").last
56
+ if factor.is_a?(Syntax)
57
+ name = factor.has_key?(:name) ? "`#{factor[:name][:desc]}`" : ""
58
+ else
59
+ name = factor.inspect
60
+ end
61
+ klass + " " + name
62
+ end
63
+
64
+ def print_lexical_factor(factor, output_io, file_loader)
65
+ src_lines = nil
66
+ unless factor_file_name = find_factor_file_name(factor)
67
+ raise "assertion error: cannot detect file_name"
68
+ end
69
+ src_str = file_loader.get_src_from_full_path(factor_file_name)
70
+ src_lines = src_str.each_line.to_a
71
+ tags = collect_factor_tags(factor)
72
+ spos = tags.map{|x| x[0]}.min{|a, b| tag_comp(a, b)}
73
+ epos = tags.map{|x| x[1]}.max{|a, b| tag_comp(a, b)}
74
+ line_nums = (spos[:line_number]..epos[:line_number]).to_a
75
+ if line_nums.size == 1
76
+ output_io << "#{factor_file_name}:#{line_nums[0]}:\n"
77
+ output_io << "> " + src_lines[line_nums[0] - 1].chomp + "\n"
78
+ output_io << " " + " " * (spos[:column_number] - 1)
79
+ output_io << ("^" * (epos[:column_number] - spos[:column_number] + 1)).colorize(:green) + "\n"
80
+ else
81
+ output_io << "#{factor_file_name}:#{line_nums.first}-#{line_nums.last}:\n"
82
+ line_nums.each do |line_num|
83
+ output_io << "> " + src_lines[line_num - 1]
84
+ end
85
+ end
86
+ end
87
+
88
+ def print_error(output_io, file_loader)
89
+ output_io << "\e[31m[Error]\e[m: #{@message}"
90
+ @factors.each do |factor|
91
+ print_lexical_factor(factor, output_io, file_loader)
92
+ end
93
+ end
94
+ end
95
+ end