implementing comptime constants
This commit is contained in:
parent
b922dbd232
commit
aef9951bc5
|
@ -3,7 +3,7 @@ global_var := 10;
|
|||
complex_global_var := fib(global_var) - 5;
|
||||
|
||||
fib := fn(n: int): int {
|
||||
if n <= 2 {
|
||||
if 2 > n {
|
||||
return n;
|
||||
}
|
||||
return fib(n - 1) + fib(n - 2);
|
||||
|
@ -12,3 +12,4 @@ fib := fn(n: int): int {
|
|||
main := fn(): int {
|
||||
return complex_global_var;
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,3 +1,5 @@
|
|||
use std::simd::cmp::SimdPartialEq;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub struct Token {
|
||||
pub kind: TokenKind,
|
||||
|
@ -290,85 +292,117 @@ impl<'a> Lexer<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn line_col(bytes: &[u8], mut start: u32) -> (usize, usize) {
|
||||
bytes
|
||||
pub fn line_col(bytes: &[u8], pos: u32) -> (usize, usize) {
|
||||
bytes[..pos as usize]
|
||||
.split(|&b| b == b'\n')
|
||||
.map(<[u8]>::len)
|
||||
.enumerate()
|
||||
.find_map(|(i, line)| {
|
||||
if start < line.len() as u32 {
|
||||
return Some((i + 1, start as usize + 1));
|
||||
}
|
||||
start -= line.len() as u32 + 1;
|
||||
None
|
||||
})
|
||||
.last()
|
||||
.map(|(line, col)| (line + 1, col + 1))
|
||||
.unwrap_or((1, 1))
|
||||
}
|
||||
|
||||
impl<'a> Iterator for Lexer<'a> {
|
||||
type Item = Token;
|
||||
pub struct LineMap {
|
||||
lines: Box<[u8]>,
|
||||
}
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
use TokenKind as T;
|
||||
loop {
|
||||
let mut start = self.pos;
|
||||
let kind = match self.advance()? {
|
||||
b'\n' | b'\r' | b'\t' | b' ' => continue,
|
||||
b'0'..=b'9' => {
|
||||
while let Some(b'0'..=b'9') = self.peek() {
|
||||
self.advance();
|
||||
}
|
||||
T::Number
|
||||
impl LineMap {
|
||||
pub fn line_col(&self, mut pos: u32) -> (usize, usize) {
|
||||
let mut line = 1;
|
||||
|
||||
let mut iter = self.lines.iter().copied();
|
||||
|
||||
while let Some(mut len) = iter.next() {
|
||||
let mut acc = 0;
|
||||
while len & 0x80 != 0 {
|
||||
acc = (acc << 7) | (len & 0x7F) as u32;
|
||||
len = iter.next().unwrap();
|
||||
}
|
||||
acc += len as u32;
|
||||
|
||||
if pos < acc {
|
||||
break;
|
||||
}
|
||||
pos = pos.saturating_sub(acc);
|
||||
line += 1;
|
||||
}
|
||||
|
||||
(line, pos as usize + 1)
|
||||
}
|
||||
|
||||
pub fn new(input: &str) -> Self {
|
||||
let bytes = input.as_bytes();
|
||||
let (start, simd_mid, end) = bytes.as_simd::<16>();
|
||||
|
||||
let query = std::simd::u8x16::splat(b'\n');
|
||||
|
||||
let nl_count = start.iter().map(|&b| (b == b'\n') as usize).sum::<usize>()
|
||||
+ simd_mid
|
||||
.iter()
|
||||
.map(|s| s.simd_eq(query).to_bitmask().count_ones())
|
||||
.sum::<u32>() as usize
|
||||
+ end.iter().map(|&b| (b == b'\n') as usize).sum::<usize>();
|
||||
|
||||
let mut lines = Vec::with_capacity(nl_count);
|
||||
let mut last_nl = 0;
|
||||
|
||||
let handle_rem = |offset: usize, bytes: &[u8], last_nl: &mut usize, lines: &mut Vec<u8>| {
|
||||
bytes
|
||||
.iter()
|
||||
.copied()
|
||||
.enumerate()
|
||||
.filter_map(|(i, b)| (b == b'\n').then_some(i + offset))
|
||||
.for_each(|i| {
|
||||
lines.push((i - *last_nl + 1) as u8);
|
||||
*last_nl = i + 1;
|
||||
});
|
||||
};
|
||||
|
||||
handle_rem(0, start, &mut last_nl, &mut lines);
|
||||
|
||||
for (i, simd) in simd_mid.iter().enumerate() {
|
||||
let mask = simd.simd_eq(query);
|
||||
let mut mask = mask.to_bitmask();
|
||||
while mask != 0 {
|
||||
let idx = mask.trailing_zeros() as usize + i * 16 + start.len();
|
||||
let mut len = idx - last_nl + 1;
|
||||
while len >= 0x80 {
|
||||
lines.push((0x80 | (len & 0x7F)) as u8);
|
||||
len >>= 7;
|
||||
}
|
||||
c @ (b'a'..=b'z' | b'A'..=b'Z' | b'_' | b'@') => {
|
||||
while let Some(b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'_') = self.peek() {
|
||||
self.advance();
|
||||
}
|
||||
lines.push(len as u8);
|
||||
last_nl = idx + 1;
|
||||
mask &= mask - 1;
|
||||
}
|
||||
}
|
||||
|
||||
if c == b'@' {
|
||||
start += 1;
|
||||
T::Driective
|
||||
} else {
|
||||
let ident = &self.bytes[start as usize..self.pos as usize];
|
||||
T::from_ident(ident)
|
||||
}
|
||||
}
|
||||
b':' if self.advance_if(b'=') => T::Decl,
|
||||
b':' => T::Colon,
|
||||
b',' => T::Comma,
|
||||
b'.' if self.advance_if(b'{') => T::Ctor,
|
||||
b'.' if self.advance_if(b'(') => T::Tupl,
|
||||
b'.' => T::Dot,
|
||||
b';' => T::Semi,
|
||||
b'!' if self.advance_if(b'=') => T::Ne,
|
||||
b'=' if self.advance_if(b'=') => T::Eq,
|
||||
b'=' => T::Assign,
|
||||
b'<' if self.advance_if(b'=') => T::Le,
|
||||
b'<' => T::Lt,
|
||||
b'>' if self.advance_if(b'=') => T::Ge,
|
||||
b'>' => T::Gt,
|
||||
b'+' => T::Add,
|
||||
b'-' => T::Sub,
|
||||
b'*' => T::Mul,
|
||||
b'/' => T::Div,
|
||||
b'&' => T::Band,
|
||||
b'(' => T::LParen,
|
||||
b')' => T::RParen,
|
||||
b'{' => T::LBrace,
|
||||
b'}' => T::RBrace,
|
||||
_ => T::Error,
|
||||
};
|
||||
handle_rem(bytes.len() - end.len(), end, &mut last_nl, &mut lines);
|
||||
|
||||
return Some(Token {
|
||||
kind,
|
||||
start,
|
||||
end: self.pos,
|
||||
});
|
||||
Self {
|
||||
lines: Box::from(lines),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
fn map_lines(input: &'static str, _: &mut String) {
|
||||
let line_map = super::LineMap::new(input);
|
||||
for i in 0..input.len() {
|
||||
assert_eq!(
|
||||
line_map.line_col(i as u32),
|
||||
//line_map.line_col(i as u32),
|
||||
super::line_col(input.as_bytes(), i as u32)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
crate::run_tests! { map_lines:
|
||||
empty_file => "";
|
||||
log_line => " ".repeat(1000).leak();
|
||||
this_file => &include_str!("parser.rs")[..1000];
|
||||
}
|
||||
|
||||
fn lex(input: &'static str, output: &mut String) {
|
||||
use {
|
||||
super::{Lexer, TokenKind as T},
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
#![feature(if_let_guard)]
|
||||
#![feature(noop_waker)]
|
||||
#![feature(portable_simd)]
|
||||
#![feature(iter_collect_into)]
|
||||
#![feature(macro_metavar_expr)]
|
||||
#![feature(let_chains)]
|
||||
|
@ -7,11 +9,7 @@
|
|||
#![feature(slice_ptr_get)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
use std::{
|
||||
collections::{HashSet, VecDeque},
|
||||
io,
|
||||
sync::{mpsc, Arc, Mutex},
|
||||
};
|
||||
use std::{collections::VecDeque, sync::Mutex};
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! run_tests {
|
||||
|
|
|
@ -14,13 +14,11 @@ fn main() -> io::Result<()> {
|
|||
.skip(1)
|
||||
.map(|path| std::fs::read_to_string(&path).map(|src| (path, src)))
|
||||
.collect::<io::Result<Vec<_>>>()?;
|
||||
let mut arena = parser::Arena::default();
|
||||
|
||||
let mut codegen = codegen::Codegen::default();
|
||||
for (path, content) in files.iter() {
|
||||
let mut parser = parser::Parser::new(&arena, &mut codegen.symbols, &parser::no_loader);
|
||||
let file = parser.file(&path, content.as_str());
|
||||
codegen.file(path, content.as_bytes(), file);
|
||||
arena.clear();
|
||||
codegen.files = vec![parser::Ast::new(&path, &content, &parser::no_loader)];
|
||||
codegen.generate();
|
||||
}
|
||||
codegen.dump(&mut std::io::stdout())
|
||||
}
|
||||
|
|
|
@ -1,20 +1,17 @@
|
|||
use std::{
|
||||
cell::{Cell, UnsafeCell},
|
||||
collections::{HashMap, HashSet},
|
||||
collections::HashMap,
|
||||
io::{self, Read},
|
||||
ops::Not,
|
||||
ops::{Deref, Not},
|
||||
path::{Path, PathBuf},
|
||||
ptr::NonNull,
|
||||
sync::{
|
||||
atomic::{AtomicU32, AtomicUsize},
|
||||
Mutex,
|
||||
},
|
||||
sync::{atomic::AtomicUsize, Mutex},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
codegen::bt,
|
||||
ident::{self, Ident},
|
||||
lexer::{Lexer, Token, TokenKind},
|
||||
lexer::{Lexer, LineMap, Token, TokenKind},
|
||||
TaskQueue,
|
||||
};
|
||||
|
||||
|
@ -22,13 +19,13 @@ pub type Pos = u32;
|
|||
pub type IdentFlags = u32;
|
||||
pub type Symbols = Vec<Symbol>;
|
||||
pub type FileId = u32;
|
||||
pub type Loader<'a> = &'a (dyn Fn(&str, &str) -> io::Result<Option<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 const REFERENCED: IdentFlags = 1 << std::mem::size_of::<IdentFlags>() * 8 - 2;
|
||||
pub const MUTABLE: IdentFlags = 1 << (std::mem::size_of::<IdentFlags>() * 8 - 1);
|
||||
pub const REFERENCED: IdentFlags = 1 << (std::mem::size_of::<IdentFlags>() * 8 - 2);
|
||||
const GIT_DEPS_DIR: &str = "git-deps";
|
||||
|
||||
pub fn parse_all(root: &str, threads: usize) -> io::Result<Vec<Ast>> {
|
||||
pub fn parse_all(threads: usize) -> io::Result<Vec<Ast>> {
|
||||
enum ImportPath<'a> {
|
||||
Root {
|
||||
path: &'a str,
|
||||
|
@ -86,9 +83,9 @@ pub fn parse_all(root: &str, threads: usize) -> io::Result<Vec<Ast>> {
|
|||
}
|
||||
|
||||
impl<'a> ImportPath<'a> {
|
||||
fn resolve(&self, from: &str, root: &str) -> Result<PathBuf, CantLoadFile> {
|
||||
fn resolve(&self, from: &str) -> Result<PathBuf, CantLoadFile> {
|
||||
match self {
|
||||
Self::Root { path } => Ok(PathBuf::from_iter([root, path])),
|
||||
Self::Root { path } => Ok(Path::new(path).to_owned()),
|
||||
Self::Rel { path } => {
|
||||
let path = PathBuf::from_iter([from, path]);
|
||||
match path.canonicalize() {
|
||||
|
@ -98,7 +95,7 @@ pub fn parse_all(root: &str, threads: usize) -> io::Result<Vec<Ast>> {
|
|||
}
|
||||
Self::Git { path, link, .. } => {
|
||||
let link = preprocess_git(link);
|
||||
Ok(PathBuf::from_iter([root, GIT_DEPS_DIR, link, path]))
|
||||
Ok(PathBuf::from_iter([GIT_DEPS_DIR, link, path]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -179,17 +176,7 @@ pub fn parse_all(root: &str, threads: usize) -> io::Result<Vec<Ast>> {
|
|||
}
|
||||
}
|
||||
|
||||
enum Task {
|
||||
LoadFile {
|
||||
id: FileId,
|
||||
physiscal_path: PathBuf,
|
||||
},
|
||||
FetchGit {
|
||||
id: FileId,
|
||||
physiscal_path: PathBuf,
|
||||
command: std::process::Command,
|
||||
},
|
||||
}
|
||||
type Task = (FileId, PathBuf, Option<std::process::Command>);
|
||||
|
||||
let seen = Mutex::new(HashMap::<PathBuf, FileId>::new());
|
||||
let tasks = TaskQueue::<Task>::new(threads);
|
||||
|
@ -198,14 +185,14 @@ pub fn parse_all(root: &str, threads: usize) -> io::Result<Vec<Ast>> {
|
|||
let loader = |path: &str, from: &str| {
|
||||
let path = ImportPath::try_from(path)?;
|
||||
|
||||
let physiscal_path = path.resolve(from, root)?;
|
||||
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(Some(*entry.get()));
|
||||
return Ok(*entry.get());
|
||||
}
|
||||
std::collections::hash_map::Entry::Vacant(entry) => {
|
||||
entry.insert(len as _);
|
||||
|
@ -214,91 +201,74 @@ pub fn parse_all(root: &str, threads: usize) -> io::Result<Vec<Ast>> {
|
|||
}
|
||||
};
|
||||
|
||||
if physiscal_path.exists() {
|
||||
tasks.push(Task::LoadFile { id, physiscal_path });
|
||||
return Ok(Some(id));
|
||||
}
|
||||
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 ImportPath::Git {
|
||||
link,
|
||||
path,
|
||||
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
|
||||
};
|
||||
|
||||
let root = PathBuf::from_iter([root, 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);
|
||||
|
||||
tasks.push(Task::FetchGit {
|
||||
id,
|
||||
physiscal_path,
|
||||
command,
|
||||
});
|
||||
|
||||
Ok(Some(id))
|
||||
tasks.push((id, physiscal_path, command));
|
||||
Ok(id)
|
||||
};
|
||||
|
||||
let load_from_path = |path: &Path, buffer: &mut Vec<u8>| -> io::Result<Ast> {
|
||||
let path = path.to_str().ok_or_else(|| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"path contains invalid characters",
|
||||
)
|
||||
})?;
|
||||
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 execute_task = |task: Task, buffer: &mut Vec<u8>| match task {
|
||||
Task::LoadFile { id, physiscal_path } => (id, load_from_path(&physiscal_path, buffer)),
|
||||
Task::FetchGit {
|
||||
id,
|
||||
physiscal_path,
|
||||
mut command,
|
||||
} => {
|
||||
let output = match command.output() {
|
||||
Ok(output) => output,
|
||||
Err(e) => return (id, Err(e)),
|
||||
};
|
||||
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 (id, Err(io::Error::new(io::ErrorKind::Other, msg)));
|
||||
return Err(io::Error::new(io::ErrorKind::Other, msg));
|
||||
}
|
||||
(id, load_from_path(&physiscal_path, buffer))
|
||||
}
|
||||
|
||||
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) = tasks.pop() {
|
||||
let (indx, res) = execute_task(task, &mut buffer);
|
||||
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;
|
||||
|
||||
buffer.clear();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -314,10 +284,11 @@ pub fn ident_flag_index(flag: IdentFlags) -> u32 {
|
|||
flag & !(MUTABLE | REFERENCED)
|
||||
}
|
||||
|
||||
pub fn no_loader(_: &str, _: &str) -> io::Result<Option<FileId>> {
|
||||
Ok(None)
|
||||
pub fn no_loader(_: &str, _: &str) -> io::Result<FileId> {
|
||||
Err(io::ErrorKind::NotFound.into())
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Symbol {
|
||||
pub name: Ident,
|
||||
pub flags: IdentFlags,
|
||||
|
@ -490,6 +461,21 @@ impl<'a, 'b> Parser<'a, 'b> {
|
|||
let frame = self.idents.len();
|
||||
let token = self.next();
|
||||
let mut expr = match token.kind {
|
||||
T::Driective if self.lexer.slice(token.range()) == "use" => {
|
||||
self.expect_advance(TokenKind::LParen);
|
||||
let str = self.expect_advance(TokenKind::String);
|
||||
self.expect_advance(TokenKind::RParen);
|
||||
let path = self.lexer.slice(str.range()).trim_matches('"');
|
||||
|
||||
E::Mod {
|
||||
pos: token.start,
|
||||
path: self.arena.alloc_str(path),
|
||||
id: match (self.loader)(path, self.path) {
|
||||
Ok(id) => id,
|
||||
Err(e) => self.report(format_args!("error loading dependency: {e:#?}")),
|
||||
},
|
||||
}
|
||||
}
|
||||
T::Driective => E::Directive {
|
||||
pos: token.start,
|
||||
name: self.move_str(token),
|
||||
|
@ -562,7 +548,9 @@ impl<'a, 'b> Parser<'a, 'b> {
|
|||
op: token.kind,
|
||||
val: {
|
||||
let expr = self.ptr_unit_expr();
|
||||
self.flag_idents(*expr, REFERENCED);
|
||||
if token.kind == T::Band {
|
||||
self.flag_idents(*expr, REFERENCED);
|
||||
}
|
||||
expr
|
||||
},
|
||||
},
|
||||
|
@ -808,6 +796,11 @@ pub enum Expr<'a> {
|
|||
name: &'a str,
|
||||
args: &'a [Self],
|
||||
},
|
||||
Mod {
|
||||
pos: Pos,
|
||||
id: FileId,
|
||||
path: &'a str,
|
||||
},
|
||||
}
|
||||
|
||||
impl<'a> Expr<'a> {
|
||||
|
@ -816,6 +809,7 @@ impl<'a> Expr<'a> {
|
|||
Self::Call { func, .. } => func.pos(),
|
||||
Self::Ident { id, .. } => ident::pos(*id),
|
||||
Self::Break { pos }
|
||||
| Self::Mod { pos, .. }
|
||||
| Self::Directive { pos, .. }
|
||||
| Self::Continue { pos }
|
||||
| Self::Closure { pos, .. }
|
||||
|
@ -837,13 +831,13 @@ impl<'a> Expr<'a> {
|
|||
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> = Cell::new(0);
|
||||
static INDENT: Cell<usize> = const { Cell::new(0) };
|
||||
}
|
||||
|
||||
fn fmt_list<'a, T>(
|
||||
fn fmt_list<T>(
|
||||
f: &mut std::fmt::Formatter,
|
||||
end: &str,
|
||||
list: &'a [T],
|
||||
list: &[T],
|
||||
fmt: impl Fn(&T, &mut std::fmt::Formatter) -> std::fmt::Result,
|
||||
) -> std::fmt::Result {
|
||||
let first = &mut true;
|
||||
|
@ -881,9 +875,8 @@ impl<'a> std::fmt::Display for Expr<'a> {
|
|||
}
|
||||
|
||||
match *self {
|
||||
Self::Field { target, field } => {
|
||||
write!(f, "{}.{field}", Postfix(target))
|
||||
}
|
||||
Self::Mod { path, .. } => write!(f, "@mod(\"{path}\")"),
|
||||
Self::Field { target, field } => write!(f, "{}.{field}", Postfix(target)),
|
||||
Self::Directive { name, args, .. } => {
|
||||
write!(f, "@{name}(")?;
|
||||
fmt_list(f, ")", args, std::fmt::Display::fmt)
|
||||
|
@ -980,12 +973,14 @@ impl<'a> std::fmt::Display for Expr<'a> {
|
|||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct AstInner<T: ?Sized> {
|
||||
pub struct AstInner<T: ?Sized> {
|
||||
ref_count: AtomicUsize,
|
||||
mem: ArenaChunk,
|
||||
exprs: *const [Expr<'static>],
|
||||
path: String,
|
||||
symbols: T,
|
||||
|
||||
pub path: Box<str>,
|
||||
pub nlines: LineMap,
|
||||
pub symbols: T,
|
||||
}
|
||||
|
||||
impl AstInner<[Symbol]> {
|
||||
|
@ -1006,19 +1001,25 @@ impl AstInner<[Symbol]> {
|
|||
|
||||
let layout = Self::layout(syms.len());
|
||||
|
||||
let ptr = unsafe { std::alloc::alloc(layout) };
|
||||
let inner: *mut Self = std::ptr::from_raw_parts_mut(ptr as *mut _, syms.len());
|
||||
unsafe {
|
||||
*(inner as *mut AstInner<()>) = AstInner {
|
||||
ref_count: AtomicUsize::new(1),
|
||||
mem: ArenaChunk::default(),
|
||||
exprs,
|
||||
path: path.to_owned(),
|
||||
symbols: (),
|
||||
};
|
||||
let ptr = std::alloc::alloc(layout);
|
||||
let inner: *mut Self = std::ptr::from_raw_parts_mut(ptr as *mut _, syms.len());
|
||||
|
||||
std::ptr::write(
|
||||
inner as *mut AstInner<()>,
|
||||
AstInner {
|
||||
ref_count: AtomicUsize::new(1),
|
||||
mem: arena.chunk.into_inner(),
|
||||
exprs,
|
||||
path: path.into(),
|
||||
nlines: LineMap::new(content),
|
||||
symbols: (),
|
||||
},
|
||||
);
|
||||
std::ptr::addr_of_mut!((*inner).symbols)
|
||||
.as_mut_ptr()
|
||||
.copy_from_nonoverlapping(syms.as_ptr(), syms.len());
|
||||
|
||||
NonNull::new_unchecked(inner)
|
||||
}
|
||||
}
|
||||
|
@ -1036,17 +1037,44 @@ impl Ast {
|
|||
unsafe { &*self.inner().exprs }
|
||||
}
|
||||
|
||||
pub fn symbols(&self) -> &[Symbol] {
|
||||
&self.inner().symbols
|
||||
}
|
||||
|
||||
pub fn path(&self) -> &str {
|
||||
&self.inner().path
|
||||
}
|
||||
|
||||
fn inner(&self) -> &AstInner<[Symbol]> {
|
||||
unsafe { self.0.as_ref() }
|
||||
}
|
||||
|
||||
pub fn find_decl(&self, id: Result<Ident, &str>) -> Option<&Expr> {
|
||||
self.exprs().iter().find_map(|expr| match expr {
|
||||
Expr::BinOp {
|
||||
left: &Expr::Ident { id: iden, name, .. },
|
||||
op: TokenKind::Decl,
|
||||
..
|
||||
} if Ok(iden) == id || Err(name) == id => Some(expr),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Ast {
|
||||
fn default() -> Self {
|
||||
Self(AstInner::new("", "", &no_loader))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(packed)]
|
||||
pub struct ExprRef(NonNull<Expr<'static>>);
|
||||
|
||||
impl ExprRef {
|
||||
pub fn new(expr: &Expr) -> Self {
|
||||
Self(NonNull::from(expr).cast())
|
||||
}
|
||||
|
||||
pub fn get<'a>(&self, from: &'a Ast) -> Option<&'a Expr<'a>> {
|
||||
// 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
|
||||
// allocations
|
||||
ArenaChunk::contains(from.mem.base, self.0.as_ptr() as _)
|
||||
.then(|| unsafe { { self.0 }.as_ref() })
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for Ast {}
|
||||
|
@ -1079,6 +1107,14 @@ impl Drop for Ast {
|
|||
}
|
||||
}
|
||||
|
||||
impl Deref for Ast {
|
||||
type Target = AstInner<[Symbol]>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.inner()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Arena<'a> {
|
||||
chunk: UnsafeCell<ArenaChunk>,
|
||||
|
@ -1092,6 +1128,10 @@ impl<'a> Arena<'a> {
|
|||
}
|
||||
|
||||
pub fn alloc<T>(&self, value: T) -> &'a mut T {
|
||||
if std::mem::size_of::<T>() == 0 {
|
||||
return unsafe { NonNull::dangling().as_mut() };
|
||||
}
|
||||
|
||||
let layout = std::alloc::Layout::new::<T>();
|
||||
let ptr = self.alloc_low(layout);
|
||||
unsafe { ptr.cast::<T>().write(value) };
|
||||
|
@ -1099,6 +1139,10 @@ impl<'a> Arena<'a> {
|
|||
}
|
||||
|
||||
pub fn alloc_slice<T: Copy>(&self, slice: &[T]) -> &'a mut [T] {
|
||||
if slice.is_empty() || std::mem::size_of::<T>() == 0 {
|
||||
return &mut [];
|
||||
}
|
||||
|
||||
let layout = std::alloc::Layout::array::<T>(slice.len()).unwrap();
|
||||
let ptr = self.alloc_low(layout);
|
||||
unsafe {
|
||||
|
@ -1109,23 +1153,6 @@ impl<'a> Arena<'a> {
|
|||
unsafe { std::slice::from_raw_parts_mut(ptr.as_ptr() as _, slice.len()) }
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
let chunk = self.chunk.get_mut();
|
||||
if chunk.base.is_null() {
|
||||
return;
|
||||
}
|
||||
|
||||
loop {
|
||||
let prev = ArenaChunk::prev(chunk.base);
|
||||
if prev.is_null() {
|
||||
break;
|
||||
}
|
||||
chunk.base = prev;
|
||||
}
|
||||
|
||||
chunk.end = unsafe { chunk.base.add(ArenaChunk::PREV_OFFSET) };
|
||||
}
|
||||
|
||||
fn alloc_low(&self, layout: std::alloc::Layout) -> NonNull<u8> {
|
||||
assert!(layout.align() <= ArenaChunk::ALIGN);
|
||||
assert!(layout.size() <= ArenaChunk::CHUNK_SIZE);
|
||||
|
@ -1136,10 +1163,8 @@ impl<'a> Arena<'a> {
|
|||
return ptr;
|
||||
}
|
||||
|
||||
if let Some(prev) = ArenaChunk::reset(ArenaChunk::prev(chunk.base)) {
|
||||
*chunk = prev;
|
||||
} else {
|
||||
*chunk = ArenaChunk::new(chunk.base);
|
||||
unsafe {
|
||||
std::ptr::write(chunk, ArenaChunk::new(chunk.base));
|
||||
}
|
||||
|
||||
chunk.alloc(layout).unwrap()
|
||||
|
@ -1164,18 +1189,13 @@ impl ArenaChunk {
|
|||
const CHUNK_SIZE: usize = 1 << 16;
|
||||
const ALIGN: usize = std::mem::align_of::<Self>();
|
||||
const NEXT_OFFSET: usize = Self::CHUNK_SIZE - std::mem::size_of::<*mut u8>();
|
||||
const PREV_OFFSET: usize = Self::NEXT_OFFSET - std::mem::size_of::<*mut u8>();
|
||||
const LAYOUT: std::alloc::Layout =
|
||||
unsafe { std::alloc::Layout::from_size_align_unchecked(Self::CHUNK_SIZE, Self::ALIGN) };
|
||||
|
||||
fn new(next: *mut u8) -> Self {
|
||||
let base = unsafe { std::alloc::alloc(Self::LAYOUT) };
|
||||
let end = unsafe { base.add(Self::PREV_OFFSET) };
|
||||
if !next.is_null() {
|
||||
Self::set_prev(next, base);
|
||||
}
|
||||
let end = unsafe { base.add(Self::NEXT_OFFSET) };
|
||||
Self::set_next(base, next);
|
||||
Self::set_prev(base, std::ptr::null_mut());
|
||||
Self { base, end }
|
||||
}
|
||||
|
||||
|
@ -1183,21 +1203,10 @@ impl ArenaChunk {
|
|||
unsafe { std::ptr::write(curr.add(Self::NEXT_OFFSET) as *mut _, next) };
|
||||
}
|
||||
|
||||
fn set_prev(curr: *mut u8, prev: *mut u8) {
|
||||
unsafe { std::ptr::write(curr.add(Self::PREV_OFFSET) as *mut _, prev) };
|
||||
}
|
||||
|
||||
fn next(curr: *mut u8) -> *mut u8 {
|
||||
unsafe { std::ptr::read(curr.add(Self::NEXT_OFFSET) as *mut _) }
|
||||
}
|
||||
|
||||
fn prev(curr: *mut u8) -> *mut u8 {
|
||||
if curr.is_null() {
|
||||
return std::ptr::null_mut();
|
||||
}
|
||||
unsafe { std::ptr::read(curr.add(Self::PREV_OFFSET) as *mut _) }
|
||||
}
|
||||
|
||||
fn reset(prev: *mut u8) -> Option<Self> {
|
||||
if prev.is_null() {
|
||||
return None;
|
||||
|
@ -1218,19 +1227,17 @@ impl ArenaChunk {
|
|||
unsafe { self.end = self.end.sub(size) };
|
||||
unsafe { Some(NonNull::new_unchecked(self.end)) }
|
||||
}
|
||||
|
||||
fn contains(base: *mut u8, arg: *mut u8) -> bool {
|
||||
!base.is_null()
|
||||
&& ((unsafe { base.add(Self::CHUNK_SIZE) } > arg && base <= arg)
|
||||
|| Self::contains(Self::next(base), arg))
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for ArenaChunk {
|
||||
fn drop(&mut self) {
|
||||
let mut current = self.base;
|
||||
|
||||
let mut prev = Self::prev(current);
|
||||
while !prev.is_null() {
|
||||
let next = Self::prev(prev);
|
||||
unsafe { std::alloc::dealloc(prev, Self::LAYOUT) };
|
||||
prev = next;
|
||||
}
|
||||
|
||||
while !current.is_null() {
|
||||
let next = Self::next(current);
|
||||
unsafe { std::alloc::dealloc(current, Self::LAYOUT) };
|
||||
|
@ -1241,16 +1248,14 @@ impl Drop for ArenaChunk {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
fn parse(input: &'static str, output: &mut String) {
|
||||
use std::fmt::Write;
|
||||
let mut arena = super::Arena::default();
|
||||
let arena = super::Arena::default();
|
||||
let mut symbols = Vec::new();
|
||||
let mut parser = super::Parser::new(&arena, &mut symbols, &super::no_loader);
|
||||
for expr in parser.file(input, "test") {
|
||||
writeln!(output, "{}", expr).unwrap();
|
||||
}
|
||||
arena.clear();
|
||||
}
|
||||
|
||||
crate::run_tests! { parse:
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
ev: Ecall
|
||||
code size: 217
|
||||
code size: 204
|
||||
ret: 0
|
||||
status: Ok(())
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
code size: 657
|
||||
code size: 569
|
||||
ret: 0
|
||||
status: Ok(())
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
code size: 308
|
||||
ret: 44
|
||||
code size: 314
|
||||
ret: 33
|
||||
status: Ok(())
|
||||
|
|
3
hblang/tests/codegen_tests_global_variables.txt
Normal file
3
hblang/tests/codegen_tests_global_variables.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
code size: 305
|
||||
ret: 50
|
||||
status: Ok(())
|
|
@ -1,3 +1,3 @@
|
|||
code size: 281
|
||||
ret: 16
|
||||
code size: 287
|
||||
ret: 55
|
||||
status: Ok(())
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
code size: 379
|
||||
code size: 366
|
||||
ret: 0
|
||||
status: Ok(())
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
code size: 461
|
||||
code size: 464
|
||||
ret: 3
|
||||
status: Ok(())
|
||||
|
|
|
@ -78,3 +78,37 @@ impl From<StoreError> for crate::VmRunError {
|
|||
Self::StoreAccessEx(value.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct HostMemory;
|
||||
impl Memory for HostMemory {
|
||||
#[inline]
|
||||
unsafe fn load(
|
||||
&mut self,
|
||||
addr: Address,
|
||||
target: *mut u8,
|
||||
count: usize,
|
||||
) -> Result<(), LoadError> {
|
||||
unsafe { core::ptr::copy(addr.get() as *const u8, target, count) }
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn store(
|
||||
&mut self,
|
||||
addr: Address,
|
||||
source: *const u8,
|
||||
count: usize,
|
||||
) -> Result<(), StoreError> {
|
||||
debug_assert!(addr.get() != 0);
|
||||
debug_assert!(source != core::ptr::null());
|
||||
unsafe { core::ptr::copy(source, addr.get() as *mut u8, count) }
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn prog_read<T: Copy>(&mut self, addr: Address) -> T {
|
||||
debug_assert!(addr.get() != 0);
|
||||
unsafe { core::ptr::read(addr.get() as *const T) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
|
||||
let mut vm = unsafe {
|
||||
Vm::<_, 0>::new(
|
||||
mem::HostMemory,
|
||||
hbvm::mem::HostMemory,
|
||||
Address::new(mmap.as_ptr().add(stack.len()) as u64),
|
||||
)
|
||||
};
|
||||
|
|
|
@ -1,37 +1,5 @@
|
|||
use std::alloc::Layout;
|
||||
|
||||
use hbvm::mem::{Address, LoadError, Memory, StoreError};
|
||||
|
||||
pub struct HostMemory;
|
||||
impl Memory for HostMemory {
|
||||
#[inline]
|
||||
unsafe fn load(
|
||||
&mut self,
|
||||
addr: Address,
|
||||
target: *mut u8,
|
||||
count: usize,
|
||||
) -> Result<(), LoadError> {
|
||||
unsafe { core::ptr::copy(addr.get() as *const u8, target, count) }
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn store(
|
||||
&mut self,
|
||||
addr: Address,
|
||||
source: *const u8,
|
||||
count: usize,
|
||||
) -> Result<(), StoreError> {
|
||||
unsafe { core::ptr::copy(source, addr.get() as *mut u8, count) }
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn prog_read<T: Copy>(&mut self, addr: Address) -> T {
|
||||
unsafe { core::ptr::read(addr.get() as *const T) }
|
||||
}
|
||||
}
|
||||
|
||||
const STACK_SIZE: usize = 2; // MiB
|
||||
type Stack = [u8; 1024 * 1024 * STACK_SIZE];
|
||||
|
||||
|
|
0
tests/codegen_tests_arithmetic.txt
Normal file
0
tests/codegen_tests_arithmetic.txt
Normal file
0
tests/codegen_tests_directives.txt
Normal file
0
tests/codegen_tests_directives.txt
Normal file
0
tests/codegen_tests_example.txt
Normal file
0
tests/codegen_tests_example.txt
Normal file
Loading…
Reference in a new issue