ported over DSC compiler code to new DSCS compiler
This commit is contained in:
Generated
+3
-1
@@ -12,12 +12,14 @@
|
|||||||
<sourceFolder url="file://$MODULE_DIR$/libraries/dsa_tasks/src" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/libraries/dsa_tasks/src" isTestSource="false" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/libraries/zxq5_egui/src" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/libraries/zxq5_egui/src" isTestSource="false" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/tooling/assembler/src" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/tooling/assembler/src" isTestSource="false" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/tooling/compiler/src" isTestSource="false" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/tooling/disassembler/src" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/tooling/disassembler/src" isTestSource="false" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/tooling/dsx_build/src" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/tooling/dsx_build/src" isTestSource="false" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/tooling/dsx_client/src" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/tooling/dsx_client/src" isTestSource="false" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/tooling/dsx_server/src" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/tooling/dsx_server/src" isTestSource="false" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/tooling/linker/src" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/tooling/linker/src" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/tooling/bf_compiler/src" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/tooling/dsfl_compiler/src" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/tooling/dscs_compiler/src" isTestSource="false" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/.idea/caches" />
|
<excludeFolder url="file://$MODULE_DIR$/.idea/caches" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/doc/.obsidian" />
|
<excludeFolder url="file://$MODULE_DIR$/doc/.obsidian" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
# idk
|
||||||
|
|
||||||
|
basic stuff.
|
||||||
|
don't like haskell's reverse order of applying operations inside out, makes code hard to read.
|
||||||
|
piping >>
|
||||||
|
|
||||||
|
// this would be the syntax to filter a list of numbers to get the evens
|
||||||
|
evens x List(Int) => x
|
||||||
|
|> filter_map (div 2 |> equals 0)
|
||||||
|
|
||||||
|
// similarly you can use lambdas (same syntax as ordinary functions)
|
||||||
|
evens x List(Int) => x
|
||||||
|
|> filter_map (x => div 2 |> equals 0)
|
||||||
|
|
||||||
|
is_even x Int => x |> div 2 |> equals 0
|
||||||
|
|
||||||
|
add_y_if_even_else_z x Int, y Int, z Int => x
|
||||||
|
|> is_even
|
||||||
|
|> then (add y)
|
||||||
|
|> else (add z)
|
||||||
|
|
||||||
|
then Bool x, (-> T) val -> Option(T) =>
|
||||||
|
// language internal
|
||||||
|
|
||||||
|
else Option(T) val (-> T) alternative -> T =>
|
||||||
|
// language internal
|
||||||
|
|
||||||
|
type Result(T, E) => Ok(T) | Err(E)
|
||||||
|
type Option(T) => Some(T) | None
|
||||||
|
type Shape => Square(Int) | Rectangle(Int, Int) | Circle(Int)
|
||||||
|
|
||||||
|
// we can pattern match
|
||||||
|
area_doubled Shape s -> Int => s
|
||||||
|
|> case Square (s => multiply s s)
|
||||||
|
|> case Rectangle multiply // multiply takes two integer arguments and Rectangle provides this!
|
||||||
|
|> case Circle (r => multiply r r |> multiply PI)
|
||||||
|
|> case Circle (r => r |> multiply r |> multiply PI)
|
||||||
|
|> case Circle (r => r, r |> multiply |> multiply PI)
|
||||||
|
|> case Circle (r => r, r |> (multiply >> multiply PI))
|
||||||
|
|> multiply 2
|
||||||
|
|
||||||
|
chained_computation Int s -> Result(Int, ErrType) => s
|
||||||
|
|> try fallible_computation_on_int
|
||||||
|
|> try fallible_computation_on_int
|
||||||
|
|
||||||
|
case T item, T matched_variant, (-> R) -> Option(R) =>
|
||||||
|
// language internal
|
||||||
@@ -0,0 +1,704 @@
|
|||||||
|
|
||||||
|
include print: "./print.dsa"
|
||||||
|
|
||||||
|
resb program: 0x1000
|
||||||
|
dw stack: 0x10000
|
||||||
|
dw input: 0x30000
|
||||||
|
|
||||||
|
_init:
|
||||||
|
ldw stack, bpr
|
||||||
|
mov bpr, spr
|
||||||
|
main:
|
||||||
|
lwi program, rga
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
stw acc, rga
|
||||||
|
addi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
stw acc, rga
|
||||||
|
addi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
stw acc, rga
|
||||||
|
addi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
stw acc, rga
|
||||||
|
addi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
addi acc, 1
|
||||||
|
stw acc, rga
|
||||||
|
subi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
stw acc, rga
|
||||||
|
subi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
jez acc, loop_end_99
|
||||||
|
loop_start_99:
|
||||||
|
nop
|
||||||
|
stw acc, rga
|
||||||
|
addi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
stw acc, rga
|
||||||
|
addi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
stw acc, rga
|
||||||
|
addi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
stw acc, rga
|
||||||
|
addi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
stw acc, rga
|
||||||
|
subi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
stw acc, rga
|
||||||
|
subi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
jez acc, loop_end_116
|
||||||
|
loop_start_116:
|
||||||
|
nop
|
||||||
|
subi acc, 1
|
||||||
|
stw acc, rga
|
||||||
|
addi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
addi acc, 1
|
||||||
|
stw acc, rga
|
||||||
|
addi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
subi acc, 1
|
||||||
|
jez acc, loop_end_122
|
||||||
|
loop_start_122:
|
||||||
|
nop
|
||||||
|
stw acc, rga
|
||||||
|
addi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
addi acc, 1
|
||||||
|
stw acc, rga
|
||||||
|
addi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
stw acc, rga
|
||||||
|
addi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
jnz acc, loop_start_122
|
||||||
|
loop_end_122:
|
||||||
|
nop
|
||||||
|
stw acc, rga
|
||||||
|
addi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
jez acc, loop_end_129
|
||||||
|
loop_start_129:
|
||||||
|
nop
|
||||||
|
addi acc, 1
|
||||||
|
jez acc, loop_end_131
|
||||||
|
loop_start_131:
|
||||||
|
nop
|
||||||
|
subi acc, 1
|
||||||
|
stw acc, rga
|
||||||
|
subi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
addi acc, 1
|
||||||
|
stw acc, rga
|
||||||
|
addi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
jnz acc, loop_start_131
|
||||||
|
loop_end_131:
|
||||||
|
nop
|
||||||
|
stw acc, rga
|
||||||
|
addi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
addi acc, 1
|
||||||
|
stw acc, rga
|
||||||
|
addi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
stw acc, rga
|
||||||
|
addi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
jnz acc, loop_start_129
|
||||||
|
loop_end_129:
|
||||||
|
nop
|
||||||
|
stw acc, rga
|
||||||
|
subi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
stw acc, rga
|
||||||
|
subi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
stw acc, rga
|
||||||
|
subi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
stw acc, rga
|
||||||
|
subi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
stw acc, rga
|
||||||
|
subi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
stw acc, rga
|
||||||
|
subi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
jnz acc, loop_start_116
|
||||||
|
loop_end_116:
|
||||||
|
nop
|
||||||
|
stw acc, rga
|
||||||
|
addi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
jez acc, loop_end_150
|
||||||
|
loop_start_150:
|
||||||
|
nop
|
||||||
|
stw acc, rga
|
||||||
|
subi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
addi acc, 1
|
||||||
|
stw acc, rga
|
||||||
|
addi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
subi acc, 1
|
||||||
|
jnz acc, loop_start_150
|
||||||
|
loop_end_150:
|
||||||
|
nop
|
||||||
|
stw acc, rga
|
||||||
|
addi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
jez acc, loop_end_157
|
||||||
|
loop_start_157:
|
||||||
|
nop
|
||||||
|
subi acc, 1
|
||||||
|
jnz acc, loop_start_157
|
||||||
|
loop_end_157:
|
||||||
|
nop
|
||||||
|
stw acc, rga
|
||||||
|
addi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
stw acc, rga
|
||||||
|
addi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
stw acc, rga
|
||||||
|
addi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
stw acc, rga
|
||||||
|
subi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
jez acc, loop_end_174
|
||||||
|
loop_start_174:
|
||||||
|
nop
|
||||||
|
subi acc, 1
|
||||||
|
stw acc, rga
|
||||||
|
addi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
subi acc, 1
|
||||||
|
jez acc, loop_end_178
|
||||||
|
loop_start_178:
|
||||||
|
nop
|
||||||
|
stw acc, rga
|
||||||
|
addi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
addi acc, 1
|
||||||
|
stw acc, rga
|
||||||
|
addi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
stw acc, rga
|
||||||
|
addi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
jnz acc, loop_start_178
|
||||||
|
loop_end_178:
|
||||||
|
nop
|
||||||
|
stw acc, rga
|
||||||
|
addi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
jez acc, loop_end_185
|
||||||
|
loop_start_185:
|
||||||
|
nop
|
||||||
|
addi acc, 1
|
||||||
|
jez acc, loop_end_187
|
||||||
|
loop_start_187:
|
||||||
|
nop
|
||||||
|
subi acc, 1
|
||||||
|
stw acc, rga
|
||||||
|
subi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
addi acc, 1
|
||||||
|
stw acc, rga
|
||||||
|
addi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
jnz acc, loop_start_187
|
||||||
|
loop_end_187:
|
||||||
|
nop
|
||||||
|
stw acc, rga
|
||||||
|
addi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
addi acc, 1
|
||||||
|
stw acc, rga
|
||||||
|
addi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
stw acc, rga
|
||||||
|
addi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
jnz acc, loop_start_185
|
||||||
|
loop_end_185:
|
||||||
|
nop
|
||||||
|
stw acc, rga
|
||||||
|
subi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
stw acc, rga
|
||||||
|
subi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
stw acc, rga
|
||||||
|
subi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
stw acc, rga
|
||||||
|
subi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
stw acc, rga
|
||||||
|
subi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
jnz acc, loop_start_174
|
||||||
|
loop_end_174:
|
||||||
|
nop
|
||||||
|
stw acc, rga
|
||||||
|
addi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
jez acc, loop_end_205
|
||||||
|
loop_start_205:
|
||||||
|
nop
|
||||||
|
subi acc, 1
|
||||||
|
jnz acc, loop_start_205
|
||||||
|
loop_end_205:
|
||||||
|
nop
|
||||||
|
stw acc, rga
|
||||||
|
addi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
stw acc, rga
|
||||||
|
addi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
jez acc, loop_end_210
|
||||||
|
loop_start_210:
|
||||||
|
nop
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
push acc
|
||||||
|
call print::print_byte
|
||||||
|
pop zero
|
||||||
|
jez acc, loop_end_260
|
||||||
|
loop_start_260:
|
||||||
|
nop
|
||||||
|
subi acc, 1
|
||||||
|
jnz acc, loop_start_260
|
||||||
|
loop_end_260:
|
||||||
|
nop
|
||||||
|
jnz acc, loop_start_210
|
||||||
|
loop_end_210:
|
||||||
|
nop
|
||||||
|
stw acc, rga
|
||||||
|
subi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
jez acc, loop_end_265
|
||||||
|
loop_start_265:
|
||||||
|
nop
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
push acc
|
||||||
|
call print::print_byte
|
||||||
|
pop zero
|
||||||
|
jez acc, loop_end_315
|
||||||
|
loop_start_315:
|
||||||
|
nop
|
||||||
|
subi acc, 1
|
||||||
|
jnz acc, loop_start_315
|
||||||
|
loop_end_315:
|
||||||
|
nop
|
||||||
|
jnz acc, loop_start_265
|
||||||
|
loop_end_265:
|
||||||
|
nop
|
||||||
|
stw acc, rga
|
||||||
|
subi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
stw acc, rga
|
||||||
|
subi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
stw acc, rga
|
||||||
|
subi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
push acc
|
||||||
|
call print::print_byte
|
||||||
|
pop zero
|
||||||
|
jez acc, loop_end_371
|
||||||
|
loop_start_371:
|
||||||
|
nop
|
||||||
|
subi acc, 1
|
||||||
|
jnz acc, loop_start_371
|
||||||
|
loop_end_371:
|
||||||
|
nop
|
||||||
|
stw acc, rga
|
||||||
|
subi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
stw acc, rga
|
||||||
|
subi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
stw acc, rga
|
||||||
|
subi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
stw acc, rga
|
||||||
|
subi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
stw acc, rga
|
||||||
|
subi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
stw acc, rga
|
||||||
|
subi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
stw acc, rga
|
||||||
|
subi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
push acc
|
||||||
|
call print::print_byte
|
||||||
|
pop zero
|
||||||
|
stw acc, rga
|
||||||
|
addi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
push acc
|
||||||
|
call print::print_byte
|
||||||
|
pop zero
|
||||||
|
stw acc, rga
|
||||||
|
addi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
stw acc, rga
|
||||||
|
addi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
jez acc, loop_end_386
|
||||||
|
loop_start_386:
|
||||||
|
nop
|
||||||
|
stw acc, rga
|
||||||
|
addi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
stw acc, rga
|
||||||
|
addi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
addi acc, 1
|
||||||
|
stw acc, rga
|
||||||
|
subi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
stw acc, rga
|
||||||
|
subi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
subi acc, 1
|
||||||
|
jnz acc, loop_start_386
|
||||||
|
loop_end_386:
|
||||||
|
nop
|
||||||
|
stw acc, rga
|
||||||
|
addi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
jez acc, loop_end_395
|
||||||
|
loop_start_395:
|
||||||
|
nop
|
||||||
|
stw acc, rga
|
||||||
|
addi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
addi acc, 1
|
||||||
|
stw acc, rga
|
||||||
|
subi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
stw acc, rga
|
||||||
|
subi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
addi acc, 1
|
||||||
|
stw acc, rga
|
||||||
|
addi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
subi acc, 1
|
||||||
|
jnz acc, loop_start_395
|
||||||
|
loop_end_395:
|
||||||
|
nop
|
||||||
|
stw acc, rga
|
||||||
|
addi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
jez acc, loop_end_405
|
||||||
|
loop_start_405:
|
||||||
|
nop
|
||||||
|
stw acc, rga
|
||||||
|
subi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
addi acc, 1
|
||||||
|
stw acc, rga
|
||||||
|
addi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
subi acc, 1
|
||||||
|
jnz acc, loop_start_405
|
||||||
|
loop_end_405:
|
||||||
|
nop
|
||||||
|
stw acc, rga
|
||||||
|
subi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
stw acc, rga
|
||||||
|
subi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
stw acc, rga
|
||||||
|
subi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
subi acc, 1
|
||||||
|
jnz acc, loop_start_99
|
||||||
|
loop_end_99:
|
||||||
|
nop
|
||||||
|
stw acc, rga
|
||||||
|
subi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
stw acc, rga
|
||||||
|
subi rga, 4
|
||||||
|
ldw rga, acc
|
||||||
|
addi acc, 1
|
||||||
|
addi acc, 1
|
||||||
|
push acc
|
||||||
|
call print::print_byte
|
||||||
|
pop zero
|
||||||
|
push acc
|
||||||
|
call print::print_byte
|
||||||
|
pop zero
|
||||||
|
push acc
|
||||||
|
call print::print_byte
|
||||||
|
pop zero
|
||||||
|
hlt
|
||||||
Binary file not shown.
@@ -0,0 +1,29 @@
|
|||||||
|
++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
>++++++++++++++++++++++++++++++++
|
||||||
|
>++++++++++++++++
|
||||||
|
>
|
||||||
|
>+
|
||||||
|
<<
|
||||||
|
[
|
||||||
|
>>
|
||||||
|
>
|
||||||
|
>++++++++++
|
||||||
|
<<
|
||||||
|
[->+>-[>+>>]>[+[-<+>]>+>>]<<<<<<]
|
||||||
|
>[<+>-]
|
||||||
|
>[-]
|
||||||
|
>>
|
||||||
|
>++++++++++
|
||||||
|
<
|
||||||
|
[->-[>+>>]>[+[-<+>]>+>>]<<<<<]
|
||||||
|
>[-]
|
||||||
|
>>[++++++++++++++++++++++++++++++++++++++++++++++++.[-]]
|
||||||
|
<[++++++++++++++++++++++++++++++++++++++++++++++++.[-]]
|
||||||
|
<<<++++++++++++++++++++++++++++++++++++++++++++++++.[-]
|
||||||
|
<<<<<<<.>.
|
||||||
|
>>[>>+<<-]
|
||||||
|
>[>+<<+>-]
|
||||||
|
>[<+>-]
|
||||||
|
<<<-
|
||||||
|
]
|
||||||
|
<<++...
|
||||||
@@ -0,0 +1,135 @@
|
|||||||
|
// GENERATED BY DSC COMPILER
|
||||||
|
// Generated at 2026-08-09 01:17:46
|
||||||
|
|
||||||
|
// Imports
|
||||||
|
include alloc: "./lib/memory/block_alloc.dsa"
|
||||||
|
include print: "./print.dsa"
|
||||||
|
|
||||||
|
// Globals & Reserved Memory
|
||||||
|
|
||||||
|
// Entry Point
|
||||||
|
dw stack: 0x010000
|
||||||
|
db message: "Process Exited with code:"
|
||||||
|
_init:
|
||||||
|
ldw stack, bpr, 0
|
||||||
|
mov bpr, spr
|
||||||
|
push zero
|
||||||
|
call main
|
||||||
|
call print::newline
|
||||||
|
lwi message, rg0
|
||||||
|
push rg0
|
||||||
|
call print::print
|
||||||
|
pop zero
|
||||||
|
call print::print_hex_word
|
||||||
|
pop zero
|
||||||
|
hlt
|
||||||
|
|
||||||
|
// Return
|
||||||
|
_ret:
|
||||||
|
mov bpr, spr
|
||||||
|
pop bpr
|
||||||
|
ret
|
||||||
|
|
||||||
|
db str_4: "successful free of ptr"
|
||||||
|
// fn main() -> u32
|
||||||
|
main:
|
||||||
|
push bpr
|
||||||
|
mov spr, bpr
|
||||||
|
lli 32, rg0
|
||||||
|
lli 64, rg1
|
||||||
|
// push arg 1
|
||||||
|
push rg0
|
||||||
|
// push arg 0
|
||||||
|
push rg1
|
||||||
|
call alloc::init
|
||||||
|
pop rg2
|
||||||
|
pop zero
|
||||||
|
push rg2
|
||||||
|
// push arg 0
|
||||||
|
push rg2
|
||||||
|
call print::print_hex_word
|
||||||
|
pop zero
|
||||||
|
call print::newline
|
||||||
|
ldw spr, rg0, 0
|
||||||
|
stw rg0, spr, 0
|
||||||
|
// push arg 0
|
||||||
|
push rg0
|
||||||
|
call alloc::alloc
|
||||||
|
pop rg1
|
||||||
|
push rg1
|
||||||
|
// push arg 0
|
||||||
|
push rg1
|
||||||
|
call print::print_hex_word
|
||||||
|
pop zero
|
||||||
|
lli 200, rg0
|
||||||
|
ldw spr, rg1, 0
|
||||||
|
stw rg0, rg1, 0
|
||||||
|
stw rg1, spr, 0
|
||||||
|
call print::newline
|
||||||
|
ldw spr, rg0, 4
|
||||||
|
stw rg0, spr, 4
|
||||||
|
// push arg 0
|
||||||
|
push rg0
|
||||||
|
call alloc::alloc
|
||||||
|
pop rg2
|
||||||
|
push rg2
|
||||||
|
// push arg 0
|
||||||
|
push rg2
|
||||||
|
call print::print_hex_word
|
||||||
|
pop zero
|
||||||
|
call print::newline
|
||||||
|
ldw spr, rg0, 4
|
||||||
|
ldw rg0, rg2, 0
|
||||||
|
stw rg0, spr, 4
|
||||||
|
// push arg 0
|
||||||
|
ldw spr, rg2, 4
|
||||||
|
stw rg2, spr, 4
|
||||||
|
addi spr, 4, rg3
|
||||||
|
ldw spr, rg2, 8
|
||||||
|
stw rg2, spr, 8
|
||||||
|
// push arg 1
|
||||||
|
push rg3
|
||||||
|
// push arg 0
|
||||||
|
push rg2
|
||||||
|
call alloc::free
|
||||||
|
pop zero
|
||||||
|
pop zero
|
||||||
|
ldw spr, rg2, 8
|
||||||
|
stw rg2, spr, 8
|
||||||
|
// push arg 0
|
||||||
|
push rg2
|
||||||
|
call alloc::alloc
|
||||||
|
pop rg3
|
||||||
|
push rg3
|
||||||
|
call print::newline
|
||||||
|
ldw spr, rg2, 0
|
||||||
|
stw rg2, spr, 0
|
||||||
|
// push arg 0
|
||||||
|
push rg2
|
||||||
|
call print::print_hex_word
|
||||||
|
pop zero
|
||||||
|
call print::newline
|
||||||
|
ldw spr, rg2, 8
|
||||||
|
stw rg2, spr, 8
|
||||||
|
// push arg 0
|
||||||
|
push rg2
|
||||||
|
call print::print_hex_word
|
||||||
|
pop zero
|
||||||
|
ldw spr, rg2, 8
|
||||||
|
lli 0, rg4
|
||||||
|
ieq rg2, rg4, rg5
|
||||||
|
jez rg5, _else_2
|
||||||
|
_then_1:
|
||||||
|
lwi str_4, rg4
|
||||||
|
stw rg2, spr, 8
|
||||||
|
// push arg 0
|
||||||
|
push rg4
|
||||||
|
call print::print
|
||||||
|
pop zero
|
||||||
|
jmp _end_3
|
||||||
|
_else_2:
|
||||||
|
nop
|
||||||
|
_end_3:
|
||||||
|
lli 0, rg4
|
||||||
|
stw rg4, bpr, 8
|
||||||
|
jmp _ret
|
||||||
Binary file not shown.
@@ -0,0 +1,30 @@
|
|||||||
|
include print: "./lib/io/print.dsa";
|
||||||
|
include arena: "./lib/memory/arena_alloc.dsa";
|
||||||
|
|
||||||
|
fn main() -> u32 {
|
||||||
|
|
||||||
|
let x: u32 = 0;
|
||||||
|
let y: u32 = &x;
|
||||||
|
|
||||||
|
let alloc: u32 = arena::new(512);
|
||||||
|
let ptr1: u32 = arena::alloc(alloc, 32);
|
||||||
|
let ptr2: u32 = arena::alloc(alloc, 32);
|
||||||
|
|
||||||
|
print::print_hex_word(alloc);
|
||||||
|
print::print_newline();
|
||||||
|
print::print_hex_word(ptr1);
|
||||||
|
print::print_newline();
|
||||||
|
print::print_hex_word(ptr2);
|
||||||
|
print::print_newline();
|
||||||
|
print::print_num(*ptr2);
|
||||||
|
print::print_newline();
|
||||||
|
*ptr2 = 42;
|
||||||
|
|
||||||
|
print::print_hex_word(ptr2);
|
||||||
|
print::print_newline();
|
||||||
|
print::print_num(*ptr2);
|
||||||
|
print::print_newline();
|
||||||
|
print::println("end");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
include print: "./lib/io/print.dsa";
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
print::print("Hello, world!");
|
||||||
|
}
|
||||||
@@ -0,0 +1,212 @@
|
|||||||
|
// block_alloc.dsa
|
||||||
|
// Fixed-size block allocator
|
||||||
|
//
|
||||||
|
// Memory layout:
|
||||||
|
// [base + 0]: free list head pointer (pointer to first free block, or 0 if none)
|
||||||
|
// [base + 4]: block size
|
||||||
|
// [base + 8]: total blocks
|
||||||
|
// [base + 12]: base address of block pool
|
||||||
|
// [base + 16+]: block pool (each block starts with a 4-byte next pointer)
|
||||||
|
//
|
||||||
|
// Usage:
|
||||||
|
// include block_alloc "./lib/memory/block_alloc.dsa"
|
||||||
|
//
|
||||||
|
// For init:
|
||||||
|
// push num_blocks (e.g., 32)
|
||||||
|
// push block_size (e.g., 64 bytes)
|
||||||
|
// call block_alloc::init
|
||||||
|
// pop block_size
|
||||||
|
// pop num_blocks
|
||||||
|
// ; result in spr+8 (allocator handle)
|
||||||
|
//
|
||||||
|
// For alloc:
|
||||||
|
// push allocator_handle
|
||||||
|
// call block_alloc::alloc
|
||||||
|
// pop allocator_handle
|
||||||
|
// ; result in spr+8 (pointer to block, or 0 if out of memory)
|
||||||
|
//
|
||||||
|
// For free:
|
||||||
|
// push block_pointer
|
||||||
|
// push allocator_handle
|
||||||
|
// call block_alloc::free
|
||||||
|
// pop allocator_handle
|
||||||
|
// pop block_pointer
|
||||||
|
|
||||||
|
dw heap_start: 0x30000 // Start of our heap area
|
||||||
|
|
||||||
|
// Initialize the allocator
|
||||||
|
// Args: block_size, num_blocks
|
||||||
|
// Returns: allocator handle (pointer to metadata)
|
||||||
|
init:
|
||||||
|
push bpr
|
||||||
|
mov spr, bpr
|
||||||
|
|
||||||
|
ldw bpr, rg0, 8 // block_size
|
||||||
|
ldw bpr, rg1, 12 // num_blocks
|
||||||
|
|
||||||
|
// Allocate metadata (16 bytes) + pool space
|
||||||
|
ldw heap_start, rg2 // base address for this allocator
|
||||||
|
mov rg2, rg3 // save base in rg3
|
||||||
|
|
||||||
|
// Calculate total size needed: 16 + (block_size * num_blocks)
|
||||||
|
// We'll use a simple multiplication by repeated addition
|
||||||
|
mov rg0, rg4 // block_size to rg4
|
||||||
|
mov rg1, rg5 // num_blocks to rg5
|
||||||
|
lli 0, acc // accumulator for multiplication
|
||||||
|
|
||||||
|
_multiply_loop:
|
||||||
|
jez rg5 _multiply_done
|
||||||
|
add acc, rg4, acc
|
||||||
|
subi rg5, 1
|
||||||
|
jmp _multiply_loop
|
||||||
|
|
||||||
|
_multiply_done:
|
||||||
|
// acc now contains block_size * num_blocks
|
||||||
|
addi acc, 16 // add metadata size
|
||||||
|
|
||||||
|
// Update heap_start for next allocation
|
||||||
|
add rg2, acc, acc
|
||||||
|
stw acc, heap_start
|
||||||
|
|
||||||
|
// Now set up metadata at rg3 (base)
|
||||||
|
// [base + 0]: free list head (will point to first block)
|
||||||
|
// [base + 4]: block_size
|
||||||
|
// [base + 8]: total blocks
|
||||||
|
// [base + 12]: pool base address
|
||||||
|
|
||||||
|
addi rg3, 16, rg6 // rg6 = pool base
|
||||||
|
|
||||||
|
stw rg6, rg3 // store pool base as free list head initially
|
||||||
|
stw rg0, rg3, 4 // store block_size
|
||||||
|
stw rg1, rg3, 8 // store total blocks
|
||||||
|
stw rg6, rg3, 12 // store pool base address
|
||||||
|
|
||||||
|
// Now initialize the free list
|
||||||
|
// Each block's first 4 bytes point to the next block
|
||||||
|
// rg6 = current block pointer
|
||||||
|
// rg0 = block_size
|
||||||
|
// rg1 = num_blocks (counter)
|
||||||
|
|
||||||
|
subi rg1 1 // we'll count down from num_blocks-1
|
||||||
|
|
||||||
|
_init_loop:
|
||||||
|
jez rg1, _init_loop_done
|
||||||
|
|
||||||
|
// Calculate next block address: current + block_size
|
||||||
|
add rg6, rg0, rg7 // rg7 = next block address
|
||||||
|
|
||||||
|
// Store next pointer at current block
|
||||||
|
stw rg7, rg6
|
||||||
|
|
||||||
|
// Move to next block
|
||||||
|
mov rg7, rg6
|
||||||
|
subi rg1, 1
|
||||||
|
jmp _init_loop
|
||||||
|
|
||||||
|
_init_loop_done:
|
||||||
|
// Last block points to null (0)
|
||||||
|
lli 0, acc
|
||||||
|
stw acc, rg6
|
||||||
|
|
||||||
|
// Return allocator handle (base address - 16 to get back to metadata start)
|
||||||
|
stw rg3, bpr, 8
|
||||||
|
|
||||||
|
mov bpr, spr
|
||||||
|
pop bpr
|
||||||
|
ret
|
||||||
|
|
||||||
|
|
||||||
|
// Allocate a block
|
||||||
|
// Args: allocator_handle
|
||||||
|
// Returns: pointer to block (or 0 if out of memory)
|
||||||
|
alloc:
|
||||||
|
push bpr
|
||||||
|
mov spr, bpr
|
||||||
|
|
||||||
|
ldw bpr, rg0, 8 // allocator handle (metadata base)
|
||||||
|
|
||||||
|
// Load free list head
|
||||||
|
ldw rg0, rg1 // rg1 = free list head
|
||||||
|
|
||||||
|
// Check if free list is empty
|
||||||
|
jez, rg1 _alloc_fail
|
||||||
|
|
||||||
|
// Free list is not empty, pop the first block
|
||||||
|
// Load the next pointer from the block we're allocating
|
||||||
|
ldw rg1, rg2 // rg2 = next free block
|
||||||
|
|
||||||
|
// Update free list head to point to next block
|
||||||
|
stw rg2, rg0
|
||||||
|
|
||||||
|
// Return the allocated block (rg1)
|
||||||
|
stw rg1, bpr, 8
|
||||||
|
jmp _alloc_done
|
||||||
|
|
||||||
|
_alloc_fail:
|
||||||
|
// No free blocks, return 0
|
||||||
|
lli 0, acc
|
||||||
|
stw acc, bpr, 8
|
||||||
|
|
||||||
|
_alloc_done:
|
||||||
|
mov bpr, spr
|
||||||
|
pop bpr
|
||||||
|
ret
|
||||||
|
|
||||||
|
|
||||||
|
// Free a block
|
||||||
|
// Args: allocator_handle, block_pointer
|
||||||
|
// Returns: nothing (but could return error code if block is invalid)
|
||||||
|
free:
|
||||||
|
push bpr
|
||||||
|
mov spr, bpr
|
||||||
|
|
||||||
|
ldw bpr, rg0, 8 // allocator handle
|
||||||
|
ldw bpr, rg6, 12 // pointer to the block pointer to free
|
||||||
|
ldw rg6, rg1 // rg1 = block pointer to free
|
||||||
|
|
||||||
|
// Load current free list head
|
||||||
|
ldw rg0, rg2 // rg2 = current head
|
||||||
|
|
||||||
|
// Set the freed block's next pointer to current head
|
||||||
|
stw rg2, rg1
|
||||||
|
|
||||||
|
// Update free list head to point to freed block
|
||||||
|
stw rg1, rg0
|
||||||
|
|
||||||
|
// Update the freed block's previous pointer to NULL
|
||||||
|
lli 0, rg1
|
||||||
|
stw rg1, rg6
|
||||||
|
|
||||||
|
mov bpr, spr
|
||||||
|
pop bpr
|
||||||
|
ret
|
||||||
|
|
||||||
|
|
||||||
|
// Debug function: get stats
|
||||||
|
// Args: allocator_handle
|
||||||
|
// Returns: nothing (but could populate a stats structure)
|
||||||
|
get_stats:
|
||||||
|
push bpr
|
||||||
|
mov spr, bpr
|
||||||
|
|
||||||
|
ldw bpr, rg0, 8 // allocator handle
|
||||||
|
|
||||||
|
// Count free blocks by traversing the free list
|
||||||
|
ldw rg0, rg1 // rg1 = free list head
|
||||||
|
lli 0, rg2 // rg2 = counter
|
||||||
|
|
||||||
|
count_loop:
|
||||||
|
jez, rg1 count_done
|
||||||
|
|
||||||
|
addi rg2, 1
|
||||||
|
ldw rg1, rg1 // move to next block
|
||||||
|
jmp count_loop
|
||||||
|
|
||||||
|
count_done:
|
||||||
|
// rg2 now contains number of free blocks
|
||||||
|
// Could store this somewhere or return it
|
||||||
|
stw rg2, bpr, 8
|
||||||
|
|
||||||
|
mov bpr, spr
|
||||||
|
pop bpr
|
||||||
|
ret
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
include print: "./lib/io/print.dsa";
|
||||||
|
include alloc: "./lib/memory/block_alloc.dsa";
|
||||||
|
|
||||||
|
fn main() -> u32 {
|
||||||
|
let allocator: u32 = alloc::init(64, 32);
|
||||||
|
|
||||||
|
print::print_hex_word(allocator);
|
||||||
|
|
||||||
|
print::print_newline();
|
||||||
|
|
||||||
|
let ptr: u32 = alloc::alloc(allocator);
|
||||||
|
print::print_hex_word(ptr);
|
||||||
|
*ptr = 200;
|
||||||
|
|
||||||
|
print::print_newline();
|
||||||
|
|
||||||
|
let p2: u32 = alloc::alloc(allocator);
|
||||||
|
print::print_hex_word(p2);
|
||||||
|
print::print_newline();
|
||||||
|
print::print_num(*ptr);
|
||||||
|
|
||||||
|
alloc::free(allocator, &ptr);
|
||||||
|
let ptr3: u32 = alloc::alloc(allocator);
|
||||||
|
|
||||||
|
print::print_newline();
|
||||||
|
|
||||||
|
print::print_hex_word(ptr3);
|
||||||
|
|
||||||
|
print::print_newline();
|
||||||
|
print::print_hex_word(ptr);
|
||||||
|
|
||||||
|
if ptr == 0 {
|
||||||
|
print::print("successful free of ptr");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
+62
-23
@@ -129,42 +129,67 @@ print_byte: func
|
|||||||
|
|
||||||
// ------------------------------------------
|
// ------------------------------------------
|
||||||
// prints the value of arg[0] to the screen in hex.
|
// prints the value of arg[0] to the screen in hex.
|
||||||
// print_hex_word:
|
print_hex_word: func
|
||||||
// push bpr
|
push rg0
|
||||||
// mov spr, bpr
|
push rg1
|
||||||
|
push rg2
|
||||||
|
push rg3
|
||||||
|
push rg4
|
||||||
|
|
||||||
// ldw current, rg1
|
ldw current, rg1
|
||||||
|
ldb bpr, rg0, 11
|
||||||
|
push rg0
|
||||||
|
call _print_hex_byte
|
||||||
|
addi spr, 4
|
||||||
|
|
||||||
// ldb bpr, rg0, 8
|
ldb bpr, rg0, 10
|
||||||
// push rg0
|
push rg0
|
||||||
// call _print_hex_byte
|
call _print_hex_byte
|
||||||
// addi spr, 4
|
addi spr, 4
|
||||||
|
|
||||||
// ldb bpr, rg0, 9
|
ldb bpr, rg0, 9
|
||||||
// push rg0
|
push rg0
|
||||||
// call _print_hex_byte
|
call _print_hex_byte
|
||||||
// addi spr, 4
|
addi spr, 4
|
||||||
|
|
||||||
// ldb bpr, rg0, 10
|
ldb bpr, rg0, 8
|
||||||
// push rg0
|
push rg0
|
||||||
// call _print_hex_byte
|
call _print_hex_byte
|
||||||
// addi spr, 4
|
addi spr, 4
|
||||||
|
|
||||||
// ldb bpr, rg0, 11
|
// save cursor
|
||||||
// push rg0
|
stw rg1, current
|
||||||
// call _print_hex_byte
|
|
||||||
// addi spr, 4
|
|
||||||
|
|
||||||
// jmp _end
|
pop rg4
|
||||||
|
pop rg3
|
||||||
|
pop rg2
|
||||||
|
pop rg1
|
||||||
|
pop rg0
|
||||||
|
return
|
||||||
|
|
||||||
// ------------------------------------------
|
// ------------------------------------------
|
||||||
// prints the last byte of arg[0] to the screen in hex.
|
// prints the last byte of arg[0] to the screen in hex.
|
||||||
print_hex_byte: func
|
print_hex_byte: func
|
||||||
|
push rg0
|
||||||
|
push rg1
|
||||||
|
push rg2
|
||||||
|
push rg3
|
||||||
|
push rg4
|
||||||
|
|
||||||
ldw bpr, rg0, 8
|
ldw bpr, rg0, 8
|
||||||
ldw current, rg1
|
ldw current, rg1
|
||||||
|
|
||||||
call _print_hex_byte
|
call _print_hex_byte
|
||||||
jmp _end
|
|
||||||
|
// save cursor
|
||||||
|
stw rg1, current
|
||||||
|
|
||||||
|
pop rg4
|
||||||
|
pop rg3
|
||||||
|
pop rg2
|
||||||
|
pop rg1
|
||||||
|
pop rg0
|
||||||
|
return
|
||||||
|
|
||||||
// function body
|
// function body
|
||||||
_print_hex_byte:
|
_print_hex_byte:
|
||||||
@@ -183,14 +208,18 @@ _print_hex_byte:
|
|||||||
// print a hex digit
|
// print a hex digit
|
||||||
_print_hex_nibble:
|
_print_hex_nibble:
|
||||||
lli 10, rg3
|
lli 10, rg3
|
||||||
|
|
||||||
ilt rg0, rg3, rg4
|
ilt rg0, rg3, rg4
|
||||||
jnz rg4, _print_hex_nibble_number
|
jnz rg4, _print_hex_nibble_number
|
||||||
|
|
||||||
|
// print a character 'A'..='F' by applying offset 0x37 ('A' - 10)
|
||||||
addi rg0, 0x37, rg0
|
addi rg0, 0x37, rg0
|
||||||
stb rg0, rg1
|
stb rg0, rg1
|
||||||
addi rg1, 1
|
addi rg1, 1
|
||||||
ret
|
ret
|
||||||
|
|
||||||
// helper function.
|
// helper function.
|
||||||
|
// prints a decimal digit by adding 0x30 ('0')
|
||||||
_print_hex_nibble_number:
|
_print_hex_nibble_number:
|
||||||
addi rg0, 0x30, rg0
|
addi rg0, 0x30, rg0
|
||||||
stb rg0, rg1
|
stb rg0, rg1
|
||||||
@@ -200,11 +229,21 @@ _print_hex_nibble_number:
|
|||||||
// ------------------------------------------
|
// ------------------------------------------
|
||||||
// print whitespace
|
// print whitespace
|
||||||
print_whitespace: func
|
print_whitespace: func
|
||||||
|
push rg0
|
||||||
|
push rg1
|
||||||
|
|
||||||
ldw current, rg1
|
ldw current, rg1
|
||||||
lli 0x20, rg0
|
lli 0x20, rg0
|
||||||
stb rg0, rg1
|
stb rg0, rg1
|
||||||
addi rg1, 1
|
addi rg1, 1
|
||||||
jmp _end
|
|
||||||
|
// save cursor
|
||||||
|
stw rg1, current
|
||||||
|
|
||||||
|
pop rg1
|
||||||
|
pop rg0
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
// ------------------------------------------
|
// ------------------------------------------
|
||||||
// print newline
|
// print newline
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
|
||||||
|
include print: "./print.dsa"
|
||||||
|
dw stack: 0x10000
|
||||||
|
|
||||||
|
_init:
|
||||||
|
ldw stack, bpr
|
||||||
|
mov bpr, spr
|
||||||
|
main:
|
||||||
|
push zero
|
||||||
|
call program
|
||||||
|
call print::print_hex_word
|
||||||
|
pop zero
|
||||||
|
hlt
|
||||||
|
|
||||||
|
program: func
|
||||||
|
push rg0
|
||||||
|
push rg1
|
||||||
|
lli 5, rg0
|
||||||
|
push rg0
|
||||||
|
lli 4, rg0
|
||||||
|
push rg0
|
||||||
|
pop rg1
|
||||||
|
pop rg0
|
||||||
|
sub rg0, rg1, rg0
|
||||||
|
push rg0
|
||||||
|
lli 4, rg0
|
||||||
|
push rg0
|
||||||
|
lli 3, rg0
|
||||||
|
push rg0
|
||||||
|
pop rg1
|
||||||
|
pop rg0
|
||||||
|
sub rg0, rg1, rg0
|
||||||
|
push rg0
|
||||||
|
lli 3, rg0
|
||||||
|
push rg0
|
||||||
|
lli 2, rg0
|
||||||
|
push rg0
|
||||||
|
pop rg1
|
||||||
|
pop rg0
|
||||||
|
sub rg0, rg1, rg0
|
||||||
|
push rg0
|
||||||
|
lli 2, rg0
|
||||||
|
push rg0
|
||||||
|
lli 1, rg0
|
||||||
|
push rg0
|
||||||
|
pop rg1
|
||||||
|
pop rg0
|
||||||
|
sub rg0, rg1, rg0
|
||||||
|
push rg0
|
||||||
|
lli 60, rg0
|
||||||
|
push rg0
|
||||||
|
pop rg1
|
||||||
|
pop rg0
|
||||||
|
add rg0, rg1, rg0
|
||||||
|
push rg0
|
||||||
|
pop rg1
|
||||||
|
pop rg0
|
||||||
|
add rg0, rg1, rg0
|
||||||
|
push rg0
|
||||||
|
pop rg1
|
||||||
|
pop rg0
|
||||||
|
add rg0, rg1, rg0
|
||||||
|
push rg0
|
||||||
|
pop rg1
|
||||||
|
pop rg0
|
||||||
|
add rg0, rg1, rg0
|
||||||
|
push rg0
|
||||||
|
|
||||||
|
// return value in bpr+8
|
||||||
|
pop rg0
|
||||||
|
stw rg0, bpr, 8
|
||||||
|
|
||||||
|
// epilogue
|
||||||
|
pop rg1
|
||||||
|
pop rg0
|
||||||
|
return
|
||||||
+13
-1
@@ -1 +1,13 @@
|
|||||||
(add 1 (or 1 (if true then 2 else 1)))
|
(add
|
||||||
|
(sub 5 4)
|
||||||
|
(add
|
||||||
|
(sub 4 3)
|
||||||
|
(add
|
||||||
|
(sub 3 2)
|
||||||
|
(add
|
||||||
|
(sub 2 1)
|
||||||
|
60
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
include print: "./lib/io/print.dsa";
|
||||||
|
|
||||||
|
fn main() -> u32 {
|
||||||
|
let x: u32 = 30;
|
||||||
|
print::print_num(x);
|
||||||
|
|
||||||
|
200 + 5;
|
||||||
|
|
||||||
|
let p: Point = Point {
|
||||||
|
x: 10,
|
||||||
|
y: 20,
|
||||||
|
test: [2, 3, 4]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Point {
|
||||||
|
x: u32,
|
||||||
|
y: u32,
|
||||||
|
test: [u32; 3],
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
include print: "./print.dsa"
|
||||||
|
|
||||||
|
dw stack: 0x10000
|
||||||
|
|
||||||
|
_init:
|
||||||
|
lwi stack, bpr
|
||||||
|
mov bpr, spr
|
||||||
|
|
||||||
|
lwi 0x100abcaa, rg0
|
||||||
|
push rg0
|
||||||
|
call print::print_hex_word
|
||||||
|
pop zero
|
||||||
|
hlt
|
||||||
@@ -39,18 +39,18 @@ pub use self::{
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Assembler {
|
pub struct Assembler {
|
||||||
src_path: PathBuf,
|
|
||||||
result_tx: mpsc::Sender<Result<Vec<u8>, AssembleError>>,
|
result_tx: mpsc::Sender<Result<Vec<u8>, AssembleError>>,
|
||||||
result_rx: Option<mpsc::Receiver<Result<Vec<u8>, AssembleError>>>,
|
result_rx: Option<mpsc::Receiver<Result<Vec<u8>, AssembleError>>>,
|
||||||
is_running: bool,
|
is_running: bool,
|
||||||
|
filename: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Assembler {
|
impl Assembler {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(src_path: impl Into<PathBuf>) -> Self {
|
pub fn new(filename: impl Into<String>) -> Self {
|
||||||
let (tx, rx) = mpsc::channel();
|
let (tx, rx) = mpsc::channel();
|
||||||
Self {
|
Self {
|
||||||
src_path: src_path.into(),
|
filename: filename.into(),
|
||||||
result_tx: tx,
|
result_tx: tx,
|
||||||
result_rx: Some(rx),
|
result_rx: Some(rx),
|
||||||
is_running: false,
|
is_running: false,
|
||||||
@@ -59,21 +59,20 @@ impl Assembler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TraceableProcess for Assembler {
|
impl TraceableProcess for Assembler {
|
||||||
|
type Input = PathBuf;
|
||||||
type Output = DseExecutable;
|
type Output = DseExecutable;
|
||||||
type Error = AssembleError;
|
type Error = AssembleError;
|
||||||
|
|
||||||
fn run(mut self: Box<Self>, ctx: ProcessAgent) -> Result<Self::Output, Self::Error> {
|
fn label(&self) -> String {
|
||||||
assemble(&self.src_path, &ctx)
|
format!("Assembling {}", self.filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn label(&self) -> String {
|
fn run(
|
||||||
format!(
|
self: Box<Self>,
|
||||||
"Assembling {}",
|
input: impl Into<Self::Input>,
|
||||||
self.src_path
|
ctx: ProcessAgent,
|
||||||
.file_name()
|
) -> Result<Self::Output, Self::Error> {
|
||||||
.expect("a valid path must have a file name")
|
assemble(&input.into(), &ctx)
|
||||||
.to_string_lossy()
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,8 +17,11 @@ fn main() {
|
|||||||
// Parse command line arguments
|
// Parse command line arguments
|
||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
|
|
||||||
let assembler = Assembler::new(PathBuf::from(args.input_path));
|
let assembler = Assembler::new(args.input_path.file_name().unwrap().to_string_lossy());
|
||||||
let result = assembler.run_sync().expect("assembler failed.");
|
let result = assembler
|
||||||
|
.run_sync(&args.input_path)
|
||||||
|
.expect("assembler failed.");
|
||||||
|
|
||||||
let mut buffer = Cursor::new(Vec::new());
|
let mut buffer = Cursor::new(Vec::new());
|
||||||
result.write(&mut buffer).unwrap();
|
result.write(&mut buffer).unwrap();
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
[package]
|
||||||
|
name = "bf_compiler"
|
||||||
|
edition.workspace = true
|
||||||
|
version.workspace = true
|
||||||
|
authors.workspace = true
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "dsabf"
|
||||||
|
path = "src/main.rs"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "dsabf_compiler"
|
||||||
|
path = "src/lib.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
clap = { version = "4.6.0", features = ["derive"] }
|
||||||
|
common = { path = "../../libraries/common" }
|
||||||
|
dsa_formats = { path = "../../libraries/dsa_formats" }
|
||||||
|
dsa_tasks = { path = "../../libraries/dsa_tasks" }
|
||||||
@@ -0,0 +1,162 @@
|
|||||||
|
use dsa_tasks::{ProcessAgent, TraceableProcess};
|
||||||
|
use std::fmt::{Display, Formatter};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct BfError(&'static str);
|
||||||
|
impl std::error::Error for BfError {}
|
||||||
|
impl Display for BfError {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{self:?}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct BfCompiler {
|
||||||
|
filename: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BfCompiler {
|
||||||
|
pub fn new(filename: impl Into<String>) -> Self {
|
||||||
|
Self {
|
||||||
|
filename: filename.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TraceableProcess for BfCompiler {
|
||||||
|
type Input = String;
|
||||||
|
type Output = String;
|
||||||
|
type Error = BfError;
|
||||||
|
|
||||||
|
fn label(&self) -> String {
|
||||||
|
format!("Compiling {}", self.filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
self: Box<Self>,
|
||||||
|
input: impl Into<Self::Input>,
|
||||||
|
ctx: ProcessAgent,
|
||||||
|
) -> Result<Self::Output, Self::Error> {
|
||||||
|
ctx.log(format!("Building {}", self.filename));
|
||||||
|
|
||||||
|
let asm_output = parse(&input.into(), &ctx);
|
||||||
|
|
||||||
|
ctx.log(format!("Successfully compiled {}", self.filename));
|
||||||
|
Ok(asm_output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
#[expect(clippy::too_many_lines)]
|
||||||
|
pub fn parse(src: &str, ctx: &ProcessAgent) -> String {
|
||||||
|
let mut result = String::new();
|
||||||
|
|
||||||
|
ctx.log("Parsing Source");
|
||||||
|
let tokens = lex(src);
|
||||||
|
|
||||||
|
ctx.log("Building Header");
|
||||||
|
|
||||||
|
result.push_str(
|
||||||
|
"
|
||||||
|
include print: \"./print.dsa\"
|
||||||
|
|
||||||
|
resb program: 0x1000
|
||||||
|
dw stack: 0x10000
|
||||||
|
dw input: 0x30000
|
||||||
|
|
||||||
|
_init:
|
||||||
|
ldw stack, bpr
|
||||||
|
mov bpr, spr
|
||||||
|
main:
|
||||||
|
lwi program, rga\n",
|
||||||
|
);
|
||||||
|
|
||||||
|
ctx.log("Building Program");
|
||||||
|
|
||||||
|
// let _id = 0;
|
||||||
|
let mut idstack = Vec::<u32>::new();
|
||||||
|
|
||||||
|
for (id, tok) in tokens.iter().enumerate() {
|
||||||
|
match tok {
|
||||||
|
BfToken::Inc => result.push_str("\taddi acc, 1\n"),
|
||||||
|
BfToken::Dec => result.push_str("\tsubi acc, 1\n"),
|
||||||
|
BfToken::IncPtr => {
|
||||||
|
result.push_str("\tstw acc, rga\n");
|
||||||
|
result.push_str("\taddi rga, 4\n");
|
||||||
|
result.push_str("\tldw rga, acc\n");
|
||||||
|
}
|
||||||
|
BfToken::DecPtr => {
|
||||||
|
result.push_str("\tstw acc, rga\n");
|
||||||
|
result.push_str("\tsubi rga, 4\n");
|
||||||
|
result.push_str("\tldw rga, acc\n");
|
||||||
|
}
|
||||||
|
BfToken::Out => {
|
||||||
|
result.push_str("\tpush acc\n");
|
||||||
|
result.push_str("\tcall print::print_byte\n");
|
||||||
|
result.push_str("\tpop zero\n");
|
||||||
|
}
|
||||||
|
// Read a byte from input and store it at the current data pointer
|
||||||
|
// Assuming we have an input function mapped to a specific memory location or I/O port
|
||||||
|
BfToken::In => result.push_str("\tldw input acc\n"),
|
||||||
|
BfToken::Forward => {
|
||||||
|
// Start of loop [
|
||||||
|
let loop_start = format!("loop_start_{id}");
|
||||||
|
let loop_end = format!("loop_end_{id}");
|
||||||
|
|
||||||
|
// Push the current position for the matching ]
|
||||||
|
idstack.push(id as u32);
|
||||||
|
|
||||||
|
// Load current cell value and check if zero
|
||||||
|
result.push_str(&format!("\tjez acc, {loop_end}\n"));
|
||||||
|
|
||||||
|
result.push_str(&format!("{loop_start}:\n"));
|
||||||
|
result.push_str("\tnop\n");
|
||||||
|
}
|
||||||
|
BfToken::Back => {
|
||||||
|
// End of loop ]
|
||||||
|
if let Some(start_id) = idstack.pop() {
|
||||||
|
let loop_start = format!("loop_start_{start_id}");
|
||||||
|
let loop_end = format!("loop_end_{start_id}");
|
||||||
|
|
||||||
|
result.push_str(&format!("\tjnz acc, {loop_start}\n"));
|
||||||
|
result.push_str(&format!("{loop_end}:\n"));
|
||||||
|
result.push_str("\tnop\n");
|
||||||
|
} else {
|
||||||
|
// Unmatched closing bracket - could add error handling here
|
||||||
|
eprintln!("Warning: Unmatched ']' at position {id}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.push_str("\thlt\n");
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
enum BfToken {
|
||||||
|
Inc,
|
||||||
|
Dec,
|
||||||
|
IncPtr,
|
||||||
|
DecPtr,
|
||||||
|
Out,
|
||||||
|
In,
|
||||||
|
Forward,
|
||||||
|
Back,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lex(src: &str) -> Vec<BfToken> {
|
||||||
|
src.chars()
|
||||||
|
.filter_map(|c| match c {
|
||||||
|
'+' => Some(BfToken::Inc),
|
||||||
|
'-' => Some(BfToken::Dec),
|
||||||
|
'>' => Some(BfToken::IncPtr),
|
||||||
|
'<' => Some(BfToken::DecPtr),
|
||||||
|
'.' => Some(BfToken::Out),
|
||||||
|
',' => Some(BfToken::In),
|
||||||
|
'[' => Some(BfToken::Forward),
|
||||||
|
']' => Some(BfToken::Back),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
use clap::Parser;
|
||||||
|
use dsa_tasks::TraceableProcess;
|
||||||
|
use dsabf_compiler::BfCompiler;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
#[derive(Parser, Debug, Clone)]
|
||||||
|
#[command(version, about, long_about = None)]
|
||||||
|
struct Args {
|
||||||
|
#[arg(short = 'i')]
|
||||||
|
pub input_path: PathBuf,
|
||||||
|
#[arg(short = 'o')]
|
||||||
|
pub output_path: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// Parse command line arguments
|
||||||
|
let args = Args::parse();
|
||||||
|
|
||||||
|
let bf_src = std::fs::read_to_string(&args.input_path).unwrap();
|
||||||
|
let result = BfCompiler::new(
|
||||||
|
args.input_path
|
||||||
|
.file_stem()
|
||||||
|
.unwrap()
|
||||||
|
.to_string_lossy()
|
||||||
|
.to_string(),
|
||||||
|
)
|
||||||
|
.run_sync(bf_src)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
println!("Successfully compiled {}", args.input_path.display());
|
||||||
|
println!("Writing result to {}", args.output_path.display());
|
||||||
|
|
||||||
|
std::fs::write(&args.output_path, result).unwrap();
|
||||||
|
|
||||||
|
println!("Success");
|
||||||
|
}
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "compiler"
|
|
||||||
edition.workspace = true
|
|
||||||
version.workspace = true
|
|
||||||
authors.workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
pub fn add(left: u64, right: u64) -> u64 {
|
|
||||||
left + right
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn it_works() {
|
|
||||||
let result = add(2, 2);
|
|
||||||
assert_eq!(result, 4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
[package]
|
||||||
|
name = "dscs_compiler"
|
||||||
|
edition.workspace = true
|
||||||
|
version.workspace = true
|
||||||
|
authors.workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
clap = { version = "4.6.0", features = ["derive"] }
|
||||||
|
common = { path = "../../libraries/common" }
|
||||||
|
dsa_formats = { path = "../../libraries/dsa_formats" }
|
||||||
|
dsa_tasks = { path = "../../libraries/dsa_tasks" }
|
||||||
|
assembler = { path = "../assembler" }
|
||||||
|
chrono = "0.4.45"
|
||||||
@@ -0,0 +1,883 @@
|
|||||||
|
use super::instruction::{InsBlock as IB, Instruction as I, Label};
|
||||||
|
use super::registers::{Register, RegisterAllocator};
|
||||||
|
use crate::backend::instruction::Comparison::{
|
||||||
|
Equal, Greater, GreaterEqual, Less, LessEqual, NotEqual,
|
||||||
|
};
|
||||||
|
use crate::model::{
|
||||||
|
AssignmentOperator, BinaryOperator, Call, CompilerError, ConstExpr, Declaration, Dependency,
|
||||||
|
Expression, Number, Program, Statement, TypeId, UnaryOperator, Variable,
|
||||||
|
};
|
||||||
|
use chrono::{DateTime, Local};
|
||||||
|
use dsa_tasks::ProcessAgent;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::sync::atomic::AtomicU32;
|
||||||
|
use std::time::SystemTime;
|
||||||
|
|
||||||
|
pub struct CodeGenerator {
|
||||||
|
ast: Program,
|
||||||
|
imports: HashMap<String, I>,
|
||||||
|
globals: HashMap<String, I>,
|
||||||
|
functions: Vec<IB>,
|
||||||
|
symbols: Vec<String>,
|
||||||
|
allocator: RegisterAllocator,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CodeGenerator {
|
||||||
|
pub fn new(ast: Program) -> Self {
|
||||||
|
CodeGenerator {
|
||||||
|
ast,
|
||||||
|
imports: HashMap::new(),
|
||||||
|
globals: HashMap::new(),
|
||||||
|
functions: Vec::new(),
|
||||||
|
symbols: Vec::new(),
|
||||||
|
allocator: RegisterAllocator::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn include(&mut self, name: impl Into<String>, path: impl Into<String>) {
|
||||||
|
let name = name.into();
|
||||||
|
self.imports.insert(name.clone(), I::include(name, path));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_global(&self, name: &str) -> bool {
|
||||||
|
// Check if this variable is in the globals list
|
||||||
|
self.globals.contains_key(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate(&mut self, ctx: &ProcessAgent) -> Result<String, CompilerError> {
|
||||||
|
// always include the print library for debugging!
|
||||||
|
self.include("print", "./lib/print.dsa");
|
||||||
|
|
||||||
|
for block in self.ast.clone().declarations {
|
||||||
|
match block {
|
||||||
|
Declaration::Variable {
|
||||||
|
var: Variable { name, .. },
|
||||||
|
..
|
||||||
|
} => self.symbols.push(name),
|
||||||
|
Declaration::Function { name, .. } => self.symbols.push(name),
|
||||||
|
Declaration::Dependency(Dependency { name, .. }) => self.symbols.push(name),
|
||||||
|
Declaration::Struct { .. } => {} /* we can't do any code generation for
|
||||||
|
* a struct yet. we may need to later
|
||||||
|
* once these become class-like
|
||||||
|
* objects with implementations */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for block in self.ast.clone().declarations {
|
||||||
|
self.generate_block(block.clone())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let assembly = self.generate_layout()?;
|
||||||
|
Ok(assembly
|
||||||
|
.iter()
|
||||||
|
.map(|i| i.to_string())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("\n"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_layout(&mut self) -> Result<IB, CompilerError> {
|
||||||
|
let datetime: DateTime<Local> = SystemTime::now().into();
|
||||||
|
|
||||||
|
let mut block = IB::new();
|
||||||
|
|
||||||
|
block.extend(vec![
|
||||||
|
I::global_comment(format!(
|
||||||
|
"GENERATED BY DSC COMPILER
|
||||||
|
Generated at {}",
|
||||||
|
datetime.format("%Y-%m-%d %H:%M:%S")
|
||||||
|
)),
|
||||||
|
I::Newline,
|
||||||
|
I::global_comment("Imports"),
|
||||||
|
]);
|
||||||
|
|
||||||
|
block.extend(self.imports.values().cloned().collect::<Vec<_>>());
|
||||||
|
|
||||||
|
block.extend(vec![
|
||||||
|
I::Newline,
|
||||||
|
I::global_comment("Globals & Reserved Memory"),
|
||||||
|
]);
|
||||||
|
|
||||||
|
block.extend(self.globals.values().cloned().collect::<Vec<_>>());
|
||||||
|
|
||||||
|
block.extend(vec![
|
||||||
|
I::Newline,
|
||||||
|
I::global_comment("Entry Point"),
|
||||||
|
I::db_word("stack", 0x10000),
|
||||||
|
I::db_string("message", "Process Exited with code:"),
|
||||||
|
// init function for stack setup.
|
||||||
|
I::label("_init"),
|
||||||
|
I::ldw_label("stack", Register::Bpr),
|
||||||
|
I::mov(Register::Bpr, Register::Spr),
|
||||||
|
I::push(Register::Zero),
|
||||||
|
I::call("main"),
|
||||||
|
I::call("print::print_newline"),
|
||||||
|
I::lwi_label("message", Register::Rg0),
|
||||||
|
I::push(Register::Rg0),
|
||||||
|
I::call("print::print"),
|
||||||
|
I::pop(Register::Zero),
|
||||||
|
I::call("print::print_hex_word"),
|
||||||
|
I::pop(Register::Zero),
|
||||||
|
I::Hlt,
|
||||||
|
]);
|
||||||
|
|
||||||
|
block.extend(vec![
|
||||||
|
I::Newline,
|
||||||
|
// default return block boilerplate
|
||||||
|
I::global_comment("Return"),
|
||||||
|
I::label("_ret"),
|
||||||
|
I::mov(Register::Bpr, Register::Spr),
|
||||||
|
I::pop(Register::Bpr),
|
||||||
|
I::Return,
|
||||||
|
]);
|
||||||
|
|
||||||
|
for function in self.functions.iter() {
|
||||||
|
block.extend(function.iter().cloned());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(block)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_global(&mut self, name: &str, init: Option<ConstExpr>) {
|
||||||
|
let init = init.unwrap_or(ConstExpr::Number(0));
|
||||||
|
match init {
|
||||||
|
ConstExpr::Number(value) => {
|
||||||
|
self.globals
|
||||||
|
.insert(name.to_string(), I::db_word(name, value as u32));
|
||||||
|
}
|
||||||
|
ConstExpr::String(str) => {
|
||||||
|
self.globals
|
||||||
|
.insert(name.to_string(), I::db_string(name, str));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_block(&mut self, block: Declaration) -> Result<(), CompilerError> {
|
||||||
|
match block {
|
||||||
|
Declaration::Variable { var, init, .. } => self.generate_global(&var.name, init),
|
||||||
|
Declaration::Function {
|
||||||
|
name,
|
||||||
|
params,
|
||||||
|
body,
|
||||||
|
return_type,
|
||||||
|
} => {
|
||||||
|
let func = self.generate_function(&name, ¶ms, &body, return_type);
|
||||||
|
self.functions.push(func);
|
||||||
|
}
|
||||||
|
Declaration::Dependency(Dependency { name, path }) => {
|
||||||
|
self.include(name, path);
|
||||||
|
}
|
||||||
|
Declaration::Struct { .. } => {} /* can't do any codegen for these yet,
|
||||||
|
* they're just types. */
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example: Generate code for a function
|
||||||
|
fn generate_function(
|
||||||
|
&mut self,
|
||||||
|
name: &str,
|
||||||
|
params: &[Variable],
|
||||||
|
body: &[Statement],
|
||||||
|
return_type: TypeId,
|
||||||
|
) -> IB {
|
||||||
|
let mut code = IB::new();
|
||||||
|
|
||||||
|
// Reset allocator for new function
|
||||||
|
self.allocator.reset();
|
||||||
|
|
||||||
|
let fmtparams = params
|
||||||
|
.iter()
|
||||||
|
.map(|p| format!("{}: {}", p.name, p.type_id))
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join(", ");
|
||||||
|
|
||||||
|
code.extend(vec![
|
||||||
|
I::global_comment(format!("fn {name}({fmtparams}) -> {return_type}")),
|
||||||
|
I::label(name),
|
||||||
|
I::push(Register::Bpr),
|
||||||
|
I::mov(Register::Spr, Register::Bpr),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Allocate parameters to registers or stack locations
|
||||||
|
for (i, param) in params.iter().enumerate() {
|
||||||
|
let offset = 8 + (i as i32 * 4); // Parameters start at bpr+8
|
||||||
|
// Track that this parameter is at a stack location
|
||||||
|
let (reg, load_code) = self.allocator.alloc_var(¶m.name).unwrap();
|
||||||
|
code.append(load_code);
|
||||||
|
code.push(I::ldw_reg_offset(Register::Bpr, reg, offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate code for function body
|
||||||
|
for stmt in body {
|
||||||
|
let stmt_code = self.generate_statement(stmt, &mut code).unwrap();
|
||||||
|
code.append(stmt_code);
|
||||||
|
}
|
||||||
|
|
||||||
|
// automatically return at function end
|
||||||
|
if let Some(x) = code.iter().last()
|
||||||
|
&& let I::Jmp { target: Label(val) } = x
|
||||||
|
&& val == "_ret"
|
||||||
|
{
|
||||||
|
} else {
|
||||||
|
code.push(I::jmp("_ret"));
|
||||||
|
}
|
||||||
|
|
||||||
|
code.insert(0, I::Newline);
|
||||||
|
|
||||||
|
code
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example: Generate code for a statement
|
||||||
|
fn generate_statement(
|
||||||
|
&mut self,
|
||||||
|
stmt: &Statement,
|
||||||
|
func_body: &mut IB,
|
||||||
|
) -> Result<IB, CompilerError> {
|
||||||
|
let mut code = IB::new();
|
||||||
|
|
||||||
|
match stmt {
|
||||||
|
Statement::Declaration { var, value } => {
|
||||||
|
if let Some(expr) = value {
|
||||||
|
// Evaluate expression
|
||||||
|
let (result_reg, expr_code) =
|
||||||
|
self.generate_expression(expr, true, func_body)?;
|
||||||
|
code.append(expr_code);
|
||||||
|
|
||||||
|
// Store result in variable
|
||||||
|
let store_code = self.allocator.store_var(&var.name, &result_reg);
|
||||||
|
code.append(store_code);
|
||||||
|
|
||||||
|
// Free temporary register
|
||||||
|
self.allocator.free_temp(result_reg);
|
||||||
|
} else {
|
||||||
|
// Just declaring variable without initialization
|
||||||
|
self.allocator.alloc_var(&var.name)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Statement::Break => unimplemented!("need scope tracking first!"),
|
||||||
|
Statement::Continue => unimplemented!("need scope tracking first!"),
|
||||||
|
Statement::Defer(_func) => unimplemented!("we need scope tracking first!"),
|
||||||
|
|
||||||
|
Statement::PtrWrite { ptr, value } => {
|
||||||
|
let (result_reg, expr_code) = self.generate_expression(value, true, func_body)?;
|
||||||
|
code.append(expr_code);
|
||||||
|
|
||||||
|
let (ptr_reg, ptr_code) = self.generate_expression(ptr, true, func_body)?;
|
||||||
|
code.append(ptr_code);
|
||||||
|
|
||||||
|
code.push(I::stw_reg(result_reg, ptr_reg));
|
||||||
|
|
||||||
|
self.allocator.free_temp(result_reg);
|
||||||
|
self.allocator.free_temp(ptr_reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
Statement::Assign {
|
||||||
|
varname,
|
||||||
|
value,
|
||||||
|
operator,
|
||||||
|
} => {
|
||||||
|
// Evaluate expression
|
||||||
|
let (result_reg, expr_code) = self.generate_expression(value, true, func_body)?;
|
||||||
|
code.append(expr_code);
|
||||||
|
|
||||||
|
if *operator == AssignmentOperator::Assign {
|
||||||
|
// Check if this is a global variable
|
||||||
|
if self.is_global(varname) {
|
||||||
|
// Store to global label
|
||||||
|
code.push(I::stw_label(result_reg, varname.clone()))
|
||||||
|
} else {
|
||||||
|
// Store result in local variable
|
||||||
|
let store_code = self.allocator.store_var(varname, &result_reg);
|
||||||
|
code.append(store_code);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free temporary register
|
||||||
|
self.allocator.free_temp(result_reg);
|
||||||
|
|
||||||
|
return Ok(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
// for more complex assignment cases we need an intermediate register.
|
||||||
|
let (temp_reg, temp_code) = self.allocator.alloc_temp()?;
|
||||||
|
code.append(temp_code);
|
||||||
|
|
||||||
|
// fetch the value of the variable
|
||||||
|
let var_reg = if self.is_global(varname) {
|
||||||
|
let instruction = I::ldw_label(varname.clone(), temp_reg);
|
||||||
|
code.push(instruction);
|
||||||
|
temp_reg
|
||||||
|
} else {
|
||||||
|
let (rg, block) = self.allocator.load_var(varname)?;
|
||||||
|
code.append(block);
|
||||||
|
rg
|
||||||
|
};
|
||||||
|
|
||||||
|
let assign_code = match operator {
|
||||||
|
AssignmentOperator::Assign => {
|
||||||
|
unreachable!("assignment was already checked earlier.")
|
||||||
|
}
|
||||||
|
AssignmentOperator::AddAssign => I::add(var_reg, result_reg, temp_reg),
|
||||||
|
AssignmentOperator::SubAssign => I::sub(var_reg, result_reg, temp_reg),
|
||||||
|
AssignmentOperator::MulAssign => {
|
||||||
|
return Err(CompilerError::Unimplemented(
|
||||||
|
"TODO: implement multiplication for assignment".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
AssignmentOperator::DivAssign => {
|
||||||
|
return Err(CompilerError::Unimplemented(
|
||||||
|
"TODO: write proper div function for DSA".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
AssignmentOperator::ModAssign => {
|
||||||
|
return Err(CompilerError::Unimplemented(
|
||||||
|
"TODO: write proper mod function for DSA".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
AssignmentOperator::AndAssign => I::and(var_reg, result_reg, temp_reg),
|
||||||
|
AssignmentOperator::OrAssign => I::or(var_reg, result_reg, temp_reg),
|
||||||
|
AssignmentOperator::XorAssign => I::xor(var_reg, result_reg, temp_reg),
|
||||||
|
AssignmentOperator::LeftShiftAssign => {
|
||||||
|
// this is only useful if we optimise out the register allocation
|
||||||
|
// inside value.
|
||||||
|
// if let Expression::Number { value, .. } = *value {
|
||||||
|
// I::shl(var_reg, value, temp_reg)
|
||||||
|
// }
|
||||||
|
I::shl(var_reg, result_reg, 0, temp_reg)
|
||||||
|
}
|
||||||
|
AssignmentOperator::RightShiftAssign => {
|
||||||
|
// this is only useful if we optimise out the register allocation
|
||||||
|
// if let Expression::Number { value, .. } = *value {
|
||||||
|
// I::shr(var_reg, value, temp_reg)
|
||||||
|
// }
|
||||||
|
I::shr(var_reg, result_reg, 0, temp_reg)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
code.push(assign_code);
|
||||||
|
|
||||||
|
// Check if this is a global variable
|
||||||
|
if self.is_global(varname) {
|
||||||
|
// Store to global label
|
||||||
|
code.push(I::stw_label(temp_reg, varname.clone()))
|
||||||
|
} else {
|
||||||
|
// Store result in local variable
|
||||||
|
let store_code = self.allocator.store_var(varname, &temp_reg);
|
||||||
|
code.append(store_code);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.allocator.free_temp(result_reg);
|
||||||
|
self.allocator.free_temp(temp_reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
Statement::Return(expr) => {
|
||||||
|
if let Some(e) = expr {
|
||||||
|
let (result_reg, expr_code) = self.generate_expression(e, true, func_body)?;
|
||||||
|
code.append(expr_code);
|
||||||
|
code.push(I::stw_reg_offset(result_reg, Register::Bpr, 8));
|
||||||
|
code.push(I::jmp("_ret"));
|
||||||
|
self.allocator.free_temp(result_reg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Statement::If {
|
||||||
|
condition,
|
||||||
|
then_stmt,
|
||||||
|
else_stmt,
|
||||||
|
} => {
|
||||||
|
// Generate condition
|
||||||
|
let (cond_reg, cond_code) = self.generate_expression(condition, true, func_body)?;
|
||||||
|
code.append(cond_code);
|
||||||
|
|
||||||
|
// Generate unique labels
|
||||||
|
let then_label = format!("_then_{}", self.get_unique_label());
|
||||||
|
let else_label = format!("_else_{}", self.get_unique_label());
|
||||||
|
let end_label = format!("_end_{}", self.get_unique_label());
|
||||||
|
|
||||||
|
// Jump to else if condition is false
|
||||||
|
code.push(I::jez(cond_reg, else_label.clone()));
|
||||||
|
self.allocator.free_temp(cond_reg);
|
||||||
|
|
||||||
|
// Then block
|
||||||
|
code.push(I::label(then_label));
|
||||||
|
for s in then_stmt {
|
||||||
|
code.append(self.generate_statement(s, func_body)?);
|
||||||
|
}
|
||||||
|
|
||||||
|
if then_stmt.is_empty() {
|
||||||
|
code.push(I::Nop);
|
||||||
|
}
|
||||||
|
|
||||||
|
code.push(I::jmp(end_label.clone()));
|
||||||
|
|
||||||
|
// Else block
|
||||||
|
code.push(I::label(else_label));
|
||||||
|
for s in else_stmt {
|
||||||
|
code.append(self.generate_statement(s, func_body)?);
|
||||||
|
}
|
||||||
|
|
||||||
|
if else_stmt.is_empty() {
|
||||||
|
code.push(I::Nop);
|
||||||
|
}
|
||||||
|
|
||||||
|
code.push(I::label(end_label));
|
||||||
|
}
|
||||||
|
|
||||||
|
Statement::While { condition, body } => {
|
||||||
|
let loop_start = format!("_while_start_{}", self.get_unique_label());
|
||||||
|
let loop_end = format!("_while_end_{}", self.get_unique_label());
|
||||||
|
|
||||||
|
code.push(I::label(&loop_start));
|
||||||
|
|
||||||
|
// Generate condition
|
||||||
|
let (cond_reg, cond_code) = self.generate_expression(condition, true, func_body)?;
|
||||||
|
code.append(cond_code);
|
||||||
|
|
||||||
|
code.push(I::jez(cond_reg, loop_end.clone()));
|
||||||
|
self.allocator.free_temp(cond_reg);
|
||||||
|
|
||||||
|
// Loop body
|
||||||
|
for s in body {
|
||||||
|
code.append(self.generate_statement(s, func_body)?);
|
||||||
|
}
|
||||||
|
|
||||||
|
code.push(I::jmp(loop_start));
|
||||||
|
code.push(I::label(loop_end));
|
||||||
|
}
|
||||||
|
|
||||||
|
Statement::Loop(body) => {
|
||||||
|
let loop_start = format!("_loop_start_{}", self.get_unique_label());
|
||||||
|
code.push(I::label(&loop_start));
|
||||||
|
|
||||||
|
for s in body {
|
||||||
|
code.append(self.generate_statement(s, func_body)?);
|
||||||
|
}
|
||||||
|
|
||||||
|
code.push(I::jmp(loop_start));
|
||||||
|
}
|
||||||
|
|
||||||
|
Statement::Expression { expr } => {
|
||||||
|
let (result_reg, expr_code) = self.generate_expression(expr, false, func_body)?;
|
||||||
|
code.append(expr_code);
|
||||||
|
self.allocator.free_temp(result_reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
Statement::Block(statements) => {
|
||||||
|
for s in statements {
|
||||||
|
code.append(self.generate_statement(s, func_body)?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example: Generate code for an expression
|
||||||
|
// Returns (register containing result, assembly code)
|
||||||
|
fn generate_expression(
|
||||||
|
&mut self,
|
||||||
|
expr: &Expression,
|
||||||
|
use_result: bool,
|
||||||
|
func_body: &mut IB,
|
||||||
|
) -> Result<(Register, IB), CompilerError> {
|
||||||
|
let mut code = IB::new();
|
||||||
|
|
||||||
|
match expr {
|
||||||
|
Expression::Empty => Ok((Register::Null, code)),
|
||||||
|
|
||||||
|
Expression::Number(n) => match n {
|
||||||
|
Number::Signed(value, _) => {
|
||||||
|
let (reg, alloc_code) = self.allocator.alloc_temp()?;
|
||||||
|
code.append(alloc_code);
|
||||||
|
|
||||||
|
// Load immediate value
|
||||||
|
code.push(I::lwi(*value as u32, reg));
|
||||||
|
|
||||||
|
Ok((reg, code))
|
||||||
|
}
|
||||||
|
Number::Unsigned(value, _) => {
|
||||||
|
let (reg, alloc_code) = self.allocator.alloc_temp()?;
|
||||||
|
code.append(alloc_code);
|
||||||
|
|
||||||
|
// Load immediate value
|
||||||
|
code.push(I::lwi(*value as u32, reg));
|
||||||
|
|
||||||
|
Ok((reg, code))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
Expression::CharLiteral(value) => {
|
||||||
|
let (reg, alloc_code) = self.allocator.alloc_temp()?;
|
||||||
|
code.append(alloc_code);
|
||||||
|
|
||||||
|
// Load immediate value
|
||||||
|
code.push(I::comment(format!("char literal '{value}'")));
|
||||||
|
code.push(I::lwi(*value as u32, reg));
|
||||||
|
|
||||||
|
Ok((reg, code))
|
||||||
|
}
|
||||||
|
|
||||||
|
Expression::StringLiteral(value) => {
|
||||||
|
let (reg, alloc_code) = self.allocator.alloc_temp()?;
|
||||||
|
code.append(alloc_code);
|
||||||
|
|
||||||
|
// write string into memory
|
||||||
|
let uuid = self.get_unique_label();
|
||||||
|
|
||||||
|
func_body.insert(0, I::db_string(format!("str_{uuid}"), value));
|
||||||
|
|
||||||
|
// Load pointer to string
|
||||||
|
code.push(I::lwi_label(format!("str_{uuid}"), reg));
|
||||||
|
|
||||||
|
Ok((reg, code))
|
||||||
|
}
|
||||||
|
|
||||||
|
Expression::ArrayLiteral { elements, type_id } => todo!(),
|
||||||
|
Expression::StructLiteral {
|
||||||
|
name,
|
||||||
|
fields,
|
||||||
|
type_id,
|
||||||
|
} => todo!(),
|
||||||
|
|
||||||
|
Expression::Variable { name, .. } => {
|
||||||
|
if self.is_global(&name.name) {
|
||||||
|
// Allocate a temporary register for the global
|
||||||
|
let (reg, alloc_code) = self.allocator.alloc_temp()?;
|
||||||
|
code.append(alloc_code);
|
||||||
|
|
||||||
|
// Load from global label
|
||||||
|
code.push(I::ldw_label(name.name.clone(), reg));
|
||||||
|
|
||||||
|
Ok((reg, code))
|
||||||
|
} else {
|
||||||
|
// Local variable - use existing allocator logic
|
||||||
|
let (reg, load_code) = self.allocator.load_var(&name.name)?;
|
||||||
|
code.append(load_code);
|
||||||
|
Ok((reg, code))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Expression::Binary {
|
||||||
|
op, left, right, ..
|
||||||
|
} => {
|
||||||
|
// Evaluate left operand
|
||||||
|
let (left_reg, left_code) = self.generate_expression(left, true, func_body)?;
|
||||||
|
code.append(left_code);
|
||||||
|
|
||||||
|
// Evaluate right operand
|
||||||
|
let (right_reg, right_code) = self.generate_expression(right, true, func_body)?;
|
||||||
|
code.append(right_code);
|
||||||
|
|
||||||
|
// Allocate result register
|
||||||
|
let (result_reg, result_alloc) = self.allocator.alloc_temp()?;
|
||||||
|
code.append(result_alloc);
|
||||||
|
|
||||||
|
// Generate operation
|
||||||
|
match op {
|
||||||
|
BinaryOperator::Add => {
|
||||||
|
code.push(I::add(left_reg, right_reg, result_reg));
|
||||||
|
}
|
||||||
|
BinaryOperator::Sub => {
|
||||||
|
code.push(I::sub(left_reg, right_reg, result_reg));
|
||||||
|
}
|
||||||
|
BinaryOperator::Mul => {
|
||||||
|
self.include("maths", "./lib/maths/core.dsa");
|
||||||
|
// Call multiply function
|
||||||
|
code.push(I::push(right_reg));
|
||||||
|
code.push(I::push(left_reg));
|
||||||
|
code.push(I::call("maths::multiply"));
|
||||||
|
code.push(I::pop(result_reg));
|
||||||
|
code.push(I::pop(Register::Zero));
|
||||||
|
}
|
||||||
|
BinaryOperator::Div => {
|
||||||
|
return Err(CompilerError::Unimplemented(
|
||||||
|
"TODO: write proper div function for DSA".to_string(),
|
||||||
|
));
|
||||||
|
// self.include("maths", "./lib/maths/core.dsa");
|
||||||
|
// // Call divide function
|
||||||
|
// code.push(format!("\tpush {}", right_reg));
|
||||||
|
// code.push(format!("\tpush {}", left_reg));
|
||||||
|
// code.push("\tcall maths::divide".to_string());
|
||||||
|
// code.push(format!("\tpop {}", result_reg));
|
||||||
|
// code.push("\tpop zero".to_string());
|
||||||
|
}
|
||||||
|
BinaryOperator::Mod => {
|
||||||
|
return Err(CompilerError::Unimplemented(
|
||||||
|
"TODO: write proper mod function for DSA".to_string(),
|
||||||
|
));
|
||||||
|
// self.include("maths", "./lib/maths/core.dsa");
|
||||||
|
// // Call modulo function
|
||||||
|
// code.push(format!("\tpush {}", right_reg));
|
||||||
|
// code.push(format!("\tpush {}", left_reg));
|
||||||
|
// code.push("\tcall maths::modulo".to_string());
|
||||||
|
// code.push(format!("\tpop {}", result_reg));
|
||||||
|
// code.push("\tpop zero".to_string());
|
||||||
|
}
|
||||||
|
BinaryOperator::BitwiseAnd => {
|
||||||
|
code.push(I::and(left_reg, right_reg, result_reg));
|
||||||
|
}
|
||||||
|
BinaryOperator::BitwiseOr => {
|
||||||
|
code.push(I::or(left_reg, right_reg, result_reg));
|
||||||
|
}
|
||||||
|
BinaryOperator::BitwiseXor => {
|
||||||
|
code.push(I::xor(left_reg, right_reg, result_reg));
|
||||||
|
}
|
||||||
|
BinaryOperator::LogicalAnd => {
|
||||||
|
return Err(CompilerError::Unimplemented(
|
||||||
|
"assembler/ISA does not yet support logical and!".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
BinaryOperator::LogicalOr => {
|
||||||
|
return Err(CompilerError::Unimplemented(
|
||||||
|
"assembler/ISA does not yet support logical or!".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
BinaryOperator::LeftShift => {
|
||||||
|
code.push(I::shl(left_reg, right_reg, 0, result_reg));
|
||||||
|
}
|
||||||
|
BinaryOperator::RightShift => {
|
||||||
|
code.push(I::shr(left_reg, right_reg, 0, result_reg));
|
||||||
|
}
|
||||||
|
// Comparison operators - return 1 (true) or 0 (false)
|
||||||
|
BinaryOperator::Equal => {
|
||||||
|
code.push(I::cmp(Equal, left_reg, right_reg, result_reg))
|
||||||
|
}
|
||||||
|
BinaryOperator::NotEqual => {
|
||||||
|
code.push(I::cmp(NotEqual, left_reg, right_reg, result_reg))
|
||||||
|
}
|
||||||
|
BinaryOperator::LessThan => {
|
||||||
|
code.push(I::cmp(Less, left_reg, right_reg, result_reg))
|
||||||
|
}
|
||||||
|
BinaryOperator::LessOrEqual => {
|
||||||
|
code.push(I::cmp(LessEqual, left_reg, right_reg, result_reg))
|
||||||
|
}
|
||||||
|
BinaryOperator::GreaterThan => {
|
||||||
|
code.push(I::cmp(Greater, left_reg, right_reg, result_reg))
|
||||||
|
}
|
||||||
|
BinaryOperator::GreaterOrEqual => {
|
||||||
|
code.push(I::cmp(GreaterEqual, left_reg, right_reg, result_reg))
|
||||||
|
} // _ => unimplemented!(),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free operand registers (allocator will protect variables)
|
||||||
|
self.allocator.free_temp(left_reg);
|
||||||
|
self.allocator.free_temp(right_reg);
|
||||||
|
|
||||||
|
Ok((result_reg, code))
|
||||||
|
}
|
||||||
|
|
||||||
|
Expression::UnaryPostfix { op, operand, .. } => {
|
||||||
|
let (operand_reg, operand_code) =
|
||||||
|
self.generate_expression(operand, true, func_body)?;
|
||||||
|
code.append(operand_code);
|
||||||
|
|
||||||
|
let (result_reg, result_alloc) = self.allocator.alloc_temp()?;
|
||||||
|
code.append(result_alloc);
|
||||||
|
|
||||||
|
match op {
|
||||||
|
UnaryOperator::Increment => {
|
||||||
|
// postfix increment - return old value
|
||||||
|
code.push(I::mov(operand_reg, result_reg));
|
||||||
|
}
|
||||||
|
UnaryOperator::Decrement => {
|
||||||
|
// postfix decrement - return old value
|
||||||
|
code.push(I::mov(operand_reg, result_reg));
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(CompilerError::Generic(format!("{op} is prefix only!")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.allocator.free_temp(operand_reg);
|
||||||
|
Ok((result_reg, code))
|
||||||
|
}
|
||||||
|
|
||||||
|
Expression::Unary { op, operand, .. } => {
|
||||||
|
let (operand_reg, operand_code) =
|
||||||
|
self.generate_expression(operand, true, func_body)?;
|
||||||
|
code.append(operand_code);
|
||||||
|
|
||||||
|
let (result_reg, result_alloc) = self.allocator.alloc_temp()?;
|
||||||
|
code.append(result_alloc);
|
||||||
|
|
||||||
|
match op {
|
||||||
|
UnaryOperator::Minus => {
|
||||||
|
// Negate: result = 0 - operand
|
||||||
|
code.push(I::sub(Register::Zero, operand_reg, result_reg));
|
||||||
|
}
|
||||||
|
UnaryOperator::Plus => {
|
||||||
|
// Just move
|
||||||
|
code.push(I::mov(operand_reg, result_reg));
|
||||||
|
}
|
||||||
|
UnaryOperator::Dereference => {
|
||||||
|
code.push(I::ldw_reg(operand_reg, result_reg));
|
||||||
|
}
|
||||||
|
UnaryOperator::AddressOf => {
|
||||||
|
// ensure the referenced variable is on the stack and return its
|
||||||
|
// address.
|
||||||
|
let (offset, alloc_code) = self.allocator.free_register(&operand_reg)?;
|
||||||
|
code.push(alloc_code);
|
||||||
|
code.push(I::iadd_dest(
|
||||||
|
Register::Spr,
|
||||||
|
offset - self.allocator.get_stack_offset(),
|
||||||
|
result_reg,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
UnaryOperator::SizeOf => {
|
||||||
|
if let Ok(id) = operand.type_id() {
|
||||||
|
let size = id.size();
|
||||||
|
code.push(I::lwi(size as u32, result_reg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UnaryOperator::Increment => {
|
||||||
|
// prefix increment
|
||||||
|
code.push(I::mov(operand_reg, result_reg));
|
||||||
|
code.push(I::iadd_dest(operand_reg, 1, result_reg));
|
||||||
|
}
|
||||||
|
UnaryOperator::Decrement => {
|
||||||
|
// prefix decrement
|
||||||
|
code.push(I::mov(operand_reg, result_reg));
|
||||||
|
code.push(I::iadd_dest(operand_reg, -1, result_reg));
|
||||||
|
}
|
||||||
|
UnaryOperator::BitwiseNot => {
|
||||||
|
code.push(I::not(operand_reg, result_reg));
|
||||||
|
}
|
||||||
|
UnaryOperator::LogicalNot => {
|
||||||
|
return Err(CompilerError::Unimplemented(
|
||||||
|
"Assembler/ISA does not yet support logical not".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(CompilerError::Generic(format!("{op} is postfix only!")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.allocator.free_temp(operand_reg);
|
||||||
|
Ok((result_reg, code))
|
||||||
|
}
|
||||||
|
|
||||||
|
Expression::Call {
|
||||||
|
func: Call { name, args },
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
// first evaluate all the args we're going to need
|
||||||
|
let mut arg_regs = Vec::new();
|
||||||
|
for arg in args.iter().rev() {
|
||||||
|
let (arg_reg, arg_code) = self.generate_expression(arg, true, func_body)?;
|
||||||
|
code.append(arg_code);
|
||||||
|
arg_regs.push(arg_reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save caller-saved registers and track which ones we saved
|
||||||
|
let saved_regs = self.allocator.get_caller_saved_registers();
|
||||||
|
for reg in &saved_regs {
|
||||||
|
// spill variables to stack
|
||||||
|
code.push(self.allocator.free_register(reg).unwrap().1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Evaluate and push arguments in reverse order
|
||||||
|
for (i, arg_reg) in arg_regs.iter().enumerate() {
|
||||||
|
code.push(I::comment(format!("push arg {}", args.len() - 1 - i)));
|
||||||
|
code.push(I::push(*arg_reg));
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.symbols.contains(&name.name) {
|
||||||
|
// Call local function
|
||||||
|
code.push(I::call(name.to_string()));
|
||||||
|
} else if let Some(ns) = name.namespace.clone()
|
||||||
|
&& self.imports.contains_key(&ns)
|
||||||
|
{
|
||||||
|
code.push(I::call(name.to_string()));
|
||||||
|
} else {
|
||||||
|
return Err(CompilerError::Undefined(name.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let result_reg: Register;
|
||||||
|
|
||||||
|
if use_result {
|
||||||
|
let (temp_result_reg, result_alloc) = self.allocator.alloc_temp()?;
|
||||||
|
result_reg = temp_result_reg;
|
||||||
|
|
||||||
|
code.append(result_alloc);
|
||||||
|
code.push(I::pop(result_reg));
|
||||||
|
|
||||||
|
// Clean up arguments
|
||||||
|
if args.len() > 1 {
|
||||||
|
for _ in 0..(args.len() - 1) {
|
||||||
|
code.push(I::pop(Register::Zero));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result_reg = Register::Zero;
|
||||||
|
|
||||||
|
// Clean up arguments
|
||||||
|
if args.len() > 0 {
|
||||||
|
for _ in 0..(args.len()) {
|
||||||
|
code.push(I::pop(Register::Zero));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free argument registers
|
||||||
|
for reg in arg_regs {
|
||||||
|
self.allocator.free_temp(reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((result_reg, code))
|
||||||
|
}
|
||||||
|
|
||||||
|
Expression::IndexAccess {
|
||||||
|
expr,
|
||||||
|
index,
|
||||||
|
type_id,
|
||||||
|
} => {
|
||||||
|
let (expr_reg, expr_alloc) = self.generate_expression(expr, true, func_body)?;
|
||||||
|
code.append(expr_alloc);
|
||||||
|
|
||||||
|
let (index_reg, index_alloc) = self.generate_expression(index, true, func_body)?;
|
||||||
|
code.append(index_alloc);
|
||||||
|
|
||||||
|
let (result_reg, result_alloc) = self.allocator.alloc_temp()?;
|
||||||
|
code.append(result_alloc);
|
||||||
|
|
||||||
|
// add the expr pointer to the index to get the final address.
|
||||||
|
code.push(I::add(expr_reg, index_reg, result_reg));
|
||||||
|
// load the value at the address.
|
||||||
|
code.push(I::ldw_reg(result_reg, result_reg));
|
||||||
|
|
||||||
|
self.allocator.free_temp(expr_reg);
|
||||||
|
self.allocator.free_temp(index_reg);
|
||||||
|
|
||||||
|
Ok((result_reg, code))
|
||||||
|
}
|
||||||
|
Expression::MemberAccess {
|
||||||
|
expr,
|
||||||
|
field_name,
|
||||||
|
type_id,
|
||||||
|
} => Err(CompilerError::Unimplemented(
|
||||||
|
"Structs are not yet implemented!".to_string(),
|
||||||
|
)),
|
||||||
|
|
||||||
|
Expression::TypeCast {
|
||||||
|
expr,
|
||||||
|
target_type,
|
||||||
|
type_id,
|
||||||
|
} => {
|
||||||
|
let (expr_reg, expr_code) = self.generate_expression(expr, true, func_body)?;
|
||||||
|
|
||||||
|
// not sure if we actually need to do anything here.
|
||||||
|
// for now we just return the previous expression.
|
||||||
|
Ok((expr_reg, expr_code))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper for generating unique labels
|
||||||
|
fn get_unique_label(&mut self) -> String {
|
||||||
|
// You'd implement a counter here
|
||||||
|
static COUNTER: AtomicU32 = AtomicU32::new(0);
|
||||||
|
|
||||||
|
let val = COUNTER.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
|
||||||
|
(val + 1).to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,858 @@
|
|||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use crate::backend::registers::Register;
|
||||||
|
|
||||||
|
pub struct InsBlock {
|
||||||
|
instructions: Vec<Instruction>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InsBlock {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
instructions: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert(&mut self, index: usize, instr: Instruction) {
|
||||||
|
self.instructions.insert(index, instr);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push(&mut self, instr: Instruction) {
|
||||||
|
self.instructions.push(instr);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn append(&mut self, mut other: Self) {
|
||||||
|
self.instructions.append(&mut other.instructions);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn extend(&mut self, instrs: impl IntoIterator<Item = Instruction>) {
|
||||||
|
self.instructions.extend(instrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.instructions.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.instructions.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iter(&self) -> impl Iterator<Item = &Instruction> {
|
||||||
|
self.instructions.iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Vec<Instruction>> for InsBlock {
|
||||||
|
fn from(instructions: Vec<Instruction>) -> Self {
|
||||||
|
Self { instructions }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Instruction> for InsBlock {
|
||||||
|
fn from(instr: Instruction) -> Self {
|
||||||
|
Self {
|
||||||
|
instructions: vec![instr],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Comparison {
|
||||||
|
Greater,
|
||||||
|
GreaterEqual,
|
||||||
|
Equal,
|
||||||
|
NotEqual,
|
||||||
|
Less,
|
||||||
|
LessEqual,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Instruction {
|
||||||
|
// Labels and comments
|
||||||
|
Label(Label),
|
||||||
|
Comment {
|
||||||
|
text: String,
|
||||||
|
top_level: bool,
|
||||||
|
},
|
||||||
|
Newline,
|
||||||
|
|
||||||
|
// Data Directives
|
||||||
|
Db {
|
||||||
|
label: String,
|
||||||
|
data: Vec<u8>,
|
||||||
|
},
|
||||||
|
Dh {
|
||||||
|
label: String,
|
||||||
|
data: Vec<u16>,
|
||||||
|
},
|
||||||
|
Dw {
|
||||||
|
label: String,
|
||||||
|
data: Vec<u32>,
|
||||||
|
},
|
||||||
|
DString {
|
||||||
|
// alias for db.
|
||||||
|
label: String,
|
||||||
|
data: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
Resx {
|
||||||
|
label: String,
|
||||||
|
size: u32,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Include
|
||||||
|
Include {
|
||||||
|
name: String,
|
||||||
|
path: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Data movement
|
||||||
|
Mov {
|
||||||
|
src: Register,
|
||||||
|
dest: Register,
|
||||||
|
},
|
||||||
|
Movs {
|
||||||
|
src: Register,
|
||||||
|
dest: Register,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Memory operations
|
||||||
|
Ldb {
|
||||||
|
src: MemOperand,
|
||||||
|
dest: Register,
|
||||||
|
},
|
||||||
|
Ldh {
|
||||||
|
src: MemOperand,
|
||||||
|
dest: Register,
|
||||||
|
},
|
||||||
|
Ldw {
|
||||||
|
src: MemOperand,
|
||||||
|
dest: Register,
|
||||||
|
},
|
||||||
|
Stb {
|
||||||
|
src: Register,
|
||||||
|
dest: MemOperand,
|
||||||
|
},
|
||||||
|
Sth {
|
||||||
|
src: Register,
|
||||||
|
dest: MemOperand,
|
||||||
|
},
|
||||||
|
Stw {
|
||||||
|
src: Register,
|
||||||
|
dest: MemOperand,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Immediate loads
|
||||||
|
Lli {
|
||||||
|
imm: Imm,
|
||||||
|
dest: Register,
|
||||||
|
},
|
||||||
|
Lui {
|
||||||
|
imm: Imm,
|
||||||
|
dest: Register,
|
||||||
|
},
|
||||||
|
Lwi {
|
||||||
|
imm: Imm,
|
||||||
|
dest: Register,
|
||||||
|
},
|
||||||
|
LwiLabel {
|
||||||
|
label: String,
|
||||||
|
dest: Register,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Arithmetic
|
||||||
|
Add {
|
||||||
|
src1: Register,
|
||||||
|
src2: Register,
|
||||||
|
dest: Register,
|
||||||
|
},
|
||||||
|
Sub {
|
||||||
|
src1: Register,
|
||||||
|
src2: Register,
|
||||||
|
dest: Register,
|
||||||
|
},
|
||||||
|
|
||||||
|
AddI {
|
||||||
|
src: Register,
|
||||||
|
imm: Imm,
|
||||||
|
dest: Option<Register>,
|
||||||
|
},
|
||||||
|
SubI {
|
||||||
|
src: Register,
|
||||||
|
imm: Imm,
|
||||||
|
dest: Option<Register>,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Bitwise
|
||||||
|
And {
|
||||||
|
src1: Register,
|
||||||
|
src2: Register,
|
||||||
|
dest: Register,
|
||||||
|
},
|
||||||
|
Or {
|
||||||
|
src1: Register,
|
||||||
|
src2: Register,
|
||||||
|
dest: Register,
|
||||||
|
},
|
||||||
|
Xor {
|
||||||
|
src1: Register,
|
||||||
|
src2: Register,
|
||||||
|
dest: Register,
|
||||||
|
},
|
||||||
|
Not {
|
||||||
|
src: Register,
|
||||||
|
dest: Register,
|
||||||
|
},
|
||||||
|
Nand {
|
||||||
|
src1: Register,
|
||||||
|
src2: Register,
|
||||||
|
dest: Register,
|
||||||
|
},
|
||||||
|
Nor {
|
||||||
|
src1: Register,
|
||||||
|
src2: Register,
|
||||||
|
dest: Register,
|
||||||
|
},
|
||||||
|
Xnor {
|
||||||
|
src1: Register,
|
||||||
|
src2: Register,
|
||||||
|
dest: Register,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Shifts
|
||||||
|
Shl {
|
||||||
|
src1: Register,
|
||||||
|
r_shamt: Register,
|
||||||
|
i_shamt: u16,
|
||||||
|
dest: Register,
|
||||||
|
},
|
||||||
|
Shr {
|
||||||
|
src1: Register,
|
||||||
|
r_shamt: Register,
|
||||||
|
i_shamt: u16,
|
||||||
|
dest: Register,
|
||||||
|
},
|
||||||
|
|
||||||
|
Ieq {
|
||||||
|
src1: Register,
|
||||||
|
src2: Register,
|
||||||
|
dest: Register,
|
||||||
|
},
|
||||||
|
Ine {
|
||||||
|
src1: Register,
|
||||||
|
src2: Register,
|
||||||
|
dest: Register,
|
||||||
|
},
|
||||||
|
Ilt {
|
||||||
|
src1: Register,
|
||||||
|
src2: Register,
|
||||||
|
dest: Register,
|
||||||
|
},
|
||||||
|
Ile {
|
||||||
|
src1: Register,
|
||||||
|
src2: Register,
|
||||||
|
dest: Register,
|
||||||
|
},
|
||||||
|
Igt {
|
||||||
|
src1: Register,
|
||||||
|
src2: Register,
|
||||||
|
dest: Register,
|
||||||
|
},
|
||||||
|
Ige {
|
||||||
|
src1: Register,
|
||||||
|
src2: Register,
|
||||||
|
dest: Register,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Jumps
|
||||||
|
Jmp {
|
||||||
|
target: Label,
|
||||||
|
},
|
||||||
|
|
||||||
|
Jnz {
|
||||||
|
src: Register,
|
||||||
|
target: Label,
|
||||||
|
},
|
||||||
|
Jez {
|
||||||
|
src: Register,
|
||||||
|
target: Label,
|
||||||
|
},
|
||||||
|
|
||||||
|
Jic {
|
||||||
|
target: Label,
|
||||||
|
},
|
||||||
|
Jnc {
|
||||||
|
target: Label,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Stack
|
||||||
|
Push {
|
||||||
|
reg: Register,
|
||||||
|
},
|
||||||
|
Pop {
|
||||||
|
reg: Register,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Function calls
|
||||||
|
Call {
|
||||||
|
target: String,
|
||||||
|
}, // namespace::function
|
||||||
|
Return,
|
||||||
|
|
||||||
|
// System
|
||||||
|
Hlt,
|
||||||
|
Nop,
|
||||||
|
Int {
|
||||||
|
code: u8,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum DataDirective {
|
||||||
|
U8(Vec<u8>),
|
||||||
|
U16(Vec<u16>),
|
||||||
|
U32(Vec<u32>),
|
||||||
|
String(String),
|
||||||
|
Char(char),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Instruction {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Label(l) => write!(f, "{}:", l),
|
||||||
|
Self::Newline => write!(f, ""), /* empty string as newlines are inserted */
|
||||||
|
// automatically.
|
||||||
|
Self::Comment { text, top_level } => write!(
|
||||||
|
f,
|
||||||
|
"{}",
|
||||||
|
text.lines()
|
||||||
|
.map(|line| format!(
|
||||||
|
"{}// {}",
|
||||||
|
if *top_level { "" } else { " " },
|
||||||
|
line.trim(),
|
||||||
|
))
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join("\n")
|
||||||
|
),
|
||||||
|
|
||||||
|
Self::Include { name, path } => write!(f, "include {name}: \"{}\"", path),
|
||||||
|
|
||||||
|
Self::Db { label, data } => write!(
|
||||||
|
f,
|
||||||
|
"db {}: {}",
|
||||||
|
label,
|
||||||
|
data.iter()
|
||||||
|
.map(|&b| format!("{:#04X}", b))
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join(", ")
|
||||||
|
),
|
||||||
|
Self::Dh { label, data } => write!(
|
||||||
|
f,
|
||||||
|
"dh {}: {}",
|
||||||
|
label,
|
||||||
|
data.iter()
|
||||||
|
.map(|&b| format!("{:#06X}", b))
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join(", ")
|
||||||
|
),
|
||||||
|
Self::Dw { label, data } => write!(
|
||||||
|
f,
|
||||||
|
"dw {}: {}",
|
||||||
|
label,
|
||||||
|
data.iter()
|
||||||
|
.map(|&b| format!("{:#08X}", b))
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join(", ")
|
||||||
|
),
|
||||||
|
Self::DString { label, data } => write!(f, "db {}: \"{}\"", label, data),
|
||||||
|
|
||||||
|
Self::Resx { label, size } => write!(f, "resx {}: {}", label, size),
|
||||||
|
|
||||||
|
Self::Mov { src, dest } => write!(f, " mov {}, {}", src, dest),
|
||||||
|
Self::Movs { src, dest } => write!(f, " movs {}, {}", src, dest),
|
||||||
|
|
||||||
|
Self::Ldb { src: addr, dest } => {
|
||||||
|
let (reg, offset) = reg_and_offset(addr);
|
||||||
|
write!(f, " ldb {}, {}, {}", reg, dest, offset)
|
||||||
|
}
|
||||||
|
Self::Ldh { src: addr, dest } => {
|
||||||
|
let (reg, offset) = reg_and_offset(addr);
|
||||||
|
write!(f, " ldh {}, {}, {}", reg, dest, offset)
|
||||||
|
}
|
||||||
|
Self::Ldw { src, dest } => {
|
||||||
|
let (reg, offset) = reg_and_offset(src);
|
||||||
|
write!(f, " ldw {}, {}, {}", reg, dest, offset)
|
||||||
|
}
|
||||||
|
// Self::Ldbs { addr, dest } => {
|
||||||
|
// write!(f, " ldbs {}, {}", format_mem_operand(addr), dest)
|
||||||
|
// }
|
||||||
|
// Self::Ldhs { addr, dest } => {
|
||||||
|
// write!(f, " ldhs {}, {}", format_mem_operand(addr), dest)
|
||||||
|
// }
|
||||||
|
// Self::Ldws { addr, dest } => {
|
||||||
|
// write!(f, " ldws {}, {}", format_mem_operand(addr), dest)
|
||||||
|
// }
|
||||||
|
Self::Stb { src, dest: addr } => {
|
||||||
|
let (reg, offset) = reg_and_offset(addr);
|
||||||
|
write!(f, " stb {}, {}, {}", src, reg, offset)
|
||||||
|
}
|
||||||
|
Self::Sth { src, dest: addr } => {
|
||||||
|
let (reg, offset) = reg_and_offset(addr);
|
||||||
|
write!(f, " sth {}, {}, {}", src, reg, offset)
|
||||||
|
}
|
||||||
|
Self::Stw { src, dest: addr } => {
|
||||||
|
let (reg, offset) = reg_and_offset(addr);
|
||||||
|
write!(f, " stw {}, {}, {}", src, reg, offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::Lli { imm, dest } => write!(f, " lli {}, {}", imm, dest),
|
||||||
|
Self::Lui { imm, dest } => write!(f, " lui {}, {}", imm, dest),
|
||||||
|
Self::Lwi { imm, dest } => write!(f, " lwi {}, {}", imm, dest),
|
||||||
|
Self::LwiLabel { label, dest } => write!(f, " lwi {}, {}", label, dest),
|
||||||
|
|
||||||
|
// arithmetic
|
||||||
|
Self::Add { src1, src2, dest } => {
|
||||||
|
write!(f, " add {}, {}, {}", src1, src2, dest)
|
||||||
|
}
|
||||||
|
Self::Sub { src1, src2, dest } => {
|
||||||
|
write!(f, " sub {}, {}, {}", src1, src2, dest)
|
||||||
|
}
|
||||||
|
Self::And { src1, src2, dest } => {
|
||||||
|
write!(f, " and {}, {}, {}", src1, src2, dest)
|
||||||
|
}
|
||||||
|
Self::Or { src1, src2, dest } => {
|
||||||
|
write!(f, " or {}, {}, {}", src1, src2, dest)
|
||||||
|
}
|
||||||
|
Self::Nand { src1, src2, dest } => {
|
||||||
|
write!(f, " nand {}, {}, {}", src1, src2, dest)
|
||||||
|
}
|
||||||
|
Self::Xor { src1, src2, dest } => {
|
||||||
|
write!(f, " xor {}, {}, {}", src1, src2, dest)
|
||||||
|
}
|
||||||
|
Self::Nor { src1, src2, dest } => {
|
||||||
|
write!(f, " nor {}, {}, {}", src1, src2, dest)
|
||||||
|
}
|
||||||
|
Self::Not { src, dest } => {
|
||||||
|
write!(f, " not {} {}", src, dest)
|
||||||
|
}
|
||||||
|
Self::Xnor { src1, src2, dest } => {
|
||||||
|
write!(f, " xnor {}, {}, {}", src1, src2, dest)
|
||||||
|
}
|
||||||
|
Self::AddI { src, imm, dest } => {
|
||||||
|
if let Some(d) = dest {
|
||||||
|
write!(f, " addi {}, {}, {}", src, imm, d)
|
||||||
|
} else {
|
||||||
|
write!(f, " addi {}, {}", src, imm)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::SubI { src, imm, dest } => {
|
||||||
|
if let Some(d) = dest {
|
||||||
|
write!(f, " subi {}, {}, {}", src, imm, d)
|
||||||
|
} else {
|
||||||
|
write!(f, " subi {}, {}", src, imm)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// shift instructions
|
||||||
|
Self::Shl {
|
||||||
|
src1,
|
||||||
|
r_shamt,
|
||||||
|
i_shamt,
|
||||||
|
dest,
|
||||||
|
} => {
|
||||||
|
write!(f, " shl {}, {}, {}, {}", src1, r_shamt, i_shamt, dest)
|
||||||
|
}
|
||||||
|
Self::Shr {
|
||||||
|
src1,
|
||||||
|
r_shamt,
|
||||||
|
i_shamt,
|
||||||
|
dest,
|
||||||
|
} => {
|
||||||
|
write!(f, " shl {}, {}, {}, {}", src1, r_shamt, i_shamt, dest)
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::Ieq { src1, src2, dest } => write!(f, " ieq {}, {}, {}", src1, src2, dest),
|
||||||
|
Self::Ine { src1, src2, dest } => write!(f, " ine {}, {}, {}", src1, src2, dest),
|
||||||
|
Self::Ilt { src1, src2, dest } => write!(f, " ilt {}, {}, {}", src1, src2, dest),
|
||||||
|
Self::Ile { src1, src2, dest } => write!(f, " ile {}, {}, {}", src1, src2, dest),
|
||||||
|
Self::Igt { src1, src2, dest } => write!(f, " igt {}, {}, {}", src1, src2, dest),
|
||||||
|
Self::Ige { src1, src2, dest } => write!(f, " ige {}, {}, {}", src1, src2, dest),
|
||||||
|
|
||||||
|
// jump instructions
|
||||||
|
Self::Jmp { target } => write!(f, " jmp {}", target),
|
||||||
|
Self::Jic { target } => write!(f, " jic {}", target),
|
||||||
|
Self::Jnc { target } => write!(f, " jnc {}", target),
|
||||||
|
Self::Jez { src, target } => write!(f, " jez {}, {}", src, target),
|
||||||
|
Self::Jnz { src, target } => write!(f, " jnz {}, {}", src, target),
|
||||||
|
|
||||||
|
// stack instructions
|
||||||
|
Self::Push { reg } => write!(f, " push {}", reg),
|
||||||
|
Self::Pop { reg } => write!(f, " pop {}", reg),
|
||||||
|
|
||||||
|
// call & return instructions
|
||||||
|
Self::Call { target } => write!(f, " call {}", target),
|
||||||
|
Self::Return => write!(f, " ret"),
|
||||||
|
|
||||||
|
// misc instructions
|
||||||
|
Self::Int { code } => write!(f, " int {}", code),
|
||||||
|
Self::Hlt => write!(f, " hlt"),
|
||||||
|
Self::Nop => write!(f, " nop"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Instruction {
|
||||||
|
// data directives
|
||||||
|
pub fn db_string(label: impl Into<String>, data: impl Into<String>) -> Self {
|
||||||
|
Self::DString {
|
||||||
|
label: label.into(),
|
||||||
|
data: data.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn db_word(label: impl Into<String>, data: u32) -> Self {
|
||||||
|
Self::Dw {
|
||||||
|
label: label.into(),
|
||||||
|
data: vec![data],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn db_bytes(label: impl Into<String>, data: &[u8]) -> Self {
|
||||||
|
Self::Db {
|
||||||
|
label: label.into(),
|
||||||
|
data: data.to_vec(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Movement
|
||||||
|
pub fn mov<R1, R2>(src: R1, dest: R2) -> Self
|
||||||
|
where
|
||||||
|
R1: Into<Register>,
|
||||||
|
R2: Into<Register>,
|
||||||
|
{
|
||||||
|
Self::Mov {
|
||||||
|
src: src.into(),
|
||||||
|
dest: dest.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Memory loads
|
||||||
|
pub fn ldw_reg<R>(base: R, dest: Register) -> Self
|
||||||
|
where
|
||||||
|
R: Into<Register>,
|
||||||
|
{
|
||||||
|
Self::Ldw {
|
||||||
|
src: MemOperand::RegIndirect(base.into()),
|
||||||
|
dest,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ldw_reg_offset<R>(base: R, dest: Register, offset: i32) -> Self
|
||||||
|
where
|
||||||
|
R: Into<Register>,
|
||||||
|
{
|
||||||
|
Self::Ldw {
|
||||||
|
src: MemOperand::RegOffset(base.into(), offset),
|
||||||
|
dest,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ldw_label(label: impl Into<Label>, dest: Register) -> Self {
|
||||||
|
Self::Ldw {
|
||||||
|
src: MemOperand::Label(label.into()),
|
||||||
|
dest,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Memory stores
|
||||||
|
pub fn stw_reg<R>(src: Register, base: R) -> Self
|
||||||
|
where
|
||||||
|
R: Into<Register>,
|
||||||
|
{
|
||||||
|
Self::Stw {
|
||||||
|
src,
|
||||||
|
dest: MemOperand::RegIndirect(base.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stw_reg_offset<R>(src: Register, base: R, offset: i32) -> Self
|
||||||
|
where
|
||||||
|
R: Into<Register>,
|
||||||
|
{
|
||||||
|
Self::Stw {
|
||||||
|
src,
|
||||||
|
dest: MemOperand::RegOffset(base.into(), offset),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stw_label(src: Register, label: impl Into<Label>) -> Self {
|
||||||
|
Self::Stw {
|
||||||
|
src,
|
||||||
|
dest: MemOperand::Label(label.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Arithmetic
|
||||||
|
pub fn add(src1: Register, src2: Register, dest: Register) -> Self {
|
||||||
|
Self::Add { src1, src2, dest }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sub(src1: Register, src2: Register, dest: Register) -> Self {
|
||||||
|
Self::Sub { src1, src2, dest }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn and(src1: Register, src2: Register, dest: Register) -> Self {
|
||||||
|
Self::And { src1, src2, dest }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn or(src1: Register, src2: Register, dest: Register) -> Self {
|
||||||
|
Self::Or { src1, src2, dest }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn xor(src1: Register, src2: Register, dest: Register) -> Self {
|
||||||
|
Self::Xor { src1, src2, dest }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn not(src: Register, dest: Register) -> Self {
|
||||||
|
Self::Not { src, dest }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn shl(src1: Register, r_shamt: Register, i_shamt: u16, dest: Register) -> Self {
|
||||||
|
Self::Shl {
|
||||||
|
src1,
|
||||||
|
r_shamt,
|
||||||
|
i_shamt,
|
||||||
|
dest,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn shr(src1: Register, r_shamt: Register, i_shamt: u16, dest: Register) -> Self {
|
||||||
|
Self::Shr {
|
||||||
|
src1,
|
||||||
|
r_shamt,
|
||||||
|
i_shamt,
|
||||||
|
dest,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iadd(src: Register, value: i64) -> Self {
|
||||||
|
let imm = Imm(value.unsigned_abs() as u32);
|
||||||
|
|
||||||
|
if value < 0 {
|
||||||
|
Self::SubI {
|
||||||
|
src,
|
||||||
|
imm,
|
||||||
|
dest: None,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Self::AddI {
|
||||||
|
src,
|
||||||
|
imm,
|
||||||
|
dest: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iadd_dest(src: Register, value: i32, dest: Register) -> Self {
|
||||||
|
let imm = Imm(value.unsigned_abs());
|
||||||
|
|
||||||
|
if value < 0 {
|
||||||
|
Self::SubI {
|
||||||
|
src,
|
||||||
|
imm,
|
||||||
|
dest: Some(dest),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Self::AddI {
|
||||||
|
src,
|
||||||
|
imm,
|
||||||
|
dest: Some(dest),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn inc(reg: Register) -> Self {
|
||||||
|
Self::AddI {
|
||||||
|
src: reg,
|
||||||
|
imm: Imm(1),
|
||||||
|
dest: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dec(reg: Register) -> Self {
|
||||||
|
Self::SubI {
|
||||||
|
src: reg,
|
||||||
|
imm: Imm(1),
|
||||||
|
dest: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Immediate loads
|
||||||
|
pub fn lwi(value: u32, dest: Register) -> Self {
|
||||||
|
if value > 0xFFFF {
|
||||||
|
Self::Lwi {
|
||||||
|
imm: Imm(value),
|
||||||
|
dest,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Self::Lli {
|
||||||
|
imm: Imm(value),
|
||||||
|
dest,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lwi_label(label: impl Into<String>, dest: Register) -> Self {
|
||||||
|
Self::LwiLabel {
|
||||||
|
label: label.into(),
|
||||||
|
dest,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Control flow
|
||||||
|
pub fn label(name: impl Into<String>) -> Self {
|
||||||
|
Self::Label(Label(name.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cmp(operand: Comparison, src1: Register, src2: Register, dest: Register) -> Self {
|
||||||
|
match operand {
|
||||||
|
Comparison::Equal => Self::Ieq { src1, src2, dest },
|
||||||
|
Comparison::NotEqual => Self::Ine { src1, src2, dest },
|
||||||
|
Comparison::Greater => Self::Igt { src1, src2, dest },
|
||||||
|
Comparison::GreaterEqual => Self::Ige { src1, src2, dest },
|
||||||
|
Comparison::Less => Self::Ilt { src1, src2, dest },
|
||||||
|
Comparison::LessEqual => Self::Ile { src1, src2, dest },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn jmp(target: impl Into<Label>) -> Self {
|
||||||
|
Self::Jmp {
|
||||||
|
target: target.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn jnz(src: Register, target: impl Into<Label>) -> Self {
|
||||||
|
Self::Jnz {
|
||||||
|
src,
|
||||||
|
target: target.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn jez(src: Register, target: impl Into<Label>) -> Self {
|
||||||
|
Self::Jez {
|
||||||
|
src,
|
||||||
|
target: target.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn jic(target: impl Into<Label>) -> Self {
|
||||||
|
Self::Jic {
|
||||||
|
target: target.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn jnc(target: impl Into<Label>) -> Self {
|
||||||
|
Self::Jnc {
|
||||||
|
target: target.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stack
|
||||||
|
pub fn push(reg: Register) -> Self {
|
||||||
|
Self::Push { reg }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pop(reg: Register) -> Self {
|
||||||
|
Self::Pop { reg }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Functions
|
||||||
|
pub fn call(target: impl Into<String>) -> Self {
|
||||||
|
Self::Call {
|
||||||
|
target: target.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn int(code: u8) -> Self {
|
||||||
|
Self::Int { code }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ret() -> Self {
|
||||||
|
Self::Return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Utilities
|
||||||
|
pub fn comment(text: impl Into<String>) -> Self {
|
||||||
|
Self::Comment {
|
||||||
|
text: text.into(),
|
||||||
|
top_level: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn global_comment(text: impl Into<String>) -> Self {
|
||||||
|
Self::Comment {
|
||||||
|
text: text.into(),
|
||||||
|
top_level: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn include(name: impl Into<String>, path: impl Into<String>) -> Self {
|
||||||
|
Self::Include {
|
||||||
|
name: name.into(),
|
||||||
|
path: path.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convenience trait for Label conversion
|
||||||
|
impl From<String> for Label {
|
||||||
|
fn from(s: String) -> Self {
|
||||||
|
Label(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&str> for Label {
|
||||||
|
fn from(s: &str) -> Self {
|
||||||
|
Label(s.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reg_and_offset(op: &MemOperand) -> (String, i32) {
|
||||||
|
match op {
|
||||||
|
MemOperand::RegIndirect(reg) => (reg.to_string(), 0),
|
||||||
|
MemOperand::RegOffset(reg, offset) => (reg.to_string(), *offset),
|
||||||
|
MemOperand::Label(label) => (label.to_string(), 0),
|
||||||
|
MemOperand::LabelOffset(label, offset) => (label.to_string(), *offset),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Memory operand for loads/stores
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum MemOperand {
|
||||||
|
/// Register indirect: [reg]
|
||||||
|
RegIndirect(Register),
|
||||||
|
/// Register with offset: [reg + offset]
|
||||||
|
RegOffset(Register, i32),
|
||||||
|
/// Label: [label]
|
||||||
|
Label(Label),
|
||||||
|
/// Label with offset: [label + offset]
|
||||||
|
LabelOffset(Label, i32),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Immediate value (16-bit or 32-bit)
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub struct Imm(pub u32);
|
||||||
|
|
||||||
|
impl fmt::Display for Imm {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Label reference
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct Label(pub String);
|
||||||
|
|
||||||
|
impl fmt::Display for Label {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
use crate::model::{CompilerError, Program};
|
||||||
|
use dsa_tasks::ProcessAgent;
|
||||||
|
|
||||||
|
mod codegen;
|
||||||
|
mod instruction;
|
||||||
|
mod registers;
|
||||||
|
mod scope;
|
||||||
|
|
||||||
|
pub fn generate_code(ast: &Program, ctx: &ProcessAgent) -> Result<String, CompilerError> {
|
||||||
|
let mut codegen = codegen::CodeGenerator::new(ast.clone());
|
||||||
|
codegen.generate(ctx)
|
||||||
|
}
|
||||||
@@ -0,0 +1,545 @@
|
|||||||
|
use std::{collections::HashMap, fmt};
|
||||||
|
|
||||||
|
use crate::backend::instruction::{InsBlock, Instruction};
|
||||||
|
use crate::model::CompilerError;
|
||||||
|
|
||||||
|
/// Register allocator for DSA assembly generation
|
||||||
|
/// Manages general-purpose registers (rg0-rgf) and handles stack spilling
|
||||||
|
pub struct RegisterAllocator {
|
||||||
|
/// Available general-purpose registers
|
||||||
|
/// Maps variable names to their current location (register or stack offset)
|
||||||
|
variable_locations: HashMap<String, Location>,
|
||||||
|
|
||||||
|
/// Maps registers to the variables they currently hold
|
||||||
|
register_contents: HashMap<Register, String>,
|
||||||
|
|
||||||
|
/// Current stack offset for local variables (relative to bpr)
|
||||||
|
/// Starts at -4 (going downward from base pointer)
|
||||||
|
stack_offset: i32,
|
||||||
|
|
||||||
|
/// Track which registers are currently in use
|
||||||
|
in_use: Vec<(Register, bool)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Location {
|
||||||
|
register: Option<Register>,
|
||||||
|
stack: Option<i32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Location {
|
||||||
|
pub fn stack(offset: i32) -> Self {
|
||||||
|
Location {
|
||||||
|
register: None,
|
||||||
|
stack: Some(offset),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn register(register: Register) -> Self {
|
||||||
|
Location {
|
||||||
|
register: Some(register),
|
||||||
|
stack: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RegisterAllocator {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
// Initialize with available GP registers (rg0-rgf = 16 registers)
|
||||||
|
let in_use = vec![
|
||||||
|
Register::Rg0,
|
||||||
|
Register::Rg1,
|
||||||
|
Register::Rg2,
|
||||||
|
Register::Rg3,
|
||||||
|
Register::Rg4,
|
||||||
|
Register::Rg5,
|
||||||
|
Register::Rg6,
|
||||||
|
Register::Rg7,
|
||||||
|
Register::Rg8,
|
||||||
|
Register::Rg9,
|
||||||
|
Register::Rga,
|
||||||
|
Register::Rgb,
|
||||||
|
Register::Rgc,
|
||||||
|
Register::Rgd,
|
||||||
|
Register::Rge,
|
||||||
|
Register::Rgf,
|
||||||
|
]
|
||||||
|
.iter()
|
||||||
|
.map(|®| (reg, false))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
RegisterAllocator {
|
||||||
|
// available_registers: registers,
|
||||||
|
variable_locations: HashMap::new(),
|
||||||
|
register_contents: HashMap::new(),
|
||||||
|
stack_offset: -4, // Start at -4 (first local below saved bpr)
|
||||||
|
in_use,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allocate a temporary register for expression evaluation
|
||||||
|
/// Returns the register name and optionally assembly code to save it
|
||||||
|
pub fn alloc_temp(&mut self) -> Result<(Register, InsBlock), CompilerError> {
|
||||||
|
// Try to find an unused register
|
||||||
|
|
||||||
|
// println!("finding! {:#?}", self.in_use);
|
||||||
|
|
||||||
|
if let Some(reg) = self.find_free_register() {
|
||||||
|
self.in_use[reg as usize].1 = true;
|
||||||
|
return Ok((reg, InsBlock::new()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// All registers in use - need to spill one
|
||||||
|
// Choose the first register with a variable we can spill
|
||||||
|
// Find a register to spill
|
||||||
|
|
||||||
|
// let reg_to_spill = self
|
||||||
|
// .available_registers
|
||||||
|
// .iter()
|
||||||
|
// .find(|reg| self.register_contents.contains_key(*reg))
|
||||||
|
// .cloned();
|
||||||
|
|
||||||
|
// if let Some(reg) = reg_to_spill {
|
||||||
|
// // Spill this variable to stack
|
||||||
|
// let spill_code = self.spill_register(®)?;
|
||||||
|
// code.extend(spill_code);
|
||||||
|
|
||||||
|
// self.in_use.insert(reg.clone(), true);
|
||||||
|
// return Ok((reg, code));
|
||||||
|
// }
|
||||||
|
|
||||||
|
todo!("an efficient stack spilling algorithm. needs scope awareness.");
|
||||||
|
|
||||||
|
Err(CompilerError::Generic(
|
||||||
|
"All registers are used up yet there are no variables to spill to the stack"
|
||||||
|
.to_string(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn set_in_use(&mut self, reg: Register, in_use: bool) {
|
||||||
|
// self.in_use[reg as usize].1 = in_use;
|
||||||
|
// }
|
||||||
|
|
||||||
|
/// Free a temporary register after use
|
||||||
|
/// NOTE: This will NOT free registers that contain variables!
|
||||||
|
/// Variables persist throughout their scope and must not be freed
|
||||||
|
pub fn free_temp(&mut self, reg: Register) {
|
||||||
|
// Check if this register contains a variable
|
||||||
|
if self.register_contents.contains_key(®) {
|
||||||
|
// This register holds a variable - don't free it!
|
||||||
|
// Variables are only freed when they go out of scope via free_var()
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a true temporary - safe to free
|
||||||
|
if !matches!(reg, Register::Zero | Register::Null) {
|
||||||
|
self.in_use[reg as usize].1 = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn free_var(&mut self, var: &str) {
|
||||||
|
// Check if this variable is in a register
|
||||||
|
if let Some(location) = self.variable_locations.get(var).cloned() {
|
||||||
|
if let Some(reg) = location.register
|
||||||
|
&& !matches!(reg, Register::Zero | Register::Null)
|
||||||
|
{
|
||||||
|
self.register_contents.remove(®);
|
||||||
|
self.in_use[reg as usize].1 = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.variable_locations.remove(var);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allocate a register for a named variable
|
||||||
|
/// Returns the register and any necessary assembly code
|
||||||
|
pub fn alloc_var(&mut self, var_name: &str) -> Result<(Register, InsBlock), CompilerError> {
|
||||||
|
if let Some(mut location) = self.variable_locations.get(var_name).cloned() {
|
||||||
|
// if the var is in a register we can use it already.
|
||||||
|
if let Some(reg) = location.register {
|
||||||
|
return Ok((reg, InsBlock::new()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the variable is on the stack only, we need to get it in a register.
|
||||||
|
if let Some(offset) = location.stack {
|
||||||
|
// Variable was pushed, need to calculate actual position and update its
|
||||||
|
// location.
|
||||||
|
let (reg, mut code) = self.alloc_temp()?;
|
||||||
|
|
||||||
|
// acknowledge var is now in a reg as well.
|
||||||
|
location.register = Some(reg);
|
||||||
|
|
||||||
|
// Load from bpr + offset (offset is negative)
|
||||||
|
// code.push(format!("\tsubi bpr {} {}", -(offset + 4), reg));
|
||||||
|
|
||||||
|
code.push(Instruction::ldw_reg_offset(
|
||||||
|
Register::Spr,
|
||||||
|
reg,
|
||||||
|
offset - self.stack_offset,
|
||||||
|
));
|
||||||
|
|
||||||
|
// Update location to register
|
||||||
|
self.variable_locations
|
||||||
|
.insert(var_name.to_string(), location);
|
||||||
|
self.register_contents.insert(reg, var_name.to_string());
|
||||||
|
|
||||||
|
return Ok((reg, code));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Variable doesn't have a location yet, allocate a new register
|
||||||
|
let (reg, code) = self.alloc_temp()?;
|
||||||
|
self.variable_locations
|
||||||
|
.insert(var_name.to_string(), Location::register(reg));
|
||||||
|
self.register_contents.insert(reg, var_name.to_string());
|
||||||
|
|
||||||
|
Ok((reg, code))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the current location of a variable
|
||||||
|
pub fn _get_var_location(&self, var_name: &str) -> Option<&Location> {
|
||||||
|
self.variable_locations.get(var_name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load a variable into a register (allocating if necessary)
|
||||||
|
/// Returns the register and assembly code to load it
|
||||||
|
pub fn load_var(&mut self, var_name: &str) -> Result<(Register, InsBlock), CompilerError> {
|
||||||
|
self.alloc_var(var_name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Store a value from a register into a variable
|
||||||
|
/// Updates tracking and returns any necessary assembly code
|
||||||
|
pub fn store_var(&mut self, var_name: &str, source_reg: &Register) -> InsBlock {
|
||||||
|
let mut block = InsBlock::new();
|
||||||
|
|
||||||
|
// Check if variable already has a location
|
||||||
|
if let Some(location) = self.variable_locations.get(var_name) {
|
||||||
|
// if the variable exists in a register we write to that.
|
||||||
|
match location.register {
|
||||||
|
Some(reg) if reg == *source_reg => {
|
||||||
|
block.push(Instruction::mov(*source_reg, reg));
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the variable exists on the stack but not a register we write here.
|
||||||
|
if let Some(offset) = location.stack {
|
||||||
|
block.push(Instruction::stw_reg_offset(
|
||||||
|
*source_reg,
|
||||||
|
Register::Spr,
|
||||||
|
offset - self.stack_offset,
|
||||||
|
));
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Variable doesn't exist yet, we can just use the same reg.
|
||||||
|
// if we can avoid a move, absolutely do that.
|
||||||
|
|
||||||
|
// if this is true then there's no permanent variable here so it's safe to use.
|
||||||
|
if !self.register_contents.contains_key(source_reg) {
|
||||||
|
self.variable_locations
|
||||||
|
.insert(var_name.to_string(), Location::register(*source_reg));
|
||||||
|
self.register_contents
|
||||||
|
.insert(*source_reg, var_name.to_string());
|
||||||
|
self.in_use[*source_reg as usize].1 = true;
|
||||||
|
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if current register isn't free, (eg is another variable) we assign somewhere
|
||||||
|
// else.
|
||||||
|
if let Some(free_reg) = self.find_free_register() {
|
||||||
|
self.variable_locations
|
||||||
|
.insert(var_name.to_string(), Location::register(free_reg));
|
||||||
|
self.register_contents
|
||||||
|
.insert(free_reg, var_name.to_string());
|
||||||
|
self.in_use[free_reg as usize].1 = true;
|
||||||
|
|
||||||
|
block.push(Instruction::mov(*source_reg, free_reg));
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No free registers - allocate on stack
|
||||||
|
// code.push(format!("\tstw {}, bpr, {}", source_reg, self.stack_offset));
|
||||||
|
// self.variable_locations
|
||||||
|
// .insert(var_name.to_string(), Location::Stack(self.stack_offset));
|
||||||
|
// self.stack_offset -= 4; // Move to next stack slot
|
||||||
|
//
|
||||||
|
todo!("an efficient stack spilling algorithm. needs scope awareness.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// spill a register to the stack (WITHOUT FREEING)
|
||||||
|
/// DO NOT USE this if it's for a pointer!!!!
|
||||||
|
pub fn _spill_register(&mut self, reg: &Register) -> Result<InsBlock, CompilerError> {
|
||||||
|
let mut code = InsBlock::new();
|
||||||
|
|
||||||
|
// check if the variable is declared.
|
||||||
|
if let Some(var_name) = self.register_contents.get(reg).cloned()
|
||||||
|
&& let Some(location) = self.variable_locations.get_mut(&var_name)
|
||||||
|
{
|
||||||
|
// check if var is on the stack
|
||||||
|
if let Some(offset) = location.stack {
|
||||||
|
code.push(Instruction::stw_reg_offset(
|
||||||
|
*reg,
|
||||||
|
Register::Spr,
|
||||||
|
offset - self.stack_offset,
|
||||||
|
));
|
||||||
|
return Ok(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Track that we pushed one word
|
||||||
|
self.stack_offset -= 4;
|
||||||
|
|
||||||
|
// if the variable is not on the stack:
|
||||||
|
// push register to stack (spr decrements automatically)
|
||||||
|
let offset = self.stack_offset;
|
||||||
|
code.push(Instruction::push(*reg));
|
||||||
|
|
||||||
|
// Update variable location - it's now at current spr
|
||||||
|
// Note: We track offset from bpr for consistency
|
||||||
|
location.stack = Some(offset);
|
||||||
|
|
||||||
|
Ok(code)
|
||||||
|
} else {
|
||||||
|
Err(CompilerError::Generic(format!(
|
||||||
|
"Register {} does not contain a variable to spill!",
|
||||||
|
reg
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// free a register by spilling it to the stack.
|
||||||
|
/// Returns assembly code to perform the spill
|
||||||
|
pub fn free_register(&mut self, reg: &Register) -> Result<(i32, Instruction), CompilerError> {
|
||||||
|
// check if the variable is declared.
|
||||||
|
if let Some(var_name) = self.register_contents.get(reg).cloned()
|
||||||
|
&& let Some(location) = self.variable_locations.get_mut(&var_name)
|
||||||
|
{
|
||||||
|
// check if var name is on the stack
|
||||||
|
if let Some(offset) = location.stack {
|
||||||
|
// store current register value in stack location
|
||||||
|
let code =
|
||||||
|
Instruction::stw_reg_offset(*reg, Register::Spr, offset - self.stack_offset);
|
||||||
|
|
||||||
|
// free the register.
|
||||||
|
location.register = None;
|
||||||
|
self.register_contents.remove(reg);
|
||||||
|
return Ok((offset, code));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Track that we pushed one word
|
||||||
|
self.stack_offset -= 4;
|
||||||
|
|
||||||
|
let offset = self.stack_offset;
|
||||||
|
let code = Instruction::push(*reg);
|
||||||
|
|
||||||
|
// Update variable location
|
||||||
|
// Note: We track offset from bpr for consistency
|
||||||
|
location.stack = Some(offset);
|
||||||
|
location.register = None;
|
||||||
|
self.register_contents.remove(reg);
|
||||||
|
|
||||||
|
Ok((offset, code))
|
||||||
|
} else {
|
||||||
|
Err(CompilerError::Generic(format!(
|
||||||
|
"Register {} does not contain a variable to spill!",
|
||||||
|
reg
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find a free register (not currently in use)
|
||||||
|
fn find_free_register(&self) -> Option<Register> {
|
||||||
|
self.in_use
|
||||||
|
.iter()
|
||||||
|
.filter(|(_, in_use)| !*in_use)
|
||||||
|
.map(|(reg, _)| *reg)
|
||||||
|
.next()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Spill all registers to stack (useful before function calls)
|
||||||
|
pub fn _spill_all(&mut self) -> InsBlock {
|
||||||
|
let mut code = InsBlock::new();
|
||||||
|
|
||||||
|
let regs_to_spill: Vec<Register> = self.register_contents.keys().cloned().collect();
|
||||||
|
|
||||||
|
for reg in regs_to_spill {
|
||||||
|
if let Ok(spill_code) = self.free_register(®) {
|
||||||
|
code.push(spill_code.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
code
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the total stack offset
|
||||||
|
pub fn get_stack_offset(&self) -> i32 {
|
||||||
|
self.stack_offset
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the total stack space needed for local variables
|
||||||
|
pub fn _get_stack_size(&self) -> i32 {
|
||||||
|
-self.stack_offset // Convert negative offset to positive size
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reset allocator for a new function
|
||||||
|
pub fn reset(&mut self) {
|
||||||
|
self.variable_locations.clear();
|
||||||
|
self.register_contents.clear();
|
||||||
|
self.stack_offset = -4;
|
||||||
|
self.in_use = vec![
|
||||||
|
Register::Rg0,
|
||||||
|
Register::Rg1,
|
||||||
|
Register::Rg2,
|
||||||
|
Register::Rg3,
|
||||||
|
Register::Rg4,
|
||||||
|
Register::Rg5,
|
||||||
|
Register::Rg6,
|
||||||
|
Register::Rg7,
|
||||||
|
Register::Rg8,
|
||||||
|
Register::Rg9,
|
||||||
|
Register::Rga,
|
||||||
|
Register::Rgb,
|
||||||
|
Register::Rgc,
|
||||||
|
Register::Rgd,
|
||||||
|
Register::Rge,
|
||||||
|
Register::Rgf,
|
||||||
|
]
|
||||||
|
.iter()
|
||||||
|
.map(|®| (reg, false))
|
||||||
|
.collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get list of registers that contain variables and are in use
|
||||||
|
/// These need to be saved before function calls
|
||||||
|
pub fn get_caller_saved_registers(&self) -> Vec<Register> {
|
||||||
|
self.register_contents
|
||||||
|
.iter()
|
||||||
|
.filter(|(reg, _)| {
|
||||||
|
self.in_use
|
||||||
|
.get(**reg as usize)
|
||||||
|
.unwrap_or(&(Register::Null, false))
|
||||||
|
.1
|
||||||
|
})
|
||||||
|
.map(|(reg, _)| *reg)
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub enum Register {
|
||||||
|
// general purpose
|
||||||
|
Rg0 = 0,
|
||||||
|
Rg1 = 1,
|
||||||
|
Rg2 = 2,
|
||||||
|
Rg3 = 3,
|
||||||
|
Rg4 = 4,
|
||||||
|
Rg5 = 5,
|
||||||
|
Rg6 = 6,
|
||||||
|
Rg7 = 7,
|
||||||
|
Rg8 = 8,
|
||||||
|
Rg9 = 9,
|
||||||
|
Rga = 10,
|
||||||
|
Rgb = 11,
|
||||||
|
Rgc = 12,
|
||||||
|
Rgd = 13,
|
||||||
|
Rge = 14,
|
||||||
|
Rgf = 15,
|
||||||
|
|
||||||
|
// special
|
||||||
|
Bpr,
|
||||||
|
Spr,
|
||||||
|
Ret,
|
||||||
|
Acc,
|
||||||
|
|
||||||
|
// read only
|
||||||
|
Pcx,
|
||||||
|
Zero,
|
||||||
|
|
||||||
|
// null
|
||||||
|
Null,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Register {
|
||||||
|
pub fn get_gp() -> [Register; 16] {
|
||||||
|
[
|
||||||
|
Register::Rg0,
|
||||||
|
Register::Rg1,
|
||||||
|
Register::Rg2,
|
||||||
|
Register::Rg3,
|
||||||
|
Register::Rg4,
|
||||||
|
Register::Rg5,
|
||||||
|
Register::Rg6,
|
||||||
|
Register::Rg7,
|
||||||
|
Register::Rg8,
|
||||||
|
Register::Rg9,
|
||||||
|
Register::Rga,
|
||||||
|
Register::Rgb,
|
||||||
|
Register::Rgc,
|
||||||
|
Register::Rgd,
|
||||||
|
Register::Rge,
|
||||||
|
Register::Rgf,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_gp(&self) -> bool {
|
||||||
|
(*self as u8) < 16
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_index(idx: usize) -> Register {
|
||||||
|
match idx {
|
||||||
|
0 => Register::Rg0,
|
||||||
|
1 => Register::Rg1,
|
||||||
|
2 => Register::Rg2,
|
||||||
|
3 => Register::Rg3,
|
||||||
|
4 => Register::Rg4,
|
||||||
|
5 => Register::Rg5,
|
||||||
|
6 => Register::Rg6,
|
||||||
|
7 => Register::Rg7,
|
||||||
|
8 => Register::Rg8,
|
||||||
|
9 => Register::Rg9,
|
||||||
|
10 => Register::Rga,
|
||||||
|
11 => Register::Rgb,
|
||||||
|
12 => Register::Rgc,
|
||||||
|
13 => Register::Rgd,
|
||||||
|
14 => Register::Rge,
|
||||||
|
15 => Register::Rgf,
|
||||||
|
_ => unreachable!("this function shouldn't ever be called with idx>15"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Register {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Rg0 => write!(f, "rg0"),
|
||||||
|
Self::Rg1 => write!(f, "rg1"),
|
||||||
|
Self::Rg2 => write!(f, "rg2"),
|
||||||
|
Self::Rg3 => write!(f, "rg3"),
|
||||||
|
Self::Rg4 => write!(f, "rg4"),
|
||||||
|
Self::Rg5 => write!(f, "rg5"),
|
||||||
|
Self::Rg6 => write!(f, "rg6"),
|
||||||
|
Self::Rg7 => write!(f, "rg7"),
|
||||||
|
Self::Rg8 => write!(f, "rg8"),
|
||||||
|
Self::Rg9 => write!(f, "rg9"),
|
||||||
|
Self::Rga => write!(f, "rga"),
|
||||||
|
Self::Rgb => write!(f, "rgb"),
|
||||||
|
Self::Rgc => write!(f, "rgc"),
|
||||||
|
Self::Rgd => write!(f, "rgd"),
|
||||||
|
Self::Rge => write!(f, "rge"),
|
||||||
|
Self::Rgf => write!(f, "rgf"),
|
||||||
|
|
||||||
|
Self::Acc => write!(f, "acc"),
|
||||||
|
Self::Ret => write!(f, "ret"),
|
||||||
|
Self::Bpr => write!(f, "bpr"),
|
||||||
|
Self::Spr => write!(f, "spr"),
|
||||||
|
|
||||||
|
Self::Zero => write!(f, "zero"),
|
||||||
|
Self::Pcx => write!(f, "pcx"),
|
||||||
|
|
||||||
|
Self::Null => write!(f, "null"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,373 @@
|
|||||||
|
use std::{cell::RefCell, collections::HashMap, ops::Deref, rc::Rc};
|
||||||
|
|
||||||
|
use crate::backend::instruction::Instruction;
|
||||||
|
use crate::backend::registers::Register;
|
||||||
|
use crate::{
|
||||||
|
error,
|
||||||
|
model::{CompilerError, Name, TypeId},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// scope object
|
||||||
|
pub struct Scope<'a> {
|
||||||
|
/// outer scope, for a function this will be the global scope.
|
||||||
|
parent: Option<&'a mut Scope<'a>>,
|
||||||
|
alloc: Rc<RefCell<Allocator>>,
|
||||||
|
|
||||||
|
/// is the scope a function body or just a loop?
|
||||||
|
/// depending on the type, ending a scope will have different behaviour
|
||||||
|
r#type: ScopeType,
|
||||||
|
|
||||||
|
/// variables
|
||||||
|
variables: HashMap<String, Variable>,
|
||||||
|
|
||||||
|
entry_stack_offset: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Scope<'a> {
|
||||||
|
pub fn new() -> Scope<'a> {
|
||||||
|
let alloc = Rc::new(RefCell::new(Allocator::new()));
|
||||||
|
let entry_stack_offset = alloc.borrow().get_stack_offset();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
alloc,
|
||||||
|
entry_stack_offset,
|
||||||
|
parent: None,
|
||||||
|
r#type: ScopeType::Function,
|
||||||
|
variables: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_from(parent: &'a mut Scope<'a>, r#type: ScopeType) -> Scope<'a> {
|
||||||
|
let alloc = Rc::clone(&parent.alloc);
|
||||||
|
let entry_stack_offset = alloc.borrow().get_stack_offset();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
alloc,
|
||||||
|
entry_stack_offset,
|
||||||
|
parent: Some(parent),
|
||||||
|
r#type,
|
||||||
|
variables: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_var(&mut self, name: String, r#type: TypeId) -> Result<(), CompilerError> {
|
||||||
|
let mut var = Variable::new(name, r#type.clone());
|
||||||
|
|
||||||
|
if r#type.size() > 4 {
|
||||||
|
let slot = self.alloc.borrow_mut().allocate_stack_slot(r#type.size());
|
||||||
|
var.stack_slot = Some(slot);
|
||||||
|
} else {
|
||||||
|
let reg = self.alloc.borrow_mut().allocate_var()?;
|
||||||
|
var.register = Some(reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.variables.insert(var.name.clone(), var);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn alloc_temp(&mut self) -> Result<TempReg, CompilerError> {
|
||||||
|
self.alloc.borrow_mut().allocate_temp()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn free_temp(&mut self, temp: &TempReg) {
|
||||||
|
self.alloc.borrow_mut().free_temp(temp)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn free_var(&mut self, reg: &AssignedReg) {
|
||||||
|
self.alloc.borrow_mut().free_var(reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn close(&mut self) {
|
||||||
|
// tell the allocator that this scope is closing
|
||||||
|
// this reverts the stack offset to what it was before this scope was created.
|
||||||
|
self.alloc.clone().borrow_mut().destroy_scope(self);
|
||||||
|
|
||||||
|
for var in self.variables.clone().values() {
|
||||||
|
if let Some(reg) = var.register {
|
||||||
|
self.free_var(®);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_var(&mut self, var: &str) -> Option<&mut Variable> {
|
||||||
|
self.variables.get_mut(var)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn offset_read(
|
||||||
|
&mut self,
|
||||||
|
var: &str,
|
||||||
|
offset: i32,
|
||||||
|
) -> Result<(TempReg, Instruction), CompilerError> {
|
||||||
|
if let Some(var) = self.get_var(var) {
|
||||||
|
let slot = var
|
||||||
|
.stack_slot
|
||||||
|
.ok_or_else(|| error("Attempt to read from a var without a stack slot!"))?;
|
||||||
|
|
||||||
|
return self.alloc.borrow_mut().offset_read(&slot, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(CompilerError::Undefined(Name::new(var, None)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn offset_write(
|
||||||
|
&mut self,
|
||||||
|
reg: &TempReg,
|
||||||
|
var: &str,
|
||||||
|
offset: i32,
|
||||||
|
) -> Result<Instruction, CompilerError> {
|
||||||
|
if let Some(var) = self.get_var(var) {
|
||||||
|
let slot = var
|
||||||
|
.stack_slot
|
||||||
|
.ok_or_else(|| error("Attempt to write to a var without a stack slot!"))?;
|
||||||
|
|
||||||
|
return Ok(self.alloc.borrow_mut().offset_write(reg, &slot, offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(CompilerError::Undefined(Name::new(var, None)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_var(&mut self, var: &str) -> Result<(AssignedReg, Instruction), CompilerError> {
|
||||||
|
if let Some(v) = self.get_var(var).cloned()
|
||||||
|
&& let Some(slot) = v.stack_slot
|
||||||
|
{
|
||||||
|
let res = self.alloc.borrow_mut().load_var(&slot)?;
|
||||||
|
self.get_var(var).unwrap().register = Some(res.0);
|
||||||
|
return Ok(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
panic!("e")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn spill_var(&mut self, var: &str) -> Result<Instruction, CompilerError> {
|
||||||
|
if let Some(v) = self.get_var(var).cloned()
|
||||||
|
&& let Some(rg) = v.register
|
||||||
|
{
|
||||||
|
let mut slot = v.stack_slot;
|
||||||
|
let res = self.alloc.borrow_mut().spill_var(&rg, &mut slot);
|
||||||
|
self.get_var(var).unwrap().stack_slot = slot;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(CompilerError::Undefined(Name::new(var, None)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Scope<'_> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Copy, Clone, Debug)]
|
||||||
|
pub enum ScopeType {
|
||||||
|
Function,
|
||||||
|
IfBlock,
|
||||||
|
LoopBlock,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Variable {
|
||||||
|
pub name: String,
|
||||||
|
|
||||||
|
/// the type of the variable.
|
||||||
|
r#type: TypeId,
|
||||||
|
|
||||||
|
/// size taken up in bytes.
|
||||||
|
/// if size > 4, value must be stored on the stack.
|
||||||
|
pub size: usize,
|
||||||
|
|
||||||
|
pub stack_slot: Option<StackSlot>,
|
||||||
|
pub register: Option<AssignedReg>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Variable {
|
||||||
|
pub fn new(name: String, r#type: TypeId) -> Self {
|
||||||
|
Self {
|
||||||
|
name,
|
||||||
|
size: r#type.size(),
|
||||||
|
r#type,
|
||||||
|
stack_slot: None,
|
||||||
|
register: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Allocator {
|
||||||
|
stack_offset: i32,
|
||||||
|
in_use: [(Register, bool); 16],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Allocator {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let mut in_use = [(Register::Null, false); 16];
|
||||||
|
in_use.copy_from_slice(&Register::get_gp().map(|r| (r, false))[0..16]);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
stack_offset: 0,
|
||||||
|
in_use,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_stack_offset(&self) -> i32 {
|
||||||
|
self.stack_offset
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroy_scope(&mut self, scope: &mut Scope) {
|
||||||
|
self.stack_offset = scope.entry_stack_offset;
|
||||||
|
|
||||||
|
for var in scope.variables.drain() {
|
||||||
|
if let Some(assigned) = var.1.register {
|
||||||
|
self.free_var(&assigned);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// what we need:
|
||||||
|
|
||||||
|
// - create var in register from temporary register. free temp and use it.
|
||||||
|
//
|
||||||
|
// - create var on stack from struct/array literal. return stack offset to write to.
|
||||||
|
//
|
||||||
|
// - spill var from register to stack. return stack offset to write to.
|
||||||
|
//
|
||||||
|
// - read/write var from stack+offset into register to use while preserving the stack
|
||||||
|
// slot.
|
||||||
|
//
|
||||||
|
// - read / write bytes from the stack+offset in a larger variable into a register.
|
||||||
|
|
||||||
|
pub fn offset_read(
|
||||||
|
&mut self,
|
||||||
|
slot: &StackSlot,
|
||||||
|
offset: i32,
|
||||||
|
) -> Result<(TempReg, Instruction), CompilerError> {
|
||||||
|
let register = self.allocate_temp()?;
|
||||||
|
|
||||||
|
// instruction: reg = *(&var + offset)
|
||||||
|
Ok((
|
||||||
|
register.clone(),
|
||||||
|
Instruction::ldw_reg_offset(
|
||||||
|
Register::Spr,
|
||||||
|
*register,
|
||||||
|
(**slot + offset) - self.stack_offset,
|
||||||
|
),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn offset_write(&mut self, reg: &TempReg, slot: &StackSlot, offset: i32) -> Instruction {
|
||||||
|
// instruction: *(&var + offset) = reg
|
||||||
|
Instruction::stw_reg_offset(**reg, Register::Spr, (**slot + offset) - self.stack_offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_var(
|
||||||
|
&mut self,
|
||||||
|
slot: &StackSlot,
|
||||||
|
) -> Result<(AssignedReg, Instruction), CompilerError> {
|
||||||
|
let reg = self.allocate_var()?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
reg.clone(),
|
||||||
|
Instruction::ldw_reg_offset(Register::Spr, *reg, **slot - self.stack_offset),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn spill_var(
|
||||||
|
&mut self,
|
||||||
|
reg: &AssignedReg,
|
||||||
|
slot: &mut Option<StackSlot>,
|
||||||
|
// var: &mut Variable,
|
||||||
|
) -> Result<Instruction, CompilerError> {
|
||||||
|
if let Some(slot) = &slot {
|
||||||
|
let block =
|
||||||
|
Instruction::stw_reg_offset(**reg, Register::Spr, **slot - self.stack_offset);
|
||||||
|
|
||||||
|
self.free_var(reg);
|
||||||
|
return Ok(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
// var doesn't have a stack slot so we need to create one
|
||||||
|
let new_slot = self.allocate_stack_slot(4); // alloc 4 bytes for reg value.
|
||||||
|
let block = Instruction::push(**reg);
|
||||||
|
|
||||||
|
self.free_var(reg);
|
||||||
|
*slot = Some(new_slot);
|
||||||
|
Ok(block)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn allocate_stack_slot(&mut self, size: usize) -> StackSlot {
|
||||||
|
self.stack_offset -= size as i32;
|
||||||
|
let offset = self.stack_offset;
|
||||||
|
StackSlot(offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn allocate_var(&mut self) -> Result<AssignedReg, CompilerError> {
|
||||||
|
if let Some(reg) = self.find_free_register() {
|
||||||
|
Ok(AssignedReg(reg))
|
||||||
|
} else {
|
||||||
|
Err(CompilerError::Generic(
|
||||||
|
"No free registers available".to_string(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn allocate_temp(&mut self) -> Result<TempReg, CompilerError> {
|
||||||
|
// allocates a temporary register
|
||||||
|
if let Some(reg) = self.find_free_register() {
|
||||||
|
Ok(TempReg(reg))
|
||||||
|
} else {
|
||||||
|
todo!("an efficient stack spilling algorithm. needs scope awareness.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn free_temp(&mut self, temp: &TempReg) {
|
||||||
|
// frees a temporary register.
|
||||||
|
self.in_use[**temp as usize].1 = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn free_var(&mut self, reg: &AssignedReg) {
|
||||||
|
// frees a register.
|
||||||
|
self.in_use[**reg as usize].1 = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we have register(s) free, return the first one.
|
||||||
|
fn find_free_register(&mut self) -> Option<Register> {
|
||||||
|
self.in_use.iter_mut().find_map(|(reg, used)| {
|
||||||
|
if !*used {
|
||||||
|
*used = true;
|
||||||
|
Some(*reg)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct TempReg(Register);
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct AssignedReg(Register);
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct StackSlot(i32);
|
||||||
|
|
||||||
|
impl Deref for TempReg {
|
||||||
|
type Target = Register;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for AssignedReg {
|
||||||
|
type Target = Register;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for StackSlot {
|
||||||
|
type Target = i32;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,38 @@
|
|||||||
|
use crate::frontend::parser::{ParseResult, Parser};
|
||||||
|
use crate::model::{CompilerError, Program};
|
||||||
|
use dsa_tasks::ProcessAgent;
|
||||||
|
|
||||||
|
// mod c;
|
||||||
|
mod lexer;
|
||||||
|
mod parser;
|
||||||
|
mod semantic_analyser;
|
||||||
|
|
||||||
|
pub fn generate_ast(input: &str, ctx: &ProcessAgent) -> Result<Program, CompilerError> {
|
||||||
|
ctx.log("Tokenising Input...");
|
||||||
|
|
||||||
|
let lexer = lexer::Lexer::new(&input);
|
||||||
|
let tokens = lexer.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
// println!("{tokens:#?}");
|
||||||
|
|
||||||
|
ctx.log(&format!("Parsing {} Tokens...", tokens.len()));
|
||||||
|
|
||||||
|
let mut parser = Parser::new(tokens);
|
||||||
|
let ast = match parser.parse() {
|
||||||
|
ParseResult::Accept(ast) => ast,
|
||||||
|
ParseResult::Reject(e) => return Err(e),
|
||||||
|
ParseResult::Deny => {
|
||||||
|
return Err(CompilerError::Generic("Parser used ::Deny".to_string()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// println!("{ast:#?}");
|
||||||
|
|
||||||
|
ctx.log("Analyzing AST...");
|
||||||
|
ctx.log("Checking Type Information...");
|
||||||
|
|
||||||
|
// let mut analyser = Analyser::new();
|
||||||
|
// analyser.analyse(ast.clone()).unwrap();
|
||||||
|
|
||||||
|
ctx.log("Type Checking Complete...");
|
||||||
|
Ok(ast)
|
||||||
|
}
|
||||||
@@ -0,0 +1,988 @@
|
|||||||
|
use super::lexer::Token;
|
||||||
|
use crate::model::{
|
||||||
|
AssignmentOperator, BinaryOperator, Block, Call, CompilerError, ConstExpr, Declaration,
|
||||||
|
Dependency, Expression, Number, Program, Statement, TypeId, UnaryOperator, Variable,
|
||||||
|
};
|
||||||
|
use crate::{expect_tt, expect_value};
|
||||||
|
use std::ops::{ControlFlow, FromResidual, Residual, Try};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum ParseResult<T, E> {
|
||||||
|
Accept(T),
|
||||||
|
Deny,
|
||||||
|
Reject(E),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Parser {
|
||||||
|
tokens: Vec<Token>,
|
||||||
|
idx: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parser {
|
||||||
|
pub fn new(tokens: Vec<Token>) -> Self {
|
||||||
|
Self { tokens, idx: 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(&mut self) -> ParseResult<Program, CompilerError> {
|
||||||
|
let mut declarations = Vec::new();
|
||||||
|
|
||||||
|
while let ParseResult::Accept(_) = self.peek_next() {
|
||||||
|
declarations.push(self.parse_declaration()?);
|
||||||
|
}
|
||||||
|
|
||||||
|
ParseResult::Accept(Program { declarations })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_declaration(&mut self) -> ParseResult<Declaration, CompilerError> {
|
||||||
|
if expect_tt!(self.peek_next()?, Fn).accepted() {
|
||||||
|
return self.parse_func();
|
||||||
|
}
|
||||||
|
|
||||||
|
if expect_tt!(self.peek_next()?, Struct).accepted() {
|
||||||
|
return self.parse_struct();
|
||||||
|
}
|
||||||
|
|
||||||
|
if expect_tt!(self.peek_next()?, Include).accepted() {
|
||||||
|
// expect include keyword
|
||||||
|
let _ = self.next();
|
||||||
|
|
||||||
|
// expect namespace identifier
|
||||||
|
let name = expect_value!(self.next()?, Identifier)?;
|
||||||
|
|
||||||
|
// expect colon
|
||||||
|
let _ = expect_tt!(self.next()?, Colon)?;
|
||||||
|
|
||||||
|
// expect string literal (module path)
|
||||||
|
let path = expect_value!(self.next()?, String)?;
|
||||||
|
|
||||||
|
// expect semicolon
|
||||||
|
let _ = expect_tt!(self.next()?, Semicolon)?;
|
||||||
|
|
||||||
|
return ParseResult::Accept(Declaration::Dependency(Dependency {
|
||||||
|
name: name.name,
|
||||||
|
path,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
if expect_tt!(self.peek_next()?, Const, Static).accepted() {
|
||||||
|
let is_const = match self.next()? {
|
||||||
|
Token::Const => true,
|
||||||
|
Token::Static => false,
|
||||||
|
_ => {
|
||||||
|
return ParseResult::Reject(CompilerError::Generic(String::from(
|
||||||
|
"This can't happen!",
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let var = self.parse_var_decl()?;
|
||||||
|
|
||||||
|
let _ = expect_tt!(self.next()?, Assign)?;
|
||||||
|
|
||||||
|
let value = self.next()?;
|
||||||
|
let init = match value {
|
||||||
|
Token::String(x) => Some(ConstExpr::String(x)),
|
||||||
|
Token::SignedInt(x, _) => Some(ConstExpr::Number(x)),
|
||||||
|
Token::UnsignedInt(x, _) => Some(ConstExpr::Number(x as i32)),
|
||||||
|
_ => {
|
||||||
|
return ParseResult::Reject(CompilerError::UnexpectedToken(
|
||||||
|
value.tt().to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let _ = expect_tt!(self.next()?, Semicolon)?;
|
||||||
|
|
||||||
|
return ParseResult::Accept(Declaration::Variable {
|
||||||
|
var,
|
||||||
|
init,
|
||||||
|
is_const,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ParseResult::Reject(CompilerError::UnexpectedEndOfInput)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_struct(&mut self) -> ParseResult<Declaration, CompilerError> {
|
||||||
|
let _ = expect_tt!(self.next()?, Struct)?;
|
||||||
|
let name = expect_value!(self.next()?, Identifier)?;
|
||||||
|
|
||||||
|
let _ = expect_tt!(self.next()?, LeftBrace)?;
|
||||||
|
|
||||||
|
let mut fields = Vec::new();
|
||||||
|
while expect_tt!(self.peek_next()?, Identifier).accepted() {
|
||||||
|
let arg = self.parse_var_decl()?;
|
||||||
|
fields.push(arg);
|
||||||
|
|
||||||
|
if expect_tt!(self.peek_next()?, Comma).accepted() {
|
||||||
|
self.next()?;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = expect_tt!(self.next()?, RightBrace)?;
|
||||||
|
ParseResult::Accept(Declaration::Struct { name, fields })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_func(&mut self) -> ParseResult<Declaration, CompilerError> {
|
||||||
|
// expect function keyword
|
||||||
|
let _ = expect_tt!(self.next()?, Fn);
|
||||||
|
// expect function name
|
||||||
|
let name = expect_value!(self.next()?, Identifier)?;
|
||||||
|
|
||||||
|
// expect left paren
|
||||||
|
let _ = expect_tt!(self.next()?, LeftParen)?;
|
||||||
|
|
||||||
|
let mut params = Vec::new();
|
||||||
|
while expect_tt!(self.peek_next()?, Identifier).accepted() {
|
||||||
|
let arg = self.parse_var_decl()?;
|
||||||
|
params.push(arg);
|
||||||
|
|
||||||
|
if expect_tt!(self.peek_next()?, Comma).accepted() {
|
||||||
|
self.next()?;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// expect right paren
|
||||||
|
let _ = expect_tt!(self.next()?, RightParen)?;
|
||||||
|
|
||||||
|
// see if we can parse the return type!
|
||||||
|
let mut return_type = TypeId::Void;
|
||||||
|
if expect_tt!(self.peek_next()?, RightArrow).accepted() {
|
||||||
|
let _ = self.next();
|
||||||
|
return_type = self.parse_type()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// expect vald block
|
||||||
|
if expect_tt!(self.peek_next()?, LeftBrace).accepted() {
|
||||||
|
ParseResult::Accept(Declaration::Function {
|
||||||
|
name: name.name,
|
||||||
|
params,
|
||||||
|
return_type,
|
||||||
|
body: self.parse_block()?,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
ParseResult::Reject(CompilerError::UnexpectedToken(
|
||||||
|
self.peek_next()?.tt().to_string(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_block(&mut self) -> ParseResult<Block, CompilerError> {
|
||||||
|
// expect left brace
|
||||||
|
let _ = expect_tt!(self.next()?, LeftBrace)?;
|
||||||
|
|
||||||
|
let mut block = Vec::new();
|
||||||
|
while !expect_tt!(self.peek_next()?, RightBrace).accepted() {
|
||||||
|
block.push(self.parse_statement()?);
|
||||||
|
}
|
||||||
|
|
||||||
|
// expect right brace
|
||||||
|
let _ = expect_tt!(self.next()?, RightBrace)?;
|
||||||
|
|
||||||
|
ParseResult::Accept(block)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_statement(&mut self) -> ParseResult<Statement, CompilerError> {
|
||||||
|
// handle if statements
|
||||||
|
if expect_tt!(self.peek_next()?, If).accepted() {
|
||||||
|
self.next()?;
|
||||||
|
|
||||||
|
let condition = self.parse_expression()?;
|
||||||
|
|
||||||
|
let then_stmt = self.parse_block()?;
|
||||||
|
|
||||||
|
if !expect_tt!(self.peek_next()?, Else).accepted() {
|
||||||
|
return ParseResult::Accept(Statement::If {
|
||||||
|
condition,
|
||||||
|
then_stmt,
|
||||||
|
else_stmt: vec![],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = expect_tt!(self.next()?, Else)?;
|
||||||
|
|
||||||
|
let else_stmt = self.parse_block()?;
|
||||||
|
|
||||||
|
return ParseResult::Accept(Statement::If {
|
||||||
|
condition,
|
||||||
|
then_stmt,
|
||||||
|
else_stmt,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle while loops
|
||||||
|
if expect_tt!(self.peek_next()?, While).accepted() {
|
||||||
|
self.next()?;
|
||||||
|
|
||||||
|
// expect valid expression
|
||||||
|
let expr = self.parse_expression()?;
|
||||||
|
|
||||||
|
// expect valid block after
|
||||||
|
let block = self.parse_block()?;
|
||||||
|
|
||||||
|
// return result
|
||||||
|
return ParseResult::Accept(Statement::While {
|
||||||
|
condition: expr,
|
||||||
|
body: block,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle indefinite loops
|
||||||
|
if expect_tt!(self.peek_next()?, Loop).accepted() {
|
||||||
|
self.next()?;
|
||||||
|
|
||||||
|
// parse the inner block
|
||||||
|
return ParseResult::Accept(Statement::Loop(self.parse_block()?));
|
||||||
|
}
|
||||||
|
|
||||||
|
if expect_tt!(self.peek_next()?, Return).accepted() {
|
||||||
|
self.next()?;
|
||||||
|
|
||||||
|
// handle case where nothing is returned
|
||||||
|
if expect_tt!(self.peek_next()?, Semicolon).accepted() {
|
||||||
|
return ParseResult::Accept(Statement::Return(None));
|
||||||
|
}
|
||||||
|
|
||||||
|
let expr = self.parse_expression()?;
|
||||||
|
expect_tt!(self.next()?, Semicolon)?;
|
||||||
|
|
||||||
|
return ParseResult::Accept(Statement::Return(Some(expr)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if expect_tt!(self.peek_next()?, Break).accepted() {
|
||||||
|
self.next()?;
|
||||||
|
|
||||||
|
// expect semicolon
|
||||||
|
expect_tt!(self.next()?, Semicolon)?;
|
||||||
|
|
||||||
|
// return result
|
||||||
|
return ParseResult::Accept(Statement::Break);
|
||||||
|
}
|
||||||
|
|
||||||
|
if expect_tt!(self.peek_next()?, Continue).accepted() {
|
||||||
|
self.next()?;
|
||||||
|
|
||||||
|
// expect semicolon
|
||||||
|
expect_tt!(self.next()?, Semicolon)?;
|
||||||
|
|
||||||
|
// return result
|
||||||
|
return ParseResult::Accept(Statement::Continue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle writes to pointers!
|
||||||
|
if expect_tt!(self.peek_next()?, Star).accepted() {
|
||||||
|
self.next()?;
|
||||||
|
|
||||||
|
let left = if expect_tt!(self.peek_next()?, Identifier).accepted() {
|
||||||
|
let identifier = expect_value!(self.next()?, Identifier)?;
|
||||||
|
|
||||||
|
Expression::Variable {
|
||||||
|
name: identifier,
|
||||||
|
expr_type: None,
|
||||||
|
}
|
||||||
|
} else if expect_tt!(self.peek_next()?, LeftParen).accepted() {
|
||||||
|
self.next()?;
|
||||||
|
|
||||||
|
let expr = self.parse_expression()?;
|
||||||
|
|
||||||
|
let _ = expect_tt!(self.next()?, RightParen).accepted();
|
||||||
|
|
||||||
|
expr
|
||||||
|
} else {
|
||||||
|
return ParseResult::Reject(CompilerError::UnexpectedToken(
|
||||||
|
self.peek_next()?.tt().to_string(),
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
let _ = expect_tt!(self.next()?, Assign)?;
|
||||||
|
|
||||||
|
let right = self.parse_expression()?;
|
||||||
|
|
||||||
|
// expect semicolon
|
||||||
|
expect_tt!(self.next()?, Semicolon)?;
|
||||||
|
|
||||||
|
// return result
|
||||||
|
return ParseResult::Accept(Statement::PtrWrite {
|
||||||
|
ptr: left,
|
||||||
|
value: right,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle let statements (declarations)
|
||||||
|
if expect_tt!(self.peek_next()?, Let).accepted() {
|
||||||
|
self.next();
|
||||||
|
|
||||||
|
// expect variable name and type.
|
||||||
|
let name = self.parse_var_decl()?;
|
||||||
|
|
||||||
|
// handle uninitialised variable case
|
||||||
|
if expect_tt!(self.peek_next()?, Semicolon).accepted() {
|
||||||
|
self.next();
|
||||||
|
return ParseResult::Accept(Statement::Declaration {
|
||||||
|
var: name,
|
||||||
|
value: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle initialised case
|
||||||
|
// expect equals
|
||||||
|
let _ = expect_tt!(self.next()?, Assign)?;
|
||||||
|
|
||||||
|
// expect a valid expression
|
||||||
|
let expr = self.parse_expression()?;
|
||||||
|
|
||||||
|
let _ = expect_tt!(self.next()?, Semicolon);
|
||||||
|
|
||||||
|
// return statement
|
||||||
|
return ParseResult::Accept(Statement::Declaration {
|
||||||
|
var: name,
|
||||||
|
value: Some(expr),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle an in-place function call
|
||||||
|
if let ParseResult::Accept(name) = expect_value!(self.peek_next()?, Identifier)
|
||||||
|
&& let ParseResult::Accept(operator) = expect_tt!(
|
||||||
|
self.peek(1)?,
|
||||||
|
Assign,
|
||||||
|
PlusEqual,
|
||||||
|
MinusEqual,
|
||||||
|
StarEqual,
|
||||||
|
SlashEqual,
|
||||||
|
PercentEqual,
|
||||||
|
AndEqual,
|
||||||
|
OrEqual,
|
||||||
|
XorEqual,
|
||||||
|
ShlEqual,
|
||||||
|
ShrEqual
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// consume name token
|
||||||
|
self.next()?;
|
||||||
|
|
||||||
|
// pattern match to find operator
|
||||||
|
let operator = match operator {
|
||||||
|
Token::Assign => AssignmentOperator::Assign,
|
||||||
|
Token::PlusEqual => AssignmentOperator::AddAssign,
|
||||||
|
Token::MinusEqual => AssignmentOperator::SubAssign,
|
||||||
|
Token::StarEqual => AssignmentOperator::MulAssign,
|
||||||
|
Token::SlashEqual => AssignmentOperator::DivAssign,
|
||||||
|
Token::PercentEqual => AssignmentOperator::ModAssign,
|
||||||
|
Token::AndEqual => AssignmentOperator::AndAssign,
|
||||||
|
Token::OrEqual => AssignmentOperator::OrAssign,
|
||||||
|
Token::XorEqual => AssignmentOperator::XorAssign,
|
||||||
|
Token::ShlEqual => AssignmentOperator::LeftShiftAssign,
|
||||||
|
Token::ShrEqual => AssignmentOperator::RightShiftAssign,
|
||||||
|
_ => {
|
||||||
|
return ParseResult::Reject(CompilerError::UnexpectedToken(
|
||||||
|
self.peek_next()?.tt().to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// consume operator token
|
||||||
|
self.next()?;
|
||||||
|
|
||||||
|
let value = self.parse_expression()?;
|
||||||
|
|
||||||
|
let _ = expect_tt!(self.next()?, Semicolon);
|
||||||
|
|
||||||
|
return ParseResult::Accept(Statement::Assign {
|
||||||
|
varname: name.name,
|
||||||
|
operator,
|
||||||
|
value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse an expression and a semicolon
|
||||||
|
let expr = self.parse_expression()?;
|
||||||
|
let _ = expect_tt!(self.next()?, Semicolon)?;
|
||||||
|
|
||||||
|
ParseResult::Accept(Statement::Expression { expr })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_expression(&mut self) -> ParseResult<Expression, CompilerError> {
|
||||||
|
self.parse_logical_or()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_logical_or(&mut self) -> ParseResult<Expression, CompilerError> {
|
||||||
|
let left = self.parse_logical_and()?;
|
||||||
|
|
||||||
|
let op = match self.peek_next()? {
|
||||||
|
Token::LogicalOr => BinaryOperator::LogicalOr,
|
||||||
|
_ => return ParseResult::Accept(left),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.next()?;
|
||||||
|
ParseResult::Accept(Expression::Binary {
|
||||||
|
op,
|
||||||
|
left: Box::new(left),
|
||||||
|
right: Box::new(self.parse_logical_or()?),
|
||||||
|
type_id: Some(TypeId::U32),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_logical_and(&mut self) -> ParseResult<Expression, CompilerError> {
|
||||||
|
let left = self.parse_bitwise_or()?;
|
||||||
|
|
||||||
|
let op = match self.peek_next()? {
|
||||||
|
Token::LogicalAnd => BinaryOperator::LogicalAnd,
|
||||||
|
_ => return ParseResult::Accept(left),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.next()?;
|
||||||
|
ParseResult::Accept(Expression::Binary {
|
||||||
|
op,
|
||||||
|
left: Box::new(left),
|
||||||
|
right: Box::new(self.parse_logical_and()?),
|
||||||
|
type_id: Some(TypeId::U32),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_bitwise_or(&mut self) -> ParseResult<Expression, CompilerError> {
|
||||||
|
let left = self.parse_bitwise_xor()?;
|
||||||
|
|
||||||
|
let op = match self.peek_next()? {
|
||||||
|
Token::Pipe => BinaryOperator::BitwiseOr,
|
||||||
|
_ => return ParseResult::Accept(left),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.next()?;
|
||||||
|
ParseResult::Accept(Expression::Binary {
|
||||||
|
op,
|
||||||
|
left: Box::new(left),
|
||||||
|
right: Box::new(self.parse_bitwise_or()?),
|
||||||
|
type_id: Some(TypeId::U32),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_bitwise_xor(&mut self) -> ParseResult<Expression, CompilerError> {
|
||||||
|
let left = self.parse_bitwise_and()?;
|
||||||
|
|
||||||
|
let op = match self.peek_next()? {
|
||||||
|
Token::Caret => BinaryOperator::BitwiseXor,
|
||||||
|
_ => return ParseResult::Accept(left),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.next()?;
|
||||||
|
ParseResult::Accept(Expression::Binary {
|
||||||
|
op,
|
||||||
|
left: Box::new(left),
|
||||||
|
right: Box::new(self.parse_bitwise_xor()?),
|
||||||
|
type_id: Some(TypeId::U32),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_bitwise_and(&mut self) -> ParseResult<Expression, CompilerError> {
|
||||||
|
let left = self.parse_comparison()?;
|
||||||
|
|
||||||
|
let op = match self.peek_next()? {
|
||||||
|
Token::Ampersand => BinaryOperator::BitwiseAnd,
|
||||||
|
_ => return ParseResult::Accept(left),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.next()?;
|
||||||
|
ParseResult::Accept(Expression::Binary {
|
||||||
|
op,
|
||||||
|
left: Box::new(left),
|
||||||
|
right: Box::new(self.parse_bitwise_and()?),
|
||||||
|
type_id: Some(TypeId::U32),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_comparison(&mut self) -> ParseResult<Expression, CompilerError> {
|
||||||
|
let left = self.parse_shift()?;
|
||||||
|
|
||||||
|
let op = match self.peek_next()? {
|
||||||
|
Token::EqualEqual => BinaryOperator::Equal,
|
||||||
|
Token::BangEqual => BinaryOperator::NotEqual,
|
||||||
|
Token::Less => BinaryOperator::LessThan,
|
||||||
|
Token::Greater => BinaryOperator::GreaterThan,
|
||||||
|
Token::LessEqual => BinaryOperator::LessOrEqual,
|
||||||
|
Token::GreaterEqual => BinaryOperator::GreaterOrEqual,
|
||||||
|
_ => return ParseResult::Accept(left),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.next()?;
|
||||||
|
ParseResult::Accept(Expression::Binary {
|
||||||
|
op,
|
||||||
|
left: Box::new(left),
|
||||||
|
right: Box::new(self.parse_comparison()?),
|
||||||
|
type_id: Some(TypeId::Bool),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_shift(&mut self) -> ParseResult<Expression, CompilerError> {
|
||||||
|
let left = self.parse_additive()?;
|
||||||
|
|
||||||
|
let op = match self.peek_next()? {
|
||||||
|
Token::LeftShift => BinaryOperator::LeftShift,
|
||||||
|
Token::RightShift => BinaryOperator::RightShift,
|
||||||
|
_ => return ParseResult::Accept(left),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.next()?;
|
||||||
|
ParseResult::Accept(Expression::Binary {
|
||||||
|
op,
|
||||||
|
left: Box::new(left),
|
||||||
|
right: Box::new(self.parse_shift()?),
|
||||||
|
type_id: Some(TypeId::U32),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_additive(&mut self) -> ParseResult<Expression, CompilerError> {
|
||||||
|
let left = self.parse_multiplicative()?;
|
||||||
|
|
||||||
|
let op = match self.peek_next()? {
|
||||||
|
Token::Plus => BinaryOperator::Add,
|
||||||
|
Token::Minus => BinaryOperator::Sub,
|
||||||
|
_ => return ParseResult::Accept(left),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.next()?;
|
||||||
|
ParseResult::Accept(Expression::Binary {
|
||||||
|
op,
|
||||||
|
left: Box::new(left),
|
||||||
|
right: Box::new(self.parse_additive()?),
|
||||||
|
type_id: Some(TypeId::U32),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_multiplicative(&mut self) -> ParseResult<Expression, CompilerError> {
|
||||||
|
let left = self.parse_unary()?;
|
||||||
|
|
||||||
|
let op = match self.peek_next()? {
|
||||||
|
Token::Star => BinaryOperator::Mul,
|
||||||
|
Token::Slash => BinaryOperator::Div,
|
||||||
|
_ => return ParseResult::Accept(left),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.next()?;
|
||||||
|
ParseResult::Accept(Expression::Binary {
|
||||||
|
op,
|
||||||
|
left: Box::new(left),
|
||||||
|
right: Box::new(self.parse_multiplicative()?),
|
||||||
|
type_id: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_unary(&mut self) -> ParseResult<Expression, CompilerError> {
|
||||||
|
let op = match self.peek_next()? {
|
||||||
|
// prefix inc/dec
|
||||||
|
Token::PlusPlus => UnaryOperator::Increment,
|
||||||
|
Token::MinusMinus => UnaryOperator::Decrement,
|
||||||
|
|
||||||
|
// arithmetic
|
||||||
|
Token::Plus => UnaryOperator::Plus,
|
||||||
|
Token::Minus => UnaryOperator::Minus,
|
||||||
|
|
||||||
|
// pointer
|
||||||
|
Token::Star => UnaryOperator::Dereference,
|
||||||
|
Token::Ampersand => UnaryOperator::AddressOf,
|
||||||
|
|
||||||
|
// boolean
|
||||||
|
Token::Bang => UnaryOperator::LogicalNot,
|
||||||
|
Token::Tilde => UnaryOperator::BitwiseNot,
|
||||||
|
|
||||||
|
Token::SizeOf => UnaryOperator::SizeOf,
|
||||||
|
_ => {
|
||||||
|
let expr = self.parse_primary()?;
|
||||||
|
return self.parse_postfix(expr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.next()?;
|
||||||
|
let operand = Box::new(self.parse_unary()?);
|
||||||
|
ParseResult::Accept(Expression::Unary {
|
||||||
|
op,
|
||||||
|
operand,
|
||||||
|
type_id: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_postfix(&mut self, mut expr: Expression) -> ParseResult<Expression, CompilerError> {
|
||||||
|
loop {
|
||||||
|
match self.peek_next()? {
|
||||||
|
// Type cast: expr as Type
|
||||||
|
Token::As => {
|
||||||
|
self.next()?; // consume 'as'
|
||||||
|
let target_type = self.parse_type()?;
|
||||||
|
expr = Expression::TypeCast {
|
||||||
|
expr: Box::new(expr),
|
||||||
|
target_type,
|
||||||
|
type_id: None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Postfix increment/decrement
|
||||||
|
Token::PlusPlus => {
|
||||||
|
self.next()?;
|
||||||
|
expr = Expression::UnaryPostfix {
|
||||||
|
op: UnaryOperator::Increment,
|
||||||
|
operand: Box::new(expr),
|
||||||
|
type_id: None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Token::MinusMinus => {
|
||||||
|
self.next()?;
|
||||||
|
expr = Expression::UnaryPostfix {
|
||||||
|
op: UnaryOperator::Decrement,
|
||||||
|
operand: Box::new(expr),
|
||||||
|
type_id: None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Array indexing: expr[index]
|
||||||
|
Token::LeftBracket => {
|
||||||
|
self.next()?; // consume '['
|
||||||
|
let index = Box::new(self.parse_expression()?);
|
||||||
|
|
||||||
|
let _ = expect_tt!(self.next()?, RightBracket)?;
|
||||||
|
|
||||||
|
expr = Expression::IndexAccess {
|
||||||
|
expr: Box::new(expr),
|
||||||
|
index,
|
||||||
|
type_id: None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function call: expr(args...)
|
||||||
|
Token::LeftParen => {
|
||||||
|
self.next()?; // consume '('
|
||||||
|
let mut args = Vec::new();
|
||||||
|
|
||||||
|
if !matches!(self.peek_next()?, Token::RightParen) {
|
||||||
|
loop {
|
||||||
|
args.push(self.parse_expression()?);
|
||||||
|
if !matches!(self.peek_next()?, Token::Comma) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
self.next()?; // consume comma
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = expect_tt!(self.next()?, RightParen)?;
|
||||||
|
|
||||||
|
if let Expression::Variable { name, .. } = expr {
|
||||||
|
expr = Expression::Call {
|
||||||
|
func: Call { name, args },
|
||||||
|
type_id: None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Member access: expr.member (if you support structs)
|
||||||
|
Token::Dot => {
|
||||||
|
self.next()?;
|
||||||
|
let field_name = expect_value!(self.next()?, Identifier)?;
|
||||||
|
expr = Expression::MemberAccess {
|
||||||
|
expr: Box::new(expr),
|
||||||
|
field_name,
|
||||||
|
type_id: None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// No more postfix operations
|
||||||
|
_ => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ParseResult::Accept(expr)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_primary(&mut self) -> ParseResult<Expression, CompilerError> {
|
||||||
|
match self.peek_next()? {
|
||||||
|
Token::UnsignedInt(value, type_id) => {
|
||||||
|
self.next()?;
|
||||||
|
ParseResult::Accept(Expression::Number(Number::Unsigned(value, type_id)))
|
||||||
|
}
|
||||||
|
Token::SignedInt(value, type_id) => {
|
||||||
|
self.next()?;
|
||||||
|
ParseResult::Accept(Expression::Number(Number::Signed(value, type_id)))
|
||||||
|
}
|
||||||
|
Token::String(value) => {
|
||||||
|
self.next()?;
|
||||||
|
ParseResult::Accept(Expression::StringLiteral(value))
|
||||||
|
}
|
||||||
|
Token::Char(value) => {
|
||||||
|
self.next()?;
|
||||||
|
ParseResult::Accept(Expression::CharLiteral(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
Token::Identifier(name) => {
|
||||||
|
self.next()?;
|
||||||
|
|
||||||
|
// if the next token isn't the beginning of a struct literal this is just
|
||||||
|
// an identifier.
|
||||||
|
// if !expect_tt!(self.peek_next()?, LeftBrace).accepted() {
|
||||||
|
// return ParseResult::Accept(Expression::Variable {
|
||||||
|
// name,
|
||||||
|
// expr_type: None,
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
ParseResult::Accept(Expression::Variable {
|
||||||
|
name,
|
||||||
|
expr_type: None,
|
||||||
|
})
|
||||||
|
|
||||||
|
// let _ = self.next()?;
|
||||||
|
|
||||||
|
// let mut fields = Vec::new();
|
||||||
|
// while !expect_tt!(self.peek_next()?, RightBrace).accepted() {
|
||||||
|
// let name = expect_value!(self.next()?, Identifier)?;
|
||||||
|
// let _ = expect_tt!(self.next()?, Colon)?;
|
||||||
|
// let expr = self.parse_expression()?;
|
||||||
|
|
||||||
|
// fields.push((name, expr));
|
||||||
|
|
||||||
|
// if expect_tt!(self.peek_next()?, Comma).accepted() {
|
||||||
|
// self.next()?;
|
||||||
|
// } else {
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// let _ = expect_tt!(self.next()?, RightBrace)?;
|
||||||
|
|
||||||
|
// ParseResult::Accept(Expression::StructLiteral {
|
||||||
|
// name,
|
||||||
|
// fields,
|
||||||
|
// type_id: None,
|
||||||
|
// })
|
||||||
|
}
|
||||||
|
Token::LeftBracket => {
|
||||||
|
self.next()?; // consume '['
|
||||||
|
let mut elements = Vec::new();
|
||||||
|
|
||||||
|
if !matches!(self.peek_next()?, Token::RightBracket) {
|
||||||
|
loop {
|
||||||
|
elements.push(self.parse_expression()?);
|
||||||
|
if !matches!(self.peek_next()?, Token::Comma) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
self.next()?; // consume comma
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expect_tt!(self.next()?, RightBracket)?;
|
||||||
|
ParseResult::Accept(Expression::ArrayLiteral {
|
||||||
|
elements,
|
||||||
|
type_id: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Token::LeftParen => {
|
||||||
|
self.next()?;
|
||||||
|
let expr = self.parse_expression()?;
|
||||||
|
let _ = expect_tt!(self.next()?, RightParen)?;
|
||||||
|
ParseResult::Accept(expr)
|
||||||
|
}
|
||||||
|
_ => ParseResult::Reject(CompilerError::UnexpectedToken(
|
||||||
|
self.peek_next()?.tt().to_string(),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_var_decl(&mut self) -> ParseResult<Variable, CompilerError> {
|
||||||
|
let name = expect_value!(self.next()?, Identifier)?;
|
||||||
|
|
||||||
|
let _ = expect_tt!(self.next()?, Colon)?;
|
||||||
|
|
||||||
|
let type_id = self.parse_type()?;
|
||||||
|
|
||||||
|
ParseResult::Accept(Variable {
|
||||||
|
name: name.name,
|
||||||
|
type_id,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_type(&mut self) -> ParseResult<TypeId, CompilerError> {
|
||||||
|
// parse primitive or named type
|
||||||
|
if expect_tt!(self.peek_next()?, Identifier).accepted() {
|
||||||
|
return self.parse_type_identifier();
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse array type
|
||||||
|
if expect_tt!(self.peek_next()?, LeftBracket).accepted() {
|
||||||
|
let _ = self.next()?;
|
||||||
|
|
||||||
|
let internal_type = self.parse_type()?;
|
||||||
|
let _ = expect_tt!(self.next()?, Semicolon)?;
|
||||||
|
|
||||||
|
let size = expect_value!(self.next()?, UnsignedInt)?;
|
||||||
|
|
||||||
|
let _ = expect_tt!(self.next()?, RightBracket)?;
|
||||||
|
|
||||||
|
return ParseResult::Accept(TypeId::Array {
|
||||||
|
r#type: Box::new(internal_type),
|
||||||
|
size: size as usize,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse tuple type
|
||||||
|
if expect_tt!(self.peek_next()?, LeftParen).accepted() {
|
||||||
|
let _ = self.next()?;
|
||||||
|
|
||||||
|
let mut types = Vec::new();
|
||||||
|
while !expect_tt!(self.peek_next()?, RightParen).accepted() {
|
||||||
|
types.push(self.parse_type()?);
|
||||||
|
if !expect_tt!(self.peek_next()?, Comma).accepted() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let _ = self.next()?;
|
||||||
|
}
|
||||||
|
let _ = expect_tt!(self.next()?, RightParen)?;
|
||||||
|
|
||||||
|
return ParseResult::Accept(TypeId::Tuple(types));
|
||||||
|
}
|
||||||
|
|
||||||
|
ParseResult::Reject(CompilerError::Generic(format!(
|
||||||
|
"Parsing type but no valid type was detected: {:?}",
|
||||||
|
self.peek_next()?
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_type_identifier(&mut self) -> ParseResult<TypeId, CompilerError> {
|
||||||
|
// get the type name incl namespace
|
||||||
|
let name = expect_value!(self.next()?, Identifier)?;
|
||||||
|
|
||||||
|
let type_id = match name.name.as_str() {
|
||||||
|
"u32" => TypeId::U32,
|
||||||
|
"u16" => TypeId::U16,
|
||||||
|
"u8" => TypeId::U8,
|
||||||
|
"i32" => TypeId::I32,
|
||||||
|
"i16" => TypeId::I16,
|
||||||
|
"i8" => TypeId::I8,
|
||||||
|
"void" => TypeId::Void,
|
||||||
|
"char" => TypeId::Char,
|
||||||
|
"str" => TypeId::Ptr(Box::new(TypeId::Char)),
|
||||||
|
_ => {
|
||||||
|
let mut generics = Vec::new();
|
||||||
|
if expect_tt!(self.peek_next()?, Less).accepted() {
|
||||||
|
let _ = self.next()?;
|
||||||
|
|
||||||
|
// loop until we find the closing '>'
|
||||||
|
while !expect_tt!(self.peek_next()?, Greater).accepted() {
|
||||||
|
generics.push(self.parse_type()?);
|
||||||
|
if !expect_tt!(self.peek_next()?, Comma).accepted() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let _ = self.next()?;
|
||||||
|
}
|
||||||
|
let _ = expect_tt!(self.next()?, Greater)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeId::UnknownCustom { name, generics }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ParseResult::Accept(type_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next(&mut self) -> ParseResult<Token, CompilerError> {
|
||||||
|
if self.idx >= self.tokens.len() {
|
||||||
|
ParseResult::Reject(CompilerError::UnexpectedEndOfInput)
|
||||||
|
} else {
|
||||||
|
let token = self.tokens[self.idx].clone();
|
||||||
|
self.idx += 1;
|
||||||
|
ParseResult::Accept(token)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn peek_next(&self) -> ParseResult<Token, CompilerError> {
|
||||||
|
if self.idx >= self.tokens.len() {
|
||||||
|
ParseResult::Reject(CompilerError::UnexpectedEndOfInput)
|
||||||
|
} else {
|
||||||
|
ParseResult::Accept(self.tokens[self.idx].clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn peek(&self, offset: usize) -> ParseResult<Token, CompilerError> {
|
||||||
|
if self.idx + offset >= self.tokens.len() {
|
||||||
|
ParseResult::Reject(CompilerError::UnexpectedEndOfInput)
|
||||||
|
} else {
|
||||||
|
ParseResult::Accept(self.tokens[self.idx + offset].clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, E> ParseResult<T, E> {
|
||||||
|
pub fn accepted(&self) -> bool {
|
||||||
|
matches!(self, ParseResult::Accept(_))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum ParseResultResidual<T> {
|
||||||
|
Deny,
|
||||||
|
Reject(T),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, E> Residual<T> for ParseResultResidual<E> {
|
||||||
|
type TryType = ParseResult<T, E>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, E> Try for ParseResult<T, E> {
|
||||||
|
type Output = T;
|
||||||
|
type Residual = ParseResultResidual<E>;
|
||||||
|
|
||||||
|
fn from_output(output: T) -> Self {
|
||||||
|
ParseResult::Accept(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
|
||||||
|
match self {
|
||||||
|
ParseResult::Accept(v) => ControlFlow::Continue(v),
|
||||||
|
ParseResult::Deny => ControlFlow::Break(ParseResultResidual::Deny),
|
||||||
|
ParseResult::Reject(e) => ControlFlow::Break(ParseResultResidual::Reject(e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, E> FromResidual for ParseResult<T, E> {
|
||||||
|
fn from_residual(residual: ParseResultResidual<E>) -> Self {
|
||||||
|
match residual {
|
||||||
|
ParseResultResidual::Deny => ParseResult::Deny,
|
||||||
|
ParseResultResidual::Reject(e) => ParseResult::Reject(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! expect_tt {
|
||||||
|
($token:expr, $($variant:ident),+) => {{
|
||||||
|
let token = $token.clone();
|
||||||
|
let tt = token.tt().to_string();
|
||||||
|
|
||||||
|
let mut vs = String::new();
|
||||||
|
$(
|
||||||
|
let s = stringify!($variant);
|
||||||
|
vs.push_str(s);
|
||||||
|
vs.push_str("|");
|
||||||
|
)+
|
||||||
|
|
||||||
|
match tt.as_str() {
|
||||||
|
$(
|
||||||
|
stringify!($variant) => ParseResult::Accept(token),
|
||||||
|
)+
|
||||||
|
_ => {
|
||||||
|
// let expected = format!("[{}]", vec![$(stringify!($variant)),+].join(" | "));
|
||||||
|
ParseResult::Reject(CompilerError::UnexpectedToken(tt))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! expect_value {
|
||||||
|
($expr:expr, $variant:ident) => {{
|
||||||
|
let tok = $expr;
|
||||||
|
match tok.clone() {
|
||||||
|
Token::$variant(first, ..) => ParseResult::Accept(first),
|
||||||
|
_ => ParseResult::Reject(CompilerError::UnexpectedToken(tok.tt().to_string())),
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
@@ -0,0 +1,217 @@
|
|||||||
|
// use std::collections::HashMap;
|
||||||
|
//
|
||||||
|
// use crate::model::{ // You'll need to add this to your imports
|
||||||
|
// CompilerError,
|
||||||
|
// Declaration,
|
||||||
|
// Dependency,
|
||||||
|
// Expression,
|
||||||
|
// Program,
|
||||||
|
// TypeId,
|
||||||
|
// UnaryOperator,
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// pub struct Analyser {
|
||||||
|
// symbol_table: HashMap<String, Declaration>,
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// const NUMERIC_TYPES: &[TypeId] = &[
|
||||||
|
// TypeId::U32,
|
||||||
|
// TypeId::I32,
|
||||||
|
// TypeId::I16,
|
||||||
|
// TypeId::U16,
|
||||||
|
// TypeId::I8,
|
||||||
|
// TypeId::U8,
|
||||||
|
// ];
|
||||||
|
//
|
||||||
|
// impl Analyser {
|
||||||
|
// pub fn new() -> Self {
|
||||||
|
// Self {
|
||||||
|
// symbol_table: HashMap::new(),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// pub fn analyse(&mut self, ast: Program) -> Result<(), CompilerError> {
|
||||||
|
// // build table of global symbols.
|
||||||
|
// for dec in ast.declarations {
|
||||||
|
// let name = match dec.clone() {
|
||||||
|
// Declaration::Function { name, .. } => name,
|
||||||
|
// Declaration::Variable { var, .. } => var.name,
|
||||||
|
// Declaration::Dependency(Dependency { name, .. }) => name,
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// self.symbol_table.insert(name, dec);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Ok(())
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// fn match_type(actual: TypeId, expected: Option<TypeId>) -> Result<TypeId, CompilerError> {
|
||||||
|
// match expected {
|
||||||
|
// Some(id) => {
|
||||||
|
// if id != actual {
|
||||||
|
// Err(CompilerError::TypeMismatch(id, actual))
|
||||||
|
// } else {
|
||||||
|
// Ok(actual)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// None => Ok(actual),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// fn get_type(
|
||||||
|
// &mut self, // Changed from &self to &mut self since we modify expr
|
||||||
|
// expr: &mut Expression,
|
||||||
|
// expected_type: Option<TypeId>,
|
||||||
|
// ) -> Result<TypeId, CompilerError> {
|
||||||
|
// match expr {
|
||||||
|
// // Correct IFF we're expecting a void type
|
||||||
|
// Expression::Empty => Self::match_type(TypeId::Void, expected_type),
|
||||||
|
//
|
||||||
|
// // Correct IFF we're expecting a char type
|
||||||
|
// Expression::CharLiteral(_) => Self::match_type(TypeId::Char, expected_type),
|
||||||
|
//
|
||||||
|
// // Correct IFF we're expecting a string slice type
|
||||||
|
// Expression::StringLiteral(_) => {
|
||||||
|
// Self::match_type(TypeId::Ptr(Box::new(TypeId::Char)), expected_type)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Expression::Variable { name, expr_type } => {
|
||||||
|
// let actual = expr_type.clone().ok_or(CompilerError::UnknownType)?;
|
||||||
|
// Self::match_type(actual, expected_type)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Expression::Number { value, type_id } => {
|
||||||
|
// // If we already know the TypeId
|
||||||
|
// if let Some(id) = type_id {
|
||||||
|
// return Self::match_type(id.clone(), expected_type);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // If we're expecting a type id, check it's numeric.
|
||||||
|
// // TODO: add checks to make sure it's valid for its size eg u8 cant be
|
||||||
|
// // more than 255
|
||||||
|
// if let Some(expected) = expected_type {
|
||||||
|
// if NUMERIC_TYPES.contains(&expected) {
|
||||||
|
// *type_id = Some(expected.clone());
|
||||||
|
// return Ok(expected);
|
||||||
|
// } else {
|
||||||
|
// return Err(CompilerError::TypeMismatch(expected, TypeId::U32));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Default to i32 if no type information is available
|
||||||
|
// *type_id = Some(TypeId::I32);
|
||||||
|
// Ok(TypeId::I32)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Expression::Binary {
|
||||||
|
// op,
|
||||||
|
// left,
|
||||||
|
// right,
|
||||||
|
// type_id,
|
||||||
|
// } => {
|
||||||
|
// // For binary operations, both operands should have compatible types
|
||||||
|
// // and the result type depends on the operation
|
||||||
|
// let left_type = self.get_type(left, None)?;
|
||||||
|
// let right_type = self.get_type(right, Some(left_type.clone()))?;
|
||||||
|
//
|
||||||
|
// // For numeric operations, result has the same type as operands
|
||||||
|
// if NUMERIC_TYPES.contains(&left_type) && NUMERIC_TYPES.contains(&right_type) {
|
||||||
|
// *type_id = Some(left_type);
|
||||||
|
// Self::match_type(left_type, expected_type)
|
||||||
|
// } else {
|
||||||
|
// Err(CompilerError::TypeMismatch(left_type, right_type))
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Expression::Unary {
|
||||||
|
// op,
|
||||||
|
// operand,
|
||||||
|
// type_id,
|
||||||
|
// } => {
|
||||||
|
// match op {
|
||||||
|
// UnaryOperator::Plus | UnaryOperator::Minus => {
|
||||||
|
// // Unary +/- require numeric operands
|
||||||
|
// let inner_type = self.get_type(operand, None)?;
|
||||||
|
//
|
||||||
|
// if NUMERIC_TYPES.contains(&inner_type) {
|
||||||
|
// *type_id = Some(inner_type.clone());
|
||||||
|
// Self::match_type(inner_type, expected_type)
|
||||||
|
// } else {
|
||||||
|
// Err(CompilerError::TypeMismatch(inner_type, TypeId::I32))
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// UnaryOperator::Dereference => {
|
||||||
|
// // For dereference (*ptr), the operand must be a pointer
|
||||||
|
// // and the result type is what the pointer points to
|
||||||
|
// let inner_type = self.get_type(operand, None)?;
|
||||||
|
//
|
||||||
|
// match inner_type {
|
||||||
|
// TypeId::Ptr(inner) => {
|
||||||
|
// let deref_type = *inner;
|
||||||
|
// *type_id = Some(deref_type.clone());
|
||||||
|
// Self::match_type(deref_type, expected_type)
|
||||||
|
// }
|
||||||
|
// _ => Err(CompilerError::Generic(format!(
|
||||||
|
// "Cannot dereference non-pointer type: {:?}",
|
||||||
|
// inner_type
|
||||||
|
// ))),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// UnaryOperator::AddressOf => {
|
||||||
|
// // For reference (&var), we need to determine what we're taking
|
||||||
|
// // a reference to, then wrap it in a Ptr
|
||||||
|
// // If expected_type is Ptr(T), then operand should have type T
|
||||||
|
// let expected_inner = match expected_type.clone() {
|
||||||
|
// Some(TypeId::Ptr(inner)) => Some(*inner),
|
||||||
|
// _ => None,
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// let inner_type = self.get_type(operand, expected_inner)?;
|
||||||
|
// let ref_type = TypeId::Ptr(Box::new(inner_type));
|
||||||
|
// *type_id = Some(ref_type.clone());
|
||||||
|
// Self::match_type(ref_type, expected_type)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Expression::Call {
|
||||||
|
// name,
|
||||||
|
// args,
|
||||||
|
// type_id,
|
||||||
|
// } => match self.symbol_table.get(&name.name) {
|
||||||
|
// Some(Declaration::Function {
|
||||||
|
// params,
|
||||||
|
// return_type,
|
||||||
|
// ..
|
||||||
|
// }) => {
|
||||||
|
// // check that we've given the right number of arguments.
|
||||||
|
// if args.len() != params.len() {
|
||||||
|
// return Err(CompilerError::Generic(format!(
|
||||||
|
// "Function {} expected {} arguments but received {}",
|
||||||
|
// name.name,
|
||||||
|
// params.len(),
|
||||||
|
// args.len()
|
||||||
|
// )));
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// for (arg, param) in args.iter_mut().zip(params.iter()) {
|
||||||
|
// // check that the argument type matches the parameter type.
|
||||||
|
// let provided_type = self.get_type(arg, Some(param.type_id))?;
|
||||||
|
// if provided_type != param.type_id {
|
||||||
|
// return Err(CompilerError::TypeMismatch(param.type_id, provided_type));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// *type_id = Some(return_type.clone());
|
||||||
|
// Self::match_type(return_type.clone(), expected_type)
|
||||||
|
// }
|
||||||
|
// _ => Err(CompilerError::Generic(format!(
|
||||||
|
// "Function {} not found in symbol table",
|
||||||
|
// name.name
|
||||||
|
// ))),
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
#![feature(try_trait_v2)]
|
||||||
|
#![feature(try_trait_v2_residual)]
|
||||||
|
|
||||||
|
use crate::model::CompilerError;
|
||||||
|
use dsa_tasks::{ProcessAgent, TraceableProcess};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
mod backend;
|
||||||
|
mod frontend;
|
||||||
|
mod model;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DSCSCompiler {
|
||||||
|
name: String,
|
||||||
|
is_lib: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DSCSCompiler {
|
||||||
|
pub fn new(name: impl Into<String>, is_lib: bool) -> Self {
|
||||||
|
Self {
|
||||||
|
name: name.into(),
|
||||||
|
is_lib,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TraceableProcess for DSCSCompiler {
|
||||||
|
type Input = PathBuf;
|
||||||
|
type Output = String;
|
||||||
|
type Error = CompilerError;
|
||||||
|
|
||||||
|
fn label(&self) -> String {
|
||||||
|
format!("Compiling {}", self.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
self: Box<Self>,
|
||||||
|
input: impl Into<PathBuf>,
|
||||||
|
ctx: ProcessAgent,
|
||||||
|
) -> Result<String, CompilerError> {
|
||||||
|
let input = std::fs::read_to_string(&input.into()).expect("Failed to read input file");
|
||||||
|
|
||||||
|
// Parse the input using the frontend, providing the file extension and data.
|
||||||
|
let ast = match frontend::generate_ast(&input, &ctx) {
|
||||||
|
Ok(ast) => ast,
|
||||||
|
Err(err) => return Err(error(format!("Compilation failed: {err:?}")).into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("Parsed AST: {:#?}", ast);
|
||||||
|
|
||||||
|
// Generate the output using the backend with the parsed result.
|
||||||
|
let result = match backend::generate_code(&ast, &ctx) {
|
||||||
|
Ok(result) => result,
|
||||||
|
Err(err) => return Err(error(format!("Compilation failed: {err:?}")).into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn error(msg: impl Into<String>) -> CompilerError {
|
||||||
|
CompilerError::Generic(msg.into())
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
use clap::Parser;
|
||||||
|
use dsa_tasks::TraceableProcess;
|
||||||
|
use dscs_compiler::DSCSCompiler;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
#[derive(Parser, Debug, Clone)]
|
||||||
|
#[command(version, about, long_about = None)]
|
||||||
|
struct Args {
|
||||||
|
#[arg(short = 'i')]
|
||||||
|
pub input_path: PathBuf,
|
||||||
|
#[arg(short = 'o')]
|
||||||
|
pub output_path: PathBuf,
|
||||||
|
#[arg(long)]
|
||||||
|
pub lib: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let args = Args::parse();
|
||||||
|
|
||||||
|
let name = args.input_path.file_name().unwrap().to_str().unwrap();
|
||||||
|
|
||||||
|
let builder = DSCSCompiler::new(name, args.lib);
|
||||||
|
let result = builder.run_sync(&args.input_path).unwrap();
|
||||||
|
|
||||||
|
std::fs::write(&args.output_path, &result).expect("Failed to write output");
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"Compilation Successful ✅ \n\tSource: {}\n\tOutput: {}\n",
|
||||||
|
args.input_path.to_string_lossy(),
|
||||||
|
args.output_path.to_string_lossy()
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,495 @@
|
|||||||
|
use core::fmt;
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum CompilerError {
|
||||||
|
UnexpectedToken(String),
|
||||||
|
UnexpectedEndOfInput,
|
||||||
|
UnexpectedCharacter(char),
|
||||||
|
Undefined(Name),
|
||||||
|
InvalidSyntax(String),
|
||||||
|
Generic(String),
|
||||||
|
UnknownType,
|
||||||
|
TypeMismatch(TypeId, TypeId),
|
||||||
|
Unimplemented(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for CompilerError {}
|
||||||
|
|
||||||
|
impl fmt::Display for CompilerError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{:?}", self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
|
pub struct Name {
|
||||||
|
pub name: String,
|
||||||
|
pub namespace: Option<String>,
|
||||||
|
}
|
||||||
|
impl Name {
|
||||||
|
pub fn new(name: impl Into<String>, namespace: Option<String>) -> Self {
|
||||||
|
Self {
|
||||||
|
name: name.into(),
|
||||||
|
namespace,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Program {
|
||||||
|
pub declarations: Vec<Declaration>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Declaration {
|
||||||
|
Function {
|
||||||
|
name: String,
|
||||||
|
return_type: TypeId,
|
||||||
|
params: Vec<Variable>,
|
||||||
|
body: Block,
|
||||||
|
},
|
||||||
|
Variable {
|
||||||
|
var: Variable,
|
||||||
|
init: Option<ConstExpr>,
|
||||||
|
is_const: bool,
|
||||||
|
},
|
||||||
|
Dependency(Dependency),
|
||||||
|
Struct {
|
||||||
|
name: Name,
|
||||||
|
fields: Vec<Variable>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Dependency {
|
||||||
|
pub name: String,
|
||||||
|
pub path: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum TypeId {
|
||||||
|
U8,
|
||||||
|
U16,
|
||||||
|
U32,
|
||||||
|
I8,
|
||||||
|
I16,
|
||||||
|
I32,
|
||||||
|
Bool,
|
||||||
|
Char,
|
||||||
|
Void,
|
||||||
|
Ptr(Box<TypeId>),
|
||||||
|
Ref(Box<TypeId>),
|
||||||
|
Tuple(Vec<TypeId>),
|
||||||
|
Array {
|
||||||
|
r#type: Box<TypeId>,
|
||||||
|
size: usize,
|
||||||
|
},
|
||||||
|
UnknownCustom {
|
||||||
|
name: Name,
|
||||||
|
generics: Vec<TypeId>,
|
||||||
|
},
|
||||||
|
Struct {
|
||||||
|
name: Name,
|
||||||
|
fields: Vec<TypeId>,
|
||||||
|
generics: Vec<TypeId>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TypeId {
|
||||||
|
pub fn size(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
Self::U8 => 1,
|
||||||
|
Self::U16 => 2,
|
||||||
|
Self::U32 => 4,
|
||||||
|
Self::I8 => 1,
|
||||||
|
Self::I16 => 2,
|
||||||
|
Self::I32 => 4,
|
||||||
|
Self::Bool => 1,
|
||||||
|
Self::Char => 1,
|
||||||
|
Self::Void => 0,
|
||||||
|
Self::Ptr(t) => t.size(),
|
||||||
|
Self::Ref(t) => t.size(),
|
||||||
|
Self::Tuple(types) => types.iter().map(|t| t.size()).sum(),
|
||||||
|
Self::Array { r#type, size } => r#type.size() * size,
|
||||||
|
Self::UnknownCustom { .. } => 1, /* TODO: calculate type size during */
|
||||||
|
// semantic analysis
|
||||||
|
Self::Struct { fields, .. } => fields.iter().map(|t| t.size()).sum(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for TypeId {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::U8 => write!(f, "u8"),
|
||||||
|
Self::U16 => write!(f, "u16"),
|
||||||
|
Self::U32 => write!(f, "u32"),
|
||||||
|
Self::I8 => write!(f, "i8"),
|
||||||
|
Self::I16 => write!(f, "i16"),
|
||||||
|
Self::I32 => write!(f, "i32"),
|
||||||
|
Self::Bool => write!(f, "bool"),
|
||||||
|
Self::Char => write!(f, "char"),
|
||||||
|
Self::Void => write!(f, "void"),
|
||||||
|
Self::Ptr(t) => write!(f, "*{}", t),
|
||||||
|
Self::Ref(t) => write!(f, "&{}", t),
|
||||||
|
Self::Tuple(elems) => write!(
|
||||||
|
f,
|
||||||
|
"({})",
|
||||||
|
elems
|
||||||
|
.iter()
|
||||||
|
.map(|t| t.to_string())
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join(", ")
|
||||||
|
),
|
||||||
|
Self::Array { r#type, size } => write!(f, "[{}; {}]", r#type, size),
|
||||||
|
Self::UnknownCustom { name, generics } => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{}<{}>",
|
||||||
|
name,
|
||||||
|
generics
|
||||||
|
.iter()
|
||||||
|
.map(|t| t.to_string())
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join(", ")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Self::Struct {
|
||||||
|
name,
|
||||||
|
fields,
|
||||||
|
generics,
|
||||||
|
} => write!(
|
||||||
|
f,
|
||||||
|
"struct<{}> {} {{{}}}",
|
||||||
|
generics
|
||||||
|
.iter()
|
||||||
|
.map(|t| t.to_string())
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join(", "),
|
||||||
|
name,
|
||||||
|
fields
|
||||||
|
.iter()
|
||||||
|
.map(|t| t.to_string())
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join(", ")
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Block = Vec<Statement>;
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub struct Variable {
|
||||||
|
pub name: String,
|
||||||
|
pub type_id: TypeId,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Statement {
|
||||||
|
Block(Block),
|
||||||
|
Declaration {
|
||||||
|
var: Variable,
|
||||||
|
value: Option<Expression>,
|
||||||
|
},
|
||||||
|
Assign {
|
||||||
|
varname: String,
|
||||||
|
operator: AssignmentOperator,
|
||||||
|
value: Expression,
|
||||||
|
},
|
||||||
|
PtrWrite {
|
||||||
|
ptr: Expression,
|
||||||
|
value: Expression,
|
||||||
|
},
|
||||||
|
Expression {
|
||||||
|
expr: Expression,
|
||||||
|
},
|
||||||
|
If {
|
||||||
|
condition: Expression,
|
||||||
|
then_stmt: Block,
|
||||||
|
else_stmt: Block,
|
||||||
|
},
|
||||||
|
While {
|
||||||
|
condition: Expression,
|
||||||
|
body: Vec<Statement>,
|
||||||
|
},
|
||||||
|
Loop(Block),
|
||||||
|
Defer(Call),
|
||||||
|
Break,
|
||||||
|
Continue,
|
||||||
|
Return(Option<Expression>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum ConstExpr {
|
||||||
|
Number(i32),
|
||||||
|
String(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for ConstExpr {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
ConstExpr::Number(n) => write!(f, "{}", n),
|
||||||
|
ConstExpr::String(s) => write!(f, "\"{}\"", s),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Expression {
|
||||||
|
Empty,
|
||||||
|
Binary {
|
||||||
|
op: BinaryOperator,
|
||||||
|
left: Box<Expression>,
|
||||||
|
right: Box<Expression>,
|
||||||
|
|
||||||
|
// Post-Semantic Analysis
|
||||||
|
type_id: Option<TypeId>,
|
||||||
|
},
|
||||||
|
Unary {
|
||||||
|
op: UnaryOperator,
|
||||||
|
operand: Box<Expression>,
|
||||||
|
|
||||||
|
// Post-Semantic Analysis
|
||||||
|
type_id: Option<TypeId>,
|
||||||
|
},
|
||||||
|
UnaryPostfix {
|
||||||
|
op: UnaryOperator,
|
||||||
|
operand: Box<Expression>,
|
||||||
|
|
||||||
|
// Post-Semantic Analysis
|
||||||
|
type_id: Option<TypeId>,
|
||||||
|
},
|
||||||
|
Variable {
|
||||||
|
name: Name,
|
||||||
|
expr_type: Option<TypeId>,
|
||||||
|
},
|
||||||
|
TypeCast {
|
||||||
|
expr: Box<Expression>,
|
||||||
|
target_type: TypeId,
|
||||||
|
|
||||||
|
// Post-Semantic Analysis
|
||||||
|
type_id: Option<TypeId>,
|
||||||
|
},
|
||||||
|
IndexAccess {
|
||||||
|
expr: Box<Expression>,
|
||||||
|
index: Box<Expression>,
|
||||||
|
|
||||||
|
// Post-Semantic Analysis
|
||||||
|
type_id: Option<TypeId>,
|
||||||
|
},
|
||||||
|
MemberAccess {
|
||||||
|
expr: Box<Expression>,
|
||||||
|
field_name: Name,
|
||||||
|
|
||||||
|
// Post-Semantic Analysis
|
||||||
|
type_id: Option<TypeId>,
|
||||||
|
},
|
||||||
|
Call {
|
||||||
|
func: Call,
|
||||||
|
|
||||||
|
// Post-Semantic Analysis
|
||||||
|
type_id: Option<TypeId>,
|
||||||
|
},
|
||||||
|
Number(Number),
|
||||||
|
StringLiteral(String),
|
||||||
|
CharLiteral(char),
|
||||||
|
ArrayLiteral {
|
||||||
|
elements: Vec<Expression>,
|
||||||
|
type_id: Option<TypeId>,
|
||||||
|
},
|
||||||
|
StructLiteral {
|
||||||
|
name: Name,
|
||||||
|
fields: Vec<(Name, Expression)>,
|
||||||
|
type_id: Option<TypeId>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Number {
|
||||||
|
Signed(i32, Option<TypeId>),
|
||||||
|
Unsigned(u32, Option<TypeId>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Call {
|
||||||
|
pub name: Name,
|
||||||
|
pub args: Vec<Expression>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Expression {
|
||||||
|
pub fn is_pure(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Expression::Number { .. } => true,
|
||||||
|
Expression::StringLiteral(_) => true,
|
||||||
|
Expression::CharLiteral(_) => true,
|
||||||
|
Expression::Call { .. } => false,
|
||||||
|
Expression::Binary { left, right, .. } => left.is_pure() && right.is_pure(),
|
||||||
|
Expression::Unary { operand, .. } => operand.is_pure(),
|
||||||
|
Expression::UnaryPostfix { operand, .. } => operand.is_pure(),
|
||||||
|
Expression::Empty => true,
|
||||||
|
Expression::Variable { .. } => true,
|
||||||
|
Expression::TypeCast { expr, .. } => expr.is_pure(),
|
||||||
|
Expression::IndexAccess { expr, index, .. } => expr.is_pure() && index.is_pure(),
|
||||||
|
Expression::MemberAccess { expr, .. } => expr.is_pure(),
|
||||||
|
Expression::ArrayLiteral { elements, .. } => {
|
||||||
|
elements.iter().all(|element| element.is_pure())
|
||||||
|
}
|
||||||
|
Expression::StructLiteral { fields, .. } => {
|
||||||
|
fields.iter().all(|(_, expr)| expr.is_pure())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn type_id(&self) -> Result<TypeId, CompilerError> {
|
||||||
|
match self {
|
||||||
|
Expression::Number(Number::Signed(_, type_id) | Number::Unsigned(_, type_id)) => {
|
||||||
|
type_id.clone().ok_or(CompilerError::UnknownType)
|
||||||
|
}
|
||||||
|
Expression::StringLiteral(_) => Ok(TypeId::Ptr(Box::new(TypeId::Char))),
|
||||||
|
Expression::CharLiteral(_) => Ok(TypeId::Char),
|
||||||
|
Expression::Call { type_id, .. } => type_id.clone().ok_or(CompilerError::UnknownType),
|
||||||
|
Expression::Binary { type_id, .. } => type_id.clone().ok_or(CompilerError::UnknownType),
|
||||||
|
Expression::Unary { type_id, .. } => type_id.clone().ok_or(CompilerError::UnknownType),
|
||||||
|
Expression::UnaryPostfix { type_id, .. } => {
|
||||||
|
type_id.clone().ok_or(CompilerError::UnknownType)
|
||||||
|
}
|
||||||
|
Expression::Empty => Ok(TypeId::Void),
|
||||||
|
Expression::Variable { expr_type, .. } => {
|
||||||
|
expr_type.clone().ok_or(CompilerError::UnknownType)
|
||||||
|
}
|
||||||
|
Expression::TypeCast { type_id, .. } => {
|
||||||
|
type_id.clone().ok_or(CompilerError::UnknownType)
|
||||||
|
}
|
||||||
|
Expression::IndexAccess { expr, .. } => expr.type_id(),
|
||||||
|
Expression::MemberAccess { expr, .. } => expr.type_id(),
|
||||||
|
Expression::ArrayLiteral { elements, .. } => {
|
||||||
|
let element_type = elements
|
||||||
|
.first()
|
||||||
|
.map_or(TypeId::Void, |e| e.type_id().unwrap_or(TypeId::Void));
|
||||||
|
Ok(TypeId::Array {
|
||||||
|
r#type: Box::new(element_type),
|
||||||
|
size: elements.len(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Expression::StructLiteral { name, fields, .. } => {
|
||||||
|
let fields = fields
|
||||||
|
.iter()
|
||||||
|
.map(|(_, expr)| expr.type_id())
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
Ok(TypeId::Struct {
|
||||||
|
name: name.clone(),
|
||||||
|
fields,
|
||||||
|
generics: Vec::new(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum AssignmentOperator {
|
||||||
|
Assign,
|
||||||
|
AddAssign,
|
||||||
|
SubAssign,
|
||||||
|
MulAssign,
|
||||||
|
DivAssign,
|
||||||
|
ModAssign,
|
||||||
|
AndAssign,
|
||||||
|
OrAssign,
|
||||||
|
XorAssign,
|
||||||
|
LeftShiftAssign,
|
||||||
|
RightShiftAssign,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum BinaryOperator {
|
||||||
|
// arithmetic
|
||||||
|
Add,
|
||||||
|
Sub,
|
||||||
|
Mul,
|
||||||
|
Div,
|
||||||
|
Mod,
|
||||||
|
|
||||||
|
// comparison
|
||||||
|
Equal,
|
||||||
|
NotEqual,
|
||||||
|
LessThan,
|
||||||
|
GreaterThan,
|
||||||
|
LessOrEqual,
|
||||||
|
GreaterOrEqual,
|
||||||
|
|
||||||
|
// bitwise
|
||||||
|
BitwiseAnd,
|
||||||
|
BitwiseOr,
|
||||||
|
BitwiseXor,
|
||||||
|
|
||||||
|
// logical
|
||||||
|
LogicalAnd,
|
||||||
|
LogicalOr,
|
||||||
|
|
||||||
|
// shift
|
||||||
|
LeftShift,
|
||||||
|
RightShift,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for BinaryOperator {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Add => write!(f, "+"),
|
||||||
|
Self::Sub => write!(f, "-"),
|
||||||
|
Self::Mul => write!(f, "*"),
|
||||||
|
Self::Div => write!(f, "/"),
|
||||||
|
Self::Mod => write!(f, "%"),
|
||||||
|
Self::Equal => write!(f, "=="),
|
||||||
|
Self::NotEqual => write!(f, "!="),
|
||||||
|
Self::LessThan => write!(f, "<"),
|
||||||
|
Self::GreaterThan => write!(f, ">"),
|
||||||
|
Self::LessOrEqual => write!(f, "<="),
|
||||||
|
Self::GreaterOrEqual => write!(f, ">="),
|
||||||
|
Self::BitwiseAnd => write!(f, "&"),
|
||||||
|
Self::BitwiseOr => write!(f, "|"),
|
||||||
|
Self::BitwiseXor => write!(f, "^"),
|
||||||
|
Self::LogicalAnd => write!(f, "&&"),
|
||||||
|
Self::LogicalOr => write!(f, "||"),
|
||||||
|
Self::LeftShift => write!(f, "<<"),
|
||||||
|
Self::RightShift => write!(f, ">>"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum UnaryOperator {
|
||||||
|
Plus,
|
||||||
|
Minus,
|
||||||
|
AddressOf,
|
||||||
|
Dereference,
|
||||||
|
BitwiseNot,
|
||||||
|
LogicalNot,
|
||||||
|
Increment,
|
||||||
|
Decrement,
|
||||||
|
SizeOf,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for UnaryOperator {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Increment => write!(f, "++"),
|
||||||
|
Self::Decrement => write!(f, "--"),
|
||||||
|
Self::Plus => write!(f, "+"),
|
||||||
|
Self::Minus => write!(f, "-"),
|
||||||
|
Self::Dereference => write!(f, "*"),
|
||||||
|
Self::AddressOf => write!(f, "&"),
|
||||||
|
Self::BitwiseNot => write!(f, "~"),
|
||||||
|
Self::LogicalNot => write!(f, "!"),
|
||||||
|
Self::SizeOf => write!(f, "sizeof"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user