1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-11-01 06:03:00 +00:00

Update Cobalt to 0.8.0

- Update Cobalt to 0.8.0, switching our Lua version to 5.2(ish).

 - Remove our `load` wrapper, as we no longer need to inject _ENV into
   the enviroment table.

 - Update the parser to handle labels and goto. This doesn't check that
   gotos are well formed, but at least means the parser doesn't fall
   over on them.

 - Update our docs to reflect the changes to Cobalt.
This commit is contained in:
Jonathan Coates
2023-11-08 18:42:17 +00:00
parent bcb3e9bd53
commit 784e623776
15 changed files with 429 additions and 352 deletions

View File

@@ -73,20 +73,20 @@ public class CobaltLuaMachine implements ILuaMachine {
.build();
// Set up our global table.
var globals = state.getMainThread().getfenv();
CoreLibraries.debugGlobals(state);
Bit32Lib.add(state, globals);
globals.rawset("_HOST", ValueFactory.valueOf(environment.hostString()));
globals.rawset("_CC_DEFAULT_SETTINGS", ValueFactory.valueOf(CoreConfig.defaultComputerSettings));
// Add default APIs
for (var api : environment.apis()) addAPI(globals, api);
// And load the BIOS
try {
var globals = state.globals();
CoreLibraries.debugGlobals(state);
Bit32Lib.add(state, globals);
globals.rawset("_HOST", ValueFactory.valueOf(environment.hostString()));
globals.rawset("_CC_DEFAULT_SETTINGS", ValueFactory.valueOf(CoreConfig.defaultComputerSettings));
// Add default APIs
for (var api : environment.apis()) addAPI(globals, api);
// And load the BIOS
var value = LoadState.load(state, bios, "@bios.lua", globals);
mainRoutine = new LuaThread(state, value, globals);
} catch (CompileException e) {
mainRoutine = new LuaThread(state, value);
} catch (LuaError | CompileException e) {
throw new MachineException(Nullability.assertNonNull(e.getMessage()));
}
@@ -171,7 +171,7 @@ public class CobaltLuaMachine implements ILuaMachine {
return found ? table : null;
}
private LuaValue toValue(@Nullable Object object, @Nullable IdentityHashMap<Object, LuaValue> values) {
private LuaValue toValue(@Nullable Object object, @Nullable IdentityHashMap<Object, LuaValue> values) throws LuaError {
if (object == null) return Constants.NIL;
if (object instanceof Number num) return ValueFactory.valueOf(num.doubleValue());
if (object instanceof Boolean bool) return ValueFactory.valueOf(bool);
@@ -235,7 +235,7 @@ public class CobaltLuaMachine implements ILuaMachine {
return Constants.NIL;
}
Varargs toValues(@Nullable Object[] objects) {
Varargs toValues(@Nullable Object[] objects) throws LuaError {
if (objects == null || objects.length == 0) return Constants.NONE;
if (objects.length == 1) return toValue(objects[0], null);

View File

@@ -18,33 +18,6 @@ do
expect = f().expect
end
if _VERSION == "Lua 5.1" then
-- If we're on Lua 5.1, install parts of the Lua 5.2/5.3 API so that programs can be written against it
local nativeload = load
function load(x, name, mode, env)
expect(1, x, "function", "string")
expect(2, name, "string", "nil")
expect(3, mode, "string", "nil")
expect(4, env, "table", "nil")
local ok, p1, p2 = pcall(function()
local result, err = nativeload(x, name, mode, env)
if result and env then
env._ENV = env
end
return result, err
end)
if ok then
return p1, p2
else
error(p1, 2)
end
end
loadstring = function(string, chunkname) return nativeload(string, chunkname) end
end
-- Inject a stub for the old bit library
_G.bit = {
bnot = bit32.bnot,

View File

@@ -535,6 +535,28 @@ function errors.unexpected_end(start_pos, end_pos)
}
end
--[[- A label statement was opened but not closed.
@tparam number open_start The start position of the opening label.
@tparam number open_end The end position of the opening label.
@tparam number tok_start The start position of the current token.
@return The resulting parse error.
]]
function errors.unclosed_label(open_start, open_end, token, start_pos, end_pos)
expect(1, open_start, "number")
expect(2, open_end, "number")
expect(3, token, "number")
expect(4, start_pos, "number")
expect(5, end_pos, "number")
return {
"Unexpected " .. token_names[token] .. ".",
annotate(open_start, open_end, "Label was started here."),
annotate(start_pos, end_pos, "Tip: Try adding " .. code("::") .. " here."),
}
end
--------------------------------------------------------------------------------
-- Generic parsing errors
--------------------------------------------------------------------------------

View File

@@ -32,12 +32,12 @@ local tokens = require "cc.internal.syntax.parser".tokens
local sub, find = string.sub, string.find
local keywords = {
["and"] = tokens.AND, ["break"] = tokens.BREAK, ["do"] = tokens.DO, ["else"] = tokens.ELSE,
["elseif"] = tokens.ELSEIF, ["end"] = tokens.END, ["false"] = tokens.FALSE, ["for"] = tokens.FOR,
["function"] = tokens.FUNCTION, ["if"] = tokens.IF, ["in"] = tokens.IN, ["local"] = tokens.LOCAL,
["nil"] = tokens.NIL, ["not"] = tokens.NOT, ["or"] = tokens.OR, ["repeat"] = tokens.REPEAT,
["return"] = tokens.RETURN, ["then"] = tokens.THEN, ["true"] = tokens.TRUE, ["until"] = tokens.UNTIL,
["while"] = tokens.WHILE,
["and"] = tokens.AND, ["break"] = tokens.BREAK, ["do"] = tokens.DO, ["else"] = tokens.ELSE,
["elseif"] = tokens.ELSEIF, ["end"] = tokens.END, ["false"] = tokens.FALSE, ["for"] = tokens.FOR,
["function"] = tokens.FUNCTION, ["goto"] = tokens.GOTO, ["if"] = tokens.IF, ["in"] = tokens.IN,
["local"] = tokens.LOCAL, ["nil"] = tokens.NIL, ["not"] = tokens.NOT, ["or"] = tokens.OR,
["repeat"] = tokens.REPEAT, ["return"] = tokens.RETURN, ["then"] = tokens.THEN, ["true"] = tokens.TRUE,
["until"] = tokens.UNTIL, ["while"] = tokens.WHILE,
}
--- Lex a newline character
@@ -292,12 +292,15 @@ local function lex_token(context, str, pos)
local next_pos = pos + 1
if sub(str, next_pos, next_pos) == "=" then return tokens.LE, next_pos end
return tokens.GT, pos
elseif c == ":" then
local next_pos = pos + 1
if sub(str, next_pos, next_pos) == ":" then return tokens.DOUBLE_COLON, next_pos end
return tokens.COLON, pos
elseif c == "~" and sub(str, pos + 1, pos + 1) == "=" then return tokens.NE, pos + 1
-- Single character tokens
elseif c == "," then return tokens.COMMA, pos
elseif c == ";" then return tokens.SEMICOLON, pos
elseif c == ":" then return tokens.COLON, pos
elseif c == "(" then return tokens.OPAREN, pos
elseif c == ")" then return tokens.CPAREN, pos
elseif c == "]" then return tokens.CSQUARE, pos

View File

@@ -10,6 +10,8 @@ import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.squiddev.cobalt.LuaError;
import org.squiddev.cobalt.LuaState;
import org.squiddev.cobalt.Prototype;
import org.squiddev.cobalt.compiler.CompileException;
import org.squiddev.cobalt.compiler.LuaC;
@@ -108,9 +110,9 @@ class LuaCoverage {
Queue<Prototype> queue = new ArrayDeque<>();
try (InputStream stream = new FileInputStream(file)) {
var proto = LuaC.compile(stream, "@" + file.getPath());
var proto = LuaC.compile(new LuaState(), stream, "@" + file.getPath());
queue.add(proto);
} catch (CompileException e) {
} catch (LuaError | CompileException e) {
throw new IllegalStateException("Cannot compile", e);
}

View File

@@ -7,6 +7,7 @@ package dan200.computercraft.core.lua;
import dan200.computercraft.api.lua.LuaException;
import org.junit.jupiter.api.Test;
import org.squiddev.cobalt.Constants;
import org.squiddev.cobalt.LuaError;
import org.squiddev.cobalt.LuaTable;
import org.squiddev.cobalt.ValueFactory;
@@ -18,7 +19,11 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
class VarargArgumentsTest {
private static LuaTable tableWithCustomType() {
var metatable = new LuaTable();
metatable.rawset(Constants.NAME, ValueFactory.valueOf("some type"));
try {
metatable.rawset(Constants.NAME, ValueFactory.valueOf("some type"));
} catch (LuaError e) {
throw new IllegalStateException("Cannot create metatable", e);
}
var table = new LuaTable();
table.setMetatable(null, metatable);

View File

@@ -188,6 +188,9 @@ local function format(value)
-- TODO: Look into something like mbs's pretty printer.
if type(value) == "string" and value:find("\n") then
return "<<<\n" .. value .. "\n>>>"
elseif type(value) == "string" then
local escaped = value:gsub("[^%g ]", function(x) return ("\\x%02x"):format(x:byte()) end)
return "\"" .. escaped .. "\""
else
local ok, res = pcall(textutils.serialise, value)
if ok then return res else return tostring(value) end

View File

@@ -79,20 +79,6 @@ describe("The Lua base library", function()
end)
describe("load", function()
it("validates arguments", function()
load("")
load(function()
end)
load("", "")
load("", "", "")
load("", "", "", _ENV)
expect.error(load, nil):eq("bad argument #1 (function or string expected, got nil)")
expect.error(load, "", false):eq("bad argument #2 (string expected, got boolean)")
expect.error(load, "", "", false):eq("bad argument #3 (string expected, got boolean)")
expect.error(load, "", "", "", false):eq("bad argument #4 (table expected, got boolean)")
end)
local function generator(parts)
return coroutine.wrap(function()
for i = 1, #parts do

View File

@@ -8,32 +8,6 @@ An exhaustive list of all error states in the parser, and the error messages we
generate for each one. This is _not_ a complete collection of all possible
errors, but is a useful guide for where we might be providing terrible messages.
```lua
break while
-- Line 1: unexpected symbol near <eof> (program)
```
```txt
Unexpected while. Expected a statement.
|
1 | break while
| ^^^^^
```
```lua
do end true
-- Line 1: unexpected symbol near 'true' (program)
```
```txt
Unexpected true. Expected a statement.
|
1 | do end true
| ^^^^
```
```lua
do until
-- Line 1: 'end' expected near 'until' (program)
@@ -63,6 +37,35 @@ Unexpected true.
```
```lua
:: xyz while
-- Line 1: '::' expected near 'while' (program)
```
```txt
Unexpected while.
|
1 | :: xyz while
| ^^ Label was started here.
|
1 | :: xyz while
| ^^^^^ Tip: Try adding :: here.
```
```lua
:: while
-- Line 1: <name> expected near 'while' (program)
```
```txt
Unexpected while.
|
1 | :: while
| ^^^^^
```
```lua
for xyz , xyz while
-- Line 1: 'in' expected near 'while' (program)
@@ -849,6 +852,20 @@ Unexpected while.
```
```lua
xyz while
-- Line 1: syntax error near 'while' (program)
```
```txt
Unexpected symbol after name.
|
1 | xyz while
| ^
Did you mean to assign this or call it as a function?
```
```lua
if xyz then else until
-- Line 1: 'end' expected near 'until' (program)
@@ -1059,22 +1076,6 @@ Unexpected while. Expected an expression.
```
```lua {repl_exprs}
{ xyz , while
-- Line 1: unexpected symbol near 'while' (repl_exprs)
```
```txt
Unexpected while. Are you missing a closing bracket?
|
1 | { xyz , while
| ^ Brackets were opened here.
|
1 | { xyz , while
| ^^^^^ Unexpected while here.
```
```lua {repl_exprs}
{ xyz = xyz while
-- Line 1: '}' expected near 'while' (repl_exprs)
@@ -1104,6 +1105,22 @@ Unexpected while. Expected an expression.
```
```lua {repl_exprs}
{ xyz ; while
-- Line 1: unexpected symbol near 'while' (repl_exprs)
```
```txt
Unexpected while. Are you missing a closing bracket?
|
1 | { xyz ; while
| ^ Brackets were opened here.
|
1 | { xyz ; while
| ^^^^^ Unexpected while here.
```
```lua {repl_exprs}
{ xyz while
-- Line 1: '}' expected near 'while' (repl_exprs)

View File

@@ -417,6 +417,49 @@ Unexpected end.
Your program contains more ends than needed. Check each block (if, for, function, ...) only has one end.
```
## `goto` and labels
We `goto` the same as normal identifiers.
```lua
goto 2
```
```txt
Unexpected symbol after name.
|
1 | goto 2
| ^
Did you mean to assign this or call it as a function?
```
Labels have a basic closing check:
```lua
::foo
```
```txt
Unexpected end of file.
|
1 | ::foo
| ^^ Label was started here.
|
1 | ::foo
| ^ Tip: Try adding :: here.
```
But we do nothing fancy for just a `::`
```lua
::
```
```txt
Unexpected end of file.
|
1 | ::
| ^
```
# Function calls
## Additional commas