class Sexp2Ruby::Processor

Generate ruby code from a sexp.

Constants

ASSIGN_NODES

Nodes that represent assignment and probably need () around them.

TODO: this should be replaced with full precedence support :/

CONSTRUCTOR_OPTIONS
HASH_SYNTAXES
LF
NODES
RUBY_19_HASH_KEY

Attributes

hash_syntax[R]
indent_lvl[R]
no_paren_methods[R]

Public Class Methods

new(option = {}) click to toggle source

Options:

  • `:hash_syntax` - either `:ruby18` or `:ruby19`. Default is `:ruby19`.

  • `:no_paren_methods` - an array of symbols, these methods will omit argument parentheses. Default is `[]`.

Calls superclass method
# File lib/sexp2ruby/processor.rb, line 132
def initialize(option = {})
  super()
  check_option_keys(option)
  @hash_syntax = extract_option(HASH_SYNTAXES, option[:hash_syntax], :ruby19)
  @no_paren_methods = option[:no_paren_methods] || []
  @indent_lvl = "  "
  self.auto_shift_type = true
  self.strict = true
  self.expected = String
  @calls = []
end

Public Instance Methods

call_pop() click to toggle source

State


# File lib/sexp2ruby/processor.rb, line 162
def call_pop
  @calls.pop
end
call_push(name) click to toggle source
# File lib/sexp2ruby/processor.rb, line 166
def call_push(name)
  @calls.push(name)
end
check_option_keys(option) click to toggle source

Utility Methods


# File lib/sexp2ruby/processor.rb, line 224
def check_option_keys(option)
  diff = option.keys - CONSTRUCTOR_OPTIONS
  unless diff.empty?
    raise InvalidOption, "Invalid option(s): #{diff}"
  end
end
cond_loop(exp, name) click to toggle source

Generate a post-or-pre conditional loop.

# File lib/sexp2ruby/processor.rb, line 232
def cond_loop(exp, name)
  cond = process(exp.shift)
  body = process(exp.shift)
  head_controlled = exp.shift

  body = indent(body).chomp if body

  code = []
  if head_controlled
    code << "#{name} #{cond} do"
    code << body if body
    code << "end"
  else
    code << "begin"
    code << body if body
    code << "end #{name} #{cond}"
  end
  code.join(LF)
end
dthing_escape(type, lit) click to toggle source

Escape something interpolated.

