1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-07-04 11:02:54 +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
No known key found for this signature in database
GPG Key ID: B9E431FF07C98D06
15 changed files with 429 additions and 352 deletions

View File

@ -57,7 +57,6 @@ repositories {
filter { filter {
includeGroup("cc.tweaked") includeGroup("cc.tweaked")
includeModule("org.squiddev", "Cobalt")
// Things we mirror // Things we mirror
includeGroup("commoble.morered") includeGroup("commoble.morered")
includeGroup("dev.architectury") includeGroup("dev.architectury")

View File

@ -21,6 +21,17 @@ of the mod should run fine on later versions.
However, some changes to the underlying game, or CC: Tweaked's own internals may break some programs. This page serves However, some changes to the underlying game, or CC: Tweaked's own internals may break some programs. This page serves
as documentation for breaking changes and "gotchas" one should look out for between versions. as documentation for breaking changes and "gotchas" one should look out for between versions.
## CC: Tweaked 1.109.0 {#cct-1.109}
- Update to Lua 5.2:
- Support for Lua 5.0's pseudo-argument `arg` has been removed. You should always use `...` for varargs.
- Environments are no longer baked into the runtime, and instead use the `_ENV` local or upvalue. `getfenv`/`setfenv`
now only work on Lua functions with an `_ENV` upvalue. `getfenv` will return the global environment when called
with other functions, and `setfenv` will have no effect.
- `load`/`loadstring` defaults to using the global environment (`_G`) rather than the current coroutine's
environment.
- Support for dumping functions (`string.dump`) and loading binary chunks has been removed.
## Minecraft 1.13 {#mc-1.13} ## Minecraft 1.13 {#mc-1.13}
- The "key code" for [`key`] and [`key_up`] events has changed, due to Minecraft updating to LWJGL 3. Make sure you're - The "key code" for [`key`] and [`key_up`] events has changed, due to Minecraft updating to LWJGL 3. Make sure you're
using the constants provided by the [`keys`] API, rather than hard-coding numerical values. using the constants provided by the [`keys`] API, rather than hard-coding numerical values.

View File

@ -9,17 +9,19 @@ SPDX-License-Identifier: MPL-2.0
--> -->
# Lua 5.2/5.3 features in CC: Tweaked # Lua 5.2/5.3 features in CC: Tweaked
CC: Tweaked is based off of the Cobalt Lua runtime, which uses Lua 5.1. However, Cobalt and CC:T implement additional features from Lua 5.2 and 5.3 (as well as some deprecated 5.0 features) that are not available in base 5.1. This page lists all of the compatibility for these newer versions. CC: Tweaked is based off of the Cobalt Lua runtime, which uses Lua 5.2. However, Cobalt and CC:T implement additional
features from Lua 5.2 and 5.3 (as well as some deprecated 5.0 and 5.1 features). This page lists all of the
compatibility for these newer versions.
## Lua 5.2 ## Lua 5.2
| Feature | Supported? | Notes | | Feature | Supported? | Notes |
|---------------------------------------------------------------|------------|-------------------------------------------------------------------| |---------------------------------------------------------------|------------|-------------------------------------------------------------------|
| `goto`/labels | | | | `goto`/labels | | |
| `_ENV` | 🔶 | The `_ENV` global points to `getfenv()`, but it cannot be set. | | `_ENV` | ✔ | |
| `\z` escape | ✔ | | | `\z` escape | ✔ | |
| `\xNN` escape | ✔ | | | `\xNN` escape | ✔ | |
| Hex literal fractional/exponent parts | ✔ | | | Hex literal fractional/exponent parts | ✔ | |
| Empty statements | | | | Empty statements | | |
| `__len` metamethod | ✔ | | | `__len` metamethod | ✔ | |
| `__ipairs` metamethod | ❌ | Deprecated in Lua 5.3. `ipairs` uses `__len`/`__index` instead. | | `__ipairs` metamethod | ❌ | Deprecated in Lua 5.3. `ipairs` uses `__len`/`__index` instead. |
| `__pairs` metamethod | ✔ | | | `__pairs` metamethod | ✔ | |
@ -27,12 +29,12 @@ CC: Tweaked is based off of the Cobalt Lua runtime, which uses Lua 5.1. However,
| `collectgarbage` isrunning, generational, incremental options | ❌ | `collectgarbage` does not exist in CC:T. | | `collectgarbage` isrunning, generational, incremental options | ❌ | `collectgarbage` does not exist in CC:T. |
| New `load` syntax | ✔ | | | New `load` syntax | ✔ | |
| `loadfile` mode parameter | ✔ | Supports both 5.1 and 5.2+ syntax. | | `loadfile` mode parameter | ✔ | Supports both 5.1 and 5.2+ syntax. |
| Removed `loadstring` | 🔶 | Only if `disable_lua51_features` is enabled in the configuration. | | Removed `loadstring` | ❌ | |
| Removed `getfenv`, `setfenv` | 🔶 | Only if `disable_lua51_features` is enabled in the configuration. | | Removed `getfenv`, `setfenv` | 🔶 | Only supports closures with an `_ENV` upvalue. |
| `rawlen` function | ✔ | | | `rawlen` function | ✔ | |
| Negative index to `select` | ✔ | | | Negative index to `select` | ✔ | |
| Removed `unpack` | 🔶 | Only if `disable_lua51_features` is enabled in the configuration. | | Removed `unpack` | ❌ | |
| Arguments to `xpcall` | ✔ | | | Arguments to `xpcall` | ✔ | |
| Second return value from `coroutine.running` | ✔ | | | Second return value from `coroutine.running` | ✔ | |
| Removed `module` | ✔ | | | Removed `module` | ✔ | |
| `package.loaders` -> `package.searchers` | ❌ | | | `package.loaders` -> `package.searchers` | ❌ | |
@ -40,14 +42,14 @@ CC: Tweaked is based off of the Cobalt Lua runtime, which uses Lua 5.1. However,
| `package.config` | ✔ | | | `package.config` | ✔ | |
| `package.searchpath` | ✔ | | | `package.searchpath` | ✔ | |
| Removed `package.seeall` | ✔ | | | Removed `package.seeall` | ✔ | |
| `string.dump` on functions with upvalues (blanks them out) | ✔ | | | `string.dump` on functions with upvalues (blanks them out) | ❌ | `string.dump` is not supported |
| `string.rep` separator | ✔ | | | `string.rep` separator | ✔ | |
| `%g` match group | ❌ | | | `%g` match group | ❌ | |
| Removal of `%z` match group | ❌ | | | Removal of `%z` match group | ❌ | |
| Removed `table.maxn` | 🔶 | Only if `disable_lua51_features` is enabled in the configuration. | | Removed `table.maxn` | ❌ | |
| `table.pack`/`table.unpack` | ✔ | | | `table.pack`/`table.unpack` | ✔ | |
| `math.log` base argument | ✔ | | | `math.log` base argument | ✔ | |
| Removed `math.log10` | 🔶 | Only if `disable_lua51_features` is enabled in the configuration. | | Removed `math.log10` | ❌ | |
| `*L` mode to `file:read` | ✔ | | | `*L` mode to `file:read` | ✔ | |
| `os.execute` exit type + return value | ❌ | `os.execute` does not exist in CC:T. | | `os.execute` exit type + return value | ❌ | `os.execute` does not exist in CC:T. |
| `os.exit` close argument | ❌ | `os.exit` does not exist in CC:T. | | `os.exit` close argument | ❌ | `os.exit` does not exist in CC:T. |
@ -61,7 +63,7 @@ CC: Tweaked is based off of the Cobalt Lua runtime, which uses Lua 5.1. However,
| Tail call hooks | ❌ | | | Tail call hooks | ❌ | |
| `=` prefix for chunks | ✔ | | | `=` prefix for chunks | ✔ | |
| Yield across C boundary | ✔ | | | Yield across C boundary | ✔ | |
| Removal of ambiguity error | | | | Removal of ambiguity error | | |
| Identifiers may no longer use locale-dependent letters | ✔ | | | Identifiers may no longer use locale-dependent letters | ✔ | |
| Ephemeron tables | ❌ | | | Ephemeron tables | ❌ | |
| Identical functions may be reused | ❌ | Removed in Lua 5.4 | | Identical functions may be reused | ❌ | Removed in Lua 5.4 |

View File

@ -19,8 +19,8 @@ parchmentMc = "1.20.1"
asm = "9.5" asm = "9.5"
autoService = "1.1.1" autoService = "1.1.1"
checkerFramework = "3.32.0" checkerFramework = "3.32.0"
cobalt = "0.7.3" cobalt = "0.8.0"
cobalt-next = "0.7.4" # Not a real version, used to constrain the version we accept. cobalt-next = "0.8.1" # Not a real version, used to constrain the version we accept.
commonsCli = "1.3.1" commonsCli = "1.3.1"
fastutil = "8.5.9" fastutil = "8.5.9"
guava = "31.1-jre" guava = "31.1-jre"
@ -51,7 +51,7 @@ jqwik = "1.7.4"
junit = "5.10.0" junit = "5.10.0"
# Build tools # Build tools
cctJavadoc = "1.8.0" cctJavadoc = "1.8.1"
checkstyle = "10.12.3" checkstyle = "10.12.3"
curseForgeGradle = "1.0.14" curseForgeGradle = "1.0.14"
errorProne-core = "2.21.1" errorProne-core = "2.21.1"
@ -68,7 +68,7 @@ mixinGradle = "0.7.+"
nullAway = "0.9.9" nullAway = "0.9.9"
spotless = "6.21.0" spotless = "6.21.0"
taskTree = "2.1.1" taskTree = "2.1.1"
teavm = "0.9.0-SQUID.1" teavm = "0.10.0-SQUID.1"
vanillaGradle = "0.2.1-SNAPSHOT" vanillaGradle = "0.2.1-SNAPSHOT"
vineflower = "1.11.0" vineflower = "1.11.0"
@ -78,7 +78,7 @@ asm = { module = "org.ow2.asm:asm", version.ref = "asm" }
asm-commons = { module = "org.ow2.asm:asm-commons", version.ref = "asm" } asm-commons = { module = "org.ow2.asm:asm-commons", version.ref = "asm" }
autoService = { module = "com.google.auto.service:auto-service", version.ref = "autoService" } autoService = { module = "com.google.auto.service:auto-service", version.ref = "autoService" }
checkerFramework = { module = "org.checkerframework:checker-qual", version.ref = "checkerFramework" } checkerFramework = { module = "org.checkerframework:checker-qual", version.ref = "checkerFramework" }
cobalt = { module = "org.squiddev:Cobalt", version.ref = "cobalt" } cobalt = { module = "cc.tweaked:cobalt", version.ref = "cobalt" }
commonsCli = { module = "commons-cli:commons-cli", version.ref = "commonsCli" } commonsCli = { module = "commons-cli:commons-cli", version.ref = "commonsCli" }
fastutil = { module = "it.unimi.dsi:fastutil", version.ref = "fastutil" } fastutil = { module = "it.unimi.dsi:fastutil", version.ref = "fastutil" }
forgeSpi = { module = "net.minecraftforge:forgespi", version.ref = "forgeSpi" } forgeSpi = { module = "net.minecraftforge:forgespi", version.ref = "forgeSpi" }

View File

@ -73,20 +73,20 @@ public class CobaltLuaMachine implements ILuaMachine {
.build(); .build();
// Set up our global table. // 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 { 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); var value = LoadState.load(state, bios, "@bios.lua", globals);
mainRoutine = new LuaThread(state, value, globals); mainRoutine = new LuaThread(state, value);
} catch (CompileException e) { } catch (LuaError | CompileException e) {
throw new MachineException(Nullability.assertNonNull(e.getMessage())); throw new MachineException(Nullability.assertNonNull(e.getMessage()));
} }
@ -171,7 +171,7 @@ public class CobaltLuaMachine implements ILuaMachine {
return found ? table : null; 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 == null) return Constants.NIL;
if (object instanceof Number num) return ValueFactory.valueOf(num.doubleValue()); if (object instanceof Number num) return ValueFactory.valueOf(num.doubleValue());
if (object instanceof Boolean bool) return ValueFactory.valueOf(bool); if (object instanceof Boolean bool) return ValueFactory.valueOf(bool);
@ -235,7 +235,7 @@ public class CobaltLuaMachine implements ILuaMachine {
return Constants.NIL; 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 == null || objects.length == 0) return Constants.NONE;
if (objects.length == 1) return toValue(objects[0], null); if (objects.length == 1) return toValue(objects[0], null);

View File

@ -18,33 +18,6 @@ do
expect = f().expect expect = f().expect
end 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 -- Inject a stub for the old bit library
_G.bit = { _G.bit = {
bnot = bit32.bnot, bnot = bit32.bnot,

View File

@ -535,6 +535,28 @@ function errors.unexpected_end(start_pos, end_pos)
} }
end 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 -- 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 sub, find = string.sub, string.find
local keywords = { local keywords = {
["and"] = tokens.AND, ["break"] = tokens.BREAK, ["do"] = tokens.DO, ["else"] = tokens.ELSE, ["and"] = tokens.AND, ["break"] = tokens.BREAK, ["do"] = tokens.DO, ["else"] = tokens.ELSE,
["elseif"] = tokens.ELSEIF, ["end"] = tokens.END, ["false"] = tokens.FALSE, ["for"] = tokens.FOR, ["elseif"] = tokens.ELSEIF, ["end"] = tokens.END, ["false"] = tokens.FALSE, ["for"] = tokens.FOR,
["function"] = tokens.FUNCTION, ["if"] = tokens.IF, ["in"] = tokens.IN, ["local"] = tokens.LOCAL, ["function"] = tokens.FUNCTION, ["goto"] = tokens.GOTO, ["if"] = tokens.IF, ["in"] = tokens.IN,
["nil"] = tokens.NIL, ["not"] = tokens.NOT, ["or"] = tokens.OR, ["repeat"] = tokens.REPEAT, ["local"] = tokens.LOCAL, ["nil"] = tokens.NIL, ["not"] = tokens.NOT, ["or"] = tokens.OR,
["return"] = tokens.RETURN, ["then"] = tokens.THEN, ["true"] = tokens.TRUE, ["until"] = tokens.UNTIL, ["repeat"] = tokens.REPEAT, ["return"] = tokens.RETURN, ["then"] = tokens.THEN, ["true"] = tokens.TRUE,
["while"] = tokens.WHILE, ["until"] = tokens.UNTIL, ["while"] = tokens.WHILE,
} }
--- Lex a newline character --- Lex a newline character
@ -292,12 +292,15 @@ local function lex_token(context, str, pos)
local next_pos = pos + 1 local next_pos = pos + 1
if sub(str, next_pos, next_pos) == "=" then return tokens.LE, next_pos end if sub(str, next_pos, next_pos) == "=" then return tokens.LE, next_pos end
return tokens.GT, pos 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 elseif c == "~" and sub(str, pos + 1, pos + 1) == "=" then return tokens.NE, pos + 1
-- Single character tokens -- Single character tokens
elseif c == "," then return tokens.COMMA, pos elseif c == "," then return tokens.COMMA, pos
elseif c == ";" then return tokens.SEMICOLON, 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.OPAREN, pos
elseif c == ")" then return tokens.CPAREN, pos elseif c == ")" then return tokens.CPAREN, pos
elseif c == "]" then return tokens.CSQUARE, 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 it.unimi.dsi.fastutil.ints.IntSet;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.squiddev.cobalt.LuaError;
import org.squiddev.cobalt.LuaState;
import org.squiddev.cobalt.Prototype; import org.squiddev.cobalt.Prototype;
import org.squiddev.cobalt.compiler.CompileException; import org.squiddev.cobalt.compiler.CompileException;
import org.squiddev.cobalt.compiler.LuaC; import org.squiddev.cobalt.compiler.LuaC;
@ -108,9 +110,9 @@ class LuaCoverage {
Queue<Prototype> queue = new ArrayDeque<>(); Queue<Prototype> queue = new ArrayDeque<>();
try (InputStream stream = new FileInputStream(file)) { 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); queue.add(proto);
} catch (CompileException e) { } catch (LuaError | CompileException e) {
throw new IllegalStateException("Cannot compile", 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 dan200.computercraft.api.lua.LuaException;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.squiddev.cobalt.Constants; import org.squiddev.cobalt.Constants;
import org.squiddev.cobalt.LuaError;
import org.squiddev.cobalt.LuaTable; import org.squiddev.cobalt.LuaTable;
import org.squiddev.cobalt.ValueFactory; import org.squiddev.cobalt.ValueFactory;
@ -18,7 +19,11 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
class VarargArgumentsTest { class VarargArgumentsTest {
private static LuaTable tableWithCustomType() { private static LuaTable tableWithCustomType() {
var metatable = new LuaTable(); 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(); var table = new LuaTable();
table.setMetatable(null, metatable); table.setMetatable(null, metatable);

View File

@ -188,6 +188,9 @@ local function format(value)
-- TODO: Look into something like mbs's pretty printer. -- TODO: Look into something like mbs's pretty printer.
if type(value) == "string" and value:find("\n") then if type(value) == "string" and value:find("\n") then
return "<<<\n" .. value .. "\n>>>" 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 else
local ok, res = pcall(textutils.serialise, value) local ok, res = pcall(textutils.serialise, value)
if ok then return res else return tostring(value) end if ok then return res else return tostring(value) end

View File

@ -79,20 +79,6 @@ describe("The Lua base library", function()
end) end)
describe("load", function() 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) local function generator(parts)
return coroutine.wrap(function() return coroutine.wrap(function()
for i = 1, #parts do 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 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. 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 ```lua
do until do until
-- Line 1: 'end' expected near 'until' (program) -- 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 ```lua
for xyz , xyz while for xyz , xyz while
-- Line 1: 'in' expected near 'while' (program) -- 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 ```lua
if xyz then else until if xyz then else until
-- Line 1: 'end' expected near 'until' (program) -- 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} ```lua {repl_exprs}
{ xyz = xyz while { xyz = xyz while
-- Line 1: '}' expected near 'while' (repl_exprs) -- 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} ```lua {repl_exprs}
{ xyz while { xyz while
-- Line 1: '}' expected near 'while' (repl_exprs) -- 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. 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 # Function calls
## Additional commas ## Additional commas