Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Sven Fuchs committed Jun 13, 2009
0 parents commit d6a0cb2
Show file tree
Hide file tree
Showing 77 changed files with 3,425 additions and 0 deletions.
20 changes: 20 additions & 0 deletions MIT-LICENSE
@@ -0,0 +1,20 @@
Copyright (c) 2009 Sven Fuchs <svenfuchs@artweb-design.de>

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
15 changes: 15 additions & 0 deletions NOTES
@@ -0,0 +1,15 @@

# MISSING STUFF

# range
# .. ...
#
# special:
# defined?
#
# hash:
# [ ] [ ]=
#
# control flow:
# if unless while until
# begin/end
11 changes: 11 additions & 0 deletions README.markdown
@@ -0,0 +1,11 @@
Ripper2Ruby
===========

Similar to ruby2ruby this library allows to parse Ruby code, modify and
recompile it back to Ruby.

Differences:

* uses Ripper for parsing (shipped with Ruby 1.9)
* produces a full object-oriented representation of the Ruby code

19 changes: 19 additions & 0 deletions lib/ansi.rb
@@ -0,0 +1,19 @@
module Ansi
COLORS = { :red =>";31", :yellow => ";33", :green => ";32" }
STYLES = { :bold => ";1", :underline => ";4" }

def ansi_format(text, formats)
res = "\e[0"
if formats.is_a?(Array) || formats.is_a?(Hash)
COLORS.each { |k,v| res += v if formats.include?(k) }
STYLES.each { |k,v| res += v if formats.include?(k) }
elsif formats.is_a?(Symbol)
COLORS.each { |k,v| res += v if formats == k }
STYLES.each { |k,v| res += v if formats == k }
elsif formats.respond_to?(:to_sym)
COLORS.each { |k,v| res += v if formats.to_sym == k }
STYLES.each { |k,v| res += v if formats.to_sym == k }
end
res += "m" + text.to_s + "\e[0m"
end
end
5 changes: 5 additions & 0 deletions lib/core_ext/object/meta_class.rb
@@ -0,0 +1,5 @@
class Object
def meta_class
(class << self; self; end)
end
end
6 changes: 6 additions & 0 deletions lib/core_ext/object/tap.rb
@@ -0,0 +1,6 @@
class Object
def tap
yield self
self
end
end
52 changes: 52 additions & 0 deletions lib/ripper/NOTES
@@ -0,0 +1,52 @@
TODO
- invent a StatementsList so that it can have params


Ripper::SCANNER_EVENTS

:CHAR
:__end__
:backref
:backtick
:comma
:comment
:const
:cvar
:embdoc
:embdoc_beg
:embdoc_end
:embexpr_beg
:embexpr_end
:embvar
:float
:gvar
:heredoc_beg
:heredoc_end
:ident
:ignored_nl "\n" # => [:@ignored_nl, "\n", [1, 0]]
:int
:ivar
:kw
:label
:lbrace
:lbracket "[a]" # => [:@lbracket, "[", [1, 0]]
:lparen "foo(b)" # => [:@lparen, "(", [1, 3]]
:nl "a\n" # => [:@nl, "\n", [1, 1]]
:op
:period
:qwords_beg "%w(a b c)" # => [:@qwords_beg, "%w(", [1, 0]]
:rbrace
:rbracket "[a]" # => [:@rbracket, "[", [1, 2]]
:regexp_beg
:regexp_end
:rparen "foo(b)" # => [:@rparen, ")", [1, 5]]
:semicolon
:sp " a" # => [:@sp, " ", [1, 0]]
:symbeg ":a" # => [:@symbeg, ":'", [1, 1]]
:tlambda
:tlambeg
:tstring_beg "'a'; %(b)" # => [:@tstring_beg, "'", [1, 0]], [:@tstring_beg, "%(", [1, 5]]
:tstring_content '"a"' # => [:@tstring_content, "a", [1, 1]]
:tstring_end "'a'; %(b)" # => [:@tstring_end, "'", [1, 2]], [:@tstring_end, ")", [1, 8]]
:words_beg
:words_sep
54 changes: 54 additions & 0 deletions lib/ripper/erb/stripper.rb
@@ -0,0 +1,54 @@
# replaces html and erb tags with whitespace so that we can parse the result
# as pure ruby preserving the exact positions of tokens in the original erb
# source code

require 'erb'
$KCODE = 'u'

