fdlint 0.1.3 → 0.2.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +2 -9
  3. data/Gemfile.lock +14 -4
  4. data/Rakefile +9 -85
  5. data/bin/fdlint +71 -8
  6. data/lib/fdlint/cli.rb +102 -0
  7. data/lib/{file_validator.rb → fdlint/file_validator.rb} +0 -0
  8. data/lib/fdlint/helper/code_type.rb +55 -0
  9. data/lib/fdlint/helper/file_reader.rb +19 -0
  10. data/lib/fdlint/helper/logger.rb +35 -0
  11. data/lib/fdlint/log_entry.rb +51 -0
  12. data/lib/fdlint/parser/base_parser.rb +151 -0
  13. data/lib/{css/parser.rb → fdlint/parser/css/css_parser.rb} +31 -28
  14. data/lib/{css → fdlint/parser/css}/struct.rb +5 -5
  15. data/lib/{encoding_error.rb → fdlint/parser/encoding_error.rb} +2 -2
  16. data/lib/{html/parser.rb → fdlint/parser/html/html_parser.rb} +26 -20
  17. data/lib/{html → fdlint/parser/html}/query.rb +0 -0
  18. data/lib/{html → fdlint/parser/html}/rule/check_tag_rule.rb +0 -0
  19. data/lib/{html → fdlint/parser/html}/struct.rb +82 -38
  20. data/lib/fdlint/parser/js/expr/expr.rb +71 -0
  21. data/lib/fdlint/parser/js/expr/left_hand.rb +65 -0
  22. data/lib/fdlint/parser/js/expr/operate.rb +94 -0
  23. data/lib/fdlint/parser/js/expr/primary.rb +168 -0
  24. data/lib/{js/parser.rb → fdlint/parser/js/js_parser.rb} +11 -9
  25. data/lib/fdlint/parser/js/stat/if.rb +27 -0
  26. data/lib/fdlint/parser/js/stat/iter.rb +88 -0
  27. data/lib/fdlint/parser/js/stat/stat.rb +120 -0
  28. data/lib/fdlint/parser/js/stat/switch.rb +67 -0
  29. data/lib/fdlint/parser/js/stat/try.rb +30 -0
  30. data/lib/fdlint/parser/js/stat/var.rb +42 -0
  31. data/lib/fdlint/parser/js/struct.rb +257 -0
  32. data/lib/fdlint/parser/node.rb +27 -0
  33. data/lib/fdlint/parser/parse_error.rb +10 -0
  34. data/lib/fdlint/parser/parser_visitable.rb +134 -0
  35. data/lib/{position_info.rb → fdlint/parser/position_info.rb} +12 -1
  36. data/lib/fdlint/parser.rb +3 -0
  37. data/lib/fdlint/printer/base_printer.rb +20 -0
  38. data/lib/fdlint/printer/console_printer.rb +80 -0
  39. data/lib/fdlint/printer/nocolor_printer.rb +29 -0
  40. data/lib/fdlint/printer/vim_printer.rb +21 -0
  41. data/lib/fdlint/printer.rb +1 -0
  42. data/lib/fdlint/rule/dsl.rb +83 -0
  43. data/lib/fdlint/rule/validation.rb +79 -0
  44. data/lib/fdlint/rule.rb +81 -0
  45. data/lib/fdlint/support/core/array.rb +5 -0
  46. data/lib/fdlint/support/core/file.rb +8 -0
  47. data/lib/fdlint/support/core/hash.rb +5 -0
  48. data/lib/fdlint/support/core/nil.rb +7 -0
  49. data/lib/fdlint/support/core/string.rb +7 -0
  50. data/lib/fdlint/support/core_ext.rb +5 -0
  51. data/lib/fdlint/validator.rb +102 -0
  52. data/lib/fdlint/version.rb +3 -0
  53. data/lib/fdlint.rb +5 -0
  54. data/rules.d/css.rule.rb +100 -0
  55. data/rules.d/filename.rule.rb +49 -0
  56. data/rules.d/html.rule.rb +169 -0
  57. data/rules.d/js.jquery.rule.rb +49 -0
  58. data/rules.d/js.rule.rb +205 -0
  59. data/test/default_test.rb +14 -0
  60. data/test/fixtures/js/scope-test.js +15 -2
  61. data/test/test_helper.rb +9 -0
  62. metadata +269 -221
  63. data/lib/base_parser.rb +0 -143
  64. data/lib/cmd_runner.rb +0 -145
  65. data/lib/context.rb +0 -31
  66. data/lib/css/reader.rb +0 -30
  67. data/lib/css/rule/check_compression_rule.rb +0 -48
  68. data/lib/css/rule/checklist.rb +0 -45
  69. data/lib/helper/code_type.rb +0 -50
  70. data/lib/helper/color_string.rb +0 -44
  71. data/lib/helper/file_reader.rb +0 -22
  72. data/lib/helper/strenc.rb +0 -65
  73. data/lib/js/expr/expr.rb +0 -66
  74. data/lib/js/expr/left_hand.rb +0 -63
  75. data/lib/js/expr/operate.rb +0 -92
  76. data/lib/js/expr/primary.rb +0 -166
  77. data/lib/js/rule/all.rb +0 -35
  78. data/lib/js/rule/checklist.rb +0 -41
  79. data/lib/js/rule/file_checker.rb +0 -42
  80. data/lib/js/rule/helper.rb +0 -96
  81. data/lib/js/rule/no_global.rb +0 -87
  82. data/lib/js/stat/if.rb +0 -25
  83. data/lib/js/stat/iter.rb +0 -85
  84. data/lib/js/stat/stat.rb +0 -117
  85. data/lib/js/stat/switch.rb +0 -65
  86. data/lib/js/stat/try.rb +0 -28
  87. data/lib/js/stat/var.rb +0 -40
  88. data/lib/js/struct.rb +0 -248
  89. data/lib/log_entry.rb +0 -49
  90. data/lib/node.rb +0 -28
  91. data/lib/parse_error.rb +0 -13
  92. data/lib/parser_visitable.rb +0 -138
  93. data/lib/printer/base_printer.rb +0 -24
  94. data/lib/printer/console_printer.rb +0 -66
  95. data/lib/printer/nocolor_printer.rb +0 -27
  96. data/lib/printer/vim_printer.rb +0 -19
  97. data/lib/rule.rb +0 -241
  98. data/lib/rule_helper.rb +0 -14
  99. data/lib/runner.rb +0 -225
  100. data/rules.d/css.rule +0 -127
  101. data/rules.d/html.dtd.rule +0 -22
  102. data/rules.d/html.prop.rule +0 -51
  103. data/rules.d/html.tag.rule +0 -136
  104. data/rules.d/js.file.rule +0 -13
  105. data/rules.d/js.jquery.rule +0 -56
  106. data/rules.d/js.mergefile.rule +0 -71
  107. data/rules.d/js.rule +0 -84
