Organize code into modules.

This commit is contained in:
Danila Fedorin 2018-08-01 22:40:41 -07:00
parent cd0e5c2919
commit 96059d6e04
18 changed files with 1493 additions and 1459 deletions

View File

@ -2,9 +2,9 @@ require "./chalk/*"
require "option_parser"
module Chalk
config = Config.parse!
config = Ui::Config.parse!
exit unless config.validate!
compiler = Compiler.new config
compiler = Compiler::Compiler.new config
compiler.run
end

View File

@ -1,21 +1,23 @@
module Chalk
class BuiltinFunction
getter param_count : Int32
module Builtin
class BuiltinFunction
getter param_count : Int32
def initialize(@param_count)
def initialize(@param_count)
end
def generate!(codegen)
end
end
def generate!(codegen)
end
end
class InlineFunction
getter param_count : Int32
class InlineFunction
getter param_count : Int32
def initialize(@param_count)
end
def initialize(@param_count)
end
def generate!(codegen, params, table, target, free)
def generate!(codegen, params, table, target, free)
end
end
end
end

View File

@ -2,141 +2,143 @@ require "./ir.cr"
require "./emitter.cr"
module Chalk
class CodeGenerator
include Emitter
module Compiler
class CodeGenerator
include Emitter
RETURN_REG = 14
STACK_REG = 13
RETURN_REG = 14
STACK_REG = 13
property instructions : Array(Instruction)
property instructions : Array(Ir::Instruction)
def initialize(table, @function : TreeFunction)
@registers = 0
@instructions = [] of Instruction
@table = Table.new table
def initialize(table, @function : Trees::TreeFunction)
@registers = 0
@instructions = [] of Ir::Instruction
@table = Table.new table
@function.params.each do |param|
@table[param] = VarEntry.new @registers
@registers += 1
end
end
def generate!(tree, function : InlineFunction, table, target, free)
function.generate!(self, tree.params, table, target, free)
end
def generate!(tree, function : TreeFunction | BuiltinFunction, table, target, free)
start_at = free
# Move I to stack
setis
# Get to correct stack position
addi STACK_REG
# Store variables
store (start_at - 1) unless start_at == 0
# Increment I and stack position
load free, start_at
opr TokenType::OpAdd, STACK_REG, free
addi free
# Calculate the parameters
tree.params.each do |param|
generate! param, table, free, free + 1
free += 1
end
# Call the function
tree.params.size.times do |time|
loadr time, time + start_at
end
call tree.name
# Reduce stack pointer
load free, start_at
opr TokenType::OpSub, STACK_REG, free
# Move I to stack
setis
# Get to correct stack position
addi STACK_REG
# Restore
restore (start_at - 1) unless start_at == 0
# Get call value into target
loadr target, RETURN_REG
end
def generate!(tree, table, target, free)
case tree
when TreeId
entry = table[tree.id]?
raise "Unknown variable" unless entry &&
entry.is_a?(VarEntry)
loadr target, entry.register
when TreeLit
load target, tree.lit
when TreeOp
generate! tree.left, table, target, free
generate! tree.right, table, free, free + 1
opr tree.op, target, free
when TreeCall
entry = table[tree.name]?
raise "Unknown function" unless entry &&
entry.is_a?(FunctionEntry)
function = entry.function
raise "Invalid call" if tree.params.size != function.param_count
generate! tree, function, table, target, free
when TreeBlock
table = Table.new(table)
tree.children.each do |child|
free += generate! child, table, free, free + 1
@function.params.each do |param|
@table[param] = VarEntry.new @registers
@registers += 1
end
when TreeVar
entry = table[tree.name]?
if entry == nil
entry = VarEntry.new free
end
def generate!(tree, function : Builtin::InlineFunction, table, target, free)
function.generate!(self, tree.params, table, target, free)
end
def generate!(tree, function : Trees::TreeFunction | Builtin::BuiltinFunction, table, target, free)
start_at = free
# Move I to stack
setis
# Get to correct stack position
addi STACK_REG
# Store variables
store (start_at - 1) unless start_at == 0
# Increment I and stack position
load free, start_at
opr TokenType::OpAdd, STACK_REG, free
addi free
# Calculate the parameters
tree.params.each do |param|
generate! param, table, free, free + 1
free += 1
table[tree.name] = entry
end
raise "Unknown variable" unless entry.is_a?(VarEntry)
generate! tree.expr, table, entry.register, free
return 1
when TreeAssign
entry = table[tree.name]?
raise "Unknown variable" unless entry &&
entry.is_a?(VarEntry)
generate! tree.expr, table, entry.register, free
when TreeIf
generate! tree.condition, table, free, free + 1
sne free, 0
jump_inst = jr 0
# Call the function
tree.params.size.times do |time|
loadr time, time + start_at
end
call tree.name
old_size = @instructions.size
generate! tree.block, table, free, free + 1
jump_after = jr 0
jump_inst.offset = @instructions.size - old_size + 1
old_size = @instructions.size
generate! tree.otherwise, table, free, free + 1 if tree.otherwise
jump_after.offset = @instructions.size - old_size + 1
when TreeWhile
before_cond = @instructions.size
generate! tree.condition, table, free, free + 1
sne free, 0
cond_jump = jr 0
old_size = @instructions.size
generate! tree.block, table, free, free + 1
after_jump = jr 0
cond_jump.offset = @instructions.size - old_size + 1
after_jump.offset = before_cond - instructions.size + 1
when TreeReturn
generate! tree.rvalue, table, RETURN_REG, free
ret
# Reduce stack pointer
load free, start_at
opr TokenType::OpSub, STACK_REG, free
# Move I to stack
setis
# Get to correct stack position
addi STACK_REG
# Restore
restore (start_at - 1) unless start_at == 0
# Get call value into target
loadr target, RETURN_REG
end
return 0
end
def generate!
generate!(@function.block, @table, -1, @registers)
return @instructions
def generate!(tree, table, target, free)
case tree
when Trees::TreeId
entry = table[tree.id]?
raise "Unknown variable" unless entry &&
entry.is_a?(VarEntry)
loadr target, entry.register
when Trees::TreeLit
load target, tree.lit
when Trees::TreeOp
generate! tree.left, table, target, free
generate! tree.right, table, free, free + 1
opr tree.op, target, free
when Trees::TreeCall
entry = table[tree.name]?
raise "Unknown function" unless entry &&
entry.is_a?(FunctionEntry)
function = entry.function
raise "Invalid call" if tree.params.size != function.param_count
generate! tree, function, table, target, free
when Trees::TreeBlock
table = Table.new(table)
tree.children.each do |child|
free += generate! child, table, free, free + 1
end
when Trees::TreeVar
entry = table[tree.name]?
if entry == nil
entry = VarEntry.new free
free += 1
table[tree.name] = entry
end
raise "Unknown variable" unless entry.is_a?(VarEntry)
generate! tree.expr, table, entry.register, free
return 1
when Trees::TreeAssign
entry = table[tree.name]?
raise "Unknown variable" unless entry &&
entry.is_a?(VarEntry)
generate! tree.expr, table, entry.register, free
when Trees::TreeIf
generate! tree.condition, table, free, free + 1
sne free, 0
jump_inst = jr 0
old_size = @instructions.size
generate! tree.block, table, free, free + 1
jump_after = jr 0
jump_inst.offset = @instructions.size - old_size + 1
old_size = @instructions.size
generate! tree.otherwise, table, free, free + 1 if tree.otherwise
jump_after.offset = @instructions.size - old_size + 1
when Trees::TreeWhile
before_cond = @instructions.size
generate! tree.condition, table, free, free + 1
sne free, 0
cond_jump = jr 0
old_size = @instructions.size
generate! tree.block, table, free, free + 1
after_jump = jr 0
cond_jump.offset = @instructions.size - old_size + 1
after_jump.offset = before_cond - instructions.size + 1
when Trees::TreeReturn
generate! tree.rvalue, table, RETURN_REG, free
ret
end
return 0
end
def generate!
generate!(@function.block, @table, -1, @registers)
return @instructions
end
end
end
end

