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
```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}
}
```

View file

@ -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()
};

View file

@ -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 {

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)?;
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;

View file

@ -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}";
}
}

View file

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