Permalink
Browse files

omg

  • Loading branch information...
Josep M. Bach
Josep M. Bach committed Dec 22, 2012
1 parent 49f9eee commit abd28d9428d7a65822428503d1407ef7e1b8fb31
View
@@ -0,0 +1,8 @@
+require 'rake/testtask'
+Rake::TestTask.new do |t|
+ t.libs << "test"
+ t.test_files = FileList['test/**/*_test.rb']
+ t.verbose = true
+end
+
+task :default => :test
View
@@ -0,0 +1,20 @@
+# Terror compiler
+
+This is a proof-of-concept compiler written in Ruby (although it runs only on
+Rubinius) that compiles a subset of Ruby to Terror bytecode.
+
+Since writing a compiler targeting a register-based VM is not trivial to me,
+register allocation is rather naive and non-efficient. Basically, reusability
+of registers is close to zero. If you have a suggestion about this (some book
+or article regarding this topic) I'd be really happy if you pointed it out for
+me to read about this.
+
+## Usage
+
+ $ rvm use rbx-head
+ $ bin/terror examples/hello_world.rb > myprogram.tvm
+
+Or just use the easy shell script to compile and run on the fly:
+
+ $ rvm use rbx-head
+ $ ./tvm examples/hello_world.rb
View
@@ -0,0 +1,12 @@
+#!/usr/bin/env rbx
+$: << 'lib'
+require 'terror'
+
+unless ARGV.first
+ puts "Terror #{Terror::VERSION}\n================"
+ puts "\tUsage: terror my_file.rb > my_file.tc"
+ exit(1)
+end
+
+verbose = ARGV[1] == '-v'
+puts Terror.parse_file(ARGV[0], verbose)
@@ -0,0 +1 @@
+puts (3-1) + (3 * (9/3))
@@ -0,0 +1,15 @@
+parent = Object.clone()
+parent.age = 50
+parent.money = 1000
+child = parent.clone()
+child.age = 25
+
+print "Parent age:"
+puts parent.age
+print "Parent money:"
+puts parent.money
+
+print "Child age:"
+puts child.age
+print "Child money:"
+puts child.money
@@ -0,0 +1,4 @@
+puts VM.primitive[:+].apply(3, 4)
+puts VM.primitive[:-].apply(4, 3)
+puts VM.primitive[:*].apply(4, 3)
+puts VM.primitive[:/].apply(4, 2)
@@ -0,0 +1 @@
+Object.prelude_ran = true
View
@@ -0,0 +1,13 @@
+require "terror/version"
+require "terror/core_ext/node"
+require "terror/visitor"
+
+module Terror
+ def self.parse_file(file, verbose=false)
+ ast = Rubinius::Melbourne19.parse_file(ARGV[0])
+ visitor = Terror::Visitor.new
+ ast.lazy_visit(visitor, ast)
+ puts visitor.generator.disassemble if verbose
+ puts visitor.finalize
+ end
+end
@@ -0,0 +1,37 @@
+module Terror
+ module Branching
+ class Label
+ def initialize(generator)
+ @g = generator
+ @now = 0
+ @then = 0
+ end
+
+ def start!
+ @now = @g.ip
+ self
+ end
+
+ def set!
+ @then = @g.ip
+ self
+ end
+
+ def offset
+ @then - @now
+ end
+
+ def to_i
+ offset
+ end
+
+ def ==(other)
+ to_i == other.to_i
+ end
+ end
+
+ def new_label
+ Label.new(self)
+ end
+ end
+end
@@ -0,0 +1,19 @@
+module Rubinius
+ module AST
+ class Node
+ # Public: Works like #visit, but it doesn't visit the children just yet;
+ # instead, lets the visitor decide when and how to do it.
+ #
+ # visitor - The visitor object. It must respond to methods named after the
+ # node names.
+ #
+ # Returns nothing.
+ def lazy_visit(visitor, parent=nil, indent=false)
+ args = [self.node_name, self, parent]
+ args.push true if indent
+
+ visitor.__send__ *args
+ end
+ end
+ end
+end
@@ -0,0 +1,138 @@
+require 'terror/instructions'
+require 'terror/branching'
+
+module Terror
+ class Generator
+ include Instructions
+ include Branching
+ attr_reader :literals, :locals, :ip
+
+ Local = Struct.new(:name)
+
+ def initialize
+ @locals = []
+ @literals = []
+ @ip = 0
+ end
+
+ def disassemble
+ instructions.map(&:to_s).join "\n"
+ end
+
+ def encode
+ # Always return nil at the end of a script
+ pushnil
+ ret
+
+ output = "_main\n"
+ output << ":%i:%i\n" % [
+ @literals.size,
+ @ip
+ ]
+ output << @literals.join("\n")
+ output << "\n"
+ # instructions.each do |i|
+ # p i
+ # end
+ output << instructions.map(&:encode).flatten.join("\n")
+ output
+ end
+
+ def push(value)
+ @ip += 1
+ _push literal(value)
+ end
+
+ def pop
+ @ip += 1
+ _pop
+ end
+
+ def pushtrue
+ @ip += 1
+ _pushtrue
+ end
+
+ def pushfalse
+ @ip += 1
+ _pushfalse
+ end
+
+ def pushnil
+ @ip += 1
+ _pushnil
+ end
+
+ def jmp(label)
+ label.start!
+ @ip += 1
+ _jmp label
+ end
+
+ def jif(label)
+ label.start!
+ @ip += 1
+ _jif label
+ end
+
+ def jit(label)
+ label.start!
+ @ip += 1
+ _jit label
+ end
+
+ def pushself
+ @ip += 1
+ _pushself
+ end
+
+ def pushlocal(name)
+ @ip += 1
+ _pushlocal local(name)
+ end
+
+ def setlocal(name)
+ @ip += 1
+ idx = local(name)
+ _setlocal idx
+ end
+
+ def getslot(name)
+ @ip += 1
+ _getslot literal(name)
+ end
+
+ def setslot(name)
+ @ip += 1
+ _setslot literal(name)
+ end
+
+ def send_message(msg, argc)
+ @ip += 1
+ _send literal(msg), argc
+ end
+
+ def ret
+ @ip += 1
+ _ret
+ end
+
+ private
+
+ def local name
+ @locals.index { |l| l.name == name } or begin
+ @locals.push Local.new name
+ @locals.size - 1
+ end
+ end
+
+ def literal value
+ val = value.is_a?(Numeric) ? value : "\"#{value}"
+
+ @literals.index(val) or begin
+ @literals.push val
+ @literals.index(val)
+ end
+ end
+ end
+end
Oops, something went wrong.

0 comments on commit abd28d9

Please sign in to comment.