View File

@ -3,164 +3,166 @@ require "./constant_folder.cr"
require "./table.cr"
module Chalk
class Compiler
def initialize(@config : Config)
@logger = Logger.new STDOUT
@logger.debug("Initialized compiler")
@logger.level = Logger::DEBUG
end
private def create_trees(file)
string = File.read(file)
@logger.debug("Tokenizing")
lexer = Lexer.new
tokens = lexer.lex string
if tokens.size == 0 && string != ""
raise "Unable to tokenize file."
module Compiler
class Compiler
def initialize(@config : Ui::Config)
@logger = Logger.new STDOUT
@logger.debug("Initialized compiler")
@logger.level = Logger::DEBUG
end
@logger.debug("Finished tokenizing")
@logger.debug("Beginning parsing")
parser = Parser.new
if trees = parser.parse?(tokens)
@logger.debug("Finished parsing")
@logger.debug("Beginning constant folding")
folder = ConstantFolder.new
trees.map! do |tree|
@logger.debug("Constant folding #{tree.name}")
tree.apply(folder).as(TreeFunction)
private def create_trees(file)
string = File.read(file)
@logger.debug("Tokenizing")
lexer = Lexer.new
tokens = lexer.lex string
if tokens.size == 0 && string != ""
raise "Unable to tokenize file."
end
@logger.debug("Done constant folding")
return trees
end
raise "Unable to parse file."
end
private def create_table(trees)
table = Table.new
@logger.debug("Creating symbol table")
trees.each do |tree|
@logger.debug("Storing #{tree.name} in symbol table")
table[tree.name] = FunctionEntry.new tree
end
@logger.debug("Done creating symbol table")
table["draw"] = FunctionEntry.new InlineDrawFunction.new
table["get_key"] = FunctionEntry.new InlineAwaitKeyFunction.new
table["get_font"] = FunctionEntry.new InlineGetFontFunction.new
table["set_delay"] = FunctionEntry.new InlineSetDelayFunction.new
table["get_delay"] = FunctionEntry.new InlineGetDelayFunction.new
return table
end
private def create_code(tree : TreeFunction, table, instruction = ReturnInstruction.new)
optimizer = Optimizer.new
generator = CodeGenerator.new table, tree
@logger.debug("Generating code for #{tree.name}")
code = generator.generate!
code << instruction
return optimizer.optimize(code)
end
private def create_code(tree : BuiltinFunction, table, instruction = nil)
instructions = [] of Instruction
tree.generate!(instructions)
return instructions
end
private def create_code(trees : Array(TreeFunction), table)
code = {} of String => Array(Instruction)
trees.each do |tree|
code[tree.name] = create_code(tree, table)
end
return code
end
private def run_tree
trees = create_trees(@config.file)
trees.each do |it|
STDOUT << it
end
end
private def run_intermediate
trees = create_trees(@config.file)
table = create_table(trees)
code = create_code(trees, table)
code.each do |name, insts|
puts "Code for #{name}:"
insts.each { |it| puts it }
puts "-----"
end
end
private def generate_binary(table, instructions, dest)
context = InstructionContext.new table, instructions.size
binary = instructions.map_with_index { |it, i| it.to_bin(context, i).to_u16 }
binary.each do |inst|
first = (inst >> 8).to_u8
dest.write_byte(first)
second = (inst & 0xff).to_u8
dest.write_byte(second)
end
end
private def collect_calls(table)
open = Set(String).new
done = Set(String).new
open << "main"
while !open.empty?
first = open.first
open.delete first
entry = table[first]?
raise "Unknown function" unless entry && entry.is_a?(FunctionEntry)
function = entry.function
next if function.is_a?(InlineFunction)
done << first
next unless function.is_a?(TreeFunction)
visitor = CallVisitor.new
function.accept(visitor)
open.concat(visitor.calls - done)
end
return done
end
private def run_binary
all_instructions = [] of Instruction
trees = create_trees(@config.file)
table = create_table(trees)
names = collect_calls(table)
names.delete "main"
main_entry = table["main"]?.as(FunctionEntry)
all_instructions.concat create_code(main_entry.function.as(TreeFunction),
table, JumpRelativeInstruction.new 0)
main_entry.addr = 0
names.each do |name|
entry = table[name]?.as(FunctionEntry)
entry.addr = all_instructions.size
function = entry.function
raise "Trying to compile inlined function" if function.is_a?(InlineFunction)
all_instructions.concat create_code(function, table)
all_instructions << ReturnInstruction.new
@logger.debug("Finished tokenizing")
@logger.debug("Beginning parsing")
parser = ParserCombinators::Parser.new
if trees = parser.parse?(tokens)
@logger.debug("Finished parsing")
@logger.debug("Beginning constant folding")
folder = Trees::ConstantFolder.new
trees.map! do |tree|
@logger.debug("Constant folding #{tree.name}")
tree.apply(folder).as(Trees::TreeFunction)
end
@logger.debug("Done constant folding")
return trees
end
raise "Unable to parse file."
end
file = File.open("out.ch8", "w")
generate_binary(table, all_instructions, file)
file.close
end
private def create_table(trees)
table = Table.new
@logger.debug("Creating symbol table")
trees.each do |tree|
@logger.debug("Storing #{tree.name} in symbol table")
table[tree.name] = FunctionEntry.new tree
end
@logger.debug("Done creating symbol table")
def run
case @config.mode
when OutputMode::Tree
run_tree
when OutputMode::Intermediate
run_intermediate
when OutputMode::Binary
run_binary
table["draw"] = FunctionEntry.new Builtin::InlineDrawFunction.new
table["get_key"] = FunctionEntry.new Builtin::InlineAwaitKeyFunction.new
table["get_font"] = FunctionEntry.new Builtin::InlineGetFontFunction.new
table["set_delay"] = FunctionEntry.new Builtin::InlineSetDelayFunction.new
table["get_delay"] = FunctionEntry.new Builtin::InlineGetDelayFunction.new
return table
end
private def create_code(tree : Trees::TreeFunction, table, instruction = Ir::ReturnInstruction.new)
optimizer = Optimizer.new
generator = CodeGenerator.new table, tree
@logger.debug("Generating code for #{tree.name}")
code = generator.generate!
code << instruction
return optimizer.optimize(code)
end
private def create_code(tree : Builtin::BuiltinFunction, table, instruction = nil)
instructions = [] of Ir::Instruction
tree.generate!(instructions)
return instructions
end
private def create_code(trees : Array(Trees::TreeFunction), table)
code = {} of String => Array(Ir::Instruction)
trees.each do |tree|
code[tree.name] = create_code(tree, table)
end
return code
end
private def run_tree
trees = create_trees(@config.file)
trees.each do |it|
STDOUT << it
end
end
private def run_intermediate
trees = create_trees(@config.file)
table = create_table(trees)
code = create_code(trees, table)
code.each do |name, insts|
puts "Code for #{name}:"
insts.each { |it| puts it }
puts "-----"
end
end
private def generate_binary(table, instructions, dest)
context = Ir::InstructionContext.new table, instructions.size
binary = instructions.map_with_index { |it, i| it.to_bin(context, i).to_u16 }
binary.each do |inst|
first = (inst >> 8).to_u8
dest.write_byte(first)
second = (inst & 0xff).to_u8
dest.write_byte(second)
end
end
private def collect_calls(table)
open = Set(String).new
done = Set(String).new
open << "main"
while !open.empty?
first = open.first
open.delete first
entry = table[first]?
raise "Unknown function" unless entry && entry.is_a?(FunctionEntry)
function = entry.function
next if function.is_a?(Builtin::InlineFunction)
done << first
next unless function.is_a?(Trees::TreeFunction)
visitor = Trees::CallVisitor.new
function.accept(visitor)
open.concat(visitor.calls - done)
end
return done
end
private def run_binary
all_instructions = [] of Ir::Instruction
trees = create_trees(@config.file)
table = create_table(trees)
names = collect_calls(table)
names.delete "main"
main_entry = table["main"]?.as(FunctionEntry)
all_instructions.concat create_code(main_entry.function.as(Trees::TreeFunction),
table, Ir::JumpRelativeInstruction.new 0)
main_entry.addr = 0
names.each do |name|
entry = table[name]?.as(FunctionEntry)
entry.addr = all_instructions.size
function = entry.function
raise "Trying to compile inlined function" if function.is_a?(Builtin::InlineFunction)
all_instructions.concat create_code(function, table)
all_instructions << Ir::ReturnInstruction.new
end
file = File.open("out.ch8", "w")
generate_binary(table, all_instructions, file)
file.close
end
def run
case @config.mode
when Ui::OutputMode::Tree
run_tree
when Ui::OutputMode::Intermediate
run_intermediate
when Ui::OutputMode::Binary
run_binary
end
end
end
end

