diff --git a/bootstrap/stdlib.ot b/bootstrap/stdlib.ot index e0a34a2..4995615 100644 --- a/bootstrap/stdlib.ot +++ b/bootstrap/stdlib.ot @@ -181,7 +181,7 @@ interface Array { unshift(item: T): number; splice(start: number, deleteCount?: number, ...items: T[]): Array; slice(start?: number, end?: number): Array; - indexOf(item: T): number; + indexOf(item: T, fromIndex?: number): number; includes(item: T): boolean; map(fn: (item: T, index: number) => U): Array; filter(fn: (item: T, index: number) => boolean): Array; diff --git a/src/main.rs b/src/main.rs index 0cb87c1..ec238f3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -123,11 +123,11 @@ fn load_and_run_script( if append { let offset = vm.append_program(bytecode); - println!(" {} ({} ops at offset {})", path, bytecode_len, offset); + eprintln!(" {} ({} ops at offset {})", path, bytecode_len, offset); } else { let path_buf = PathBuf::from(path); vm.load_program_with_path(bytecode, path_buf); - println!(" {} ({} ops)", path, bytecode_len); + eprintln!(" {} ({} ops)", path, bytecode_len); } vm.run_until_halt(); diff --git a/src/vm/mod.rs b/src/vm/mod.rs index 5add6f8..9ff9e01 100644 --- a/src/vm/mod.rs +++ b/src/vm/mod.rs @@ -2349,7 +2349,15 @@ impl VM { self.stack.push(JsValue::String(result)); } "indexOf" => { - // Find substring position + // Pop args in reverse order (last arg on top of stack) + let start_index = if arg_count > 1 { + match self.stack.pop() { + Some(JsValue::Number(n)) if n >= 0.0 => n as usize, + _ => 0, + } + } else { + 0 + }; let search = if arg_count > 0 { match self.stack.pop() { Some(JsValue::String(ss)) => ss, @@ -2359,11 +2367,14 @@ impl VM { } else { String::new() }; - // Pop remaining args - for _ in 1..arg_count { + for _ in 2..arg_count { self.stack.pop(); } - let result = s.find(&search).map(|i| i as f64).unwrap_or(-1.0); + let result = s + .get(start_index..) + .and_then(|sub| sub.find(&search)) + .map(|i| (i + start_index) as f64) + .unwrap_or(-1.0); self.stack.push(JsValue::Number(result)); } "split" => { @@ -2576,6 +2587,15 @@ impl VM { self.stack.push(JsValue::String(result)); } "lastIndexOf" => { + // Pop args in reverse order (last arg on top of stack) + let end_index = if arg_count > 1 { + match self.stack.pop() { + Some(JsValue::Number(n)) if n >= 0.0 => Some(n as usize), + _ => None, + } + } else { + None + }; let search = if arg_count > 0 { match self.stack.pop() { Some(JsValue::String(ss)) => ss, @@ -2585,10 +2605,19 @@ impl VM { } else { String::new() }; - for _ in 1..arg_count { + for _ in 2..arg_count { self.stack.pop(); } - let result = s.rfind(&search).map(|i| i as f64).unwrap_or(-1.0); + let result = match end_index { + Some(end) => { + let end = (end + search.len()).min(s.len()); + s.get(..end) + .and_then(|sub| sub.rfind(&search)) + .map(|i| i as f64) + .unwrap_or(-1.0) + } + None => s.rfind(&search).map(|i| i as f64).unwrap_or(-1.0), + }; self.stack.push(JsValue::Number(result)); } "padStart" => { @@ -2815,47 +2844,78 @@ impl VM { return ExecResult::Continue; } "indexOf" => { + // Pop args in reverse order (last arg on top of stack) + let start_index = if arg_count > 1 { + match self.stack.pop() { + Some(JsValue::Number(n)) if n >= 0.0 => n as usize, + _ => 0, + } + } else { + 0 + }; let search = if arg_count > 0 { self.stack.pop().unwrap_or(JsValue::Undefined) } else { JsValue::Undefined }; - for _ in 1..arg_count { + for _ in 2..arg_count { self.stack.pop(); } - let result = arr.iter().position(|v| match (v, &search) { - (JsValue::Number(a), JsValue::Number(b)) => a == b, - (JsValue::String(a), JsValue::String(b)) => a == b, - (JsValue::Boolean(a), JsValue::Boolean(b)) => a == b, - (JsValue::Null, JsValue::Null) => true, - (JsValue::Undefined, JsValue::Undefined) => true, - (JsValue::Object(a), JsValue::Object(b)) => a == b, - _ => false, - }); + let search_slice = if start_index < arr.len() { + &arr[start_index..] + } else { + &[] as &[JsValue] + }; + let result = + search_slice.iter().position(|v| match (v, &search) { + (JsValue::Number(a), JsValue::Number(b)) => a == b, + (JsValue::String(a), JsValue::String(b)) => a == b, + (JsValue::Boolean(a), JsValue::Boolean(b)) => a == b, + (JsValue::Null, JsValue::Null) => true, + (JsValue::Undefined, JsValue::Undefined) => true, + (JsValue::Object(a), JsValue::Object(b)) => a == b, + _ => false, + }); self.stack.push(JsValue::Number( - result.map(|i| i as f64).unwrap_or(-1.0), + result.map(|i| (i + start_index) as f64).unwrap_or(-1.0), )); self.ip += 1; return ExecResult::Continue; } "lastIndexOf" => { + // Pop args in reverse order (last arg on top of stack) + let from_index = if arg_count > 1 { + match self.stack.pop() { + Some(JsValue::Number(n)) if n >= 0.0 => { + Some(n as usize) + } + _ => None, + } + } else { + None + }; let search = if arg_count > 0 { self.stack.pop().unwrap_or(JsValue::Undefined) } else { JsValue::Undefined }; - for _ in 1..arg_count { + for _ in 2..arg_count { self.stack.pop(); } - let result = arr.iter().rposition(|v| match (v, &search) { - (JsValue::Number(a), JsValue::Number(b)) => a == b, - (JsValue::String(a), JsValue::String(b)) => a == b, - (JsValue::Boolean(a), JsValue::Boolean(b)) => a == b, - (JsValue::Null, JsValue::Null) => true, - (JsValue::Undefined, JsValue::Undefined) => true, - (JsValue::Object(a), JsValue::Object(b)) => a == b, - _ => false, - }); + let end = match from_index { + Some(fi) => (fi + 1).min(arr.len()), + None => arr.len(), + }; + let result = + arr[..end].iter().rposition(|v| match (v, &search) { + (JsValue::Number(a), JsValue::Number(b)) => a == b, + (JsValue::String(a), JsValue::String(b)) => a == b, + (JsValue::Boolean(a), JsValue::Boolean(b)) => a == b, + (JsValue::Null, JsValue::Null) => true, + (JsValue::Undefined, JsValue::Undefined) => true, + (JsValue::Object(a), JsValue::Object(b)) => a == b, + _ => false, + }); self.stack.push(JsValue::Number( result.map(|i| i as f64).unwrap_or(-1.0), ));