module Klam::CompilationStages::EmitRuby
Emit Ruby
This is the final stage in the compilation pipeline. It is responsible for converting the compiler's internal representation to a string containing Ruby code that is suitable for execution via instance_eval in the context of a Klam::Environment
object.
By the time compilation reaches this stage, all of the heavy lifting should be complete. Emitting Ruby is simply a matter of transliterating the simplified s-expression into Ruby.
Constants
- PRIMITIVE_TEMPLATES
Public Instance Methods
emit_ruby(sexp)
click to toggle source
# File lib/klam/compilation_stages/emit_ruby.rb, line 37 def emit_ruby(sexp) case sexp when Symbol emit_symbol(sexp) when String emit_string(sexp) when Klam::Constant, Klam::Variable, Numeric, true, false sexp.to_s when Array if sexp.empty? Klam::Primitives::Lists::EMPTY_LIST.inspect else emit_compound_form(sexp) end end end
Private Instance Methods
emit_application(form)
click to toggle source
# File lib/klam/compilation_stages/emit_ruby.rb, line 99 def emit_application(form) rator = form[0] rands = form[1..-1] rator_rb = emit_ruby(rator) rands_rb = rands.map { |rand| emit_ruby(rand) } if rator.kind_of?(Symbol) # Application of a function defined in the environment. At this # point partial application and currying have been taken care of, # so a simple send suffices. render_string('__send__($1)', [rator_rb] + rands_rb) else render_string('__apply($1)', [rator_rb] + rands_rb) end end
emit_compound_form(form)
click to toggle source
# File lib/klam/compilation_stages/emit_ruby.rb, line 56 def emit_compound_form(form) # Handle special forms and fall back to normal function application case form[0] when :defun emit_defun(form) when :if emit_if(form) when :lambda emit_lambda(form) when :let emit_let(form) when :"trap-error" emit_trap_error(form) when :do emit_do(form) when :"[DEFUN-CLOSURE]" emit_defun_closure(form) when :"[FIX-VARS]" emit_fix_vars(form) when :"[LOOP]" emit_loop(form) when :"[RECUR]" emit_recur(form) else if full_primitive_form?(form) emit_primitive(form) else emit_application(form) end end end
emit_defun(form)
click to toggle source
# File lib/klam/compilation_stages/emit_ruby.rb, line 115 def emit_defun(form) _, name, params, body = form name_rb = emit_ruby(name) params_rb = params.map { |param| emit_ruby(param) } body_rb = emit_ruby(body) # Some valid Kl function names (e.g. foo-bar) are not valid when used # with Ruby's def syntax. They will work with define_method, but the # resulting methods are slower than if they had been defined via def. # To maximize performance, methods are defined with def and then # renamed to their intended name afterwards. mangled_name = '__klam_fn_' + name.to_s.gsub(/[^a-zA-Z0-9]/, '_') render_string(<<-EOT, name_rb, params_rb, body_rb, mangled_name, params.size) def $4($2) $3 end @eigenclass.rename_method(:$4, $1) @arities[$1] = $5 @curried_methods.delete($1) $1 EOT end
emit_defun_closure(form)
click to toggle source
# File lib/klam/compilation_stages/emit_ruby.rb, line 138 def emit_defun_closure(form) _, name, params, body = form name_rb = emit_ruby(name) params_rb = params.map { |param| emit_ruby(param) } body_rb = emit_ruby(body) # Some valid Kl function names (e.g. foo-bar) are not valid when used # with Ruby's def syntax. They will work with define_method, but the # resulting methods are slower than if they had been defined via def. # To maximize performance, methods are defined with def and then # renamed to their intended name afterwards. mangled_name = ('__klam_fn_' + name.to_s.gsub(/[^a-zA-Z0-9]/, '_')).intern mangled_name_rb = emit_ruby(mangled_name) render_string(<<-EOT, name_rb, params_rb, body_rb, mangled_name_rb, params.size) @eigenclass.def_method($4, ::Kernel.lambda { |$2| $3 }) @eigenclass.rename_method($4, $1) @arities[$1] = $5 @curried_methods.delete($1) $1 EOT end
emit_do(form)
click to toggle source
# File lib/klam/compilation_stages/emit_ruby.rb, line 160 def emit_do(form) rands = form[1,2] rands_rb = rands.map { |rand| emit_ruby(rand) } '(' + rands_rb.join(';') + ')' end
emit_fix_vars(form)
click to toggle source
# File lib/klam/compilation_stages/emit_ruby.rb, line 166 def emit_fix_vars(form) _, params, expr = form params_rb = params.map { |param| emit_ruby(param) } expr_rb = emit_ruby(expr) render_string('(::Kernel.lambda { |$1| $2 }).call($1)', params_rb, expr_rb) end
emit_if(form)
click to toggle source
# File lib/klam/compilation_stages/emit_ruby.rb, line 175 def emit_if(form) args = form[1..3].map { |sexp| emit_ruby(sexp) } render_string('($1 ? $2 : $3)', *args) end
emit_lambda(form)
click to toggle source
# File lib/klam/compilation_stages/emit_ruby.rb, line 180 def emit_lambda(form) _, params, body = form params_rb = params.map { |param| emit_ruby(param) } body_rb = emit_ruby(body) render_string('(::Kernel.lambda { |$1| $2 })', params_rb, body_rb) end
emit_let(form)
click to toggle source
# File lib/klam/compilation_stages/emit_ruby.rb, line 188 def emit_let(form) _, var, value_expr, body_expr = form var_rb = emit_ruby(var) value_expr_rb = emit_ruby(value_expr) body_expr_rb = emit_ruby(body_expr) render_string('($1 = $2; $3)', var_rb, value_expr_rb, body_expr_rb) end
emit_loop(form)
click to toggle source
# File lib/klam/compilation_stages/emit_ruby.rb, line 198 def emit_loop(form) _, loop_var, expr = form val_var = fresh_variable expr_rb = emit_ruby(expr) loop_var_rb = emit_ruby(loop_var) val_var_rb = emit_ruby(val_var) render_string(<<-EOT, loop_var_rb, val_var_rb, expr_rb) $1 = false $2 = nil begin $1 = false $2 = $3 end while $1 $2 EOT end
emit_primitive(form)
click to toggle source
# File lib/klam/compilation_stages/emit_ruby.rb, line 93 def emit_primitive(form) _, template = PRIMITIVE_TEMPLATES[form[0]] rands_rb = form[1..-1].map { |rand| emit_ruby(rand) } render_string(template, *rands_rb) end
emit_recur(form)
click to toggle source
# File lib/klam/compilation_stages/emit_ruby.rb, line 217 def emit_recur(form) _, params, new_value_exprs, loop_var = form loop_var_rb = emit_ruby(loop_var) if params.size > 0 params_rb = params.map { |param| emit_ruby(param) } new_value_exprs_rb = new_value_exprs.map { |expr| emit_ruby(expr) } render_string('(($1 = $2); $3 = true)', params_rb, new_value_exprs_rb, loop_var_rb) else render_string('$1 = true', loop_var_rb) end end
emit_string(str)
click to toggle source
# File lib/klam/compilation_stages/emit_ruby.rb, line 232 def emit_string(str) str.inspect end
emit_symbol(sym)
click to toggle source
# File lib/klam/compilation_stages/emit_ruby.rb, line 236 def emit_symbol(sym) ':"' + sym.to_s + '"' end
emit_trap_error(form)
click to toggle source
# File lib/klam/compilation_stages/emit_ruby.rb, line 240 def emit_trap_error(form) _, expr, handler = form err_var = fresh_variable expr_rb = emit_ruby(expr) apply_handler_rb = emit_application([handler, err_var]) err_var_rb = emit_ruby(err_var) render_string('(begin; $2; rescue => $1; $3; end)', err_var_rb, expr_rb, apply_handler_rb) end
full_primitive_form?(form)
click to toggle source
# File lib/klam/compilation_stages/emit_ruby.rb, line 88 def full_primitive_form?(form) num_args, template = PRIMITIVE_TEMPLATES[form[0]] num_args && (num_args == form.size - 1) end