diff --git a/Cargo.lock b/Cargo.lock index 5cbe78c7..18710bf0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -292,6 +292,7 @@ dependencies = [ name = "mira-core" version = "0.1.76" dependencies = [ + "bumpalo", "divan", "mimalloc", "mira-core", diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index 13fec772..dcfef03f 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -24,6 +24,7 @@ divan = "0.1.21" mira-core = { path = ".", features = ["mimalloc"] } [dependencies] +bumpalo = { version = "3", features = ["boxed"] } unicode-ident = "1" winnow = "1" strum = { version = "0.28", features = ["derive"] } diff --git a/crates/core/src/arena.rs b/crates/core/src/arena.rs new file mode 100644 index 00000000..767e8658 --- /dev/null +++ b/crates/core/src/arena.rs @@ -0,0 +1,28 @@ +use bumpalo::Bump; + +/// Arena allocator for AST nodes. +/// +/// Allocations made through this arena live until the arena is dropped or [`AstArena::reset`] is called. +pub struct AstArena { + bump: Bump, +} + +impl AstArena { + pub fn new() -> Self { + Self { bump: Bump::new() } + } + + pub fn alloc<'a, T>(&'a self, value: T) -> bumpalo::boxed::Box<'a, T> { + bumpalo::boxed::Box::new_in(value, &self.bump) + } + + pub fn reset(&mut self) { + self.bump.reset(); + } +} + +impl Default for AstArena { + fn default() -> Self { + Self::new() + } +} diff --git a/crates/core/src/compile/mod.rs b/crates/core/src/compile/mod.rs index ee93c023..fb0312da 100644 --- a/crates/core/src/compile/mod.rs +++ b/crates/core/src/compile/mod.rs @@ -68,12 +68,16 @@ impl<'s, 'c: 's> Compiler<'s, 'c> { ) } - pub fn parse<'t>(&mut self, tokens: &'t [Token<'t>]) -> Option> { + pub fn parse<'t, 'a>( + &mut self, + arena: &'a crate::arena::AstArena, + tokens: &'t [Token<'t>], + ) -> Option> { assert!(!tokens.is_empty(), "Cannot parse an empty token list"); // Parsing let mut stream = parser::to_input(tokens); - let Ok(mut script) = parser::parse(&mut stream) else { + let Ok(mut script) = parser::parse(arena, &mut stream) else { let remaining = stream.peek_finish(); let range = if remaining.is_empty() { tokens.last().unwrap().range.clone() @@ -93,7 +97,7 @@ impl<'s, 'c: 's> Compiler<'s, 'c> { Some(script) } - pub fn emit(&mut self, script: &Script<'s>) -> Option> { + pub fn emit<'a>(&mut self, script: &Script<'s, 'a>) -> Option> { // Emitting let bytecode = emitter::emit(script, &mut self.diagnostics_collector); @@ -118,7 +122,8 @@ impl<'s, 'c: 's> Compiler<'s, 'c> { let Some(tokens) = compiler.lex() else { return (None, compiler.encode_diagnostics()); }; - let Some(script) = compiler.parse(&tokens) else { + let arena = crate::arena::AstArena::new(); + let Some(script) = compiler.parse(&arena, &tokens) 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..0c2f4a0c 100644 --- a/crates/core/src/emitter/emitter_closure.rs +++ b/crates/core/src/emitter/emitter_closure.rs @@ -59,10 +59,10 @@ impl DerefMut for Closures { } impl<'s, 'c> Emitter<'s, 'c> { - pub fn declare_block( + pub fn declare_block<'a>( &mut self, - stmts: &'s Vec>, - expr: &'s Option>>, + stmts: &'s Vec>, + expr: &'s Option>>, exports: &mut ModuleExports<'s, 'c>, ) { for stmt in stmts { @@ -72,10 +72,10 @@ impl<'s, 'c> Emitter<'s, 'c> { self.declare_expression(expr); } } - pub fn emit_block( + pub fn emit_block<'a>( &mut self, - stmts: &'s Vec>, - expr: &'s Option>>, + stmts: &'s Vec>, + expr: &'s Option>>, scope_range: SourceRange, ret: Register, brk: Option, @@ -100,13 +100,13 @@ impl<'s, 'c> Emitter<'s, 'c> { }; has_never } - pub fn emit_fn( + pub fn emit_fn<'a>( &mut self, ret: Register, name_range: SourceRange, scope_start: usize, - args: &'s Option>, - body: &'s Expression<'s>, + args: &'s Option>, + body: &'s Expression<'s, 'a>, ) { let Expression::Block(_, stmts, expr, cp) = body else { // unreachable!("Expected block expression"); @@ -121,14 +121,14 @@ impl<'s, 'c> Emitter<'s, 'c> { expr, ); } - pub fn emit_fn_like( + pub fn emit_fn_like<'a>( &mut self, ret: Register, name_range: SourceRange, scope_range: SourceRange, - args: &'s Option>, - stmts: &'s Vec>, - expr: &'s Option>>, + args: &'s Option>, + stmts: &'s Vec>, + 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/emitter/emitter_expression.rs b/crates/core/src/emitter/emitter_expression.rs index 459265ba..46c3e400 100644 --- a/crates/core/src/emitter/emitter_expression.rs +++ b/crates/core/src/emitter/emitter_expression.rs @@ -18,7 +18,7 @@ use super::{ variable::BindType, }; -fn number_constant(exp: &Expression<'_>) -> Option { +fn number_constant(exp: &Expression<'_, '_>) -> Option { match exp { Expression::Literal(token) => match token.kind { TokenKind::Number(n, _) => Some(n), @@ -32,7 +32,7 @@ fn number_constant(exp: &Expression<'_>) -> Option { } impl<'s, 'c> Emitter<'s, 'c> { - fn declare_callable_expr(&mut self, callable: &'s Expression<'s>) { + fn declare_callable_expr<'a>(&mut self, callable: &'s Expression<'s, 'a>) { // 此时的 Grouping 用于标记 callable 为复杂表达式以启用空安全,跳过 declare_expression 的 Grouping 处理 if let Expression::Grouping(_, inner, _) = callable && (inner.is_variable() || inner.is_grouping()) @@ -46,7 +46,7 @@ impl<'s, 'c> Emitter<'s, 'c> { self.declare_expression(callable); } } - fn declare_cond_expr(&mut self, cond: &'s Expression<'s>) { + fn declare_cond_expr<'a>(&mut self, cond: &'s Expression<'s, 'a>) { if let Literal(lit) = cond && !lit.is_boolean_literal() { @@ -55,20 +55,20 @@ impl<'s, 'c> Emitter<'s, 'c> { } self.declare_expression(cond); } - fn declare_indexing_expr(&mut self, record_like: &'s Expression<'s>) { + fn declare_indexing_expr<'a>(&mut self, record_like: &'s Expression<'s, 'a>) { if record_like.is_literal() || record_like.is_interpolated_string() { self.diagnostics .push(DiagnosticCode::LiteralNotIndexable, record_like.range()); } self.declare_expression(record_like); } - fn declare_call( + fn declare_call<'a>( &mut self, - this: Option<&'s Expression<'s>>, + this: Option<&'s Expression<'s, 'a>>, l: &'s TokenRef<'s>, r: &'s TokenRef<'s>, - callable: &'s Callable<'s>, - args: &'s [ArgElement<'s>], + callable: &'s Callable<'s, 'a>, + args: &'s [ArgElement<'s, 'a>], ) { if self.config.diagnostic_tag { let start = this.map_or_else(|| callable.range().start, |c| c.range().start); @@ -117,7 +117,7 @@ impl<'s, 'c> Emitter<'s, 'c> { }; }); } - pub fn declare_expression(&mut self, outer: &'s Expression<'s>) { + pub fn declare_expression<'a>(&mut self, outer: &'s Expression<'s, 'a>) { match outer { Literal(_) => (), InterpolatedString(_, exprs) => { @@ -325,9 +325,9 @@ impl<'s, 'c> Emitter<'s, 'c> { Unknown { .. } => (), } } - pub fn emit_expression_reg( + pub fn emit_expression_reg<'a>( &mut self, - expr: &'s Expression<'s>, + expr: &'s Expression<'s, 'a>, brk: Option, ) -> Register { if let Variable(id_token) = expr { @@ -379,7 +379,7 @@ impl<'s, 'c> Emitter<'s, 'c> { Some(id) } - fn emit_global_access(&mut self, expr: &'s Expression<'s>) -> Option> { + fn emit_global_access<'a>(&mut self, expr: &'s Expression<'s, 'a>) -> Option> { let id = if let Variable(id_token) = expr { let id = id_token.to_id_name()?; if self.scopes.find_variable(id).is_some() { @@ -408,9 +408,9 @@ impl<'s, 'c> Emitter<'s, 'c> { Some(id) } - fn emit_expr_callable( + fn emit_expr_callable<'a>( &mut self, - callable: &'s Expression<'s>, + callable: &'s Expression<'s, 'a>, args_reg: impl FnOnce(&mut Self) -> (Vec, Vec), ret: Register, brk: Option, @@ -437,11 +437,11 @@ impl<'s, 'c> Emitter<'s, 'c> { } } - fn emit_call( + fn emit_call<'a>( &mut self, - callable: &'s Callable<'s>, - arg0: Option<&'s Expression<'s>>, - args: &'s [ArgElement<'s>], + callable: &'s Callable<'s, 'a>, + arg0: Option<&'s Expression<'s, 'a>>, + args: &'s [ArgElement<'s, 'a>], ret: Register, brk: Option, ) { @@ -506,9 +506,9 @@ impl<'s, 'c> Emitter<'s, 'c> { } } - pub fn emit_expression( + pub fn emit_expression<'a>( &mut self, - expr: &'s Expression<'s>, + expr: &'s Expression<'s, 'a>, ret: Register, brk: Option, ) { diff --git a/crates/core/src/emitter/emitter_pattern.rs b/crates/core/src/emitter/emitter_pattern.rs index 22f7eeb9..60747978 100644 --- a/crates/core/src/emitter/emitter_pattern.rs +++ b/crates/core/src/emitter/emitter_pattern.rs @@ -20,9 +20,9 @@ use super::{ }; impl<'s, 'c> Emitter<'s, 'c> { - pub fn declare_pattern( + pub fn declare_pattern<'a>( &mut self, - pattern: &'s Pattern<'s>, + pattern: &'s Pattern<'s, 'a>, bind_type: Option, kw_pub: &Option>, exports: &mut ModuleExports<'s, 'c>, @@ -94,7 +94,11 @@ impl<'s, 'c> Emitter<'s, 'c> { } } - fn emit_failed_pattern(&mut self, pattern: &'s Pattern<'s>, bind_type: Option) { + fn emit_failed_pattern<'a>( + &mut self, + pattern: &'s Pattern<'s, 'a>, + bind_type: Option, + ) { // This function is called from `emit_pattern`, // Do not emit diagnostics, initialization, or set markers match pattern { @@ -153,9 +157,9 @@ impl<'s, 'c> Emitter<'s, 'c> { } } - fn emit_literal_constant( + fn emit_literal_constant<'a>( &mut self, - pattern_constant: &'s Pattern<'s>, + pattern_constant: &'s Pattern<'s, 'a>, value: Register, ) -> Option> { match pattern_constant { @@ -206,10 +210,10 @@ impl<'s, 'c> Emitter<'s, 'c> { } } - fn emit_constant_pattern( + fn emit_constant_pattern<'a, const SAME: bool>( &mut self, success: Register, - pattern: &'s Pattern<'s>, + pattern: &'s Pattern<'s, 'a>, value: Register, ) -> bool { let op: OpCode = if SAME { OpCode::Same } else { OpCode::Nsame }; @@ -249,10 +253,10 @@ impl<'s, 'c> Emitter<'s, 'c> { } } - fn emit_literal_guard( + fn emit_literal_guard<'a>( &mut self, success: Register, - pattern: &'s Pattern<'s>, + pattern: &'s Pattern<'s, 'a>, value: Register, literal: Constant<'s>, ) { @@ -273,10 +277,10 @@ impl<'s, 'c> Emitter<'s, 'c> { Constant::String(_) => self.op_unary(pattern.range(), success, OpCode::IsString, value), } } - fn emit_constant_guard( + fn emit_constant_guard<'a>( &mut self, success: Register, - pattern: &'s Pattern<'s>, + pattern: &'s Pattern<'s, 'a>, value: Register, constant: Register, ) { @@ -293,10 +297,10 @@ impl<'s, 'c> Emitter<'s, 'c> { ); } - pub fn emit_pattern( + pub fn emit_pattern<'a>( &mut self, success: Register, - pattern: &'s Pattern<'s>, + pattern: &'s Pattern<'s, 'a>, value: Register, bind_type: Option, ) { diff --git a/crates/core/src/emitter/emitter_statement.rs b/crates/core/src/emitter/emitter_statement.rs index 05f55c56..019e0fac 100644 --- a/crates/core/src/emitter/emitter_statement.rs +++ b/crates/core/src/emitter/emitter_statement.rs @@ -17,9 +17,9 @@ use super::{ }; impl<'s, 'c> Emitter<'s, 'c> { - pub fn declare_statement( + pub fn declare_statement<'a>( &mut self, - stmt: &'s Statement<'s>, + stmt: &'s Statement<'s, 'a>, exports: &mut ModuleExports<'s, 'c>, ) { match stmt { @@ -74,7 +74,11 @@ impl<'s, 'c> Emitter<'s, 'c> { } } - pub fn emit_statement(&mut self, stmt: &'s Statement<'s>, brk: Option) -> bool { + pub fn emit_statement<'a>( + &mut self, + stmt: &'s Statement<'s, 'a>, + brk: Option, + ) -> bool { match stmt { Expression(expression, _) | BlockExpression(expression) => { self.emit_expression(expression, Register::EMPTY, brk); diff --git a/crates/core/src/emitter/mod.rs b/crates/core/src/emitter/mod.rs index ca9f2e52..08387d46 100644 --- a/crates/core/src/emitter/mod.rs +++ b/crates/core/src/emitter/mod.rs @@ -25,8 +25,8 @@ use emitter_struct::Emitter; pub use opcode::OpCode; use opcode::Register; -pub fn emit<'s, 'c>( - script: &Script<'s>, +pub fn emit<'s, 'c, 'a>( + script: &Script<'s, 'a>, diagnostics_collector: &mut DiagnosticsCollector<'s, 'c>, ) -> Vec { let mut emitter = Emitter::new(diagnostics_collector); diff --git a/crates/core/src/emitter/utils.rs b/crates/core/src/emitter/utils.rs index fcadecb9..5382cb3a 100644 --- a/crates/core/src/emitter/utils.rs +++ b/crates/core/src/emitter/utils.rs @@ -1,5 +1,5 @@ use crate::{Keyword, parser::Expression}; -pub(super) fn is_global_expression(expr: &Expression) -> bool { +pub(super) fn is_global_expression(expr: &Expression<'_, '_>) -> bool { matches!(expr, Expression::Variable(token) if token.kind == Keyword::Global) } diff --git a/crates/core/src/formatter/array_element.rs b/crates/core/src/formatter/array_element.rs index cceba20a..1729ec3e 100644 --- a/crates/core/src/formatter/array_element.rs +++ b/crates/core/src/formatter/array_element.rs @@ -2,7 +2,7 @@ use crate::parser::ArrayElementBase; use super::prelude::*; -impl Formattable for ArrayElementBase<'_, E, S> { +impl Formattable for ArrayElementBase<'_, '_, E, S> { fn measure(&self, formatter: &Formatter, indent: usize) -> usize { use ArrayElementBase::*; match self { diff --git a/crates/core/src/formatter/callable.rs b/crates/core/src/formatter/callable.rs index 21233f70..0d0f8494 100644 --- a/crates/core/src/formatter/callable.rs +++ b/crates/core/src/formatter/callable.rs @@ -2,7 +2,7 @@ use crate::parser::Callable; use super::prelude::*; -impl Formattable for Callable<'_> { +impl Formattable for Callable<'_, '_> { fn measure(&self, formatter: &Formatter, indent: usize) -> usize { use Callable::*; match self { diff --git a/crates/core/src/formatter/else_block.rs b/crates/core/src/formatter/else_block.rs index 85eb71c8..9197cb9a 100644 --- a/crates/core/src/formatter/else_block.rs +++ b/crates/core/src/formatter/else_block.rs @@ -2,7 +2,7 @@ use crate::parser::ElseBlock; use super::prelude::*; -impl Formattable for ElseBlock<'_> { +impl Formattable for ElseBlock<'_, '_> { fn measure(&self, formatter: &Formatter, indent: usize) -> usize { self.1.measure(formatter, indent) } diff --git a/crates/core/src/formatter/expression.rs b/crates/core/src/formatter/expression.rs index 23eac528..77d9e5ae 100644 --- a/crates/core/src/formatter/expression.rs +++ b/crates/core/src/formatter/expression.rs @@ -2,7 +2,7 @@ use crate::{Expression, Operator, parser::MatchCase}; use super::prelude::*; -impl Formattable for Expression<'_> { +impl Formattable for Expression<'_, '_> { fn measure(&self, formatter: &Formatter, indent: usize) -> usize { use Expression::*; match self { diff --git a/crates/core/src/formatter/iterable.rs b/crates/core/src/formatter/iterable.rs index c6d9d9af..32469d22 100644 --- a/crates/core/src/formatter/iterable.rs +++ b/crates/core/src/formatter/iterable.rs @@ -2,7 +2,7 @@ use crate::parser::Iterable; use super::prelude::*; -impl Formattable for Iterable<'_> { +impl Formattable for Iterable<'_, '_> { fn measure(&self, formatter: &Formatter, indent: usize) -> usize { use Iterable::*; match self { diff --git a/crates/core/src/formatter/list_items.rs b/crates/core/src/formatter/list_items.rs index a839706e..d5f23add 100644 --- a/crates/core/src/formatter/list_items.rs +++ b/crates/core/src/formatter/list_items.rs @@ -4,7 +4,7 @@ use crate::{Operator, parser::ListItem}; use super::prelude::*; -impl Formattable for Vec> +impl Formattable for Vec> where T: Formattable, { diff --git a/crates/core/src/formatter/manager/token.rs b/crates/core/src/formatter/manager/token.rs index b845c914..5beabbe8 100644 --- a/crates/core/src/formatter/manager/token.rs +++ b/crates/core/src/formatter/manager/token.rs @@ -27,7 +27,7 @@ impl<'o> FormatManager<'o> { pub fn write_str_token<'s>( &mut self, s: &Token<'_>, - expressions: &[Expression<'s>], + expressions: &[Expression<'s, '_>], measurement: usize, ) { for trivia in &s.leading_trivia { diff --git a/crates/core/src/formatter/manager/types.rs b/crates/core/src/formatter/manager/types.rs index cba3e79d..16737845 100644 --- a/crates/core/src/formatter/manager/types.rs +++ b/crates/core/src/formatter/manager/types.rs @@ -22,7 +22,7 @@ pub(crate) trait Formattable { fn format(&self, formatter: &mut FormatManager, complexity: usize); } -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) } diff --git a/crates/core/src/formatter/mod.rs b/crates/core/src/formatter/mod.rs index c4c537a1..480c3203 100644 --- a/crates/core/src/formatter/mod.rs +++ b/crates/core/src/formatter/mod.rs @@ -36,7 +36,7 @@ impl Default for FormatOptions { } } -pub fn format(input: &Script<'_>, options: &FormatOptions) -> String { +pub fn format(input: &Script<'_, '_>, options: &FormatOptions) -> String { let Script(stmts, expr, eof) = input; let mut formatter = Formatter::new(options, 0); for statement in stmts { @@ -66,7 +66,11 @@ pub fn format(input: &Script<'_>, options: &FormatOptions) -> String { formatter.done() } -pub fn format_statement(input: &Statement<'_>, options: &FormatOptions, indent: usize) -> String { +pub fn format_statement( + input: &Statement<'_, '_>, + options: &FormatOptions, + indent: usize, +) -> String { let mut formatter = Formatter::new(options, indent); let measurement = input.measure(&formatter, indent); input.format(&mut formatter, measurement); diff --git a/crates/core/src/formatter/parameter_list.rs b/crates/core/src/formatter/parameter_list.rs index 851ac734..6d868630 100644 --- a/crates/core/src/formatter/parameter_list.rs +++ b/crates/core/src/formatter/parameter_list.rs @@ -2,7 +2,7 @@ use crate::parser::ParameterList; use super::prelude::*; -impl Formattable for ParameterList<'_> { +impl Formattable for ParameterList<'_, '_> { fn measure(&self, formatter: &Formatter, indent: usize) -> usize { self.1.measure(formatter, indent) } diff --git a/crates/core/src/formatter/pattern.rs b/crates/core/src/formatter/pattern.rs index a1c29b89..7eccb800 100644 --- a/crates/core/src/formatter/pattern.rs +++ b/crates/core/src/formatter/pattern.rs @@ -2,7 +2,7 @@ use crate::{Operator, Pattern}; use super::prelude::*; -impl Formattable for Pattern<'_> { +impl Formattable for Pattern<'_, '_> { fn measure(&self, formatter: &Formatter, indent: usize) -> usize { use Pattern::*; match self { diff --git a/crates/core/src/formatter/range.rs b/crates/core/src/formatter/range.rs index 4072e01d..08e3beb8 100644 --- a/crates/core/src/formatter/range.rs +++ b/crates/core/src/formatter/range.rs @@ -2,7 +2,7 @@ use crate::parser::Range; use super::prelude::*; -impl Formattable for Range<'_> { +impl Formattable for Range<'_, '_> { fn measure(&self, formatter: &Formatter, indent: usize) -> usize { let inner = usize::max( self.0.measure(formatter, indent), diff --git a/crates/core/src/formatter/record_element.rs b/crates/core/src/formatter/record_element.rs index 8ca16472..d022401b 100644 --- a/crates/core/src/formatter/record_element.rs +++ b/crates/core/src/formatter/record_element.rs @@ -2,7 +2,7 @@ use crate::parser::RecordElementBase; use super::prelude::*; -impl Formattable for RecordElementBase<'_, E, I> +impl Formattable for RecordElementBase<'_, '_, E, I> where E: Formattable, I: Formattable, diff --git a/crates/core/src/formatter/statement.rs b/crates/core/src/formatter/statement.rs index 741e009f..659243a1 100644 --- a/crates/core/src/formatter/statement.rs +++ b/crates/core/src/formatter/statement.rs @@ -17,7 +17,7 @@ fn format_bind( formatter.write_token(semicolon); } -impl Formattable for Statement<'_> { +impl Formattable for Statement<'_, '_> { fn measure(&self, formatter: &Formatter, indent: usize) -> usize { use Statement::*; match self { diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index 5a89cfb5..ae25158c 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -1,3 +1,4 @@ +pub mod arena; pub mod compile; pub mod config; pub mod diagnostic; @@ -5,6 +6,7 @@ pub mod emitter; pub mod lexer; pub mod parser; +pub use arena::AstArena; pub use compile::Compiler; pub use config::Config; pub use diagnostic::{DiagnosticCode, SerializedDiagnostics, SourceDiagnostic, SourceRange}; diff --git a/crates/core/src/parser/array_element.rs b/crates/core/src/parser/array_element.rs index e01dce18..46ebc5e0 100644 --- a/crates/core/src/parser/array_element.rs +++ b/crates/core/src/parser/array_element.rs @@ -1,22 +1,25 @@ use super::prelude::*; -#[derive(Debug, Clone, PartialEq, strum::EnumIs)] -pub enum ArrayElementBase<'s, E, S> { +#[derive(Debug, PartialEq, strum::EnumIs)] +pub enum ArrayElementBase<'s, 'a, E, S> { /// Element - Element(Box), + Element(ABox<'a, E>), /// `..` Spread - Spread(TokenRef<'s>, Box), + Spread(TokenRef<'s>, ABox<'a, S>), } use ArrayElementBase::*; -pub type ArrayElement<'s> = ListItem<'s, ArrayElementBase<'s, Iterable<'s>, Expression<'s>>>; +pub type ArrayElement<'s, 'a> = + ListItem<'s, 'a, ArrayElementBase<'s, 'a, Iterable<'s, 'a>, Expression<'s, 'a>>>; -pub type ArgElement<'s> = ListItem<'s, ArrayElementBase<'s, Expression<'s>, Expression<'s>>>; +pub type ArgElement<'s, 'a> = + ListItem<'s, 'a, ArrayElementBase<'s, 'a, Expression<'s, 'a>, Expression<'s, 'a>>>; -pub type ArrayPattern<'s> = ListItem<'s, ArrayElementBase<'s, Pattern<'s>, Pattern<'s>>>; +pub type ArrayPattern<'s, 'a> = + ListItem<'s, 'a, ArrayElementBase<'s, 'a, Pattern<'s, 'a>, Pattern<'s, 'a>>>; -impl<'s, E: AstWalker<'s>, S: AstWalker<'s>> AstWalker<'s> for ArrayElementBase<'s, E, S> { +impl<'s, 'a, E: AstWalker<'s>, S: AstWalker<'s>> AstWalker<'s> for ArrayElementBase<'s, 'a, E, S> { fn collect_diagnostics(&mut self, collector: &mut DiagnosticsCollector<'_, '_>) { match self { Element(value) => { diff --git a/crates/core/src/parser/array_helper.rs b/crates/core/src/parser/array_helper.rs index 64f5f7db..17702484 100644 --- a/crates/core/src/parser/array_helper.rs +++ b/crates/core/src/parser/array_helper.rs @@ -8,12 +8,13 @@ use super::{ prelude::*, }; -type _ArrayElement<'s, E, S> = ListItem<'s, ArrayElementBase<'s, E, S>>; -fn array_element<'s, E: Clone + PartialEq + 's, S: Clone + PartialEq + 's>( +type _ArrayElement<'s, 'a, E, S> = ListItem<'s, 'a, ArrayElementBase<'s, 'a, E, S>>; +fn array_element<'s: 'a, 'a, E: PartialEq + 'a, S: PartialEq + 'a>( + arena: &'a AstArena, element: impl Parser<'s, E>, spread: impl Parser<'s, S>, mut missing: impl FnMut(usize) -> E + Copy, -) -> impl Parser<'s, _ArrayElement<'s, E, S>> { +) -> impl Parser<'s, _ArrayElement<'s, 'a, E, S>> { move |i: &mut Input<'s>| { let first = peek(any).parse_next(i)?; if *first == Operator::Comma { @@ -21,7 +22,8 @@ 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)), + arena, + ArrayElementBase::Element(arena.alloc(missing)), comma, )); } @@ -34,11 +36,11 @@ fn array_element<'s, E: Clone + PartialEq + 's, S: Clone + PartialEq + 's>( } let result = if *first == Operator::SpreadRange { (token(Operator::SpreadRange), spread) - .map(|(s, e)| ArrayElementBase::Spread(s, e.into())) + .map(|(s, e)| ArrayElementBase::Spread(s, arena.alloc(e))) .parse_next(i)? } else { element - .map(|e| ArrayElementBase::Element(Box::new(e))) + .map(|e| ArrayElementBase::Element(arena.alloc(e))) .parse_next(i)? }; let last = peek(any).parse_next(i)?; @@ -57,24 +59,26 @@ 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(arena, result)); } let comma = token_or_insert(Operator::Comma, DiagnosticCode::MissingComma).parse_next(i)?; - Ok(ListItem::new_with_comma(result, comma)) + Ok(ListItem::new_with_comma(arena, result, comma)) } } -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>( +type _ArrayLike<'s, 'a, E, S> = (TokenRef<'s>, Vec<_ArrayElement<'s, 'a, E, S>>, TokenRef<'s>); +pub(super) fn array_base<'t, 's: 't + 'a, 'a, E: PartialEq + 'a, S: PartialEq + 'a>( + arena: &'a AstArena, mut open: impl Parser<'s, TokenRef<'s>>, mut close: impl Parser<'s, TokenRef<'s>>, element: impl Parser<'s, E>, spread: impl Parser<'s, S>, missing: impl FnMut(usize) -> E + Copy, -) -> impl Parser<'s, _ArrayLike<'s, E, S>> { +) -> impl Parser<'s, _ArrayLike<'s, 'a, E, S>> { move |i: &mut Input<'s>| { let open = open.parse_next(i)?; - let parts: Vec<_> = repeat(0.., array_element(element, spread, missing)).parse_next(i)?; + let parts: Vec<_> = + repeat(0.., array_element(arena, element, spread, missing)).parse_next(i)?; let close = close.parse_next(i)?; Ok((open, parts, close)) } diff --git a/crates/core/src/parser/ast_visitor.rs b/crates/core/src/parser/ast_visitor.rs index 8dd53559..967c4908 100644 --- a/crates/core/src/parser/ast_visitor.rs +++ b/crates/core/src/parser/ast_visitor.rs @@ -61,7 +61,7 @@ impl<'s, E: AstWalker<'s>> AstWalker<'s> for Option { } } -impl<'s, E: AstWalker<'s>> AstWalker<'s> for Box { +impl<'s, 'a, E: AstWalker<'s>> AstWalker<'s> for ABox<'a, E> { 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..3207ba45 100644 --- a/crates/core/src/parser/basic_expressions.rs +++ b/crates/core/src/parser/basic_expressions.rs @@ -14,11 +14,14 @@ use super::{ to_input, }; -fn to_interpolate_expr<'s>(token: &'s Token<'s>) -> Expression<'s> { +fn to_interpolate_expr<'s: 'a, 'a>( + arena: &'a AstArena, + token: &'s Token<'s>, +) -> Expression<'s, 'a> { let TokenKind::InterpolatedString(parts, _) = &token.kind else { unreachable!("Expected InterpolatedString"); }; - let expressions: Vec> = parts[0..parts.len() - 1] + let expressions: Vec> = parts[0..parts.len() - 1] .iter() .map(|(_, tokens, _)| { debug_assert!( @@ -39,10 +42,12 @@ fn to_interpolate_expr<'s>(token: &'s Token<'s>) -> Expression<'s> { ); } let mut token_input = to_input(tokens); - let result = (expression, opt(eof.value(()))).parse_next(&mut token_input); + let result = (|i: &mut Input<'s>| expression(arena, i), opt(eof.value(()))) + .parse_next(&mut token_input); match result { Ok((expr, Some(_))) => expr, Ok((expr, None)) => expr.wrap_as_unknown( + arena, token_input .peek_finish() .iter() @@ -60,13 +65,14 @@ fn to_interpolate_expr<'s>(token: &'s Token<'s>) -> Expression<'s> { Expression::InterpolatedString(token, expressions) } -fn record_like<'s>(i: &mut Input<'s>) -> Result> { +fn record_like<'s: 'a, 'a>(arena: &'a AstArena, i: &mut Input<'s>) -> Result> { let (open, parts, close) = record_base( - expression, - |t: &Token<'s>| to_interpolate_expr(t), - expression, - expression, - expression, + arena, + |i: &mut Input<'s>| expression(arena, i), + |t: &Token<'s>| to_interpolate_expr(arena, t), + |i: &mut Input<'s>| expression(arena, i), + |i: &mut Input<'s>| expression(arena, i), + |i: &mut Input<'s>| expression(arena, i), expression_expected, ) .parse_next(i)?; @@ -81,10 +87,10 @@ fn record_like<'s>(i: &mut Input<'s>) -> Result> { Ok(result) } -fn array<'s>(i: &mut Input<'s>) -> Result> { +fn array<'s: 'a, 'a>(arena: &'a AstArena, i: &mut Input<'s>) -> Result> { let spread = |i: &mut Input<'s>| { let pos = i.previous_token_end(); - opt(expression) + opt(|i: &mut Input<'s>| expression(arena, i)) .map(|e| { if let Some(e) = e { e @@ -102,9 +108,10 @@ fn array<'s>(i: &mut Input<'s>) -> Result> { .parse_next(i) }; array_base( + arena, token(Operator::OpenBracket), token_or_insert(Operator::CloseBracket, DiagnosticCode::MissingCloseBracket), - iterable, + |i: &mut Input<'s>| iterable(arena, i), spread, |pos| Iterable::Value(expression_expected(pos)), ) @@ -112,104 +119,119 @@ fn array<'s>(i: &mut Input<'s>) -> Result> { .parse_next(i) } -pub(super) fn interpolation<'s>(i: &mut Input<'s>) -> Result> { +pub(super) fn interpolation<'s: 'a, 'a>( + arena: &'a AstArena, + 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(arena, token)) } /// callable '(' ('..'? arg),* ')' -type Call<'s> = ( - Callable<'s>, +type Call<'s, 'a> = ( + Callable<'s, 'a>, TokenRef<'s>, - Vec>, + Vec>, TokenRef<'s>, ); -fn pseudo_function<'t, 's: 't, const EXTENSION_CALL: bool>(i: &mut Input<'s>) -> Result> { +fn pseudo_function<'t, 's: 't + 'a, 'a, const EXTENSION_CALL: bool>( + arena: &'a AstArena, + i: &mut Input<'s>, +) -> Result> { let provided: usize = if EXTENSION_CALL { 1 } else { 0 }; let (kw_type, (open, args, close)) = ( token(Keyword::Type), - arg_list(token_or_insert( - Operator::OpenParen, - DiagnosticCode::MissingOpenParenAfterType, - )), + arg_list( + arena, + token_or_insert( + Operator::OpenParen, + DiagnosticCode::MissingOpenParenAfterType, + ), + ), ) .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( + vec![ListItem::new( + arena, + ArrayElementBase::Element(arena.alloc(Expression::unknown_range( [], SourceRange { start: kw_type.range.start, end: close.range.end, }, DiagnosticCode::InvalidTypeCall, - ) - .into(), - ))] + ))), + )] } else { args }; Ok((Callable::Type(kw_type), open, exp, close)) } -fn primary<'s>(i: &mut Input<'s>) -> Result> { +fn primary<'s: 'a, 'a>(arena: &'a AstArena, i: &mut Input<'s>) -> Result> { (alt(( - pseudo_function::.map(|(e, o, a, c)| Expression::Call(e, o, a, c)), - block_like_expression, + (|i: &mut Input<'s>| pseudo_function::(arena, i)) + .map(|(e, o, a, c)| Expression::Call(e, o, a, c)), + |i: &mut Input<'s>| block_like_expression(arena, i), literal_token.map(Expression::Literal), - interpolation, + |i: &mut Input<'s>| interpolation(arena, i), variable_token(false, true).map(Expression::Variable), - record_like, - array, + |i: &mut Input<'s>| record_like(arena, i), + |i: &mut Input<'s>| array(arena, i), ))) .parse_next(i) } -fn arg_list<'s>( +fn arg_list<'s: 'a, 'a>( + arena: &'a AstArena, open: impl Parser<'s, TokenRef<'s>>, -) -> impl Parser<'s, (TokenRef<'s>, Vec>, TokenRef<'s>)> { +) -> impl Parser<'s, (TokenRef<'s>, Vec>, TokenRef<'s>)> { move |i: &mut Input<'s>| { array_base( + arena, open, token_or_insert(Operator::CloseParen, DiagnosticCode::MissingCloseParen), - expression, - expression, + |i: &mut Input<'s>| expression(arena, i), + |i: &mut Input<'s>| expression(arena, i), expression_expected, ) .parse_next(i) } } -enum AccessIndex<'s> { +enum AccessIndex<'s, 'a> { /// `.` identifier Access(TokenRef<'s>, TokenRef<'s>), /// `[` expression `]` - Index(TokenRef<'s>, Box>, TokenRef<'s>), + Index(TokenRef<'s>, ABox<'a, Expression<'s, 'a>>, 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> { +fn access_index<'s: 'a, 'a>(arena: &'a AstArena, i: &mut Input<'s>) -> Result> { 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: 'a, 'a>( + arena: &'a AstArena, + i: &mut Input<'s>, + ) -> Result>> { let mut precedence_additive = precedence_of(&TokenKind::Operator(Operator::SpreadRange)); precedence_additive.value += 2; - pratt(precedence_additive, false) + pratt(arena, precedence_additive, false) .verify_map(verify_expr) - .map(Box::new) + .map(|e| arena.alloc(e)) .parse_next(i) } fn range_op<'s>(i: &mut Input<'s>) -> Result> { @@ -230,24 +252,24 @@ fn access_index<'s>(i: &mut Input<'s>) -> Result> { ( token(Operator::OpenBracket), range_op, - opt(additive), + opt(|i: &mut Input<'s>| additive(arena, i)), token(Operator::CloseBracket), ) .map(|(l, op, end, r)| AccessIndex::Slice(l, None, op, end, r)), // `[` expression `]` | `[` additive (`..` | `..<`) additive `]` ( token(Operator::OpenBracket), - iterable, + |i: &mut Input<'s>| iterable(arena, i), token(Operator::CloseBracket), ) .map(|(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, arena.alloc(expr), c), }), // `[` additive (`..` | `..<`) `]` ( token(Operator::OpenBracket), - additive, + |i: &mut Input<'s>| additive(arena, i), range_op, token(Operator::CloseBracket), ) @@ -256,13 +278,14 @@ fn access_index<'s>(i: &mut Input<'s>) -> Result> { .parse_next(i) } -fn extension_call<'s>(i: &mut Input<'s>) -> Result> { +fn extension_call<'s: 'a, 'a>(arena: &'a AstArena, i: &mut Input<'s>) -> Result> { let parenthesised = |i: &mut Input<'s>| { - record_like + (|i: &mut Input<'s>| record_like(arena, i)) .with_taken() .map(|(r, t)| { if r.is_record() { r.wrap_as_unknown( + arena, t.iter().map(TokenRef::borrow).collect::>(), DiagnosticCode::RecordLiteralInExtensionCaller, ) @@ -273,22 +296,25 @@ fn extension_call<'s>(i: &mut Input<'s>) -> Result> { .parse_next(i) }; let access_chain = |i: &mut Input<'s>| { - (variable_token(false, true), repeat(0.., access_index)) + ( + variable_token(false, true), + repeat(0.., |i: &mut Input<'s>| access_index(arena, i)), + ) .map(|(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(arena.alloc(acc), token); } AccessIndex::Access(dot, token) => { - acc = Expression::Access(Box::new(acc), dot, token); + acc = Expression::Access(arena.alloc(acc), dot, token); } AccessIndex::Index(open, exp, close) => { - acc = Expression::Index(Box::new(acc), open, exp, close); + acc = Expression::Index(arena.alloc(acc), open, exp, close); } AccessIndex::Slice(left, start, op, end, right) => { - acc = Expression::Slice(Box::new(acc), left, start, op, end, right); + acc = Expression::Slice(arena.alloc(acc), left, start, op, end, right); } } } @@ -298,51 +324,57 @@ fn extension_call<'s>(i: &mut Input<'s>) -> Result> { }; alt(( ( - alt((parenthesised, access_chain)).map(|e| Callable::Expression(Box::new(e))), - arg_list(token_or_insert( - Operator::OpenParen, - DiagnosticCode::MissingOpenParenAfterExtension, - )), + alt((parenthesised, access_chain)).map(|e| Callable::Expression(arena.alloc(e))), + arg_list( + arena, + token_or_insert( + Operator::OpenParen, + DiagnosticCode::MissingOpenParenAfterExtension, + ), + ), ) .map(|(e, (o, a, c))| (e, o, a, c)), - pseudo_function::, + |i: &mut Input<'s>| pseudo_function::(arena, i), )) .parse_next(i) } -fn postfix<'s>(i: &mut Input<'s>) -> Result> { - enum Function<'s> { - Call(TokenRef<'s>, Vec>, TokenRef<'s>), - TaggedString(Box>), +fn postfix<'s: 'a, 'a>(arena: &'a AstArena, i: &mut Input<'s>) -> Result> { + enum Function<'s, 'a> { + Call(TokenRef<'s>, Vec>, TokenRef<'s>), + TaggedString(ABox<'a, Expression<'s, 'a>>), Extension( TokenRef<'s>, - Callable<'s>, + Callable<'s, 'a>, TokenRef<'s>, - Vec>, + Vec>, TokenRef<'s>, ), Access(TokenRef<'s>, TokenRef<'s>), - Index(TokenRef<'s>, Box>, TokenRef<'s>), + Index(TokenRef<'s>, ABox<'a, Expression<'s, 'a>>, TokenRef<'s>), Slice( TokenRef<'s>, - Option>>, + Option>>, TokenRef<'s>, - Option>>, + Option>>, TokenRef<'s>, ), NonNil(TokenRef<'s>), } - let first = primary.parse_next(i)?; - let functions: Vec> = repeat( + let first = primary(arena, i)?; + let functions: Vec> = repeat( 0.., alt(( - (token(Operator::ColonColon), extension_call) + (token(Operator::ColonColon), |i: &mut Input<'s>| { + extension_call(arena, i) + }) .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)))) + Function::TaggedString(arena.alloc(Expression::Literal(TokenRef::borrow(token)))) }), - interpolation.map(|ex| Function::TaggedString(ex.into())), - access_index.map(|t| match t { + (|i: &mut Input<'s>| interpolation(arena, i)) + .map(|ex| Function::TaggedString(arena.alloc(ex))), + (|i: &mut Input<'s>| access_index(arena, i)).map(|t| match t { AccessIndex::NonNil(token) => Function::NonNil(token), AccessIndex::Access(dot, token) => Function::Access(dot, token), AccessIndex::Index(open, exp, close) => Function::Index(open, exp, close), @@ -350,7 +382,7 @@ fn postfix<'s>(i: &mut Input<'s>) -> Result> { Function::Slice(left, start, op, end, right) } }), - arg_list(token(Operator::OpenParen)).map(|(o, a, c)| Function::Call(o, a, c)), + arg_list(arena, token(Operator::OpenParen)).map(|(o, a, c)| Function::Call(o, a, c)), )), ) .fold(Vec::new, |mut v, t| { @@ -364,18 +396,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(arena.alloc(acc)), o, args, c) } - Function::TaggedString(ex) => Expression::TaggedString(Box::new(acc), ex), + Function::TaggedString(ex) => Expression::TaggedString(arena.alloc(acc), ex), Function::Extension(e, ex, o, arg, c) => { - Expression::Extension(Box::new(acc), e, ex, o, arg, c) + Expression::Extension(arena.alloc(acc), 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(arena.alloc(acc), dot, token), + Function::Index(l, index, r) => Expression::Index(arena.alloc(acc), l, index, r), Function::Slice(left, start, op, end, right) => { - Expression::Slice(Box::new(acc), left, start, op, end, right) + Expression::Slice(arena.alloc(acc), left, start, op, end, right) } - Function::NonNil(token) => Expression::NonNil(Box::new(acc), token), + Function::NonNil(token) => Expression::NonNil(arena.alloc(acc), token), })) } @@ -417,29 +449,32 @@ fn precedence_of(t: &TokenKind<'_>) -> PrecedenceResult { } } -fn pratt_prefix<'s>(i: &mut Input<'s>) -> Result> { +fn pratt_prefix<'s: 'a, 'a>(arena: &'a AstArena, i: &mut Input<'s>) -> Result> { let token = peek(any).parse_next(i)?; let precedence = precedence_of(token); if precedence.can_be_prefix { let op = any.parse_next(i)?; - let expr = pratt(precedence, false) + let expr = pratt(arena, precedence, false) .verify_map(verify_expr) .parse_next(i)?; - Ok(Expression::Prefix(op.into(), expr.into())) + Ok(Expression::Prefix(op.into(), arena.alloc(expr))) } else { - postfix.parse_next(i) + postfix(arena, i) } } -fn pratt_infix<'s>( - left: Box>, +fn pratt_infix<'s: 'a, 'a>( + arena: &'a AstArena, + left: ABox<'a, Expression<'s, 'a>>, op: &'s Token<'s>, mut precedence: PrecedenceResult, allow_range: bool, i: &mut Input<'s>, -) -> Result> { +) -> Result> { if *op == Keyword::Is { - let right = pattern(false).map(Box::new).parse_next(i)?; + let right = pattern(arena, false) + .map(|p| arena.alloc(p)) + .parse_next(i)?; return Ok(Iterable::Value(Expression::Is(left, op.into(), right))); } // 调整优先级以实现右结合 @@ -447,13 +482,13 @@ fn pratt_infix<'s>( precedence.value -= 1; } let parse_right = |i: &mut Input<'s>| { - let expr = pratt(precedence, false) + let expr = pratt(arena, precedence, false) .verify_map(verify_expr) .parse_next(i)?; - Ok(Box::new(expr)) + Ok(arena.alloc(expr)) }; if *op == Operator::Question { - let then_exp = expression.parse_next(i)?.into(); + let then_exp = arena.alloc(expression(arena, i)?); let colon = token_or_insert(Operator::Colon, DiagnosticCode::MissingColon).parse_next(i)?; let else_exp = parse_right(i)?; return Ok(Iterable::Value(Expression::Cond( @@ -484,9 +519,13 @@ fn pratt_infix<'s>( Ok(Iterable::Value(Expression::Infix(left, op.into(), right))) } -fn pratt<'s>(precedence: PrecedenceResult, allow_range: bool) -> impl Parser<'s, Iterable<'s>> { +fn pratt<'s: 'a, 'a>( + arena: &'a AstArena, + precedence: PrecedenceResult, + allow_range: bool, +) -> impl Parser<'s, Iterable<'s, 'a>> { move |i: &mut Input<'s>| { - let mut left = pratt_prefix.parse_next(i)?; + let mut left = pratt_prefix(arena, i)?; loop { if i.is_empty() { @@ -500,7 +539,7 @@ 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)? { + match pratt_infix(arena, arena.alloc(left), op, op_precedence, allow_range, i)? { Iterable::Value(e) => left = e, Iterable::Range(r) => return Ok(Iterable::Range(r)), } @@ -510,19 +549,25 @@ fn pratt<'s>(precedence: PrecedenceResult, allow_range: bool) -> impl Parser<'s, } } -fn verify_expr<'s>(e: Iterable<'s>) -> Option> { +fn verify_expr<'s, 'a>(e: Iterable<'s, 'a>) -> Option> { match e { Iterable::Value(e) => Some(e), _ => None, } } -pub(super) fn basic_expression<'s>(i: &mut Input<'s>) -> Result> { - pratt(PrecedenceResult::default(), false) +pub(super) fn basic_expression<'s: 'a, 'a>( + arena: &'a AstArena, + i: &mut Input<'s>, +) -> Result> { + pratt(arena, PrecedenceResult::default(), false) .verify_map(verify_expr) .parse_next(i) } -pub(super) fn iterable<'s>(i: &mut Input<'s>) -> Result> { - pratt(PrecedenceResult::default(), true).parse_next(i) +pub(super) fn iterable<'s: 'a, 'a>( + arena: &'a AstArena, + i: &mut Input<'s>, +) -> Result> { + pratt(arena, PrecedenceResult::default(), true).parse_next(i) } diff --git a/crates/core/src/parser/block_expressions.rs b/crates/core/src/parser/block_expressions.rs index fb20dc6b..973520f3 100644 --- a/crates/core/src/parser/block_expressions.rs +++ b/crates/core/src/parser/block_expressions.rs @@ -13,33 +13,45 @@ use super::{ prelude::*, }; -fn optional_else<'s>(i: &mut Input<'s>) -> Result>> { +fn optional_else<'s, 'a>( + arena: &'a AstArena, + i: &mut Input<'s>, +) -> Result>> { let Some(kw_else) = opt(token(Keyword::Else)).parse_next(i)? else { return Ok(None); }; - let block = alt((if_expression, block_expression)) - .map(Box::new) - .parse_next(i)?; + let block = alt(( + |i: &mut Input<'s>| if_expression(arena, i), + |i: &mut Input<'s>| block_expression(arena, i), + )) + .map(|e| arena.alloc(e)) + .parse_next(i)?; Ok(Some(ElseBlock(kw_else, block))) } -pub(super) fn if_expression<'s>(i: &mut Input<'s>) -> Result> { +pub(super) fn if_expression<'s, 'a>( + arena: &'a AstArena, + 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), - optional_else, + expression_or_insert(arena, |t| *t == Operator::OpenBrace).map(|e| arena.alloc(e)), + (|i: &mut Input<'s>| block_expression(arena, i)).map(|e| arena.alloc(e)), + |i: &mut Input<'s>| optional_else(arena, i), )) .parse_next(i) } -pub(super) fn block_expression<'s>(i: &mut Input<'s>) -> Result> { +pub(super) fn block_expression<'s, 'a>( + arena: &'a AstArena, + i: &mut Input<'s>, +) -> Result> { not(json_start).parse_next(i)?; ( token_or_insert(Operator::OpenBrace, DiagnosticCode::MissingOpenBrace), - statements_and_expression, + |i: &mut Input<'s>| statements_and_expression(arena, i), token_or_insert(Operator::CloseBrace, DiagnosticCode::MissingCloseBrace), ) .map(|(open, (statements, expression), close)| { @@ -48,10 +60,13 @@ pub(super) fn block_expression<'s>(i: &mut Input<'s>) -> Result> .parse_next(i) } -pub(super) fn block_expression_no_expr<'s>(i: &mut Input<'s>) -> Result> { +pub(super) fn block_expression_no_expr<'s, 'a>( + arena: &'a AstArena, + i: &mut Input<'s>, +) -> Result> { ( token_or_insert(Operator::OpenBrace, DiagnosticCode::MissingOpenBrace), - statements_and_expression, + |i: &mut Input<'s>| statements_and_expression(arena, i), token_or_insert(Operator::CloseBrace, DiagnosticCode::MissingCloseBrace), ) .map(|(open, (mut statements, expr), close)| { @@ -75,61 +90,75 @@ pub(super) fn block_expression_no_expr<'s>(i: &mut Input<'s>) -> Result(i: &mut Input<'s>) -> Result> { +pub(super) fn fn_expression<'s, 'a>( + arena: &'a AstArena, + i: &mut Input<'s>, +) -> Result> { seq!(Expression::Function( token(Keyword::Fn), - parameter_list, - block_expression.map(Box::new), + |i: &mut Input<'s>| parameter_list(arena, i), + (|i: &mut Input<'s>| block_expression(arena, i)).map(|e| arena.alloc(e)), )) .parse_next(i) } -pub(super) fn loop_expression<'s>(i: &mut Input<'s>) -> Result> { +pub(super) fn loop_expression<'s, 'a>( + arena: &'a AstArena, + i: &mut Input<'s>, +) -> Result> { seq!(Expression::Loop( token(Keyword::Loop), - block_expression_no_expr.map(Box::new), + (|i: &mut Input<'s>| block_expression_no_expr(arena, i)).map(|e| arena.alloc(e)), )) .parse_next(i) } -pub(super) fn while_expression<'s>(i: &mut Input<'s>) -> Result> { +pub(super) fn while_expression<'s, 'a>( + arena: &'a AstArena, + 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), - optional_else, + expression_or_insert(arena, |t| *t == Operator::OpenBrace).map(|e| arena.alloc(e)), + (|i: &mut Input<'s>| block_expression_no_expr(arena, i)).map(|e| arena.alloc(e)), + |i: &mut Input<'s>| optional_else(arena, i), )) .parse_next(i) } -pub(super) fn match_expression<'s>(i: &mut Input<'s>) -> Result> { - fn branch_parser<'s>(i: &mut Input<'s>) -> Result> { +pub(super) fn match_expression<'s, 'a>( + arena: &'a AstArena, + i: &mut Input<'s>, +) -> Result> { + fn branch_parser<'s, 'a>(arena: &'a AstArena, i: &mut Input<'s>) -> Result> { ( alt(( ( token(Keyword::Case), - pattern_or_insert(false, |t| *t == Operator::OpenBrace || *t == Keyword::If), + pattern_or_insert(arena, false, |t| { + *t == Operator::OpenBrace || *t == Keyword::If + }), opt(( token(Keyword::If), - expression_or_insert(|t| *t == Operator::OpenBrace), + expression_or_insert(arena, |t| *t == Operator::OpenBrace), )), ), ( token_or_insert(Keyword::Case, DiagnosticCode::MissingCase), - pattern(false), - opt((token(Keyword::If), expression)), + pattern(arena, false), + opt((token(Keyword::If), |i: &mut Input<'s>| expression(arena, i))), ), )), - block_expression, + |i: &mut Input<'s>| block_expression(arena, i), ) .map(|((kw_case, pattern, guard), body)| MatchCase(kw_case, pattern, guard, body)) .parse_next(i) } ( token(Keyword::Match), - expression_or_insert(|t| *t == Operator::OpenBrace).map(Box::new), + expression_or_insert(arena, |t| *t == Operator::OpenBrace).map(|e| arena.alloc(e)), token_or_insert(Operator::OpenBrace, DiagnosticCode::MissingOpenBrace), - repeat(0.., branch_parser), + repeat(0.., |i: &mut Input<'s>| branch_parser(arena, i)), token_or_insert(Operator::CloseBrace, DiagnosticCode::MissingCloseBrace), ) .map(|(kw_match, expr, open, branches, close)| { @@ -138,31 +167,37 @@ pub(super) fn match_expression<'s>(i: &mut Input<'s>) -> Result> .parse_next(i) } -pub(super) fn for_in_expression<'s>(i: &mut Input<'s>) -> Result> { +pub(super) fn for_in_expression<'s, 'a>( + arena: &'a AstArena, + i: &mut Input<'s>, +) -> Result> { seq!(Expression::ForIn( token(Keyword::For), // 由后边的 `in` 定位,无条件插入 - pattern_or_insert(false, |_| true).map(Box::new), + pattern_or_insert(arena, false, |_| true).map(|p| arena.alloc(p)), token(Keyword::In), - iterable.map(Box::new), - block_expression_no_expr.map(Box::new), - optional_else, + (|i: &mut Input<'s>| iterable(arena, i)).map(|e| arena.alloc(e)), + (|i: &mut Input<'s>| block_expression_no_expr(arena, i)).map(|e| arena.alloc(e)), + |i: &mut Input<'s>| optional_else(arena, i), )) .parse_next(i) } -pub(super) fn block_like_expression<'s>(i: &mut Input<'s>) -> Result> { +pub(super) fn block_like_expression<'s, 'a>( + arena: &'a AstArena, + i: &mut Input<'s>, +) -> Result> { dispatch! {peek(any); t if *t == Operator::OpenBrace => alt(( - json_expression, - block_expression, + |i: &mut Input<'s>| json_expression(arena, i), + |i: &mut Input<'s>| block_expression(arena, i), )), - t if *t == Keyword::If => if_expression, - t if *t == Keyword::Fn => fn_expression, - t if *t == Keyword::Loop => loop_expression, - t if *t == Keyword::While => while_expression, - t if *t == Keyword::Match => match_expression, - t if *t == Keyword::For => for_in_expression, + t if *t == Keyword::If => |i: &mut Input<'s>| if_expression(arena, i), + t if *t == Keyword::Fn => |i: &mut Input<'s>| fn_expression(arena, i), + t if *t == Keyword::Loop => |i: &mut Input<'s>| loop_expression(arena, i), + t if *t == Keyword::While => |i: &mut Input<'s>| while_expression(arena, i), + t if *t == Keyword::Match => |i: &mut Input<'s>| match_expression(arena, i), + t if *t == Keyword::For => |i: &mut Input<'s>| for_in_expression(arena, i), &Token{..} => fail, } diff --git a/crates/core/src/parser/expression.rs b/crates/core/src/parser/expression.rs index 8f175f41..faef5908 100644 --- a/crates/core/src/parser/expression.rs +++ b/crates/core/src/parser/expression.rs @@ -2,15 +2,15 @@ use crate::parser::helper::unknown_range; use super::prelude::*; -#[derive(Debug, Clone, PartialEq, strum::EnumIs)] -pub enum Callable<'s> { +#[derive(Debug, PartialEq, strum::EnumIs)] +pub enum Callable<'s, 'a> { /// `type` Type(TokenRef<'s>), /// expression - Expression(Box>), + Expression(ABox<'a, Expression<'s, 'a>>), } -impl<'s> AstWalker<'s> for Callable<'s> { +impl<'s, 'a> AstWalker<'s> for Callable<'s, 'a> { fn collect_diagnostics(&mut self, collector: &mut DiagnosticsCollector<'_, '_>) { use Callable::*; match self { @@ -28,10 +28,10 @@ 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, 'a>(pub TokenRef<'s>, pub ABox<'a, Expression<'s, 'a>>); -impl<'s> AstWalker<'s> for ElseBlock<'s> { +impl<'s, 'a> AstWalker<'s> for ElseBlock<'s, 'a> { fn collect_diagnostics(&mut self, collector: &mut DiagnosticsCollector<'_, '_>) { self.0.collect_diagnostics(collector); self.1.collect_diagnostics(collector); @@ -42,15 +42,15 @@ impl<'s> AstWalker<'s> for ElseBlock<'s> { } /// `case` pattern (`if` expression)? block_expression -#[derive(Debug, Clone, PartialEq)] -pub struct MatchCase<'s>( +#[derive(Debug, PartialEq)] +pub struct MatchCase<'s, 'a>( pub TokenRef<'s>, - pub Pattern<'s>, - pub Option<(TokenRef<'s>, Expression<'s>)>, - pub Expression<'s>, + pub Pattern<'s, 'a>, + pub Option<(TokenRef<'s>, Expression<'s, 'a>)>, + pub Expression<'s, 'a>, ); -impl<'s> AstWalker<'s> for MatchCase<'s> { +impl<'s, 'a> AstWalker<'s> for MatchCase<'s, 'a> { fn collect_diagnostics(&mut self, collector: &mut DiagnosticsCollector<'_, '_>) { self.0.collect_diagnostics(collector); self.1.collect_diagnostics(collector); @@ -67,8 +67,8 @@ impl<'s> AstWalker<'s> for MatchCase<'s> { } } -#[derive(Debug, Clone, PartialEq, strum::EnumIs)] -pub enum Expression<'s> { +#[derive(Debug, PartialEq, strum::EnumIs)] +pub enum Expression<'s, 'a> { // primary /// number | string | ordinal | `true` | `false` | `nil` Literal(TokenRef<'s>), @@ -76,11 +76,11 @@ pub enum Expression<'s> { /// /// Holds a [crate::lexer::TokenKind::InterpolatedString], and a list of expressions /// that are interpolated into the string. - InterpolatedString(&'s Token<'s>, Vec>), + InterpolatedString(&'s Token<'s>, Vec>), /// identifier Variable(TokenRef<'s>), /// `(` expression `)` - Grouping(TokenRef<'s>, Box>, TokenRef<'s>), + Grouping(TokenRef<'s>, ABox<'a, Expression<'s, 'a>>, TokenRef<'s>), /// `(` element* `)` /// /// Use `()` for an empty record. @@ -90,24 +90,24 @@ pub enum Expression<'s> { /// For convenience for JSON-like data, allows following JSON-style trailing record: /// /// `{` ( ( string | interpolated_string ) `:` expression `,` )+ `}` - Record(TokenRef<'s>, Vec>, TokenRef<'s>), + Record(TokenRef<'s>, Vec>, TokenRef<'s>), /// `[` element* `]` /// /// Use `[]` for an empty list. - Array(TokenRef<'s>, Vec>, TokenRef<'s>), + Array(TokenRef<'s>, Vec>, TokenRef<'s>), // postfix /// callable `(` arguments `)` /// /// Arguments are a list of expressions, trailing comma is optional. Call( - Callable<'s>, + Callable<'s, 'a>, TokenRef<'s>, - Vec>, + Vec>, TokenRef<'s>, ), /// expression ( interpolated_string | string ) - TaggedString(Box>, Box>), + TaggedString(ABox<'a, Expression<'s, 'a>>, ABox<'a, Expression<'s, 'a>>), /// expression `::` extension `(` arguments `)` /// extension /// : identifier (`.` ( identifier | ordinal ))* @@ -117,35 +117,35 @@ pub enum Expression<'s> { /// /// Like `Call`, but `expression` is used as the first argument. Extension( - Box>, + ABox<'a, Expression<'s, 'a>>, TokenRef<'s>, - Callable<'s>, + Callable<'s, 'a>, TokenRef<'s>, - Vec>, + Vec>, TokenRef<'s>, ), /// expression `.` field /// /// Field must be an identifier or an ordinal. - Access(Box>, TokenRef<'s>, TokenRef<'s>), + Access(ABox<'a, Expression<'s, 'a>>, TokenRef<'s>, TokenRef<'s>), /// expression `[` expression `]` Index( - Box>, + ABox<'a, Expression<'s, 'a>>, TokenRef<'s>, - Box>, + ABox<'a, Expression<'s, 'a>>, TokenRef<'s>, ), /// expression `[` additive_expression? (`..` | `..<`) additive_expression? `]` Slice( - Box>, + ABox<'a, Expression<'s, 'a>>, TokenRef<'s>, - Option>>, + Option>>, TokenRef<'s>, - Option>>, + Option>>, TokenRef<'s>, ), /// expression `!` - NonNil(Box>, TokenRef<'s>), + NonNil(ABox<'a, Expression<'s, 'a>>, TokenRef<'s>), /// op expression /// @@ -153,7 +153,7 @@ pub enum Expression<'s> { /// - `!` logical not /// - `-` negation /// - `+` unary plus - Prefix(TokenRef<'s>, Box>), + Prefix(TokenRef<'s>, ABox<'a, Expression<'s, 'a>>), // 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( + ABox<'a, Expression<'s, 'a>>, + TokenRef<'s>, + ABox<'a, Expression<'s, 'a>>, + ), /// expression `is` pattern - Is(Box>, TokenRef<'s>, Box>), + Is( + ABox<'a, Expression<'s, 'a>>, + TokenRef<'s>, + ABox<'a, Pattern<'s, 'a>>, + ), // block-like /// `{` statements* expression? `}` @@ -178,8 +186,8 @@ pub enum Expression<'s> { /// If no expression is present, the value is `nil`. Block( TokenRef<'s>, - Vec>, - Option>>, + Vec>, + 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>, ABox<'a, Expression<'s, 'a>>), /// `while` expression block_expression (`else` expression)? /// /// The final expression of the block must not present. @@ -199,9 +207,9 @@ pub enum Expression<'s> { /// the value is the value of the `else_block`. Otherwise, `nil`. While( TokenRef<'s>, - Box>, - Box>, - Option>, + ABox<'a, Expression<'s, 'a>>, + ABox<'a, Expression<'s, 'a>>, + Option>, ), /// `for` pattern `in` expression block_expression (`else` expression)? /// @@ -214,11 +222,11 @@ pub enum Expression<'s> { /// the value is the value of the `else_block`. Otherwise, `nil`. ForIn( TokenRef<'s>, - Box>, + ABox<'a, Pattern<'s, 'a>>, TokenRef<'s>, - Box>, - Box>, - Option>, + ABox<'a, Iterable<'s, 'a>>, + ABox<'a, Expression<'s, 'a>>, + 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>, - Option>, + ABox<'a, Expression<'s, 'a>>, + ABox<'a, Expression<'s, 'a>>, + Option>, ), /// cond ? expression : expression Cond( - Box>, + ABox<'a, Expression<'s, 'a>>, TokenRef<'s>, - Box>, + ABox<'a, Expression<'s, 'a>>, TokenRef<'s>, - Box>, + ABox<'a, Expression<'s, 'a>>, ), /// `match` expression `{` ( `case` pattern (`if` expression)? block_expression )* `}` /// @@ -246,27 +254,32 @@ pub enum Expression<'s> { /// If no match is found, the value is `nil`. Match( TokenRef<'s>, - Box>, + ABox<'a, Expression<'s, 'a>>, TokenRef<'s>, - Vec>, + Vec>, TokenRef<'s>, ), /// `fn` parameters? block_expression /// /// Just like function declarations, but without the identifier. /// See [Statement::Function] for more details. - Function(TokenRef<'s>, Option>, Box>), + Function( + TokenRef<'s>, + Option>, + ABox<'a, Expression<'s, 'a>>, + ), /// Unknown expression Unknown { - recovered: Option>>, + recovered: Option>>, tokens: Vec>, errors: Vec, }, } -impl<'s> Expression<'s> { +impl<'s, 'a> Expression<'s, 'a> { pub(crate) fn wrap_as_unknown>>>( self, + arena: &'a AstArena, tokens: T, error: DiagnosticCode, ) -> Self { @@ -275,7 +288,7 @@ impl<'s> Expression<'s> { let mut range = tokens[0].range.clone(); range.end = tokens.last().unwrap().range.end; Expression::Unknown { - recovered: Some(Box::new(self)), + recovered: Some(arena.alloc(self)), tokens, errors: vec![SourceDiagnostic::new(range, error)], } @@ -332,7 +345,7 @@ impl<'s> Expression<'s> { } } -impl<'s> AstWalker<'s> for Expression<'s> { +impl<'s, 'a> AstWalker<'s> for Expression<'s, 'a> { fn collect_diagnostics(&mut self, collector: &mut DiagnosticsCollector<'_, '_>) { use Expression::*; match self { @@ -491,9 +504,10 @@ impl<'s> AstWalker<'s> for Expression<'s> { } => { collector.append(errors); tokens.collect_diagnostics(collector); - if let Some(mut recovered) = std::mem::take(recovered) { + if let Some(recovered) = std::mem::take(recovered) { + let mut recovered = ABox::into_inner(recovered); recovered.collect_diagnostics(collector); - *self = *recovered; + *self = recovered; } } } diff --git a/crates/core/src/parser/expressions.rs b/crates/core/src/parser/expressions.rs index 1c492e75..43b40791 100644 --- a/crates/core/src/parser/expressions.rs +++ b/crates/core/src/parser/expressions.rs @@ -6,7 +6,7 @@ use winnow::{ use super::{basic_expressions::basic_expression, prelude::*}; -fn unknown_expression<'s>(i: &mut Input<'s>) -> Result> { +fn unknown_expression<'s, 'a>(i: &mut Input<'s>) -> Result> { take_till(1.., |t: &Token<'s>| { *t == TokenKind::Eof || *t == Keyword::If @@ -44,15 +44,23 @@ fn unknown_expression<'s>(i: &mut Input<'s>) -> Result> { .parse_next(i) } -pub(super) fn expression<'s>(i: &mut Input<'s>) -> Result> { - alt((basic_expression, unknown_expression)).parse_next(i) +pub(super) fn expression<'s: 'a, 'a>( + arena: &'a AstArena, + i: &mut Input<'s>, +) -> Result> { + alt(( + |i: &mut Input<'s>| basic_expression(arena, i), + unknown_expression, + )) + .parse_next(i) } -pub(super) fn expression_or_insert<'s>( +pub(super) fn expression_or_insert<'s: 'a, 'a>( + arena: &'a AstArena, mut insert_cond: impl FnMut(&'s Token<'s>) -> bool + Copy, -) -> impl Parser<'s, Expression<'s>> { +) -> impl Parser<'s, Expression<'s, 'a>> { move |i: &mut Input<'s>| { - let e = opt(expression).parse_next(i)?; + let e = opt(|i: &mut Input<'s>| expression(arena, i)).parse_next(i)?; if let Some(e) = e { return Ok(e); } @@ -65,7 +73,7 @@ pub(super) fn expression_or_insert<'s>( } } -pub(super) fn expression_expected<'s>(pos: usize) -> Expression<'s> { +pub(super) fn expression_expected<'s, 'a>(pos: usize) -> Expression<'s, 'a> { Expression::unknown_range( vec![Token::empty(pos).into()], pos..pos, diff --git a/crates/core/src/parser/helper.rs b/crates/core/src/parser/helper.rs index d7a9594b..0fbd338c 100644 --- a/crates/core/src/parser/helper.rs +++ b/crates/core/src/parser/helper.rs @@ -5,11 +5,12 @@ use winnow::{ 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); +pub(super) fn construct_statements_and_expression<'s, 'a>( + arena: &'a AstArena, + mut statements: Vec>, + expression: Option>, +) -> (Vec>, Option>>) { + let expression = expression.map(|e| arena.alloc(e)); if expression.is_some() || statements.is_empty() { return (statements, expression); } @@ -25,12 +26,18 @@ pub(super) fn construct_statements_and_expression<'s>( (statements, expression) } -pub(super) fn statements_and_expression<'s>( +pub(super) fn statements_and_expression<'s, 'a>( + arena: &'a AstArena, i: &mut Input<'s>, -) -> Result<(Vec>, Option>>)> { - let (statements, expression): (Vec<_>, _) = - (repeat(0.., statement), opt(expression)).parse_next(i)?; - Ok(construct_statements_and_expression(statements, expression)) +) -> Result<(Vec>, Option>>)> { + let (statements, expression): (Vec<_>, _) = ( + repeat(0.., |i: &mut Input<'s>| statement(arena, i)), + opt(|i: &mut Input<'s>| expression(arena, i)), + ) + .parse_next(i)?; + Ok(construct_statements_and_expression( + arena, statements, expression, + )) } pub(super) fn literal_token<'s>(i: &mut Input<'s>) -> Result> { @@ -55,7 +62,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..8c16f3ba 100644 --- a/crates/core/src/parser/iterable.rs +++ b/crates/core/src/parser/iterable.rs @@ -1,12 +1,12 @@ use super::prelude::*; -#[derive(Debug, Clone, PartialEq)] -pub enum Iterable<'s> { - Range(Range<'s>), - Value(Expression<'s>), +#[derive(Debug, PartialEq)] +pub enum Iterable<'s, 'a> { + Range(Range<'s, 'a>), + Value(Expression<'s, 'a>), } -impl<'s> AstWalker<'s> for Iterable<'s> { +impl<'s, 'a> AstWalker<'s> for Iterable<'s, 'a> { fn collect_diagnostics(&mut self, collector: &mut DiagnosticsCollector<'_, '_>) { match self { Iterable::Range(range) => range.collect_diagnostics(collector), diff --git a/crates/core/src/parser/json_expressions.rs b/crates/core/src/parser/json_expressions.rs index bea433ea..54e6cb9a 100644 --- a/crates/core/src/parser/json_expressions.rs +++ b/crates/core/src/parser/json_expressions.rs @@ -28,43 +28,52 @@ pub(super) fn json_start<'s>(i: &mut Input<'s>) -> Result<()> { .parse_next(i) } -enum JsonFieldName<'s> { +enum JsonFieldName<'s, 'a> { Literal(TokenRef<'s>), - Interpolated(Box>), + Interpolated(ABox<'a, Expression<'s, 'a>>), } -struct JsonElement<'s> { - key: JsonFieldName<'s>, +struct JsonElement<'s, 'a> { + key: JsonFieldName<'s, 'a>, colon: TokenRef<'s>, - value: Box>, + value: ABox<'a, Expression<'s, 'a>>, comma: TokenRef<'s>, } -fn json_field_name<'s>(i: &mut Input<'s>) -> Result> { +fn json_field_name<'s: 'a, 'a>( + arena: &'a AstArena, + i: &mut Input<'s>, +) -> Result> { 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))), + (|i: &mut Input<'s>| interpolation(arena, i)) + .map(|e| JsonFieldName::Interpolated(arena.alloc(e))), )) .parse_next(i) } -pub(super) fn json_expression<'s>(i: &mut Input<'s>) -> Result> { +pub(super) fn json_expression<'s: 'a, 'a>( + arena: &'a AstArena, + i: &mut Input<'s>, +) -> Result> { // 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)?; let elements: Vec<_> = repeat( 1.., ( - json_field_name, + |i: &mut Input<'s>| json_field_name(arena, i), token_or_insert(Operator::Colon, DiagnosticCode::MissingColon), - expression_or_insert(|t| *t == Operator::Comma || *t == Operator::CloseBrace), + expression_or_insert(arena, |t| { + *t == Operator::Comma || *t == Operator::CloseBrace + }), token_or_insert(Operator::Comma, DiagnosticCode::MissingComma), ) .map(|(key, colon, value, comma)| JsonElement { key, colon, - value: Box::new(value), + value: arena.alloc(value), comma, }), ) @@ -85,9 +94,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(arena, el) } else { - RecordElement::new_with_comma(el, e.comma) + RecordElement::new_with_comma(arena, el, e.comma) } }) .collect(); diff --git a/crates/core/src/parser/list_item.rs b/crates/core/src/parser/list_item.rs index afba15dd..c53d71e4 100644 --- a/crates/core/src/parser/list_item.rs +++ b/crates/core/src/parser/list_item.rs @@ -3,10 +3,10 @@ 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, 'a, T>(pub ABox<'a, T>, pub Option>); -impl<'s, T: AstWalker<'s>> AstWalker<'s> for ListItem<'s, T> { +impl<'s, 'a, T: AstWalker<'s>> AstWalker<'s> for ListItem<'s, 'a, T> { fn collect_diagnostics(&mut self, collector: &mut DiagnosticsCollector<'_, '_>) { self.0.collect_diagnostics(collector); self.1.collect_diagnostics(collector); @@ -19,13 +19,13 @@ 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)) +impl<'s, 'a, T> ListItem<'s, 'a, T> { + pub fn new_with_comma(arena: &'a AstArena, item: T, tail_comma: TokenRef<'s>) -> Self { + Self(arena.alloc(item), Some(tail_comma)) } - pub fn new(item: T) -> Self { - Self(Box::new(item), None) + pub fn new(arena: &'a AstArena, item: T) -> Self { + Self(arena.alloc(item), None) } pub fn has_tail_comma(&self) -> bool { @@ -36,11 +36,11 @@ impl<'s, T> ListItem<'s, T> { } pub fn unwrap(self) -> T { - *self.0 + ABox::into_inner(self.0) } } -impl Deref for ListItem<'_, T> { +impl Deref for ListItem<'_, '_, T> { type Target = T; fn deref(&self) -> &Self::Target { @@ -48,7 +48,7 @@ impl Deref for ListItem<'_, T> { } } -impl DerefMut for ListItem<'_, T> { +impl DerefMut for ListItem<'_, '_, T> { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } diff --git a/crates/core/src/parser/mod.rs b/crates/core/src/parser/mod.rs index ebc577eb..ee7556c3 100644 --- a/crates/core/src/parser/mod.rs +++ b/crates/core/src/parser/mod.rs @@ -1,5 +1,5 @@ use winnow::{ - ModalResult, Parser as _, + ModalResult, error::{EmptyError, ErrMode}, stream::TokenSlice, }; @@ -52,12 +52,15 @@ impl<'s, Output, F> Parser<'s, Output> for F where } mod prelude { + pub(super) use bumpalo::boxed::Box as ABox; + 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, }; pub(super) use crate::{ + arena::AstArena, diagnostic::{DiagnosticCode, DiagnosticsCollector, SourceDiagnostic, SourceRange}, lexer::{Keyword, Operator, Token, TokenKind}, }; @@ -71,6 +74,9 @@ pub fn to_input<'s>(tokens: &'s [Token<'s>]) -> Input<'s> { TokenSlice::new(tokens) } -pub fn parse<'s>(i: &mut Input<'s>) -> Result> { - scripts::script.parse_next(i) +pub fn parse<'s, 'a>( + arena: &'a crate::arena::AstArena, + i: &mut Input<'s>, +) -> Result> { + scripts::script(arena, i) } diff --git a/crates/core/src/parser/parameter_list.rs b/crates/core/src/parser/parameter_list.rs index 3d83b019..c9885dde 100644 --- a/crates/core/src/parser/parameter_list.rs +++ b/crates/core/src/parser/parameter_list.rs @@ -9,28 +9,28 @@ use super::{ }; /// `(` ...items `)` -#[derive(Debug, Clone, PartialEq)] -pub struct ParameterList<'s>( +#[derive(Debug, PartialEq)] +pub struct ParameterList<'s, 'a>( pub TokenRef<'s>, - pub Vec>, + pub Vec>, pub TokenRef<'s>, ); -impl<'s> Deref for ParameterList<'s> { - type Target = Vec>; +impl<'s, 'a> Deref for ParameterList<'s, 'a> { + type Target = Vec>; fn deref(&self) -> &Self::Target { &self.1 } } -impl DerefMut for ParameterList<'_> { +impl DerefMut for ParameterList<'_, '_> { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.1 } } -impl<'s> AstWalker<'s> for ParameterList<'s> { +impl<'s, 'a> AstWalker<'s> for ParameterList<'s, 'a> { fn collect_diagnostics(&mut self, collector: &mut DiagnosticsCollector<'_, '_>) { self.0.collect_diagnostics(collector); self.1.collect_diagnostics(collector); @@ -41,8 +41,12 @@ impl<'s> AstWalker<'s> for ParameterList<'s> { } } -pub(super) fn parameter_list<'s>(i: &mut Input<'s>) -> Result>> { +pub(super) fn parameter_list<'s, 'a>( + arena: &'a AstArena, + i: &mut Input<'s>, +) -> Result>> { let list = opt(array_pattern_like( + arena, token(Operator::OpenParen), token_or_insert(Operator::CloseParen, DiagnosticCode::MissingCloseParen), false, @@ -67,9 +71,11 @@ pub(super) fn parameter_list<'s>(i: &mut Input<'s>) -> Result { +#[derive(Debug, PartialEq, strum::EnumIs)] +pub enum Pattern<'s, 'a> { /// `(` pattern `)` /// /// Grouping pattern. - Grouping(TokenRef<'s>, Box>, TokenRef<'s>), + Grouping(TokenRef<'s>, ABox<'a, Pattern<'s, 'a>>, 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>, ABox<'a, Pattern<'s, 'a>>), /// (pattern_constant | pattern_literal) ( `..` | `..<` ) (pattern_constant | pattern_literal) /// /// Matches against a range of constant values. - Range(Box>, TokenRef<'s>, Box>), + Range( + ABox<'a, Pattern<'s, 'a>>, + TokenRef<'s>, + ABox<'a, Pattern<'s, 'a>>, + ), /// `_` /// /// Matches and discards a value. @@ -48,7 +52,7 @@ pub enum Pattern<'s> { /// ; /// `````` /// Matches a record pattern. - Record(TokenRef<'s>, Vec>, TokenRef<'s>), + Record(TokenRef<'s>, Vec>, TokenRef<'s>), /// ```antlr /// pattern_array /// : '[' sub_pattern* ']' @@ -58,7 +62,7 @@ pub enum Pattern<'s> { /// | '..' pattern? ','? /// ; /// ``` - Array(TokenRef<'s>, Vec>, TokenRef<'s>), + Array(TokenRef<'s>, Vec>, TokenRef<'s>), /// prefix<`..`> /// /// Contains no token. @@ -73,27 +77,36 @@ pub enum Pattern<'s> { /// pattern `and` pattern /// /// Matches all of the patterns. - And(Box>, TokenRef<'s>, Box>), + And( + ABox<'a, Pattern<'s, 'a>>, + TokenRef<'s>, + ABox<'a, Pattern<'s, 'a>>, + ), /// pattern `or` pattern /// /// Matches any of the patterns. - Or(Box>, TokenRef<'s>, Box>), + Or( + ABox<'a, Pattern<'s, 'a>>, + TokenRef<'s>, + ABox<'a, Pattern<'s, 'a>>, + ), /// `not` pattern /// /// Matches if the pattern does not match. - Not(TokenRef<'s>, Box>), + Not(TokenRef<'s>, ABox<'a, Pattern<'s, 'a>>), /// Unknown pattern. Unknown { - recovered: Option>>, + recovered: Option>>, tokens: Vec>, errors: Vec, }, } -impl<'s> Pattern<'s> { +impl<'s, 'a> Pattern<'s, 'a> { pub(crate) fn wrap_as_unknown>>>( self, + arena: &'a AstArena, tokens: T, error: DiagnosticCode, ) -> Self { @@ -102,7 +115,7 @@ impl<'s> Pattern<'s> { let mut range = tokens[0].range.clone(); range.end = tokens.last().unwrap().range.end; Pattern::Unknown { - recovered: Some(Box::new(self)), + recovered: Some(arena.alloc(self)), tokens, errors: vec![SourceDiagnostic::new(range, error)], } @@ -143,7 +156,7 @@ impl<'s> Pattern<'s> { } } -impl<'s> AstWalker<'s> for Pattern<'s> { +impl<'s, 'a> AstWalker<'s> for Pattern<'s, 'a> { fn collect_diagnostics(&mut self, collector: &mut DiagnosticsCollector<'_, '_>) { use Pattern::*; match self { @@ -209,9 +222,10 @@ impl<'s> AstWalker<'s> for Pattern<'s> { } => { collector.append(errors); tokens.collect_diagnostics(collector); - if let Some(mut recovered) = std::mem::take(recovered) { + if let Some(recovered) = std::mem::take(recovered) { + let mut recovered = ABox::into_inner(recovered); recovered.collect_diagnostics(collector); - *self = *recovered; + *self = recovered; } } } diff --git a/crates/core/src/parser/patterns.rs b/crates/core/src/parser/patterns.rs index f3e8f305..bb173680 100644 --- a/crates/core/src/parser/patterns.rs +++ b/crates/core/src/parser/patterns.rs @@ -15,7 +15,7 @@ use super::{ record_helper::record_base, }; -fn unknown_pattern<'s>(i: &mut Input<'s>) -> Result> { +fn unknown_pattern<'s, 'a>(_arena: &'a AstArena, i: &mut Input<'s>) -> Result> { take_till(1.., |t: &Token<'s>| { *t == TokenKind::Eof || *t == Keyword::If @@ -45,12 +45,13 @@ fn unknown_pattern<'s>(i: &mut Input<'s>) -> Result> { .parse_next(i) } -pub(super) fn pattern_or_insert<'s>( +pub(super) fn pattern_or_insert<'s: 'a, 'a>( + arena: &'a AstArena, rebind: bool, mut insert_cond: impl FnMut(&'s Token<'s>) -> bool + Copy, -) -> impl Parser<'s, Pattern<'s>> { +) -> impl Parser<'s, Pattern<'s, 'a>> { move |i: &mut Input<'s>| { - let p = opt(pattern(rebind)).parse_next(i)?; + let p = opt(pattern(arena, rebind)).parse_next(i)?; if let Some(p) = p { return Ok(p); } @@ -63,7 +64,7 @@ pub(super) fn pattern_or_insert<'s>( } } -pub(super) fn pattern_expected<'s>(pos: usize) -> Pattern<'s> { +pub(super) fn pattern_expected<'s, 'a>(pos: usize) -> Pattern<'s, 'a> { Pattern::unknown_range( vec![Token::empty(pos).into()], pos..pos, @@ -71,58 +72,67 @@ pub(super) fn pattern_expected<'s>(pos: usize) -> Pattern<'s> { ) } -pub(super) fn pattern<'s>(rebind: bool) -> impl Parser<'s, Pattern<'s>> { - move |i: &mut Input<'s>| or_pattern(rebind).parse_next(i) +pub(super) fn pattern<'s: 'a, 'a>( + arena: &'a AstArena, + rebind: bool, +) -> impl Parser<'s, Pattern<'s, 'a>> { + move |i: &mut Input<'s>| or_pattern(arena, rebind).parse_next(i) } -fn primary_pattern<'s>(rebind: bool) -> impl Parser<'s, Pattern<'s>> { +fn primary_pattern<'s: 'a, 'a>( + arena: &'a AstArena, + rebind: bool, +) -> impl Parser<'s, Pattern<'s, 'a>> { move |i: &mut Input<'s>| { alt(( - relation_pattern, - record_like_pattern(rebind), - array_pattern(rebind), - range_pattern, - literal_constant_pattern::, - discard_bind_pattern(rebind), - not_pattern(rebind), - unknown_pattern, + |i: &mut Input<'s>| relation_pattern(arena, i), + record_like_pattern(arena, rebind), + array_pattern(arena, rebind), + |i: &mut Input<'s>| range_pattern(arena, i), + |i: &mut Input<'s>| literal_constant_pattern::(arena, i), + discard_bind_pattern(arena, rebind), + not_pattern(arena, rebind), + |i: &mut Input<'s>| unknown_pattern(arena, i), )) .parse_next(i) } } -fn not_pattern<'s>(rebind: bool) -> impl Parser<'s, Pattern<'s>> { +fn not_pattern<'s: 'a, 'a>(arena: &'a AstArena, rebind: bool) -> impl Parser<'s, Pattern<'s, 'a>> { move |i: &mut Input<'s>| { - (token(Keyword::Not), primary_pattern(rebind)) - .map(|(kw_not, p)| Pattern::Not(kw_not, Box::new(p))) + (token(Keyword::Not), primary_pattern(arena, rebind)) + .map(|(kw_not, p)| Pattern::Not(kw_not, arena.alloc(p))) .parse_next(i) } } -fn and_pattern<'s>(rebind: bool) -> impl Parser<'s, Pattern<'s>> { +fn and_pattern<'s: 'a, 'a>(arena: &'a AstArena, rebind: bool) -> impl Parser<'s, Pattern<'s, 'a>> { move |i: &mut Input<'s>| { separated_foldl1( - primary_pattern(rebind), + primary_pattern(arena, rebind), token(Keyword::And), - |left, op, right| Pattern::And(Box::new(left), op, Box::new(right)), + |left, op, right| Pattern::And(arena.alloc(left), op, arena.alloc(right)), ) .parse_next(i) } } -fn or_pattern<'s>(rebind: bool) -> impl Parser<'s, Pattern<'s>> { +fn or_pattern<'s: 'a, 'a>(arena: &'a AstArena, rebind: bool) -> impl Parser<'s, Pattern<'s, 'a>> { move |i: &mut Input<'s>| { separated_foldl1( - and_pattern(rebind), + and_pattern(arena, rebind), token(Keyword::Or), - |left, op, right| Pattern::Or(Box::new(left), op, Box::new(right)), + |left, op, right| Pattern::Or(arena.alloc(left), op, arena.alloc(right)), ) .parse_next(i) } } -fn literal_constant_pattern<'s, const CUT: bool>(i: &mut Input<'s>) -> Result> { - let literal_pattern = |i: &mut Input<'s>| -> Result> { +fn literal_constant_pattern<'s, 'a, const CUT: bool>( + arena: &'a AstArena, + i: &mut Input<'s>, +) -> Result> { + let literal_pattern = |i: &mut Input<'s>| -> Result> { ( opt(one_of(|t: &Token<'s>| { *t == Operator::Plus || *t == Operator::Minus || *t == Operator::Exclamation @@ -135,28 +145,35 @@ fn literal_constant_pattern<'s, const CUT: bool>(i: &mut Input<'s>) -> Result| -> Result> { + let constant_pattern = |i: &mut Input<'s>| -> Result> { variable_token(false, false) .verify_map(|t: TokenRef<'s>| { let name = t.to_id_name()?; let is_const = name.starts_with('@'); Some(if !is_const { if CUT { - Pattern::Constant(t.clone()) - .wrap_as_unknown([t], DiagnosticCode::InvalidConstantLiteral) + Pattern::Constant(t.clone()).wrap_as_unknown( + arena, + [t], + DiagnosticCode::InvalidConstantLiteral, + ) } else { return None; } @@ -169,18 +186,18 @@ fn literal_constant_pattern<'s, const CUT: bool>(i: &mut Input<'s>) -> Result(i: &mut Input<'s>) -> Result> { +fn relation_pattern<'s, 'a>(arena: &'a AstArena, 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), + (|i: &mut Input<'s>| literal_constant_pattern::(arena, i)).map(|p| arena.alloc(p)), )) .parse_next(i) } -fn range_pattern<'s>(i: &mut Input<'s>) -> Result> { - fn range_guard<'s>(p: Pattern<'s>) -> Box> { - match p { +fn range_pattern<'s, 'a>(arena: &'a AstArena, i: &mut Input<'s>) -> Result> { + fn range_guard<'s, 'a>(arena: &'a AstArena, p: Pattern<'s, 'a>) -> ABox<'a, Pattern<'s, 'a>> { + arena.alloc(match p { Pattern::Literal(op, l) => { if l.is_number_literal() { Pattern::Literal(op, l) @@ -193,19 +210,23 @@ fn range_pattern<'s>(i: &mut Input<'s>) -> Result> { } } _ => p, - } - .into() + }) } seq!(Pattern::Range( - literal_constant_pattern::.map(range_guard), + (|i: &mut Input<'s>| literal_constant_pattern::(arena, i)) + .map(|p| range_guard(arena, p)), one_of(|t: &Token<'s>| *t == Operator::SpreadRange || *t == Operator::HalfOpenRange) .map(TokenRef::borrow), - literal_constant_pattern::.map(range_guard), + (|i: &mut Input<'s>| literal_constant_pattern::(arena, i)) + .map(|p| range_guard(arena, p)), )) .parse_next(i) } -fn discard_bind_pattern<'s>(rebind: bool) -> impl Parser<'s, Pattern<'s>> { +fn discard_bind_pattern<'s: 'a, 'a>( + arena: &'a AstArena, + rebind: bool, +) -> impl Parser<'s, Pattern<'s, 'a>> { move |i: &mut Input<'s>| { (opt(token(Keyword::Mut)), variable_token(true, false)) .map(|(kw_mut, id)| { @@ -240,8 +261,11 @@ fn discard_bind_pattern<'s>(rebind: bool) -> impl Parser<'s, Pattern<'s>> { } if name.starts_with('@') { let tokens = vec![kw_mut.clone().unwrap(), id.clone()]; - return Pattern::Bind(kw_mut, id) - .wrap_as_unknown(tokens, DiagnosticCode::ConstantInBindPattern); + return Pattern::Bind(kw_mut, id).wrap_as_unknown( + arena, + tokens, + DiagnosticCode::ConstantInBindPattern, + ); } Pattern::Bind(kw_mut, id) }) @@ -249,17 +273,20 @@ fn discard_bind_pattern<'s>(rebind: bool) -> impl Parser<'s, Pattern<'s>> { } } -fn pattern_spread<'s>(rebind: bool) -> impl Parser<'s, Pattern<'s>> { +fn pattern_spread<'s: 'a, 'a>( + arena: &'a AstArena, + rebind: bool, +) -> impl Parser<'s, Pattern<'s, 'a>> { move |i: &mut Input<'s>| { let pos = i.previous_token_end(); - opt(pattern(rebind)) + opt(pattern(arena, rebind)) .map(|p| { let Some(p) = p else { return Pattern::SpreadDiscard(pos); }; if let Pattern::Discard(t) = &p { let tokens = [t.clone()]; - p.wrap_as_unknown(tokens, DiagnosticCode::DiscardInSpreadPattern) + p.wrap_as_unknown(arena, tokens, DiagnosticCode::DiscardInSpreadPattern) } else { p } @@ -268,16 +295,20 @@ fn pattern_spread<'s>(rebind: bool) -> impl Parser<'s, Pattern<'s>> { } } -fn record_like_pattern<'s>(rebind: bool) -> impl Parser<'s, Pattern<'s>> { - let omit_named = move |i: &mut Input<'s>| -> Result> { +fn record_like_pattern<'s: 'a, 'a>( + arena: &'a AstArena, + rebind: bool, +) -> impl Parser<'s, Pattern<'s, 'a>> { + let omit_named = move |i: &mut Input<'s>| -> Result> { // omit named 会消费 `:`,可以无条件插入 - pattern_or_insert(rebind, |_| true) + pattern_or_insert(arena, rebind, |_| true) .with_taken() .map(|(p, t)| { if p.is_bind() || p.is_unknown() { p } else { p.wrap_as_unknown( + arena, t.iter().map(TokenRef::borrow).collect::>(), DiagnosticCode::BadOmitKeyRecordPattern, ) @@ -288,7 +319,7 @@ fn record_like_pattern<'s>(rebind: bool) -> impl Parser<'s, Pattern<'s>> { // unnamed 不额外消费字符,仅在分隔符前插入 let unnamed = move |i: &mut Input<'s>| { - pattern_or_insert(rebind, |t| { + pattern_or_insert(arena, rebind, |t| { *t == Operator::Comma || *t == Operator::CloseParen }) .parse_next(i) @@ -296,12 +327,13 @@ fn record_like_pattern<'s>(rebind: bool) -> impl Parser<'s, Pattern<'s>> { move |i: &mut Input<'s>| { let (open, mut parts, close) = record_base( + arena, // named 会消费 name `:`,可以无条件插入 - pattern_or_insert(rebind, |_| true), + pattern_or_insert(arena, rebind, |_| true), |t| Pattern::unknown([t.into()], DiagnosticCode::InterpolatedNameRecordPattern), omit_named, unnamed, - pattern_spread(rebind), + pattern_spread(arena, rebind), pattern_expected, ) .parse_next(i)?; @@ -331,19 +363,21 @@ fn record_like_pattern<'s>(rebind: bool) -> impl Parser<'s, Pattern<'s>> { } } -pub(crate) fn array_pattern_like<'s>( +pub(crate) fn array_pattern_like<'s: 'a, 'a>( + arena: &'a AstArena, open: impl Parser<'s, TokenRef<'s>>, close: impl Parser<'s, TokenRef<'s>>, rebind: bool, -) -> impl Parser<'s, Pattern<'s>> { +) -> impl Parser<'s, Pattern<'s, 'a>> { let element_pattern = move |i: &mut Input<'s>| { - pattern_or_insert(rebind, |t| { + pattern_or_insert(arena, rebind, |t| { *t == Operator::Comma || *t == Operator::CloseBracket }) .with_taken() .map(|(p, t)| { if matches!(p, Pattern::Range(..)) { p.wrap_as_unknown( + arena, t.iter().map(TokenRef::borrow).collect::>(), DiagnosticCode::AmbiguousRangePattern, ) @@ -355,10 +389,11 @@ pub(crate) fn array_pattern_like<'s>( }; move |i: &mut Input<'s>| { let (open, parts, close) = array_base( + arena, open, close, element_pattern, - pattern_spread(rebind), + pattern_spread(arena, rebind), pattern_expected, ) .parse_next(i)?; @@ -366,9 +401,13 @@ pub(crate) fn array_pattern_like<'s>( } } -fn array_pattern<'s>(rebind: bool) -> impl Parser<'s, Pattern<'s>> { - move |i: &mut Input<'s>| -> Result> { +fn array_pattern<'s: 'a, 'a>( + arena: &'a AstArena, + rebind: bool, +) -> impl Parser<'s, Pattern<'s, 'a>> { + move |i: &mut Input<'s>| -> Result> { let mut p = array_pattern_like( + arena, token(Operator::OpenBracket), token_or_insert(Operator::CloseBracket, DiagnosticCode::MissingCloseBracket), rebind, @@ -389,9 +428,11 @@ 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( - pattern.wrap_as_unknown([kw.clone()], DiagnosticCode::DuplicateSpreadPattern), - )); + *part = ArrayElementBase::Element(arena.alloc(pattern.wrap_as_unknown( + arena, + [kw.clone()], + DiagnosticCode::DuplicateSpreadPattern, + ))); } } diff --git a/crates/core/src/parser/range.rs b/crates/core/src/parser/range.rs index bd02e301..bea668dc 100644 --- a/crates/core/src/parser/range.rs +++ b/crates/core/src/parser/range.rs @@ -3,20 +3,20 @@ use super::prelude::*; /// A range expression. /// /// `start..end` or `start..( - pub Box>, +#[derive(Debug, PartialEq)] +pub struct Range<'s, 'a>( + pub ABox<'a, Expression<'s, 'a>>, pub TokenRef<'s>, - pub Box>, + pub ABox<'a, Expression<'s, 'a>>, ); -impl<'s> Range<'s> { +impl<'s, 'a> Range<'s, 'a> { pub fn exclusive(&self) -> bool { *self.1.as_ref() == Operator::HalfOpenRange } } -impl<'s> AstWalker<'s> for Range<'s> { +impl<'s, 'a> AstWalker<'s> for Range<'s, 'a> { fn collect_diagnostics(&mut self, collector: &mut DiagnosticsCollector<'_, '_>) { let Range(start, op, end) = self; start.collect_diagnostics(collector); diff --git a/crates/core/src/parser/record_element.rs b/crates/core/src/parser/record_element.rs index 5ed5230f..61af81a8 100644 --- a/crates/core/src/parser/record_element.rs +++ b/crates/core/src/parser/record_element.rs @@ -1,26 +1,28 @@ use super::prelude::*; -#[derive(Debug, Clone, PartialEq, strum::EnumIs)] -pub enum RecordElementBase<'s, E, I> { +#[derive(Debug, PartialEq, strum::EnumIs)] +pub enum RecordElementBase<'s, 'a, E, I> { /// name colon Named - Named(TokenRef<'s>, TokenRef<'s>, Box), + Named(TokenRef<'s>, TokenRef<'s>, ABox<'a, E>), /// interpolated_string colon Named - InterpolateNamed(Box, TokenRef<'s>, Box), + InterpolateNamed(ABox<'a, I>, TokenRef<'s>, ABox<'a, E>), /// colon OmitNamed - OmitNamed(TokenRef<'s>, Box), + OmitNamed(TokenRef<'s>, ABox<'a, E>), /// Unnamed - Unnamed(Box), + Unnamed(ABox<'a, E>), /// `..` Spread - Spread(TokenRef<'s>, Box), + Spread(TokenRef<'s>, ABox<'a, E>), } use RecordElementBase::*; -pub type RecordElement<'s> = ListItem<'s, RecordElementBase<'s, Expression<'s>, Expression<'s>>>; +pub type RecordElement<'s, 'a> = + ListItem<'s, 'a, RecordElementBase<'s, 'a, Expression<'s, 'a>, Expression<'s, 'a>>>; -pub type RecordPattern<'s> = ListItem<'s, RecordElementBase<'s, Pattern<'s>, Pattern<'s>>>; +pub type RecordPattern<'s, 'a> = + ListItem<'s, 'a, RecordElementBase<'s, 'a, Pattern<'s, 'a>, Pattern<'s, 'a>>>; -impl<'s, E, I> RecordElementBase<'s, E, I> { +impl<'s, 'a, E, I> RecordElementBase<'s, 'a, E, I> { pub fn colon(&self) -> Option<&Token<'s>> { match self { Named(_, colon, _) | InterpolateNamed(_, colon, _) | OmitNamed(colon, _) => Some(colon), @@ -29,7 +31,7 @@ impl<'s, E, I> RecordElementBase<'s, E, I> { } } -impl<'s, E: AstWalker<'s>, I: AstWalker<'s>> AstWalker<'s> for RecordElementBase<'s, E, I> { +impl<'s, 'a, E: AstWalker<'s>, I: AstWalker<'s>> AstWalker<'s> for RecordElementBase<'s, 'a, E, I> { fn collect_diagnostics(&mut self, collector: &mut DiagnosticsCollector<'_, '_>) { match self { Named(name, colon, value) => { diff --git a/crates/core/src/parser/record_helper.rs b/crates/core/src/parser/record_helper.rs index 03b3c3c3..6d66fa5e 100644 --- a/crates/core/src/parser/record_helper.rs +++ b/crates/core/src/parser/record_helper.rs @@ -21,20 +21,22 @@ 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 + 'a, 'a, E: PartialEq + 'a, I: PartialEq + 'a>( + arena: &'a AstArena, named: impl Parser<'s, E>, mut interpolate_name: impl FnMut(&'s Token<'s>) -> I + Copy, omit_named: impl Parser<'s, E>, unnamed: impl Parser<'s, E>, spread: impl Parser<'s, E>, mut missing: impl FnMut(usize) -> E + Copy, -) -> impl Parser<'s, ListItem<'s, RecordElementBase<'s, E, I>>> { +) -> impl Parser<'s, ListItem<'s, 'a, RecordElementBase<'s, 'a, E, I>>> { let colon = |t: &Token<'s>| -> bool { *t == Operator::Colon || *t == Operator::QuestionColon }; move |i: &mut Input<'s>| { let first = peek(any).parse_next(i)?; if *first == Operator::Comma { return Ok(ListItem::new_with_comma( - RecordElementBase::Unnamed(missing(first.range.start).into()), + arena, + RecordElementBase::Unnamed(arena.alloc(missing(first.range.start))), token(Operator::Comma).parse_next(i)?, )); } @@ -47,9 +49,9 @@ fn record_element<'t, 's: 't, E: Clone + PartialEq + 's, I: Clone + PartialEq + } let result = alt(( (token(Operator::SpreadRange), spread) - .map(|(s, e)| RecordElementBase::Spread(s, e.into())), + .map(|(s, e)| RecordElementBase::Spread(s, arena.alloc(e))), (one_of(colon), omit_named) - .map(|(c, o)| RecordElementBase::OmitNamed(c.into(), o.into())), + .map(|(c, o)| RecordElementBase::OmitNamed(c.into(), arena.alloc(o))), ( one_of(|t: &Token<'s>| t.is_interpolated_string()), one_of(colon), @@ -57,14 +59,14 @@ fn record_element<'t, 's: 't, E: Clone + PartialEq + 's, I: Clone + PartialEq + ) .map(|(r, c, n)| { RecordElementBase::InterpolateNamed( - Box::new(interpolate_name(r)), + arena.alloc(interpolate_name(r)), c.into(), - n.into(), + arena.alloc(n), ) }), (record_name, one_of(colon), named) - .map(|(r, c, n)| RecordElementBase::Named(r, c.into(), n.into())), - unnamed.map(|u| RecordElementBase::Unnamed(u.into())), + .map(|(r, c, n)| RecordElementBase::Named(r, c.into(), arena.alloc(n))), + unnamed.map(|u| RecordElementBase::Unnamed(arena.alloc(u))), )) .parse_next(i)?; let last = peek(any).parse_next(i)?; @@ -83,14 +85,15 @@ 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(arena, result)); } let comma = token_or_insert(Operator::Comma, DiagnosticCode::MissingComma).parse_next(i)?; - Ok(ListItem::new_with_comma(result, comma)) + Ok(ListItem::new_with_comma(arena, result, comma)) } } -pub(super) fn record_base<'t, 's: 't, E: Clone + PartialEq + 's, I: Clone + PartialEq + 's>( +pub(super) fn record_base<'t, 's: 't + 'a, 'a, E: PartialEq + 'a, I: PartialEq + 'a>( + arena: &'a AstArena, named: impl Parser<'s, E>, interpolate_name: impl FnMut(&'s Token<'s>) -> I + Copy, omit_named: impl Parser<'s, E>, @@ -101,7 +104,7 @@ pub(super) fn record_base<'t, 's: 't, E: Clone + PartialEq + 's, I: Clone + Part 's, ( TokenRef<'s>, - Vec>>, + Vec>>, TokenRef<'s>, ), > { @@ -110,6 +113,7 @@ pub(super) fn record_base<'t, 's: 't, E: Clone + PartialEq + 's, I: Clone + Part let parts: Vec<_> = repeat( 0.., record_element( + arena, named, interpolate_name, omit_named, diff --git a/crates/core/src/parser/script.rs b/crates/core/src/parser/script.rs index 0a76b5f6..d9887e94 100644 --- a/crates/core/src/parser/script.rs +++ b/crates/core/src/parser/script.rs @@ -7,14 +7,14 @@ 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)] -pub struct Script<'s>( - pub Vec>, - pub Option>>, +#[derive(Debug, PartialEq)] +pub struct Script<'s, 'a>( + pub Vec>, + pub Option>>, pub TokenRef<'s>, ); -impl<'s> AstWalker<'s> for Script<'s> { +impl<'s, 'a> AstWalker<'s> for Script<'s, 'a> { fn collect_diagnostics(&mut self, collector: &mut DiagnosticsCollector<'_, '_>) { for statement in &mut self.0 { statement.collect_diagnostics(collector); @@ -33,8 +33,8 @@ impl<'s> AstWalker<'s> for Script<'s> { } } -impl<'s> Deref for Script<'s> { - type Target = Vec>; +impl<'s, 'a> Deref for Script<'s, 'a> { + type Target = Vec>; fn deref(&self) -> &Self::Target { &self.0 diff --git a/crates/core/src/parser/scripts.rs b/crates/core/src/parser/scripts.rs index 1d82c537..9f260ad7 100644 --- a/crates/core/src/parser/scripts.rs +++ b/crates/core/src/parser/scripts.rs @@ -10,15 +10,15 @@ use super::{ statements::{semicolon, statement}, }; -pub fn script<'s>(i: &mut Input<'s>) -> Result> { +pub fn script<'s, 'a>(arena: &'a AstArena, i: &mut Input<'s>) -> Result> { let mut statements = vec![]; - let exp: Option; + let exp: Option>; let eof: Option<&Token>; loop { - let s: Vec<_> = repeat(0.., statement).parse_next(i)?; + let s: Vec<_> = repeat(0.., |i: &mut Input<'s>| statement(arena, i)).parse_next(i)?; let s_empty = s.is_empty(); statements.extend(s); - let e = opt(expression).parse_next(i)?; + let e = opt(|i: &mut Input<'s>| expression(arena, i)).parse_next(i)?; let next = peek(opt(any)).parse_next(i)?; if let Some(next) = next { if next.is_eof() { @@ -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(arena.alloc(e), 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(arena, statements, exp); 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..4cba1fd5 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)] -pub enum Statement<'s> { +#[derive(Debug, PartialEq, strum::EnumIs)] +pub enum Statement<'s, 'a> { /// `';'` /// /// An empty statement. Empty(TokenRef<'s>), /// `expression ';'` - Expression(Box>, TokenRef<'s>), + Expression(ABox<'a, Expression<'s, 'a>>, 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(ABox<'a, Expression<'s, 'a>>), /// `'pub'? 'mod' identifier block_expression_no_expr` Module( Option>, TokenRef<'s>, TokenRef<'s>, - Box>, + ABox<'a, Expression<'s, 'a>>, ), /// `'pub'? 'let' pattern '=' expression ';'` Bind( Option>, TokenRef<'s>, - Box>, + ABox<'a, Pattern<'s, 'a>>, TokenRef<'s>, - Box>, + ABox<'a, Expression<'s, 'a>>, TokenRef<'s>, ), /// `pattern_rebind '=' expression ';'` Rebind( - Box>, + ABox<'a, Pattern<'s, 'a>>, TokenRef<'s>, - Box>, + ABox<'a, Expression<'s, 'a>>, TokenRef<'s>, ), /// `'pub'? 'const' @id '=' expression ';'` @@ -41,7 +41,7 @@ pub enum Statement<'s> { TokenRef<'s>, TokenRef<'s>, TokenRef<'s>, - Box>, + ABox<'a, Expression<'s, 'a>>, 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>, + ABox<'a, Expression<'s, 'a>>, TokenRef<'s>, - Box>, + ABox<'a, Expression<'s, 'a>>, TokenRef<'s>, ), /// `'pub'? 'fn' identifier (parameters) block_expression` @@ -72,17 +72,25 @@ pub enum Statement<'s> { Option>, TokenRef<'s>, TokenRef<'s>, - Option>, - Box>, + Option>, + ABox<'a, Expression<'s, 'a>>, ), /// `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. @@ -92,7 +100,7 @@ pub enum Statement<'s> { }, } -impl<'s> Statement<'s> { +impl<'s, 'a> Statement<'s, 'a> { pub(crate) fn unknown>>>(tokens: T, error: DiagnosticCode) -> Self { let tokens = tokens.into(); assert!(!tokens.is_empty()); @@ -126,7 +134,7 @@ impl<'s> Statement<'s> { } } -impl<'s> AstWalker<'s> for Statement<'s> { +impl<'s, 'a> AstWalker<'s> for Statement<'s, 'a> { fn collect_diagnostics(&mut self, collector: &mut DiagnosticsCollector<'_, '_>) { use Statement::*; match self { diff --git a/crates/core/src/parser/statements.rs b/crates/core/src/parser/statements.rs index 2403cc1c..73c1891a 100644 --- a/crates/core/src/parser/statements.rs +++ b/crates/core/src/parser/statements.rs @@ -16,19 +16,19 @@ pub(super) fn semicolon<'s>(i: &mut Input<'s>) -> Result> { token_or_insert(Operator::Semicolon, DiagnosticCode::MissingSemicolon).parse_next(i) } -fn empty_statement<'s>(i: &mut Input<'s>) -> Result> { +fn empty_statement<'s, 'a>(_arena: &'a AstArena, i: &mut Input<'s>) -> Result> { token(Operator::Semicolon) .map(Statement::Empty) .parse_next(i) } -fn fn_statement<'s>(i: &mut Input<'s>) -> Result> { +fn fn_statement<'s, 'a>(arena: &'a AstArena, i: &mut Input<'s>) -> Result> { ( opt(token(Keyword::Pub)), token(Keyword::Fn), opt(variable_token(false, false)), - parameter_list, - block_expression.map(Box::new), + |i: &mut Input<'s>| parameter_list(arena, i), + (|i: &mut Input<'s>| block_expression(arena, i)).map(|e| arena.alloc(e)), ) .map(|(kw_pub, kw_fn, name, params, body)| { let mut name = name.unwrap_or_else(|| { @@ -47,51 +47,54 @@ fn fn_statement<'s>(i: &mut Input<'s>) -> Result> { .parse_next(i) } -fn return_statement<'s>(i: &mut Input<'s>) -> Result> { +fn return_statement<'s, 'a>(arena: &'a AstArena, i: &mut Input<'s>) -> Result> { seq!(Statement::Return( token(Keyword::Return), - opt(expression.map(Box::new)), + opt((|i: &mut Input<'s>| expression(arena, i)).map(|e| arena.alloc(e))), semicolon, )) .parse_next(i) } -fn break_statement<'s>(i: &mut Input<'s>) -> Result> { +fn break_statement<'s, 'a>(arena: &'a AstArena, i: &mut Input<'s>) -> Result> { seq!(Statement::Break( token(Keyword::Break), - opt(expression.map(Box::new)), + opt((|i: &mut Input<'s>| expression(arena, i)).map(|e| arena.alloc(e))), semicolon, )) .parse_next(i) } -fn continue_statement<'s>(i: &mut Input<'s>) -> Result> { +fn continue_statement<'s, 'a>( + _arena: &'a AstArena, + i: &mut Input<'s>, +) -> Result> { seq!(Statement::Continue(token(Keyword::Continue), semicolon,)).parse_next(i) } -fn bind_statement<'s>(i: &mut Input<'s>) -> Result> { +fn bind_statement<'s, 'a>(arena: &'a AstArena, 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), + pattern_or_insert(arena, false, |t| *t == Operator::Assign).map(|p| arena.alloc(p)), token_or_insert(Operator::Assign, DiagnosticCode::MissingBindOperator), - expression_or_insert(|t| *t == Operator::Semicolon).map(Box::new), + expression_or_insert(arena, |t| *t == Operator::Semicolon).map(|e| arena.alloc(e)), semicolon, )) .parse_next(i) } -fn rebind_statement<'s>(i: &mut Input<'s>) -> Result> { +fn rebind_statement<'s, 'a>(arena: &'a AstArena, i: &mut Input<'s>) -> Result> { seq!(Statement::Rebind( - pattern_or_insert(true, |t| *t == Operator::Assign).map(Box::new), + pattern_or_insert(arena, true, |t| *t == Operator::Assign).map(|p| arena.alloc(p)), token(Operator::Assign), - expression_or_insert(|t| *t == Operator::Semicolon).map(Box::new), + expression_or_insert(arena, |t| *t == Operator::Semicolon).map(|e| arena.alloc(e)), semicolon, )) .parse_next(i) } -fn const_statement<'s>(i: &mut Input<'s>) -> Result> { +fn const_statement<'s, 'a>(arena: &'a AstArena, i: &mut Input<'s>) -> Result> { seq!(Statement::Const( opt(token(Keyword::Pub)), token(Keyword::Const), @@ -102,7 +105,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), + expression_or_insert(arena, |t| *t == Operator::Semicolon).map(|e| arena.alloc(e)), semicolon, )) .parse_next(i) @@ -119,21 +122,24 @@ fn insert_semicolon<'s>(i: &mut Input<'s>) -> Result<()> { .parse_next(i) } -fn assign_or_expression_statement<'s>(i: &mut Input<'s>) -> Result> { +fn assign_or_expression_statement<'s, 'a>( + arena: &'a AstArena, + i: &mut Input<'s>, +) -> Result> { fn is_assign_op(t: &Token<'_>) -> bool { matches!(t.kind, TokenKind::Operator(t) if t == Operator::Assign || t.is_compound()) } // Common expr of expr1 = expr2; and expr1; - let expr1 = expression_or_insert(is_assign_op) - .map(Box::new) + let expr1 = expression_or_insert(arena, is_assign_op) + .map(|e| arena.alloc(e)) .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) + let expr2 = expression_or_insert(arena, |t| *t == Operator::Semicolon) + .map(|e| arena.alloc(e)) .parse_next(i)?; let semi = semicolon.parse_next(i)?; return Ok(Statement::Assign( @@ -150,7 +156,7 @@ fn assign_or_expression_statement<'s>(i: &mut Input<'s>) -> Result Ok(Statement::Expression(expr1, semi)) } -fn unknown_statement<'s>(i: &mut Input<'s>) -> Result> { +fn unknown_statement<'s, 'a>(_arena: &'a AstArena, i: &mut Input<'s>) -> Result> { fail.map(|t: &[Token<'s>]| { Statement::unknown( t.iter().map(TokenRef::borrow).collect::>(), @@ -160,12 +166,12 @@ fn unknown_statement<'s>(i: &mut Input<'s>) -> Result> { .parse_next(i) } -fn mod_statement<'s>(i: &mut Input<'s>) -> Result> { +fn mod_statement<'s, 'a>(arena: &'a AstArena, i: &mut Input<'s>) -> Result> { ( opt(token(Keyword::Pub)), token(Keyword::Mod), opt(variable_token(false, false)), - block_expression_no_expr.map(Box::new), + (|i: &mut Input<'s>| block_expression_no_expr(arena, i)).map(|e| arena.alloc(e)), ) .map(|(kw_pub, kw_mod, name, body)| { let name = name.unwrap_or_else(|| { @@ -181,29 +187,32 @@ fn mod_statement<'s>(i: &mut Input<'s>) -> Result> { .parse_next(i) } -pub(super) fn statement<'s>(i: &mut Input<'s>) -> Result> { +pub(super) fn statement<'s, 'a>( + arena: &'a AstArena, + 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 => (|i: &mut Input<'s>| block_expression(arena, i)).map(|e| arena.alloc(e)).map(Statement::BlockExpression), + t if *t == Keyword::If => (|i: &mut Input<'s>| if_expression(arena, i)).map(|e| arena.alloc(e)).map(Statement::BlockExpression), + t if *t == Keyword::Loop => (|i: &mut Input<'s>| loop_expression(arena, i)).map(|e| arena.alloc(e)).map(Statement::BlockExpression), + t if *t == Keyword::While => (|i: &mut Input<'s>| while_expression(arena, i)).map(|e| arena.alloc(e)).map(Statement::BlockExpression), + t if *t == Keyword::Match => (|i: &mut Input<'s>| match_expression(arena, i)).map(|e| arena.alloc(e)).map(Statement::BlockExpression), + t if *t == Keyword::For => (|i: &mut Input<'s>| for_in_expression(arena, i)).map(|e| arena.alloc(e)).map(Statement::BlockExpression), - t if *t == Keyword::Return => return_statement, - t if *t == Keyword::Break => break_statement, - t if *t == Keyword::Continue => continue_statement, + t if *t == Keyword::Return => |i: &mut Input<'s>| return_statement(arena, i), + t if *t == Keyword::Break => |i: &mut Input<'s>| break_statement(arena, i), + t if *t == Keyword::Continue => |i: &mut Input<'s>| continue_statement(arena, i), - t if *t == Operator::Semicolon => empty_statement, + t if *t == Operator::Semicolon => |i: &mut Input<'s>| empty_statement(arena, i), &Token{..} => alt(( - mod_statement, - fn_statement, - const_statement, - bind_statement, - rebind_statement, - assign_or_expression_statement, - unknown_statement, + |i: &mut Input<'s>| mod_statement(arena, i), + |i: &mut Input<'s>| fn_statement(arena, i), + |i: &mut Input<'s>| const_statement(arena, i), + |i: &mut Input<'s>| bind_statement(arena, i), + |i: &mut Input<'s>| rebind_statement(arena, i), + |i: &mut Input<'s>| assign_or_expression_statement(arena, i), + |i: &mut Input<'s>| unknown_statement(arena, i), )), } .parse_next(i) diff --git a/crates/wasm/src/monaco.rs b/crates/wasm/src/monaco.rs index 9021d49a..f2ba9a81 100644 --- a/crates/wasm/src/monaco.rs +++ b/crates/wasm/src/monaco.rs @@ -1,7 +1,8 @@ use std::pin::Pin; use mira_core::{ - Compiler, Config, Script, SourceDiagnostic, diagnostic::encode_diagnostics, lexer::Token, + AstArena, Compiler, Config, Script, SourceDiagnostic, diagnostic::encode_diagnostics, + lexer::Token, }; use wasm_bindgen::prelude::*; @@ -12,7 +13,9 @@ pub struct MonacoCompiler { has_parse_error: bool, diagnostics: Vec, tokens: Pin]>>, - script: Option>, + script: Option>, + // Must be declared after `script` so the script is dropped before the arena. + arena: Option, } #[wasm_bindgen] @@ -26,6 +29,7 @@ impl MonacoCompiler { has_parse_error: false, tokens: Box::pin([]), script: None, + arena: None, } } @@ -38,16 +42,26 @@ impl MonacoCompiler { }; let config: &'static Config = unsafe { &*(&self.config as *const Config) }; let mut compiler = Compiler::new(input, config); + // Drop the old script and arena before creating a new one. + self.script = None; + self.arena = None; 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) { + self.arena = Some(AstArena::new()); + // Safety: the arena outlives the script — `script` is always dropped + // (set to `None`) before `arena` is replaced or dropped. + let arena: &'static AstArena = + unsafe { &*(self.arena.as_ref().unwrap() as *const AstArena) }; + if let Some(script) = compiler.parse(arena, tokens) { self.script = Some(script); self.diagnostics = compiler.diagnostics_collector.drain(..).collect(); self.has_parse_error = self.diagnostics.iter().any(|d| d.is_error()); return true; } + // Clear arena on parse failure to avoid retaining partial AST nodes. + self.arena = None; } self.diagnostics = compiler.diagnostics_collector.drain(..).collect(); self.has_parse_error = self.diagnostics.iter().any(|d| d.is_error());