forked from AbleOS/holey-bytes
foobar
This commit is contained in:
parent
5555b9865a
commit
cac99cd34d
316
hblang/README.md
316
hblang/README.md
|
@ -47,22 +47,22 @@ main := fn(): int {
|
|||
#### arithmetic
|
||||
```hb
|
||||
main := fn(): int {
|
||||
return 10 - 20 / 2 + 4 * (2 + 2) - 4 * 4 + 1;
|
||||
return 10 - 20 / 2 + 4 * (2 + 2) - 4 * 4 + 1
|
||||
}
|
||||
```
|
||||
|
||||
#### functions
|
||||
```hb
|
||||
main := fn(): int {
|
||||
return add_one(10) + add_two(20);
|
||||
return add_one(10) + add_two(20)
|
||||
}
|
||||
|
||||
add_two := fn(x: int): int {
|
||||
return x + 2;
|
||||
return x + 2
|
||||
}
|
||||
|
||||
add_one := fn(x: int): int {
|
||||
return x + 1;
|
||||
return x + 1
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -71,12 +71,11 @@ add_one := fn(x: int): int {
|
|||
// commant is an item
|
||||
main := fn(): int {
|
||||
// comment is a statement
|
||||
foo(/* comment is an exprression /* if you are crazy */ */);
|
||||
return 0;
|
||||
foo(/* comment is an exprression /* if you are crazy */ */)
|
||||
return 0
|
||||
}
|
||||
|
||||
foo := fn(comment: void): void
|
||||
return /* comment evaluates to void */;
|
||||
foo := fn(comment: void): void return /* comment evaluates to void */
|
||||
|
||||
// comments might be formatted in the future
|
||||
```
|
||||
|
@ -84,14 +83,14 @@ foo := fn(comment: void): void
|
|||
#### if_statements
|
||||
```hb
|
||||
main := fn(): int {
|
||||
return fib(10);
|
||||
return fib(10)
|
||||
}
|
||||
|
||||
fib := fn(x: int): int {
|
||||
if x <= 2 {
|
||||
return 1;
|
||||
return 1
|
||||
} else {
|
||||
return fib(x - 1) + fib(x - 2);
|
||||
return fib(x - 1) + fib(x - 2)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -99,55 +98,55 @@ fib := fn(x: int): int {
|
|||
#### variables
|
||||
```hb
|
||||
main := fn(): int {
|
||||
ඞ := 1;
|
||||
b := 2;
|
||||
ඞ = ඞ + 1;
|
||||
return ඞ - b;
|
||||
ඞ := 1
|
||||
b := 2
|
||||
ඞ += 1
|
||||
return ඞ - b
|
||||
}
|
||||
```
|
||||
|
||||
#### loops
|
||||
```hb
|
||||
main := fn(): int {
|
||||
return fib(10);
|
||||
return fib(10)
|
||||
}
|
||||
|
||||
fib := fn(n: int): int {
|
||||
a := 0;
|
||||
b := 1;
|
||||
a := 0
|
||||
b := 1
|
||||
loop {
|
||||
if n == 0 break;
|
||||
c := a + b;
|
||||
a = b;
|
||||
b = c;
|
||||
n -= 1;
|
||||
if n == 0 break
|
||||
c := a + b
|
||||
a = b
|
||||
b = c
|
||||
n -= 1
|
||||
|
||||
stack_reclamation_edge_case := 0;
|
||||
stack_reclamation_edge_case := 0
|
||||
|
||||
continue;
|
||||
continue
|
||||
}
|
||||
return a;
|
||||
return a
|
||||
}
|
||||
```
|
||||
|
||||
#### pointers
|
||||
```hb
|
||||
main := fn(): int {
|
||||
a := 1;
|
||||
b := &a;
|
||||
modify(b);
|
||||
drop(a);
|
||||
stack_reclamation_edge_case := 0;
|
||||
return *b - 2;
|
||||
a := 1
|
||||
b := &a
|
||||
modify(b)
|
||||
drop(a)
|
||||
stack_reclamation_edge_case := 0
|
||||
return *b - 2
|
||||
}
|
||||
|
||||
modify := fn(a: ^int): void {
|
||||
*a = 2;
|
||||
return;
|
||||
*a = 2
|
||||
return
|
||||
}
|
||||
|
||||
drop := fn(a: int): void {
|
||||
return;
|
||||
return
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -164,21 +163,21 @@ Ty2 := struct {
|
|||
}
|
||||
|
||||
main := fn(): int {
|
||||
finst := Ty2.{ ty: Ty.{ a: 4, b: 1 }, c: 3 };
|
||||
inst := odher_pass(finst);
|
||||
finst := Ty2.{ty: Ty.{a: 4, b: 1}, c: 3}
|
||||
inst := odher_pass(finst)
|
||||
if inst.c == 3 {
|
||||
return pass(&inst.ty);
|
||||
return pass(&inst.ty)
|
||||
}
|
||||
return 0;
|
||||
return 0
|
||||
}
|
||||
|
||||
pass := fn(t: ^Ty): int {
|
||||
.{ a, b } := *t;
|
||||
return a - b;
|
||||
.{a, b} := *t
|
||||
return a - b
|
||||
}
|
||||
|
||||
odher_pass := fn(t: Ty2): Ty2 {
|
||||
return t;
|
||||
return t
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -195,51 +194,51 @@ Rect := struct {
|
|||
}
|
||||
|
||||
main := fn(): int {
|
||||
a := Point.(1, 2);
|
||||
b := Point.(3, 4);
|
||||
a := Point.(1, 2)
|
||||
b := Point.(3, 4)
|
||||
|
||||
d := Rect.(a + b, b - a);
|
||||
d2 := Rect.(Point.(0, 0) - b, a);
|
||||
d2 = d2 + d;
|
||||
d := Rect.(a + b, b - a)
|
||||
d2 := Rect.(Point.(0, 0) - b, a)
|
||||
d2 += d
|
||||
|
||||
c := d2.a + d2.b;
|
||||
return c.x + c.y;
|
||||
c := d2.a + d2.b
|
||||
return c.x + c.y
|
||||
}
|
||||
```
|
||||
|
||||
#### global_variables
|
||||
```hb
|
||||
global_var := 10;
|
||||
global_var := 10
|
||||
|
||||
complex_global_var := fib(global_var) - 5;
|
||||
complex_global_var := fib(global_var) - 5
|
||||
|
||||
fib := fn(n: int): int {
|
||||
if 2 > n {
|
||||
return n;
|
||||
return n
|
||||
}
|
||||
return fib(n - 1) + fib(n - 2);
|
||||
return fib(n - 1) + fib(n - 2)
|
||||
}
|
||||
|
||||
main := fn(): int {
|
||||
return complex_global_var;
|
||||
return complex_global_var
|
||||
}
|
||||
```
|
||||
note: values of global variables are evaluated at compile time
|
||||
|
||||
#### directives
|
||||
```hb
|
||||
foo := @use("foo.hb");
|
||||
foo := @use("foo.hb")
|
||||
|
||||
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(foo.Type);
|
||||
align_of_Type_in_bytes := @alignof(foo.Type);
|
||||
hardcoded_pointer := @as(^u8, @bitcast(10));
|
||||
ecall_that_returns_int := @eca(int, 1, foo.Type.(10, 20), 5, 6);
|
||||
return 0;
|
||||
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(foo.Type)
|
||||
align_of_Type_in_bytes := @alignof(foo.Type)
|
||||
hardcoded_pointer := @as(^u8, @bitcast(10))
|
||||
ecall_that_returns_int := @eca(int, 1, foo.Type.(10, 20), 5, 6)
|
||||
return 0
|
||||
}
|
||||
|
||||
// in module: foo.hb
|
||||
|
@ -262,65 +261,65 @@ Type := struct {
|
|||
#### c_strings
|
||||
```hb
|
||||
str_len := fn(str: ^u8): int {
|
||||
len := 0;
|
||||
len := 0
|
||||
loop if *str == 0 break else {
|
||||
len += 1;
|
||||
str += 1;
|
||||
len += 1
|
||||
str += 1
|
||||
}
|
||||
return len;
|
||||
return len
|
||||
}
|
||||
|
||||
main := fn(): int {
|
||||
// when string ends with '\0' its a C string and thus type is '^u8'
|
||||
some_str := "abඞ\n\r\t\{ff}\{fff0f0ff}\0";
|
||||
len := str_len(some_str);
|
||||
some_other_str := "fff\0";
|
||||
lep := str_len(some_other_str);
|
||||
return lep + len;
|
||||
some_str := "abඞ\n\r\t\{ff}\{fff0f0ff}\0"
|
||||
len := str_len(some_str)
|
||||
some_other_str := "fff\0"
|
||||
lep := str_len(some_other_str)
|
||||
return lep + len
|
||||
}
|
||||
```
|
||||
|
||||
#### struct_patterns
|
||||
```hb
|
||||
.{ fib, fib_iter, Fiber } := @use("fibs.hb");
|
||||
.{fib, fib_iter, Fiber} := @use("fibs.hb")
|
||||
|
||||
main := fn(): int {
|
||||
.{ a, b } := Fiber.{ a: 10, b: 10 };
|
||||
return fib(a) - fib_iter(b);
|
||||
.{a, b} := Fiber.{a: 10, b: 10}
|
||||
return fib(a) - fib_iter(b)
|
||||
}
|
||||
|
||||
// in module: fibs.hb
|
||||
|
||||
Fiber := struct { a: u8, b: u8 };
|
||||
Fiber := struct {a: u8, b: u8}
|
||||
|
||||
fib := fn(n: int): int if n < 2 {
|
||||
return n;
|
||||
return n
|
||||
} else {
|
||||
return fib(n - 1) + fib(n - 2);
|
||||
};
|
||||
return fib(n - 1) + fib(n - 2)
|
||||
}
|
||||
|
||||
fib_iter := fn(n: int): int {
|
||||
a := 0;
|
||||
b := 1;
|
||||
a := 0
|
||||
b := 1
|
||||
loop if n == 0 break else {
|
||||
c := a + b;
|
||||
a = b;
|
||||
b = c;
|
||||
n -= 1;
|
||||
c := a + b
|
||||
a = b
|
||||
b = c
|
||||
n -= 1
|
||||
}
|
||||
return a;
|
||||
return a
|
||||
}
|
||||
```
|
||||
|
||||
#### arrays
|
||||
```hb
|
||||
main := fn(): int {
|
||||
arr := [int].(1, 2, 4);
|
||||
return pass(&arr);
|
||||
arr := [int].(1, 2, 4)
|
||||
return pass(&arr)
|
||||
}
|
||||
|
||||
pass := fn(arr: ^[int; 3]): int {
|
||||
return arr[0] + arr[1] + arr[arr[1]];
|
||||
return arr[0] + arr[1] + arr[arr[1]]
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -329,13 +328,13 @@ pass := fn(arr: ^[int; 3]): int {
|
|||
#### comptime_pointers
|
||||
```hb
|
||||
main := fn(): int {
|
||||
$integer := 7;
|
||||
modify(&integer);
|
||||
return integer;
|
||||
$integer := 7
|
||||
modify(&integer)
|
||||
return integer
|
||||
}
|
||||
|
||||
modify := fn($num: ^int): void {
|
||||
$: *num = 0;
|
||||
$: *num = 0
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -344,116 +343,111 @@ modify := fn($num: ^int): void {
|
|||
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);
|
||||
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,
|
||||
len: uint,
|
||||
cap: uint,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
new := fn($Elem: type): Vec(Elem) return Vec(Elem).{ data: @bitcast(0), len: 0, cap: 0 };
|
||||
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);
|
||||
*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;
|
||||
vec.cap = 1
|
||||
} else {
|
||||
vec.cap *= 2;
|
||||
vec.cap *= 2
|
||||
}
|
||||
|
||||
new_alloc := @as(^Elem, @bitcast(malloc(vec.cap * @sizeof(Elem), @alignof(Elem))));
|
||||
if new_alloc == 0 return 0;
|
||||
new_alloc := @as(^Elem, @bitcast(malloc(vec.cap * @sizeof(Elem), @alignof(Elem))))
|
||||
if new_alloc == 0 return 0
|
||||
|
||||
src_cursor := vec.data
|
||||
dst_cursor := new_alloc
|
||||
end := vec.data + vec.len
|
||||
|
||||
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;
|
||||
*dst_cursor = *src_cursor
|
||||
src_cursor += 1
|
||||
dst_cursor += 1
|
||||
}
|
||||
|
||||
if vec.len != 0 {
|
||||
free(@bitcast(vec.data), vec.len * @sizeof(Elem), @alignof(Elem));
|
||||
free(@bitcast(vec.data), vec.len * @sizeof(Elem), @alignof(Elem))
|
||||
}
|
||||
vec.data = new_alloc;
|
||||
vec.data = new_alloc
|
||||
}
|
||||
|
||||
slot := vec.data + vec.len;
|
||||
*slot = value;
|
||||
vec.len += 1;
|
||||
return slot;
|
||||
*slot = value
|
||||
vec.len += 1
|
||||
return slot
|
||||
}
|
||||
|
||||
main := fn(): int {
|
||||
vec := new(int);
|
||||
push(int, &vec, 69);
|
||||
res := *vec.data;
|
||||
deinit(int, &vec);
|
||||
return res;
|
||||
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;
|
||||
add := fn($T: type, a: T, b: T): T return a + b
|
||||
|
||||
main := fn(): int {
|
||||
return add(u32, 2, 2) - add(int, 1, 3);
|
||||
return add(u32, 2, 2) - add(int, 1, 3)
|
||||
}
|
||||
```
|
||||
|
||||
#### fb_driver
|
||||
```hb
|
||||
arm_fb_ptr := fn(): int return 100;
|
||||
x86_fb_ptr := fn(): int return 100;
|
||||
|
||||
arm_fb_ptr := fn(): int return 100
|
||||
x86_fb_ptr := fn(): int return 100
|
||||
|
||||
check_platform := fn(): int {
|
||||
return x86_fb_ptr();
|
||||
return x86_fb_ptr()
|
||||
}
|
||||
|
||||
set_pixel := fn(x: int, y: int, width: int): int {
|
||||
pix_offset := y * width + x;
|
||||
|
||||
return 0;
|
||||
pix_offset := y * width + x
|
||||
return 0
|
||||
}
|
||||
|
||||
main := fn(): int {
|
||||
fb_ptr := check_platform();
|
||||
width := 100;
|
||||
height := 30;
|
||||
x:= 0;
|
||||
y:= 0;
|
||||
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;
|
||||
loop {
|
||||
if x <= height + 1 {
|
||||
set_pixel(x, y, width)
|
||||
x += 1
|
||||
} else {
|
||||
set_pixel(x, y, width)
|
||||
x = 0
|
||||
y += 1
|
||||
}
|
||||
if y == width {
|
||||
break
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -490,33 +484,33 @@ main := fn(): int {
|
|||
x: 0,
|
||||
y: 2,
|
||||
},
|
||||
};
|
||||
|
||||
soupan := 1;
|
||||
}
|
||||
|
||||
soupan := 1
|
||||
if *(&pixel.point.x + soupan) != 2 {
|
||||
return 0;
|
||||
return 0
|
||||
}
|
||||
|
||||
if *(&pixel.point.y - 1) != 0 {
|
||||
return 64;
|
||||
return 64
|
||||
}
|
||||
|
||||
return pixel.point.x + pixel.point.y + pixel.color.r
|
||||
+ pixel.color.g + pixel.color.b + pixel.color.a;
|
||||
+ pixel.color.g + pixel.color.b + pixel.color.a
|
||||
}
|
||||
```
|
||||
|
||||
#### struct_return_from_module_function
|
||||
```hb
|
||||
bar := @use("bar.hb");
|
||||
bar := @use("bar.hb")
|
||||
|
||||
main := fn(): int {
|
||||
return 7 - bar.foo().x - bar.foo().y - bar.foo().z;
|
||||
return 7 - bar.foo().x - bar.foo().y - bar.foo().z
|
||||
}
|
||||
|
||||
// in module: bar.hb
|
||||
|
||||
foo := fn(): struct { x: int, y: u32, z: u32 } {
|
||||
return .{ x: 3, y: 2, z: 2 };
|
||||
foo := fn(): struct {x: int, y: u32, z: u32} {
|
||||
return .{x: 3, y: 2, z: 2}
|
||||
}
|
||||
```
|
||||
|
|
|
@ -1382,7 +1382,13 @@ impl Codegen {
|
|||
} else {
|
||||
let values = captured
|
||||
.iter()
|
||||
.map(|&id| E::Ident { pos: 0, id, name: "booodab", index: u16::MAX })
|
||||
.map(|&id| E::Ident {
|
||||
pos: 0,
|
||||
is_ct: false,
|
||||
id,
|
||||
name: "booodab",
|
||||
index: u16::MAX,
|
||||
})
|
||||
.map(|expr| self.expr(&expr))
|
||||
.collect::<Option<Vec<_>>>()?;
|
||||
let values_size =
|
||||
|
@ -3221,7 +3227,9 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
let input = find_block(input, ident);
|
||||
let input = find_block(input, ident).trim();
|
||||
|
||||
parser::test::format(ident, input);
|
||||
|
||||
let mut module_map = Vec::new();
|
||||
let mut last_start = 0;
|
||||
|
@ -3245,7 +3253,7 @@ mod tests {
|
|||
let mut codegen = super::Codegen {
|
||||
files: module_map
|
||||
.iter()
|
||||
.map(|&(path, content)| parser::Ast::new(path, content, &loader))
|
||||
.map(|&(path, content)| parser::Ast::new(path, content, &loader, false))
|
||||
.collect(),
|
||||
..Default::default()
|
||||
};
|
||||
|
|
|
@ -261,6 +261,18 @@ impl<'a> Lexer<'a> {
|
|||
Some(c)
|
||||
}
|
||||
|
||||
pub fn last(&mut self) -> Token {
|
||||
let mut token = self.next();
|
||||
loop {
|
||||
let next = self.next();
|
||||
if next.kind == TokenKind::Eof {
|
||||
break;
|
||||
}
|
||||
token = next;
|
||||
}
|
||||
token
|
||||
}
|
||||
|
||||
pub fn next(&mut self) -> Token {
|
||||
use TokenKind as T;
|
||||
loop {
|
||||
|
|
|
@ -424,7 +424,7 @@ pub fn parse_from_fs(extra_threads: usize, root: &str) -> io::Result<Vec<Ast>> {
|
|||
let mut file = std::fs::File::open(path)?;
|
||||
file.read_to_end(buffer)?;
|
||||
let src = std::str::from_utf8(buffer).map_err(InvalidFileData)?;
|
||||
Ok(Ast::new(path, src, &loader))
|
||||
Ok(Ast::new(path, src, &loader, false))
|
||||
};
|
||||
|
||||
let thread = || {
|
||||
|
@ -543,6 +543,33 @@ pub struct Options {
|
|||
pub extra_threads: usize,
|
||||
}
|
||||
|
||||
fn format_to(
|
||||
ast: &parser::Ast,
|
||||
source: &str,
|
||||
out: &mut impl std::io::Write,
|
||||
) -> std::io::Result<()> {
|
||||
parser::with_fmt_source(source, || {
|
||||
for (i, expr) in ast.exprs().iter().enumerate() {
|
||||
write!(out, "{expr}")?;
|
||||
if let Some(expr) = ast.exprs().get(i + 1)
|
||||
&& let Some(rest) = source.get(expr.pos() as usize..)
|
||||
{
|
||||
if parser::insert_needed_semicolon(rest) {
|
||||
write!(out, ";")?;
|
||||
}
|
||||
if parser::preserve_newlines(&source[..expr.pos() as usize]) > 1 {
|
||||
writeln!(out)?;
|
||||
}
|
||||
}
|
||||
|
||||
if i + 1 != ast.exprs().len() {
|
||||
writeln!(out)?;
|
||||
}
|
||||
}
|
||||
std::io::Result::Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
pub fn run_compiler(
|
||||
root_file: &str,
|
||||
options: Options,
|
||||
|
@ -550,30 +577,10 @@ pub fn run_compiler(
|
|||
) -> io::Result<()> {
|
||||
let parsed = parse_from_fs(options.extra_threads, root_file)?;
|
||||
|
||||
fn format_to(ast: &parser::Ast, out: &mut impl std::io::Write) -> std::io::Result<()> {
|
||||
let source = std::fs::read_to_string(&*ast.path)?;
|
||||
parser::with_fmt_source(&source, || {
|
||||
for (i, expr) in ast.exprs().iter().enumerate() {
|
||||
write!(out, "{expr}")?;
|
||||
if let Some(expr) = ast.exprs().get(i + 1)
|
||||
&& let Some(rest) = source.get(expr.pos() as usize..)
|
||||
{
|
||||
if parser::insert_needed_semicolon(rest) {
|
||||
write!(out, ";")?;
|
||||
}
|
||||
for _ in 1..parser::preserve_newlines(&source[..expr.pos() as usize]) {
|
||||
writeln!(out)?;
|
||||
}
|
||||
}
|
||||
writeln!(out)?;
|
||||
}
|
||||
std::io::Result::Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn format_ast(ast: parser::Ast) -> std::io::Result<()> {
|
||||
let mut output = Vec::new();
|
||||
format_to(&ast, &mut output)?;
|
||||
let source = std::fs::read_to_string(&*ast.path)?;
|
||||
format_to(&ast, &source, &mut output)?;
|
||||
std::fs::write(&*ast.path, output)?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -583,7 +590,9 @@ pub fn run_compiler(
|
|||
format_ast(parsed)?;
|
||||
}
|
||||
} else if options.fmt_current {
|
||||
format_to(&parsed.into_iter().next().unwrap(), &mut std::io::stdout())?;
|
||||
let ast = parsed.into_iter().next().unwrap();
|
||||
let source = std::fs::read_to_string(&*ast.path)?;
|
||||
format_to(&ast, &source, &mut std::io::stdout())?;
|
||||
} else {
|
||||
let mut codegen = codegen::Codegen::default();
|
||||
codegen.files = parsed;
|
||||
|
|
|
@ -70,10 +70,16 @@ pub struct Parser<'a, 'b> {
|
|||
trailing_sep: bool,
|
||||
idents: Vec<ScopeIdent>,
|
||||
captured: Vec<Ident>,
|
||||
loose: bool,
|
||||
}
|
||||
|
||||
impl<'a, 'b> Parser<'a, 'b> {
|
||||
pub fn new(arena: &'b Arena<'a>, symbols: &'b mut Symbols, loader: Loader<'b>) -> Self {
|
||||
pub fn new(
|
||||
arena: &'b Arena<'a>,
|
||||
symbols: &'b mut Symbols,
|
||||
loader: Loader<'b>,
|
||||
loose: bool,
|
||||
) -> Self {
|
||||
let mut lexer = Lexer::new("");
|
||||
Self {
|
||||
loader,
|
||||
|
@ -86,6 +92,7 @@ impl<'a, 'b> Parser<'a, 'b> {
|
|||
trailing_sep: false,
|
||||
idents: Vec::new(),
|
||||
captured: Vec::new(),
|
||||
loose,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -213,7 +220,7 @@ impl<'a, 'b> Parser<'a, 'b> {
|
|||
}
|
||||
|
||||
let index = self.idents.binary_search_by_key(&id, |s| s.ident).expect("fck up");
|
||||
if std::mem::replace(&mut self.idents[index].declared, true) {
|
||||
if std::mem::replace(&mut self.idents[index].declared, true) && !self.loose {
|
||||
self.report_pos(
|
||||
pos,
|
||||
format_args!("redeclaration of identifier: {}", self.lexer.slice(ident::range(id))),
|
||||
|
@ -316,11 +323,12 @@ impl<'a, 'b> Parser<'a, 'b> {
|
|||
}
|
||||
token.start
|
||||
},
|
||||
trailing_comma: std::mem::take(&mut self.trailing_sep),
|
||||
},
|
||||
T::Ident | T::CtIdent => {
|
||||
let (id, index) = self.resolve_ident(token);
|
||||
let name = self.move_str(token);
|
||||
E::Ident { pos: token.start, name, id, index }
|
||||
E::Ident { pos: token.start, is_ct: token.kind == T::CtIdent, name, id, index }
|
||||
}
|
||||
T::If => E::If {
|
||||
pos: token.start,
|
||||
|
@ -348,7 +356,13 @@ impl<'a, 'b> Parser<'a, 'b> {
|
|||
let (id, index) = s.resolve_ident(name);
|
||||
s.declare(name.start, id, None);
|
||||
s.expect_advance(T::Colon);
|
||||
Arg { name: s.move_str(name), id, index, ty: s.expr() }
|
||||
Arg {
|
||||
name: s.move_str(name),
|
||||
is_ct: name.kind == T::CtIdent,
|
||||
id,
|
||||
index,
|
||||
ty: s.expr(),
|
||||
}
|
||||
})
|
||||
},
|
||||
ret: {
|
||||
|
@ -461,7 +475,7 @@ impl<'a, 'b> Parser<'a, 'b> {
|
|||
s.expr()
|
||||
} else {
|
||||
let (id, index) = s.resolve_ident(name_tok);
|
||||
Expr::Ident { pos: name_tok.start, id, name, index }
|
||||
Expr::Ident { pos: name_tok.start, is_ct: false, id, name, index }
|
||||
},
|
||||
}
|
||||
}),
|
||||
|
@ -566,6 +580,7 @@ pub fn find_symbol(symbols: &[Symbol], id: Ident) -> &Symbol {
|
|||
pub struct Arg<'a> {
|
||||
pub name: &'a str,
|
||||
pub id: Ident,
|
||||
pub is_ct: bool,
|
||||
pub index: IdentIndex,
|
||||
pub ty: Expr<'a>,
|
||||
}
|
||||
|
@ -592,19 +607,21 @@ macro_rules! generate_expr {
|
|||
}
|
||||
}
|
||||
|
||||
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
|
||||
},
|
||||
)*}
|
||||
}
|
||||
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
|
||||
},
|
||||
)*}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
(@filed_names $variant:ident $ident1:ident) => { Self::$variant { $ident1: a } };
|
||||
|
||||
(@first ($($first:tt)*), $($rest:tt)*) => { $($first)* };
|
||||
(@last ($($ign:tt)*), $($rest:tt)*) => { $($rest)* };
|
||||
(@last ($($last:tt)*),) => { $($last)* };
|
||||
|
@ -616,7 +633,7 @@ generate_expr! {
|
|||
pub enum Expr<'a> {
|
||||
Ct {
|
||||
pos: Pos,
|
||||
value: &'a Expr<'a>,
|
||||
value: &'a Self,
|
||||
},
|
||||
String {
|
||||
pos: Pos,
|
||||
|
@ -649,6 +666,7 @@ generate_expr! {
|
|||
},
|
||||
Ident {
|
||||
pos: Pos,
|
||||
is_ct: bool,
|
||||
id: Ident,
|
||||
name: &'a str,
|
||||
index: IdentIndex,
|
||||
|
@ -685,6 +703,7 @@ generate_expr! {
|
|||
pos: Pos,
|
||||
fields: &'a [(&'a str, Self)],
|
||||
captured: &'a [Ident],
|
||||
trailing_comma: bool,
|
||||
},
|
||||
Ctor {
|
||||
pos: Pos,
|
||||
|
@ -809,6 +828,7 @@ impl<'a> std::fmt::Display for Expr<'a> {
|
|||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
thread_local! {
|
||||
static INDENT: Cell<usize> = const { Cell::new(0) };
|
||||
static DISPLAY_BUFFER: Cell<String> = const { Cell::new(String::new()) };
|
||||
}
|
||||
|
||||
fn fmt_list<T>(
|
||||
|
@ -874,7 +894,7 @@ impl<'a> std::fmt::Display for Expr<'a> {
|
|||
Consecutive => Expr::UnOp { .. },
|
||||
}
|
||||
|
||||
let source = unsafe { &*FMT_SOURCE.with(|s| s.get()) };
|
||||
let source: &str = unsafe { &*FMT_SOURCE.with(|s| s.get()) };
|
||||
|
||||
match *self {
|
||||
Self::Ct { value, .. } => write!(f, "$: {}", value),
|
||||
|
@ -886,9 +906,11 @@ impl<'a> std::fmt::Display for Expr<'a> {
|
|||
write!(f, "@{name}(")?;
|
||||
fmt_list(f, false, ")", args, std::fmt::Display::fmt)
|
||||
}
|
||||
Self::Struct { fields, .. } => {
|
||||
Self::Struct { fields, trailing_comma, .. } => {
|
||||
write!(f, "struct {{")?;
|
||||
fmt_list(f, true, "}", fields, |(name, val), f| write!(f, "{name}: {val}",))
|
||||
fmt_list(f, trailing_comma, "}", fields, |(name, val), f| {
|
||||
write!(f, "{name}: {val}",)
|
||||
})
|
||||
}
|
||||
Self::Ctor { ty, fields, trailing_comma, .. } => {
|
||||
if let Some(ty) = ty {
|
||||
|
@ -912,8 +934,8 @@ impl<'a> std::fmt::Display for Expr<'a> {
|
|||
fmt_list(f, trailing_comma, ")", fields, std::fmt::Display::fmt)
|
||||
}
|
||||
Self::Slice { item, size, .. } => match size {
|
||||
Some(size) => write!(f, "[{size}]{item}"),
|
||||
None => write!(f, "[]{item}"),
|
||||
Some(size) => write!(f, "[{item}; {size}]"),
|
||||
None => write!(f, "[{item}]"),
|
||||
},
|
||||
Self::Index { base, index } => write!(f, "{base}[{index}]"),
|
||||
Self::UnOp { op, val, .. } => write!(f, "{op}{}", Unary(val)),
|
||||
|
@ -929,7 +951,12 @@ impl<'a> std::fmt::Display for Expr<'a> {
|
|||
Self::Loop { body, .. } => write!(f, "loop {body}"),
|
||||
Self::Closure { ret, body, args, .. } => {
|
||||
write!(f, "fn(")?;
|
||||
fmt_list(f, false, "", args, |arg, f| write!(f, "{}: {}", arg.name, arg.ty))?;
|
||||
fmt_list(f, false, "", args, |arg, f| {
|
||||
if arg.is_ct {
|
||||
write!(f, "$")?;
|
||||
}
|
||||
write!(f, "{}: {}", arg.name, arg.ty)
|
||||
})?;
|
||||
write!(f, "): {ret} {body}")?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -939,7 +966,8 @@ impl<'a> std::fmt::Display for Expr<'a> {
|
|||
}
|
||||
Self::Return { val: Some(val), .. } => write!(f, "return {val}"),
|
||||
Self::Return { val: None, .. } => write!(f, "return"),
|
||||
Self::Ident { name, .. } => write!(f, "{name}"),
|
||||
Self::Ident { name, is_ct: true, .. } => write!(f, "${name}"),
|
||||
Self::Ident { name, is_ct: false, .. } => write!(f, "{name}"),
|
||||
Self::Block { stmts, .. } => {
|
||||
write!(f, "{{")?;
|
||||
writeln!(f)?;
|
||||
|
@ -956,7 +984,7 @@ impl<'a> std::fmt::Display for Expr<'a> {
|
|||
if insert_needed_semicolon(rest) {
|
||||
write!(f, ";")?;
|
||||
}
|
||||
for _ in 1..preserve_newlines(&source[..expr.pos() as usize]) {
|
||||
if preserve_newlines(&source[..expr.pos() as usize]) > 1 {
|
||||
writeln!(f)?;
|
||||
}
|
||||
}
|
||||
|
@ -974,13 +1002,25 @@ impl<'a> std::fmt::Display for Expr<'a> {
|
|||
Self::Number { value, .. } => write!(f, "{value}"),
|
||||
Self::Bool { value, .. } => write!(f, "{value}"),
|
||||
Self::BinOp {
|
||||
left: left @ Self::Ident { id, .. },
|
||||
left,
|
||||
op: TokenKind::Assign,
|
||||
right: Self::BinOp { left: Self::Ident { id: oid, .. }, op, right },
|
||||
} if id == oid => {
|
||||
right: Self::BinOp { left: lleft, op, right },
|
||||
} if DISPLAY_BUFFER.with(|cb| {
|
||||
use std::fmt::Write;
|
||||
let mut b = cb.take();
|
||||
write!(b, "{lleft}").unwrap();
|
||||
let len = b.len();
|
||||
write!(b, "{left}").unwrap();
|
||||
let (lleft, left) = b.split_at(len);
|
||||
let res = lleft == left;
|
||||
b.clear();
|
||||
cb.set(b);
|
||||
res
|
||||
}) =>
|
||||
{
|
||||
write!(f, "{left} {op}= {right}")
|
||||
}
|
||||
Self::BinOp { left, right, op } => {
|
||||
Self::BinOp { right, op, left } => {
|
||||
let display_branch = |f: &mut std::fmt::Formatter, expr: &Self| {
|
||||
if let Self::BinOp { op: lop, .. } = expr
|
||||
&& op.precedence() > lop.precedence()
|
||||
|
@ -992,7 +1032,24 @@ impl<'a> std::fmt::Display for Expr<'a> {
|
|||
};
|
||||
|
||||
display_branch(f, left)?;
|
||||
write!(f, " {op} ")?;
|
||||
if let Some(mut prev) = source.get(..right.pos() as usize) {
|
||||
prev = prev.trim_end();
|
||||
let estimate_bound =
|
||||
prev.rfind(|c: char| c.is_ascii_whitespace()).map_or(prev.len(), |i| i + 1);
|
||||
let exact_bound = lexer::Lexer::new(&prev[estimate_bound..]).last().start;
|
||||
prev = &prev[..exact_bound as usize + estimate_bound];
|
||||
if preserve_newlines(prev) > 0 {
|
||||
writeln!(f)?;
|
||||
for _ in 0..INDENT.with(|i| i.get()) + 1 {
|
||||
write!(f, "\t")?;
|
||||
}
|
||||
write!(f, "{op} ")?;
|
||||
} else {
|
||||
write!(f, " {op} ")?;
|
||||
}
|
||||
} else {
|
||||
write!(f, " {op} ")?;
|
||||
}
|
||||
display_branch(f, right)
|
||||
}
|
||||
}
|
||||
|
@ -1000,8 +1057,7 @@ impl<'a> std::fmt::Display for Expr<'a> {
|
|||
}
|
||||
|
||||
pub fn preserve_newlines(source: &str) -> usize {
|
||||
let trailing_whitespace = &source[source.trim_end().len()..];
|
||||
trailing_whitespace.chars().filter(|&c| c == '\n').count().min(2)
|
||||
source[source.trim_end().len()..].chars().filter(|&c| c == '\n').count()
|
||||
}
|
||||
|
||||
pub fn insert_needed_semicolon(source: &str) -> bool {
|
||||
|
@ -1028,10 +1084,10 @@ impl AstInner<[Symbol]> {
|
|||
.0
|
||||
}
|
||||
|
||||
fn new(content: &str, path: &str, loader: Loader) -> NonNull<Self> {
|
||||
fn new(content: &str, path: &str, loader: Loader, loose: bool) -> NonNull<Self> {
|
||||
let arena = Arena::default();
|
||||
let mut syms = Vec::new();
|
||||
let mut parser = Parser::new(&arena, &mut syms, loader);
|
||||
let mut parser = Parser::new(&arena, &mut syms, loader, loose);
|
||||
let exprs = parser.file(content, path) as *const [Expr<'static>];
|
||||
|
||||
syms.sort_unstable_by_key(|s| s.name);
|
||||
|
@ -1063,8 +1119,8 @@ impl AstInner<[Symbol]> {
|
|||
pub struct Ast(NonNull<AstInner<[Symbol]>>);
|
||||
|
||||
impl Ast {
|
||||
pub fn new(path: &str, content: &str, loader: Loader) -> Self {
|
||||
Self(AstInner::new(content, path, loader))
|
||||
pub fn new(path: &str, content: &str, loader: Loader, loose: bool) -> Self {
|
||||
Self(AstInner::new(content, path, loader, loose))
|
||||
}
|
||||
|
||||
pub fn exprs(&self) -> &[Expr] {
|
||||
|
@ -1094,7 +1150,7 @@ impl std::fmt::Display for Ast {
|
|||
|
||||
impl Default for Ast {
|
||||
fn default() -> Self {
|
||||
Self(AstInner::new("", "", &no_loader))
|
||||
Self(AstInner::new("", "", &no_loader, false))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1268,16 +1324,11 @@ impl Drop for ArenaChunk {
|
|||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
fn format(ident: &str, input: &str) {
|
||||
let ast = super::Ast::new(ident, input, &super::no_loader);
|
||||
let mut output = String::new();
|
||||
super::with_fmt_source(input, || {
|
||||
for expr in ast.exprs() {
|
||||
use std::fmt::Write;
|
||||
writeln!(output, "{expr}").unwrap();
|
||||
}
|
||||
});
|
||||
pub mod test {
|
||||
pub fn format(ident: &str, input: &str) {
|
||||
let ast = super::Ast::new(ident, input, &|_, _| Ok(0), true);
|
||||
let mut output = Vec::new();
|
||||
crate::format_to(&ast, input, &mut output).unwrap();
|
||||
|
||||
let input_path = format!("formatter_{ident}.expected");
|
||||
let output_path = format!("formatter_{ident}.actual");
|
||||
|
@ -1308,13 +1359,13 @@ mod test {
|
|||
|
||||
test! {
|
||||
comments => "// comment\n// comment\n\n// comment\n\n\
|
||||
/* comment */\n/* comment */\n\n/* comment */\n";
|
||||
some_ordinary_code => "loft := fn(): int return loft(1, 2, 3);\n";
|
||||
/* comment */\n/* comment */\n\n/* comment */";
|
||||
some_ordinary_code => "loft := fn(): int return loft(1, 2, 3)";
|
||||
some_arg_per_line_code => "loft := fn(): int return loft(\
|
||||
\n\t1,\n\t2,\n\t3,\n);\n";
|
||||
some_ordinary_struct => "loft := fn(): int return loft.{a: 1, b: 2};\n";
|
||||
\n\t1,\n\t2,\n\t3,\n)";
|
||||
some_ordinary_struct => "loft := fn(): int return loft.{a: 1, b: 2}";
|
||||
some_ordinary_fild_per_lin_struct => "loft := fn(): int return loft.{\
|
||||
\n\ta: 1,\n\tb: 2,\n};\n";
|
||||
code_block => "loft := fn(): int {\n\tloft();\n\treturn 1;\n}\n";
|
||||
\n\ta: 1,\n\tb: 2,\n}";
|
||||
code_block => "loft := fn(): int {\n\tloft()\n\treturn 1\n}";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
code size: 1503
|
||||
code size: 1504
|
||||
ret: 69
|
||||
status: Ok(())
|
||||
|
|
Loading…
Reference in a new issue