mirror of
				https://github.com/neovim/neovim.git
				synced 2025-11-04 09:44:31 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			536 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			536 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
local t = require('test.unit.testutil')
 | 
						|
local itp = t.gen_itp(it)
 | 
						|
local t_viml = require('test.unit.viml.testutil')
 | 
						|
 | 
						|
local make_enum_conv_tab = t.make_enum_conv_tab
 | 
						|
local child_call_once = t.child_call_once
 | 
						|
local alloc_log_new = t.alloc_log_new
 | 
						|
local kvi_destroy = t.kvi_destroy
 | 
						|
local conv_enum = t.conv_enum
 | 
						|
local debug_log = t.debug_log
 | 
						|
local ptr2key = t.ptr2key
 | 
						|
local cimport = t.cimport
 | 
						|
local ffi = t.ffi
 | 
						|
local neq = t.neq
 | 
						|
local eq = t.eq
 | 
						|
local mergedicts_copy = t.mergedicts_copy
 | 
						|
local format_string = require('test.format_string').format_string
 | 
						|
local format_luav = require('test.format_string').format_luav
 | 
						|
local intchar2lua = t.intchar2lua
 | 
						|
local dictdiff = t.dictdiff
 | 
						|
 | 
						|
local conv_ccs = t_viml.conv_ccs
 | 
						|
local new_pstate = t_viml.new_pstate
 | 
						|
local conv_cmp_type = t_viml.conv_cmp_type
 | 
						|
local pstate_set_str = t_viml.pstate_set_str
 | 
						|
local conv_expr_asgn_type = t_viml.conv_expr_asgn_type
 | 
						|
 | 
						|
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,
 | 
						|
  WinSeparator = true,
 | 
						|
  EndOfBuffer = true,
 | 
						|
  QuickFixLine = true,
 | 
						|
  Substitute = true,
 | 
						|
  Whitespace = true,
 | 
						|
  Error = true,
 | 
						|
  Todo = true,
 | 
						|
  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,
 | 
						|
 | 
						|
  -- 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,
 | 
						|
  Comment = true,
 | 
						|
  Constant = true,
 | 
						|
  Special = true,
 | 
						|
  Identifier = true,
 | 
						|
  Statement = true,
 | 
						|
  PreProc = true,
 | 
						|
  Type = true,
 | 
						|
  Underlined = true,
 | 
						|
  Ignore = 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
 | 
						|
 | 
						|
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)
 |