From 965f4c90a90f557542762864c32574e60fe0556c Mon Sep 17 00:00:00 2001 From: mlokr Date: Fri, 19 Jul 2024 21:04:22 +0200 Subject: [PATCH] foobar --- hblang/README.md | 316 +++++++++---------- hblang/src/codegen.rs | 14 +- hblang/src/lexer.rs | 12 + hblang/src/lib.rs | 57 ++-- hblang/src/parser.rs | 155 ++++++--- hblang/tests/codegen_tests_generic_types.txt | 2 +- 6 files changed, 315 insertions(+), 241 deletions(-) diff --git a/hblang/README.md b/hblang/README.md index 7b0d1f7..2796a17 100644 --- a/hblang/README.md +++ b/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} } ``` diff --git a/hblang/src/codegen.rs b/hblang/src/codegen.rs index 96112d2..28553c6 100644 --- a/hblang/src/codegen.rs +++ b/hblang/src/codegen.rs @@ -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::>>()?; 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() }; diff --git a/hblang/src/lexer.rs b/hblang/src/lexer.rs index 543fdd2..02c9617 100644 --- a/hblang/src/lexer.rs +++ b/hblang/src/lexer.rs @@ -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 { diff --git a/hblang/src/lib.rs b/hblang/src/lib.rs index 32d9b99..2c33ebf 100644 --- a/hblang/src/lib.rs +++ b/hblang/src/lib.rs @@ -424,7 +424,7 @@ pub fn parse_from_fs(extra_threads: usize, root: &str) -> io::Result> { 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; diff --git a/hblang/src/parser.rs b/hblang/src/parser.rs index c311784..d3e24af 100644 --- a/hblang/src/parser.rs +++ b/hblang/src/parser.rs @@ -70,10 +70,16 @@ pub struct Parser<'a, 'b> { trailing_sep: bool, idents: Vec, captured: Vec, + 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 = const { Cell::new(0) }; + static DISPLAY_BUFFER: Cell = const { Cell::new(String::new()) }; } fn fmt_list( @@ -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 { + fn new(content: &str, path: &str, loader: Loader, loose: bool) -> NonNull { 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>); 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}"; } } diff --git a/hblang/tests/codegen_tests_generic_types.txt b/hblang/tests/codegen_tests_generic_types.txt index 3333114..6ec333d 100644 --- a/hblang/tests/codegen_tests_generic_types.txt +++ b/hblang/tests/codegen_tests_generic_types.txt @@ -1,3 +1,3 @@ -code size: 1503 +code size: 1504 ret: 69 status: Ok(())