Skip to content

Commit

Permalink
Redesign Intl API and implement some services (#2478)
Browse files Browse the repository at this point in the history
This Pull Request fixes/closes #1180. (I'll open a tracking issue for the progress)

It changes the following:

- Redesigns the internal API of Intl to (hopefully!) make it easier to implement a service.
- Implements the `Intl.Locale` service. 
- Implements the `Intl.Collator` service.
- Implements the `Intl.ListFormat` service.

On the subject of the failing tests. Some of them are caused by missing locale data in the `icu_testdata` crate; we would need to regenerate that with the missing locales, or vendor a custom default data.

On the other hand, there are some tests that are bugs from the ICU4X crate. The repo https://github.com/jedel1043/icu4x-test262 currently tracks the found bugs when running test262. I'll sync with the ICU4X team to try to fix those.

cc @sffc
  • Loading branch information
jedel1043 committed Dec 26, 2022
1 parent f871697 commit 3bf5de2
Show file tree
Hide file tree
Showing 42 changed files with 6,470 additions and 1,698 deletions.
2,821 changes: 2,601 additions & 220 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ members = [
"boa_wasm",
"boa_examples",
"boa_macros",
"boa_icu_provider",
]

[workspace.package]
Expand All @@ -32,6 +33,7 @@ boa_unicode = { version = "0.16.0", path = "boa_unicode" }
boa_macros = { version = "0.16.0", path = "boa_macros" }
boa_ast = { version = "0.16.0", path = "boa_ast" }
boa_parser = { version = "0.16.0", path = "boa_parser" }
boa_icu_provider = { version = "0.16.0", path = "boa_icu_provider" }

[workspace.metadata.workspaces]
allow_branch = "main"
Expand Down
19 changes: 10 additions & 9 deletions boa_ast/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Boa's **boa_ast** crate implements an ECMAScript abstract syntax tree.
//! Boa's **`boa_ast`** crate implements an ECMAScript abstract syntax tree.
//!
//! # Crate Overview
//! **boa_ast** contains representations of [**Parse Nodes**][grammar] as defined by the ECMAScript
//! **`boa_ast`** contains representations of [**Parse Nodes**][grammar] as defined by the ECMAScript
//! spec. Some `Parse Node`s are not represented by Boa's AST, because a lot of grammar productions
//! are only used to throw [**Early Errors**][early], and don't influence the evaluation of the AST
//! itself.
Expand All @@ -17,13 +17,14 @@
//! Try out the most recent release with Boa's live demo [playground][boa-playground].
//!
//! # Boa Crates
//! - **boa_ast** - Boa's ECMAScript Abstract Syntax Tree.
//! - **boa_engine** - Boa's implementation of ECMAScript builtin objects and execution.
//! - **boa_gc** - Boa's garbage collector
//! - **boa_interner** - Boa's string interner
//! - **boa_parser** - Boa's lexer and parser
//! - **boa_profiler** - Boa's code profiler
//! - **boa_unicode** - Boa's Unicode identifier
//! - **`boa_ast`** - Boa's ECMAScript Abstract Syntax Tree.
//! - **`boa_engine`** - Boa's implementation of ECMAScript builtin objects and execution.
//! - **`boa_gc`** - Boa's garbage collector.
//! - **`boa_interner`** - Boa's string interner.
//! - **`boa_parser`** - Boa's lexer and parser.
//! - **`boa_profiler`** - Boa's code profiler.
//! - **`boa_unicode`** - Boa's Unicode identifier.
//! - **`boa_icu_provider`** - Boa's ICU4X data provider.
//!
//! [grammar]: https://tc39.es/ecma262/#sec-syntactic-grammar
//! [early]: https://tc39.es/ecma262/#sec-static-semantic-rules
Expand Down
2 changes: 1 addition & 1 deletion boa_cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ repository.workspace = true
rust-version.workspace = true

[dependencies]
boa_engine = { workspace = true, features = ["deser", "console", "flowgraph"] }
boa_engine = { workspace = true, features = ["deser", "console", "flowgraph", "intl"] }
boa_ast = { workspace = true, features = ["serde"]}
boa_parser.workspace = true
rustyline = "10.0.0"
Expand Down
30 changes: 21 additions & 9 deletions boa_engine/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,18 @@ rust-version.workspace = true
profiler = ["boa_profiler/profiler"]
deser = ["boa_interner/serde", "boa_ast/serde"]
intl = [
"dep:icu_locale_canonicalizer",
"dep:boa_icu_provider",
"dep:icu_locid_transform",
"dep:icu_locid",
"dep:icu_datetime",
"dep:icu_plurals",
"dep:icu_provider",
"dep:icu_testdata",
"dep:sys-locale"
"dep:icu_provider_adapters",
"dep:icu_calendar",
"dep:icu_collator",
"dep:icu_list",
"dep:writeable",
"dep:sys-locale",
]

fuzz = ["boa_ast/fuzz", "boa_interner/fuzz"]
Expand Down Expand Up @@ -59,12 +64,19 @@ tap = "1.0.1"
sptr = "0.3.2"
static_assertions = "1.1.0"
thiserror = "1.0.38"
icu_locale_canonicalizer = { version = "0.6.0", features = ["serde"], optional = true }
icu_locid = { version = "0.6.0", features = ["serde"], optional = true }
icu_datetime = { version = "0.6.0", features = ["serde"], optional = true }
icu_plurals = { version = "0.6.0", features = ["serde"], optional = true }
icu_provider = { version = "0.6.0", optional = true }
icu_testdata = { version = "0.6.0", optional = true }

# intl deps
boa_icu_provider = { workspace = true, optional = true }
icu_locid_transform = { version = "1.0.0", features = ["serde"], optional = true }
icu_locid = { version = "1.0.0", features = ["serde"], optional = true }
icu_datetime = { version = "1.0.0", features = ["serde", "experimental"], optional = true }
icu_calendar = { version = "1.0.0", optional = true }
icu_collator = { version = "1.0.1", features = ["serde"], optional = true }
icu_plurals = { version = "1.0.0", features = ["serde"], optional = true }
icu_provider = { version = "1.0.1", optional = true }
icu_provider_adapters = { version = "1.0.0", features = ["serde"], optional = true }
icu_list = { version = "1.0.0", features = ["serde"], optional = true }
writeable = { version = "0.5.0", optional = true }
sys-locale = { version = "0.2.3", optional = true }

[dev-dependencies]
Expand Down
68 changes: 68 additions & 0 deletions boa_engine/src/builtins/array/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ pub mod array_iterator;
#[cfg(test)]
mod tests;

use boa_macros::utf16;
use boa_profiler::Profiler;
use tap::{Conv, Pipe};

Expand Down Expand Up @@ -117,6 +118,7 @@ impl BuiltIn for Array {
.method(Self::some, "some", 1)
.method(Self::sort, "sort", 1)
.method(Self::splice, "splice", 2)
.method(Self::to_locale_string, "toLocaleString", 0)
.method(Self::reduce, "reduce", 1)
.method(Self::reduce_right, "reduceRight", 1)
.method(Self::keys, "keys", 0)
Expand Down Expand Up @@ -2027,6 +2029,72 @@ impl Array {
Ok(a.into())
}

/// [`Array.prototype.toLocaleString ( [ locales [ , options ] ] )`][spec].
///
/// Returns a string representing the elements of the array. The elements are converted to
/// strings using their `toLocaleString` methods and these strings are separated by a
/// locale-specific string (such as a comma ",").
///
/// More information:
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma402/#sup-array.prototype.tolocalestring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toLocaleString
pub(crate) fn to_locale_string(
this: &JsValue,
args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
// 1. Let array be ? ToObject(this value).
let array = this.to_object(context)?;
// 2. Let len be ? ToLength(? Get(array, "length")).
let len = array.length_of_array_like(context)?;

// 3. Let separator be the implementation-defined list-separator String value appropriate for the host environment's current locale (such as ", ").
let separator = {
#[cfg(feature = "intl")]
{
// TODO: this should eventually return a locale-sensitive separator.
utf16!(", ")
}

#[cfg(not(feature = "intl"))]
{
utf16!(", ")
}
};

// 4. Let R be the empty String.
let mut r = Vec::new();

// 5. Let k be 0.
// 6. Repeat, while k < len,
for k in 0..len {
// a. If k > 0, then
if k > 0 {
// i. Set R to the string-concatenation of R and separator.
r.extend_from_slice(separator);
}

// b. Let nextElement be ? Get(array, ! ToString(k)).
let next = array.get(k, context)?;

// c. If nextElement is not undefined or null, then
if !next.is_null_or_undefined() {
// i. Let S be ? ToString(? Invoke(nextElement, "toLocaleString", « locales, options »)).
let s = next
.invoke("toLocaleString", args, context)?
.to_string(context)?;

// ii. Set R to the string-concatenation of R and S.
r.extend_from_slice(&s);
}
// d. Increase k by 1.
}
// 7. Return R.
Ok(js_string!(r).into())
}

/// `Array.prototype.splice ( start, [deleteCount[, ...items]] )`
///
/// Splices an array by following
Expand Down
23 changes: 9 additions & 14 deletions boa_engine/src/builtins/console/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,19 +52,16 @@ fn logger(msg: LogMessage, console_state: &Console) {

/// This represents the `console` formatter.
pub fn formatter(data: &[JsValue], context: &mut Context) -> JsResult<String> {
let target = data
.get(0)
.cloned()
.unwrap_or_default()
.to_string(context)?;

match data.len() {
0 => Ok(String::new()),
1 => Ok(target.to_std_string_escaped()),
_ => {
match data {
[] => Ok(String::new()),
[val] => Ok(val.to_string(context)?.to_std_string_escaped()),
data => {
let mut formatted = String::new();
let mut arg_index = 1;
let target = target.to_std_string_escaped();
let target = data
.get_or_undefined(0)
.to_string(context)?
.to_std_string_escaped();
let mut chars = target.chars();
while let Some(c) = chars.next() {
if c == '%' {
Expand Down Expand Up @@ -94,9 +91,7 @@ pub fn formatter(data: &[JsValue], context: &mut Context) -> JsResult<String> {
/* string */
's' => {
let arg = data
.get(arg_index)
.cloned()
.unwrap_or_default()
.get_or_undefined(arg_index)
.to_string(context)?
.to_std_string_escaped();
formatted.push_str(&arg);
Expand Down
Loading

0 comments on commit 3bf5de2

Please sign in to comment.