Compare commits

...

9 commits

Author SHA1 Message Date
mlokr c85437e4e8
fixed a significant bugus 2024-06-24 17:45:58 +02:00
mlokr 76b3f9ff4b
pekomaaaaa 2024-06-24 17:26:00 +02:00
mlokr 66c3f7b0d4
cleanup 2024-06-23 13:55:48 +02:00
mlokr b04d9e517e
tests pass again 2024-06-23 09:26:03 +02:00
mlokr b46c64db4f
tests pass again 2024-06-23 09:09:33 +02:00
mlokr 6de8496aa5
whew 2024-06-21 23:07:32 +02:00
mlokr 499fe34f1d
psl 2024-06-20 11:23:37 +02:00
mlokr 20903ef294
smh my head 2024-06-12 16:29:41 +02:00
mlokr aafcb2fbbd
size improvement 2024-06-06 15:39:10 +02:00
38 changed files with 2907 additions and 2687 deletions

View file

@ -1,46 +0,0 @@
Dbg: dropping chunk of size: 0
Dbg: dropping chunk of size: 544
Dbg: deallocating full chunk
Dbg: dropping chunk of size: 0
Dbg: dropping chunk of size: 3200
Dbg: deallocating full chunk
Dbg: dropping chunk of size: 0
Dbg: dropping chunk of size: 1032
Dbg: deallocating full chunk
Dbg: dropping chunk of size: 0
Dbg: dropping chunk of size: 224
Dbg: deallocating full chunk
Dbg: dropping chunk of size: 0
Dbg: dropping chunk of size: 3240
Dbg: deallocating full chunk
Dbg: dropping chunk of size: 0
Dbg: dropping chunk of size: 1144
Dbg: deallocating full chunk
Dbg: dropping chunk of size: 0
Dbg: dropping chunk of size: 1352
Dbg: deallocating full chunk
Dbg: dropping chunk of size: 0
Dbg: dropping chunk of size: 1400
Dbg: deallocating full chunk
Dbg: dropping chunk of size: 0
Dbg: dropping chunk of size: 1128
Dbg: deallocating full chunk
Dbg: dropping chunk of size: 0
Dbg: dropping chunk of size: 1632
Dbg: deallocating full chunk
Dbg: dropping chunk of size: 0
Dbg: dropping chunk of size: 1528
Dbg: deallocating full chunk
Dbg: dropping chunk of size: 0
Dbg: dropping chunk of size: 2496
Dbg: deallocating full chunk
Dbg: dropping chunk of size: 0
Dbg: dropping chunk of size: 2440
Dbg: deallocating full chunk
Dbg: dropping chunk of size: 0
Dbg: dropping chunk of size: 600
Dbg: deallocating full chunk
test parser::tests::arithmetic ... Dbg: dropping chunk of size: 544
Dbg: deallocating full chunk
test parser::tests::example ... Dbg: dropping chunk of size: 224
Dbg: deallocating full chunk

View file

@ -1,46 +0,0 @@
Dbg: dropping chunk of size: 0
Dbg: dropping chunk of size: 936
Dbg: deallocating full chunk
Dbg: dropping chunk of size: 0
Dbg: dropping chunk of size: 4040
Dbg: deallocating full chunk
Dbg: dropping chunk of size: 0
Dbg: dropping chunk of size: 1112
Dbg: deallocating full chunk
Dbg: dropping chunk of size: 0
Dbg: dropping chunk of size: 296
Dbg: deallocating full chunk
Dbg: dropping chunk of size: 0
Dbg: dropping chunk of size: 4328
Dbg: deallocating full chunk
Dbg: dropping chunk of size: 0
Dbg: dropping chunk of size: 1464
Dbg: deallocating full chunk
Dbg: dropping chunk of size: 0
Dbg: dropping chunk of size: 1616
Dbg: deallocating full chunk
Dbg: dropping chunk of size: 0
Dbg: dropping chunk of size: 1864
Dbg: deallocating full chunk
Dbg: dropping chunk of size: 0
Dbg: dropping chunk of size: 1504
Dbg: deallocating full chunk
Dbg: dropping chunk of size: 0
Dbg: dropping chunk of size: 2160
Dbg: deallocating full chunk
Dbg: dropping chunk of size: 0
Dbg: dropping chunk of size: 2000
Dbg: deallocating full chunk
Dbg: dropping chunk of size: 0
Dbg: dropping chunk of size: 3048
Dbg: deallocating full chunk
Dbg: dropping chunk of size: 0
Dbg: dropping chunk of size: 2960
Dbg: deallocating full chunk
Dbg: dropping chunk of size: 0
Dbg: dropping chunk of size: 848
Dbg: deallocating full chunk
test parser::tests::arithmetic ... Dbg: dropping chunk of size: 936
Dbg: deallocating full chunk
test parser::tests::example ... Dbg: dropping chunk of size: 296
Dbg: deallocating full chunk

View file

