rouge 3.6.0 → 3.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -1
  3. data/lib/rouge/demos/ada +26 -0
  4. data/lib/rouge/demos/apex +9 -0
  5. data/lib/rouge/demos/armasm +12 -0
  6. data/lib/rouge/demos/batchfile +3 -0
  7. data/lib/rouge/demos/bbcbasic +6 -0
  8. data/lib/rouge/demos/clean +6 -0
  9. data/lib/rouge/demos/cmhg +8 -0
  10. data/lib/rouge/demos/csvs +8 -0
  11. data/lib/rouge/demos/cuda +11 -0
  12. data/lib/rouge/demos/cython +6 -0
  13. data/lib/rouge/demos/eex +1 -0
  14. data/lib/rouge/demos/epp +4 -0
  15. data/lib/rouge/demos/gdscript +18 -0
  16. data/lib/rouge/demos/haxe +5 -0
  17. data/lib/rouge/demos/hocon +8 -0
  18. data/lib/rouge/demos/hql +5 -0
  19. data/lib/rouge/demos/jsl +3 -0
  20. data/lib/rouge/demos/liquid +0 -1
  21. data/lib/rouge/demos/lustre +6 -0
  22. data/lib/rouge/demos/lutin +18 -0
  23. data/lib/rouge/demos/mason +22 -0
  24. data/lib/rouge/demos/msgtrans +4 -0
  25. data/lib/rouge/demos/opentype_feature_file +6 -0
  26. data/lib/rouge/demos/plist +1 -132
  27. data/lib/rouge/demos/reasonml +12 -0
  28. data/lib/rouge/demos/robot_framework +27 -0
  29. data/lib/rouge/demos/sas +13 -0
  30. data/lib/rouge/demos/sparql +6 -0
  31. data/lib/rouge/demos/terraform +0 -15
  32. data/lib/rouge/formatters/html_line_table.rb +3 -1
  33. data/lib/rouge/formatters/tex.rb +14 -12
  34. data/lib/rouge/guessers/disambiguation.rb +17 -0
  35. data/lib/rouge/lexers/ada.rb +162 -0
  36. data/lib/rouge/lexers/apex.rb +126 -0
  37. data/lib/rouge/lexers/armasm.rb +145 -0
  38. data/lib/rouge/lexers/batchfile.rb +164 -0
  39. data/lib/rouge/lexers/bbcbasic.rb +112 -0
  40. data/lib/rouge/lexers/clean.rb +156 -0
  41. data/lib/rouge/lexers/cmhg.rb +34 -0
  42. data/lib/rouge/lexers/common_lisp.rb +1 -1
  43. data/lib/rouge/lexers/console.rb +1 -1
  44. data/lib/rouge/lexers/coq.rb +12 -9
  45. data/lib/rouge/lexers/cpp.rb +4 -1
  46. data/lib/rouge/lexers/csvs.rb +44 -0
  47. data/lib/rouge/lexers/cuda.rb +35 -0
  48. data/lib/rouge/lexers/cython.rb +151 -0
  49. data/lib/rouge/lexers/eex.rb +51 -0
  50. data/lib/rouge/lexers/elixir.rb +20 -9
  51. data/lib/rouge/lexers/epp.rb +51 -0
  52. data/lib/rouge/lexers/escape.rb +3 -0
  53. data/lib/rouge/lexers/gdscript.rb +171 -0
  54. data/lib/rouge/lexers/gherkin.rb +4 -2
  55. data/lib/rouge/lexers/graphql.rb +10 -3
  56. data/lib/rouge/lexers/handlebars.rb +14 -3
  57. data/lib/rouge/lexers/haxe.rb +246 -0
  58. data/lib/rouge/lexers/hocon.rb +86 -0
  59. data/lib/rouge/lexers/hql.rb +139 -0
  60. data/lib/rouge/lexers/html.rb +2 -2
  61. data/lib/rouge/lexers/http.rb +5 -5
  62. data/lib/rouge/lexers/igorpro.rb +1 -1
  63. data/lib/rouge/lexers/javascript.rb +1 -1
  64. data/lib/rouge/lexers/jsl.rb +55 -0
  65. data/lib/rouge/lexers/json.rb +43 -5
  66. data/lib/rouge/lexers/julia.rb +1 -1
  67. data/lib/rouge/lexers/kotlin.rb +21 -28
  68. data/lib/rouge/lexers/liquid.rb +82 -108
  69. data/lib/rouge/lexers/lustre.rb +79 -0
  70. data/lib/rouge/lexers/lutin.rb +33 -0
  71. data/lib/rouge/lexers/make.rb +39 -12
  72. data/lib/rouge/lexers/mason.rb +115 -0
  73. data/lib/rouge/lexers/matlab.rb +4 -2
  74. data/lib/rouge/lexers/matlab/builtins.yml +3515 -0
  75. data/lib/rouge/lexers/msgtrans.rb +26 -0
  76. data/lib/rouge/lexers/ocaml.rb +12 -48
  77. data/lib/rouge/lexers/ocaml/common.rb +53 -0
  78. data/lib/rouge/lexers/opentype_feature_file.rb +113 -0
  79. data/lib/rouge/lexers/php.rb +31 -9
  80. data/lib/rouge/lexers/php/builtins.rb +181 -174
  81. data/lib/rouge/lexers/plain_text.rb +1 -1
  82. data/lib/rouge/lexers/puppet.rb +2 -2
  83. data/lib/rouge/lexers/q.rb +1 -1
  84. data/lib/rouge/lexers/r.rb +2 -3
  85. data/lib/rouge/lexers/reasonml.rb +65 -0
  86. data/lib/rouge/lexers/robot_framework.rb +249 -0
  87. data/lib/rouge/lexers/rust.rb +12 -9
  88. data/lib/rouge/lexers/sas.rb +563 -0
  89. data/lib/rouge/lexers/sed.rb +1 -1
  90. data/lib/rouge/lexers/shell.rb +5 -3
  91. data/lib/rouge/lexers/smarty.rb +10 -10
  92. data/lib/rouge/lexers/sparql.rb +129 -0
  93. data/lib/rouge/lexers/sql.rb +26 -6
  94. data/lib/rouge/lexers/swift.rb +1 -1
  95. data/lib/rouge/lexers/terraform.rb +8 -0
  96. data/lib/rouge/tex_theme_renderer.rb +5 -1
  97. data/lib/rouge/themes/magritte.rb +3 -3
  98. data/lib/rouge/themes/thankful_eyes.rb +1 -1
  99. data/lib/rouge/themes/tulip.rb +1 -1
  100. data/lib/rouge/version.rb +1 -1
  101. data/rouge.gemspec +4 -3
  102. metadata +61 -6
  103. data/lib/rouge/lexers/matlab/builtins.rb +0 -13
