2018-07-26 19:47:56 -07:00
|
|
|
require "logger"
|
|
|
|
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)
|
2018-07-27 22:50:31 -07:00
|
|
|
@logger.debug("Tokenizing")
|
|
|
|
lexer = Lexer.new
|
|
|
|
tokens = lexer.lex string
|
2018-07-26 19:47:56 -07:00
|
|
|
if tokens.size == 0 && string != ""
|
|
|
|
raise "Unable to tokenize file."
|
|
|
|
end
|
|
|
|
@logger.debug("Finished tokenizing")
|
2018-07-27 22:50:31 -07:00
|
|
|
@logger.debug("Beginning parsing")
|
|
|
|
parser = Parser.new
|
|
|
|
if trees = parser.parse?(tokens)
|
2018-07-26 19:47:56 -07:00
|
|
|
@logger.debug("Finished parsing")
|
2018-07-27 22:50:31 -07:00
|
|
|
@logger.debug("Beginning constant folding")
|
|
|
|
folder = ConstantFolder.new
|
|
|
|
trees.map! do |tree|
|
|
|
|
@logger.debug("Constant folding #{tree.name}")
|
|
|
|
tree.apply(folder).as(TreeFunction)
|
|
|
|
end
|
|
|
|
@logger.debug("Done constant folding")
|
2018-07-26 19:47:56 -07:00
|
|
|
return trees
|
|
|
|
end
|
|
|
|
raise "Unable to parse file."
|
|
|
|
end
|
|
|
|
|
2018-07-27 22:50:31 -07:00
|
|
|
private def create_table(trees)
|
2018-07-26 19:47:56 -07:00
|
|
|
table = Table.new
|
2018-07-27 22:50:31 -07:00
|
|
|
@logger.debug("Creating symbol table")
|
2018-07-26 19:47:56 -07:00
|
|
|
trees.each do |tree|
|
|
|
|
@logger.debug("Storing #{tree.name} in symbol table")
|
|
|
|
table[tree.name] = FunctionEntry.new tree
|
|
|
|
end
|
2018-07-27 22:50:31 -07:00
|
|
|
@logger.debug("Done creating symbol table")
|
2018-07-26 19:47:56 -07:00
|
|
|
return table
|
|
|
|
end
|
|
|
|
|
2018-07-27 23:24:37 -07:00
|
|
|
private def create_code(tree : TreeFunction, table)
|
2018-07-27 00:06:33 -07:00
|
|
|
generator = CodeGenerator.new table, tree
|
|
|
|
@logger.debug("Generating code for #{tree.name}")
|
2018-07-27 23:24:37 -07:00
|
|
|
return generator.generate!
|
|
|
|
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)
|
2018-07-26 19:47:56 -07:00
|
|
|
end
|
2018-07-27 00:06:33 -07:00
|
|
|
return code
|
2018-07-26 19:47:56 -07:00
|
|
|
end
|
|
|
|
|
|
|
|
private def run_tree
|
|
|
|
trees = create_trees(@config.file)
|
|
|
|
trees.each do |it|
|
|
|
|
STDOUT << it
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-07-27 19:56:26 -07:00
|
|
|
private def run_intermediate
|
2018-07-27 22:50:31 -07:00
|
|
|
trees = create_trees(@config.file)
|
|
|
|
table = create_table(trees)
|
|
|
|
code = create_code(trees, table)
|
2018-07-27 00:06:33 -07:00
|
|
|
code.each do |name, insts|
|
|
|
|
puts "Code for #{name}:"
|
|
|
|
insts.each { |it| puts it }
|
|
|
|
puts "-----"
|
|
|
|
end
|
2018-07-26 19:47:56 -07:00
|
|
|
end
|
|
|
|
|
2018-07-27 22:50:31 -07:00
|
|
|
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
|
2018-07-27 19:56:26 -07:00
|
|
|
end
|
|
|
|
|
2018-07-27 23:24:37 -07:00
|
|
|
private def collect_calls(table)
|
|
|
|
open = Set(String).new
|
|
|
|
done = Set(String).new
|
|
|
|
|
|
|
|
open << "main"
|
|
|
|
while !open.empty?
|
|
|
|
first = open.first
|
|
|
|
open.delete first
|
|
|
|
done << first
|
|
|
|
|
|
|
|
entry = table[first]?
|
|
|
|
raise "Unknown function" unless entry && entry.is_a?(FunctionEntry)
|
|
|
|
next unless entry.function.is_a?(TreeFunction)
|
|
|
|
|
|
|
|
visitor = CallVisitor.new
|
|
|
|
entry.function.accept(visitor)
|
|
|
|
open.concat(visitor.calls - done)
|
|
|
|
end
|
|
|
|
return done
|
|
|
|
end
|
|
|
|
|
2018-07-26 19:47:56 -07:00
|
|
|
private def run_binary
|
2018-07-27 19:56:26 -07:00
|
|
|
all_instructions = [] of Instruction
|
2018-07-27 22:50:31 -07:00
|
|
|
trees = create_trees(@config.file)
|
|
|
|
table = create_table(trees)
|
2018-07-27 23:24:37 -07:00
|
|
|
names = collect_calls(table)
|
|
|
|
names.delete "main"
|
|
|
|
|
|
|
|
main_entry = table["main"]?.as(FunctionEntry)
|
|
|
|
all_instructions.concat create_code(main_entry.function, table)
|
|
|
|
main_entry.addr = 0
|
2018-07-27 19:56:26 -07:00
|
|
|
all_instructions << JumpRelativeInstruction.new 0
|
2018-07-27 23:24:37 -07:00
|
|
|
|
|
|
|
names.each do |name|
|
|
|
|
entry = table[name]?.as(FunctionEntry)
|
|
|
|
entry.addr = all_instructions.size
|
|
|
|
all_instructions.concat create_code(entry.function, table)
|
2018-07-27 19:56:26 -07:00
|
|
|
all_instructions << ReturnInstruction.new
|
|
|
|
end
|
2018-07-27 23:24:37 -07:00
|
|
|
|
2018-07-27 19:56:26 -07:00
|
|
|
file = File.open("out.ch8", "w")
|
2018-07-27 22:50:31 -07:00
|
|
|
generate_binary(table, all_instructions, file)
|
2018-07-27 19:56:26 -07:00
|
|
|
file.close
|
2018-07-26 19:47:56 -07:00
|
|
|
end
|
|
|
|
|
|
|
|
def run
|
|
|
|
case @config.mode
|
|
|
|
when OutputMode::Tree
|
|
|
|
run_tree
|
|
|
|
when OutputMode::Intermediate
|
|
|
|
run_intermediate
|
|
|
|
when OutputMode::Binary
|
|
|
|
run_binary
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|