Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DSL experiments #182

Open
wants to merge 68 commits into
base: possible-speedups
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
5e3df7f
experiment with JSON scanner
korny Oct 28, 2013
8dc6d8b
ws
korny Oct 28, 2013
41c211d
Merge branch 'master' into dsl
korny Mar 21, 2015
615ac96
add alternative JSON scanners
korny Mar 21, 2015
300ccd3
no need to modify file_extension
korny Mar 21, 2015
f4f0db4
add variant tasks like rake test:scanner:json:2
korny Mar 21, 2015
f1ea428
this seems obsolete
korny Mar 21, 2015
b01a3fb
add SKIP_UPDATE_SCANNER_SUITE switch
korny Mar 21, 2015
2499b1e
first version of RuleBasedScanner for JavaScript
korny Mar 21, 2015
647e9c0
more work on DSL scanner for JavaScript
korny Mar 22, 2015
63c9f26
finally, a version that is fast without eval!
korny Mar 24, 2015
e6753b9
cleanup .gitignore
korny Mar 24, 2015
6eaa589
use instance variable instead of class variable
korny Mar 25, 2015
5e954e2
add check_unless
korny Apr 3, 2015
0cd3e62
add DSL CSS scanner
korny Apr 3, 2015
e8bef10
move RuleBasedScanner into own file
korny Apr 3, 2015
235e01b
add push/pop state, working on C scanner
korny Apr 21, 2015
463ffa1
Merge branch 'master' into dsl
korny Dec 24, 2015
7dcbf8a
Debug encoder should count tokens for better inspection
korny Feb 12, 2016
e6d46f9
just show the array
korny Feb 12, 2016
61a9d96
scanner tweaks
korny Feb 12, 2016
c274a90
fix comment
korny Feb 12, 2016
36af5ca
add explicit pattern method; make pattern optional
korny Feb 12, 2016
40f1fa7
Push and Pop take optional group argument now
korny Feb 12, 2016
aa93af4
quick increment/decrement, yay!
korny Feb 12, 2016
3df8487
use explicit pattern method
korny Feb 12, 2016
7561e8d
warn about error tokens
korny Feb 12, 2016
a1a7b2c
add json scanner using RuleBasedScanner
korny Feb 12, 2016
4da772b
add generated Lua scanner
korny Feb 12, 2016
9f4af60
highlight generated C scanner (like the others)
korny Feb 12, 2016
aaa1705
ignore benchmark results
korny Feb 12, 2016
0d1c786
move comment to the top
korny Feb 13, 2016
dd1d779
move setup to superclass
korny Feb 13, 2016
42e2ca3
cleanup
korny Feb 13, 2016
f1bd833
use setup
korny Feb 13, 2016
ca3f15f
remove whitespace
korny Feb 13, 2016
ee9e840
cleanup
korny Feb 13, 2016
3c92a65
Merge branch 'master' into dsl
korny Feb 13, 2016
ae94f2f
Merge branch 'master' into dsl
korny Feb 13, 2016
f8cadd9
add line number to eval
korny Feb 14, 2016
13ac3fd
optional push state (return nil)
korny Feb 14, 2016
6b80e1e
remove obsolete flag, fix order of rules
korny Feb 14, 2016
dcf73a6
nicer debug output
korny Feb 14, 2016
9526bd8
generate scanner code automatically
korny Feb 14, 2016
14339bf
some more variables that are set by the scanner
korny Feb 14, 2016
90c5c91
remove templates, yay!
korny Feb 14, 2016
23e23f2
Merge branch 'master' into dsl
korny Mar 12, 2016
86b0bcf
Merge branch 'master' into dsl
korny Jun 4, 2016
a38b337
Merge branch 'possible-speedups' into dsl
korny Jun 4, 2016
545398f
update version; this will be CodeRay 2
korny Jun 4, 2016
e01ccf7
Merge branch 'possible-speedups' into dsl
korny Jun 12, 2016
26f915f
Merge branch 'possible-speedups' into dsl
korny Dec 28, 2016
548e2d0
default set(:flag) to true
korny Jan 15, 2017
7a02cdd
working towards DSL scanner
korny Apr 9, 2017
1bdaeef
starting with SimpleScanner
korny Apr 9, 2017
1f2367a
Merge branch 'master' into dsl
korny Nov 2, 2017
775255a
Merge branch 'possible-speedups' into dsl
korny Nov 2, 2017
e101dbe
normalize class names
korny Nov 2, 2017
bc580c5
Merge branch 'possible-speedups' into dsl
korny Nov 2, 2017
e69b878
sort .gitignore, add spec/example.txt
korny Nov 2, 2017
8d46c46
Merge branch 'possible-speedups' into dsl
korny Nov 2, 2017
579c00b
testing SingleStateRuleBasedScanner; not faster :(
korny Nov 5, 2017
738aae5
fix autoloading of DSL scanners
korny Nov 5, 2017
465e6c3
remove obsolete "protected"
korny Nov 5, 2017
a9e04e1
fix specs, update SimpleScannerDSL
korny Nov 5, 2017
ec89197
trying to fix tests for Ruby 2.4
korny Nov 5, 2017
3a703d2
Merge branch 'master' into dsl
korny Nov 18, 2017
434006c
testing rouge scanner
korny Nov 27, 2017
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
3 changes: 1 addition & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ rvm:
- 2.1
- 2.2
- 2.3
- 2.4
- 2.4.2
- ruby-head
- jruby
branches:
Expand All @@ -21,6 +21,5 @@ matrix:
allow_failures:
- rvm: ruby-head
- rvm: jruby
- rvm: rbx
script: "rake test" # test:scanners"
sudo: false
10 changes: 10 additions & 0 deletions lib/coderay/encoders/debug.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@ class Debug < Encoder

register_for :debug

attr_reader :size

FILE_EXTENSION = 'raydebug'

def text_token text, kind
@size += 1
if kind == :space
@out << text
else
Expand All @@ -43,6 +46,13 @@ def end_line kind
@out << ']'
end

protected

def setup options
super
@size = 0
end

end

end
Expand Down
4 changes: 2 additions & 2 deletions lib/coderay/encoders/debug_lint.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def begin_group kind
end

def end_group kind
raise Lint::IncorrectTokenGroupNesting, 'We are inside %s, not %p (end_group)' % [@opened.reverse.map(&:inspect).join(' < '), kind] if @opened.last != kind
raise Lint::IncorrectTokenGroupNesting, 'We are inside %p, not %p (end_group)' % [@opened.reverse, kind] if @opened.last != kind
@opened.pop
super
end
Expand All @@ -40,7 +40,7 @@ def begin_line kind
end

def end_line kind
raise Lint::IncorrectTokenGroupNesting, 'We are inside %s, not %p (end_line)' % [@opened.reverse.map(&:inspect).join(' < '), kind] if @opened.last != kind
raise Lint::IncorrectTokenGroupNesting, 'We are inside %p, not %p (end_line)' % [@opened.reverse, kind] if @opened.last != kind
@opened.pop
super
end
Expand Down
49 changes: 49 additions & 0 deletions lib/coderay/rouge_scanner.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
require 'set'
require 'coderay/rouge_scanner_dsl'

module CodeRay
module Scanners
class RougeScanner < Scanner
require 'rouge'
include Rouge::Token::Tokens

extend RougeScannerDSL

class << self
def define_scan_tokens!
if ENV['PUTS']
puts CodeRay.scan(scan_tokens_code, :ruby).terminal
puts "callbacks: #{callbacks.size}"
end

class_eval <<-RUBY
def scan_tokens encoder, options
@encoder = encoder
#{ scan_tokens_code.chomp.gsub(/^/, ' ') }
end
RUBY
end
end

def scan_tokens tokens, options
self.class.define_scan_tokens!

scan_tokens tokens, options
end

protected

def setup
@state = :root
end

def close_groups encoder, states
# TODO
end

def token token
@encoder.text_token @match, token
end
end
end
end
199 changes: 199 additions & 0 deletions lib/coderay/rouge_scanner_dsl.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
require 'set'

module CodeRay
module Scanners
module RougeScannerDSL
NoStatesError = Class.new StandardError

State = Struct.new :name, :rules do
def initialize(name, &block)
super name, []

instance_eval(&block)
end

def code scanner
<<-RUBY
when #{name.inspect}
#{ rules_code(scanner).chomp.gsub(/^/, ' ') }
else
encoder.text_token getch, :error
end
RUBY
end

def rules_code scanner, first: true
raise 'no rules defined for %p' % [self] if rules.empty?

[
rules.first.code(scanner, first: first),
*rules.drop(1).map { |rule| rule.code(scanner) }
].join
end

protected

# DSL

def rule pattern, token = nil, next_state = nil, &block
unless token || block
raise 'please pass `rule` a token to yield or a callback'
end

case token
when Class
unless token < Rouge::Token
raise "invalid token: #{token.inspect}"
end

case next_state
when Symbol
rules << Rule.new(pattern, token, next_state)
when nil
rules << Rule.new(pattern, token)
else
raise "invalid next state: #{next_state.inspect}"
end
when nil
rules << CallbackRule.new(pattern, block)
else
raise "invalid token: #{token.inspect}"
end
end

def mixin state_name
rules << Mixin.new(state_name)
end
end

Rule = Struct.new :pattern, :token, :action do
def initialize(pattern, token, action = nil)
super
end

def code scanner, first: false
<<-RUBY + action_code.to_s
#{'els' unless first}if match = scan(#{pattern.inspect})
encoder.text_token match, #{token.token_chain.map(&:name).join('::')}
RUBY
end

def action_code
case action
when :pop!
<<-RUBY
states.pop
state = states.last
RUBY
when Symbol
<<-RUBY
state = #{action.inspect}
states << state
RUBY
end
end
end

CallbackRule = Struct.new :pattern, :callback do
def code scanner, first: false
<<-RUBY
#{'els' unless first}if match = scan(#{pattern.inspect})
@match = match
#{scanner.add_callback(callback)}
RUBY
end
end

Mixin = Struct.new(:state_name) do
def code scanner, first: false
scanner.states[state_name].rules_code(scanner, first: first)
end
end

attr_accessor :states

def state name, &block
@states ||= {}
@states[name] = State.new(name, &block)
end

def add_callback block
base_name = "__callback_line_#{block.source_location.last}"
callback_name = base_name
counter = 'a'
while callbacks.key?(callback_name)
callback_name = "#{base_name}_#{counter}"
counter = counter.succ
end

callbacks[callback_name] = define_method(callback_name, &block)

parameters = block.parameters

if parameters.empty?
callback_name
else
parameter_names = parameters.map do |type, name|
raise "callbacks don't allow rest parameters: %p" % [parameters] unless type == :req || type == :opt
name = :match if name == :m
name
end

parameter_names.each { |name| variables << name }
"#{callback_name}(#{parameter_names.join(', ')})"
end
end

def add_variable name
variables << name
end

protected

def callbacks
@callbacks ||= {}
end

def variables
@variables ||= Set.new
end

def additional_variables
variables - %i(encoder options state states match kind)
end

def scan_tokens_code
<<-"RUBY"
state = options[:state] || @state
states = [state]
#{ restore_local_variables_code }
until eos?
case state
#{ states_code.chomp.gsub(/^/, ' ') }
else
raise_inspect 'Unknown state: %p' % [state], encoder
end
end

@state = state if options[:keep_state]

close_groups(encoder, states)

encoder
RUBY
end

def restore_local_variables_code
additional_variables.sort.map { |name| "#{name} = @#{name}" }.join("\n")
end

def states_code
unless defined?(@states) && [email protected]?
raise NoStatesError, 'no states defined for %p' % [self.class]
end

@states.values.map { |state| state.code(self) }.join
end
end
end
end
Loading