@@ -7,7 +7,7 @@ module Rouge
7
7
  title "Common Lisp"
8
8
  desc "The Common Lisp variant of Lisp (common-lisp.net)"
9
9
  tag 'common_lisp'
10
- aliases 'cl', 'common-lisp', 'elisp', 'emacs-lisp'
10
+ aliases 'cl', 'common-lisp', 'elisp', 'emacs-lisp', 'lisp'
11
11
 
12
12
  filenames '*.cl', '*.lisp', '*.asd', '*.el' # used for Elisp too
13
13
  mimetypes 'text/x-common-lisp'
@@ -116,7 +116,7 @@ module Rouge
116
116
  # before we pass to the lang lexer so it can determine where
117
117
  # the "real" beginning of the line is
118
118
  $' =~ /\A\s*/
119
- yield Text, $& unless $&.empty?
119
+ yield Text::Whitespace, $& unless $&.empty?
120
120
 
121
121
  lang_lexer.continue_lex($', &output)
122
122
  elsif comment_regex =~ input[0].strip
@@ -58,7 +58,7 @@ module Rouge
58
58
 
59
59
  def self.keyopts
60
60
  @keyopts ||= Set.new %w(
61
- := => -> /\\ \\/ _ ; :> :
61
+ := => -> /\\ \\/ _ ; :> : ⇒ → ↔ ⇔ ≔ ≡ ∀ ∃ ∧ ∨ ¬ ⊤ ⊥ ⊢ ⊨ ∈
62
62
  )
63
63
  end
64
64
 
@@ -115,14 +115,6 @@ module Rouge
115
115
  end
116
116
  rule %r(/\\), Operator
117
117
  rule %r/\\\//, Operator
118
- rule operator do |m|
119
- match = m[0]
120
- if self.class.keyopts.include? match
121
- token Punctuation
122
- else
123
- token Operator
124
- end
125
- end
126
118
 
127
119
  rule %r/-?\d[\d_]*(.[\d_]*)?(e[+-]?\d[\d_]*)/i, Num::Float
128
120
  rule %r/\d[\d_]*/, Num::Integer
@@ -131,6 +123,17 @@ module Rouge
131
123
  rule %r/'/, Keyword
132
124
  rule %r/"/, Str::Double, :string
133
125
  rule %r/[~?]#{id}/, Name::Variable
126
+
127
+ rule %r/./ do |m|
128
+ match = m[0]
129
+ if self.class.keyopts.include? match
130
+ token Punctuation
131
+ elsif match =~ operator
132
+ token Operator
133
+ else
134
+ token Error
135
+ end
136
+ end
134
137
  end
135
138
 
136
139
  state :comment do
@@ -17,7 +17,7 @@ module Rouge
17
17
  '*.cc', '*.hh',
18
18
  '*.cxx', '*.hxx',
19
19
  '*.pde', '*.ino',
20
- '*.tpp'
20
+ '*.tpp', '*.h'
21
21
  mimetypes 'text/x-c++hdr', 'text/x-c++src'
22
22
 
23
23
  def self.keywords
@@ -58,9 +58,11 @@ module Rouge
58
58
 
59
59
  prepend :statements do
60
60
  rule %r/class\b/, Keyword, :classname
61
+ rule %r/\d+(\.\d+)?(?:h|(?:min)|s|(?:ms)|(?:us)|(?:ns))/, Num::Other
61
62
  rule %r((#{dq}[.]#{dq}?|[.]#{dq})(e[+-]?#{dq}[lu]*)?)i, Num::Float
62
63
  rule %r(#{dq}e[+-]?#{dq}[lu]*)i, Num::Float
63
64
  rule %r/0x\h('?\h)*[lu]*/i, Num::Hex
65
+ rule %r/0b[01]+(?:_[01]+)*/, Num::Bin
64
66
  rule %r/0[0-7]('?[0-7])*[lu]*/i, Num::Oct
65
67
  rule %r/#{dq}[lu]*/i, Num::Integer
66
68
  rule %r/\bnullptr\b/, Name::Builtin
@@ -72,6 +74,7 @@ module Rouge
72
74
 
73
75
  # template specification
74
76
  rule %r/\s*(?=>)/m, Text, :pop!
77
+ rule %r/[.]{3}/, Operator
75
78
  mixin :whitespace
76
79
  end
77
80
  end
@@ -0,0 +1,44 @@
1
+ # -*- coding: utf-8 -*- #
2
+ # frozen_string_literal: true
3
+
4
+ module Rouge
5
+ module Lexers
6
+ class CSVS < RegexLexer
7
+ tag 'csvs'
8
+ title "csvs"
9
+ desc 'The CSV Schema Language (digital-preservation.github.io)'
10
+ filenames '*.csvs'
11
+
12
+ state :root do
13
+ rule %r/\s+/m, Text
14
+
15
+ rule %r(//[\S\t ]*), Comment::Single
16
+ rule %r(/\*[^*]*\*/)m, Comment::Multiline
17
+
18
+ rule %r/(version)( )(\d+\.\d+)/ do
19
+ groups Keyword, Text::Whitespace, Num::Float
20
+ end
21
+
22
+ rule %r/T?\d{2}:\d{2}:\d{2}(\.\d{5})?(Z|(?:[-+]\d{2}:\d{2}))?/, Literal::Date
23
+ rule %r/\d{4}-\d{2}-\d{2}/, Literal::Date
24
+ rule %r/\d{2}\/\d{2}\/\d{4}/, Literal::Date
25
+
26
+ rule %r((\d+[.]?\d*|\d*[.]\d+)(e[+-]?[0-9]+)?)i, Num::Float
27
+ rule %r/\d+/, Num::Integer
28
+
29
+ rule %r/@\w+/, Keyword::Pseudo
30
+
31
+ rule %r/[-.\w]+:/, Name::Variable
32
+ rule %r/^"[^"]+"/, Name::Variable
33
+ rule %r/\$([-.\w]+|("[^"]+"))\/?/, Name::Variable
34
+
35
+ rule %r/[A-Z]+/i, Keyword
36
+
37
+ rule %r/"[^"]*"/, Str::Double
38
+ rule %r/'[^\r\n\f']'/, Str::Char
39
+
40
+ rule %r/[,()*]/, Punctuation
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,35 @@
1
+ # -*- coding: utf-8 -*- #
2
+
3
+ module Rouge
4
+ module Lexers
5
+ load_lexer 'cpp.rb'
6
+
7
+ class CUDA < Cpp
8
+ title "CUDA"
9
+ desc "Compute Unified Device Architecture, used for programming with NVIDIA GPU"
10
+
11
+ tag 'cuda'
12
+ filenames '*.cu', '*.cuh'
13
+
14
+ def self.keywords
15
+ @keywords ||= super + Set.new(%w(
16
+ __global__ __device__ __host__ __noinline__ __forceinline__
17
+ __constant__ __shared__ __managed__ __restrict__
18
+ ))
19
+ end
20
+
21
+ def self.keywords_type
22
+ @keywords_type ||= super + Set.new(%w(
23
+ char1 char2 char3 char4 uchar1 uchar2 uchar3 uchar4
24
+ short1 short2 short3 short4 ushort1 ushort2 ushort3 ushort4
25
+ int1 int2 int3 int4 uint1 uint2 uint3 uint4
26
+ long1 long2 long3 long4 ulong1 ulong2 ulong3 ulong4
27
+ longlong1 longlong2 longlong3 longlong4
28
+ ulonglong1 ulonglong2 ulonglong3 ulonglong4
29
+ float1 float2 float3 float4 double1 double2 double3 double4
30
+ dim3
31
+ ))
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,151 @@
1
+ # -*- coding: utf-8 -*- #
2
+ # frozen_string_literal: true
3
+
4
+ module Rouge
5
+ module Lexers
6
+ load_lexer 'python.rb'
7
+
8
+ class Cython < Python
9
+ title "Cython"
10
+ desc "Cython and Pyrex source code (cython.org)"
11
+ tag 'cython'
12
+ aliases 'pyx', 'pyrex'
13
+ filenames '*.pyx', '*.pxd', '*.pxi'
14
+ mimetypes 'text/x-cython', 'application/x-cython'
15
+
16
+ def initialize(opts = {})
17
+ super opts
18
+ @indentation = nil
19
+ end
20
+
21
+ def self.keywords
22
+ @keywords ||= super + %w(
23
+ by except? fused gil nogil
24
+ )
25
+ end
26
+
27
+ def self.c_keywords
28
+ @ckeywords ||= %w(
29
+ public readonly extern api inline enum union
30
+ )
31
+ end
32
+
33
+ identifier = /[a-z_]\w*/i
34
+ dotted_identifier = /[a-z_.][\w.]*/i
35
+
36
+ prepend :root do
37
+ rule %r/cp?def|ctypedef/ do
38
+ token Keyword
39
+ push :c_definitions
40
+ push :c_start
41
+ end
42
+
43
+ rule %r/(from)((?:\\\s|\s)+)(#{dotted_identifier})((?:\\\s|\s)+)(cimport)/ do
44
+ groups Keyword::Namespace,
45
+ Text,
46
+ Name::Namespace,
47
+ Text,
48
+ Keyword::Namespace
49
+ end
50
+
51
+ rule %r/(cimport)(\s+)(#{dotted_identifier})/ do
52
+ groups Keyword::Namespace, Text, Name::Namespace
53
+ end
54
+
55
+ rule %r/(struct)((?:\\\s|\s)+)/ do
56
+ groups Keyword, Text
57
+ push :classname
58
+ end
59
+
60
+ mixin :func_call_fix
61
+
62
+ rule %r/[(,]/, Punctuation, :c_start
63
+ end
64
+
65
+ prepend :classname do
66
+ rule %r/(?:\\\s|\s)+/, Text
67
+ end
68
+
69
+ prepend :funcname do
70
+ rule %r/(?:\\\s|\s)+/, Text
71
+ end
72
+ # This is a fix for the way that function calls are lexed in the Python
73
+ # lexer. This should be moved to the Python lexer once confirmed that it
74
+ # does not cause any regressions.
75
+ state :func_call_fix do
76
+ rule %r/#{identifier}(?=\()/ do |m|
77
+ if self.class.keywords.include? m[0]
78
+ token Keyword
79
+ elsif self.class.exceptions.include? m[0]
80
+ token Name::Builtin
81
+ elsif self.class.builtins.include? m[0]
82
+ token Name::Builtin
83
+ elsif self.class.builtins_pseudo.include? m[0]
84
+ token Name::Builtin::Pseudo
85
+ else
86
+ token Name::Function
87
+ end
88
+ end
89
+ end
90
+
91
+ # The Cython lexer adds three states to those already in the Python lexer.
92
+ # Calls to `cdef`, `cpdef` and `ctypedef` move the lexer into the :c_start
93
+ # state. The primary purpose of this state is to highlight datatypes. Once
94
+ # this has been done, the lexer moves to the :c_definitions state where
95
+ # the majority of text in a definition is lexed. Finally, newlines cause
96
+ # the lexer to move to :c_indent. This state is used to check whether we
97
+ # have moved out of a C block.
98
+
99
+ state :c_start do
100
+ rule %r/[^\S\n]+/, Text
101
+
102
+ rule %r/cp?def|ctypedef/, Keyword
103
+
104
+ rule %r/(?:un)?signed/, Keyword::Type
105
+
106
+ # This rule matches identifiers that could be type declarations. The
107
+ # lookahead matches (1) pointers, (2) arrays and (3) variable names.
108
+ rule %r/#{identifier}(?=(?:\*+)|(?:[ \t]*\[)|(?:[ \t]+\w))/ do |m|
109
+ if self.class.keywords.include? m[0]
110
+ token Keyword
111
+ pop!
112
+ elsif %w(def).include? m[0]
113
+ token Keyword
114
+ goto :funcname
115
+ elsif %w(struct class).include? m[0]
116
+ token Keyword::Reserved
117
+ goto :classname
118
+ elsif self.class.c_keywords.include? m[0]
119
+ token Keyword::Reserved
120
+ else
121
+ token Keyword::Type
122
+ pop!
123
+ end
124
+ end
125
+
126
+ rule(//) { pop! }
127
+ end
128
+
129
+ state :c_definitions do
130
+ rule %r/\n/, Text, :c_indent
131
+ mixin :root
132
+ end
133
+
134
+ state :c_indent do
135
+ rule %r/[ \t]+/ do |m|
136
+ token Text
137
+ goto :c_start
138
+
139
+ if @indentation.nil?
140
+ @indentation = m[0]
141
+ elsif @indentation.length > m[0].length
142
+ @indentation = nil
143
+ pop! 2 # Pop :c_start and :c_definitions
144
+ end
145
+ end
146
+
147
+ rule(//) { @indentation = nil; reset_stack }
148
+ end
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,51 @@
1
+ # -*- coding: utf-8 -*- #
2
+
3
+ module Rouge
4
+ module Lexers
5
+ class EEX < TemplateLexer
6
+ title "EEX"
7
+ desc "Embedded Elixir"
8
+
9
+ tag 'eex'
10
+
11
+ filenames '*.eex'
12
+
13
+ def initialize(opts={})
14
+ @elixir_lexer = Elixir.new(opts)
15
+
16
+ super(opts)
17
+ end
18
+
19
+ start do
20
+ parent.reset!
21
+ @elixir_lexer.reset!
22
+ end
23
+
24
+ open = /<%%|<%=|<%#|<%/
25
+ close = /%%>|%>/
26
+
27
+ state :root do
28
+ rule %r/<%#/, Comment, :comment
29
+
30
+ rule open, Comment::Preproc, :elixir
31
+
32
+ rule %r/.+?(?=#{open})|.+/mo do
33
+ delegate parent
34
+ end
35
+ end
36
+
37
+ state :comment do
38
+ rule close, Comment, :pop!
39
+ rule %r/.+?(?=#{close})|.+/mo, Comment
40
+ end
41
+
42
+ state :elixir do
43
+ rule close, Comment::Preproc, :pop!
44
+
45
+ rule %r/.+?(?=#{close})|.+/mo do
46
+ delegate @elixir_lexer
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -51,15 +51,25 @@ module Rouge
51
51
  state :strings do
52
52
  rule %r/(%[A-Ba-z])?"""(?:.|\n)*?"""/, Str::Doc
53
53
  rule %r/'''(?:.|\n)*?'''/, Str::Doc
54
- rule %r/"/, Str::Doc, :dqs
55
- rule %r/'.*?'/, Str::Single
54
+ rule %r/"/, Str::Double, :dqs
55
+ rule %r/'/, Str::Single, :sqs
56
56
  rule %r{(?<!\w)\?(\\(x\d{1,2}|\h{1,2}(?!\h)\b|0[0-7]{0,2}(?![0-7])\b[^x0MC])|(\\[MC]-)+\w|[^\s\\])}, Str::Other
57
-
58
57
  end
59
58
 
60
59
  state :dqs do
60
+ mixin :escapes
61
+ mixin :interpoling
62
+ rule %r/[^#"\\]+/, Str::Double
61
63
  rule %r/"/, Str::Double, :pop!
62
- mixin :enddoublestr
64
+ rule %r/[#\\]/, Str::Double
65
+ end
66
+
67
+ state :sqs do
68
+ mixin :escapes
69
+ mixin :interpoling
70
+ rule %r/[^#'\\]+/, Str::Single
71
+ rule %r/'/, Str::Single, :pop!
72
+ rule %r/[#\\]/, Str::Single
63
73
  end
64
74
 
65
75
  state :interpoling do
@@ -71,17 +81,18 @@ module Rouge
71
81
  mixin :root
72
82
  end
73
83
 
84
+ state :escapes do
85
+ rule %r/\\x\h{2}/, Str::Escape
86
+ rule %r/\\u\{?\d+\}?/, Str::Escape
87
+ rule %r/\\[\\abdefnrstv0"']/, Str::Escape
88
+ end
89
+
74
90
  state :interpoling_symbol do
75
91
  rule %r/"/, Str::Symbol, :pop!
76
92
  mixin :interpoling
77
93
  rule %r/[^#"]+/, Str::Symbol
78
94
  end
79
95
 
80
- state :enddoublestr do
81
- mixin :interpoling
82
- rule %r/[^#"]+/, Str::Double
83
- end
84
-
85
96
  state :sigil_strings do
86
97
  # ~-sigiled strings
87
98
  # ~(abc), ~[abc], ~<abc>, ~|abc|, ~r/abc/, etc
@@ -0,0 +1,51 @@
1
+ # -*- coding: utf-8 -*- #
2
+
3
+ module Rouge
4
+ module Lexers
5
+ class EPP < TemplateLexer
6
+ title "EPP"
7
+ desc "Embedded Puppet template files"
8
+
9
+ tag 'epp'
10
+
11
+ filenames '*.epp'
12
+
13
+ def initialize(opts={})
14
+ super(opts)
15
+ @parent = lexer_option(:parent) { PlainText.new(opts) }
16
+ @puppet_lexer = Puppet.new(opts)
17
+ end
18
+
19
+ start do
20
+ parent.reset!
21
+ @puppet_lexer.reset!
22
+ end
23
+
24
+ open = /<%%|<%=|<%#|(<%-|<%)(\s*\|)?/
25
+ close = /%%>|(\|\s*)?(-%>|%>)/
26
+
27
+ state :root do
28
+ rule %r/<%#/, Comment, :comment
29
+
30
+ rule open, Comment::Preproc, :puppet
31
+
32
+ rule %r/.+?(?=#{open})|.+/m do
33
+ delegate parent
34
+ end
35
+ end
36
+
37
+ state :comment do
38
+ rule close, Comment, :pop!
39
+ rule %r/.+?(?=#{close})|.+/m, Comment
40
+ end
41
+
42
+ state :puppet do
43
+ rule close, Comment::Preproc, :pop!
44
+
45
+ rule %r/.+?(?=#{close})|.+/m do
46
+ delegate @puppet_lexer
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end