mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-10-27 11:57:38 +00:00
Merge branch 'mc-1.20.x' into mc-1.21.x
This commit is contained in:
@@ -101,7 +101,7 @@ public class FSAPI implements ILuaAPI {
|
||||
* }</pre>
|
||||
*/
|
||||
@LuaFunction
|
||||
public final String[] list(String path) throws LuaException {
|
||||
public final List<String> list(String path) throws LuaException {
|
||||
try (var ignored = environment.time(Metrics.FS_OPS)) {
|
||||
return getFileSystem().list(path);
|
||||
} catch (FileSystemException e) {
|
||||
|
||||
@@ -122,7 +122,7 @@ public class OSAPI implements ILuaAPI {
|
||||
}
|
||||
|
||||
private static long getEpochForCalendar(Calendar c) {
|
||||
return c.getTime().getTime();
|
||||
return c.getTimeInMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -298,7 +298,7 @@ public class OSAPI implements ILuaAPI {
|
||||
* textutils.formatTime(os.time())
|
||||
* }</pre>
|
||||
* @cc.since 1.2
|
||||
* @cc.changed 1.80pr1 Add support for getting the local local and UTC time.
|
||||
* @cc.changed 1.80pr1 Add support for getting the local and UTC time.
|
||||
* @cc.changed 1.82.0 Arguments are now case insensitive.
|
||||
* @cc.changed 1.83.0 {@link #time(IArguments)} now accepts table arguments and converts them to UNIX timestamps.
|
||||
* @see #date To get a date table that can be converted with this function.
|
||||
|
||||
@@ -177,9 +177,9 @@ public abstract class AbstractHandle {
|
||||
/**
|
||||
* Read the remainder of the file.
|
||||
*
|
||||
* @return The file, or {@code null} if at the end of it.
|
||||
* @return The remaining contents of the file, or {@code null} in the event of an error.
|
||||
* @throws LuaException If the file has been closed.
|
||||
* @cc.treturn string|nil The remaining contents of the file, or {@code nil} if we are at the end.
|
||||
* @cc.treturn string|nil The remaining contents of the file, or {@code nil} in the event of an error.
|
||||
* @cc.since 1.80pr1
|
||||
*/
|
||||
@Nullable
|
||||
|
||||
@@ -57,7 +57,7 @@ interface AddressPredicate {
|
||||
prefixSize = Integer.parseInt(prefixSizeStr);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new InvalidRuleException(String.format(
|
||||
"Invalid host host '%s': Cannot extract size of CIDR mask from '%s'.",
|
||||
"Invalid host '%s': Cannot extract size of CIDR mask from '%s'.",
|
||||
addressStr + '/' + prefixSizeStr, prefixSizeStr
|
||||
));
|
||||
}
|
||||
|
||||
@@ -270,7 +270,7 @@ final class Generator<T> {
|
||||
}
|
||||
|
||||
// Fold over the original method's arguments, excluding the target in reverse. For each argument, we reduce
|
||||
// a method of type type (target, args..., arg_n, context..., IArguments) -> _ to (target, args..., context..., IArguments) -> _
|
||||
// a method of type (target, args..., arg_n, context..., IArguments) -> _ to (target, args..., context..., IArguments) -> _
|
||||
// until eventually we've flattened the whole list.
|
||||
for (var i = parameterTypes.size() - 1; i >= 0; i--) {
|
||||
handle = MethodHandles.foldArguments(handle, i + 1, argSelectors.get(i));
|
||||
|
||||
@@ -122,6 +122,10 @@ public class FileSystem {
|
||||
}
|
||||
|
||||
var lastSlash = path.lastIndexOf('/');
|
||||
|
||||
// If the trailing segment is a "..", then just append another one.
|
||||
if (path.substring(lastSlash < 0 ? 0 : lastSlash + 1).equals("..")) return path + "/..";
|
||||
|
||||
if (lastSlash >= 0) {
|
||||
return path.substring(0, lastSlash);
|
||||
} else {
|
||||
@@ -145,7 +149,7 @@ public class FileSystem {
|
||||
return getMount(sanitizePath(path)).getAttributes(sanitizePath(path));
|
||||
}
|
||||
|
||||
public synchronized String[] list(String path) throws FileSystemException {
|
||||
public synchronized List<String> list(String path) throws FileSystemException {
|
||||
path = sanitizePath(path);
|
||||
var mount = getMount(path);
|
||||
|
||||
@@ -161,10 +165,8 @@ public class FileSystem {
|
||||
}
|
||||
|
||||
// Return list
|
||||
var array = new String[list.size()];
|
||||
list.toArray(array);
|
||||
Arrays.sort(array);
|
||||
return array;
|
||||
list.sort(Comparator.naturalOrder());
|
||||
return list;
|
||||
}
|
||||
|
||||
public synchronized boolean exists(String path) throws FileSystemException {
|
||||
|
||||
@@ -13,6 +13,7 @@ import dan200.computercraft.core.Logging;
|
||||
import dan200.computercraft.core.computer.TimeoutState;
|
||||
import dan200.computercraft.core.methods.LuaMethod;
|
||||
import dan200.computercraft.core.methods.MethodSupplier;
|
||||
import dan200.computercraft.core.util.LuaUtil;
|
||||
import dan200.computercraft.core.util.Nullability;
|
||||
import dan200.computercraft.core.util.SanitisedError;
|
||||
import org.slf4j.Logger;
|
||||
@@ -183,10 +184,35 @@ public class CobaltLuaMachine implements ILuaMachine {
|
||||
return ValueFactory.valueOf(bytes);
|
||||
}
|
||||
|
||||
// Don't share singleton values, and instead convert them to a new table.
|
||||
if (LuaUtil.isSingletonCollection(object)) return new LuaTable();
|
||||
|
||||
if (values == null) values = new IdentityHashMap<>(1);
|
||||
var result = values.get(object);
|
||||
if (result != null) return result;
|
||||
|
||||
var wrapped = toValueWorker(object, values);
|
||||
if (wrapped == null) {
|
||||
LOG.warn(Logging.JAVA_ERROR, "Received unknown type '{}', returning nil.", object.getClass().getName());
|
||||
return Constants.NIL;
|
||||
}
|
||||
|
||||
values.put(object, wrapped);
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a complex Java object (such as a collection or Lua object) to a Lua value.
|
||||
* <p>
|
||||
* This is a worker function for {@link #toValue(Object, IdentityHashMap)}, which handles the actual construction
|
||||
* of values, without reading/writing from the value map.
|
||||
*
|
||||
* @param object The object to convert.
|
||||
* @param values The map of Java to Lua values.
|
||||
* @return The converted value, or {@code null} if it could not be converted.
|
||||
* @throws LuaError If the value could not be converted.
|
||||
*/
|
||||
private @Nullable LuaValue toValueWorker(Object object, IdentityHashMap<Object, LuaValue> values) throws LuaError {
|
||||
if (object instanceof ILuaFunction) {
|
||||
return new ResultInterpreterFunction(this, FUNCTION_METHOD, object, context, object.toString());
|
||||
}
|
||||
@@ -194,15 +220,12 @@ public class CobaltLuaMachine implements ILuaMachine {
|
||||
if (object instanceof IDynamicLuaObject) {
|
||||
LuaValue wrapped = wrapLuaObject(object);
|
||||
if (wrapped == null) wrapped = new LuaTable();
|
||||
values.put(object, wrapped);
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
if (object instanceof Map<?, ?> map) {
|
||||
var table = new LuaTable();
|
||||
values.put(object, table);
|
||||
|
||||
for (Map.Entry<?, ?> pair : map.entrySet()) {
|
||||
for (var pair : map.entrySet()) {
|
||||
var key = toValue(pair.getKey(), values);
|
||||
var value = toValue(pair.getValue(), values);
|
||||
if (!key.isNil() && !value.isNil()) table.rawset(key, value);
|
||||
@@ -212,27 +235,18 @@ public class CobaltLuaMachine implements ILuaMachine {
|
||||
|
||||
if (object instanceof Collection<?> objects) {
|
||||
var table = new LuaTable(objects.size(), 0);
|
||||
values.put(object, table);
|
||||
var i = 0;
|
||||
for (Object child : objects) table.rawset(++i, toValue(child, values));
|
||||
for (var child : objects) table.rawset(++i, toValue(child, values));
|
||||
return table;
|
||||
}
|
||||
|
||||
if (object instanceof Object[] objects) {
|
||||
var table = new LuaTable(objects.length, 0);
|
||||
values.put(object, table);
|
||||
for (var i = 0; i < objects.length; i++) table.rawset(i + 1, toValue(objects[i], values));
|
||||
return table;
|
||||
}
|
||||
|
||||
var wrapped = wrapLuaObject(object);
|
||||
if (wrapped != null) {
|
||||
values.put(object, wrapped);
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
LOG.warn(Logging.JAVA_ERROR, "Received unknown type '{}', returning nil.", object.getClass().getName());
|
||||
return Constants.NIL;
|
||||
return wrapLuaObject(object);
|
||||
}
|
||||
|
||||
Varargs toValues(@Nullable Object[] objects) throws LuaError {
|
||||
|
||||
@@ -4,9 +4,18 @@
|
||||
|
||||
package dan200.computercraft.core.util;
|
||||
|
||||
import dan200.computercraft.core.lua.ILuaMachine;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class LuaUtil {
|
||||
private static final List<?> EMPTY_LIST = List.of();
|
||||
private static final Set<?> EMPTY_SET = Set.of();
|
||||
private static final Map<?, ?> EMPTY_MAP = Map.of();
|
||||
|
||||
public static Object[] consArray(Object value, Collection<?> rest) {
|
||||
if (rest.isEmpty()) return new Object[]{ value };
|
||||
|
||||
@@ -14,7 +23,20 @@ public class LuaUtil {
|
||||
var out = new Object[rest.size() + 1];
|
||||
out[0] = value;
|
||||
var i = 1;
|
||||
for (Object additionalType : rest) out[i++] = additionalType;
|
||||
for (var additionalType : rest) out[i++] = additionalType;
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether a value is a singleton collection, such as one created with {@link List#of()}.
|
||||
* <p>
|
||||
* These collections are treated specially by {@link ILuaMachine} implementations: we skip sharing for them, and
|
||||
* create a new table each time.
|
||||
*
|
||||
* @param value The value to test.
|
||||
* @return Whether this is a singleton collection.
|
||||
*/
|
||||
public static boolean isSingletonCollection(Object value) {
|
||||
return value == EMPTY_LIST || value == EMPTY_SET || value == EMPTY_MAP;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -851,13 +851,32 @@ unserialise = unserialize -- GB version
|
||||
|
||||
--[[- Returns a JSON representation of the given data.
|
||||
|
||||
This function attempts to guess whether a table is a JSON array or
|
||||
object. However, empty tables are assumed to be empty objects - use
|
||||
[`textutils.empty_json_array`] to mark an empty array.
|
||||
|
||||
This is largely intended for interacting with various functions from the
|
||||
[`commands`] API, though may also be used in making [`http`] requests.
|
||||
|
||||
Lua has a rather different data model to Javascript/JSON. As a result, some Lua
|
||||
values do not serialise cleanly into JSON.
|
||||
|
||||
- Lua tables can contain arbitrary key-value pairs, but JSON only accepts arrays,
|
||||
and objects (which require a string key). When serialising a table, if it only
|
||||
has numeric keys, then it will be treated as an array. Otherwise, the table will
|
||||
be serialised to an object using the string keys. Non-string keys (such as numbers
|
||||
or tables) will be dropped.
|
||||
|
||||
A consequence of this is that an empty table will always be serialised to an object,
|
||||
not an array. [`textutils.empty_json_array`] may be used to express an empty array.
|
||||
|
||||
- Lua strings are an a sequence of raw bytes, and do not have any specific encoding.
|
||||
However, JSON strings must be valid unicode. By default, non-ASCII characters in a
|
||||
string are serialised to their unicode code point (for instance, `"\xfe"` is
|
||||
converted to `"\u00fe"`). The `unicode_strings` option may be set to treat all input
|
||||
strings as UTF-8.
|
||||
|
||||
- Lua does not distinguish between missing keys (`undefined` in JS) and ones explicitly
|
||||
set to `null`. As a result `{ x = nil }` is serialised to `{}`. [`textutils.json_null`]
|
||||
may be used to get an explicit null value (`{ x = textutils.json_null }` will serialise
|
||||
to `{"x": null}`).
|
||||
|
||||
@param[1] t The value to serialise. Like [`textutils.serialise`], this should not
|
||||
contain recursive tables or functions.
|
||||
@tparam[1,opt] {
|
||||
|
||||
@@ -114,7 +114,7 @@ local vector = {
|
||||
--
|
||||
-- @tparam Vector self The first vector to compute the dot product of.
|
||||
-- @tparam Vector o The second vector to compute the dot product of.
|
||||
-- @treturn Vector The dot product of `self` and `o`.
|
||||
-- @treturn number The dot product of `self` and `o`.
|
||||
-- @usage v1:dot(v2)
|
||||
dot = function(self, o)
|
||||
if getmetatable(self) ~= vmetatable then expect(1, self, "vector") end
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
# New features in CC: Tweaked 1.113.0
|
||||
|
||||
* Allow placing printed pages and books in lecterns.
|
||||
|
||||
Several bug fixes:
|
||||
* Various documentation fixes (MCJack123)
|
||||
* Fix computers and turtles not being dropped when exploded with TNT.
|
||||
* Fix crash when turtles are broken while mining a block.
|
||||
* Fix pocket computer terminals not updating when in the off-hand.
|
||||
* Fix disk drives not being exposed as a peripheral.
|
||||
* Fix item details being non-serialisable due to duplicated tables.
|
||||
|
||||
# New features in CC: Tweaked 1.112.0
|
||||
|
||||
* Report a custom error when using `!` instead of `not`.
|
||||
@@ -811,7 +823,7 @@ And several bug fixes:
|
||||
# New features in CC: Tweaked 1.86.2
|
||||
|
||||
* Fix peripheral.getMethods returning an empty table.
|
||||
* Update to Minecraft 1.15.2. This is currently alpha-quality and so is missing missing features and may be unstable.
|
||||
* Update to Minecraft 1.15.2. This is currently alpha-quality and so is missing features and may be unstable.
|
||||
|
||||
# New features in CC: Tweaked 1.86.1
|
||||
|
||||
@@ -1427,7 +1439,7 @@ And several bug fixes:
|
||||
* Turtles can now compare items in their inventories
|
||||
* Turtles can place signs with text on them with `turtle.place( [signText] )`
|
||||
* Turtles now optionally require fuel items to move, and can refuel themselves
|
||||
* The size of the the turtle inventory has been increased to 16
|
||||
* The size of the turtle inventory has been increased to 16
|
||||
* The size of the turtle screen has been increased
|
||||
* New turtle functions: `turtle.compareTo( [slotNum] )`, `turtle.craft()`, `turtle.attack()`, `turtle.attackUp()`, `turtle.attackDown()`, `turtle.dropUp()`, `turtle.dropDown()`, `turtle.getFuelLevel()`, `turtle.refuel()`
|
||||
* New disk function: disk.getID()
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
New features in CC: Tweaked 1.112.0
|
||||
New features in CC: Tweaked 1.113.0
|
||||
|
||||
* Report a custom error when using `!` instead of `not`.
|
||||
* Update several translations (zyxkad, MineKID-LP).
|
||||
* Add `cc.strings.split` function.
|
||||
* Allow placing printed pages and books in lecterns.
|
||||
|
||||
Several bug fixes:
|
||||
* Fix `drive.getAudioTitle` returning `nil` when no disk is inserted.
|
||||
* Preserve item data when upgrading pocket computers.
|
||||
* Add missing bounds check to `cc.strings.wrap` (Lupus950).
|
||||
* Fix dyed turtles rendering transparent.
|
||||
* Fix dupe bug when crafting with turtles.
|
||||
* Various documentation fixes (MCJack123)
|
||||
* Fix computers and turtles not being dropped when exploded with TNT.
|
||||
* Fix crash when turtles are broken while mining a block.
|
||||
* Fix pocket computer terminals not updating when in the off-hand.
|
||||
* Fix disk drives not being exposed as a peripheral.
|
||||
* Fix item details being non-serialisable due to duplicated tables.
|
||||
|
||||
Type "help changelog" to see the full version history.
|
||||
|
||||
@@ -13,7 +13,7 @@ Typically DFPWM audio is read from [the filesystem][`fs.ReadHandle`] or a [a web
|
||||
and converted a format suitable for [`speaker.playAudio`].
|
||||
|
||||
## Encoding and decoding files
|
||||
This modules exposes two key functions, [`make_decoder`] and [`make_encoder`], which construct a new decoder or encoder.
|
||||
This module exposes two key functions, [`make_decoder`] and [`make_encoder`], which construct a new decoder or encoder.
|
||||
The returned encoder/decoder is itself a function, which converts between the two kinds of data.
|
||||
|
||||
These encoders and decoders have lots of hidden state, so you should be careful to use the same encoder or decoder for
|
||||
@@ -21,9 +21,9 @@ a specific audio stream. Typically you will want to create a decoder for each st
|
||||
for each one you write.
|
||||
|
||||
## Converting audio to DFPWM
|
||||
DFPWM is not a popular file format and so standard audio processing tools will not have an option to export to it.
|
||||
DFPWM is not a popular file format and so standard audio processing tools may not have an option to export to it.
|
||||
Instead, you can convert audio files online using [music.madefor.cc], the [LionRay Wav Converter][LionRay] Java
|
||||
application or development builds of [FFmpeg].
|
||||
application or [FFmpeg] 5.1 or later.
|
||||
|
||||
[music.madefor.cc]: https://music.madefor.cc/ "DFPWM audio converter for Computronics and CC: Tweaked"
|
||||
[LionRay]: https://github.com/gamax92/LionRay/ "LionRay Wav Converter "
|
||||
@@ -211,7 +211,7 @@ end
|
||||
|
||||
--[[- A convenience function for encoding a complete file of audio at once.
|
||||
|
||||
This should only be used for complete pieces of audio. If you are writing writing multiple chunks to the same place,
|
||||
This should only be used for complete pieces of audio. If you are writing multiple chunks to the same place,
|
||||
you should use an encoder returned by [`make_encoder`] instead.
|
||||
|
||||
@tparam { number... } input The table of amplitude data.
|
||||
|
||||
@@ -5,11 +5,14 @@
|
||||
package dan200.computercraft.core.computer;
|
||||
|
||||
import com.google.common.io.CharStreams;
|
||||
import dan200.computercraft.api.lua.ILuaAPI;
|
||||
import dan200.computercraft.api.lua.LuaFunction;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static java.time.Duration.ofSeconds;
|
||||
@@ -30,6 +33,26 @@ public class ComputerTest {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDuplicateObjects() {
|
||||
class CustomApi implements ILuaAPI {
|
||||
@Override
|
||||
public String[] getNames() {
|
||||
return new String[]{ "custom" };
|
||||
}
|
||||
|
||||
@LuaFunction
|
||||
public final Object[] getObjects() {
|
||||
return new Object[]{ List.of(), List.of() };
|
||||
}
|
||||
}
|
||||
|
||||
ComputerBootstrap.run("""
|
||||
local x, y = custom.getObjects()
|
||||
assert(x ~= y)
|
||||
""", i -> i.addApi(new CustomApi()), 50);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
var stream = ComputerTest.class.getClassLoader().getResourceAsStream("benchmark.lua");
|
||||
try (var reader = new InputStreamReader(Objects.requireNonNull(stream), StandardCharsets.UTF_8)) {
|
||||
|
||||
@@ -158,6 +158,65 @@ describe("The fs library", function()
|
||||
expect(fs.combine("", "a")):eq("a")
|
||||
expect(fs.combine("a", "", "b", "c")):eq("a/b/c")
|
||||
end)
|
||||
|
||||
it("preserves pattern characters", function()
|
||||
expect(fs.combine("foo*?")):eq("foo*?")
|
||||
end)
|
||||
|
||||
it("sanitises paths", function()
|
||||
expect(fs.combine("foo\":<>|")):eq("foo")
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("fs.getName", function()
|
||||
it("returns 'root' for the empty path", function()
|
||||
expect(fs.getName("")):eq("root")
|
||||
expect(fs.getName("foo/..")):eq("root")
|
||||
end)
|
||||
|
||||
it("returns the file name", function()
|
||||
expect(fs.getName("foo/bar")):eq("bar")
|
||||
expect(fs.getName("foo/bar/")):eq("bar")
|
||||
expect(fs.getName("../foo")):eq("foo")
|
||||
end)
|
||||
|
||||
it("returns '..' for parent directories", function()
|
||||
expect(fs.getName("..")):eq("..")
|
||||
end)
|
||||
|
||||
it("preserves pattern characters", function()
|
||||
expect(fs.getName("foo*?")):eq("foo*?")
|
||||
end)
|
||||
|
||||
it("sanitises paths", function()
|
||||
expect(fs.getName("foo\":<>|")):eq("foo")
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("fs.getDir", function()
|
||||
it("returns '..' for the empty path", function()
|
||||
expect(fs.getDir("")):eq("..")
|
||||
expect(fs.getDir("foo/..")):eq("..")
|
||||
end)
|
||||
|
||||
it("returns the directory name", function()
|
||||
expect(fs.getDir("foo/bar")):eq("foo")
|
||||
expect(fs.getDir("foo/bar/")):eq("foo")
|
||||
expect(fs.getDir("../foo")):eq("..")
|
||||
end)
|
||||
|
||||
it("returns '..' for parent directories", function()
|
||||
expect(fs.getDir("..")):eq("../..")
|
||||
expect(fs.getDir("../..")):eq("../../..")
|
||||
end)
|
||||
|
||||
it("preserves pattern characters", function()
|
||||
expect(fs.getDir("foo*?/x")):eq("foo*?")
|
||||
end)
|
||||
|
||||
it("sanitises paths", function()
|
||||
expect(fs.getDir("foo\":<>|/x")):eq("foo")
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("fs.getSize", function()
|
||||
@@ -200,6 +259,14 @@ describe("The fs library", function()
|
||||
handle.close()
|
||||
end)
|
||||
|
||||
it("reading an empty file returns nil", function()
|
||||
local file = create_test_file ""
|
||||
|
||||
local handle = fs.open(file, mode)
|
||||
expect(handle.read()):eq(nil)
|
||||
handle.close()
|
||||
end)
|
||||
|
||||
it("can read a line of text", function()
|
||||
local file = create_test_file "some\nfile\r\ncontents\n\n"
|
||||
|
||||
@@ -223,6 +290,16 @@ describe("The fs library", function()
|
||||
expect(handle.readLine(true)):eq(nil)
|
||||
handle.close()
|
||||
end)
|
||||
|
||||
it("readAll always returns a string", function()
|
||||
local contents = "some\nfile\ncontents"
|
||||
local file = create_test_file "some\nfile\ncontents"
|
||||
|
||||
local handle = fs.open(file, mode)
|
||||
expect(handle.readAll()):eq(contents)
|
||||
expect(handle.readAll()):eq("")
|
||||
handle.close()
|
||||
end)
|
||||
end
|
||||
|
||||
describe("reading", function()
|
||||
|
||||
@@ -188,4 +188,31 @@ describe("The os library", function()
|
||||
expect.error(os.loadAPI, nil):eq("bad argument #1 (string expected, got nil)")
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("os.queueEvent", function()
|
||||
local function roundtrip(...)
|
||||
local event_name = ("event_%08x"):format(math.random(1, 0x7FFFFFFF))
|
||||
os.queueEvent(event_name, ...)
|
||||
return select(2, os.pullEvent(event_name))
|
||||
end
|
||||
|
||||
it("preserves references in tables", function()
|
||||
local tbl = {}
|
||||
local xs = roundtrip({ tbl, tbl })
|
||||
expect(xs[1]):eq(xs[2])
|
||||
end)
|
||||
|
||||
it("does not preserve references in separate args", function()
|
||||
-- I'm not sure I like this behaviour, but it is what CC has always done.
|
||||
local tbl = {}
|
||||
local xs, ys = roundtrip(tbl, tbl)
|
||||
expect(xs):ne(ys)
|
||||
end)
|
||||
|
||||
it("clones objects", function()
|
||||
local tbl = {}
|
||||
local xs = roundtrip(tbl)
|
||||
expect(xs):ne(tbl)
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
|
||||
@@ -13,13 +13,13 @@ correct tokens and positions, and that it can report sensible error messages.
|
||||
We can lex some basic comments:
|
||||
|
||||
```lua
|
||||
-- A basic singleline comment comment
|
||||
-- A basic singleline comment
|
||||
--[ Not a multiline comment
|
||||
--[= Also not a multiline comment!
|
||||
```
|
||||
|
||||
```txt
|
||||
1:1-1:37 COMMENT -- A basic singleline comment comment
|
||||
1:1-1:29 COMMENT -- A basic singleline comment
|
||||
2:1-2:27 COMMENT --[ Not a multiline comment
|
||||
3:1-3:34 COMMENT --[= Also not a multiline comment!
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user