diff --git a/Cargo.lock b/Cargo.lock index afbefe02..f01a8bbc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,16 +20,6 @@ version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" -[[package]] -name = "cc" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e" -dependencies = [ - "find-msvc-tools", - "shlex", -] - [[package]] name = "cfg-if" version = "1.0.4" @@ -128,12 +118,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "find-msvc-tools" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de" - [[package]] name = "futures" version = "0.3.34" @@ -250,15 +234,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "libmimalloc-sys" -version = "0.1.49" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a45a52f43e1c16f667ccfe4dd8c85b7f7c204fd5e3bf46c5b0db9a5c3c0b8e9" -dependencies = [ - "cc", -] - [[package]] name = "linux-raw-sys" version = "0.12.1" @@ -271,15 +246,6 @@ version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" -[[package]] -name = "mimalloc" -version = "0.1.52" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d4139bb28d14ad1facf21d5eb8825051b326e172d216b39f6d31df53cc97862" -dependencies = [ - "libmimalloc-sys", -] - [[package]] name = "mira-constants" version = "0.1.83" @@ -292,8 +258,8 @@ dependencies = [ name = "mira-core" version = "0.1.83" dependencies = [ + "bumpalo", "divan", - "mimalloc", "mira-core", "serde", "strum", @@ -569,12 +535,6 @@ dependencies = [ "zmij", ] -[[package]] -name = "shlex" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" - [[package]] name = "slab" version = "0.4.12" diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index 0d66d1ee..fcdda0eb 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -17,11 +17,10 @@ formatter = [] wasm = ["dep:wasm-bindgen"] wasm-constants = ["dep:wasm-bindgen"] serde = ["dep:serde"] -mimalloc = ["dep:mimalloc"] [dev-dependencies] divan = "0.1.21" -mira-core = { path = ".", features = ["mimalloc"] } +mira-core = { path = "." } [dependencies] unicode-ident = "1" @@ -29,8 +28,4 @@ winnow = "1" strum = { version = "0.28", features = ["derive"] } wasm-bindgen = { version = "0.2", optional = true } serde = { version = "1", optional = true, features = ["derive"] } - -[target.'cfg(target_os = "windows")'.dependencies] -mimalloc = { version = "0.1", optional = true, features = [] } -[target.'cfg(not(target_os = "windows"))'.dependencies] -mimalloc = { version = "0.1", optional = true, features = ["v2"] } +bumpalo = { version = "3", features = ["boxed"] } diff --git a/crates/core/src/compile/mod.rs b/crates/core/src/compile/mod.rs index ee93c023..793ce51e 100644 --- a/crates/core/src/compile/mod.rs +++ b/crates/core/src/compile/mod.rs @@ -1,3 +1,4 @@ +use bumpalo::Bump; use winnow::stream::{Location, Stream}; use crate::parser::{self, AstWalker}; @@ -68,11 +69,11 @@ impl<'s, 'c: 's> Compiler<'s, 'c> { ) } - pub fn parse<'t>(&mut self, tokens: &'t [Token<'t>]) -> Option> { + pub fn parse<'t>(&mut self, tokens: &'t [Token<'t>], arena: &'t Bump) -> Option> { assert!(!tokens.is_empty(), "Cannot parse an empty token list"); // Parsing - let mut stream = parser::to_input(tokens); + let mut stream = parser::to_input(tokens, arena); let Ok(mut script) = parser::parse(&mut stream) else { let remaining = stream.peek_finish(); let range = if remaining.is_empty() { @@ -115,10 +116,11 @@ impl<'s, 'c: 's> Compiler<'s, 'c> { pub fn compile(input: &str, config: &Config) -> CompileResult { let mut compiler = Compiler::new(input, config); + let arena = Bump::new(); let Some(tokens) = compiler.lex() else { return (None, compiler.encode_diagnostics()); }; - let Some(script) = compiler.parse(&tokens) else { + let Some(script) = compiler.parse(&tokens, &arena) else { return (None, compiler.encode_diagnostics()); }; let Some(chunk) = compiler.emit(&script) else { diff --git a/crates/core/src/emitter/emitter_closure.rs b/crates/core/src/emitter/emitter_closure.rs index 2085ac18..3b01bc39 100644 --- a/crates/core/src/emitter/emitter_closure.rs +++ b/crates/core/src/emitter/emitter_closure.rs @@ -3,7 +3,9 @@ use std::ops::{Deref, DerefMut}; use crate::{ diagnostic::SourceRange, emitter::variable::Variable, - parser::{ArrayElementBase, AstWalker as _, Expression, ParameterList, Pattern, Statement}, + parser::{ + ArrayElementBase, AstBox, AstWalker as _, Expression, ParameterList, Pattern, Statement, + }, }; use super::{ @@ -62,7 +64,7 @@ impl<'s, 'c> Emitter<'s, 'c> { pub fn declare_block( &mut self, stmts: &'s Vec>, - expr: &'s Option>>, + expr: &'s Option>>, exports: &mut ModuleExports<'s, 'c>, ) { for stmt in stmts { @@ -75,7 +77,7 @@ impl<'s, 'c> Emitter<'s, 'c> { pub fn emit_block( &mut self, stmts: &'s Vec>, - expr: &'s Option>>, + expr: &'s Option>>, scope_range: SourceRange, ret: Register, brk: Option, @@ -128,7 +130,7 @@ impl<'s, 'c> Emitter<'s, 'c> { scope_range: SourceRange, args: &'s Option>, stmts: &'s Vec>, - expr: &'s Option>>, + expr: &'s Option>>, ) { let arg_len = args.as_ref().map_or(1, |args| args.len()); let mut has_var_args = false; diff --git a/crates/core/src/formatter/manager/types.rs b/crates/core/src/formatter/manager/types.rs index cba3e79d..dd6164bc 100644 --- a/crates/core/src/formatter/manager/types.rs +++ b/crates/core/src/formatter/manager/types.rs @@ -32,6 +32,16 @@ impl Formattable for Box { } } +impl Formattable for bumpalo::boxed::Box<'_, T> { + fn measure(&self, formatter: &FormatManager, indent: usize) -> usize { + self.as_ref().measure(formatter, indent) + } + + fn format(&self, formatter: &mut FormatManager, complexity: usize) { + self.as_ref().format(formatter, complexity) + } +} + impl Formattable for Option { fn measure(&self, formatter: &FormatManager, indent: usize) -> usize { if let Some(value) = self { diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index 5a89cfb5..668f2bfb 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -5,6 +5,7 @@ pub mod emitter; pub mod lexer; pub mod parser; +pub use bumpalo::Bump; pub use compile::Compiler; pub use config::Config; pub use diagnostic::{DiagnosticCode, SerializedDiagnostics, SourceDiagnostic, SourceRange}; @@ -22,7 +23,3 @@ pub mod prelude { pub use std::string::ToString as _; pub use strum::VariantArray as _; } - -#[cfg(all(feature = "mimalloc", not(target_family = "wasm")))] -#[global_allocator] -static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; diff --git a/crates/core/src/parser/array_element.rs b/crates/core/src/parser/array_element.rs index e01dce18..be90a6c4 100644 --- a/crates/core/src/parser/array_element.rs +++ b/crates/core/src/parser/array_element.rs @@ -1,11 +1,11 @@ use super::prelude::*; -#[derive(Debug, Clone, PartialEq, strum::EnumIs)] +#[derive(Debug, PartialEq, strum::EnumIs)] pub enum ArrayElementBase<'s, E, S> { /// Element - Element(Box), + Element(AstBox<'s, E>), /// `..` Spread - Spread(TokenRef<'s>, Box), + Spread(TokenRef<'s>, AstBox<'s, S>), } use ArrayElementBase::*; diff --git a/crates/core/src/parser/array_helper.rs b/crates/core/src/parser/array_helper.rs index 64f5f7db..a7431be3 100644 --- a/crates/core/src/parser/array_helper.rs +++ b/crates/core/src/parser/array_helper.rs @@ -9,7 +9,7 @@ use super::{ }; type _ArrayElement<'s, E, S> = ListItem<'s, ArrayElementBase<'s, E, S>>; -fn array_element<'s, E: Clone + PartialEq + 's, S: Clone + PartialEq + 's>( +fn array_element<'s, E: PartialEq + 's, S: PartialEq + 's>( element: impl Parser<'s, E>, spread: impl Parser<'s, S>, mut missing: impl FnMut(usize) -> E + Copy, @@ -21,8 +21,9 @@ fn array_element<'s, E: Clone + PartialEq + 's, S: Clone + PartialEq + 's>( let pos = comma.range.start; let missing = missing(pos); return Ok(ListItem::new_with_comma( - ArrayElementBase::Element(Box::new(missing)), + ArrayElementBase::Element(AstBox::new_in(missing, i.state)), comma, + i.state, )); } if *first == Operator::CloseBracket @@ -32,13 +33,14 @@ fn array_element<'s, E: Clone + PartialEq + 's, S: Clone + PartialEq + 's>( { return fail.parse_next(i); } + let arena = i.state; let result = if *first == Operator::SpreadRange { (token(Operator::SpreadRange), spread) - .map(|(s, e)| ArrayElementBase::Spread(s, e.into())) + .map(move |(s, e)| ArrayElementBase::Spread(s, AstBox::new_in(e, arena))) .parse_next(i)? } else { element - .map(|e| ArrayElementBase::Element(Box::new(e))) + .map(move |e| ArrayElementBase::Element(AstBox::new_in(e, arena))) .parse_next(i)? }; let last = peek(any).parse_next(i)?; @@ -57,15 +59,15 @@ fn array_element<'s, E: Clone + PartialEq + 's, S: Clone + PartialEq + 's>( || *last == Keyword::Let || *last == Keyword::Const { - return Ok(ListItem::new(result)); + return Ok(ListItem::new(result, i.state)); } let comma = token_or_insert(Operator::Comma, DiagnosticCode::MissingComma).parse_next(i)?; - Ok(ListItem::new_with_comma(result, comma)) + Ok(ListItem::new_with_comma(result, comma, i.state)) } } type _ArrayLike<'s, E, S> = (TokenRef<'s>, Vec<_ArrayElement<'s, E, S>>, TokenRef<'s>); -pub(super) fn array_base<'t, 's: 't, E: Clone + PartialEq + 's, S: Clone + PartialEq + 's>( +pub(super) fn array_base<'t, 's: 't, E: PartialEq + 's, S: PartialEq + 's>( mut open: impl Parser<'s, TokenRef<'s>>, mut close: impl Parser<'s, TokenRef<'s>>, element: impl Parser<'s, E>, diff --git a/crates/core/src/parser/ast_visitor.rs b/crates/core/src/parser/ast_visitor.rs index 8dd53559..8bce3473 100644 --- a/crates/core/src/parser/ast_visitor.rs +++ b/crates/core/src/parser/ast_visitor.rs @@ -61,6 +61,15 @@ impl<'s, E: AstWalker<'s>> AstWalker<'s> for Option { } } +impl<'s, E: AstWalker<'s>> AstWalker<'s> for AstBox<'s, E> { + fn collect_diagnostics(&mut self, collector: &mut DiagnosticsCollector<'_, '_>) { + self.deref_mut().collect_diagnostics(collector); + } + fn range(&self) -> SourceRange { + self.deref().range() + } +} + impl<'s, E: AstWalker<'s>> AstWalker<'s> for Box { fn collect_diagnostics(&mut self, collector: &mut DiagnosticsCollector<'_, '_>) { self.deref_mut().collect_diagnostics(collector); diff --git a/crates/core/src/parser/basic_expressions.rs b/crates/core/src/parser/basic_expressions.rs index e41487c4..53803b10 100644 --- a/crates/core/src/parser/basic_expressions.rs +++ b/crates/core/src/parser/basic_expressions.rs @@ -14,7 +14,7 @@ use super::{ to_input, }; -fn to_interpolate_expr<'s>(token: &'s Token<'s>) -> Expression<'s> { +fn to_interpolate_expr<'s>(token: &'s Token<'s>, arena: &'s bumpalo::Bump) -> Expression<'s> { let TokenKind::InterpolatedString(parts, _) = &token.kind else { unreachable!("Expected InterpolatedString"); }; @@ -38,7 +38,7 @@ fn to_interpolate_expr<'s>(token: &'s Token<'s>) -> Expression<'s> { DiagnosticCode::UnterminatedInterpolation, ); } - let mut token_input = to_input(tokens); + let mut token_input = to_input(tokens, arena); let result = (expression, opt(eof.value(()))).parse_next(&mut token_input); match result { Ok((expr, Some(_))) => expr, @@ -61,9 +61,10 @@ fn to_interpolate_expr<'s>(token: &'s Token<'s>) -> Expression<'s> { } fn record_like<'s>(i: &mut Input<'s>) -> Result> { + let arena = i.state; let (open, parts, close) = record_base( expression, - |t: &Token<'s>| to_interpolate_expr(t), + move |t: &Token<'s>| to_interpolate_expr(t, arena), expression, expression, expression, @@ -115,7 +116,7 @@ fn array<'s>(i: &mut Input<'s>) -> Result> { pub(super) fn interpolation<'s>(i: &mut Input<'s>) -> Result> { let token = one_of(|t: &Token<'s>| matches!(&t.kind, &TokenKind::InterpolatedString(..))) .parse_next(i)?; - Ok(to_interpolate_expr(token)) + Ok(to_interpolate_expr(token, i.state)) } /// callable '(' ('..'? arg),* ')' @@ -127,6 +128,7 @@ type Call<'s> = ( ); fn pseudo_function<'t, 's: 't, const EXTENSION_CALL: bool>(i: &mut Input<'s>) -> Result> { + let arena = i.state; let provided: usize = if EXTENSION_CALL { 1 } else { 0 }; let (kw_type, (open, args, close)) = ( token(Keyword::Type), @@ -137,17 +139,20 @@ fn pseudo_function<'t, 's: 't, const EXTENSION_CALL: bool>(i: &mut Input<'s>) -> ) .parse_next(i)?; let exp = if args.len() != (1 - provided) || args.first().is_some_and(|a| a.is_spread()) { - vec![ListItem::new(ArrayElementBase::Element( - Expression::unknown_range( - [], - SourceRange { - start: kw_type.range.start, - end: close.range.end, - }, - DiagnosticCode::InvalidTypeCall, - ) - .into(), - ))] + vec![ListItem::new( + ArrayElementBase::Element(AstBox::new_in( + Expression::unknown_range( + [], + SourceRange { + start: kw_type.range.start, + end: close.range.end, + }, + DiagnosticCode::InvalidTypeCall, + ), + arena, + )), + arena, + )] } else { args }; @@ -186,31 +191,32 @@ enum AccessIndex<'s> { /// `.` identifier Access(TokenRef<'s>, TokenRef<'s>), /// `[` expression `]` - Index(TokenRef<'s>, Box>, TokenRef<'s>), + Index(TokenRef<'s>, AstBox<'s, Expression<'s>>, TokenRef<'s>), /// `[` additive_expression? (`..` | `..<`) additive_expression? `]` Slice( TokenRef<'s>, - Option>>, + Option>>, TokenRef<'s>, - Option>>, + Option>>, TokenRef<'s>, ), /// `!` NonNil(TokenRef<'s>), } fn access_index<'s>(i: &mut Input<'s>) -> Result> { + let arena = i.state; fn access_token<'s>(i: &mut Input<'s>) -> Result> { one_of(|t: &Token<'s>| matches!(t.kind, TokenKind::Identifier(_) | TokenKind::Ordinal(_))) .map(TokenRef::borrow) .parse_next(i) } - fn additive<'s>(i: &mut Input<'s>) -> Result>> { + fn additive<'s>(i: &mut Input<'s>) -> Result>> { let mut precedence_additive = precedence_of(&TokenKind::Operator(Operator::SpreadRange)); precedence_additive.value += 2; - pratt(precedence_additive, false) + let expression = pratt(precedence_additive, false) .verify_map(verify_expr) - .map(Box::new) - .parse_next(i) + .parse_next(i)?; + Ok(AstBox::new_in(expression, i.state)) } fn range_op<'s>(i: &mut Input<'s>) -> Result> { one_of(|t: &Token<'s>| *t == Operator::SpreadRange || *t == Operator::HalfOpenRange) @@ -240,9 +246,9 @@ fn access_index<'s>(i: &mut Input<'s>) -> Result> { iterable, token(Operator::CloseBracket), ) - .map(|(o, e, c)| match e { + .map(move |(o, e, c)| match e { Iterable::Range(r) => AccessIndex::Slice(o, Some(r.0), r.1, Some(r.2), c), - Iterable::Value(expr) => AccessIndex::Index(o, Box::new(expr), c), + Iterable::Value(expr) => AccessIndex::Index(o, AstBox::new_in(expr, arena), c), }), // `[` additive (`..` | `..<`) `]` ( @@ -257,6 +263,7 @@ fn access_index<'s>(i: &mut Input<'s>) -> Result> { } fn extension_call<'s>(i: &mut Input<'s>) -> Result> { + let arena = i.state; let parenthesised = |i: &mut Input<'s>| { record_like .with_taken() @@ -274,21 +281,28 @@ fn extension_call<'s>(i: &mut Input<'s>) -> Result> { }; let access_chain = |i: &mut Input<'s>| { (variable_token(false, true), repeat(0.., access_index)) - .map(|(first, rest): (_, Vec<_>)| { + .map(move |(first, rest): (_, Vec<_>)| { let mut acc = Expression::Variable(first); for access_index in rest { match access_index { AccessIndex::NonNil(token) => { - acc = Expression::NonNil(Box::new(acc), token); + acc = Expression::NonNil(AstBox::new_in(acc, arena), token); } AccessIndex::Access(dot, token) => { - acc = Expression::Access(Box::new(acc), dot, token); + acc = Expression::Access(AstBox::new_in(acc, arena), dot, token); } AccessIndex::Index(open, exp, close) => { - acc = Expression::Index(Box::new(acc), open, exp, close); + acc = Expression::Index(AstBox::new_in(acc, arena), open, exp, close); } AccessIndex::Slice(left, start, op, end, right) => { - acc = Expression::Slice(Box::new(acc), left, start, op, end, right); + acc = Expression::Slice( + AstBox::new_in(acc, arena), + left, + start, + op, + end, + right, + ); } } } @@ -296,9 +310,13 @@ fn extension_call<'s>(i: &mut Input<'s>) -> Result> { }) .parse_next(i) }; + let callable = move |i: &mut Input<'s>| { + let expression = alt((parenthesised, access_chain)).parse_next(i)?; + Ok(Callable::Expression(AstBox::new_in(expression, arena))) + }; alt(( ( - alt((parenthesised, access_chain)).map(|e| Callable::Expression(Box::new(e))), + callable, arg_list(token_or_insert( Operator::OpenParen, DiagnosticCode::MissingOpenParenAfterExtension, @@ -311,9 +329,10 @@ fn extension_call<'s>(i: &mut Input<'s>) -> Result> { } fn postfix<'s>(i: &mut Input<'s>) -> Result> { + let arena = i.state; enum Function<'s> { Call(TokenRef<'s>, Vec>, TokenRef<'s>), - TaggedString(Box>), + TaggedString(AstBox<'s, Expression<'s>>), Extension( TokenRef<'s>, Callable<'s>, @@ -322,12 +341,12 @@ fn postfix<'s>(i: &mut Input<'s>) -> Result> { TokenRef<'s>, ), Access(TokenRef<'s>, TokenRef<'s>), - Index(TokenRef<'s>, Box>, TokenRef<'s>), + Index(TokenRef<'s>, AstBox<'s, Expression<'s>>, TokenRef<'s>), Slice( TokenRef<'s>, - Option>>, + Option>>, TokenRef<'s>, - Option>>, + Option>>, TokenRef<'s>, ), NonNil(TokenRef<'s>), @@ -338,10 +357,13 @@ fn postfix<'s>(i: &mut Input<'s>) -> Result> { alt(( (token(Operator::ColonColon), extension_call) .map(|(kw, (ex, o, a, c))| Function::Extension(kw, ex, o, a, c)), - one_of(|t: &Token<'s>| matches!(t.kind, TokenKind::String(..))).map(|token| { - Function::TaggedString(Box::new(Expression::Literal(TokenRef::borrow(token)))) + one_of(|t: &Token<'s>| matches!(t.kind, TokenKind::String(..))).map(move |token| { + Function::TaggedString(AstBox::new_in( + Expression::Literal(TokenRef::borrow(token)), + arena, + )) }), - interpolation.map(|ex| Function::TaggedString(ex.into())), + boxed(interpolation).map(Function::TaggedString), access_index.map(|t| match t { AccessIndex::NonNil(token) => Function::NonNil(token), AccessIndex::Access(dot, token) => Function::Access(dot, token), @@ -364,18 +386,18 @@ fn postfix<'s>(i: &mut Input<'s>) -> Result> { // left-associative Ok(functions.into_iter().fold(first, |acc, exp| match exp { Function::Call(o, args, c) => { - Expression::Call(Callable::Expression(Box::new(acc)), o, args, c) + Expression::Call(Callable::Expression(AstBox::new_in(acc, arena)), o, args, c) } - Function::TaggedString(ex) => Expression::TaggedString(Box::new(acc), ex), + Function::TaggedString(ex) => Expression::TaggedString(AstBox::new_in(acc, arena), ex), Function::Extension(e, ex, o, arg, c) => { - Expression::Extension(Box::new(acc), e, ex, o, arg, c) + Expression::Extension(AstBox::new_in(acc, arena), e, ex, o, arg, c) } - Function::Access(dot, token) => Expression::Access(Box::new(acc), dot, token), - Function::Index(l, index, r) => Expression::Index(Box::new(acc), l, index, r), + Function::Access(dot, token) => Expression::Access(AstBox::new_in(acc, arena), dot, token), + Function::Index(l, index, r) => Expression::Index(AstBox::new_in(acc, arena), l, index, r), Function::Slice(left, start, op, end, right) => { - Expression::Slice(Box::new(acc), left, start, op, end, right) + Expression::Slice(AstBox::new_in(acc, arena), left, start, op, end, right) } - Function::NonNil(token) => Expression::NonNil(Box::new(acc), token), + Function::NonNil(token) => Expression::NonNil(AstBox::new_in(acc, arena), token), })) } @@ -425,21 +447,21 @@ fn pratt_prefix<'s>(i: &mut Input<'s>) -> Result> { let expr = pratt(precedence, false) .verify_map(verify_expr) .parse_next(i)?; - Ok(Expression::Prefix(op.into(), expr.into())) + Ok(Expression::Prefix(op.into(), AstBox::new_in(expr, i.state))) } else { postfix.parse_next(i) } } fn pratt_infix<'s>( - left: Box>, + left: AstBox<'s, Expression<'s>>, op: &'s Token<'s>, mut precedence: PrecedenceResult, allow_range: bool, i: &mut Input<'s>, ) -> Result> { if *op == Keyword::Is { - let right = pattern(false).map(Box::new).parse_next(i)?; + let right = boxed(pattern(false)).parse_next(i)?; return Ok(Iterable::Value(Expression::Is(left, op.into(), right))); } // 调整优先级以实现右结合 @@ -450,10 +472,10 @@ fn pratt_infix<'s>( let expr = pratt(precedence, false) .verify_map(verify_expr) .parse_next(i)?; - Ok(Box::new(expr)) + Ok(AstBox::new_in(expr, i.state)) }; if *op == Operator::Question { - let then_exp = expression.parse_next(i)?.into(); + let then_exp = AstBox::new_in(expression.parse_next(i)?, i.state); let colon = token_or_insert(Operator::Colon, DiagnosticCode::MissingColon).parse_next(i)?; let else_exp = parse_right(i)?; return Ok(Iterable::Value(Expression::Cond( @@ -500,7 +522,8 @@ fn pratt<'s>(precedence: PrecedenceResult, allow_range: bool) -> impl Parser<'s, } let op = any.parse_next(i)?; - match pratt_infix(left.into(), op, op_precedence, allow_range, i)? { + let left_box = AstBox::new_in(left, i.state); + match pratt_infix(left_box, op, op_precedence, allow_range, i)? { Iterable::Value(e) => left = e, Iterable::Range(r) => return Ok(Iterable::Range(r)), } diff --git a/crates/core/src/parser/block_expressions.rs b/crates/core/src/parser/block_expressions.rs index fb20dc6b..2f048075 100644 --- a/crates/core/src/parser/block_expressions.rs +++ b/crates/core/src/parser/block_expressions.rs @@ -18,9 +18,8 @@ fn optional_else<'s>(i: &mut Input<'s>) -> Result>> { return Ok(None); }; - let block = alt((if_expression, block_expression)) - .map(Box::new) - .parse_next(i)?; + let block = alt((if_expression, block_expression)).parse_next(i)?; + let block = AstBox::new_in(block, i.state); Ok(Some(ElseBlock(kw_else, block))) } @@ -28,8 +27,8 @@ fn optional_else<'s>(i: &mut Input<'s>) -> Result>> { pub(super) fn if_expression<'s>(i: &mut Input<'s>) -> Result> { seq!(Expression::If( token(Keyword::If), - expression_or_insert(|t| *t == Operator::OpenBrace).map(Box::new), - block_expression.map(Box::new), + boxed(expression_or_insert(|t| *t == Operator::OpenBrace)), + boxed(block_expression), optional_else, )) .parse_next(i) @@ -79,7 +78,7 @@ pub(super) fn fn_expression<'s>(i: &mut Input<'s>) -> Result> { seq!(Expression::Function( token(Keyword::Fn), parameter_list, - block_expression.map(Box::new), + boxed(block_expression), )) .parse_next(i) } @@ -87,7 +86,7 @@ pub(super) fn fn_expression<'s>(i: &mut Input<'s>) -> Result> { pub(super) fn loop_expression<'s>(i: &mut Input<'s>) -> Result> { seq!(Expression::Loop( token(Keyword::Loop), - block_expression_no_expr.map(Box::new), + boxed(block_expression_no_expr), )) .parse_next(i) } @@ -95,8 +94,8 @@ pub(super) fn loop_expression<'s>(i: &mut Input<'s>) -> Result> { pub(super) fn while_expression<'s>(i: &mut Input<'s>) -> Result> { seq!(Expression::While( token(Keyword::While), - expression_or_insert(|t| *t == Operator::OpenBrace).map(Box::new), - block_expression_no_expr.map(Box::new), + boxed(expression_or_insert(|t| *t == Operator::OpenBrace)), + boxed(block_expression_no_expr), optional_else, )) .parse_next(i) @@ -127,7 +126,7 @@ pub(super) fn match_expression<'s>(i: &mut Input<'s>) -> Result> } ( token(Keyword::Match), - expression_or_insert(|t| *t == Operator::OpenBrace).map(Box::new), + boxed(expression_or_insert(|t| *t == Operator::OpenBrace)), token_or_insert(Operator::OpenBrace, DiagnosticCode::MissingOpenBrace), repeat(0.., branch_parser), token_or_insert(Operator::CloseBrace, DiagnosticCode::MissingCloseBrace), @@ -142,10 +141,10 @@ pub(super) fn for_in_expression<'s>(i: &mut Input<'s>) -> Result> seq!(Expression::ForIn( token(Keyword::For), // 由后边的 `in` 定位,无条件插入 - pattern_or_insert(false, |_| true).map(Box::new), + boxed(pattern_or_insert(false, |_| true)), token(Keyword::In), - iterable.map(Box::new), - block_expression_no_expr.map(Box::new), + boxed(iterable), + boxed(block_expression_no_expr), optional_else, )) .parse_next(i) diff --git a/crates/core/src/parser/expression.rs b/crates/core/src/parser/expression.rs index 8f175f41..8dc27877 100644 --- a/crates/core/src/parser/expression.rs +++ b/crates/core/src/parser/expression.rs @@ -2,12 +2,12 @@ use crate::parser::helper::unknown_range; use super::prelude::*; -#[derive(Debug, Clone, PartialEq, strum::EnumIs)] +#[derive(Debug, PartialEq, strum::EnumIs)] pub enum Callable<'s> { /// `type` Type(TokenRef<'s>), /// expression - Expression(Box>), + Expression(AstBox<'s, Expression<'s>>), } impl<'s> AstWalker<'s> for Callable<'s> { @@ -28,8 +28,8 @@ impl<'s> AstWalker<'s> for Callable<'s> { } /// `else` (block_expr | if_expr) -#[derive(Debug, Clone, PartialEq)] -pub struct ElseBlock<'s>(pub TokenRef<'s>, pub Box>); +#[derive(Debug, PartialEq)] +pub struct ElseBlock<'s>(pub TokenRef<'s>, pub AstBox<'s, Expression<'s>>); impl<'s> AstWalker<'s> for ElseBlock<'s> { fn collect_diagnostics(&mut self, collector: &mut DiagnosticsCollector<'_, '_>) { @@ -42,7 +42,7 @@ impl<'s> AstWalker<'s> for ElseBlock<'s> { } /// `case` pattern (`if` expression)? block_expression -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, PartialEq)] pub struct MatchCase<'s>( pub TokenRef<'s>, pub Pattern<'s>, @@ -67,7 +67,7 @@ impl<'s> AstWalker<'s> for MatchCase<'s> { } } -#[derive(Debug, Clone, PartialEq, strum::EnumIs)] +#[derive(Debug, PartialEq, strum::EnumIs)] pub enum Expression<'s> { // primary /// number | string | ordinal | `true` | `false` | `nil` @@ -80,7 +80,7 @@ pub enum Expression<'s> { /// identifier Variable(TokenRef<'s>), /// `(` expression `)` - Grouping(TokenRef<'s>, Box>, TokenRef<'s>), + Grouping(TokenRef<'s>, AstBox<'s, Expression<'s>>, TokenRef<'s>), /// `(` element* `)` /// /// Use `()` for an empty record. @@ -107,7 +107,7 @@ pub enum Expression<'s> { TokenRef<'s>, ), /// expression ( interpolated_string | string ) - TaggedString(Box>, Box>), + TaggedString(AstBox<'s, Expression<'s>>, AstBox<'s, Expression<'s>>), /// expression `::` extension `(` arguments `)` /// extension /// : identifier (`.` ( identifier | ordinal ))* @@ -117,7 +117,7 @@ pub enum Expression<'s> { /// /// Like `Call`, but `expression` is used as the first argument. Extension( - Box>, + AstBox<'s, Expression<'s>>, TokenRef<'s>, Callable<'s>, TokenRef<'s>, @@ -127,25 +127,25 @@ pub enum Expression<'s> { /// expression `.` field /// /// Field must be an identifier or an ordinal. - Access(Box>, TokenRef<'s>, TokenRef<'s>), + Access(AstBox<'s, Expression<'s>>, TokenRef<'s>, TokenRef<'s>), /// expression `[` expression `]` Index( - Box>, + AstBox<'s, Expression<'s>>, TokenRef<'s>, - Box>, + AstBox<'s, Expression<'s>>, TokenRef<'s>, ), /// expression `[` additive_expression? (`..` | `..<`) additive_expression? `]` Slice( - Box>, + AstBox<'s, Expression<'s>>, TokenRef<'s>, - Option>>, + Option>>, TokenRef<'s>, - Option>>, + Option>>, TokenRef<'s>, ), /// expression `!` - NonNil(Box>, TokenRef<'s>), + NonNil(AstBox<'s, Expression<'s>>, TokenRef<'s>), /// op expression /// @@ -153,7 +153,7 @@ pub enum Expression<'s> { /// - `!` logical not /// - `-` negation /// - `+` unary plus - Prefix(TokenRef<'s>, Box>), + Prefix(TokenRef<'s>, AstBox<'s, Expression<'s>>), // infix /// expression op expression @@ -167,9 +167,17 @@ pub enum Expression<'s> { /// 1. `==` `!=` `=~` `!~` equality /// 1. `&&` logical and /// 1. `||` logical or - Infix(Box>, TokenRef<'s>, Box>), + Infix( + AstBox<'s, Expression<'s>>, + TokenRef<'s>, + AstBox<'s, Expression<'s>>, + ), /// expression `is` pattern - Is(Box>, TokenRef<'s>, Box>), + Is( + AstBox<'s, Expression<'s>>, + TokenRef<'s>, + AstBox<'s, Pattern<'s>>, + ), // block-like /// `{` statements* expression? `}` @@ -179,7 +187,7 @@ pub enum Expression<'s> { Block( TokenRef<'s>, Vec>, - Option>>, + Option>>, TokenRef<'s>, ), /// `loop` block_expression @@ -187,7 +195,7 @@ pub enum Expression<'s> { /// The final expression of the block must not present. /// /// The value of the block is the expression of the `break` statement if present. Otherwise, `nil`. - Loop(TokenRef<'s>, Box>), + Loop(TokenRef<'s>, AstBox<'s, Expression<'s>>), /// `while` expression block_expression (`else` expression)? /// /// The final expression of the block must not present. @@ -199,8 +207,8 @@ pub enum Expression<'s> { /// the value is the value of the `else_block`. Otherwise, `nil`. While( TokenRef<'s>, - Box>, - Box>, + AstBox<'s, Expression<'s>>, + AstBox<'s, Expression<'s>>, Option>, ), /// `for` pattern `in` expression block_expression (`else` expression)? @@ -214,10 +222,10 @@ pub enum Expression<'s> { /// the value is the value of the `else_block`. Otherwise, `nil`. ForIn( TokenRef<'s>, - Box>, + AstBox<'s, Pattern<'s>>, TokenRef<'s>, - Box>, - Box>, + AstBox<'s, Iterable<'s>>, + AstBox<'s, Expression<'s>>, Option>, ), /// `if` expression block_expression (`else` expression)? @@ -227,17 +235,17 @@ pub enum Expression<'s> { /// The `else_block` is a block expression or an if expression. If( TokenRef<'s>, - Box>, - Box>, + AstBox<'s, Expression<'s>>, + AstBox<'s, Expression<'s>>, Option>, ), /// cond ? expression : expression Cond( - Box>, + AstBox<'s, Expression<'s>>, TokenRef<'s>, - Box>, + AstBox<'s, Expression<'s>>, TokenRef<'s>, - Box>, + AstBox<'s, Expression<'s>>, ), /// `match` expression `{` ( `case` pattern (`if` expression)? block_expression )* `}` /// @@ -246,7 +254,7 @@ pub enum Expression<'s> { /// If no match is found, the value is `nil`. Match( TokenRef<'s>, - Box>, + AstBox<'s, Expression<'s>>, TokenRef<'s>, Vec>, TokenRef<'s>, @@ -255,7 +263,11 @@ pub enum Expression<'s> { /// /// Just like function declarations, but without the identifier. /// See [Statement::Function] for more details. - Function(TokenRef<'s>, Option>, Box>), + Function( + TokenRef<'s>, + Option>, + AstBox<'s, Expression<'s>>, + ), /// Unknown expression Unknown { recovered: Option>>, diff --git a/crates/core/src/parser/helper.rs b/crates/core/src/parser/helper.rs index d7a9594b..bf2f2454 100644 --- a/crates/core/src/parser/helper.rs +++ b/crates/core/src/parser/helper.rs @@ -8,8 +8,9 @@ use super::{expressions::expression, prelude::*, statements::statement}; pub(super) fn construct_statements_and_expression<'s>( mut statements: Vec>, expression: Option>, -) -> (Vec>, Option>>) { - let expression = expression.map(Box::new); + arena: &'s bumpalo::Bump, +) -> (Vec>, Option>>) { + let expression = expression.map(|expression| AstBox::new_in(expression, arena)); if expression.is_some() || statements.is_empty() { return (statements, expression); } @@ -27,10 +28,12 @@ pub(super) fn construct_statements_and_expression<'s>( pub(super) fn statements_and_expression<'s>( i: &mut Input<'s>, -) -> Result<(Vec>, Option>>)> { +) -> Result<(Vec>, Option>>)> { let (statements, expression): (Vec<_>, _) = (repeat(0.., statement), opt(expression)).parse_next(i)?; - Ok(construct_statements_and_expression(statements, expression)) + Ok(construct_statements_and_expression( + statements, expression, i.state, + )) } pub(super) fn literal_token<'s>(i: &mut Input<'s>) -> Result> { @@ -55,7 +58,7 @@ pub(super) fn variable_token<'s>( move |i: &mut Input<'s>| { let t = one_of(|t: &Token<'s>| { matches!(&t.kind, &TokenKind::Identifier(_)) - || matches!(&t.kind, &TokenKind::Keyword(kw) + || matches!(&t.kind, &TokenKind::Keyword(kw) if kw.is_reserved() || kw == Keyword::Underscore || kw == Keyword::Global) }) .parse_next(i)?; diff --git a/crates/core/src/parser/iterable.rs b/crates/core/src/parser/iterable.rs index 0c2a8673..1a5ad3b9 100644 --- a/crates/core/src/parser/iterable.rs +++ b/crates/core/src/parser/iterable.rs @@ -1,6 +1,6 @@ use super::prelude::*; -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, PartialEq)] pub enum Iterable<'s> { Range(Range<'s>), Value(Expression<'s>), diff --git a/crates/core/src/parser/json_expressions.rs b/crates/core/src/parser/json_expressions.rs index bea433ea..25b4289d 100644 --- a/crates/core/src/parser/json_expressions.rs +++ b/crates/core/src/parser/json_expressions.rs @@ -30,26 +30,28 @@ pub(super) fn json_start<'s>(i: &mut Input<'s>) -> Result<()> { enum JsonFieldName<'s> { Literal(TokenRef<'s>), - Interpolated(Box>), + Interpolated(AstBox<'s, Expression<'s>>), } struct JsonElement<'s> { key: JsonFieldName<'s>, colon: TokenRef<'s>, - value: Box>, + value: AstBox<'s, Expression<'s>>, comma: TokenRef<'s>, } fn json_field_name<'s>(i: &mut Input<'s>) -> Result> { + let arena = i.state; alt(( // "xxx" one_of(|t: &Token<'s>| matches!(&t.kind, &TokenKind::String(..))) .map(|t: &Token<'s>| JsonFieldName::Literal(t.into())), // `$xxx` - interpolation.map(|e| JsonFieldName::Interpolated(Box::new(e))), + interpolation.map(move |e| JsonFieldName::Interpolated(AstBox::new_in(e, arena))), )) .parse_next(i) } pub(super) fn json_expression<'s>(i: &mut Input<'s>) -> Result> { + let arena = i.state; // Peek to see if it's a JSON object-like expression peek(json_start).parse_next(i)?; let open = token(Operator::OpenBrace).parse_next(i)?; @@ -61,10 +63,10 @@ pub(super) fn json_expression<'s>(i: &mut Input<'s>) -> Result> { expression_or_insert(|t| *t == Operator::Comma || *t == Operator::CloseBrace), token_or_insert(Operator::Comma, DiagnosticCode::MissingComma), ) - .map(|(key, colon, value, comma)| JsonElement { + .map(move |(key, colon, value, comma)| JsonElement { key, colon, - value: Box::new(value), + value: AstBox::new_in(value, arena), comma, }), ) @@ -85,9 +87,9 @@ pub(super) fn json_expression<'s>(i: &mut Input<'s>) -> Result> { }; if idx == el_count - 1 && e.comma.is_unknown() { // Remove the trailing comma diagnostic for the last element - RecordElement::new(el) + RecordElement::new(el, arena) } else { - RecordElement::new_with_comma(el, e.comma) + RecordElement::new_with_comma(el, e.comma, arena) } }) .collect(); diff --git a/crates/core/src/parser/list_item.rs b/crates/core/src/parser/list_item.rs index afba15dd..ac391a62 100644 --- a/crates/core/src/parser/list_item.rs +++ b/crates/core/src/parser/list_item.rs @@ -3,8 +3,8 @@ use std::ops::{Deref, DerefMut}; use super::prelude::*; /// item ','? -#[derive(Debug, Clone, PartialEq)] -pub struct ListItem<'s, T>(pub Box, pub Option>); +#[derive(Debug, PartialEq)] +pub struct ListItem<'s, T: 's>(pub AstBox<'s, T>, pub Option>); impl<'s, T: AstWalker<'s>> AstWalker<'s> for ListItem<'s, T> { fn collect_diagnostics(&mut self, collector: &mut DiagnosticsCollector<'_, '_>) { @@ -20,12 +20,12 @@ impl<'s, T: AstWalker<'s>> AstWalker<'s> for ListItem<'s, T> { } impl<'s, T> ListItem<'s, T> { - pub fn new_with_comma(item: T, tail_comma: TokenRef<'s>) -> Self { - Self(Box::new(item), Some(tail_comma)) + pub fn new_with_comma(item: T, tail_comma: TokenRef<'s>, arena: &'s bumpalo::Bump) -> Self { + Self(AstBox::new_in(item, arena), Some(tail_comma)) } - pub fn new(item: T) -> Self { - Self(Box::new(item), None) + pub fn new(item: T, arena: &'s bumpalo::Bump) -> Self { + Self(AstBox::new_in(item, arena), None) } pub fn has_tail_comma(&self) -> bool { @@ -36,7 +36,7 @@ impl<'s, T> ListItem<'s, T> { } pub fn unwrap(self) -> T { - *self.0 + AstBox::into_inner(self.0) } } diff --git a/crates/core/src/parser/mod.rs b/crates/core/src/parser/mod.rs index ebc577eb..5602e5f7 100644 --- a/crates/core/src/parser/mod.rs +++ b/crates/core/src/parser/mod.rs @@ -1,9 +1,11 @@ use winnow::{ ModalResult, Parser as _, error::{EmptyError, ErrMode}, - stream::TokenSlice, + stream::{Stateful, TokenSlice}, }; +use bumpalo::Bump; + use crate::lexer::Token; mod array_element; @@ -42,7 +44,8 @@ pub use script::Script; pub use statement::Statement; pub use token_ref::TokenRef; -pub type Input<'s> = TokenSlice<'s, Token<'s>>; +pub type AstBox<'s, T> = bumpalo::boxed::Box<'s, T>; +pub type Input<'s> = Stateful>, &'s Bump>; pub(crate) type Result = ModalResult; trait Parser<'s, Output>: winnow::Parser, Output, ErrMode> + Copy {} @@ -51,11 +54,21 @@ impl<'s, Output, F> Parser<'s, Output> for F where { } +fn boxed<'s, Output: 's>( + mut parser: impl Parser<'s, Output>, +) -> impl Parser<'s, AstBox<'s, Output>> { + move |input: &mut Input<'s>| { + let output = parser.parse_next(input)?; + Ok(AstBox::new_in(output, input.state)) + } +} + mod prelude { pub(super) use super::{ - ArgElement, ArrayElement, ArrayElementBase, ArrayPattern, AstWalker, Callable, ElseBlock, - Expression, Input, Iterable, ListItem, MatchCase, ParameterList, Parser, Pattern, Range, - RecordElement, RecordElementBase, RecordPattern, Result, Script, Statement, TokenRef, + ArgElement, ArrayElement, ArrayElementBase, ArrayPattern, AstBox, AstWalker, Callable, + ElseBlock, Expression, Input, Iterable, ListItem, MatchCase, ParameterList, Parser, + Pattern, Range, RecordElement, RecordElementBase, RecordPattern, Result, Script, Statement, + TokenRef, boxed, }; pub(super) use crate::{ diagnostic::{DiagnosticCode, DiagnosticsCollector, SourceDiagnostic, SourceRange}, @@ -67,8 +80,11 @@ mod prelude { }; } -pub fn to_input<'s>(tokens: &'s [Token<'s>]) -> Input<'s> { - TokenSlice::new(tokens) +pub fn to_input<'s>(tokens: &'s [Token<'s>], arena: &'s Bump) -> Input<'s> { + Stateful { + input: TokenSlice::new(tokens), + state: arena, + } } pub fn parse<'s>(i: &mut Input<'s>) -> Result> { diff --git a/crates/core/src/parser/parameter_list.rs b/crates/core/src/parser/parameter_list.rs index 3d83b019..e2c5a776 100644 --- a/crates/core/src/parser/parameter_list.rs +++ b/crates/core/src/parser/parameter_list.rs @@ -9,7 +9,7 @@ use super::{ }; /// `(` ...items `)` -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, PartialEq)] pub struct ParameterList<'s>( pub TokenRef<'s>, pub Vec>, @@ -42,6 +42,7 @@ impl<'s> AstWalker<'s> for ParameterList<'s> { } pub(super) fn parameter_list<'s>(i: &mut Input<'s>) -> Result>> { + let arena = i.state; let list = opt(array_pattern_like( token(Operator::OpenParen), token_or_insert(Operator::CloseParen, DiagnosticCode::MissingCloseParen), @@ -67,8 +68,9 @@ pub(super) fn parameter_list<'s>(i: &mut Input<'s>) -> Result { /// `(` pattern `)` /// /// Grouping pattern. - Grouping(TokenRef<'s>, Box>, TokenRef<'s>), + Grouping(TokenRef<'s>, AstBox<'s, Pattern<'s>>, TokenRef<'s>), /// ( `+` | `-` )? literal /// /// Matches against a literal value. @@ -19,11 +19,15 @@ pub enum Pattern<'s> { /// ( `>` | `>=` | `<=` | `<` | `==` | `!=` | `=~` | `!~` ) (pattern_constant | pattern_literal) /// /// Matches against a relation with constant values. - Relation(TokenRef<'s>, Box>), + Relation(TokenRef<'s>, AstBox<'s, Pattern<'s>>), /// (pattern_constant | pattern_literal) ( `..` | `..<` ) (pattern_constant | pattern_literal) /// /// Matches against a range of constant values. - Range(Box>, TokenRef<'s>, Box>), + Range( + AstBox<'s, Pattern<'s>>, + TokenRef<'s>, + AstBox<'s, Pattern<'s>>, + ), /// `_` /// /// Matches and discards a value. @@ -73,15 +77,23 @@ pub enum Pattern<'s> { /// pattern `and` pattern /// /// Matches all of the patterns. - And(Box>, TokenRef<'s>, Box>), + And( + AstBox<'s, Pattern<'s>>, + TokenRef<'s>, + AstBox<'s, Pattern<'s>>, + ), /// pattern `or` pattern /// /// Matches any of the patterns. - Or(Box>, TokenRef<'s>, Box>), + Or( + AstBox<'s, Pattern<'s>>, + TokenRef<'s>, + AstBox<'s, Pattern<'s>>, + ), /// `not` pattern /// /// Matches if the pattern does not match. - Not(TokenRef<'s>, Box>), + Not(TokenRef<'s>, AstBox<'s, Pattern<'s>>), /// Unknown pattern. Unknown { diff --git a/crates/core/src/parser/patterns.rs b/crates/core/src/parser/patterns.rs index 3204ebca..9568ad81 100644 --- a/crates/core/src/parser/patterns.rs +++ b/crates/core/src/parser/patterns.rs @@ -93,18 +93,26 @@ fn primary_pattern<'s>(rebind: bool) -> impl Parser<'s, Pattern<'s>> { fn not_pattern<'s>(rebind: bool) -> impl Parser<'s, Pattern<'s>> { move |i: &mut Input<'s>| { + let arena = i.state; (token(Keyword::Not), primary_pattern(rebind)) - .map(|(kw_not, p)| Pattern::Not(kw_not, Box::new(p))) + .map(move |(kw_not, p)| Pattern::Not(kw_not, AstBox::new_in(p, arena))) .parse_next(i) } } fn and_pattern<'s>(rebind: bool) -> impl Parser<'s, Pattern<'s>> { move |i: &mut Input<'s>| { + let arena = i.state; separated_foldl1( primary_pattern(rebind), token(Keyword::And), - |left, op, right| Pattern::And(Box::new(left), op, Box::new(right)), + move |left, op, right| { + Pattern::And( + AstBox::new_in(left, arena), + op, + AstBox::new_in(right, arena), + ) + }, ) .parse_next(i) } @@ -112,10 +120,17 @@ fn and_pattern<'s>(rebind: bool) -> impl Parser<'s, Pattern<'s>> { fn or_pattern<'s>(rebind: bool) -> impl Parser<'s, Pattern<'s>> { move |i: &mut Input<'s>| { + let arena = i.state; separated_foldl1( and_pattern(rebind), token(Keyword::Or), - |left, op, right| Pattern::Or(Box::new(left), op, Box::new(right)), + move |left, op, right| { + Pattern::Or( + AstBox::new_in(left, arena), + op, + AstBox::new_in(right, arena), + ) + }, ) .parse_next(i) } @@ -173,17 +188,17 @@ fn relation_pattern<'s>(i: &mut Input<'s>) -> Result> { seq!(Pattern::Relation( one_of(|t: &Token<'s>| matches!(t.kind, TokenKind::Operator(op) if op.is_relation())) .map(TokenRef::borrow), - literal_constant_pattern::.map(Box::new), + boxed(literal_constant_pattern::), )) .parse_next(i) } fn range_pattern<'s>(i: &mut Input<'s>) -> Result> { seq!(Pattern::Range( - literal_constant_pattern::.map(Box::new), + boxed(literal_constant_pattern::), one_of(|t: &Token<'s>| *t == Operator::SpreadRange || *t == Operator::HalfOpenRange) .map(TokenRef::borrow), - literal_constant_pattern::.map(Box::new), + boxed(literal_constant_pattern::), )) .parse_next(i) } @@ -372,8 +387,9 @@ fn array_pattern<'s>(rebind: bool) -> impl Parser<'s, Pattern<'s>> { unreachable!(); }; let pattern = std::mem::replace(&mut **p, Pattern::SpreadDiscard(kw.range.start)); - *part = ArrayElementBase::Element(Box::new( + *part = ArrayElementBase::Element(AstBox::new_in( pattern.wrap_as_unknown([kw.clone()], DiagnosticCode::DuplicateSpreadPattern), + i.state, )); } } diff --git a/crates/core/src/parser/range.rs b/crates/core/src/parser/range.rs index bd02e301..5cb44e20 100644 --- a/crates/core/src/parser/range.rs +++ b/crates/core/src/parser/range.rs @@ -3,11 +3,11 @@ use super::prelude::*; /// A range expression. /// /// `start..end` or `start..( - pub Box>, + pub AstBox<'s, Expression<'s>>, pub TokenRef<'s>, - pub Box>, + pub AstBox<'s, Expression<'s>>, ); impl<'s> Range<'s> { diff --git a/crates/core/src/parser/record_element.rs b/crates/core/src/parser/record_element.rs index 5ed5230f..6d653ed8 100644 --- a/crates/core/src/parser/record_element.rs +++ b/crates/core/src/parser/record_element.rs @@ -1,17 +1,17 @@ use super::prelude::*; -#[derive(Debug, Clone, PartialEq, strum::EnumIs)] +#[derive(Debug, PartialEq, strum::EnumIs)] pub enum RecordElementBase<'s, E, I> { /// name colon Named - Named(TokenRef<'s>, TokenRef<'s>, Box), + Named(TokenRef<'s>, TokenRef<'s>, AstBox<'s, E>), /// interpolated_string colon Named - InterpolateNamed(Box, TokenRef<'s>, Box), + InterpolateNamed(AstBox<'s, I>, TokenRef<'s>, AstBox<'s, E>), /// colon OmitNamed - OmitNamed(TokenRef<'s>, Box), + OmitNamed(TokenRef<'s>, AstBox<'s, E>), /// Unnamed - Unnamed(Box), + Unnamed(AstBox<'s, E>), /// `..` Spread - Spread(TokenRef<'s>, Box), + Spread(TokenRef<'s>, AstBox<'s, E>), } use RecordElementBase::*; diff --git a/crates/core/src/parser/record_helper.rs b/crates/core/src/parser/record_helper.rs index 03b3c3c3..cf2c7ada 100644 --- a/crates/core/src/parser/record_helper.rs +++ b/crates/core/src/parser/record_helper.rs @@ -21,7 +21,7 @@ fn record_name<'s>(i: &mut Input<'s>) -> Result> { .parse_next(i) } -fn record_element<'t, 's: 't, E: Clone + PartialEq + 's, I: Clone + PartialEq + 's>( +fn record_element<'t, 's: 't, E: PartialEq + 's, I: PartialEq + 's>( named: impl Parser<'s, E>, mut interpolate_name: impl FnMut(&'s Token<'s>) -> I + Copy, omit_named: impl Parser<'s, E>, @@ -33,9 +33,11 @@ fn record_element<'t, 's: 't, E: Clone + PartialEq + 's, I: Clone + PartialEq + move |i: &mut Input<'s>| { let first = peek(any).parse_next(i)?; if *first == Operator::Comma { + let arena = i.state; return Ok(ListItem::new_with_comma( - RecordElementBase::Unnamed(missing(first.range.start).into()), + RecordElementBase::Unnamed(AstBox::new_in(missing(first.range.start), arena)), token(Operator::Comma).parse_next(i)?, + arena, )); } if *first == Operator::CloseBracket @@ -45,26 +47,29 @@ fn record_element<'t, 's: 't, E: Clone + PartialEq + 's, I: Clone + PartialEq + { return fail.parse_next(i); } + let arena = i.state; let result = alt(( (token(Operator::SpreadRange), spread) - .map(|(s, e)| RecordElementBase::Spread(s, e.into())), - (one_of(colon), omit_named) - .map(|(c, o)| RecordElementBase::OmitNamed(c.into(), o.into())), + .map(move |(s, e)| RecordElementBase::Spread(s, AstBox::new_in(e, arena))), + (one_of(colon), omit_named).map(move |(c, o)| { + RecordElementBase::OmitNamed(c.into(), AstBox::new_in(o, arena)) + }), ( one_of(|t: &Token<'s>| t.is_interpolated_string()), one_of(colon), named, ) - .map(|(r, c, n)| { + .map(move |(r, c, n)| { RecordElementBase::InterpolateNamed( - Box::new(interpolate_name(r)), + AstBox::new_in(interpolate_name(r), arena), c.into(), - n.into(), + AstBox::new_in(n, arena), ) }), - (record_name, one_of(colon), named) - .map(|(r, c, n)| RecordElementBase::Named(r, c.into(), n.into())), - unnamed.map(|u| RecordElementBase::Unnamed(u.into())), + (record_name, one_of(colon), named).map(move |(r, c, n)| { + RecordElementBase::Named(r, c.into(), AstBox::new_in(n, arena)) + }), + unnamed.map(move |u| RecordElementBase::Unnamed(AstBox::new_in(u, arena))), )) .parse_next(i)?; let last = peek(any).parse_next(i)?; @@ -83,14 +88,14 @@ fn record_element<'t, 's: 't, E: Clone + PartialEq + 's, I: Clone + PartialEq + || *last == Keyword::Let || *last == Keyword::Const { - return Ok(ListItem::new(result)); + return Ok(ListItem::new(result, i.state)); } let comma = token_or_insert(Operator::Comma, DiagnosticCode::MissingComma).parse_next(i)?; - Ok(ListItem::new_with_comma(result, comma)) + Ok(ListItem::new_with_comma(result, comma, i.state)) } } -pub(super) fn record_base<'t, 's: 't, E: Clone + PartialEq + 's, I: Clone + PartialEq + 's>( +pub(super) fn record_base<'t, 's: 't, E: PartialEq + 's, I: PartialEq + 's>( named: impl Parser<'s, E>, interpolate_name: impl FnMut(&'s Token<'s>) -> I + Copy, omit_named: impl Parser<'s, E>, diff --git a/crates/core/src/parser/script.rs b/crates/core/src/parser/script.rs index 0a76b5f6..84255502 100644 --- a/crates/core/src/parser/script.rs +++ b/crates/core/src/parser/script.rs @@ -7,10 +7,10 @@ use super::prelude::*; /// statement* expression? EOF /// /// A script is a source file that contains a sequence of statements and an optional expression. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, PartialEq)] pub struct Script<'s>( pub Vec>, - pub Option>>, + pub Option>>, pub TokenRef<'s>, ); diff --git a/crates/core/src/parser/scripts.rs b/crates/core/src/parser/scripts.rs index 1d82c537..b2bf7682 100644 --- a/crates/core/src/parser/scripts.rs +++ b/crates/core/src/parser/scripts.rs @@ -33,7 +33,7 @@ pub fn script<'s>(i: &mut Input<'s>) -> Result> { } if let Some(e) = e { let s = semicolon.parse_next(i)?; - statements.push(Statement::Expression(e.into(), s)); + statements.push(Statement::Expression(AstBox::new_in(e, i.state), s)); } else if s_empty { // eats nothing in this loop and not reach the end // eats next token and try again @@ -61,7 +61,7 @@ pub fn script<'s>(i: &mut Input<'s>) -> Result> { } } } - let (statements, exp) = construct_statements_and_expression(statements, exp); + let (statements, exp) = construct_statements_and_expression(statements, exp, i.state); let eof = eof.map(TokenRef::borrow).unwrap_or_else(|| { Token::unknown_at(0, TokenKind::Eof, DiagnosticCode::UnexpectedToken).into() }); diff --git a/crates/core/src/parser/statement.rs b/crates/core/src/parser/statement.rs index 5bba9b2c..17f9c043 100644 --- a/crates/core/src/parser/statement.rs +++ b/crates/core/src/parser/statement.rs @@ -1,38 +1,38 @@ use super::prelude::*; -#[derive(Debug, Clone, PartialEq, strum::EnumIs)] +#[derive(Debug, PartialEq, strum::EnumIs)] pub enum Statement<'s> { /// `';'` /// /// An empty statement. Empty(TokenRef<'s>), /// `expression ';'` - Expression(Box>, TokenRef<'s>), + Expression(AstBox<'s, Expression<'s>>, TokenRef<'s>), /// `expression_ends_with_block` /// /// No trailing semicolon in this case. For expressions that end with a semicolon, use [Statement::Expression]. - BlockExpression(Box>), + BlockExpression(AstBox<'s, Expression<'s>>), /// `'pub'? 'mod' identifier block_expression_no_expr` Module( Option>, TokenRef<'s>, TokenRef<'s>, - Box>, + AstBox<'s, Expression<'s>>, ), /// `'pub'? 'let' pattern '=' expression ';'` Bind( Option>, TokenRef<'s>, - Box>, + AstBox<'s, Pattern<'s>>, TokenRef<'s>, - Box>, + AstBox<'s, Expression<'s>>, TokenRef<'s>, ), /// `pattern_rebind '=' expression ';'` Rebind( - Box>, + AstBox<'s, Pattern<'s>>, TokenRef<'s>, - Box>, + AstBox<'s, Expression<'s>>, TokenRef<'s>, ), /// `'pub'? 'const' @id '=' expression ';'` @@ -41,7 +41,7 @@ pub enum Statement<'s> { TokenRef<'s>, TokenRef<'s>, TokenRef<'s>, - Box>, + AstBox<'s, Expression<'s>>, TokenRef<'s>, ), /// `expression ('=' | '+=' | '-=' | '*=' | '/=' | '%=' | '^=' | '&&=' | '||=') expression ';'` @@ -51,9 +51,9 @@ pub enum Statement<'s> { /// - `expression_access` where the accessed is an extern /// - `expression_index` where the indexed is an extern Assign( - Box>, + AstBox<'s, Expression<'s>>, TokenRef<'s>, - Box>, + AstBox<'s, Expression<'s>>, TokenRef<'s>, ), /// `'pub'? 'fn' identifier (parameters) block_expression` @@ -73,16 +73,24 @@ pub enum Statement<'s> { TokenRef<'s>, TokenRef<'s>, Option>, - Box>, + AstBox<'s, Expression<'s>>, ), /// `return expression;` or `return;` /// /// If the expression is omitted, the return value is `nil`. - Return(TokenRef<'s>, Option>>, TokenRef<'s>), + Return( + TokenRef<'s>, + Option>>, + TokenRef<'s>, + ), /// `break expression;` or `break;` /// /// The expression is only allowed in a `loop` expression. - Break(TokenRef<'s>, Option>>, TokenRef<'s>), + Break( + TokenRef<'s>, + Option>>, + TokenRef<'s>, + ), /// `continue;` Continue(TokenRef<'s>, TokenRef<'s>), /// Unknown statement. diff --git a/crates/core/src/parser/statements.rs b/crates/core/src/parser/statements.rs index 2403cc1c..7bb06d8b 100644 --- a/crates/core/src/parser/statements.rs +++ b/crates/core/src/parser/statements.rs @@ -28,7 +28,7 @@ fn fn_statement<'s>(i: &mut Input<'s>) -> Result> { token(Keyword::Fn), opt(variable_token(false, false)), parameter_list, - block_expression.map(Box::new), + boxed(block_expression), ) .map(|(kw_pub, kw_fn, name, params, body)| { let mut name = name.unwrap_or_else(|| { @@ -50,7 +50,7 @@ fn fn_statement<'s>(i: &mut Input<'s>) -> Result> { fn return_statement<'s>(i: &mut Input<'s>) -> Result> { seq!(Statement::Return( token(Keyword::Return), - opt(expression.map(Box::new)), + opt(boxed(expression)), semicolon, )) .parse_next(i) @@ -59,7 +59,7 @@ fn return_statement<'s>(i: &mut Input<'s>) -> Result> { fn break_statement<'s>(i: &mut Input<'s>) -> Result> { seq!(Statement::Break( token(Keyword::Break), - opt(expression.map(Box::new)), + opt(boxed(expression)), semicolon, )) .parse_next(i) @@ -73,9 +73,9 @@ fn bind_statement<'s>(i: &mut Input<'s>) -> Result> { seq!(Statement::Bind( opt(token(Keyword::Pub)), token(Keyword::Let), - pattern_or_insert(false, |t| *t == Operator::Assign).map(Box::new), + boxed(pattern_or_insert(false, |t| *t == Operator::Assign)), token_or_insert(Operator::Assign, DiagnosticCode::MissingBindOperator), - expression_or_insert(|t| *t == Operator::Semicolon).map(Box::new), + boxed(expression_or_insert(|t| *t == Operator::Semicolon)), semicolon, )) .parse_next(i) @@ -83,9 +83,9 @@ fn bind_statement<'s>(i: &mut Input<'s>) -> Result> { fn rebind_statement<'s>(i: &mut Input<'s>) -> Result> { seq!(Statement::Rebind( - pattern_or_insert(true, |t| *t == Operator::Assign).map(Box::new), + boxed(pattern_or_insert(true, |t| *t == Operator::Assign)), token(Operator::Assign), - expression_or_insert(|t| *t == Operator::Semicolon).map(Box::new), + boxed(expression_or_insert(|t| *t == Operator::Semicolon)), semicolon, )) .parse_next(i) @@ -102,7 +102,7 @@ fn const_statement<'s>(i: &mut Input<'s>) -> Result> { t }), token_or_insert(Operator::Assign, DiagnosticCode::MissingBindOperator), - expression_or_insert(|t| *t == Operator::Semicolon).map(Box::new), + boxed(expression_or_insert(|t| *t == Operator::Semicolon)), semicolon, )) .parse_next(i) @@ -125,16 +125,12 @@ fn assign_or_expression_statement<'s>(i: &mut Input<'s>) -> Result } // Common expr of expr1 = expr2; and expr1; - let expr1 = expression_or_insert(is_assign_op) - .map(Box::new) - .parse_next(i)?; + let expr1 = boxed(expression_or_insert(is_assign_op)).parse_next(i)?; let cp = i.checkpoint(); // Try to parse as assignment first let assign: Result<_> = one_of(is_assign_op).parse_next(i); if let Ok(assign) = assign { - let expr2 = expression_or_insert(|t| *t == Operator::Semicolon) - .map(Box::new) - .parse_next(i)?; + let expr2 = boxed(expression_or_insert(|t| *t == Operator::Semicolon)).parse_next(i)?; let semi = semicolon.parse_next(i)?; return Ok(Statement::Assign( expr1, @@ -165,7 +161,7 @@ fn mod_statement<'s>(i: &mut Input<'s>) -> Result> { opt(token(Keyword::Pub)), token(Keyword::Mod), opt(variable_token(false, false)), - block_expression_no_expr.map(Box::new), + boxed(block_expression_no_expr), ) .map(|(kw_pub, kw_mod, name, body)| { let name = name.unwrap_or_else(|| { @@ -183,12 +179,12 @@ fn mod_statement<'s>(i: &mut Input<'s>) -> Result> { pub(super) fn statement<'s>(i: &mut Input<'s>) -> Result> { dispatch! {peek(any); - t if *t == Operator::OpenBrace => block_expression.map(Box::new).map(Statement::BlockExpression), - t if *t == Keyword::If => if_expression.map(Box::new).map(Statement::BlockExpression), - t if *t == Keyword::Loop => loop_expression.map(Box::new).map(Statement::BlockExpression), - t if *t == Keyword::While => while_expression.map(Box::new).map(Statement::BlockExpression), - t if *t == Keyword::Match => match_expression.map(Box::new).map(Statement::BlockExpression), - t if *t == Keyword::For => for_in_expression.map(Box::new).map(Statement::BlockExpression), + t if *t == Operator::OpenBrace => boxed(block_expression).map(Statement::BlockExpression), + t if *t == Keyword::If => boxed(if_expression).map(Statement::BlockExpression), + t if *t == Keyword::Loop => boxed(loop_expression).map(Statement::BlockExpression), + t if *t == Keyword::While => boxed(while_expression).map(Statement::BlockExpression), + t if *t == Keyword::Match => boxed(match_expression).map(Statement::BlockExpression), + t if *t == Keyword::For => boxed(for_in_expression).map(Statement::BlockExpression), t if *t == Keyword::Return => return_statement, t if *t == Keyword::Break => break_statement, diff --git a/crates/napi/Cargo.toml b/crates/napi/Cargo.toml index 3a33c1d6..ea0570b4 100644 --- a/crates/napi/Cargo.toml +++ b/crates/napi/Cargo.toml @@ -7,10 +7,6 @@ publish = false [lib] crate-type = ["cdylib"] -[features] -default = ["mimalloc"] -mimalloc = ["mira-core/mimalloc"] - [dependencies] napi = { version = "3", features = ["serde-json"] } napi-derive = "3" diff --git a/crates/python/Cargo.toml b/crates/python/Cargo.toml index 903d1a74..a0b2b485 100644 --- a/crates/python/Cargo.toml +++ b/crates/python/Cargo.toml @@ -8,10 +8,6 @@ edition = "2021" name = "mirascript" crate-type = ["cdylib"] -[features] -default = ["mimalloc"] -mimalloc = ["mira-core/mimalloc"] - [dependencies] serde = "1" mira-core = { path = "../core", features = ["serde"] } diff --git a/crates/wasm/src/monaco.rs b/crates/wasm/src/monaco.rs index 9021d49a..fc93d36e 100644 --- a/crates/wasm/src/monaco.rs +++ b/crates/wasm/src/monaco.rs @@ -1,7 +1,7 @@ use std::pin::Pin; use mira_core::{ - Compiler, Config, Script, SourceDiagnostic, diagnostic::encode_diagnostics, lexer::Token, + Bump, Compiler, Config, Script, SourceDiagnostic, diagnostic::encode_diagnostics, lexer::Token, }; use wasm_bindgen::prelude::*; @@ -11,8 +11,9 @@ pub struct MonacoCompiler { input: Pin, has_parse_error: bool, diagnostics: Vec, - tokens: Pin]>>, script: Option>, + tokens: Pin]>>, + arena: Box, } #[wasm_bindgen] @@ -24,25 +25,29 @@ impl MonacoCompiler { input: Pin::new(input), diagnostics: Vec::new(), has_parse_error: false, - tokens: Box::pin([]), script: None, + tokens: Box::pin([]), + arena: Box::new(Bump::new()), } } #[wasm_bindgen] pub fn parse(&mut self) -> bool { + self.script = None; + self.arena.reset(); let input: &'static str = unsafe { let ptr = self.input.as_ptr(); let len = self.input.len(); str::from_utf8_unchecked(std::slice::from_raw_parts(ptr, len)) }; let config: &'static Config = unsafe { &*(&self.config as *const Config) }; + let arena: &'static Bump = unsafe { &*(&*self.arena as *const Bump) }; let mut compiler = Compiler::new(input, config); if let Some(tokens) = compiler.lex() { self.tokens = tokens.into(); let tokens = unsafe { std::slice::from_raw_parts(self.tokens.as_ptr(), self.tokens.len()) }; - if let Some(script) = compiler.parse(tokens) { + if let Some(script) = compiler.parse(tokens, arena) { self.script = Some(script); self.diagnostics = compiler.diagnostics_collector.drain(..).collect(); self.has_parse_error = self.diagnostics.iter().any(|d| d.is_error());