mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
Merge branch 'master' into s-dash-stdin
This commit is contained in:
320
test/unit/charset/vim_str2nr_spec.lua
Normal file
320
test/unit/charset/vim_str2nr_spec.lua
Normal file
@@ -0,0 +1,320 @@
|
||||
local helpers = require("test.unit.helpers")(after_each)
|
||||
local bit = require('bit')
|
||||
|
||||
local itp = helpers.gen_itp(it)
|
||||
|
||||
local child_call_once = helpers.child_call_once
|
||||
local cimport = helpers.cimport
|
||||
local ffi = helpers.ffi
|
||||
|
||||
local lib = cimport('./src/nvim/charset.h')
|
||||
|
||||
local ARGTYPES
|
||||
|
||||
child_call_once(function()
|
||||
ARGTYPES = {
|
||||
num = ffi.typeof('varnumber_T[1]'),
|
||||
unum = ffi.typeof('uvarnumber_T[1]'),
|
||||
pre = ffi.typeof('int[1]'),
|
||||
len = ffi.typeof('int[1]'),
|
||||
}
|
||||
end)
|
||||
|
||||
local icnt = -42
|
||||
local ucnt = 4242
|
||||
|
||||
local function arginit(arg)
|
||||
if arg == 'unum' then
|
||||
ucnt = ucnt + 1
|
||||
return ARGTYPES[arg]({ucnt})
|
||||
else
|
||||
icnt = icnt - 1
|
||||
return ARGTYPES[arg]({icnt})
|
||||
end
|
||||
end
|
||||
|
||||
local function argreset(arg, args)
|
||||
if arg == 'unum' then
|
||||
ucnt = ucnt + 1
|
||||
args[arg][0] = ucnt
|
||||
else
|
||||
icnt = icnt - 1
|
||||
args[arg][0] = icnt
|
||||
end
|
||||
end
|
||||
|
||||
local function test_vim_str2nr(s, what, exp, maxlen)
|
||||
local bits = {}
|
||||
for k, _ in pairs(exp) do
|
||||
bits[#bits + 1] = k
|
||||
end
|
||||
maxlen = maxlen or #s
|
||||
local args = {}
|
||||
for k, _ in pairs(ARGTYPES) do
|
||||
args[k] = arginit(k)
|
||||
end
|
||||
for case = 0, ((2 ^ (#bits)) - 1) do
|
||||
local cv = {}
|
||||
for b = 0, (#bits - 1) do
|
||||
if bit.band(case, (2 ^ b)) == 0 then
|
||||
local k = bits[b + 1]
|
||||
argreset(k, args)
|
||||
cv[k] = args[k]
|
||||
end
|
||||
end
|
||||
lib.vim_str2nr(s, cv.pre, cv.len, what, cv.num, cv.unum, maxlen)
|
||||
for cck, ccv in pairs(cv) do
|
||||
if exp[cck] ~= tonumber(ccv[0]) then
|
||||
error(('Failed check (%s = %d) in test (s=%s, w=%u, m=%d): %d'):format(
|
||||
cck, exp[cck], s, tonumber(what), maxlen, tonumber(ccv[0])
|
||||
))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local _itp = itp
|
||||
itp = function(...)
|
||||
collectgarbage('restart')
|
||||
_itp(...)
|
||||
end
|
||||
|
||||
describe('vim_str2nr()', function()
|
||||
itp('works fine when it has nothing to do', function()
|
||||
test_vim_str2nr('', 0, {len = 0, num = 0, unum = 0, pre = 0}, 0)
|
||||
test_vim_str2nr('', lib.STR2NR_ALL, {len = 0, num = 0, unum = 0, pre = 0}, 0)
|
||||
test_vim_str2nr('', lib.STR2NR_BIN, {len = 0, num = 0, unum = 0, pre = 0}, 0)
|
||||
test_vim_str2nr('', lib.STR2NR_OCT, {len = 0, num = 0, unum = 0, pre = 0}, 0)
|
||||
test_vim_str2nr('', lib.STR2NR_HEX, {len = 0, num = 0, unum = 0, pre = 0}, 0)
|
||||
test_vim_str2nr('', lib.STR2NR_FORCE + lib.STR2NR_DEC, {len = 0, num = 0, unum = 0, pre = 0}, 0)
|
||||
test_vim_str2nr('', lib.STR2NR_FORCE + lib.STR2NR_BIN, {len = 0, num = 0, unum = 0, pre = 0}, 0)
|
||||
test_vim_str2nr('', lib.STR2NR_FORCE + lib.STR2NR_OCT, {len = 0, num = 0, unum = 0, pre = 0}, 0)
|
||||
test_vim_str2nr('', lib.STR2NR_FORCE + lib.STR2NR_HEX, {len = 0, num = 0, unum = 0, pre = 0}, 0)
|
||||
end)
|
||||
itp('works with decimal numbers', function()
|
||||
for _, flags in ipairs({
|
||||
0,
|
||||
lib.STR2NR_BIN,
|
||||
lib.STR2NR_OCT,
|
||||
lib.STR2NR_HEX,
|
||||
lib.STR2NR_BIN + lib.STR2NR_OCT,
|
||||
lib.STR2NR_BIN + lib.STR2NR_HEX,
|
||||
lib.STR2NR_OCT + lib.STR2NR_HEX,
|
||||
lib.STR2NR_ALL,
|
||||
lib.STR2NR_FORCE + lib.STR2NR_DEC,
|
||||
}) do
|
||||
-- Check that all digits are recognized
|
||||
test_vim_str2nr( '12345', flags, {len = 5, num = 12345, unum = 12345, pre = 0}, 0)
|
||||
test_vim_str2nr( '67890', flags, {len = 5, num = 67890, unum = 67890, pre = 0}, 0)
|
||||
test_vim_str2nr( '12345A', flags, {len = 5, num = 12345, unum = 12345, pre = 0}, 0)
|
||||
test_vim_str2nr( '67890A', flags, {len = 5, num = 67890, unum = 67890, pre = 0}, 0)
|
||||
|
||||
test_vim_str2nr( '42', flags, {len = 2, num = 42, unum = 42, pre = 0}, 0)
|
||||
test_vim_str2nr( '42', flags, {len = 1, num = 4, unum = 4, pre = 0}, 1)
|
||||
test_vim_str2nr( '42', flags, {len = 2, num = 42, unum = 42, pre = 0}, 2)
|
||||
test_vim_str2nr( '42', flags, {len = 2, num = 42, unum = 42, pre = 0}, 3) -- includes NUL byte in maxlen
|
||||
|
||||
test_vim_str2nr( '42x', flags, {len = 2, num = 42, unum = 42, pre = 0}, 0)
|
||||
test_vim_str2nr( '42x', flags, {len = 2, num = 42, unum = 42, pre = 0}, 3)
|
||||
|
||||
test_vim_str2nr('-42', flags, {len = 3, num = -42, unum = 42, pre = 0}, 3)
|
||||
test_vim_str2nr('-42', flags, {len = 1, num = 0, unum = 0, pre = 0}, 1)
|
||||
|
||||
test_vim_str2nr('-42x', flags, {len = 3, num = -42, unum = 42, pre = 0}, 0)
|
||||
test_vim_str2nr('-42x', flags, {len = 3, num = -42, unum = 42, pre = 0}, 4)
|
||||
end
|
||||
end)
|
||||
itp('works with binary numbers', function()
|
||||
for _, flags in ipairs({
|
||||
lib.STR2NR_BIN,
|
||||
lib.STR2NR_BIN + lib.STR2NR_OCT,
|
||||
lib.STR2NR_BIN + lib.STR2NR_HEX,
|
||||
lib.STR2NR_ALL,
|
||||
lib.STR2NR_FORCE + lib.STR2NR_BIN,
|
||||
}) do
|
||||
local bin
|
||||
local BIN
|
||||
if flags > lib.STR2NR_FORCE then
|
||||
bin = 0
|
||||
BIN = 0
|
||||
else
|
||||
bin = ('b'):byte()
|
||||
BIN = ('B'):byte()
|
||||
end
|
||||
|
||||
test_vim_str2nr( '0b101', flags, {len = 5, num = 5, unum = 5, pre = bin}, 0)
|
||||
test_vim_str2nr( '0b101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1)
|
||||
test_vim_str2nr( '0b101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 2)
|
||||
test_vim_str2nr( '0b101', flags, {len = 3, num = 1, unum = 1, pre = bin}, 3)
|
||||
test_vim_str2nr( '0b101', flags, {len = 4, num = 2, unum = 2, pre = bin}, 4)
|
||||
test_vim_str2nr( '0b101', flags, {len = 5, num = 5, unum = 5, pre = bin}, 5)
|
||||
test_vim_str2nr( '0b101', flags, {len = 5, num = 5, unum = 5, pre = bin}, 6)
|
||||
|
||||
test_vim_str2nr( '0b1012', flags, {len = 5, num = 5, unum = 5, pre = bin}, 0)
|
||||
test_vim_str2nr( '0b1012', flags, {len = 5, num = 5, unum = 5, pre = bin}, 6)
|
||||
|
||||
test_vim_str2nr('-0b101', flags, {len = 6, num = -5, unum = 5, pre = bin}, 0)
|
||||
test_vim_str2nr('-0b101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1)
|
||||
test_vim_str2nr('-0b101', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 2)
|
||||
test_vim_str2nr('-0b101', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 3)
|
||||
test_vim_str2nr('-0b101', flags, {len = 4, num = -1, unum = 1, pre = bin}, 4)
|
||||
test_vim_str2nr('-0b101', flags, {len = 5, num = -2, unum = 2, pre = bin}, 5)
|
||||
test_vim_str2nr('-0b101', flags, {len = 6, num = -5, unum = 5, pre = bin}, 6)
|
||||
test_vim_str2nr('-0b101', flags, {len = 6, num = -5, unum = 5, pre = bin}, 7)
|
||||
|
||||
test_vim_str2nr('-0b1012', flags, {len = 6, num = -5, unum = 5, pre = bin}, 0)
|
||||
test_vim_str2nr('-0b1012', flags, {len = 6, num = -5, unum = 5, pre = bin}, 7)
|
||||
|
||||
test_vim_str2nr( '0B101', flags, {len = 5, num = 5, unum = 5, pre = BIN}, 0)
|
||||
test_vim_str2nr( '0B101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1)
|
||||
test_vim_str2nr( '0B101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 2)
|
||||
test_vim_str2nr( '0B101', flags, {len = 3, num = 1, unum = 1, pre = BIN}, 3)
|
||||
test_vim_str2nr( '0B101', flags, {len = 4, num = 2, unum = 2, pre = BIN}, 4)
|
||||
test_vim_str2nr( '0B101', flags, {len = 5, num = 5, unum = 5, pre = BIN}, 5)
|
||||
test_vim_str2nr( '0B101', flags, {len = 5, num = 5, unum = 5, pre = BIN}, 6)
|
||||
|
||||
test_vim_str2nr( '0B1012', flags, {len = 5, num = 5, unum = 5, pre = BIN}, 0)
|
||||
test_vim_str2nr( '0B1012', flags, {len = 5, num = 5, unum = 5, pre = BIN}, 6)
|
||||
|
||||
test_vim_str2nr('-0B101', flags, {len = 6, num = -5, unum = 5, pre = BIN}, 0)
|
||||
test_vim_str2nr('-0B101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1)
|
||||
test_vim_str2nr('-0B101', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 2)
|
||||
test_vim_str2nr('-0B101', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 3)
|
||||
test_vim_str2nr('-0B101', flags, {len = 4, num = -1, unum = 1, pre = BIN}, 4)
|
||||
test_vim_str2nr('-0B101', flags, {len = 5, num = -2, unum = 2, pre = BIN}, 5)
|
||||
test_vim_str2nr('-0B101', flags, {len = 6, num = -5, unum = 5, pre = BIN}, 6)
|
||||
test_vim_str2nr('-0B101', flags, {len = 6, num = -5, unum = 5, pre = BIN}, 7)
|
||||
|
||||
test_vim_str2nr('-0B1012', flags, {len = 6, num = -5, unum = 5, pre = BIN}, 0)
|
||||
test_vim_str2nr('-0B1012', flags, {len = 6, num = -5, unum = 5, pre = BIN}, 7)
|
||||
|
||||
if flags > lib.STR2NR_FORCE then
|
||||
test_vim_str2nr('-101', flags, {len = 4, num = -5, unum = 5, pre = 0}, 0)
|
||||
end
|
||||
end
|
||||
end)
|
||||
itp('works with octal numbers', function()
|
||||
for _, flags in ipairs({
|
||||
lib.STR2NR_OCT,
|
||||
lib.STR2NR_OCT + lib.STR2NR_BIN,
|
||||
lib.STR2NR_OCT + lib.STR2NR_HEX,
|
||||
lib.STR2NR_ALL,
|
||||
lib.STR2NR_FORCE + lib.STR2NR_OCT,
|
||||
}) do
|
||||
local oct
|
||||
if flags > lib.STR2NR_FORCE then
|
||||
oct = 0
|
||||
else
|
||||
oct = ('0'):byte()
|
||||
end
|
||||
|
||||
-- Check that all digits are recognized
|
||||
test_vim_str2nr( '012345670', flags, {len = 9, num = 2739128, unum = 2739128, pre = oct}, 0)
|
||||
|
||||
test_vim_str2nr( '054', flags, {len = 3, num = 44, unum = 44, pre = oct}, 0)
|
||||
test_vim_str2nr( '054', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1)
|
||||
test_vim_str2nr( '054', flags, {len = 2, num = 5, unum = 5, pre = oct}, 2)
|
||||
test_vim_str2nr( '054', flags, {len = 3, num = 44, unum = 44, pre = oct}, 3)
|
||||
test_vim_str2nr( '0548', flags, {len = 3, num = 44, unum = 44, pre = oct}, 3)
|
||||
test_vim_str2nr( '054', flags, {len = 3, num = 44, unum = 44, pre = oct}, 4)
|
||||
|
||||
test_vim_str2nr( '054x', flags, {len = 3, num = 44, unum = 44, pre = oct}, 4)
|
||||
test_vim_str2nr( '054x', flags, {len = 3, num = 44, unum = 44, pre = oct}, 0)
|
||||
|
||||
test_vim_str2nr('-054', flags, {len = 4, num = -44, unum = 44, pre = oct}, 0)
|
||||
test_vim_str2nr('-054', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1)
|
||||
test_vim_str2nr('-054', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 2)
|
||||
test_vim_str2nr('-054', flags, {len = 3, num = -5, unum = 5, pre = oct}, 3)
|
||||
test_vim_str2nr('-054', flags, {len = 4, num = -44, unum = 44, pre = oct}, 4)
|
||||
test_vim_str2nr('-0548', flags, {len = 4, num = -44, unum = 44, pre = oct}, 4)
|
||||
test_vim_str2nr('-054', flags, {len = 4, num = -44, unum = 44, pre = oct}, 5)
|
||||
|
||||
test_vim_str2nr('-054x', flags, {len = 4, num = -44, unum = 44, pre = oct}, 5)
|
||||
test_vim_str2nr('-054x', flags, {len = 4, num = -44, unum = 44, pre = oct}, 0)
|
||||
|
||||
if flags > lib.STR2NR_FORCE then
|
||||
test_vim_str2nr('-54', flags, {len = 3, num = -44, unum = 44, pre = 0}, 0)
|
||||
test_vim_str2nr('-0548', flags, {len = 4, num = -44, unum = 44, pre = 0}, 5)
|
||||
test_vim_str2nr('-0548', flags, {len = 4, num = -44, unum = 44, pre = 0}, 0)
|
||||
else
|
||||
test_vim_str2nr('-0548', flags, {len = 5, num = -548, unum = 548, pre = 0}, 5)
|
||||
test_vim_str2nr('-0548', flags, {len = 5, num = -548, unum = 548, pre = 0}, 0)
|
||||
end
|
||||
end
|
||||
end)
|
||||
itp('works with hexadecimal numbers', function()
|
||||
for _, flags in ipairs({
|
||||
lib.STR2NR_HEX,
|
||||
lib.STR2NR_HEX + lib.STR2NR_BIN,
|
||||
lib.STR2NR_HEX + lib.STR2NR_OCT,
|
||||
lib.STR2NR_ALL,
|
||||
lib.STR2NR_FORCE + lib.STR2NR_HEX,
|
||||
}) do
|
||||
local hex
|
||||
local HEX
|
||||
if flags > lib.STR2NR_FORCE then
|
||||
hex = 0
|
||||
HEX = 0
|
||||
else
|
||||
hex = ('x'):byte()
|
||||
HEX = ('X'):byte()
|
||||
end
|
||||
|
||||
-- Check that all digits are recognized
|
||||
test_vim_str2nr('0x12345', flags, {len = 7, num = 74565, unum = 74565, pre = hex}, 0)
|
||||
test_vim_str2nr('0x67890', flags, {len = 7, num = 424080, unum = 424080, pre = hex}, 0)
|
||||
test_vim_str2nr('0xABCDEF', flags, {len = 8, num = 11259375, unum = 11259375, pre = hex}, 0)
|
||||
test_vim_str2nr('0xabcdef', flags, {len = 8, num = 11259375, unum = 11259375, pre = hex}, 0)
|
||||
|
||||
test_vim_str2nr( '0x101', flags, {len = 5, num = 257, unum =257, pre = hex}, 0)
|
||||
test_vim_str2nr( '0x101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1)
|
||||
test_vim_str2nr( '0x101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 2)
|
||||
test_vim_str2nr( '0x101', flags, {len = 3, num = 1, unum = 1, pre = hex}, 3)
|
||||
test_vim_str2nr( '0x101', flags, {len = 4, num = 16, unum = 16, pre = hex}, 4)
|
||||
test_vim_str2nr( '0x101', flags, {len = 5, num = 257, unum =257, pre = hex}, 5)
|
||||
test_vim_str2nr( '0x101', flags, {len = 5, num = 257, unum =257, pre = hex}, 6)
|
||||
|
||||
test_vim_str2nr( '0x101G', flags, {len = 5, num = 257, unum =257, pre = hex}, 0)
|
||||
test_vim_str2nr( '0x101G', flags, {len = 5, num = 257, unum =257, pre = hex}, 6)
|
||||
|
||||
test_vim_str2nr('-0x101', flags, {len = 6, num =-257, unum =257, pre = hex}, 0)
|
||||
test_vim_str2nr('-0x101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1)
|
||||
test_vim_str2nr('-0x101', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 2)
|
||||
test_vim_str2nr('-0x101', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 3)
|
||||
test_vim_str2nr('-0x101', flags, {len = 4, num = -1, unum = 1, pre = hex}, 4)
|
||||
test_vim_str2nr('-0x101', flags, {len = 5, num = -16, unum = 16, pre = hex}, 5)
|
||||
test_vim_str2nr('-0x101', flags, {len = 6, num =-257, unum =257, pre = hex}, 6)
|
||||
test_vim_str2nr('-0x101', flags, {len = 6, num =-257, unum =257, pre = hex}, 7)
|
||||
|
||||
test_vim_str2nr('-0x101G', flags, {len = 6, num =-257, unum =257, pre = hex}, 0)
|
||||
test_vim_str2nr('-0x101G', flags, {len = 6, num =-257, unum =257, pre = hex}, 7)
|
||||
|
||||
test_vim_str2nr( '0X101', flags, {len = 5, num = 257, unum =257, pre = HEX}, 0)
|
||||
test_vim_str2nr( '0X101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1)
|
||||
test_vim_str2nr( '0X101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 2)
|
||||
test_vim_str2nr( '0X101', flags, {len = 3, num = 1, unum = 1, pre = HEX}, 3)
|
||||
test_vim_str2nr( '0X101', flags, {len = 4, num = 16, unum = 16, pre = HEX}, 4)
|
||||
test_vim_str2nr( '0X101', flags, {len = 5, num = 257, unum =257, pre = HEX}, 5)
|
||||
test_vim_str2nr( '0X101', flags, {len = 5, num = 257, unum =257, pre = HEX}, 6)
|
||||
|
||||
test_vim_str2nr( '0X101G', flags, {len = 5, num = 257, unum =257, pre = HEX}, 0)
|
||||
test_vim_str2nr( '0X101G', flags, {len = 5, num = 257, unum =257, pre = HEX}, 6)
|
||||
|
||||
test_vim_str2nr('-0X101', flags, {len = 6, num =-257, unum =257, pre = HEX}, 0)
|
||||
test_vim_str2nr('-0X101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1)
|
||||
test_vim_str2nr('-0X101', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 2)
|
||||
test_vim_str2nr('-0X101', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 3)
|
||||
test_vim_str2nr('-0X101', flags, {len = 4, num = -1, unum = 1, pre = HEX}, 4)
|
||||
test_vim_str2nr('-0X101', flags, {len = 5, num = -16, unum = 16, pre = HEX}, 5)
|
||||
test_vim_str2nr('-0X101', flags, {len = 6, num =-257, unum =257, pre = HEX}, 6)
|
||||
test_vim_str2nr('-0X101', flags, {len = 6, num =-257, unum =257, pre = HEX}, 7)
|
||||
|
||||
test_vim_str2nr('-0X101G', flags, {len = 6, num =-257, unum =257, pre = HEX}, 0)
|
||||
test_vim_str2nr('-0X101G', flags, {len = 6, num =-257, unum =257, pre = HEX}, 7)
|
||||
|
||||
if flags > lib.STR2NR_FORCE then
|
||||
test_vim_str2nr('-101', flags, {len = 4, num = -257, unum = 257, pre = 0}, 0)
|
||||
end
|
||||
end
|
||||
end)
|
||||
end)
|
||||
@@ -1,12 +1,13 @@
|
||||
local helpers = require('test.unit.helpers')(nil)
|
||||
|
||||
local ptr2key = helpers.ptr2key
|
||||
local cimport = helpers.cimport
|
||||
local to_cstr = helpers.to_cstr
|
||||
local ffi = helpers.ffi
|
||||
local eq = helpers.eq
|
||||
|
||||
local eval = cimport('./src/nvim/eval.h', './src/nvim/eval/typval.h',
|
||||
'./src/nvim/hashtab.h')
|
||||
'./src/nvim/hashtab.h', './src/nvim/memory.h')
|
||||
|
||||
local null_string = {[true]='NULL string'}
|
||||
local null_list = {[true]='NULL list'}
|
||||
@@ -23,10 +24,19 @@ local nil_value = {[true]='nil'}
|
||||
|
||||
local lua2typvalt
|
||||
|
||||
local function tv_list_item_alloc()
|
||||
return ffi.cast('listitem_T*', eval.xmalloc(ffi.sizeof('listitem_T')))
|
||||
end
|
||||
|
||||
local function tv_list_item_free(li)
|
||||
eval.tv_clear(li.li_tv)
|
||||
eval.xfree(li)
|
||||
end
|
||||
|
||||
local function li_alloc(nogc)
|
||||
local gcfunc = eval.tv_list_item_free
|
||||
local gcfunc = tv_list_item_free
|
||||
if nogc then gcfunc = nil end
|
||||
local li = ffi.gc(eval.tv_list_item_alloc(), gcfunc)
|
||||
local li = ffi.gc(tv_list_item_alloc(), gcfunc)
|
||||
li.li_next = nil
|
||||
li.li_prev = nil
|
||||
li.li_tv = {v_type=eval.VAR_UNKNOWN, v_lock=eval.VAR_UNLOCKED}
|
||||
@@ -40,7 +50,7 @@ local function populate_list(l, lua_l, processed)
|
||||
processed[lua_l] = l
|
||||
for i = 1, #lua_l do
|
||||
local item_tv = ffi.gc(lua2typvalt(lua_l[i], processed), nil)
|
||||
local item_li = eval.tv_list_item_alloc()
|
||||
local item_li = tv_list_item_alloc()
|
||||
item_li.li_tv = item_tv
|
||||
eval.tv_list_append(l, item_li)
|
||||
end
|
||||
@@ -91,10 +101,6 @@ local function populate_partial(pt, lua_pt, processed)
|
||||
return pt
|
||||
end
|
||||
|
||||
local ptr2key = function(ptr)
|
||||
return tostring(ptr)
|
||||
end
|
||||
|
||||
local lst2tbl
|
||||
local dct2tbl
|
||||
|
||||
@@ -306,7 +312,7 @@ local lua2typvalt_type_tab = {
|
||||
processed[l].lv_refcount = processed[l].lv_refcount + 1
|
||||
return typvalt(eval.VAR_LIST, {v_list=processed[l]})
|
||||
end
|
||||
local lst = populate_list(eval.tv_list_alloc(), l, processed)
|
||||
local lst = populate_list(eval.tv_list_alloc(#l), l, processed)
|
||||
return typvalt(eval.VAR_LIST, {v_list=lst})
|
||||
end,
|
||||
[dict_type] = function(l, processed)
|
||||
@@ -427,7 +433,8 @@ local function int(n)
|
||||
end
|
||||
|
||||
local function list(...)
|
||||
return populate_list(ffi.gc(eval.tv_list_alloc(), eval.tv_list_unref),
|
||||
return populate_list(ffi.gc(eval.tv_list_alloc(select('#', ...)),
|
||||
eval.tv_list_unref),
|
||||
{...}, {})
|
||||
end
|
||||
|
||||
@@ -536,6 +543,7 @@ return {
|
||||
typvalt=typvalt,
|
||||
|
||||
li_alloc=li_alloc,
|
||||
tv_list_item_free=tv_list_item_free,
|
||||
|
||||
dict_iter=dict_iter,
|
||||
list_iter=list_iter,
|
||||
|
||||
@@ -41,6 +41,7 @@ local tbl2callback = eval_helpers.tbl2callback
|
||||
local dict_watchers = eval_helpers.dict_watchers
|
||||
|
||||
local concat_tables = global_helpers.concat_tables
|
||||
local map = global_helpers.map
|
||||
|
||||
local lib = cimport('./src/nvim/eval/typval.h', './src/nvim/memory.h',
|
||||
'./src/nvim/mbyte.h', './src/nvim/garray.h',
|
||||
@@ -80,8 +81,6 @@ local function get_alloc_rets(exp_log, res)
|
||||
return exp_log
|
||||
end
|
||||
|
||||
local to_cstr_nofree = function(v) return lib.xstrdup(v) end
|
||||
|
||||
local alloc_log = alloc_log_new()
|
||||
|
||||
before_each(function()
|
||||
@@ -121,87 +120,6 @@ end
|
||||
describe('typval.c', function()
|
||||
describe('list', function()
|
||||
describe('item', function()
|
||||
describe('alloc()/free()', function()
|
||||
itp('works', function()
|
||||
local li = li_alloc(true)
|
||||
neq(nil, li)
|
||||
lib.tv_list_item_free(li)
|
||||
alloc_log:check({
|
||||
a.li(li),
|
||||
a.freed(li),
|
||||
})
|
||||
end)
|
||||
itp('also frees the value', function()
|
||||
local li
|
||||
local s
|
||||
local l
|
||||
local tv
|
||||
li = li_alloc(true)
|
||||
li.li_tv.v_type = lib.VAR_NUMBER
|
||||
li.li_tv.vval.v_number = 10
|
||||
lib.tv_list_item_free(li)
|
||||
alloc_log:check({
|
||||
a.li(li),
|
||||
a.freed(li),
|
||||
})
|
||||
|
||||
li = li_alloc(true)
|
||||
li.li_tv.v_type = lib.VAR_FLOAT
|
||||
li.li_tv.vval.v_float = 10.5
|
||||
lib.tv_list_item_free(li)
|
||||
alloc_log:check({
|
||||
a.li(li),
|
||||
a.freed(li),
|
||||
})
|
||||
|
||||
li = li_alloc(true)
|
||||
li.li_tv.v_type = lib.VAR_STRING
|
||||
li.li_tv.vval.v_string = nil
|
||||
lib.tv_list_item_free(li)
|
||||
alloc_log:check({
|
||||
a.li(li),
|
||||
a.freed(alloc_log.null),
|
||||
a.freed(li),
|
||||
})
|
||||
|
||||
li = li_alloc(true)
|
||||
li.li_tv.v_type = lib.VAR_STRING
|
||||
s = to_cstr_nofree('test')
|
||||
li.li_tv.vval.v_string = s
|
||||
lib.tv_list_item_free(li)
|
||||
alloc_log:check({
|
||||
a.li(li),
|
||||
a.str(s, #('test')),
|
||||
a.freed(s),
|
||||
a.freed(li),
|
||||
})
|
||||
|
||||
li = li_alloc(true)
|
||||
li.li_tv.v_type = lib.VAR_LIST
|
||||
l = ffi.gc(list(), nil)
|
||||
l.lv_refcount = 2
|
||||
li.li_tv.vval.v_list = l
|
||||
lib.tv_list_item_free(li)
|
||||
alloc_log:check({
|
||||
a.li(li),
|
||||
a.list(l),
|
||||
a.freed(li),
|
||||
})
|
||||
eq(1, l.lv_refcount)
|
||||
|
||||
li = li_alloc(true)
|
||||
tv = lua2typvalt({})
|
||||
tv.vval.v_dict.dv_refcount = 2
|
||||
li.li_tv = tv
|
||||
lib.tv_list_item_free(li)
|
||||
alloc_log:check({
|
||||
a.li(li),
|
||||
a.dict(tv.vval.v_dict),
|
||||
a.freed(li),
|
||||
})
|
||||
eq(1, tv.vval.v_dict.dv_refcount)
|
||||
end)
|
||||
end)
|
||||
describe('remove()', function()
|
||||
itp('works', function()
|
||||
local l = list(1, 2, 3, 4, 5, 6, 7)
|
||||
@@ -218,24 +136,63 @@ describe('typval.c', function()
|
||||
a.li(lis[7]),
|
||||
})
|
||||
|
||||
lib.tv_list_item_remove(l, lis[1])
|
||||
eq(lis[2], lib.tv_list_item_remove(l, lis[1]))
|
||||
alloc_log:check({
|
||||
a.freed(table.remove(lis, 1)),
|
||||
})
|
||||
eq(lis, list_items(l))
|
||||
|
||||
lib.tv_list_item_remove(l, lis[6])
|
||||
eq(lis[7], lib.tv_list_item_remove(l, lis[6]))
|
||||
alloc_log:check({
|
||||
a.freed(table.remove(lis)),
|
||||
})
|
||||
eq(lis, list_items(l))
|
||||
|
||||
lib.tv_list_item_remove(l, lis[3])
|
||||
eq(lis[4], lib.tv_list_item_remove(l, lis[3]))
|
||||
alloc_log:check({
|
||||
a.freed(table.remove(lis, 3)),
|
||||
})
|
||||
eq(lis, list_items(l))
|
||||
end)
|
||||
itp('also frees the value', function()
|
||||
local l = list('a', 'b', 'c', 'd')
|
||||
neq(nil, l)
|
||||
local lis = list_items(l)
|
||||
alloc_log:check({
|
||||
a.list(l),
|
||||
a.str(lis[1].li_tv.vval.v_string, 1),
|
||||
a.li(lis[1]),
|
||||
a.str(lis[2].li_tv.vval.v_string, 1),
|
||||
a.li(lis[2]),
|
||||
a.str(lis[3].li_tv.vval.v_string, 1),
|
||||
a.li(lis[3]),
|
||||
a.str(lis[4].li_tv.vval.v_string, 1),
|
||||
a.li(lis[4]),
|
||||
})
|
||||
local strings = map(function(li) return li.li_tv.vval.v_string end,
|
||||
lis)
|
||||
|
||||
eq(lis[2], lib.tv_list_item_remove(l, lis[1]))
|
||||
alloc_log:check({
|
||||
a.freed(table.remove(strings, 1)),
|
||||
a.freed(table.remove(lis, 1)),
|
||||
})
|
||||
eq(lis, list_items(l))
|
||||
|
||||
eq(lis[3], lib.tv_list_item_remove(l, lis[2]))
|
||||
alloc_log:check({
|
||||
a.freed(table.remove(strings, 2)),
|
||||
a.freed(table.remove(lis, 2)),
|
||||
})
|
||||
eq(lis, list_items(l))
|
||||
|
||||
eq(nil, lib.tv_list_item_remove(l, lis[2]))
|
||||
alloc_log:check({
|
||||
a.freed(table.remove(strings, 2)),
|
||||
a.freed(table.remove(lis, 2)),
|
||||
})
|
||||
eq(lis, list_items(l))
|
||||
end)
|
||||
itp('works and adjusts watchers correctly', function()
|
||||
local l = ffi.gc(list(1, 2, 3, 4, 5, 6, 7), nil)
|
||||
neq(nil, l)
|
||||
@@ -257,19 +214,19 @@ describe('typval.c', function()
|
||||
a.li(lis[7]),
|
||||
})
|
||||
|
||||
lib.tv_list_item_remove(l, lis[4])
|
||||
eq(lis[5], lib.tv_list_item_remove(l, lis[4]))
|
||||
alloc_log:check({a.freed(lis[4])})
|
||||
eq({lis[1], lis[5], lis[7]}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item})
|
||||
|
||||
lib.tv_list_item_remove(l, lis[2])
|
||||
eq(lis[3], lib.tv_list_item_remove(l, lis[2]))
|
||||
alloc_log:check({a.freed(lis[2])})
|
||||
eq({lis[1], lis[5], lis[7]}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item})
|
||||
|
||||
lib.tv_list_item_remove(l, lis[7])
|
||||
eq(nil, lib.tv_list_item_remove(l, lis[7]))
|
||||
alloc_log:check({a.freed(lis[7])})
|
||||
eq({lis[1], lis[5], nil}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item == nil and nil})
|
||||
|
||||
lib.tv_list_item_remove(l, lis[1])
|
||||
eq(lis[3], lib.tv_list_item_remove(l, lis[1]))
|
||||
alloc_log:check({a.freed(lis[1])})
|
||||
eq({lis[3], lis[5], nil}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item == nil and nil})
|
||||
|
||||
@@ -449,7 +406,7 @@ describe('typval.c', function()
|
||||
})
|
||||
end)
|
||||
end)
|
||||
describe('remove_items()', function()
|
||||
describe('drop_items()', function()
|
||||
itp('works', function()
|
||||
local l_tv = lua2typvalt({1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13})
|
||||
local l = l_tv.vval.v_list
|
||||
@@ -462,21 +419,92 @@ describe('typval.c', function()
|
||||
}
|
||||
alloc_log:clear()
|
||||
|
||||
lib.tv_list_remove_items(l, lis[1], lis[3])
|
||||
lib.tv_list_drop_items(l, lis[1], lis[3])
|
||||
eq({4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, typvalt2lua(l_tv))
|
||||
eq({lis[4], lis[7], lis[13]}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item})
|
||||
|
||||
lib.tv_list_remove_items(l, lis[11], lis[13])
|
||||
lib.tv_list_drop_items(l, lis[11], lis[13])
|
||||
eq({4, 5, 6, 7, 8, 9, 10}, typvalt2lua(l_tv))
|
||||
eq({lis[4], lis[7], nil}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item == nil and nil})
|
||||
|
||||
lib.tv_list_remove_items(l, lis[6], lis[8])
|
||||
lib.tv_list_drop_items(l, lis[6], lis[8])
|
||||
eq({4, 5, 9, 10}, typvalt2lua(l_tv))
|
||||
eq({lis[4], lis[9], nil}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item == nil and nil})
|
||||
|
||||
lib.tv_list_drop_items(l, lis[4], lis[10])
|
||||
eq(empty_list, typvalt2lua(l_tv))
|
||||
eq({true, true, true}, {lws[1].lw_item == nil, lws[2].lw_item == nil, lws[3].lw_item == nil})
|
||||
|
||||
lib.tv_list_watch_remove(l, lws[1])
|
||||
lib.tv_list_watch_remove(l, lws[2])
|
||||
lib.tv_list_watch_remove(l, lws[3])
|
||||
|
||||
alloc_log:check({})
|
||||
end)
|
||||
end)
|
||||
describe('remove_items()', function()
|
||||
itp('works', function()
|
||||
local l_tv = lua2typvalt({'1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13'})
|
||||
local l = l_tv.vval.v_list
|
||||
local lis = list_items(l)
|
||||
local strings = map(function(li) return li.li_tv.vval.v_string end, lis)
|
||||
-- Three watchers: pointing to first, middle and last elements.
|
||||
local lws = {
|
||||
list_watch(l, lis[1]),
|
||||
list_watch(l, lis[7]),
|
||||
list_watch(l, lis[13]),
|
||||
}
|
||||
alloc_log:clear()
|
||||
|
||||
lib.tv_list_remove_items(l, lis[1], lis[3])
|
||||
eq({'4', '5', '6', '7', '8', '9', '10', '11', '12', '13'}, typvalt2lua(l_tv))
|
||||
eq({lis[4], lis[7], lis[13]}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item})
|
||||
alloc_log:check({
|
||||
a.freed(strings[1]),
|
||||
a.freed(lis[1]),
|
||||
a.freed(strings[2]),
|
||||
a.freed(lis[2]),
|
||||
a.freed(strings[3]),
|
||||
a.freed(lis[3]),
|
||||
})
|
||||
|
||||
lib.tv_list_remove_items(l, lis[11], lis[13])
|
||||
eq({'4', '5', '6', '7', '8', '9', '10'}, typvalt2lua(l_tv))
|
||||
eq({lis[4], lis[7], nil}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item == nil and nil})
|
||||
alloc_log:check({
|
||||
a.freed(strings[11]),
|
||||
a.freed(lis[11]),
|
||||
a.freed(strings[12]),
|
||||
a.freed(lis[12]),
|
||||
a.freed(strings[13]),
|
||||
a.freed(lis[13]),
|
||||
})
|
||||
|
||||
lib.tv_list_remove_items(l, lis[6], lis[8])
|
||||
eq({'4', '5', '9', '10'}, typvalt2lua(l_tv))
|
||||
eq({lis[4], lis[9], nil}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item == nil and nil})
|
||||
alloc_log:check({
|
||||
a.freed(strings[6]),
|
||||
a.freed(lis[6]),
|
||||
a.freed(strings[7]),
|
||||
a.freed(lis[7]),
|
||||
a.freed(strings[8]),
|
||||
a.freed(lis[8]),
|
||||
})
|
||||
|
||||
lib.tv_list_remove_items(l, lis[4], lis[10])
|
||||
eq(empty_list, typvalt2lua(l_tv))
|
||||
eq({true, true, true}, {lws[1].lw_item == nil, lws[2].lw_item == nil, lws[3].lw_item == nil})
|
||||
alloc_log:check({
|
||||
a.freed(strings[4]),
|
||||
a.freed(lis[4]),
|
||||
a.freed(strings[5]),
|
||||
a.freed(lis[5]),
|
||||
a.freed(strings[9]),
|
||||
a.freed(lis[9]),
|
||||
a.freed(strings[10]),
|
||||
a.freed(lis[10]),
|
||||
})
|
||||
|
||||
lib.tv_list_watch_remove(l, lws[1])
|
||||
lib.tv_list_watch_remove(l, lws[2])
|
||||
@@ -678,6 +706,66 @@ describe('typval.c', function()
|
||||
eq({int(-100500), int(100500)}, typvalt2lua(l_tv))
|
||||
end)
|
||||
end)
|
||||
describe('tv()', function()
|
||||
itp('works', function()
|
||||
local l_tv = lua2typvalt(empty_list)
|
||||
local l = l_tv.vval.v_list
|
||||
|
||||
local l_l_tv = lua2typvalt(empty_list)
|
||||
alloc_log:clear()
|
||||
local l_l = l_l_tv.vval.v_list
|
||||
eq(1, l_l.lv_refcount)
|
||||
lib.tv_list_append_tv(l, l_l_tv)
|
||||
eq(2, l_l.lv_refcount)
|
||||
eq(l_l, l.lv_first.li_tv.vval.v_list)
|
||||
alloc_log:check({
|
||||
a.li(l.lv_first),
|
||||
})
|
||||
|
||||
local l_s_tv = lua2typvalt('test')
|
||||
alloc_log:check({
|
||||
a.str(l_s_tv.vval.v_string, 'test'),
|
||||
})
|
||||
lib.tv_list_append_tv(l, l_s_tv)
|
||||
alloc_log:check({
|
||||
a.li(l.lv_last),
|
||||
a.str(l.lv_last.li_tv.vval.v_string, 'test'),
|
||||
})
|
||||
|
||||
eq({empty_list, 'test'}, typvalt2lua(l_tv))
|
||||
end)
|
||||
end)
|
||||
describe('owned tv()', function()
|
||||
itp('works', function()
|
||||
local l_tv = lua2typvalt(empty_list)
|
||||
local l = l_tv.vval.v_list
|
||||
|
||||
local l_l_tv = lua2typvalt(empty_list)
|
||||
alloc_log:clear()
|
||||
local l_l = l_l_tv.vval.v_list
|
||||
eq(1, l_l.lv_refcount)
|
||||
lib.tv_list_append_owned_tv(l, l_l_tv)
|
||||
eq(1, l_l.lv_refcount)
|
||||
l_l.lv_refcount = l_l.lv_refcount + 1
|
||||
eq(l_l, l.lv_first.li_tv.vval.v_list)
|
||||
alloc_log:check({
|
||||
a.li(l.lv_first),
|
||||
})
|
||||
|
||||
local l_s_tv = ffi.gc(lua2typvalt('test'), nil)
|
||||
alloc_log:check({
|
||||
a.str(l_s_tv.vval.v_string, 'test'),
|
||||
})
|
||||
lib.tv_list_append_owned_tv(l, l_s_tv)
|
||||
eq(l_s_tv.vval.v_string, l.lv_last.li_tv.vval.v_string)
|
||||
l_s_tv.vval.v_string = nil
|
||||
alloc_log:check({
|
||||
a.li(l.lv_last),
|
||||
})
|
||||
|
||||
eq({empty_list, 'test'}, typvalt2lua(l_tv))
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
describe('copy()', function()
|
||||
local function tv_list_copy(...)
|
||||
@@ -2319,7 +2407,7 @@ describe('typval.c', function()
|
||||
describe('list ret()', function()
|
||||
itp('works', function()
|
||||
local rettv = typvalt(lib.VAR_UNKNOWN)
|
||||
local l = lib.tv_list_alloc_ret(rettv)
|
||||
local l = lib.tv_list_alloc_ret(rettv, 0)
|
||||
eq(empty_list, typvalt2lua(rettv))
|
||||
eq(rettv.vval.v_list, l)
|
||||
end)
|
||||
|
||||
@@ -65,11 +65,12 @@ local tokens = P { "tokens";
|
||||
identifier = Ct(C(R("az","AZ","__") * R("09","az","AZ","__")^0) * Cc"identifier"),
|
||||
|
||||
-- Single character in a string
|
||||
string_char = R("az","AZ","09") + S"$%^&*()_-+={[}]:;@~#<,>.!?/ \t" + (P"\\" * S[[ntvbrfa\?'"0x]]),
|
||||
sstring_char = R("\001&","([","]\255") + (P"\\" * S[[ntvbrfa\?'"0x]]),
|
||||
dstring_char = R("\001!","#[","]\255") + (P"\\" * S[[ntvbrfa\?'"0x]]),
|
||||
|
||||
-- String literal
|
||||
string = Ct(C(P"'" * (V"string_char" + P'"')^0 * P"'" +
|
||||
P'"' * (V"string_char" + P"'")^0 * P'"') * Cc"string"),
|
||||
string = Ct(C(P"'" * (V"sstring_char" + P'"')^0 * P"'" +
|
||||
P'"' * (V"dstring_char" + P"'")^0 * P'"') * Cc"string"),
|
||||
|
||||
-- Operator
|
||||
operator = Ct(C(P">>=" + P"<<=" + P"..." +
|
||||
|
||||
@@ -138,7 +138,7 @@ local function filter_complex_blocks(body)
|
||||
for line in body:gmatch("[^\r\n]+") do
|
||||
if not (string.find(line, "(^)", 1, true) ~= nil
|
||||
or string.find(line, "_ISwupper", 1, true)
|
||||
or string.find(line, "_Float128")
|
||||
or string.find(line, "_Float")
|
||||
or string.find(line, "msgpack_zone_push_finalizer")
|
||||
or string.find(line, "msgpack_unpacker_reserve_buffer")
|
||||
or string.find(line, "UUID_NULL") -- static const uuid_t UUID_NULL = {...}
|
||||
@@ -316,7 +316,7 @@ local function alloc_log_new()
|
||||
eq(exp, self.log)
|
||||
self:clear()
|
||||
end
|
||||
function log:clear_tmp_allocs()
|
||||
function log:clear_tmp_allocs(clear_null_frees)
|
||||
local toremove = {}
|
||||
local allocs = {}
|
||||
for i, v in ipairs(self.log) do
|
||||
@@ -328,6 +328,8 @@ local function alloc_log_new()
|
||||
if v.func == 'free' then
|
||||
toremove[#toremove + 1] = i
|
||||
end
|
||||
elseif clear_null_frees and v.args[1] == self.null then
|
||||
toremove[#toremove + 1] = i
|
||||
end
|
||||
if v.func == 'realloc' then
|
||||
allocs[tostring(v.ret)] = i
|
||||
@@ -528,9 +530,13 @@ local hook_numlen = 5
|
||||
local hook_msglen = 1 + 1 + 1 + (1 + hook_fnamelen) + (1 + hook_sfnamelen) + (1 + hook_numlen) + 1
|
||||
|
||||
local tracehelp = dedent([[
|
||||
Trace: either in the format described below or custom debug output starting
|
||||
with `>`. Latter lines still have the same width in byte.
|
||||
|
||||
┌ Trace type: _r_eturn from function , function _c_all, _l_ine executed,
|
||||
│ _t_ail return, _C_ount (should not actually appear),
|
||||
│ _s_aved from previous run for reference.
|
||||
│ _s_aved from previous run for reference, _>_ for custom debug
|
||||
│ output.
|
||||
│┏ Function type: _L_ua function, _C_ function, _m_ain part of chunk,
|
||||
│┃ function that did _t_ail call.
|
||||
│┃┌ Function name type: _g_lobal, _l_ocal, _m_ethod, _f_ield, _u_pvalue,
|
||||
@@ -628,14 +634,24 @@ end
|
||||
|
||||
local trace_end_msg = ('E%s\n'):format((' '):rep(hook_msglen - 2))
|
||||
|
||||
local _debug_log
|
||||
|
||||
local debug_log = only_separate(function(...)
|
||||
return _debug_log(...)
|
||||
end)
|
||||
|
||||
local function itp_child(wr, func)
|
||||
init()
|
||||
collectgarbage('stop')
|
||||
child_sethook(wr)
|
||||
local err, emsg = pcall(func)
|
||||
collectgarbage('restart')
|
||||
collectgarbage()
|
||||
debug.sethook()
|
||||
_debug_log = function(s)
|
||||
s = s:sub(1, hook_msglen - 2)
|
||||
sc.write(wr, '>' .. s .. (' '):rep(hook_msglen - 2 - #s) .. '\n')
|
||||
end
|
||||
local err, emsg = pcall(init)
|
||||
if err then
|
||||
collectgarbage('stop')
|
||||
child_sethook(wr)
|
||||
err, emsg = pcall(func)
|
||||
debug.sethook()
|
||||
end
|
||||
emsg = tostring(emsg)
|
||||
sc.write(wr, trace_end_msg)
|
||||
if not err then
|
||||
@@ -644,19 +660,21 @@ local function itp_child(wr, func)
|
||||
end
|
||||
sc.write(wr, ('-\n%05u\n%s'):format(#emsg, emsg))
|
||||
deinit()
|
||||
sc.close(wr)
|
||||
sc.exit(1)
|
||||
else
|
||||
sc.write(wr, '+\n')
|
||||
deinit()
|
||||
sc.close(wr)
|
||||
sc.exit(0)
|
||||
end
|
||||
collectgarbage('restart')
|
||||
collectgarbage()
|
||||
sc.write(wr, '$\n')
|
||||
sc.close(wr)
|
||||
sc.exit(err and 0 or 1)
|
||||
end
|
||||
|
||||
local function check_child_err(rd)
|
||||
local trace = {}
|
||||
local did_traceline = false
|
||||
local maxtrace = tonumber(os.getenv('NVIM_TEST_MAXTRACE')) or 1024
|
||||
while true do
|
||||
local traceline = sc.read(rd, hook_msglen)
|
||||
if #traceline ~= hook_msglen then
|
||||
@@ -671,36 +689,48 @@ local function check_child_err(rd)
|
||||
break
|
||||
end
|
||||
trace[#trace + 1] = traceline
|
||||
if #trace > maxtrace then
|
||||
table.remove(trace, 1)
|
||||
end
|
||||
end
|
||||
local res = sc.read(rd, 2)
|
||||
if #res ~= 2 then
|
||||
local error
|
||||
if #trace == 0 then
|
||||
error = '\nTest crashed, no trace available\n'
|
||||
else
|
||||
error = '\nTest crashed, trace:\n' .. tracehelp
|
||||
for i = 1, #trace do
|
||||
error = error .. trace[i]
|
||||
if #res == 2 then
|
||||
local err = ''
|
||||
if res ~= '+\n' then
|
||||
eq('-\n', res)
|
||||
local len_s = sc.read(rd, 5)
|
||||
local len = tonumber(len_s)
|
||||
neq(0, len)
|
||||
if os.getenv('NVIM_TEST_TRACE_ON_ERROR') == '1' and #trace ~= 0 then
|
||||
err = '\nTest failed, trace:\n' .. tracehelp
|
||||
for _, traceline in ipairs(trace) do
|
||||
err = err .. traceline
|
||||
end
|
||||
end
|
||||
err = err .. sc.read(rd, len + 1)
|
||||
end
|
||||
local eres = sc.read(rd, 2)
|
||||
if eres ~= '$\n' then
|
||||
if #trace == 0 then
|
||||
err = '\nTest crashed, no trace available\n'
|
||||
else
|
||||
err = '\nTest crashed, trace:\n' .. tracehelp
|
||||
for i = 1, #trace do
|
||||
err = err .. trace[i]
|
||||
end
|
||||
end
|
||||
if not did_traceline then
|
||||
err = err .. '\nNo end of trace occurred'
|
||||
end
|
||||
local cc_err, cc_emsg = pcall(check_cores, Paths.test_luajit_prg, true)
|
||||
if not cc_err then
|
||||
err = err .. '\ncheck_cores failed: ' .. cc_emsg
|
||||
end
|
||||
end
|
||||
if not did_traceline then
|
||||
error = error .. '\nNo end of trace occurred'
|
||||
if err ~= '' then
|
||||
assert.just_fail(err)
|
||||
end
|
||||
local cc_err, cc_emsg = pcall(check_cores, Paths.test_luajit_prg, true)
|
||||
if not cc_err then
|
||||
error = error .. '\ncheck_cores failed: ' .. cc_emsg
|
||||
end
|
||||
assert.just_fail(error)
|
||||
end
|
||||
if res == '+\n' then
|
||||
return
|
||||
end
|
||||
eq('-\n', res)
|
||||
local len_s = sc.read(rd, 5)
|
||||
local len = tonumber(len_s)
|
||||
neq(0, len)
|
||||
local err = sc.read(rd, len + 1)
|
||||
assert.just_fail(err)
|
||||
end
|
||||
|
||||
local function itp_parent(rd, pid, allow_failure)
|
||||
@@ -754,6 +784,60 @@ end
|
||||
|
||||
cimport('./src/nvim/types.h', './src/nvim/main.h', './src/nvim/os/time.h')
|
||||
|
||||
local function conv_enum(etab, eval)
|
||||
local n = tonumber(eval)
|
||||
return etab[n] or n
|
||||
end
|
||||
|
||||
local function array_size(arr)
|
||||
return ffi.sizeof(arr) / ffi.sizeof(arr[0])
|
||||
end
|
||||
|
||||
local function kvi_size(kvi)
|
||||
return array_size(kvi.init_array)
|
||||
end
|
||||
|
||||
local function kvi_init(kvi)
|
||||
kvi.capacity = kvi_size(kvi)
|
||||
kvi.items = kvi.init_array
|
||||
return kvi
|
||||
end
|
||||
|
||||
local function kvi_destroy(kvi)
|
||||
if kvi.items ~= kvi.init_array then
|
||||
lib.xfree(kvi.items)
|
||||
end
|
||||
end
|
||||
|
||||
local function kvi_new(ct)
|
||||
return kvi_init(ffi.new(ct))
|
||||
end
|
||||
|
||||
local function make_enum_conv_tab(m, values, skip_pref, set_cb)
|
||||
child_call_once(function()
|
||||
local ret = {}
|
||||
for _, v in ipairs(values) do
|
||||
local str_v = v
|
||||
if v:sub(1, #skip_pref) == skip_pref then
|
||||
str_v = v:sub(#skip_pref + 1)
|
||||
end
|
||||
ret[tonumber(m[v])] = str_v
|
||||
end
|
||||
set_cb(ret)
|
||||
end)
|
||||
end
|
||||
|
||||
local function ptr2addr(ptr)
|
||||
return tonumber(ffi.cast('intptr_t', ffi.cast('void *', ptr)))
|
||||
end
|
||||
|
||||
local s = ffi.new('char[64]', {0})
|
||||
|
||||
local function ptr2key(ptr)
|
||||
ffi.C.snprintf(s, ffi.sizeof(s), '%p', ffi.cast('void *', ptr))
|
||||
return ffi.string(s)
|
||||
end
|
||||
|
||||
local module = {
|
||||
cimport = cimport,
|
||||
cppimport = cppimport,
|
||||
@@ -774,6 +858,16 @@ local module = {
|
||||
child_call_once = child_call_once,
|
||||
child_cleanup_once = child_cleanup_once,
|
||||
sc = sc,
|
||||
conv_enum = conv_enum,
|
||||
array_size = array_size,
|
||||
kvi_destroy = kvi_destroy,
|
||||
kvi_size = kvi_size,
|
||||
kvi_init = kvi_init,
|
||||
kvi_new = kvi_new,
|
||||
make_enum_conv_tab = make_enum_conv_tab,
|
||||
ptr2addr = ptr2addr,
|
||||
ptr2key = ptr2key,
|
||||
debug_log = debug_log,
|
||||
}
|
||||
return function()
|
||||
return module
|
||||
|
||||
71
test/unit/keymap_spec.lua
Normal file
71
test/unit/keymap_spec.lua
Normal file
@@ -0,0 +1,71 @@
|
||||
local helpers = require("test.unit.helpers")(after_each)
|
||||
local itp = helpers.gen_itp(it)
|
||||
|
||||
local ffi = helpers.ffi
|
||||
local eq = helpers.eq
|
||||
local neq = helpers.neq
|
||||
|
||||
local keymap = helpers.cimport("./src/nvim/keymap.h")
|
||||
|
||||
describe('keymap.c', function()
|
||||
|
||||
describe('find_special_key()', function()
|
||||
local srcp = ffi.new('const unsigned char *[1]')
|
||||
local modp = ffi.new('int[1]')
|
||||
|
||||
itp('no keycode', function()
|
||||
srcp[0] = 'abc'
|
||||
eq(0, keymap.find_special_key(srcp, 3, modp, false, false, false))
|
||||
end)
|
||||
|
||||
itp('keycode with multiple modifiers', function()
|
||||
srcp[0] = '<C-M-S-A>'
|
||||
neq(0, keymap.find_special_key(srcp, 9, modp, false, false, false))
|
||||
neq(0, modp[0])
|
||||
end)
|
||||
|
||||
itp('case-insensitive', function()
|
||||
-- Compare other capitalizations to this.
|
||||
srcp[0] = '<C-A>'
|
||||
local all_caps_key =
|
||||
keymap.find_special_key(srcp, 5, modp, false, false, false)
|
||||
local all_caps_mod = modp[0]
|
||||
|
||||
srcp[0] = '<C-a>'
|
||||
eq(all_caps_key,
|
||||
keymap.find_special_key(srcp, 5, modp, false, false, false))
|
||||
eq(all_caps_mod, modp[0])
|
||||
|
||||
srcp[0] = '<c-A>'
|
||||
eq(all_caps_key,
|
||||
keymap.find_special_key(srcp, 5, modp, false, false, false))
|
||||
eq(all_caps_mod, modp[0])
|
||||
|
||||
srcp[0] = '<c-a>'
|
||||
eq(all_caps_key,
|
||||
keymap.find_special_key(srcp, 5, modp, false, false, false))
|
||||
eq(all_caps_mod, modp[0])
|
||||
end)
|
||||
|
||||
itp('double-quote in keycode #7411', function()
|
||||
-- Unescaped with in_string=false
|
||||
srcp[0] = '<C-">'
|
||||
eq(string.byte('"'),
|
||||
keymap.find_special_key(srcp, 5, modp, false, false, false))
|
||||
|
||||
-- Unescaped with in_string=true
|
||||
eq(0, keymap.find_special_key(srcp, 5, modp, false, false, true))
|
||||
|
||||
-- Escaped with in_string=false
|
||||
srcp[0] = '<C-\\">'
|
||||
-- Should fail because the key is invalid
|
||||
-- (more than 1 non-modifier character).
|
||||
eq(0, keymap.find_special_key(srcp, 6, modp, false, false, false))
|
||||
|
||||
-- Escaped with in_string=true
|
||||
eq(string.byte('"'),
|
||||
keymap.find_special_key(srcp, 6, modp, false, false, true))
|
||||
end)
|
||||
end)
|
||||
|
||||
end)
|
||||
@@ -199,7 +199,7 @@ describe('fs.c', function()
|
||||
|
||||
itp('returns the absolute path when given an executable inside $PATH', function()
|
||||
local fullpath = exe('ls')
|
||||
eq(1, fs.path_is_absolute_path(to_cstr(fullpath)))
|
||||
eq(1, fs.path_is_absolute(to_cstr(fullpath)))
|
||||
end)
|
||||
|
||||
itp('returns the absolute path when given an executable relative to the current dir', function()
|
||||
|
||||
@@ -261,7 +261,7 @@ describe('path.c', function()
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('path_shorten_fname_if_possible', function()
|
||||
describe('path_try_shorten_fname', function()
|
||||
local cwd = lfs.currentdir()
|
||||
|
||||
before_each(function()
|
||||
@@ -273,22 +273,22 @@ describe('path_shorten_fname_if_possible', function()
|
||||
lfs.rmdir('ut_directory')
|
||||
end)
|
||||
|
||||
describe('path_shorten_fname_if_possible', function()
|
||||
describe('path_try_shorten_fname', function()
|
||||
itp('returns shortened path if possible', function()
|
||||
lfs.chdir('ut_directory')
|
||||
local full = to_cstr(lfs.currentdir() .. '/subdir/file.txt')
|
||||
eq('subdir/file.txt', (ffi.string(cimp.path_shorten_fname_if_possible(full))))
|
||||
eq('subdir/file.txt', (ffi.string(cimp.path_try_shorten_fname(full))))
|
||||
end)
|
||||
|
||||
itp('returns `full_path` if a shorter version is not possible', function()
|
||||
local old = lfs.currentdir()
|
||||
lfs.chdir('ut_directory')
|
||||
local full = old .. '/subdir/file.txt'
|
||||
eq(full, (ffi.string(cimp.path_shorten_fname_if_possible(to_cstr(full)))))
|
||||
eq(full, (ffi.string(cimp.path_try_shorten_fname(to_cstr(full)))))
|
||||
end)
|
||||
|
||||
itp('returns NULL if `full_path` is NULL', function()
|
||||
eq(NULL, (cimp.path_shorten_fname_if_possible(NULL)))
|
||||
eq(NULL, (cimp.path_try_shorten_fname(NULL)))
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
@@ -585,22 +585,22 @@ describe('path.c', function()
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('path_is_absolute_path', function()
|
||||
local function path_is_absolute_path(filename)
|
||||
describe('path_is_absolute', function()
|
||||
local function path_is_absolute(filename)
|
||||
filename = to_cstr(filename)
|
||||
return cimp.path_is_absolute_path(filename)
|
||||
return cimp.path_is_absolute(filename)
|
||||
end
|
||||
|
||||
itp('returns true if filename starts with a slash', function()
|
||||
eq(OK, path_is_absolute_path('/some/directory/'))
|
||||
eq(OK, path_is_absolute('/some/directory/'))
|
||||
end)
|
||||
|
||||
itp('returns true if filename starts with a tilde', function()
|
||||
eq(OK, path_is_absolute_path('~/in/my/home~/directory'))
|
||||
eq(OK, path_is_absolute('~/in/my/home~/directory'))
|
||||
end)
|
||||
|
||||
itp('returns false if filename starts not with slash nor tilde', function()
|
||||
eq(FAIL, path_is_absolute_path('not/in/my/home~/directory'))
|
||||
eq(FAIL, path_is_absolute('not/in/my/home~/directory'))
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
|
||||
428
test/unit/viml/expressions/lexer_spec.lua
Normal file
428
test/unit/viml/expressions/lexer_spec.lua
Normal file
@@ -0,0 +1,428 @@
|
||||
local helpers = require('test.unit.helpers')(after_each)
|
||||
local global_helpers = require('test.helpers')
|
||||
local itp = helpers.gen_itp(it)
|
||||
local viml_helpers = require('test.unit.viml.helpers')
|
||||
|
||||
local child_call_once = helpers.child_call_once
|
||||
local conv_enum = helpers.conv_enum
|
||||
local cimport = helpers.cimport
|
||||
local ffi = helpers.ffi
|
||||
local eq = helpers.eq
|
||||
|
||||
local conv_ccs = viml_helpers.conv_ccs
|
||||
local new_pstate = viml_helpers.new_pstate
|
||||
local conv_cmp_type = viml_helpers.conv_cmp_type
|
||||
local pstate_set_str = viml_helpers.pstate_set_str
|
||||
local conv_expr_asgn_type = viml_helpers.conv_expr_asgn_type
|
||||
|
||||
local shallowcopy = global_helpers.shallowcopy
|
||||
local intchar2lua = global_helpers.intchar2lua
|
||||
|
||||
local lib = cimport('./src/nvim/viml/parser/expressions.h')
|
||||
|
||||
local eltkn_type_tab, eltkn_mul_type_tab, eltkn_opt_scope_tab
|
||||
child_call_once(function()
|
||||
eltkn_type_tab = {
|
||||
[tonumber(lib.kExprLexInvalid)] = 'Invalid',
|
||||
[tonumber(lib.kExprLexMissing)] = 'Missing',
|
||||
[tonumber(lib.kExprLexSpacing)] = 'Spacing',
|
||||
[tonumber(lib.kExprLexEOC)] = 'EOC',
|
||||
|
||||
[tonumber(lib.kExprLexQuestion)] = 'Question',
|
||||
[tonumber(lib.kExprLexColon)] = 'Colon',
|
||||
[tonumber(lib.kExprLexOr)] = 'Or',
|
||||
[tonumber(lib.kExprLexAnd)] = 'And',
|
||||
[tonumber(lib.kExprLexComparison)] = 'Comparison',
|
||||
[tonumber(lib.kExprLexPlus)] = 'Plus',
|
||||
[tonumber(lib.kExprLexMinus)] = 'Minus',
|
||||
[tonumber(lib.kExprLexDot)] = 'Dot',
|
||||
[tonumber(lib.kExprLexMultiplication)] = 'Multiplication',
|
||||
|
||||
[tonumber(lib.kExprLexNot)] = 'Not',
|
||||
|
||||
[tonumber(lib.kExprLexNumber)] = 'Number',
|
||||
[tonumber(lib.kExprLexSingleQuotedString)] = 'SingleQuotedString',
|
||||
[tonumber(lib.kExprLexDoubleQuotedString)] = 'DoubleQuotedString',
|
||||
[tonumber(lib.kExprLexOption)] = 'Option',
|
||||
[tonumber(lib.kExprLexRegister)] = 'Register',
|
||||
[tonumber(lib.kExprLexEnv)] = 'Env',
|
||||
[tonumber(lib.kExprLexPlainIdentifier)] = 'PlainIdentifier',
|
||||
|
||||
[tonumber(lib.kExprLexBracket)] = 'Bracket',
|
||||
[tonumber(lib.kExprLexFigureBrace)] = 'FigureBrace',
|
||||
[tonumber(lib.kExprLexParenthesis)] = 'Parenthesis',
|
||||
[tonumber(lib.kExprLexComma)] = 'Comma',
|
||||
[tonumber(lib.kExprLexArrow)] = 'Arrow',
|
||||
|
||||
[tonumber(lib.kExprLexAssignment)] = 'Assignment',
|
||||
}
|
||||
|
||||
eltkn_mul_type_tab = {
|
||||
[tonumber(lib.kExprLexMulMul)] = 'Mul',
|
||||
[tonumber(lib.kExprLexMulDiv)] = 'Div',
|
||||
[tonumber(lib.kExprLexMulMod)] = 'Mod',
|
||||
}
|
||||
|
||||
eltkn_opt_scope_tab = {
|
||||
[tonumber(lib.kExprOptScopeUnspecified)] = 'Unspecified',
|
||||
[tonumber(lib.kExprOptScopeGlobal)] = 'Global',
|
||||
[tonumber(lib.kExprOptScopeLocal)] = 'Local',
|
||||
}
|
||||
end)
|
||||
|
||||
local function conv_eltkn_type(typ)
|
||||
return conv_enum(eltkn_type_tab, typ)
|
||||
end
|
||||
|
||||
local bracket_types = {
|
||||
Bracket = true,
|
||||
FigureBrace = true,
|
||||
Parenthesis = true,
|
||||
}
|
||||
|
||||
local function eltkn2lua(pstate, tkn)
|
||||
local ret = {
|
||||
type = conv_eltkn_type(tkn.type),
|
||||
}
|
||||
pstate_set_str(pstate, tkn.start, tkn.len, ret)
|
||||
if not ret.error and (#(ret.str) ~= ret.len) then
|
||||
ret.error = '#str /= len'
|
||||
end
|
||||
if ret.type == 'Comparison' then
|
||||
ret.data = {
|
||||
type = conv_cmp_type(tkn.data.cmp.type),
|
||||
ccs = conv_ccs(tkn.data.cmp.ccs),
|
||||
inv = (not not tkn.data.cmp.inv),
|
||||
}
|
||||
elseif ret.type == 'Multiplication' then
|
||||
ret.data = { type = conv_enum(eltkn_mul_type_tab, tkn.data.mul.type) }
|
||||
elseif bracket_types[ret.type] then
|
||||
ret.data = { closing = (not not tkn.data.brc.closing) }
|
||||
elseif ret.type == 'Register' then
|
||||
ret.data = { name = intchar2lua(tkn.data.reg.name) }
|
||||
elseif (ret.type == 'SingleQuotedString'
|
||||
or ret.type == 'DoubleQuotedString') then
|
||||
ret.data = { closed = (not not tkn.data.str.closed) }
|
||||
elseif ret.type == 'Option' then
|
||||
ret.data = {
|
||||
scope = conv_enum(eltkn_opt_scope_tab, tkn.data.opt.scope),
|
||||
name = ffi.string(tkn.data.opt.name, tkn.data.opt.len),
|
||||
}
|
||||
elseif ret.type == 'PlainIdentifier' then
|
||||
ret.data = {
|
||||
scope = intchar2lua(tkn.data.var.scope),
|
||||
autoload = (not not tkn.data.var.autoload),
|
||||
}
|
||||
elseif ret.type == 'Number' then
|
||||
ret.data = {
|
||||
is_float = (not not tkn.data.num.is_float),
|
||||
base = tonumber(tkn.data.num.base),
|
||||
}
|
||||
ret.data.val = tonumber(tkn.data.num.is_float
|
||||
and tkn.data.num.val.floating
|
||||
or tkn.data.num.val.integer)
|
||||
elseif ret.type == 'Assignment' then
|
||||
ret.data = { type = conv_expr_asgn_type(tkn.data.ass.type) }
|
||||
elseif ret.type == 'Invalid' then
|
||||
ret.data = { error = ffi.string(tkn.data.err.msg) }
|
||||
end
|
||||
return ret, tkn
|
||||
end
|
||||
|
||||
local function next_eltkn(pstate, flags)
|
||||
return eltkn2lua(pstate, lib.viml_pexpr_next_token(pstate, flags))
|
||||
end
|
||||
|
||||
describe('Expressions lexer', function()
|
||||
local flags = 0
|
||||
local should_advance = true
|
||||
local function check_advance(pstate, bytes_to_advance, initial_col)
|
||||
local tgt = initial_col + bytes_to_advance
|
||||
if should_advance then
|
||||
if pstate.reader.lines.items[0].size == tgt then
|
||||
eq(1, pstate.pos.line)
|
||||
eq(0, pstate.pos.col)
|
||||
else
|
||||
eq(0, pstate.pos.line)
|
||||
eq(tgt, pstate.pos.col)
|
||||
end
|
||||
else
|
||||
eq(0, pstate.pos.line)
|
||||
eq(initial_col, pstate.pos.col)
|
||||
end
|
||||
end
|
||||
local function singl_eltkn_test(typ, str, data)
|
||||
local pstate = new_pstate({str})
|
||||
eq({data=data, len=#str, start={col=0, line=0}, str=str, type=typ},
|
||||
next_eltkn(pstate, flags))
|
||||
check_advance(pstate, #str, 0)
|
||||
if not (
|
||||
typ == 'Spacing'
|
||||
or (typ == 'Register' and str == '@')
|
||||
or ((typ == 'SingleQuotedString' or typ == 'DoubleQuotedString')
|
||||
and not data.closed)
|
||||
) then
|
||||
pstate = new_pstate({str .. ' '})
|
||||
eq({data=data, len=#str, start={col=0, line=0}, str=str, type=typ},
|
||||
next_eltkn(pstate, flags))
|
||||
check_advance(pstate, #str, 0)
|
||||
end
|
||||
pstate = new_pstate({'x' .. str})
|
||||
pstate.pos.col = 1
|
||||
eq({data=data, len=#str, start={col=1, line=0}, str=str, type=typ},
|
||||
next_eltkn(pstate, flags))
|
||||
check_advance(pstate, #str, 1)
|
||||
end
|
||||
local function scope_test(scope)
|
||||
singl_eltkn_test('PlainIdentifier', scope .. ':test#var', {autoload=true, scope=scope})
|
||||
singl_eltkn_test('PlainIdentifier', scope .. ':', {autoload=false, scope=scope})
|
||||
end
|
||||
local function comparison_test(op, inv_op, cmp_type)
|
||||
singl_eltkn_test('Comparison', op, {type=cmp_type, inv=false, ccs='UseOption'})
|
||||
singl_eltkn_test('Comparison', inv_op, {type=cmp_type, inv=true, ccs='UseOption'})
|
||||
singl_eltkn_test('Comparison', op .. '#', {type=cmp_type, inv=false, ccs='MatchCase'})
|
||||
singl_eltkn_test('Comparison', inv_op .. '#', {type=cmp_type, inv=true, ccs='MatchCase'})
|
||||
singl_eltkn_test('Comparison', op .. '?', {type=cmp_type, inv=false, ccs='IgnoreCase'})
|
||||
singl_eltkn_test('Comparison', inv_op .. '?', {type=cmp_type, inv=true, ccs='IgnoreCase'})
|
||||
end
|
||||
local function simple_test(pstate_arg, exp_type, exp_len, exp)
|
||||
local pstate = new_pstate(pstate_arg)
|
||||
exp = shallowcopy(exp)
|
||||
exp.type = exp_type
|
||||
exp.len = exp_len or #(pstate_arg[0])
|
||||
exp.start = { col = 0, line = 0 }
|
||||
eq(exp, next_eltkn(pstate, flags))
|
||||
end
|
||||
local function stable_tests()
|
||||
singl_eltkn_test('Parenthesis', '(', {closing=false})
|
||||
singl_eltkn_test('Parenthesis', ')', {closing=true})
|
||||
singl_eltkn_test('Bracket', '[', {closing=false})
|
||||
singl_eltkn_test('Bracket', ']', {closing=true})
|
||||
singl_eltkn_test('FigureBrace', '{', {closing=false})
|
||||
singl_eltkn_test('FigureBrace', '}', {closing=true})
|
||||
singl_eltkn_test('Question', '?')
|
||||
singl_eltkn_test('Colon', ':')
|
||||
singl_eltkn_test('Dot', '.')
|
||||
singl_eltkn_test('Assignment', '.=', {type='Concat'})
|
||||
singl_eltkn_test('Plus', '+')
|
||||
singl_eltkn_test('Assignment', '+=', {type='Add'})
|
||||
singl_eltkn_test('Comma', ',')
|
||||
singl_eltkn_test('Multiplication', '*', {type='Mul'})
|
||||
singl_eltkn_test('Multiplication', '/', {type='Div'})
|
||||
singl_eltkn_test('Multiplication', '%', {type='Mod'})
|
||||
singl_eltkn_test('Spacing', ' \t\t \t\t')
|
||||
singl_eltkn_test('Spacing', ' ')
|
||||
singl_eltkn_test('Spacing', '\t')
|
||||
singl_eltkn_test('Invalid', '\x01\x02\x03', {error='E15: Invalid control character present in input: %.*s'})
|
||||
singl_eltkn_test('Number', '0123', {is_float=false, base=8, val=83})
|
||||
singl_eltkn_test('Number', '01234567', {is_float=false, base=8, val=342391})
|
||||
singl_eltkn_test('Number', '012345678', {is_float=false, base=10, val=12345678})
|
||||
singl_eltkn_test('Number', '0x123', {is_float=false, base=16, val=291})
|
||||
singl_eltkn_test('Number', '0x56FF', {is_float=false, base=16, val=22271})
|
||||
singl_eltkn_test('Number', '0xabcdef', {is_float=false, base=16, val=11259375})
|
||||
singl_eltkn_test('Number', '0xABCDEF', {is_float=false, base=16, val=11259375})
|
||||
singl_eltkn_test('Number', '0x0', {is_float=false, base=16, val=0})
|
||||
singl_eltkn_test('Number', '00', {is_float=false, base=8, val=0})
|
||||
singl_eltkn_test('Number', '0b0', {is_float=false, base=2, val=0})
|
||||
singl_eltkn_test('Number', '0b010111', {is_float=false, base=2, val=23})
|
||||
singl_eltkn_test('Number', '0b100111', {is_float=false, base=2, val=39})
|
||||
singl_eltkn_test('Number', '0', {is_float=false, base=10, val=0})
|
||||
singl_eltkn_test('Number', '9', {is_float=false, base=10, val=9})
|
||||
singl_eltkn_test('Env', '$abc')
|
||||
singl_eltkn_test('Env', '$')
|
||||
singl_eltkn_test('PlainIdentifier', 'test', {autoload=false, scope=0})
|
||||
singl_eltkn_test('PlainIdentifier', '_test', {autoload=false, scope=0})
|
||||
singl_eltkn_test('PlainIdentifier', '_test_foo', {autoload=false, scope=0})
|
||||
singl_eltkn_test('PlainIdentifier', 't', {autoload=false, scope=0})
|
||||
singl_eltkn_test('PlainIdentifier', 'test5', {autoload=false, scope=0})
|
||||
singl_eltkn_test('PlainIdentifier', 't0', {autoload=false, scope=0})
|
||||
singl_eltkn_test('PlainIdentifier', 'test#var', {autoload=true, scope=0})
|
||||
singl_eltkn_test('PlainIdentifier', 'test#var#val###', {autoload=true, scope=0})
|
||||
singl_eltkn_test('PlainIdentifier', 't#####', {autoload=true, scope=0})
|
||||
singl_eltkn_test('And', '&&')
|
||||
singl_eltkn_test('Or', '||')
|
||||
singl_eltkn_test('Invalid', '&', {error='E112: Option name missing: %.*s'})
|
||||
singl_eltkn_test('Option', '&opt', {scope='Unspecified', name='opt'})
|
||||
singl_eltkn_test('Option', '&t_xx', {scope='Unspecified', name='t_xx'})
|
||||
singl_eltkn_test('Option', '&t_\r\r', {scope='Unspecified', name='t_\r\r'})
|
||||
singl_eltkn_test('Option', '&t_\t\t', {scope='Unspecified', name='t_\t\t'})
|
||||
singl_eltkn_test('Option', '&t_ ', {scope='Unspecified', name='t_ '})
|
||||
singl_eltkn_test('Option', '&g:opt', {scope='Global', name='opt'})
|
||||
singl_eltkn_test('Option', '&l:opt', {scope='Local', name='opt'})
|
||||
singl_eltkn_test('Invalid', '&l:', {error='E112: Option name missing: %.*s'})
|
||||
singl_eltkn_test('Invalid', '&g:', {error='E112: Option name missing: %.*s'})
|
||||
singl_eltkn_test('Register', '@', {name=-1})
|
||||
singl_eltkn_test('Register', '@a', {name='a'})
|
||||
singl_eltkn_test('Register', '@\r', {name=13})
|
||||
singl_eltkn_test('Register', '@ ', {name=' '})
|
||||
singl_eltkn_test('Register', '@\t', {name=9})
|
||||
singl_eltkn_test('SingleQuotedString', '\'test', {closed=false})
|
||||
singl_eltkn_test('SingleQuotedString', '\'test\'', {closed=true})
|
||||
singl_eltkn_test('SingleQuotedString', '\'\'\'\'', {closed=true})
|
||||
singl_eltkn_test('SingleQuotedString', '\'x\'\'\'', {closed=true})
|
||||
singl_eltkn_test('SingleQuotedString', '\'\'\'x\'', {closed=true})
|
||||
singl_eltkn_test('SingleQuotedString', '\'\'\'', {closed=false})
|
||||
singl_eltkn_test('SingleQuotedString', '\'x\'\'', {closed=false})
|
||||
singl_eltkn_test('SingleQuotedString', '\'\'\'x', {closed=false})
|
||||
singl_eltkn_test('DoubleQuotedString', '"test', {closed=false})
|
||||
singl_eltkn_test('DoubleQuotedString', '"test"', {closed=true})
|
||||
singl_eltkn_test('DoubleQuotedString', '"\\""', {closed=true})
|
||||
singl_eltkn_test('DoubleQuotedString', '"x\\""', {closed=true})
|
||||
singl_eltkn_test('DoubleQuotedString', '"\\"x"', {closed=true})
|
||||
singl_eltkn_test('DoubleQuotedString', '"\\"', {closed=false})
|
||||
singl_eltkn_test('DoubleQuotedString', '"x\\"', {closed=false})
|
||||
singl_eltkn_test('DoubleQuotedString', '"\\"x', {closed=false})
|
||||
singl_eltkn_test('Not', '!')
|
||||
singl_eltkn_test('Assignment', '=', {type='Plain'})
|
||||
comparison_test('==', '!=', 'Equal')
|
||||
comparison_test('=~', '!~', 'Matches')
|
||||
comparison_test('>', '<=', 'Greater')
|
||||
comparison_test('>=', '<', 'GreaterOrEqual')
|
||||
singl_eltkn_test('Minus', '-')
|
||||
singl_eltkn_test('Assignment', '-=', {type='Subtract'})
|
||||
singl_eltkn_test('Arrow', '->')
|
||||
singl_eltkn_test('Invalid', '~', {error='E15: Unidentified character: %.*s'})
|
||||
simple_test({{data=nil, size=0}}, 'EOC', 0, {error='start.col >= #pstr'})
|
||||
simple_test({''}, 'EOC', 0, {error='start.col >= #pstr'})
|
||||
simple_test({'2.'}, 'Number', 1, {data={is_float=false, base=10, val=2}, str='2'})
|
||||
simple_test({'2e5'}, 'Number', 1, {data={is_float=false, base=10, val=2}, str='2'})
|
||||
simple_test({'2.x'}, 'Number', 1, {data={is_float=false, base=10, val=2}, str='2'})
|
||||
simple_test({'2.2.'}, 'Number', 1, {data={is_float=false, base=10, val=2}, str='2'})
|
||||
simple_test({'2.0x'}, 'Number', 1, {data={is_float=false, base=10, val=2}, str='2'})
|
||||
simple_test({'2.0e'}, 'Number', 1, {data={is_float=false, base=10, val=2}, str='2'})
|
||||
simple_test({'2.0e+'}, 'Number', 1, {data={is_float=false, base=10, val=2}, str='2'})
|
||||
simple_test({'2.0e-'}, 'Number', 1, {data={is_float=false, base=10, val=2}, str='2'})
|
||||
simple_test({'2.0e+x'}, 'Number', 1, {data={is_float=false, base=10, val=2}, str='2'})
|
||||
simple_test({'2.0e-x'}, 'Number', 1, {data={is_float=false, base=10, val=2}, str='2'})
|
||||
simple_test({'2.0e+1a'}, 'Number', 1, {data={is_float=false, base=10, val=2}, str='2'})
|
||||
simple_test({'2.0e-1a'}, 'Number', 1, {data={is_float=false, base=10, val=2}, str='2'})
|
||||
simple_test({'0b102'}, 'Number', 4, {data={is_float=false, base=2, val=2}, str='0b10'})
|
||||
simple_test({'10F'}, 'Number', 2, {data={is_float=false, base=10, val=10}, str='10'})
|
||||
simple_test({'0x0123456789ABCDEFG'}, 'Number', 18, {data={is_float=false, base=16, val=81985529216486895}, str='0x0123456789ABCDEF'})
|
||||
simple_test({{data='00', size=2}}, 'Number', 2, {data={is_float=false, base=8, val=0}, str='00'})
|
||||
simple_test({{data='009', size=2}}, 'Number', 2, {data={is_float=false, base=8, val=0}, str='00'})
|
||||
simple_test({{data='01', size=1}}, 'Number', 1, {data={is_float=false, base=10, val=0}, str='0'})
|
||||
end
|
||||
|
||||
local function regular_scope_tests()
|
||||
scope_test('s')
|
||||
scope_test('g')
|
||||
scope_test('v')
|
||||
scope_test('b')
|
||||
scope_test('w')
|
||||
scope_test('t')
|
||||
scope_test('l')
|
||||
scope_test('a')
|
||||
|
||||
simple_test({'g:'}, 'PlainIdentifier', 2, {data={scope='g', autoload=false}, str='g:'})
|
||||
simple_test({'g:is#foo'}, 'PlainIdentifier', 8, {data={scope='g', autoload=true}, str='g:is#foo'})
|
||||
simple_test({'g:isnot#foo'}, 'PlainIdentifier', 11, {data={scope='g', autoload=true}, str='g:isnot#foo'})
|
||||
end
|
||||
|
||||
local function regular_is_tests()
|
||||
comparison_test('is', 'isnot', 'Identical')
|
||||
|
||||
simple_test({'is'}, 'Comparison', 2, {data={type='Identical', inv=false, ccs='UseOption'}, str='is'})
|
||||
simple_test({'isnot'}, 'Comparison', 5, {data={type='Identical', inv=true, ccs='UseOption'}, str='isnot'})
|
||||
simple_test({'is?'}, 'Comparison', 3, {data={type='Identical', inv=false, ccs='IgnoreCase'}, str='is?'})
|
||||
simple_test({'isnot?'}, 'Comparison', 6, {data={type='Identical', inv=true, ccs='IgnoreCase'}, str='isnot?'})
|
||||
simple_test({'is#'}, 'Comparison', 3, {data={type='Identical', inv=false, ccs='MatchCase'}, str='is#'})
|
||||
simple_test({'isnot#'}, 'Comparison', 6, {data={type='Identical', inv=true, ccs='MatchCase'}, str='isnot#'})
|
||||
simple_test({'is#foo'}, 'Comparison', 3, {data={type='Identical', inv=false, ccs='MatchCase'}, str='is#'})
|
||||
simple_test({'isnot#foo'}, 'Comparison', 6, {data={type='Identical', inv=true, ccs='MatchCase'}, str='isnot#'})
|
||||
end
|
||||
|
||||
local function regular_number_tests()
|
||||
simple_test({'2.0'}, 'Number', 1, {data={is_float=false, base=10, val=2}, str='2'})
|
||||
simple_test({'2.0e5'}, 'Number', 1, {data={is_float=false, base=10, val=2}, str='2'})
|
||||
simple_test({'2.0e+5'}, 'Number', 1, {data={is_float=false, base=10, val=2}, str='2'})
|
||||
simple_test({'2.0e-5'}, 'Number', 1, {data={is_float=false, base=10, val=2}, str='2'})
|
||||
end
|
||||
|
||||
local function regular_eoc_tests()
|
||||
singl_eltkn_test('EOC', '|')
|
||||
singl_eltkn_test('EOC', '\0')
|
||||
singl_eltkn_test('EOC', '\n')
|
||||
end
|
||||
|
||||
itp('works (single tokens, zero flags)', function()
|
||||
stable_tests()
|
||||
|
||||
regular_eoc_tests()
|
||||
regular_scope_tests()
|
||||
regular_is_tests()
|
||||
regular_number_tests()
|
||||
end)
|
||||
itp('peeks', function()
|
||||
flags = tonumber(lib.kELFlagPeek)
|
||||
should_advance = false
|
||||
stable_tests()
|
||||
|
||||
regular_eoc_tests()
|
||||
regular_scope_tests()
|
||||
regular_is_tests()
|
||||
regular_number_tests()
|
||||
end)
|
||||
itp('forbids scope', function()
|
||||
flags = tonumber(lib.kELFlagForbidScope)
|
||||
stable_tests()
|
||||
|
||||
regular_eoc_tests()
|
||||
regular_is_tests()
|
||||
regular_number_tests()
|
||||
|
||||
simple_test({'g:'}, 'PlainIdentifier', 1, {data={scope=0, autoload=false}, str='g'})
|
||||
end)
|
||||
itp('allows floats', function()
|
||||
flags = tonumber(lib.kELFlagAllowFloat)
|
||||
stable_tests()
|
||||
|
||||
regular_eoc_tests()
|
||||
regular_scope_tests()
|
||||
regular_is_tests()
|
||||
|
||||
simple_test({'2.2'}, 'Number', 3, {data={is_float=true, base=10, val=2.2}, str='2.2'})
|
||||
simple_test({'2.0e5'}, 'Number', 5, {data={is_float=true, base=10, val=2e5}, str='2.0e5'})
|
||||
simple_test({'2.0e+5'}, 'Number', 6, {data={is_float=true, base=10, val=2e5}, str='2.0e+5'})
|
||||
simple_test({'2.0e-5'}, 'Number', 6, {data={is_float=true, base=10, val=2e-5}, str='2.0e-5'})
|
||||
simple_test({'2.500000e-5'}, 'Number', 11, {data={is_float=true, base=10, val=2.5e-5}, str='2.500000e-5'})
|
||||
simple_test({'2.5555e2'}, 'Number', 8, {data={is_float=true, base=10, val=2.5555e2}, str='2.5555e2'})
|
||||
simple_test({'2.5555e+2'}, 'Number', 9, {data={is_float=true, base=10, val=2.5555e2}, str='2.5555e+2'})
|
||||
simple_test({'2.5555e-2'}, 'Number', 9, {data={is_float=true, base=10, val=2.5555e-2}, str='2.5555e-2'})
|
||||
simple_test({{data='2.5e-5', size=3}},
|
||||
'Number', 3, {data={is_float=true, base=10, val=2.5}, str='2.5'})
|
||||
simple_test({{data='2.5e5', size=4}},
|
||||
'Number', 1, {data={is_float=false, base=10, val=2}, str='2'})
|
||||
simple_test({{data='2.5e-50', size=6}},
|
||||
'Number', 6, {data={is_float=true, base=10, val=2.5e-5}, str='2.5e-5'})
|
||||
end)
|
||||
itp('treats `is` as an identifier', function()
|
||||
flags = tonumber(lib.kELFlagIsNotCmp)
|
||||
stable_tests()
|
||||
|
||||
regular_eoc_tests()
|
||||
regular_scope_tests()
|
||||
regular_number_tests()
|
||||
|
||||
simple_test({'is'}, 'PlainIdentifier', 2, {data={scope=0, autoload=false}, str='is'})
|
||||
simple_test({'isnot'}, 'PlainIdentifier', 5, {data={scope=0, autoload=false}, str='isnot'})
|
||||
simple_test({'is?'}, 'PlainIdentifier', 2, {data={scope=0, autoload=false}, str='is'})
|
||||
simple_test({'isnot?'}, 'PlainIdentifier', 5, {data={scope=0, autoload=false}, str='isnot'})
|
||||
simple_test({'is#'}, 'PlainIdentifier', 3, {data={scope=0, autoload=true}, str='is#'})
|
||||
simple_test({'isnot#'}, 'PlainIdentifier', 6, {data={scope=0, autoload=true}, str='isnot#'})
|
||||
simple_test({'is#foo'}, 'PlainIdentifier', 6, {data={scope=0, autoload=true}, str='is#foo'})
|
||||
simple_test({'isnot#foo'}, 'PlainIdentifier', 9, {data={scope=0, autoload=true}, str='isnot#foo'})
|
||||
end)
|
||||
itp('forbids EOC', function()
|
||||
flags = tonumber(lib.kELFlagForbidEOC)
|
||||
stable_tests()
|
||||
|
||||
regular_scope_tests()
|
||||
regular_is_tests()
|
||||
regular_number_tests()
|
||||
|
||||
singl_eltkn_test('Invalid', '|', {error='E15: Unexpected EOC character: %.*s'})
|
||||
singl_eltkn_test('Invalid', '\0', {error='E15: Unexpected EOC character: %.*s'})
|
||||
singl_eltkn_test('Invalid', '\n', {error='E15: Unexpected EOC character: %.*s'})
|
||||
end)
|
||||
end)
|
||||
540
test/unit/viml/expressions/parser_spec.lua
Normal file
540
test/unit/viml/expressions/parser_spec.lua
Normal file
@@ -0,0 +1,540 @@
|
||||
local helpers = require('test.unit.helpers')(after_each)
|
||||
local global_helpers = require('test.helpers')
|
||||
local itp = helpers.gen_itp(it)
|
||||
local viml_helpers = require('test.unit.viml.helpers')
|
||||
|
||||
local make_enum_conv_tab = helpers.make_enum_conv_tab
|
||||
local child_call_once = helpers.child_call_once
|
||||
local alloc_log_new = helpers.alloc_log_new
|
||||
local kvi_destroy = helpers.kvi_destroy
|
||||
local conv_enum = helpers.conv_enum
|
||||
local debug_log = helpers.debug_log
|
||||
local ptr2key = helpers.ptr2key
|
||||
local cimport = helpers.cimport
|
||||
local ffi = helpers.ffi
|
||||
local neq = helpers.neq
|
||||
local eq = helpers.eq
|
||||
|
||||
local conv_ccs = viml_helpers.conv_ccs
|
||||
local new_pstate = viml_helpers.new_pstate
|
||||
local conv_cmp_type = viml_helpers.conv_cmp_type
|
||||
local pstate_set_str = viml_helpers.pstate_set_str
|
||||
local conv_expr_asgn_type = viml_helpers.conv_expr_asgn_type
|
||||
|
||||
local mergedicts_copy = global_helpers.mergedicts_copy
|
||||
local format_string = global_helpers.format_string
|
||||
local format_luav = global_helpers.format_luav
|
||||
local intchar2lua = global_helpers.intchar2lua
|
||||
local dictdiff = global_helpers.dictdiff
|
||||
|
||||
local lib = cimport('./src/nvim/viml/parser/expressions.h',
|
||||
'./src/nvim/syntax.h')
|
||||
|
||||
local alloc_log = alloc_log_new()
|
||||
|
||||
local predefined_hl_defs = {
|
||||
-- From highlight_init_both
|
||||
Conceal=true,
|
||||
Cursor=true,
|
||||
lCursor=true,
|
||||
DiffText=true,
|
||||
ErrorMsg=true,
|
||||
IncSearch=true,
|
||||
ModeMsg=true,
|
||||
NonText=true,
|
||||
PmenuSbar=true,
|
||||
StatusLine=true,
|
||||
StatusLineNC=true,
|
||||
TabLineFill=true,
|
||||
TabLineSel=true,
|
||||
TermCursor=true,
|
||||
VertSplit=true,
|
||||
WildMenu=true,
|
||||
EndOfBuffer=true,
|
||||
QuickFixLine=true,
|
||||
Substitute=true,
|
||||
Whitespace=true,
|
||||
|
||||
-- From highlight_init_(dark|light)
|
||||
ColorColumn=true,
|
||||
CursorColumn=true,
|
||||
CursorLine=true,
|
||||
CursorLineNr=true,
|
||||
DiffAdd=true,
|
||||
DiffChange=true,
|
||||
DiffDelete=true,
|
||||
Directory=true,
|
||||
FoldColumn=true,
|
||||
Folded=true,
|
||||
LineNr=true,
|
||||
MatchParen=true,
|
||||
MoreMsg=true,
|
||||
Pmenu=true,
|
||||
PmenuSel=true,
|
||||
PmenuThumb=true,
|
||||
Question=true,
|
||||
Search=true,
|
||||
SignColumn=true,
|
||||
SpecialKey=true,
|
||||
SpellBad=true,
|
||||
SpellCap=true,
|
||||
SpellLocal=true,
|
||||
SpellRare=true,
|
||||
TabLine=true,
|
||||
Title=true,
|
||||
Visual=true,
|
||||
WarningMsg=true,
|
||||
Normal=true,
|
||||
|
||||
-- From syncolor.vim, if &background
|
||||
Comment=true,
|
||||
Constant=true,
|
||||
Special=true,
|
||||
Identifier=true,
|
||||
Statement=true,
|
||||
PreProc=true,
|
||||
Type=true,
|
||||
Underlined=true,
|
||||
Ignore=true,
|
||||
|
||||
-- From syncolor.vim, below if &background
|
||||
Error=true,
|
||||
Todo=true,
|
||||
|
||||
-- From syncolor.vim, links at the bottom
|
||||
String=true,
|
||||
Character=true,
|
||||
Number=true,
|
||||
Boolean=true,
|
||||
Float=true,
|
||||
Function=true,
|
||||
Conditional=true,
|
||||
Repeat=true,
|
||||
Label=true,
|
||||
Operator=true,
|
||||
Keyword=true,
|
||||
Exception=true,
|
||||
Include=true,
|
||||
Define=true,
|
||||
Macro=true,
|
||||
PreCondit=true,
|
||||
StorageClass=true,
|
||||
Structure=true,
|
||||
Typedef=true,
|
||||
Tag=true,
|
||||
SpecialChar=true,
|
||||
Delimiter=true,
|
||||
SpecialComment=true,
|
||||
Debug=true,
|
||||
}
|
||||
|
||||
local nvim_hl_defs = {}
|
||||
|
||||
child_call_once(function()
|
||||
local i = 0
|
||||
while lib.highlight_init_cmdline[i] ~= nil do
|
||||
local hl_args = lib.highlight_init_cmdline[i]
|
||||
local s = ffi.string(hl_args)
|
||||
local err, msg = pcall(function()
|
||||
if s:sub(1, 13) == 'default link ' then
|
||||
local new_grp, grp_link = s:match('^default link (%w+) (%w+)$')
|
||||
neq(nil, new_grp)
|
||||
-- Note: group to link to must be already defined at the time of
|
||||
-- linking, otherwise it will be created as cleared. So existence
|
||||
-- of the group is checked here and not in the next pass over
|
||||
-- nvim_hl_defs.
|
||||
eq(true, not not (nvim_hl_defs[grp_link]
|
||||
or predefined_hl_defs[grp_link]))
|
||||
eq(false, not not (nvim_hl_defs[new_grp]
|
||||
or predefined_hl_defs[new_grp]))
|
||||
nvim_hl_defs[new_grp] = {'link', grp_link}
|
||||
else
|
||||
local new_grp, grp_args = s:match('^(%w+) (.*)')
|
||||
neq(nil, new_grp)
|
||||
eq(false, not not (nvim_hl_defs[new_grp]
|
||||
or predefined_hl_defs[new_grp]))
|
||||
nvim_hl_defs[new_grp] = {'definition', grp_args}
|
||||
end
|
||||
end)
|
||||
if not err then
|
||||
msg = format_string(
|
||||
'Error while processing string %s at position %u:\n%s', s, i, msg)
|
||||
error(msg)
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
for k, _ in ipairs(nvim_hl_defs) do
|
||||
eq('Nvim', k:sub(1, 4))
|
||||
-- NvimInvalid
|
||||
-- 12345678901
|
||||
local err, msg = pcall(function()
|
||||
if k:sub(5, 11) == 'Invalid' then
|
||||
neq(nil, nvim_hl_defs['Nvim' .. k:sub(12)])
|
||||
else
|
||||
neq(nil, nvim_hl_defs['NvimInvalid' .. k:sub(5)])
|
||||
end
|
||||
end)
|
||||
if not err then
|
||||
msg = format_string('Error while processing group %s:\n%s', k, msg)
|
||||
error(msg)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
local function hls_to_hl_fs(hls)
|
||||
local ret = {}
|
||||
local next_col = 0
|
||||
for i, v in ipairs(hls) do
|
||||
local group, line, col, str = v:match('^Nvim([a-zA-Z]+):(%d+):(%d+):(.*)$')
|
||||
col = tonumber(col)
|
||||
line = tonumber(line)
|
||||
assert(line == 0)
|
||||
local col_shift = col - next_col
|
||||
assert(col_shift >= 0)
|
||||
next_col = col + #str
|
||||
ret[i] = format_string('hl(%r, %r%s)',
|
||||
group,
|
||||
str,
|
||||
(col_shift == 0
|
||||
and ''
|
||||
or (', %u'):format(col_shift)))
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
local function format_check(expr, format_check_data, opts)
|
||||
-- That forces specific order.
|
||||
local zflags = opts.flags[1]
|
||||
local zdata = format_check_data[zflags]
|
||||
local dig_len
|
||||
if opts.funcname then
|
||||
print(format_string('\n%s(%r, {', opts.funcname, expr))
|
||||
dig_len = #opts.funcname + 2
|
||||
else
|
||||
print(format_string('\n_check_parsing(%r, %r, {', opts, expr))
|
||||
dig_len = #('_check_parsing(, \'') + #(format_string('%r', opts))
|
||||
end
|
||||
local digits = ' --' .. (' '):rep(dig_len - #(' --'))
|
||||
local digits2 = digits:sub(1, -10)
|
||||
for i = 0, #expr - 1 do
|
||||
if i % 10 == 0 then
|
||||
digits2 = ('%s%10u'):format(digits2, i / 10)
|
||||
end
|
||||
digits = ('%s%u'):format(digits, i % 10)
|
||||
end
|
||||
print(digits)
|
||||
if #expr > 10 then
|
||||
print(digits2)
|
||||
end
|
||||
if zdata.ast.len then
|
||||
print((' len = %u,'):format(zdata.ast.len))
|
||||
end
|
||||
print(' ast = ' .. format_luav(zdata.ast.ast, ' ') .. ',')
|
||||
if zdata.ast.err then
|
||||
print(' err = {')
|
||||
print(' arg = ' .. format_luav(zdata.ast.err.arg) .. ',')
|
||||
print(' msg = ' .. format_luav(zdata.ast.err.msg) .. ',')
|
||||
print(' },')
|
||||
end
|
||||
print('}, {')
|
||||
for _, v in ipairs(zdata.hl_fs) do
|
||||
print(' ' .. v .. ',')
|
||||
end
|
||||
local diffs = {}
|
||||
local diffs_num = 0
|
||||
for flags, v in pairs(format_check_data) do
|
||||
if flags ~= zflags then
|
||||
diffs[flags] = dictdiff(zdata, v)
|
||||
if diffs[flags] then
|
||||
if flags == 3 + zflags then
|
||||
if (dictdiff(format_check_data[1 + zflags],
|
||||
format_check_data[3 + zflags]) == nil
|
||||
or dictdiff(format_check_data[2 + zflags],
|
||||
format_check_data[3 + zflags]) == nil)
|
||||
then
|
||||
diffs[flags] = nil
|
||||
else
|
||||
diffs_num = diffs_num + 1
|
||||
end
|
||||
else
|
||||
diffs_num = diffs_num + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if diffs_num ~= 0 then
|
||||
print('}, {')
|
||||
local flags = 1
|
||||
while diffs_num ~= 0 do
|
||||
if diffs[flags] then
|
||||
diffs_num = diffs_num - 1
|
||||
local diff = diffs[flags]
|
||||
print((' [%u] = {'):format(flags))
|
||||
if diff.ast then
|
||||
print(' ast = ' .. format_luav(diff.ast, ' ') .. ',')
|
||||
end
|
||||
if diff.hl_fs then
|
||||
print(' hl_fs = ' .. format_luav(diff.hl_fs, ' ', {
|
||||
literal_strings=true
|
||||
}) .. ',')
|
||||
end
|
||||
print(' },')
|
||||
end
|
||||
flags = flags + 1
|
||||
end
|
||||
end
|
||||
print('})')
|
||||
end
|
||||
|
||||
local east_node_type_tab
|
||||
make_enum_conv_tab(lib, {
|
||||
'kExprNodeMissing',
|
||||
'kExprNodeOpMissing',
|
||||
'kExprNodeTernary',
|
||||
'kExprNodeTernaryValue',
|
||||
'kExprNodeRegister',
|
||||
'kExprNodeSubscript',
|
||||
'kExprNodeListLiteral',
|
||||
'kExprNodeUnaryPlus',
|
||||
'kExprNodeBinaryPlus',
|
||||
'kExprNodeNested',
|
||||
'kExprNodeCall',
|
||||
'kExprNodePlainIdentifier',
|
||||
'kExprNodePlainKey',
|
||||
'kExprNodeComplexIdentifier',
|
||||
'kExprNodeUnknownFigure',
|
||||
'kExprNodeLambda',
|
||||
'kExprNodeDictLiteral',
|
||||
'kExprNodeCurlyBracesIdentifier',
|
||||
'kExprNodeComma',
|
||||
'kExprNodeColon',
|
||||
'kExprNodeArrow',
|
||||
'kExprNodeComparison',
|
||||
'kExprNodeConcat',
|
||||
'kExprNodeConcatOrSubscript',
|
||||
'kExprNodeInteger',
|
||||
'kExprNodeFloat',
|
||||
'kExprNodeSingleQuotedString',
|
||||
'kExprNodeDoubleQuotedString',
|
||||
'kExprNodeOr',
|
||||
'kExprNodeAnd',
|
||||
'kExprNodeUnaryMinus',
|
||||
'kExprNodeBinaryMinus',
|
||||
'kExprNodeNot',
|
||||
'kExprNodeMultiplication',
|
||||
'kExprNodeDivision',
|
||||
'kExprNodeMod',
|
||||
'kExprNodeOption',
|
||||
'kExprNodeEnvironment',
|
||||
'kExprNodeAssignment',
|
||||
}, 'kExprNode', function(ret) east_node_type_tab = ret end)
|
||||
|
||||
local function conv_east_node_type(typ)
|
||||
return conv_enum(east_node_type_tab, typ)
|
||||
end
|
||||
|
||||
local eastnodelist2lua
|
||||
|
||||
local function eastnode2lua(pstate, eastnode, checked_nodes)
|
||||
local key = ptr2key(eastnode)
|
||||
if checked_nodes[key] then
|
||||
checked_nodes[key].duplicate_key = key
|
||||
return { duplicate = key }
|
||||
end
|
||||
local typ = conv_east_node_type(eastnode.type)
|
||||
local ret = {}
|
||||
checked_nodes[key] = ret
|
||||
ret.children = eastnodelist2lua(pstate, eastnode.children, checked_nodes)
|
||||
local str = pstate_set_str(pstate, eastnode.start, eastnode.len)
|
||||
local ret_str
|
||||
if str.error then
|
||||
ret_str = 'error:' .. str.error
|
||||
else
|
||||
ret_str = ('%u:%u:%s'):format(str.start.line, str.start.col, str.str)
|
||||
end
|
||||
if typ == 'Register' then
|
||||
typ = typ .. ('(name=%s)'):format(
|
||||
tostring(intchar2lua(eastnode.data.reg.name)))
|
||||
elseif typ == 'PlainIdentifier' then
|
||||
typ = typ .. ('(scope=%s,ident=%s)'):format(
|
||||
tostring(intchar2lua(eastnode.data.var.scope)),
|
||||
ffi.string(eastnode.data.var.ident, eastnode.data.var.ident_len))
|
||||
elseif typ == 'PlainKey' then
|
||||
typ = typ .. ('(key=%s)'):format(
|
||||
ffi.string(eastnode.data.var.ident, eastnode.data.var.ident_len))
|
||||
elseif (typ == 'UnknownFigure' or typ == 'DictLiteral'
|
||||
or typ == 'CurlyBracesIdentifier' or typ == 'Lambda') then
|
||||
typ = typ .. ('(%s)'):format(
|
||||
(eastnode.data.fig.type_guesses.allow_lambda and '\\' or '-')
|
||||
.. (eastnode.data.fig.type_guesses.allow_dict and 'd' or '-')
|
||||
.. (eastnode.data.fig.type_guesses.allow_ident and 'i' or '-'))
|
||||
elseif typ == 'Comparison' then
|
||||
typ = typ .. ('(type=%s,inv=%u,ccs=%s)'):format(
|
||||
conv_cmp_type(eastnode.data.cmp.type), eastnode.data.cmp.inv and 1 or 0,
|
||||
conv_ccs(eastnode.data.cmp.ccs))
|
||||
elseif typ == 'Integer' then
|
||||
typ = typ .. ('(val=%u)'):format(tonumber(eastnode.data.num.value))
|
||||
elseif typ == 'Float' then
|
||||
typ = typ .. format_string('(val=%e)', tonumber(eastnode.data.flt.value))
|
||||
elseif typ == 'SingleQuotedString' or typ == 'DoubleQuotedString' then
|
||||
if eastnode.data.str.value == nil then
|
||||
typ = typ .. '(val=NULL)'
|
||||
else
|
||||
local s = ffi.string(eastnode.data.str.value, eastnode.data.str.size)
|
||||
typ = format_string('%s(val=%q)', typ, s)
|
||||
end
|
||||
elseif typ == 'Option' then
|
||||
typ = ('%s(scope=%s,ident=%s)'):format(
|
||||
typ,
|
||||
tostring(intchar2lua(eastnode.data.opt.scope)),
|
||||
ffi.string(eastnode.data.opt.ident, eastnode.data.opt.ident_len))
|
||||
elseif typ == 'Environment' then
|
||||
typ = ('%s(ident=%s)'):format(
|
||||
typ,
|
||||
ffi.string(eastnode.data.env.ident, eastnode.data.env.ident_len))
|
||||
elseif typ == 'Assignment' then
|
||||
typ = ('%s(%s)'):format(typ, conv_expr_asgn_type(eastnode.data.ass.type))
|
||||
end
|
||||
ret_str = typ .. ':' .. ret_str
|
||||
local can_simplify = not ret.children
|
||||
if can_simplify then
|
||||
ret = ret_str
|
||||
else
|
||||
ret[1] = ret_str
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
eastnodelist2lua = function(pstate, eastnode, checked_nodes)
|
||||
local ret = {}
|
||||
while eastnode ~= nil do
|
||||
ret[#ret + 1] = eastnode2lua(pstate, eastnode, checked_nodes)
|
||||
eastnode = eastnode.next
|
||||
end
|
||||
if #ret == 0 then
|
||||
ret = nil
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
local function east2lua(str, pstate, east)
|
||||
local checked_nodes = {}
|
||||
local len = tonumber(pstate.pos.col)
|
||||
if pstate.pos.line == 1 then
|
||||
len = tonumber(pstate.reader.lines.items[0].size)
|
||||
end
|
||||
if type(str) == 'string' and len == #str then
|
||||
len = nil
|
||||
end
|
||||
return {
|
||||
err = east.err.msg ~= nil and {
|
||||
msg = ffi.string(east.err.msg),
|
||||
arg = ffi.string(east.err.arg, east.err.arg_len),
|
||||
} or nil,
|
||||
len = len,
|
||||
ast = eastnodelist2lua(pstate, east.root, checked_nodes),
|
||||
}
|
||||
end
|
||||
|
||||
local function phl2lua(pstate)
|
||||
local ret = {}
|
||||
for i = 0, (tonumber(pstate.colors.size) - 1) do
|
||||
local chunk = pstate.colors.items[i]
|
||||
local chunk_tbl = pstate_set_str(
|
||||
pstate, chunk.start, chunk.end_col - chunk.start.col, {
|
||||
group = ffi.string(chunk.group),
|
||||
})
|
||||
ret[i + 1] = ('%s:%u:%u:%s'):format(
|
||||
chunk_tbl.group,
|
||||
chunk_tbl.start.line,
|
||||
chunk_tbl.start.col,
|
||||
chunk_tbl.str)
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
child_call_once(function()
|
||||
assert:set_parameter('TableFormatLevel', 1000000)
|
||||
end)
|
||||
|
||||
describe('Expressions parser', function()
|
||||
local function _check_parsing(opts, str, exp_ast, exp_highlighting_fs,
|
||||
nz_flags_exps)
|
||||
local zflags = opts.flags[1]
|
||||
nz_flags_exps = nz_flags_exps or {}
|
||||
local format_check_data = {}
|
||||
for _, flags in ipairs(opts.flags) do
|
||||
debug_log(('Running test case (%s, %u)'):format(str, flags))
|
||||
local err, msg = pcall(function()
|
||||
if os.getenv('NVIM_TEST_PARSER_SPEC_PRINT_TEST_CASE') == '1' then
|
||||
print(str, flags)
|
||||
end
|
||||
alloc_log:check({})
|
||||
|
||||
local pstate = new_pstate({str})
|
||||
local east = lib.viml_pexpr_parse(pstate, flags)
|
||||
local ast = east2lua(str, pstate, east)
|
||||
local hls = phl2lua(pstate)
|
||||
if exp_ast == nil then
|
||||
format_check_data[flags] = {ast=ast, hl_fs=hls_to_hl_fs(hls)}
|
||||
else
|
||||
local exps = {
|
||||
ast = exp_ast,
|
||||
hl_fs = exp_highlighting_fs,
|
||||
}
|
||||
local add_exps = nz_flags_exps[flags]
|
||||
if not add_exps and flags == 3 + zflags then
|
||||
add_exps = nz_flags_exps[1 + zflags] or nz_flags_exps[2 + zflags]
|
||||
end
|
||||
if add_exps then
|
||||
if add_exps.ast then
|
||||
exps.ast = mergedicts_copy(exps.ast, add_exps.ast)
|
||||
end
|
||||
if add_exps.hl_fs then
|
||||
exps.hl_fs = mergedicts_copy(exps.hl_fs, add_exps.hl_fs)
|
||||
end
|
||||
end
|
||||
eq(exps.ast, ast)
|
||||
if exp_highlighting_fs then
|
||||
local exp_highlighting = {}
|
||||
local next_col = 0
|
||||
for i, h in ipairs(exps.hl_fs) do
|
||||
exp_highlighting[i], next_col = h(next_col)
|
||||
end
|
||||
eq(exp_highlighting, hls)
|
||||
end
|
||||
end
|
||||
lib.viml_pexpr_free_ast(east)
|
||||
kvi_destroy(pstate.colors)
|
||||
alloc_log:clear_tmp_allocs(true)
|
||||
alloc_log:check({})
|
||||
end)
|
||||
if not err then
|
||||
msg = format_string('Error while processing test (%r, %u):\n%s',
|
||||
str, flags, msg)
|
||||
error(msg)
|
||||
end
|
||||
end
|
||||
if exp_ast == nil then
|
||||
format_check(str, format_check_data, opts)
|
||||
end
|
||||
end
|
||||
local function hl(group, str, shift)
|
||||
return function(next_col)
|
||||
if nvim_hl_defs['Nvim' .. group] == nil then
|
||||
error(('Unknown group: Nvim%s'):format(group))
|
||||
end
|
||||
local col = next_col + (shift or 0)
|
||||
return (('%s:%u:%u:%s'):format(
|
||||
'Nvim' .. group,
|
||||
0,
|
||||
col,
|
||||
str)), (col + #str)
|
||||
end
|
||||
end
|
||||
local function fmtn(typ, args, rest)
|
||||
return ('%s(%s)%s'):format(typ, args, rest)
|
||||
end
|
||||
require('test.unit.viml.expressions.parser_tests')(
|
||||
itp, _check_parsing, hl, fmtn)
|
||||
end)
|
||||
8317
test/unit/viml/expressions/parser_tests.lua
Normal file
8317
test/unit/viml/expressions/parser_tests.lua
Normal file
File diff suppressed because it is too large
Load Diff
130
test/unit/viml/helpers.lua
Normal file
130
test/unit/viml/helpers.lua
Normal file
@@ -0,0 +1,130 @@
|
||||
local helpers = require('test.unit.helpers')(nil)
|
||||
|
||||
local ffi = helpers.ffi
|
||||
local cimport = helpers.cimport
|
||||
local kvi_new = helpers.kvi_new
|
||||
local kvi_init = helpers.kvi_init
|
||||
local conv_enum = helpers.conv_enum
|
||||
local make_enum_conv_tab = helpers.make_enum_conv_tab
|
||||
|
||||
local lib = cimport('./src/nvim/viml/parser/expressions.h')
|
||||
|
||||
local function new_pstate(strings)
|
||||
local strings_idx = 0
|
||||
local function get_line(_, ret_pline)
|
||||
strings_idx = strings_idx + 1
|
||||
local str = strings[strings_idx]
|
||||
local data, size
|
||||
if type(str) == 'string' then
|
||||
data = str
|
||||
size = #str
|
||||
elseif type(str) == 'nil' then
|
||||
data = nil
|
||||
size = 0
|
||||
elseif type(str) == 'table' then
|
||||
data = str.data
|
||||
size = str.size
|
||||
elseif type(str) == 'function' then
|
||||
data, size = str()
|
||||
size = size or 0
|
||||
end
|
||||
ret_pline.data = data
|
||||
ret_pline.size = size
|
||||
ret_pline.allocated = false
|
||||
end
|
||||
local state = {
|
||||
reader = {
|
||||
get_line = get_line,
|
||||
cookie = nil,
|
||||
conv = {
|
||||
vc_type = 0,
|
||||
vc_factor = 1,
|
||||
vc_fail = false,
|
||||
},
|
||||
},
|
||||
pos = { line = 0, col = 0 },
|
||||
colors = kvi_new('ParserHighlight'),
|
||||
can_continuate = false,
|
||||
}
|
||||
local ret = ffi.new('ParserState', state)
|
||||
kvi_init(ret.reader.lines)
|
||||
kvi_init(ret.stack)
|
||||
return ret
|
||||
end
|
||||
|
||||
local function pline2lua(pline)
|
||||
return ffi.string(pline.data, pline.size)
|
||||
end
|
||||
|
||||
local function pstate_str(pstate, start, len)
|
||||
local str = nil
|
||||
local err = nil
|
||||
if start.line < pstate.reader.lines.size then
|
||||
local pstr = pline2lua(pstate.reader.lines.items[start.line])
|
||||
if start.col >= #pstr then
|
||||
err = 'start.col >= #pstr'
|
||||
else
|
||||
str = pstr:sub(tonumber(start.col) + 1, tonumber(start.col + len))
|
||||
end
|
||||
else
|
||||
err = 'start.line >= pstate.reader.lines.size'
|
||||
end
|
||||
return str, err
|
||||
end
|
||||
|
||||
local function pstate_set_str(pstate, start, len, ret)
|
||||
ret = ret or {}
|
||||
ret.start = {
|
||||
line = tonumber(start.line),
|
||||
col = tonumber(start.col)
|
||||
}
|
||||
ret.len = tonumber(len)
|
||||
ret.str, ret.error = pstate_str(pstate, start, len)
|
||||
return ret
|
||||
end
|
||||
|
||||
local eltkn_cmp_type_tab
|
||||
make_enum_conv_tab(lib, {
|
||||
'kExprCmpEqual',
|
||||
'kExprCmpMatches',
|
||||
'kExprCmpGreater',
|
||||
'kExprCmpGreaterOrEqual',
|
||||
'kExprCmpIdentical',
|
||||
}, 'kExprCmp', function(ret) eltkn_cmp_type_tab = ret end)
|
||||
|
||||
local function conv_cmp_type(typ)
|
||||
return conv_enum(eltkn_cmp_type_tab, typ)
|
||||
end
|
||||
|
||||
local ccs_tab
|
||||
make_enum_conv_tab(lib, {
|
||||
'kCCStrategyUseOption',
|
||||
'kCCStrategyMatchCase',
|
||||
'kCCStrategyIgnoreCase',
|
||||
}, 'kCCStrategy', function(ret) ccs_tab = ret end)
|
||||
|
||||
local function conv_ccs(ccs)
|
||||
return conv_enum(ccs_tab, ccs)
|
||||
end
|
||||
|
||||
local expr_asgn_type_tab
|
||||
make_enum_conv_tab(lib, {
|
||||
'kExprAsgnPlain',
|
||||
'kExprAsgnAdd',
|
||||
'kExprAsgnSubtract',
|
||||
'kExprAsgnConcat',
|
||||
}, 'kExprAsgn', function(ret) expr_asgn_type_tab = ret end)
|
||||
|
||||
local function conv_expr_asgn_type(expr_asgn_type)
|
||||
return conv_enum(expr_asgn_type_tab, expr_asgn_type)
|
||||
end
|
||||
|
||||
return {
|
||||
conv_ccs = conv_ccs,
|
||||
pline2lua = pline2lua,
|
||||
pstate_str = pstate_str,
|
||||
new_pstate = new_pstate,
|
||||
conv_cmp_type = conv_cmp_type,
|
||||
pstate_set_str = pstate_set_str,
|
||||
conv_expr_asgn_type = conv_expr_asgn_type,
|
||||
}
|
||||
Reference in New Issue
Block a user