neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

lexer_spec.lua (22753B)


      1 local t = require('test.unit.testutil')
      2 local itp = t.gen_itp(it)
      3 local t_viml = require('test.unit.viml.testutil')
      4 
      5 local child_call_once = t.child_call_once
      6 local conv_enum = t.conv_enum
      7 local cimport = t.cimport
      8 local ffi = t.ffi
      9 local eq = t.eq
     10 local shallowcopy = t.shallowcopy
     11 local intchar2lua = t.intchar2lua
     12 
     13 local conv_ccs = t_viml.conv_ccs
     14 local new_pstate = t_viml.new_pstate
     15 local conv_cmp_type = t_viml.conv_cmp_type
     16 local pstate_set_str = t_viml.pstate_set_str
     17 local conv_expr_asgn_type = t_viml.conv_expr_asgn_type
     18 
     19 local lib = cimport('./src/nvim/viml/parser/expressions.h')
     20 
     21 local eltkn_type_tab, eltkn_mul_type_tab, eltkn_opt_scope_tab
     22 child_call_once(function()
     23  eltkn_type_tab = {
     24    [tonumber(lib.kExprLexInvalid)] = 'Invalid',
     25    [tonumber(lib.kExprLexMissing)] = 'Missing',
     26    [tonumber(lib.kExprLexSpacing)] = 'Spacing',
     27    [tonumber(lib.kExprLexEOC)] = 'EOC',
     28 
     29    [tonumber(lib.kExprLexQuestion)] = 'Question',
     30    [tonumber(lib.kExprLexColon)] = 'Colon',
     31    [tonumber(lib.kExprLexOr)] = 'Or',
     32    [tonumber(lib.kExprLexAnd)] = 'And',
     33    [tonumber(lib.kExprLexComparison)] = 'Comparison',
     34    [tonumber(lib.kExprLexPlus)] = 'Plus',
     35    [tonumber(lib.kExprLexMinus)] = 'Minus',
     36    [tonumber(lib.kExprLexDot)] = 'Dot',
     37    [tonumber(lib.kExprLexMultiplication)] = 'Multiplication',
     38 
     39    [tonumber(lib.kExprLexNot)] = 'Not',
     40 
     41    [tonumber(lib.kExprLexNumber)] = 'Number',
     42    [tonumber(lib.kExprLexSingleQuotedString)] = 'SingleQuotedString',
     43    [tonumber(lib.kExprLexDoubleQuotedString)] = 'DoubleQuotedString',
     44    [tonumber(lib.kExprLexOption)] = 'Option',
     45    [tonumber(lib.kExprLexRegister)] = 'Register',
     46    [tonumber(lib.kExprLexEnv)] = 'Env',
     47    [tonumber(lib.kExprLexPlainIdentifier)] = 'PlainIdentifier',
     48 
     49    [tonumber(lib.kExprLexBracket)] = 'Bracket',
     50    [tonumber(lib.kExprLexFigureBrace)] = 'FigureBrace',
     51    [tonumber(lib.kExprLexParenthesis)] = 'Parenthesis',
     52    [tonumber(lib.kExprLexComma)] = 'Comma',
     53    [tonumber(lib.kExprLexArrow)] = 'Arrow',
     54 
     55    [tonumber(lib.kExprLexAssignment)] = 'Assignment',
     56  }
     57 
     58  eltkn_mul_type_tab = {
     59    [tonumber(lib.kExprLexMulMul)] = 'Mul',
     60    [tonumber(lib.kExprLexMulDiv)] = 'Div',
     61    [tonumber(lib.kExprLexMulMod)] = 'Mod',
     62  }
     63 
     64  eltkn_opt_scope_tab = {
     65    [tonumber(lib.kExprOptScopeUnspecified)] = 'Unspecified',
     66    [tonumber(lib.kExprOptScopeGlobal)] = 'Global',
     67    [tonumber(lib.kExprOptScopeLocal)] = 'Local',
     68  }
     69 end)
     70 
     71 local function conv_eltkn_type(typ)
     72  return conv_enum(eltkn_type_tab, typ)
     73 end
     74 
     75 local bracket_types = {
     76  Bracket = true,
     77  FigureBrace = true,
     78  Parenthesis = true,
     79 }
     80 
     81 local function eltkn2lua(pstate, tkn)
     82  local ret = {
     83    type = conv_eltkn_type(tkn.type),
     84  }
     85  pstate_set_str(pstate, tkn.start, tkn.len, ret)
     86  if not ret.error and (#ret.str ~= ret.len) then
     87    ret.error = '#str /= len'
     88  end
     89  if ret.type == 'Comparison' then
     90    ret.data = {
     91      type = conv_cmp_type(tkn.data.cmp.type),
     92      ccs = conv_ccs(tkn.data.cmp.ccs),
     93      inv = not not tkn.data.cmp.inv,
     94    }
     95  elseif ret.type == 'Multiplication' then
     96    ret.data = { type = conv_enum(eltkn_mul_type_tab, tkn.data.mul.type) }
     97  elseif bracket_types[ret.type] then
     98    ret.data = { closing = not not tkn.data.brc.closing }
     99  elseif ret.type == 'Register' then
    100    ret.data = { name = intchar2lua(tkn.data.reg.name) }
    101  elseif ret.type == 'SingleQuotedString' or ret.type == 'DoubleQuotedString' then
    102    ret.data = { closed = not not tkn.data.str.closed }
    103  elseif ret.type == 'Option' then
    104    ret.data = {
    105      scope = conv_enum(eltkn_opt_scope_tab, tkn.data.opt.scope),
    106      name = ffi.string(tkn.data.opt.name, tkn.data.opt.len),
    107    }
    108  elseif ret.type == 'PlainIdentifier' then
    109    ret.data = {
    110      scope = intchar2lua(tkn.data.var.scope),
    111      autoload = not not tkn.data.var.autoload,
    112    }
    113  elseif ret.type == 'Number' then
    114    ret.data = {
    115      is_float = not not tkn.data.num.is_float,
    116      base = tonumber(tkn.data.num.base),
    117    }
    118    ret.data.val =
    119      tonumber(tkn.data.num.is_float and tkn.data.num.val.floating or tkn.data.num.val.integer)
    120  elseif ret.type == 'Assignment' then
    121    ret.data = { type = conv_expr_asgn_type(tkn.data.ass.type) }
    122  elseif ret.type == 'Invalid' then
    123    ret.data = { error = ffi.string(tkn.data.err.msg) }
    124  end
    125  return ret, tkn
    126 end
    127 
    128 local function next_eltkn(pstate, flags)
    129  return eltkn2lua(pstate, lib.viml_pexpr_next_token(pstate, flags))
    130 end
    131 
    132 describe('Expressions lexer', function()
    133  local flags = 0
    134  local should_advance = true
    135  local function check_advance(pstate, bytes_to_advance, initial_col)
    136    local tgt = initial_col + bytes_to_advance
    137    if should_advance then
    138      if pstate.reader.lines.items[0].size == tgt then
    139        eq(1, pstate.pos.line)
    140        eq(0, pstate.pos.col)
    141      else
    142        eq(0, pstate.pos.line)
    143        eq(tgt, pstate.pos.col)
    144      end
    145    else
    146      eq(0, pstate.pos.line)
    147      eq(initial_col, pstate.pos.col)
    148    end
    149  end
    150  local function singl_eltkn_test(typ, str, data)
    151    local pstate = new_pstate({ str })
    152    eq(
    153      { data = data, len = #str, start = { col = 0, line = 0 }, str = str, type = typ },
    154      next_eltkn(pstate, flags)
    155    )
    156    check_advance(pstate, #str, 0)
    157    if
    158      not (
    159        typ == 'Spacing'
    160        or (typ == 'Register' and str == '@')
    161        or ((typ == 'SingleQuotedString' or typ == 'DoubleQuotedString') and not data.closed)
    162      )
    163    then
    164      pstate = new_pstate({ str .. ' ' })
    165      eq(
    166        { data = data, len = #str, start = { col = 0, line = 0 }, str = str, type = typ },
    167        next_eltkn(pstate, flags)
    168      )
    169      check_advance(pstate, #str, 0)
    170    end
    171    pstate = new_pstate({ 'x' .. str })
    172    pstate.pos.col = 1
    173    eq(
    174      { data = data, len = #str, start = { col = 1, line = 0 }, str = str, type = typ },
    175      next_eltkn(pstate, flags)
    176    )
    177    check_advance(pstate, #str, 1)
    178  end
    179  local function scope_test(scope)
    180    singl_eltkn_test('PlainIdentifier', scope .. ':test#var', { autoload = true, scope = scope })
    181    singl_eltkn_test('PlainIdentifier', scope .. ':', { autoload = false, scope = scope })
    182  end
    183  local function comparison_test(op, inv_op, cmp_type)
    184    singl_eltkn_test('Comparison', op, { type = cmp_type, inv = false, ccs = 'UseOption' })
    185    singl_eltkn_test('Comparison', inv_op, { type = cmp_type, inv = true, ccs = 'UseOption' })
    186    singl_eltkn_test('Comparison', op .. '#', { type = cmp_type, inv = false, ccs = 'MatchCase' })
    187    singl_eltkn_test(
    188      'Comparison',
    189      inv_op .. '#',
    190      { type = cmp_type, inv = true, ccs = 'MatchCase' }
    191    )
    192    singl_eltkn_test('Comparison', op .. '?', { type = cmp_type, inv = false, ccs = 'IgnoreCase' })
    193    singl_eltkn_test(
    194      'Comparison',
    195      inv_op .. '?',
    196      { type = cmp_type, inv = true, ccs = 'IgnoreCase' }
    197    )
    198  end
    199  local function simple_test(pstate_arg, exp_type, exp_len, exp)
    200    local pstate = new_pstate(pstate_arg)
    201    exp = shallowcopy(exp)
    202    exp.type = exp_type
    203    exp.len = exp_len or #pstate_arg[0]
    204    exp.start = { col = 0, line = 0 }
    205    eq(exp, next_eltkn(pstate, flags))
    206  end
    207  local function stable_tests()
    208    singl_eltkn_test('Parenthesis', '(', { closing = false })
    209    singl_eltkn_test('Parenthesis', ')', { closing = true })
    210    singl_eltkn_test('Bracket', '[', { closing = false })
    211    singl_eltkn_test('Bracket', ']', { closing = true })
    212    singl_eltkn_test('FigureBrace', '{', { closing = false })
    213    singl_eltkn_test('FigureBrace', '}', { closing = true })
    214    singl_eltkn_test('Question', '?')
    215    singl_eltkn_test('Colon', ':')
    216    singl_eltkn_test('Dot', '.')
    217    singl_eltkn_test('Assignment', '.=', { type = 'Concat' })
    218    singl_eltkn_test('Plus', '+')
    219    singl_eltkn_test('Assignment', '+=', { type = 'Add' })
    220    singl_eltkn_test('Comma', ',')
    221    singl_eltkn_test('Multiplication', '*', { type = 'Mul' })
    222    singl_eltkn_test('Multiplication', '/', { type = 'Div' })
    223    singl_eltkn_test('Multiplication', '%', { type = 'Mod' })
    224    singl_eltkn_test('Spacing', '  \t\t  \t\t')
    225    singl_eltkn_test('Spacing', ' ')
    226    singl_eltkn_test('Spacing', '\t')
    227    singl_eltkn_test(
    228      'Invalid',
    229      '\x01\x02\x03',
    230      { error = 'E15: Invalid control character present in input: %.*s' }
    231    )
    232    singl_eltkn_test('Number', '0123', { is_float = false, base = 8, val = 83 })
    233    singl_eltkn_test('Number', '01234567', { is_float = false, base = 8, val = 342391 })
    234    singl_eltkn_test('Number', '012345678', { is_float = false, base = 10, val = 12345678 })
    235    singl_eltkn_test('Number', '0x123', { is_float = false, base = 16, val = 291 })
    236    singl_eltkn_test('Number', '0x56FF', { is_float = false, base = 16, val = 22271 })
    237    singl_eltkn_test('Number', '0xabcdef', { is_float = false, base = 16, val = 11259375 })
    238    singl_eltkn_test('Number', '0xABCDEF', { is_float = false, base = 16, val = 11259375 })
    239    singl_eltkn_test('Number', '0x0', { is_float = false, base = 16, val = 0 })
    240    singl_eltkn_test('Number', '00', { is_float = false, base = 8, val = 0 })
    241    singl_eltkn_test('Number', '0b0', { is_float = false, base = 2, val = 0 })
    242    singl_eltkn_test('Number', '0b010111', { is_float = false, base = 2, val = 23 })
    243    singl_eltkn_test('Number', '0b100111', { is_float = false, base = 2, val = 39 })
    244    singl_eltkn_test('Number', '0', { is_float = false, base = 10, val = 0 })
    245    singl_eltkn_test('Number', '9', { is_float = false, base = 10, val = 9 })
    246    singl_eltkn_test('Env', '$abc')
    247    singl_eltkn_test('Env', '$')
    248    singl_eltkn_test('PlainIdentifier', 'test', { autoload = false, scope = 0 })
    249    singl_eltkn_test('PlainIdentifier', '_test', { autoload = false, scope = 0 })
    250    singl_eltkn_test('PlainIdentifier', '_test_foo', { autoload = false, scope = 0 })
    251    singl_eltkn_test('PlainIdentifier', 't', { autoload = false, scope = 0 })
    252    singl_eltkn_test('PlainIdentifier', 'test5', { autoload = false, scope = 0 })
    253    singl_eltkn_test('PlainIdentifier', 't0', { autoload = false, scope = 0 })
    254    singl_eltkn_test('PlainIdentifier', 'test#var', { autoload = true, scope = 0 })
    255    singl_eltkn_test('PlainIdentifier', 'test#var#val###', { autoload = true, scope = 0 })
    256    singl_eltkn_test('PlainIdentifier', 't#####', { autoload = true, scope = 0 })
    257    singl_eltkn_test('And', '&&')
    258    singl_eltkn_test('Or', '||')
    259    singl_eltkn_test('Invalid', '&', { error = 'E112: Option name missing: %.*s' })
    260    singl_eltkn_test('Option', '&opt', { scope = 'Unspecified', name = 'opt' })
    261    singl_eltkn_test('Option', '&t_xx', { scope = 'Unspecified', name = 't_xx' })
    262    singl_eltkn_test('Option', '&t_\r\r', { scope = 'Unspecified', name = 't_\r\r' })
    263    singl_eltkn_test('Option', '&t_\t\t', { scope = 'Unspecified', name = 't_\t\t' })
    264    singl_eltkn_test('Option', '&t_  ', { scope = 'Unspecified', name = 't_  ' })
    265    singl_eltkn_test('Option', '&g:opt', { scope = 'Global', name = 'opt' })
    266    singl_eltkn_test('Option', '&l:opt', { scope = 'Local', name = 'opt' })
    267    singl_eltkn_test('Invalid', '&l:', { error = 'E112: Option name missing: %.*s' })
    268    singl_eltkn_test('Invalid', '&g:', { error = 'E112: Option name missing: %.*s' })
    269    singl_eltkn_test('Register', '@', { name = -1 })
    270    singl_eltkn_test('Register', '@a', { name = 'a' })
    271    singl_eltkn_test('Register', '@\r', { name = 13 })
    272    singl_eltkn_test('Register', '@ ', { name = ' ' })
    273    singl_eltkn_test('Register', '@\t', { name = 9 })
    274    singl_eltkn_test('SingleQuotedString', "'test", { closed = false })
    275    singl_eltkn_test('SingleQuotedString', "'test'", { closed = true })
    276    singl_eltkn_test('SingleQuotedString', "''''", { closed = true })
    277    singl_eltkn_test('SingleQuotedString', "'x'''", { closed = true })
    278    singl_eltkn_test('SingleQuotedString', "'''x'", { closed = true })
    279    singl_eltkn_test('SingleQuotedString', "'''", { closed = false })
    280    singl_eltkn_test('SingleQuotedString', "'x''", { closed = false })
    281    singl_eltkn_test('SingleQuotedString', "'''x", { closed = false })
    282    singl_eltkn_test('DoubleQuotedString', '"test', { closed = false })
    283    singl_eltkn_test('DoubleQuotedString', '"test"', { closed = true })
    284    singl_eltkn_test('DoubleQuotedString', '"\\""', { closed = true })
    285    singl_eltkn_test('DoubleQuotedString', '"x\\""', { closed = true })
    286    singl_eltkn_test('DoubleQuotedString', '"\\"x"', { closed = true })
    287    singl_eltkn_test('DoubleQuotedString', '"\\"', { closed = false })
    288    singl_eltkn_test('DoubleQuotedString', '"x\\"', { closed = false })
    289    singl_eltkn_test('DoubleQuotedString', '"\\"x', { closed = false })
    290    singl_eltkn_test('Not', '!')
    291    singl_eltkn_test('Assignment', '=', { type = 'Plain' })
    292    comparison_test('==', '!=', 'Equal')
    293    comparison_test('=~', '!~', 'Matches')
    294    comparison_test('>', '<=', 'Greater')
    295    comparison_test('>=', '<', 'GreaterOrEqual')
    296    singl_eltkn_test('Minus', '-')
    297    singl_eltkn_test('Assignment', '-=', { type = 'Subtract' })
    298    singl_eltkn_test('Arrow', '->')
    299    singl_eltkn_test('Invalid', '~', { error = 'E15: Unidentified character: %.*s' })
    300    simple_test({ { data = nil, size = 0 } }, 'EOC', 0, { error = 'start.col >= #pstr' })
    301    simple_test({ '' }, 'EOC', 0, { error = 'start.col >= #pstr' })
    302    simple_test(
    303      { '2.' },
    304      'Number',
    305      1,
    306      { data = { is_float = false, base = 10, val = 2 }, str = '2' }
    307    )
    308    simple_test(
    309      { '2e5' },
    310      'Number',
    311      1,
    312      { data = { is_float = false, base = 10, val = 2 }, str = '2' }
    313    )
    314    simple_test(
    315      { '2.x' },
    316      'Number',
    317      1,
    318      { data = { is_float = false, base = 10, val = 2 }, str = '2' }
    319    )
    320    simple_test(
    321      { '2.2.' },
    322      'Number',
    323      1,
    324      { data = { is_float = false, base = 10, val = 2 }, str = '2' }
    325    )
    326    simple_test(
    327      { '2.0x' },
    328      'Number',
    329      1,
    330      { data = { is_float = false, base = 10, val = 2 }, str = '2' }
    331    )
    332    simple_test(
    333      { '2.0e' },
    334      'Number',
    335      1,
    336      { data = { is_float = false, base = 10, val = 2 }, str = '2' }
    337    )
    338    simple_test(
    339      { '2.0e+' },
    340      'Number',
    341      1,
    342      { data = { is_float = false, base = 10, val = 2 }, str = '2' }
    343    )
    344    simple_test(
    345      { '2.0e-' },
    346      'Number',
    347      1,
    348      { data = { is_float = false, base = 10, val = 2 }, str = '2' }
    349    )
    350    simple_test(
    351      { '2.0e+x' },
    352      'Number',
    353      1,
    354      { data = { is_float = false, base = 10, val = 2 }, str = '2' }
    355    )
    356    simple_test(
    357      { '2.0e-x' },
    358      'Number',
    359      1,
    360      { data = { is_float = false, base = 10, val = 2 }, str = '2' }
    361    )
    362    simple_test(
    363      { '2.0e+1a' },
    364      'Number',
    365      1,
    366      { data = { is_float = false, base = 10, val = 2 }, str = '2' }
    367    )
    368    simple_test(
    369      { '2.0e-1a' },
    370      'Number',
    371      1,
    372      { data = { is_float = false, base = 10, val = 2 }, str = '2' }
    373    )
    374    simple_test(
    375      { '0b102' },
    376      'Number',
    377      4,
    378      { data = { is_float = false, base = 2, val = 2 }, str = '0b10' }
    379    )
    380    simple_test(
    381      { '10F' },
    382      'Number',
    383      2,
    384      { data = { is_float = false, base = 10, val = 10 }, str = '10' }
    385    )
    386    simple_test({ '0x0123456789ABCDEFG' }, 'Number', 18, {
    387      data = { is_float = false, base = 16, val = 81985529216486895 },
    388      str = '0x0123456789ABCDEF',
    389    })
    390    simple_test(
    391      { { data = '00', size = 2 } },
    392      'Number',
    393      2,
    394      { data = { is_float = false, base = 8, val = 0 }, str = '00' }
    395    )
    396    simple_test(
    397      { { data = '009', size = 2 } },
    398      'Number',
    399      2,
    400      { data = { is_float = false, base = 8, val = 0 }, str = '00' }
    401    )
    402    simple_test(
    403      { { data = '01', size = 1 } },
    404      'Number',
    405      1,
    406      { data = { is_float = false, base = 10, val = 0 }, str = '0' }
    407    )
    408  end
    409 
    410  local function regular_scope_tests()
    411    scope_test('s')
    412    scope_test('g')
    413    scope_test('v')
    414    scope_test('b')
    415    scope_test('w')
    416    scope_test('t')
    417    scope_test('l')
    418    scope_test('a')
    419 
    420    simple_test(
    421      { 'g:' },
    422      'PlainIdentifier',
    423      2,
    424      { data = { scope = 'g', autoload = false }, str = 'g:' }
    425    )
    426    simple_test(
    427      { 'g:is#foo' },
    428      'PlainIdentifier',
    429      8,
    430      { data = { scope = 'g', autoload = true }, str = 'g:is#foo' }
    431    )
    432    simple_test(
    433      { 'g:isnot#foo' },
    434      'PlainIdentifier',
    435      11,
    436      { data = { scope = 'g', autoload = true }, str = 'g:isnot#foo' }
    437    )
    438  end
    439 
    440  local function regular_is_tests()
    441    comparison_test('is', 'isnot', 'Identical')
    442 
    443    simple_test(
    444      { 'is' },
    445      'Comparison',
    446      2,
    447      { data = { type = 'Identical', inv = false, ccs = 'UseOption' }, str = 'is' }
    448    )
    449    simple_test(
    450      { 'isnot' },
    451      'Comparison',
    452      5,
    453      { data = { type = 'Identical', inv = true, ccs = 'UseOption' }, str = 'isnot' }
    454    )
    455    simple_test(
    456      { 'is?' },
    457      'Comparison',
    458      3,
    459      { data = { type = 'Identical', inv = false, ccs = 'IgnoreCase' }, str = 'is?' }
    460    )
    461    simple_test(
    462      { 'isnot?' },
    463      'Comparison',
    464      6,
    465      { data = { type = 'Identical', inv = true, ccs = 'IgnoreCase' }, str = 'isnot?' }
    466    )
    467    simple_test(
    468      { 'is#' },
    469      'Comparison',
    470      3,
    471      { data = { type = 'Identical', inv = false, ccs = 'MatchCase' }, str = 'is#' }
    472    )
    473    simple_test(
    474      { 'isnot#' },
    475      'Comparison',
    476      6,
    477      { data = { type = 'Identical', inv = true, ccs = 'MatchCase' }, str = 'isnot#' }
    478    )
    479    simple_test(
    480      { 'is#foo' },
    481      'Comparison',
    482      3,
    483      { data = { type = 'Identical', inv = false, ccs = 'MatchCase' }, str = 'is#' }
    484    )
    485    simple_test(
    486      { 'isnot#foo' },
    487      'Comparison',
    488      6,
    489      { data = { type = 'Identical', inv = true, ccs = 'MatchCase' }, str = 'isnot#' }
    490    )
    491  end
    492 
    493  local function regular_number_tests()
    494    simple_test(
    495      { '2.0' },
    496      'Number',
    497      1,
    498      { data = { is_float = false, base = 10, val = 2 }, str = '2' }
    499    )
    500    simple_test(
    501      { '2.0e5' },
    502      'Number',
    503      1,
    504      { data = { is_float = false, base = 10, val = 2 }, str = '2' }
    505    )
    506    simple_test(
    507      { '2.0e+5' },
    508      'Number',
    509      1,
    510      { data = { is_float = false, base = 10, val = 2 }, str = '2' }
    511    )
    512    simple_test(
    513      { '2.0e-5' },
    514      'Number',
    515      1,
    516      { data = { is_float = false, base = 10, val = 2 }, str = '2' }
    517    )
    518  end
    519 
    520  local function regular_eoc_tests()
    521    singl_eltkn_test('EOC', '|')
    522    singl_eltkn_test('EOC', '\0')
    523    singl_eltkn_test('EOC', '\n')
    524  end
    525 
    526  itp('works (single tokens, zero flags)', function()
    527    stable_tests()
    528 
    529    regular_eoc_tests()
    530    regular_scope_tests()
    531    regular_is_tests()
    532    regular_number_tests()
    533  end)
    534  itp('peeks', function()
    535    flags = tonumber(lib.kELFlagPeek)
    536    should_advance = false
    537    stable_tests()
    538 
    539    regular_eoc_tests()
    540    regular_scope_tests()
    541    regular_is_tests()
    542    regular_number_tests()
    543  end)
    544  itp('forbids scope', function()
    545    flags = tonumber(lib.kELFlagForbidScope)
    546    stable_tests()
    547 
    548    regular_eoc_tests()
    549    regular_is_tests()
    550    regular_number_tests()
    551 
    552    simple_test(
    553      { 'g:' },
    554      'PlainIdentifier',
    555      1,
    556      { data = { scope = 0, autoload = false }, str = 'g' }
    557    )
    558  end)
    559  itp('allows floats', function()
    560    flags = tonumber(lib.kELFlagAllowFloat)
    561    stable_tests()
    562 
    563    regular_eoc_tests()
    564    regular_scope_tests()
    565    regular_is_tests()
    566 
    567    simple_test(
    568      { '2.2' },
    569      'Number',
    570      3,
    571      { data = { is_float = true, base = 10, val = 2.2 }, str = '2.2' }
    572    )
    573    simple_test(
    574      { '2.0e5' },
    575      'Number',
    576      5,
    577      { data = { is_float = true, base = 10, val = 2e5 }, str = '2.0e5' }
    578    )
    579    simple_test(
    580      { '2.0e+5' },
    581      'Number',
    582      6,
    583      { data = { is_float = true, base = 10, val = 2e5 }, str = '2.0e+5' }
    584    )
    585    simple_test(
    586      { '2.0e-5' },
    587      'Number',
    588      6,
    589      { data = { is_float = true, base = 10, val = 2e-5 }, str = '2.0e-5' }
    590    )
    591    simple_test(
    592      { '2.500000e-5' },
    593      'Number',
    594      11,
    595      { data = { is_float = true, base = 10, val = 2.5e-5 }, str = '2.500000e-5' }
    596    )
    597    simple_test(
    598      { '2.5555e2' },
    599      'Number',
    600      8,
    601      { data = { is_float = true, base = 10, val = 2.5555e2 }, str = '2.5555e2' }
    602    )
    603    simple_test(
    604      { '2.5555e+2' },
    605      'Number',
    606      9,
    607      { data = { is_float = true, base = 10, val = 2.5555e2 }, str = '2.5555e+2' }
    608    )
    609    simple_test(
    610      { '2.5555e-2' },
    611      'Number',
    612      9,
    613      { data = { is_float = true, base = 10, val = 2.5555e-2 }, str = '2.5555e-2' }
    614    )
    615    simple_test(
    616      { { data = '2.5e-5', size = 3 } },
    617      'Number',
    618      3,
    619      { data = { is_float = true, base = 10, val = 2.5 }, str = '2.5' }
    620    )
    621    simple_test(
    622      { { data = '2.5e5', size = 4 } },
    623      'Number',
    624      1,
    625      { data = { is_float = false, base = 10, val = 2 }, str = '2' }
    626    )
    627    simple_test(
    628      { { data = '2.5e-50', size = 6 } },
    629      'Number',
    630      6,
    631      { data = { is_float = true, base = 10, val = 2.5e-5 }, str = '2.5e-5' }
    632    )
    633  end)
    634  itp('treats `is` as an identifier', function()
    635    flags = tonumber(lib.kELFlagIsNotCmp)
    636    stable_tests()
    637 
    638    regular_eoc_tests()
    639    regular_scope_tests()
    640    regular_number_tests()
    641 
    642    simple_test(
    643      { 'is' },
    644      'PlainIdentifier',
    645      2,
    646      { data = { scope = 0, autoload = false }, str = 'is' }
    647    )
    648    simple_test(
    649      { 'isnot' },
    650      'PlainIdentifier',
    651      5,
    652      { data = { scope = 0, autoload = false }, str = 'isnot' }
    653    )
    654    simple_test(
    655      { 'is?' },
    656      'PlainIdentifier',
    657      2,
    658      { data = { scope = 0, autoload = false }, str = 'is' }
    659    )
    660    simple_test(
    661      { 'isnot?' },
    662      'PlainIdentifier',
    663      5,
    664      { data = { scope = 0, autoload = false }, str = 'isnot' }
    665    )
    666    simple_test(
    667      { 'is#' },
    668      'PlainIdentifier',
    669      3,
    670      { data = { scope = 0, autoload = true }, str = 'is#' }
    671    )
    672    simple_test(
    673      { 'isnot#' },
    674      'PlainIdentifier',
    675      6,
    676      { data = { scope = 0, autoload = true }, str = 'isnot#' }
    677    )
    678    simple_test(
    679      { 'is#foo' },
    680      'PlainIdentifier',
    681      6,
    682      { data = { scope = 0, autoload = true }, str = 'is#foo' }
    683    )
    684    simple_test(
    685      { 'isnot#foo' },
    686      'PlainIdentifier',
    687      9,
    688      { data = { scope = 0, autoload = true }, str = 'isnot#foo' }
    689    )
    690  end)
    691  itp('forbids EOC', function()
    692    flags = tonumber(lib.kELFlagForbidEOC)
    693    stable_tests()
    694 
    695    regular_scope_tests()
    696    regular_is_tests()
    697    regular_number_tests()
    698 
    699    singl_eltkn_test('Invalid', '|', { error = 'E15: Unexpected EOC character: %.*s' })
    700    singl_eltkn_test('Invalid', '\0', { error = 'E15: Unexpected EOC character: %.*s' })
    701    singl_eltkn_test('Invalid', '\n', { error = 'E15: Unexpected EOC character: %.*s' })
    702  end)
    703 end)