aboutsummaryrefslogtreecommitdiff
path: root/test/unit/viml/expressions/lexer_spec.lua
diff options
context:
space:
mode:
Diffstat (limited to 'test/unit/viml/expressions/lexer_spec.lua')
-rw-r--r--test/unit/viml/expressions/lexer_spec.lua247
1 files changed, 196 insertions, 51 deletions
diff --git a/test/unit/viml/expressions/lexer_spec.lua b/test/unit/viml/expressions/lexer_spec.lua
index 32182f650d..972478c2e5 100644
--- a/test/unit/viml/expressions/lexer_spec.lua
+++ b/test/unit/viml/expressions/lexer_spec.lua
@@ -1,5 +1,6 @@
local helpers = require('test.unit.helpers')(after_each)
local viml_helpers = require('test.unit.viml.helpers')
+local global_helpers = require('test.helpers')
local itp = helpers.gen_itp(it)
local child_call_once = helpers.child_call_once
@@ -13,6 +14,8 @@ local new_pstate = viml_helpers.new_pstate
local intchar2lua = viml_helpers.intchar2lua
local pstate_set_str = viml_helpers.pstate_set_str
+local shallowcopy = global_helpers.shallowcopy
+
local lib = cimport('./src/nvim/viml/parser/expressions.h')
local eltkn_type_tab, eltkn_cmp_type_tab, ccs_tab, eltkn_mul_type_tab
@@ -121,37 +124,81 @@ local function eltkn2lua(pstate, tkn)
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),
+ }
elseif ret.type == 'Invalid' then
ret.data = { error = ffi.string(tkn.data.err.msg) }
end
return ret, tkn
end
-local function next_eltkn(pstate)
- return eltkn2lua(pstate, lib.viml_pexpr_next_token(pstate, false))
+local function next_eltkn(pstate, flags)
+ return eltkn2lua(pstate, lib.viml_pexpr_next_token(pstate, flags))
end
describe('Expressions lexer', function()
- itp('works (single tokens)', function()
- 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))
- 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))
+ 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
- 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))
+ 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)
+ local 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})
@@ -170,9 +217,9 @@ describe('Expressions lexer', function()
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')
- singl_eltkn_test('Number', '0')
- singl_eltkn_test('Number', '9')
+ singl_eltkn_test('Number', '0123', {is_float=false})
+ singl_eltkn_test('Number', '0', {is_float=false})
+ singl_eltkn_test('Number', '9', {is_float=false})
singl_eltkn_test('Env', '$abc')
singl_eltkn_test('Env', '$')
singl_eltkn_test('PlainIdentifier', 'test', {autoload=false, scope=0})
@@ -184,28 +231,8 @@ describe('Expressions lexer', function()
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})
- 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
- scope_test('s')
- scope_test('g')
- scope_test('v')
- scope_test('b')
- scope_test('w')
- scope_test('t')
- scope_test('l')
- scope_test('a')
- 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
- comparison_test('is', 'isnot', 'Identical')
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'})
@@ -245,16 +272,134 @@ describe('Expressions lexer', function()
comparison_test('>=', '<', 'GreaterOrEqual')
singl_eltkn_test('Minus', '-')
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}, str='2'})
+ simple_test({'2.x'}, 'Number', 1, {data={is_float=false}, str='2'})
+ 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}, str='2'})
+ simple_test({'2.0x'}, 'Number', 1, {data={is_float=false}, str='2'})
+ simple_test({'2.0e'}, 'Number', 1, {data={is_float=false}, str='2'})
+ simple_test({'2.0e+'}, 'Number', 1, {data={is_float=false}, str='2'})
+ simple_test({'2.0e-'}, 'Number', 1, {data={is_float=false}, str='2'})
+ simple_test({'2.0e+x'}, 'Number', 1, {data={is_float=false}, str='2'})
+ simple_test({'2.0e-x'}, 'Number', 1, {data={is_float=false}, str='2'})
+ simple_test({'2.0e5'}, 'Number', 1, {data={is_float=false}, str='2'})
+ simple_test({'2.0e+5'}, 'Number', 1, {data={is_float=false}, str='2'})
+ simple_test({'2.0e-5'}, 'Number', 1, {data={is_float=false}, str='2'})
+ end
+
+ local function regular_eoc_tests()
+ singl_eltkn_test('EOC', '|')
singl_eltkn_test('EOC', '\0')
singl_eltkn_test('EOC', '\n')
- singl_eltkn_test('Invalid', '~', {error='E15: Unidentified character: %.*s'})
+ 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.0'}, 'Number', 3, {data={is_float=true}, str='2.0'})
+ simple_test({'2.0x'}, 'Number', 3, {data={is_float=true}, str='2.0'})
+ simple_test({'2.0e'}, 'Number', 3, {data={is_float=true}, str='2.0'})
+ simple_test({'2.0e+'}, 'Number', 3, {data={is_float=true}, str='2.0'})
+ simple_test({'2.0e-'}, 'Number', 3, {data={is_float=true}, str='2.0'})
+ simple_test({'2.0e+x'}, 'Number', 3, {data={is_float=true}, str='2.0'})
+ simple_test({'2.0e-x'}, 'Number', 3, {data={is_float=true}, str='2.0'})
+ simple_test({'2.0e5'}, 'Number', 5, {data={is_float=true}, str='2.0e5'})
+ simple_test({'2.0e+5'}, 'Number', 6, {data={is_float=true}, str='2.0e+5'})
+ simple_test({'2.0e-5'}, 'Number', 6, {data={is_float=true}, str='2.0e-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()
- local pstate = new_pstate({{data=nil, size=0}})
- eq({len=0, error='start.col >= #pstr', start={col=0, line=0}, type='EOC'},
- next_eltkn(pstate))
+ regular_scope_tests()
+ regular_is_tests()
+ regular_number_tests()
- local pstate = new_pstate({''})
- eq({len=0, error='start.col >= #pstr', start={col=0, line=0}, type='EOC'},
- next_eltkn(pstate))
+ 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)