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 "./chalk/*"
|
||||||
|
require "option_parser"
|
||||||
|
|
||||||
module Chalk
|
module Chalk
|
||||||
lexer = Lexer.new
|
config = Config.parse!
|
||||||
parser = Parser.new
|
exit unless config.validate!
|
||||||
|
|
||||||
tokens = lexer.lex(File.read("test.txt"))
|
generator = CodeGenerator.new (Table.new)
|
||||||
trees = parser.parse?(tokens)
|
compiler = Compiler.new config
|
||||||
trees.try do |trees|
|
compiler.run
|
||||||
trees.each { |tree| puts tree }
|
|
||||||
end
|
|
||||||
end
|
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
|
|
@ -48,16 +48,16 @@ module Chalk
|
||||||
end
|
end
|
||||||
|
|
||||||
macro forward(text, type)
|
macro forward(text, type)
|
||||||
def visit(tree : {{type}})
|
def visit(tree : {{type}})
|
||||||
print_indent
|
print_indent
|
||||||
@stream << {{text}} << "\n"
|
@stream << {{text}} << "\n"
|
||||||
@indent += 1
|
@indent += 1
|
||||||
end
|
end
|
||||||
|
|
||||||
def finish(tree : {{type}})
|
def finish(tree : {{type}})
|
||||||
@indent -= 1
|
@indent -= 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
forward("[call]", TreeCall)
|
forward("[call]", TreeCall)
|
||||||
forward("[block]", TreeBlock)
|
forward("[block]", TreeBlock)
|
||||||
|
@ -67,4 +67,10 @@ module Chalk
|
||||||
forward("[while]", TreeWhile)
|
forward("[while]", TreeWhile)
|
||||||
forward("[return]", TreeReturn)
|
forward("[return]", TreeReturn)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class Tree
|
||||||
|
def to_s(io)
|
||||||
|
accept(PrintVisitor.new io)
|
||||||
|
end
|
||||||
|
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
|
module Chalk
|
||||||
class Visitor
|
class Visitor
|
||||||
def visit(tree : Tree)
|
def visit(tree : Tree)
|
||||||
|
@ -9,14 +7,20 @@ module Chalk
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class Transformer
|
||||||
|
def transform(tree : Tree) : Tree
|
||||||
|
return tree
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
class Tree
|
class Tree
|
||||||
def accept(v : Visitor)
|
def accept(v : Visitor)
|
||||||
v.visit(self)
|
v.visit(self)
|
||||||
v.finish(self)
|
v.finish(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_s(io)
|
def apply(t : Transformer)
|
||||||
accept(PrintVisitor.new io)
|
return t.transform(self)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -46,6 +50,13 @@ module Chalk
|
||||||
@params.each &.accept(v)
|
@params.each &.accept(v)
|
||||||
v.finish(self)
|
v.finish(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def apply(t : Transformer)
|
||||||
|
@params.map! do |param|
|
||||||
|
param.apply(t)
|
||||||
|
end
|
||||||
|
return t.transform(self)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class TreeOp < Tree
|
class TreeOp < Tree
|
||||||
|
@ -62,9 +73,17 @@ module Chalk
|
||||||
@right.accept(v)
|
@right.accept(v)
|
||||||
v.finish(self)
|
v.finish(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def apply(t : Transformer)
|
||||||
|
@left = @left.apply(t)
|
||||||
|
@right = @right.apply(t)
|
||||||
|
return t.transform(self)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class TreeBlock < Tree
|
class TreeBlock < Tree
|
||||||
|
property children : Array(Tree)
|
||||||
|
|
||||||
def initialize(@children : Array(Tree))
|
def initialize(@children : Array(Tree))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -73,6 +92,13 @@ module Chalk
|
||||||
@children.each &.accept(v)
|
@children.each &.accept(v)
|
||||||
v.finish(self)
|
v.finish(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def apply(t : Transformer)
|
||||||
|
@children.map! do |child|
|
||||||
|
child.apply(t)
|
||||||
|
end
|
||||||
|
return t.transform(self)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class TreeFunction < Tree
|
class TreeFunction < Tree
|
||||||
|
@ -88,6 +114,11 @@ module Chalk
|
||||||
@block.accept(v)
|
@block.accept(v)
|
||||||
v.finish(self)
|
v.finish(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def apply(t : Transformer)
|
||||||
|
@block = @block.apply(t)
|
||||||
|
return t.transform(self)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class TreeVar < Tree
|
class TreeVar < Tree
|
||||||
|
@ -102,6 +133,11 @@ module Chalk
|
||||||
@expr.accept(v)
|
@expr.accept(v)
|
||||||
v.finish(self)
|
v.finish(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def apply(t : Transformer)
|
||||||
|
@expr = @expr.apply(t)
|
||||||
|
return t.transform(self)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class TreeAssign < Tree
|
class TreeAssign < Tree
|
||||||
|
@ -116,6 +152,11 @@ module Chalk
|
||||||
@expr.accept(v)
|
@expr.accept(v)
|
||||||
v.finish(self)
|
v.finish(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def apply(t : Transformer)
|
||||||
|
@expr = @expr.apply(t)
|
||||||
|
return t.transform(self)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class TreeIf < Tree
|
class TreeIf < Tree
|
||||||
|
@ -133,6 +174,13 @@ module Chalk
|
||||||
@otherwise.try &.accept(v)
|
@otherwise.try &.accept(v)
|
||||||
v.finish(self)
|
v.finish(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def apply(t : Transformer)
|
||||||
|
@condition = @condition.apply(t)
|
||||||
|
@block = @block.apply(t)
|
||||||
|
@otherwise = @otherwise.try &.apply(t)
|
||||||
|
return t.transform(self)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class TreeWhile < Tree
|
class TreeWhile < Tree
|
||||||
|
@ -148,6 +196,12 @@ module Chalk
|
||||||
@block.accept(v)
|
@block.accept(v)
|
||||||
v.finish(self)
|
v.finish(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def apply(t : Transformer)
|
||||||
|
@condition = @condition.apply(t)
|
||||||
|
@block = @block.apply(t)
|
||||||
|
return t.transform(self)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class TreeReturn < Tree
|
class TreeReturn < Tree
|
||||||
|
@ -161,5 +215,10 @@ module Chalk
|
||||||
@rvalue.accept(v)
|
@rvalue.accept(v)
|
||||||
v.finish(self)
|
v.finish(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def apply(t : Transformer)
|
||||||
|
@rvalue = @rvalue.apply(t)
|
||||||
|
return t.transform(self)
|
||||||
|
end
|
||||||
end
|
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