View File

@ -1,51 +1,53 @@
module Chalk
enum OutputMode
Tree,
Intermediate,
Binary
end
class Config
property file : String
property mode : OutputMode
def initialize(@file = "",
@mode = OutputMode::Tree)
module Ui
enum OutputMode
Tree,
Intermediate,
Binary
end
def self.parse!
config = self.new
OptionParser.parse! do |parser|
parser.banner = "Usage: chalk [arguments]"
parser.on("-m", "--mode=MODE", "Set the mode of the compiler.") do |mode|
case mode.downcase
when "tree", "t"
config.mode = OutputMode::Tree
when "intermediate", "i"
config.mode = OutputMode::Intermediate
when "binary", "b"
config.mode = OutputMode::Binary
else
puts "Invalid mode type. Using default."
class Config
property file : String
property mode : OutputMode
def initialize(@file = "",
@mode = OutputMode::Tree)
end
def self.parse!
config = self.new
OptionParser.parse! do |parser|
parser.banner = "Usage: chalk [arguments]"
parser.on("-m", "--mode=MODE", "Set the mode of the compiler.") do |mode|
case mode.downcase
when "tree", "t"
config.mode = OutputMode::Tree
when "intermediate", "i"
config.mode = OutputMode::Intermediate
when "binary", "b"
config.mode = OutputMode::Binary
else
puts "Invalid mode type. Using default."
end
end
parser.on("-f", "--file=FILE", "Set the input file to compile.") do |file|
config.file = file
end
parser.on("-h", "--help", "Show this message.") { puts parser }
end
parser.on("-f", "--file=FILE", "Set the input file to compile.") do |file|
config.file = file
end
parser.on("-h", "--help", "Show this message.") { puts parser }
return config
end
return config
end
def validate!
if file == ""
puts "No source file specified."
return false
elsif !File.exists? file
puts "Unable to open source file."
return false
def validate!
if file == ""
puts "No source file specified."
return false
elsif !File.exists? file
puts "Unable to open source file."
return false
end
return true
end
return true
end
end
end