@@ -1,63 +0,0 @@
1
- module XRay
2
- module JS
3
- module Expr
4
-
5
- module LeftHand
6
-
7
- def parse_expr_lefthand
8
- log 'parse expr lefthand'
9
- expr = check(/new\b/) ? parse_expr_new : parse_expr_member
10
- expr.left_hand = true
11
- expr
12
- end
13
-
14
- def parse_expr_new
15
- log 'parse expr new'
16
-
17
- pos = skip /new/
18
- expr = check(/new\b/) ? parse_expr_new : parse_expr_member
19
- args = check(/\(/) ? parse_arguments_list : nil
20
-
21
- create_element Expression, 'new', expr, args, pos
22
- end
23
-
24
- def parse_expr_member
25
- log 'parse expr member'
26
- parse_expr_with_operate(:parse_expr_member_left) do
27
- if check /[.]/
28
- skip /[.]/
29
- ['.', parse_expr_identifier]
30
- elsif check /[\[]/
31
- skip /\[/
32
- expr = parse_expression
33
- skip /\]/
34
- ['[', expr]
35
- elsif check /\(/
36
- ['(', parse_arguments_list]
37
- end
38
- end
39
- end
40
-
41
- protected
42
-
43
- def parse_expr_member_left
44
- if check /function\b/
45
- create_element FunctionExpression, parse_function_declaration(true)
46
- else
47
- parse_expr_primary
48
- end
49
- end
50
-
51
-
52
- def parse_arguments_list
53
- log 'parse arguments list'
54
- skip /\(/
55
- params = batch :parse_expr_assignment, /\)/, /,/
56
- skip /\)/
57
- Elements.new params
58
- end
59
-
60
- end
61
- end
62
- end
63
- end
@@ -1,92 +0,0 @@
1
- module XRay
2
- module JS
3
- module Expr
4
-
5
- module Operate
6
-
7
- def parse_expr_postfix
8
- log 'parse expr postfix'
9
- expr = parse_expr_lefthand
10
- if check(/[ \t]*(?:\+\+|--)/, true)
11
- op = scan /\+\+|--/
12
- expr = create_element Expression, op.text, expr
13
- end
14
- expr
15
- end
16
-
17
- def parse_expr_unary
18
- log 'parse expr unary'
19
- r = /delete|void|typeof|\+\+|--|\+|-|~|!/
20
- if check r
21
- op = scan r
22
- create_element Expression, op.text, nil, parse_expr_unary
23
- else
24
- parse_expr_postfix
25
- end
26
- end
27
-
28
- def parse_expr_mul
29
- log 'parse expr mul'
30
- parse_expr_with_operate :parse_expr_unary, /(?:\*|\/|%)(?!=)/
31
- end
32
-
33
- def parse_expr_add
34
- log 'parse expr add'
35
- parse_expr_with_operate :parse_expr_mul, /(?:\+|-)(?!=)/
36
- end
37
-
38
- def parse_expr_shift
39
- log 'parse expr shift'
40
- parse_expr_with_operate :parse_expr_add, /(?:<<|>>>|>>)(?!=)/
41
- end
42
-
43
- def parse_expr_relation
44
- not_in = expr_operate_not_in?
45
- log "parse expr relational#{not_in ? '(notin)' : ''}"
46
- pattern = not_in ? (/>=|<=|>|<|\binstanceof\b/) : (/>=|<=|>|<|\binstanceof\b|\bin\b/)
47
- parse_expr_with_operate :parse_expr_shift, pattern
48
- end
49
-
50
- def parse_expr_equal
51
- log 'parse expr equal'
52
- parse_expr_with_operate :parse_expr_relation, /===|!==|==|!=/
53
- end
54
-
55
- def parse_expr_bit_and
56
- log 'parse expr bit and'
57
- parse_expr_with_operate :parse_expr_equal, /&(?![&=])/
58
- end
59
-
60
- def parse_expr_bit_xor
61
- log 'parse expr bit xor'
62
- parse_expr_with_operate :parse_expr_bit_and, /\^(?!=)/
63
- end
64
-
65
- def parse_expr_bit_or
66
- log 'parse expr bit or'
67
- parse_expr_with_operate :parse_expr_bit_xor, /\|(?![|=])/
68
- end
69
-
70
- def parse_expr_logical_and
71
- log 'parse expr logical and'
72
- parse_expr_with_operate :parse_expr_bit_or, /&&/
73
- end
74
-
75
- def parse_expr_logical_or
76
- log 'parse expr logical or'
77
- parse_expr_with_operate :parse_expr_logical_and, /\|\|/
78
- end
79
-
80
- def expr_operate_not_in?
81
- @operate_not_in || false
82
- end
83
-
84
- def expr_operate_not_in=(value)
85
- @operate_not_in = !!value
86
- end
87
-
88
- end
89
-
90
- end
91
- end
92
- end
@@ -1,166 +0,0 @@
1
- module XRay
2
- module JS
3
- module Expr
4
-
5
- module Primary
6
-
7
- R_IDENTIFY = /[a-zA-Z_$][\w$]*/
8
-
9
- RESERVED_WORDS = %w(
10
- break case catch continue debugger default delete do else
11
- finally for function if in instanceof new return switch this
12
- throw try typeof var void while with
13
- class const enum export extends import super
14
- implements interface let package private protected public static yield
15
- null true false
16
- )
17
-
18
- R_THIS_NULL_BOOLEAN = /(?:this|null|true|false)\b/
19
- R_HEX = /0[xX]/
20
- R_NUMBERIC = /[+-]?(?:\d|(?:[.]\d))/
21
- R_STRING = /['"]/
22
- R_REGEXP = /\/[^\/]/
23
-
24
- def parse_expr_primary
25
- log "parse expr primary"
26
- if check /\(/
27
- parse_expr_parentheses
28
-
29
- elsif check /\[/
30
- parse_expr_array
31
-
32
- elsif check /\{/
33
- parse_expr_object
34
-
35
- # literal
36
- elsif check R_THIS_NULL_BOOLEAN
37
- parse_expr_literal_this_null_boolean
38
-
39
- elsif check R_HEX
40
- parse_expr_literal_hex
41
-
42
- elsif check R_NUMBERIC
43
- parse_expr_literal_number
44
-
45
- elsif check R_STRING
46
- parse_expr_literal_string
47
-
48
- elsif check R_REGEXP
49
- parse_expr_literal_regexp
50
- #~
51
-
52
- else
53
- parse_expr_identifier
54
- end
55
- end
56
-
57
- def parse_expr_parentheses
58
- log 'parse expr parentheses'
59
-
60
- pos = skip /\(/
61
- expr = parse_expression
62
- skip /\)/
63
-
64
- create_expression 'parentheses', expr, pos
65
- end
66
-
67
- def parse_expr_array
68
- log 'parse expr array'
69
-
70
- pos = skip /\[/
71
- elms = batch(:parse_expr_assignment, /\]/, /,/)
72
- skip /\]/
73
-
74
- create_expression 'array', Elements.new(elms), pos
75
- end
76
-
77
- def parse_expr_object
78
- log 'parse expr object'
79
-
80
- pos = skip /\{/
81
- elms = batch(:parse_expr_object_item, /\}/, /,/)
82
- skip /\}/
83
-
84
- create_expression 'object', Elements.new(elms), pos
85
- end
86
-
87
- def parse_expr_object_item
88
- log 'parse expr object item'
89
-
90
- name = if check R_STRING
91
- parse_expr_literal_string
92
- elsif check R_NUMBERIC
93
- parse_expr_literal_number
94
- else
95
- parse_expr_identifier
96
- end
97
-
98
- skip /:/
99
- value = parse_expr_assignment
100
-
101
- Expression.new ':', name, value
102
- end
103
-
104
- def parse_expr_identifier
105
- log 'parse expr identifier'
106
-
107
- id = scan(R_IDENTIFY)
108
- RESERVED_WORDS.include?(id.text) ?
109
- parse_error("identifier can not be reserved word: #{id}") : id
110
-
111
- create_expression 'id', id
112
- end
113
-
114
- def parse_expr_literal_this_null_boolean
115
- log 'parse expr literal this null boolean'
116
- expr = scan /this|null|true|false/
117
- type = expr.text.gsub(/true|false/, 'boolean')
118
-
119
- create_expression type, expr
120
- end
121
-
122
- def parse_expr_literal_string
123
- log 'parse expr literal string'
124
-
125
- expr = if check /'/
126
- scan /'(?:(?:\\')|(?:\\\n)|[^'\n])*'/
127
- elsif check /"/
128
- scan /"(?:(?:\\")|(?:\\\n)|[^"\n])*"/
129
- else
130
- raise 'assert false'
131
- end
132
-
133
- create_expression 'string', expr
134
- end
135
-
136
- def parse_expr_literal_hex
137
- log 'parse expr literal hex'
138
- expr = scan /0[xX][0-9a-fA-F]+/
139
- create_expression 'number', expr
140
- end
141
-
142
- def parse_expr_literal_number
143
- log 'parse expr literal number'
144
- expr = scan /[+-]?(?:(?:\d*[.]\d+)|(?:\d+))(?:[eE][+-]?\d+)?/
145
- create_expression 'number', expr
146
- end
147
-
148
- def parse_expr_literal_regexp
149
- log 'parse expr literal regexp'
150
- expr = scan %r{/(?:(?:\\.)|(?:\[(?:\\.|[^\[\]])+\])|[^/\\])+/[a-z]*}
151
- create_expression 'regexp', expr
152
- end
153
-
154
- private
155
-
156
- def create_expression(*args)
157
- expr = PrimaryExpression.new *args
158
- log " #{expr}"
159
- expr
160
- end
161
-
162
- end
163
-
164
- end
165
- end
166
- end
data/lib/js/rule/all.rb DELETED
@@ -1,35 +0,0 @@
1
- require 'delegate'
2
-
3
- module XRay
4
- module JS
5
- module Rule
6
-
7
- class All < SimpleDelegator
8
-
9
- attr_reader :rules
10
-
11
- def initialize(options = {})
12
- @rules = load_sub_rules.collect do |name|
13
- klass = Rule.const_get name
14
- klass.method(:initialize).arity >= 1 ? klass.new(options) :
15
- klass.new
16
- end
17
-
18
- super @rules
19
- end
20
-
21
- def load_sub_rules
22
- Dir.glob File.expand_path( '*.rb', File.dirname(__FILE__) ) do |file|
23
- require file
24
- end
25
- XRay::JS::Rule.constants.select { |c| c.to_s.end_with? 'Rule' }
26
- end
27
-
28
-
29
- end
30
-
31
- end
32
- end
33
- end
34
-
35
-
@@ -1,41 +0,0 @@
1
- # encoding: utf-8
2
- require_relative 'helper'
3
- require_relative '../../rule'
4
-
5
- module XRay
6
- module JS
7
- module Rule
8
-
9
- class ChecklistRule
10
-
11
- include XRay::JS::Rule::Helper, XRay::Rule
12
-
13
- def visit_statement(stat)
14
- check_js_statement stat
15
- end
16
-
17
- def visit_stat_if(stat)
18
- check_js_stat_if stat
19
- end
20
-
21
- def visit_expr_member(expr)
22
- check_js_expr_member expr
23
- end
24
-
25
- def visit_expr_new(expr)
26
- check_js_expr_new expr
27
- end
28
-
29
- def visit_expr_equal(expr)
30
- check_js_expr_equal expr
31
- end
32
-
33
- def visit_stat_try(stat)
34
- check_js_stat_try stat
35
- end
36
-
37
- end
38
-
39
- end
40
- end
41
- end
@@ -1,42 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require_relative '../../helper/file_reader'
4
- require_relative 'helper'
5
- require_relative '../../rule'
6
-
7
-
8
- module XRay
9
- module JS
10
- module Rule
11
-
12
- class FileChecker
13
-
14
- include Helper, XRay::Rule
15
-
16
- def check_file(path)
17
- src = readfile(path)
18
- ret = results_to_logs check_js_file( path, src )
19
-
20
- if merge_file? path
21
- pathes = grep_import_js_path( src )
22
- ret.concat results_to_logs( check_js_merge_file( path, src, pathes ) )
23
- ret.concat results_to_logs( check_merge_imports( pathes, path ) )
24
- end
25
- ret
26
- end
27
-
28
- def check_merge_imports( pathes, path )
29
- ret = []
30
- pathes.each do |import|
31
- r = check_js_merge_importing import, pathes, path
32
- ret.concat r
33
- end
34
- ret
35
- end
36
-
37
-
38
- end
39
-
40
- end
41
- end
42
- end
@@ -1,96 +0,0 @@
1
- module XRay
2
- module JS
3
- module Rule
4
-
5
- module Helper
6
- def find_expr_member(expr)
7
- while expr.is_a?(Expression) && !yield(expr)
8
- expr = expr.left
9
- end
10
- expr && expr.is_a?(Expression) && yield(expr) ? expr : nil
11
- end
12
-
13
- def has_doc_comment?(body)
14
- body =~ /^\s*\/\*\*[^*]*\*+([^\/*][^*]*\*+)*\//
15
- end
16
-
17
- def readfile(path)
18
- text, encoding = XRay::Helper::FileReader.readfile path
19
- text
20
- end
21
-
22
- def merge_file?(path)
23
- return path =~ /\w+-merge([-_]?\d+)?\.js$/
24
- end
25
-
26
- def min_file?(path)
27
- return path =~ /\w+-min\.js$/
28
- end
29
-
30
- def func_file?(path)
31
- !merge_file?(path) and !min_file?(path)
32
- end
33
-
34
- def grep_import_js_path(body)
35
- lines = body.split /\n/
36
- pattern = /(ImportJavscript\s*\.\s*url\s*\(\s*['"]?([^'"]+)['"]?\s*\)\s*;?)/
37
-
38
- pathes = []
39
- lines.each_with_index do |line, index|
40
- line.scan(pattern) do |text, url|
41
- pathes << {
42
- :text => text,
43
- :url => url,
44
- :row => index + 1,
45
- :col => Regexp.last_match.begin(0) + 1
46
- }
47
- end
48
- end
49
- pathes
50
- end
51
-
52
-
53
- def scope(path)
54
- parts = path.split(/\\\//)
55
- if parts.include? 'global'
56
- :global
57
- elsif parts.include? 'page'
58
- :page
59
- elsif parts.include? 'lib' or parts.include? 'sys'
60
- :lib
61
- end
62
- end
63
-
64
- def global_scope?( path )
65
- scope(path) == :global
66
- end
67
-
68
- def page_scope?( path )
69
- scope(path) == :page
70
- end
71
-
72
- def lib_scope?( path )
73
- scope(path) == :lib
74
- end
75
-
76
- def relative?( path )
77
- path !~ %r(^https?://)
78
- end
79
-
80
- def app( path )
81
- sep = /(?:\\|\/)/
82
- path[/#{sep}app#{sep}(.*?)#{sep}(?:.+#{sep})*(global|page|module)(?:\\|\/)/, 1]
83
- end
84
-
85
- def results_to_logs( results )
86
- results.map do |r|
87
- msg, level, row, column = *r
88
- LogEntry.new(msg, level, row || 0, column || 0)
89
- end
90
- end
91
- end
92
-
93
- end
94
- end
95
- end
96
-
@@ -1,87 +0,0 @@
1
- # encoding: utf-8
2
-
3
- module XRay
4
- module JS
5
- module Rule
6
-
7
- class NoGlobalRule
8
-
9
- def visit_stat_var(stat)
10
- unless @scope_index > 0
11
- ['不允许使用全局变量', :error]
12
- end
13
- end
14
-
15
- def visit_stat_var_declaration(dec)
16
- scope = current_scope
17
- scope << dec.left.text
18
- nil
19
- end
20
-
21
- def visit_function_name(name)
22
- current_scope << name.text
23
- ['不允许申明全局函数', :error] if @scope_index == 0
24
- end
25
-
26
- def visit_function_parameters(params)
27
- @scope_index = (@scope_index || 0) + 1
28
- scope = current_scope(true)
29
- params.each do |param|
30
- scope << param.text
31
- end
32
- nil
33
- end
34
-
35
- def visit_stat_for(stat)
36
- first = stat.condition.first
37
- nil
38
- end
39
-
40
- def visit_function_declaration(fun)
41
- @scope_index -= 1
42
- nil
43
- end
44
-
45
- def visit_expr_assignment(expr)
46
- if expr.type == '='
47
- id = find_assignment_id(expr.left)
48
-
49
- if id && use_id_global?(id.text)
50
- ['禁止使用未定义的变量(或全局变量)', :error]
51
- end
52
- end
53
- end
54
-
55
- private
56
-
57
- def current_scope(empty = false)
58
- @scopes ||= []
59
- @scope_index ||= 0
60
- @scopes[@scope_index] = [] if empty
61
- @scopes[@scope_index] ||= []
62
- end
63
-
64
- def find_assignment_id(expr)
65
- expr.type == 'id' ? expr :
66
- expr.type == '.' && expr.left.text == 'window' &&
67
- expr.right.type == 'id' ? expr.right : nil
68
- end
69
-
70
- def use_id_global?(id)
71
- white_list = %w(ImportJavscript)
72
-
73
- return false if white_list.include? id
74
- return true unless @scopes && @scope_index
75
-
76
- @scope_index.downto(0) do |index|
77
- scope = @scopes[index]
78
- return false if scope && scope.find { |name| name == id}
79
- end
80
- true
81
- end
82
-
83
- end
84
-
85
- end
86
- end
87
- end
data/lib/js/stat/if.rb DELETED
@@ -1,25 +0,0 @@
1
- module XRay
2
- module JS
3
- module Stat
4
-
5
- module If
6
- def parse_stat_if
7
- log 'parse stat if'
8
-
9
- pos = skip /if\s*\(/
10
- condition = parse_expression
11
- skip /\)/
12
-
13
- true_part = parse_statement
14
- false_part = if check /else\b/
15
- skip /else/
16
- parse_statement
17
- end
18
-
19
- create_element IfStatement, condition, true_part, false_part, pos
20
- end
21
- end
22
-
23
- end
24
- end
25
- end