mirror of
https://github.com/luau-lang/luau.git
synced 2024-11-16 06:45:44 +08:00
d40107dc60
It seems more consistent and unambiguous if we mark RFCs as being implemented when the implementation lands instead of expecting to cross-reference documentation. That also makes it easier for us to flag stale RFCs.
99 lines
4.7 KiB
Markdown
99 lines
4.7 KiB
Markdown
# continue statement
|
|
|
|
> Note: this RFC was adapted from an internal proposal that predates RFC process
|
|
|
|
**Status**: Implemented
|
|
|
|
## Summary
|
|
|
|
Add `continue` statement to `for`, `while` and `repeat` loops using a context-sensitive keyword to preserve compatibility.
|
|
|
|
## Motivation
|
|
|
|
`continue` statement is a feature present in basically all modern programming languages. It's great for ergonomics - often you want the loop to only process items of a specific kind, so you can say `if item.kind ~= "blah" then continue end` in the beginning of the loop.
|
|
|
|
`continue` never makes code that was previously impossible to write possible, but it makes some code easier to write.
|
|
|
|
We'd like to add this to Luau but we need to keep backwards compatibility - all existing scripts that parse correctly must parse as they do now. The rest of the proposal outlines the exact syntax and semantics that makes it possible.
|
|
|
|
## Design
|
|
|
|
`continue` statement shall be the statement that *starts* with "continue" identifier (*NOT* keyword - effectively it will be a context-sensitive keyword), and such that the *next* token is none of (`.`, `[`, `:`, `{`, `(`, `=`, string literal or ',').
|
|
|
|
These rules effectively say that continue statement is the statement that *does not* parse as a function call or the beginning of an assignment statement.
|
|
|
|
This is a continue statement:
|
|
|
|
```
|
|
do
|
|
continue
|
|
end
|
|
```
|
|
|
|
This is not a continue statement:
|
|
|
|
```
|
|
do
|
|
continue = 5
|
|
end
|
|
```
|
|
|
|
This is not a continue statement:
|
|
|
|
```
|
|
do
|
|
continue(5)
|
|
end
|
|
```
|
|
|
|
This is not a continue statement either, why do you ask?
|
|
|
|
```
|
|
do
|
|
continue, foo = table.unpack(...)
|
|
end
|
|
```
|
|
|
|
These rules are simple to implement. In any Lua parser there is already a point where you have to disambiguate an identifier that starts an assignment statement (`foo = 5`) from an identifier that starts a function call (`foo(5)`). It's one of the few, if not the only, place in the Lua grammar where single token lookahead is not sufficient to parse Lua, because you could have `foo.bar(5)` or `foo.bar=5` or `foo.bar(5)[6] = 7`.
|
|
|
|
Because of this, we need to parse the entire left hand side of an assignment statement (primaryexpr in Lua's BNF) and then check if it was a function call; if not, we'd expect it to be an assignment statement.
|
|
|
|
Alternatively in this specific case we could parse "continue", parse the next token, and if it's one of the exclusion list above, roll the parser state back and re-parse the non-continue statement. Our lexer currently doesn't support rollbacks but it's also an easy strategy that other implementations might employ for `continue` specifically.
|
|
|
|
The rules make it so that the only time we interpret `continue` as a continuation statement is when in the old Lua the program would not have compiled correctly - because this is not valid Lua 5.x:
|
|
|
|
```
|
|
do
|
|
continue
|
|
end
|
|
```
|
|
|
|
There is one case where this can create new confusion in the newly written code - code like this:
|
|
|
|
```
|
|
do
|
|
continue
|
|
(foo())(5)
|
|
end
|
|
```
|
|
|
|
could be interpreted both as a function call to `continue` (which it is!) and as a continuation statement followed by a function call (which it is not!). Programmers writing this code might expect the second treatment which is wrong.
|
|
|
|
We have an existing linter rule to prevent this, however *for now* we will solve this in a stronger way:
|
|
|
|
Once we parse `continue`, we will treat this as a block terminator - similarly to `break`/`return`, we will expect the block to end and the next statement will have to be `end`. This will make sure there's no ambiguity. We may relax this later and rely on the linter to tell people when the code is wrong.
|
|
|
|
Semantically, continue will work as you would expect - it would skip the rest of the loop body, evaluate the condition for loop continuation (e.g. check the counter value for numeric loops, call the loop iterator for generic loops, evaluate while/repeat condition for while/repeat loops) and proceed accordingly. Locals declared in the loop body would be closed as well.
|
|
|
|
One special case is the `until` expression: since it has access to the entire scope of `repeat` statement, using `continue` is invalid when it would result in `until` expression accessing local variables that are declared after `continue`.
|
|
|
|
## Drawbacks
|
|
|
|
Adding `continue` requires a context-sensitive keyword; this makes editor integration such as syntax highlighting more challenging, as you can't simply assume any occurrence of the word `continue` is referring to the statement - this is different from `break`.
|
|
|
|
Implementing `continue` requires special care for `until` statement as highlighted in the design, which may make compiler slower and more complicated.
|
|
|
|
## Alternatives
|
|
|
|
In later versions of Lua, instead of `continue` you can use `goto`. However, that changes control flow to be unstructured and requires more complex implementation and syntactic changes.
|