View File

@ -1,33 +1,35 @@
require "./tree.cr"
module Chalk
class ConstantFolder < Transformer
private def perform_op(op, left, right)
case op
when TokenType::OpAdd
left + right
when TokenType::OpSub
left - right
when TokenType::OpMul
left*right
when TokenType::OpDiv
left/right
when TokenType::OpAnd
left & right
when TokenType::OpOr
left | right
else TokenType::OpXor
left ^ right
module Trees
class ConstantFolder < Transformer
private def perform_op(op, left, right)
case op
when Compiler::TokenType::OpAdd
left + right
when Compiler::TokenType::OpSub
left - right
when Compiler::TokenType::OpMul
left*right
when Compiler::TokenType::OpDiv
left/right
when Compiler::TokenType::OpAnd
left & right
when Compiler::TokenType::OpOr
left | right
else Compiler::TokenType::OpXor
left ^ right
end
end
end
def transform(tree : TreeOp)
if tree.left.is_a?(TreeLit) && tree.right.is_a?(TreeLit)
return TreeLit.new perform_op(tree.op,
tree.left.as(TreeLit).lit,
tree.right.as(TreeLit).lit)
def transform(tree : Trees::TreeOp)
if tree.left.is_a?(Trees::TreeLit) && tree.right.is_a?(Trees::TreeLit)
return Trees::TreeLit.new perform_op(tree.op,
tree.left.as(Trees::TreeLit).lit,
tree.right.as(Trees::TreeLit).lit)
end
return tree
end
return tree
end
end
end

