Compare commits
22 Commits
8d015d47f3
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| bc798f11fe | |||
| d40ea2a8ae | |||
| eb32577d38 | |||
| e740fd7688 | |||
| 5ce0d41a53 | |||
| b79d717f1a | |||
| 60c320dfea | |||
| 7fe5cec941 | |||
| 89709fef97 | |||
| 45f3e51890 | |||
| 61fb44bbce | |||
| 230a50c532 | |||
| a96d503095 | |||
| caaae87344 | |||
| 86ee6557cf | |||
| a7e24c059b | |||
| 2ed2a4932c | |||
| 1150e2b3ef | |||
| 4de89d98a1 | |||
| ecbe84134d | |||
| 3fa292347f | |||
| f8320bcb82 |
56
README.md
56
README.md
@@ -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
|
||||||
|
|||||||
7
programs/basic_arithmetic.chalk
Normal file
7
programs/basic_arithmetic.chalk
Normal 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
21
programs/basic_call.chalk
Normal 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();
|
||||||
|
}
|
||||||
3
programs/basic_draw_number.chalk
Normal file
3
programs/basic_draw_number.chalk
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
fun main(): u0 {
|
||||||
|
draw_number(69, 1, 1);
|
||||||
|
}
|
||||||
3
programs/basic_empty.chalk
Normal file
3
programs/basic_empty.chalk
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
fun main(): u0 {
|
||||||
|
|
||||||
|
}
|
||||||
17
programs/basic_if.chalk
Normal file
17
programs/basic_if.chalk
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
4
programs/basic_input.chalk
Normal file
4
programs/basic_input.chalk
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
fun main(): u0 {
|
||||||
|
var a = get_key();
|
||||||
|
var b = get_key();
|
||||||
|
}
|
||||||
12
programs/basic_sprite.chalk
Normal file
12
programs/basic_sprite.chalk
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
sprite dum [
|
||||||
|
` x x `
|
||||||
|
` x x `
|
||||||
|
` x x `
|
||||||
|
` `
|
||||||
|
`x x`
|
||||||
|
` xxxxxx `
|
||||||
|
]
|
||||||
|
|
||||||
|
fun main(): u0 {
|
||||||
|
draw_sprite(dum, 0, 0);
|
||||||
|
}
|
||||||
8
programs/basic_while.chalk
Normal file
8
programs/basic_while.chalk
Normal 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
14
programs/comb_fib.chalk
Normal 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
13
programs/comb_input.chalk
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
39
programs/comb_sprite.chalk
Normal file
39
programs/comb_sprite.chalk
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
34
programs/comb_sprite_2.chalk
Normal file
34
programs/comb_sprite_2.chalk
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,15 +4,14 @@ module Chalk
|
|||||||
# that is provided by chalk's standard library, and therefore
|
# that is provided by chalk's standard library, and therefore
|
||||||
# has predefined output.
|
# has predefined output.
|
||||||
abstract class BuiltinFunction
|
abstract class BuiltinFunction
|
||||||
# Gets the number of parameters this function has.
|
|
||||||
getter param_count : Int32
|
|
||||||
|
|
||||||
# Creates a new function with *param_count* parameters.
|
# Creates a new function with *param_count* parameters.
|
||||||
def initialize(@param_count)
|
def initialize()
|
||||||
end
|
end
|
||||||
|
|
||||||
# Uses the given `Compiler::Emitter` to output code.
|
# Uses the given `Compiler::Emitter` to output code.
|
||||||
abstract def generate!(codegen)
|
abstract def generate!(codegen)
|
||||||
|
# Gets the `Compiler::FunctionType` of this function.
|
||||||
|
abstract def type
|
||||||
end
|
end
|
||||||
|
|
||||||
# A function to which a call is not generated. This function
|
# A function to which a call is not generated. This function
|
||||||
@@ -20,11 +19,8 @@ module Chalk
|
|||||||
# function also accepts trees rather than register numbers,
|
# function also accepts trees rather than register numbers,
|
||||||
# and therefore can accept and manipulate trees.
|
# and therefore can accept and manipulate trees.
|
||||||
abstract class InlineFunction
|
abstract class InlineFunction
|
||||||
# Gets the number of parameters this function has.
|
|
||||||
getter param_count : Int32
|
|
||||||
|
|
||||||
# Creates a new function with *param_count* parameters.
|
# Creates a new function with *param_count* parameters.
|
||||||
def initialize(@param_count)
|
def initialize()
|
||||||
end
|
end
|
||||||
|
|
||||||
# Generates code like `Compiler::CodeGenerator` would.
|
# Generates code like `Compiler::CodeGenerator` would.
|
||||||
@@ -32,6 +28,8 @@ module Chalk
|
|||||||
# the *params* are trees that are being passed as arguments.
|
# the *params* are trees that are being passed as arguments.
|
||||||
# See `Compiler::CodeGenerator#generate!` for what the other parameters mean.
|
# See `Compiler::CodeGenerator#generate!` for what the other parameters mean.
|
||||||
abstract def generate!(codegen, params, table, target, free)
|
abstract def generate!(codegen, params, table, target, free)
|
||||||
|
# Gets the `Compiler::FunctionType` of this function.
|
||||||
|
abstract def type
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -8,11 +8,6 @@ module Chalk
|
|||||||
class CodeGenerator
|
class CodeGenerator
|
||||||
include Emitter
|
include Emitter
|
||||||
|
|
||||||
# The register into which the return value of a function is stored.
|
|
||||||
RETURN_REG = 14
|
|
||||||
# The register into which the "stack pointer" is stored.
|
|
||||||
STACK_REG = 13
|
|
||||||
|
|
||||||
# Gets the instructions currently emitted by this code generator.
|
# Gets the instructions currently emitted by this code generator.
|
||||||
getter instructions
|
getter instructions
|
||||||
|
|
||||||
@@ -24,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
|
||||||
@@ -42,10 +37,7 @@ module Chalk
|
|||||||
# `#generate!` call.
|
# `#generate!` call.
|
||||||
def generate!(tree, function : Trees::TreeFunction | Builtin::BuiltinFunction, table, target, free)
|
def generate!(tree, function : Trees::TreeFunction | Builtin::BuiltinFunction, table, target, free)
|
||||||
start_at = free
|
start_at = free
|
||||||
# Move I to stack
|
to_stack
|
||||||
setis
|
|
||||||
# Get to correct stack position
|
|
||||||
addi STACK_REG
|
|
||||||
# Store variables
|
# Store variables
|
||||||
store (start_at - 1) unless start_at == 0
|
store (start_at - 1) unless start_at == 0
|
||||||
# Increment I and stack position
|
# Increment I and stack position
|
||||||
@@ -67,10 +59,7 @@ module Chalk
|
|||||||
# Reduce stack pointer
|
# Reduce stack pointer
|
||||||
load free, start_at
|
load free, start_at
|
||||||
opr TokenType::OpSub, STACK_REG, free
|
opr TokenType::OpSub, STACK_REG, free
|
||||||
# Move I to stack
|
to_stack
|
||||||
setis
|
|
||||||
# Get to correct stack position
|
|
||||||
addi STACK_REG
|
|
||||||
# Restore
|
# Restore
|
||||||
restore (start_at - 1) unless start_at == 0
|
restore (start_at - 1) unless start_at == 0
|
||||||
# Get call value into target
|
# Get call value into target
|
||||||
@@ -85,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
|
||||||
@@ -96,31 +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]?
|
entry = table.get_function?(tree.name).not_nil!
|
||||||
raise "Unknown function" unless entry &&
|
raise "Unknown function" unless entry.is_a?(FunctionEntry)
|
||||||
entry.is_a?(FunctionEntry)
|
generate! tree, entry.function, table, target, free
|
||||||
function = entry.function
|
|
||||||
raise "Invalid call" if tree.params.size != function.param_count
|
|
||||||
generate! tree, 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
|
||||||
@@ -148,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
|
||||||
@@ -156,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
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ module Chalk
|
|||||||
def initialize(@config : Ui::Config)
|
def initialize(@config : Ui::Config)
|
||||||
@logger = Logger.new STDOUT
|
@logger = Logger.new STDOUT
|
||||||
@logger.debug("Initialized compiler")
|
@logger.debug("Initialized compiler")
|
||||||
@logger.level = Logger::DEBUG
|
@logger.level = @config.loglevel
|
||||||
end
|
end
|
||||||
|
|
||||||
# Reads a file an extracts instances of
|
# Reads a file an extracts instances of
|
||||||
@@ -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["draw"] = FunctionEntry.new Builtin::InlineDrawFunction.new
|
table.set_function "get_key", FunctionEntry.new Builtin::InlineAwaitKeyFunction.new
|
||||||
table["get_key"] = FunctionEntry.new Builtin::InlineAwaitKeyFunction.new
|
table.set_function "set_delay", FunctionEntry.new Builtin::InlineSetDelayFunction.new
|
||||||
table["get_font"] = FunctionEntry.new Builtin::InlineGetFontFunction.new
|
table.set_function "get_delay", FunctionEntry.new Builtin::InlineGetDelayFunction.new
|
||||||
table["set_delay"] = FunctionEntry.new Builtin::InlineSetDelayFunction.new
|
table.set_function "set_sound", FunctionEntry.new Builtin::InlineSetSoundFunction.new
|
||||||
table["get_delay"] = FunctionEntry.new Builtin::InlineGetDelayFunction.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
|
||||||
|
|
||||||
@@ -65,12 +76,13 @@ module Chalk
|
|||||||
# looking up identifiers in the symbol *table*, and appending the given *instruction*
|
# looking up identifiers in the symbol *table*, and appending the given *instruction*
|
||||||
# at the end of the function to ensure correct program flow.
|
# at the end of the function to ensure correct program flow.
|
||||||
private def create_code(tree : Trees::TreeFunction, table, instruction = Ir::ReturnInstruction.new)
|
private def create_code(tree : Trees::TreeFunction, table, instruction = Ir::ReturnInstruction.new)
|
||||||
|
tree.reduce(Trees::TypeChecker.new table, tree.type.return_type)
|
||||||
optimizer = Optimizer.new
|
optimizer = Optimizer.new
|
||||||
generator = CodeGenerator.new table, tree
|
generator = CodeGenerator.new table, tree
|
||||||
@logger.debug("Generating code for #{tree.name}")
|
@logger.debug("Generating code for #{tree.name}")
|
||||||
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*
|
||||||
@@ -92,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.
|
||||||
@@ -125,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
|
||||||
|
|
||||||
@@ -142,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
|
||||||
@@ -165,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)
|
||||||
@@ -179,8 +196,22 @@ module Chalk
|
|||||||
all_instructions << Ir::ReturnInstruction.new
|
all_instructions << Ir::ReturnInstruction.new
|
||||||
end
|
end
|
||||||
|
|
||||||
file = File.open("out.ch8", "w")
|
sprite_bytes = [] of UInt8
|
||||||
generate_binary(table, all_instructions, file)
|
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")
|
||||||
|
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
|
||||||
|
|
||||||
|
|||||||
@@ -22,10 +22,20 @@ module Chalk
|
|||||||
getter mode : OutputMode
|
getter mode : OutputMode
|
||||||
# Sets the mode in which the compiler should operate.
|
# Sets the mode in which the compiler should operate.
|
||||||
setter mode : OutputMode
|
setter mode : OutputMode
|
||||||
|
# Gets the log level.
|
||||||
|
getter loglevel : Logger::Severity
|
||||||
|
# Sets the log level.
|
||||||
|
setter loglevel : Logger::Severity
|
||||||
|
# Gets the output file destination.
|
||||||
|
getter output : String
|
||||||
|
# Sets the output file destination.
|
||||||
|
setter output : String
|
||||||
|
|
||||||
# Creates a new configuration.
|
# Creates a new configuration.
|
||||||
def initialize(@file = "",
|
def initialize(@file = "",
|
||||||
@mode = OutputMode::Tree)
|
@mode = OutputMode::Tree,
|
||||||
|
@loglevel = Logger::Severity::DEBUG,
|
||||||
|
@output : String = "out.ch8")
|
||||||
end
|
end
|
||||||
|
|
||||||
# Reads a configuration from the command line options.
|
# Reads a configuration from the command line options.
|
||||||
@@ -34,20 +44,35 @@ module Chalk
|
|||||||
OptionParser.parse! do |parser|
|
OptionParser.parse! do |parser|
|
||||||
parser.banner = "Usage: chalk [arguments]"
|
parser.banner = "Usage: chalk [arguments]"
|
||||||
parser.on("-m", "--mode=MODE", "Set the mode of the compiler.") do |mode|
|
parser.on("-m", "--mode=MODE", "Set the mode of the compiler.") do |mode|
|
||||||
case mode.downcase
|
hash = {
|
||||||
when "tree", "t"
|
"tree" => OutputMode::Tree,
|
||||||
config.mode = OutputMode::Tree
|
"t" => OutputMode::Tree,
|
||||||
when "intermediate", "i"
|
"intermediate" => OutputMode::Intermediate,
|
||||||
config.mode = OutputMode::Intermediate
|
"i" => OutputMode::Intermediate,
|
||||||
when "binary", "b"
|
"binary" => OutputMode::Binary,
|
||||||
config.mode = OutputMode::Binary
|
"b" => OutputMode::Binary
|
||||||
else
|
}
|
||||||
puts "Invalid mode type. Using default."
|
puts "Invalid mode type. Using default." if !hash.has_key?(mode)
|
||||||
end
|
config.mode = hash[mode]? || OutputMode::Tree
|
||||||
end
|
end
|
||||||
parser.on("-f", "--file=FILE", "Set the input file to compile.") do |file|
|
parser.on("-f", "--file=FILE", "Set the input file to compile.") do |file|
|
||||||
config.file = file
|
config.file = file
|
||||||
end
|
end
|
||||||
|
parser.on("-o", "--output=OUT", "Sets the output file.") do |out|
|
||||||
|
config.output = out
|
||||||
|
end
|
||||||
|
parser.on("-l", "--log=LOG", "Set the log level of the compiler.") do |log|
|
||||||
|
hash = {
|
||||||
|
"debug" => Logger::Severity::DEBUG,
|
||||||
|
"fatal" => Logger::Severity::FATAL,
|
||||||
|
"error" => Logger::Severity::ERROR,
|
||||||
|
"info" => Logger::Severity::INFO,
|
||||||
|
"unknown" => Logger::Severity::UNKNOWN,
|
||||||
|
"warn" => Logger::Severity::WARN
|
||||||
|
}
|
||||||
|
puts "Invalid log level. Using default." if !hash.has_key?(log)
|
||||||
|
config.loglevel = hash[log]? || Logger::Severity::DEBUG
|
||||||
|
end
|
||||||
parser.on("-h", "--help", "Show this message.") { puts parser }
|
parser.on("-h", "--help", "Show this message.") { puts parser }
|
||||||
end
|
end
|
||||||
return config
|
return config
|
||||||
|
|||||||
@@ -1,8 +1,21 @@
|
|||||||
module Chalk
|
module Chalk
|
||||||
module Compiler
|
module Compiler
|
||||||
|
# The register into which the return value of a function is stored.
|
||||||
|
RETURN_REG = 14
|
||||||
|
# The register into which the "stack pointer" is stored.
|
||||||
|
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.
|
||||||
module Emitter
|
module Emitter
|
||||||
|
# Moves I to the next available value on the stack.
|
||||||
|
def to_stack
|
||||||
|
setis
|
||||||
|
addi STACK_REG
|
||||||
|
end
|
||||||
|
|
||||||
# Emits an instruction to load a *value* into a register, *into*.
|
# Emits an instruction to load a *value* into a register, *into*.
|
||||||
def load(into, value)
|
def load(into, value)
|
||||||
inst = Ir::LoadInstruction.new into, value.to_i32
|
inst = Ir::LoadInstruction.new into, value.to_i32
|
||||||
|
|||||||
@@ -1,65 +1,111 @@
|
|||||||
|
require "./builtin"
|
||||||
|
require "./type"
|
||||||
|
|
||||||
module Chalk
|
module Chalk
|
||||||
module Builtin
|
module Builtin
|
||||||
# Inline function to draw sprite at address I.
|
|
||||||
class InlineDrawFunction < InlineFunction
|
|
||||||
def initialize
|
|
||||||
@param_count = 3
|
|
||||||
end
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
# Inline function to await for a key and return it.
|
# Inline function to await for a key and return it.
|
||||||
class InlineAwaitKeyFunction < InlineFunction
|
class InlineAwaitKeyFunction < InlineFunction
|
||||||
def initialize
|
|
||||||
@param_count = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
def generate!(emitter, params, table, target, free)
|
def generate!(emitter, params, table, target, free)
|
||||||
emitter.instructions << Ir::AwaitKeyInstruction.new target
|
emitter.instructions << Ir::AwaitKeyInstruction.new target
|
||||||
end
|
end
|
||||||
end
|
def type
|
||||||
|
return Compiler::FunctionType.new(([] of Compiler::Type), Compiler::Type::U8)
|
||||||
# Inline function to get font for a given value.
|
|
||||||
class InlineGetFontFunction < InlineFunction
|
|
||||||
def initialize
|
|
||||||
@param_count = 1
|
|
||||||
end
|
|
||||||
|
|
||||||
def generate!(emitter, params, table, target, free)
|
|
||||||
emitter.generate! params[0], table, free, free + 1
|
|
||||||
emitter.instructions << Ir::GetFontInstruction.new free
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Inline function to set the delay timer.
|
# Inline function to set the delay timer.
|
||||||
class InlineSetDelayFunction < InlineFunction
|
class InlineSetDelayFunction < InlineFunction
|
||||||
def initialize
|
|
||||||
@param_count = 1
|
|
||||||
end
|
|
||||||
|
|
||||||
def generate!(emitter, params, table, target, free)
|
def generate!(emitter, params, table, target, free)
|
||||||
emitter.generate! params[0], table, free, free + 1
|
emitter.generate! params[0], table, free, free + 1
|
||||||
emitter.instructions << Ir::SetDelayTimerInstruction.new free
|
emitter.instructions << Ir::SetDelayTimerInstruction.new free
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def type
|
||||||
|
return Compiler::FunctionType.new([Compiler::Type::U8], Compiler::Type::U0)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Inline function to get the delay timer.
|
# Inline function to get the delay timer.
|
||||||
class InlineGetDelayFunction < InlineFunction
|
class InlineGetDelayFunction < InlineFunction
|
||||||
def initialize
|
|
||||||
@param_count = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
def generate!(emitter, params, table, target, free)
|
def generate!(emitter, params, table, target, free)
|
||||||
emitter.instructions << Ir::GetDelayTimerInstruction.new target
|
emitter.instructions << Ir::GetDelayTimerInstruction.new target
|
||||||
end
|
end
|
||||||
|
def type
|
||||||
|
return Compiler::FunctionType.new(([] of Compiler::Type), Compiler::Type::U8)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Function to set the sound timer.
|
||||||
|
class InlineSetSoundFunction < InlineFunction
|
||||||
|
def generate!(emitter, params, table, target, free)
|
||||||
|
emitter.generate! params[0], table, free, free + 1
|
||||||
|
emitter.instructions << Ir::SetSoundTimerInstruction.new free
|
||||||
|
end
|
||||||
|
|
||||||
|
def type
|
||||||
|
return Compiler::FunctionType.new([Compiler::Type::U8], Compiler::Type::U0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Function to draw numbers.
|
||||||
|
class InlineDrawNumberFunction < InlineFunction
|
||||||
|
def generate!(emitter, params, table, target, free)
|
||||||
|
emitter.to_stack
|
||||||
|
# Save variables from R0-R2
|
||||||
|
emitter.store 0x2
|
||||||
|
emitter.load free, 0x3
|
||||||
|
emitter.addi free
|
||||||
|
# Write BCD values to I
|
||||||
|
emitter.generate! params[0], table, free, free + 1
|
||||||
|
emitter.instructions << Ir::BCDInstruction.new free
|
||||||
|
emitter.restore 0x2
|
||||||
|
# Get the coordinates
|
||||||
|
free = 3 if free < 3
|
||||||
|
emitter.generate! params[1], table, free, free + 1
|
||||||
|
emitter.generate! params[2], table, free + 1, free + 2
|
||||||
|
# Draw
|
||||||
|
emitter.instructions << Ir::GetFontInstruction.new 0x0
|
||||||
|
emitter.instructions << Ir::DrawInstruction.new free, free + 1, 5
|
||||||
|
emitter.op(Compiler::TokenType::OpAdd, free, 6)
|
||||||
|
emitter.instructions << Ir::GetFontInstruction.new 0x1
|
||||||
|
emitter.instructions << Ir::DrawInstruction.new free, free + 1, 5
|
||||||
|
emitter.op(Compiler::TokenType::OpAdd, free, 6)
|
||||||
|
emitter.instructions << Ir::GetFontInstruction.new 0x2
|
||||||
|
emitter.instructions << Ir::DrawInstruction.new free, free + 1, 5
|
||||||
|
# Load variables from RO-R2 back
|
||||||
|
emitter.to_stack
|
||||||
|
emitter.restore 0x2
|
||||||
|
end
|
||||||
|
|
||||||
|
def type
|
||||||
|
return Compiler::FunctionType.new([Compiler::Type::U8] * 3, Compiler::Type::U0)
|
||||||
|
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
|
end
|
||||||
|
|||||||
@@ -12,6 +12,33 @@ module Chalk
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Instruction to clear the screen
|
||||||
|
class ClearInstruction < Instruction
|
||||||
|
def to_s(io)
|
||||||
|
io << "clear"
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_bin(table, stack, index)
|
||||||
|
return 0x00e0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Instruction to assign a random value to a register.
|
||||||
|
class RandomInstruction < Instruction
|
||||||
|
def initialize(@register : Int32, @value : Int32)
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s(io)
|
||||||
|
io << "rand R"
|
||||||
|
@register.to_s(16, io)
|
||||||
|
io << " " << @value
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_bin(table, stack, index)
|
||||||
|
return 0xc000 | (@register << 8) | (@value)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Instruction to load a value into a register.
|
# Instruction to load a value into a register.
|
||||||
class LoadInstruction < Instruction
|
class LoadInstruction < Instruction
|
||||||
def initialize(@register : Int32, @value : Int32)
|
def initialize(@register : Int32, @value : Int32)
|
||||||
@@ -255,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
|
||||||
|
|
||||||
@@ -270,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)
|
||||||
@@ -367,5 +409,34 @@ module Chalk
|
|||||||
return 0xf007 | (@into << 8)
|
return 0xf007 | (@into << 8)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Instruction to set the sound timer to a value.
|
||||||
|
class SetSoundTimerInstruction < Instruction
|
||||||
|
def initialize(@from : Int32)
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s(io)
|
||||||
|
io << "set_sound R"
|
||||||
|
@from.to_s(16, io)
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_bin(table, stack, index)
|
||||||
|
return 0xf018 | (@from << 8)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class BCDInstruction < Instruction
|
||||||
|
def initialize(@from : Int32)
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s(io)
|
||||||
|
io << "bcd R"
|
||||||
|
@from.to_s(16, io)
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_bin(table, stack, index)
|
||||||
|
return 0xf033 | (@from << 8)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ module Chalk
|
|||||||
Any,
|
Any,
|
||||||
Str,
|
Str,
|
||||||
Id,
|
Id,
|
||||||
|
SpriteRow,
|
||||||
LitDec,
|
LitDec,
|
||||||
LitBin,
|
LitBin,
|
||||||
LitHex,
|
LitHex,
|
||||||
@@ -21,6 +22,7 @@ module Chalk
|
|||||||
KwInline
|
KwInline
|
||||||
KwFun
|
KwFun
|
||||||
KwU0
|
KwU0
|
||||||
|
KwU4
|
||||||
KwU8
|
KwU8
|
||||||
KwU12
|
KwU12
|
||||||
KwVar
|
KwVar
|
||||||
@@ -53,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]+",
|
||||||
@@ -70,6 +74,7 @@ module Chalk
|
|||||||
@lexer.add_pattern("inline", TokenType::KwInline.value)
|
@lexer.add_pattern("inline", TokenType::KwInline.value)
|
||||||
@lexer.add_pattern("fun", TokenType::KwFun.value)
|
@lexer.add_pattern("fun", TokenType::KwFun.value)
|
||||||
@lexer.add_pattern("u0", TokenType::KwU0.value)
|
@lexer.add_pattern("u0", TokenType::KwU0.value)
|
||||||
|
@lexer.add_pattern("u4", TokenType::KwU4.value)
|
||||||
@lexer.add_pattern("u8", TokenType::KwU8.value)
|
@lexer.add_pattern("u8", TokenType::KwU8.value)
|
||||||
@lexer.add_pattern("u12", TokenType::KwU12.value)
|
@lexer.add_pattern("u12", TokenType::KwU12.value)
|
||||||
@lexer.add_pattern("var", TokenType::KwVar.value)
|
@lexer.add_pattern("var", TokenType::KwVar.value)
|
||||||
|
|||||||
@@ -6,9 +6,26 @@ 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), type(Compiler::TokenType::KwU8), type(Compiler::TokenType::KwU12))
|
either(type(Compiler::TokenType::KwU0),
|
||||||
|
type(Compiler::TokenType::KwU4),
|
||||||
|
type(Compiler::TokenType::KwU8),
|
||||||
|
type(Compiler::TokenType::KwU12))
|
||||||
end
|
end
|
||||||
|
|
||||||
# Creates a parser for an integer literal.
|
# Creates a parser for an integer literal.
|
||||||
@@ -176,14 +193,20 @@ module Chalk
|
|||||||
params = arr[3..arr.size - 5].map &.as(Compiler::Token).string
|
params = arr[3..arr.size - 5].map &.as(Compiler::Token).string
|
||||||
code = arr[arr.size - 1].as(Trees::Tree)
|
code = arr[arr.size - 1].as(Trees::Tree)
|
||||||
type = arr[arr.size - 2].as(Compiler::Token).type
|
type = arr[arr.size - 2].as(Compiler::Token).type
|
||||||
Trees::TreeFunction.new(name, params, code)
|
table = {
|
||||||
|
Compiler::TokenType::KwU0 => Compiler::Type::U0,
|
||||||
|
Compiler::TokenType::KwU4 => Compiler::Type::U4,
|
||||||
|
Compiler::TokenType::KwU8 => Compiler::Type::U8,
|
||||||
|
Compiler::TokenType::KwU12 => Compiler::Type::U12
|
||||||
|
}
|
||||||
|
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
97
src/chalk/sprite.cr
Normal 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
|
||||||
@@ -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]?
|
end
|
||||||
return entry
|
|
||||||
end
|
def set_{{name}}(key, value)
|
||||||
return @parent.try &.[key]?
|
@{{name}}s[key] = value
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Stores an *entry* under the given *key* into this table.
|
table_functions function
|
||||||
def []=(key, entry)
|
table_functions var
|
||||||
@data[key] = entry
|
table_functions sprite
|
||||||
end
|
|
||||||
|
|
||||||
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
|
||||||
|
|||||||
@@ -18,6 +18,11 @@ module Chalk
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class Reducer(T)
|
||||||
|
def reduce(tree, children)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# The base class of a tree.
|
# The base class of a tree.
|
||||||
class Tree
|
class Tree
|
||||||
def accept(v)
|
def accept(v)
|
||||||
@@ -28,6 +33,10 @@ module Chalk
|
|||||||
def apply(t)
|
def apply(t)
|
||||||
return t.transform(self)
|
return t.transform(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def reduce(r : Reducer(T)) forall T
|
||||||
|
return r.reduce(self, [] of T)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# A tree that represents an ID.
|
# A tree that represents an ID.
|
||||||
@@ -66,6 +75,10 @@ module Chalk
|
|||||||
end
|
end
|
||||||
return t.transform(self)
|
return t.transform(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def reduce(r : Reducer(T)) forall T
|
||||||
|
return r.reduce(self, @params.map &.reduce(r))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# A tree that represents an operation on two values.
|
# A tree that represents an operation on two values.
|
||||||
@@ -89,6 +102,10 @@ module Chalk
|
|||||||
@right = @right.apply(t)
|
@right = @right.apply(t)
|
||||||
return t.transform(self)
|
return t.transform(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def reduce(r : Reducer(T)) forall T
|
||||||
|
return r.reduce(self, [@left.reduce(r), @right.reduce(r)])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# A tree that represents a block of statements.
|
# A tree that represents a block of statements.
|
||||||
@@ -110,6 +127,9 @@ module Chalk
|
|||||||
end
|
end
|
||||||
return t.transform(self)
|
return t.transform(self)
|
||||||
end
|
end
|
||||||
|
def reduce(r : Reducer(T)) forall T
|
||||||
|
return r.reduce(self, @children.map &.reduce(r))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# A tree that represents a function declaration.
|
# A tree that represents a function declaration.
|
||||||
@@ -117,8 +137,10 @@ module Chalk
|
|||||||
property name : String
|
property name : String
|
||||||
property params : Array(String)
|
property params : Array(String)
|
||||||
property block : Tree
|
property block : Tree
|
||||||
|
getter type
|
||||||
|
|
||||||
def initialize(@name, @params, @block)
|
def initialize(@name, @params, return_type : Compiler::Type, @block)
|
||||||
|
@type = Compiler::FunctionType.new([Compiler::Type::U8] * @params.size, return_type)
|
||||||
end
|
end
|
||||||
|
|
||||||
def param_count
|
def param_count
|
||||||
@@ -135,6 +157,10 @@ module Chalk
|
|||||||
@block = @block.apply(t)
|
@block = @block.apply(t)
|
||||||
return t.transform(self)
|
return t.transform(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def reduce(r : Reducer(T)) forall T
|
||||||
|
return r.reduce(self, [@block.reduce(r)])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# A tree that represents the declaration of
|
# A tree that represents the declaration of
|
||||||
@@ -156,6 +182,10 @@ module Chalk
|
|||||||
@expr = @expr.apply(t)
|
@expr = @expr.apply(t)
|
||||||
return t.transform(self)
|
return t.transform(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def reduce(r : Reducer(T)) forall T
|
||||||
|
r.reduce(self, [@expr.reduce(r)])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# A tree that represents the assignment
|
# A tree that represents the assignment
|
||||||
@@ -177,6 +207,10 @@ module Chalk
|
|||||||
@expr = @expr.apply(t)
|
@expr = @expr.apply(t)
|
||||||
return t.transform(self)
|
return t.transform(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def reduce(r : Reducer(T)) forall T
|
||||||
|
r.reduce(self, [@expr.reduce(r)])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# A tree that represents an if statement.
|
# A tree that represents an if statement.
|
||||||
@@ -202,6 +236,16 @@ module Chalk
|
|||||||
@otherwise = @otherwise.try &.apply(t)
|
@otherwise = @otherwise.try &.apply(t)
|
||||||
return t.transform(self)
|
return t.transform(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def reduce(r : Reducer(T)) forall T
|
||||||
|
cond = @condition.reduce(r)
|
||||||
|
blk = @block.reduce(r)
|
||||||
|
if other = @otherwise
|
||||||
|
r.reduce(self, [cond, blk,other.reduce(r)])
|
||||||
|
else
|
||||||
|
r.reduce(self, [cond, blk])
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# A tree that represents a while loop.
|
# A tree that represents a while loop.
|
||||||
@@ -224,6 +268,10 @@ module Chalk
|
|||||||
@block = @block.apply(t)
|
@block = @block.apply(t)
|
||||||
return t.transform(self)
|
return t.transform(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def reduce(r : Reducer(T)) forall T
|
||||||
|
r.reduce(self, [@condition.reduce(r), @block.reduce(r)])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# A tree that represents a return statement.
|
# A tree that represents a return statement.
|
||||||
@@ -243,6 +291,18 @@ module Chalk
|
|||||||
@rvalue = @rvalue.apply(t)
|
@rvalue = @rvalue.apply(t)
|
||||||
return t.transform(self)
|
return t.transform(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def reduce(r : Reducer(T)) forall T
|
||||||
|
r.reduce(self, [@rvalue.reduce(r)])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class TreeSprite < Tree
|
||||||
|
property name : String
|
||||||
|
property sprite : Compiler::Sprite
|
||||||
|
|
||||||
|
def initialize(@name, @sprite)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
35
src/chalk/type.cr
Normal file
35
src/chalk/type.cr
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
module Chalk
|
||||||
|
module Compiler
|
||||||
|
# Possible types of values in chalk
|
||||||
|
enum Type
|
||||||
|
U0
|
||||||
|
U4
|
||||||
|
U8
|
||||||
|
U12
|
||||||
|
|
||||||
|
# Checks if one value can be cast to another.
|
||||||
|
def casts_to?(other)
|
||||||
|
return false if other == Type::U0 || self == Type::U0
|
||||||
|
return other.value >= self.value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# A type of a function.
|
||||||
|
class FunctionType
|
||||||
|
# Gets the types of the function's parameters.
|
||||||
|
getter param_types
|
||||||
|
# Gets the return type of the function.
|
||||||
|
getter return_type
|
||||||
|
|
||||||
|
def initialize(@param_types : Array(Type), @return_type : Type)
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s(io)
|
||||||
|
io << "("
|
||||||
|
io << param_types.map(&.to_s).join(", ")
|
||||||
|
io << ") -> "
|
||||||
|
return_type.to_s(io)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
58
src/chalk/typecheck.cr
Normal file
58
src/chalk/typecheck.cr
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
require "./type"
|
||||||
|
|
||||||
|
module Chalk
|
||||||
|
module Trees
|
||||||
|
# Reducer to check types.
|
||||||
|
class TypeChecker < Reducer(Compiler::Type)
|
||||||
|
def initialize(@table : Compiler::Table, @return_type : Compiler::Type)
|
||||||
|
end
|
||||||
|
|
||||||
|
def reduce(t, children)
|
||||||
|
return Compiler::Type::U0
|
||||||
|
end
|
||||||
|
|
||||||
|
def reduce(t : TreeCall, children)
|
||||||
|
entry = @table.get_function? t.name
|
||||||
|
raise "Unknwon function" unless entry
|
||||||
|
type = entry.function.type
|
||||||
|
raise "Invalid parameters" if type.param_types.size != children.size
|
||||||
|
children.each_with_index do |child, i|
|
||||||
|
raise "Incompatible parameter" if !child.casts_to?(type.param_types[i])
|
||||||
|
end
|
||||||
|
return entry.function.type.return_type
|
||||||
|
end
|
||||||
|
|
||||||
|
def reduce(t : TreeId, children)
|
||||||
|
return Compiler::Type::U8
|
||||||
|
end
|
||||||
|
|
||||||
|
def reduce(t : TreeLit, children)
|
||||||
|
max_12 = (2 ** 12) - 1
|
||||||
|
max_8 = (2 ** 8) - 1
|
||||||
|
max_4 = (2 ** 4) - 1
|
||||||
|
raise "Number too big" if t.lit > max_12
|
||||||
|
return Compiler::Type::U12 if t.lit > max_8
|
||||||
|
return Compiler::Type::U8 if t.lit > max_4
|
||||||
|
return Compiler::Type::U4
|
||||||
|
end
|
||||||
|
|
||||||
|
def reduce(t : TreeOp, children)
|
||||||
|
left = children[0]
|
||||||
|
right = children[1]
|
||||||
|
return left if right.casts_to?(left)
|
||||||
|
return right if left.casts_to?(right)
|
||||||
|
raise "Invalid operation"
|
||||||
|
end
|
||||||
|
|
||||||
|
def reduce(t : TreeAssign | TreeVar, children)
|
||||||
|
raise "Invalid assignment" if !children[0].casts_to?(Compiler::Type::U8)
|
||||||
|
return Compiler::Type::U0
|
||||||
|
end
|
||||||
|
|
||||||
|
def reduce(t : TreeReturn, children)
|
||||||
|
raise "Incompatible return type" if !children[0].casts_to?(@return_type)
|
||||||
|
return Compiler::Type::U0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
25
src/test.cr
Normal file
25
src/test.cr
Normal 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
|
||||||
Reference in New Issue
Block a user