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" require "option_parser"
module Chalk module Chalk
config = Config.parse! config = Ui::Config.parse!
exit unless config.validate! exit unless config.validate!
compiler = Compiler.new config compiler = Compiler::Compiler.new config
compiler.run compiler.run
end end

View File

@ -1,4 +1,5 @@
module Chalk module Chalk
module Builtin
class BuiltinFunction class BuiltinFunction
getter param_count : Int32 getter param_count : Int32
@ -18,4 +19,5 @@ module Chalk
def generate!(codegen, params, table, target, free) def generate!(codegen, params, table, target, free)
end end
end end
end
end end

View File

@ -2,17 +2,18 @@ require "./ir.cr"
require "./emitter.cr" require "./emitter.cr"
module Chalk module Chalk
module Compiler
class CodeGenerator class CodeGenerator
include Emitter include Emitter
RETURN_REG = 14 RETURN_REG = 14
STACK_REG = 13 STACK_REG = 13
property instructions : Array(Instruction) property instructions : Array(Ir::Instruction)
def initialize(table, @function : TreeFunction) def initialize(table, @function : Trees::TreeFunction)
@registers = 0 @registers = 0
@instructions = [] of Instruction @instructions = [] of Ir::Instruction
@table = Table.new table @table = Table.new table
@function.params.each do |param| @function.params.each do |param|
@ -21,11 +22,11 @@ module Chalk
end end
end end
def generate!(tree, function : InlineFunction, table, target, free) def generate!(tree, function : Builtin::InlineFunction, table, target, free)
function.generate!(self, tree.params, table, target, free) function.generate!(self, tree.params, table, target, free)
end end
def generate!(tree, function : TreeFunction | BuiltinFunction, table, target, free) def generate!(tree, function : Trees::TreeFunction | Builtin::BuiltinFunction, table, target, free)
start_at = free start_at = free
# Move I to stack # Move I to stack
setis setis
@ -64,30 +65,30 @@ module Chalk
def generate!(tree, table, target, free) def generate!(tree, table, target, free)
case tree case tree
when TreeId when Trees::TreeId
entry = table[tree.id]? entry = table[tree.id]?
raise "Unknown variable" unless entry && raise "Unknown variable" unless entry &&
entry.is_a?(VarEntry) entry.is_a?(VarEntry)
loadr target, entry.register loadr target, entry.register
when TreeLit when Trees::TreeLit
load target, tree.lit load target, tree.lit
when TreeOp when Trees::TreeOp
generate! tree.left, table, target, free generate! tree.left, table, target, free
generate! tree.right, table, free, free + 1 generate! tree.right, table, free, free + 1
opr tree.op, target, free opr tree.op, target, free
when TreeCall when Trees::TreeCall
entry = table[tree.name]? entry = table[tree.name]?
raise "Unknown function" unless entry && raise "Unknown function" unless entry &&
entry.is_a?(FunctionEntry) entry.is_a?(FunctionEntry)
function = entry.function function = entry.function
raise "Invalid call" if tree.params.size != function.param_count raise "Invalid call" if tree.params.size != function.param_count
generate! tree, function, table, target, free generate! tree, function, table, target, free
when TreeBlock when Trees::TreeBlock
table = Table.new(table) table = Table.new(table)
tree.children.each do |child| tree.children.each do |child|
free += generate! child, table, free, free + 1 free += generate! child, table, free, free + 1
end end
when TreeVar when Trees::TreeVar
entry = table[tree.name]? entry = table[tree.name]?
if entry == nil if entry == nil
entry = VarEntry.new free entry = VarEntry.new free
@ -97,12 +98,12 @@ module Chalk
raise "Unknown variable" unless entry.is_a?(VarEntry) raise "Unknown variable" unless entry.is_a?(VarEntry)
generate! tree.expr, table, entry.register, free generate! tree.expr, table, entry.register, free
return 1 return 1
when TreeAssign when Trees::TreeAssign
entry = table[tree.name]? entry = table[tree.name]?
raise "Unknown variable" unless entry && raise "Unknown variable" unless entry &&
entry.is_a?(VarEntry) entry.is_a?(VarEntry)
generate! tree.expr, table, entry.register, free generate! tree.expr, table, entry.register, free
when TreeIf when Trees::TreeIf
generate! tree.condition, table, free, free + 1 generate! tree.condition, table, free, free + 1
sne free, 0 sne free, 0
jump_inst = jr 0 jump_inst = jr 0
@ -115,7 +116,7 @@ module Chalk
old_size = @instructions.size old_size = @instructions.size
generate! tree.otherwise, table, free, free + 1 if tree.otherwise generate! tree.otherwise, table, free, free + 1 if tree.otherwise
jump_after.offset = @instructions.size - old_size + 1 jump_after.offset = @instructions.size - old_size + 1
when TreeWhile when Trees::TreeWhile
before_cond = @instructions.size before_cond = @instructions.size
generate! tree.condition, table, free, free + 1 generate! tree.condition, table, free, free + 1
sne free, 0 sne free, 0
@ -127,7 +128,7 @@ module Chalk
cond_jump.offset = @instructions.size - old_size + 1 cond_jump.offset = @instructions.size - old_size + 1
after_jump.offset = before_cond - instructions.size + 1 after_jump.offset = before_cond - instructions.size + 1
when TreeReturn when Trees::TreeReturn
generate! tree.rvalue, table, RETURN_REG, free generate! tree.rvalue, table, RETURN_REG, free
ret ret
end end
@ -139,4 +140,5 @@ module Chalk
return @instructions return @instructions
end end
end end
end
end end