View File

@ -1,75 +1,77 @@
module Chalk
module Emitter
def load(into, value)
inst = LoadInstruction.new into, value.to_i32
@instructions << inst
return inst
end
module Compiler
module Emitter
def load(into, value)
inst = Ir::LoadInstruction.new into, value.to_i32
@instructions << inst
return inst
end
def loadr(into, from)
inst = LoadRegInstruction.new into, from
@instructions << inst
return inst
end
def loadr(into, from)
inst = Ir::LoadRegInstruction.new into, from
@instructions << inst
return inst
end
def op(op, into, from)
inst = OpInstruction.new op, into, from
@instructions << inst
return inst
end
def op(op, into, from)
inst = Ir::OpInstruction.new op, into, from
@instructions << inst
return inst
end
def opr(op, into, from)
inst = OpRegInstruction.new op, into, from
@instructions << inst
return inst
end
def opr(op, into, from)
inst = Ir::OpRegInstruction.new op, into, from
@instructions << inst
return inst
end
def sne(l, r)
inst = SkipNeInstruction.new l, r
@instructions << inst
return inst
end
def sne(l, r)
inst = Ir::SkipNeInstruction.new l, r
@instructions << inst
return inst
end
def jr(o)
inst = JumpRelativeInstruction.new o
@instructions << inst
return inst
end
def jr(o)
inst = Ir::JumpRelativeInstruction.new o
@instructions << inst
return inst
end
def store(up_to)
inst = StoreInstruction.new up_to
@instructions << inst
return inst
end
def store(up_to)
inst = Ir::StoreInstruction.new up_to
@instructions << inst
return inst
end
def restore(up_to)
inst = RestoreInstruction.new up_to
@instructions << inst
return inst
end
def restore(up_to)
inst = Ir::RestoreInstruction.new up_to
@instructions << inst
return inst
end
def ret
inst = ReturnInstruction.new
@instructions << inst
return inst
end
def ret
inst = Ir::ReturnInstruction.new
@instructions << inst
return inst
end
def call(func)
inst = CallInstruction.new func
@instructions << inst
return inst
end
def call(func)
inst = Ir::CallInstruction.new func
@instructions << inst
return inst
end
def setis
inst = SetIStackInstruction.new
@instructions << inst
return inst
end
def setis
inst = Ir::SetIStackInstruction.new
@instructions << inst
return inst
end
def addi(reg)
inst = AddIRegInstruction.new reg
@instructions << inst
return inst
def addi(reg)
inst = Ir::AddIRegInstruction.new reg
@instructions << inst
return inst
end
end
end
end

