From 8adf2a2d2eabd9ac972603a128448b7f919c6db8 Mon Sep 17 00:00:00 2001 From: quix Date: Thu, 14 Jan 2010 19:22:56 -0500 Subject: [PATCH] add redparse compiler --- lib/pure/compiler/red_parse.rb | 62 ++++++++++++++++++++++ lib/pure/parser/impl/red_parse.rb | 4 ++ spec/compiler_red_parse_spec.rb | 85 +++++++++++++++++++++++++++++++ 3 files changed, 151 insertions(+) create mode 100644 lib/pure/compiler/red_parse.rb create mode 100644 spec/compiler_red_parse_spec.rb diff --git a/lib/pure/compiler/red_parse.rb b/lib/pure/compiler/red_parse.rb new file mode 100644 index 0000000..1941bc7 --- /dev/null +++ b/lib/pure/compiler/red_parse.rb @@ -0,0 +1,62 @@ +# +# This file is self-contained, intended for use by an external server. +# +# It is not included by the top-level pure.rb. +# + +require 'pure/version' +require 'pure/names' +require 'redparse' + +module Pure + module Compiler #:nodoc: + class RedParse #:nodoc: + # + # Compiles and evaluates a function spec with arg values. + # + def evaluate_function(spec, *args) + compile_function(spec).send(spec[:name], *args) + end + + # + # Compiles a function spec extracted by + # Pure::Parser::RedParse. + # + # Returns an object which responds to spec[:name]. + # + def compile_function(spec) + code_data = ( + if spec[:origin] == :fun + fun_to_define_method(spec[:name], spec[:code]) + else + spec[:code] + end + ) + + instance = Names.new(spec[:name], spec[:args]) + Thread.current[:pure_compiler_input] = code_data.unparse + + # use singleton to hide locals + class << instance + eval(Thread.current[:pure_compiler_input]) + end + + instance + end + + # + # Code-transform `fun' definitions into `define_method' definitions. + # + def fun_to_define_method(name, code_data) + # it's unclear how to create a LiteralNode by hand + literal_node = ::RedParse.new(":x").parse + literal_node[0] = name + + code_data[1] = "define_method" + code_data[2] = [literal_node] + code_data + end + end + end +end + diff --git a/lib/pure/parser/impl/red_parse.rb b/lib/pure/parser/impl/red_parse.rb index cc40725..6b5822a 100644 --- a/lib/pure/parser/impl/red_parse.rb +++ b/lib/pure/parser/impl/red_parse.rb @@ -8,6 +8,10 @@ class << self def extract(mod, method_name, file, line) BaseParser.extract(mod, method_name, file, line, Processor) end + + def compiler + %w[pure/compiler/red_parse Pure::Compiler::RedParse] + end end class Processor diff --git a/spec/compiler_red_parse_spec.rb b/spec/compiler_red_parse_spec.rb new file mode 100644 index 0000000..310cb10 --- /dev/null +++ b/spec/compiler_red_parse_spec.rb @@ -0,0 +1,85 @@ +require File.dirname(__FILE__) + '/pure_spec_base' + +require 'pure/parser/red_parse' +require 'pure/compiler/red_parse' + +describe Pure::Compiler::RedParse do + before :all do + @compiler = Pure::Compiler::RedParse.new + end + + it "should transform `fun' definitions to `define_method' definitions" do + def_f = pure(Pure::Parser::RedParse) do + def f(x, y) + x + y + end + end + + fun_f = pure(Pure::Parser::RedParse) do + fun :f => [:x, :y] do |a, b| + a + b + end + end + + def_g = pure(Pure::Parser::RedParse) do + def g + end + end + + fun_g = pure(Pure::Parser::RedParse) do + fun :g do + end + end + + def_h = pure(Pure::Parser::RedParse) do + def h(x) + x**2 + end + end + + fun_h = pure(Pure::Parser::RedParse) do + fun :h => :x do |a| + a**2 + end + end + + fun_i = pure(Pure::Parser::RedParse) do + fun :i => [:p, :q] do |*s| + s.size + end + end + + fun_j = pure(Pure::Parser::RedParse) do + fun :j => [:p, :q] do |r, *s| + r + s + end + end + + [ + [:def, def_f, :f, "def f(x,y)\nx+y;\nend"], + [:fun, fun_f, :f, "define_method :f do |a, b|\na+b\nend"], + [:def, def_g, :g, "def g\n;\nend"], + [:fun, fun_g, :g, "define_method :g do \nend"], + [:def, def_h, :h, "def h(x)\nx**2;\nend"], + [:fun, fun_h, :h, "define_method :h do |a|\na**2\nend"], + [:fun, fun_i, :i, "define_method :i do |*s|\ns.size\nend"], + [:fun, fun_j, :j, "define_method :j do |r, *s|\nr+s\nend"], + ].each { |type, mod, name, expected| + entry = Pure::ExtractedFunctions[Pure::Parser::RedParse][mod][name] + entry[:code].should be_a(RedParse::Node) + code_data = Marshal.load(Marshal.dump(entry[:code])) + if entry[:origin] == :fun + code_data = @compiler.fun_to_define_method(entry[:name], code_data) + end + recovered = ( + code_data. + unparse. + gsub(%r!\n+!, "\n"). + gsub(%r!^ +!, ""). + gsub(%r! +!, " ") + ) + recovered.should == expected + } + end +end +