Skip to content

Commit 434006c

Browse files
committed
testing rouge scanner
1 parent 3a703d2 commit 434006c

File tree

5 files changed

+518
-0
lines changed

5 files changed

+518
-0
lines changed

lib/coderay/rouge_scanner.rb

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
require 'set'
2+
require 'coderay/rouge_scanner_dsl'
3+
4+
module CodeRay
5+
module Scanners
6+
class RougeScanner < Scanner
7+
require 'rouge'
8+
include Rouge::Token::Tokens
9+
10+
extend RougeScannerDSL
11+
12+
class << self
13+
def define_scan_tokens!
14+
if ENV['PUTS']
15+
puts CodeRay.scan(scan_tokens_code, :ruby).terminal
16+
puts "callbacks: #{callbacks.size}"
17+
end
18+
19+
class_eval <<-RUBY
20+
def scan_tokens encoder, options
21+
@encoder = encoder
22+
#{ scan_tokens_code.chomp.gsub(/^/, ' ') }
23+
end
24+
RUBY
25+
end
26+
end
27+
28+
def scan_tokens tokens, options
29+
self.class.define_scan_tokens!
30+
31+
scan_tokens tokens, options
32+
end
33+
34+
protected
35+
36+
def setup
37+
@state = :root
38+
end
39+
40+
def close_groups encoder, states
41+
# TODO
42+
end
43+
44+
def token token
45+
@encoder.text_token @match, token
46+
end
47+
end
48+
end
49+
end

