From ede71551766f41f85866d0347edc14f566269934 Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Tue, 30 May 2023 16:11:27 +0900 Subject: [PATCH 1/5] rename build to to_asm --- lib/vaporware/compiler.rb | 4 ++-- lib/vaporware/compiler/generator.rb | 26 +++++++++++++------------- sig/vaporware.rbs | 2 +- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/vaporware/compiler.rb b/lib/vaporware/compiler.rb index bd48cfc..96f6493 100644 --- a/lib/vaporware/compiler.rb +++ b/lib/vaporware/compiler.rb @@ -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 diff --git a/lib/vaporware/compiler/generator.rb b/lib/vaporware/compiler/generator.rb index 2fd562a..a23b75a 100644 --- a/lib/vaporware/compiler/generator.rb +++ b/lib/vaporware/compiler/generator.rb @@ -70,7 +70,7 @@ def method(method, node, output) define_method_prologue(node, output) node.children.each do |child| next unless child.kind_of?(Parser::AST::Node) - build(child, output, true) + to_asm(child, output, true) end ret(output) @doned << method @@ -101,7 +101,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 @@ -146,7 +146,7 @@ def ret(output) output.puts " ret" end - def build(node, output, method_tree = false) + def to_asm(node, output, method_tree = false) return unless node.kind_of?(Parser::AST::Node) type = node.type center = case type @@ -164,7 +164,7 @@ 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 @@ -189,7 +189,7 @@ def build(node, output, method_tree = false) # 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" @@ -199,22 +199,22 @@ def build(node, output, method_tree = false) return 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 @@ -223,23 +223,23 @@ def build(node, output, method_tree = false) 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 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" diff --git a/sig/vaporware.rbs b/sig/vaporware.rbs index d96a8ee..8b390f6 100644 --- a/sig/vaporware.rbs +++ b/sig/vaporware.rbs @@ -38,7 +38,6 @@ module Vaporware # 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_method: (untyped, File, bool) -> nil def comp: (String, File) -> nil @@ -52,6 +51,7 @@ module Vaporware 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 From 30b5a7500abb769c1299e337ed6ccf52c929d7a4 Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Tue, 30 May 2023 16:14:30 +0900 Subject: [PATCH 2/5] use instance variable for debugging hoge --- lib/vaporware/compiler/generator.rb | 2 +- sig/vaporware.rbs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/vaporware/compiler/generator.rb b/lib/vaporware/compiler/generator.rb index a23b75a..39787ba 100644 --- a/lib/vaporware/compiler/generator.rb +++ b/lib/vaporware/compiler/generator.rb @@ -30,7 +30,7 @@ def register_var_and_method(node) 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 diff --git a/sig/vaporware.rbs b/sig/vaporware.rbs index 8b390f6..6fff4c2 100644 --- a/sig/vaporware.rbs +++ b/sig/vaporware.rbs @@ -38,7 +38,7 @@ module Vaporware # instance private methods def already_build_methods?: -> bool def args: (untyped, File) -> 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] From 823cbf615f332b0c2e92d3a795e2cd161752355d Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Tue, 30 May 2023 16:15:01 +0900 Subject: [PATCH 3/5] reorder by method definitions in rbs file --- sig/vaporware.rbs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sig/vaporware.rbs b/sig/vaporware.rbs index 6fff4c2..633946c 100644 --- a/sig/vaporware.rbs +++ b/sig/vaporware.rbs @@ -35,6 +35,7 @@ module Vaporware def doned: -> Set[Symbol] def seq: -> Integer def shared: -> bool + # instance private methods def already_build_methods?: -> bool def args: (untyped, File) -> nil @@ -45,12 +46,12 @@ module Vaporware 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 ret: (File) -> nil - def lvar_offset: (Symbol | nil) -> Integer def register_var_and_method: (untyped) -> nil + def ret: (File) -> nil def to_asm: (untyped, File, ?bool) -> nil def variable_or_method?: (Symbol) -> bool end From 81cd7412b7790a017dd0f0cc6eeb9d1b187bd69e Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Thu, 1 Jun 2023 13:38:03 +0900 Subject: [PATCH 4/5] replace instance variables and getter for instance variables to attr_reader --- sig/vaporware.rbs | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/sig/vaporware.rbs b/sig/vaporware.rbs index 633946c..4bc9481 100644 --- a/sig/vaporware.rbs +++ b/sig/vaporware.rbs @@ -12,30 +12,26 @@ module Vaporware 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: untyped + # 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 From a0722ac2a697542cb1e2df9007003501af201227 Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Sat, 3 Jun 2023 21:35:25 +0900 Subject: [PATCH 5/5] replace RubyVM::AbstractSyntaxTree --- lib/vaporware/compiler/generator.rb | 52 ++++++++++++++--------------- sig/vaporware.rbs | 5 +-- 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/lib/vaporware/compiler/generator.rb b/lib/vaporware/compiler/generator.rb index 39787ba..3a15d85 100644 --- a/lib/vaporware/compiler/generator.rb +++ b/lib/vaporware/compiler/generator.rb @@ -1,34 +1,35 @@ # 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"]) base_name = File.basename(output, ".*") @@ -69,7 +70,7 @@ 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) + next unless child.kind_of?(RubyVM::AbstractSyntaxTree::Node) to_asm(child, output, true) end ret(output) @@ -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]" @@ -147,13 +148,16 @@ def ret(output) end def to_asm(node, output, method_tree = false) - return unless node.kind_of?(Parser::AST::Node) + 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 @@ -167,27 +171,23 @@ def to_asm(node, output, method_tree = false) 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) to_asm(right, output, method_tree) @@ -197,7 +197,7 @@ def to_asm(node, output, method_tree = false) output.puts " push rdi" output.puts " pop rax" return - when :if + when :IF cond, tblock, fblock = node.children to_asm(cond, output) output.puts " pop rax" @@ -220,7 +220,7 @@ def to_asm(node, output, method_tree = false) end @seq += 1 return - when :while + when :WHILE cond, tblock = node.children output.puts ".Lbegin#{seq}:" to_asm(cond, output, method_tree) @@ -233,7 +233,7 @@ def to_asm(node, output, method_tree = false) output.puts ".Lend#{seq}:" @seq += 1 return - when :send + when :OPCALL left, center, right = node.children to_asm(left, output, method_tree) unless left.nil? if left.nil? diff --git a/sig/vaporware.rbs b/sig/vaporware.rbs index 4bc9481..95de99f 100644 --- a/sig/vaporware.rbs +++ b/sig/vaporware.rbs @@ -7,6 +7,7 @@ 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 @@ -27,7 +28,7 @@ module Vaporware attr_reader shared: bool # temporarily using untyped types since parser.gem's rbs information is unchecked. - attr_reader ast: untyped + attr_reader ast: RubyVM::AbstractSyntaxTree::Node # class methods def initialize: (String, precompile: String, debug: bool, shared: bool) -> untyped @@ -46,7 +47,7 @@ module Vaporware def method: (Symbol, untyped, File) -> nil def prologue: (untyped, File) -> nil def prologue_methods: (File) -> nil - def register_var_and_method: (untyped) -> nil + def register_var_and_method: (RubyVM::AbstractSyntaxTree::Node) -> nil def ret: (File) -> nil def to_asm: (untyped, File, ?bool) -> nil def variable_or_method?: (Symbol) -> bool