diff options
Diffstat (limited to 'test/unit/viml/expressions/lexer_spec.lua')
-rw-r--r-- | test/unit/viml/expressions/lexer_spec.lua | 247 |
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) |