Compare commits

..

16 Commits

25 changed files with 510 additions and 62 deletions

View File

@@ -1,22 +1,64 @@
# chalk # chalk
TODO: Write a description here Chalk is a basic compiler from a toy language into CHIP-8 bytecode.
It supports some higher-level constructs like looks and if-statements,
and has some higher-level functions for drawing numbers and sprites.
## Installation ## Installation
TODO: Write installation instructions here To compile chalk, simply run
```Bash
crystal build --release src/chalk.cr
```
## Usage ## Usage
### Syntax
Chalk has minimal syntax. All code goes into functions, which are declared as follows:
```
fun function(param, another_param): return_type {
TODO: Write usage instructions here }
```
All parameters are assumed to be of type `u8`, an unsigned byte.
The return type can be either `u8` or `u0`, the latter being the same as `void` in C.
Users can declare variables using the `var` keyword:
```
var name = 5;
```
Assigning to existing variables is performed as such:
```
name = 6;
```
It's possible to call another function, which uses the CHIP-8 stack. This stack
is used exclusively for function calls, and may not exceed 12, as per the specifications
of the CHIP-8 virtual machine.
```
var result = double(name);
```
Additionally, to ensure that user-defined variables stored
in registers are not modified, chalk uses its own custom stack, placed at the end of the program.
This stack is accessed by modifying a stack pointer register, which holds the offset from the beginning of the stack. Because CHIP-8 registers can only hold up 8-bit unsinged numbers, the stack is therefore limited to 254 items.
## Development If statements take "truthy" values to be those that have a nonzero value. As such,
`if (0) {}` will never run, while `while(5) {}` will never terminate. Input can be awaited
using the `get_key` function, which returns the number of the next key pressed.
TODO: Write development instructions here Sprites can be declared using a quasi-visual syntax:
```
sprite dum [
` x x `
` x x `
` x x `
` `
`x x`
` xxxxxx `
]
```
These sprites can then be used in calls to `draw_sprite(sprite, x, y)`.
## Contributing ## Contributing
1. Fork it (<https://github.com/your-github-user/chalk/fork>) 1. Fork it (<https://github.com/DanilaFe/chalk/fork>)
2. Create your feature branch (`git checkout -b my-new-feature`) 2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`) 3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`) 4. Push to the branch (`git push origin my-new-feature`)
@@ -24,4 +66,4 @@ TODO: Write development instructions here
## Contributors ## Contributors
- [your-github-user](https://github.com/your-github-user) Danila Fedorin - creator, maintainer - [DanilaFe](https://github.com/DanilaFe) Danila Fedorin - creator, maintainer

View File

@@ -0,0 +1,7 @@
fun main(): u0 {
1+2+3;
var a = 5+5;
var b = a;
var a = a + b;
var c = a - b;
}

21
programs/basic_call.chalk Normal file
View File

@@ -0,0 +1,21 @@
fun double(a): u8 {
return a + a;
}
fun quadruple(a): u8 {
return double(double(a));
}
fun reuse(): u0 {
var one = 2;
var two = 4;
var three = 6;
}
fun main(): u0 {
var a = quadruple(4);
var one = 1;
var two = 2;
var three = 3;
reuse();
}

View File

@@ -0,0 +1,3 @@
fun main(): u0 {
draw_number(69, 1, 1);
}

View File

@@ -0,0 +1,3 @@
fun main(): u0 {
}

17
programs/basic_if.chalk Normal file
View File

@@ -0,0 +1,17 @@
fun main(): u0 {
var a = 3+3;
if(a) {
a = 0;
}
var b = 0;
if(b) {
b = 4;
}
if(b) {
b = 4;
} else {
b = 5;
}
}

View File

@@ -0,0 +1,4 @@
fun main(): u0 {
var a = get_key();
var b = get_key();
}

View File

@@ -0,0 +1,12 @@
sprite dum [
` x x `
` x x `
` x x `
` `
`x x`
` xxxxxx `
]
fun main(): u0 {
draw_sprite(dum, 0, 0);
}

View File

@@ -0,0 +1,8 @@
fun main(): u0 {
var a = 5;
var b = 0;
while(a) {
b = b + 2;
a = a - 1;
}
}

14
programs/comb_fib.chalk Normal file
View File

@@ -0,0 +1,14 @@
fun main(): u0 {
var a = 1;
var b = 1;
while(233 - a) {
draw_number(a, 24, 13);
set_delay(30);
var temp = a;
a = b;
b = b + temp;
while(get_delay()){}
draw_number(temp, 24, 13);
}
draw_number(a, 24, 13);
}

13
programs/comb_input.chalk Normal file
View File

@@ -0,0 +1,13 @@
fun main(): u0 {
var a = 1;
var b = 0;
var previous = 0;
draw_number(0, 24, 13);
while(a) {
previous = b;
b = get_key();
draw_number(previous, 24, 13);
draw_number(b, 24, 13);
}
}

View File

@@ -0,0 +1,39 @@
sprite dum [
` x x `
` x x `
` x x `
` `
`x x`
` xxxxxx `
]
fun main(): u8 {
var x = 0;
var y = 5;
var vy = 0;
var ay = 1;
var mode = 0;
while(1) {
draw_sprite(dum, x, y);
set_delay(1);
while(get_delay()){}
draw_sprite(dum, x, y);
if(5 - y) {
} else {
mode = 0;
}
if(26 - y) {
} else {
mode = 1;
}
if(mode) {
vy = vy - ay;
y = y - vy;
} else {
y = y + vy;
vy = vy + ay;
}
}
}

View File

@@ -0,0 +1,34 @@
sprite dum [
` x x `
` x x `
` x x `
` `
`x x`
` xxxxxx `
]
fun main(): u8 {
var x = 0;
var y = 27;
var vx = 1;
var vy = 0;
var a = 1;
while(1) {
var draw_y = 32 - y;
draw_sprite(dum, x, draw_y);
set_delay(1);
while(get_delay()){}
vy = vy - a;
y = y + vy;
x = x + vx;
if(6-y) {
} else {
var vtemp = vy;
vy = 0 - vtemp;
vy = vy + 1;
}
clear();
}
}

View File

@@ -19,7 +19,7 @@ module Chalk
@table = Table.new table @table = Table.new table
@function.params.each do |param| @function.params.each do |param|
@table[param] = VarEntry.new @registers @table.set_var param, VarEntry.new @registers
@registers += 1 @registers += 1
end end
end end
@@ -74,9 +74,8 @@ module Chalk
def generate!(tree, table, target, free) def generate!(tree, table, target, free)
case tree case tree
when Trees::TreeId when Trees::TreeId
entry = table[tree.id]? entry = table.get_var? tree.id
raise "Unknown variable" unless entry && raise "Unknown variable" unless entry
entry.is_a?(VarEntry)
loadr target, entry.register loadr target, entry.register
when Trees::TreeLit when Trees::TreeLit
load target, tree.lit load target, tree.lit
@@ -85,28 +84,26 @@ module Chalk
generate! tree.right, table, free, free + 1 generate! tree.right, table, free, free + 1
opr tree.op, target, free opr tree.op, target, free
when Trees::TreeCall when Trees::TreeCall
entry = table[tree.name]?.not_nil! entry = table.get_function?(tree.name).not_nil!
raise "Unknown function" unless entry.is_a?(FunctionEntry) raise "Unknown function" unless entry.is_a?(FunctionEntry)
generate! tree, entry.function, table, target, free generate! tree, entry.function, table, target, free
when Trees::TreeBlock when Trees::TreeBlock
table = Table.new(table) table = Table.new(table)
tree.children.each do |child| tree.children.each do |child|
free += generate! child, table, free, free + 1 free += generate! child, table, THROWAWAY_REG, free
end end
when Trees::TreeVar when Trees::TreeVar
entry = table[tree.name]? entry = table.get_var? tree.name
if entry == nil if entry == nil
entry = VarEntry.new free entry = VarEntry.new free
free += 1 free += 1
table[tree.name] = entry table.set_var tree.name, entry
end end
raise "Unknown variable" unless entry.is_a?(VarEntry) generate! tree.expr, table, entry.as(VarEntry).register, free
generate! tree.expr, table, entry.register, free
return 1 return 1
when Trees::TreeAssign when Trees::TreeAssign
entry = table[tree.name]? entry = table.get_var? tree.name
raise "Unknown variable" unless entry && raise "Unknown variable" unless entry
entry.is_a?(VarEntry)
generate! tree.expr, table, entry.register, free generate! tree.expr, table, entry.register, free
when Trees::TreeIf when Trees::TreeIf
generate! tree.condition, table, free, free + 1 generate! tree.condition, table, free, free + 1
@@ -134,7 +131,8 @@ module Chalk
cond_jump.offset = @instructions.size - old_size + 1 cond_jump.offset = @instructions.size - old_size + 1
after_jump.offset = before_cond - instructions.size + 1 after_jump.offset = before_cond - instructions.size + 1
when Trees::TreeReturn when Trees::TreeReturn
generate! tree.rvalue, table, RETURN_REG, free generate! tree.rvalue, table, free, free + 1
loadr RETURN_REG, free
ret ret
end end
return 0 return 0
@@ -142,7 +140,7 @@ module Chalk
# Generates code for the function that was given to it. # Generates code for the function that was given to it.
def generate! def generate!
generate!(@function.block, @table, -1, @registers) generate!(@function.block, @table, 0, @registers)
return @instructions return @instructions
end end
end end

View File

@@ -33,8 +33,9 @@ module Chalk
@logger.debug("Beginning constant folding") @logger.debug("Beginning constant folding")
folder = Trees::ConstantFolder.new folder = Trees::ConstantFolder.new
trees.map! do |tree| trees.map! do |tree|
next tree unless tree.is_a?(Trees::TreeFunction)
@logger.debug("Constant folding #{tree.name}") @logger.debug("Constant folding #{tree.name}")
tree.apply(folder).as(Trees::TreeFunction) tree.apply(folder)
end end
@logger.debug("Done constant folding") @logger.debug("Done constant folding")
return trees return trees
@@ -48,16 +49,26 @@ module Chalk
table = Table.new table = Table.new
@logger.debug("Creating symbol table") @logger.debug("Creating symbol table")
trees.each do |tree| trees.each do |tree|
@logger.debug("Storing #{tree.name} in symbol table") case tree
table[tree.name] = FunctionEntry.new tree when Trees::TreeSprite
@logger.debug("Storing sprite #{tree.name} in symbol table")
table.set_sprite tree.name, SpriteEntry.new tree.sprite
when Trees::TreeFunction
@logger.debug("Storing function #{tree.name} in symbol table")
table.set_function tree.name, FunctionEntry.new tree
else
@logger.debug("Unexpected tree type in input.")
end
end end
@logger.debug("Done creating symbol table") @logger.debug("Done creating symbol table")
table["get_key"] = FunctionEntry.new Builtin::InlineAwaitKeyFunction.new table.set_function "get_key", FunctionEntry.new Builtin::InlineAwaitKeyFunction.new
table["set_delay"] = FunctionEntry.new Builtin::InlineSetDelayFunction.new table.set_function "set_delay", FunctionEntry.new Builtin::InlineSetDelayFunction.new
table["get_delay"] = FunctionEntry.new Builtin::InlineGetDelayFunction.new table.set_function "get_delay", FunctionEntry.new Builtin::InlineGetDelayFunction.new
table["set_sound"] = FunctionEntry.new Builtin::InlineSetSoundFunction.new table.set_function "set_sound", FunctionEntry.new Builtin::InlineSetSoundFunction.new
table["draw_number"] = FunctionEntry.new Builtin::InlineDrawNumberFunction.new table.set_function "draw_number", FunctionEntry.new Builtin::InlineDrawNumberFunction.new
table.set_function "draw_sprite", FunctionEntry.new Builtin::InlineDrawSpriteFunction.new
table.set_function "clear", FunctionEntry.new Builtin::InlineClearFunction.new
return table return table
end end
@@ -71,7 +82,7 @@ module Chalk
@logger.debug("Generating code for #{tree.name}") @logger.debug("Generating code for #{tree.name}")
code = generator.generate! code = generator.generate!
code << instruction code << instruction
return optimizer.optimize(code) return code # optimizer.optimize(code)
end end
# Generate code for a builtin function. Neither the *table* nor the *instruction* # Generate code for a builtin function. Neither the *table* nor the *instruction*
@@ -93,6 +104,11 @@ module Chalk
return code return code
end end
private def create_code(trees : Array(Trees::Tree), table)
functions = trees.select &.is_a?(Trees::TreeFunction)
return create_code(functions.map &.as(Trees::TreeFunction), table)
end
# Runs in the tree `Ui::OutputMode`. The file is # Runs in the tree `Ui::OutputMode`. The file is
# tokenized and parsed, and the result is printed # tokenized and parsed, and the result is printed
# to the standard output. # to the standard output.
@@ -126,9 +142,9 @@ module Chalk
binary = instructions.map_with_index { |it, i| it.to_bin(table, instructions.size, i).to_u16 } binary = instructions.map_with_index { |it, i| it.to_bin(table, instructions.size, i).to_u16 }
binary.each do |inst| binary.each do |inst|
first = (inst >> 8).to_u8 first = (inst >> 8).to_u8
dest.write_byte(first) dest << first
second = (inst & 0xff).to_u8 second = (inst & 0xff).to_u8
dest.write_byte(second) dest << second
end end
end end
@@ -143,8 +159,8 @@ module Chalk
first = open.first first = open.first
open.delete first open.delete first
entry = table[first]? entry = table.get_function? first
raise "Unknown function" unless entry && entry.is_a?(FunctionEntry) raise "Unknown function" unless entry
function = entry.function function = entry.function
next if function.is_a?(Builtin::InlineFunction) next if function.is_a?(Builtin::InlineFunction)
done << first done << first
@@ -166,13 +182,13 @@ module Chalk
names = collect_calls(table) names = collect_calls(table)
names.delete "main" names.delete "main"
main_entry = table["main"]?.as(FunctionEntry) main_entry = table.get_function?("main").not_nil!
all_instructions.concat create_code(main_entry.function.as(Trees::TreeFunction), all_instructions.concat create_code(main_entry.function.as(Trees::TreeFunction),
table, Ir::JumpRelativeInstruction.new 0) table, Ir::JumpRelativeInstruction.new 0)
main_entry.addr = 0 main_entry.addr = 0
names.each do |name| names.each do |name|
entry = table[name]?.as(FunctionEntry) entry = table.get_function?(name).not_nil!
entry.addr = all_instructions.size entry.addr = all_instructions.size
function = entry.function function = entry.function
raise "Trying to compile inlined function" if function.is_a?(Builtin::InlineFunction) raise "Trying to compile inlined function" if function.is_a?(Builtin::InlineFunction)
@@ -180,8 +196,22 @@ module Chalk
all_instructions << Ir::ReturnInstruction.new all_instructions << Ir::ReturnInstruction.new
end end
sprite_bytes = [] of UInt8
offset = 0
table.sprites.each do |k, v|
data = v.sprite.encode
v.addr = offset + all_instructions.size * 2
offset += data.size
sprite_bytes.concat data
end
binary = [] of UInt8
file = File.open(@config.output, "w") file = File.open(@config.output, "w")
generate_binary(table, all_instructions, file) generate_binary(table, all_instructions, binary)
binary.concat sprite_bytes
binary.each do |byte|
file.write_byte byte
end
file.close file.close
end end

View File

@@ -4,6 +4,8 @@ module Chalk
RETURN_REG = 14 RETURN_REG = 14
# The register into which the "stack pointer" is stored. # The register into which the "stack pointer" is stored.
STACK_REG = 13 STACK_REG = 13
# Register used for throwing away values.
THROWAWAY_REG = 12
# Module to emit instructions and store # Module to emit instructions and store
# them into an existing array. # them into an existing array.

View File

@@ -81,5 +81,31 @@ module Chalk
return Compiler::FunctionType.new([Compiler::Type::U8] * 3, Compiler::Type::U0) return Compiler::FunctionType.new([Compiler::Type::U8] * 3, Compiler::Type::U0)
end end
end end
class InlineDrawSpriteFunction < InlineFunction
def generate!(emitter, params, table, target, free)
raise "First parameter should be a sprite name." if !params[0].is_a?(Trees::TreeId)
sprite_name = params[0].as(Trees::TreeId).id
sprite = table.get_sprite?(sprite_name).not_nil!.sprite
emitter.generate! params[1], table, free, free + 1
emitter.generate! params[2], table, free + 1, free + 2
emitter.instructions << Ir::SetISpriteInstruction.new params[0].as(Trees::TreeId).id
emitter.instructions << Ir::DrawInstruction.new free, free + 1, sprite.height.to_i32
end
def type
return Compiler::FunctionType.new([Compiler::Type::U8] * 3, Compiler::Type::U0)
end
end
class InlineClearFunction < InlineFunction
def generate!(emitter, params, table, target, free)
emitter.instructions << Ir::ClearInstruction.new
end
def type
return Compiler::FunctionType.new(([] of Compiler::Type), Compiler::Type::U0)
end
end
end end
end end

View File

@@ -282,7 +282,7 @@ module Chalk
end end
def to_bin(table, stack, index) def to_bin(table, stack, index)
return 0x2000 | (table[name]?.as(Compiler::FunctionEntry).addr * 2 + 0x200) return 0x2000 | (table.get_function?(name).as(Compiler::FunctionEntry).addr * 2 + 0x200)
end end
end end
@@ -297,6 +297,21 @@ module Chalk
end end
end end
class SetISpriteInstruction < Instruction
getter name
def initialize(@name : String)
end
def to_s(io)
io << "setispr " << @name
end
def to_bin(table, stack, index)
return 0xa000 | (table.get_sprite?(@name).not_nil!.addr + 0x200)
end
end
# Instruction to add a register to I. # Instruction to add a register to I.
class AddIRegInstruction < Instruction class AddIRegInstruction < Instruction
def initialize(@reg : Int32) def initialize(@reg : Int32)

View File

@@ -7,6 +7,7 @@ module Chalk
Any, Any,
Str, Str,
Id, Id,
SpriteRow,
LitDec, LitDec,
LitBin, LitBin,
LitHex, LitHex,
@@ -54,6 +55,8 @@ module Chalk
TokenType::Str.value) TokenType::Str.value)
@lexer.add_pattern("[a-zA-Z_][a-zA-Z_0-9]*", @lexer.add_pattern("[a-zA-Z_][a-zA-Z_0-9]*",
TokenType::Id.value) TokenType::Id.value)
@lexer.add_pattern("`[ x]*`",
TokenType::SpriteRow.value)
@lexer.add_pattern("[0-9]+", @lexer.add_pattern("[0-9]+",
TokenType::LitDec.value) TokenType::LitDec.value)
@lexer.add_pattern("0b[0-1]+", @lexer.add_pattern("0b[0-1]+",

View File

@@ -6,6 +6,20 @@ module Chalk
class Parser class Parser
include ParserBuilder include ParserBuilder
private def create_sprite
type(Compiler::TokenType::KwSprite)
.then(type(Compiler::TokenType::Id))
.then(char('['))
.then(many(type(Compiler::TokenType::SpriteRow)))
.then(char(']'))
.transform do |array|
array = array.flatten
name = array[1].string
sprite = Compiler::Sprite.new(array[3..array.size - 2].map &.string)
Trees::TreeSprite.new(name, sprite).as(Trees::Tree)
end
end
# Creates a parser for a type. # Creates a parser for a type.
private def create_type private def create_type
either(type(Compiler::TokenType::KwU0), either(type(Compiler::TokenType::KwU0),
@@ -185,14 +199,14 @@ module Chalk
Compiler::TokenType::KwU8 => Compiler::Type::U8, Compiler::TokenType::KwU8 => Compiler::Type::U8,
Compiler::TokenType::KwU12 => Compiler::Type::U12 Compiler::TokenType::KwU12 => Compiler::Type::U12
} }
Trees::TreeFunction.new(name, params, table[type], code) Trees::TreeFunction.new(name, params, table[type], code).as(Trees::Tree)
end end
return func return func
end end
def initialize def initialize
_, block = create_statement_block _, block = create_statement_block
@parser = many(create_func(block, create_type)).as(BasicParser(Array(Trees::TreeFunction))) @parser = many(either(create_func(block, create_type), create_sprite)).as(BasicParser(Array(Trees::Tree)))
end end
# Parses the given tokens into a tree. # Parses the given tokens into a tree.

97
src/chalk/sprite.cr Normal file
View File

@@ -0,0 +1,97 @@
module Chalk
module Compiler
class Sprite
def initialize
@pixels = Hash(UInt8, UInt8).new(default_value: 0_u8)
end
def height
return (@pixels.keys.max || 0_u8) + 1
end
def initialize(string, blank_char = ' ')
@pixels = Hash(UInt8, UInt8).new(default_value: 0_u8)
string.split("\n").each_with_index do |s, i|
break if i > 15
index = 0
byte = 0_u8
s.each_char do |char|
break if index > 7
bit = (char == blank_char) ? 0_u8 : 1_u8
byte |= (bit << (7 - index))
index += 1
end
@pixels[i.to_u8] = byte
end
end
def initialize(tokens)
raise "Invalid sprite" if tokens.size > 15
@pixels = Hash(UInt8, UInt8).new(default_value: 0_u8)
tokens.each_with_index do |token, i|
byte = 0_u8
substring = token[1..token.size - 2]
raise "Invalid sprite" if substring.size > 8
bit = 0b10000000
substring.each_char do |char|
byte |= bit if char == 'x'
bit >>= 1
end
@pixels[i.to_u8] = byte
end
end
def set_pixel(x, y)
raise "Invalid x-coordinate" if x > 7
raise "Invalid y-coordinate" if y > 15
x = x.to_u8
y = y.to_u8
char = @pixels.fetch y, 0_u8
char |= (1 << (7 - x))
@pixels[y] = char
end
def unset_pixel(x, y)
raise "Invalid x-coordinate" if x > 7
raise "Invalid y-coordinate" if y > 15
x = x.to_u8
y = y.to_u8
char = @pixels.fetch y, 0_u8
char &= ~(1 << (7 - x))
@pixels[y] = char
end
def toggle_pixel(x, y)
raise "Invalid x-coordinate" if x > 7
raise "Invalid y-coordinate" if y > 15
x = x.to_u8
y = y.to_u8
char = @pixels.fetch y, 0_u8
char ^= (1 << (7 - x))
@pixels[y] = char
end
def draw(io = STDOUT, blank_char = ' ', ink_char = 'x')
until_y = @pixels.keys.max?
return unless until_y
(0..until_y).each do |y|
row = @pixels.fetch y, 0_u8
pointer = 0b10000000
while pointer != 0
draw_pixel = (row & pointer) != 0
io << (draw_pixel ? ink_char : blank_char)
pointer = pointer >> 1
end
io << '\n'
end
end
def encode
if until_y = @pixels.keys.max?
return (0..until_y).map { |it| @pixels.fetch it, 0_u8 }
end
return [0_u8]
end
end
end
end

View File

@@ -1,11 +1,9 @@
require "./sprite.cr"
module Chalk module Chalk
module Compiler module Compiler
# An entry in the symbol table.
class Entry
end
# An entry that represents a function in the symbol table. # An entry that represents a function in the symbol table.
class FunctionEntry < Entry class FunctionEntry
# Gets the function stored in this entry. # Gets the function stored in this entry.
getter function getter function
# Gets the address in code of this function. # Gets the address in code of this function.
@@ -23,7 +21,7 @@ module Chalk
end end
# An entry that represents a variable in the symbol table. # An entry that represents a variable in the symbol table.
class VarEntry < Entry class VarEntry
# Gets the register occupied by the variable # Gets the register occupied by the variable
# in this entry. # in this entry.
getter register getter register
@@ -36,32 +34,47 @@ module Chalk
end end
end end
class SpriteEntry
property sprite : Sprite
property addr : Int32
def initialize(@sprite, @addr = -1)
end
end
# A symbol table. # A symbol table.
class Table class Table
# Gets the parent of this table. # Gets the parent of this table.
getter parent getter parent
# Gets the functions hash.
getter functions
# Gets the variables hash.
getter vars
# Gets the sprites hash.
getter sprites
def initialize(@parent : Table? = nil) def initialize(@parent : Table? = nil)
@data = {} of String => Entry @functions = {} of String => FunctionEntry
@vars = {} of String => VarEntry
@sprites = {} of String => SpriteEntry
end end
# Looks up the given *key* first in this table, macro table_functions(name)
# then in its parent, continuing recursively. def get_{{name}}?(key)
def []?(key) @{{name}}s[key]? || @parent.try &.get_{{name}}?(key)
if entry = @data[key]?
return entry
end
return @parent.try &.[key]?
end end
# Stores an *entry* under the given *key* into this table. def set_{{name}}(key, value)
def []=(key, entry) @{{name}}s[key] = value
@data[key] = entry
end end
end
table_functions function
table_functions var
table_functions sprite
def to_s(io) def to_s(io)
@parent.try &.to_s(io) @parent.try &.to_s(io)
io << @data.map { |k, v| k + ": " + v.to_s }.join("\n")
end end
end end
end end

View File

@@ -296,5 +296,13 @@ module Chalk
r.reduce(self, [@rvalue.reduce(r)]) r.reduce(self, [@rvalue.reduce(r)])
end end
end end
class TreeSprite < Tree
property name : String
property sprite : Compiler::Sprite
def initialize(@name, @sprite)
end
end
end end
end end

View File

@@ -12,8 +12,8 @@ module Chalk
end end
def reduce(t : TreeCall, children) def reduce(t : TreeCall, children)
entry = @table[t.name]? entry = @table.get_function? t.name
raise "Unknwon function" unless entry && entry.is_a?(Compiler::FunctionEntry) raise "Unknwon function" unless entry
type = entry.function.type type = entry.function.type
raise "Invalid parameters" if type.param_types.size != children.size raise "Invalid parameters" if type.param_types.size != children.size
children.each_with_index do |child, i| children.each_with_index do |child, i|

25
src/test.cr Normal file
View File

@@ -0,0 +1,25 @@
require "./chalk/*"
module Chalk
regex = /([^.]+)\.chalk/
source_dir = "programs"
dest_dir = "out"
Dir.mkdir_p dest_dir
exit if !File.directory? source_dir
Dir.new(source_dir)
.children
.compact_map { |it| regex.match(it) }
.each do |match|
config = Ui::Config.new file: (source_dir + File::SEPARATOR + match[0]),
output: (dest_dir + File::SEPARATOR + match[1] + ".ch8"),
loglevel: Logger::Severity::ERROR,
mode: Ui::OutputMode::Binary
compiler = Compiler::Compiler.new config
begin
compiler.run
rescue e
puts "Exception compiling #{match[0]}"
end
end
end