View File

@ -3,8 +3,9 @@ require "./constant_folder.cr"
require "./table.cr" require "./table.cr"
module Chalk module Chalk
module Compiler
class Compiler class Compiler
def initialize(@config : Config) def initialize(@config : Ui::Config)
@logger = Logger.new STDOUT @logger = Logger.new STDOUT
@logger.debug("Initialized compiler") @logger.debug("Initialized compiler")
@logger.level = Logger::DEBUG @logger.level = Logger::DEBUG
@ -20,14 +21,14 @@ module Chalk
end end
@logger.debug("Finished tokenizing") @logger.debug("Finished tokenizing")
@logger.debug("Beginning parsing") @logger.debug("Beginning parsing")
parser = Parser.new parser = ParserCombinators::Parser.new
if trees = parser.parse?(tokens) if trees = parser.parse?(tokens)
@logger.debug("Finished parsing") @logger.debug("Finished parsing")
@logger.debug("Beginning constant folding") @logger.debug("Beginning constant folding")
folder = ConstantFolder.new folder = Trees::ConstantFolder.new
trees.map! do |tree| trees.map! do |tree|
@logger.debug("Constant folding #{tree.name}") @logger.debug("Constant folding #{tree.name}")
tree.apply(folder).as(TreeFunction) tree.apply(folder).as(Trees::TreeFunction)
end end
@logger.debug("Done constant folding") @logger.debug("Done constant folding")
return trees return trees
@ -44,15 +45,15 @@ module Chalk
end end
@logger.debug("Done creating symbol table") @logger.debug("Done creating symbol table")
table["draw"] = FunctionEntry.new InlineDrawFunction.new table["draw"] = FunctionEntry.new Builtin::InlineDrawFunction.new
table["get_key"] = FunctionEntry.new InlineAwaitKeyFunction.new table["get_key"] = FunctionEntry.new Builtin::InlineAwaitKeyFunction.new
table["get_font"] = FunctionEntry.new InlineGetFontFunction.new table["get_font"] = FunctionEntry.new Builtin::InlineGetFontFunction.new
table["set_delay"] = FunctionEntry.new InlineSetDelayFunction.new table["set_delay"] = FunctionEntry.new Builtin::InlineSetDelayFunction.new
table["get_delay"] = FunctionEntry.new InlineGetDelayFunction.new table["get_delay"] = FunctionEntry.new Builtin::InlineGetDelayFunction.new
return table return table
end end
private def create_code(tree : TreeFunction, table, instruction = ReturnInstruction.new) private def create_code(tree : Trees::TreeFunction, table, instruction = Ir::ReturnInstruction.new)
optimizer = Optimizer.new optimizer = Optimizer.new
generator = CodeGenerator.new table, tree generator = CodeGenerator.new table, tree
@logger.debug("Generating code for #{tree.name}") @logger.debug("Generating code for #{tree.name}")
@ -61,14 +62,14 @@ module Chalk
return optimizer.optimize(code) return optimizer.optimize(code)
end end
private def create_code(tree : BuiltinFunction, table, instruction = nil) private def create_code(tree : Builtin::BuiltinFunction, table, instruction = nil)
instructions = [] of Instruction instructions = [] of Ir::Instruction
tree.generate!(instructions) tree.generate!(instructions)
return instructions return instructions
end end
private def create_code(trees : Array(TreeFunction), table) private def create_code(trees : Array(Trees::TreeFunction), table)
code = {} of String => Array(Instruction) code = {} of String => Array(Ir::Instruction)
trees.each do |tree| trees.each do |tree|
code[tree.name] = create_code(tree, table) code[tree.name] = create_code(tree, table)
end end
@ -94,7 +95,7 @@ module Chalk
end end
private def generate_binary(table, instructions, dest) private def generate_binary(table, instructions, dest)
context = InstructionContext.new table, instructions.size context = Ir::InstructionContext.new table, instructions.size
binary = instructions.map_with_index { |it, i| it.to_bin(context, i).to_u16 } binary = instructions.map_with_index { |it, i| it.to_bin(context, i).to_u16 }
binary.each do |inst| binary.each do |inst|
first = (inst >> 8).to_u8 first = (inst >> 8).to_u8
@ -116,11 +117,11 @@ module Chalk
entry = table[first]? entry = table[first]?
raise "Unknown function" unless entry && entry.is_a?(FunctionEntry) raise "Unknown function" unless entry && entry.is_a?(FunctionEntry)
function = entry.function function = entry.function
next if function.is_a?(InlineFunction) next if function.is_a?(Builtin::InlineFunction)
done << first done << first
next unless function.is_a?(TreeFunction) next unless function.is_a?(Trees::TreeFunction)
visitor = CallVisitor.new visitor = Trees::CallVisitor.new
function.accept(visitor) function.accept(visitor)
open.concat(visitor.calls - done) open.concat(visitor.calls - done)
end end
@ -128,24 +129,24 @@ module Chalk
end end
private def run_binary private def run_binary
all_instructions = [] of Instruction all_instructions = [] of Ir::Instruction
trees = create_trees(@config.file) trees = create_trees(@config.file)
table = create_table(trees) table = create_table(trees)
names = collect_calls(table) names = collect_calls(table)
names.delete "main" names.delete "main"
main_entry = table["main"]?.as(FunctionEntry) main_entry = table["main"]?.as(FunctionEntry)
all_instructions.concat create_code(main_entry.function.as(TreeFunction), all_instructions.concat create_code(main_entry.function.as(Trees::TreeFunction),
table, JumpRelativeInstruction.new 0) table, Ir::JumpRelativeInstruction.new 0)
main_entry.addr = 0 main_entry.addr = 0
names.each do |name| names.each do |name|
entry = table[name]?.as(FunctionEntry) entry = table[name]?.as(FunctionEntry)
entry.addr = all_instructions.size entry.addr = all_instructions.size
function = entry.function function = entry.function
raise "Trying to compile inlined function" if function.is_a?(InlineFunction) raise "Trying to compile inlined function" if function.is_a?(Builtin::InlineFunction)
all_instructions.concat create_code(function, table) all_instructions.concat create_code(function, table)
all_instructions << ReturnInstruction.new all_instructions << Ir::ReturnInstruction.new
end end
file = File.open("out.ch8", "w") file = File.open("out.ch8", "w")
@ -155,13 +156,14 @@ module Chalk
def run def run
case @config.mode case @config.mode
when OutputMode::Tree when Ui::OutputMode::Tree
run_tree run_tree
when OutputMode::Intermediate when Ui::OutputMode::Intermediate
run_intermediate run_intermediate
when OutputMode::Binary when Ui::OutputMode::Binary
run_binary run_binary
end end
end end
end end
end
end end