# File lib/sexp2ruby/processor.rb, line 253
def dthing_escape type, lit
  lit = lit.gsub(/\n/, '\n')
  case type
  when :dregx then
    lit.gsub(/(\A|[^\\])\//, '\1\/')
  when :dstr, :dsym then
    lit.gsub(/"/, '\"')
  when :dxstr then
    lit.gsub(/`/, '\`')
  else
    raise "unsupported type #{type.inspect}"
  end
end
extract_option(array, value, default) click to toggle source

Check that `value` is in `array` of valid option values, or raise InvalidOption. If `value` is nil, return `default`.

# File lib/sexp2ruby/processor.rb, line 269
def extract_option(array, value, default)
  if value.nil?
    default
  elsif array.include?(value)
    value
  else
    raise InvalidOption, "Invalid option value: #{value}"
  end
end
finish(exp) click to toggle source

Process all the remaining stuff in exp and return the results sans-nils.

# File lib/sexp2ruby/processor.rb, line 281
def finish exp # REFACTOR: work this out of the rest of the processors
  body = []
  until exp.empty? do
    body << process(exp.shift)
  end
  body.compact
end
indent(s) click to toggle source

Indent all lines of s to the current indent level.

# File lib/sexp2ruby/processor.rb, line 299
def indent(s)
  s.to_s.split(/\n/).map{|line| @indent_lvl + line}.join(LF)
end
parenthesize(exp) click to toggle source

Wrap appropriate expressions in matching parens.

# File lib/sexp2ruby/processor.rb, line 304
def parenthesize exp
  case self.context[1]
  when nil, :defn, :defs, :class, :sclass, :if, :iter, :resbody, :when, :while then
    exp
  else
    "(#{exp})"
  end
end
rewrite_attrasgn(exp) click to toggle source

Rewriters


# File lib/sexp2ruby/processor.rb, line 173
def rewrite_attrasgn exp
  if context.first(2) == [:array, :masgn]
    exp[0] = :call
    exp[2] = exp[2].to_s.sub(/=$/, '').to_sym
  end

  exp
end
rewrite_ensure(exp) click to toggle source
# File lib/sexp2ruby/processor.rb, line 182
def rewrite_ensure exp
  exp = s(:begin, exp) unless context.first == :begin
  exp
end
rewrite_resbody(exp) click to toggle source
# File lib/sexp2ruby/processor.rb, line 187
def rewrite_resbody exp
  raise "no exception list in #{exp.inspect}" unless exp.size > 2 && exp[1]
  raise exp[1].inspect if exp[1][0] != :array
  # for now, do nothing, just check and freak if we see an errant structure
  exp
end
rewrite_rescue(exp) click to toggle source
# File lib/sexp2ruby/processor.rb, line 194
def rewrite_rescue exp
  complex = false
  complex ||= exp.size > 3
  complex ||= exp.resbody.block
  complex ||= exp.resbody.size > 3
  complex ||= exp.find_nodes(:resbody).any? { |n| n[1] != s(:array) }
  complex ||= exp.find_nodes(:resbody).any? { |n| n.last.nil? }
  complex ||= exp.find_nodes(:resbody).any? { |n| n[2] and n[2].node_type == :block }

  handled = context.first == :ensure

  exp = s(:begin, exp) if complex unless handled

  exp
end
rewrite_svalue(exp) click to toggle source
# File lib/sexp2ruby/processor.rb, line 210
def rewrite_svalue(exp)
  case exp.last.first
  when :array
    s(:svalue, *exp[1][1..-1])
  when :splat
    exp
  else
    raise "huh: #{exp.inspect}"
  end
end
ruby19_hash_key?(exp) click to toggle source

Given `exp` representing the left side of a hash pair, return true if it is compatible with the ruby 1.9 hash syntax. For example, the symbol `:foo` is compatible, but the literal `7` is not. Note that strings are not considered “compatible”. If we converted string keys to symbol keys, we wouldn't be faithfully representing the input.

# File lib/sexp2ruby/processor.rb, line 294
def ruby19_hash_key?(exp)
  exp.sexp_type == :lit && exp.length == 2 && RUBY_19_HASH_KEY === exp[1].to_s
end
splat(sym) click to toggle source

Return a splatted symbol for sym.

# File lib/sexp2ruby/processor.rb, line 314
def splat(sym)
  :"*#{sym}"
end
util_dthing(type, exp) click to toggle source

Generate something interpolated.

# File lib/sexp2ruby/processor.rb, line 319
def util_dthing(type, exp)
  s = []

  # first item in sexp is a string literal
  s << dthing_escape(type, exp.shift)

  until exp.empty?
    pt = exp.shift
    case pt
    when Sexp then
      case pt.first
      when :str then
        s << dthing_escape(type, pt.last)
      when :evstr then
        s << '#{' << process(pt) << '}' # do not use interpolation here
      else
        raise "unknown type: #{pt.inspect}"
      end
    else
      raise "unhandled value in d-thing: #{pt.inspect}"
    end
  end

  s.join
end
util_module_or_class(exp, is_class=false) click to toggle source

Utility method to generate ether a module or class.

# File lib/sexp2ruby/processor.rb, line 346
def util_module_or_class(exp, is_class=false)
  result = []

  name = exp.shift
  name = process name if Sexp === name

  result << name

  if is_class
    superk = process(exp.shift)
    result << " < #{superk}" if superk
  end

  result << LF

  body = []
  begin
    code = process(exp.shift) unless exp.empty?
    body << code.chomp unless code.nil? or code.chomp.empty?
  end until exp.empty?

  body = body.empty? ? "" : indent(body.join("\n\n")) + LF
  result << body
  result << "end"

  result.join
end