Skip to content

Commit

Permalink
Implement string.byte and string.char
Browse files Browse the repository at this point in the history
  • Loading branch information
ginnyTheCat committed Nov 4, 2024
1 parent fcbaabc commit 977899b
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 32 deletions.
4 changes: 2 additions & 2 deletions COMPATIBILITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,8 @@ likely not be implemented due to differences between piccolo and PUC-Lua.

| Status | Function | Differences | Notes |
| ------ | --------------------------------- | ----------- | ----- |
| ⚫️️ | `byte(s[, i, j])` | | |
| ⚫️️ | `char(args...)` | | |
| 🔵 | `byte(s[, i, j])` | | |
| 🔵 | `char(args...)` | | |
| ⚫️️ | `dump(function[, strip])` | | |
| ⚫️️ | `find(s, pattern[, init, plain])` | | |
| ⚫️️ | `format(formatstring, args...)` | | |
Expand Down
83 changes: 53 additions & 30 deletions src/stdlib/string.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{Callback, CallbackReturn, Context, String, Table};
use crate::{Callback, CallbackReturn, Context, FromValue, String, Table, Value};

pub fn load_string<'gc>(ctx: Context<'gc>) {
let string = Table::new(&ctx);
Expand All @@ -16,39 +16,37 @@ pub fn load_string<'gc>(ctx: Context<'gc>) {

string.set_field(
ctx,
"sub",
"byte",
Callback::from_fn(&ctx, |ctx, _, mut stack| {
fn operate_sub(
string: &[u8],
i: i64,
j: Option<i64>,
) -> Result<&[u8], std::num::TryFromIntError> {
let i = match i {
i if i > 0 => i.saturating_sub(1).try_into()?,
0 => 0,
i => string.len().saturating_sub(i.unsigned_abs().try_into()?),
};
let j = if let Some(j) = j {
if j >= 0 {
j.try_into()?
} else {
let j: usize = j.unsigned_abs().try_into()?;
string.len().saturating_sub(j.saturating_sub(1))
}
} else {
string.len()
}
.clamp(0, string.len());
let (string, i, j) = stack.consume::<(String, Option<i64>, Option<i64>)>(ctx)?;
let i = i.unwrap_or(1);
let substr = sub(string.as_bytes(), i, j.or(Some(i)))?;
stack.extend(substr.iter().map(|b| Value::Integer(i64::from(*b))));
Ok(CallbackReturn::Return)
}),
);

Ok(if i >= j || i >= string.len() {
&[]
} else {
&string[i..j]
})
}
string.set_field(
ctx,
"char",
Callback::from_fn(&ctx, |ctx, _, mut stack| {
let string = ctx.intern(
&stack
.into_iter()
.map(|c| u8::from_value(ctx, c))
.collect::<Result<Vec<_>, _>>()?,
);
stack.replace(ctx, string);
Ok(CallbackReturn::Return)
}),
);

string.set_field(
ctx,
"sub",
Callback::from_fn(&ctx, |ctx, _, mut stack| {
let (string, i, j) = stack.consume::<(String, i64, Option<i64>)>(ctx)?;
let substr = ctx.intern(operate_sub(string.as_bytes(), i, j)?);
let substr = ctx.intern(sub(string.as_bytes(), i, j)?);
stack.replace(ctx, substr);
Ok(CallbackReturn::Return)
}),
Expand Down Expand Up @@ -101,3 +99,28 @@ pub fn load_string<'gc>(ctx: Context<'gc>) {

ctx.set_global("string", string);
}

fn sub(string: &[u8], i: i64, j: Option<i64>) -> Result<&[u8], std::num::TryFromIntError> {
let i = match i {
i if i > 0 => i.saturating_sub(1).try_into()?,
0 => 0,
i => string.len().saturating_sub(i.unsigned_abs().try_into()?),
};
let j = if let Some(j) = j {
if j >= 0 {
j.try_into()?
} else {
let j: usize = j.unsigned_abs().try_into()?;
string.len().saturating_sub(j.saturating_sub(1))
}
} else {
string.len()
}
.clamp(0, string.len());

Ok(if i >= j || i >= string.len() {
&[]
} else {
&string[i..j]
})
}
41 changes: 41 additions & 0 deletions tests/scripts/string.lua
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,44 @@ do
assert(string.upper(80) == "80")
assert(string.upper(3.14) == "3.14")
end

do
assert(is_err(function() return string.byte(nil) end))
assert(is_err(function() return string.byte(true) end))
assert(is_err(function() return string.byte(false) end))
assert(is_err(function() return string.byte({}) end))
assert(is_err(function() return string.byte(is_err) end))
assert(is_err(function() return string.byte(coroutine.create(test_coroutine_len)) end))
assert(string.byte("") == nil)
assert(string.byte("abcd") == 97)
assert(string.byte("\xef") == 0xef)
assert(string.byte("\0") == 0)
assert(string.byte(1) == string.byte("1"))
assert(string.byte("abcd", 2) == string.byte("b"))
assert(string.byte("abcd", -1) == string.byte("d"))

local b, c, d, e = string.byte("abcd", 2, -1)
assert(b == string.byte("b") and
c == string.byte("c") and
d == string.byte("d") and
e == nil)
b, c, d = string.byte("abcd", 2, 3)
assert(b == string.byte("b") and
c == string.byte("c") and
d == nil)
assert(string.byte("abcd", 2, -5) == nil)
assert(string.byte("abcd", 3, 1) == nil)
end

do
assert(is_err(function() return string.char(nil) end))
assert(is_err(function() return string.char(true) end))
assert(is_err(function() return string.char(false) end))
assert(is_err(function() return string.char({}) end))
assert(is_err(function() return string.char(is_err) end))
assert(is_err(function() return string.char(coroutine.create(test_coroutine_len)) end))
assert(string.char() == "")
assert(string.char(0, "1", 97, 98, 255) == "\0\x01ab\xff")
assert(is_err(function() return string.char(256) end))
assert(is_err(function() return string.char(-1) end))
end

0 comments on commit 977899b

Please sign in to comment.