Skip to content

Commit

Permalink
Merge pull request #16 from katsyoshi/remove-parser-gem
Browse files Browse the repository at this point in the history
Remove parser gem
  • Loading branch information
katsyoshi authored Jun 3, 2023
2 parents f4a1c3c + a0722ac commit c3e6bb8
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 64 deletions.
4 changes: 2 additions & 2 deletions lib/vaporware/compiler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@ def compile(compiler: "gcc", compiler_options: ["-O0"])
output.puts " push rbp"
output.puts " mov rbp, rsp"
output.puts " sub rsp, #{@generator.defined_variables.size * 8}"
@generator.build(@generator.ast, output)
@generator.to_asm(@generator.ast, output)
# epilogue
@generator.epilogue(output)
else
@generator.prologue_methods(output)
output.puts ".globl main" unless @generator.shared
@generator.build(@generator.ast, output)
@generator.to_asm(@generator.ast, output)
# epilogue
@generator.epilogue(output)
end
Expand Down
80 changes: 40 additions & 40 deletions lib/vaporware/compiler/generator.rb
Original file line number Diff line number Diff line change
@@ -1,36 +1,37 @@
# frozen_string_literal: true
require "parser/current"

module Vaporware
class Compiler
class Generator
REGISTER = %w(rdi rsi rdx rcx r8 r9)
attr_accessor :main
attr_reader :ast, :precompile, :debug, :seq, :defined_variables, :doned, :shared, :defined_methods
def initialize(source, precompile:, debug:, shared:)
@precompile, @debug, @shared = precompile, debug, shared
@doned, @defined_methods, @defined_variables = Set.new, Set.new, Set.new
@seq, @main = 0, false
src = File.read(File.expand_path(source))
@ast = Parser::CurrentRuby.parse(src)
@ast = RubyVM::AbstractSyntaxTree.parse_file(source)
end

def compile_shared_option = %w(-shared -fPIC)

def register_var_and_method(node)
return unless node.kind_of?(Parser::AST::Node)
return unless node.kind_of?(RubyVM::AbstractSyntaxTree::Node)
type = node.type
if variable_or_method?(type)
name, _ = node.children
name = [:lvasgn, :arg].include?(type) ? "lvar_#{name}".to_sym : name
type == :def ? @defined_methods << name : @defined_variables << name
variables, *_ = node.children
case type
when :SCOPE
variables.each { |v| @defined_variables << v }
when :DEFN
@defined_methods << variables
end
node.children.each { |n| register_var_and_method(n) }
nil
end

def already_build_methods? = defined_methods.sort == @doned.to_a.sort
def variable_or_method?(type) = [:lvasgn, :arg, :def].include?(type)

def call_compiler(output: precompile, compiler: "gcc", compiler_options: ["-O0"], debug: false)
def call_compiler(output: precompile, compiler: "gcc", compiler_options: ["-O0"])
base_name = File.basename(output, ".*")
name = shared ? "lib#{base_name}.so" : base_name
compile_commands = [compiler, *compiler_options, "-o", name, output].compact
Expand Down Expand Up @@ -69,8 +70,8 @@ def method(method, node, output)
output.puts "#{method}:"
define_method_prologue(node, output)
node.children.each do |child|
next unless child.kind_of?(Parser::AST::Node)
build(child, output, true)
next unless child.kind_of?(RubyVM::AbstractSyntaxTree::Node)
to_asm(child, output, true)
end
ret(output)
@doned << method
Expand All @@ -79,7 +80,7 @@ def method(method, node, output)

def args(node, output)
node.children.each do |child|
name = "arg_#{child.children.first}".to_sym
name = "arg_#{child.children.first}".to_sym rescue binding.irb
lvar(name, output)
output.puts " pop rax"
output.puts " mov rax, [rax]"
Expand All @@ -101,7 +102,7 @@ def call_method(node, output, method_tree)
output.puts " push rax"
_, name, *args = node.children
args.each_with_index do |arg, i|
build(arg, output, method_tree)
to_asm(arg, output, method_tree)
output.puts " pop #{REGISTER[i]}"
end

Expand Down Expand Up @@ -146,14 +147,17 @@ def ret(output)
output.puts " ret"
end

def build(node, output, method_tree = false)
return unless node.kind_of?(Parser::AST::Node)
def to_asm(node, output, method_tree = false)
return unless node.kind_of?(RubyVM::AbstractSyntaxTree::Node)
type = node.type
center = case type
when :int
when :LIT
output.puts " push #{node.children.last}"
return
when :begin
when :LIST, :BLOCK
node.children.each { |n| to_asm(n, output, method_tree) }
return
when :SCOPE
node.children.each do |child|
if already_build_methods? && !@main
return if shared
Expand All @@ -164,82 +168,78 @@ def build(node, output, method_tree = false)
output.puts " push rax"
@main = true
end
build(child, output)
to_asm(child, output)
end
return
when :def
when :DEFN
name, _ = node.children
method(name, node, output)
return
when :args
args(node, output)
return
when :lvar
when :LVAR
return if method_tree
name = "lvar_#{node.children.last}".to_sym
name = node.children.last
lvar(name, output)
# lvar
output.puts " pop rax"
output.puts " mov rax, [rax]"
output.puts " push rax"
return
when :lvasgn
left, right = node.children
when :LASGN
name, right = node.children

