diff --git a/Cargo.lock b/Cargo.lock index c48d0daae..8b98b85e9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6338,6 +6338,7 @@ name = "vectorcraft-js" version = "0.21.0" dependencies = [ "proptest", + "unicode-general-category", ] [[package]] diff --git a/crates/js/Cargo.toml b/crates/js/Cargo.toml index f39ab33b6..0108b50d9 100644 --- a/crates/js/Cargo.toml +++ b/crates/js/Cargo.toml @@ -9,5 +9,8 @@ rust-version.workspace = true [lints] workspace = true +[dependencies] +unicode-general-category = "1" + [dev-dependencies] proptest = { workspace = true } diff --git a/crates/js/src/chars.rs b/crates/js/src/chars.rs index fb6da4f36..75c0b92aa 100644 --- a/crates/js/src/chars.rs +++ b/crates/js/src/chars.rs @@ -1,6 +1,8 @@ //! Character classes of ECMA-262 5.1 section 7: white space, line terminators and the characters //! identifiers are made of. +use unicode_general_category::{GeneralCategory as G, get_general_category}; + /// A line terminator: LF, CR, U+2028 or U+2029 (section 7.3). pub fn is_line_terminator(c: u32) -> bool { matches!(c, 0x0A | 0x0D | 0x2028 | 0x2029) @@ -21,7 +23,9 @@ pub fn is_id_start(c: u32) -> bool { 0..=0x7F => false, // U+2E2F VERTICAL TILDE is a letter (Lm) that Unicode leaves out of identifiers. 0x2E2F => false, - _ => char::from_u32(c).is_some_and(|ch| ch.is_alphabetic() && !is_combining_mark(c)), + _ => category(c).is_some_and(|g| { + matches!(g, G::UppercaseLetter | G::LowercaseLetter | G::TitlecaseLetter | G::ModifierLetter | G::OtherLetter | G::LetterNumber) + }), } } @@ -35,49 +39,13 @@ pub fn is_id_part(c: u32) -> bool { 0x2E2F => false, _ if is_id_start(c) => true, 0..=0x7F => false, - _ => char::from_u32(c).is_some_and(|ch| ch.is_alphanumeric()) || is_combining_mark(c) || is_connector(c), + _ => category(c).is_some_and(|g| matches!(g, G::NonspacingMark | G::SpacingMark | G::DecimalNumber | G::ConnectorPunctuation)), } } -/// The blocks of combining marks (categories Mn and Mc) that scripts are most likely to use. -fn is_combining_mark(c: u32) -> bool { - matches!( - c, - 0x0300..=0x036F - | 0x0483..=0x0487 - | 0x0591..=0x05BD - | 0x05BF - | 0x05C1..=0x05C2 - | 0x05C4..=0x05C5 - | 0x05C7 - | 0x0610..=0x061A - | 0x064B..=0x065F - | 0x0670 - | 0x06D6..=0x06DC - | 0x06DF..=0x06E4 - | 0x06E7..=0x06E8 - | 0x06EA..=0x06ED - | 0x0900..=0x0903 - | 0x093A..=0x093C - | 0x093E..=0x094F - | 0x0951..=0x0957 - | 0x0962..=0x0963 - | 0x0E31 - | 0x0E34..=0x0E3A - | 0x0E47..=0x0E4E - | 0x1AB0..=0x1AFF - | 0x1DC0..=0x1DFF - | 0x20D0..=0x20FF - | 0x302A..=0x302F - | 0x3099..=0x309A - | 0xFE00..=0xFE0F - | 0xFE20..=0xFE2F - ) -} - -/// Connector punctuation (category Pc) other than `_`. -fn is_connector(c: u32) -> bool { - matches!(c, 0x203F | 0x2040 | 0x2054 | 0xFE33 | 0xFE34 | 0xFE4D..=0xFE4F | 0xFF3F) +/// The Unicode general category of a code point; `None` for a lone surrogate. +fn category(c: u32) -> Option { + char::from_u32(c).map(get_general_category) } /// The value of a hexadecimal digit. diff --git a/crates/js/src/parser.rs b/crates/js/src/parser.rs index 17424e42f..d56b75617 100644 --- a/crates/js/src/parser.rs +++ b/crates/js/src/parser.rs @@ -7,6 +7,8 @@ //! stack, and prefix operators, assignment chains, `else if` chains and labels with loops, so long //! runs of them do not nest at all. +use std::collections::{HashMap, HashSet}; + use crate::LineTable; use crate::ast::{ AssignOp, Ast, BinaryOp, CatchClause, Expr, ExprId, ForInTarget, ForInit, Function, FunctionKind, LogicalOp, Name, Program, Property, @@ -18,20 +20,14 @@ use crate::token::{Directive, Keyword, Punct, Span, Token, TokenKind, strict_res type Result = std::result::Result; -/// A label in scope, and whether it labels an iteration statement (`continue` may name only -/// those). -#[derive(Clone, Debug)] -struct Label { - name: String, - is_loop: bool, -} - /// What the code being parsed allows, saved and replaced at each function body. #[derive(Clone, Debug, Default)] struct Context { strict: bool, in_function: bool, - labels: Vec