View File

@ -1,4 +1,5 @@
module Chalk module Chalk
module Ui
enum OutputMode enum OutputMode
Tree, Tree,
Intermediate, Intermediate,
@ -48,4 +49,5 @@ module Chalk
return true return true
end end
end end
end
end end

View File

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

View File

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

View File

@ -1,4 +1,5 @@
module Chalk module Chalk
module Trees
class CallVisitor < Visitor class CallVisitor < Visitor
property calls : Set(String) property calls : Set(String)
@ -10,4 +11,5 @@ module Chalk
@calls << t.name @calls << t.name
end end
end end
end
end end

View File

@ -1,16 +1,17 @@
module Chalk module Chalk
module Builtin
class InlineDrawFunction < InlineFunction class InlineDrawFunction < InlineFunction
def initialize def initialize
@param_count = 3 @param_count = 3
end end
def generate!(emitter, params, table, target, free) def generate!(emitter, params, table, target, free)
if !params[2].is_a?(TreeLit) if !params[2].is_a?(Trees::TreeLit)
raise "Third parameter must be a constant." raise "Third parameter must be a constant."
end end
emitter.generate! params[0], table, free, free + 1 emitter.generate! params[0], table, free, free + 1
emitter.generate! params[1], table, free + 1, free + 2 emitter.generate! params[1], table, free + 1, free + 2
emitter.instructions << DrawInstruction.new free, free + 1, params[2].as(TreeLit).lit.to_i32 emitter.instructions << Ir::DrawInstruction.new free, free + 1, params[2].as(Trees::TreeLit).lit.to_i32
end end
end end
@ -20,7 +21,7 @@ module Chalk
end end
def generate!(emitter, params, table, target, free) def generate!(emitter, params, table, target, free)
emitter.instructions << AwaitKeyInstruction.new target emitter.instructions << Ir::AwaitKeyInstruction.new target
end end
end end
@ -31,7 +32,7 @@ module Chalk
def generate!(emitter, params, table, target, free) def generate!(emitter, params, table, target, free)
emitter.generate! params[0], table, free, free + 1 emitter.generate! params[0], table, free, free + 1
emitter.instructions << GetFontInstruction.new free emitter.instructions << Ir::GetFontInstruction.new free
end end
end end
@ -42,7 +43,7 @@ module Chalk
def generate!(emitter, params, table, target, free) def generate!(emitter, params, table, target, free)
emitter.generate! params[0], table, free, free + 1 emitter.generate! params[0], table, free, free + 1
emitter.instructions << SetDelayTimerInstruction.new free emitter.instructions << Ir::SetDelayTimerInstruction.new free
end end
end end
@ -52,7 +53,8 @@ module Chalk
end end
def generate!(emitter, params, table, target, free) def generate!(emitter, params, table, target, free)
emitter.instructions << GetDelayTimerInstruction.new target emitter.instructions << Ir::GetDelayTimerInstruction.new target
end
end end
end end
end end

