Compare commits
9 commits
36d978d798
...
c85437e4e8
Author | SHA1 | Date | |
---|---|---|---|
c85437e4e8 | |||
76b3f9ff4b | |||
66c3f7b0d4 | |||
b04d9e517e | |||
b46c64db4f | |||
6de8496aa5 | |||
499fe34f1d | |||
20903ef294 | |||
aafcb2fbbd |
|
@ -1,46 +0,0 @@
|
|||
Dbg: dropping chunk of size: 0
|
||||
Dbg: dropping chunk of size: 544
|
||||
Dbg: deallocating full chunk
|
||||
Dbg: dropping chunk of size: 0
|
||||
Dbg: dropping chunk of size: 3200
|
||||
Dbg: deallocating full chunk
|
||||
Dbg: dropping chunk of size: 0
|
||||
Dbg: dropping chunk of size: 1032
|
||||
Dbg: deallocating full chunk
|
||||
Dbg: dropping chunk of size: 0
|
||||
Dbg: dropping chunk of size: 224
|
||||
Dbg: deallocating full chunk
|
||||
Dbg: dropping chunk of size: 0
|
||||
Dbg: dropping chunk of size: 3240
|
||||
Dbg: deallocating full chunk
|
||||
Dbg: dropping chunk of size: 0
|
||||
Dbg: dropping chunk of size: 1144
|
||||
Dbg: deallocating full chunk
|
||||
Dbg: dropping chunk of size: 0
|
||||
Dbg: dropping chunk of size: 1352
|
||||
Dbg: deallocating full chunk
|
||||
Dbg: dropping chunk of size: 0
|
||||
Dbg: dropping chunk of size: 1400
|
||||
Dbg: deallocating full chunk
|
||||
Dbg: dropping chunk of size: 0
|
||||
Dbg: dropping chunk of size: 1128
|
||||
Dbg: deallocating full chunk
|
||||
Dbg: dropping chunk of size: 0
|
||||
Dbg: dropping chunk of size: 1632
|
||||
Dbg: deallocating full chunk
|
||||
Dbg: dropping chunk of size: 0
|
||||
Dbg: dropping chunk of size: 1528
|
||||
Dbg: deallocating full chunk
|
||||
Dbg: dropping chunk of size: 0
|
||||
Dbg: dropping chunk of size: 2496
|
||||
Dbg: deallocating full chunk
|
||||
Dbg: dropping chunk of size: 0
|
||||
Dbg: dropping chunk of size: 2440
|
||||
Dbg: deallocating full chunk
|
||||
Dbg: dropping chunk of size: 0
|
||||
Dbg: dropping chunk of size: 600
|
||||
Dbg: deallocating full chunk
|
||||
test parser::tests::arithmetic ... Dbg: dropping chunk of size: 544
|
||||
Dbg: deallocating full chunk
|
||||
test parser::tests::example ... Dbg: dropping chunk of size: 224
|
||||
Dbg: deallocating full chunk
|
|
@ -1,46 +0,0 @@
|
|||
Dbg: dropping chunk of size: 0
|
||||
Dbg: dropping chunk of size: 936
|
||||
Dbg: deallocating full chunk
|
||||
Dbg: dropping chunk of size: 0
|
||||
Dbg: dropping chunk of size: 4040
|
||||
Dbg: deallocating full chunk
|
||||
Dbg: dropping chunk of size: 0
|
||||
Dbg: dropping chunk of size: 1112
|
||||
Dbg: deallocating full chunk
|
||||
Dbg: dropping chunk of size: 0
|
||||
Dbg: dropping chunk of size: 296
|
||||
Dbg: deallocating full chunk
|
||||
Dbg: dropping chunk of size: 0
|
||||
Dbg: dropping chunk of size: 4328
|
||||
Dbg: deallocating full chunk
|
||||
Dbg: dropping chunk of size: 0
|
||||
Dbg: dropping chunk of size: 1464
|
||||
Dbg: deallocating full chunk
|
||||
Dbg: dropping chunk of size: 0
|
||||
Dbg: dropping chunk of size: 1616
|
||||
Dbg: deallocating full chunk
|
||||
Dbg: dropping chunk of size: 0
|
||||
Dbg: dropping chunk of size: 1864
|
||||
Dbg: deallocating full chunk
|
||||
Dbg: dropping chunk of size: 0
|
||||
Dbg: dropping chunk of size: 1504
|
||||
Dbg: deallocating full chunk
|
||||
Dbg: dropping chunk of size: 0
|
||||
Dbg: dropping chunk of size: 2160
|
||||
Dbg: deallocating full chunk
|
||||
Dbg: dropping chunk of size: 0
|
||||
Dbg: dropping chunk of size: 2000
|
||||
Dbg: deallocating full chunk
|
||||
Dbg: dropping chunk of size: 0
|
||||
Dbg: dropping chunk of size: 3048
|
||||
Dbg: deallocating full chunk
|
||||
Dbg: dropping chunk of size: 0
|
||||
Dbg: dropping chunk of size: 2960
|
||||
Dbg: deallocating full chunk
|
||||
Dbg: dropping chunk of size: 0
|
||||
Dbg: dropping chunk of size: 848
|
||||
Dbg: deallocating full chunk
|
||||
test parser::tests::arithmetic ... Dbg: dropping chunk of size: 936
|
||||
Dbg: deallocating full chunk
|
||||
test parser::tests::example ... Dbg: dropping chunk of size: 296
|
||||
Dbg: deallocating full chunk
|
|
@ -241,6 +241,15 @@ main := fn(): int {
|
|||
|
||||
#### generic_types
|
||||
```hb
|
||||
MALLOC_SYS_CALL := 69
|
||||
FREE_SYS_CALL := 96
|
||||
|
||||
malloc := fn(size: uint, align: uint): ^void
|
||||
return @eca(^void, MALLOC_SYS_CALL, size, align);
|
||||
|
||||
free := fn(ptr: ^void, size: uint, align: uint): void
|
||||
return @eca(void, FREE_SYS_CALL, ptr, size, align);
|
||||
|
||||
Vec := fn($Elem: type): type {
|
||||
return struct {
|
||||
data: ^Elem,
|
||||
|
@ -249,14 +258,62 @@ Vec := fn($Elem: type): type {
|
|||
};
|
||||
}
|
||||
|
||||
new := fn($Elem: type): Vec(Elem) return Vec(Elem).{ data: @bitcast(0), len: 0, cap: 0 };
|
||||
|
||||
deinit := fn($Elem: type, vec: ^Vec(Elem)): void {
|
||||
free(@bitcast(vec.data), vec.cap * @sizeof(Elem), @alignof(Elem));
|
||||
*vec = new(Elem);
|
||||
return;
|
||||
}
|
||||
|
||||
push := fn($Elem: type, vec: ^Vec(Elem), value: Elem): ^Elem {
|
||||
if vec.len == vec.cap {
|
||||
if vec.cap == 0 {
|
||||
vec.cap = 1;
|
||||
} else {
|
||||
vec.cap *= 2;
|
||||
}
|
||||
|
||||
new_alloc := @as(^Elem, @bitcast(malloc(vec.cap * @sizeof(Elem), @alignof(Elem))));
|
||||
if @as(uint, @bitcast(new_alloc)) == 0 return @bitcast(0);
|
||||
|
||||
src_cursor := vec.data;
|
||||
dst_cursor := new_alloc;
|
||||
end := vec.data + vec.len;
|
||||
|
||||
loop if src_cursor == end break else {
|
||||
*dst_cursor = *src_cursor;
|
||||
src_cursor += 1;
|
||||
dst_cursor += 1;
|
||||
}
|
||||
|
||||
if vec.len != 0 {
|
||||
free(@bitcast(vec.data), vec.len * @sizeof(Elem), @alignof(Elem));
|
||||
}
|
||||
vec.data = new_alloc;
|
||||
}
|
||||
|
||||
slot := vec.data + vec.len;
|
||||
*slot = value;
|
||||
vec.len += 1;
|
||||
return slot;
|
||||
}
|
||||
|
||||
main := fn(): int {
|
||||
i := 69;
|
||||
vec := Vec(int).{
|
||||
data: &i,
|
||||
len: 1,
|
||||
cap: 1,
|
||||
};
|
||||
return *vec.data;
|
||||
vec := new(int);
|
||||
push(int, &vec, 69);
|
||||
res := *vec.data;
|
||||
deinit(int, &vec);
|
||||
return res;
|
||||
}
|
||||
```
|
||||
|
||||
#### generic_functions
|
||||
```hb
|
||||
add := fn($T: type, a: T, b: T): T return a + b;
|
||||
|
||||
main := fn(): int {
|
||||
return add(u32, 2, 2) - add(int, 1, 3);
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
@ -7,7 +7,10 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
|
||||
let mut generated = String::new();
|
||||
|
||||
writeln!(generated, "#![allow(dead_code)]")?;
|
||||
writeln!(
|
||||
generated,
|
||||
"#![allow(dead_code)] #![allow(clippy::upper_case_acronyms)]"
|
||||
)?;
|
||||
gen_max_size(&mut generated)?;
|
||||
gen_encodes(&mut generated)?;
|
||||
gen_structs(&mut generated)?;
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
main := fn(): int {
|
||||
return 10 - 20 / 2 + 4 * (2 + 2) - 4 * 4 + 1;
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
|
||||
Color := struct {
|
||||
r: u8,
|
||||
g: u8,
|
||||
b: u8,
|
||||
a: u8,
|
||||
}
|
||||
|
||||
Point := struct {
|
||||
x: u32,
|
||||
y: u32,
|
||||
}
|
||||
|
||||
Pixel := struct {
|
||||
color: Color,
|
||||
point: Point,
|
||||
}
|
||||
|
||||
main := fn(): int {
|
||||
pixel := Pixel.{
|
||||
color: Color.{
|
||||
r: 255,
|
||||
g: 0,
|
||||
b: 0,
|
||||
a: 255,
|
||||
},
|
||||
point: Point.{
|
||||
x: 0,
|
||||
y: 2,
|
||||
},
|
||||
};
|
||||
|
||||
if *(&pixel.point.x + 1) != 2 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if *(&pixel.point.y - 1) != 0 {
|
||||
return 64;
|
||||
}
|
||||
|
||||
return pixel.point.x + pixel.point.y + pixel.color.r
|
||||
+ pixel.color.g + pixel.color.b + pixel.color.a;
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
Type := struct {
|
||||
brah: int,
|
||||
blah: int,
|
||||
}
|
||||
|
||||
main := fn(): int {
|
||||
byte := @as(u8, 10);
|
||||
same_type_as_byte := @as(@TypeOf(byte), 30);
|
||||
wide_uint := @as(u32, 40);
|
||||
truncated_uint := @as(u8, @intcast(wide_uint));
|
||||
size_of_Type_in_bytes := @sizeof(Type);
|
||||
align_of_Type_in_bytes := @alignof(Type);
|
||||
hardcoded_pointer := @as(^u8, @bitcast(10));
|
||||
ecall_that_returns_int := @eca(int, 1, Type.(10, 20), 5, 6);
|
||||
return 0;
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
arm_fb_ptr := fn(): int return 100;
|
||||
x86_fb_ptr := fn(): int return 100;
|
||||
|
||||
|
||||
check_platform := fn(): int {
|
||||
return x86_fb_ptr();
|
||||
}
|
||||
|
||||
set_pixel := fn(x: int, y: int, width: int): int {
|
||||
pix_offset := y * width + x;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
main := fn(): int {
|
||||
fb_ptr := check_platform();
|
||||
width := 100;
|
||||
height := 30;
|
||||
x:= 0;
|
||||
y:= 0;
|
||||
|
||||
loop {
|
||||
if x <= height + 1 {
|
||||
set_pixel(x,y,width);
|
||||
x = x + 1;
|
||||
} else {
|
||||
set_pixel(x,y,width);
|
||||
x = 0;
|
||||
y = y + 1;
|
||||
}
|
||||
if y == width {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
|
||||
main := fn(): int {
|
||||
return add_one(10) + add_two(20);
|
||||
}
|
||||
|
||||
add_two := fn(x: int): int {
|
||||
return x + 2;
|
||||
}
|
||||
|
||||
add_one := fn(x: int): int {
|
||||
return x + 1;
|
||||
}
|
||||
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
Vec := fn($Elem: type): type {
|
||||
return struct {
|
||||
data: ^Elem,
|
||||
len: uint,
|
||||
cap: uint,
|
||||
};
|
||||
}
|
||||
|
||||
main := fn(): int {
|
||||
i := 69;
|
||||
vec := Vec(int).{
|
||||
data: &i,
|
||||
len: 1,
|
||||
cap: 1,
|
||||
};
|
||||
return *vec.data;
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
global_var := 10;
|
||||
|
||||
complex_global_var := fib(global_var) - 5;
|
||||
|
||||
fib := fn(n: int): int {
|
||||
if 2 > n {
|
||||
return n;
|
||||
}
|
||||
return fib(n - 1) + fib(n - 2);
|
||||
}
|
||||
|
||||
main := fn(): int {
|
||||
return complex_global_var;
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
|
||||
main := fn(): int {
|
||||
return fib(10);
|
||||
}
|
||||
|
||||
fib := fn(x: int): int {
|
||||
if x <= 2 {
|
||||
return 1;
|
||||
} else {
|
||||
return fib(x - 1) + fib(x - 2);
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
main := fn(): int {
|
||||
return fib(10);
|
||||
}
|
||||
|
||||
fib := fn(n: int): int {
|
||||
a := 0;
|
||||
b := 1;
|
||||
loop {
|
||||
if n == 0 {
|
||||
break;
|
||||
}
|
||||
c := a + b;
|
||||
a = b;
|
||||
b = c;
|
||||
n -= 1;
|
||||
|
||||
stack_reclamation_edge_case := 0;
|
||||
|
||||
continue;
|
||||
}
|
||||
return a;
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
main := fn(): int {
|
||||
return 1;
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
main := fn(): int {
|
||||
a := 1;
|
||||
b := &a;
|
||||
modify(b);
|
||||
drop(a);
|
||||
stack_reclamation_edge_case := 0;
|
||||
return *b - 2;
|
||||
}
|
||||
|
||||
modify := fn(a: ^int): void {
|
||||
*a = 2;
|
||||
return;
|
||||
}
|
||||
|
||||
drop := fn(a: int): void {
|
||||
return;
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
Point := struct {
|
||||
x: int,
|
||||
y: int,
|
||||
}
|
||||
|
||||
Rect := struct {
|
||||
a: Point,
|
||||
b: Point,
|
||||
}
|
||||
|
||||
main := fn(): int {
|
||||
a := Point.(1, 2);
|
||||
b := Point.(3, 4);
|
||||
|
||||
d := Rect.(a + b, b - a);
|
||||
d2 := Rect.(Point.(0, 0) - b, a);
|
||||
d2 = d2 + d;
|
||||
|
||||
c := d2.a + d2.b;
|
||||
return c.x + c.y;
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
Ty := struct {
|
||||
a: int,
|
||||
b: int,
|
||||
}
|
||||
|
||||
Ty2 := struct {
|
||||
ty: Ty,
|
||||
c: int,
|
||||
}
|
||||
|
||||
main := fn(): int {
|
||||
finst := Ty2.{ ty: Ty.{ a: 4, b: 1 }, c: 3 };
|
||||
inst := odher_pass(finst);
|
||||
if inst.c == 3 {
|
||||
return pass(&inst.ty);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
pass := fn(t: ^Ty): int {
|
||||
return t.a - t.b;
|
||||
}
|
||||
|
||||
odher_pass := fn(t: Ty2): Ty2 {
|
||||
return t;
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
main := fn(): int {
|
||||
a := 1;
|
||||
b := 2;
|
||||
a = a + 1;
|
||||
return a - b;
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -1,25 +0,0 @@
|
|||
pub type Ident = u32;
|
||||
|
||||
const LEN_BITS: u32 = 6;
|
||||
|
||||
pub fn len(ident: Ident) -> u32 {
|
||||
ident & ((1 << LEN_BITS) - 1)
|
||||
}
|
||||
|
||||
pub fn is_null(ident: Ident) -> bool {
|
||||
(ident >> LEN_BITS) == 0
|
||||
}
|
||||
|
||||
pub fn pos(ident: Ident) -> u32 {
|
||||
(ident >> LEN_BITS).saturating_sub(1)
|
||||
}
|
||||
|
||||
pub fn new(pos: u32, len: u32) -> Ident {
|
||||
debug_assert!(len < (1 << LEN_BITS));
|
||||
((pos + 1) << LEN_BITS) | len
|
||||
}
|
||||
|
||||
pub fn range(ident: Ident) -> std::ops::Range<usize> {
|
||||
let (len, pos) = (len(ident) as usize, pos(ident) as usize);
|
||||
pos..pos + len
|
||||
}
|
|
@ -156,8 +156,12 @@ pub struct Lexer<'a> {
|
|||
|
||||
impl<'a> Lexer<'a> {
|
||||
pub fn new(input: &'a str) -> Self {
|
||||
Self::restore(input, 0)
|
||||
}
|
||||
|
||||
pub fn restore(input: &'a str, pos: u32) -> Self {
|
||||
Self {
|
||||
pos: 0,
|
||||
pos,
|
||||
bytes: input.as_bytes(),
|
||||
}
|
||||
}
|
||||
|
@ -317,13 +321,17 @@ impl LineMap {
|
|||
|
||||
let mut iter = self.lines.iter().copied();
|
||||
|
||||
while let Some(mut len) = iter.next() {
|
||||
loop {
|
||||
let mut acc = 0;
|
||||
while len & 0x80 != 0 {
|
||||
acc = (acc << 7) | (len & 0x7F) as u32;
|
||||
len = iter.next().unwrap();
|
||||
let mut idx = 0;
|
||||
loop {
|
||||
let len = iter.next().unwrap();
|
||||
acc |= ((len & 0x7F) as u32) << (7 * idx);
|
||||
idx += 1;
|
||||
if len & 0x80 == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
acc += len as u32;
|
||||
|
||||
if pos < acc {
|
||||
break;
|
||||
|
@ -372,7 +380,7 @@ impl LineMap {
|
|||
let idx = mask.trailing_zeros() as usize + i * 16 + start.len();
|
||||
let mut len = idx - last_nl + 1;
|
||||
while len >= 0x80 {
|
||||
lines.push((0x80 | (len & 0x7F)) as u8);
|
||||
lines.push(0x80 | (len & 0x7F) as u8);
|
||||
len >>= 7;
|
||||
}
|
||||
lines.push(len as u8);
|
||||
|
@ -388,3 +396,30 @@ impl LineMap {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
#[test]
|
||||
fn test_smh() {
|
||||
let example = include_str!("../README.md");
|
||||
|
||||
let nlines = super::LineMap::new(example);
|
||||
|
||||
fn slow_nline_search(str: &str, mut pos: usize) -> (usize, usize) {
|
||||
(
|
||||
str.lines()
|
||||
.take_while(|l| match pos.checked_sub(l.len() + 1) {
|
||||
Some(nl) => (pos = nl, true).1,
|
||||
None => false,
|
||||
})
|
||||
.count()
|
||||
+ 1,
|
||||
pos + 1,
|
||||
)
|
||||
}
|
||||
|
||||
for i in 0..example.len() {
|
||||
assert_eq!(slow_nline_search(example, i), nlines.line_col(i as _));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
#![feature(vec_pop_if)]
|
||||
#![feature(new_uninit)]
|
||||
#![feature(anonymous_lifetime_in_impl_trait)]
|
||||
#![feature(inline_const_pat)]
|
||||
#![feature(pattern)]
|
||||
#![feature(never_type)]
|
||||
#![feature(unwrap_infallible)]
|
||||
#![feature(if_let_guard)]
|
||||
#![feature(slice_partition_dedup)]
|
||||
#![feature(noop_waker)]
|
||||
|
@ -10,6 +15,7 @@
|
|||
#![feature(ptr_metadata)]
|
||||
#![feature(const_mut_refs)]
|
||||
#![feature(slice_ptr_get)]
|
||||
#![allow(clippy::format_collect)]
|
||||
|
||||
use std::{
|
||||
collections::VecDeque,
|
||||
|
@ -20,26 +26,100 @@ use std::{
|
|||
|
||||
use parser::Ast;
|
||||
|
||||
use crate::parser::FileId;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! run_tests {
|
||||
($runner:path: $($name:ident => $input:expr;)*) => {$(
|
||||
#[test]
|
||||
fn $name() {
|
||||
$crate::tests::run_test(std::any::type_name_of_val(&$name), stringify!($name), $input, $runner);
|
||||
$crate::run_test(std::any::type_name_of_val(&$name), stringify!($name), $input, $runner);
|
||||
}
|
||||
)*};
|
||||
}
|
||||
|
||||
pub mod codegen;
|
||||
mod ident;
|
||||
pub mod parser;
|
||||
|
||||
mod instrs;
|
||||
mod lexer;
|
||||
mod log;
|
||||
pub mod parser;
|
||||
mod tests;
|
||||
mod typechk;
|
||||
|
||||
mod ident {
|
||||
pub type Ident = u32;
|
||||
|
||||
const LEN_BITS: u32 = 6;
|
||||
|
||||
pub fn len(ident: u32) -> u32 {
|
||||
ident & ((1 << LEN_BITS) - 1)
|
||||
}
|
||||
|
||||
pub fn is_null(ident: u32) -> bool {
|
||||
(ident >> LEN_BITS) == 0
|
||||
}
|
||||
|
||||
pub fn pos(ident: u32) -> u32 {
|
||||
(ident >> LEN_BITS).saturating_sub(1)
|
||||
}
|
||||
|
||||
pub fn new(pos: u32, len: u32) -> u32 {
|
||||
debug_assert!(len < (1 << LEN_BITS));
|
||||
((pos + 1) << LEN_BITS) | len
|
||||
}
|
||||
|
||||
pub fn range(ident: u32) -> std::ops::Range<usize> {
|
||||
let (len, pos) = (len(ident) as usize, pos(ident) as usize);
|
||||
pos..pos + len
|
||||
}
|
||||
}
|
||||
|
||||
mod log {
|
||||
#![allow(unused_macros)]
|
||||
|
||||
#[derive(PartialOrd, PartialEq, Ord, Eq, Debug)]
|
||||
pub enum Level {
|
||||
Err,
|
||||
Wrn,
|
||||
Inf,
|
||||
Dbg,
|
||||
}
|
||||
|
||||
pub const LOG_LEVEL: Level = match option_env!("LOG_LEVEL") {
|
||||
Some(val) => match val.as_bytes()[0] {
|
||||
b'e' => Level::Err,
|
||||
b'w' => Level::Wrn,
|
||||
b'i' => Level::Inf,
|
||||
b'd' => Level::Dbg,
|
||||
_ => panic!("Invalid log level."),
|
||||
},
|
||||
None => {
|
||||
if cfg!(debug_assertions) {
|
||||
Level::Dbg
|
||||
} else {
|
||||
Level::Err
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
macro_rules! log {
|
||||
($level:expr, $fmt:literal $($expr:tt)*) => {
|
||||
if $level <= $crate::log::LOG_LEVEL {
|
||||
eprintln!("{:?}: {}", $level, format_args!($fmt $($expr)*));
|
||||
}
|
||||
};
|
||||
|
||||
($level:expr, $($arg:expr),*) => {
|
||||
if $level <= $crate::log::LOG_LEVEL {
|
||||
$(eprintln!("[{}{}{}][{:?}]: {} = {:?}", line!(), column!(), file!(), $level, stringify!($arg), $arg);)*
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! err { ($($arg:tt)*) => { $crate::log::log!($crate::log::Level::Err, $($arg)*) }; }
|
||||
macro_rules! wrn { ($($arg:tt)*) => { $crate::log::log!($crate::log::Level::Wrn, $($arg)*) }; }
|
||||
macro_rules! inf { ($($arg:tt)*) => { $crate::log::log!($crate::log::Level::Inf, $($arg)*) }; }
|
||||
macro_rules! dbg { ($($arg:tt)*) => { $crate::log::log!($crate::log::Level::Dbg, $($arg)*) }; }
|
||||
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use {dbg, err, inf, log, wrn};
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn encode<T>(instr: T) -> (usize, [u8; instrs::MAX_SIZE]) {
|
||||
|
@ -139,9 +219,15 @@ impl<T> TaskQueueInner<T> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn parse_all(threads: usize, root: &str) -> io::Result<Vec<Ast>> {
|
||||
pub fn parse_from_fs(threads: usize, root: &str) -> io::Result<Vec<Ast>> {
|
||||
const GIT_DEPS_DIR: &str = "git-deps";
|
||||
|
||||
enum Chk<'a> {
|
||||
Branch(&'a str),
|
||||
Rev(&'a str),
|
||||
Tag(&'a str),
|
||||
}
|
||||
|
||||
enum ImportPath<'a> {
|
||||
Root {
|
||||
path: &'a str,
|
||||
|
@ -152,9 +238,7 @@ pub fn parse_all(threads: usize, root: &str) -> io::Result<Vec<Ast>> {
|
|||
Git {
|
||||
link: &'a str,
|
||||
path: &'a str,
|
||||
branch: Option<&'a str>,
|
||||
tag: Option<&'a str>,
|
||||
rev: Option<&'a str>,
|
||||
chk: Option<Chk<'a>>,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -171,22 +255,16 @@ pub fn parse_all(threads: usize, root: &str) -> io::Result<Vec<Ast>> {
|
|||
let (link, path) =
|
||||
path.split_once(':').ok_or(ParseImportError::ExpectedPath)?;
|
||||
let (link, params) = link.split_once('?').unwrap_or((link, ""));
|
||||
let [mut branch, mut tag, mut rev] = [None; 3];
|
||||
for (key, value) in params.split('&').filter_map(|s| s.split_once('=')) {
|
||||
match key {
|
||||
"branch" => branch = Some(value),
|
||||
"tag" => tag = Some(value),
|
||||
"rev" => rev = Some(value),
|
||||
_ => return Err(ParseImportError::UnexpectedParam),
|
||||
}
|
||||
}
|
||||
Ok(Self::Git {
|
||||
link,
|
||||
path,
|
||||
branch,
|
||||
tag,
|
||||
rev,
|
||||
})
|
||||
let chk = params
|
||||
.split('&')
|
||||
.filter_map(|s| s.split_once('='))
|
||||
.find_map(|(key, value)| match key {
|
||||
"branch" => Some(Chk::Branch(value)),
|
||||
"rev" => Some(Chk::Rev(value)),
|
||||
"tag" => Some(Chk::Tag(value)),
|
||||
_ => None,
|
||||
});
|
||||
Ok(Self::Git { link, path, chk })
|
||||
}
|
||||
_ => Err(ParseImportError::InvalidPrefix),
|
||||
}
|
||||
|
@ -216,21 +294,15 @@ pub fn parse_all(threads: usize, root: &str) -> io::Result<Vec<Ast>> {
|
|||
enum ParseImportError {
|
||||
ExpectedPath,
|
||||
InvalidPrefix,
|
||||
UnexpectedParam,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ParseImportError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::ExpectedPath => write!(f, "expected path"),
|
||||
Self::InvalidPrefix => write!(
|
||||
f,
|
||||
"invalid prefix, expected one of rel, \
|
||||
Self::ExpectedPath => "expected path".fmt(f),
|
||||
Self::InvalidPrefix => "invalid prefix, expected one of rel, \
|
||||
git or none followed by colon"
|
||||
),
|
||||
Self::UnexpectedParam => {
|
||||
write!(f, "unexpected git param, expected branch, tag or rev")
|
||||
}
|
||||
.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -285,9 +357,9 @@ pub fn parse_all(threads: usize, root: &str) -> io::Result<Vec<Ast>> {
|
|||
}
|
||||
}
|
||||
|
||||
type Task = (FileId, PathBuf, Option<std::process::Command>);
|
||||
type Task = (u32, PathBuf, Option<std::process::Command>);
|
||||
|
||||
let seen = Mutex::new(HashMap::<PathBuf, FileId>::default());
|
||||
let seen = Mutex::new(HashMap::<PathBuf, u32>::default());
|
||||
let tasks = TaskQueue::<Task>::new(threads);
|
||||
let ast = Mutex::new(Vec::<io::Result<Ast>>::new());
|
||||
|
||||
|
@ -305,20 +377,13 @@ pub fn parse_all(threads: usize, root: &str) -> io::Result<Vec<Ast>> {
|
|||
}
|
||||
std::collections::hash_map::Entry::Vacant(entry) => {
|
||||
entry.insert(len as _);
|
||||
len as FileId
|
||||
len as u32
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let command = if !physiscal_path.exists() {
|
||||
let ImportPath::Git {
|
||||
link,
|
||||
branch,
|
||||
rev,
|
||||
tag,
|
||||
..
|
||||
} = path
|
||||
else {
|
||||
let ImportPath::Git { link, chk, .. } = path else {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::NotFound,
|
||||
format!("can't find file: {}", physiscal_path.display()),
|
||||
|
@ -328,13 +393,15 @@ pub fn parse_all(threads: usize, root: &str) -> io::Result<Vec<Ast>> {
|
|||
let root = PathBuf::from_iter([GIT_DEPS_DIR, preprocess_git(link)]);
|
||||
|
||||
let mut command = std::process::Command::new("git");
|
||||
command
|
||||
.args(["clone", "--depth", "1"])
|
||||
.args(branch.map(|b| ["--branch", b]).into_iter().flatten())
|
||||
.args(tag.map(|t| ["--tag", t]).into_iter().flatten())
|
||||
.args(rev.map(|r| ["--rev", r]).into_iter().flatten())
|
||||
.arg(link)
|
||||
.arg(root);
|
||||
command.args(["clone", "--depth", "1"]);
|
||||
if let Some(chk) = chk {
|
||||
command.args(match chk {
|
||||
Chk::Branch(b) => ["--branch", b],
|
||||
Chk::Tag(t) => ["--tag", t],
|
||||
Chk::Rev(r) => ["--rev", r],
|
||||
});
|
||||
}
|
||||
command.arg(link).arg(root);
|
||||
Some(command)
|
||||
} else {
|
||||
None
|
||||
|
@ -394,9 +461,7 @@ pub fn parse_all(threads: usize, root: &str) -> io::Result<Vec<Ast>> {
|
|||
.collect::<io::Result<Vec<_>>>()
|
||||
}
|
||||
|
||||
type HashMap<K, V> = std::collections::HashMap<K, V, FnvBuildHash>;
|
||||
|
||||
type FnvBuildHash = std::hash::BuildHasherDefault<FnvHasher>;
|
||||
type HashMap<K, V> = std::collections::HashMap<K, V, std::hash::BuildHasherDefault<FnvHasher>>;
|
||||
|
||||
struct FnvHasher(u64);
|
||||
|
||||
|
@ -421,6 +486,70 @@ impl Default for FnvHasher {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn run_test(
|
||||
name: &'static str,
|
||||
ident: &'static str,
|
||||
input: &'static str,
|
||||
test: fn(&'static str, &'static str, &mut String),
|
||||
) {
|
||||
use std::{io::Write, path::PathBuf};
|
||||
|
||||
let filter = std::env::var("PT_FILTER").unwrap_or_default();
|
||||
if !filter.is_empty() && !name.contains(&filter) {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut output = String::new();
|
||||
test(ident, input, &mut output);
|
||||
|
||||
let mut root = PathBuf::from(
|
||||
std::env::var("PT_TEST_ROOT")
|
||||
.unwrap_or(concat!(env!("CARGO_MANIFEST_DIR"), "/tests").to_string()),
|
||||
);
|
||||
root.push(
|
||||
name.replace("::", "_")
|
||||
.replace(concat!(env!("CARGO_PKG_NAME"), "_"), ""),
|
||||
);
|
||||
root.set_extension("txt");
|
||||
|
||||
let expected = std::fs::read_to_string(&root).unwrap_or_default();
|
||||
|
||||
if output == expected {
|
||||
return;
|
||||
}
|
||||
|
||||
if std::env::var("PT_UPDATE").is_ok() {
|
||||
std::fs::write(&root, output).unwrap();
|
||||
return;
|
||||
}
|
||||
|
||||
if !root.exists() {
|
||||
std::fs::create_dir_all(root.parent().unwrap()).unwrap();
|
||||
std::fs::write(&root, vec![]).unwrap();
|
||||
}
|
||||
|
||||
let mut proc = std::process::Command::new("diff")
|
||||
.arg("-u")
|
||||
.arg("--color")
|
||||
.arg(&root)
|
||||
.arg("-")
|
||||
.stdin(std::process::Stdio::piped())
|
||||
.stdout(std::process::Stdio::inherit())
|
||||
.spawn()
|
||||
.unwrap();
|
||||
|
||||
proc.stdin
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.write_all(output.as_bytes())
|
||||
.unwrap();
|
||||
|
||||
proc.wait().unwrap();
|
||||
|
||||
panic!("test failed");
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::sync::Arc;
|
||||
|
|
|
@ -1,48 +0,0 @@
|
|||
#![allow(unused_macros)]
|
||||
|
||||
#[derive(PartialOrd, PartialEq, Ord, Eq, Debug)]
|
||||
pub enum Level {
|
||||
Err,
|
||||
Wrn,
|
||||
Inf,
|
||||
Dbg,
|
||||
}
|
||||
|
||||
pub const LOG_LEVEL: Level = match option_env!("LOG_LEVEL") {
|
||||
Some(val) => match val.as_bytes()[0] {
|
||||
b'e' => Level::Err,
|
||||
b'w' => Level::Wrn,
|
||||
b'i' => Level::Inf,
|
||||
b'd' => Level::Dbg,
|
||||
_ => panic!("Invalid log level."),
|
||||
},
|
||||
None => {
|
||||
if cfg!(debug_assertions) {
|
||||
Level::Dbg
|
||||
} else {
|
||||
Level::Err
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
macro_rules! log {
|
||||
($level:expr, $fmt:literal $($expr:tt)*) => {
|
||||
if $level <= $crate::log::LOG_LEVEL {
|
||||
println!("{:?}: {}", $level, format_args!($fmt $($expr)*));
|
||||
}
|
||||
};
|
||||
|
||||
($level:expr, $($arg:expr),*) => {
|
||||
if $level <= $crate::log::LOG_LEVEL {
|
||||
$(println!("[{}{}{}][{:?}]: {} = {:?}", line!(), column!(), file!(), $level, stringify!($arg), $arg);)*
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! err { ($($arg:tt)*) => { $crate::log::log!($crate::log::Level::Err, $($arg)*) }; }
|
||||
macro_rules! wrn { ($($arg:tt)*) => { $crate::log::log!($crate::log::Level::Wrn, $($arg)*) }; }
|
||||
macro_rules! inf { ($($arg:tt)*) => { $crate::log::log!($crate::log::Level::Inf, $($arg)*) }; }
|
||||
macro_rules! dbg { ($($arg:tt)*) => { $crate::log::log!($crate::log::Level::Dbg, $($arg)*) }; }
|
||||
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use {dbg, err, inf, log, wrn};
|
|
@ -3,7 +3,7 @@ fn main() -> std::io::Result<()> {
|
|||
.nth(1)
|
||||
.unwrap_or_else(|| "main.hb".to_string());
|
||||
|
||||
let parsed = hblang::parse_all(1, &root)?;
|
||||
let parsed = hblang::parse_from_fs(1, &root)?;
|
||||
let mut codegen = hblang::codegen::Codegen::default();
|
||||
codegen.files = parsed;
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ use std::{
|
|||
};
|
||||
|
||||
use crate::{
|
||||
codegen::bt,
|
||||
codegen,
|
||||
ident::{self, Ident},
|
||||
lexer::{Lexer, LineMap, Token, TokenKind},
|
||||
log,
|
||||
|
@ -136,16 +136,36 @@ impl<'a, 'b> Parser<'a, 'b> {
|
|||
break;
|
||||
}
|
||||
|
||||
let checkpoint = self.token.start;
|
||||
let op = self.next().kind;
|
||||
|
||||
let op_ass = op.assign_op().map(|op| {
|
||||
// this abomination reparses the left side, so that the desubaring adheres to the
|
||||
// parser invariants.
|
||||
let source = self.lexer.slice(0..checkpoint as usize);
|
||||
let prev_lexer =
|
||||
std::mem::replace(&mut self.lexer, Lexer::restore(source, fold.pos()));
|
||||
let prev_token = std::mem::replace(&mut self.token, self.lexer.next());
|
||||
let clone = self.expr();
|
||||
self.lexer = prev_lexer;
|
||||
self.token = prev_token;
|
||||
|
||||
(op, clone)
|
||||
});
|
||||
|
||||
let right = self.unit_expr();
|
||||
let right = self.bin_expr(right, prec);
|
||||
let right = self.arena.alloc(right);
|
||||
let left = self.arena.alloc(fold);
|
||||
|
||||
if let Some(op) = op.assign_op() {
|
||||
if let Some((op, clone)) = op_ass {
|
||||
self.flag_idents(*left, idfl::MUTABLE);
|
||||
let right = Expr::BinOp { left, op, right };
|
||||
|
||||
let right = Expr::BinOp {
|
||||
left: self.arena.alloc(clone),
|
||||
op,
|
||||
right,
|
||||
};
|
||||
fold = Expr::BinOp {
|
||||
left,
|
||||
op: TokenKind::Assign,
|
||||
|
@ -163,10 +183,10 @@ impl<'a, 'b> Parser<'a, 'b> {
|
|||
}
|
||||
|
||||
fn resolve_ident(&mut self, token: Token, decl: bool) -> (Ident, u16) {
|
||||
let is_ct = self.token.kind == TokenKind::CtIdent;
|
||||
let is_ct = token.kind == TokenKind::CtIdent;
|
||||
let name = self.lexer.slice(token.range());
|
||||
|
||||
if let Some(builtin) = bt::from_str(name) {
|
||||
if let Some(builtin) = codegen::ty::from_str(name) {
|
||||
return (builtin, 0);
|
||||
}
|
||||
|
||||
|
@ -270,7 +290,12 @@ impl<'a, 'b> Parser<'a, 'b> {
|
|||
T::Ident | T::CtIdent => {
|
||||
let (id, index) = self.resolve_ident(token, self.token.kind == T::Decl);
|
||||
let name = self.move_str(token);
|
||||
E::Ident { name, id, index }
|
||||
E::Ident {
|
||||
pos: token.start,
|
||||
name,
|
||||
id,
|
||||
index,
|
||||
}
|
||||
}
|
||||
T::If => E::If {
|
||||
pos: token.start,
|
||||
|
@ -526,13 +551,14 @@ macro_rules! generate_expr {
|
|||
pub fn pos(&self) -> Pos {
|
||||
#[allow(unused_variables)]
|
||||
match self {
|
||||
$(Self::$variant { $($field),* } => generate_expr!(@first $(($field),)*).posi(self),)*
|
||||
$(Self::$variant { $($field),* } => generate_expr!(@first $(($field),)*).posi(),)*
|
||||
}
|
||||
}
|
||||
|
||||
pub fn used_bytes(&self) -> usize {
|
||||
match self {$(
|
||||
Self::$variant { $($field,)* } => {
|
||||
#[allow(clippy::size_of_ref)]
|
||||
let fields = [$(($field as *const _ as usize - self as *const _ as usize, std::mem::size_of_val($field)),)*];
|
||||
let (last, size) = fields.iter().copied().max().unwrap();
|
||||
last + size
|
||||
|
@ -572,6 +598,7 @@ generate_expr! {
|
|||
val: Option<&'a Self>,
|
||||
},
|
||||
Ident {
|
||||
pos: Pos,
|
||||
id: Ident,
|
||||
name: &'a str,
|
||||
index: u16,
|
||||
|
@ -636,21 +663,17 @@ generate_expr! {
|
|||
}
|
||||
|
||||
trait Poser {
|
||||
fn posi(self, expr: &Expr) -> Pos;
|
||||
fn posi(self) -> Pos;
|
||||
}
|
||||
|
||||
impl Poser for Pos {
|
||||
fn posi(self, expr: &Expr) -> Pos {
|
||||
if matches!(expr, Expr::Ident { .. }) {
|
||||
ident::pos(self)
|
||||
} else {
|
||||
fn posi(self) -> Pos {
|
||||
self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Poser for &Expr<'a> {
|
||||
fn posi(self, _: &Expr) -> Pos {
|
||||
fn posi(self) -> Pos {
|
||||
self.pos()
|
||||
}
|
||||
}
|
||||
|
@ -905,11 +928,11 @@ impl ExprRef {
|
|||
}
|
||||
|
||||
pub fn get<'a>(&self, from: &'a Ast) -> Option<&'a Expr<'a>> {
|
||||
ArenaChunk::contains(from.mem.base, self.0.as_ptr() as _).then_some(())?;
|
||||
// SAFETY: the pointer is or was a valid reference in the past, if it points within one of
|
||||
// arenas regions, it muts be walid, since arena does not give invalid pointers to its
|
||||
// allocations
|
||||
ArenaChunk::contains(from.mem.base, self.0.as_ptr() as _)
|
||||
.then(|| unsafe { { self.0 }.as_ref() })
|
||||
Some(unsafe { { self.0 }.as_ref() })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,64 +0,0 @@
|
|||
#![cfg(test)]
|
||||
|
||||
pub fn run_test(
|
||||
name: &'static str,
|
||||
ident: &'static str,
|
||||
input: &'static str,
|
||||
test: fn(&'static str, &'static str, &mut String),
|
||||
) {
|
||||
use std::{io::Write, path::PathBuf};
|
||||
|
||||
let filter = std::env::var("PT_FILTER").unwrap_or_default();
|
||||
if !filter.is_empty() && !name.contains(&filter) {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut output = String::new();
|
||||
test(ident, input, &mut output);
|
||||
|
||||
let mut root = PathBuf::from(
|
||||
std::env::var("PT_TEST_ROOT")
|
||||
.unwrap_or(concat!(env!("CARGO_MANIFEST_DIR"), "/tests").to_string()),
|
||||
);
|
||||
root.push(
|
||||
name.replace("::", "_")
|
||||
.replace(concat!(env!("CARGO_PKG_NAME"), "_"), ""),
|
||||
);
|
||||
root.set_extension("txt");
|
||||
|
||||
let expected = std::fs::read_to_string(&root).unwrap_or_default();
|
||||
|
||||
if output == expected {
|
||||
return;
|
||||
}
|
||||
|
||||
if std::env::var("PT_UPDATE").is_ok() {
|
||||
std::fs::write(&root, output).unwrap();
|
||||
return;
|
||||
}
|
||||
|
||||
if !root.exists() {
|
||||
std::fs::create_dir_all(root.parent().unwrap()).unwrap();
|
||||
std::fs::write(&root, vec![]).unwrap();
|
||||
}
|
||||
|
||||
let mut proc = std::process::Command::new("diff")
|
||||
.arg("-u")
|
||||
.arg("--color")
|
||||
.arg(&root)
|
||||
.arg("-")
|
||||
.stdin(std::process::Stdio::piped())
|
||||
.stdout(std::process::Stdio::inherit())
|
||||
.spawn()
|
||||
.unwrap();
|
||||
|
||||
proc.stdin
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.write_all(output.as_bytes())
|
||||
.unwrap();
|
||||
|
||||
proc.wait().unwrap();
|
||||
|
||||
panic!();
|
||||
}
|
|
@ -1,3 +1,3 @@
|
|||
code size: 471
|
||||
code size: 522
|
||||
ret: 512
|
||||
status: Ok(())
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
code size: 513
|
||||
code size: 525
|
||||
ret: 0
|
||||
status: Ok(())
|
||||
|
|
3
hblang/tests/codegen_tests_generic_functions.txt
Normal file
3
hblang/tests/codegen_tests_generic_functions.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
code size: 296
|
||||
ret: 0
|
||||
status: Ok(())
|
|
@ -1,3 +1,3 @@
|
|||
code size: 359
|
||||
code size: 1503
|
||||
ret: 69
|
||||
status: Ok(())
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
code size: 283
|
||||
code size: 277
|
||||
ret: 50
|
||||
status: Ok(())
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
code size: 265
|
||||
code size: 268
|
||||
ret: 55
|
||||
status: Ok(())
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
code size: 322
|
||||
code size: 330
|
||||
ret: 0
|
||||
status: Ok(())
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
code size: 778
|
||||
code size: 786
|
||||
ret: 10
|
||||
status: Ok(())
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
code size: 417
|
||||
code size: 460
|
||||
ret: 3
|
||||
status: Ok(())
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
code size: 110
|
||||
code size: 116
|
||||
ret: 0
|
||||
status: Ok(())
|
||||
|
|
|
@ -101,7 +101,7 @@ impl Memory for HostMemory {
|
|||
count: usize,
|
||||
) -> Result<(), StoreError> {
|
||||
debug_assert!(addr.get() != 0);
|
||||
debug_assert!(source != core::ptr::null());
|
||||
debug_assert!(!source.is_null());
|
||||
unsafe { core::ptr::copy(source, addr.get() as *mut u8, count) }
|
||||
Ok(())
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue