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
|
||||
- `%`: 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
|
||||
- `<`: Less than
|
||||
- `<=`: Less than or equal to
|
||||
|
||||
@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file.
|
||||
### Added
|
||||
- ESP32 ROM SHA Hardware Acceleration to BearSSL (#23819)
|
||||
- Extend state JSON message with functional hostname and ipaddress which could be WiFi or Ethernet
|
||||
- Berry multiplication between string and int
|
||||
|
||||
### 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)
|
||||
{
|
||||
if (be_eqstr(s1, s2)) {
|
||||
|
||||
@ -16,6 +16,7 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
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);
|
||||
bstring* be_num2str(bvm *vm, bvalue *v);
|
||||
void be_val2str(bvm *vm, int index);
|
||||
|
||||
@ -463,6 +463,25 @@ static void make_range(bvm *vm, bvalue lower, bvalue upper)
|
||||
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)
|
||||
{
|
||||
bstring *s;
|
||||
@ -705,6 +724,10 @@ newframe: /* a new call frame */
|
||||
breal x = var2real(a), y = var2real(b);
|
||||
var_setreal(dst, x * y);
|
||||
#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)) {
|
||||
ins_binop(vm, "*", ins);
|
||||
} else {
|
||||
|
||||
@ -175,13 +175,6 @@ assert(f"S = {a}" == 'S = foobar{0}')
|
||||
assert(f"S = {a:i}" == 'S = 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
|
||||
assert(string.startswith("", "") == 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", "tr", 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