View File

@ -1,6 +1,7 @@
require "./lexer.cr" require "./lexer.cr"
module Chalk module Chalk
module Ir
class Instruction class Instruction
def to_bin(i, index) def to_bin(i, index)
return 0 return 0
@ -8,7 +9,7 @@ module Chalk
end end
class InstructionContext class InstructionContext
property table : Table property table : Compiler::Table
property stack : Int32 property stack : Int32
def initialize(@table, @stack) def initialize(@table, @stack)
@ -53,7 +54,7 @@ module Chalk
end end
class OpInstruction < Instruction class OpInstruction < Instruction
property op : TokenType property op : Compiler::TokenType
property into : Int32 property into : Int32
property value : Int32 property value : Int32
@ -68,7 +69,7 @@ module Chalk
def to_bin(i, index) def to_bin(i, index)
case op case op
when TokenType::OpAdd when Compiler::TokenType::OpAdd
return 0x7000 | (@into << 8) | @value return 0x7000 | (@into << 8) | @value
else else
raise "Invalid instruction" raise "Invalid instruction"
@ -77,7 +78,7 @@ module Chalk
end end
class OpRegInstruction < Instruction class OpRegInstruction < Instruction
property op : TokenType property op : Compiler::TokenType
property into : Int32 property into : Int32
property from : Int32 property from : Int32
@ -94,15 +95,15 @@ module Chalk
def to_bin(i, index) def to_bin(i, index)
code = 0 code = 0
case op case op
when TokenType::OpAdd when Compiler::TokenType::OpAdd
code = 4 code = 4
when TokenType::OpSub when Compiler::TokenType::OpSub
code = 5 code = 5
when TokenType::OpOr when Compiler::TokenType::OpOr
code = 1 code = 1
when TokenType::OpAnd when Compiler::TokenType::OpAnd
code = 2 code = 2
when TokenType::OpXor when Compiler::TokenType::OpXor
code = 3 code = 3
else else
raise "Invalid instruction" raise "Invalid instruction"
@ -256,7 +257,7 @@ module Chalk
end end
def to_bin(i, index) def to_bin(i, index)
return 0x2000 | (i.table[name]?.as(FunctionEntry).addr * 2 + 0x200) return 0x2000 | (i.table[name]?.as(Compiler::FunctionEntry).addr * 2 + 0x200)
end end
end end
@ -370,4 +371,5 @@ module Chalk
return 0xf007 | (@into << 8) return 0xf007 | (@into << 8)
end end
end end
end
end end

