vis
a vi-like editor based on Plan 9's structural regular expressions
git clone https://9o.is/git/vis.git
factor.lua
(2869B)
1 -- Copyright 2013 Michael T. Richter. See LICENSE.
2 -- Copyright 2024 John Benediktsson <mrjbq7@gmail.com>
3 -- Factor lexer (http://factorcode.org).
4
5 -- At this time the lexer is usable, but not perfect. Problems include:
6 -- * identifiers like (foo) get treated and coloured like stack declarations
7 -- * other as-yet unknown display bugs :-)
8
9 local lexer = lexer
10 local P, R, S = lpeg.P, lpeg.R, lpeg.S
11
12 local lex = lexer.new(...)
13
14 -- General building blocks.
15 local pre = lexer.upper^1
16 local post = pre
17 local opt_pre = pre^-1
18 local opt_post = opt_pre
19
20 -- Comments.
21 lex:add_rule('comment', lex:tag(lexer.COMMENT, P('#')^-1 * lexer.to_eol('!')))
22
23 -- Strings.
24 local dq1_str = opt_pre * lexer.range('"', true)
25 lex:add_rule('string', lex:tag(lexer.STRING, dq1_str))
26
27 -- Numbers.
28 -- Note that complex literals like C{ 1/3 27.3 } are not covered by this lexer.
29 -- The C{ ... } notation is treated as an operator--to be specific a
30 -- "constructor" (for want of a better term).
31 -- Also note that we cannot use lexer.number because numbers do not support the '+' prefix or
32 -- case-insensitive base-changing prefixes.
33 local hex_digits = lexer.xdigit^1
34 local binary = P('-')^-1 * '0b' * S('01')^1
35 local octal = P('-')^-1 * '0o' * R('07')^1
36 local decimal = P('-')^-1 * lexer.digit^1
37 local hexadecimal = P('-')^-1 * '0x' * hex_digits
38 local integer = binary + octal + hexadecimal + decimal
39 local ratio = decimal * '/' * decimal
40 local dfloat_component = decimal * '.' * decimal^-1
41 local hfloat_component = hexadecimal * ('.' * hex_digits^-1)^-1
42 local float = (dfloat_component * (S('eE') * decimal)^-1) + (hfloat_component * S('pP') * decimal) +
43 (ratio * '.') + (P('-')^-1 * '1/0.') + ('0/0')
44 lex:add_rule('number', lex:tag(lexer.NUMBER, (float + ratio + integer)))
45
46 -- Keywords.
47 -- Things like NAN:, USE:, USING:, POSTPONE:, etc. are considered keywords, as are similar
48 -- words that end in #.
49 -- Patterns like <<WORD ... WORD>> are similarly considered to be "keywords" (for want of a
50 -- better term).
51 local colon_words = pre * S(':#') + S(':;')^1
52 local angle_words = (P('<')^1 * post) + (pre * P('>')^1)
53 lex:add_rule('keyword', lex:tag(lexer.KEYWORD, (colon_words + angle_words)))
54
55 -- Operators.
56 -- The usual suspects like braces, brackets, angle brackets, parens, etc. are considered to be
57 -- operators. They may, however, have prefixes like C{ ... }.
58 local constructor_words = opt_pre * S('{[<') + S('}]>') + pre * '(' + ')'
59 local stack_declaration = lexer.range('(', ')')
60 local other_operators = S('+-*/<>')
61 lex:add_rule('operator',
62 lex:tag(lexer.OPERATOR, (stack_declaration + constructor_words + other_operators)))
63
64 -- Identifiers.
65 -- Identifiers can be practically anything but whitespace.
66 local symbols = S('`~!@#$%^&*()_-+={[<>]}:;X,?/')
67 lex:add_rule('identifier', lex:tag(lexer.IDENTIFIER, (lexer.alnum + symbols)^1))
68
69 lexer.property['factor.comment'] = '!'
70
71 return lex