Skip to content

Commit 71573f2

Browse files
authored
Merge pull request #1496 from harehare/feat/lang-get-negative-index
✨ feat(lang): support negative index for get on array, string, and markdown
2 parents c01c637 + 2240b98 commit 71573f2

File tree

2 files changed

+44
-8
lines changed

2 files changed

+44
-8
lines changed

crates/mq-lang/src/eval.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1646,7 +1646,7 @@ mod tests {
16461646
use crate::ast::node::{Args, IdentWithToken, Param};
16471647
use crate::error::runtime::RuntimeError;
16481648
use crate::eval::module::error::ModuleError;
1649-
use crate::number::{INFINITE, NAN};
1649+
use crate::number::{INFINITE, NAN, Number};
16501650
use crate::range::Range;
16511651
use crate::{AstExpr, AstNode, DefaultModuleLoader, ModuleLoader, token_alloc};
16521652
use crate::{Token, TokenKind};
@@ -3712,11 +3712,24 @@ mod tests {
37123712
ast_call("get", smallvec![ast_node(ast::Expr::Literal(ast::Literal::Number(5.into())))])
37133713
],
37143714
Ok(vec![RuntimeValue::NONE]))]
3715-
#[case::get_array(vec![RuntimeValue::Array(vec!["test1".to_string().into()])],
3715+
#[case::get_string(vec![RuntimeValue::String("test1".to_string())],
3716+
vec![
3717+
ast_call("get", smallvec![ast_node(ast::Expr::Literal(ast::Literal::Number(Number::new(-1.0))))])
3718+
],
3719+
Ok(vec![RuntimeValue::String("1".to_string())]))]
3720+
#[case::get_array(vec![RuntimeValue::Array(vec!["1".to_string().into()])],
37163721
vec![
37173722
ast_call("get", smallvec![ast_node(ast::Expr::Literal(ast::Literal::Number(2.into())))])
37183723
],
37193724
Ok(vec![RuntimeValue::NONE]))]
3725+
#[case::get_array(vec![RuntimeValue::Array(vec![
3726+
RuntimeValue::String("test1".to_string()),
3727+
RuntimeValue::String("test2".to_string()),
3728+
])],
3729+
vec![
3730+
ast_call("get", smallvec![ast_node(ast::Expr::Literal(ast::Literal::Number(Number::new(-1.0))))])
3731+
],
3732+
Ok(vec![RuntimeValue::String("test2".to_string())]))]
37203733
#[case::get(vec![RuntimeValue::TRUE],
37213734
vec![
37223735
ast_call("get", smallvec![ast_node(ast::Expr::Literal(ast::Literal::Number(0.into())))])

crates/mq-lang/src/eval/builtin.rs

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1913,20 +1913,43 @@ define_builtin!(
19131913
.unwrap_or(RuntimeValue::NONE)),
19141914
[RuntimeValue::Dict(map), RuntimeValue::Symbol(key)] =>
19151915
Ok(map.get_mut(key).map(std::mem::take).unwrap_or(RuntimeValue::NONE)),
1916-
[RuntimeValue::Array(array), RuntimeValue::Number(index)] => Ok(array
1917-
.get_mut(index.value() as usize)
1918-
.map(std::mem::take)
1919-
.unwrap_or(RuntimeValue::NONE)),
1916+
[RuntimeValue::Array(array), RuntimeValue::Number(index)] => {
1917+
let len = array.len();
1918+
let idx = index.value() as isize;
1919+
let real_idx = if idx < 0 {
1920+
(len as isize + idx).max(0) as usize
1921+
} else {
1922+
idx as usize
1923+
};
1924+
Ok(array
1925+
.get_mut(real_idx)
1926+
.map(std::mem::take)
1927+
.unwrap_or(RuntimeValue::NONE))
1928+
}
19201929
[RuntimeValue::String(s), RuntimeValue::Number(n)] => {
1921-
match s.chars().nth(n.value() as usize) {
1930+
let len = s.chars().count();
1931+
let idx = n.value() as isize;
1932+
let real_idx = if idx < 0 {
1933+
(len as isize + idx).max(0) as usize
1934+
} else {
1935+
idx as usize
1936+
};
1937+
match s.chars().nth(real_idx) {
19221938
Some(o) => Ok(o.to_string().into()),
19231939
None => Ok(RuntimeValue::NONE),
19241940
}
19251941
}
19261942
[RuntimeValue::Markdown(node, _), RuntimeValue::Number(i)] => {
1943+
let idx = i.value() as isize;
1944+
let real_idx = if idx < 0 {
1945+
let len = node.value().chars().count();
1946+
(len as isize + idx).max(0) as usize
1947+
} else {
1948+
idx as usize
1949+
};
19271950
Ok(RuntimeValue::Markdown(
19281951
std::mem::replace(node, mq_markdown::Node::Empty),
1929-
Some(runtime_value::Selector::Index(i.value() as usize)),
1952+
Some(runtime_value::Selector::Index(real_idx)),
19301953
))
19311954
}
19321955
[RuntimeValue::None, _] | [_, RuntimeValue::None] => Ok(RuntimeValue::NONE),

0 commit comments

Comments
 (0)