View File

@ -1,6 +1,7 @@
require "lex" require "lex"
module Chalk module Chalk
module Compiler
enum TokenType enum TokenType
Any, Any,
Str, Str,
@ -79,4 +80,5 @@ module Chalk
end end
end end
end end
end
end end

View File

@ -1,7 +1,8 @@
module Chalk module Chalk
module Compiler
class Optimizer class Optimizer
private def check_dead(inst) private def check_dead(inst)
if inst.is_a?(LoadRegInstruction) if inst.is_a?(Ir::LoadRegInstruction)
return inst.from == inst.into return inst.from == inst.into
end end
return false return false
@ -20,7 +21,7 @@ module Chalk
instructions = instructions.dup instructions = instructions.dup
block_boundaries = [instructions.size] block_boundaries = [instructions.size]
instructions.each_with_index do |inst, i| instructions.each_with_index do |inst, i|
if inst.is_a?(JumpRelativeInstruction) if inst.is_a?(Ir::JumpRelativeInstruction)
block_boundaries << i block_boundaries << i
block_boundaries << (inst.offset + i) block_boundaries << (inst.offset + i)
end end
@ -37,7 +38,7 @@ module Chalk
end end
instructions.each_with_index do |inst, i| instructions.each_with_index do |inst, i|
next if !inst.is_a?(JumpRelativeInstruction) next if !inst.is_a?(Ir::JumpRelativeInstruction)
jump_to = inst.offset + i jump_to = inst.offset + i
next unless deletions_at[jump_to]? next unless deletions_at[jump_to]?
deletions_offset = deletions_at[i] - deletions_at[jump_to] deletions_offset = deletions_at[i] - deletions_at[jump_to]
@ -52,4 +53,5 @@ module Chalk
return instructions return instructions
end end
end end
end
end end

View File

