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"
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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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