Compare commits
6 Commits
316e60eedb
...
73265f9d1e
| Author | SHA1 | Date | |
|---|---|---|---|
| 73265f9d1e | |||
| 12f283e5f8 | |||
| 88915bbc8a | |||
| 930d7b56cc | |||
| d1b65dc1ed | |||
| b79cd46b4b |
@@ -16,13 +16,13 @@ func main[] : i64
|
||||
else if op == '<'
|
||||
p = p - 1
|
||||
else if op == '+'
|
||||
str.set(memory, p, memory[p] + 1)
|
||||
memory[p] = memory[p] + 1
|
||||
else if op == '-'
|
||||
str.set(memory, p, memory[p] - 1)
|
||||
memory[p] = memory[p] - 1
|
||||
else if op == '.'
|
||||
io.print_char(memory[p])
|
||||
else if op == ','
|
||||
str.set(memory, p, io.read_char())
|
||||
memory[p] = io.read_char()
|
||||
else if op == '['
|
||||
if !memory[p]
|
||||
i = i + 1
|
||||
|
||||
328
examples/chip8.zr
Normal file
328
examples/chip8.zr
Normal file
@@ -0,0 +1,328 @@
|
||||
// needs to be compiled with -m -C="-lraylib"
|
||||
extern InitWindow
|
||||
extern SetTargetFPS
|
||||
extern WindowShouldClose
|
||||
extern BeginDrawing
|
||||
extern ClearBackground
|
||||
extern DrawRectangle
|
||||
extern EndDrawing
|
||||
extern IsKeyDown
|
||||
extern printf // TODO: replace with std
|
||||
extern rand // TODO: replace with std
|
||||
|
||||
struct CHIP8
|
||||
memory: ptr
|
||||
pc: i64
|
||||
stack: array
|
||||
sp: i64
|
||||
reg: ptr
|
||||
I: i64
|
||||
delay_timer: i64
|
||||
sound_timer: i64
|
||||
keyboard: array
|
||||
display: ptr
|
||||
keyboard_map: array
|
||||
|
||||
func chip8_create[] : CHIP8
|
||||
let c: CHIP8 = new CHIP8
|
||||
c->memory = mem.alloc(4096)
|
||||
mem.zero(c->memory, 4096)
|
||||
c->display = mem.alloc(64*32)
|
||||
mem.zero(c->display, 64*32)
|
||||
c->reg = mem.alloc(16)
|
||||
mem.zero(c->reg, 16)
|
||||
|
||||
c->stack = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||
c->keyboard = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||
|
||||
let fonts: array = [0xf0, 0x90, 0x90, 0x90, 0xf0, 0x20, 0x60, 0x20, 0x20, 0x70, 0xf0, 0x10, 0xf0, 0x80, 0xf0, 0xf0, 0x10, 0xf0, 0x10, 0xf0, 0x90, 0x90, 0xf0, 0x10, 0x10, 0xf0, 0x80, 0xf0, 0x10, 0xf0, 0xf0, 0x80, 0xf0, 0x90, 0xf0, 0xf0, 0x10, 0x20, 0x40, 0x40, 0xf0, 0x90, 0xf0, 0x90, 0xf0, 0xf0, 0x90, 0xf0, 0x10, 0xf0, 0xf0, 0x90, 0xf0, 0x90, 0x90, 0xe0, 0x90, 0xe0, 0x90, 0xe0, 0xf0, 0x80, 0x80, 0x80, 0xf0, 0xe0, 0x90, 0x90, 0x90, 0xe0, 0xf0, 0x80, 0xf0, 0x80, 0xf0, 0xf0, 0x80, 0xf0, 0x80, 0x80]
|
||||
for i in 0..80
|
||||
c->memory[i] = array.nth(fonts, i)
|
||||
mem.free(fonts)
|
||||
|
||||
c->pc = 0x200
|
||||
|
||||
// see KeyboardKey in raylib.h
|
||||
c->keyboard_map = [49, 50, 51, 52, 53, 265, 32, 263, 264, 262, 87, 69, 82, 84, 89, 85]
|
||||
|
||||
return c
|
||||
|
||||
func chip8_disassemble[c: CHIP8, ins_count: i64] : void
|
||||
for i in 0..ins_count
|
||||
printf("0x%x: ", c->pc)
|
||||
|
||||
let high: i64 = c->memory[c->pc]
|
||||
let low: i64 = c->memory[c->pc + 1]
|
||||
c->pc = c->pc + 2
|
||||
|
||||
let ins: i64 = (high << 8) | low
|
||||
let nnn: i64 = ins & 0x0fff
|
||||
let kk: i64 = ins & 0x00ff
|
||||
let n: i64 = ins & 0x000f
|
||||
let x: i64 = (ins >> 8) & 0x0f
|
||||
let y: i64 = (ins >> 4) & 0x0f
|
||||
|
||||
let op: i64 = (ins >> 12) & 0x0f
|
||||
if op == 0x0
|
||||
if nnn == 0x0e0
|
||||
io.println("CLS")
|
||||
else if nnn == 0x0ee
|
||||
io.println("RET")
|
||||
else
|
||||
io.print("SYS ")
|
||||
io.println_i64(nnn)
|
||||
else if op == 0x1
|
||||
printf("JP 0x%x\n", nnn)
|
||||
else if op == 0x2
|
||||
io.print("CALL ")
|
||||
io.println_i64(nnn)
|
||||
else if op == 0x3
|
||||
printf("SE V%x, %u\n", x, kk)
|
||||
else if op == 0x4
|
||||
printf("SNE V%x, %u\n", x, kk)
|
||||
else if op == 0x5
|
||||
printf("SE V%x, V%x\n", x, y)
|
||||
else if op == 0x6
|
||||
printf("LD V%x, %u\n", x, kk)
|
||||
else if op == 0x7
|
||||
printf("ADD V%x, %u\n", x, kk)
|
||||
else if op == 0x8
|
||||
if n == 0x0
|
||||
printf("LD V%x, V%x\n", x, y)
|
||||
else if n == 0x1
|
||||
printf("OR V%x, V%x\n", x, y)
|
||||
else if n == 0x2
|
||||
printf("AND V%x, V%x\n", x, y)
|
||||
else if n == 0x3
|
||||
printf("XOR V%x, V%x\n", x, y)
|
||||
else if n == 0x4
|
||||
printf("ADD V%x, V%x\n", x, y)
|
||||
else if n == 0x5
|
||||
printf("SUB V%x, V%x\n", x, y)
|
||||
else if n == 0x6
|
||||
printf("SHR V%x\n", x)
|
||||
else if n == 0x7
|
||||
printf("SUBN V%x, V%x\n", x, y)
|
||||
else if n == 0xE
|
||||
printf("SHL V%x\n", x)
|
||||
else
|
||||
printf("??? (%x)\n", ins)
|
||||
else if op == 0x9
|
||||
printf("SNE V%x, V%x\n", x, y)
|
||||
else if op == 0xa
|
||||
printf("LD I, 0x%x\n", nnn)
|
||||
else if op == 0xb
|
||||
printf("JP V0, %u\n", nnn)
|
||||
else if op == 0xc
|
||||
printf("RND V%x, %u\n", x, kk)
|
||||
else if op == 0xd
|
||||
printf("DRW V%x, V%x, %u\n", x, y, n)
|
||||
else if op == 0xe
|
||||
if kk == 0x9e
|
||||
printf("SKP V%x\n", x)
|
||||
else if kk == 0xa1
|
||||
printf("SKNP V%x\n", x)
|
||||
else
|
||||
printf("??? (%x)\n", ins)
|
||||
else if op == 0xf
|
||||
if kk == 0x07
|
||||
printf("LD V%x, DT\n", x)
|
||||
else if kk == 0x0A
|
||||
printf("LD V%x, K\n", x)
|
||||
else if kk == 0x15
|
||||
printf("LD DT, V%x\n", x)
|
||||
else if kk == 0x18
|
||||
printf("LD ST, V%x\n", x)
|
||||
else if kk == 0x1E
|
||||
printf("ADD I, V%x\n", x)
|
||||
else if kk == 0x29
|
||||
printf("LD F, V%x\n", x)
|
||||
else if kk == 0x33
|
||||
printf("LD B, V%x\n", x)
|
||||
else if kk == 0x55
|
||||
printf("LD [I], V%x\n", x)
|
||||
else if kk == 0x65
|
||||
printf("LD V%x, [I]\n", x)
|
||||
else
|
||||
printf("??? (%x)\n", ins)
|
||||
else
|
||||
printf("??? (%x)\n", ins)
|
||||
|
||||
func chip8_step[c: CHIP8] : void
|
||||
let high: i64 = c->memory[c->pc]
|
||||
let low: i64 = c->memory[c->pc + 1]
|
||||
c->pc = c->pc + 2
|
||||
|
||||
let ins: i64 = (high << 8) | low
|
||||
let nnn: i64 = ins & 0x0fff
|
||||
let kk: i64 = ins & 0x00ff
|
||||
let n: i64 = ins & 0x000f
|
||||
let x: i64 = (ins >> 8) & 0x0f
|
||||
let y: i64 = (ins >> 4) & 0x0f
|
||||
|
||||
let op: i64 = (ins >> 12) & 0x0f
|
||||
if op == 0x0
|
||||
if nnn == 0x0e0
|
||||
mem.zero(c->display, 64 * 32)
|
||||
else if nnn == 0x0ee
|
||||
c->pc = array.nth(c->stack, c->sp)
|
||||
c->sp = c->sp - 1
|
||||
else if op == 0x1
|
||||
c->pc = nnn
|
||||
else if op == 0x2
|
||||
c->sp = c->sp + 1
|
||||
array.set(c->stack, c->sp, c->pc)
|
||||
c->pc = nnn
|
||||
else if op == 0x3
|
||||
if c->reg[x] == kk
|
||||
c->pc = c->pc + 2
|
||||
else if op == 0x4
|
||||
if c->reg[x] != kk
|
||||
c->pc = c->pc + 2
|
||||
else if op == 0x5
|
||||
if c->reg[x] == c->reg[y]
|
||||
c->pc = c->pc + 2
|
||||
else if op == 0x6
|
||||
c->reg[x] = kk
|
||||
else if op == 0x7
|
||||
c->reg[x] = c->reg[x] + kk
|
||||
else if op == 0x8
|
||||
if n == 0x0
|
||||
c->reg[x] = c->reg[y]
|
||||
else if n == 0x1
|
||||
c->reg[x] = c->reg[x] | c->reg[y]
|
||||
else if n == 0x2
|
||||
c->reg[x] = c->reg[x] & c->reg[y]
|
||||
else if n == 0x3
|
||||
c->reg[x] = c->reg[x] ^ c->reg[y]
|
||||
else if n == 0x4
|
||||
let res: i64 = c->reg[x] + c->reg[y]
|
||||
c->reg[0xf] = res > 0xff
|
||||
c->reg[x] = res
|
||||
else if n == 0x5
|
||||
c->reg[0xf] = c->reg[x] > c->reg[y]
|
||||
c->reg[x] = c->reg[x] - c->reg[y]
|
||||
else if n == 0x6
|
||||
c->reg[0xf] = c->reg[x] & 0x1
|
||||
c->reg[x] = c->reg[x] >> 1
|
||||
else if n == 0x7
|
||||
c->reg[0xf] = c->reg[y] > c->reg[x]
|
||||
c->reg[x] = c->reg[y] - c->reg[x]
|
||||
else if n == 0xE
|
||||
c->reg[0xf] = (c->reg[x] & 0x80) >> 7
|
||||
c->reg[x] = c->reg[x] << 1
|
||||
else if op == 0x9
|
||||
if c->reg[x] != c->reg[y]
|
||||
c->pc = c->pc + 2
|
||||
else if op == 0xa
|
||||
c->I = nnn
|
||||
else if op == 0xb
|
||||
c->pc = nnn + c->reg[0]
|
||||
else if op == 0xc
|
||||
c->reg[x] = (rand() % 256) & kk
|
||||
else if op == 0xd
|
||||
c->reg[0xf] = 0
|
||||
for row in 0..n
|
||||
for col in 0..8
|
||||
if (c->memory[c->I + row] & (0x80 >> col)) != 0
|
||||
let pixel_x: i64 = (c->reg[x] + col) % 64
|
||||
let pixel_y: i64 = (c->reg[y] + row) % 32
|
||||
let offset: i64 = pixel_x + (pixel_y * 64)
|
||||
|
||||
if c->display[offset] == 1
|
||||
c->reg[0xf] = 1
|
||||
c->display[offset] = c->display[offset] ^ 1
|
||||
else if op == 0xe
|
||||
if kk == 0x9e
|
||||
if IsKeyDown(array.nth(c->keyboard_map, c->reg[x]))
|
||||
c->pc = c->pc + 2
|
||||
else if kk == 0xa1
|
||||
if !IsKeyDown(array.nth(c->keyboard_map, c->reg[x]))
|
||||
c->pc = c->pc + 2
|
||||
else if op == 0xf
|
||||
if kk == 0x07
|
||||
c->reg[x] = c->delay_timer
|
||||
else if kk == 0x0A
|
||||
let key_pressed: bool = false
|
||||
while !key_pressed && !WindowShouldClose()
|
||||
for i in 0..16
|
||||
if IsKeyDown(array.nth(c->keyboard_map, i))
|
||||
key_pressed = true
|
||||
break
|
||||
else if kk == 0x15
|
||||
c->delay_timer = c->reg[x]
|
||||
else if kk == 0x18
|
||||
c->sound_timer = c->reg[x]
|
||||
else if kk == 0x1E
|
||||
c->I = c->I + c->reg[x]
|
||||
else if kk == 0x29
|
||||
c->I = c->reg[x] * 5
|
||||
else if kk == 0x33
|
||||
c->memory[c->I] = c->reg[x] / 100
|
||||
c->memory[c->I + 1] = (c->reg[x] / 10) % 10
|
||||
c->memory[c->I + 2] = c->reg[x] % 10
|
||||
else if kk == 0x55
|
||||
for i in 0..x+1
|
||||
c->memory[c->I + i] = c->reg[i]
|
||||
else if kk == 0x65
|
||||
for i in 0..x+1
|
||||
c->reg[i] = c->memory[c->I + i]
|
||||
|
||||
func chip8_free[c: CHIP8] : void
|
||||
mem.free(c->memory)
|
||||
array.free(c->stack)
|
||||
mem.free(c->reg)
|
||||
array.free(c->keyboard)
|
||||
mem.free(c->display)
|
||||
array.free(c->keyboard_map)
|
||||
mem.free(c)
|
||||
|
||||
func main[argc: i64, argv: ptr] : i64
|
||||
let path: str = 0
|
||||
let disassemble: bool = 0
|
||||
|
||||
for i in 1..argc
|
||||
let arg: str = mem.read64(argv + i * 8)
|
||||
if str.equal(arg, "-d")
|
||||
disassemble = 1
|
||||
else
|
||||
path = arg
|
||||
|
||||
if path == 0
|
||||
io.println("Usage: chip8 -d <path>")
|
||||
return 1
|
||||
|
||||
let c: CHIP8 = chip8_create()
|
||||
|
||||
let bytes_size: ptr = 0
|
||||
let bytes: ptr = io.read_binary_file(path, ^bytes_size)
|
||||
|
||||
for i in 0..bytes_size
|
||||
c->memory[0x200 + i] = bytes[i]
|
||||
|
||||
if disassemble
|
||||
chip8_disassemble(c, bytes_size / 2)
|
||||
return 0
|
||||
|
||||
InitWindow(640, 320, "CHIP-8")
|
||||
SetTargetFPS(60)
|
||||
|
||||
while !WindowShouldClose()
|
||||
if c->delay_timer > 0
|
||||
c->delay_timer = c->delay_timer - 1
|
||||
if c->sound_timer > 0
|
||||
c->sound_timer = c->sound_timer - 1
|
||||
// TODO: buzzer
|
||||
|
||||
for i in 0..25
|
||||
chip8_step(c)
|
||||
|
||||
BeginDrawing()
|
||||
|
||||
ClearBackground(0xffffffff)
|
||||
for y in 0..32
|
||||
for x in 0..64
|
||||
if c->display[x + y * 64] == 1
|
||||
DrawRectangle(x * 10, y * 10, 10, 10, 0xff000000)
|
||||
|
||||
EndDrawing()
|
||||
@@ -32,7 +32,7 @@ func main[] : i64
|
||||
|
||||
let base_point: ptr = mem.alloc(32)
|
||||
mem.zero(base_point, 32)
|
||||
mem.write8(base_point, 9)
|
||||
base_point[0] = 9
|
||||
|
||||
let alice_private: ptr = str.hex_decode("77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a")
|
||||
io.print("A_priv: ")
|
||||
|
||||
@@ -44,11 +44,11 @@ func part2[lines: array] : void
|
||||
for y in 1..str.len(array.nth(lines, x))-1
|
||||
if array.nth(lines, x)[y] == 'A'
|
||||
let s: str = mem.alloc(5)
|
||||
str.set(s, 0, array.nth(lines, x - 1)[y - 1])
|
||||
str.set(s, 1, array.nth(lines, x + 1)[y - 1])
|
||||
str.set(s, 2, array.nth(lines, x + 1)[y + 1])
|
||||
str.set(s, 3, array.nth(lines, x - 1)[y + 1])
|
||||
str.set(s, 4, 0)
|
||||
s[0] = array.nth(lines, x - 1)[y - 1]
|
||||
s[1] = array.nth(lines, x + 1)[y - 1]
|
||||
s[2] = array.nth(lines, x + 1)[y + 1]
|
||||
s[3] = array.nth(lines, x - 1)[y + 1]
|
||||
s[4] = 0
|
||||
|
||||
if str.equal(s, "MSSM") || str.equal(s, "SMMS") || str.equal(s, "MMSS") || str.equal(s, "SSMM")
|
||||
out = out + 1
|
||||
|
||||
@@ -8,9 +8,9 @@ func part1[lines: array] : void
|
||||
for j in 0..str.len(line)
|
||||
for k in (j+1)..str.len(line)
|
||||
let s: str = mem.alloc(3)
|
||||
str.set(s, 0, line[j])
|
||||
str.set(s, 1, line[k])
|
||||
str.set(s, 2, 0)
|
||||
s[0] = line[j]
|
||||
s[1] = line[k]
|
||||
s[2] = 0
|
||||
let n: i64 = str.parse_i64(s)
|
||||
if n > largest
|
||||
largest = n
|
||||
|
||||
@@ -8,6 +8,7 @@ use crate::{
|
||||
pub struct Analyzer {
|
||||
pub functions: HashMap<String, i32>,
|
||||
pub constants: HashMap<String, u64>,
|
||||
pub structs: HashMap<String, HashMap<String, usize>>,
|
||||
}
|
||||
|
||||
impl Analyzer {
|
||||
@@ -15,6 +16,7 @@ impl Analyzer {
|
||||
Analyzer {
|
||||
functions: HashMap::new(),
|
||||
constants: HashMap::new(),
|
||||
structs: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,7 +119,17 @@ impl Analyzer {
|
||||
}
|
||||
self.functions.insert(name.lexeme.clone(), -1);
|
||||
}
|
||||
Stmt::Struct { name: _, fields: _ } => todo!(),
|
||||
Stmt::Struct { name, fields } => {
|
||||
let mut fields_map: HashMap<String, usize> = HashMap::new();
|
||||
|
||||
let mut offset: usize = 0;
|
||||
for field in fields {
|
||||
fields_map.insert(field.var_name.lexeme.clone(), offset);
|
||||
offset += 8;
|
||||
}
|
||||
|
||||
self.structs.insert(name.lexeme.clone(), fields_map);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -138,7 +150,8 @@ impl Analyzer {
|
||||
self.analyze_expr(right)?;
|
||||
}
|
||||
Expr::Variable(_) => {}
|
||||
Expr::Assign { name: _, value } => {
|
||||
Expr::Assign { left, op: _, value } => {
|
||||
self.analyze_expr(left)?;
|
||||
self.analyze_expr(value)?;
|
||||
}
|
||||
Expr::Call {
|
||||
@@ -190,6 +203,9 @@ impl Analyzer {
|
||||
self.analyze_expr(expr)?;
|
||||
}
|
||||
Expr::New(_) => {}
|
||||
Expr::MemberAccess { left, field: _ } => {
|
||||
self.analyze_expr(left)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::{collections::HashMap, fmt::Write};
|
||||
use crate::{
|
||||
analyzer::Analyzer,
|
||||
parser::{Expr, Stmt},
|
||||
tokenizer::{TokenType, ZernError, error},
|
||||
tokenizer::{Token, TokenType, ZernError, error},
|
||||
};
|
||||
|
||||
struct Var {
|
||||
@@ -69,6 +69,9 @@ macro_rules! emit {
|
||||
|
||||
static REGISTERS: [&str; 6] = ["rdi", "rsi", "rdx", "rcx", "r8", "r9"];
|
||||
|
||||
// TODO: currently they are all just 64 bit values
|
||||
static BUILTIN_TYPES: [&str; 7] = ["void", "u8", "i64", "str", "bool", "ptr", "array"];
|
||||
|
||||
pub struct CodegenX86_64<'a> {
|
||||
output: String,
|
||||
data_section: String,
|
||||
@@ -108,11 +111,6 @@ _builtin_read64:
|
||||
mov rax, qword [rdi]
|
||||
ret
|
||||
|
||||
section .text._builtin_set8
|
||||
_builtin_set8:
|
||||
mov [rdi], sil
|
||||
ret
|
||||
|
||||
section .text._builtin_set64
|
||||
_builtin_set64:
|
||||
mov [rdi], rsi
|
||||
@@ -170,6 +168,10 @@ _builtin_environ:
|
||||
},
|
||||
};
|
||||
|
||||
if !self.is_valid_type_name(&var_type) {
|
||||
return error!(&name.loc, "unrecognized type: ".to_owned() + &var_type);
|
||||
}
|
||||
|
||||
self.compile_expr(env, initializer)?;
|
||||
let offset = env.define_var(name.lexeme.clone(), var_type);
|
||||
emit!(&mut self.output, " mov QWORD [rbp-{}], rax", offset);
|
||||
@@ -238,6 +240,13 @@ _builtin_environ:
|
||||
emit!(&mut self.output, " sub rsp, 256"); // TODO
|
||||
|
||||
for (i, param) in params.iter().enumerate() {
|
||||
if !self.is_valid_type_name(¶m.var_type.lexeme) {
|
||||
return error!(
|
||||
&name.loc,
|
||||
"unrecognized type: ".to_owned() + ¶m.var_type.lexeme
|
||||
);
|
||||
}
|
||||
|
||||
let offset = env
|
||||
.define_var(param.var_name.lexeme.clone(), param.var_type.lexeme.clone());
|
||||
if let Some(reg) = REGISTERS.get(i) {
|
||||
@@ -504,21 +513,48 @@ _builtin_environ:
|
||||
);
|
||||
}
|
||||
}
|
||||
Expr::Assign { name, value } => {
|
||||
Expr::Assign { left, op, value } => {
|
||||
self.compile_expr(env, value)?;
|
||||
|
||||
// TODO: move to analyzer
|
||||
let var = match env.get_var(&name.lexeme) {
|
||||
Some(x) => x,
|
||||
None => {
|
||||
return error!(name.loc, format!("undefined variable: {}", &name.lexeme));
|
||||
match left.as_ref() {
|
||||
Expr::Variable(name) => {
|
||||
// TODO: move to analyzer
|
||||
let var = match env.get_var(&name.lexeme) {
|
||||
Some(x) => x,
|
||||
None => {
|
||||
return error!(
|
||||
name.loc,
|
||||
format!("undefined variable: {}", &name.lexeme)
|
||||
);
|
||||
}
|
||||
};
|
||||
emit!(
|
||||
&mut self.output,
|
||||
" mov QWORD [rbp-{}], rax",
|
||||
var.stack_offset,
|
||||
);
|
||||
}
|
||||
Expr::Index { expr, index } => {
|
||||
emit!(&mut self.output, " push rax");
|
||||
self.compile_expr(env, expr)?;
|
||||
emit!(&mut self.output, " push rax");
|
||||
self.compile_expr(env, index)?;
|
||||
emit!(&mut self.output, " pop rbx");
|
||||
emit!(&mut self.output, " add rbx, rax");
|
||||
emit!(&mut self.output, " pop rax");
|
||||
emit!(&mut self.output, " mov BYTE [rbx], al");
|
||||
}
|
||||
Expr::MemberAccess { left, field } => {
|
||||
emit!(&mut self.output, " push rax");
|
||||
|
||||
let offset = self.get_field_offset(env, left, field)?;
|
||||
|
||||
self.compile_expr(env, left)?;
|
||||
emit!(&mut self.output, " pop rbx");
|
||||
emit!(&mut self.output, " mov QWORD [rax+{}], rbx", offset);
|
||||
}
|
||||
_ => return error!(&op.loc, "invalid assignment target"),
|
||||
};
|
||||
emit!(
|
||||
&mut self.output,
|
||||
" mov QWORD [rbp-{}], rax",
|
||||
var.stack_offset,
|
||||
);
|
||||
}
|
||||
Expr::Call {
|
||||
callee,
|
||||
@@ -627,8 +663,70 @@ _builtin_environ:
|
||||
return error!(&op.loc, "can only take address of variables and functions");
|
||||
}
|
||||
},
|
||||
Expr::New(_) => todo!(),
|
||||
Expr::New(struct_name) => {
|
||||
let struct_fields = &self.analyzer.structs[&struct_name.lexeme];
|
||||
|
||||
let memory_size = struct_fields.len() * 8;
|
||||
emit!(&mut self.output, " mov rdi, {}", memory_size);
|
||||
emit!(&mut self.output, " call mem.alloc");
|
||||
emit!(&mut self.output, " push rax");
|
||||
emit!(&mut self.output, " mov rdi, rax");
|
||||
emit!(&mut self.output, " mov rsi, {}", memory_size);
|
||||
emit!(&mut self.output, " call mem.zero");
|
||||
emit!(&mut self.output, " pop rax");
|
||||
}
|
||||
Expr::MemberAccess { left, field } => {
|
||||
let offset = self.get_field_offset(env, left, field)?;
|
||||
self.compile_expr(env, left)?;
|
||||
emit!(&mut self.output, " mov rax, QWORD [rax+{}]", offset);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_valid_type_name(&self, name: &str) -> bool {
|
||||
if BUILTIN_TYPES.contains(&name) {
|
||||
return true;
|
||||
}
|
||||
if self.analyzer.structs.contains_key(name) {
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn get_field_offset(
|
||||
&self,
|
||||
env: &mut Env,
|
||||
left: &Expr,
|
||||
field: &Token,
|
||||
) -> Result<usize, ZernError> {
|
||||
let struct_name = match left {
|
||||
Expr::Variable(name) => match env.get_var(&name.lexeme) {
|
||||
Some(v) => v.var_type.clone(),
|
||||
None => {
|
||||
return error!(name.loc, format!("undefined variable: {}", &name.lexeme));
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
return error!(
|
||||
&field.loc,
|
||||
"cannot determine struct type for member assignment"
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let fields = match self.analyzer.structs.get(&struct_name) {
|
||||
Some(f) => f,
|
||||
None => {
|
||||
return error!(&field.loc, format!("unknown struct type: {}", struct_name));
|
||||
}
|
||||
};
|
||||
|
||||
let offset = match fields.get(&field.lexeme) {
|
||||
Some(o) => *o,
|
||||
None => return error!(&field.loc, format!("unknown field: {}", &field.lexeme)),
|
||||
};
|
||||
|
||||
Ok(offset)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,8 @@ pub enum Expr {
|
||||
},
|
||||
Variable(Token),
|
||||
Assign {
|
||||
name: Token,
|
||||
left: Box<Expr>,
|
||||
op: Token,
|
||||
value: Box<Expr>,
|
||||
},
|
||||
Call {
|
||||
@@ -89,11 +90,12 @@ pub enum Expr {
|
||||
expr: Box<Expr>,
|
||||
},
|
||||
New(Token),
|
||||
MemberAccess {
|
||||
left: Box<Expr>,
|
||||
field: Token,
|
||||
},
|
||||
}
|
||||
|
||||
// TODO: currently they are all just 64 bit values
|
||||
static TYPES: [&str; 7] = ["void", "u8", "i64", "str", "bool", "ptr", "array"];
|
||||
|
||||
pub struct Parser {
|
||||
tokens: Vec<Token>,
|
||||
current: usize,
|
||||
@@ -159,9 +161,6 @@ impl Parser {
|
||||
self.consume(TokenType::Colon, "expected ':' after parameter name")?;
|
||||
|
||||
let var_type = self.consume(TokenType::Identifier, "expected parameter type")?;
|
||||
if !TYPES.contains(&var_type.lexeme.as_str()) {
|
||||
return error!(&name.loc, format!("unknown type: {}", var_type.lexeme));
|
||||
}
|
||||
|
||||
params.push(Param { var_type, var_name });
|
||||
if !self.match_token(&[TokenType::Comma]) {
|
||||
@@ -173,9 +172,6 @@ impl Parser {
|
||||
self.consume(TokenType::RightBracket, "expected ']' after arguments")?;
|
||||
self.consume(TokenType::Colon, "expected ':' after '['")?;
|
||||
let return_type = self.consume(TokenType::Identifier, "expected return type")?;
|
||||
if !TYPES.contains(&return_type.lexeme.as_str()) {
|
||||
return error!(&name.loc, format!("unknown type: {}", return_type.lexeme));
|
||||
}
|
||||
|
||||
self.is_inside_function = true;
|
||||
let body = Box::new(self.block()?);
|
||||
@@ -201,9 +197,6 @@ impl Parser {
|
||||
self.consume(TokenType::Colon, "expected ':' after field name")?;
|
||||
|
||||
let var_type = self.consume(TokenType::Identifier, "expected field type")?;
|
||||
if !TYPES.contains(&var_type.lexeme.as_str()) {
|
||||
return error!(&name.loc, format!("unknown type: {}", var_type.lexeme));
|
||||
}
|
||||
|
||||
fields.push(Param { var_type, var_name });
|
||||
}
|
||||
@@ -218,9 +211,6 @@ impl Parser {
|
||||
|
||||
let var_type = if self.match_token(&[TokenType::Colon]) {
|
||||
let token = self.consume(TokenType::Identifier, "expected variable type")?;
|
||||
if !TYPES.contains(&token.lexeme.as_str()) {
|
||||
return error!(&name.loc, format!("unknown type: {}", token.lexeme));
|
||||
}
|
||||
Some(token)
|
||||
} else {
|
||||
None
|
||||
@@ -332,13 +322,11 @@ impl Parser {
|
||||
let equals = self.previous().clone();
|
||||
let value = self.assignment()?;
|
||||
|
||||
return match expr {
|
||||
Expr::Variable(name) => Ok(Expr::Assign {
|
||||
name,
|
||||
value: Box::new(value),
|
||||
}),
|
||||
_ => return error!(equals.loc, "invalid assignment target"),
|
||||
};
|
||||
return Ok(Expr::Assign {
|
||||
left: Box::new(expr),
|
||||
op: equals,
|
||||
value: Box::new(value),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(expr)
|
||||
@@ -521,6 +509,13 @@ impl Parser {
|
||||
expr: Box::new(expr),
|
||||
index: Box::new(index),
|
||||
}
|
||||
} else if self.match_token(&[TokenType::Arrow]) {
|
||||
let field =
|
||||
self.consume(TokenType::Identifier, "expected field name after '->'")?;
|
||||
expr = Expr::MemberAccess {
|
||||
left: Box::new(expr),
|
||||
field,
|
||||
};
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -85,9 +85,9 @@ func crypto.blake2b.hash[outlen: i64, key: ptr, keylen: i64, input: ptr, inputle
|
||||
|
||||
if keylen > 0
|
||||
for i in 0..keylen
|
||||
mem.write8(block + i, key[i])
|
||||
block[i] = key[i]
|
||||
for i in (keylen)..128
|
||||
mem.write8(block + i, 0)
|
||||
block[i] = 0
|
||||
c = 128
|
||||
else
|
||||
c = 0
|
||||
@@ -99,7 +99,7 @@ func crypto.blake2b.hash[outlen: i64, key: ptr, keylen: i64, input: ptr, inputle
|
||||
t1 = t1 + 1
|
||||
crypto.blake2b._compress(h, block, t0, t1, 0, iv, sigma)
|
||||
c = 0
|
||||
mem.write8(block + c, input[i])
|
||||
block[c] = input[i]
|
||||
c = c + 1
|
||||
|
||||
t0 = t0 + c
|
||||
@@ -108,11 +108,11 @@ func crypto.blake2b.hash[outlen: i64, key: ptr, keylen: i64, input: ptr, inputle
|
||||
|
||||
if c < 128
|
||||
for i in (c)..128
|
||||
mem.write8(block + i, 0)
|
||||
block[i] = 0
|
||||
crypto.blake2b._compress(h, block, t0, t1, 1, iv, sigma)
|
||||
|
||||
for i in 0..outlen
|
||||
mem.write8(out + i, ((mem.read64(h + (i >> 3) * 8) >> (8 * (i & 7))) & 0xff))
|
||||
out[i] = (mem.read64(h + (i >> 3) * 8) >> (8 * (i & 7))) & 0xff
|
||||
|
||||
mem.zero_and_free(h, 8 * 8)
|
||||
mem.zero_and_free(block, 128)
|
||||
@@ -210,10 +210,9 @@ func crypto.xchacha20._stream[key: ptr, nonce: ptr, out: ptr, len: i64] : void
|
||||
crypto.xchacha20._hchacha20(key, nonce, subkey)
|
||||
|
||||
let nonce12: ptr = mem.alloc(12)
|
||||
for i in 0..12
|
||||
mem.write8(nonce12 + i, 0)
|
||||
mem.zero(nonce12, 12)
|
||||
for i in 0..8
|
||||
mem.write8(nonce12 + 4 + i, nonce[16 + i])
|
||||
nonce12[i + 4] = nonce[16 + i]
|
||||
|
||||
let blocknum = 0
|
||||
let remaining: i64 = len
|
||||
@@ -225,7 +224,7 @@ func crypto.xchacha20._stream[key: ptr, nonce: ptr, out: ptr, len: i64] : void
|
||||
if remaining < 64
|
||||
take = remaining
|
||||
for i in 0..take
|
||||
mem.write8(out + (len - remaining) + i, block[i])
|
||||
out[len - remaining + i] = block[i]
|
||||
remaining = remaining - take
|
||||
blocknum = blocknum + 1
|
||||
mem.zero_and_free(block, 64)
|
||||
@@ -237,13 +236,13 @@ func crypto.xchacha20._stream[key: ptr, nonce: ptr, out: ptr, len: i64] : void
|
||||
func crypto.xchacha20.xor_no_auth[key: ptr, nonce: ptr, input: ptr, len: i64] : ptr
|
||||
if len <= 0
|
||||
let out: ptr = mem.alloc(1)
|
||||
mem.write8(out, 0)
|
||||
out[0] = 0
|
||||
return out
|
||||
let out: ptr = mem.alloc(len)
|
||||
let ks: ptr = mem.alloc(len)
|
||||
crypto.xchacha20._stream(key, nonce, ks, len)
|
||||
for i in 0..len
|
||||
mem.write8(out + i, input[i] ^ ks[i])
|
||||
out[i] = input[i] ^ ks[i]
|
||||
mem.zero_and_free(ks, len)
|
||||
return out
|
||||
|
||||
@@ -328,8 +327,8 @@ func crypto.x25519.pack[out: ptr, input: ptr] : void
|
||||
|
||||
for i in 0..16
|
||||
let v: i64 = mem.read64(t + i * 8)
|
||||
mem.write8(out + i * 2, v & 0xff)
|
||||
mem.write8(out + i * 2 + 1, (v >> 8) & 0xff)
|
||||
out[i * 2] = v & 0xff
|
||||
out[i * 2 + 1] = (v >> 8) & 0xff
|
||||
|
||||
mem.zero_and_free(t, 16 * 8)
|
||||
mem.zero_and_free(m, 16 * 8)
|
||||
@@ -353,9 +352,9 @@ func crypto.x25519.scalarmult[scalar: ptr, point: ptr] : ptr
|
||||
|
||||
// copy and clamp scalar
|
||||
for i in 0..32
|
||||
mem.write8(clamped + i, scalar[i])
|
||||
mem.write8(clamped, clamped[0] & 0xf8)
|
||||
mem.write8(clamped + 31, (clamped[31] & 0x7f) | 0x40)
|
||||
clamped[i] = scalar[i]
|
||||
clamped[0] = clamped[0] & 0xf8
|
||||
clamped[31] = (clamped[31] & 0x7f) | 0x40
|
||||
|
||||
// load point
|
||||
crypto.x25519.unpack(x, point)
|
||||
|
||||
103
src/std/std.zr
103
src/std/std.zr
@@ -21,7 +21,7 @@ func mem.zero_and_free[x: ptr, size: i64] : void
|
||||
|
||||
func mem.zero[x: ptr, size: i64] : void
|
||||
for i in 0..size
|
||||
mem.write8(x + i, 0)
|
||||
x[i] = 0
|
||||
|
||||
func mem.read8[x: ptr] : u8
|
||||
return x[0]
|
||||
@@ -35,14 +35,11 @@ func mem.read32[x: ptr] : i64
|
||||
func mem.read64[x: ptr] : i64
|
||||
return _builtin_read64(x)
|
||||
|
||||
func mem.write8[x: ptr, d: u8] : void
|
||||
_builtin_set8(x, d)
|
||||
|
||||
func mem.write32[x: ptr, d: i64] : void
|
||||
mem.write8(x, d & 0xff)
|
||||
mem.write8(x + 1, (d >> 8) & 0xff)
|
||||
mem.write8(x + 2, (d >> 16) & 0xff)
|
||||
mem.write8(x + 3, (d >> 24) & 0xff)
|
||||
x[0] = d & 0xff
|
||||
x[1] = (d >> 8) & 0xff
|
||||
x[2] = (d >> 16) & 0xff
|
||||
x[3] = (d >> 24) & 0xff
|
||||
|
||||
func mem.write64[x: ptr, d: i64] : void
|
||||
_builtin_set64(x, d)
|
||||
@@ -80,11 +77,12 @@ func io.read_line[]: str
|
||||
let buffer: str = mem.alloc(MAX_SIZE + 1)
|
||||
let n: i64 = _builtin_syscall(SYS_read, 0, buffer, MAX_SIZE)
|
||||
if n < 0
|
||||
mem.free(buffer)
|
||||
return ""
|
||||
str.set(buffer, n, 0)
|
||||
buffer[n] = 0
|
||||
return buffer
|
||||
|
||||
func io.read_file[path: str]: str
|
||||
func io.read_file[path: str] : str
|
||||
let fd: i64 = _builtin_syscall(SYS_openat, -100, path, 0, 0)
|
||||
if fd <= 0
|
||||
dbg.panic("failed to open file")
|
||||
@@ -94,8 +92,22 @@ func io.read_file[path: str]: str
|
||||
|
||||
let buffer: str = mem.alloc(size + 1)
|
||||
let n: i64 = _builtin_syscall(SYS_read, fd, buffer, size)
|
||||
str.set(buffer, n, 0)
|
||||
_builtin_syscall(SYS_close, fd)
|
||||
buffer[n] = 0
|
||||
return buffer
|
||||
|
||||
func io.read_binary_file[path: str, len_ptr: ptr] : ptr
|
||||
let fd: i64 = _builtin_syscall(SYS_openat, -100, path, 0, 0)
|
||||
if fd < 0
|
||||
dbg.panic("failed to open file")
|
||||
|
||||
let size: i64 = _builtin_syscall(SYS_lseek, fd, 0, 2)
|
||||
_builtin_syscall(SYS_lseek, fd, 0, 0)
|
||||
|
||||
let buffer: ptr = mem.alloc(size)
|
||||
_builtin_syscall(SYS_read, fd, buffer, size)
|
||||
_builtin_syscall(SYS_close, fd)
|
||||
mem.write64(len_ptr, size)
|
||||
return buffer
|
||||
|
||||
func io.write_file[path: str, content: str] : void
|
||||
@@ -116,12 +128,9 @@ func str.copy[s: str] : str
|
||||
let size: i64 = str.len(s) + 1
|
||||
let dup: str = mem.alloc(size)
|
||||
for i in 0..size
|
||||
str.set(dup, i, s[i])
|
||||
dup[i] = s[i]
|
||||
return dup
|
||||
|
||||
func str.set[s: str, n: i64, c: u8] : void
|
||||
mem.write8(s + n, c)
|
||||
|
||||
func str.equal[a: str, b: str] : bool
|
||||
let i = 0
|
||||
while a[i] != 0 && b[i] != 0
|
||||
@@ -156,10 +165,10 @@ func str.concat[a: str, b: str] : str
|
||||
let b_len: i64 = str.len(b)
|
||||
let out: str = mem.alloc(a_len + b_len + 1)
|
||||
for i in 0..a_len
|
||||
str.set(out, i, a[i])
|
||||
out[i] = a[i]
|
||||
for i in 0..b_len
|
||||
str.set(out, a_len + i, b[i])
|
||||
str.set(out, a_len + b_len, 0)
|
||||
out[a_len + i] = b[i]
|
||||
out[a_len + b_len] = 0
|
||||
return out
|
||||
|
||||
func str.find[haystack: str, needle: str] : i64
|
||||
@@ -185,8 +194,8 @@ func str.substr[s: str, start: i64, length: i64] : str
|
||||
|
||||
let out: str = mem.alloc(length + 1)
|
||||
for i in 0..length
|
||||
str.set(out, i, s[start + i])
|
||||
str.set(out, length, 0)
|
||||
out[i] = s[start + i]
|
||||
out[length] = 0
|
||||
return out
|
||||
|
||||
func str.trim[s: str] : str
|
||||
@@ -242,8 +251,8 @@ func str.reverse[s: str] : str
|
||||
let out: str = mem.alloc(len + 1)
|
||||
|
||||
for i in 0..len
|
||||
str.set(out, i, s[len - i - 1])
|
||||
str.set(out, len, 0)
|
||||
out[i] = s[len - i - 1]
|
||||
out[len] = 0
|
||||
return out
|
||||
|
||||
// not sure this covers all wacky edge cases
|
||||
@@ -258,21 +267,21 @@ func str.from_i64[n: i64] : str
|
||||
let i = 0
|
||||
while n > 0
|
||||
let d: u8 = n % 10
|
||||
str.set(buf, i, '0' + d)
|
||||
buf[i] = '0' + d
|
||||
n = n / 10
|
||||
i = i + 1
|
||||
if neg
|
||||
str.set(buf, i, '-')
|
||||
buf[i] = '-'
|
||||
i = i + 1
|
||||
str.set(buf, i, 0)
|
||||
buf[i] = 0
|
||||
let s: str = str.reverse(buf)
|
||||
mem.free(buf)
|
||||
return s
|
||||
|
||||
func str.from_char[c: u8] : str
|
||||
let s: str = mem.alloc(2)
|
||||
str.set(s, 0, c)
|
||||
str.set(s, 1, 0)
|
||||
s[0] = c
|
||||
s[1] = 0
|
||||
return s
|
||||
|
||||
func str.parse_i64[s: str] : i64
|
||||
@@ -301,11 +310,11 @@ func str.hex_encode[s: str, s_len: i64] : str
|
||||
for i in 0..s_len
|
||||
let high: u8 = (s[i] >> 4) & 15
|
||||
let low: u8 = s[i] & 15
|
||||
str.set(out, j, hex_chars[high])
|
||||
str.set(out, j + 1, hex_chars[low])
|
||||
out[j] = hex_chars[high]
|
||||
out[j + 1] = hex_chars[low]
|
||||
j = j + 2
|
||||
|
||||
str.set(out, j, 0)
|
||||
out[j] = 0
|
||||
return out
|
||||
|
||||
func str._hex_digit_to_int[d: u8] : i64
|
||||
@@ -322,11 +331,11 @@ func str.hex_decode[s: str] : str
|
||||
let out: str = mem.alloc(s_len / 2 + 1)
|
||||
|
||||
while i < s_len
|
||||
str.set(out, j, str._hex_digit_to_int(s[i]) * 16 + str._hex_digit_to_int(s[i + 1]))
|
||||
out[j] = str._hex_digit_to_int(s[i]) * 16 + str._hex_digit_to_int(s[i + 1])
|
||||
i = i + 2
|
||||
j = j + 1
|
||||
|
||||
str.set(out, j, 0)
|
||||
out[j] = 0
|
||||
return out
|
||||
|
||||
func math.gcd[a: i64, b: i64] : i64
|
||||
@@ -606,14 +615,14 @@ func net.listen[packed_host: i64, port: i64] : i64
|
||||
|
||||
let sa: ptr = mem.alloc(16)
|
||||
mem.zero(sa, 16)
|
||||
mem.write8(sa + 0, 2)
|
||||
mem.write8(sa + 1, 0)
|
||||
mem.write8(sa + 2, (port >> 8) & 255)
|
||||
mem.write8(sa + 3, port & 255)
|
||||
mem.write8(sa + 4, (packed_host >> 24) & 255)
|
||||
mem.write8(sa + 5, (packed_host >> 16) & 255)
|
||||
mem.write8(sa + 6, (packed_host >> 8) & 255)
|
||||
mem.write8(sa + 7, packed_host & 255)
|
||||
sa[0] = 2
|
||||
sa[1] = 0
|
||||
sa[2] = (port >> 8) & 255
|
||||
sa[3] = port & 255
|
||||
sa[4] = (packed_host >> 24) & 255
|
||||
sa[5] = (packed_host >> 16) & 255
|
||||
sa[6] = (packed_host >> 8) & 255
|
||||
sa[7] = packed_host & 255
|
||||
|
||||
if _builtin_syscall(SYS_bind, s, sa, 16) < 0
|
||||
_builtin_syscall(SYS_close, s)
|
||||
@@ -640,13 +649,13 @@ func net.connect[host: str, port: i64] : i64
|
||||
|
||||
let sa: ptr = mem.alloc(16)
|
||||
mem.zero(sa, 16)
|
||||
mem.write8(sa + 0, 2)
|
||||
mem.write8(sa + 2, (port >> 8) & 255)
|
||||
mem.write8(sa + 3, port & 255)
|
||||
mem.write8(sa + 4, ip_ptr[0])
|
||||
mem.write8(sa + 5, ip_ptr[1])
|
||||
mem.write8(sa + 6, ip_ptr[2])
|
||||
mem.write8(sa + 7, ip_ptr[3])
|
||||
sa[0] = 2
|
||||
sa[2] = (port >> 8) & 255
|
||||
sa[3] = port & 255
|
||||
sa[4] = ip_ptr[0]
|
||||
sa[5] = ip_ptr[1]
|
||||
sa[6] = ip_ptr[2]
|
||||
sa[7] = ip_ptr[3]
|
||||
|
||||
if _builtin_syscall(SYS_connect, s, sa, 16) < 0
|
||||
mem.free(sa)
|
||||
|
||||
@@ -23,6 +23,7 @@ pub enum TokenType {
|
||||
DoubleDot,
|
||||
ShiftLeft,
|
||||
ShiftRight,
|
||||
Arrow,
|
||||
|
||||
Equal,
|
||||
DoubleEqual,
|
||||
@@ -155,10 +156,16 @@ impl Tokenizer {
|
||||
'+' => self.add_token(TokenType::Plus),
|
||||
'*' => self.add_token(TokenType::Star),
|
||||
',' => self.add_token(TokenType::Comma),
|
||||
'-' => self.add_token(TokenType::Minus),
|
||||
'%' => self.add_token(TokenType::Mod),
|
||||
'^' => self.add_token(TokenType::Xor),
|
||||
':' => self.add_token(TokenType::Colon),
|
||||
'-' => {
|
||||
if self.match_char('>') {
|
||||
self.add_token(TokenType::Arrow)
|
||||
} else {
|
||||
self.add_token(TokenType::Minus)
|
||||
}
|
||||
}
|
||||
'.' => {
|
||||
if self.match_char('.') {
|
||||
self.add_token(TokenType::DoubleDot)
|
||||
|
||||
Reference in New Issue
Block a user