View File

@ -1,13 +1,15 @@
module Chalk
class CallVisitor < Visitor
property calls : Set(String)
module Trees
class CallVisitor < Visitor
property calls : Set(String)
def initialize
@calls = Set(String).new
end
def initialize
@calls = Set(String).new
end
def visit(t : TreeCall)
@calls << t.name
def visit(t : TreeCall)
@calls << t.name
end
end
end
end

View File

@ -1,58 +1,60 @@
module Chalk
class InlineDrawFunction < InlineFunction
def initialize
@param_count = 3
end
def generate!(emitter, params, table, target, free)
if !params[2].is_a?(TreeLit)
raise "Third parameter must be a constant."
module Builtin
class InlineDrawFunction < InlineFunction
def initialize
@param_count = 3
end
emitter.generate! params[0], table, free, free + 1
emitter.generate! params[1], table, free + 1, free + 2
emitter.instructions << DrawInstruction.new free, free + 1, params[2].as(TreeLit).lit.to_i32
end
end
class InlineAwaitKeyFunction < InlineFunction
def initialize
@param_count = 0
def generate!(emitter, params, table, target, free)
if !params[2].is_a?(Trees::TreeLit)
raise "Third parameter must be a constant."
end
emitter.generate! params[0], table, free, free + 1
emitter.generate! params[1], table, free + 1, free + 2
emitter.instructions << Ir::DrawInstruction.new free, free + 1, params[2].as(Trees::TreeLit).lit.to_i32
end
end
def generate!(emitter, params, table, target, free)
emitter.instructions << AwaitKeyInstruction.new target
end
end
class InlineAwaitKeyFunction < InlineFunction
def initialize
@param_count = 0
end
class InlineGetFontFunction < InlineFunction
def initialize
@param_count = 1
def generate!(emitter, params, table, target, free)
emitter.instructions << Ir::AwaitKeyInstruction.new target
end
end
def generate!(emitter, params, table, target, free)
emitter.generate! params[0], table, free, free + 1
emitter.instructions << GetFontInstruction.new free
end
end
class InlineGetFontFunction < InlineFunction
def initialize
@param_count = 1
end
class InlineSetDelayFunction < InlineFunction
def initialize
@param_count = 1
def generate!(emitter, params, table, target, free)
emitter.generate! params[0], table, free, free + 1
emitter.instructions << Ir::GetFontInstruction.new free
end
end
def generate!(emitter, params, table, target, free)
emitter.generate! params[0], table, free, free + 1
emitter.instructions << SetDelayTimerInstruction.new free
end
end
class InlineSetDelayFunction < InlineFunction
def initialize
@param_count = 1
end
class InlineGetDelayFunction < InlineFunction
def initialize
@param_count = 0
def generate!(emitter, params, table, target, free)
emitter.generate! params[0], table, free, free + 1
emitter.instructions << Ir::SetDelayTimerInstruction.new free
end
end
def generate!(emitter, params, table, target, free)
emitter.instructions << GetDelayTimerInstruction.new target
class InlineGetDelayFunction < InlineFunction
def initialize
@param_count = 0
end
def generate!(emitter, params, table, target, free)
emitter.instructions << Ir::GetDelayTimerInstruction.new target
end
end
end
end

