Berry multiplication between string and int (#23850)
This commit is contained in:
parent
9426df28f9
commit
5a08bc11e3
@ -181,6 +181,19 @@ print(i) # Only i is accessible here
|
|||||||
- `/`: Division
|
- `/`: Division
|
||||||
- `%`: Modulo (remainder)
|
- `%`: Modulo (remainder)
|
||||||
|
|
||||||
|
**String Multiplication**: The `*` operator supports string multiplication when the left operand is a string and the right operand is an integer or boolean:
|
||||||
|
|
||||||
|
```berry
|
||||||
|
"aze" * 3 # "azeazeaze" - repeat string 3 times
|
||||||
|
"aze" * 0 # "" - empty string
|
||||||
|
"aze" * true # "aze" - string if true
|
||||||
|
"aze" * false # "" - empty string if false
|
||||||
|
|
||||||
|
# Common use cases:
|
||||||
|
" " * indent # Create indentation spaces
|
||||||
|
f"{n} time{'s' * bool(n >= 2)}" # Conditional pluralization
|
||||||
|
```
|
||||||
|
|
||||||
#### Relational Operators
|
#### Relational Operators
|
||||||
- `<`: Less than
|
- `<`: Less than
|
||||||
- `<=`: Less than or equal to
|
- `<=`: Less than or equal to
|
||||||
|
|||||||
@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file.
|
|||||||
### Added
|
### Added
|
||||||
- ESP32 ROM SHA Hardware Acceleration to BearSSL (#23819)
|
- ESP32 ROM SHA Hardware Acceleration to BearSSL (#23819)
|
||||||
- Extend state JSON message with functional hostname and ipaddress which could be WiFi or Ethernet
|
- Extend state JSON message with functional hostname and ipaddress which could be WiFi or Ethernet
|
||||||
|
- Berry multiplication between string and int
|
||||||
|
|
||||||
### Breaking Changed
|
### Breaking Changed
|
||||||
|
|
||||||
|
|||||||
@ -77,6 +77,50 @@ bstring* be_strcat(bvm *vm, bstring *s1, bstring *s2)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bstring* be_strmul(bvm *vm, bstring *s1, bint n)
|
||||||
|
{
|
||||||
|
/* Handle edge cases */
|
||||||
|
if (n == 1) {
|
||||||
|
return s1;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t string_len = (size_t)str_len(s1);
|
||||||
|
if ((n <= 0) || (string_len == 0)) {
|
||||||
|
return be_newstrn(vm, "", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for potential overflow */
|
||||||
|
if (n > (bint)(vm->bytesmaxsize / string_len)) {
|
||||||
|
be_raise(vm, "runtime_error", "string multiplication result too large");
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t total_len = string_len * (size_t)n;
|
||||||
|
|
||||||
|
/* Use the same pattern as be_strcat for short vs long strings */
|
||||||
|
if (total_len <= SHORT_STR_MAX_LEN) {
|
||||||
|
char buf[SHORT_STR_MAX_LEN + 1];
|
||||||
|
const char *src = str(s1);
|
||||||
|
char *dst = buf;
|
||||||
|
|
||||||
|
for (bint i = 0; i < n; i++) {
|
||||||
|
memcpy(dst, src, string_len);
|
||||||
|
dst += string_len;
|
||||||
|
}
|
||||||
|
buf[total_len] = '\0';
|
||||||
|
return be_newstrn(vm, buf, total_len);
|
||||||
|
} else {
|
||||||
|
/* Long string */
|
||||||
|
bstring *result = be_newstrn(vm, NULL, total_len);
|
||||||
|
char *dst = (char*)str(result);
|
||||||
|
const char *src = str(s1);
|
||||||
|
|
||||||
|
for (bint i = 0; i < n; i++) {
|
||||||
|
memcpy(dst + i * string_len, src, string_len);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int be_strcmp(bstring *s1, bstring *s2)
|
int be_strcmp(bstring *s1, bstring *s2)
|
||||||
{
|
{
|
||||||
if (be_eqstr(s1, s2)) {
|
if (be_eqstr(s1, s2)) {
|
||||||
|
|||||||
@ -16,6 +16,7 @@ extern "C" {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
bstring* be_strcat(bvm *vm, bstring *s1, bstring *s2);
|
bstring* be_strcat(bvm *vm, bstring *s1, bstring *s2);
|
||||||
|
bstring* be_strmul(bvm *vm, bstring *s1, bint n);
|
||||||
int be_strcmp(bstring *s1, bstring *s2);
|
int be_strcmp(bstring *s1, bstring *s2);
|
||||||
bstring* be_num2str(bvm *vm, bvalue *v);
|
bstring* be_num2str(bvm *vm, bvalue *v);
|
||||||
void be_val2str(bvm *vm, int index);
|
void be_val2str(bvm *vm, int index);
|
||||||
|
|||||||
@ -463,6 +463,25 @@ static void make_range(bvm *vm, bvalue lower, bvalue upper)
|
|||||||
vm->top -= 3;
|
vm->top -= 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void multiply_str(bvm *vm, bvalue *a_value, bvalue *count)
|
||||||
|
{
|
||||||
|
bint n = 0;
|
||||||
|
bstring *result;
|
||||||
|
bstring *str = var_tostr(a_value);
|
||||||
|
|
||||||
|
/* Convert count to integer */
|
||||||
|
if (var_isint(count)) {
|
||||||
|
n = var_toint(count);
|
||||||
|
} else if (var_isbool(count)) {
|
||||||
|
n = var_tobool(count) ? 1 : 0;
|
||||||
|
} else {
|
||||||
|
binop_error(vm, "*", a_value, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
result = be_strmul(vm, str, n);
|
||||||
|
var_setstr(vm->top, result);
|
||||||
|
}
|
||||||
|
|
||||||
static void connect_str(bvm *vm, bstring *a, bvalue *b)
|
static void connect_str(bvm *vm, bstring *a, bvalue *b)
|
||||||
{
|
{
|
||||||
bstring *s;
|
bstring *s;
|
||||||
@ -705,6 +724,10 @@ newframe: /* a new call frame */
|
|||||||
breal x = var2real(a), y = var2real(b);
|
breal x = var2real(a), y = var2real(b);
|
||||||
var_setreal(dst, x * y);
|
var_setreal(dst, x * y);
|
||||||
#endif // CONFIG_IDF_TARGET_ESP32
|
#endif // CONFIG_IDF_TARGET_ESP32
|
||||||
|
} else if (var_isstr(a) && (var_isint(b) || var_isbool(b))) {
|
||||||
|
multiply_str(vm, a, b);
|
||||||
|
reg = vm->reg;
|
||||||
|
*RA() = *vm->top; /* copy result to R(A) */
|
||||||
} else if (var_isinstance(a)) {
|
} else if (var_isinstance(a)) {
|
||||||
ins_binop(vm, "*", ins);
|
ins_binop(vm, "*", ins);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -175,13 +175,6 @@ assert(f"S = {a}" == 'S = foobar{0}')
|
|||||||
assert(f"S = {a:i}" == 'S = 0')
|
assert(f"S = {a:i}" == 'S = 0')
|
||||||
assert(f"{a=}" == 'a=foobar{0}')
|
assert(f"{a=}" == 'a=foobar{0}')
|
||||||
|
|
||||||
# new option for f-strings, '::' encodes for ':'
|
|
||||||
assert(f"{true ? 1 :: 2}" == "1")
|
|
||||||
assert(f"{false ? 1 :: 2}" == "2")
|
|
||||||
assert(f"{false ? 1 :: 2 : i}" == " 2")
|
|
||||||
assert(f"{false ? 1 :: 2:i}" == "2")
|
|
||||||
assert(f"{false ? 1 :: 2:04i}" == "0002")
|
|
||||||
|
|
||||||
# startswith case sensitive
|
# startswith case sensitive
|
||||||
assert(string.startswith("", "") == true)
|
assert(string.startswith("", "") == true)
|
||||||
assert(string.startswith("qwerty", "qw") == true)
|
assert(string.startswith("qwerty", "qw") == true)
|
||||||
@ -220,3 +213,103 @@ assert(string.endswith("qwerty", "tY", true) == true)
|
|||||||
assert(string.endswith("qwerty", "TY", true) == true)
|
assert(string.endswith("qwerty", "TY", true) == true)
|
||||||
assert(string.endswith("qwerty", "tr", true) == false)
|
assert(string.endswith("qwerty", "tr", true) == false)
|
||||||
assert(string.endswith("qwerty", "qwertyw", true) == false)
|
assert(string.endswith("qwerty", "qwertyw", true) == false)
|
||||||
|
|
||||||
|
# unicode literals
|
||||||
|
assert("\uF014" == "\xEF\x80\x94")
|
||||||
|
|
||||||
|
# string multiplication tests
|
||||||
|
# Basic integer multiplication
|
||||||
|
assert("aze" * 3 == "azeazeaze")
|
||||||
|
assert("ab" * 5 == "ababababab")
|
||||||
|
assert("x" * 1 == "x")
|
||||||
|
assert("hello" * 2 == "hellohello")
|
||||||
|
|
||||||
|
# Zero and negative multiplication
|
||||||
|
assert("aze" * 0 == "")
|
||||||
|
assert("hello" * -1 == "")
|
||||||
|
assert("test" * -5 == "")
|
||||||
|
|
||||||
|
# Boolean multiplication
|
||||||
|
assert("aze" * true == "aze")
|
||||||
|
assert("aze" * false == "")
|
||||||
|
assert("hello" * true == "hello")
|
||||||
|
assert("world" * false == "")
|
||||||
|
|
||||||
|
# Empty string multiplication
|
||||||
|
assert("" * 0 == "")
|
||||||
|
assert("" * 1 == "")
|
||||||
|
assert("" * 5 == "")
|
||||||
|
assert("" * 100 == "")
|
||||||
|
assert("" * true == "")
|
||||||
|
assert("" * false == "")
|
||||||
|
|
||||||
|
# Single character multiplication
|
||||||
|
assert("a" * 10 == "aaaaaaaaaa")
|
||||||
|
assert("z" * 0 == "")
|
||||||
|
assert("!" * 3 == "!!!")
|
||||||
|
|
||||||
|
# Large multiplication (testing performance and memory)
|
||||||
|
var large_result = "abc" * 20
|
||||||
|
assert(size(large_result) == 60)
|
||||||
|
assert(large_result == "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc")
|
||||||
|
|
||||||
|
# Edge cases with special characters
|
||||||
|
assert("\n" * 3 == "\n\n\n")
|
||||||
|
assert("\t" * 2 == "\t\t")
|
||||||
|
assert("\"" * 4 == "\"\"\"\"")
|
||||||
|
assert("\\" * 3 == "\\\\\\")
|
||||||
|
|
||||||
|
# Verify that regular multiplication still works
|
||||||
|
assert(3 * 4 == 12)
|
||||||
|
assert(2.5 * 4 == 10)
|
||||||
|
assert(5 * 3 == 15)
|
||||||
|
assert(0 * 10 == 0)
|
||||||
|
|
||||||
|
# Test that invalid combinations still throw errors
|
||||||
|
try
|
||||||
|
var result = "hello" * "world"
|
||||||
|
assert(false, "Should have thrown an error")
|
||||||
|
except 'type_error'
|
||||||
|
# Expected error
|
||||||
|
end
|
||||||
|
|
||||||
|
try
|
||||||
|
var result = "hello" * 3.14
|
||||||
|
assert(false, "Should have thrown an error")
|
||||||
|
except 'type_error'
|
||||||
|
# Expected error
|
||||||
|
end
|
||||||
|
|
||||||
|
try
|
||||||
|
var result = "hello" * nil
|
||||||
|
assert(false, "Should have thrown an error")
|
||||||
|
except 'type_error'
|
||||||
|
# Expected error
|
||||||
|
end
|
||||||
|
|
||||||
|
# Test with variables
|
||||||
|
var s = "test"
|
||||||
|
var count = 3
|
||||||
|
assert(s * count == "testtesttest")
|
||||||
|
|
||||||
|
var bool_true = true
|
||||||
|
var bool_false = false
|
||||||
|
assert(s * bool_true == "test")
|
||||||
|
assert(s * bool_false == "")
|
||||||
|
|
||||||
|
# Test chaining and expressions
|
||||||
|
assert(("a" * 2) + ("b" * 3) == "aabbb")
|
||||||
|
assert("x" * (1 + 2) == "xxx")
|
||||||
|
assert("y" * (true && true) == "y")
|
||||||
|
assert("z" * (false || false) == "")
|
||||||
|
|
||||||
|
# Test with longer strings
|
||||||
|
var long_str = "Hello, World!"
|
||||||
|
assert(long_str * 0 == "")
|
||||||
|
assert(long_str * 1 == "Hello, World!")
|
||||||
|
assert(long_str * 2 == "Hello, World!Hello, World!")
|
||||||
|
|
||||||
|
# Test boundary conditions
|
||||||
|
assert("a" * 64 == "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") # SHORT_STR_MAX_LEN
|
||||||
|
var very_long = "a" * 100 # Should use long string path
|
||||||
|
assert(size(very_long) == 100)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user