refactor(build): include lpeg as a library

This commit is contained in:
bfredl 2023-04-20 13:19:38 +02:00
parent 9f0762f1fe
commit 45bcf83869
19 changed files with 376 additions and 52 deletions

View File

@ -43,5 +43,6 @@ globals = {
exclude_files = {
'test/functional/fixtures/lua/syntax_error.lua',
'runtime/lua/vim/treesitter/_meta.lua'
'runtime/lua/vim/treesitter/_meta.lua',
'runtime/lua/vim/re.lua',
}

View File

@ -1,3 +1,4 @@
/scripts
/src
/test
/runtime/lua/vim/re.lua

View File

@ -160,27 +160,20 @@ foreach(CFGNAME ${CMAKE_CONFIGURATION_TYPES})
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${CFGNAME} ${CMAKE_BINARY_DIR}/lib)
endforeach()
set(LUA_DEPENDENCIES lpeg)
if(NOT LUA_PRG)
foreach(CURRENT_LUA_PRG luajit lua5.1 lua5.2 lua)
unset(_CHECK_LUA_PRG CACHE)
unset(LUA_PRG_WORKS)
find_program(_CHECK_LUA_PRG ${CURRENT_LUA_PRG})
if(_CHECK_LUA_PRG)
check_lua_deps(${_CHECK_LUA_PRG} "${LUA_DEPENDENCIES}" LUA_PRG_WORKS)
if(LUA_PRG_WORKS)
set(LUA_PRG "${_CHECK_LUA_PRG}" CACHE FILEPATH "Path to a program.")
break()
endif()
set(LUA_PRG "${_CHECK_LUA_PRG}" CACHE FILEPATH "Path to a program.")
break()
endif()
endforeach()
unset(_CHECK_LUA_PRG CACHE)
else()
check_lua_deps(${LUA_PRG} "${LUA_DEPENDENCIES}" LUA_PRG_WORKS)
endif()
if(NOT LUA_PRG_WORKS)
if(NOT LUA_PRG)
message(FATAL_ERROR "Failed to find a Lua 5.1-compatible interpreter")
endif()

View File

