Skip to content
Merged
54 changes: 54 additions & 0 deletions lib/caotral/compiler/analyzer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
require_relative "context"

module Caotral
class Compiler
class Analyzer
def self.analyze(ast, context = Caotral::Compiler::Context.new)
new(ast, context).analyze
end

def initialize(ast, context)
@ast = ast
@context = context
end

def analyze
register_entry(@ast)
register_variables_and_methods(@ast)
@context
end

private
def register_variables_and_methods(node)
return unless node.kind_of?(RubyVM::AbstractSyntaxTree::Node)
type = node.type
variables, *_ = node.children
case type
when :SCOPE
variables.each { |v| @context.register_local_variable(v) }
when :DEFN
@context.discover_method(variables)
function = Caotral::Compiler::Context::Function.new(**analyze_function_scope(node))
@context.register_function(function)
end
node.children.each { |n| register_variables_and_methods(n) }
nil
end

def register_entry(scope)
locals, _args, body = scope.children
entry = Caotral::Compiler::Context::Function.new(name: nil, locals:, body:)
@context.register_entry(entry)
end

def analyze_function_scope(node)
name, function_scope = node.children
locals, args, body = function_scope.children
parameter_count = args.children.first
parameters = locals.take(parameter_count)
locals = locals.drop(parameter_count)
Comment thread
katsyoshi marked this conversation as resolved.
{ name:, parameters:, locals:, body: }
end
end
end
end
30 changes: 30 additions & 0 deletions lib/caotral/compiler/context.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
require_relative "context/function"
require_relative "context/variables"

module Caotral
class Compiler
class Context
attr_reader :discovered_methods, :label_sequence, :entry, :functions
def initialize
@label_sequence = 0
@entry_emitted = false
@emitted_methods = Set[]
@discovered_methods = Set[]
@entry = nil
@variables = Caotral::Compiler::Context::Variables.new
@functions = {}
end

def entry_emitted? = @entry_emitted
def mark_entry_emitted = @entry_emitted = true
def mark_method_emitted(name) = @emitted_methods << name
def discover_method(name) = @discovered_methods << name
def register_local_variable(name) = @variables.register_local(name)
def all_methods_emitted? = @discovered_methods == @emitted_methods
def increment_label_sequence = @label_sequence += 1
def local_variables = @variables.locals
def register_entry(function) = @entry = function
def register_function(function) = @functions[function.name] = function
end
end
end
21 changes: 21 additions & 0 deletions lib/caotral/compiler/context/function.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
require_relative "variables"

module Caotral
class Compiler
class Context
class Function
attr_reader :name, :parameters, :body
def initialize(name:, parameters: [], locals: [], body:)
@name = name
@parameters = parameters
@variables = Caotral::Compiler::Context::Variables.new
locals.each { |name| @variables.register_local(name) }
@body = body
end

def locals = @variables.locals
def variables = parameters + locals.to_a
end
end
end
end
11 changes: 11 additions & 0 deletions lib/caotral/compiler/context/variables.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module Caotral
class Compiler
class Context
class Variables
attr_reader :locals
def initialize = @locals = Set[]
def register_local(name) = @locals.add?(name)
end
end
end
end
16 changes: 16 additions & 0 deletions lib/caotral/compiler/emitter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
module Caotral
class Compiler
class Emitter
INDENT = " " * 2
def initialize(io) = @io = io

def instruction(operation, *operands)
ops = operands.empty? ? "" : " #{operands.join(", ")}"
@io.puts("#{INDENT}#{operation}#{ops}")
end
def label(name) = @io.puts("#{name}:")
def directive(row) = @io.puts(row)
def close = @io.close
end
end
end
Loading
Loading