# rvar
name = "lvar_#{left}".to_sym
lvar(name, output)
build(right, output, method_tree)
to_asm(right, output, method_tree)

output.puts " pop rdi"
output.puts " pop rax"
output.puts " mov [rax], rdi"
output.puts " push rdi"
output.puts " pop rax"
return
when :if
when :IF
cond, tblock, fblock = node.children
build(cond, output)
to_asm(cond, output)
output.puts " pop rax"
output.puts " push rax"
output.puts " cmp rax, 0"
if fblock
output.puts " je .Lelse#{seq}"
build(tblock, output, method_tree)
to_asm(tblock, output, method_tree)
ret(output)
output.puts " jmp .Lend#{seq}"
output.puts ".Lelse#{seq}:"
build(fblock, output, method_tree)
to_asm(fblock, output, method_tree)
ret(output)
output.puts ".Lend#{seq}:"
else
output.puts " je .Lend#{seq}"
build(tblock, output, method_tree)
to_asm(tblock, output, method_tree)
ret(output)
output.puts ".Lend#{seq}:"
end
@seq += 1
return
when :while
when :WHILE
cond, tblock = node.children
output.puts ".Lbegin#{seq}:"
build(cond, output, method_tree)
to_asm(cond, output, method_tree)
output.puts " pop rax"
output.puts " push rax"
output.puts " cmp rax, 0"
output.puts " je .Lend#{seq}"
build(tblock, output, method_tree)
to_asm(tblock, output, method_tree)
output.puts " jmp .Lbegin#{seq}"
output.puts ".Lend#{seq}:"
@seq += 1
return
when :send
when :OPCALL
left, center, right = node.children
build(left, output, method_tree) unless left.nil?
to_asm(left, output, method_tree) unless left.nil?
if left.nil?
call_method(node, output, method_tree)
else
build(right, output, method_tree)
to_asm(right, output, method_tree)
output.puts " pop rdi"
end
output.puts " pop rax"
Expand Down
42 changes: 20 additions & 22 deletions sig/vaporware.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -7,51 +7,49 @@ module Vaporware
# class methods
def self.compile: (String, ?compiler: String, ?dest: String, ?debug: bool, ?compiler_options: Array[String], ?shared: bool) -> nil
def initialize: (String, ?_precompile: String, ?debug: bool, ?shared: bool) -> untyped

@generator: Vaporware::Compiler::Generator
# instance methods
def compile: (?compiler: String, ?compiler_options: Array[String]) -> nil
class Generator
REGISTER: Array[String]
# instance variables
@precompile: String
@debug: bool
@doned: Set[Symbol]
@defined_methods: Set[Symbol]
@defined_variables: Set[Symbol]
@seq: Integer

# attr_accessor and define instance variables
@main: bool
@shared: bool
attr_accessor main (): bool

# attr_reader for instance variables
attr_reader debug: bool
attr_reader doned: Set[Symbol]
attr_reader defined_methods: Set[Symbol]
attr_reader defined_variables: Set[Symbol]
attr_reader precompile: String
attr_reader seq: Integer
attr_reader shared: bool

# temporarily using untyped types since parser.gem's rbs information is unchecked.
@ast: untyped
attr_reader ast: RubyVM::AbstractSyntaxTree::Node

# class methods
def initialize: (String, precompile: String, debug: bool, shared: bool) -> untyped

# attr reader for instance variables
def precompile: -> String
def ast: -> untyped # Parser::AST::Node
def defined_methods: -> Set[Symbol]
def defined_variables: -> Set[Symbol]
def debug: -> bool
def doned: -> Set[Symbol]
def seq: -> Integer
def shared: -> bool
# instance private methods
def already_build_methods?: -> bool
def args: (untyped, File) -> nil
def build: (untyped, File, ?bool) -> nil
def call_compiler: (?output: String, ?compiler: String, ?compiler_options: Array[String], ?debug: bool) -> nil
def call_compiler: (?output: String, ?compiler: String, ?compiler_options: Array[String]) -> nil
def call_method: (untyped, File, bool) -> nil
def comp: (String, File) -> nil
def compile_shared_option: () -> Array[String]
def define_method_prologue: (untyped, File) -> nil
def epilogue: (File) -> nil
def lvar: (untyped, File) -> nil
def lvar_offset: (Symbol | nil) -> Integer
def method: (Symbol, untyped, File) -> nil
def prologue: (untyped, File) -> nil
def prologue_methods: (File) -> nil
def register_var_and_method: (RubyVM::AbstractSyntaxTree::Node) -> nil
def ret: (File) -> nil
def lvar_offset: (Symbol | nil) -> Integer
def register_var_and_method: (untyped) -> nil
def to_asm: (untyped, File, ?bool) -> nil
def variable_or_method?: (Symbol) -> bool
end
end
Expand Down

0 comments on commit c3e6bb8

Please sign in to comment.