some stuff

This commit is contained in:
mlokr 2024-05-20 14:11:58 +02:00
parent aef9951bc5
commit e494785f93
26 changed files with 998 additions and 680 deletions

View file

@ -7,6 +7,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut generated = String::new(); let mut generated = String::new();
writeln!(generated, "#![allow(dead_code)]")?;
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

@ -0,0 +1,17 @@
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

@ -7,7 +7,7 @@ main := fn(): int {
return *b - 2; return *b - 2;
} }
modify := fn(a: *int): void { modify := fn(a: ^int): void {
*a = 2; *a = 2;
return; return;
} }

View file

@ -17,7 +17,7 @@ main := fn(): int {
return 0; return 0;
} }
pass := fn(t: *Ty): int { pass := fn(t: ^Ty): int {
return t.a - t.b; return t.a - t.b;
} }

File diff suppressed because it is too large Load diff

View file

@ -11,10 +11,6 @@ impl Token {
pub fn range(&self) -> std::ops::Range<usize> { pub fn range(&self) -> std::ops::Range<usize> {
self.start as usize..self.end as usize self.start as usize..self.end as usize
} }
pub fn len(&self) -> u32 {
self.end - self.start
}
} }
macro_rules! gen_token_kind { macro_rules! gen_token_kind {

View file

@ -1,4 +1,5 @@
#![feature(if_let_guard)] #![feature(if_let_guard)]
#![feature(slice_partition_dedup)]
#![feature(noop_waker)] #![feature(noop_waker)]
#![feature(portable_simd)] #![feature(portable_simd)]
#![feature(iter_collect_into)] #![feature(iter_collect_into)]
@ -7,9 +8,17 @@
#![feature(ptr_metadata)] #![feature(ptr_metadata)]
#![feature(const_mut_refs)] #![feature(const_mut_refs)]
#![feature(slice_ptr_get)] #![feature(slice_ptr_get)]
#![allow(dead_code)]
use std::{collections::VecDeque, sync::Mutex}; use std::{
collections::VecDeque,
io::{self, Read},
path::{Path, PathBuf},
sync::Mutex,
};
use parser::Ast;
use crate::parser::FileId;
#[macro_export] #[macro_export]
macro_rules! run_tests { macro_rules! run_tests {
@ -128,6 +137,288 @@ impl<T> TaskQueueInner<T> {
} }
} }
pub fn parse_all(threads: usize) -> io::Result<Vec<Ast>> {
const GIT_DEPS_DIR: &str = "git-deps";
enum ImportPath<'a> {
Root {
path: &'a str,
},
Rel {
path: &'a str,
},
Git {
link: &'a str,
path: &'a str,
branch: Option<&'a str>,
tag: Option<&'a str>,
rev: Option<&'a str>,
},
}
impl<'a> TryFrom<&'a str> for ImportPath<'a> {
type Error = ParseImportError;
fn try_from(value: &'a str) -> Result<Self, Self::Error> {
let (prefix, path) = value.split_once(':').unwrap_or(("", value));
match prefix {
"" => Ok(Self::Root { path }),
"rel" => Ok(Self::Rel { path }),
"git" => {
let (link, path) =
path.split_once(':').ok_or(ParseImportError::ExpectedPath)?;
let (link, params) = link.split_once('?').unwrap_or((link, ""));
let [mut branch, mut tag, mut rev] = [None; 3];
for (key, value) in params.split('&').filter_map(|s| s.split_once('=')) {
match key {
"branch" => branch = Some(value),
"tag" => tag = Some(value),
"rev" => rev = Some(value),
_ => return Err(ParseImportError::UnexpectedParam),
}
}
Ok(Self::Git {
link,
path,
branch,
tag,
rev,
})
}
_ => Err(ParseImportError::InvalidPrefix),
}
}
}
fn preprocess_git(link: &str) -> &str {
let link = link.strip_prefix("https://").unwrap_or(link);
link.strip_suffix(".git").unwrap_or(link)
}
impl<'a> ImportPath<'a> {
fn resolve(&self, from: &str) -> Result<PathBuf, CantLoadFile> {
match self {
Self::Root { path } => Ok(Path::new(path).to_owned()),
Self::Rel { path } => {
let path = PathBuf::from_iter([from, path]);
match path.canonicalize() {
Ok(path) => Ok(path),
Err(e) => Err(CantLoadFile(path, e)),
}
}
Self::Git { path, link, .. } => {
let link = preprocess_git(link);
Ok(PathBuf::from_iter([GIT_DEPS_DIR, link, path]))
}
}
}
}
#[derive(Debug)]
enum ParseImportError {
ExpectedPath,
InvalidPrefix,
UnexpectedParam,
}
impl std::fmt::Display for ParseImportError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::ExpectedPath => write!(f, "expected path"),
Self::InvalidPrefix => write!(
f,
"invalid prefix, expected one of rel, \
git or none followed by colon"
),
Self::UnexpectedParam => {
write!(f, "unexpected git param, expected branch, tag or rev")
}
}
}
}
impl std::error::Error for ParseImportError {}
impl From<ParseImportError> for io::Error {
fn from(e: ParseImportError) -> Self {
io::Error::new(io::ErrorKind::InvalidInput, e)
}
}
#[derive(Debug)]
struct CantLoadFile(PathBuf, io::Error);
impl std::fmt::Display for CantLoadFile {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "can't load file: {}", self.0.display())
}
}
impl std::error::Error for CantLoadFile {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(&self.1)
}
}
impl From<CantLoadFile> for io::Error {
fn from(e: CantLoadFile) -> Self {
io::Error::new(io::ErrorKind::InvalidData, e)
}
}
#[derive(Debug)]
struct InvalidFileData(std::str::Utf8Error);
impl std::fmt::Display for InvalidFileData {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "invalid file data")
}
}
impl std::error::Error for InvalidFileData {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(&self.0)
}
}
impl From<InvalidFileData> for io::Error {
fn from(e: InvalidFileData) -> Self {
io::Error::new(io::ErrorKind::InvalidData, e)
}
}
type Task = (FileId, PathBuf, Option<std::process::Command>);
let seen = Mutex::new(HashMap::<PathBuf, FileId>::default());
let tasks = TaskQueue::<Task>::new(threads);
let ast = Mutex::new(Vec::<io::Result<Ast>>::new());
let loader = |path: &str, from: &str| {
let path = ImportPath::try_from(path)?;
let physiscal_path = path.resolve(from)?;
let id = {
let mut seen = seen.lock().unwrap();
let len = seen.len();
match seen.entry(physiscal_path.clone()) {
std::collections::hash_map::Entry::Occupied(entry) => {
return Ok(*entry.get());
}
std::collections::hash_map::Entry::Vacant(entry) => {
entry.insert(len as _);
len as FileId
}
}
};
let command = if !physiscal_path.exists() {
let ImportPath::Git {
link,
branch,
rev,
tag,
..
} = path
else {
return Err(io::Error::new(
io::ErrorKind::NotFound,
format!("can't find file: {}", physiscal_path.display()),
));
};
let root = PathBuf::from_iter([GIT_DEPS_DIR, preprocess_git(link)]);
let mut command = std::process::Command::new("git");
command
.args(["clone", "--depth", "1"])
.args(branch.map(|b| ["--branch", b]).into_iter().flatten())
.args(tag.map(|t| ["--tag", t]).into_iter().flatten())
.args(rev.map(|r| ["--rev", r]).into_iter().flatten())
.arg(link)
.arg(root);
Some(command)
} else {
None
};
tasks.push((id, physiscal_path, command));
Ok(id)
};
let execute_task = |(_, path, command): Task, buffer: &mut Vec<u8>| {
if let Some(mut command) = command {
let output = command.output()?;
if !output.status.success() {
let msg = format!(
"git command failed: {}",
String::from_utf8_lossy(&output.stderr)
);
return Err(io::Error::new(io::ErrorKind::Other, msg));
}
}
let path = path.to_str().ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidData,
format!("path contains invalid characters: {}", path.display()),
)
})?;
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))
};
let thread = || {
let mut buffer = Vec::new();
while let Some(task @ (indx, ..)) = tasks.pop() {
let res = execute_task(task, &mut buffer);
buffer.clear();
let mut ast = ast.lock().unwrap();
let len = ast.len().max(indx as usize + 1);
ast.resize_with(len, || Err(io::ErrorKind::InvalidData.into()));
ast[indx as usize] = res;
}
};
std::thread::scope(|s| (0..threads).for_each(|_| _ = s.spawn(thread)));
ast.into_inner()
.unwrap()
.into_iter()
.collect::<io::Result<Vec<_>>>()
}
type HashMap<K, V> = std::collections::HashMap<K, V, FnvBuildHash>;
type FnvBuildHash = std::hash::BuildHasherDefault<FnvHasher>;
struct FnvHasher(u64);
impl std::hash::Hasher for FnvHasher {
fn finish(&self) -> u64 {
self.0
}
fn write(&mut self, bytes: &[u8]) {
self.0 = bytes.iter().fold(self.0, |hash, &byte| {
let mut hash = hash;
hash ^= byte as u64;
hash = hash.wrapping_mul(0x100000001B3);
hash
});
}
}
impl Default for FnvHasher {
fn default() -> Self {
Self(0xCBF29CE484222325)
}
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use std::sync::Arc; use std::sync::Arc;

View file

@ -1,18 +1,15 @@
use std::{ use std::{
cell::{Cell, UnsafeCell}, cell::{Cell, UnsafeCell},
collections::HashMap, io,
io::{self, Read},
ops::{Deref, Not}, ops::{Deref, Not},
path::{Path, PathBuf},
ptr::NonNull, ptr::NonNull,
sync::{atomic::AtomicUsize, Mutex}, sync::atomic::AtomicUsize,
}; };
use crate::{ use crate::{
codegen::bt, codegen::bt,
ident::{self, Ident}, ident::{self, Ident},
lexer::{Lexer, LineMap, Token, TokenKind}, lexer::{Lexer, LineMap, Token, TokenKind},
TaskQueue,
}; };
pub type Pos = u32; pub type Pos = u32;
@ -21,267 +18,25 @@ pub type Symbols = Vec<Symbol>;
pub type FileId = u32; pub type FileId = u32;
pub type Loader<'a> = &'a (dyn Fn(&str, &str) -> io::Result<FileId> + 'a); pub type Loader<'a> = &'a (dyn Fn(&str, &str) -> io::Result<FileId> + 'a);
pub const MUTABLE: IdentFlags = 1 << (std::mem::size_of::<IdentFlags>() * 8 - 1); pub mod idfl {
pub const REFERENCED: IdentFlags = 1 << (std::mem::size_of::<IdentFlags>() * 8 - 2); use super::*;
const GIT_DEPS_DIR: &str = "git-deps";
pub fn parse_all(threads: usize) -> io::Result<Vec<Ast>> { macro_rules! flags {
enum ImportPath<'a> { ($($name:ident,)*) => {
Root { $(pub const $name: IdentFlags = 1 << (std::mem::size_of::<IdentFlags>() * 8 - 1 - ${index(0)});)*
path: &'a str, pub const ALL: IdentFlags = 0 $(| $name)*;
},
Rel {
path: &'a str,
},
Git {
link: &'a str,
path: &'a str,
branch: Option<&'a str>,
tag: Option<&'a str>,
rev: Option<&'a str>,
},
}
impl<'a> TryFrom<&'a str> for ImportPath<'a> {
type Error = ParseImportError;
fn try_from(value: &'a str) -> Result<Self, Self::Error> {
let (prefix, path) = value.split_once(':').unwrap_or(("", value));
match prefix {
"" => Ok(Self::Root { path }),
"rel" => Ok(Self::Rel { path }),
"git" => {
let (link, path) =
path.split_once(':').ok_or(ParseImportError::ExpectedPath)?;
let (link, params) = link.split_once('?').unwrap_or((link, ""));
let [mut branch, mut tag, mut rev] = [None; 3];
for (key, value) in params.split('&').filter_map(|s| s.split_once('=')) {
match key {
"branch" => branch = Some(value),
"tag" => tag = Some(value),
"rev" => rev = Some(value),
_ => return Err(ParseImportError::UnexpectedParam),
}
}
Ok(Self::Git {
link,
path,
branch,
tag,
rev,
})
}
_ => Err(ParseImportError::InvalidPrefix),
}
}
}
fn preprocess_git(link: &str) -> &str {
let link = link.strip_prefix("https://").unwrap_or(link);
link.strip_suffix(".git").unwrap_or(link)
}
impl<'a> ImportPath<'a> {
fn resolve(&self, from: &str) -> Result<PathBuf, CantLoadFile> {
match self {
Self::Root { path } => Ok(Path::new(path).to_owned()),
Self::Rel { path } => {
let path = PathBuf::from_iter([from, path]);
match path.canonicalize() {
Ok(path) => Ok(path),
Err(e) => Err(CantLoadFile(path, e)),
}
}
Self::Git { path, link, .. } => {
let link = preprocess_git(link);
Ok(PathBuf::from_iter([GIT_DEPS_DIR, link, path]))
}
}
}
}
#[derive(Debug)]
enum ParseImportError {
ExpectedPath,
InvalidPrefix,
ExpectedGitAlias,
UnexpectedParam,
}
impl std::fmt::Display for ParseImportError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::ExpectedPath => write!(f, "expected path"),
Self::InvalidPrefix => write!(
f,
"invalid prefix, expected one of rel, \
git or none followed by colon"
),
Self::ExpectedGitAlias => write!(f, "expected git alias as ':<alias>$'"),
Self::UnexpectedParam => {
write!(f, "unexpected git param, expected branch, tag or rev")
}
}
}
}
impl std::error::Error for ParseImportError {}
impl From<ParseImportError> for io::Error {
fn from(e: ParseImportError) -> Self {
io::Error::new(io::ErrorKind::InvalidInput, e)
}
}
#[derive(Debug)]
struct CantLoadFile(PathBuf, io::Error);
impl std::fmt::Display for CantLoadFile {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "can't load file: {}", self.0.display())
}
}
impl std::error::Error for CantLoadFile {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(&self.1)
}
}
impl From<CantLoadFile> for io::Error {
fn from(e: CantLoadFile) -> Self {
io::Error::new(io::ErrorKind::InvalidData, e)
}
}
#[derive(Debug)]
struct InvalidFileData(std::str::Utf8Error);
impl std::fmt::Display for InvalidFileData {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "invalid file data")
}
}
impl std::error::Error for InvalidFileData {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(&self.0)
}
}
impl From<InvalidFileData> for io::Error {
fn from(e: InvalidFileData) -> Self {
io::Error::new(io::ErrorKind::InvalidData, e)
}
}
type Task = (FileId, PathBuf, Option<std::process::Command>);
let seen = Mutex::new(HashMap::<PathBuf, FileId>::new());
let tasks = TaskQueue::<Task>::new(threads);
let ast = Mutex::new(Vec::<io::Result<Ast>>::new());
let loader = |path: &str, from: &str| {
let path = ImportPath::try_from(path)?;
let physiscal_path = path.resolve(from)?;
let id = {
let mut seen = seen.lock().unwrap();
let len = seen.len();
match seen.entry(physiscal_path.clone()) {
std::collections::hash_map::Entry::Occupied(entry) => {
return Ok(*entry.get());
}
std::collections::hash_map::Entry::Vacant(entry) => {
entry.insert(len as _);
len as FileId
}
}
}; };
}
let command = if !physiscal_path.exists() { flags! {
let ImportPath::Git { MUTABLE,
link, REFERENCED,
branch, CAPTURED,
rev, }
tag,
..
} = path
else {
return Err(io::Error::new(
io::ErrorKind::NotFound,
format!("can't find file: {}", physiscal_path.display()),
));
};
let root = PathBuf::from_iter([GIT_DEPS_DIR, preprocess_git(link)]); pub fn index(i: IdentFlags) -> u32 {
i & !ALL
let mut command = std::process::Command::new("git"); }
command
.args(["clone", "--depth", "1"])
.args(branch.map(|b| ["--branch", b]).into_iter().flatten())
.args(tag.map(|t| ["--tag", t]).into_iter().flatten())
.args(rev.map(|r| ["--rev", r]).into_iter().flatten())
.arg(link)
.arg(root);
Some(command)
} else {
None
};
tasks.push((id, physiscal_path, command));
Ok(id)
};
let execute_task = |(_, path, command): Task, buffer: &mut Vec<u8>| {
if let Some(mut command) = command {
let output = command.output()?;
if !output.status.success() {
let msg = format!(
"git command failed: {}",
String::from_utf8_lossy(&output.stderr)
);
return Err(io::Error::new(io::ErrorKind::Other, msg));
}
}
let path = path.to_str().ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidData,
format!("path contains invalid characters: {}", path.display()),
)
})?;
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))
};
let thread = || {
let mut buffer = Vec::new();
while let Some(task @ (indx, ..)) = tasks.pop() {
let res = execute_task(task, &mut buffer);
buffer.clear();
let mut ast = ast.lock().unwrap();
let len = ast.len().max(indx as usize + 1);
ast.resize_with(len, || Err(io::ErrorKind::InvalidData.into()));
ast[indx as usize] = res;
}
};
std::thread::scope(|s| (0..threads).for_each(|_| _ = s.spawn(thread)));
ast.into_inner()
.unwrap()
.into_iter()
.collect::<io::Result<Vec<_>>>()
}
pub fn ident_flag_index(flag: IdentFlags) -> u32 {
flag & !(MUTABLE | REFERENCED)
} }
pub fn no_loader(_: &str, _: &str) -> io::Result<FileId> { pub fn no_loader(_: &str, _: &str) -> io::Result<FileId> {
@ -301,13 +56,15 @@ struct ScopeIdent {
} }
pub struct Parser<'a, 'b> { pub struct Parser<'a, 'b> {
path: &'b str, path: &'b str,
loader: Loader<'b>, loader: Loader<'b>,
lexer: Lexer<'b>, lexer: Lexer<'b>,
arena: &'b Arena<'a>, arena: &'b Arena<'a>,
token: Token, token: Token,
idents: Vec<ScopeIdent>, idents: Vec<ScopeIdent>,
symbols: &'b mut Symbols, symbols: &'b mut Symbols,
ns_bound: usize,
captured: Vec<Ident>,
} }
impl<'a, 'b> Parser<'a, 'b> { impl<'a, 'b> Parser<'a, 'b> {
@ -321,6 +78,8 @@ impl<'a, 'b> Parser<'a, 'b> {
arena, arena,
idents: Vec::new(), idents: Vec::new(),
symbols, symbols,
ns_bound: 0,
captured: Vec::new(),
} }
} }
@ -382,7 +141,7 @@ impl<'a, 'b> Parser<'a, 'b> {
let left = &*self.arena.alloc(fold); let left = &*self.arena.alloc(fold);
if let Some(op) = op.assign_op() { if let Some(op) = op.assign_op() {
self.flag_idents(*left, MUTABLE); self.flag_idents(*left, idfl::MUTABLE);
let right = Expr::BinOp { left, op, right }; let right = Expr::BinOp { left, op, right };
fold = Expr::BinOp { fold = Expr::BinOp {
left, left,
@ -392,7 +151,7 @@ impl<'a, 'b> Parser<'a, 'b> {
} else { } else {
fold = Expr::BinOp { left, right, op }; fold = Expr::BinOp { left, right, op };
if op == TokenKind::Assign { if op == TokenKind::Assign {
self.flag_idents(*left, MUTABLE); self.flag_idents(*left, idfl::MUTABLE);
} }
} }
} }
@ -400,41 +159,25 @@ impl<'a, 'b> Parser<'a, 'b> {
fold fold
} }
fn try_resolve_builtin(name: &str) -> Option<Ident> {
// FIXME: we actually do this the second time in the codegen
Some(match name {
"int" | "i64" => bt::INT,
"i8" => bt::I8,
"i16" => bt::I16,
"i32" => bt::I32,
"u8" => bt::U8,
"u16" => bt::U16,
"uint" | "u32" => bt::U32,
"bool" => bt::BOOL,
"void" => bt::VOID,
"never" => bt::NEVER,
_ => return None,
})
}
fn resolve_ident(&mut self, token: Token, decl: bool) -> (Ident, u32) { fn resolve_ident(&mut self, token: Token, decl: bool) -> (Ident, u32) {
let name = self.lexer.slice(token.range()); let name = self.lexer.slice(token.range());
if let Some(builtin) = Self::try_resolve_builtin(name) { if let Some(builtin) = bt::from_str(name) {
return (builtin, 0); return (builtin, 0);
} }
let id = match self let (i, id) = match self
.idents .idents
.iter_mut() .iter_mut()
.rfind(|elem| self.lexer.slice(ident::range(elem.ident)) == name) .enumerate()
.rfind(|(_, elem)| self.lexer.slice(ident::range(elem.ident)) == name)
{ {
Some(elem) if decl && elem.declared => { Some((_, elem)) if decl && elem.declared => {
self.report(format_args!("redeclaration of identifier: {name}")) self.report(format_args!("redeclaration of identifier: {name}"))
} }
Some(elem) => { Some((i, elem)) => {
elem.flags += 1; elem.flags += 1;
elem (i, elem)
} }
None => { None => {
let id = ident::new(token.start, name.len() as _); let id = ident::new(token.start, name.len() as _);
@ -443,13 +186,17 @@ impl<'a, 'b> Parser<'a, 'b> {
declared: false, declared: false,
flags: 0, flags: 0,
}); });
self.idents.last_mut().unwrap() (self.idents.len() - 1, self.idents.last_mut().unwrap())
} }
}; };
id.declared |= decl; id.declared |= decl;
if self.ns_bound > i && id.declared {
id.flags |= idfl::CAPTURED;
self.captured.push(id.ident);
}
(id.ident, ident_flag_index(id.flags)) (id.ident, idfl::index(id.flags))
} }
fn move_str(&mut self, range: Token) -> &'a str { fn move_str(&mut self, range: Token) -> &'a str {
@ -460,6 +207,8 @@ impl<'a, 'b> Parser<'a, 'b> {
use {Expr as E, TokenKind as T}; use {Expr as E, TokenKind as T};
let frame = self.idents.len(); let frame = self.idents.len();
let token = self.next(); let token = self.next();
let prev_boundary = self.ns_bound;
let prev_captured = self.captured.len();
let mut expr = match token.kind { let mut expr = match token.kind {
T::Driective if self.lexer.slice(token.range()) == "use" => { T::Driective if self.lexer.slice(token.range()) == "use" => {
self.expect_advance(TokenKind::LParen); self.expect_advance(TokenKind::LParen);
@ -489,8 +238,8 @@ impl<'a, 'b> Parser<'a, 'b> {
value: true, value: true,
}, },
T::Struct => E::Struct { T::Struct => E::Struct {
pos: token.start, fields: {
fields: { self.ns_bound = self.idents.len();
self.expect_advance(T::LBrace); self.expect_advance(T::LBrace);
self.collect_list(T::Comma, T::RBrace, |s| { self.collect_list(T::Comma, T::RBrace, |s| {
let name = s.expect_advance(T::Ident); let name = s.expect_advance(T::Ident);
@ -499,6 +248,20 @@ impl<'a, 'b> Parser<'a, 'b> {
(s.move_str(name), ty) (s.move_str(name), ty)
}) })
}, },
captured: {
self.ns_bound = prev_boundary;
self.captured[prev_captured..].sort_unstable();
let preserved = self.captured[prev_captured..].partition_dedup().0.len();
self.captured.truncate(prev_captured + preserved);
self.arena.alloc_slice(&self.captured[prev_captured..])
},
pos: {
if self.ns_bound == 0 {
// we might save some memory
self.captured.clear();
}
token.start
},
}, },
T::Ident => { T::Ident => {
let (id, index) = self.resolve_ident(token, self.token.kind == T::Decl); let (id, index) = self.resolve_ident(token, self.token.kind == T::Decl);
@ -543,13 +306,13 @@ impl<'a, 'b> Parser<'a, 'b> {
}, },
body: self.ptr_expr(), body: self.ptr_expr(),
}, },
T::Band | T::Mul => E::UnOp { T::Band | T::Mul | T::Xor => E::UnOp {
pos: token.start, pos: token.start,
op: token.kind, op: token.kind,
val: { val: {
let expr = self.ptr_unit_expr(); let expr = self.ptr_unit_expr();
if token.kind == T::Band { if token.kind == T::Band {
self.flag_idents(*expr, REFERENCED); self.flag_idents(*expr, idfl::REFERENCED);
} }
expr expr
}, },
@ -775,8 +538,9 @@ pub enum Expr<'a> {
val: &'a Self, val: &'a Self,
}, },
Struct { Struct {
pos: Pos, pos: Pos,
fields: &'a [(&'a str, Self)], fields: &'a [(&'a str, Self)],
captured: &'a [Ident],
}, },
Ctor { Ctor {
pos: Pos, pos: Pos,
@ -1041,13 +805,13 @@ impl Ast {
unsafe { self.0.as_ref() } unsafe { self.0.as_ref() }
} }
pub fn find_decl(&self, id: Result<Ident, &str>) -> Option<&Expr> { pub fn find_decl(&self, id: Result<Ident, &str>) -> Option<(&Expr, Ident)> {
self.exprs().iter().find_map(|expr| match expr { self.exprs().iter().find_map(|expr| match expr {
Expr::BinOp { Expr::BinOp {
left: &Expr::Ident { id: iden, name, .. }, left: &Expr::Ident { id: iden, name, .. },
op: TokenKind::Decl, op: TokenKind::Decl,
.. ..
} if Ok(iden) == id || Err(name) == id => Some(expr), } if Ok(iden) == id || Err(name) == id => Some((expr, iden)),
_ => None, _ => None,
}) })
} }
@ -1207,17 +971,6 @@ impl ArenaChunk {
unsafe { std::ptr::read(curr.add(Self::NEXT_OFFSET) as *mut _) } unsafe { std::ptr::read(curr.add(Self::NEXT_OFFSET) as *mut _) }
} }
fn reset(prev: *mut u8) -> Option<Self> {
if prev.is_null() {
return None;
}
Some(Self {
base: prev,
end: unsafe { prev.add(Self::CHUNK_SIZE) },
})
}
fn alloc(&mut self, layout: std::alloc::Layout) -> Option<NonNull<u8>> { fn alloc(&mut self, layout: std::alloc::Layout) -> Option<NonNull<u8>> {
let padding = self.end as usize - (self.end as usize & !(layout.align() - 1)); let padding = self.end as usize - (self.end as usize & !(layout.align() - 1));
let size = layout.size() + padding; let size = layout.size() + padding;

View file

@ -11,7 +11,10 @@ pub fn run_test(name: &'static str, input: &'static str, test: fn(&'static str,
let mut output = String::new(); let mut output = String::new();
test(input, &mut output); test(input, &mut output);
let mut root = PathBuf::from(std::env::var("PT_TEST_ROOT").unwrap_or("tests".to_string())); let mut root = PathBuf::from(
std::env::var("PT_TEST_ROOT")
.unwrap_or(concat!(env!("CARGO_MANIFEST_DIR"), "/tests").to_string()),
);
root.push( root.push(
name.replace("::", "_") name.replace("::", "_")
.replace(concat!(env!("CARGO_PKG_NAME"), "_"), ""), .replace(concat!(env!("CARGO_PKG_NAME"), "_"), ""),

View file

@ -1,3 +1,3 @@
code size: 200 code size: 189
ret: 1 ret: 1
status: Ok(()) status: Ok(())

View file

@ -1,3 +1,3 @@
code size: 499 code size: 477
ret: 512 ret: 512
status: Ok(()) status: Ok(())

View file

@ -1,4 +1,4 @@
ev: Ecall ev: Ecall
code size: 204 code size: 182
ret: 0 ret: 0
status: Ok(()) status: Ok(())

View file

@ -1,3 +1,3 @@
code size: 96 code size: 85
ret: 1 ret: 1
status: Ok(()) status: Ok(())

View file

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

View file

@ -1,3 +1,3 @@
code size: 314 code size: 281
ret: 33 ret: 33
status: Ok(()) status: Ok(())

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,3 +1,3 @@
code size: 464 code size: 420
ret: 3 ret: 3
status: Ok(()) status: Ok(())

View file

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