@ -1,29 +1,30 @@
require "./parser_builder.cr" require "./parser_builder.cr"
module Chalk module Chalk
module ParserCombinators
class Parser class Parser
include ParserBuilder include ParserBuilder
private def create_type private def create_type
either(type(TokenType::KwU0), type(TokenType::KwU8), type(TokenType::KwU12)) either(type(Compiler::TokenType::KwU0), type(Compiler::TokenType::KwU8), type(Compiler::TokenType::KwU12))
end end
private def create_lit private def create_lit
dec_parser = type(TokenType::LitDec).transform &.string.to_i64 dec_parser = type(Compiler::TokenType::LitDec).transform &.string.to_i64
hex_parser = type(TokenType::LitHex).transform &.string.lchop("0x").to_i64(16) hex_parser = type(Compiler::TokenType::LitHex).transform &.string.lchop("0x").to_i64(16)
bin_parser = type(TokenType::LitBin).transform &.string.lchop("0b").to_i64(2) bin_parser = type(Compiler::TokenType::LitBin).transform &.string.lchop("0b").to_i64(2)
lit_parser = either(dec_parser, hex_parser, bin_parser).transform { |it| TreeLit.new(it).as(Tree) } lit_parser = either(dec_parser, hex_parser, bin_parser).transform { |it| Trees::TreeLit.new(it).as(Trees::Tree) }
return lit_parser return lit_parser
end end
private def create_op_expr(atom, op) private def create_op_expr(atom, op)
pl = PlaceholderParser(Tree).new pl = PlaceholderParser(Trees::Tree).new
recurse = atom.then(op).then(pl).transform do |arr| recurse = atom.then(op).then(pl).transform do |arr|
arr = arr.flatten arr = arr.flatten
TreeOp.new( Trees::TreeOp.new(
arr[1].as(Token).type, arr[1].as(Compiler::Token).type,
arr[0].as(Tree), arr[0].as(Trees::Tree),
arr[2].as(Tree)).as(Tree) arr[2].as(Trees::Tree)).as(Trees::Tree)
end end
pl.parser = either(recurse, atom) pl.parser = either(recurse, atom)
return pl return pl
@ -36,27 +37,27 @@ module Chalk
end end
private def create_call(expr) private def create_call(expr)
call = type(TokenType::Id).then(char '(').then(delimited(expr, char ',')).then(char ')').transform do |arr| call = type(Compiler::TokenType::Id).then(char '(').then(delimited(expr, char ',')).then(char ')').transform do |arr|
arr = arr.flatten arr = arr.flatten
name = arr[0].as(Token).string name = arr[0].as(Compiler::Token).string
params = arr[2..arr.size - 2].map &.as(Tree) params = arr[2..arr.size - 2].map &.as(Trees::Tree)
TreeCall.new(name, params).as(Tree) Trees::TreeCall.new(name, params).as(Trees::Tree)
end end
return call return call
end end
private def create_expr private def create_expr
expr_place = PlaceholderParser(Tree).new expr_place = PlaceholderParser(Trees::Tree).new
literal = create_lit literal = create_lit
id = type(TokenType::Id).transform { |it| TreeId.new(it.string).as(Tree) } id = type(Compiler::TokenType::Id).transform { |it| Trees::TreeId.new(it.string).as(Trees::Tree) }
call = create_call(expr_place) call = create_call(expr_place)
atom = either(literal, call, id) atom = either(literal, call, id)
ops = [either(type(TokenType::OpMul), type(TokenType::OpDiv)), ops = [either(type(Compiler::TokenType::OpMul), type(Compiler::TokenType::OpDiv)),
either(type(TokenType::OpAdd), type(TokenType::OpSub)), either(type(Compiler::TokenType::OpAdd), type(Compiler::TokenType::OpSub)),
type(TokenType::OpXor), type(Compiler::TokenType::OpXor),
type(TokenType::OpAnd), type(Compiler::TokenType::OpAnd),
type(TokenType::OpOr)] type(Compiler::TokenType::OpOr)]
expr = create_op_exprs(atom, ops) expr = create_op_exprs(atom, ops)
expr_place.parser = expr expr_place.parser = expr
@ -64,60 +65,60 @@ module Chalk
end end
private def create_var(expr) private def create_var(expr)
var = type(TokenType::KwVar).then(type(TokenType::Id)).then(char '=').then(expr).then(char ';').transform do |arr| var = type(Compiler::TokenType::KwVar).then(type(Compiler::TokenType::Id)).then(char '=').then(expr).then(char ';').transform do |arr|
arr = arr.flatten arr = arr.flatten
name = arr[1].as(Token).string name = arr[1].as(Compiler::Token).string
exp = arr[arr.size - 2].as(Tree) exp = arr[arr.size - 2].as(Trees::Tree)
TreeVar.new(name, exp).as(Tree) Trees::TreeVar.new(name, exp).as(Trees::Tree)
end end
return var return var
end end
private def create_assign(expr) private def create_assign(expr)
assign = type(TokenType::Id).then(char '=').then(expr).then(char ';').transform do |arr| assign = type(Compiler::TokenType::Id).then(char '=').then(expr).then(char ';').transform do |arr|
arr = arr.flatten arr = arr.flatten
name = arr[0].as(Token).string name = arr[0].as(Compiler::Token).string
exp = arr[arr.size - 2].as(Tree) exp = arr[arr.size - 2].as(Trees::Tree)
TreeAssign.new(name, exp).as(Tree) Trees::TreeAssign.new(name, exp).as(Trees::Tree)
end end
return assign return assign
end end
private def create_basic(expr) private def create_basic(expr)
basic = expr.then(char ';').transform do |arr| basic = expr.then(char ';').transform do |arr|
arr.flatten[0].as(Tree) arr.flatten[0].as(Trees::Tree)
end end
return basic return basic
end end
private def create_if(expr, block) private def create_if(expr, block)
iff = type(TokenType::KwIf).then(char '(').then(expr).then(char ')').then(block) iff = type(Compiler::TokenType::KwIf).then(char '(').then(expr).then(char ')').then(block)
.then(optional(type(TokenType::KwElse).then(block))) .then(optional(type(Compiler::TokenType::KwElse).then(block)))
.transform do |arr| .transform do |arr|
arr = arr.flatten arr = arr.flatten
cond = arr[2].as(Tree) cond = arr[2].as(Trees::Tree)
code = arr[4].as(Tree) code = arr[4].as(Trees::Tree)
otherwise = arr.size == 7 ? arr[6].as(Tree) : nil otherwise = arr.size == 7 ? arr[6].as(Trees::Tree) : nil
TreeIf.new(cond, code, otherwise).as(Tree) Trees::TreeIf.new(cond, code, otherwise).as(Trees::Tree)
end end
return iff return iff
end end
private def create_while(expr, block) private def create_while(expr, block)
whilee = type(TokenType::KwWhile).then(char '(').then(expr).then(char ')').then(block).transform do |arr| whilee = type(Compiler::TokenType::KwWhile).then(char '(').then(expr).then(char ')').then(block).transform do |arr|
arr = arr.flatten arr = arr.flatten
cond = arr[2].as(Tree) cond = arr[2].as(Trees::Tree)
code = arr[4].as(Tree) code = arr[4].as(Trees::Tree)
TreeWhile.new(cond, code).as(Tree) Trees::TreeWhile.new(cond, code).as(Trees::Tree)
end end
return whilee return whilee
end end
private def create_return(expr) private def create_return(expr)
returnn = type(TokenType::KwReturn).then(expr).then(char ';').transform do |arr| returnn = type(Compiler::TokenType::KwReturn).then(expr).then(char ';').transform do |arr|
arr = arr.flatten arr = arr.flatten
value = arr[1].as(Tree) value = arr[1].as(Trees::Tree)
TreeReturn.new(value).as(Tree) Trees::TreeReturn.new(value).as(Trees::Tree)
end end
return returnn return returnn
end end
@ -125,14 +126,14 @@ module Chalk
private def create_block(statement) private def create_block(statement)
block = char('{').then(many(statement)).then(char '}').transform do |arr| block = char('{').then(many(statement)).then(char '}').transform do |arr|
arr = arr.flatten arr = arr.flatten
params = arr[1..arr.size - 2].map &.as(Tree) params = arr[1..arr.size - 2].map &.as(Trees::Tree)
TreeBlock.new(params).as(Tree) Trees::TreeBlock.new(params).as(Trees::Tree)
end end
return block return block
end end
private def create_statement_block private def create_statement_block
statement_place = PlaceholderParser(Tree).new statement_place = PlaceholderParser(Trees::Tree).new
expr = create_expr expr = create_expr
block = create_block(statement_place) block = create_block(statement_place)
iff = create_if(expr, block) iff = create_if(expr, block)
@ -147,27 +148,28 @@ module Chalk
end end
private def create_func(block, type) private def create_func(block, type)
func = type(TokenType::KwFun).then(type(TokenType::Id)) func = type(Compiler::TokenType::KwFun).then(type(Compiler::TokenType::Id))
.then(char '(').then(delimited(type(TokenType::Id), char ',')).then(char ')') .then(char '(').then(delimited(type(Compiler::TokenType::Id), char ',')).then(char ')')
.then(char ':').then(type) .then(char ':').then(type)
.then(block).transform do |arr| .then(block).transform do |arr|
arr = arr.flatten arr = arr.flatten
name = arr[1].as(Token).string name = arr[1].as(Compiler::Token).string
params = arr[3..arr.size - 5].map &.as(Token).string params = arr[3..arr.size - 5].map &.as(Compiler::Token).string
code = arr[arr.size - 1].as(Tree) code = arr[arr.size - 1].as(Trees::Tree)
type = arr[arr.size - 2].as(Token).type type = arr[arr.size - 2].as(Compiler::Token).type
TreeFunction.new(name, params, code) Trees::TreeFunction.new(name, params, code)
end end
return func return func
end end
def initialize def initialize
_, block = create_statement_block _, block = create_statement_block
@parser = many(create_func(block, create_type)).as(BasicParser(Array(TreeFunction))) @parser = many(create_func(block, create_type)).as(BasicParser(Array(Trees::TreeFunction)))
end end
def parse?(tokens) def parse?(tokens)
return @parser.parse?(tokens, 0).try &.[0] return @parser.parse?(tokens, 0).try &.[0]
end end
end end
end
end end

