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
|
#### generic_types
|
||||||
```hb
|
```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 {
|
Vec := fn($Elem: type): type {
|
||||||
return struct {
|
return struct {
|
||||||
data: ^Elem,
|
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 {
|
main := fn(): int {
|
||||||
i := 69;
|
vec := new(int);
|
||||||
vec := Vec(int).{
|
push(int, &vec, 69);
|
||||||
data: &i,
|
res := *vec.data;
|
||||||
len: 1,
|
deinit(int, &vec);
|
||||||
cap: 1,
|
return res;
|
||||||
};
|
}
|
||||||
return *vec.data;
|
```
|
||||||
|
|
||||||
|
#### 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();
|
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_max_size(&mut generated)?;
|
||||||
gen_encodes(&mut generated)?;
|
gen_encodes(&mut generated)?;
|
||||||
gen_structs(&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> {
|
impl<'a> Lexer<'a> {
|
||||||
pub fn new(input: &'a str) -> Self {
|
pub fn new(input: &'a str) -> Self {
|
||||||
|
Self::restore(input, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn restore(input: &'a str, pos: u32) -> Self {
|
||||||
Self {
|
Self {
|
||||||
pos: 0,
|
pos,
|
||||||
bytes: input.as_bytes(),
|
bytes: input.as_bytes(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -317,13 +321,17 @@ impl LineMap {
|
||||||
|
|
||||||
let mut iter = self.lines.iter().copied();
|
let mut iter = self.lines.iter().copied();
|
||||||
|
|
||||||
while let Some(mut len) = iter.next() {
|
loop {
|
||||||
let mut acc = 0;
|
let mut acc = 0;
|
||||||
while len & 0x80 != 0 {
|
let mut idx = 0;
|
||||||
acc = (acc << 7) | (len & 0x7F) as u32;
|
loop {
|
||||||
len = iter.next().unwrap();
|
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 {
|
if pos < acc {
|
||||||
break;
|
break;
|
||||||
|
@ -372,7 +380,7 @@ impl LineMap {
|
||||||
let idx = mask.trailing_zeros() as usize + i * 16 + start.len();
|
let idx = mask.trailing_zeros() as usize + i * 16 + start.len();
|
||||||
let mut len = idx - last_nl + 1;
|
let mut len = idx - last_nl + 1;
|
||||||
while len >= 0x80 {
|
while len >= 0x80 {
|
||||||
lines.push((0x80 | (len & 0x7F)) as u8);
|
lines.push(0x80 | (len & 0x7F) as u8);
|
||||||
len >>= 7;
|
len >>= 7;
|
||||||
}
|
}
|
||||||
lines.push(len as u8);
|
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(vec_pop_if)]
|
||||||
|
#![feature(new_uninit)]
|
||||||
|
#![feature(anonymous_lifetime_in_impl_trait)]
|
||||||
|
#![feature(inline_const_pat)]
|
||||||
#![feature(pattern)]
|
#![feature(pattern)]
|
||||||
|
#![feature(never_type)]
|
||||||
|
#![feature(unwrap_infallible)]
|
||||||
#![feature(if_let_guard)]
|
#![feature(if_let_guard)]
|
||||||
#![feature(slice_partition_dedup)]
|
#![feature(slice_partition_dedup)]
|
||||||
#![feature(noop_waker)]
|
#![feature(noop_waker)]
|
||||||
|
@ -10,6 +15,7 @@
|
||||||
#![feature(ptr_metadata)]
|
#![feature(ptr_metadata)]
|
||||||
#![feature(const_mut_refs)]
|
#![feature(const_mut_refs)]
|
||||||
#![feature(slice_ptr_get)]
|
#![feature(slice_ptr_get)]
|
||||||
|
#![allow(clippy::format_collect)]
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
collections::VecDeque,
|
collections::VecDeque,
|
||||||
|
@ -20,26 +26,100 @@ use std::{
|
||||||
|
|
||||||
use parser::Ast;
|
use parser::Ast;
|
||||||
|
|
||||||
use crate::parser::FileId;
|
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! run_tests {
|
macro_rules! run_tests {
|
||||||
($runner:path: $($name:ident => $input:expr;)*) => {$(
|
($runner:path: $($name:ident => $input:expr;)*) => {$(
|
||||||
#[test]
|
#[test]
|
||||||
fn $name() {
|
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;
|
pub mod codegen;
|
||||||
mod ident;
|
pub mod parser;
|
||||||
|
|
||||||
mod instrs;
|
mod instrs;
|
||||||
mod lexer;
|
mod lexer;
|
||||||
mod log;
|
|
||||||
pub mod parser;
|
mod ident {
|
||||||
mod tests;
|
pub type Ident = u32;
|
||||||
mod typechk;
|
|
||||||
|
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]
|
#[inline]
|
||||||
unsafe fn encode<T>(instr: T) -> (usize, [u8; instrs::MAX_SIZE]) {
|
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";
|
const GIT_DEPS_DIR: &str = "git-deps";
|
||||||
|
|
||||||
|
enum Chk<'a> {
|
||||||
|
Branch(&'a str),
|
||||||
|
Rev(&'a str),
|
||||||
|
Tag(&'a str),
|
||||||
|
}
|
||||||
|
|
||||||
enum ImportPath<'a> {
|
enum ImportPath<'a> {
|
||||||
Root {
|
Root {
|
||||||
path: &'a str,
|
path: &'a str,
|
||||||
|
@ -152,9 +238,7 @@ pub fn parse_all(threads: usize, root: &str) -> io::Result<Vec<Ast>> {
|
||||||
Git {
|
Git {
|
||||||
link: &'a str,
|
link: &'a str,
|
||||||
path: &'a str,
|
path: &'a str,
|
||||||
branch: Option<&'a str>,
|
chk: Option<Chk<'a>>,
|
||||||
tag: Option<&'a str>,
|
|
||||||
rev: Option<&'a str>,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,22 +255,16 @@ pub fn parse_all(threads: usize, root: &str) -> io::Result<Vec<Ast>> {
|
||||||
let (link, path) =
|
let (link, path) =
|
||||||
path.split_once(':').ok_or(ParseImportError::ExpectedPath)?;
|
path.split_once(':').ok_or(ParseImportError::ExpectedPath)?;
|
||||||
let (link, params) = link.split_once('?').unwrap_or((link, ""));
|
let (link, params) = link.split_once('?').unwrap_or((link, ""));
|
||||||
let [mut branch, mut tag, mut rev] = [None; 3];
|
let chk = params
|
||||||
for (key, value) in params.split('&').filter_map(|s| s.split_once('=')) {
|
.split('&')
|
||||||
match key {
|
.filter_map(|s| s.split_once('='))
|
||||||
"branch" => branch = Some(value),
|
.find_map(|(key, value)| match key {
|
||||||
"tag" => tag = Some(value),
|
"branch" => Some(Chk::Branch(value)),
|
||||||
"rev" => rev = Some(value),
|
"rev" => Some(Chk::Rev(value)),
|
||||||
_ => return Err(ParseImportError::UnexpectedParam),
|
"tag" => Some(Chk::Tag(value)),
|
||||||
}
|
_ => None,
|
||||||
}
|
});
|
||||||
Ok(Self::Git {
|
Ok(Self::Git { link, path, chk })
|
||||||
link,
|
|
||||||
path,
|
|
||||||
branch,
|
|
||||||
tag,
|
|
||||||
rev,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
_ => Err(ParseImportError::InvalidPrefix),
|
_ => Err(ParseImportError::InvalidPrefix),
|
||||||
}
|
}
|
||||||
|
@ -216,21 +294,15 @@ pub fn parse_all(threads: usize, root: &str) -> io::Result<Vec<Ast>> {
|
||||||
enum ParseImportError {
|
enum ParseImportError {
|
||||||
ExpectedPath,
|
ExpectedPath,
|
||||||
InvalidPrefix,
|
InvalidPrefix,
|
||||||
UnexpectedParam,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for ParseImportError {
|
impl std::fmt::Display for ParseImportError {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::ExpectedPath => write!(f, "expected path"),
|
Self::ExpectedPath => "expected path".fmt(f),
|
||||||
Self::InvalidPrefix => write!(
|
Self::InvalidPrefix => "invalid prefix, expected one of rel, \
|
||||||
f,
|
|
||||||
"invalid prefix, expected one of rel, \
|
|
||||||
git or none followed by colon"
|
git or none followed by colon"
|
||||||
),
|
.fmt(f),
|
||||||
Self::UnexpectedParam => {
|
|
||||||
write!(f, "unexpected git param, expected branch, tag or rev")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 tasks = TaskQueue::<Task>::new(threads);
|
||||||
let ast = Mutex::new(Vec::<io::Result<Ast>>::new());
|
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) => {
|
std::collections::hash_map::Entry::Vacant(entry) => {
|
||||||
entry.insert(len as _);
|
entry.insert(len as _);
|
||||||
len as FileId
|
len as u32
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let command = if !physiscal_path.exists() {
|
let command = if !physiscal_path.exists() {
|
||||||
let ImportPath::Git {
|
let ImportPath::Git { link, chk, .. } = path else {
|
||||||
link,
|
|
||||||
branch,
|
|
||||||
rev,
|
|
||||||
tag,
|
|
||||||
..
|
|
||||||
} = path
|
|
||||||
else {
|
|
||||||
return Err(io::Error::new(
|
return Err(io::Error::new(
|
||||||
io::ErrorKind::NotFound,
|
io::ErrorKind::NotFound,
|
||||||
format!("can't find file: {}", physiscal_path.display()),
|
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 root = PathBuf::from_iter([GIT_DEPS_DIR, preprocess_git(link)]);
|
||||||
|
|
||||||
let mut command = std::process::Command::new("git");
|
let mut command = std::process::Command::new("git");
|
||||||
command
|
command.args(["clone", "--depth", "1"]);
|
||||||
.args(["clone", "--depth", "1"])
|
if let Some(chk) = chk {
|
||||||
.args(branch.map(|b| ["--branch", b]).into_iter().flatten())
|
command.args(match chk {
|
||||||
.args(tag.map(|t| ["--tag", t]).into_iter().flatten())
|
Chk::Branch(b) => ["--branch", b],
|
||||||
.args(rev.map(|r| ["--rev", r]).into_iter().flatten())
|
Chk::Tag(t) => ["--tag", t],
|
||||||
.arg(link)
|
Chk::Rev(r) => ["--rev", r],
|
||||||
.arg(root);
|
});
|
||||||
|
}
|
||||||
|
command.arg(link).arg(root);
|
||||||
Some(command)
|
Some(command)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -394,9 +461,7 @@ pub fn parse_all(threads: usize, root: &str) -> io::Result<Vec<Ast>> {
|
||||||
.collect::<io::Result<Vec<_>>>()
|
.collect::<io::Result<Vec<_>>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
type HashMap<K, V> = std::collections::HashMap<K, V, FnvBuildHash>;
|
type HashMap<K, V> = std::collections::HashMap<K, V, std::hash::BuildHasherDefault<FnvHasher>>;
|
||||||
|
|
||||||
type FnvBuildHash = std::hash::BuildHasherDefault<FnvHasher>;
|
|
||||||
|
|
||||||
struct FnvHasher(u64);
|
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)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use std::sync::Arc;
|
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)
|
.nth(1)
|
||||||
.unwrap_or_else(|| "main.hb".to_string());
|
.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();
|
let mut codegen = hblang::codegen::Codegen::default();
|
||||||
codegen.files = parsed;
|
codegen.files = parsed;
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
codegen::bt,
|
codegen,
|
||||||
ident::{self, Ident},
|
ident::{self, Ident},
|
||||||
lexer::{Lexer, LineMap, Token, TokenKind},
|
lexer::{Lexer, LineMap, Token, TokenKind},
|
||||||
log,
|
log,
|
||||||
|
@ -136,16 +136,36 @@ impl<'a, 'b> Parser<'a, 'b> {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let checkpoint = self.token.start;
|
||||||
let op = self.next().kind;
|
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.unit_expr();
|
||||||
let right = self.bin_expr(right, prec);
|
let right = self.bin_expr(right, prec);
|
||||||
let right = self.arena.alloc(right);
|
let right = self.arena.alloc(right);
|
||||||
let left = self.arena.alloc(fold);
|
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);
|
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 {
|
fold = Expr::BinOp {
|
||||||
left,
|
left,
|
||||||
op: TokenKind::Assign,
|
op: TokenKind::Assign,
|
||||||
|
@ -163,10 +183,10 @@ impl<'a, 'b> Parser<'a, 'b> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_ident(&mut self, token: Token, decl: bool) -> (Ident, u16) {
|
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());
|
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);
|
return (builtin, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -270,7 +290,12 @@ impl<'a, 'b> Parser<'a, 'b> {
|
||||||
T::Ident | T::CtIdent => {
|
T::Ident | T::CtIdent => {
|
||||||
let (id, index) = self.resolve_ident(token, self.token.kind == T::Decl);
|
let (id, index) = self.resolve_ident(token, self.token.kind == T::Decl);
|
||||||
let name = self.move_str(token);
|
let name = self.move_str(token);
|
||||||
E::Ident { name, id, index }
|
E::Ident {
|
||||||
|
pos: token.start,
|
||||||
|
name,
|
||||||
|
id,
|
||||||
|
index,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
T::If => E::If {
|
T::If => E::If {
|
||||||
pos: token.start,
|
pos: token.start,
|
||||||
|
@ -526,13 +551,14 @@ macro_rules! generate_expr {
|
||||||
pub fn pos(&self) -> Pos {
|
pub fn pos(&self) -> Pos {
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
match self {
|
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 {
|
pub fn used_bytes(&self) -> usize {
|
||||||
match self {$(
|
match self {$(
|
||||||
Self::$variant { $($field,)* } => {
|
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 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();
|
let (last, size) = fields.iter().copied().max().unwrap();
|
||||||
last + size
|
last + size
|
||||||
|
@ -572,6 +598,7 @@ generate_expr! {
|
||||||
val: Option<&'a Self>,
|
val: Option<&'a Self>,
|
||||||
},
|
},
|
||||||
Ident {
|
Ident {
|
||||||
|
pos: Pos,
|
||||||
id: Ident,
|
id: Ident,
|
||||||
name: &'a str,
|
name: &'a str,
|
||||||
index: u16,
|
index: u16,
|
||||||
|
@ -636,21 +663,17 @@ generate_expr! {
|
||||||
}
|
}
|
||||||
|
|
||||||
trait Poser {
|
trait Poser {
|
||||||
fn posi(self, expr: &Expr) -> Pos;
|
fn posi(self) -> Pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Poser for Pos {
|
impl Poser for Pos {
|
||||||
fn posi(self, expr: &Expr) -> Pos {
|
fn posi(self) -> Pos {
|
||||||
if matches!(expr, Expr::Ident { .. }) {
|
|
||||||
ident::pos(self)
|
|
||||||
} else {
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Poser for &Expr<'a> {
|
impl<'a> Poser for &Expr<'a> {
|
||||||
fn posi(self, _: &Expr) -> Pos {
|
fn posi(self) -> Pos {
|
||||||
self.pos()
|
self.pos()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -905,11 +928,11 @@ impl ExprRef {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get<'a>(&self, from: &'a Ast) -> Option<&'a Expr<'a>> {
|
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
|
// 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
|
// arenas regions, it muts be walid, since arena does not give invalid pointers to its
|
||||||
// allocations
|
// allocations
|
||||||
ArenaChunk::contains(from.mem.base, self.0.as_ptr() as _)
|
Some(unsafe { { self.0 }.as_ref() })
|
||||||
.then(|| 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
|
ret: 512
|
||||||
status: Ok(())
|
status: Ok(())
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
code size: 513
|
code size: 525
|
||||||
ret: 0
|
ret: 0
|
||||||
status: Ok(())
|
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
|
ret: 69
|
||||||
status: Ok(())
|
status: Ok(())
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
code size: 283
|
code size: 277
|
||||||
ret: 50
|
ret: 50
|
||||||
status: Ok(())
|
status: Ok(())
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
code size: 265
|
code size: 268
|
||||||
ret: 55
|
ret: 55
|
||||||
status: Ok(())
|
status: Ok(())
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
code size: 322
|
code size: 330
|
||||||
ret: 0
|
ret: 0
|
||||||
status: Ok(())
|
status: Ok(())
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
code size: 778
|
code size: 786
|
||||||
ret: 10
|
ret: 10
|
||||||
status: Ok(())
|
status: Ok(())
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
code size: 417
|
code size: 460
|
||||||
ret: 3
|
ret: 3
|
||||||
status: Ok(())
|
status: Ok(())
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
code size: 110
|
code size: 116
|
||||||
ret: 0
|
ret: 0
|
||||||
status: Ok(())
|
status: Ok(())
|
||||||
|
|
|
@ -101,7 +101,7 @@ impl Memory for HostMemory {
|
||||||
count: usize,
|
count: usize,
|
||||||
) -> Result<(), StoreError> {
|
) -> Result<(), StoreError> {
|
||||||
debug_assert!(addr.get() != 0);
|
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) }
|
unsafe { core::ptr::copy(source, addr.get() as *mut u8, count) }
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue