Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ performance loop below is the contract.
- Burndown recorder: `./scripts/test262-burndown.sh --report <dir> | --entry <file>`
- Performance inner loop (counter screen, symbol sizes, optional trace):
`./scripts/perf-loop.sh --plan tasks/performance-units/<unit>.json`
- Diagnostic A/B, front-end cost, executor placement:
`python3 -m tools.benchmark.screen --candidate B --base A --case external`,
`python3 -m tools.benchmark.front_end --binary B --reference <ng>`,
`./scripts/layout-scan.sh --offsets 0x0,0x200` (see docs/performance-workflow.md)
- Formal local comparison (feeds queue/decide):
`./scripts/perf-compare.sh --base <sha> --output-dir target/comparison/<run>`
- Source size report: `./scripts/source-size-report.sh [limit] [--vendor]`
Expand Down
34 changes: 20 additions & 14 deletions crates/qjs-cli/hot-functions.order
Original file line number Diff line number Diff line change
Expand Up @@ -11,43 +11,51 @@
# budget 0x700 __RNvMNtNtNtCs9nYd1Hk1rek_11qjs_runtime5value6object10slot_readsNtB4_9ObjectRef22own_data_property_read
# budget 0xa00 __RNvMs6_NtNtCs9nYd1Hk1rek_11qjs_runtime5value6objectNtB5_9ObjectRef32write_existing_own_data_property
# budget 0x300 __RNvNtNtNtCs9nYd1Hk1rek_11qjs_runtime8bytecode10typed_loop7execute16boxed_truthiness
# pinned: typed-loop executor at 0x0, interpreter's at 0xc40 mod 4 KiB (python3 -m tools.benchmark.layout_pin), head 38
# budget 0x300 __RNvNtNtNtCs9nYd1Hk1rek_11qjs_runtime8bytecode10typed_loop7execute14boxed_equality
# budget 0x500 __RNvNtNtNtCs9nYd1Hk1rek_11qjs_runtime8bytecode10typed_loop7execute9get_named
# pinned: typed-loop executor at 0x200, interpreter's at 0xc40 mod 4 KiB (python3 -m tools.benchmark.layout_pin), head 44
__RNvMsz_NtNtNtCs1OjIl8oxbrv_5alloc11collections5btree3mapINtB5_8IntoIteryNtNtNtCscUtGwbhD4WH_5gimli4read6abbrev12AbbreviationE10dying_nextCsg55jX0GwzBC_3std
__RNvXs7_NtNtCsg55jX0GwzBC_3std2io5errorNtB5_5ErrorNtNtCsl8K0bEFm1U0_4core3fmt7Display3fmt
__RINvNtNtNtCs9nYd1Hk1rek_11qjs_runtime8bytecode10typed_loop7execute3runNtNtB6_2vm2VmEB8_
__RNvNvNtCsg55jX0GwzBC_3std2fs4read5inner
__RNvNtNtNtCsg55jX0GwzBC_3std3sys3env4unix13env_read_lock
__RNvMs8_NtCsg55jX0GwzBC_3std4pathNtB5_10Components7as_path
__RNvMs0_NtNtCscUtGwbhD4WH_5gimli4read5dwarfINtB5_5DwarfINtNtB7_12endian_slice11EndianSliceNtNtB9_9endianity12LittleEndianEE11attr_stringCsg55jX0GwzBC_3std
__RINvNtNtNtCs9nYd1Hk1rek_11qjs_runtime8bytecode10typed_loop7execute3runNtNtNtNtB6_10compact_fn4wide10loop_frame13WideLoopFrameEB8_
__RNvMs_NtNtNtCsg55jX0GwzBC_3std2io8buffered9bufwriterINtB4_9BufWriterNtNtB8_5stdio9StdoutRawE9flush_bufBa_
__RNvYINtNvNtCsg55jX0GwzBC_3std2io17default_write_fmt7AdapterNtNtB7_5stdio10StderrLockENtNtCsl8K0bEFm1U0_4core3fmt5Write10write_charB9_
__RINvNtNtNtCs9nYd1Hk1rek_11qjs_runtime8bytecode10typed_loop7execute18try_run_typed_loopNtNtNtNtB6_10compact_fn4wide10loop_frame13WideLoopFrameEB8_
__RNvXs2_NtNtCsg55jX0GwzBC_3std4hash6randomNtB5_13DefaultHasherNtNtCsl8K0bEFm1U0_4core4hash6Hasher5write
__RNvYINtNvNtCsg55jX0GwzBC_3std2io17default_write_fmt7AdapterINtNtCs1OjIl8oxbrv_5alloc3vec3VechEENtNtCsl8K0bEFm1U0_4core3fmt5Write10write_charB9_
__RNvYNtNtNtNtCsg55jX0GwzBC_3std3sys5stdio4unix6StderrNtNtBa_2io5Write9write_allBa_
__RNvNtNtNtCs9nYd1Hk1rek_11qjs_runtime8bytecode10typed_loop7execute16get_named_object
__RNvMs8_NtCsg55jX0GwzBC_3std4pathNtB5_10Components7as_path
__RNvNvNtCsg55jX0GwzBC_3std2fs4read5inner
__RNvMs4_NtNtCs9nYd1Hk1rek_11qjs_runtime5value5arrayNtB5_8ArrayRef24direct_dense_index_value
__RNvMsn_NtCs1OjIl8oxbrv_5alloc4syncINtB5_3ArcINtNtNtCscUtGwbhD4WH_5gimli4read5dwarf5DwarfINtNtBL_12endian_slice11EndianSliceNtNtBN_9endianity12LittleEndianEEE9drop_slowCsg55jX0GwzBC_3std
__RNvXs7_NtNtCsg55jX0GwzBC_3std2io5stdioNtB5_9StdinLockNtB7_7BufRead9read_line
__RNvNtNtNtCs9nYd1Hk1rek_11qjs_runtime8bytecode10typed_loop7execute9call_leaf
__RNvXs_NvNtCsg55jX0GwzBC_3std9panicking13panic_handlerNtB4_19FormatStringPayloadNtNtCsl8K0bEFm1U0_4core5panic12PanicPayload3get
__RNvMs_NtNtNtCsg55jX0GwzBC_3std2io8buffered9bufwriterINtB4_9BufWriterNtNtB8_5stdio9StdoutRawE14write_all_coldBa_
__RNvYINtNtNtCscUtGwbhD4WH_5gimli4read12endian_slice11EndianSliceNtNtB9_9endianity12LittleEndianENtNtB7_6reader6Reader17read_sized_offsetCsg55jX0GwzBC_3std
__RNvNtNtCs9nYd1Hk1rek_11qjs_runtime8bytecode15vm_numeric_leaf21try_eval_numeric_leaf
__RNvXNvNtCsg55jX0GwzBC_3std2io17default_write_fmtINtB2_7AdapterNtNtNtNtB6_3sys5stdio4unix6StderrENtNtCsl8K0bEFm1U0_4core3fmt5Write9write_strB6_
__RNvMs3_NtNtCs9nYd1Hk1rek_11qjs_runtime5value5arrayNtB5_9ArrayData21has_property_at_index
__RNvYINtNvNtCsg55jX0GwzBC_3std2io17default_write_fmt7AdapterINtNtCs1OjIl8oxbrv_5alloc3vec3VechEENtNtCsl8K0bEFm1U0_4core3fmt5Write10write_charB9_
__RNvXso_NtNtCsg55jX0GwzBC_3std2io5stdioRNtB5_6StderrNtB7_5Write9write_fmt
__RNvNtNtCsg55jX0GwzBC_3std2io5stdio7__eprint
__RNvNtNtCs9nYd1Hk1rek_11qjs_runtime8bytecode15vm_numeric_leaf20direct_number_binary
__RNvXso_NtNtCsg55jX0GwzBC_3std2io5stdioRNtB5_6StderrNtB7_5Write9write_fmt
__RNvXse_NtNtCsg55jX0GwzBC_3std2io5stdioRNtB5_6StdoutNtB7_5Write9write_fmt
__RNvNtNtNtCs9nYd1Hk1rek_11qjs_runtime8bytecode10typed_loop7execute22ordinary_data_property
__RNvMsn_NtCs1OjIl8oxbrv_5alloc4syncINtB5_3ArcNtNtNtNtCsg55jX0GwzBC_3std3sys2fs4unix12InnerReadDirE9drop_slowBO_
__RNvXs1g_NtCsl8K0bEFm1U0_4core3fmtRINtNtCs1OjIl8oxbrv_5alloc3vec3VechENtB6_5Debug3fmtCsg55jX0GwzBC_3std
__RNvXs_NvNtCsg55jX0GwzBC_3std9panicking13panic_handlerNtB4_19FormatStringPayloadNtNtCsl8K0bEFm1U0_4core5panic12PanicPayload3get
__RNvMs_NtNtNtCsg55jX0GwzBC_3std2io8buffered9bufwriterINtB4_9BufWriterNtNtB8_5stdio9StdoutRawE14write_all_coldBa_
__RNvMNtNtNtCs9nYd1Hk1rek_11qjs_runtime5value6object10slot_readsNtB4_9ObjectRef22own_data_property_read
__RNvMs1_NtNtNtNtCsg55jX0GwzBC_3std3sys4sync6rwlock5queueNtB5_6RwLock21read_unlock_contended
__RNvMs1_NtNtNtNtCsg55jX0GwzBC_3std3sys4sync6rwlock5queueNtB5_6RwLock16unlock_contended
__RNvMs6_NtNtCs9nYd1Hk1rek_11qjs_runtime5value6objectNtB5_9ObjectRef32write_existing_own_data_property
__RNvNtNtNtCsg55jX0GwzBC_3std12backtrace_rs9symbolize5gimli4mmap
__RNvNtNtNtCs9nYd1Hk1rek_11qjs_runtime8bytecode10typed_loop7execute16boxed_truthiness
__RNvXse_NtNtCsg55jX0GwzBC_3std2io5stdioRNtB5_6StdoutNtB7_5Write9write_fmt
__RNvYNtNtNtNtCsg55jX0GwzBC_3std3sys5stdio4unix6StderrNtNtBa_2io5Write9write_allBa_
__RNvNtNtNtNtCsg55jX0GwzBC_3std3sys2io5error4unix17decode_error_kind
__RNvXNvNtCsg55jX0GwzBC_3std2io17default_write_fmtINtB2_7AdapterINtNtB4_6cursor6CursorQShEENtNtCsl8K0bEFm1U0_4core3fmt5Write9write_strB6_
__RNvNtNtNtCs9nYd1Hk1rek_11qjs_runtime8bytecode10typed_loop7execute14boxed_equality
__RNvMsn_NtCs1OjIl8oxbrv_5alloc4syncINtB5_3ArcNtNtNtNtCsg55jX0GwzBC_3std3sys2fs4unix12InnerReadDirE9drop_slowBO_
__RNvYINtNvNtCsg55jX0GwzBC_3std2io17default_write_fmt7AdapterNtNtNtNtB9_3sys5stdio4unix6StderrENtNtCsl8K0bEFm1U0_4core3fmt5Write10write_charB9_
__RNvNtNtNtCs9nYd1Hk1rek_11qjs_runtime8bytecode10typed_loop7execute9get_named
__RNvNtNtCsg55jX0GwzBC_3std3sys2fs8read_dir
__RNvXs1_NtCs9nYd1Hk1rek_11qjs_runtime5valueNtB5_5ValueNtNtCsl8K0bEFm1U0_4core5clone5Clone5clone
__RINvNtCsl8K0bEFm1U0_4core3ptr13drop_in_placeNtNtCs9nYd1Hk1rek_11qjs_runtime5value5ValueEBK_
__RNvNtNtCs9nYd1Hk1rek_11qjs_runtime8function4call25call_direct_leaf_function
Expand Down Expand Up @@ -98,7 +106,6 @@ __RINvMs4_NtNtCs9nYd1Hk1rek_11qjs_runtime5value5arrayNtB6_8ArrayRef28with_dense_
__RINvMs4_NtNtCs9nYd1Hk1rek_11qjs_runtime5value5arrayNtB6_8ArrayRef37with_distinct_dense_writable_elementsNtNtNtNtNtBa_8bytecode24vm_numeric_mutation_loop5dense6nested13RegionOutcomeNCNvMs0_B1G_NtB1G_15NestedDensePlan7try_runs0_0EBa_
__RNvNtNtNtCs9nYd1Hk1rek_11qjs_runtime8bytecode10compact_fn8property14get_prop_named
__RNvNtNtNtNtNtCs9nYd1Hk1rek_11qjs_runtime8bytecode24vm_numeric_mutation_loop5dense6legacy11typed_array7try_run
__RNvNtNtNtCs9nYd1Hk1rek_11qjs_runtime8bytecode10typed_loop7execute9get_named
__RNCNvMs7_NtNtNtCs9nYd1Hk1rek_11qjs_runtime8bytecode24vm_numeric_mutation_loop5denseNtB7_16DynamicDensePlan24try_run_hole_tail_append0Bd_
__RINvMs4_NtNtCs9nYd1Hk1rek_11qjs_runtime5value5arrayNtB6_8ArrayRef37with_distinct_dense_writable_elementsNtNtNtNtBa_8bytecode24vm_numeric_mutation_loop5dense17DynamicProgramRunNCNvMs7_B1G_NtB1G_16DynamicDensePlan7try_runs6_0EBa_
__RNvMs5_NtNtCs9nYd1Hk1rek_11qjs_runtime8bytecode2vmNtB5_2Vm16construct_callee
Expand Down Expand Up @@ -149,7 +156,6 @@ __RNvNtNtNtNtCs9nYd1Hk1rek_11qjs_runtime8bytecode24vm_numeric_mutation_loop5dens
__RNvNtNtNtCs9nYd1Hk1rek_11qjs_runtime8bytecode10compact_fn8property14set_prop_named
__RNvMs4_NtNtCs9nYd1Hk1rek_11qjs_runtime5value6objectNtB5_15PropertyStorage6insert
__RINvNtNtNtCs9nYd1Hk1rek_11qjs_runtime8bytecode10typed_loop7execute23fill_hole_or_grow_valueNtNtNtNtB6_10compact_fn4wide10loop_frame13WideLoopFrameEB8_
__RNvNtNtNtCs9nYd1Hk1rek_11qjs_runtime8bytecode10typed_loop7execute14boxed_equality
__RNvMs4_NtCs1OjIl8oxbrv_5alloc7raw_vecNtB5_11RawVecInner11finish_growCs9nYd1Hk1rek_11qjs_runtime
__RNCNvMs_NtNtCs9nYd1Hk1rek_11qjs_runtime8bytecode8compilerNtB6_8Compiler24compile_into_with_hoists0Ba_
__RNvMs_NtNtCs9nYd1Hk1rek_11qjs_runtime8bytecode8compilerNtB4_8Compiler12compile_stmt
Expand Down
42 changes: 42 additions & 0 deletions crates/qjs-parser/src/cursor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::{ParseError, Parser};
impl Parser {
pub(crate) fn new(tokens: Vec<Token>, source: String, source_is_wtf16: bool) -> Self {
let closers = matching_closers(&tokens);
let bracket_depths = bracket_depths(&tokens);
Self {
source,
source_is_wtf16,
Expand All @@ -14,6 +15,8 @@ impl Parser {
goal: crate::Goal::Script,
strict: false,
allow_in: true,
no_in_depth: 0,
bracket_depths,
in_method: false,
in_derived_constructor: false,
in_field_initializer: false,
Expand All @@ -30,6 +33,19 @@ impl Parser {
}
}

/// Whether `in` is a relational operator at the cursor (`no_in_depth`).
pub(crate) fn in_allowed(&self) -> bool {
self.allow_in
|| self
.bracket_depths
.get(self.cursor)
.is_some_and(|&depth| depth > self.no_in_depth)
}

pub(crate) fn bracket_depth(&self) -> u32 {
self.bracket_depths.get(self.cursor).copied().unwrap_or(0)
}

pub(crate) fn at(&self, kind: &TokenKind) -> bool {
// Every precedence level asks this about each of its operators for
// every operand, and nearly every answer is no: comparing the variant
Expand Down Expand Up @@ -97,6 +113,32 @@ impl Parser {
pub(crate) const NO_CLOSER: u32 = u32::MAX;

/// The index of the token closing each bracket token, found in one pass.
/// The number of brackets (and template substitutions) open at each token;
/// a closing token counts at the depth it returns to.
fn bracket_depths(tokens: &[Token]) -> Vec<u32> {
let mut depth = 0_u32;
tokens
.iter()
.map(|token| match token.kind {
TokenKind::LeftParen
| TokenKind::LeftBracket
| TokenKind::LeftBrace
| TokenKind::TemplateHead(_) => {
depth += 1;
depth - 1
}
TokenKind::RightParen
| TokenKind::RightBracket
| TokenKind::RightBrace
| TokenKind::TemplateTail(_) => {
depth = depth.saturating_sub(1);
depth
}
_ => depth,
})
.collect()
}

fn matching_closers(tokens: &[Token]) -> Vec<u32> {
let mut closers = vec![NO_CLOSER; tokens.len()];
let mut open: Vec<usize> = Vec::new();
Expand Down
5 changes: 3 additions & 2 deletions crates/qjs-parser/src/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,11 @@ impl Parser {
}

pub(crate) fn expression_no_in(&mut self) -> Result<Expr, ParseError> {
let previous = self.allow_in;
let previous = (self.allow_in, self.no_in_depth);
self.allow_in = false;
self.no_in_depth = self.bracket_depth();
let result = self.expression();
self.allow_in = previous;
(self.allow_in, self.no_in_depth) = previous;
result
}

Expand Down
177 changes: 61 additions & 116 deletions crates/qjs-parser/src/expression/binary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@ use qjs_lexer::TokenKind;

use crate::{ParseError, Parser};

/// Binary precedence levels, loosest first; the levels between these are
/// `&&`, `|`, `^`, `&` after `||`, and `+ -`, `* / %` after the shifts.
const LOGICAL_OR: u8 = 1;
const EQUALITY: u8 = 6;
const RELATIONAL: u8 = 7;
const SHIFT: u8 = 8;

impl Parser {
pub(crate) fn nullish_coalescing(&mut self) -> Result<Expr, ParseError> {
let mut left_start = self.cursor;
Expand Down Expand Up @@ -45,111 +52,69 @@ impl Parser {
}

fn logical_or(&mut self) -> Result<Expr, ParseError> {
self.binary_left_assoc(
Self::logical_and,
&[(TokenKind::PipePipe, BinaryOp::LogicalOr)],
)
}

fn logical_and(&mut self) -> Result<Expr, ParseError> {
self.binary_left_assoc(
Self::bitwise_or,
&[(TokenKind::AmpersandAmpersand, BinaryOp::LogicalAnd)],
)
}

fn bitwise_or(&mut self) -> Result<Expr, ParseError> {
self.binary_left_assoc(Self::bitwise_xor, &[(TokenKind::Pipe, BinaryOp::BitwiseOr)])
}

fn bitwise_xor(&mut self) -> Result<Expr, ParseError> {
self.binary_left_assoc(
Self::bitwise_and,
&[(TokenKind::Caret, BinaryOp::BitwiseXor)],
)
}

fn bitwise_and(&mut self) -> Result<Expr, ParseError> {
self.binary_left_assoc(
Self::equality,
&[(TokenKind::Ampersand, BinaryOp::BitwiseAnd)],
)
}

fn equality(&mut self) -> Result<Expr, ParseError> {
self.binary_left_assoc(
Self::comparison,
&[
(TokenKind::EqualEqual, BinaryOp::Eq),
(TokenKind::EqualEqualEqual, BinaryOp::StrictEq),
(TokenKind::BangEqual, BinaryOp::Ne),
(TokenKind::BangEqualEqual, BinaryOp::StrictNe),
],
)
}

fn comparison(&mut self) -> Result<Expr, ParseError> {
if !self.allow_in {
return self.binary_left_assoc(
Self::shift,
&[
(TokenKind::Less, BinaryOp::Lt),
(TokenKind::LessEqual, BinaryOp::Le),
(TokenKind::Greater, BinaryOp::Gt),
(TokenKind::GreaterEqual, BinaryOp::Ge),
(TokenKind::Instanceof, BinaryOp::Instanceof),
],
);
}
self.binary_left_assoc(
Self::shift,
&[
(TokenKind::Less, BinaryOp::Lt),
(TokenKind::LessEqual, BinaryOp::Le),
(TokenKind::Greater, BinaryOp::Gt),
(TokenKind::GreaterEqual, BinaryOp::Ge),
(TokenKind::In, BinaryOp::In),
(TokenKind::Instanceof, BinaryOp::Instanceof),
],
)
self.binary_at_least(LOGICAL_OR)
}

/// Parses a `ShiftExpression`, used as the right operand of a `#x in obj`
/// ergonomic brand check.
pub(crate) fn shift_expression(&mut self) -> Result<Expr, ParseError> {
self.shift()
self.binary_at_least(SHIFT)
}

fn shift(&mut self) -> Result<Expr, ParseError> {
self.binary_left_assoc(
Self::additive,
&[
(TokenKind::LessLess, BinaryOp::Shl),
(TokenKind::GreaterGreater, BinaryOp::Shr),
(TokenKind::GreaterGreaterGreater, BinaryOp::UShr),
],
)
}

fn additive(&mut self) -> Result<Expr, ParseError> {
self.binary_left_assoc(
Self::multiplicative,
&[
(TokenKind::Plus, BinaryOp::Add),
(TokenKind::Minus, BinaryOp::Sub),
],
)
/// The left-associative binary levels from `||` down to `*`, by
/// precedence climbing: an operand is parsed once and each operator
/// binds by its level. The grammar's one-function-per-level descent
/// built the same trees but passed every operand up through all ten
/// levels, each returning the expression by value and testing its own
/// operator list -- most of parsing a large script.
fn binary_at_least(&mut self, minimum: u8) -> Result<Expr, ParseError> {
let mut expr = self.exponentiation()?;
while let Some((op, precedence)) = self.binary_operator()
&& precedence >= minimum
{
self.cursor += 1;
let right = self.binary_at_least(precedence + 1)?;
let span = Span::new(expr.span().start, right.span().end);
expr = Expr::Binary {
left: Box::new(expr),
op,
right: Box::new(right),
span,
};
}
Ok(expr)
}

fn multiplicative(&mut self) -> Result<Expr, ParseError> {
self.binary_left_assoc(
Self::exponentiation,
&[
(TokenKind::Star, BinaryOp::Mul),
(TokenKind::Slash, BinaryOp::Div),
(TokenKind::Percent, BinaryOp::Rem),
],
)
/// The binary operator at the cursor and its precedence; `in` only
/// where the grammar's `[In]` parameter allows it.
fn binary_operator(&self) -> Option<(BinaryOp, u8)> {
let operator = match self.peek()?.kind {
TokenKind::PipePipe => (BinaryOp::LogicalOr, LOGICAL_OR),
TokenKind::AmpersandAmpersand => (BinaryOp::LogicalAnd, LOGICAL_OR + 1),
TokenKind::Pipe => (BinaryOp::BitwiseOr, LOGICAL_OR + 2),
TokenKind::Caret => (BinaryOp::BitwiseXor, LOGICAL_OR + 3),
TokenKind::Ampersand => (BinaryOp::BitwiseAnd, LOGICAL_OR + 4),
TokenKind::EqualEqual => (BinaryOp::Eq, EQUALITY),
TokenKind::EqualEqualEqual => (BinaryOp::StrictEq, EQUALITY),
TokenKind::BangEqual => (BinaryOp::Ne, EQUALITY),
TokenKind::BangEqualEqual => (BinaryOp::StrictNe, EQUALITY),
TokenKind::Less => (BinaryOp::Lt, RELATIONAL),
TokenKind::LessEqual => (BinaryOp::Le, RELATIONAL),
TokenKind::Greater => (BinaryOp::Gt, RELATIONAL),
TokenKind::GreaterEqual => (BinaryOp::Ge, RELATIONAL),
TokenKind::Instanceof => (BinaryOp::Instanceof, RELATIONAL),
TokenKind::In if self.in_allowed() => (BinaryOp::In, RELATIONAL),
TokenKind::LessLess => (BinaryOp::Shl, SHIFT),
TokenKind::GreaterGreater => (BinaryOp::Shr, SHIFT),
TokenKind::GreaterGreaterGreater => (BinaryOp::UShr, SHIFT),
TokenKind::Plus => (BinaryOp::Add, SHIFT + 1),
TokenKind::Minus => (BinaryOp::Sub, SHIFT + 1),
TokenKind::Star => (BinaryOp::Mul, SHIFT + 2),
TokenKind::Slash => (BinaryOp::Div, SHIFT + 2),
TokenKind::Percent => (BinaryOp::Rem, SHIFT + 2),
_ => return None,
};
Some(operator)
}

fn exponentiation(&mut self) -> Result<Expr, ParseError> {
Expand Down Expand Up @@ -197,26 +162,6 @@ impl Parser {
})
}

fn binary_left_assoc(
&mut self,
next: fn(&mut Self) -> Result<Expr, ParseError>,
operators: &[(TokenKind, BinaryOp)],
) -> Result<Expr, ParseError> {
let mut expr = next(self)?;
while let Some((kind, op)) = operators.iter().find(|(kind, _)| self.at(kind)) {
self.expect(kind)?;
let right = next(self)?;
let span = Span::new(expr.span().start, right.span().end);
expr = Expr::Binary {
left: Box::new(expr),
op: *op,
right: Box::new(right),
span,
};
}
Ok(expr)
}

fn expression_range_is_parenthesized(&self, start: usize, end: usize) -> bool {
let Some(first) = self.tokens.get(start) else {
return false;
Expand Down
Loading
Loading