Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions lib/rouge/regex_lexer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ def to_s
end
end

# exception class for fallthrough - using an exception is slightly faster
# than catch { ... }, but it is not semantically an error that should be
# rescued from.
class Fallthrough < Exception # rubocop:disable Lint/InheritException
end

# A rule is a tuple of a regular expression to test, and a callback
# to perform if the test succeeds.
#
Expand Down Expand Up @@ -365,8 +371,6 @@ def step(state, stream)
if (size = stream.skip(rule.re))
puts " got: #{stream[0].inspect}" if @debug

instance_exec(stream, &rule.callback)

if size.zero?
@null_steps += 1
if @null_steps > MAX_NULL_SCANS
Expand All @@ -377,6 +381,13 @@ def step(state, stream)
@null_steps = 0
end

begin
instance_exec(stream, &rule.callback)
rescue Fallthrough
stream.unscan
next
end

return true
end
end
Expand Down Expand Up @@ -435,6 +446,10 @@ def recurse(text=nil)
delegate(self.class, text)
end

def fallthrough!
raise Fallthrough
end

# Push a state onto the stack. If no state name is given and you've
# passed a block, a state will be dynamically created using the
# {StateDSL}.
Expand Down
27 changes: 27 additions & 0 deletions spec/lexer_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -243,4 +243,31 @@ def initialize(*)
assert { php.instance_variable_get(:@start_inline) == :guess }
assert { inline_php.instance_variable_get(:@start_inline) == true }
end

it 'falls through' do
new_lexer = Class.new(Rouge::RegexLexer) do
state :root do
rule %r/\w+\b/ do |m|
fallthrough! unless %w(testy1 testy2).include?(m[0])

token 'builtin'
end

rule %r/\w+\b/ do |m|
token 'function'
end

rule %r/\s+/, 'text'
end
end

result = new_lexer.lex('testy1 foobar testy2').to_a

assert { result.size == 5 }
assert { result[0] == ['builtin', 'testy1'] }
assert { result[1] == ['text', ' '] }
assert { result[2] == ['function', 'foobar'] }
assert { result[3] == ['text', ' '] }
assert { result[4] == ['builtin', 'testy2'] }
end
end