Skip to content

Add support for Lua 5.5 #3218

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 9 commits into
base: master
Choose a base branch
from
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ The Lua language server provides various language features for Lua to make devel

## Features

- ⚙️ Supports `Lua 5.4`, `Lua 5.3`, `Lua 5.2`, `Lua 5.1`, and `LuaJIT`
- ⚙️ Supports `Lua 5.5`, `Lua 5.4`, `Lua 5.3`, `Lua 5.2`, `Lua 5.1`, and `LuaJIT`
- 📄 Over 20 supported [annotations](https://luals.github.io/wiki/annotations/) for documenting your code
- ↪ Go to definition
- 🦺 Dynamic [type checking](https://luals.github.io/wiki/type-checking/)
Expand Down
3 changes: 3 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## Unreleased
<!-- Add all new changes here. They will be moved under a version at release -->
* `NEW` Add support for Lua 5.5 runtime version
* `NEW` Support `global` keyword syntax for Lua 5.5
* `NEW` Add diagnostic for read-only for-loop variables in Lua 5.5

## 3.15.0
`2025-6-25`
Expand Down
2 changes: 2 additions & 0 deletions locale/en-us/script.lua
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,8 @@ PARSER_MISS_SEP_IN_TABLE =
'Miss symbol `,` or `;` .'
PARSER_SET_CONST =
'Assignment to const variable.'
PARSER_SET_FOR_LOOP_VAR =
'Cannot assign to for-loop variable `{}` (read-only in Lua 5.5).'
PARSER_UNICODE_NAME =
'Contains Unicode characters.'
PARSER_ERR_NONSTANDARD_SYMBOL =
Expand Down
2 changes: 2 additions & 0 deletions locale/es-419/script.lua
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,8 @@ PARSER_MISS_SEP_IN_TABLE =
'Falta el símbolo `,` ó `;` .'
PARSER_SET_CONST =
'Asignación de valor a una variable constante.'
PARSER_SET_FOR_LOOP_VAR =
'No se puede asignar a la variable de bucle for `{}` (solo lectura en Lua 5.5).'
PARSER_UNICODE_NAME =
'Contiene caracteres Unicode.'
PARSER_ERR_NONSTANDARD_SYMBOL =
Expand Down
2 changes: 2 additions & 0 deletions locale/ja-jp/script.lua
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,8 @@ PARSER_MISS_SEP_IN_TABLE =
'区切りには `,` または `;` が必要です。'
PARSER_SET_CONST =
'定数に値を代入できません。'
PARSER_SET_FOR_LOOP_VAR =
'forループ変数`{}`に代入できません(Lua 5.5では読み取り専用)。'
PARSER_UNICODE_NAME =
'Unicode 文字が含まれています。'
PARSER_ERR_NONSTANDARD_SYMBOL =
Expand Down
2 changes: 2 additions & 0 deletions locale/pt-br/script.lua
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,8 @@ PARSER_MISS_SEP_IN_TABLE =
'Falta o símbolo `,` ou `;` .'
PARSER_SET_CONST =
'Atribuição à variável constante.'
PARSER_SET_FOR_LOOP_VAR =
'Não é possível atribuir à variável de loop for `{}` (somente leitura no Lua 5.5).'
PARSER_UNICODE_NAME =
'Contém caracteres Unicode.'
PARSER_ERR_NONSTANDARD_SYMBOL =
Expand Down
4 changes: 4 additions & 0 deletions locale/zh-cn/script.lua
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ DIAG_UNUSED_VARARG =
'未使用的不定参数。'
DIAG_REDEFINED_LOCAL =
'重定义局部变量 `{}`。'
DIAG_READONLY_FOR_LOOP_VAR =
'无法给 for 循环变量 `{}` 赋值(在 Lua 5.5 中为只读)。'
DIAG_DUPLICATE_INDEX =
'重复的索引 `{}`。'
DIAG_DUPLICATE_METHOD =
Expand Down Expand Up @@ -293,6 +295,8 @@ PARSER_MISS_SEP_IN_TABLE =
'需要用`,`或`;`进行分割。'
PARSER_SET_CONST =
'不能对常量赋值。'
PARSER_SET_FOR_LOOP_VAR =
'不能对for循环变量`{}`赋值(在Lua 5.5中只读)。'
PARSER_UNICODE_NAME =
'包含了 Unicode 字符。'
PARSER_ERR_NONSTANDARD_SYMBOL =
Expand Down
2 changes: 2 additions & 0 deletions locale/zh-tw/script.lua
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,8 @@ PARSER_MISS_SEP_IN_TABLE =
'需要用 `,` 或 `;` 進行分割。'
PARSER_SET_CONST =
'不能對常數賦值。'
PARSER_SET_FOR_LOOP_VAR =
'不能對for迴圈變數`{}`賦值(在Lua 5.5中唯讀)。'
PARSER_UNICODE_NAME =
'包含了 Unicode 字元。'
PARSER_ERR_NONSTANDARD_SYMBOL =
Expand Down
2 changes: 2 additions & 0 deletions meta/template/basic.lua
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,8 @@ _VERSION = "Lua 5.2"
_VERSION = "Lua 5.3"
---#elseif VERSION == 5.4 then
_VERSION = "Lua 5.4"
---#elseif VERSION == 5.5 then
_VERSION = "Lua 5.5"
---#end

---@version >5.4
Expand Down
1 change: 1 addition & 0 deletions script/config/template.lua
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ local template = {
'Lua 5.2',
'Lua 5.3',
'Lua 5.4',
'Lua 5.5',
'LuaJIT',
},
['Lua.runtime.path'] = Type.Array(Type.String) >> {
Expand Down
8 changes: 8 additions & 0 deletions script/core/completion/keyword.lua
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,14 @@ end"
end
return false
end },
{ 'global', function(info, results)
local version = config.get(info.uri, 'Lua.runtime.version')
if version ~= 'Lua 5.5' then
return false
end
-- Note: No special completion for 'global function' syntax as it doesn't exist in Lua 5.5
return false
end },
{ 'nil' },
{ 'not' },
{ 'or' },
Expand Down
4 changes: 4 additions & 0 deletions script/library.lua
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ local function getDocFormater(uri)
return 'HOVER_NATIVE_DOCUMENT_LUA53'
elseif version == 'Lua 5.4' then
return 'HOVER_NATIVE_DOCUMENT_LUA54'
elseif version == 'Lua 5.5' then
return 'HOVER_NATIVE_DOCUMENT_LUA54' -- Use 5.4 docs for 5.5 until 5.5 specific docs are available
elseif version == 'LuaJIT' then
return 'HOVER_NATIVE_DOCUMENT_LUAJIT'
end
Expand All @@ -43,6 +45,8 @@ local function getDocFormater(uri)
return 'HOVER_DOCUMENT_LUA53'
elseif version == 'Lua 5.4' then
return 'HOVER_DOCUMENT_LUA54'
elseif version == 'Lua 5.5' then
return 'HOVER_DOCUMENT_LUA54' -- Use 5.4 docs for 5.5 until 5.5 specific docs are available
elseif version == 'LuaJIT' then
return 'HOVER_DOCUMENT_LUAJIT'
end
Expand Down
125 changes: 113 additions & 12 deletions script/parser/compile.lua
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ local NLMap = {
local LineMulti = 10000

-- goto 单独处理
-- global 单独处理
local KeyWord = {
['and'] = true,
['break'] = true,
Expand Down Expand Up @@ -262,6 +263,36 @@ local function addSpecial(name, obj)
obj.special = name
end

---@param local parser.object
---@return boolean
local function isForLoopVariable(local_)
-- Check if this local is a for-loop variable
if not local_ or local_.type ~= 'local' then
return false
end

local parent = local_.parent
if not parent then
return false
end

-- Check if parent is a numeric for-loop
if parent.type == 'loop' and parent.loc == local_ then
return true
end

-- Check if parent is a for-in loop
if parent.type == 'in' and parent.keys then
for i = 1, #parent.keys do
if parent.keys[i] == local_ then
return true
end
end
end

return false
end

---@param offset integer
---@param leftOrRight '"left"'|'"right"'
local function getPosition(offset, leftOrRight)
Expand Down Expand Up @@ -706,12 +737,12 @@ local function parseLocalAttrs()
else
missSymbol '>'
end
if State.version ~= 'Lua 5.4' then
if State.version ~= 'Lua 5.4' and State.version ~= 'Lua 5.5' then
pushError {
type = 'UNSUPPORT_SYMBOL',
start = attr.start,
finish = attr.finish,
version = 'Lua 5.4',
version = {'Lua 5.4', 'Lua 5.5'},
info = {
version = State.version
}
Expand Down Expand Up @@ -752,6 +783,19 @@ local function createLocal(obj, attrs)
return obj
end

---@param obj table
local function createGlobal(obj, attrs)
obj.type = 'setglobal'
obj.effect = obj.finish

if attrs then
obj.attrs = attrs
attrs.parent = obj
end

return obj
end

local function pushChunk(chunk)
Chunk[#Chunk+1] = chunk
end
Expand Down Expand Up @@ -906,13 +950,14 @@ local function parseStringUnicode()
end
if State.version ~= 'Lua 5.3'
and State.version ~= 'Lua 5.4'
and State.version ~= 'Lua 5.5'
and State.version ~= 'LuaJIT'
then
pushError {
type = 'ERR_ESC',
start = leftPos - 2,
finish = rightPos,
version = {'Lua 5.3', 'Lua 5.4', 'LuaJIT'},
version = {'Lua 5.3', 'Lua 5.4', 'Lua 5.5', 'LuaJIT'},
info = {
version = State.version,
}
Expand All @@ -932,7 +977,7 @@ local function parseStringUnicode()
end
return nil, offset
end
if State.version == 'Lua 5.4' then
if State.version == 'Lua 5.4' or State.version == 'Lua 5.5' then
if byte < 0 or byte > 0x7FFFFFFF then
pushError {
type = 'UTF8_MAX',
Expand All @@ -951,7 +996,7 @@ local function parseStringUnicode()
type = 'UTF8_MAX',
start = leftPos,
finish = rightPos,
version = byte <= 0x7FFFFFFF and 'Lua 5.4' or nil,
version = (byte <= 0x7FFFFFFF and {'Lua 5.4', 'Lua 5.5'}) or nil,
info = {
min = '000000',
max = '10FFFF',
Expand Down Expand Up @@ -1095,7 +1140,7 @@ local function parseShortString()
type = 'ERR_ESC',
start = left,
finish = left + 4,
version = {'Lua 5.2', 'Lua 5.3', 'Lua 5.4', 'LuaJIT'},
version = {'Lua 5.2', 'Lua 5.3', 'Lua 5.4', 'Lua 5.5', 'LuaJIT'},
info = {
version = State.version,
}
Expand Down Expand Up @@ -1274,7 +1319,7 @@ local function parseNumber2(start)
finish = getPosition(offset - 1, 'right'),
version = 'LuaJIT',
info = {
version = 'Lua 5.4',
version = {'Lua 5.4', 'Lua 5.5'},
}
}
end
Expand Down Expand Up @@ -1409,6 +1454,18 @@ local function isKeyWord(word, nextToken)
end
return true
end
if word == 'global' then
if State.version ~= 'Lua 5.5' then
return false
end
if not nextToken then
return false
end
if CharMapWord[ssub(nextToken, 1, 1)] then
return true
end
return false
end
return false
end

Expand Down Expand Up @@ -2673,10 +2730,11 @@ local function parseBinaryOP(asAction, level)
or token == '<<'
or token == '>>' then
if State.version ~= 'Lua 5.3'
and State.version ~= 'Lua 5.4' then
and State.version ~= 'Lua 5.4'
and State.version ~= 'Lua 5.5' then
pushError {
type = 'UNSUPPORT_SYMBOL',
version = {'Lua 5.3', 'Lua 5.4'},
version = {'Lua 5.3', 'Lua 5.4', 'Lua 5.5'},
start = op.start,
finish = op.finish,
info = {
Expand Down Expand Up @@ -2893,6 +2951,15 @@ local function bindValue(n, v, index, lastValue, isLocal, isSet)
start = n.start,
finish = n.finish,
}
elseif State.version == 'Lua 5.5' and isForLoopVariable(loc) then
pushError {
type = 'SET_FOR_LOOP_VAR',
start = n.start,
finish = n.finish,
info = {
name = loc[1],
},
}
end
end
end
Expand Down Expand Up @@ -3119,6 +3186,27 @@ local function parseLocal()
return loc
end

local function parseGlobal()
local globalPos = getPosition(Tokens[Index], 'left')

Index = Index + 2
skipSpace()

local name = parseName(true)
if not name then
missName()
return nil
end
local glob = createGlobal(name)
glob.globPos = globalPos
glob.effect = maxinteger
pushActionIntoCurrentChunk(glob)
skipSpace()
parseMultiVars(glob, parseName, false)

return glob
end

local function parseDo()
local doLeft = getPosition(Tokens[Index], 'left')
local doRight = getPosition(Tokens[Index] + 1, 'right')
Expand Down Expand Up @@ -3229,7 +3317,7 @@ local function parseLabel()
local name = label[1]
local olabel = guide.getLabel(block, name)
if olabel then
if State.version == 'Lua 5.4'
if (State.version == 'Lua 5.4' or State.version == 'Lua 5.5')
or block == guide.getBlock(olabel) then
pushError {
type = 'REDEFINED_LABEL',
Expand All @@ -3252,7 +3340,7 @@ local function parseLabel()
type = 'UNSUPPORT_SYMBOL',
start = left,
finish = lastRightPosition(),
version = {'Lua 5.2', 'Lua 5.3', 'Lua 5.4', 'LuaJIT'},
version = {'Lua 5.2', 'Lua 5.3', 'Lua 5.4', 'Lua 5.5', 'LuaJIT'},
info = {
version = State.version,
}
Expand Down Expand Up @@ -3634,7 +3722,7 @@ local function parseFor()
missExp()
end

if State.version == 'Lua 5.4' then
if State.version == 'Lua 5.4' or State.version == 'Lua 5.5' then
forStateVars = 4
else
forStateVars = 3
Expand Down Expand Up @@ -3902,6 +3990,10 @@ function parseAction()
return parseLocal()
end

if token == 'global' and isKeyWord('global', Tokens[Index + 3]) then
return parseGlobal()
end

if token == 'if'
or token == 'elseif'
or token == 'else' then
Expand Down Expand Up @@ -3958,6 +4050,15 @@ function parseAction()
start = name.start,
finish = name.finish,
}
elseif State.version == 'Lua 5.5' and isForLoopVariable(loc) then
pushError {
type = 'SET_FOR_LOOP_VAR',
start = name.start,
finish = name.finish,
info = {
name = loc[1],
},
}
end
end
pushActionIntoCurrentChunk(name)
Expand Down
Loading