2024-07-08 00:22:53 -05:00
|
|
|
use {
|
|
|
|
crate::{
|
|
|
|
codegen,
|
|
|
|
ident::{self, Ident},
|
2024-09-01 14:15:29 -05:00
|
|
|
lexer::{self, Lexer, Token, TokenKind},
|
2024-07-08 00:22:53 -05:00
|
|
|
},
|
|
|
|
std::{
|
|
|
|
cell::{Cell, UnsafeCell},
|
|
|
|
io,
|
|
|
|
ops::{Deref, Not},
|
|
|
|
ptr::NonNull,
|
|
|
|
sync::atomic::AtomicUsize,
|
|
|
|
},
|
2024-05-12 04:52:58 -05:00
|
|
|
};
|
|
|
|
|
2024-05-12 16:19:45 -05:00
|
|
|
pub type Pos = u32;
|
2024-05-16 05:23:37 -05:00
|
|
|
pub type IdentFlags = u32;
|
2024-05-17 12:53:59 -05:00
|
|
|
pub type Symbols = Vec<Symbol>;
|
|
|
|
pub type FileId = u32;
|
2024-07-08 00:22:53 -05:00
|
|
|
pub type IdentIndex = u16;
|
2024-05-19 11:20:42 -05:00
|
|
|
pub type Loader<'a> = &'a (dyn Fn(&str, &str) -> io::Result<FileId> + 'a);
|
2024-05-16 05:23:37 -05:00
|
|
|
|
2024-05-20 07:11:58 -05:00
|
|
|
pub mod idfl {
|
|
|
|
use super::*;
|
2024-05-17 12:53:59 -05:00
|
|
|
|
2024-05-20 07:11:58 -05:00
|
|
|
macro_rules! flags {
|
|
|
|
($($name:ident,)*) => {
|
|
|
|
$(pub const $name: IdentFlags = 1 << (std::mem::size_of::<IdentFlags>() * 8 - 1 - ${index(0)});)*
|
|
|
|
pub const ALL: IdentFlags = 0 $(| $name)*;
|
|
|
|
};
|
2024-05-17 12:53:59 -05:00
|
|
|
}
|
|
|
|
|
2024-05-20 07:11:58 -05:00
|
|
|
flags! {
|
|
|
|
MUTABLE,
|
|
|
|
REFERENCED,
|
2024-06-01 13:30:07 -05:00
|
|
|
COMPTIME,
|
2024-05-17 12:53:59 -05:00
|
|
|
}
|
|
|
|
|
2024-09-09 12:36:53 -05:00
|
|
|
pub fn count(i: IdentFlags) -> IdentIndex {
|
2024-06-01 13:30:07 -05:00
|
|
|
(i & !ALL) as _
|
2024-05-17 12:53:59 -05:00
|
|
|
}
|
2024-05-16 05:23:37 -05:00
|
|
|
}
|
|
|
|
|
2024-05-19 11:20:42 -05:00
|
|
|
pub fn no_loader(_: &str, _: &str) -> io::Result<FileId> {
|
|
|
|
Err(io::ErrorKind::NotFound.into())
|
2024-05-17 12:53:59 -05:00
|
|
|
}
|
|
|
|
|
2024-05-19 11:20:42 -05:00
|
|
|
#[derive(Debug)]
|
2024-05-17 12:53:59 -05:00
|
|
|
pub struct Symbol {
|
2024-07-08 00:22:53 -05:00
|
|
|
pub name: Ident,
|
2024-05-17 12:53:59 -05:00
|
|
|
pub flags: IdentFlags,
|
|
|
|
}
|
|
|
|
|
2024-06-01 13:30:07 -05:00
|
|
|
#[derive(Clone, Copy)]
|
2024-05-17 12:53:59 -05:00
|
|
|
struct ScopeIdent {
|
2024-07-08 00:22:53 -05:00
|
|
|
ident: Ident,
|
2024-05-12 04:52:58 -05:00
|
|
|
declared: bool,
|
2024-07-08 00:22:53 -05:00
|
|
|
flags: IdentFlags,
|
2024-05-12 04:52:58 -05:00
|
|
|
}
|
2024-05-09 16:41:59 -05:00
|
|
|
|
2024-05-10 08:29:11 -05:00
|
|
|
pub struct Parser<'a, 'b> {
|
2024-07-08 00:22:53 -05:00
|
|
|
path: &'b str,
|
|
|
|
loader: Loader<'b>,
|
|
|
|
lexer: Lexer<'b>,
|
|
|
|
arena: &'b Arena<'a>,
|
|
|
|
token: Token,
|
|
|
|
symbols: &'b mut Symbols,
|
|
|
|
ns_bound: usize,
|
2024-06-25 14:41:12 -05:00
|
|
|
trailing_sep: bool,
|
2024-07-08 00:22:53 -05:00
|
|
|
idents: Vec<ScopeIdent>,
|
|
|
|
captured: Vec<Ident>,
|
2024-05-09 16:41:59 -05:00
|
|
|
}
|
|
|
|
|
2024-05-10 08:29:11 -05:00
|
|
|
impl<'a, 'b> Parser<'a, 'b> {
|
2024-07-19 14:19:03 -05:00
|
|
|
pub fn new(arena: &'b Arena<'a>, symbols: &'b mut Symbols, loader: Loader<'b>) -> Self {
|
2024-05-13 02:38:33 -05:00
|
|
|
let mut lexer = Lexer::new("");
|
2024-05-10 08:29:11 -05:00
|
|
|
Self {
|
2024-05-17 12:53:59 -05:00
|
|
|
loader,
|
|
|
|
token: lexer.next(),
|
2024-05-10 08:29:11 -05:00
|
|
|
lexer,
|
2024-05-13 02:38:33 -05:00
|
|
|
path: "",
|
2024-05-10 08:29:11 -05:00
|
|
|
arena,
|
2024-05-17 12:53:59 -05:00
|
|
|
symbols,
|
2024-05-20 07:11:58 -05:00
|
|
|
ns_bound: 0,
|
2024-06-25 14:41:12 -05:00
|
|
|
trailing_sep: false,
|
2024-06-01 13:30:07 -05:00
|
|
|
idents: Vec::new(),
|
2024-05-20 07:11:58 -05:00
|
|
|
captured: Vec::new(),
|
2024-05-10 08:29:11 -05:00
|
|
|
}
|
2024-05-09 16:41:59 -05:00
|
|
|
}
|
|
|
|
|
2024-05-17 12:53:59 -05:00
|
|
|
pub fn file(&mut self, input: &'b str, path: &'b str) -> &'a [Expr<'a>] {
|
2024-05-13 02:38:33 -05:00
|
|
|
self.path = path;
|
|
|
|
self.lexer = Lexer::new(input);
|
|
|
|
self.token = self.lexer.next();
|
|
|
|
|
2024-07-08 00:22:53 -05:00
|
|
|
let f = self.collect_list(TokenKind::Semi, TokenKind::Eof, |s| s.expr_low(true));
|
2024-05-17 12:53:59 -05:00
|
|
|
|
2024-05-12 04:52:58 -05:00
|
|
|
self.pop_scope(0);
|
|
|
|
let has_undeclared = !self.idents.is_empty();
|
|
|
|
for id in self.idents.drain(..) {
|
|
|
|
let (line, col) = self.lexer.line_col(ident::pos(id.ident));
|
|
|
|
eprintln!(
|
|
|
|
"{}:{}:{} => undeclared identifier: {}",
|
2024-05-12 17:02:32 -05:00
|
|
|
self.path,
|
2024-05-12 04:52:58 -05:00
|
|
|
line,
|
|
|
|
col,
|
|
|
|
self.lexer.slice(ident::range(id.ident))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if has_undeclared {
|
2024-05-17 12:53:59 -05:00
|
|
|
// TODO: we need error recovery
|
2024-05-12 04:52:58 -05:00
|
|
|
unreachable!();
|
|
|
|
}
|
|
|
|
|
|
|
|
f
|
2024-05-09 16:41:59 -05:00
|
|
|
}
|
|
|
|
|
2024-05-10 08:29:11 -05:00
|
|
|
fn next(&mut self) -> Token {
|
|
|
|
std::mem::replace(&mut self.token, self.lexer.next())
|
2024-05-09 16:41:59 -05:00
|
|
|
}
|
|
|
|
|
2024-05-11 10:05:22 -05:00
|
|
|
fn ptr_expr(&mut self) -> &'a Expr<'a> {
|
2024-05-10 08:29:11 -05:00
|
|
|
self.arena.alloc(self.expr())
|
2024-05-09 16:41:59 -05:00
|
|
|
}
|
|
|
|
|
2024-07-08 00:22:53 -05:00
|
|
|
fn expr_low(&mut self, top_level: bool) -> Expr<'a> {
|
2024-05-10 15:54:12 -05:00
|
|
|
let left = self.unit_expr();
|
2024-07-08 00:22:53 -05:00
|
|
|
self.bin_expr(left, 0, top_level)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn expr(&mut self) -> Expr<'a> {
|
|
|
|
self.expr_low(false)
|
2024-05-10 15:54:12 -05:00
|
|
|
}
|
|
|
|
|
2024-07-08 00:22:53 -05:00
|
|
|
fn bin_expr(&mut self, mut fold: Expr<'a>, min_prec: u8, top_level: bool) -> Expr<'a> {
|
2024-05-10 15:54:12 -05:00
|
|
|
loop {
|
|
|
|
let Some(prec) = self.token.kind.precedence() else {
|
|
|
|
break;
|
|
|
|
};
|
|
|
|
|
2024-05-13 06:36:29 -05:00
|
|
|
if prec <= min_prec {
|
2024-05-10 15:54:12 -05:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2024-06-24 10:26:00 -05:00
|
|
|
let checkpoint = self.token.start;
|
2024-05-10 15:54:12 -05:00
|
|
|
let op = self.next().kind;
|
2024-06-01 13:30:07 -05:00
|
|
|
|
2024-07-08 00:22:53 -05:00
|
|
|
if op == TokenKind::Decl {
|
|
|
|
self.declare_rec(&fold, top_level);
|
|
|
|
}
|
|
|
|
|
|
|
|
let op_ass = op.ass_op().map(|op| {
|
2024-06-24 10:26:00 -05:00
|
|
|
// 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)
|
|
|
|
});
|
|
|
|
|
2024-05-10 15:54:12 -05:00
|
|
|
let right = self.unit_expr();
|
2024-07-08 00:22:53 -05:00
|
|
|
let right = self.bin_expr(right, prec, false);
|
2024-06-01 13:30:07 -05:00
|
|
|
let right = self.arena.alloc(right);
|
|
|
|
let left = self.arena.alloc(fold);
|
2024-05-15 03:37:39 -05:00
|
|
|
|
2024-06-24 10:26:00 -05:00
|
|
|
if let Some((op, clone)) = op_ass {
|
2024-05-20 07:11:58 -05:00
|
|
|
self.flag_idents(*left, idfl::MUTABLE);
|
2024-06-24 10:26:00 -05:00
|
|
|
|
2024-07-08 00:22:53 -05:00
|
|
|
let right = Expr::BinOp { left: self.arena.alloc(clone), op, right };
|
|
|
|
fold = Expr::BinOp { left, op: TokenKind::Assign, right: self.arena.alloc(right) };
|
2024-05-15 03:37:39 -05:00
|
|
|
} else {
|
|
|
|
fold = Expr::BinOp { left, right, op };
|
2024-05-16 05:23:37 -05:00
|
|
|
if op == TokenKind::Assign {
|
2024-05-20 07:11:58 -05:00
|
|
|
self.flag_idents(*left, idfl::MUTABLE);
|
2024-05-16 05:23:37 -05:00
|
|
|
}
|
2024-05-15 03:37:39 -05:00
|
|
|
}
|
2024-05-10 15:54:12 -05:00
|
|
|
}
|
|
|
|
|
2024-05-15 03:37:39 -05:00
|
|
|
fold
|
2024-05-10 15:54:12 -05:00
|
|
|
}
|
|
|
|
|
2024-07-08 00:22:53 -05:00
|
|
|
fn declare_rec(&mut self, expr: &Expr, top_level: bool) {
|
|
|
|
let idx = |idx| top_level.not().then_some(idx);
|
|
|
|
match *expr {
|
|
|
|
Expr::Ident { pos, id, index, .. } => self.declare(pos, id, idx(index)),
|
|
|
|
Expr::Ctor { fields, .. } => {
|
|
|
|
for CtorField { value, .. } in fields {
|
|
|
|
self.declare_rec(value, top_level)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => self.report_pos(expr.pos(), "cant declare this shit (yet)"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn declare(&mut self, pos: Pos, id: Ident, index_to_check: Option<IdentIndex>) {
|
|
|
|
if let Some(index) = index_to_check
|
|
|
|
&& index != 0
|
|
|
|
{
|
|
|
|
self.report_pos(
|
|
|
|
pos,
|
|
|
|
format_args!(
|
|
|
|
"out of order declaration not allowed: {}",
|
|
|
|
self.lexer.slice(ident::range(id))
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
let index = self.idents.binary_search_by_key(&id, |s| s.ident).expect("fck up");
|
2024-07-19 14:19:03 -05:00
|
|
|
if std::mem::replace(&mut self.idents[index].declared, true) {
|
2024-07-08 00:22:53 -05:00
|
|
|
self.report_pos(
|
|
|
|
pos,
|
|
|
|
format_args!("redeclaration of identifier: {}", self.lexer.slice(ident::range(id))),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn resolve_ident(&mut self, token: Token) -> (Ident, IdentIndex) {
|
2024-06-24 10:26:00 -05:00
|
|
|
let is_ct = token.kind == TokenKind::CtIdent;
|
2024-05-12 04:52:58 -05:00
|
|
|
let name = self.lexer.slice(token.range());
|
|
|
|
|
2024-06-23 02:09:33 -05:00
|
|
|
if let Some(builtin) = codegen::ty::from_str(name) {
|
2024-05-17 12:53:59 -05:00
|
|
|
return (builtin, 0);
|
2024-05-12 04:52:58 -05:00
|
|
|
}
|
|
|
|
|
2024-05-20 07:11:58 -05:00
|
|
|
let (i, id) = match self
|
2024-05-12 04:52:58 -05:00
|
|
|
.idents
|
|
|
|
.iter_mut()
|
2024-05-20 07:11:58 -05:00
|
|
|
.enumerate()
|
|
|
|
.rfind(|(_, elem)| self.lexer.slice(ident::range(elem.ident)) == name)
|
2024-05-12 04:52:58 -05:00
|
|
|
{
|
2024-05-20 07:11:58 -05:00
|
|
|
Some((i, elem)) => {
|
2024-05-17 12:53:59 -05:00
|
|
|
elem.flags += 1;
|
2024-05-20 07:11:58 -05:00
|
|
|
(i, elem)
|
2024-05-16 05:23:37 -05:00
|
|
|
}
|
2024-05-12 04:52:58 -05:00
|
|
|
None => {
|
|
|
|
let id = ident::new(token.start, name.len() as _);
|
2024-07-08 00:22:53 -05:00
|
|
|
self.idents.push(ScopeIdent { ident: id, declared: false, flags: 0 });
|
2024-05-20 07:11:58 -05:00
|
|
|
(self.idents.len() - 1, self.idents.last_mut().unwrap())
|
2024-05-12 04:52:58 -05:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2024-06-01 13:30:07 -05:00
|
|
|
id.flags |= idfl::COMPTIME * is_ct as u32;
|
|
|
|
if id.declared && self.ns_bound > i {
|
|
|
|
id.flags |= idfl::COMPTIME;
|
2024-05-20 07:11:58 -05:00
|
|
|
self.captured.push(id.ident);
|
|
|
|
}
|
2024-05-12 04:52:58 -05:00
|
|
|
|
2024-09-09 12:36:53 -05:00
|
|
|
(id.ident, idfl::count(id.flags))
|
2024-05-17 12:53:59 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
fn move_str(&mut self, range: Token) -> &'a str {
|
|
|
|
self.arena.alloc_str(self.lexer.slice(range.range()))
|
2024-05-12 04:52:58 -05:00
|
|
|
}
|
|
|
|
|
2024-05-10 15:54:12 -05:00
|
|
|
fn unit_expr(&mut self) -> Expr<'a> {
|
2024-05-11 15:22:08 -05:00
|
|
|
use {Expr as E, TokenKind as T};
|
2024-05-12 04:52:58 -05:00
|
|
|
let frame = self.idents.len();
|
2024-09-09 12:36:53 -05:00
|
|
|
let token @ Token { start: pos, .. } = self.next();
|
2024-05-20 07:11:58 -05:00
|
|
|
let prev_boundary = self.ns_bound;
|
|
|
|
let prev_captured = self.captured.len();
|
2024-05-11 09:04:13 -05:00
|
|
|
let mut expr = match token.kind {
|
2024-09-09 12:36:53 -05:00
|
|
|
T::Ct => E::Ct { pos, value: self.ptr_expr() },
|
2024-06-25 11:39:59 -05:00
|
|
|
T::Directive if self.lexer.slice(token.range()) == "use" => {
|
2024-05-19 11:20:42 -05:00
|
|
|
self.expect_advance(TokenKind::LParen);
|
2024-07-06 08:05:56 -05:00
|
|
|
let str = self.expect_advance(TokenKind::DQuote);
|
2024-05-19 11:20:42 -05:00
|
|
|
self.expect_advance(TokenKind::RParen);
|
|
|
|
let path = self.lexer.slice(str.range()).trim_matches('"');
|
|
|
|
|
|
|
|
E::Mod {
|
2024-09-09 12:36:53 -05:00
|
|
|
pos,
|
2024-05-19 11:20:42 -05:00
|
|
|
path: self.arena.alloc_str(path),
|
2024-07-08 00:22:53 -05:00
|
|
|
id: match (self.loader)(path, self.path) {
|
2024-05-19 11:20:42 -05:00
|
|
|
Ok(id) => id,
|
2024-07-06 08:18:57 -05:00
|
|
|
Err(e) => self.report(format_args!("error loading dependency: {e:#}")),
|
2024-05-19 11:20:42 -05:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
2024-06-25 11:39:59 -05:00
|
|
|
T::Directive => E::Directive {
|
2024-09-09 12:36:53 -05:00
|
|
|
pos,
|
2024-05-17 12:53:59 -05:00
|
|
|
name: self.move_str(token),
|
2024-05-14 16:07:32 -05:00
|
|
|
args: {
|
|
|
|
self.expect_advance(T::LParen);
|
|
|
|
self.collect_list(T::Comma, T::RParen, Self::expr)
|
|
|
|
},
|
|
|
|
},
|
2024-09-09 12:36:53 -05:00
|
|
|
T::True => E::Bool { pos, value: true },
|
|
|
|
T::False => E::Bool { pos, value: false },
|
|
|
|
T::Idk => E::Idk { pos },
|
|
|
|
T::DQuote => E::String { pos, literal: self.move_str(token) },
|
2024-05-12 05:16:40 -05:00
|
|
|
T::Struct => E::Struct {
|
2024-07-08 00:22:53 -05:00
|
|
|
fields: {
|
2024-05-20 07:11:58 -05:00
|
|
|
self.ns_bound = self.idents.len();
|
2024-05-12 05:16:40 -05:00
|
|
|
self.expect_advance(T::LBrace);
|
|
|
|
self.collect_list(T::Comma, T::RBrace, |s| {
|
|
|
|
let name = s.expect_advance(T::Ident);
|
|
|
|
s.expect_advance(T::Colon);
|
2024-06-01 13:30:07 -05:00
|
|
|
(s.move_str(name), s.expr())
|
2024-05-12 05:16:40 -05:00
|
|
|
})
|
|
|
|
},
|
2024-05-20 07:11:58 -05:00
|
|
|
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..])
|
|
|
|
},
|
2024-07-08 00:22:53 -05:00
|
|
|
pos: {
|
2024-05-20 07:11:58 -05:00
|
|
|
if self.ns_bound == 0 {
|
|
|
|
// we might save some memory
|
|
|
|
self.captured.clear();
|
|
|
|
}
|
2024-09-09 12:36:53 -05:00
|
|
|
pos
|
2024-05-20 07:11:58 -05:00
|
|
|
},
|
2024-07-19 14:04:22 -05:00
|
|
|
trailing_comma: std::mem::take(&mut self.trailing_sep),
|
2024-05-12 05:16:40 -05:00
|
|
|
},
|
2024-06-01 13:30:07 -05:00
|
|
|
T::Ident | T::CtIdent => {
|
2024-07-08 00:22:53 -05:00
|
|
|
let (id, index) = self.resolve_ident(token);
|
2024-05-17 12:53:59 -05:00
|
|
|
let name = self.move_str(token);
|
2024-09-09 12:36:53 -05:00
|
|
|
E::Ident { pos, is_ct: token.kind == T::CtIdent, name, id, index }
|
2024-05-12 04:52:58 -05:00
|
|
|
}
|
2024-05-11 15:22:08 -05:00
|
|
|
T::If => E::If {
|
2024-09-09 12:36:53 -05:00
|
|
|
pos,
|
2024-07-08 00:22:53 -05:00
|
|
|
cond: self.ptr_expr(),
|
|
|
|
then: self.ptr_expr(),
|
2024-05-11 15:22:08 -05:00
|
|
|
else_: self.advance_if(T::Else).then(|| self.ptr_expr()),
|
|
|
|
},
|
2024-09-09 12:36:53 -05:00
|
|
|
T::Loop => E::Loop { pos, body: self.ptr_expr() },
|
|
|
|
T::Break => E::Break { pos },
|
|
|
|
T::Continue => E::Continue { pos },
|
2024-05-11 15:22:08 -05:00
|
|
|
T::Return => E::Return {
|
2024-09-09 12:36:53 -05:00
|
|
|
pos,
|
2024-07-19 06:44:35 -05:00
|
|
|
val: (!matches!(
|
|
|
|
self.token.kind,
|
|
|
|
T::Semi | T::RBrace | T::RBrack | T::RParen | T::Comma
|
|
|
|
))
|
|
|
|
.then(|| self.ptr_expr()),
|
2024-05-09 16:41:59 -05:00
|
|
|
},
|
2024-05-11 15:22:08 -05:00
|
|
|
T::Fn => E::Closure {
|
2024-09-09 12:36:53 -05:00
|
|
|
pos,
|
2024-05-11 15:22:08 -05:00
|
|
|
args: {
|
|
|
|
self.expect_advance(T::LParen);
|
|
|
|
self.collect_list(T::Comma, T::RParen, |s| {
|
2024-06-01 13:30:07 -05:00
|
|
|
let name = s.advance_ident();
|
2024-07-08 00:22:53 -05:00
|
|
|
let (id, index) = s.resolve_ident(name);
|
|
|
|
s.declare(name.start, id, None);
|
2024-05-11 15:22:08 -05:00
|
|
|
s.expect_advance(T::Colon);
|
2024-07-19 14:04:22 -05:00
|
|
|
Arg {
|
|
|
|
name: s.move_str(name),
|
|
|
|
is_ct: name.kind == T::CtIdent,
|
|
|
|
id,
|
|
|
|
index,
|
|
|
|
ty: s.expr(),
|
|
|
|
}
|
2024-05-11 09:04:13 -05:00
|
|
|
})
|
2024-05-11 15:22:08 -05:00
|
|
|
},
|
2024-07-08 00:22:53 -05:00
|
|
|
ret: {
|
2024-05-11 15:22:08 -05:00
|
|
|
self.expect_advance(T::Colon);
|
|
|
|
self.ptr_expr()
|
|
|
|
},
|
|
|
|
body: self.ptr_expr(),
|
|
|
|
},
|
2024-09-09 12:36:53 -05:00
|
|
|
T::Ctor => self.ctor(pos, None),
|
|
|
|
T::Tupl => self.tupl(pos, None),
|
2024-07-08 11:08:58 -05:00
|
|
|
T::LBrack => E::Slice {
|
|
|
|
item: self.ptr_unit_expr(),
|
|
|
|
size: self.advance_if(T::Semi).then(|| self.ptr_expr()),
|
|
|
|
pos: {
|
|
|
|
self.expect_advance(T::RBrack);
|
2024-09-09 12:36:53 -05:00
|
|
|
pos
|
2024-07-08 11:08:58 -05:00
|
|
|
},
|
|
|
|
},
|
2024-05-20 07:11:58 -05:00
|
|
|
T::Band | T::Mul | T::Xor => E::UnOp {
|
2024-09-09 12:36:53 -05:00
|
|
|
pos,
|
2024-07-08 00:22:53 -05:00
|
|
|
op: token.kind,
|
2024-05-17 12:53:59 -05:00
|
|
|
val: {
|
2024-07-08 11:08:58 -05:00
|
|
|
let expr = self.ptr_unit_expr();
|
2024-05-19 11:20:42 -05:00
|
|
|
if token.kind == T::Band {
|
2024-05-20 07:11:58 -05:00
|
|
|
self.flag_idents(*expr, idfl::REFERENCED);
|
2024-05-19 11:20:42 -05:00
|
|
|
}
|
2024-05-17 12:53:59 -05:00
|
|
|
expr
|
2024-05-16 05:23:37 -05:00
|
|
|
},
|
2024-05-12 04:52:58 -05:00
|
|
|
},
|
2024-09-09 12:36:53 -05:00
|
|
|
T::LBrace => E::Block { pos, stmts: self.collect_list(T::Semi, T::RBrace, Self::expr) },
|
2024-09-01 14:07:19 -05:00
|
|
|
T::Number => {
|
|
|
|
let slice = self.lexer.slice(token.range());
|
|
|
|
let (slice, radix) = match &slice.get(0..2) {
|
|
|
|
Some("0x") => (slice.trim_start_matches("0x"), Radix::Hex),
|
|
|
|
Some("0b") => (slice.trim_start_matches("0b"), Radix::Binary),
|
|
|
|
Some("0o") => (slice.trim_start_matches("0o"), Radix::Octal),
|
|
|
|
_ => (slice, Radix::Decimal),
|
|
|
|
};
|
|
|
|
E::Number {
|
2024-09-09 12:36:53 -05:00
|
|
|
pos,
|
2024-09-04 10:13:43 -05:00
|
|
|
value: match u64::from_str_radix(slice, radix as u32) {
|
2024-09-01 12:42:04 -05:00
|
|
|
Ok(value) => value,
|
|
|
|
Err(e) => self.report(format_args!("invalid number: {e}")),
|
2024-09-04 10:13:43 -05:00
|
|
|
} as i64,
|
2024-09-01 14:07:19 -05:00
|
|
|
radix,
|
|
|
|
}
|
|
|
|
}
|
2024-05-11 15:22:08 -05:00
|
|
|
T::LParen => {
|
2024-05-10 15:54:12 -05:00
|
|
|
let expr = self.expr();
|
2024-05-11 15:22:08 -05:00
|
|
|
self.expect_advance(T::RParen);
|
2024-05-10 15:54:12 -05:00
|
|
|
expr
|
|
|
|
}
|
2024-09-09 12:36:53 -05:00
|
|
|
T::Comment => Expr::Comment { pos, literal: self.move_str(token) },
|
2024-05-10 08:29:11 -05:00
|
|
|
tok => self.report(format_args!("unexpected token: {tok:?}")),
|
2024-05-09 16:41:59 -05:00
|
|
|
};
|
|
|
|
|
2024-05-11 09:04:13 -05:00
|
|
|
loop {
|
2024-05-14 05:17:39 -05:00
|
|
|
let token = self.token;
|
2024-07-08 11:08:58 -05:00
|
|
|
if matches!(token.kind, T::LParen | T::Ctor | T::Dot | T::Tupl | T::LBrack) {
|
2024-05-12 05:16:40 -05:00
|
|
|
self.next();
|
|
|
|
}
|
|
|
|
|
2024-05-14 05:17:39 -05:00
|
|
|
expr = match token.kind {
|
2024-05-12 05:16:40 -05:00
|
|
|
T::LParen => Expr::Call {
|
|
|
|
func: self.arena.alloc(expr),
|
2024-05-17 12:53:59 -05:00
|
|
|
args: self.collect_list(T::Comma, T::RParen, Self::expr),
|
2024-06-25 14:41:12 -05:00
|
|
|
trailing_comma: std::mem::take(&mut self.trailing_sep),
|
2024-05-12 05:16:40 -05:00
|
|
|
},
|
2024-07-07 12:16:15 -05:00
|
|
|
T::Ctor => self.ctor(token.start, Some(expr)),
|
|
|
|
T::Tupl => self.tupl(token.start, Some(expr)),
|
2024-07-08 11:08:58 -05:00
|
|
|
T::LBrack => E::Index {
|
|
|
|
base: self.arena.alloc(expr),
|
|
|
|
index: {
|
|
|
|
let index = self.expr();
|
|
|
|
self.expect_advance(T::RBrack);
|
|
|
|
self.arena.alloc(index)
|
|
|
|
},
|
|
|
|
},
|
2024-05-12 05:16:40 -05:00
|
|
|
T::Dot => E::Field {
|
2024-05-12 06:13:36 -05:00
|
|
|
target: self.arena.alloc(expr),
|
2024-07-08 00:22:53 -05:00
|
|
|
name: {
|
2024-05-12 05:16:40 -05:00
|
|
|
let token = self.expect_advance(T::Ident);
|
2024-05-17 12:53:59 -05:00
|
|
|
self.move_str(token)
|
2024-05-12 05:16:40 -05:00
|
|
|
},
|
|
|
|
},
|
2024-05-11 09:04:13 -05:00
|
|
|
_ => break,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-12 04:52:58 -05:00
|
|
|
if matches!(token.kind, T::Loop | T::LBrace | T::Fn) {
|
|
|
|
self.pop_scope(frame);
|
|
|
|
}
|
2024-05-09 16:41:59 -05:00
|
|
|
|
|
|
|
expr
|
|
|
|
}
|
|
|
|
|
2024-07-07 12:16:15 -05:00
|
|
|
fn tupl(&mut self, pos: Pos, ty: Option<Expr<'a>>) -> Expr<'a> {
|
|
|
|
Expr::Tupl {
|
|
|
|
pos,
|
|
|
|
ty: ty.map(|ty| self.arena.alloc(ty)),
|
|
|
|
fields: self.collect_list(TokenKind::Comma, TokenKind::RParen, Self::expr),
|
|
|
|
trailing_comma: std::mem::take(&mut self.trailing_sep),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn ctor(&mut self, pos: Pos, ty: Option<Expr<'a>>) -> Expr<'a> {
|
|
|
|
Expr::Ctor {
|
|
|
|
pos,
|
|
|
|
ty: ty.map(|ty| self.arena.alloc(ty)),
|
|
|
|
fields: self.collect_list(TokenKind::Comma, TokenKind::RBrace, |s| {
|
2024-07-08 00:22:53 -05:00
|
|
|
let name_tok = s.advance_ident();
|
|
|
|
let name = s.move_str(name_tok);
|
|
|
|
CtorField {
|
|
|
|
pos: name_tok.start,
|
|
|
|
name,
|
|
|
|
value: if s.advance_if(TokenKind::Colon) {
|
|
|
|
s.expr()
|
|
|
|
} else {
|
|
|
|
let (id, index) = s.resolve_ident(name_tok);
|
2024-07-19 14:04:22 -05:00
|
|
|
Expr::Ident { pos: name_tok.start, is_ct: false, id, name, index }
|
2024-07-08 00:22:53 -05:00
|
|
|
},
|
|
|
|
}
|
2024-07-07 12:16:15 -05:00
|
|
|
}),
|
|
|
|
trailing_comma: std::mem::take(&mut self.trailing_sep),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-01 13:30:07 -05:00
|
|
|
fn advance_ident(&mut self) -> Token {
|
|
|
|
if matches!(self.token.kind, TokenKind::Ident | TokenKind::CtIdent) {
|
|
|
|
self.next()
|
|
|
|
} else {
|
2024-07-08 00:22:53 -05:00
|
|
|
self.report(format_args!("expected identifier, found {:?}", self.token.kind))
|
2024-06-01 13:30:07 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-12 04:52:58 -05:00
|
|
|
fn pop_scope(&mut self, frame: usize) {
|
|
|
|
let mut undeclared_count = frame;
|
|
|
|
for i in frame..self.idents.len() {
|
2024-06-01 13:30:07 -05:00
|
|
|
if !&self.idents[i].declared {
|
2024-05-12 04:52:58 -05:00
|
|
|
self.idents.swap(i, undeclared_count);
|
|
|
|
undeclared_count += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-17 12:53:59 -05:00
|
|
|
self.idents
|
|
|
|
.drain(undeclared_count..)
|
2024-07-08 00:22:53 -05:00
|
|
|
.map(|ident| Symbol { name: ident.ident, flags: ident.flags })
|
2024-05-17 12:53:59 -05:00
|
|
|
.collect_into(self.symbols);
|
2024-05-12 04:52:58 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
fn ptr_unit_expr(&mut self) -> &'a Expr<'a> {
|
|
|
|
self.arena.alloc(self.unit_expr())
|
|
|
|
}
|
|
|
|
|
2024-05-11 15:22:08 -05:00
|
|
|
fn collect_list<T: Copy>(
|
|
|
|
&mut self,
|
|
|
|
delim: TokenKind,
|
|
|
|
end: TokenKind,
|
|
|
|
mut f: impl FnMut(&mut Self) -> T,
|
|
|
|
) -> &'a [T] {
|
|
|
|
self.collect(|s| {
|
|
|
|
s.advance_if(end).not().then(|| {
|
|
|
|
let val = f(s);
|
2024-06-25 14:41:12 -05:00
|
|
|
s.trailing_sep = s.advance_if(delim);
|
2024-05-11 15:22:08 -05:00
|
|
|
val
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2024-05-11 10:05:22 -05:00
|
|
|
fn collect<T: Copy>(&mut self, mut f: impl FnMut(&mut Self) -> Option<T>) -> &'a [T] {
|
2024-07-08 00:22:53 -05:00
|
|
|
// TODO: avoid this allocation
|
2024-05-11 09:04:13 -05:00
|
|
|
let vec = std::iter::from_fn(|| f(self)).collect::<Vec<_>>();
|
|
|
|
self.arena.alloc_slice(&vec)
|
2024-05-10 08:29:11 -05:00
|
|
|
}
|
|
|
|
|
2024-05-09 16:41:59 -05:00
|
|
|
fn advance_if(&mut self, kind: TokenKind) -> bool {
|
|
|
|
if self.token.kind == kind {
|
|
|
|
self.next();
|
|
|
|
true
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-11 09:04:13 -05:00
|
|
|
fn expect_advance(&mut self, kind: TokenKind) -> Token {
|
2024-05-09 16:41:59 -05:00
|
|
|
if self.token.kind != kind {
|
2024-07-08 00:22:53 -05:00
|
|
|
self.report(format_args!("expected {:?}, found {:?}", kind, self.token.kind));
|
2024-05-09 16:41:59 -05:00
|
|
|
}
|
2024-05-11 09:04:13 -05:00
|
|
|
self.next()
|
2024-05-09 16:41:59 -05:00
|
|
|
}
|
|
|
|
|
2024-06-01 13:30:07 -05:00
|
|
|
#[track_caller]
|
2024-05-09 16:41:59 -05:00
|
|
|
fn report(&self, msg: impl std::fmt::Display) -> ! {
|
2024-06-01 13:30:07 -05:00
|
|
|
self.report_pos(self.token.start, msg)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[track_caller]
|
|
|
|
fn report_pos(&self, pos: Pos, msg: impl std::fmt::Display) -> ! {
|
|
|
|
let (line, col) = self.lexer.line_col(pos);
|
2024-05-12 17:02:32 -05:00
|
|
|
eprintln!("{}:{}:{} => {}", self.path, line, col, msg);
|
2024-05-09 16:41:59 -05:00
|
|
|
unreachable!();
|
|
|
|
}
|
2024-05-16 05:23:37 -05:00
|
|
|
|
2024-05-17 12:53:59 -05:00
|
|
|
fn flag_idents(&mut self, e: Expr<'a>, flags: IdentFlags) {
|
|
|
|
match e {
|
|
|
|
Expr::Ident { id, .. } => find_ident(&mut self.idents, id).flags |= flags,
|
|
|
|
Expr::Field { target, .. } => self.flag_idents(*target, flags),
|
|
|
|
_ => {}
|
|
|
|
}
|
2024-05-16 05:23:37 -05:00
|
|
|
}
|
2024-05-09 16:41:59 -05:00
|
|
|
}
|
|
|
|
|
2024-05-17 12:53:59 -05:00
|
|
|
fn find_ident(idents: &mut [ScopeIdent], id: Ident) -> &mut ScopeIdent {
|
2024-07-08 00:22:53 -05:00
|
|
|
idents.binary_search_by_key(&id, |si| si.ident).map(|i| &mut idents[i]).unwrap()
|
2024-05-17 12:53:59 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn find_symbol(symbols: &[Symbol], id: Ident) -> &Symbol {
|
2024-07-08 00:22:53 -05:00
|
|
|
symbols.binary_search_by_key(&id, |s| s.name).map(|i| &symbols[i]).unwrap()
|
2024-05-17 12:53:59 -05:00
|
|
|
}
|
|
|
|
|
2024-05-12 04:52:58 -05:00
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
|
|
pub struct Arg<'a> {
|
2024-07-08 00:22:53 -05:00
|
|
|
pub name: &'a str,
|
|
|
|
pub id: Ident,
|
2024-07-19 14:04:22 -05:00
|
|
|
pub is_ct: bool,
|
2024-07-08 00:22:53 -05:00
|
|
|
pub index: IdentIndex,
|
|
|
|
pub ty: Expr<'a>,
|
2024-05-12 04:52:58 -05:00
|
|
|
}
|
|
|
|
|
2024-06-01 13:30:07 -05:00
|
|
|
macro_rules! generate_expr {
|
|
|
|
($(#[$meta:meta])* $vis:vis enum $name:ident<$lt:lifetime> {$(
|
|
|
|
$(#[$field_meta:meta])*
|
|
|
|
$variant:ident {
|
2024-09-03 10:51:28 -05:00
|
|
|
|
2024-06-01 13:30:07 -05:00
|
|
|
$($field:ident: $ty:ty,)*
|
|
|
|
},
|
|
|
|
)*}) => {
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
|
|
$vis enum $name<$lt> {$(
|
2024-09-03 10:51:28 -05:00
|
|
|
$(#[$field_meta])*
|
2024-06-01 13:30:07 -05:00
|
|
|
$variant {
|
|
|
|
$($field: $ty,)*
|
|
|
|
},
|
|
|
|
)*}
|
|
|
|
|
|
|
|
impl<$lt> $name<$lt> {
|
|
|
|
pub fn pos(&self) -> Pos {
|
|
|
|
#[allow(unused_variables)]
|
|
|
|
match self {
|
2024-06-24 10:26:00 -05:00
|
|
|
$(Self::$variant { $($field),* } => generate_expr!(@first $(($field),)*).posi(),)*
|
2024-06-01 13:30:07 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-07-19 14:04:22 -05:00
|
|
|
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
|
|
|
|
},
|
|
|
|
)*}
|
|
|
|
}
|
2024-06-01 13:30:07 -05:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2024-07-19 14:04:22 -05:00
|
|
|
(@filed_names $variant:ident $ident1:ident) => { Self::$variant { $ident1: a } };
|
|
|
|
|
2024-06-01 13:30:07 -05:00
|
|
|
(@first ($($first:tt)*), $($rest:tt)*) => { $($first)* };
|
|
|
|
(@last ($($ign:tt)*), $($rest:tt)*) => { $($rest)* };
|
|
|
|
(@last ($($last:tt)*),) => { $($last)* };
|
|
|
|
}
|
|
|
|
|
2024-09-01 14:07:19 -05:00
|
|
|
#[repr(u32)]
|
|
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
|
|
|
pub enum Radix {
|
|
|
|
Hex = 16,
|
|
|
|
Octal = 8,
|
|
|
|
Binary = 2,
|
|
|
|
Decimal = 10,
|
|
|
|
}
|
|
|
|
|
2024-06-01 13:30:07 -05:00
|
|
|
generate_expr! {
|
2024-09-03 10:51:28 -05:00
|
|
|
/// `LIST(start, sep, end, elem) => start { elem sep } [elem] end`
|
|
|
|
/// `OP := grep for `#define OP:`
|
2024-06-01 13:30:07 -05:00
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
|
|
pub enum Expr<'a> {
|
2024-09-03 10:51:28 -05:00
|
|
|
/// `'ct' Expr`
|
2024-07-19 05:00:55 -05:00
|
|
|
Ct {
|
|
|
|
pos: Pos,
|
2024-07-19 14:04:22 -05:00
|
|
|
value: &'a Self,
|
2024-07-19 05:00:55 -05:00
|
|
|
},
|
2024-09-03 10:51:28 -05:00
|
|
|
/// `'"([^"]|\\")"'`
|
2024-07-02 07:49:05 -05:00
|
|
|
String {
|
|
|
|
pos: Pos,
|
|
|
|
literal: &'a str,
|
|
|
|
},
|
2024-09-03 10:51:28 -05:00
|
|
|
/// `'//[^\n]' | '/*' { '([^/*]|*/)*' | Comment } '*/'
|
2024-06-25 12:55:25 -05:00
|
|
|
Comment {
|
|
|
|
pos: Pos,
|
|
|
|
literal: &'a str,
|
|
|
|
},
|
2024-09-03 10:51:28 -05:00
|
|
|
/// `'break'`
|
2024-06-01 13:30:07 -05:00
|
|
|
Break {
|
|
|
|
pos: Pos,
|
|
|
|
},
|
2024-09-03 10:51:28 -05:00
|
|
|
/// `'continue'`
|
2024-06-01 13:30:07 -05:00
|
|
|
Continue {
|
|
|
|
pos: Pos,
|
|
|
|
},
|
2024-09-03 10:51:28 -05:00
|
|
|
/// `'fn' LIST('(', ',', ')', Ident ':' Expr) ':' Expr Expr`
|
2024-06-01 13:30:07 -05:00
|
|
|
Closure {
|
|
|
|
pos: Pos,
|
|
|
|
args: &'a [Arg<'a>],
|
|
|
|
ret: &'a Self,
|
|
|
|
body: &'a Self,
|
|
|
|
},
|
2024-09-03 10:51:28 -05:00
|
|
|
/// `Expr LIST('(', ',', ')', Expr)`
|
2024-06-01 13:30:07 -05:00
|
|
|
Call {
|
|
|
|
func: &'a Self,
|
|
|
|
args: &'a [Self],
|
2024-06-25 14:41:12 -05:00
|
|
|
trailing_comma: bool,
|
2024-06-01 13:30:07 -05:00
|
|
|
},
|
2024-09-03 10:51:28 -05:00
|
|
|
/// `'return' [Expr]`
|
2024-06-01 13:30:07 -05:00
|
|
|
Return {
|
|
|
|
pos: Pos,
|
|
|
|
val: Option<&'a Self>,
|
|
|
|
},
|
2024-09-03 10:51:28 -05:00
|
|
|
/// note: ':unicode:' is any utf-8 character except ascii
|
|
|
|
/// `'[a-zA-Z_:unicode:][a-zA-Z0-9_:unicode:]*'`
|
2024-06-01 13:30:07 -05:00
|
|
|
Ident {
|
2024-06-24 10:26:00 -05:00
|
|
|
pos: Pos,
|
2024-07-19 14:04:22 -05:00
|
|
|
is_ct: bool,
|
2024-06-01 13:30:07 -05:00
|
|
|
id: Ident,
|
|
|
|
name: &'a str,
|
2024-07-08 00:22:53 -05:00
|
|
|
index: IdentIndex,
|
2024-06-01 13:30:07 -05:00
|
|
|
},
|
2024-09-03 10:51:28 -05:00
|
|
|
/// `LIST('{', [';'], '}', Expr)`
|
2024-06-01 13:30:07 -05:00
|
|
|
Block {
|
|
|
|
pos: Pos,
|
|
|
|
stmts: &'a [Self],
|
|
|
|
},
|
2024-09-03 10:51:28 -05:00
|
|
|
/// `'0b[01]+' | '0o[0-7]+' | '[0-9]+' | '0b[01]+'`
|
2024-06-01 13:30:07 -05:00
|
|
|
Number {
|
|
|
|
pos: Pos,
|
2024-09-03 10:51:28 -05:00
|
|
|
value: i64,
|
2024-09-01 14:07:19 -05:00
|
|
|
radix: Radix,
|
2024-06-01 13:30:07 -05:00
|
|
|
},
|
2024-09-03 10:51:28 -05:00
|
|
|
/// node: precedence defined in `OP` applies
|
|
|
|
/// `Expr OP Expr`
|
2024-06-01 13:30:07 -05:00
|
|
|
BinOp {
|
|
|
|
left: &'a Self,
|
|
|
|
op: TokenKind,
|
|
|
|
right: &'a Self,
|
|
|
|
},
|
2024-09-03 10:51:28 -05:00
|
|
|
/// `'if' Expr Expr [else Expr]`
|
2024-06-01 13:30:07 -05:00
|
|
|
If {
|
|
|
|
pos: Pos,
|
|
|
|
cond: &'a Self,
|
|
|
|
then: &'a Self,
|
|
|
|
else_: Option<&'a Self>,
|
|
|
|
},
|
2024-09-03 10:51:28 -05:00
|
|
|
/// `'loop' Expr`
|
2024-06-01 13:30:07 -05:00
|
|
|
Loop {
|
|
|
|
pos: Pos,
|
|
|
|
body: &'a Self,
|
|
|
|
},
|
2024-09-03 10:51:28 -05:00
|
|
|
/// `('&' | '*' | '^') Expr`
|
2024-06-01 13:30:07 -05:00
|
|
|
UnOp {
|
|
|
|
pos: Pos,
|
|
|
|
op: TokenKind,
|
|
|
|
val: &'a Self,
|
|
|
|
},
|
2024-09-03 10:51:28 -05:00
|
|
|
/// `'struct' LIST('{', ',', '}', Ident ':' Expr)`
|
2024-06-01 13:30:07 -05:00
|
|
|
Struct {
|
|
|
|
pos: Pos,
|
|
|
|
fields: &'a [(&'a str, Self)],
|
|
|
|
captured: &'a [Ident],
|
2024-07-19 14:04:22 -05:00
|
|
|
trailing_comma: bool,
|
2024-06-01 13:30:07 -05:00
|
|
|
},
|
2024-09-03 10:51:28 -05:00
|
|
|
/// `[Expr] LIST('.{', ',', '}', Ident [':' Expr])`
|
2024-06-01 13:30:07 -05:00
|
|
|
Ctor {
|
|
|
|
pos: Pos,
|
|
|
|
ty: Option<&'a Self>,
|
2024-07-08 00:22:53 -05:00
|
|
|
fields: &'a [CtorField<'a>],
|
2024-07-07 12:16:15 -05:00
|
|
|
trailing_comma: bool,
|
|
|
|
},
|
2024-09-03 10:51:28 -05:00
|
|
|
/// `[Expr] LIST('.(', ',', ')', Ident [':' Expr])`
|
2024-07-07 12:16:15 -05:00
|
|
|
Tupl {
|
|
|
|
pos: Pos,
|
|
|
|
ty: Option<&'a Self>,
|
|
|
|
fields: &'a [Self],
|
2024-06-25 14:51:41 -05:00
|
|
|
trailing_comma: bool,
|
2024-06-01 13:30:07 -05:00
|
|
|
},
|
2024-09-03 10:51:28 -05:00
|
|
|
/// `'[' Expr [';' Expr] ']'`
|
2024-07-08 11:08:58 -05:00
|
|
|
Slice {
|
|
|
|
pos: Pos,
|
|
|
|
size: Option<&'a Self>,
|
|
|
|
item: &'a Self,
|
|
|
|
},
|
2024-09-03 10:51:28 -05:00
|
|
|
/// `Expr '[' Expr ']'`
|
2024-07-08 11:08:58 -05:00
|
|
|
Index {
|
|
|
|
base: &'a Self,
|
|
|
|
index: &'a Self,
|
|
|
|
},
|
2024-09-03 10:51:28 -05:00
|
|
|
/// `Expr '.' Ident`
|
2024-06-01 13:30:07 -05:00
|
|
|
Field {
|
|
|
|
target: &'a Self,
|
2024-07-08 00:22:53 -05:00
|
|
|
name: &'a str,
|
2024-06-01 13:30:07 -05:00
|
|
|
},
|
2024-09-03 10:51:28 -05:00
|
|
|
/// `'true' | 'false'`
|
2024-06-01 13:30:07 -05:00
|
|
|
Bool {
|
|
|
|
pos: Pos,
|
|
|
|
value: bool,
|
|
|
|
},
|
2024-09-09 12:36:53 -05:00
|
|
|
/// `'idk'`
|
|
|
|
Idk {
|
|
|
|
pos: Pos,
|
|
|
|
},
|
2024-09-03 10:51:28 -05:00
|
|
|
/// `'@' Ident List('(', ',', ')', Expr)`
|
2024-06-01 13:30:07 -05:00
|
|
|
Directive {
|
|
|
|
pos: u32,
|
|
|
|
name: &'a str,
|
|
|
|
args: &'a [Self],
|
|
|
|
},
|
2024-09-03 10:51:28 -05:00
|
|
|
/// `'@use' '(' String ')'`
|
2024-06-01 13:30:07 -05:00
|
|
|
Mod {
|
|
|
|
pos: Pos,
|
|
|
|
id: FileId,
|
|
|
|
path: &'a str,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-07-08 00:22:53 -05:00
|
|
|
impl<'a> Expr<'a> {
|
|
|
|
pub fn declares(&self, iden: Result<Ident, &str>) -> Option<Ident> {
|
|
|
|
match *self {
|
|
|
|
Self::Ident { id, name, .. } if iden == Ok(id) || iden == Err(name) => Some(id),
|
|
|
|
Self::Ctor { fields, .. } => fields.iter().find_map(|f| f.value.declares(iden)),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-07-19 05:00:55 -05:00
|
|
|
pub fn has_ct(&self, symbols: &[Symbol]) -> bool {
|
|
|
|
match *self {
|
|
|
|
Self::Ident { id, .. } => find_symbol(symbols, id).flags & idfl::COMPTIME != 0,
|
|
|
|
Self::Ctor { fields, .. } => fields.iter().any(|f| f.value.has_ct(symbols)),
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-07-08 00:22:53 -05:00
|
|
|
pub fn find_pattern_path<F: FnOnce(&Expr)>(
|
|
|
|
&self,
|
|
|
|
ident: Ident,
|
|
|
|
target: &Expr,
|
|
|
|
mut with_final: F,
|
|
|
|
) -> Result<(), F> {
|
|
|
|
match *self {
|
|
|
|
Self::Ident { id, .. } if id == ident => {
|
|
|
|
with_final(target);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
Self::Ctor { fields, .. } => {
|
|
|
|
for CtorField { name, value, .. } in fields {
|
|
|
|
match value.find_pattern_path(ident, &Expr::Field { target, name }, with_final)
|
|
|
|
{
|
|
|
|
Ok(()) => return Ok(()),
|
|
|
|
Err(e) => with_final = e,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Err(with_final)
|
|
|
|
}
|
|
|
|
_ => Err(with_final),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
|
|
pub struct CtorField<'a> {
|
|
|
|
pub pos: Pos,
|
|
|
|
pub name: &'a str,
|
|
|
|
pub value: Expr<'a>,
|
|
|
|
}
|
|
|
|
|
2024-06-01 13:30:07 -05:00
|
|
|
trait Poser {
|
2024-06-24 10:26:00 -05:00
|
|
|
fn posi(self) -> Pos;
|
2024-05-09 16:41:59 -05:00
|
|
|
}
|
|
|
|
|
2024-06-01 13:30:07 -05:00
|
|
|
impl Poser for Pos {
|
2024-06-24 10:26:00 -05:00
|
|
|
fn posi(self) -> Pos {
|
|
|
|
self
|
2024-05-12 16:19:45 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-01 13:30:07 -05:00
|
|
|
impl<'a> Poser for &Expr<'a> {
|
2024-06-24 10:26:00 -05:00
|
|
|
fn posi(self) -> Pos {
|
2024-06-01 13:30:07 -05:00
|
|
|
self.pos()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-25 14:41:12 -05:00
|
|
|
thread_local! {
|
|
|
|
static FMT_SOURCE: Cell<*const str> = const { Cell::new("") };
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn with_fmt_source<T>(source: &str, f: impl FnOnce() -> T) -> T {
|
|
|
|
FMT_SOURCE.with(|s| s.set(source));
|
|
|
|
let r = f();
|
|
|
|
FMT_SOURCE.with(|s| s.set(""));
|
|
|
|
r
|
|
|
|
}
|
|
|
|
|
2024-05-10 08:29:11 -05:00
|
|
|
impl<'a> std::fmt::Display for Expr<'a> {
|
2024-05-09 16:41:59 -05:00
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
|
|
thread_local! {
|
2024-05-19 11:20:42 -05:00
|
|
|
static INDENT: Cell<usize> = const { Cell::new(0) };
|
2024-07-19 14:04:22 -05:00
|
|
|
static DISPLAY_BUFFER: Cell<String> = const { Cell::new(String::new()) };
|
2024-05-09 16:41:59 -05:00
|
|
|
}
|
|
|
|
|
2024-05-19 11:20:42 -05:00
|
|
|
fn fmt_list<T>(
|
2024-05-14 16:07:32 -05:00
|
|
|
f: &mut std::fmt::Formatter,
|
2024-07-07 12:16:15 -05:00
|
|
|
trailing: bool,
|
2024-05-14 16:07:32 -05:00
|
|
|
end: &str,
|
2024-05-19 11:20:42 -05:00
|
|
|
list: &[T],
|
2024-05-14 16:07:32 -05:00
|
|
|
fmt: impl Fn(&T, &mut std::fmt::Formatter) -> std::fmt::Result,
|
|
|
|
) -> std::fmt::Result {
|
2024-07-07 12:16:15 -05:00
|
|
|
if !trailing {
|
|
|
|
let first = &mut true;
|
|
|
|
for expr in list {
|
|
|
|
if !std::mem::take(first) {
|
|
|
|
write!(f, ", ")?;
|
|
|
|
}
|
|
|
|
fmt(expr, f)?;
|
2024-05-14 16:07:32 -05:00
|
|
|
}
|
2024-07-07 12:16:15 -05:00
|
|
|
return write!(f, "{end}");
|
2024-05-14 16:07:32 -05:00
|
|
|
}
|
|
|
|
|
2024-06-25 14:41:12 -05:00
|
|
|
writeln!(f)?;
|
|
|
|
INDENT.with(|i| i.set(i.get() + 1));
|
|
|
|
let res = (|| {
|
|
|
|
for stmt in list {
|
|
|
|
for _ in 0..INDENT.with(|i| i.get()) {
|
|
|
|
write!(f, "\t")?;
|
|
|
|
}
|
|
|
|
fmt(stmt, f)?;
|
|
|
|
writeln!(f, ",")?;
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
})();
|
|
|
|
INDENT.with(|i| i.set(i.get() - 1));
|
|
|
|
|
|
|
|
for _ in 0..INDENT.with(|i| i.get()) {
|
|
|
|
write!(f, "\t")?;
|
|
|
|
}
|
|
|
|
write!(f, "{end}")?;
|
|
|
|
res
|
|
|
|
}
|
|
|
|
|
2024-05-15 07:36:38 -05:00
|
|
|
macro_rules! impl_parenter {
|
|
|
|
($($name:ident => $pat:pat,)*) => {
|
|
|
|
$(
|
|
|
|
struct $name<'a>(&'a Expr<'a>);
|
|
|
|
|
|
|
|
impl<'a> std::fmt::Display for $name<'a> {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
|
|
if matches!(self.0, $pat) {
|
|
|
|
write!(f, "({})", self.0)
|
|
|
|
} else {
|
|
|
|
write!(f, "{}", self.0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)*
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
impl_parenter! {
|
|
|
|
Unary => Expr::BinOp { .. },
|
|
|
|
Postfix => Expr::UnOp { .. } | Expr::BinOp { .. },
|
|
|
|
Consecutive => Expr::UnOp { .. },
|
|
|
|
}
|
|
|
|
|
2024-07-19 14:04:22 -05:00
|
|
|
let source: &str = unsafe { &*FMT_SOURCE.with(|s| s.get()) };
|
2024-06-25 14:41:12 -05:00
|
|
|
|
2024-05-10 15:54:12 -05:00
|
|
|
match *self {
|
2024-07-19 05:00:55 -05:00
|
|
|
Self::Ct { value, .. } => write!(f, "$: {}", value),
|
2024-07-02 07:49:05 -05:00
|
|
|
Self::String { literal, .. } => write!(f, "{}", literal),
|
2024-06-25 14:41:12 -05:00
|
|
|
Self::Comment { literal, .. } => write!(f, "{}", literal.trim_end()),
|
2024-07-19 05:51:26 -05:00
|
|
|
Self::Mod { path, .. } => write!(f, "@use(\"{path}\")"),
|
2024-07-08 00:22:53 -05:00
|
|
|
Self::Field { target, name: field } => write!(f, "{}.{field}", Postfix(target)),
|
2024-05-14 16:07:32 -05:00
|
|
|
Self::Directive { name, args, .. } => {
|
|
|
|
write!(f, "@{name}(")?;
|
2024-07-07 12:16:15 -05:00
|
|
|
fmt_list(f, false, ")", args, std::fmt::Display::fmt)
|
2024-05-14 16:07:32 -05:00
|
|
|
}
|
2024-07-19 14:04:22 -05:00
|
|
|
Self::Struct { fields, trailing_comma, .. } => {
|
2024-05-12 05:16:40 -05:00
|
|
|
write!(f, "struct {{")?;
|
2024-07-19 14:04:22 -05:00
|
|
|
fmt_list(f, trailing_comma, "}", fields, |(name, val), f| {
|
|
|
|
write!(f, "{name}: {val}",)
|
|
|
|
})
|
2024-05-12 05:16:40 -05:00
|
|
|
}
|
2024-07-08 00:22:53 -05:00
|
|
|
Self::Ctor { ty, fields, trailing_comma, .. } => {
|
2024-05-14 05:17:39 -05:00
|
|
|
if let Some(ty) = ty {
|
2024-05-15 07:36:38 -05:00
|
|
|
write!(f, "{}", Unary(ty))?;
|
2024-05-14 05:17:39 -05:00
|
|
|
}
|
2024-07-07 12:16:15 -05:00
|
|
|
write!(f, ".{{")?;
|
2024-07-08 00:22:53 -05:00
|
|
|
let fmt_field = |CtorField { name, value, .. }: &_, f: &mut std::fmt::Formatter| {
|
|
|
|
if matches!(value, Expr::Ident { name: n, .. } if name == n) {
|
2024-07-07 12:16:15 -05:00
|
|
|
write!(f, "{name}")
|
2024-07-08 00:22:53 -05:00
|
|
|
} else {
|
|
|
|
write!(f, "{name}: {value}")
|
2024-05-14 05:17:39 -05:00
|
|
|
}
|
2024-06-25 14:51:41 -05:00
|
|
|
};
|
2024-07-07 12:16:15 -05:00
|
|
|
fmt_list(f, trailing_comma, "}", fields, fmt_field)
|
|
|
|
}
|
2024-07-08 00:22:53 -05:00
|
|
|
Self::Tupl { ty, fields, trailing_comma, .. } => {
|
2024-07-07 12:16:15 -05:00
|
|
|
if let Some(ty) = ty {
|
|
|
|
write!(f, "{}", Unary(ty))?;
|
2024-05-12 05:16:40 -05:00
|
|
|
}
|
2024-07-07 12:16:15 -05:00
|
|
|
write!(f, ".(")?;
|
|
|
|
fmt_list(f, trailing_comma, ")", fields, std::fmt::Display::fmt)
|
2024-05-12 05:16:40 -05:00
|
|
|
}
|
2024-07-08 11:08:58 -05:00
|
|
|
Self::Slice { item, size, .. } => match size {
|
2024-07-19 14:04:22 -05:00
|
|
|
Some(size) => write!(f, "[{item}; {size}]"),
|
|
|
|
None => write!(f, "[{item}]"),
|
2024-07-08 11:08:58 -05:00
|
|
|
},
|
|
|
|
Self::Index { base, index } => write!(f, "{base}[{index}]"),
|
2024-05-15 07:36:38 -05:00
|
|
|
Self::UnOp { op, val, .. } => write!(f, "{op}{}", Unary(val)),
|
2024-07-07 11:21:07 -05:00
|
|
|
Self::Break { .. } => write!(f, "break"),
|
|
|
|
Self::Continue { .. } => write!(f, "continue"),
|
2024-07-08 00:22:53 -05:00
|
|
|
Self::If { cond, then, else_, .. } => {
|
2024-05-15 07:36:38 -05:00
|
|
|
write!(f, "if {cond} {}", Consecutive(then))?;
|
2024-05-11 11:16:27 -05:00
|
|
|
if let Some(else_) = else_ {
|
2024-05-14 16:07:32 -05:00
|
|
|
write!(f, " else {else_}")?;
|
2024-05-11 11:16:27 -05:00
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
2024-05-14 16:07:32 -05:00
|
|
|
Self::Loop { body, .. } => write!(f, "loop {body}"),
|
2024-07-08 00:22:53 -05:00
|
|
|
Self::Closure { ret, body, args, .. } => {
|
2024-05-14 16:07:32 -05:00
|
|
|
write!(f, "fn(")?;
|
2024-07-19 14:04:22 -05:00
|
|
|
fmt_list(f, false, "", args, |arg, f| {
|
|
|
|
if arg.is_ct {
|
|
|
|
write!(f, "$")?;
|
|
|
|
}
|
|
|
|
write!(f, "{}: {}", arg.name, arg.ty)
|
|
|
|
})?;
|
2024-07-07 11:21:07 -05:00
|
|
|
write!(f, "): {ret} {body}")?;
|
|
|
|
Ok(())
|
2024-05-11 09:04:13 -05:00
|
|
|
}
|
2024-07-08 00:22:53 -05:00
|
|
|
Self::Call { func, args, trailing_comma } => {
|
2024-05-15 07:36:38 -05:00
|
|
|
write!(f, "{}(", Postfix(func))?;
|
2024-07-07 12:16:15 -05:00
|
|
|
fmt_list(f, trailing_comma, ")", args, std::fmt::Display::fmt)
|
2024-05-11 09:04:13 -05:00
|
|
|
}
|
2024-07-19 06:21:14 -05:00
|
|
|
Self::Return { val: Some(val), .. } => write!(f, "return {val}"),
|
|
|
|
Self::Return { val: None, .. } => write!(f, "return"),
|
2024-07-19 14:04:22 -05:00
|
|
|
Self::Ident { name, is_ct: true, .. } => write!(f, "${name}"),
|
|
|
|
Self::Ident { name, is_ct: false, .. } => write!(f, "{name}"),
|
2024-05-11 15:22:08 -05:00
|
|
|
Self::Block { stmts, .. } => {
|
2024-06-25 14:41:12 -05:00
|
|
|
write!(f, "{{")?;
|
2024-07-07 07:26:43 -05:00
|
|
|
writeln!(f)?;
|
|
|
|
INDENT.with(|i| i.set(i.get() + 1));
|
|
|
|
let res = (|| {
|
2024-07-19 06:38:30 -05:00
|
|
|
for (i, stmt) in stmts.iter().enumerate() {
|
2024-07-07 07:26:43 -05:00
|
|
|
for _ in 0..INDENT.with(|i| i.get()) {
|
|
|
|
write!(f, "\t")?;
|
|
|
|
}
|
2024-07-19 06:38:30 -05:00
|
|
|
write!(f, "{stmt}")?;
|
|
|
|
if let Some(expr) = stmts.get(i + 1)
|
|
|
|
&& let Some(rest) = source.get(expr.pos() as usize..)
|
|
|
|
{
|
2024-07-19 08:51:02 -05:00
|
|
|
if insert_needed_semicolon(rest) {
|
|
|
|
write!(f, ";")?;
|
|
|
|
}
|
2024-07-19 14:04:22 -05:00
|
|
|
if preserve_newlines(&source[..expr.pos() as usize]) > 1 {
|
2024-07-19 08:51:02 -05:00
|
|
|
writeln!(f)?;
|
|
|
|
}
|
2024-07-19 06:38:30 -05:00
|
|
|
}
|
2024-07-19 06:39:42 -05:00
|
|
|
writeln!(f)?;
|
2024-07-07 07:26:43 -05:00
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
})();
|
|
|
|
INDENT.with(|i| i.set(i.get() - 1));
|
|
|
|
for _ in 0..INDENT.with(|i| i.get()) {
|
|
|
|
write!(f, "\t")?;
|
|
|
|
}
|
2024-07-07 07:29:37 -05:00
|
|
|
write!(f, "}}")?;
|
2024-07-07 07:26:43 -05:00
|
|
|
res
|
2024-05-09 16:41:59 -05:00
|
|
|
}
|
2024-09-01 14:07:19 -05:00
|
|
|
Self::Number { value, radix, .. } => match radix {
|
|
|
|
Radix::Decimal => write!(f, "{value}"),
|
|
|
|
Radix::Hex => write!(f, "{value:#X}"),
|
|
|
|
Radix::Octal => write!(f, "{value:#o}"),
|
|
|
|
Radix::Binary => write!(f, "{value:#b}"),
|
|
|
|
},
|
2024-05-14 16:07:32 -05:00
|
|
|
Self::Bool { value, .. } => write!(f, "{value}"),
|
2024-09-09 12:36:53 -05:00
|
|
|
Self::Idk { .. } => write!(f, "idk"),
|
2024-07-19 07:39:30 -05:00
|
|
|
Self::BinOp {
|
2024-07-19 14:04:22 -05:00
|
|
|
left,
|
2024-07-19 07:39:30 -05:00
|
|
|
op: TokenKind::Assign,
|
2024-07-19 14:04:22 -05:00
|
|
|
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
|
|
|
|
}) =>
|
|
|
|
{
|
2024-07-19 07:39:30 -05:00
|
|
|
write!(f, "{left} {op}= {right}")
|
|
|
|
}
|
2024-07-19 14:04:22 -05:00
|
|
|
Self::BinOp { right, op, left } => {
|
2024-05-10 15:54:12 -05:00
|
|
|
let display_branch = |f: &mut std::fmt::Formatter, expr: &Self| {
|
|
|
|
if let Self::BinOp { op: lop, .. } = expr
|
|
|
|
&& op.precedence() > lop.precedence()
|
|
|
|
{
|
2024-05-14 16:07:32 -05:00
|
|
|
write!(f, "({expr})")
|
2024-05-10 15:54:12 -05:00
|
|
|
} else {
|
2024-05-14 16:07:32 -05:00
|
|
|
write!(f, "{expr}")
|
2024-05-10 15:54:12 -05:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
display_branch(f, left)?;
|
2024-07-19 14:04:22 -05:00
|
|
|
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} ")?;
|
|
|
|
}
|
2024-07-19 06:21:14 -05:00
|
|
|
display_branch(f, right)
|
2024-05-10 15:54:12 -05:00
|
|
|
}
|
2024-05-09 16:41:59 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-07-19 08:51:02 -05:00
|
|
|
pub fn preserve_newlines(source: &str) -> usize {
|
2024-07-19 14:04:22 -05:00
|
|
|
source[source.trim_end().len()..].chars().filter(|&c| c == '\n').count()
|
2024-07-19 08:51:02 -05:00
|
|
|
}
|
|
|
|
|
2024-07-19 07:17:45 -05:00
|
|
|
pub fn insert_needed_semicolon(source: &str) -> bool {
|
|
|
|
let kind = lexer::Lexer::new(source).next().kind;
|
2024-07-19 07:24:58 -05:00
|
|
|
kind.precedence().is_some() || matches!(kind, TokenKind::Ctor | TokenKind::Tupl)
|
2024-07-19 07:17:45 -05:00
|
|
|
}
|
|
|
|
|
2024-05-17 12:53:59 -05:00
|
|
|
#[repr(C)]
|
2024-05-19 11:20:42 -05:00
|
|
|
pub struct AstInner<T: ?Sized> {
|
2024-05-17 12:53:59 -05:00
|
|
|
ref_count: AtomicUsize,
|
2024-07-08 00:22:53 -05:00
|
|
|
mem: ArenaChunk,
|
|
|
|
exprs: *const [Expr<'static>],
|
2024-05-19 11:20:42 -05:00
|
|
|
|
2024-07-08 00:22:53 -05:00
|
|
|
pub path: Box<str>,
|
2024-09-01 14:15:29 -05:00
|
|
|
pub file: Box<str>,
|
2024-05-19 11:20:42 -05:00
|
|
|
pub symbols: T,
|
2024-05-17 12:53:59 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
impl AstInner<[Symbol]> {
|
|
|
|
fn layout(syms: usize) -> std::alloc::Layout {
|
|
|
|
std::alloc::Layout::new::<AstInner<()>>()
|
|
|
|
.extend(std::alloc::Layout::array::<Symbol>(syms).unwrap())
|
|
|
|
.unwrap()
|
|
|
|
.0
|
|
|
|
}
|
|
|
|
|
2024-09-01 14:15:29 -05:00
|
|
|
fn new(content: String, path: &str, loader: Loader) -> NonNull<Self> {
|
2024-05-17 12:53:59 -05:00
|
|
|
let arena = Arena::default();
|
|
|
|
let mut syms = Vec::new();
|
2024-07-19 14:19:03 -05:00
|
|
|
let mut parser = Parser::new(&arena, &mut syms, loader);
|
2024-09-01 14:15:29 -05:00
|
|
|
let exprs = parser.file(&content, path) as *const [Expr<'static>];
|
2024-05-17 12:53:59 -05:00
|
|
|
|
|
|
|
syms.sort_unstable_by_key(|s| s.name);
|
|
|
|
|
|
|
|
let layout = Self::layout(syms.len());
|
|
|
|
|
|
|
|
unsafe {
|
2024-05-19 11:20:42 -05:00
|
|
|
let ptr = std::alloc::alloc(layout);
|
|
|
|
let inner: *mut Self = std::ptr::from_raw_parts_mut(ptr as *mut _, syms.len());
|
|
|
|
|
2024-07-08 00:22:53 -05:00
|
|
|
std::ptr::write(inner as *mut AstInner<()>, AstInner {
|
|
|
|
ref_count: AtomicUsize::new(1),
|
|
|
|
mem: arena.chunk.into_inner(),
|
|
|
|
exprs,
|
|
|
|
path: path.into(),
|
2024-09-01 14:15:29 -05:00
|
|
|
file: content.into(),
|
2024-07-08 00:22:53 -05:00
|
|
|
symbols: (),
|
|
|
|
});
|
2024-05-17 12:53:59 -05:00
|
|
|
std::ptr::addr_of_mut!((*inner).symbols)
|
|
|
|
.as_mut_ptr()
|
|
|
|
.copy_from_nonoverlapping(syms.as_ptr(), syms.len());
|
2024-05-19 11:20:42 -05:00
|
|
|
|
2024-05-17 12:53:59 -05:00
|
|
|
NonNull::new_unchecked(inner)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(PartialEq, Eq, Hash)]
|
|
|
|
pub struct Ast(NonNull<AstInner<[Symbol]>>);
|
|
|
|
|
|
|
|
impl Ast {
|
2024-09-01 14:15:29 -05:00
|
|
|
pub fn new(path: &str, content: String, loader: Loader) -> Self {
|
2024-07-19 14:19:03 -05:00
|
|
|
Self(AstInner::new(content, path, loader))
|
2024-05-17 12:53:59 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn exprs(&self) -> &[Expr] {
|
|
|
|
unsafe { &*self.inner().exprs }
|
|
|
|
}
|
|
|
|
|
2024-05-19 11:20:42 -05:00
|
|
|
fn inner(&self) -> &AstInner<[Symbol]> {
|
|
|
|
unsafe { self.0.as_ref() }
|
2024-05-17 12:53:59 -05:00
|
|
|
}
|
|
|
|
|
2024-05-20 07:11:58 -05:00
|
|
|
pub fn find_decl(&self, id: Result<Ident, &str>) -> Option<(&Expr, Ident)> {
|
2024-05-19 11:20:42 -05:00
|
|
|
self.exprs().iter().find_map(|expr| match expr {
|
2024-07-08 00:22:53 -05:00
|
|
|
Expr::BinOp { left, op: TokenKind::Decl, .. } => left.declares(id).map(|id| (expr, id)),
|
2024-05-19 11:20:42 -05:00
|
|
|
_ => None,
|
|
|
|
})
|
2024-05-17 12:53:59 -05:00
|
|
|
}
|
2024-09-03 10:51:28 -05:00
|
|
|
|
|
|
|
pub fn ident_str(&self, ident: Ident) -> &str {
|
|
|
|
&self.file[ident::range(ident)]
|
|
|
|
}
|
2024-05-19 11:20:42 -05:00
|
|
|
}
|
2024-05-17 12:53:59 -05:00
|
|
|
|
2024-06-01 13:30:07 -05:00
|
|
|
impl std::fmt::Display for Ast {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
for expr in self.exprs() {
|
|
|
|
writeln!(f, "{expr}\n")?;
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-19 11:20:42 -05:00
|
|
|
impl Default for Ast {
|
|
|
|
fn default() -> Self {
|
2024-09-01 14:15:29 -05:00
|
|
|
Self(AstInner::new(String::new(), "", &no_loader))
|
2024-05-19 11:20:42 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[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>> {
|
2024-06-21 16:07:32 -05:00
|
|
|
ArenaChunk::contains(from.mem.base, self.0.as_ptr() as _).then_some(())?;
|
2024-05-19 11:20:42 -05:00
|
|
|
// 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
|
2024-06-21 16:07:32 -05:00
|
|
|
Some(unsafe { { self.0 }.as_ref() })
|
2024-05-17 12:53:59 -05:00
|
|
|
}
|
2024-09-04 09:54:34 -05:00
|
|
|
|
|
|
|
pub fn dangling() -> Self {
|
|
|
|
Self(NonNull::dangling())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for ExprRef {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self::dangling()
|
|
|
|
}
|
2024-05-17 12:53:59 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
unsafe impl Send for Ast {}
|
|
|
|
unsafe impl Sync for Ast {}
|
|
|
|
|
|
|
|
impl Clone for Ast {
|
|
|
|
fn clone(&self) -> Self {
|
2024-07-08 00:22:53 -05:00
|
|
|
unsafe { self.0.as_ref() }.ref_count.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
2024-05-17 12:53:59 -05:00
|
|
|
Self(self.0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Drop for Ast {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
let inner = unsafe { self.0.as_ref() };
|
2024-07-08 00:22:53 -05:00
|
|
|
if inner.ref_count.fetch_sub(1, std::sync::atomic::Ordering::Relaxed) == 1 {
|
2024-05-17 12:53:59 -05:00
|
|
|
unsafe { std::ptr::drop_in_place(self.0.as_ptr()) };
|
|
|
|
|
|
|
|
let layout = AstInner::layout(inner.symbols.len());
|
|
|
|
unsafe {
|
|
|
|
std::alloc::dealloc(self.0.as_ptr() as _, layout);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-19 11:20:42 -05:00
|
|
|
impl Deref for Ast {
|
|
|
|
type Target = AstInner<[Symbol]>;
|
|
|
|
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
|
|
self.inner()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-10 08:29:11 -05:00
|
|
|
#[derive(Default)]
|
|
|
|
pub struct Arena<'a> {
|
2024-05-17 12:53:59 -05:00
|
|
|
chunk: UnsafeCell<ArenaChunk>,
|
2024-07-08 00:22:53 -05:00
|
|
|
ph: std::marker::PhantomData<&'a ()>,
|
2024-05-10 08:29:11 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Arena<'a> {
|
|
|
|
pub fn alloc_str(&self, token: &str) -> &'a str {
|
|
|
|
let ptr = self.alloc_slice(token.as_bytes());
|
2024-06-01 13:30:07 -05:00
|
|
|
unsafe { std::str::from_utf8_unchecked(ptr) }
|
2024-05-10 08:29:11 -05:00
|
|
|
}
|
|
|
|
|
2024-06-01 13:30:07 -05:00
|
|
|
pub fn alloc(&self, expr: Expr<'a>) -> &'a Expr<'a> {
|
|
|
|
let align = std::mem::align_of::<Expr<'a>>();
|
|
|
|
let size = expr.used_bytes();
|
|
|
|
let layout = unsafe { std::alloc::Layout::from_size_align_unchecked(size, align) };
|
2024-05-10 08:29:11 -05:00
|
|
|
let ptr = self.alloc_low(layout);
|
2024-06-01 13:30:07 -05:00
|
|
|
unsafe {
|
2024-07-08 00:22:53 -05:00
|
|
|
ptr.cast::<u64>().copy_from_nonoverlapping(NonNull::from(&expr).cast(), size / 8)
|
2024-06-01 13:30:07 -05:00
|
|
|
};
|
|
|
|
unsafe { ptr.cast::<Expr<'a>>().as_ref() }
|
2024-05-10 08:29:11 -05:00
|
|
|
}
|
|
|
|
|
2024-06-01 13:30:07 -05:00
|
|
|
pub fn alloc_slice<T: Copy>(&self, slice: &[T]) -> &'a [T] {
|
2024-05-19 11:20:42 -05:00
|
|
|
if slice.is_empty() || std::mem::size_of::<T>() == 0 {
|
|
|
|
return &mut [];
|
|
|
|
}
|
|
|
|
|
2024-05-10 08:29:11 -05:00
|
|
|
let layout = std::alloc::Layout::array::<T>(slice.len()).unwrap();
|
|
|
|
let ptr = self.alloc_low(layout);
|
2024-07-08 00:22:53 -05:00
|
|
|
unsafe { ptr.as_ptr().cast::<T>().copy_from_nonoverlapping(slice.as_ptr(), slice.len()) };
|
2024-06-01 13:30:07 -05:00
|
|
|
unsafe { std::slice::from_raw_parts(ptr.as_ptr() as _, slice.len()) }
|
2024-05-10 08:29:11 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
fn alloc_low(&self, layout: std::alloc::Layout) -> NonNull<u8> {
|
|
|
|
assert!(layout.align() <= ArenaChunk::ALIGN);
|
|
|
|
assert!(layout.size() <= ArenaChunk::CHUNK_SIZE);
|
|
|
|
|
2024-05-17 12:53:59 -05:00
|
|
|
let chunk = unsafe { &mut *self.chunk.get() };
|
2024-05-10 08:29:11 -05:00
|
|
|
|
2024-05-17 12:53:59 -05:00
|
|
|
if let Some(ptr) = chunk.alloc(layout) {
|
|
|
|
return ptr;
|
2024-05-10 08:29:11 -05:00
|
|
|
}
|
|
|
|
|
2024-05-19 11:20:42 -05:00
|
|
|
unsafe {
|
|
|
|
std::ptr::write(chunk, ArenaChunk::new(chunk.base));
|
2024-05-10 08:29:11 -05:00
|
|
|
}
|
2024-05-17 12:53:59 -05:00
|
|
|
|
|
|
|
chunk.alloc(layout).unwrap()
|
2024-05-10 08:29:11 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ArenaChunk {
|
|
|
|
base: *mut u8,
|
2024-07-08 00:22:53 -05:00
|
|
|
end: *mut u8,
|
2024-05-10 08:29:11 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for ArenaChunk {
|
|
|
|
fn default() -> Self {
|
2024-07-08 00:22:53 -05:00
|
|
|
Self { base: std::ptr::null_mut(), end: std::ptr::null_mut() }
|
2024-05-10 08:29:11 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ArenaChunk {
|
|
|
|
const ALIGN: usize = std::mem::align_of::<Self>();
|
2024-07-08 00:22:53 -05:00
|
|
|
const CHUNK_SIZE: usize = 1 << 16;
|
2024-05-10 08:29:11 -05:00
|
|
|
const LAYOUT: std::alloc::Layout =
|
|
|
|
unsafe { std::alloc::Layout::from_size_align_unchecked(Self::CHUNK_SIZE, Self::ALIGN) };
|
2024-07-08 00:22:53 -05:00
|
|
|
const NEXT_OFFSET: usize = Self::CHUNK_SIZE - std::mem::size_of::<*mut u8>();
|
2024-05-10 08:29:11 -05:00
|
|
|
|
|
|
|
fn new(next: *mut u8) -> Self {
|
|
|
|
let base = unsafe { std::alloc::alloc(Self::LAYOUT) };
|
2024-05-19 11:20:42 -05:00
|
|
|
let end = unsafe { base.add(Self::NEXT_OFFSET) };
|
2024-05-10 08:29:11 -05:00
|
|
|
Self::set_next(base, next);
|
|
|
|
Self { base, end }
|
|
|
|
}
|
|
|
|
|
|
|
|
fn set_next(curr: *mut u8, next: *mut u8) {
|
|
|
|
unsafe { std::ptr::write(curr.add(Self::NEXT_OFFSET) as *mut _, next) };
|
|
|
|
}
|
|
|
|
|
|
|
|
fn next(curr: *mut u8) -> *mut u8 {
|
|
|
|
unsafe { std::ptr::read(curr.add(Self::NEXT_OFFSET) as *mut _) }
|
|
|
|
}
|
|
|
|
|
|
|
|
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 size = layout.size() + padding;
|
|
|
|
if size > self.end as usize - self.base as usize {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
unsafe { self.end = self.end.sub(size) };
|
|
|
|
unsafe { Some(NonNull::new_unchecked(self.end)) }
|
|
|
|
}
|
2024-05-19 11:20:42 -05:00
|
|
|
|
|
|
|
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))
|
|
|
|
}
|
2024-05-10 08:29:11 -05:00
|
|
|
}
|
|
|
|
|
2024-05-17 12:53:59 -05:00
|
|
|
impl Drop for ArenaChunk {
|
|
|
|
fn drop(&mut self) {
|
2024-09-02 17:27:50 -05:00
|
|
|
//log::inf!(
|
|
|
|
// "dropping chunk of size: {}",
|
|
|
|
// (Self::LAYOUT.size() - (self.end as usize - self.base as usize))
|
|
|
|
// * !self.end.is_null() as usize
|
|
|
|
//);
|
2024-05-17 12:53:59 -05:00
|
|
|
let mut current = self.base;
|
|
|
|
while !current.is_null() {
|
|
|
|
let next = Self::next(current);
|
|
|
|
unsafe { std::alloc::dealloc(current, Self::LAYOUT) };
|
|
|
|
current = next;
|
2024-09-02 17:27:50 -05:00
|
|
|
//log::dbg!("deallocating full chunk");
|
2024-05-17 12:53:59 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-06-25 14:41:12 -05:00
|
|
|
|
|
|
|
#[cfg(test)]
|
2024-07-19 14:04:22 -05:00
|
|
|
pub mod test {
|
|
|
|
pub fn format(ident: &str, input: &str) {
|
2024-09-01 14:15:29 -05:00
|
|
|
let ast = super::Ast::new(ident, input.to_owned(), &|_, _| Ok(0));
|
2024-07-19 14:04:22 -05:00
|
|
|
let mut output = Vec::new();
|
|
|
|
crate::format_to(&ast, input, &mut output).unwrap();
|
2024-06-25 14:41:12 -05:00
|
|
|
|
|
|
|
let input_path = format!("formatter_{ident}.expected");
|
|
|
|
let output_path = format!("formatter_{ident}.actual");
|
|
|
|
std::fs::write(&input_path, input).unwrap();
|
|
|
|
std::fs::write(&output_path, output).unwrap();
|
|
|
|
|
|
|
|
let success = std::process::Command::new("diff")
|
|
|
|
.arg("-u")
|
|
|
|
.arg("--color")
|
|
|
|
.arg(&input_path)
|
|
|
|
.arg(&output_path)
|
|
|
|
.status()
|
|
|
|
.unwrap()
|
|
|
|
.success();
|
|
|
|
std::fs::remove_file(&input_path).unwrap();
|
|
|
|
std::fs::remove_file(&output_path).unwrap();
|
|
|
|
assert!(success, "test failed");
|
|
|
|
}
|
|
|
|
|
|
|
|
macro_rules! test {
|
|
|
|
($($name:ident => $input:expr;)*) => {$(
|
|
|
|
#[test]
|
|
|
|
fn $name() {
|
|
|
|
format(stringify!($name), $input);
|
|
|
|
}
|
|
|
|
)*};
|
|
|
|
}
|
|
|
|
|
|
|
|
test! {
|
|
|
|
comments => "// comment\n// comment\n\n// comment\n\n\
|
2024-07-19 14:04:22 -05:00
|
|
|
/* comment */\n/* comment */\n\n/* comment */";
|
|
|
|
some_ordinary_code => "loft := fn(): int return loft(1, 2, 3)";
|
2024-06-25 14:41:12 -05:00
|
|
|
some_arg_per_line_code => "loft := fn(): int return loft(\
|
2024-07-19 14:04:22 -05:00
|
|
|
\n\t1,\n\t2,\n\t3,\n)";
|
|
|
|
some_ordinary_struct => "loft := fn(): int return loft.{a: 1, b: 2}";
|
2024-06-25 14:51:41 -05:00
|
|
|
some_ordinary_fild_per_lin_struct => "loft := fn(): int return loft.{\
|
2024-07-19 14:04:22 -05:00
|
|
|
\n\ta: 1,\n\tb: 2,\n}";
|
|
|
|
code_block => "loft := fn(): int {\n\tloft()\n\treturn 1\n}";
|
2024-06-25 14:41:12 -05:00
|
|
|
}
|
|
|
|
}
|