diff --git a/src/chalk.cr b/src/chalk.cr index 226b0bc..10f4fb8 100644 --- a/src/chalk.cr +++ b/src/chalk.cr @@ -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 diff --git a/src/chalk/builtin.cr b/src/chalk/builtin.cr index 568d58a..0f547ab 100644 --- a/src/chalk/builtin.cr +++ b/src/chalk/builtin.cr @@ -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 diff --git a/src/chalk/codegen.cr b/src/chalk/codegen.cr index f18ee7a..f860b0c 100644 --- a/src/chalk/codegen.cr +++ b/src/chalk/codegen.cr @@ -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 diff --git a/src/chalk/compiler.cr b/src/chalk/compiler.cr index b4783be..99b1ae2 100644 --- a/src/chalk/compiler.cr +++ b/src/chalk/compiler.cr @@ -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 diff --git a/src/chalk/config.cr b/src/chalk/config.cr index 15f812f..b9d6aae 100644 --- a/src/chalk/config.cr +++ b/src/chalk/config.cr @@ -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 diff --git a/src/chalk/constant_folder.cr b/src/chalk/constant_folder.cr index f5dc229..e529c02 100644 --- a/src/chalk/constant_folder.cr +++ b/src/chalk/constant_folder.cr @@ -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 diff --git a/src/chalk/emitter.cr b/src/chalk/emitter.cr index 1d3cdf4..dfcd777 100644 --- a/src/chalk/emitter.cr +++ b/src/chalk/emitter.cr @@ -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 diff --git a/src/chalk/function_finder.cr b/src/chalk/function_finder.cr index 7683396..0c47753 100644 --- a/src/chalk/function_finder.cr +++ b/src/chalk/function_finder.cr @@ -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 diff --git a/src/chalk/inline.cr b/src/chalk/inline.cr index 62f6e68..e058d0a 100644 --- a/src/chalk/inline.cr +++ b/src/chalk/inline.cr @@ -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 diff --git a/src/chalk/ir.cr b/src/chalk/ir.cr index e7258d2..ec82686 100644 --- a/src/chalk/ir.cr +++ b/src/chalk/ir.cr @@ -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" - @left.to_s(16, io) - io << " R" - @right.to_s(16, io) + class CallInstruction < Instruction + property name : String + + def initialize(@name) + end + + def to_s(io) + io << "call " << @name + end + + def to_bin(i, index) + return 0x2000 | (i.table[name]?.as(Compiler::FunctionEntry).addr * 2 + 0x200) + end end - def to_bin(i, index) - return 0x5000 | (@left << 8) | (@right << 4) - end - end + class SetIStackInstruction < Instruction + def to_s(io) + io << "setis" + end - class SkipRegNeInstruction < Instruction - property left : Int32 - property right : Int32 - - def initialize(@left, @right) + def to_bin(i, index) + return 0xa000 | (i.stack * 2 + 0x200) + end end - def to_s(io) - io << "sner R" - @left.to_s(16, io) - io << " R" - @right.to_s(16, io) + class AddIRegInstruction < Instruction + property reg : Int32 + + def initialize(@reg) + end + + def to_s(io) + io << "addi R" + reg.to_s(16, io) + end + + def to_bin(i, index) + return 0xf000 | (@reg << 8) | 0x1e + end end - def to_bin(i, index) - return 0x9000 | (@left << 8) | (@right << 4) - end - end + class DrawInstruction < Instruction + property x : Int32 + property y : Int32 + property height : Int32 - class CallInstruction < Instruction - property name : String + def initialize(@x, @y, @height) + end - def initialize(@name) + def to_s(io) + io << "draw R" + x.to_s(16, io) + io << " R" + y.to_s(16, io) + io << " " << height + end + + def to_bin(i, index) + return 0xd000 | (@x << 8) | (@y << 4) | height + end end - def to_s(io) - io << "call " << @name + class AwaitKeyInstruction < Instruction + property into : Int32 + + def initialize(@into) + end + + def to_s(io) + io << "getk R" + @into.to_s(16, io) + end + + def to_bin(i, index) + return 0xf00a | (@into << 8) + end end - def to_bin(i, index) - return 0x2000 | (i.table[name]?.as(FunctionEntry).addr * 2 + 0x200) - end - end + class GetFontInstruction < Instruction + property from : Int32 - class SetIStackInstruction < Instruction - def to_s(io) - io << "setis" + def initialize(@from) + end + + def to_s(io) + io << "font R" + @from.to_s(16, io) + end + + def to_bin(i, index) + return 0xf029 | (@from << 8) + end end - def to_bin(i, index) - return 0xa000 | (i.stack * 2 + 0x200) - end - end + class SetDelayTimerInstruction < Instruction + property from : Int32 - class AddIRegInstruction < Instruction - property reg : Int32 + def initialize(@from) + end - def initialize(@reg) + def to_s(io) + io << "set_delay R" + @from.to_s(16, io) + end + + def to_bin(i, index) + return 0xf015 | (@from << 8) + end end - def to_s(io) - io << "addi R" - reg.to_s(16, io) - end + class GetDelayTimerInstruction < Instruction + property into : Int32 - def to_bin(i, index) - return 0xf000 | (@reg << 8) | 0x1e - end - end + def initialize(@into) + end - class DrawInstruction < Instruction - property x : Int32 - property y : Int32 - property height : Int32 + def to_s(io) + io << "get_delay R" + @into.to_s(16, io) + end - def initialize(@x, @y, @height) - end - - def to_s(io) - io << "draw R" - x.to_s(16, io) - io << " R" - y.to_s(16, io) - io << " " << height - end - - def to_bin(i, index) - return 0xd000 | (@x << 8) | (@y << 4) | height - end - end - - class AwaitKeyInstruction < Instruction - property into : Int32 - - def initialize(@into) - end - - def to_s(io) - io << "getk R" - @into.to_s(16, io) - end - - def to_bin(i, index) - return 0xf00a | (@into << 8) - end - end - - class GetFontInstruction < Instruction - property from : Int32 - - def initialize(@from) - end - - def to_s(io) - io << "font R" - @from.to_s(16, io) - end - - def to_bin(i, index) - return 0xf029 | (@from << 8) - end - end - - class SetDelayTimerInstruction < Instruction - property from : Int32 - - def initialize(@from) - end - - def to_s(io) - io << "set_delay R" - @from.to_s(16, io) - end - - def to_bin(i, index) - return 0xf015 | (@from << 8) - end - end - - class GetDelayTimerInstruction < Instruction - property into : Int32 - - def initialize(@into) - end - - def to_s(io) - io << "get_delay R" - @into.to_s(16, io) - end - - def to_bin(i, index) - return 0xf007 | (@into << 8) + def to_bin(i, index) + return 0xf007 | (@into << 8) + end end end end diff --git a/src/chalk/lexer.cr b/src/chalk/lexer.cr index 0d3124c..b632282 100644 --- a/src/chalk/lexer.cr +++ b/src/chalk/lexer.cr @@ -1,82 +1,84 @@ require "lex" module Chalk - enum TokenType - Any, - Str, - Id, - LitDec, - LitBin, - LitHex, - OpAdd - OpSub - OpMul - OpDiv - OpOr - OpAnd - OpXor - KwSprite - KwInline - KwFun - KwU0 - KwU8 - KwU12 - KwVar - KwIf - KwElse - KwWhile - KwReturn - end - - class Token - def initialize(@string : String, @type : TokenType) + module Compiler + enum TokenType + Any, + Str, + Id, + LitDec, + LitBin, + LitHex, + OpAdd + OpSub + OpMul + OpDiv + OpOr + OpAnd + OpXor + KwSprite + KwInline + KwFun + KwU0 + KwU8 + KwU12 + KwVar + KwIf + KwElse + KwWhile + KwReturn end - getter string : String - getter type : TokenType - end + class Token + def initialize(@string : String, @type : TokenType) + end - class Lexer - def initialize - @lexer = Lex::Lexer.new - @lexer.add_pattern(".", TokenType::Any.value) - @lexer.add_pattern("\"(\\\\\"|[^\"])*\"", - TokenType::Str.value) - @lexer.add_pattern("[a-zA-Z_][a-zA-Z_0-9]*", - TokenType::Id.value) - @lexer.add_pattern("[0-9]+", - TokenType::LitDec.value) - @lexer.add_pattern("0b[0-1]+", - TokenType::LitBin.value) - @lexer.add_pattern("0x[0-9a-fA-F]+", - TokenType::LitHex.value) - @lexer.add_pattern("\\+", TokenType::OpAdd.value) - @lexer.add_pattern("-", TokenType::OpSub.value) - @lexer.add_pattern("\\*", TokenType::OpMul.value) - @lexer.add_pattern("/", TokenType::OpDiv.value) - @lexer.add_pattern("&", TokenType::OpAdd.value) - @lexer.add_pattern("\\|", TokenType::OpOr.value) - @lexer.add_pattern("^", TokenType::OpXor.value) - @lexer.add_pattern("sprite", TokenType::KwSprite.value) - @lexer.add_pattern("inline", TokenType::KwInline.value) - @lexer.add_pattern("fun", TokenType::KwFun.value) - @lexer.add_pattern("u0", TokenType::KwU0.value) - @lexer.add_pattern("u8", TokenType::KwU8.value) - @lexer.add_pattern("u12", TokenType::KwU12.value) - @lexer.add_pattern("var", TokenType::KwVar.value) - @lexer.add_pattern("if", TokenType::KwIf.value) - @lexer.add_pattern("else", TokenType::KwElse.value) - @lexer.add_pattern("while", TokenType::KwWhile.value) - @lexer.add_pattern("return", TokenType::KwReturn.value) + getter string : String + getter type : TokenType end - def lex(string) - return @lexer.lex(string) - .select { |t| !t[0][0].whitespace? } - .map do |tuple| - string, id = tuple - Token.new(string, TokenType.new(id)) - end + class Lexer + def initialize + @lexer = Lex::Lexer.new + @lexer.add_pattern(".", TokenType::Any.value) + @lexer.add_pattern("\"(\\\\\"|[^\"])*\"", + TokenType::Str.value) + @lexer.add_pattern("[a-zA-Z_][a-zA-Z_0-9]*", + TokenType::Id.value) + @lexer.add_pattern("[0-9]+", + TokenType::LitDec.value) + @lexer.add_pattern("0b[0-1]+", + TokenType::LitBin.value) + @lexer.add_pattern("0x[0-9a-fA-F]+", + TokenType::LitHex.value) + @lexer.add_pattern("\\+", TokenType::OpAdd.value) + @lexer.add_pattern("-", TokenType::OpSub.value) + @lexer.add_pattern("\\*", TokenType::OpMul.value) + @lexer.add_pattern("/", TokenType::OpDiv.value) + @lexer.add_pattern("&", TokenType::OpAdd.value) + @lexer.add_pattern("\\|", TokenType::OpOr.value) + @lexer.add_pattern("^", TokenType::OpXor.value) + @lexer.add_pattern("sprite", TokenType::KwSprite.value) + @lexer.add_pattern("inline", TokenType::KwInline.value) + @lexer.add_pattern("fun", TokenType::KwFun.value) + @lexer.add_pattern("u0", TokenType::KwU0.value) + @lexer.add_pattern("u8", TokenType::KwU8.value) + @lexer.add_pattern("u12", TokenType::KwU12.value) + @lexer.add_pattern("var", TokenType::KwVar.value) + @lexer.add_pattern("if", TokenType::KwIf.value) + @lexer.add_pattern("else", TokenType::KwElse.value) + @lexer.add_pattern("while", TokenType::KwWhile.value) + @lexer.add_pattern("return", TokenType::KwReturn.value) + end + + def lex(string) + return @lexer.lex(string) + .select { |t| !t[0][0].whitespace? } + .map do |tuple| + string, id = tuple + Token.new(string, TokenType.new(id)) + end + end end end end diff --git a/src/chalk/optimizer.cr b/src/chalk/optimizer.cr index cfb8129..5eab38f 100644 --- a/src/chalk/optimizer.cr +++ b/src/chalk/optimizer.cr @@ -1,55 +1,57 @@ module Chalk - class Optimizer - private def check_dead(inst) - if inst.is_a?(LoadRegInstruction) - return inst.from == inst.into - end - return false - end - - private def optimize!(instructions, range, deletions, deletions_at) - range.each do |index| - if check_dead(instructions[index]) - deletions << index + module Compiler + class Optimizer + private def check_dead(inst) + if inst.is_a?(Ir::LoadRegInstruction) + return inst.from == inst.into end - deletions_at[index] = deletions.size + return false end - end - def optimize(instructions) - instructions = instructions.dup - block_boundaries = [instructions.size] - instructions.each_with_index do |inst, i| - if inst.is_a?(JumpRelativeInstruction) - block_boundaries << i - block_boundaries << (inst.offset + i) + private def optimize!(instructions, range, deletions, deletions_at) + range.each do |index| + if check_dead(instructions[index]) + deletions << index + end + deletions_at[index] = deletions.size end end - block_boundaries.uniq!.sort! - previous = 0 - deletions = [] of Int32 - deletions_at = {} of Int32 => Int32 - block_boundaries.each do |boundary| - range = previous...boundary - optimize!(instructions, range, deletions, deletions_at) - previous = boundary + def optimize(instructions) + instructions = instructions.dup + block_boundaries = [instructions.size] + instructions.each_with_index do |inst, i| + if inst.is_a?(Ir::JumpRelativeInstruction) + block_boundaries << i + block_boundaries << (inst.offset + i) + end + end + block_boundaries.uniq!.sort! + + previous = 0 + deletions = [] of Int32 + deletions_at = {} of Int32 => Int32 + block_boundaries.each do |boundary| + range = previous...boundary + optimize!(instructions, range, deletions, deletions_at) + previous = boundary + end + + instructions.each_with_index do |inst, i| + next if !inst.is_a?(Ir::JumpRelativeInstruction) + jump_to = inst.offset + i + next unless deletions_at[jump_to]? + deletions_offset = deletions_at[i] - deletions_at[jump_to] + inst.offset += deletions_offset + end + + deletions.reverse! + deletions.each do |i| + instructions.delete_at i + end + + return instructions end - - instructions.each_with_index do |inst, i| - next if !inst.is_a?(JumpRelativeInstruction) - jump_to = inst.offset + i - next unless deletions_at[jump_to]? - deletions_offset = deletions_at[i] - deletions_at[jump_to] - inst.offset += deletions_offset - end - - deletions.reverse! - deletions.each do |i| - instructions.delete_at i - end - - return instructions end end end diff --git a/src/chalk/parser.cr b/src/chalk/parser.cr index 2d78bdb..260c572 100644 --- a/src/chalk/parser.cr +++ b/src/chalk/parser.cr @@ -1,173 +1,175 @@ require "./parser_builder.cr" module Chalk - class Parser - include ParserBuilder + module ParserCombinators + class Parser + include ParserBuilder - private def create_type - either(type(TokenType::KwU0), type(TokenType::KwU8), type(TokenType::KwU12)) - end - - private def create_lit - dec_parser = type(TokenType::LitDec).transform &.string.to_i64 - hex_parser = type(TokenType::LitHex).transform &.string.lchop("0x").to_i64(16) - bin_parser = type(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) } - return lit_parser - end - - private def create_op_expr(atom, op) - pl = PlaceholderParser(Tree).new - recurse = atom.then(op).then(pl).transform do |arr| - arr = arr.flatten - TreeOp.new( - arr[1].as(Token).type, - arr[0].as(Tree), - arr[2].as(Tree)).as(Tree) + private def create_type + either(type(Compiler::TokenType::KwU0), type(Compiler::TokenType::KwU8), type(Compiler::TokenType::KwU12)) end - pl.parser = either(recurse, atom) - return pl - end - private def create_op_exprs(atom, ops) - ops.reduce(atom) do |previous, current| - create_op_expr(previous, current) + private def create_lit + dec_parser = type(Compiler::TokenType::LitDec).transform &.string.to_i64 + hex_parser = type(Compiler::TokenType::LitHex).transform &.string.lchop("0x").to_i64(16) + bin_parser = type(Compiler::TokenType::LitBin).transform &.string.lchop("0b").to_i64(2) + lit_parser = either(dec_parser, hex_parser, bin_parser).transform { |it| Trees::TreeLit.new(it).as(Trees::Tree) } + return lit_parser end - end - private def create_call(expr) - call = type(TokenType::Id).then(char '(').then(delimited(expr, char ',')).then(char ')').transform do |arr| - arr = arr.flatten - name = arr[0].as(Token).string - params = arr[2..arr.size - 2].map &.as(Tree) - TreeCall.new(name, params).as(Tree) - end - return call - end - - private def create_expr - expr_place = PlaceholderParser(Tree).new - literal = create_lit - id = type(TokenType::Id).transform { |it| TreeId.new(it.string).as(Tree) } - call = create_call(expr_place) - atom = either(literal, call, id) - - ops = [either(type(TokenType::OpMul), type(TokenType::OpDiv)), - either(type(TokenType::OpAdd), type(TokenType::OpSub)), - type(TokenType::OpXor), - type(TokenType::OpAnd), - type(TokenType::OpOr)] - expr = create_op_exprs(atom, ops) - expr_place.parser = expr - - return expr - end - - private def create_var(expr) - var = type(TokenType::KwVar).then(type(TokenType::Id)).then(char '=').then(expr).then(char ';').transform do |arr| - arr = arr.flatten - name = arr[1].as(Token).string - exp = arr[arr.size - 2].as(Tree) - TreeVar.new(name, exp).as(Tree) - end - return var - end - - private def create_assign(expr) - assign = type(TokenType::Id).then(char '=').then(expr).then(char ';').transform do |arr| - arr = arr.flatten - name = arr[0].as(Token).string - exp = arr[arr.size - 2].as(Tree) - TreeAssign.new(name, exp).as(Tree) - end - return assign - end - - private def create_basic(expr) - basic = expr.then(char ';').transform do |arr| - arr.flatten[0].as(Tree) - end - return basic - end - - private def create_if(expr, block) - iff = type(TokenType::KwIf).then(char '(').then(expr).then(char ')').then(block) - .then(optional(type(TokenType::KwElse).then(block))) - .transform do |arr| + private def create_op_expr(atom, op) + pl = PlaceholderParser(Trees::Tree).new + recurse = atom.then(op).then(pl).transform do |arr| arr = arr.flatten - cond = arr[2].as(Tree) - code = arr[4].as(Tree) - otherwise = arr.size == 7 ? arr[6].as(Tree) : nil - TreeIf.new(cond, code, otherwise).as(Tree) + Trees::TreeOp.new( + arr[1].as(Compiler::Token).type, + arr[0].as(Trees::Tree), + arr[2].as(Trees::Tree)).as(Trees::Tree) end - return iff - end - - private def create_while(expr, block) - whilee = type(TokenType::KwWhile).then(char '(').then(expr).then(char ')').then(block).transform do |arr| - arr = arr.flatten - cond = arr[2].as(Tree) - code = arr[4].as(Tree) - TreeWhile.new(cond, code).as(Tree) + pl.parser = either(recurse, atom) + return pl end - return whilee - end - private def create_return(expr) - returnn = type(TokenType::KwReturn).then(expr).then(char ';').transform do |arr| - arr = arr.flatten - value = arr[1].as(Tree) - TreeReturn.new(value).as(Tree) + private def create_op_exprs(atom, ops) + ops.reduce(atom) do |previous, current| + create_op_expr(previous, current) + end end - return returnn - end - private def create_block(statement) - block = char('{').then(many(statement)).then(char '}').transform do |arr| - arr = arr.flatten - params = arr[1..arr.size - 2].map &.as(Tree) - TreeBlock.new(params).as(Tree) - end - return block - end - - private def create_statement_block - statement_place = PlaceholderParser(Tree).new - expr = create_expr - block = create_block(statement_place) - iff = create_if(expr, block) - whilee = create_while(expr, block) - returnn = create_return(expr) - var = create_var(expr) - assign = create_assign(expr) - basic = create_basic(expr) - statement = either(basic, var, assign, block, iff, whilee, returnn) - statement_place.parser = statement - return {statement, block} - end - - private def create_func(block, type) - func = type(TokenType::KwFun).then(type(TokenType::Id)) - .then(char '(').then(delimited(type(TokenType::Id), char ',')).then(char ')') - .then(char ':').then(type) - .then(block).transform do |arr| + private def create_call(expr) + call = type(Compiler::TokenType::Id).then(char '(').then(delimited(expr, char ',')).then(char ')').transform do |arr| arr = arr.flatten - name = arr[1].as(Token).string - params = arr[3..arr.size - 5].map &.as(Token).string - code = arr[arr.size - 1].as(Tree) - type = arr[arr.size - 2].as(Token).type - TreeFunction.new(name, params, code) + name = arr[0].as(Compiler::Token).string + params = arr[2..arr.size - 2].map &.as(Trees::Tree) + Trees::TreeCall.new(name, params).as(Trees::Tree) end - return func - end + return call + end - def initialize - _, block = create_statement_block - @parser = many(create_func(block, create_type)).as(BasicParser(Array(TreeFunction))) - end + private def create_expr + expr_place = PlaceholderParser(Trees::Tree).new + literal = create_lit + id = type(Compiler::TokenType::Id).transform { |it| Trees::TreeId.new(it.string).as(Trees::Tree) } + call = create_call(expr_place) + atom = either(literal, call, id) - def parse?(tokens) - return @parser.parse?(tokens, 0).try &.[0] + ops = [either(type(Compiler::TokenType::OpMul), type(Compiler::TokenType::OpDiv)), + either(type(Compiler::TokenType::OpAdd), type(Compiler::TokenType::OpSub)), + type(Compiler::TokenType::OpXor), + type(Compiler::TokenType::OpAnd), + type(Compiler::TokenType::OpOr)] + expr = create_op_exprs(atom, ops) + expr_place.parser = expr + + return expr + end + + private def create_var(expr) + var = type(Compiler::TokenType::KwVar).then(type(Compiler::TokenType::Id)).then(char '=').then(expr).then(char ';').transform do |arr| + arr = arr.flatten + name = arr[1].as(Compiler::Token).string + exp = arr[arr.size - 2].as(Trees::Tree) + Trees::TreeVar.new(name, exp).as(Trees::Tree) + end + return var + end + + private def create_assign(expr) + assign = type(Compiler::TokenType::Id).then(char '=').then(expr).then(char ';').transform do |arr| + arr = arr.flatten + name = arr[0].as(Compiler::Token).string + exp = arr[arr.size - 2].as(Trees::Tree) + Trees::TreeAssign.new(name, exp).as(Trees::Tree) + end + return assign + end + + private def create_basic(expr) + basic = expr.then(char ';').transform do |arr| + arr.flatten[0].as(Trees::Tree) + end + return basic + end + + private def create_if(expr, block) + iff = type(Compiler::TokenType::KwIf).then(char '(').then(expr).then(char ')').then(block) + .then(optional(type(Compiler::TokenType::KwElse).then(block))) + .transform do |arr| + arr = arr.flatten + cond = arr[2].as(Trees::Tree) + code = arr[4].as(Trees::Tree) + otherwise = arr.size == 7 ? arr[6].as(Trees::Tree) : nil + Trees::TreeIf.new(cond, code, otherwise).as(Trees::Tree) + end + return iff + end + + private def create_while(expr, block) + whilee = type(Compiler::TokenType::KwWhile).then(char '(').then(expr).then(char ')').then(block).transform do |arr| + arr = arr.flatten + cond = arr[2].as(Trees::Tree) + code = arr[4].as(Trees::Tree) + Trees::TreeWhile.new(cond, code).as(Trees::Tree) + end + return whilee + end + + private def create_return(expr) + returnn = type(Compiler::TokenType::KwReturn).then(expr).then(char ';').transform do |arr| + arr = arr.flatten + value = arr[1].as(Trees::Tree) + Trees::TreeReturn.new(value).as(Trees::Tree) + end + return returnn + end + + private def create_block(statement) + block = char('{').then(many(statement)).then(char '}').transform do |arr| + arr = arr.flatten + params = arr[1..arr.size - 2].map &.as(Trees::Tree) + Trees::TreeBlock.new(params).as(Trees::Tree) + end + return block + end + + private def create_statement_block + statement_place = PlaceholderParser(Trees::Tree).new + expr = create_expr + block = create_block(statement_place) + iff = create_if(expr, block) + whilee = create_while(expr, block) + returnn = create_return(expr) + var = create_var(expr) + assign = create_assign(expr) + basic = create_basic(expr) + statement = either(basic, var, assign, block, iff, whilee, returnn) + statement_place.parser = statement + return {statement, block} + end + + private def create_func(block, type) + func = type(Compiler::TokenType::KwFun).then(type(Compiler::TokenType::Id)) + .then(char '(').then(delimited(type(Compiler::TokenType::Id), char ',')).then(char ')') + .then(char ':').then(type) + .then(block).transform do |arr| + arr = arr.flatten + name = arr[1].as(Compiler::Token).string + params = arr[3..arr.size - 5].map &.as(Compiler::Token).string + code = arr[arr.size - 1].as(Trees::Tree) + type = arr[arr.size - 2].as(Compiler::Token).type + Trees::TreeFunction.new(name, params, code) + end + return func + end + + def initialize + _, block = create_statement_block + @parser = many(create_func(block, create_type)).as(BasicParser(Array(Trees::TreeFunction))) + end + + def parse?(tokens) + return @parser.parse?(tokens, 0).try &.[0] + end end end end diff --git a/src/chalk/parser_builder.cr b/src/chalk/parser_builder.cr index c91de47..afced3b 100644 --- a/src/chalk/parser_builder.cr +++ b/src/chalk/parser_builder.cr @@ -2,37 +2,39 @@ require "./lexer.cr" require "./parsers.cr" module Chalk - module ParserBuilder - def type(type) : BasicParser(Token) - return TypeParser.new(type).as(BasicParser(Token)) - end + module ParserCombinators + module ParserBuilder + def type(type) : BasicParser(Compiler::Token) + return TypeParser.new(type).as(BasicParser(Compiler::Token)) + end - def char(type) : BasicParser(Token) - return CharParser.new(type).as(BasicParser(Token)) - end + def char(type) : BasicParser(Compiler::Token) + return CharParser.new(type).as(BasicParser(Compiler::Token)) + end - def transform(parser : BasicParser(T), &transform : T -> R) forall T, R - return TransformParser.new(parser, &transform).as(BasicParser(R)) - end + def transform(parser : BasicParser(T), &transform : T -> R) forall T, R + return TransformParser.new(parser, &transform).as(BasicParser(R)) + end - def optional(parser : BasicParser(T)) : BasicParser(T?) forall T - return OptionalParser.new(parser).as(BasicParser(T?)) - end + def optional(parser : BasicParser(T)) : BasicParser(T?) forall T + return OptionalParser.new(parser).as(BasicParser(T?)) + end - def either(*args : BasicParser(T)) : BasicParser(T) forall T - return EitherParser.new(args.to_a).as(BasicParser(T)) - end + def either(*args : BasicParser(T)) : BasicParser(T) forall T + return EitherParser.new(args.to_a).as(BasicParser(T)) + end - def many(parser : BasicParser(T)) : BasicParser(Array(T)) forall T - return ManyParser.new(parser).as(BasicParser(Array(T))) - end + def many(parser : BasicParser(T)) : BasicParser(Array(T)) forall T + return ManyParser.new(parser).as(BasicParser(Array(T))) + end - def delimited(parser : BasicParser(T), delimiter : BasicParser(R)) : BasicParser(Array(T)) forall T, R - return DelimitedParser.new(parser, delimiter).as(BasicParser(Array(T))) - end + def delimited(parser : BasicParser(T), delimiter : BasicParser(R)) : BasicParser(Array(T)) forall T, R + return DelimitedParser.new(parser, delimiter).as(BasicParser(Array(T))) + end - def then(first : BasicParser(T), second : BasicParser(R)) forall T, R - return NextParser.new(first, second).as(BasicParser(Array(T | R))) + def then(first : BasicParser(T), second : BasicParser(R)) forall T, R + return NextParser.new(first, second).as(BasicParser(Array(T | R))) + end end end end diff --git a/src/chalk/parsers.cr b/src/chalk/parsers.cr index fbb6f9f..230d08f 100644 --- a/src/chalk/parsers.cr +++ b/src/chalk/parsers.cr @@ -1,146 +1,148 @@ module Chalk - abstract class BasicParser(T) - abstract def parse?(tokens : Array(Token), - index : Int64) : Tuple(T, Int64)? + module ParserCombinators + abstract class BasicParser(T) + abstract def parse?(tokens : Array(Compiler::Token), + index : Int64) : Tuple(T, Int64)? - def parse(tokens, index) - return parse?(tokens, index).not_nil! - end - - def transform(&transform : T -> R) forall R - return TransformParser.new(self, &transform).as(BasicParser(R)) - end - - def then(other : BasicParser(R)) : BasicParser(Array(T | R)) forall R - return NextParser.new(self, other).as(BasicParser(Array(T | R))) - end - end - - class TypeParser < BasicParser(Token) - def initialize(@type : TokenType) - end - - def parse?(tokens, index) - return nil unless index < tokens.size - return nil unless tokens[index].type == @type - return {tokens[index], index + 1} - end - end - - class CharParser < BasicParser(Token) - def initialize(@char : Char) - end - - def parse?(tokens, index) - return nil unless index < tokens.size - return nil unless (tokens[index].type == TokenType::Any) && - tokens[index].string[0] == @char - return {tokens[index], index + 1} - end - end - - class TransformParser(T, R) < BasicParser(R) - def initialize(@parser : BasicParser(T), &@block : T -> R) - end - - def parse?(tokens, index) - if parsed = @parser.parse?(tokens, index) - return {@block.call(parsed[0]), parsed[1]} + def parse(tokens, index) + return parse?(tokens, index).not_nil! end - return nil - end - end - class OptionalParser(T) < BasicParser(T?) - def initialize(@parser : BasicParser(T)) - end - - def parse?(tokens, index) - if parsed = @parser.parse?(tokens, index) - return {parsed[0], parsed[1]} + def transform(&transform : T -> R) forall R + return TransformParser.new(self, &transform).as(BasicParser(R)) end - return {nil, index} - end - end - class EitherParser(T) < BasicParser(T) - def initialize(@parsers : Array(BasicParser(T))) + def then(other : BasicParser(R)) : BasicParser(Array(T | R)) forall R + return NextParser.new(self, other).as(BasicParser(Array(T | R))) + end end - def parse?(tokens, index) - @parsers.each do |parser| - if parsed = parser.parse?(tokens, index) - return parsed + class TypeParser < BasicParser(Compiler::Token) + def initialize(@type : Compiler::TokenType) + end + + def parse?(tokens, index) + return nil unless index < tokens.size + return nil unless tokens[index].type == @type + return {tokens[index], index + 1} + end + end + + class CharParser < BasicParser(Compiler::Token) + def initialize(@char : Char) + end + + def parse?(tokens, index) + return nil unless index < tokens.size + return nil unless (tokens[index].type == Compiler::TokenType::Any) && + tokens[index].string[0] == @char + return {tokens[index], index + 1} + end + end + + class TransformParser(T, R) < BasicParser(R) + def initialize(@parser : BasicParser(T), &@block : T -> R) + end + + def parse?(tokens, index) + if parsed = @parser.parse?(tokens, index) + return {@block.call(parsed[0]), parsed[1]} end + return nil end - return nil - end - end - - class ManyParser(T) < BasicParser(Array(T)) - def initialize(@parser : BasicParser(T)) end - def parse?(tokens, index) - many = [] of T - while parsed = @parser.parse?(tokens, index) - item, index = parsed - many << item - end - return {many, index} - end - end - - class DelimitedParser(T, R) < BasicParser(Array(T)) - def initialize(@parser : BasicParser(T), @delimiter : BasicParser(R)) - end - - def parse?(tokens, index) - array = [] of T - first = @parser.parse?(tokens, index) - return {array, index} unless first - first_value, index = first - array << first_value - while delimiter = @delimiter.parse?(tokens, index) - _, new_index = delimiter - new = @parser.parse?(tokens, new_index) - break unless new - new_value, index = new - array << new_value + class OptionalParser(T) < BasicParser(T?) + def initialize(@parser : BasicParser(T)) end - return {array, index} - end - end - - class NextParser(T, R) < BasicParser(Array(T | R)) - def initialize(@first : BasicParser(T), @second : BasicParser(R)) + def parse?(tokens, index) + if parsed = @parser.parse?(tokens, index) + return {parsed[0], parsed[1]} + end + return {nil, index} + end end - def parse?(tokens, index) - first = @first.parse?(tokens, index) - return nil unless first - first_value, index = first + class EitherParser(T) < BasicParser(T) + def initialize(@parsers : Array(BasicParser(T))) + end - second = @second.parse?(tokens, index) - return nil unless second - second_value, index = second - - array = Array(T | R).new - array << first_value << second_value - return {array, index} - end - end - - class PlaceholderParser(T) < BasicParser(T) - property parser : BasicParser(T)? - - def initialize - @parser = nil + def parse?(tokens, index) + @parsers.each do |parser| + if parsed = parser.parse?(tokens, index) + return parsed + end + end + return nil + end end - def parse?(tokens, index) - @parser.try &.parse?(tokens, index) + class ManyParser(T) < BasicParser(Array(T)) + def initialize(@parser : BasicParser(T)) + end + + def parse?(tokens, index) + many = [] of T + while parsed = @parser.parse?(tokens, index) + item, index = parsed + many << item + end + return {many, index} + end + end + + class DelimitedParser(T, R) < BasicParser(Array(T)) + def initialize(@parser : BasicParser(T), @delimiter : BasicParser(R)) + end + + def parse?(tokens, index) + array = [] of T + first = @parser.parse?(tokens, index) + return {array, index} unless first + first_value, index = first + array << first_value + while delimiter = @delimiter.parse?(tokens, index) + _, new_index = delimiter + new = @parser.parse?(tokens, new_index) + break unless new + new_value, index = new + array << new_value + end + + return {array, index} + end + end + + class NextParser(T, R) < BasicParser(Array(T | R)) + def initialize(@first : BasicParser(T), @second : BasicParser(R)) + end + + def parse?(tokens, index) + first = @first.parse?(tokens, index) + return nil unless first + first_value, index = first + + second = @second.parse?(tokens, index) + return nil unless second + second_value, index = second + + array = Array(T | R).new + array << first_value << second_value + return {array, index} + end + end + + class PlaceholderParser(T) < BasicParser(T) + property parser : BasicParser(T)? + + def initialize + @parser = nil + end + + def parse?(tokens, index) + @parser.try &.parse?(tokens, index) + end end end end diff --git a/src/chalk/print_visitor.cr b/src/chalk/print_visitor.cr index d21c13f..e8ea9d4 100644 --- a/src/chalk/print_visitor.cr +++ b/src/chalk/print_visitor.cr @@ -1,53 +1,54 @@ require "./tree.cr" module Chalk - class PrintVisitor < Visitor - def initialize(@stream : IO) - @indent = 0 - end - - def print_indent - @indent.times do - @stream << " " + module Trees + class PrintVisitor < Visitor + def initialize(@stream : IO) + @indent = 0 end - end - def visit(id : TreeId) - print_indent - @stream << id.id << "\n" - end - - def visit(lit : TreeLit) - print_indent - @stream << lit.lit << "\n" - end - - def visit(op : TreeOp) - print_indent - @stream << "[op] " - @stream << op.op << "\n" - @indent += 1 - end - - def finish(op : TreeOp) - @indent -= 1 - end - - def visit(function : TreeFunction) - print_indent - @stream << "[function] " << function.name << "( " - function.params.each do |param| - @stream << param << " " + def print_indent + @indent.times do + @stream << " " + end end - @stream << ")" << "\n" - @indent += 1 - end - def finish(function : TreeFunction) - @indent -= 1 - end + def visit(id : TreeId) + print_indent + @stream << id.id << "\n" + end - macro forward(text, type) + def visit(lit : TreeLit) + print_indent + @stream << lit.lit << "\n" + end + + def visit(op : TreeOp) + print_indent + @stream << "[op] " + @stream << op.op << "\n" + @indent += 1 + end + + def finish(op : TreeOp) + @indent -= 1 + end + + def visit(function : TreeFunction) + print_indent + @stream << "[function] " << function.name << "( " + function.params.each do |param| + @stream << param << " " + end + @stream << ")" << "\n" + @indent += 1 + end + + def finish(function : TreeFunction) + @indent -= 1 + end + + macro forward(text, type) def visit(tree : {{type}}) print_indent @stream << {{text}} << "\n" @@ -59,18 +60,19 @@ module Chalk end end - forward("[call]", TreeCall) - forward("[block]", TreeBlock) - forward("[var]", TreeVar) - forward("[assign]", TreeAssign) - forward("[if]", TreeIf) - forward("[while]", TreeWhile) - forward("[return]", TreeReturn) - end + forward("[call]", TreeCall) + forward("[block]", TreeBlock) + forward("[var]", TreeVar) + forward("[assign]", TreeAssign) + forward("[if]", TreeIf) + forward("[while]", TreeWhile) + forward("[return]", TreeReturn) + end - class Tree - def to_s(io) - accept(PrintVisitor.new io) + class Tree + def to_s(io) + accept(PrintVisitor.new io) + end end end end diff --git a/src/chalk/table.cr b/src/chalk/table.cr index 7d3492e..1ccb62c 100644 --- a/src/chalk/table.cr +++ b/src/chalk/table.cr @@ -1,51 +1,53 @@ module Chalk - class Entry - end - - class FunctionEntry < Entry - property function : TreeFunction | BuiltinFunction | InlineFunction - property addr : Int32 - - def initialize(@function, @addr = -1) + module Compiler + class Entry end - def to_s(io) - io << "[function]" - end - end + class FunctionEntry < Entry + property function : Trees::TreeFunction | Builtin::BuiltinFunction | Builtin::InlineFunction + property addr : Int32 - class VarEntry < Entry - property register : Int32 - - def initialize(@register) - end - - def to_s(io) - io << "[variable] " << "(R" << @register.to_s(16) << ")" - end - end - - class Table - property parent : Table? - - def initialize(@parent = nil) - @data = {} of String => Entry - end - - def []?(key) - if entry = @data[key]? - return entry + def initialize(@function, @addr = -1) + end + + def to_s(io) + io << "[function]" end - return @parent.try &.[key]? end - def []=(key, entry) - @data[key] = entry + class VarEntry < Entry + property register : Int32 + + def initialize(@register) + end + + def to_s(io) + io << "[variable] " << "(R" << @register.to_s(16) << ")" + end end - def to_s(io) - @parent.try &.to_s(io) - io << @data.map { |k, v| k + ": " + v.to_s }.join("\n") + class Table + property parent : Table? + + def initialize(@parent = nil) + @data = {} of String => Entry + end + + def []?(key) + 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 end diff --git a/src/chalk/tree.cr b/src/chalk/tree.cr index 250eaf9..c4f285d 100644 --- a/src/chalk/tree.cr +++ b/src/chalk/tree.cr @@ -1,228 +1,230 @@ module Chalk - class Visitor - def visit(tree) - end - - def finish(tree) - end - end - - class Transformer - def transform(tree) - return tree - end - end - - class Tree - def accept(v) - v.visit(self) - v.finish(self) - end - - def apply(t) - return t.transform(self) - end - end - - class TreeId < Tree - property id : String - - def initialize(@id) - end - end - - class TreeLit < Tree - property lit : Int64 - - def initialize(@lit) - end - end - - class TreeCall < Tree - property name : String - property params : Array(Tree) - - def initialize(@name, @params) - end - - def accept(v) - v.visit(self) - @params.each &.accept(v) - v.finish(self) - end - - def apply(t) - @params.map! do |param| - param.apply(t) + module Trees + class Visitor + def visit(tree) end - return t.transform(self) - end - end - class TreeOp < Tree - property op : TokenType - property left : Tree - property right : Tree - - def initialize(@op, @left, @right) - end - - def accept(v) - v.visit(self) - @left.accept(v) - @right.accept(v) - v.finish(self) - end - - def apply(t) - @left = @left.apply(t) - @right = @right.apply(t) - return t.transform(self) - end - end - - class TreeBlock < Tree - property children : Array(Tree) - - def initialize(@children) - end - - def accept(v) - v.visit(self) - @children.each &.accept(v) - v.finish(self) - end - - def apply(t) - @children.map! do |child| - child.apply(t) + def finish(tree) end - return t.transform(self) - end - end - - class TreeFunction < Tree - property name : String - property params : Array(String) - property block : Tree - - def initialize(@name, @params, @block) end - def param_count - return @params.size + class Transformer + def transform(tree) + return tree + end end - def accept(v) - v.visit(self) - @block.accept(v) - v.finish(self) + class Tree + def accept(v) + v.visit(self) + v.finish(self) + end + + def apply(t) + return t.transform(self) + end end - def apply(t) - @block = @block.apply(t) - return t.transform(self) - end - end + class TreeId < Tree + property id : String - class TreeVar < Tree - property name : String - property expr : Tree - - def initialize(@name, @expr) + def initialize(@id) + end end - def accept(v) - v.visit(self) - @expr.accept(v) - v.finish(self) + class TreeLit < Tree + property lit : Int64 + + def initialize(@lit) + end end - def apply(t) - @expr = @expr.apply(t) - return t.transform(self) - end - end + class TreeCall < Tree + property name : String + property params : Array(Tree) - class TreeAssign < Tree - property name : String - property expr : Tree + def initialize(@name, @params) + end - def initialize(@name, @expr) + def accept(v) + v.visit(self) + @params.each &.accept(v) + v.finish(self) + end + + def apply(t) + @params.map! do |param| + param.apply(t) + end + return t.transform(self) + end end - def accept(v) - v.visit(self) - @expr.accept(v) - v.finish(self) + class TreeOp < Tree + property op : Compiler::TokenType + property left : Tree + property right : Tree + + def initialize(@op, @left, @right) + end + + def accept(v) + v.visit(self) + @left.accept(v) + @right.accept(v) + v.finish(self) + end + + def apply(t) + @left = @left.apply(t) + @right = @right.apply(t) + return t.transform(self) + end end - def apply(t) - @expr = @expr.apply(t) - return t.transform(self) - end - end + class TreeBlock < Tree + property children : Array(Tree) - class TreeIf < Tree - property condition : Tree - property block : Tree - property otherwise : Tree? + def initialize(@children) + end - def initialize(@condition, @block, @otherwise = nil) + def accept(v) + v.visit(self) + @children.each &.accept(v) + v.finish(self) + end + + def apply(t) + @children.map! do |child| + child.apply(t) + end + return t.transform(self) + end end - def accept(v) - v.visit(self) - @condition.accept(v) - @block.accept(v) - @otherwise.try &.accept(v) - v.finish(self) + class TreeFunction < Tree + property name : String + property params : Array(String) + property block : Tree + + def initialize(@name, @params, @block) + end + + def param_count + return @params.size + end + + def accept(v) + v.visit(self) + @block.accept(v) + v.finish(self) + end + + def apply(t) + @block = @block.apply(t) + return t.transform(self) + end end - def apply(t) - @condition = @condition.apply(t) - @block = @block.apply(t) - @otherwise = @otherwise.try &.apply(t) - return t.transform(self) - end - end + class TreeVar < Tree + property name : String + property expr : Tree - class TreeWhile < Tree - property condition : Tree - property block : Tree + def initialize(@name, @expr) + end - def initialize(@condition, @block) + def accept(v) + v.visit(self) + @expr.accept(v) + v.finish(self) + end + + def apply(t) + @expr = @expr.apply(t) + return t.transform(self) + end end - def accept(v) - v.visit(self) - @condition.accept(v) - @block.accept(v) - v.finish(self) + class TreeAssign < Tree + property name : String + property expr : Tree + + def initialize(@name, @expr) + end + + def accept(v) + v.visit(self) + @expr.accept(v) + v.finish(self) + end + + def apply(t) + @expr = @expr.apply(t) + return t.transform(self) + end end - def apply(t) - @condition = @condition.apply(t) - @block = @block.apply(t) - return t.transform(self) - end - end + class TreeIf < Tree + property condition : Tree + property block : Tree + property otherwise : Tree? - class TreeReturn < Tree - property rvalue : Tree + def initialize(@condition, @block, @otherwise = nil) + end - def initialize(@rvalue) + def accept(v) + v.visit(self) + @condition.accept(v) + @block.accept(v) + @otherwise.try &.accept(v) + v.finish(self) + end + + def apply(t) + @condition = @condition.apply(t) + @block = @block.apply(t) + @otherwise = @otherwise.try &.apply(t) + return t.transform(self) + end end - def accept(v) - v.visit(self) - @rvalue.accept(v) - v.finish(self) + class TreeWhile < Tree + property condition : Tree + property block : Tree + + def initialize(@condition, @block) + end + + def accept(v) + v.visit(self) + @condition.accept(v) + @block.accept(v) + v.finish(self) + end + + def apply(t) + @condition = @condition.apply(t) + @block = @block.apply(t) + return t.transform(self) + end end - def apply(t) - @rvalue = @rvalue.apply(t) - return t.transform(self) + class TreeReturn < Tree + property rvalue : Tree + + def initialize(@rvalue) + end + + def accept(v) + v.visit(self) + @rvalue.accept(v) + v.finish(self) + end + + def apply(t) + @rvalue = @rvalue.apply(t) + return t.transform(self) + end end end end