Add a basic CLI, and begin work on codegen.
This commit is contained in:
parent
fbb0da9e44
commit
3853d212b9
13
src/chalk.cr
13
src/chalk.cr
|
@ -1,12 +1,11 @@
|
|||
require "./chalk/*"
|
||||
require "option_parser"
|
||||
|
||||
module Chalk
|
||||
lexer = Lexer.new
|
||||
parser = Parser.new
|
||||
config = Config.parse!
|
||||
exit unless config.validate!
|
||||
|
||||
tokens = lexer.lex(File.read("test.txt"))
|
||||
trees = parser.parse?(tokens)
|
||||
trees.try do |trees|
|
||||
trees.each { |tree| puts tree }
|
||||
end
|
||||
generator = CodeGenerator.new (Table.new)
|
||||
compiler = Compiler.new config
|
||||
compiler.run
|
||||
end
|
||||
|
|
79
src/chalk/codegen.cr
Normal file
79
src/chalk/codegen.cr
Normal file
|
@ -0,0 +1,79 @@
|
|||
require "./ir.cr"
|
||||
|
||||
module Chalk
|
||||
class CodeGenerator
|
||||
def initialize(@table : Table)
|
||||
@register = 0
|
||||
@instructions = [] of Instruction
|
||||
@block_edges = [] of Int32
|
||||
end
|
||||
|
||||
private def load(into, value)
|
||||
@instructions << LoadInstruction.new into, value.to_i32
|
||||
end
|
||||
|
||||
private def loadr(into, from)
|
||||
@instructions << LoadRegInstruction.new into, from
|
||||
end
|
||||
|
||||
private def op(op, into, from)
|
||||
@instructions << OpRegInstruction.new op, into, from
|
||||
end
|
||||
|
||||
private def store(up_to)
|
||||
@instructions << StoreInstruction.new up_to
|
||||
end
|
||||
|
||||
private def restore(up_to)
|
||||
@instructions << RestoreInstruction.new up_to
|
||||
end
|
||||
|
||||
private def ret(reg)
|
||||
@instructions << ReturnInstruction.new reg
|
||||
end
|
||||
|
||||
def generate(tree : Tree, target : Int32)
|
||||
case tree
|
||||
when TreeId
|
||||
entry = @table[tree.id]?
|
||||
raise "Unknown variable" unless entry &&
|
||||
entry.is_a?(VarEntry)
|
||||
loadr target, entry.as(VarEntry).register
|
||||
when TreeLit
|
||||
load target, tree.lit
|
||||
when TreeOp
|
||||
generate tree.left, target
|
||||
generate tree.right, @register
|
||||
op tree.op, target, @register
|
||||
when TreeBlock
|
||||
tree.children.each do |child|
|
||||
generate child, @register
|
||||
end
|
||||
when TreeVar
|
||||
entry = @table[tree.name]?
|
||||
if entry == nil
|
||||
entry = VarEntry.new @register
|
||||
@register += 1
|
||||
@table[tree.name] = entry
|
||||
end
|
||||
raise "Unknown variable" unless entry.is_a?(VarEntry)
|
||||
generate tree.expr, entry.register
|
||||
when TreeAssign
|
||||
entry = @table[tree.name]?
|
||||
raise "Unknown variable" unless entry &&
|
||||
entry.is_a?(VarEntry)
|
||||
generate tree.expr, entry.as(VarEntry).register
|
||||
when TreeReturn
|
||||
generate tree.rvalue, target
|
||||
ret target
|
||||
end
|
||||
end
|
||||
|
||||
def generate(tree : Tree)
|
||||
generate(tree, 0)
|
||||
@block_edges << @instructions.size
|
||||
@block_edges.sort!
|
||||
return @instructions
|
||||
end
|
||||
end
|
||||
end
|
77
src/chalk/compiler.cr
Normal file
77
src/chalk/compiler.cr
Normal file
|
@ -0,0 +1,77 @@
|
|||
require "logger"
|
||||
require "./constant_folder.cr"
|
||||
require "./table.cr"
|
||||
|
||||
module Chalk
|
||||
class Compiler
|
||||
def initialize(@config : Config)
|
||||
@logger = Logger.new STDOUT
|
||||
@lexer = Lexer.new
|
||||
@parser = Parser.new
|
||||
@logger.debug("Initialized compiler")
|
||||
@logger.level = Logger::DEBUG
|
||||
end
|
||||
|
||||
private def create_trees(file)
|
||||
string = File.read(file)
|
||||
tokens = @lexer.lex string
|
||||
if tokens.size == 0 && string != ""
|
||||
raise "Unable to tokenize file."
|
||||
end
|
||||
@logger.debug("Finished tokenizing")
|
||||
if trees = @parser.parse?(tokens)
|
||||
@logger.debug("Finished parsing")
|
||||
return trees
|
||||
end
|
||||
raise "Unable to parse file."
|
||||
end
|
||||
|
||||
private def process_initial(trees)
|
||||
table = Table.new
|
||||
folder = ConstantFolder.new
|
||||
trees.each do |tree|
|
||||
tree = tree.as(TreeFunction)
|
||||
@logger.debug("Constant folding #{tree.name}")
|
||||
tree = tree.apply(folder).as(TreeFunction)
|
||||
@logger.debug("Storing #{tree.name} in symbol table")
|
||||
table[tree.name] = FunctionEntry.new tree
|
||||
end
|
||||
return table
|
||||
end
|
||||
|
||||
private def generate_code(trees, table)
|
||||
trees.each do |tree|
|
||||
generator = CodeGenerator.new table
|
||||
instructions = generator.generate tree.as(TreeFunction).block
|
||||
instructions.each { |it| puts it }
|
||||
end
|
||||
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 = process_initial(trees)
|
||||
generate_code(trees, table)
|
||||
end
|
||||
|
||||
private def run_binary
|
||||
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
|
33
src/chalk/constant_folder.cr
Normal file
33
src/chalk/constant_folder.cr
Normal file
|
@ -0,0 +1,33 @@
|
|||
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
|
||||
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)
|
||||
end
|
||||
return tree
|
||||
end
|
||||
end
|
||||
end
|
103
src/chalk/ir.cr
Normal file
103
src/chalk/ir.cr
Normal file
|
@ -0,0 +1,103 @@
|
|||
require "./lexer.cr"
|
||||
|
||||
module Chalk
|
||||
class Instruction
|
||||
end
|
||||
|
||||
class LoadInstruction < Instruction
|
||||
property register : Int32
|
||||
property value : Int32
|
||||
|
||||
def initialize(@register : Int32, @value : Int32)
|
||||
end
|
||||
|
||||
def to_s(io)
|
||||
io << "load R"
|
||||
@register.to_s(16, io)
|
||||
io << " " << @value
|
||||
end
|
||||
end
|
||||
|
||||
class LoadRegInstruction < Instruction
|
||||
property into : Int32
|
||||
property from : Int32
|
||||
|
||||
def initialize(@into : Int32, @from : Int32)
|
||||
end
|
||||
|
||||
def to_s(io)
|
||||
io << "loadr R"
|
||||
@into.to_s(16, io)
|
||||
io << " R"
|
||||
@from.to_s(16, io)
|
||||
end
|
||||
end
|
||||
|
||||
class OpInstruction < Instruction
|
||||
property op : TokenType
|
||||
property into : Int32
|
||||
property value : Int32
|
||||
|
||||
def initialize(@op : TokenType, @into : Int32,
|
||||
@value : Int32)
|
||||
end
|
||||
|
||||
def to_s(io)
|
||||
io << "op " << op << " R"
|
||||
@into.to_s(16, io)
|
||||
io << " " << @value
|
||||
end
|
||||
end
|
||||
|
||||
class OpRegInstruction < Instruction
|
||||
property op : TokenType
|
||||
property into : Int32
|
||||
property from : Int32
|
||||
|
||||
def initialize(@op : TokenType, @into : Int32, @from : Int32)
|
||||
end
|
||||
|
||||
def to_s(io)
|
||||
io << "opr " << op << " R"
|
||||
@into.to_s(16, io)
|
||||
io << " R"
|
||||
@from.to_s(16, io)
|
||||
end
|
||||
end
|
||||
|
||||
class StoreInstruction < Instruction
|
||||
property up_to : Int32
|
||||
|
||||
def initialize(@up_to : Int32)
|
||||
end
|
||||
|
||||
def to_s(io)
|
||||
io << "store R"
|
||||
@up_to.to_s(16, io)
|
||||
end
|
||||
end
|
||||
|
||||
class RestoreInstruction < Instruction
|
||||
property up_to : Int32
|
||||
|
||||
def initialize(@up_to : Int32)
|
||||
end
|
||||
|
||||
def to_s(io)
|
||||
io << "restore R"
|
||||
@up_to.to_s(16, io)
|
||||
end
|
||||
end
|
||||
|
||||
class ReturnInstruction < Instruction
|
||||
property to_return : Int32
|
||||
|
||||
def initialize(@to_return : Int32)
|
||||
end
|
||||
|
||||
def to_s(io)
|
||||
io << "return R"
|
||||
@to_return.to_s(16, io)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -67,4 +67,10 @@ module Chalk
|
|||
forward("[while]", TreeWhile)
|
||||
forward("[return]", TreeReturn)
|
||||
end
|
||||
|
||||
class Tree
|
||||
def to_s(io)
|
||||
accept(PrintVisitor.new io)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
51
src/chalk/table.cr
Normal file
51
src/chalk/table.cr
Normal file
|
@ -0,0 +1,51 @@
|
|||
module Chalk
|
||||
class Entry
|
||||
end
|
||||
|
||||
class FunctionEntry < Entry
|
||||
property function : TreeFunction
|
||||
property addr : Int32
|
||||
|
||||
def initialize(@function : TreeFunction, @addr : Int32 = -1)
|
||||
end
|
||||
|
||||
def to_s(io)
|
||||
io << "[function]"
|
||||
end
|
||||
end
|
||||
|
||||
class VarEntry < Entry
|
||||
property register : Int32
|
||||
|
||||
def initialize(@register : Int32)
|
||||
end
|
||||
|
||||
def to_s(io)
|
||||
io << "[variable] " << "(R" << @register.to_s(16) << ")"
|
||||
end
|
||||
end
|
||||
|
||||
class Table
|
||||
property parent : Table?
|
||||
|
||||
def initialize(@parent : Table? = nil)
|
||||
@data = {} of String => Entry
|
||||
end
|
||||
|
||||
def []?(key) : Entry?
|
||||
if entry = @data[key]?
|
||||
return entry
|
||||
end
|
||||
return @parent.try &.[key]?
|
||||
end
|
||||
|
||||
def []=(key, entry)
|
||||
@data[key] = entry
|
||||
end
|
||||
|
||||
def to_s(io)
|
||||
@parent.try &.to_s(io)
|
||||
io << @data.map { |k, v| k + ": " + v.to_s }.join("\n")
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,5 +1,3 @@
|
|||
require "./print_visitor.cr"
|
||||
|
||||
module Chalk
|
||||
class Visitor
|
||||
def visit(tree : Tree)
|
||||
|
@ -9,14 +7,20 @@ module Chalk
|
|||
end
|
||||
end
|
||||
|
||||
class Transformer
|
||||
def transform(tree : Tree) : Tree
|
||||
return tree
|
||||
end
|
||||
end
|
||||
|
||||
class Tree
|
||||
def accept(v : Visitor)
|
||||
v.visit(self)
|
||||
v.finish(self)
|
||||
end
|
||||
|
||||
def to_s(io)
|
||||
accept(PrintVisitor.new io)
|
||||
def apply(t : Transformer)
|
||||
return t.transform(self)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -46,6 +50,13 @@ module Chalk
|
|||
@params.each &.accept(v)
|
||||
v.finish(self)
|
||||
end
|
||||
|
||||
def apply(t : Transformer)
|
||||
@params.map! do |param|
|
||||
param.apply(t)
|
||||
end
|
||||
return t.transform(self)
|
||||
end
|
||||
end
|
||||
|
||||
class TreeOp < Tree
|
||||
|
@ -62,9 +73,17 @@ module Chalk
|
|||
@right.accept(v)
|
||||
v.finish(self)
|
||||
end
|
||||
|
||||
def apply(t : Transformer)
|
||||
@left = @left.apply(t)
|
||||
@right = @right.apply(t)
|
||||
return t.transform(self)
|
||||
end
|
||||
end
|
||||
|
||||
class TreeBlock < Tree
|
||||
property children : Array(Tree)
|
||||
|
||||
def initialize(@children : Array(Tree))
|
||||
end
|
||||
|
||||
|
@ -73,6 +92,13 @@ module Chalk
|
|||
@children.each &.accept(v)
|
||||
v.finish(self)
|
||||
end
|
||||
|
||||
def apply(t : Transformer)
|
||||
@children.map! do |child|
|
||||
child.apply(t)
|
||||
end
|
||||
return t.transform(self)
|
||||
end
|
||||
end
|
||||
|
||||
class TreeFunction < Tree
|
||||
|
@ -88,6 +114,11 @@ module Chalk
|
|||
@block.accept(v)
|
||||
v.finish(self)
|
||||
end
|
||||
|
||||
def apply(t : Transformer)
|
||||
@block = @block.apply(t)
|
||||
return t.transform(self)
|
||||
end
|
||||
end
|
||||
|
||||
class TreeVar < Tree
|
||||
|
@ -102,6 +133,11 @@ module Chalk
|
|||
@expr.accept(v)
|
||||
v.finish(self)
|
||||
end
|
||||
|
||||
def apply(t : Transformer)
|
||||
@expr = @expr.apply(t)
|
||||
return t.transform(self)
|
||||
end
|
||||
end
|
||||
|
||||
class TreeAssign < Tree
|
||||
|
@ -116,6 +152,11 @@ module Chalk
|
|||
@expr.accept(v)
|
||||
v.finish(self)
|
||||
end
|
||||
|
||||
def apply(t : Transformer)
|
||||
@expr = @expr.apply(t)
|
||||
return t.transform(self)
|
||||
end
|
||||
end
|
||||
|
||||
class TreeIf < Tree
|
||||
|
@ -133,6 +174,13 @@ module Chalk
|
|||
@otherwise.try &.accept(v)
|
||||
v.finish(self)
|
||||
end
|
||||
|
||||
def apply(t : Transformer)
|
||||
@condition = @condition.apply(t)
|
||||
@block = @block.apply(t)
|
||||
@otherwise = @otherwise.try &.apply(t)
|
||||
return t.transform(self)
|
||||
end
|
||||
end
|
||||
|
||||
class TreeWhile < Tree
|
||||
|
@ -148,6 +196,12 @@ module Chalk
|
|||
@block.accept(v)
|
||||
v.finish(self)
|
||||
end
|
||||
|
||||
def apply(t : Transformer)
|
||||
@condition = @condition.apply(t)
|
||||
@block = @block.apply(t)
|
||||
return t.transform(self)
|
||||
end
|
||||
end
|
||||
|
||||
class TreeReturn < Tree
|
||||
|
@ -161,5 +215,10 @@ module Chalk
|
|||
@rvalue.accept(v)
|
||||
v.finish(self)
|
||||
end
|
||||
|
||||
def apply(t : Transformer)
|
||||
@rvalue = @rvalue.apply(t)
|
||||
return t.transform(self)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
51
src/chalk/ui.cr
Normal file
51
src/chalk/ui.cr
Normal file
|
@ -0,0 +1,51 @@
|
|||
module Chalk
|
||||
enum OutputMode
|
||||
Tree,
|
||||
Intermediate,
|
||||
Binary
|
||||
end
|
||||
|
||||
class Config
|
||||
property file : String
|
||||
property mode : OutputMode
|
||||
|
||||
def initialize(@file : String = "",
|
||||
@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
|
||||
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
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue
Block a user