lib/coderay/rouge_scanner_dsl.rb

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
require 'set'
2+
3+
module CodeRay
4+
module Scanners
5+
module RougeScannerDSL
6+
NoStatesError = Class.new StandardError
7+
8+
State = Struct.new :name, :rules do
9+
def initialize(name, &block)
10+
super name, []
11+
12+
instance_eval(&block)
13+
end
14+
15+
def code scanner
16+
<<-RUBY
17+
when #{name.inspect}
18+
#{ rules_code(scanner).chomp.gsub(/^/, ' ') }
19+
else
20+
encoder.text_token getch, :error
21+
end
22+
RUBY
23+
end
24+
25+
def rules_code scanner, first: true
26+
raise 'no rules defined for %p' % [self] if rules.empty?
27+
28+
[
29+
rules.first.code(scanner, first: first),
30+
*rules.drop(1).map { |rule| rule.code(scanner) }
31+
].join
32+
end
33+
34+
protected
35+
36+
# DSL
37+
38+
def rule pattern, token = nil, next_state = nil, &block
39+
unless token || block
40+
raise 'please pass `rule` a token to yield or a callback'
41+
end
42+
43+
case token
44+
when Class
45+
unless token < Rouge::Token
46+
raise "invalid token: #{token.inspect}"
47+
end
48+
49+
case next_state
50+
when Symbol
51+
rules << Rule.new(pattern, token, next_state)
52+
when nil
53+
rules << Rule.new(pattern, token)
54+
else
55+
raise "invalid next state: #{next_state.inspect}"
56+
end
57+
when nil
58+
rules << CallbackRule.new(pattern, block)
59+
else
60+
raise "invalid token: #{token.inspect}"
61+
end
62+
end
63+
64+
def mixin state_name
65+
rules << Mixin.new(state_name)
66+
end
67+
end
68+
69+
Rule = Struct.new :pattern, :token, :action do
70+
def initialize(pattern, token, action = nil)
71+
super
72+
end
73+
74+
def code scanner, first: false
75+
<<-RUBY + action_code.to_s
76+
#{'els' unless first}if match = scan(#{pattern.inspect})
77+
encoder.text_token match, #{token.token_chain.map(&:name).join('::')}
78+
RUBY
79+
end
80+
81+
def action_code
82+
case action
83+
when :pop!
84+
<<-RUBY
85+
states.pop
86+
state = states.last
87+
RUBY
88+
when Symbol
89+
<<-RUBY
90+
state = #{action.inspect}
91+
states << state
92+
RUBY
93+
end
94+
end
95+
end
96+
97+
CallbackRule = Struct.new :pattern, :callback do
98+
def code scanner, first: false
99+
<<-RUBY
100+
#{'els' unless first}if match = scan(#{pattern.inspect})
101+
@match = match
102+
#{scanner.add_callback(callback)}
103+
RUBY
104+
end
105+
end
106+
107+
Mixin = Struct.new(:state_name) do
108+
def code scanner, first: false
109+
scanner.states[state_name].rules_code(scanner, first: first)
110+
end
111+
end
112+
113+
attr_accessor :states
114+
115+
def state name, &block
116+
@states ||= {}
117+
@states[name] = State.new(name, &block)
118+
end
119+
120+
def add_callback block
121+
base_name = "__callback_line_#{block.source_location.last}"
122+
callback_name = base_name
123+
counter = 'a'
124+
while callbacks.key?(callback_name)
125+
callback_name = "#{base_name}_#{counter}"
126+
counter = counter.succ
127+
end
128+
129+
callbacks[callback_name] = define_method(callback_name, &block)
130+
131+
parameters = block.parameters
132+
133+
if parameters.empty?
134+
callback_name
135+
else
136+
parameter_names = parameters.map do |type, name|
137+
raise "callbacks don't allow rest parameters: %p" % [parameters] unless type == :req || type == :opt
138+
name = :match if name == :m
139+
name
140+
end
141+
142+
parameter_names.each { |name| variables << name }
143+
"#{callback_name}(#{parameter_names.join(', ')})"
144+
end
145+
end
146+
147+
def add_variable name
148+
variables << name
149+
end
150+
151+
protected
152+
153+
def callbacks
154+
@callbacks ||= {}
155+
end
156+
157+
def variables
158+
@variables ||= Set.new
159+
end
160+
161+
def additional_variables
162+
variables - %i(encoder options state states match kind)
163+
end
164+
165+
def scan_tokens_code
166+
<<-"RUBY"
167+
state = options[:state] || @state
168+
states = [state]
169+
#{ restore_local_variables_code }
170+
until eos?
171+
case state
172+
#{ states_code.chomp.gsub(/^/, ' ') }
173+
else
174+
raise_inspect 'Unknown state: %p' % [state], encoder
175+
end
176+
end
177+
178+
@state = state if options[:keep_state]
179+
180+
close_groups(encoder, states)
181+
182+
encoder
183+
RUBY
184+
end
185+
186+
def restore_local_variables_code
187+
additional_variables.sort.map { |name| "#{name} = @#{name}" }.join("\n")
188+
end
189+
190+
def states_code
191+
unless defined?(@states) && !@states.empty?
192+
raise NoStatesError, 'no states defined for %p' % [self.class]
193+
end
194+
195+
@states.values.map { |state| state.code(self) }.join
196+
end
197+
end
198+
end
199+
end

lib/coderay/scanners.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ module Scanners
2626
autoload :RuleBasedScanner, CodeRay.coderay_path('rule_based_scanner')
2727
autoload :SingleStateRuleBasedScanner, CodeRay.coderay_path('single_state_rule_based_scanner')
2828
autoload :StateBasedScanner, CodeRay.coderay_path('state_based_scanner')
29+
autoload :RougeScanner, CodeRay.coderay_path('rouge_scanner')
2930
autoload :SimpleScanner, CodeRay.coderay_path('simple_scanner')
3031

3132
end

lib/coderay/scanners/_map.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ module Scanners
1616
:javascript4 => :java_script4,
1717
:javascript5 => :java_script5,
1818
:javascript6 => :java_script6,
19+
:javascript7 => :java_script7,
1920
:js => :java_script,
2021
:pascal => :delphi,
2122
:patch => :diff,

0 commit comments

Comments
 (0)