@ -241,6 +241,15 @@ main := fn(): int {
#### generic_types #### generic_types
```hb ```hb
MALLOC_SYS_CALL := 69
FREE_SYS_CALL := 96
malloc := fn(size: uint, align: uint): ^void
return @eca(^void, MALLOC_SYS_CALL, size, align);
free := fn(ptr: ^void, size: uint, align: uint): void
return @eca(void, FREE_SYS_CALL, ptr, size, align);
Vec := fn($Elem: type): type { Vec := fn($Elem: type): type {
return struct { return struct {
data: ^Elem, data: ^Elem,
@ -249,14 +258,62 @@ Vec := fn($Elem: type): type {
}; };
} }
new := fn($Elem: type): Vec(Elem) return Vec(Elem).{ data: @bitcast(0), len: 0, cap: 0 };
deinit := fn($Elem: type, vec: ^Vec(Elem)): void {
free(@bitcast(vec.data), vec.cap * @sizeof(Elem), @alignof(Elem));
*vec = new(Elem);
return;
}
push := fn($Elem: type, vec: ^Vec(Elem), value: Elem): ^Elem {
if vec.len == vec.cap {
if vec.cap == 0 {
vec.cap = 1;
} else {
vec.cap *= 2;
}
new_alloc := @as(^Elem, @bitcast(malloc(vec.cap * @sizeof(Elem), @alignof(Elem))));
if @as(uint, @bitcast(new_alloc)) == 0 return @bitcast(0);
src_cursor := vec.data;
dst_cursor := new_alloc;
end := vec.data + vec.len;
loop if src_cursor == end break else {
*dst_cursor = *src_cursor;
src_cursor += 1;
dst_cursor += 1;
}
if vec.len != 0 {
free(@bitcast(vec.data), vec.len * @sizeof(Elem), @alignof(Elem));
}
vec.data = new_alloc;
}
slot := vec.data + vec.len;
*slot = value;
vec.len += 1;
return slot;
}
main := fn(): int { main := fn(): int {
i := 69; vec := new(int);
vec := Vec(int).{ push(int, &vec, 69);
data: &i, res := *vec.data;
len: 1, deinit(int, &vec);
cap: 1, return res;
}; }
return *vec.data; ```
#### generic_functions
```hb
add := fn($T: type, a: T, b: T): T return a + b;
main := fn(): int {
return add(u32, 2, 2) - add(int, 1, 3);
} }
``` ```

View file

@ -7,7 +7,10 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut generated = String::new(); let mut generated = String::new();
writeln!(generated, "#![allow(dead_code)]")?; writeln!(
generated,
"#![allow(dead_code)] #![allow(clippy::upper_case_acronyms)]"
)?;
gen_max_size(&mut generated)?; gen_max_size(&mut generated)?;
gen_encodes(&mut generated)?; gen_encodes(&mut generated)?;
gen_structs(&mut generated)?; gen_structs(&mut generated)?;

View file

@ -1,3 +0,0 @@
main := fn(): int {
return 10 - 20 / 2 + 4 * (2 + 2) - 4 * 4 + 1;
}

View file

@ -1,43 +0,0 @@
Color := struct {
r: u8,
g: u8,
b: u8,
a: u8,
}
Point := struct {
x: u32,
y: u32,
}
Pixel := struct {
color: Color,
point: Point,
}
main := fn(): int {
pixel := Pixel.{
color: Color.{
r: 255,
g: 0,
b: 0,
a: 255,
},
point: Point.{
x: 0,
y: 2,
},
};
if *(&pixel.point.x + 1) != 2 {
return 0;
}
if *(&pixel.point.y - 1) != 0 {
return 64;
}
return pixel.point.x + pixel.point.y + pixel.color.r
+ pixel.color.g + pixel.color.b + pixel.color.a;
}

View file

@ -1,16 +0,0 @@
Type := struct {
brah: int,
blah: int,
}
main := fn(): int {
byte := @as(u8, 10);
same_type_as_byte := @as(@TypeOf(byte), 30);
wide_uint := @as(u32, 40);
truncated_uint := @as(u8, @intcast(wide_uint));
size_of_Type_in_bytes := @sizeof(Type);
align_of_Type_in_bytes := @alignof(Type);
hardcoded_pointer := @as(^u8, @bitcast(10));
ecall_that_returns_int := @eca(int, 1, Type.(10, 20), 5, 6);
return 0;
}

View file

@ -1,36 +0,0 @@
arm_fb_ptr := fn(): int return 100;
x86_fb_ptr := fn(): int return 100;
check_platform := fn(): int {
return x86_fb_ptr();
}
set_pixel := fn(x: int, y: int, width: int): int {
pix_offset := y * width + x;
return 0;
}
main := fn(): int {
fb_ptr := check_platform();
width := 100;
height := 30;
x:= 0;
y:= 0;
loop {
if x <= height + 1 {
set_pixel(x,y,width);
x = x + 1;
} else {
set_pixel(x,y,width);
x = 0;
y = y + 1;
}
if y == width {
break;
}
}
return 0;
}

View file

@ -1,14 +0,0 @@
main := fn(): int {
return add_one(10) + add_two(20);
}
add_two := fn(x: int): int {
return x + 2;
}
add_one := fn(x: int): int {
return x + 1;
}

View file

@ -1,17 +0,0 @@
Vec := fn($Elem: type): type {
return struct {
data: ^Elem,
len: uint,
cap: uint,
};
}
main := fn(): int {
i := 69;
vec := Vec(int).{
data: &i,
len: 1,
cap: 1,
};
return *vec.data;
}

View file

@ -1,15 +0,0 @@
global_var := 10;
complex_global_var := fib(global_var) - 5;
fib := fn(n: int): int {
if 2 > n {
return n;
}
return fib(n - 1) + fib(n - 2);
}
main := fn(): int {
return complex_global_var;
}

View file

@ -1,12 +0,0 @@
main := fn(): int {
return fib(10);
}
fib := fn(x: int): int {
if x <= 2 {
return 1;
} else {
return fib(x - 1) + fib(x - 2);
}
}

View file

@ -1,22 +0,0 @@
main := fn(): int {
return fib(10);
}
fib := fn(n: int): int {
a := 0;
b := 1;
loop {
if n == 0 {
break;
}
c := a + b;
a = b;
b = c;
n -= 1;
stack_reclamation_edge_case := 0;
continue;
}
return a;
}

View file

@ -1,3 +0,0 @@
main := fn(): int {
return 1;
}

View file

@ -1,17 +0,0 @@
main := fn(): int {
a := 1;
b := &a;
modify(b);
drop(a);
stack_reclamation_edge_case := 0;
return *b - 2;
}
modify := fn(a: ^int): void {
*a = 2;
return;
}
drop := fn(a: int): void {
return;
}

View file

@ -1,21 +0,0 @@
Point := struct {
x: int,
y: int,
}
Rect := struct {
a: Point,
b: Point,
}
main := fn(): int {
a := Point.(1, 2);
b := Point.(3, 4);
d := Rect.(a + b, b - a);
d2 := Rect.(Point.(0, 0) - b, a);
d2 = d2 + d;
c := d2.a + d2.b;
return c.x + c.y;
}

View file

@ -1,26 +0,0 @@
Ty := struct {
a: int,
b: int,
}
Ty2 := struct {
ty: Ty,
c: int,
}
main := fn(): int {
finst := Ty2.{ ty: Ty.{ a: 4, b: 1 }, c: 3 };
inst := odher_pass(finst);
if inst.c == 3 {
return pass(&inst.ty);
}
return 0;
}
pass := fn(t: ^Ty): int {
return t.a - t.b;
}
odher_pass := fn(t: Ty2): Ty2 {
return t;
}

View file

@ -1,6 +0,0 @@
main := fn(): int {
a := 1;
b := 2;
a = a + 1;
return a - b;
}

File diff suppressed because it is too large Load diff

View file

@ -1,25 +0,0 @@
pub type Ident = u32;
const LEN_BITS: u32 = 6;
pub fn len(ident: Ident) -> u32 {
ident & ((1 << LEN_BITS) - 1)
}
pub fn is_null(ident: Ident) -> bool {
(ident >> LEN_BITS) == 0
}
pub fn pos(ident: Ident) -> u32 {
(ident >> LEN_BITS).saturating_sub(1)
}
pub fn new(pos: u32, len: u32) -> Ident {
debug_assert!(len < (1 << LEN_BITS));
((pos + 1) << LEN_BITS) | len
}
pub fn range(ident: Ident) -> std::ops::Range<usize> {
let (len, pos) = (len(ident) as usize, pos(ident) as usize);
pos..pos + len
}

View file

@ -156,8 +156,12 @@ pub struct Lexer<'a> {
impl<'a> Lexer<'a> { impl<'a> Lexer<'a> {
pub fn new(input: &'a str) -> Self { pub fn new(input: &'a str) -> Self {
Self::restore(input, 0)
}
pub fn restore(input: &'a str, pos: u32) -> Self {
Self { Self {
pos: 0, pos,
bytes: input.as_bytes(), bytes: input.as_bytes(),
} }
} }
@ -317,13 +321,17 @@ impl LineMap {
let mut iter = self.lines.iter().copied(); let mut iter = self.lines.iter().copied();
while let Some(mut len) = iter.next() { loop {
let mut acc = 0; let mut acc = 0;
while len & 0x80 != 0 { let mut idx = 0;
acc = (acc << 7) | (len & 0x7F) as u32; loop {
len = iter.next().unwrap(); let len = iter.next().unwrap();
acc |= ((len & 0x7F) as u32) << (7 * idx);
idx += 1;
if len & 0x80 == 0 {
break;
}
} }
acc += len as u32;
if pos < acc { if pos < acc {
break; break;
@ -372,7 +380,7 @@ impl LineMap {
let idx = mask.trailing_zeros() as usize + i * 16 + start.len(); let idx = mask.trailing_zeros() as usize + i * 16 + start.len();
let mut len = idx - last_nl + 1; let mut len = idx - last_nl + 1;
while len >= 0x80 { while len >= 0x80 {
lines.push((0x80 | (len & 0x7F)) as u8); lines.push(0x80 | (len & 0x7F) as u8);
len >>= 7; len >>= 7;
} }
lines.push(len as u8); lines.push(len as u8);
@ -388,3 +396,30 @@ impl LineMap {
} }
} }
} }
#[cfg(test)]
mod test {
#[test]
fn test_smh() {
let example = include_str!("../README.md");
let nlines = super::LineMap::new(example);
fn slow_nline_search(str: &str, mut pos: usize) -> (usize, usize) {
(
str.lines()
.take_while(|l| match pos.checked_sub(l.len() + 1) {
Some(nl) => (pos = nl, true).1,
None => false,
})
.count()
+ 1,
pos + 1,
)
}
for i in 0..example.len() {
assert_eq!(slow_nline_search(example, i), nlines.line_col(i as _));
}
}
}

View file

@ -1,5 +1,10 @@
#![feature(vec_pop_if)] #![feature(vec_pop_if)]
#![feature(new_uninit)]
#![feature(anonymous_lifetime_in_impl_trait)]
#![feature(inline_const_pat)]
#![feature(pattern)] #![feature(pattern)]
#![feature(never_type)]
#![feature(unwrap_infallible)]
#![feature(if_let_guard)] #![feature(if_let_guard)]
#![feature(slice_partition_dedup)] #![feature(slice_partition_dedup)]
#![feature(noop_waker)] #![feature(noop_waker)]
@ -10,6 +15,7 @@
#![feature(ptr_metadata)] #![feature(ptr_metadata)]
#![feature(const_mut_refs)] #![feature(const_mut_refs)]
#![feature(slice_ptr_get)] #![feature(slice_ptr_get)]
#![allow(clippy::format_collect)]
use std::{ use std::{
collections::VecDeque, collections::VecDeque,
@ -20,26 +26,100 @@ use std::{
use parser::Ast; use parser::Ast;
use crate::parser::FileId;
#[macro_export] #[macro_export]
macro_rules! run_tests { macro_rules! run_tests {
($runner:path: $($name:ident => $input:expr;)*) => {$( ($runner:path: $($name:ident => $input:expr;)*) => {$(
#[test] #[test]
fn $name() { fn $name() {
$crate::tests::run_test(std::any::type_name_of_val(&$name), stringify!($name), $input, $runner); $crate::run_test(std::any::type_name_of_val(&$name), stringify!($name), $input, $runner);
} }
)*}; )*};
} }
pub mod codegen; pub mod codegen;
mod ident; pub mod parser;
mod instrs; mod instrs;
mod lexer; mod lexer;
mod log;
pub mod parser; mod ident {
mod tests; pub type Ident = u32;
mod typechk;
const LEN_BITS: u32 = 6;
pub fn len(ident: u32) -> u32 {
ident & ((1 << LEN_BITS) - 1)
}
pub fn is_null(ident: u32) -> bool {
(ident >> LEN_BITS) == 0
}
pub fn pos(ident: u32) -> u32 {
(ident >> LEN_BITS).saturating_sub(1)
}
pub fn new(pos: u32, len: u32) -> u32 {
debug_assert!(len < (1 << LEN_BITS));
((pos + 1) << LEN_BITS) | len
}
pub fn range(ident: u32) -> std::ops::Range<usize> {
let (len, pos) = (len(ident) as usize, pos(ident) as usize);
pos..pos + len
}
}
mod log {
#![allow(unused_macros)]
#[derive(PartialOrd, PartialEq, Ord, Eq, Debug)]
pub enum Level {
Err,
Wrn,
Inf,
Dbg,
}
pub const LOG_LEVEL: Level = match option_env!("LOG_LEVEL") {
Some(val) => match val.as_bytes()[0] {
b'e' => Level::Err,
b'w' => Level::Wrn,
b'i' => Level::Inf,
b'd' => Level::Dbg,
_ => panic!("Invalid log level."),
},
None => {
if cfg!(debug_assertions) {
Level::Dbg
} else {
Level::Err
}
}
};
macro_rules! log {
($level:expr, $fmt:literal $($expr:tt)*) => {
if $level <= $crate::log::LOG_LEVEL {
eprintln!("{:?}: {}", $level, format_args!($fmt $($expr)*));
}
};
($level:expr, $($arg:expr),*) => {
if $level <= $crate::log::LOG_LEVEL {
$(eprintln!("[{}{}{}][{:?}]: {} = {:?}", line!(), column!(), file!(), $level, stringify!($arg), $arg);)*
}
};
}
macro_rules! err { ($($arg:tt)*) => { $crate::log::log!($crate::log::Level::Err, $($arg)*) }; }
macro_rules! wrn { ($($arg:tt)*) => { $crate::log::log!($crate::log::Level::Wrn, $($arg)*) }; }
macro_rules! inf { ($($arg:tt)*) => { $crate::log::log!($crate::log::Level::Inf, $($arg)*) }; }
macro_rules! dbg { ($($arg:tt)*) => { $crate::log::log!($crate::log::Level::Dbg, $($arg)*) }; }
#[allow(unused_imports)]
pub(crate) use {dbg, err, inf, log, wrn};
}
#[inline] #[inline]
unsafe fn encode<T>(instr: T) -> (usize, [u8; instrs::MAX_SIZE]) { unsafe fn encode<T>(instr: T) -> (usize, [u8; instrs::MAX_SIZE]) {
@ -139,9 +219,15 @@ impl<T> TaskQueueInner<T> {
} }
} }
pub fn parse_all(threads: usize, root: &str) -> io::Result<Vec<Ast>> { pub fn parse_from_fs(threads: usize, root: &str) -> io::Result<Vec<Ast>> {
const GIT_DEPS_DIR: &str = "git-deps"; const GIT_DEPS_DIR: &str = "git-deps";
enum Chk<'a> {
Branch(&'a str),
Rev(&'a str),
Tag(&'a str),
}
enum ImportPath<'a> { enum ImportPath<'a> {
Root { Root {
path: &'a str, path: &'a str,
@ -152,9 +238,7 @@ pub fn parse_all(threads: usize, root: &str) -> io::Result<Vec<Ast>> {
Git { Git {
link: &'a str, link: &'a str,
path: &'a str, path: &'a str,
branch: Option<&'a str>, chk: Option<Chk<'a>>,
tag: Option<&'a str>,
rev: Option<&'a str>,
}, },
} }
@ -171,22 +255,16 @@ pub fn parse_all(threads: usize, root: &str) -> io::Result<Vec<Ast>> {
let (link, path) = let (link, path) =
path.split_once(':').ok_or(ParseImportError::ExpectedPath)?; path.split_once(':').ok_or(ParseImportError::ExpectedPath)?;
let (link, params) = link.split_once('?').unwrap_or((link, "")); let (link, params) = link.split_once('?').unwrap_or((link, ""));
let [mut branch, mut tag, mut rev] = [None; 3]; let chk = params
for (key, value) in params.split('&').filter_map(|s| s.split_once('=')) { .split('&')
match key { .filter_map(|s| s.split_once('='))
"branch" => branch = Some(value), .find_map(|(key, value)| match key {
"tag" => tag = Some(value), "branch" => Some(Chk::Branch(value)),
"rev" => rev = Some(value), "rev" => Some(Chk::Rev(value)),
_ => return Err(ParseImportError::UnexpectedParam), "tag" => Some(Chk::Tag(value)),
} _ => None,
} });
Ok(Self::Git { Ok(Self::Git { link, path, chk })
link,
path,
branch,
tag,
rev,
})
} }
_ => Err(ParseImportError::InvalidPrefix), _ => Err(ParseImportError::InvalidPrefix),
} }
@ -216,21 +294,15 @@ pub fn parse_all(threads: usize, root: &str) -> io::Result<Vec<Ast>> {
enum ParseImportError { enum ParseImportError {
ExpectedPath, ExpectedPath,
InvalidPrefix, InvalidPrefix,
UnexpectedParam,
} }
impl std::fmt::Display for ParseImportError { impl std::fmt::Display for ParseImportError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self { match self {
Self::ExpectedPath => write!(f, "expected path"), Self::ExpectedPath => "expected path".fmt(f),
Self::InvalidPrefix => write!( Self::InvalidPrefix => "invalid prefix, expected one of rel, \
f,
"invalid prefix, expected one of rel, \
git or none followed by colon" git or none followed by colon"
), .fmt(f),
Self::UnexpectedParam => {
write!(f, "unexpected git param, expected branch, tag or rev")
}
} }
} }
} }
@ -285,9 +357,9 @@ pub fn parse_all(threads: usize, root: &str) -> io::Result<Vec<Ast>> {
} }
} }
type Task = (FileId, PathBuf, Option<std::process::Command>); type Task = (u32, PathBuf, Option<std::process::Command>);
let seen = Mutex::new(HashMap::<PathBuf, FileId>::default()); let seen = Mutex::new(HashMap::<PathBuf, u32>::default());
let tasks = TaskQueue::<Task>::new(threads); let tasks = TaskQueue::<Task>::new(threads);
let ast = Mutex::new(Vec::<io::Result<Ast>>::new()); let ast = Mutex::new(Vec::<io::Result<Ast>>::new());
@ -305,20 +377,13 @@ pub fn parse_all(threads: usize, root: &str) -> io::Result<Vec<Ast>> {
} }
std::collections::hash_map::Entry::Vacant(entry) => { std::collections::hash_map::Entry::Vacant(entry) => {
entry.insert(len as _); entry.insert(len as _);
len as FileId len as u32
} }
} }
}; };
let command = if !physiscal_path.exists() { let command = if !physiscal_path.exists() {
let ImportPath::Git { let ImportPath::Git { link, chk, .. } = path else {
link,
branch,
rev,
tag,
..
} = path
else {
return Err(io::Error::new( return Err(io::Error::new(
io::ErrorKind::NotFound, io::ErrorKind::NotFound,
format!("can't find file: {}", physiscal_path.display()), format!("can't find file: {}", physiscal_path.display()),
@ -328,13 +393,15 @@ pub fn parse_all(threads: usize, root: &str) -> io::Result<Vec<Ast>> {
let root = PathBuf::from_iter([GIT_DEPS_DIR, preprocess_git(link)]); let root = PathBuf::from_iter([GIT_DEPS_DIR, preprocess_git(link)]);
let mut command = std::process::Command::new("git"); let mut command = std::process::Command::new("git");
command command.args(["clone", "--depth", "1"]);
.args(["clone", "--depth", "1"]) if let Some(chk) = chk {
.args(branch.map(|b| ["--branch", b]).into_iter().flatten()) command.args(match chk {
.args(tag.map(|t| ["--tag", t]).into_iter().flatten()) Chk::Branch(b) => ["--branch", b],
.args(rev.map(|r| ["--rev", r]).into_iter().flatten()) Chk::Tag(t) => ["--tag", t],
.arg(link) Chk::Rev(r) => ["--rev", r],
.arg(root); });
}
command.arg(link).arg(root);
Some(command) Some(command)
} else { } else {
None None
@ -394,9 +461,7 @@ pub fn parse_all(threads: usize, root: &str) -> io::Result<Vec<Ast>> {
.collect::<io::Result<Vec<_>>>() .collect::<io::Result<Vec<_>>>()
} }
type HashMap<K, V> = std::collections::HashMap<K, V, FnvBuildHash>; type HashMap<K, V> = std::collections::HashMap<K, V, std::hash::BuildHasherDefault<FnvHasher>>;
type FnvBuildHash = std::hash::BuildHasherDefault<FnvHasher>;
struct FnvHasher(u64); struct FnvHasher(u64);
@ -421,6 +486,70 @@ impl Default for FnvHasher {
} }
} }
#[cfg(test)]
pub fn run_test(
name: &'static str,
ident: &'static str,
input: &'static str,
test: fn(&'static str, &'static str, &mut String),
) {
use std::{io::Write, path::PathBuf};
let filter = std::env::var("PT_FILTER").unwrap_or_default();
if !filter.is_empty() && !name.contains(&filter) {
return;
}
let mut output = String::new();
test(ident, input, &mut output);
let mut root = PathBuf::from(
std::env::var("PT_TEST_ROOT")
.unwrap_or(concat!(env!("CARGO_MANIFEST_DIR"), "/tests").to_string()),
);
root.push(
name.replace("::", "_")
.replace(concat!(env!("CARGO_PKG_NAME"), "_"), ""),
);
root.set_extension("txt");
let expected = std::fs::read_to_string(&root).unwrap_or_default();
if output == expected {
return;
}
if std::env::var("PT_UPDATE").is_ok() {
std::fs::write(&root, output).unwrap();
return;
}
if !root.exists() {
std::fs::create_dir_all(root.parent().unwrap()).unwrap();
std::fs::write(&root, vec![]).unwrap();
}
let mut proc = std::process::Command::new("diff")
.arg("-u")
.arg("--color")
.arg(&root)
.arg("-")
.stdin(std::process::Stdio::piped())
.stdout(std::process::Stdio::inherit())
.spawn()
.unwrap();
proc.stdin
.as_mut()
.unwrap()
.write_all(output.as_bytes())
.unwrap();
proc.wait().unwrap();
panic!("test failed");
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use std::sync::Arc; use std::sync::Arc;

View file

@ -1,48 +0,0 @@
#![allow(unused_macros)]
#[derive(PartialOrd, PartialEq, Ord, Eq, Debug)]
pub enum Level {
Err,
Wrn,
Inf,
Dbg,
}
pub const LOG_LEVEL: Level = match option_env!("LOG_LEVEL") {
Some(val) => match val.as_bytes()[0] {
b'e' => Level::Err,
b'w' => Level::Wrn,
b'i' => Level::Inf,
b'd' => Level::Dbg,
_ => panic!("Invalid log level."),
},
None => {
if cfg!(debug_assertions) {
Level::Dbg
} else {
Level::Err
}
}
};
macro_rules! log {
($level:expr, $fmt:literal $($expr:tt)*) => {
if $level <= $crate::log::LOG_LEVEL {
println!("{:?}: {}", $level, format_args!($fmt $($expr)*));
}
};
($level:expr, $($arg:expr),*) => {
if $level <= $crate::log::LOG_LEVEL {
$(println!("[{}{}{}][{:?}]: {} = {:?}", line!(), column!(), file!(), $level, stringify!($arg), $arg);)*
}
};
}
macro_rules! err { ($($arg:tt)*) => { $crate::log::log!($crate::log::Level::Err, $($arg)*) }; }
macro_rules! wrn { ($($arg:tt)*) => { $crate::log::log!($crate::log::Level::Wrn, $($arg)*) }; }
macro_rules! inf { ($($arg:tt)*) => { $crate::log::log!($crate::log::Level::Inf, $($arg)*) }; }
macro_rules! dbg { ($($arg:tt)*) => { $crate::log::log!($crate::log::Level::Dbg, $($arg)*) }; }
#[allow(unused_imports)]
pub(crate) use {dbg, err, inf, log, wrn};

View file

@ -3,7 +3,7 @@ fn main() -> std::io::Result<()> {
.nth(1) .nth(1)
.unwrap_or_else(|| "main.hb".to_string()); .unwrap_or_else(|| "main.hb".to_string());
let parsed = hblang::parse_all(1, &root)?; let parsed = hblang::parse_from_fs(1, &root)?;
let mut codegen = hblang::codegen::Codegen::default(); let mut codegen = hblang::codegen::Codegen::default();
codegen.files = parsed; codegen.files = parsed;

View file

@ -7,7 +7,7 @@ use std::{
}; };
use crate::{ use crate::{
codegen::bt, codegen,
ident::{self, Ident}, ident::{self, Ident},
lexer::{Lexer, LineMap, Token, TokenKind}, lexer::{Lexer, LineMap, Token, TokenKind},
log, log,
@ -136,16 +136,36 @@ impl<'a, 'b> Parser<'a, 'b> {
break; break;
} }
let checkpoint = self.token.start;
let op = self.next().kind; let op = self.next().kind;
let op_ass = op.assign_op().map(|op| {
// this abomination reparses the left side, so that the desubaring adheres to the
// parser invariants.
let source = self.lexer.slice(0..checkpoint as usize);
let prev_lexer =
std::mem::replace(&mut self.lexer, Lexer::restore(source, fold.pos()));
let prev_token = std::mem::replace(&mut self.token, self.lexer.next());
let clone = self.expr();
self.lexer = prev_lexer;
self.token = prev_token;
(op, clone)
});
let right = self.unit_expr(); let right = self.unit_expr();
let right = self.bin_expr(right, prec); let right = self.bin_expr(right, prec);
let right = self.arena.alloc(right); let right = self.arena.alloc(right);
let left = self.arena.alloc(fold); let left = self.arena.alloc(fold);
if let Some(op) = op.assign_op() { if let Some((op, clone)) = op_ass {
self.flag_idents(*left, idfl::MUTABLE); self.flag_idents(*left, idfl::MUTABLE);
let right = Expr::BinOp { left, op, right };
let right = Expr::BinOp {
left: self.arena.alloc(clone),
op,
right,
};
fold = Expr::BinOp { fold = Expr::BinOp {
left, left,
op: TokenKind::Assign, op: TokenKind::Assign,
@ -163,10 +183,10 @@ impl<'a, 'b> Parser<'a, 'b> {
} }
fn resolve_ident(&mut self, token: Token, decl: bool) -> (Ident, u16) { fn resolve_ident(&mut self, token: Token, decl: bool) -> (Ident, u16) {
let is_ct = self.token.kind == TokenKind::CtIdent; let is_ct = token.kind == TokenKind::CtIdent;
let name = self.lexer.slice(token.range()); let name = self.lexer.slice(token.range());
if let Some(builtin) = bt::from_str(name) { if let Some(builtin) = codegen::ty::from_str(name) {
return (builtin, 0); return (builtin, 0);
} }
@ -270,7 +290,12 @@ impl<'a, 'b> Parser<'a, 'b> {
T::Ident | T::CtIdent => { T::Ident | T::CtIdent => {
let (id, index) = self.resolve_ident(token, self.token.kind == T::Decl); let (id, index) = self.resolve_ident(token, self.token.kind == T::Decl);
let name = self.move_str(token); let name = self.move_str(token);
E::Ident { name, id, index } E::Ident {
pos: token.start,
name,
id,
index,
}
} }
T::If => E::If { T::If => E::If {
pos: token.start, pos: token.start,
@ -526,13 +551,14 @@ macro_rules! generate_expr {
pub fn pos(&self) -> Pos { pub fn pos(&self) -> Pos {
#[allow(unused_variables)] #[allow(unused_variables)]
match self { match self {
$(Self::$variant { $($field),* } => generate_expr!(@first $(($field),)*).posi(self),)* $(Self::$variant { $($field),* } => generate_expr!(@first $(($field),)*).posi(),)*
} }
} }
pub fn used_bytes(&self) -> usize { pub fn used_bytes(&self) -> usize {
match self {$( match self {$(
Self::$variant { $($field,)* } => { Self::$variant { $($field,)* } => {
#[allow(clippy::size_of_ref)]
let fields = [$(($field as *const _ as usize - self as *const _ as usize, std::mem::size_of_val($field)),)*]; let fields = [$(($field as *const _ as usize - self as *const _ as usize, std::mem::size_of_val($field)),)*];
let (last, size) = fields.iter().copied().max().unwrap(); let (last, size) = fields.iter().copied().max().unwrap();
last + size last + size
@ -572,6 +598,7 @@ generate_expr! {
val: Option<&'a Self>, val: Option<&'a Self>,
}, },
Ident { Ident {
pos: Pos,
id: Ident, id: Ident,
name: &'a str, name: &'a str,
index: u16, index: u16,
@ -636,21 +663,17 @@ generate_expr! {
} }
trait Poser { trait Poser {
fn posi(self, expr: &Expr) -> Pos; fn posi(self) -> Pos;
} }
impl Poser for Pos { impl Poser for Pos {
fn posi(self, expr: &Expr) -> Pos { fn posi(self) -> Pos {
if matches!(expr, Expr::Ident { .. }) {
ident::pos(self)
} else {
self self
} }
}
} }
impl<'a> Poser for &Expr<'a> { impl<'a> Poser for &Expr<'a> {
fn posi(self, _: &Expr) -> Pos { fn posi(self) -> Pos {
self.pos() self.pos()
} }
} }
@ -905,11 +928,11 @@ impl ExprRef {
} }
pub fn get<'a>(&self, from: &'a Ast) -> Option<&'a Expr<'a>> { pub fn get<'a>(&self, from: &'a Ast) -> Option<&'a Expr<'a>> {
ArenaChunk::contains(from.mem.base, self.0.as_ptr() as _).then_some(())?;
// SAFETY: the pointer is or was a valid reference in the past, if it points within one of // SAFETY: the pointer is or was a valid reference in the past, if it points within one of
// arenas regions, it muts be walid, since arena does not give invalid pointers to its // arenas regions, it muts be walid, since arena does not give invalid pointers to its
// allocations // allocations
ArenaChunk::contains(from.mem.base, self.0.as_ptr() as _) Some(unsafe { { self.0 }.as_ref() })
.then(|| unsafe { { self.0 }.as_ref() })
} }
} }

View file

@ -1,64 +0,0 @@
#![cfg(test)]
pub fn run_test(
name: &'static str,
ident: &'static str,
input: &'static str,
test: fn(&'static str, &'static str, &mut String),
) {
use std::{io::Write, path::PathBuf};
let filter = std::env::var("PT_FILTER").unwrap_or_default();
if !filter.is_empty() && !name.contains(&filter) {
return;
}
let mut output = String::new();
test(ident, input, &mut output);
let mut root = PathBuf::from(
std::env::var("PT_TEST_ROOT")
.unwrap_or(concat!(env!("CARGO_MANIFEST_DIR"), "/tests").to_string()),
);
root.push(
name.replace("::", "_")
.replace(concat!(env!("CARGO_PKG_NAME"), "_"), ""),
);
root.set_extension("txt");
let expected = std::fs::read_to_string(&root).unwrap_or_default();
if output == expected {
return;
}
if std::env::var("PT_UPDATE").is_ok() {
std::fs::write(&root, output).unwrap();
return;
}
if !root.exists() {
std::fs::create_dir_all(root.parent().unwrap()).unwrap();
std::fs::write(&root, vec![]).unwrap();
}
let mut proc = std::process::Command::new("diff")
.arg("-u")
.arg("--color")
.arg(&root)
.arg("-")
.stdin(std::process::Stdio::piped())
.stdout(std::process::Stdio::inherit())
.spawn()
.unwrap();
proc.stdin
.as_mut()
.unwrap()
.write_all(output.as_bytes())
.unwrap();
proc.wait().unwrap();
panic!();
}

View file

View file

@ -1,3 +1,3 @@
code size: 471 code size: 522
ret: 512 ret: 512
status: Ok(()) status: Ok(())

View file

@ -1,3 +1,3 @@
code size: 513 code size: 525
ret: 0 ret: 0
status: Ok(()) status: Ok(())

View file

@ -0,0 +1,3 @@
code size: 296
ret: 0
status: Ok(())

View file

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

View file

@ -1,3 +1,3 @@
code size: 283 code size: 277
ret: 50 ret: 50
status: Ok(()) status: Ok(())

View file

@ -1,3 +1,3 @@
code size: 265 code size: 268
ret: 55 ret: 55
status: Ok(()) status: Ok(())

View file

@ -1,3 +1,3 @@
code size: 322 code size: 330
ret: 0 ret: 0
status: Ok(()) status: Ok(())

View file

@ -1,3 +1,3 @@
code size: 778 code size: 786
ret: 10 ret: 10
status: Ok(()) status: Ok(())

View file

@ -1,3 +1,3 @@
code size: 417 code size: 460
ret: 3 ret: 3
status: Ok(()) status: Ok(())

View file

@ -1,3 +1,3 @@
code size: 110 code size: 116
ret: 0 ret: 0
status: Ok(()) status: Ok(())

View file

@ -101,7 +101,7 @@ impl Memory for HostMemory {
count: usize, count: usize,
) -> Result<(), StoreError> { ) -> Result<(), StoreError> {
debug_assert!(addr.get() != 0); debug_assert!(addr.get() != 0);
debug_assert!(source != core::ptr::null()); debug_assert!(!source.is_null());
unsafe { core::ptr::copy(source, addr.get() as *mut u8, count) } unsafe { core::ptr::copy(source, addr.get() as *mut u8, count) }
Ok(()) Ok(())
} }