vis

a vi-like editor based on Plan 9's structural regular expressions

git clone https://9o.is/git/vis.git

gleam.lua

(4265B)


      1 -- Copyright 2021-2025 Mitchell. See LICENSE.
      2 -- Gleam LPeg lexer
      3 -- https://gleam.run/
      4 -- Contributed by Tynan Beatty
      5 
      6 local lexer = require('lexer')
      7 local token, word_match = lexer.token, lexer.word_match
      8 local P, S = lpeg.P, lpeg.S
      9 
     10 local KEY, OP = lexer.KEYWORD, lexer.OPERATOR
     11 
     12 local lex = lexer.new('gleam')
     13 
     14 -- Whitespace.
     15 local gleam_ws = token(lexer.WHITESPACE, lexer.space^1)
     16 lex:add_rule('whitespace', gleam_ws)
     17 
     18 -- Types.
     19 local typ_tok = token(lexer.TYPE, lexer.upper * lexer.alnum^0)
     20 lex:add_rule('type', typ_tok)
     21 
     22 -- Modules.
     23 local name = (lexer.lower + '_') * (lexer.lower + lexer.digit + '_')^0
     24 local fn_name = token(lexer.FUNCTION, name)
     25 local mod_name = token('module', name)
     26 local typ_or_fn = typ_tok + fn_name
     27 local function mod_tok(ws)
     28 	return token(KEY, 'import') * ws^1 * mod_name * (ws^0 * token(OP, '/') * ws^0 * mod_name)^0 *
     29 		(ws^1 * token(KEY, 'as') * ws^1 * mod_name)^-1 *
     30 		(ws^0 * token(OP, '.') * ws^0 * token(OP, '{') * ws^0 * typ_or_fn *
     31 			(ws^0 * token(OP, ',') * ws^0 * typ_or_fn)^0 * ws^0 * token(OP, '}'))^-1
     32 end
     33 lex:add_rule('module', mod_tok(gleam_ws))
     34 lex:add_style('module', lexer.styles.constant)
     35 
     36 -- Keywords.
     37 local key_tok = token(KEY, word_match(
     38 	'as assert case const external fn if import let opaque pub todo try tuple type'))
     39 lex:add_rule('keyword', key_tok)
     40 
     41 -- Functions.
     42 local function fn_tok(ws)
     43 	local mod_name_op = mod_name * ws^0 * token(OP, '.')
     44 	local fn_def_call = mod_name_op^-1 * ws^0 * fn_name * ws^0 * #P('(')
     45 	local fn_pipe = token(OP, '|>') * ws^0 * (token(KEY, 'fn') + mod_name_op^-1 * fn_name)
     46 	return fn_def_call + fn_pipe
     47 end
     48 lex:add_rule('function', fn_tok(gleam_ws))
     49 
     50 -- Labels.
     51 local id = token(lexer.IDENTIFIER, name)
     52 local function lab_tok(ws)
     53 	return token(OP, S('(,')) * ws^0 * token(lexer.LABEL, name) * #(ws^1 * id)
     54 end
     55 lex:add_rule('label', lab_tok(gleam_ws))
     56 
     57 -- Identifiers.
     58 local discard_id = token('discard', '_' * name)
     59 local id_tok = discard_id + id
     60 lex:add_rule('identifier', id_tok)
     61 lex:add_style('discard', lexer.styles.comment)
     62 
     63 -- Strings.
     64 local str_tok = token(lexer.STRING, lexer.range('"'))
     65 lex:add_rule('string', str_tok)
     66 
     67 -- Comments.
     68 local com_tok = token(lexer.COMMENT, lexer.to_eol('//'))
     69 lex:add_rule('comment', com_tok)
     70 
     71 -- Numbers.
     72 local function can_neg(patt) return (lpeg.B(lexer.space + S('+-/*%<>=&|:,.')) * '-')^-1 * patt end
     73 local function can_sep(patt) return (P('_')^-1 * patt^1)^1 end
     74 local dec = lexer.digit * can_sep(lexer.digit)^0
     75 local float = dec * '.' * dec^0
     76 local bin = '0' * S('bB') * can_sep(S('01')) * -lexer.xdigit
     77 local oct = '0' * S('oO') * can_sep(lpeg.R('07'))
     78 local hex = '0' * S('xX') * can_sep(lexer.xdigit)
     79 local num_tok = token(lexer.NUMBER, can_neg(float) + bin + oct + hex + can_neg(dec))
     80 lex:add_rule('number', num_tok)
     81 
     82 -- Operators.
     83 local op_tok = token(OP, S('+-*/%#!=<>&|.,:;{}[]()'))
     84 lex:add_rule('operator', op_tok)
     85 
     86 -- Errors.
     87 local err_tok = token(lexer.ERROR, lexer.any)
     88 lex:add_rule('error', err_tok)
     89 
     90 -- Fold points.
     91 lex:add_fold_point(lexer.OPERATOR, '{', '}')
     92 lex:add_fold_point(lexer.OPERATOR, '[', ']')
     93 lex:add_fold_point(lexer.OPERATOR, '(', ')')
     94 
     95 -- Embedded Bit Strings.
     96 -- Mimic lexer.load() by creating a bitstring-specific whitespace style.
     97 local bitstring = lexer.new(lex._name .. '_bitstring')
     98 local bitstring_ws = token(bitstring._name .. '_whitespace', lexer.space^1)
     99 bitstring:add_rule('whitespace', bitstring_ws)
    100 bitstring:add_style(bitstring._name .. '_whitespace', lexer.styles.whitespace)
    101 bitstring:add_rule('type', typ_tok)
    102 bitstring:add_rule('module', mod_tok(bitstring_ws))
    103 bitstring:add_rule('keyword', key_tok + token(KEY, word_match{
    104 	'binary', 'bytes', 'int', 'float', 'bit_string', 'bits', 'utf8', 'utf16', 'utf32',
    105 	'utf8_codepoint', 'utf16_codepoint', 'utf32_codepoint', 'signed', 'unsigned', 'big', 'little',
    106 	'native', 'unit', 'size'
    107 }))
    108 bitstring:add_rule('function', fn_tok(bitstring_ws))
    109 bitstring:add_rule('label', lab_tok(bitstring_ws))
    110 bitstring:add_rule('identifier', id_tok)
    111 bitstring:add_rule('string', str_tok)
    112 bitstring:add_rule('comment', com_tok)
    113 bitstring:add_rule('number', num_tok)
    114 bitstring:add_rule('operator', op_tok)
    115 bitstring:add_rule('error', err_tok)
    116 lex:embed(bitstring, token(OP, '<<'), token(OP, '>>'))
    117 
    118 lexer.property['scintillua.comment'] = '//'
    119 
    120 return lex