This commit is contained in:
mlokr 2024-07-19 21:04:22 +02:00
parent 5555b9865a
commit cac99cd34d
No known key found for this signature in database
GPG key ID: DEA147DDEE644993
6 changed files with 315 additions and 241 deletions

View file

@ -47,22 +47,22 @@ main := fn(): int {
#### arithmetic #### arithmetic
```hb ```hb
main := fn(): int { main := fn(): int {
return 10 - 20 / 2 + 4 * (2 + 2) - 4 * 4 + 1; return 10 - 20 / 2 + 4 * (2 + 2) - 4 * 4 + 1
} }
``` ```
#### functions #### functions
```hb ```hb
main := fn(): int { main := fn(): int {
return add_one(10) + add_two(20); return add_one(10) + add_two(20)
} }
add_two := fn(x: int): int { add_two := fn(x: int): int {
return x + 2; return x + 2
} }
add_one := fn(x: int): int { 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 // commant is an item
main := fn(): int { main := fn(): int {
// comment is a statement // comment is a statement
foo(/* comment is an exprression /* if you are crazy */ */); foo(/* comment is an exprression /* if you are crazy */ */)
return 0; return 0
} }
foo := fn(comment: void): void foo := fn(comment: void): void return /* comment evaluates to void */
return /* comment evaluates to void */;
// comments might be formatted in the future // comments might be formatted in the future
``` ```
@ -84,14 +83,14 @@ foo := fn(comment: void): void
#### if_statements #### if_statements
```hb ```hb
main := fn(): int { main := fn(): int {
return fib(10); return fib(10)
} }
fib := fn(x: int): int { fib := fn(x: int): int {
if x <= 2 { if x <= 2 {
return 1; return 1
} else { } 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 #### variables
```hb ```hb
main := fn(): int { main := fn(): int {
ඞ := 1; ඞ := 1
b := 2; b := 2
= ඞ + 1; ඞ += 1
return ඞ - b; return ඞ - b
} }
``` ```
#### loops #### loops
```hb ```hb
main := fn(): int { main := fn(): int {
return fib(10); return fib(10)
} }
fib := fn(n: int): int { fib := fn(n: int): int {
a := 0; a := 0
b := 1; b := 1
loop { loop {
if n == 0 break; if n == 0 break
c := a + b; c := a + b
a = b; a = b
b = c; b = c
n -= 1; n -= 1
stack_reclamation_edge_case := 0; stack_reclamation_edge_case := 0
continue; continue
} }
return a; return a
} }
``` ```
#### pointers #### pointers
```hb ```hb
main := fn(): int { main := fn(): int {
a := 1; a := 1
b := &a; b := &a
modify(b); modify(b)
drop(a); drop(a)
stack_reclamation_edge_case := 0; stack_reclamation_edge_case := 0
return *b - 2; return *b - 2
} }
modify := fn(a: ^int): void { modify := fn(a: ^int): void {
*a = 2; *a = 2
return; return
} }
drop := fn(a: int): void { drop := fn(a: int): void {
return; return
} }
``` ```
@ -164,21 +163,21 @@ Ty2 := struct {
} }
main := fn(): int { main := fn(): int {
finst := Ty2.{ ty: Ty.{ a: 4, b: 1 }, c: 3 }; finst := Ty2.{ty: Ty.{a: 4, b: 1}, c: 3}
inst := odher_pass(finst); inst := odher_pass(finst)
if inst.c == 3 { if inst.c == 3 {
return pass(&inst.ty); return pass(&inst.ty)
} }
return 0; return 0
} }
pass := fn(t: ^Ty): int { pass := fn(t: ^Ty): int {
.{ a, b } := *t; .{a, b} := *t
return a - b; return a - b
} }
odher_pass := fn(t: Ty2): Ty2 { odher_pass := fn(t: Ty2): Ty2 {
return t; return t
} }
``` ```
@ -195,51 +194,51 @@ Rect := struct {
} }
main := fn(): int { main := fn(): int {
a := Point.(1, 2); a := Point.(1, 2)
b := Point.(3, 4); b := Point.(3, 4)
d := Rect.(a + b, b - a); d := Rect.(a + b, b - a)
d2 := Rect.(Point.(0, 0) - b, a); d2 := Rect.(Point.(0, 0) - b, a)
d2 = d2 + d; d2 += d
c := d2.a + d2.b; c := d2.a + d2.b
return c.x + c.y; return c.x + c.y
} }
``` ```
#### global_variables #### global_variables
```hb ```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 { fib := fn(n: int): int {
if 2 > n { if 2 > n {
return n; return n
} }
return fib(n - 1) + fib(n - 2); return fib(n - 1) + fib(n - 2)
} }
main := fn(): int { main := fn(): int {
return complex_global_var; return complex_global_var
} }
``` ```
note: values of global variables are evaluated at compile time note: values of global variables are evaluated at compile time
#### directives #### directives
```hb ```hb
foo := @use("foo.hb"); foo := @use("foo.hb")
main := fn(): int { main := fn(): int {
byte := @as(u8, 10); byte := @as(u8, 10)
same_type_as_byte := @as(@TypeOf(byte), 30); same_type_as_byte := @as(@TypeOf(byte), 30)
wide_uint := @as(u32, 40); wide_uint := @as(u32, 40)
truncated_uint := @as(u8, @intcast(wide_uint)); truncated_uint := @as(u8, @intcast(wide_uint))
size_of_Type_in_bytes := @sizeof(foo.Type); size_of_Type_in_bytes := @sizeof(foo.Type)
align_of_Type_in_bytes := @alignof(foo.Type); align_of_Type_in_bytes := @alignof(foo.Type)
hardcoded_pointer := @as(^u8, @bitcast(10)); hardcoded_pointer := @as(^u8, @bitcast(10))
ecall_that_returns_int := @eca(int, 1, foo.Type.(10, 20), 5, 6); ecall_that_returns_int := @eca(int, 1, foo.Type.(10, 20), 5, 6)
return 0; return 0
} }
// in module: foo.hb // in module: foo.hb
@ -262,65 +261,65 @@ Type := struct {
#### c_strings #### c_strings
```hb ```hb
str_len := fn(str: ^u8): int { str_len := fn(str: ^u8): int {
len := 0; len := 0
loop if *str == 0 break else { loop if *str == 0 break else {
len += 1; len += 1
str += 1; str += 1
} }
return len; return len
} }
main := fn(): int { main := fn(): int {
// when string ends with '\0' its a C string and thus type is '^u8' // when string ends with '\0' its a C string and thus type is '^u8'
some_str := "abඞ\n\r\t\{ff}\{fff0f0ff}\0"; some_str := "abඞ\n\r\t\{ff}\{fff0f0ff}\0"
len := str_len(some_str); len := str_len(some_str)
some_other_str := "fff\0"; some_other_str := "fff\0"
lep := str_len(some_other_str); lep := str_len(some_other_str)
return lep + len; return lep + len
} }
``` ```
#### struct_patterns #### struct_patterns
```hb ```hb
.{ fib, fib_iter, Fiber } := @use("fibs.hb"); .{fib, fib_iter, Fiber} := @use("fibs.hb")
main := fn(): int { main := fn(): int {
.{ a, b } := Fiber.{ a: 10, b: 10 }; .{a, b} := Fiber.{a: 10, b: 10}
return fib(a) - fib_iter(b); return fib(a) - fib_iter(b)
} }
// in module: fibs.hb // in module: fibs.hb
Fiber := struct { a: u8, b: u8 }; Fiber := struct {a: u8, b: u8}
fib := fn(n: int): int if n < 2 { fib := fn(n: int): int if n < 2 {
return n; return n
} else { } else {
return fib(n - 1) + fib(n - 2); return fib(n - 1) + fib(n - 2)
}; }
fib_iter := fn(n: int): int { fib_iter := fn(n: int): int {
a := 0; a := 0
b := 1; b := 1
loop if n == 0 break else { loop if n == 0 break else {
c := a + b; c := a + b
a = b; a = b
b = c; b = c
n -= 1; n -= 1
} }
return a; return a
} }
``` ```
#### arrays #### arrays
```hb ```hb
main := fn(): int { main := fn(): int {
arr := [int].(1, 2, 4); arr := [int].(1, 2, 4)
return pass(&arr); return pass(&arr)
} }
pass := fn(arr: ^[int; 3]): int { 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 #### comptime_pointers
```hb ```hb
main := fn(): int { main := fn(): int {
$integer := 7; $integer := 7
modify(&integer); modify(&integer)
return integer; return integer
} }
modify := fn($num: ^int): void { modify := fn($num: ^int): void {
$: *num = 0; $: *num = 0
} }
``` ```
@ -344,116 +343,111 @@ modify := fn($num: ^int): void {
MALLOC_SYS_CALL := 69 MALLOC_SYS_CALL := 69
FREE_SYS_CALL := 96 FREE_SYS_CALL := 96
malloc := fn(size: uint, align: uint): ^void malloc := fn(size: uint, align: uint): ^void return @eca(^void, MALLOC_SYS_CALL, size, align)
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)
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,
len: uint, len: uint,
cap: 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 { deinit := fn($Elem: type, vec: ^Vec(Elem)): void {
free(@bitcast(vec.data), vec.cap * @sizeof(Elem), @alignof(Elem)); free(@bitcast(vec.data), vec.cap * @sizeof(Elem), @alignof(Elem));
*vec = new(Elem); *vec = new(Elem)
return return
} }
push := fn($Elem: type, vec: ^Vec(Elem), value: Elem): ^Elem { push := fn($Elem: type, vec: ^Vec(Elem), value: Elem): ^Elem {
if vec.len == vec.cap { if vec.len == vec.cap {
if vec.cap == 0 { if vec.cap == 0 {
vec.cap = 1; vec.cap = 1
} else { } else {
vec.cap *= 2; vec.cap *= 2
} }
new_alloc := @as(^Elem, @bitcast(malloc(vec.cap * @sizeof(Elem), @alignof(Elem)))); new_alloc := @as(^Elem, @bitcast(malloc(vec.cap * @sizeof(Elem), @alignof(Elem))))
if new_alloc == 0 return 0; if new_alloc == 0 return 0
src_cursor := vec.data; src_cursor := vec.data
dst_cursor := new_alloc; dst_cursor := new_alloc
end := vec.data + vec.len; end := vec.data + vec.len
loop if src_cursor == end break else { loop if src_cursor == end break else {
*dst_cursor = *src_cursor; *dst_cursor = *src_cursor
src_cursor += 1; src_cursor += 1
dst_cursor += 1; dst_cursor += 1
} }
if vec.len != 0 { 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 := vec.data + vec.len;
*slot = value; *slot = value
vec.len += 1; vec.len += 1
return slot; return slot
} }
main := fn(): int { main := fn(): int {
vec := new(int); vec := new(int)
push(int, &vec, 69); push(int, &vec, 69)
res := *vec.data; res := *vec.data
deinit(int, &vec); deinit(int, &vec)
return res; return res
} }
``` ```
#### generic_functions #### generic_functions
```hb ```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 { main := fn(): int {
return add(u32, 2, 2) - add(int, 1, 3); return add(u32, 2, 2) - add(int, 1, 3)
} }
``` ```
#### fb_driver #### fb_driver
```hb ```hb
arm_fb_ptr := fn(): int return 100; arm_fb_ptr := fn(): int return 100
x86_fb_ptr := fn(): int return 100; x86_fb_ptr := fn(): int return 100
check_platform := fn(): int { check_platform := fn(): int {
return x86_fb_ptr(); return x86_fb_ptr()
} }
set_pixel := fn(x: int, y: int, width: int): int { set_pixel := fn(x: int, y: int, width: int): int {
pix_offset := y * width + x; pix_offset := y * width + x
return 0
return 0;
} }
main := fn(): int { main := fn(): int {
fb_ptr := check_platform(); fb_ptr := check_platform()
width := 100; width := 100
height := 30; height := 30
x:= 0; x := 0
y:= 0; y := 0
loop { loop {
if x <= height + 1 { if x <= height + 1 {
set_pixel(x,y,width); set_pixel(x, y, width)
x = x + 1; x += 1
} else { } else {
set_pixel(x,y,width); set_pixel(x, y, width)
x = 0; x = 0
y = y + 1; y += 1
} }
if y == width { if y == width {
break; break
} }
} }
return 0; return 0
} }
``` ```
@ -490,33 +484,33 @@ main := fn(): int {
x: 0, x: 0,
y: 2, y: 2,
}, },
}; }
soupan := 1; soupan := 1
if *(&pixel.point.x + soupan) != 2 { if *(&pixel.point.x + soupan) != 2 {
return 0; return 0
} }
if *(&pixel.point.y - 1) != 0 { if *(&pixel.point.y - 1) != 0 {
return 64; return 64
} }
return pixel.point.x + pixel.point.y + pixel.color.r 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 #### struct_return_from_module_function
```hb ```hb
bar := @use("bar.hb"); bar := @use("bar.hb")
main := fn(): int { 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 // in module: bar.hb
foo := fn(): struct {x: int, y: u32, z: u32} { foo := fn(): struct {x: int, y: u32, z: u32} {
return .{ x: 3, y: 2, z: 2 }; return .{x: 3, y: 2, z: 2}
} }
``` ```

View file

@ -1382,7 +1382,13 @@ impl Codegen {
} else { } else {
let values = captured let values = captured
.iter() .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)) .map(|expr| self.expr(&expr))
.collect::<Option<Vec<_>>>()?; .collect::<Option<Vec<_>>>()?;
let values_size = 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 module_map = Vec::new();
let mut last_start = 0; let mut last_start = 0;
@ -3245,7 +3253,7 @@ mod tests {
let mut codegen = super::Codegen { let mut codegen = super::Codegen {
files: module_map files: module_map
.iter() .iter()
.map(|&(path, content)| parser::Ast::new(path, content, &loader)) .map(|&(path, content)| parser::Ast::new(path, content, &loader, false))
.collect(), .collect(),
..Default::default() ..Default::default()
}; };

View file

@ -261,6 +261,18 @@ impl<'a> Lexer<'a> {
Some(c) 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 { pub fn next(&mut self) -> Token {
use TokenKind as T; use TokenKind as T;
loop { loop {

View file

@ -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)?; let mut file = std::fs::File::open(path)?;
file.read_to_end(buffer)?; file.read_to_end(buffer)?;
let src = std::str::from_utf8(buffer).map_err(InvalidFileData)?; 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 = || { let thread = || {
@ -543,16 +543,12 @@ pub struct Options {
pub extra_threads: usize, pub extra_threads: usize,
} }
pub fn run_compiler( fn format_to(
root_file: &str, ast: &parser::Ast,
options: Options, source: &str,
out: &mut impl std::io::Write, out: &mut impl std::io::Write,
) -> io::Result<()> { ) -> std::io::Result<()> {
let parsed = parse_from_fs(options.extra_threads, root_file)?; parser::with_fmt_source(source, || {
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() { for (i, expr) in ast.exprs().iter().enumerate() {
write!(out, "{expr}")?; write!(out, "{expr}")?;
if let Some(expr) = ast.exprs().get(i + 1) if let Some(expr) = ast.exprs().get(i + 1)
@ -561,19 +557,30 @@ pub fn run_compiler(
if parser::insert_needed_semicolon(rest) { if parser::insert_needed_semicolon(rest) {
write!(out, ";")?; write!(out, ";")?;
} }
for _ in 1..parser::preserve_newlines(&source[..expr.pos() as usize]) { if parser::preserve_newlines(&source[..expr.pos() as usize]) > 1 {
writeln!(out)?; writeln!(out)?;
} }
} }
if i + 1 != ast.exprs().len() {
writeln!(out)?; writeln!(out)?;
} }
}
std::io::Result::Ok(()) std::io::Result::Ok(())
}) })
} }
pub fn run_compiler(
root_file: &str,
options: Options,
out: &mut impl std::io::Write,
) -> io::Result<()> {
let parsed = parse_from_fs(options.extra_threads, root_file)?;
fn format_ast(ast: parser::Ast) -> std::io::Result<()> { fn format_ast(ast: parser::Ast) -> std::io::Result<()> {
let mut output = Vec::new(); 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)?; std::fs::write(&*ast.path, output)?;
Ok(()) Ok(())
} }
@ -583,7 +590,9 @@ pub fn run_compiler(
format_ast(parsed)?; format_ast(parsed)?;
} }
} else if options.fmt_current { } 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 { } else {
let mut codegen = codegen::Codegen::default(); let mut codegen = codegen::Codegen::default();
codegen.files = parsed; codegen.files = parsed;

View file

@ -70,10 +70,16 @@ pub struct Parser<'a, 'b> {
trailing_sep: bool, trailing_sep: bool,
idents: Vec<ScopeIdent>, idents: Vec<ScopeIdent>,
captured: Vec<Ident>, captured: Vec<Ident>,
loose: bool,
} }
impl<'a, 'b> Parser<'a, 'b> { 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(""); let mut lexer = Lexer::new("");
Self { Self {
loader, loader,
@ -86,6 +92,7 @@ impl<'a, 'b> Parser<'a, 'b> {
trailing_sep: false, trailing_sep: false,
idents: Vec::new(), idents: Vec::new(),
captured: 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"); 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( self.report_pos(
pos, pos,
format_args!("redeclaration of identifier: {}", self.lexer.slice(ident::range(id))), format_args!("redeclaration of identifier: {}", self.lexer.slice(ident::range(id))),
@ -316,11 +323,12 @@ impl<'a, 'b> Parser<'a, 'b> {
} }
token.start token.start
}, },
trailing_comma: std::mem::take(&mut self.trailing_sep),
}, },
T::Ident | T::CtIdent => { T::Ident | T::CtIdent => {
let (id, index) = self.resolve_ident(token); let (id, index) = self.resolve_ident(token);
let name = self.move_str(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 { T::If => E::If {
pos: token.start, pos: token.start,
@ -348,7 +356,13 @@ impl<'a, 'b> Parser<'a, 'b> {
let (id, index) = s.resolve_ident(name); let (id, index) = s.resolve_ident(name);
s.declare(name.start, id, None); s.declare(name.start, id, None);
s.expect_advance(T::Colon); 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: { ret: {
@ -461,7 +475,7 @@ impl<'a, 'b> Parser<'a, 'b> {
s.expr() s.expr()
} else { } else {
let (id, index) = s.resolve_ident(name_tok); 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 struct Arg<'a> {
pub name: &'a str, pub name: &'a str,
pub id: Ident, pub id: Ident,
pub is_ct: bool,
pub index: IdentIndex, pub index: IdentIndex,
pub ty: Expr<'a>, pub ty: Expr<'a>,
} }
@ -605,6 +620,8 @@ macro_rules! generate_expr {
} }
}; };
(@filed_names $variant:ident $ident1:ident) => { Self::$variant { $ident1: a } };
(@first ($($first:tt)*), $($rest:tt)*) => { $($first)* }; (@first ($($first:tt)*), $($rest:tt)*) => { $($first)* };
(@last ($($ign:tt)*), $($rest:tt)*) => { $($rest)* }; (@last ($($ign:tt)*), $($rest:tt)*) => { $($rest)* };
(@last ($($last:tt)*),) => { $($last)* }; (@last ($($last:tt)*),) => { $($last)* };
@ -616,7 +633,7 @@ generate_expr! {
pub enum Expr<'a> { pub enum Expr<'a> {
Ct { Ct {
pos: Pos, pos: Pos,
value: &'a Expr<'a>, value: &'a Self,
}, },
String { String {
pos: Pos, pos: Pos,
@ -649,6 +666,7 @@ generate_expr! {
}, },
Ident { Ident {
pos: Pos, pos: Pos,
is_ct: bool,
id: Ident, id: Ident,
name: &'a str, name: &'a str,
index: IdentIndex, index: IdentIndex,
@ -685,6 +703,7 @@ generate_expr! {
pos: Pos, pos: Pos,
fields: &'a [(&'a str, Self)], fields: &'a [(&'a str, Self)],
captured: &'a [Ident], captured: &'a [Ident],
trailing_comma: bool,
}, },
Ctor { Ctor {
pos: Pos, 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 { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
thread_local! { thread_local! {
static INDENT: Cell<usize> = const { Cell::new(0) }; static INDENT: Cell<usize> = const { Cell::new(0) };
static DISPLAY_BUFFER: Cell<String> = const { Cell::new(String::new()) };
} }
fn fmt_list<T>( fn fmt_list<T>(
@ -874,7 +894,7 @@ impl<'a> std::fmt::Display for Expr<'a> {
Consecutive => Expr::UnOp { .. }, Consecutive => Expr::UnOp { .. },
} }
let source = unsafe { &*FMT_SOURCE.with(|s| s.get()) }; let source: &str = unsafe { &*FMT_SOURCE.with(|s| s.get()) };
match *self { match *self {
Self::Ct { value, .. } => write!(f, "$: {}", value), Self::Ct { value, .. } => write!(f, "$: {}", value),
@ -886,9 +906,11 @@ impl<'a> std::fmt::Display for Expr<'a> {
write!(f, "@{name}(")?; write!(f, "@{name}(")?;
fmt_list(f, false, ")", args, std::fmt::Display::fmt) fmt_list(f, false, ")", args, std::fmt::Display::fmt)
} }
Self::Struct { fields, .. } => { Self::Struct { fields, trailing_comma, .. } => {
write!(f, "struct {{")?; 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, .. } => { Self::Ctor { ty, fields, trailing_comma, .. } => {
if let Some(ty) = ty { 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) fmt_list(f, trailing_comma, ")", fields, std::fmt::Display::fmt)
} }
Self::Slice { item, size, .. } => match size { Self::Slice { item, size, .. } => match size {
Some(size) => write!(f, "[{size}]{item}"), Some(size) => write!(f, "[{item}; {size}]"),
None => write!(f, "[]{item}"), None => write!(f, "[{item}]"),
}, },
Self::Index { base, index } => write!(f, "{base}[{index}]"), Self::Index { base, index } => write!(f, "{base}[{index}]"),
Self::UnOp { op, val, .. } => write!(f, "{op}{}", Unary(val)), 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::Loop { body, .. } => write!(f, "loop {body}"),
Self::Closure { ret, body, args, .. } => { Self::Closure { ret, body, args, .. } => {
write!(f, "fn(")?; 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}")?; write!(f, "): {ret} {body}")?;
Ok(()) 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: Some(val), .. } => write!(f, "return {val}"),
Self::Return { val: None, .. } => write!(f, "return"), 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, .. } => { Self::Block { stmts, .. } => {
write!(f, "{{")?; write!(f, "{{")?;
writeln!(f)?; writeln!(f)?;
@ -956,7 +984,7 @@ impl<'a> std::fmt::Display for Expr<'a> {
if insert_needed_semicolon(rest) { if insert_needed_semicolon(rest) {
write!(f, ";")?; write!(f, ";")?;
} }
for _ in 1..preserve_newlines(&source[..expr.pos() as usize]) { if preserve_newlines(&source[..expr.pos() as usize]) > 1 {
writeln!(f)?; writeln!(f)?;
} }
} }
@ -974,13 +1002,25 @@ impl<'a> std::fmt::Display for Expr<'a> {
Self::Number { value, .. } => write!(f, "{value}"), Self::Number { value, .. } => write!(f, "{value}"),
Self::Bool { value, .. } => write!(f, "{value}"), Self::Bool { value, .. } => write!(f, "{value}"),
Self::BinOp { Self::BinOp {
left: left @ Self::Ident { id, .. }, left,
op: TokenKind::Assign, op: TokenKind::Assign,
right: Self::BinOp { left: Self::Ident { id: oid, .. }, op, right }, right: Self::BinOp { left: lleft, op, right },
} if id == oid => { } 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}") 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| { let display_branch = |f: &mut std::fmt::Formatter, expr: &Self| {
if let Self::BinOp { op: lop, .. } = expr if let Self::BinOp { op: lop, .. } = expr
&& op.precedence() > lop.precedence() && op.precedence() > lop.precedence()
@ -992,7 +1032,24 @@ impl<'a> std::fmt::Display for Expr<'a> {
}; };
display_branch(f, left)?; display_branch(f, left)?;
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} ")?; write!(f, "{op} ")?;
} else {
write!(f, " {op} ")?;
}
} else {
write!(f, " {op} ")?;
}
display_branch(f, right) display_branch(f, right)
} }
} }
@ -1000,8 +1057,7 @@ impl<'a> std::fmt::Display for Expr<'a> {
} }
pub fn preserve_newlines(source: &str) -> usize { pub fn preserve_newlines(source: &str) -> usize {
let trailing_whitespace = &source[source.trim_end().len()..]; source[source.trim_end().len()..].chars().filter(|&c| c == '\n').count()
trailing_whitespace.chars().filter(|&c| c == '\n').count().min(2)
} }
pub fn insert_needed_semicolon(source: &str) -> bool { pub fn insert_needed_semicolon(source: &str) -> bool {
@ -1028,10 +1084,10 @@ impl AstInner<[Symbol]> {
.0 .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 arena = Arena::default();
let mut syms = Vec::new(); 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>]; let exprs = parser.file(content, path) as *const [Expr<'static>];
syms.sort_unstable_by_key(|s| s.name); syms.sort_unstable_by_key(|s| s.name);
@ -1063,8 +1119,8 @@ impl AstInner<[Symbol]> {
pub struct Ast(NonNull<AstInner<[Symbol]>>); pub struct Ast(NonNull<AstInner<[Symbol]>>);
impl Ast { impl Ast {
pub fn new(path: &str, content: &str, loader: Loader) -> Self { pub fn new(path: &str, content: &str, loader: Loader, loose: bool) -> Self {
Self(AstInner::new(content, path, loader)) Self(AstInner::new(content, path, loader, loose))
} }
pub fn exprs(&self) -> &[Expr] { pub fn exprs(&self) -> &[Expr] {
@ -1094,7 +1150,7 @@ impl std::fmt::Display for Ast {
impl Default for Ast { impl Default for Ast {
fn default() -> Self { fn default() -> Self {
Self(AstInner::new("", "", &no_loader)) Self(AstInner::new("", "", &no_loader, false))
} }
} }
@ -1268,16 +1324,11 @@ impl Drop for ArenaChunk {
} }
#[cfg(test)] #[cfg(test)]
mod test { pub mod test {
fn format(ident: &str, input: &str) { pub fn format(ident: &str, input: &str) {
let ast = super::Ast::new(ident, input, &super::no_loader); let ast = super::Ast::new(ident, input, &|_, _| Ok(0), true);
let mut output = String::new(); let mut output = Vec::new();
super::with_fmt_source(input, || { crate::format_to(&ast, input, &mut output).unwrap();
for expr in ast.exprs() {
use std::fmt::Write;
writeln!(output, "{expr}").unwrap();
}
});
let input_path = format!("formatter_{ident}.expected"); let input_path = format!("formatter_{ident}.expected");
let output_path = format!("formatter_{ident}.actual"); let output_path = format!("formatter_{ident}.actual");
@ -1308,13 +1359,13 @@ mod test {
test! { test! {
comments => "// comment\n// comment\n\n// comment\n\n\ comments => "// comment\n// comment\n\n// comment\n\n\
/* comment */\n/* comment */\n\n/* comment */\n"; /* comment */\n/* comment */\n\n/* comment */";
some_ordinary_code => "loft := fn(): int return loft(1, 2, 3);\n"; some_ordinary_code => "loft := fn(): int return loft(1, 2, 3)";
some_arg_per_line_code => "loft := fn(): int return loft(\ some_arg_per_line_code => "loft := fn(): int return loft(\
\n\t1,\n\t2,\n\t3,\n);\n"; \n\t1,\n\t2,\n\t3,\n)";
some_ordinary_struct => "loft := fn(): int return loft.{a: 1, b: 2};\n"; some_ordinary_struct => "loft := fn(): int return loft.{a: 1, b: 2}";
some_ordinary_fild_per_lin_struct => "loft := fn(): int return loft.{\ some_ordinary_fild_per_lin_struct => "loft := fn(): int return loft.{\
\n\ta: 1,\n\tb: 2,\n};\n"; \n\ta: 1,\n\tb: 2,\n}";
code_block => "loft := fn(): int {\n\tloft();\n\treturn 1;\n}\n"; code_block => "loft := fn(): int {\n\tloft()\n\treturn 1\n}";
} }
} }

View file

@ -1,3 +1,3 @@
code size: 1503 code size: 1504
ret: 69 ret: 69
status: Ok(()) status: Ok(())