@ -41,6 +41,7 @@ set(DEPS_BIN_DIR "${DEPS_INSTALL_DIR}/bin")
set(DEPS_LIB_DIR "${DEPS_INSTALL_DIR}/lib")
set(DEPS_BUILD_DIR "${CMAKE_BINARY_DIR}/build")
set(DEPS_DOWNLOAD_DIR "${DEPS_BUILD_DIR}/downloads")
set(DEPS_INCLUDE_FLAGS "-I${DEPS_INSTALL_DIR}/include -I${DEPS_INSTALL_DIR}/include/luajit-2.1")
list(APPEND DEPS_CMAKE_ARGS -D CMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR})
@ -54,6 +55,7 @@ option(USE_BUNDLED_MSGPACK "Use the bundled msgpack." ${USE_BUNDLED})
option(USE_BUNDLED_LUAJIT "Use the bundled version of luajit." ${USE_BUNDLED})
option(USE_BUNDLED_LUAROCKS "Use the bundled version of luarocks." ${USE_BUNDLED})
option(USE_BUNDLED_LUV "Use the bundled version of luv." ${USE_BUNDLED})
option(USE_BUNDLED_LPEG "Use the bundled lpeg." ${USE_BUNDLED})
#XXX(tarruda): Lua is only used for debugging the functional test client, don't
# build it unless explicitly requested
option(USE_BUNDLED_LUA "Use the bundled version of lua." OFF)
@ -161,6 +163,9 @@ set(LIBVTERM_SHA256 25a8ad9c15485368dfd0a8a9dca1aec8fea5c27da3fa74ec518d5d3787f0
set(LUV_URL https://github.com/luvit/luv/archive/093a977b82077591baefe1e880d37dfa2730bd54.tar.gz)
set(LUV_SHA256 222b38b6425f0926218e14e7da81481fdde6f9660c1feac25a53e6fb52e886e6)
set(LPEG_URL http://www.inf.puc-rio.br/~roberto/lpeg/lpeg-1.0.2.tar.gz)
set(LPEG_SHA256 48d66576051b6c78388faad09b70493093264588fcd0f258ddaab1cdd4a15ffe)
set(LUA_COMPAT53_URL https://github.com/keplerproject/lua-compat-5.3/archive/v0.9.tar.gz)
set(LUA_COMPAT53_SHA256 ad05540d2d96a48725bb79a1def35cf6652a4e2ec26376e2617c8ce2baa6f416)
@ -245,6 +250,10 @@ if(USE_BUNDLED_LUV)
include(BuildLuv)
endif()
if(USE_BUNDLED_LPEG)
include(BuildLpeg)
endif()
if(USE_BUNDLED_GETTEXT)
include(BuildGettext)
endif()

View File

@ -0,0 +1,18 @@
list(APPEND LPEG_CMAKE_ARGS "-DCMAKE_C_FLAGS:STRING=${DEPS_INCLUDE_FLAGS}")
ExternalProject_Add(lpeg
URL ${LPEG_URL}
URL_HASH SHA256=${LPEG_SHA256}
DOWNLOAD_NO_PROGRESS TRUE
DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/lpeg
PATCH_COMMAND ${CMAKE_COMMAND} -E copy
${CMAKE_CURRENT_SOURCE_DIR}/cmake/LpegCMakeLists.txt
${DEPS_BUILD_DIR}/src/lpeg/CMakeLists.txt
CMAKE_ARGS ${DEPS_CMAKE_ARGS} ${LPEG_CMAKE_ARGS}
CMAKE_CACHE_ARGS ${DEPS_CMAKE_CACHE_ARGS})
if(USE_BUNDLED_LUAJIT)
add_dependencies(lpeg luajit)
elseif(USE_BUNDLED_LUA)
add_dependencies(lpeg lua)
endif()

View File

@ -119,8 +119,6 @@ function(Download ROCK VER)
set(CURRENT_DEP ${ROCK} PARENT_SCOPE)
endfunction()
Download(lpeg 1.0.2-1)
if(USE_BUNDLED_BUSTED)
if(WIN32)
set(BUSTED_EXE "${DEPS_BIN_DIR}/busted.bat")

View File

@ -1,6 +1,3 @@
set(LUV_INCLUDE_FLAGS
"-I${DEPS_INSTALL_DIR}/include -I${DEPS_INSTALL_DIR}/include/luajit-2.1")
set(LUV_CMAKE_ARGS
-D LUA_BUILD_TYPE=System
-D LUA_COMPAT53_DIR=${DEPS_BUILD_DIR}/src/lua-compat-5.3
@ -26,8 +23,7 @@ if(USE_BUNDLED_LIBUV)
list(APPEND LUV_CMAKE_ARGS -D CMAKE_PREFIX_PATH=${DEPS_INSTALL_DIR})
endif()
list(APPEND LUV_CMAKE_ARGS
"-DCMAKE_C_FLAGS:STRING=${LUV_INCLUDE_FLAGS}")
list(APPEND LUV_CMAKE_ARGS "-DCMAKE_C_FLAGS:STRING=${DEPS_INCLUDE_FLAGS}")
if(CMAKE_GENERATOR MATCHES "Unix Makefiles" AND
(CMAKE_SYSTEM_NAME MATCHES ".*BSD" OR CMAKE_SYSTEM_NAME MATCHES "DragonFly"))
list(APPEND LUV_CMAKE_ARGS -D CMAKE_MAKE_PROGRAM=gmake)

View File

@ -0,0 +1,11 @@
cmake_minimum_required(VERSION 3.10)
project (lpeg C)
include(GNUInstallDirs)
file(GLOB LPEG_SOURCES ${CMAKE_SOURCE_DIR}/*.c)
add_library(lpeg ${LPEG_SOURCES})
install(TARGETS lpeg ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
# vim: set ft=cmake:

14
cmake/FindLpeg.cmake Normal file
View File

@ -0,0 +1,14 @@
find_library(LPEG_LIBRARY NAMES lpeg_a lpeg liblpeg_a)
# Ubuntu-specific workaround to find system paths
function(ubuntu)
set(CMAKE_FIND_LIBRARY_PREFIXES "")
find_library(LPEG_LIBRARY NAMES lpeg PATH_SUFFIXES lua/5.1)
endfunction()
ubuntu()
find_package_handle_standard_args(Lpeg DEFAULT_MSG LPEG_LIBRARY)
mark_as_advanced(LPEG_LIBRARY)
add_library(lpeg INTERFACE)
target_link_libraries(lpeg INTERFACE ${LPEG_LIBRARY})

View File

@ -12,26 +12,3 @@ function(check_lua_module LUA_PRG_PATH MODULE RESULT_VAR)
set(${RESULT_VAR} True PARENT_SCOPE)
endif()
endfunction()
# Check Lua interpreter for dependencies
function(check_lua_deps LUA_PRG_PATH MODULES RESULT_VAR)
# Check if the lua interpreter at the given path
# satisfies all Neovim dependencies
message(STATUS "Checking Lua interpreter: ${LUA_PRG_PATH}")
if(NOT EXISTS ${LUA_PRG_PATH})
message(STATUS
"[${LUA_PRG_PATH}] file not found")
endif()
foreach(module ${MODULES})
check_lua_module(${LUA_PRG_PATH} ${module} has_module)
if(NOT has_module)
message(STATUS
"[${LUA_PRG_PATH}] The '${module}' lua package is required for building Neovim")
set(${RESULT_VAR} False PARENT_SCOPE)
return()
endif()
endforeach()
set(${RESULT_VAR} True PARENT_SCOPE)
endfunction()

View File

@ -648,6 +648,14 @@ regex:match_line({bufnr}, {line_idx} [, {start}, {end}]) *regex:match_line()*
|regex:match_str()|. If {start} is used, then the returned byte indices
will be relative {start}.
------------------------------------------------------------------------------
VIM.LPEG *lua-lpeg*
*vim.lpeg* *vim.re*
The Lpeg library for parsing expression grammars is being included as
`vim.lpeg` (https://www.inf.puc-rio.br/~roberto/lpeg/). In addition, its regex-like
interface is available as `vim.re` (https://www.inf.puc-rio.br/~roberto/lpeg/re.html).
------------------------------------------------------------------------------
VIM.DIFF *lua-diff*

View File

@ -56,6 +56,7 @@ vim._submodules = {
version = true,
fs = true,
iter = true,
re = true,
}
-- These are for loading runtime modules in the vim namespace lazily.

269
runtime/lua/vim/re.lua Normal file
View File

@ -0,0 +1,269 @@
-- $Id: re.lua $
-- vendored from lpeg-1.0.2
-- Copyright © 2007-2019 Lua.org, PUC-Rio.
-- imported functions and modules
local tonumber, type, print, error = tonumber, type, print, error
local setmetatable = setmetatable
local m = require"lpeg"
-- 'm' will be used to parse expressions, and 'mm' will be used to
-- create expressions; that is, 're' runs on 'm', creating patterns
-- on 'mm'
local mm = m
-- pattern's metatable
local mt = getmetatable(mm.P(0))
-- No more global accesses after this point
local version = _VERSION
if version == "Lua 5.2" then _ENV = nil end
local any = m.P(1)
-- Pre-defined names
local Predef = { nl = m.P"\n" }
local mem
local fmem
local gmem
local function updatelocale ()
mm.locale(Predef)
Predef.a = Predef.alpha
Predef.c = Predef.cntrl
Predef.d = Predef.digit
Predef.g = Predef.graph
Predef.l = Predef.lower
Predef.p = Predef.punct
Predef.s = Predef.space
Predef.u = Predef.upper
Predef.w = Predef.alnum
Predef.x = Predef.xdigit
Predef.A = any - Predef.a
Predef.C = any - Predef.c
Predef.D = any - Predef.d
Predef.G = any - Predef.g
Predef.L = any - Predef.l
Predef.P = any - Predef.p
Predef.S = any - Predef.s
Predef.U = any - Predef.u
Predef.W = any - Predef.w
Predef.X = any - Predef.x
mem = {} -- restart memoization
fmem = {}
gmem = {}
local mt = {__mode = "v"}
setmetatable(mem, mt)
setmetatable(fmem, mt)
setmetatable(gmem, mt)
end
updatelocale()
local I = m.P(function (s,i) print(i, s:sub(1, i-1)); return i end)
local function patt_error (s, i)
local msg = (#s < i + 20) and s:sub(i)
or s:sub(i,i+20) .. "..."
msg = ("pattern error near '%s'"):format(msg)
error(msg, 2)
end
local function mult (p, n)
local np = mm.P(true)
while n >= 1 do
if n%2 >= 1 then np = np * p end
p = p * p
n = n/2
end
return np
end
local function equalcap (s, i, c)
if type(c) ~= "string" then return nil end
local e = #c + i
if s:sub(i, e - 1) == c then return e else return nil end
end
local S = (Predef.space + "--" * (any - Predef.nl)^0)^0
local name = m.R("AZ", "az", "__") * m.R("AZ", "az", "__", "09")^0
local arrow = S * "<-"
local seq_follow = m.P"/" + ")" + "}" + ":}" + "~}" + "|}" + (name * arrow) + -1
name = m.C(name)
-- a defined name only have meaning in a given environment
local Def = name * m.Carg(1)
local function getdef (id, defs)
local c = defs and defs[id]
if not c then error("undefined name: " .. id) end
return c
end
-- match a name and return a group of its corresponding definition
-- and 'f' (to be folded in 'Suffix')
local function defwithfunc (f)
return m.Cg(Def / getdef * m.Cc(f))
end
local num = m.C(m.R"09"^1) * S / tonumber
local String = "'" * m.C((any - "'")^0) * "'" +
'"' * m.C((any - '"')^0) * '"'
local defined = "%" * Def / function (c,Defs)
local cat = Defs and Defs[c] or Predef[c]
if not cat then error ("name '" .. c .. "' undefined") end
return cat
end
local Range = m.Cs(any * (m.P"-"/"") * (any - "]")) / mm.R
local item = (defined + Range + m.C(any)) / m.P
local Class =
"["
* (m.C(m.P"^"^-1)) -- optional complement symbol
* m.Cf(item * (item - "]")^0, mt.__add) /
function (c, p) return c == "^" and any - p or p end
* "]"
local function adddef (t, k, exp)
if t[k] then
error("'"..k.."' already defined as a rule")
else
t[k] = exp
end
return t
end
local function firstdef (n, r) return adddef({n}, n, r) end
local function NT (n, b)
if not b then
error("rule '"..n.."' used outside a grammar")
else return mm.V(n)
end
end
local exp = m.P{ "Exp",
Exp = S * ( m.V"Grammar"
+ m.Cf(m.V"Seq" * ("/" * S * m.V"Seq")^0, mt.__add) );
Seq = m.Cf(m.Cc(m.P"") * m.V"Prefix"^0 , mt.__mul)
* (#seq_follow + patt_error);
Prefix = "&" * S * m.V"Prefix" / mt.__len
+ "!" * S * m.V"Prefix" / mt.__unm
+ m.V"Suffix";
Suffix = m.Cf(m.V"Primary" * S *
( ( m.P"+" * m.Cc(1, mt.__pow)
+ m.P"*" * m.Cc(0, mt.__pow)
+ m.P"?" * m.Cc(-1, mt.__pow)
+ "^" * ( m.Cg(num * m.Cc(mult))
+ m.Cg(m.C(m.S"+-" * m.R"09"^1) * m.Cc(mt.__pow))
)
+ "->" * S * ( m.Cg((String + num) * m.Cc(mt.__div))
+ m.P"{}" * m.Cc(nil, m.Ct)
+ defwithfunc(mt.__div)
)
+ "=>" * S * defwithfunc(m.Cmt)
+ "~>" * S * defwithfunc(m.Cf)
) * S
)^0, function (a,b,f) return f(a,b) end );
Primary = "(" * m.V"Exp" * ")"
+ String / mm.P
+ Class
+ defined
+ "{:" * (name * ":" + m.Cc(nil)) * m.V"Exp" * ":}" /
function (n, p) return mm.Cg(p, n) end
+ "=" * name / function (n) return mm.Cmt(mm.Cb(n), equalcap) end
+ m.P"{}" / mm.Cp
+ "{~" * m.V"Exp" * "~}" / mm.Cs
+ "{|" * m.V"Exp" * "|}" / mm.Ct
+ "{" * m.V"Exp" * "}" / mm.C
+ m.P"." * m.Cc(any)
+ (name * -arrow + "<" * name * ">") * m.Cb("G") / NT;
Definition = name * arrow * m.V"Exp";
Grammar = m.Cg(m.Cc(true), "G") *
m.Cf(m.V"Definition" / firstdef * m.Cg(m.V"Definition")^0,
adddef) / mm.P
}
local pattern = S * m.Cg(m.Cc(false), "G") * exp / mm.P * (-any + patt_error)
local function compile (p, defs)
if mm.type(p) == "pattern" then return p end -- already compiled
local cp = pattern:match(p, 1, defs)
if not cp then error("incorrect pattern", 3) end
return cp
end
local function match (s, p, i)
local cp = mem[p]
if not cp then
cp = compile(p)
mem[p] = cp
end
return cp:match(s, i or 1)
end
local function find (s, p, i)
local cp = fmem[p]
if not cp then
cp = compile(p) / 0
cp = mm.P{ mm.Cp() * cp * mm.Cp() + 1 * mm.V(1) }
fmem[p] = cp
end
local i, e = cp:match(s, i or 1)
if i then return i, e - 1
else return i
end
end
local function gsub (s, p, rep)
local g = gmem[p] or {} -- ensure gmem[p] is not collected while here
gmem[p] = g
local cp = g[rep]
if not cp then
cp = compile(p)
cp = mm.Cs((cp / rep + 1)^0)
g[rep] = cp
end
return cp:match(s)
end
-- exported names
local re = {
compile = compile,
match = match,
find = find,
gsub = gsub,
updatelocale = updatelocale,
}
if version == "Lua 5.1" then _G.re = re end
return re

View File

@ -9,5 +9,10 @@ LUA_API int luaopen_nlua0(lua_State* L) {
luaopen_mpack(L);
lua_setfield(L, -2, "mpack");
int luaopen_lpeg(lua_State *);
luaopen_lpeg(L);
lua_setfield(L, -3, "lpeg");
lua_pop(L, 2);
return 1;
}

View File

@ -26,6 +26,7 @@ target_include_directories(main_lib SYSTEM BEFORE INTERFACE ${LIBLUV_INCLUDE_DIR
target_link_libraries(main_lib INTERFACE ${LIBLUV_LIBRARY})
find_package(Iconv REQUIRED)
find_package(Lpeg REQUIRED)
find_package(Libtermkey 0.22 REQUIRED)
find_package(Libvterm 0.3 REQUIRED)
find_package(Msgpack 1.0.0 REQUIRED)
@ -38,7 +39,9 @@ target_link_libraries(main_lib INTERFACE
libvterm
msgpack
treesitter
unibilium)
unibilium
lpeg)
target_link_libraries(nlua0 PUBLIC lpeg)
# Libintl (not Intl) selects our FindLibintl.cmake script. #8464
find_package(Libintl REQUIRED)
@ -324,6 +327,7 @@ if(PREFER_LUA)
endif()
list(APPEND NLUA0_SOURCES ${PROJECT_SOURCE_DIR}/src/nlua0.c)
foreach(subdir
os
api
@ -443,6 +447,9 @@ add_custom_command(
"${NVIM_VERSION_DEF_H}"
DEPENDS "${PROJECT_BINARY_DIR}/cmake.config/auto/versiondef-$<CONFIG>.h")
set(LUA_GEN ${LUA_GEN_PRG} ${GENERATOR_PRELOAD} ${PROJECT_SOURCE_DIR} $<TARGET_FILE:nlua0>)
set(LUA_GEN_DEPS ${GENERATOR_PRELOAD} $<TARGET_FILE:nlua0>)
# NVIM_GENERATED_FOR_HEADERS: generated headers to be included in headers
# NVIM_GENERATED_FOR_SOURCES: generated headers to be included in sources
# NVIM_GENERATED_SOURCES: generated source files
@ -476,7 +483,7 @@ foreach(sfile ${NVIM_SOURCES}
set(PREPROC_OUTPUT -E -o ${gf_i})
endif()
set(depends "${HEADER_GENERATOR}" "${sfile}")
set(depends "${HEADER_GENERATOR}" "${sfile}" "${LUA_GEN_DEPS}")
if("${f}" STREQUAL "version.c")
# Ensure auto/versiondef_git.h exists after "make clean".
list(APPEND depends update_version_stamp "${NVIM_VERSION_GIT_H}" "${NVIM_VERSION_DEF_H}")
@ -484,7 +491,7 @@ foreach(sfile ${NVIM_SOURCES}
add_custom_command(
OUTPUT "${gf_c_h}" "${gf_h_h}"
COMMAND ${CMAKE_C_COMPILER} ${sfile} ${PREPROC_OUTPUT} ${gen_cflags}
COMMAND "${LUA_PRG}" "${HEADER_GENERATOR}" "${sfile}" "${gf_c_h}" "${gf_h_h}" "${gf_i}"
COMMAND ${LUA_GEN} "${HEADER_GENERATOR}" "${sfile}" "${gf_c_h}" "${gf_h_h}" "${gf_i}"
DEPENDS ${depends})
list(APPEND NVIM_GENERATED_FOR_SOURCES "${gf_c_h}")
list(APPEND NVIM_GENERATED_FOR_HEADERS "${gf_h_h}")
@ -502,9 +509,6 @@ add_custom_command(OUTPUT ${GENERATED_UNICODE_TABLES}
${UNICODE_FILES}
)
set(LUA_GEN ${LUA_GEN_PRG} ${GENERATOR_PRELOAD} ${PROJECT_SOURCE_DIR} $<TARGET_FILE:nlua0>)
set(LUA_GEN_DEPS ${GENERATOR_PRELOAD} $<TARGET_FILE:nlua0>)
add_custom_command(
OUTPUT ${GENERATED_API_DISPATCH} ${GENERATED_FUNCS_METADATA}
${API_METADATA} ${LUA_API_C_BINDINGS} ${GENERATED_KEYSETS_DEFS}

View File

@ -1,4 +1,4 @@
local lpeg = require('lpeg')
local lpeg = vim.lpeg
-- lpeg grammar for building api metadata from a set of header files. It
-- ignores comments and preprocessor commands and parses a very small subset

5
src/nvim/generators/gen_declarations.lua Executable file → Normal file
View File

@ -1,12 +1,9 @@
#!/usr/bin/lua
local fname = arg[1]
local static_fname = arg[2]
local non_static_fname = arg[3]
local preproc_fname = arg[4]
local lpeg = require('lpeg')
local lpeg = vim.lpeg
local fold = function (func, ...)
local result = nil

View File

@ -611,6 +611,19 @@ void nlua_state_add_stdlib(lua_State *const lstate, bool is_thread)
lua_setfield(lstate, -2, "mpack");
lua_pop(lstate, 3);
// vim.lpeg
int luaopen_lpeg(lua_State *);
luaopen_lpeg(lstate);
lua_pushvalue(lstate, -1);
lua_setfield(lstate, -4, "lpeg");
// package.loaded.lpeg = vim.lpeg
lua_getglobal(lstate, "package");
lua_getfield(lstate, -1, "loaded");
lua_pushvalue(lstate, -3);
lua_setfield(lstate, -2, "lpeg");
lua_pop(lstate, 4);
// vim.diff
lua_pushcfunction(lstate, &nlua_xdl_diff);
lua_setfield(lstate, -2, "diff");

View File

@ -3031,6 +3031,15 @@ describe('lua stdlib', function()
eq(false, if_nil(d, c))
eq(NIL, if_nil(a))
end)
it('lpeg', function()
eq(5, exec_lua [[
local m = vim.lpeg
return m.match(m.R'09'^1, '4504ab')
]])
eq(4, exec_lua [[ return vim.re.match("abcde", '[a-c]+') ]])
end)
end)
describe('lua: builtin modules', function()