View File

@ -2,13 +2,14 @@ require "./lexer.cr"
require "./parsers.cr" require "./parsers.cr"
module Chalk module Chalk
module ParserCombinators
module ParserBuilder module ParserBuilder
def type(type) : BasicParser(Token) def type(type) : BasicParser(Compiler::Token)
return TypeParser.new(type).as(BasicParser(Token)) return TypeParser.new(type).as(BasicParser(Compiler::Token))
end end
def char(type) : BasicParser(Token) def char(type) : BasicParser(Compiler::Token)
return CharParser.new(type).as(BasicParser(Token)) return CharParser.new(type).as(BasicParser(Compiler::Token))
end end
def transform(parser : BasicParser(T), &transform : T -> R) forall T, R def transform(parser : BasicParser(T), &transform : T -> R) forall T, R
@ -35,4 +36,5 @@ module Chalk
return NextParser.new(first, second).as(BasicParser(Array(T | R))) return NextParser.new(first, second).as(BasicParser(Array(T | R)))
end end
end end
end
end end

View File

@ -1,6 +1,7 @@
module Chalk module Chalk
module ParserCombinators
abstract class BasicParser(T) abstract class BasicParser(T)
abstract def parse?(tokens : Array(Token), abstract def parse?(tokens : Array(Compiler::Token),
index : Int64) : Tuple(T, Int64)? index : Int64) : Tuple(T, Int64)?
def parse(tokens, index) def parse(tokens, index)
@ -16,8 +17,8 @@ module Chalk
end end
end end
class TypeParser < BasicParser(Token) class TypeParser < BasicParser(Compiler::Token)
def initialize(@type : TokenType) def initialize(@type : Compiler::TokenType)
end end
def parse?(tokens, index) def parse?(tokens, index)
@ -27,13 +28,13 @@ module Chalk
end end
end end
class CharParser < BasicParser(Token) class CharParser < BasicParser(Compiler::Token)
def initialize(@char : Char) def initialize(@char : Char)
end end
def parse?(tokens, index) def parse?(tokens, index)
return nil unless index < tokens.size return nil unless index < tokens.size
return nil unless (tokens[index].type == TokenType::Any) && return nil unless (tokens[index].type == Compiler::TokenType::Any) &&
tokens[index].string[0] == @char tokens[index].string[0] == @char
return {tokens[index], index + 1} return {tokens[index], index + 1}
end end
@ -143,4 +144,5 @@ module Chalk
@parser.try &.parse?(tokens, index) @parser.try &.parse?(tokens, index)
end end
end end
end
end end