class Ripper
module Erb
class Scanner < ERB::Compiler::Scanner
def scan
stag_reg = /(.*?)(^[ \t]*<%%|<%=|<%#|<%-|<%|\z)/m
etag_reg = /(.*?)(%%>|\-%>|%>|\z)/m
scanner = StringScanner.new(@src)
while !scanner.eos?
scanner.scan(@stag ? etag_reg : stag_reg)
yield(scanner[1]) unless scanner[1].nil?
yield(scanner[2]) unless scanner[2].nil?
end
end
end
ERB::Compiler::Scanner.regist_scanner(Scanner, nil, false)

class Stripper
def to_ruby(source)
result = ''
comment = false
scanner = ERB::Compiler.new(nil).make_scanner(source)
scanner.scan do |token|
comment = true if token == '<%#'
if scanner.stag.nil?
result << to_whitespace(token)
scanner.stag = token if ['<%', '<%-', '<%=', '<%#'].include?(token)
elsif ['%>', '-%>'].include?(token)
result << to_whitespace(token.gsub(/>/, ';'))
scanner.stag = nil
else
result << (comment ? to_whitespace(token) : token)
comment = false
end
end
result
end

def to_whitespace(str)
str.gsub(/[^\s;]/, ' ')
end

# def content_dump(string)
# string.split("\n").map { |s| to_whitespace(s) }.join("\n")
# end
end
end
end
73 changes: 73 additions & 0 deletions lib/ripper/ruby_builder.rb
@@ -0,0 +1,73 @@
require 'ripper'
require 'ruby'
require 'ripper/ruby_builder/token'
require 'ripper/ruby_builder/stack'

Dir[File.dirname(__FILE__) + '/ruby_builder/callbacks/*.rb'].each { |file| require file }

class Ripper
class RubyBuilder < Ripper::SexpBuilder
class << self
def build(src)
new(src).parse
end
end

WHITESPACE = [:@sp, :@nl, :@ignored_nl]

include Core, Program, Block, Params, Call, String, Symbol, Hash, Array,
Args, Assignment, Operator, Scanner

attr_reader :src, :filename, :stack

def initialize(src, filename = nil, lineno = nil)
@src = src || filename && File.read(filename)
@filename = filename
@whitespace = ''
@stack = []
@_stack_ignore_stack = [[]]
@stack = Stack.new
super
end

def position
[lineno - 1, column]
end

def push(sexp)
stack.push(Token.new(*sexp))
end

protected

def build_identifier(token)
end

def build_token(token)
Ruby::Token.new(token.value, token.position, token.whitespace) if token
end

def pop(*types)
stack.pop(*types)
end

def pop_delim(type, options = {})
pop_delims(type, options).first
end

def pop_delims(*types)
options = types.last.is_a?(::Hash) ? types.pop : {}
stack_ignore(*WHITESPACE) do
types.map { |type| pop(type, options).map { |token| build_token(token) } }.flatten.compact
end
end

def pop_whitespace
pop(*WHITESPACE).reverse.map { |token| token.value }.join
end

def stack_ignore(*types, &block)
stack.ignore_types(*types, &block)
end
end
end
46 changes: 46 additions & 0 deletions lib/ripper/ruby_builder/callbacks/args.rb
@@ -0,0 +1,46 @@
class Ripper
class RubyBuilder < Ripper::SexpBuilder
module Args
def on_arg_paren(args)
args ||= Ruby::ArgsList.new # will be nil when call has an empty arglist, e.g. I18n.t()

pop_delim(:@rparen).tap { |r| args.rdelim = r if r }
pop_delim(:@lparen).tap { |l| args.ldelim = l if l }

args
end

def on_args_add_block(args, block)
rdelim = pop_delim(:@rparen, :@rbracket) # also used by :aref_field assignments, i.e. array[0] = :foo
operator = pop_delim(:@op, :value => '&')
separators = pop_delims(:@comma)
ldelim = pop_delim(:@lparen, :@lbracket)

args << Ruby::BlockArg.new(block, operator) if block
args.separators += separators if separators
args.ldelim = ldelim if ldelim
args.rdelim = rdelim if rdelim
args
end

def on_args_add_star(args, arg)
stack_ignore(:@rparen, :@period) do
pop_delims(:@comma).tap { |s| args.separators += s.reverse }
star = pop_delim(:@op)
end
args << arg
args
end

def on_args_add(args, arg)
pop_delims(:@comma).tap { |s| args.separators += s.reverse }
args << arg
args
end

def on_args_new
Ruby::ArgsList.new
end
end
end
end
33 changes: 33 additions & 0 deletions lib/ripper/ruby_builder/callbacks/array.rb
@@ -0,0 +1,33 @@
class Ripper
class RubyBuilder < Ripper::SexpBuilder
module Array
# elements and separators are collected in on_args_add
# confusingly ripper throws the same events

def on_array(elements)
rdelim, ldelim = pop_delims(:@rbracket, :@lbracket)
separators = elements ? elements.separators : []
elements = elements ? elements.to_a : []
Ruby::Array.new(elements, ldelim, rdelim, separators)
end

def on_qwords_new(*args)
Ruby::Array.new(nil, pop_delim(:@qwords_beg))
end

def on_qwords_add(array, arg)
tokens = pop_delims(:@words_sep)

array.separators += tokens.select { |t| t.token =~ /^\s*$/ }
array.rdelim = (tokens - array.separators).first
array << arg
array
end

def on_aref_field(target, args)
Ruby::Call.new(target, nil, nil, args)
end
end
end
end

0 comments on commit d6a0cb2

Please sign in to comment.