Class: Sexp2Ruby::Processor
- Inherits:
-
SexpProcessor
- Object
- SexpProcessor
- Sexp2Ruby::Processor
- Defined in:
- lib/sexp2ruby/processor.rb
Overview
Generate ruby code from a sexp.
Constant Summary collapse
- LF =
"\n"- ASSIGN_NODES =
Nodes that represent assignment and probably need () around them.
TODO: this should be replaced with full precedence support :/
[ :dasgn, :flip2, :flip3, :lasgn, :masgn, :attrasgn, :op_asgn1, :op_asgn2, :op_asgn_and, :op_asgn_or, :return, :if, # HACK :rescue, ]
- HASH_SYNTAXES =
[:ruby18, :ruby19]
- RUBY_19_HASH_KEY =
/\A[a-z][_a-zA-Z0-9]+\Z/- CONSTRUCTOR_OPTIONS =
[ :hash_syntax, :no_paren_methods ]
- NODES =
[ :alias, :and, :arglist, :args, :array, :attrasgn, :back_ref, :begin, :block, :block_pass, :break, :call, :case, :cdecl, :class, :colon2, :colon3, :const, :cvar, :cvasgn, :cvdecl, :defined, :defn, :defs, :dot2, :dot3, :dregx, :dregx_once, :dstr, :dsym, :dxstr, :ensure, :evstr, :false, :flip2, :flip3, :for, :gasgn, :gvar, :hash, :iasgn, :if, :iter, :ivar, :kwsplat, :lasgn, :lit, :lvar, :masgn, :match, :match2, :match3, :module, :next, :nil, :not, :nth_ref, :op_asgn1, :op_asgn2, :op_asgn_and, :op_asgn_or, :or, :postexe, :redo, :resbody, :rescue, :retry, :return, :sclass, :self, :splat, :str, :super, :svalue, :to_ary, :true, :undef, :until, :valias, :when, :while, :xstr, :yield, :zsuper ]
Instance Attribute Summary collapse
-
#hash_syntax ⇒ Object
readonly
Returns the value of attribute hash_syntax.
-
#indent_lvl ⇒ Object
readonly
Returns the value of attribute indent_lvl.
-
#no_paren_methods ⇒ Object
readonly
Returns the value of attribute no_paren_methods.
Instance Method Summary collapse
-
#call_pop ⇒ Object
State —–.
- #call_push(name) ⇒ Object
-
#check_option_keys(option) ⇒ Object
Utility Methods —————.
-
#cond_loop(exp, name) ⇒ Object
Generate a post-or-pre conditional loop.
-
#dthing_escape(type, lit) ⇒ Object
Escape something interpolated.
-
#extract_option(array, value, default) ⇒ Object
Check that
valueis inarrayof valid option values, or raise InvalidOption. -
#finish(exp) ⇒ Object
Process all the remaining stuff in
expand return the results sans-nils. -
#indent(s) ⇒ Object
Indent all lines of
sto the current indent level. -
#initialize(option = {}) ⇒ Processor
constructor
Options:.
-
#parenthesize(exp) ⇒ Object
Wrap appropriate expressions in matching parens.
-
#rewrite_attrasgn(exp) ⇒ Object
Rewriters ———.
- #rewrite_ensure(exp) ⇒ Object
- #rewrite_resbody(exp) ⇒ Object
- #rewrite_rescue(exp) ⇒ Object
- #rewrite_svalue(exp) ⇒ Object
-
#ruby19_hash_key?(exp) ⇒ Boolean
Given
exprepresenting the left side of a hash pair, return true if it is compatible with the ruby 1.9 hash syntax. -
#splat(sym) ⇒ Object
Return a splatted symbol for
sym. -
#util_dthing(type, exp) ⇒ Object
Generate something interpolated.
-
#util_module_or_class(exp, is_class = false) ⇒ Object
Utility method to generate ether a module or class.
Constructor Details
#initialize(option = {}) ⇒ Processor
Options:
-
:hash_syntax- either:ruby18or:ruby19. Default is:ruby19. -
:no_paren_methods- an array of symbols, these methods will omit argument parentheses. Default is[].
132 133 134 135 136 137 138 139 140 141 142 |
# 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 |
Instance Attribute Details
#hash_syntax ⇒ Object (readonly)
Returns the value of attribute hash_syntax.
124 125 126 |
# File 'lib/sexp2ruby/processor.rb', line 124 def hash_syntax @hash_syntax end |
#indent_lvl ⇒ Object (readonly)
Returns the value of attribute indent_lvl.
124 125 126 |
# File 'lib/sexp2ruby/processor.rb', line 124 def indent_lvl @indent_lvl end |
#no_paren_methods ⇒ Object (readonly)
Returns the value of attribute no_paren_methods.
124 125 126 |
# File 'lib/sexp2ruby/processor.rb', line 124 def no_paren_methods @no_paren_methods end |
Instance Method Details
#call_pop ⇒ Object
State
162 163 164 |
# File 'lib/sexp2ruby/processor.rb', line 162 def call_pop @calls.pop end |
#call_push(name) ⇒ Object
166 167 168 |
# File 'lib/sexp2ruby/processor.rb', line 166 def call_push(name) @calls.push(name) end |
#check_option_keys(option) ⇒ Object
Utility Methods
224 225 226 227 228 229 |
# 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) ⇒ Object
Generate a post-or-pre conditional loop.
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 |
# 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) ⇒ Object
Escape something interpolated.
253 254 255 256 257 258 259 260 261 262 263 264 265 |
# 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) ⇒ Object
Check that value is in array of valid option values, or raise InvalidOption. If value is nil, return default.
269 270 271 272 273 274 275 276 277 |
# 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) ⇒ Object
Process all the remaining stuff in exp and return the results sans-nils.
281 282 283 284 285 286 287 |
# 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) ⇒ Object
Indent all lines of s to the current indent level.
299 300 301 |
# 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) ⇒ Object
Wrap appropriate expressions in matching parens.
304 305 306 307 308 309 310 311 |
# 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) ⇒ Object
Rewriters
173 174 175 176 177 178 179 180 |
# 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) ⇒ Object
182 183 184 185 |
# File 'lib/sexp2ruby/processor.rb', line 182 def rewrite_ensure exp exp = s(:begin, exp) unless context.first == :begin exp end |
#rewrite_resbody(exp) ⇒ Object
187 188 189 190 191 192 |
# 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) ⇒ Object
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 |
# 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) ⇒ Object
210 211 212 213 214 215 216 217 218 219 |
# 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) ⇒ Boolean
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.
294 295 296 |
# 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) ⇒ Object
Return a splatted symbol for sym.
314 315 316 |
# File 'lib/sexp2ruby/processor.rb', line 314 def splat(sym) :"*#{sym}" end |
#util_dthing(type, exp) ⇒ Object
Generate something interpolated.
319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 |
# 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) ⇒ Object
Utility method to generate ether a module or class.
346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 |
# 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 |