Skip to content
This repository has been archived by the owner on Oct 23, 2019. It is now read-only.

Commit

Permalink
Rough cut at function definition; still need code cleanup, passing te…
Browse files Browse the repository at this point in the history
…sts; @brixen rocks!
  • Loading branch information
undees committed Mar 6, 2012
1 parent 4e628e0 commit 060c08c
Show file tree
Hide file tree
Showing 10 changed files with 221 additions and 25 deletions.
2 changes: 0 additions & 2 deletions bin/thnad
Expand Up @@ -5,5 +5,3 @@
loader = Rubinius::CodeLoader.new(ARGV.first)
method = loader.load_compiled_file(ARGV.first, Rubinius::Signature, 18)
result = Rubinius.run_script(method)


20 changes: 16 additions & 4 deletions example.thnad
@@ -1,5 +1,17 @@
if (0) {
print(42)
} else {
print(667)
function eq(a,b) {
if(minus(a,b)) {
0
} else {
1
}
}

function factorial(n) {
if (eq(n, 1)) {
1
} else {
times(n, factorial(minus(n, 1)))
}
}

print(factorial(4))
131 changes: 118 additions & 13 deletions lib/thnad/compiler.rb
Expand Up @@ -2,6 +2,22 @@
require 'thnad/transform'
require 'thnad/builtins'

class Rubinius::Generator
def set_thnad_receiver
push_rubinius
push_literal :Thnad
push_const :Object
send :new, 0
send :const_set, 2
pop
end

def push_thnad_receiver
push_const :Thnad
end
end


module Thnad
class Compiler
def initialize(filename)
Expand All @@ -12,19 +28,110 @@ def initialize(filename)

def compile
tree = parse_source
funcs, exprs = split_functions tree
classname = @classname
klass = make_class(classname)

method = klass.dynamic_method :main do |generator|
g = Rubinius::Generator.new
g.name = @classname.to_sym
g.file = @filename.to_sym
g.set_line 1
g.push_rubinius
g.add_scope
g.set_thnad_receiver

g.push_thnad_receiver
inner = Rubinius::Generator.new
inner.name = :minus
inner.file = @filename.to_sym
inner.set_line 1
inner.required_args = 2
inner.total_args = inner.required_args
inner.push_local 0
inner.push_local 1
inner.send :-, 1
inner.ret
inner.close
inner.use_detected
inner.encode

cm = inner.package Rubinius::CompiledMethod

g.push_rubinius
g.push_literal :minus
g.push_literal cm
g.push_scope
g.push_thnad_receiver
g.send :attach_method, 4
g.pop

g.push_thnad_receiver
inner = Rubinius::Generator.new
inner.name = :times
inner.file = @filename.to_sym
inner.set_line 1
inner.required_args = 2
inner.total_args = inner.required_args
inner.push_local 0
inner.push_local 1
inner.send :*, 1
inner.ret
inner.close
inner.use_detected
inner.encode

cm = inner.package Rubinius::CompiledMethod

g.push_rubinius
g.push_literal :times
g.push_literal cm
g.push_scope
g.push_thnad_receiver
g.send :attach_method, 4
g.pop

funcs.each do |f|
inner = Rubinius::Generator.new
inner.name = f.name.to_sym
inner.file = @filename.to_sym
inner.set_line 1
inner.required_args = f.params.count
inner.total_args = inner.required_args

context = Hash.new
tree.each do |e|
e.eval(context, generator)
end
f.eval(context, inner)

generator.ret
inner.close
inner.use_detected
inner.encode

cm = inner.package Rubinius::CompiledMethod

g.push_rubinius
g.push_literal f.name.to_sym
g.push_literal cm
g.push_scope
g.push_thnad_receiver
g.send :attach_method, 4
g.pop
end

Rubinius::CompiledFile.dump method, @outname, Rubinius::Signature, 18
context = Hash.new
exprs.each do |e|
e.eval(context, g)
end

g.push_true
g.ret

g.close
g.use_detected
p g.stream
g.encode

main = g.package Rubinius::CompiledMethod

Rubinius::CompiledFile.dump main, @outname, Rubinius::Signature, 18
end

private
Expand All @@ -41,14 +148,12 @@ def parse_source
tree.is_a?(Array) ? tree : [tree]
end

def write_result(builder)
destination = File.expand_path(@classname + '.class')
def split_functions(tree)
first_expr = tree.index { |t| ! t.is_a?(Function) }
funcs = first_expr ? tree[0...first_expr] : tree
exprs = first_expr ? tree[first_expr..-1] : []

builder.generate do |n, b|
File.open(destination, 'wb') do |f|
f.write b.generate
end
end
[funcs, exprs]
end

def make_class(name)
Expand Down
19 changes: 16 additions & 3 deletions lib/thnad/nodes.rb
Expand Up @@ -7,14 +7,17 @@ def eval(context, builder)

class Name < Struct.new :name
def eval(context, builder)
value = context.fetch(name) { raise "Unknown parameter #{name}" }
builder.push value
param_names = context[:params] || []
position = param_names.index(name)
raise "Unknown parameter #{name}" unless position

builder.push_local position
end
end

class Funcall < Struct.new :name, :args
def eval(context, builder)
builder.push_self
builder.push_thnad_receiver
args.each { |a| a.eval(context, builder) }
builder.allow_private
builder.send name.to_sym, args.length
Expand Down Expand Up @@ -47,4 +50,14 @@ def eval(context, builder)
endif_label.set!
end
end

class Function < Struct.new :name, :params, :body
def eval(context, builder)
param_names = (params.is_a?(Array) ? params : [params]).map(&:name)
context[:params] = param_names

self.body.eval(context, builder)
builder.ret
end
end
end
14 changes: 13 additions & 1 deletion lib/thnad/parser.rb
Expand Up @@ -35,6 +35,18 @@ class Parser < Parslet::Parser
rule(:if_kw) { str('if') >> space? }
rule(:else_kw) { str('else') >> space? }

rule(:root) { expression }
rule(:func) {
func_kw >> name.as(:func) >> params >> body
}

rule(:func_kw) { str('function') >> space? }

rule(:params) {
lparen >>
((name.as(:param) >> (comma >> name.as(:param)).repeat(0)).maybe).as(:params) >>
rparen
}

rule(:root) { func.repeat(0) >> expression }
end
end
11 changes: 11 additions & 0 deletions lib/thnad/transform.rb
Expand Up @@ -18,5 +18,16 @@ class Transform < Parslet::Transform
rule(:cond => simple(:cond),
:if_true => {:body => simple(:if_true)},
:if_false => {:body => simple(:if_false)}) { Conditional.new(cond, if_true, if_false) }

rule(:param => simple(:param)) { param }
rule(:params => sequence(:params)) { params }

rule(:func => simple(:func),
:params => simple(:name),
:body => simple(:body)) { Function.new(func.name, [name], body) }

rule(:func => simple(:func),
:params => sequence(:params),
:body => simple(:body)) { Function.new(func.name, params, body) }
end
end
5 changes: 5 additions & 0 deletions test/fake_builder.rb
Expand Up @@ -21,6 +21,10 @@ def initialize
@num_labels = 0
end

def int
'int'
end

def new_label
@num_labels += 1
FakeLabel.new self, @num_labels
Expand All @@ -32,5 +36,6 @@ def method_missing(name, *args, &block)
@result += args.empty? ?
"#{name}\n" :
"#{name} #{args.map(&:inspect).join(', ')}\n"
block.call(self) if name.to_s == 'dynamic_method'
end
end
20 changes: 18 additions & 2 deletions test/test_nodes.rb
Expand Up @@ -25,14 +25,14 @@
end

it 'emits a function call' do
@context['foo'] = 667
@context[:params] = 'foo'

input = Thnad::Funcall.new 'baz', [Thnad::Number.new(42),
Thnad::Name.new('foo')]
expected = <<HERE
push_self
push 42
push 667
push_local 0
allow_private
send :baz, 2
HERE
Expand All @@ -57,6 +57,22 @@
label_1:
push 667
label_2:
HERE

input.eval @context, @builder
@builder.result.must_equal expected
end

it 'emits a function definition' do
input = Thnad::Function.new \
'foo',
Thnad::Name.new('x'),
Thnad::Number.new(5)

expected = <<HERE
dynamic_method :foo
push 5
ret
HERE

input.eval @context, @builder
Expand Down
12 changes: 12 additions & 0 deletions test/test_parser.rb
Expand Up @@ -57,4 +57,16 @@

@parser.cond.parse(input).must_equal expected
end

it 'reads a function definition' do
input = <<HERE
function foo(x) {
5
}
HERE
expected = {:func => {:name => 'foo'},
:params => {:param => {:name => 'x'}},
:body => {:number => '5'}}
@parser.func.parse(input).must_equal expected
end
end
12 changes: 12 additions & 0 deletions test/test_transform.rb
Expand Up @@ -63,4 +63,16 @@

@transform.apply(input).must_equal expected
end

it 'transforms a function definition' do
input = {:func => {:name => 'foo'},
:params => {:param => {:name => 'x'}},
:body => {:number => '5'}}
expected = Thnad::Function.new \
'foo',
[Thnad::Name.new('x')],
Thnad::Number.new(5)

@transform.apply(input).must_equal expected
end
end

0 comments on commit 060c08c

Please sign in to comment.