mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-24 02:31:13 +08:00
feat: 支持 RegExpr 与 Symbol 部分方法
This commit is contained in:
@@ -0,0 +1,235 @@
|
||||
use oxide_compiler::compiler::Compiler;
|
||||
use oxide_types::value::JsValue;
|
||||
use oxide_vm::vm::Vm;
|
||||
|
||||
fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
|
||||
let allocator = oxide_parser::Allocator::default();
|
||||
let program =
|
||||
oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
|
||||
let module = Compiler::new()
|
||||
.compile(&program)
|
||||
.map_err(|e| format!("Compile error: {}", e))?;
|
||||
vm.run(&module)
|
||||
}
|
||||
|
||||
fn to_str(vm: &Vm, val: JsValue) -> String {
|
||||
vm.kernel()
|
||||
.string_forge()
|
||||
.lookup(val.as_string_index())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
// --- RegExp constructor ---
|
||||
|
||||
#[test]
|
||||
fn regexp_constructor_creates_object() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "typeof new RegExp('abc')").unwrap();
|
||||
assert_eq!(to_str(&vm, result), "object");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn regexp_constructor_with_flags() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "new RegExp('abc', 'gi').ignoreCase").unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn regexp_constructor_invalid_pattern_syntax_error() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "new RegExp('[')");
|
||||
assert!(result.is_err() || (result.is_ok() && result.unwrap().is_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn regexp_source_property() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "new RegExp('hello', 'g').source").unwrap();
|
||||
assert_eq!(to_str(&vm, result), "hello");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn regexp_flags_property() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "new RegExp('abc', 'gi').flags").unwrap();
|
||||
assert_eq!(to_str(&vm, result), "gi");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn regexp_global_property() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "new RegExp('abc', 'g').global").unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn regexp_multiline_property() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "new RegExp('abc', 'm').multiline").unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn regexp_last_index_property() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "new RegExp('abc', 'g').lastIndex").unwrap();
|
||||
assert_eq!(result.as_int(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn regexp_dotall_sticky_unicode_default_false() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "new RegExp('abc').dotAll").unwrap();
|
||||
assert!(!result.as_bool());
|
||||
}
|
||||
|
||||
// --- RegExp.prototype.test ---
|
||||
|
||||
#[test]
|
||||
fn regexp_test_basic() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "new RegExp('abc').test('abc')").unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn regexp_test_no_match() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "new RegExp('abc').test('xyz')").unwrap();
|
||||
assert!(!result.as_bool());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn regexp_test_case_insensitive() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "new RegExp('abc', 'i').test('ABC')").unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn regexp_test_global_flag_iteration() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "var re = /a/g; re.test('a')").unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
// --- RegExp.prototype.exec ---
|
||||
|
||||
#[test]
|
||||
fn regexp_exec_basic() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "/hello/.exec('hello world') !== null").unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn regexp_exec_no_match_returns_null() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "/xyz/.exec('hello')").unwrap();
|
||||
assert!(result.is_null());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn regexp_exec_returns_index() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "/ell/.exec('hello')").unwrap();
|
||||
assert!(result.is_object());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn regexp_exec_returns_input() {
|
||||
let mut vm = Vm::new();
|
||||
let m = eval(&mut vm, "/ell/.exec('hello')").unwrap();
|
||||
assert!(m.is_object());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn regexp_exec_global_last_index_advances() {
|
||||
let mut vm = Vm::new();
|
||||
let re = eval(&mut vm, "var re = /a/g; re.exec('aba'); re.lastIndex").unwrap();
|
||||
assert_eq!(re.as_int(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn regexp_exec_global_resets_on_exhaustion() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "var re = /a/g; re.exec('a'); re.exec('a') === null").unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
// --- RegExp literal compilation ---
|
||||
|
||||
#[test]
|
||||
fn regexp_literal_basic() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "/hello/").unwrap();
|
||||
assert!(result.is_object());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn regexp_literal_with_flags() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "/abc/i.test('ABC')").unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn regexp_literal_global_exec() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "var re = /a/g; re.exec('aba'); re.lastIndex").unwrap();
|
||||
assert_eq!(result.as_int(), 1);
|
||||
}
|
||||
|
||||
// --- String integration ---
|
||||
|
||||
#[test]
|
||||
fn string_match_with_regexp() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "'hello world'.match(/ell/)").unwrap();
|
||||
assert!(result.is_object());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn string_replace_with_regexp() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "'hello'.replace(/ell/, 'ipp')").unwrap();
|
||||
assert_eq!(to_str(&vm, result), "hippo");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn string_replace_with_regexp_global() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "'aba'.replace(/a/g, 'c')").unwrap();
|
||||
assert_eq!(to_str(&vm, result), "cbc");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn string_search_with_regexp() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "'hello'.search(/ell/)").unwrap();
|
||||
assert_eq!(result.as_int(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn string_search_not_found() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "'hello'.search(/xyz/)").unwrap();
|
||||
assert_eq!(result.as_int(), -1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn string_split_with_regexp() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "'a,b,c'.split(/,/)").unwrap();
|
||||
assert!(result.is_object());
|
||||
}
|
||||
|
||||
// --- toString ---
|
||||
|
||||
#[test]
|
||||
fn regexp_to_string() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "/abc/gi.toString()").unwrap();
|
||||
assert_eq!(to_str(&vm, result), "/abc/gi");
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
use oxide_compiler::compiler::Compiler;
|
||||
use oxide_types::value::JsValue;
|
||||
use oxide_vm::vm::Vm;
|
||||
|
||||
fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
|
||||
let allocator = oxide_parser::Allocator::default();
|
||||
let program =
|
||||
oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
|
||||
let module = Compiler::new()
|
||||
.compile(&program)
|
||||
.map_err(|e| format!("Compile error: {}", e))?;
|
||||
vm.run(&module)
|
||||
}
|
||||
|
||||
fn to_str(vm: &Vm, val: JsValue) -> String {
|
||||
vm.kernel()
|
||||
.string_forge()
|
||||
.lookup(val.as_string_index())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
// --- Symbol constructor ---
|
||||
|
||||
#[test]
|
||||
fn symbol_creates_value() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "Symbol()").unwrap();
|
||||
assert!(result.is_symbol());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn symbol_with_description() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "Symbol('hello') !== Symbol('hello')").unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn symbol_unique_identity() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "var a = Symbol('x'); var b = Symbol('x'); a === b").unwrap();
|
||||
assert!(!result.as_bool());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn symbol_same_reference_equals() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "var s = Symbol('x'); s === s").unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
// --- typeof Symbol ---
|
||||
|
||||
#[test]
|
||||
fn typeof_symbol_returns_symbol() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "typeof Symbol()").unwrap();
|
||||
assert_eq!(to_str(&vm, result), "symbol");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn typeof_symbol_variable() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "var s = Symbol('test'); typeof s").unwrap();
|
||||
assert_eq!(to_str(&vm, result), "symbol");
|
||||
}
|
||||
|
||||
// --- Symbol well-known properties ---
|
||||
|
||||
#[test]
|
||||
fn symbol_match_exists() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "typeof Symbol.match").unwrap();
|
||||
assert_eq!(to_str(&vm, result), "object");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn symbol_replace_exists() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "typeof Symbol.replace").unwrap();
|
||||
assert_eq!(to_str(&vm, result), "object");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn symbol_search_exists() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "typeof Symbol.search").unwrap();
|
||||
assert_eq!(to_str(&vm, result), "object");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn symbol_split_exists() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "typeof Symbol.split").unwrap();
|
||||
assert_eq!(to_str(&vm, result), "object");
|
||||
}
|
||||
|
||||
// --- Symbol coercion ---
|
||||
|
||||
#[test]
|
||||
fn symbol_is_truthy() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "Symbol() ? true : false").unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
// --- Symbol types ---
|
||||
|
||||
#[test]
|
||||
fn symbol_not_equals_object() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "Symbol() === {}").unwrap();
|
||||
assert!(!result.as_bool());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn symbol_not_equals_string() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "Symbol() === 'symbol'").unwrap();
|
||||
assert!(!result.as_bool());
|
||||
}
|
||||
Reference in New Issue
Block a user