gitlab-rouge 1.9.2
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 +7 -0
- data/Gemfile +23 -0
- data/LICENSE +186 -0
- data/bin/rougify +16 -0
- data/gitlab-rouge.gemspec +17 -0
- data/lib/rouge.rb +57 -0
- data/lib/rouge/cli.rb +363 -0
- data/lib/rouge/demos/apache +21 -0
- data/lib/rouge/demos/applescript +2 -0
- data/lib/rouge/demos/c +8 -0
- data/lib/rouge/demos/clojure +5 -0
- data/lib/rouge/demos/coffeescript +5 -0
- data/lib/rouge/demos/common_lisp +1 -0
- data/lib/rouge/demos/conf +4 -0
- data/lib/rouge/demos/cpp +8 -0
- data/lib/rouge/demos/csharp +5 -0
- data/lib/rouge/demos/css +4 -0
- data/lib/rouge/demos/dart +6 -0
- data/lib/rouge/demos/diff +7 -0
- data/lib/rouge/demos/elixir +1 -0
- data/lib/rouge/demos/erb +1 -0
- data/lib/rouge/demos/erlang +7 -0
- data/lib/rouge/demos/factor +5 -0
- data/lib/rouge/demos/gherkin +17 -0
- data/lib/rouge/demos/glsl +14 -0
- data/lib/rouge/demos/go +7 -0
- data/lib/rouge/demos/groovy +9 -0
- data/lib/rouge/demos/haml +5 -0
- data/lib/rouge/demos/handlebars +7 -0
- data/lib/rouge/demos/haskell +6 -0
- data/lib/rouge/demos/html +8 -0
- data/lib/rouge/demos/http +14 -0
- data/lib/rouge/demos/ini +4 -0
- data/lib/rouge/demos/io +11 -0
- data/lib/rouge/demos/java +5 -0
- data/lib/rouge/demos/javascript +1 -0
- data/lib/rouge/demos/json +1 -0
- data/lib/rouge/demos/json-doc +1 -0
- data/lib/rouge/demos/liquid +11 -0
- data/lib/rouge/demos/literate_coffeescript +3 -0
- data/lib/rouge/demos/literate_haskell +7 -0
- data/lib/rouge/demos/llvm +20 -0
- data/lib/rouge/demos/lua +12 -0
- data/lib/rouge/demos/make +6 -0
- data/lib/rouge/demos/markdown +4 -0
- data/lib/rouge/demos/matlab +6 -0
- data/lib/rouge/demos/moonscript +16 -0
- data/lib/rouge/demos/nginx +5 -0
- data/lib/rouge/demos/nim +27 -0
- data/lib/rouge/demos/objective_c +14 -0
- data/lib/rouge/demos/ocaml +12 -0
- data/lib/rouge/demos/perl +5 -0
- data/lib/rouge/demos/php +3 -0
- data/lib/rouge/demos/plaintext +1 -0
- data/lib/rouge/demos/powershell +49 -0
- data/lib/rouge/demos/prolog +9 -0
- data/lib/rouge/demos/properties +7 -0
- data/lib/rouge/demos/puppet +6 -0
- data/lib/rouge/demos/python +6 -0
- data/lib/rouge/demos/qml +9 -0
- data/lib/rouge/demos/r +8 -0
- data/lib/rouge/demos/racket +24 -0
- data/lib/rouge/demos/ruby +9 -0
- data/lib/rouge/demos/rust +12 -0
- data/lib/rouge/demos/sass +3 -0
- data/lib/rouge/demos/scala +3 -0
- data/lib/rouge/demos/scheme +4 -0
- data/lib/rouge/demos/scss +5 -0
- data/lib/rouge/demos/sed +4 -0
- data/lib/rouge/demos/shell +2 -0
- data/lib/rouge/demos/slim +17 -0
- data/lib/rouge/demos/smalltalk +6 -0
- data/lib/rouge/demos/sml +4 -0
- data/lib/rouge/demos/sql +1 -0
- data/lib/rouge/demos/swift +5 -0
- data/lib/rouge/demos/tcl +1 -0
- data/lib/rouge/demos/tex +1 -0
- data/lib/rouge/demos/toml +9 -0
- data/lib/rouge/demos/tulip +14 -0
- data/lib/rouge/demos/vb +4 -0
- data/lib/rouge/demos/viml +5 -0
- data/lib/rouge/demos/xml +2 -0
- data/lib/rouge/demos/yaml +4 -0
- data/lib/rouge/formatter.rb +50 -0
- data/lib/rouge/formatters/html.rb +117 -0
- data/lib/rouge/formatters/null.rb +19 -0
- data/lib/rouge/formatters/terminal256.rb +176 -0
- data/lib/rouge/lexer.rb +443 -0
- data/lib/rouge/lexers/apache.rb +68 -0
- data/lib/rouge/lexers/apache/keywords.yml +453 -0
- data/lib/rouge/lexers/apple_script.rb +367 -0
- data/lib/rouge/lexers/c.rb +212 -0
- data/lib/rouge/lexers/clojure.rb +112 -0
- data/lib/rouge/lexers/coffeescript.rb +174 -0
- data/lib/rouge/lexers/common_lisp.rb +345 -0
- data/lib/rouge/lexers/conf.rb +24 -0
- data/lib/rouge/lexers/cpp.rb +66 -0
- data/lib/rouge/lexers/csharp.rb +88 -0
- data/lib/rouge/lexers/css.rb +271 -0
- data/lib/rouge/lexers/dart.rb +104 -0
- data/lib/rouge/lexers/diff.rb +31 -0
- data/lib/rouge/lexers/elixir.rb +108 -0
- data/lib/rouge/lexers/erb.rb +56 -0
- data/lib/rouge/lexers/erlang.rb +118 -0
- data/lib/rouge/lexers/factor.rb +302 -0
- data/lib/rouge/lexers/gherkin.rb +137 -0
- data/lib/rouge/lexers/gherkin/keywords.rb +14 -0
- data/lib/rouge/lexers/glsl.rb +135 -0
- data/lib/rouge/lexers/go.rb +178 -0
- data/lib/rouge/lexers/groovy.rb +104 -0
- data/lib/rouge/lexers/haml.rb +228 -0
- data/lib/rouge/lexers/handlebars.rb +79 -0
- data/lib/rouge/lexers/haskell.rb +183 -0
- data/lib/rouge/lexers/html.rb +94 -0
- data/lib/rouge/lexers/http.rb +80 -0
- data/lib/rouge/lexers/ini.rb +57 -0
- data/lib/rouge/lexers/io.rb +68 -0
- data/lib/rouge/lexers/java.rb +76 -0
- data/lib/rouge/lexers/javascript.rb +297 -0
- data/lib/rouge/lexers/liquid.rb +287 -0
- data/lib/rouge/lexers/literate_coffeescript.rb +33 -0
- data/lib/rouge/lexers/literate_haskell.rb +36 -0
- data/lib/rouge/lexers/llvm.rb +84 -0
- data/lib/rouge/lexers/lua.rb +122 -0
- data/lib/rouge/lexers/lua/builtins.rb +22 -0
- data/lib/rouge/lexers/make.rb +116 -0
- data/lib/rouge/lexers/markdown.rb +154 -0
- data/lib/rouge/lexers/matlab.rb +74 -0
- data/lib/rouge/lexers/matlab/builtins.rb +11 -0
- data/lib/rouge/lexers/moonscript.rb +110 -0
- data/lib/rouge/lexers/nginx.rb +71 -0
- data/lib/rouge/lexers/nim.rb +152 -0
- data/lib/rouge/lexers/objective_c.rb +197 -0
- data/lib/rouge/lexers/ocaml.rb +111 -0
- data/lib/rouge/lexers/perl.rb +197 -0
- data/lib/rouge/lexers/php.rb +173 -0
- data/lib/rouge/lexers/php/builtins.rb +204 -0
- data/lib/rouge/lexers/plain_text.rb +25 -0
- data/lib/rouge/lexers/powershell.rb +96 -0
- data/lib/rouge/lexers/prolog.rb +64 -0
- data/lib/rouge/lexers/properties.rb +55 -0
- data/lib/rouge/lexers/puppet.rb +128 -0
- data/lib/rouge/lexers/python.rb +228 -0
- data/lib/rouge/lexers/qml.rb +72 -0
- data/lib/rouge/lexers/r.rb +56 -0
- data/lib/rouge/lexers/racket.rb +542 -0
- data/lib/rouge/lexers/ruby.rb +415 -0
- data/lib/rouge/lexers/rust.rb +191 -0
- data/lib/rouge/lexers/sass.rb +74 -0
- data/lib/rouge/lexers/sass/common.rb +180 -0
- data/lib/rouge/lexers/scala.rb +142 -0
- data/lib/rouge/lexers/scheme.rb +112 -0
- data/lib/rouge/lexers/scss.rb +34 -0
- data/lib/rouge/lexers/sed.rb +170 -0
- data/lib/rouge/lexers/shell.rb +152 -0
- data/lib/rouge/lexers/slim.rb +228 -0
- data/lib/rouge/lexers/smalltalk.rb +116 -0
- data/lib/rouge/lexers/sml.rb +347 -0
- data/lib/rouge/lexers/sql.rb +140 -0
- data/lib/rouge/lexers/swift.rb +144 -0
- data/lib/rouge/lexers/tcl.rb +192 -0
- data/lib/rouge/lexers/tex.rb +72 -0
- data/lib/rouge/lexers/toml.rb +71 -0
- data/lib/rouge/lexers/tulip.rb +75 -0
- data/lib/rouge/lexers/vb.rb +164 -0
- data/lib/rouge/lexers/viml.rb +101 -0
- data/lib/rouge/lexers/viml/keywords.rb +12 -0
- data/lib/rouge/lexers/xml.rb +59 -0
- data/lib/rouge/lexers/yaml.rb +364 -0
- data/lib/rouge/plugins/redcarpet.rb +30 -0
- data/lib/rouge/regex_lexer.rb +439 -0
- data/lib/rouge/template_lexer.rb +22 -0
- data/lib/rouge/text_analyzer.rb +48 -0
- data/lib/rouge/theme.rb +195 -0
- data/lib/rouge/themes/base16.rb +130 -0
- data/lib/rouge/themes/colorful.rb +67 -0
- data/lib/rouge/themes/github.rb +71 -0
- data/lib/rouge/themes/molokai.rb +82 -0
- data/lib/rouge/themes/monokai.rb +92 -0
- data/lib/rouge/themes/monokai_sublime.rb +90 -0
- data/lib/rouge/themes/thankful_eyes.rb +71 -0
- data/lib/rouge/token.rb +182 -0
- data/lib/rouge/util.rb +101 -0
- data/lib/rouge/version.rb +7 -0
- metadata +231 -0
@@ -0,0 +1,439 @@
|
|
1
|
+
# -*- coding: utf-8 -*- #
|
2
|
+
|
3
|
+
module Rouge
|
4
|
+
# @abstract
|
5
|
+
# A stateful lexer that uses sets of regular expressions to
|
6
|
+
# tokenize a string. Most lexers are instances of RegexLexer.
|
7
|
+
class RegexLexer < Lexer
|
8
|
+
# A rule is a tuple of a regular expression to test, and a callback
|
9
|
+
# to perform if the test succeeds.
|
10
|
+
#
|
11
|
+
# @see StateDSL#rule
|
12
|
+
class Rule
|
13
|
+
attr_reader :callback
|
14
|
+
attr_reader :re
|
15
|
+
attr_reader :beginning_of_line
|
16
|
+
def initialize(re, callback)
|
17
|
+
@re = re
|
18
|
+
@callback = callback
|
19
|
+
@beginning_of_line = re.source[0] == ?^
|
20
|
+
end
|
21
|
+
|
22
|
+
def inspect
|
23
|
+
"#<Rule #{@re.inspect}>"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# a State is a named set of rules that can be tested for or
|
28
|
+
# mixed in.
|
29
|
+
#
|
30
|
+
# @see RegexLexer.state
|
31
|
+
class State
|
32
|
+
attr_reader :name, :rules
|
33
|
+
def initialize(name, rules)
|
34
|
+
@name = name
|
35
|
+
@rules = rules
|
36
|
+
end
|
37
|
+
|
38
|
+
def inspect
|
39
|
+
"#<#{self.class.name} #{@name.inspect}>"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class StateDSL
|
44
|
+
attr_reader :rules
|
45
|
+
def initialize(name, &defn)
|
46
|
+
@name = name
|
47
|
+
@defn = defn
|
48
|
+
@rules = []
|
49
|
+
end
|
50
|
+
|
51
|
+
def to_state(lexer_class)
|
52
|
+
load!
|
53
|
+
rules = @rules.map do |rule|
|
54
|
+
rule.is_a?(String) ? lexer_class.get_state(rule) : rule
|
55
|
+
end
|
56
|
+
State.new(@name, rules)
|
57
|
+
end
|
58
|
+
|
59
|
+
def prepended(&defn)
|
60
|
+
parent_defn = @defn
|
61
|
+
StateDSL.new(@name) do
|
62
|
+
instance_eval(&defn)
|
63
|
+
instance_eval(&parent_defn)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def appended(&defn)
|
68
|
+
parent_defn = @defn
|
69
|
+
StateDSL.new(@name) do
|
70
|
+
instance_eval(&parent_defn)
|
71
|
+
instance_eval(&defn)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
protected
|
76
|
+
# Define a new rule for this state.
|
77
|
+
#
|
78
|
+
# @overload rule(re, token, next_state=nil)
|
79
|
+
# @overload rule(re, &callback)
|
80
|
+
#
|
81
|
+
# @param [Regexp] re
|
82
|
+
# a regular expression for this rule to test.
|
83
|
+
# @param [String] tok
|
84
|
+
# the token type to yield if `re` matches.
|
85
|
+
# @param [#to_s] next_state
|
86
|
+
# (optional) a state to push onto the stack if `re` matches.
|
87
|
+
# If `next_state` is `:pop!`, the state stack will be popped
|
88
|
+
# instead.
|
89
|
+
# @param [Proc] callback
|
90
|
+
# a block that will be evaluated in the context of the lexer
|
91
|
+
# if `re` matches. This block has access to a number of lexer
|
92
|
+
# methods, including {RegexLexer#push}, {RegexLexer#pop!},
|
93
|
+
# {RegexLexer#token}, and {RegexLexer#delegate}. The first
|
94
|
+
# argument can be used to access the match groups.
|
95
|
+
def rule(re, tok=nil, next_state=nil, &callback)
|
96
|
+
if tok.nil? && callback.nil?
|
97
|
+
raise "please pass `rule` a token to yield or a callback"
|
98
|
+
end
|
99
|
+
|
100
|
+
callback ||= case next_state
|
101
|
+
when :pop!
|
102
|
+
proc do |stream|
|
103
|
+
puts " yielding #{tok.qualname}, #{stream[0].inspect}" if @debug
|
104
|
+
@output_stream.call(tok, stream[0])
|
105
|
+
puts " popping stack: #{1}" if @debug
|
106
|
+
@stack.pop or raise 'empty stack!'
|
107
|
+
end
|
108
|
+
when :push
|
109
|
+
proc do |stream|
|
110
|
+
puts " yielding #{tok.qualname}, #{stream[0].inspect}" if @debug
|
111
|
+
@output_stream.call(tok, stream[0])
|
112
|
+
puts " pushing #{@stack.last.name}" if @debug
|
113
|
+
@stack.push(@stack.last)
|
114
|
+
end
|
115
|
+
when Symbol
|
116
|
+
proc do |stream|
|
117
|
+
puts " yielding #{tok.qualname}, #{stream[0].inspect}" if @debug
|
118
|
+
@output_stream.call(tok, stream[0])
|
119
|
+
state = @states[next_state] || self.class.get_state(next_state)
|
120
|
+
puts " pushing #{state.name}" if @debug
|
121
|
+
@stack.push(state)
|
122
|
+
end
|
123
|
+
when nil
|
124
|
+
proc do |stream|
|
125
|
+
puts " yielding #{tok.qualname}, #{stream[0].inspect}" if @debug
|
126
|
+
@output_stream.call(tok, stream[0])
|
127
|
+
end
|
128
|
+
else
|
129
|
+
raise "invalid next state: #{next_state.inspect}"
|
130
|
+
end
|
131
|
+
|
132
|
+
rules << Rule.new(re, callback)
|
133
|
+
end
|
134
|
+
|
135
|
+
# Mix in the rules from another state into this state. The rules
|
136
|
+
# from the mixed-in state will be tried in order before moving on
|
137
|
+
# to the rest of the rules in this state.
|
138
|
+
def mixin(state)
|
139
|
+
rules << state.to_s
|
140
|
+
end
|
141
|
+
|
142
|
+
private
|
143
|
+
def load!
|
144
|
+
return if @loaded
|
145
|
+
@loaded = true
|
146
|
+
instance_eval(&@defn)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# The states hash for this lexer.
|
151
|
+
# @see state
|
152
|
+
def self.states
|
153
|
+
@states ||= {}
|
154
|
+
end
|
155
|
+
|
156
|
+
def self.state_definitions
|
157
|
+
@state_definitions ||= InheritableHash.new(superclass.state_definitions)
|
158
|
+
end
|
159
|
+
@state_definitions = {}
|
160
|
+
|
161
|
+
def self.replace_state(name, new_defn)
|
162
|
+
states[name] = nil
|
163
|
+
state_definitions[name] = new_defn
|
164
|
+
end
|
165
|
+
|
166
|
+
# The routines to run at the beginning of a fresh lex.
|
167
|
+
# @see start
|
168
|
+
def self.start_procs
|
169
|
+
@start_procs ||= InheritableList.new(superclass.start_procs)
|
170
|
+
end
|
171
|
+
@start_procs = []
|
172
|
+
|
173
|
+
# Specify an action to be run every fresh lex.
|
174
|
+
#
|
175
|
+
# @example
|
176
|
+
# start { puts "I'm lexing a new string!" }
|
177
|
+
def self.start(&b)
|
178
|
+
start_procs << b
|
179
|
+
end
|
180
|
+
|
181
|
+
# Define a new state for this lexer with the given name.
|
182
|
+
# The block will be evaluated in the context of a {StateDSL}.
|
183
|
+
def self.state(name, &b)
|
184
|
+
name = name.to_s
|
185
|
+
state_definitions[name] = StateDSL.new(name, &b)
|
186
|
+
end
|
187
|
+
|
188
|
+
def self.prepend(name, &b)
|
189
|
+
name = name.to_s
|
190
|
+
dsl = state_definitions[name] or raise "no such state #{name.inspect}"
|
191
|
+
replace_state(name, dsl.prepended(&b))
|
192
|
+
end
|
193
|
+
|
194
|
+
def self.append(state, &b)
|
195
|
+
name = name.to_s
|
196
|
+
dsl = state_definitions[name] or raise "no such state #{name.inspect}"
|
197
|
+
replace_state(name, dsl.appended(&b))
|
198
|
+
end
|
199
|
+
|
200
|
+
# @private
|
201
|
+
def self.get_state(name)
|
202
|
+
return name if name.is_a? State
|
203
|
+
|
204
|
+
states[name.to_sym] ||= begin
|
205
|
+
defn = state_definitions[name.to_s] or raise "unknown state: #{name.inspect}"
|
206
|
+
defn.to_state(self)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
# @private
|
211
|
+
def get_state(state_name)
|
212
|
+
self.class.get_state(state_name)
|
213
|
+
end
|
214
|
+
|
215
|
+
# The state stack. This is initially the single state `[:root]`.
|
216
|
+
# It is an error for this stack to be empty.
|
217
|
+
# @see #state
|
218
|
+
def stack
|
219
|
+
@stack ||= [get_state(:root)]
|
220
|
+
end
|
221
|
+
|
222
|
+
# The current state - i.e. one on top of the state stack.
|
223
|
+
#
|
224
|
+
# NB: if the state stack is empty, this will throw an error rather
|
225
|
+
# than returning nil.
|
226
|
+
def state
|
227
|
+
stack.last or raise 'empty stack!'
|
228
|
+
end
|
229
|
+
|
230
|
+
# reset this lexer to its initial state. This runs all of the
|
231
|
+
# start_procs.
|
232
|
+
def reset!
|
233
|
+
@stack = nil
|
234
|
+
@current_stream = nil
|
235
|
+
|
236
|
+
self.class.start_procs.each do |pr|
|
237
|
+
instance_eval(&pr)
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
# This implements the lexer protocol, by yielding [token, value] pairs.
|
242
|
+
#
|
243
|
+
# The process for lexing works as follows, until the stream is empty:
|
244
|
+
#
|
245
|
+
# 1. We look at the state on top of the stack (which by default is
|
246
|
+
# `[:root]`).
|
247
|
+
# 2. Each rule in that state is tried until one is successful. If one
|
248
|
+
# is found, that rule's callback is evaluated - which may yield
|
249
|
+
# tokens and manipulate the state stack. Otherwise, one character
|
250
|
+
# is consumed with an `'Error'` token, and we continue at (1.)
|
251
|
+
#
|
252
|
+
# @see #step #step (where (2.) is implemented)
|
253
|
+
def stream_tokens(str, &b)
|
254
|
+
stream = StringScanner.new(str)
|
255
|
+
|
256
|
+
@current_stream = stream
|
257
|
+
@output_stream = b
|
258
|
+
@states = self.class.states
|
259
|
+
@null_steps = 0
|
260
|
+
|
261
|
+
until stream.eos?
|
262
|
+
if @debug
|
263
|
+
puts "lexer: #{self.class.tag}"
|
264
|
+
puts "stack: #{stack.map(&:name).inspect}"
|
265
|
+
puts "stream: #{stream.peek(20).inspect}"
|
266
|
+
end
|
267
|
+
|
268
|
+
success = step(state, stream)
|
269
|
+
|
270
|
+
if !success
|
271
|
+
puts " no match, yielding Error" if @debug
|
272
|
+
b.call(Token::Tokens::Error, stream.getch)
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
# The number of successive scans permitted without consuming
|
278
|
+
# the input stream. If this is exceeded, the match fails.
|
279
|
+
MAX_NULL_SCANS = 5
|
280
|
+
|
281
|
+
# Runs one step of the lex. Rules in the current state are tried
|
282
|
+
# until one matches, at which point its callback is called.
|
283
|
+
#
|
284
|
+
# @return true if a rule was tried successfully
|
285
|
+
# @return false otherwise.
|
286
|
+
def step(state, stream)
|
287
|
+
state.rules.each do |rule|
|
288
|
+
if rule.is_a?(State)
|
289
|
+
puts " entering mixin #{rule.name}" if @debug
|
290
|
+
return true if step(rule, stream)
|
291
|
+
puts " exiting mixin #{rule.name}" if @debug
|
292
|
+
else
|
293
|
+
puts " trying #{rule.inspect}" if @debug
|
294
|
+
|
295
|
+
# XXX HACK XXX
|
296
|
+
# StringScanner's implementation of ^ is b0rken.
|
297
|
+
# see http://bugs.ruby-lang.org/issues/7092
|
298
|
+
# TODO: this doesn't cover cases like /(a|^b)/, but it's
|
299
|
+
# the most common, for now...
|
300
|
+
next if rule.beginning_of_line && !stream.beginning_of_line?
|
301
|
+
|
302
|
+
if size = stream.skip(rule.re)
|
303
|
+
puts " got #{stream[0].inspect}" if @debug
|
304
|
+
|
305
|
+
instance_exec(stream, &rule.callback)
|
306
|
+
|
307
|
+
if size.zero?
|
308
|
+
@null_steps += 1
|
309
|
+
if @null_steps > MAX_NULL_SCANS
|
310
|
+
puts " too many scans without consuming the string!" if @debug
|
311
|
+
return false
|
312
|
+
end
|
313
|
+
else
|
314
|
+
@null_steps = 0
|
315
|
+
end
|
316
|
+
|
317
|
+
return true
|
318
|
+
end
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
false
|
323
|
+
end
|
324
|
+
|
325
|
+
# Yield a token.
|
326
|
+
#
|
327
|
+
# @param tok
|
328
|
+
# the token type
|
329
|
+
# @param val
|
330
|
+
# (optional) the string value to yield. If absent, this defaults
|
331
|
+
# to the entire last match.
|
332
|
+
def token(tok, val=@current_stream[0])
|
333
|
+
yield_token(tok, val)
|
334
|
+
end
|
335
|
+
|
336
|
+
# @deprecated
|
337
|
+
#
|
338
|
+
# Yield a token with the next matched group. Subsequent calls
|
339
|
+
# to this method will yield subsequent groups.
|
340
|
+
def group(tok)
|
341
|
+
raise "RegexLexer#group is deprecated: use #groups instead"
|
342
|
+
end
|
343
|
+
|
344
|
+
# Yield tokens corresponding to the matched groups of the current
|
345
|
+
# match.
|
346
|
+
def groups(*tokens)
|
347
|
+
tokens.each_with_index do |tok, i|
|
348
|
+
yield_token(tok, @current_stream[i+1])
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
# Delegate the lex to another lexer. The #lex method will be called
|
353
|
+
# with `:continue` set to true, so that #reset! will not be called.
|
354
|
+
# In this way, a single lexer can be repeatedly delegated to while
|
355
|
+
# maintaining its own internal state stack.
|
356
|
+
#
|
357
|
+
# @param [#lex] lexer
|
358
|
+
# The lexer or lexer class to delegate to
|
359
|
+
# @param [String] text
|
360
|
+
# The text to delegate. This defaults to the last matched string.
|
361
|
+
def delegate(lexer, text=nil)
|
362
|
+
puts " delegating to #{lexer.inspect}" if @debug
|
363
|
+
text ||= @current_stream[0]
|
364
|
+
|
365
|
+
lexer.lex(text, :continue => true) do |tok, val|
|
366
|
+
puts " delegated token: #{tok.inspect}, #{val.inspect}" if @debug
|
367
|
+
yield_token(tok, val)
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
def recurse(text=nil)
|
372
|
+
delegate(self.class, text)
|
373
|
+
end
|
374
|
+
|
375
|
+
# Push a state onto the stack. If no state name is given and you've
|
376
|
+
# passed a block, a state will be dynamically created using the
|
377
|
+
# {StateDSL}.
|
378
|
+
def push(state_name=nil, &b)
|
379
|
+
push_state = if state_name
|
380
|
+
get_state(state_name)
|
381
|
+
elsif block_given?
|
382
|
+
StateDSL.new(b.inspect, &b).to_state(self.class)
|
383
|
+
else
|
384
|
+
# use the top of the stack by default
|
385
|
+
self.state
|
386
|
+
end
|
387
|
+
|
388
|
+
puts " pushing #{push_state.name}" if @debug
|
389
|
+
stack.push(push_state)
|
390
|
+
end
|
391
|
+
|
392
|
+
# Pop the state stack. If a number is passed in, it will be popped
|
393
|
+
# that number of times.
|
394
|
+
def pop!(times=1)
|
395
|
+
raise 'empty stack!' if stack.empty?
|
396
|
+
|
397
|
+
puts " popping stack: #{times}" if @debug
|
398
|
+
|
399
|
+
stack.pop(times)
|
400
|
+
|
401
|
+
nil
|
402
|
+
end
|
403
|
+
|
404
|
+
# replace the head of the stack with the given state
|
405
|
+
def goto(state_name)
|
406
|
+
raise 'empty stack!' if stack.empty?
|
407
|
+
|
408
|
+
puts " going to state #{state_name} " if @debug
|
409
|
+
stack[-1] = get_state(state_name)
|
410
|
+
end
|
411
|
+
|
412
|
+
# reset the stack back to `[:root]`.
|
413
|
+
def reset_stack
|
414
|
+
puts ' resetting stack' if @debug
|
415
|
+
stack.clear
|
416
|
+
stack.push get_state(:root)
|
417
|
+
end
|
418
|
+
|
419
|
+
# Check if `state_name` is in the state stack.
|
420
|
+
def in_state?(state_name)
|
421
|
+
state_name = state_name.to_s
|
422
|
+
stack.any? do |state|
|
423
|
+
state.name == state_name.to_s
|
424
|
+
end
|
425
|
+
end
|
426
|
+
|
427
|
+
# Check if `state_name` is the state on top of the state stack.
|
428
|
+
def state?(state_name)
|
429
|
+
state_name.to_s == state.name
|
430
|
+
end
|
431
|
+
|
432
|
+
private
|
433
|
+
def yield_token(tok, val)
|
434
|
+
return if val.nil? || val.empty?
|
435
|
+
puts " yielding #{tok.qualname}, #{val.inspect}" if @debug
|
436
|
+
@output_stream.yield(tok, val)
|
437
|
+
end
|
438
|
+
end
|
439
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- coding: utf-8 -*- #
|
2
|
+
|
3
|
+
module Rouge
|
4
|
+
# @abstract
|
5
|
+
# A TemplateLexer is one that accepts a :parent option, to specify
|
6
|
+
# which language is being templated. The lexer class can specify its
|
7
|
+
# own default for the parent lexer, which is otherwise defaulted to
|
8
|
+
# HTML.
|
9
|
+
class TemplateLexer < RegexLexer
|
10
|
+
# the parent lexer - the one being templated.
|
11
|
+
def parent
|
12
|
+
return @parent if instance_variable_defined? :@parent
|
13
|
+
@parent = option(:parent) || 'html'
|
14
|
+
if @parent.is_a? ::String
|
15
|
+
lexer_class = Lexer.find(@parent)
|
16
|
+
@parent = lexer_class.new(self.options)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
start { parent.reset! }
|
21
|
+
end
|
22
|
+
end
|