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