vis
a vi-like editor based on Plan 9's structural regular expressions
git clone https://9o.is/git/vis.git
snobol4.lua
(2589B)
1 -- Copyright 2013-2025 Michael T. Richter. See LICENSE.
2 -- SNOBOL4 lexer.
3 -- This lexer works with classic SNOBOL4 as well as the CSNOBOL4 extensions.
4
5 local lexer = require('lexer')
6 local token, word_match = lexer.token, lexer.word_match
7 local B, P, S = lpeg.B, lpeg.P, lpeg.S
8
9 local lex = lexer.new('snobol4')
10
11 -- Whitespace.
12 lex:add_rule('whitespace', token(lexer.WHITESPACE, lexer.space^1))
13
14 -- Keywords.
15 lex:add_rule('keyword', token(lexer.KEYWORD, word_match({
16 'ABORT', 'ARRAY', 'CONTINUE', 'DEFINE', 'END', 'FRETURN', 'INPUT', 'NRETURN', 'OUTPUT', 'PUNCH',
17 'RETURN', 'SCONTINUE', 'TABLE'
18 }, true) + '&' * lexer.word))
19
20 -- Helper patterns.
21 local dotted_id = lexer.word * ('.' * lexer.word)^0
22
23 -- Labels.
24 lex:add_rule('label', token(lexer.LABEL, lexer.starts_line(dotted_id)))
25
26 -- Targets.
27 local branch = B(lexer.space * ':(') * dotted_id * #P(')')
28 local sbranch = B(lexer.space * ':' * S('SsFf') * '(') * dotted_id * #P(')')
29 local sbranchx = B(')' * S('SsFf') * '(') * dotted_id * #P(')')
30 lex:add_rule('target', token(lexer.LABEL, branch + sbranch + sbranchx))
31
32 -- Patterns.
33 lex:add_rule('pattern', lexer.token(lexer.CLASS, word_match({
34 -- Keep distinct.
35 'ABORT', 'ANY', 'ARB', 'ARBNO', 'BAL', 'BREAK', 'BREAKX', 'FAIL', 'FENCE', 'LEN', 'NOTANY', 'POS',
36 'REM', 'RPOS', 'RTAB', 'SPAN', 'SUCCEED', 'TAB'
37 }, true) * #P('(')))
38
39 -- Token definitions.
40 lex:add_rule('built-in', token(lexer.FUNCTION, word_match({
41 'APPLY', 'ARRAY', 'CHAR', 'CONVERT', 'COPY', 'DATA', 'DATE', 'DIFFER', 'DUPL', 'EQ', 'EVAL',
42 'FILE_ABSPATH', 'FILE_ISDIR', 'FREEZE', 'FUNCTION', 'GE', 'GT', 'HOST', 'IDENT', 'INTEGER',
43 'IO_FINDUNIT', 'ITEM', 'LABEL', 'LOAD', 'LPAD', 'LE', 'LGT', 'LT', 'NE', 'OPSYN', 'ORD',
44 'PROTOTYPE', 'REMDR', 'REPLACE', 'REVERSE', 'RPAD', 'RSORT', 'SERV_LISTEN', 'SET', 'SETEXIT',
45 'SIZE', 'SORT', 'SQRT', 'SSET', 'SUBSTR', 'TABLE', 'THAW', 'TIME', 'TRACE', 'TRIM', 'UNLOAD',
46 'VALUE', 'VDIFFER'
47 }, true) * #P('(')))
48
49 -- Identifiers.
50 lex:add_rule('identifier', token(lexer.DEFAULT, dotted_id))
51
52 -- Strings.
53 local dq_str = lexer.range('"', true, false)
54 local sq_str = lexer.range("'", true, false)
55 lex:add_rule('string', token(lexer.STRING, sq_str + dq_str))
56
57 -- Comments.
58 lex:add_rule('comment', token(lexer.COMMENT, lexer.starts_line(lexer.to_eol(S('*#|;!')))))
59
60 -- Numbers.
61 lex:add_rule('number', token(lexer.NUMBER, lexer.number))
62
63 -- Control.
64 lex:add_rule('control', token(lexer.PREPROCESSOR, lexer.starts_line('-' * lexer.word)))
65
66 -- Operators.
67 lex:add_rule('operator', token(lexer.OPERATOR, S'¬?$.!%*/#+-@⊥&^~\\='))
68
69 lexer.property['scintillua.comment'] = '#'
70
71 return lex