Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
While this will be a part of Laser, a small independent tool would make a lot of people happy. Long story short: auto-convert hash syntaxes.
- Loading branch information
Michael Edgar
committed
Jun 24, 2011
1 parent
bf111f3
commit 327dde8
Showing
11 changed files
with
285 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,3 +19,5 @@ rdoc | |
pkg | ||
|
||
## PROJECT::SPECIFIC | ||
|
||
.rvmrc |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
#!/usr/bin/env ruby -w | ||
|
||
require 'rubygems' | ||
require 'hash_syntax' | ||
HashSyntax::Runner.new.run! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
#!/usr/bin/env ruby -w | ||
require 'ripper' | ||
require 'object_regex' | ||
require 'trollop' | ||
|
||
require 'hash_syntax/token' | ||
require 'hash_syntax/runner' | ||
require 'hash_syntax/transformer' | ||
require 'hash_syntax/version' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
module HashSyntax | ||
class Runner | ||
|
||
def run! | ||
options = gather_options | ||
validate_options(options) | ||
files = gather_files | ||
files.each do |name| | ||
transformed_file = Transformer.transform(File.read(name), options) | ||
File.open(name, 'w') { |fp| fp.write(transformed_file) } | ||
end | ||
end | ||
|
||
private | ||
|
||
def gather_options | ||
Trollop::options do | ||
version HashSyntax::Version::STRING | ||
banner <<-EOF | ||
hash_syntax #{HashSyntax::Version::STRING} by Michael Edgar (adgar@carboni.ca) | ||
Automatically convert hash symbol syntaxes in your Ruby code. | ||
EOF | ||
opt :to_18, 'Convert to Ruby 1.8 syntax (:key => value)' | ||
opt :to_19, 'Convert to Ruby 1.9 syntax (key: value)' | ||
end | ||
end | ||
|
||
def validate_options(opts) | ||
Trollop::die 'Must specify --to_18 or --to_19' unless opts[:to_18] or opts[:to_19] | ||
end | ||
|
||
AUTO_SUBDIRS = %w(app ext features lib spec test) | ||
|
||
def gather_files | ||
if ARGV.empty? | ||
AUTO_SUBDIRS.map { |dir| Dir["#{Dir.pwd}/#{dir}/**/*.rb"] }.flatten | ||
else | ||
ARGV | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
module HashSyntax | ||
Token = Struct.new(:type, :body, :line, :col) do | ||
# Unpacks the token from Ripper and breaks it into its separate components. | ||
# | ||
# @param [Array<Array<Integer, Integer>, Symbol, String>] token the token | ||
# from Ripper that we're wrapping | ||
def initialize(token) | ||
(self.line, self.col), self.type, self.body = token | ||
end | ||
|
||
def width | ||
body.size | ||
end | ||
|
||
def reg_desc | ||
if type == :on_op && body == '=>' | ||
'hashrocket' | ||
else | ||
type.to_s.sub(/^on_/, '') | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
module HashSyntax | ||
module Transformer | ||
MATCH_18 = ObjectRegex.new('symbeg (ident | kw) sp? hashrocket') | ||
MATCH_19 = ObjectRegex.new('label') | ||
|
||
extend self | ||
|
||
def transform(input_text, options) | ||
tokens = extract_tokens(input_text) | ||
if options[:to_18] | ||
transform_to_18(input_text, tokens, options) | ||
elsif options[:to_19] | ||
transform_to_19(input_text, tokens, options) | ||
else | ||
raise ArgumentError.new('Either :to_18 or :to_19 must be specified.') | ||
end | ||
end | ||
|
||
private | ||
|
||
def extract_tokens(text) | ||
swizzle_parser_flags do | ||
Ripper.lex(text).map { |token| Token.new(token) } | ||
end | ||
end | ||
|
||
def swizzle_parser_flags | ||
old_w = $-w | ||
old_v = $-v | ||
old_d = $-d | ||
$-w = $-v = $-d = false | ||
yield | ||
ensure | ||
$-w = old_w | ||
$-v = old_v | ||
$-d = old_d | ||
end | ||
|
||
def transform_to_18(input_text, tokens, options) | ||
lines = input_text.lines.to_a # eagerly expand lines | ||
matches = MATCH_19.all_matches(tokens) | ||
line_adjustments = Hash.new(0) | ||
matches.each do |label_list| | ||
label = label_list.first | ||
lines[label.line - 1][label.col + line_adjustments[label.line],label.width] = ":#{label.body[0..-2]} =>" | ||
line_adjustments[label.line] += 3 # " =>" is inserted and is 3 chars | ||
end | ||
lines.join | ||
end | ||
|
||
def transform_to_19(input_text, tokens, options) | ||
lines = input_text.lines.to_a # eagerly expand lines | ||
matches = MATCH_18.all_matches(tokens) | ||
line_adjustments = Hash.new(0) | ||
matches.each do |match_tokens| | ||
symbeg, ident, *spacing_and_comments, rocket = match_tokens | ||
lines[symbeg.line - 1][symbeg.col + line_adjustments[symbeg.line],1] = '' | ||
lines[ident.line - 1].insert(ident.col + line_adjustments[ident.line] + ident.width - 1, ':') | ||
lines[rocket.line - 1][rocket.col + line_adjustments[rocket.line],2] = '' | ||
if spacing_and_comments.last != nil && spacing_and_comments.last.type == :on_sp | ||
lines[rocket.line - 1][rocket.col + line_adjustments[rocket.line] - 1,1] = '' | ||
line_adjustments[rocket.line] -= 3 # chomped " =>" | ||
else | ||
line_adjustments[rocket.line] -= 2 # only chomped the "=>" | ||
end | ||
end | ||
lines.join | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
module HashSyntax | ||
module Version | ||
MAJOR = 1 | ||
MINOR = 0 | ||
PATCH = 0 | ||
BUILD = 'pre1' | ||
|
||
if BUILD.empty? | ||
STRING = [MAJOR, MINOR, PATCH].compact.join('.') | ||
else | ||
STRING = [MAJOR, MINOR, PATCH, BUILD].compact.join('.') | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
require File.expand_path(File.dirname(__FILE__) + '/spec_helper') | ||
require_relative 'spec_helper' | ||
|
||
describe "HashSyntax" do | ||
it "fails" do | ||
fail "hey buddy, you should probably rename this file and start specing for real" | ||
it "has a version" do | ||
HashSyntax::Version::STRING.should be >= "1.0.0" | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,22 @@ | ||
$LOAD_PATH.unshift(File.dirname(__FILE__)) | ||
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) | ||
require 'hash_syntax' | ||
require 'spec' | ||
require 'spec/autorun' | ||
require 'rspec' | ||
require 'rspec/autorun' | ||
|
||
Spec::Runner.configure do |config| | ||
RSpec::Matchers.define :transform_to do |output, target| | ||
match do |input| | ||
@result = HashSyntax::Transformer.transform(input, target => true) | ||
@result == output | ||
end | ||
|
||
failure_message_for_should do |input| | ||
"expected '#{input}' to correct to #{output}, not #{@result}" | ||
end | ||
|
||
diffable | ||
end | ||
|
||
RSpec.configure do |config| | ||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
require_relative 'spec_helper' | ||
|
||
describe HashSyntax::Transformer do | ||
describe 'transforming from 1.8 to 1.9 syntax' do | ||
it 'can transform a simple hash' do | ||
'x = {:foo => :bar}'.should transform_to('x = {foo: :bar}', :to_19) | ||
end | ||
|
||
it 'transforms all hashes in a block of code' do | ||
input = %q{ | ||
with_jumps_redirected(:break => ensure_body[1], :redo => ensure_body[1], :next => ensure_body[1], | ||
:return => ensure_body[1], :rescue => ensure_body[1], | ||
:yield_fail => ensure_body[1]) do | ||
rescue_target, yield_fail_target = | ||
build_rescue_target(node, result, rescue_body, ensure_block, | ||
current_rescue, current_yield_fail) | ||
walk_body_with_rescue_target(result, body, body_block, rescue_target, yield_fail_target) | ||
end | ||
} | ||
output = %q{ | ||
with_jumps_redirected(break: ensure_body[1], redo: ensure_body[1], next: ensure_body[1], | ||
return: ensure_body[1], rescue: ensure_body[1], | ||
yield_fail: ensure_body[1]) do | ||
rescue_target, yield_fail_target = | ||
build_rescue_target(node, result, rescue_body, ensure_block, | ||
current_rescue, current_yield_fail) | ||
walk_body_with_rescue_target(result, body, body_block, rescue_target, yield_fail_target) | ||
end | ||
} | ||
input.should transform_to(output, :to_19) | ||
end | ||
|
||
it 'transforms all hashes in a block of code without minding tight spacing' do | ||
input = %q{ | ||
with_jumps_redirected(:break=>ensure_body[1], :redo=>ensure_body[1], :next=>ensure_body[1], | ||
:return=>ensure_body[1], :rescue=>ensure_body[1], | ||
:yield_fail=>ensure_body[1]) do | ||
rescue_target, yield_fail_target = | ||
build_rescue_target(node, result, rescue_body, ensure_block, | ||
current_rescue, current_yield_fail) | ||
walk_body_with_rescue_target(result, body, body_block, rescue_target, yield_fail_target) | ||
end | ||
} | ||
output = %q{ | ||
with_jumps_redirected(break:ensure_body[1], redo:ensure_body[1], next:ensure_body[1], | ||
return:ensure_body[1], rescue:ensure_body[1], | ||
yield_fail:ensure_body[1]) do | ||
rescue_target, yield_fail_target = | ||
build_rescue_target(node, result, rescue_body, ensure_block, | ||
current_rescue, current_yield_fail) | ||
walk_body_with_rescue_target(result, body, body_block, rescue_target, yield_fail_target) | ||
end | ||
} | ||
input.should transform_to(output, :to_19) | ||
end | ||
|
||
end | ||
|
||
describe 'transforming from 1.9 to 1.8 syntax' do | ||
it 'can transform a simple hash' do | ||
'x = {foo: :bar}'.should transform_to('x = {:foo => :bar}', :to_18) | ||
end | ||
|
||
it 'transforms all hashes in a block of code' do | ||
input = %q{ | ||
with_jumps_redirected(break: ensure_body[1], redo: ensure_body[1], next: ensure_body[1], | ||
return: ensure_body[1], rescue: ensure_body[1], | ||
yield_fail: ensure_body[1]) do | ||
rescue_target, yield_fail_target = | ||
build_rescue_target(node, result, rescue_body, ensure_block, | ||
current_rescue, current_yield_fail) | ||
walk_body_with_rescue_target(result, body, body_block, rescue_target, yield_fail_target) | ||
end | ||
} | ||
output = %q{ | ||
with_jumps_redirected(:break => ensure_body[1], :redo => ensure_body[1], :next => ensure_body[1], | ||
:return => ensure_body[1], :rescue => ensure_body[1], | ||
:yield_fail => ensure_body[1]) do | ||
rescue_target, yield_fail_target = | ||
build_rescue_target(node, result, rescue_body, ensure_block, | ||
current_rescue, current_yield_fail) | ||
walk_body_with_rescue_target(result, body, body_block, rescue_target, yield_fail_target) | ||
end | ||
} | ||
input.should transform_to(output, :to_18) | ||
end | ||
end | ||
end |