View File

@ -1,373 +1,375 @@
require "./lexer.cr"
module Chalk
class Instruction
def to_bin(i, index)
return 0
end
end
class InstructionContext
property table : Table
property stack : Int32
def initialize(@table, @stack)
end
end
class LoadInstruction < Instruction
property register : Int32
property value : Int32
def initialize(@register, @value)
end
def to_s(io)
io << "load R"
@register.to_s(16, io)
io << " " << @value
end
def to_bin(i, index)
0x6000 | (@register << 8) | @value
end
end
class LoadRegInstruction < Instruction
property into : Int32
property from : Int32
def initialize(@into, @from)
end
def to_s(io)
io << "loadr R"
@into.to_s(16, io)
io << " R"
@from.to_s(16, io)
end
def to_bin(i, index)
0x8000 | (@into << 8) | (@from << 4)
end
end
class OpInstruction < Instruction
property op : TokenType
property into : Int32
property value : Int32
def initialize(@op, @into, @value)
end
def to_s(io)
io << "op " << op << " R"
@into.to_s(16, io)
io << " " << @value
end
def to_bin(i, index)
case op
when TokenType::OpAdd
return 0x7000 | (@into << 8) | @value
else
raise "Invalid instruction"
module Ir
class Instruction
def to_bin(i, index)
return 0
end
end
end
class OpRegInstruction < Instruction
property op : TokenType
property into : Int32
property from : Int32
class InstructionContext
property table : Compiler::Table
property stack : Int32
def initialize(@op, @into, @from)
end
def to_s(io)
io << "opr " << op << " R"
@into.to_s(16, io)
io << " R"
@from.to_s(16, io)
end
def to_bin(i, index)
code = 0
case op
when TokenType::OpAdd
code = 4
when TokenType::OpSub
code = 5
when TokenType::OpOr
code = 1
when TokenType::OpAnd
code = 2
when TokenType::OpXor
code = 3
else
raise "Invalid instruction"
def initialize(@table, @stack)
end
return 0x8000 | (@into << 8) | (@from << 4) | code
end
end
class StoreInstruction < Instruction
property up_to : Int32
def initialize(@up_to)
end
def to_s(io)
io << "store R"
@up_to.to_s(16, io)
class LoadInstruction < Instruction
property register : Int32
property value : Int32
def initialize(@register, @value)
end
def to_s(io)
io << "load R"
@register.to_s(16, io)
io << " " << @value
end
def to_bin(i, index)
0x6000 | (@register << 8) | @value
end
end
def to_bin(i, index)
return 0xf055 | (@up_to << 8)
end
end
class LoadRegInstruction < Instruction
property into : Int32
property from : Int32
class RestoreInstruction < Instruction
property up_to : Int32
def initialize(@into, @from)
end
def initialize(@up_to)
def to_s(io)
io << "loadr R"
@into.to_s(16, io)
io << " R"
@from.to_s(16, io)
end
def to_bin(i, index)
0x8000 | (@into << 8) | (@from << 4)
end
end
def to_s(io)
io << "restore R"
@up_to.to_s(16, io)
class OpInstruction < Instruction
property op : Compiler::TokenType
property into : Int32
property value : Int32
def initialize(@op, @into, @value)
end
def to_s(io)
io << "op " << op << " R"
@into.to_s(16, io)
io << " " << @value
end
def to_bin(i, index)
case op
when Compiler::TokenType::OpAdd
return 0x7000 | (@into << 8) | @value
else
raise "Invalid instruction"
end
end
end
def to_bin(i, index)
return 0xf065 | (@up_to << 8)
end
end
class OpRegInstruction < Instruction
property op : Compiler::TokenType
property into : Int32
property from : Int32
class ReturnInstruction < Instruction
def initialize
def initialize(@op, @into, @from)
end
def to_s(io)
io << "opr " << op << " R"
@into.to_s(16, io)
io << " R"
@from.to_s(16, io)
end
def to_bin(i, index)
code = 0
case op
when Compiler::TokenType::OpAdd
code = 4
when Compiler::TokenType::OpSub
code = 5
when Compiler::TokenType::OpOr
code = 1
when Compiler::TokenType::OpAnd
code = 2
when Compiler::TokenType::OpXor
code = 3
else
raise "Invalid instruction"
end
return 0x8000 | (@into << 8) | (@from << 4) | code
end
end
def to_s(io)
io << "return"
class StoreInstruction < Instruction
property up_to : Int32
def initialize(@up_to)
end
def to_s(io)
io << "store R"
@up_to.to_s(16, io)
end
def to_bin(i, index)
return 0xf055 | (@up_to << 8)
end
end
def to_bin(i, index)
return 0x00ee
end
end
class RestoreInstruction < Instruction
property up_to : Int32
class JumpRelativeInstruction < Instruction
property offset : Int32
def initialize(@up_to)
end
def initialize(@offset)
def to_s(io)
io << "restore R"
@up_to.to_s(16, io)
end
def to_bin(i, index)
return 0xf065 | (@up_to << 8)
end
end
def to_s(io)
io << "jr " << @offset
class ReturnInstruction < Instruction
def initialize
end
def to_s(io)
io << "return"
end
def to_bin(i, index)
return 0x00ee
end
end
def to_bin(i, index)
return 0x1000 | ((@offset + index) * 2 + 0x200)
end
end
class JumpRelativeInstruction < Instruction
property offset : Int32
class SkipEqInstruction < Instruction
property left : Int32
property right : Int32
def initialize(@offset)
end
def initialize(@left, @right)
def to_s(io)
io << "jr " << @offset
end
def to_bin(i, index)
return 0x1000 | ((@offset + index) * 2 + 0x200)
end
end
def to_s(io)
io << "seq R"
@left.to_s(16, io)
io << " " << right
class SkipEqInstruction < Instruction
property left : Int32
property right : Int32
def initialize(@left, @right)
end
def to_s(io)
io << "seq R"
@left.to_s(16, io)
io << " " << right
end
def to_bin(i, index)
return 0x3000 | (@left << 8) | @right
end
end
def to_bin(i, index)
return 0x3000 | (@left << 8) | @right
end
end
class SkipNeInstruction < Instruction
property left : Int32
property right : Int32
class SkipNeInstruction < Instruction
property left : Int32
property right : Int32
def initialize(@left, @right)
end
def initialize(@left, @right)
def to_s(io)
io << "sne R"
@left.to_s(16, io)
io << " " << right
end
def to_bin(i, index)
return 0x4000 | (@left << 8) | @right
end
end
def to_s(io)
io << "sne R"
@left.to_s(16, io)
io << " " << right
class SkipRegEqInstruction < Instruction
property left : Int32
property right : Int32
def initialize(@left, @right)
end
def to_s(io)
io << "seqr R"
@left.to_s(16, io)
io << " R"
@right.to_s(16, io)
end
def to_bin(i, index)
return 0x5000 | (@left << 8) | (@right << 4)
end
end
def to_bin(i, index)
return 0x4000 | (@left << 8) | @right
end
end
class SkipRegNeInstruction < Instruction
property left : Int32
property right : Int32
class SkipRegEqInstruction < Instruction
property left : Int32
property right : Int32
def initialize(@left, @right)
end
def initialize(@left, @right)
def to_s(io)
io << "sner R"
@left.to_s(16, io)
io << " R"
@right.to_s(16, io)
end
def to_bin(i, index)
return 0x9000 | (@left << 8) | (@right << 4)
end
end
def to_s(io)
io << "seqr R"