View File

@ -1,6 +1,7 @@
require "./tree.cr" require "./tree.cr"
module Chalk module Chalk
module Trees
class PrintVisitor < Visitor class PrintVisitor < Visitor
def initialize(@stream : IO) def initialize(@stream : IO)
@indent = 0 @indent = 0
@ -73,4 +74,5 @@ module Chalk
accept(PrintVisitor.new io) accept(PrintVisitor.new io)
end end
end end
end
end end

View File

@ -1,9 +1,10 @@
module Chalk module Chalk
module Compiler
class Entry class Entry
end end
class FunctionEntry < Entry class FunctionEntry < Entry
property function : TreeFunction | BuiltinFunction | InlineFunction property function : Trees::TreeFunction | Builtin::BuiltinFunction | Builtin::InlineFunction
property addr : Int32 property addr : Int32
def initialize(@function, @addr = -1) def initialize(@function, @addr = -1)
@ -48,4 +49,5 @@ module Chalk
io << @data.map { |k, v| k + ": " + v.to_s }.join("\n") io << @data.map { |k, v| k + ": " + v.to_s }.join("\n")
end end
end end
end
end end

View File

@ -1,4 +1,5 @@
module Chalk module Chalk
module Trees
class Visitor class Visitor
def visit(tree) def visit(tree)
end end
@ -60,7 +61,7 @@ module Chalk
end end
class TreeOp < Tree class TreeOp < Tree
property op : TokenType property op : Compiler::TokenType
property left : Tree property left : Tree
property right : Tree property right : Tree
@ -225,4 +226,5 @@ module Chalk
return t.transform(self) return t.transform(self)
end end
end end
end
end end