Skip to content

Commit 0695fa4

Browse files
author
klokocka
committed
crontab update
1 parent 375c797 commit 0695fa4

File tree

1 file changed

+60
-118
lines changed

1 file changed

+60
-118
lines changed

lexers/crontab.lua

Lines changed: 60 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -1,139 +1,81 @@
1-
-- Copyright 2025 Stepan Klokocka. See LICENSE.
1+
-- Copyright 2005 Stepan Klokocka. See LICENSE.
22
-- Crontab LPeg lexer.
33

44
local lexer = require('lexer')
5-
local token, word_match = lexer.token, lexer.word_match
6-
local P, R, S, V = lpeg.P, lpeg.R, lpeg.S, lpeg.V
5+
local P, S, R = lpeg.P, lpeg.S, lpeg.R
76

8-
local lex = lexer.new('crontab')
7+
local lex = lexer.new(...)
98

10-
-- Whitespace.
11-
lex:add_rule('whitespace', token(lexer.WHITESPACE, lexer.space^1))
9+
-- Define basic patterns
10+
local space = S(' \t')
11+
local newline = P('\r\n') + P('\n') + P('\r')
12+
local digit = R('09')
13+
local alpha = R('az', 'AZ')
14+
local alnum = alpha + digit
1215

13-
-- Comments.
14-
lex:add_rule('comment', token(lexer.COMMENT, '#' * lexer.nonnewline^0))
16+
-- Whitespace
17+
lex:add_rule('whitespace', lexer.token(lexer.WHITESPACE, space^1))
1518

16-
-- Numbers.
17-
local digit = R('09')
18-
local number = digit^1
19-
lex:add_rule('number', token(lexer.NUMBER, number))
19+
-- Comments
20+
lex:add_rule('comment', lexer.token(lexer.COMMENT, P('#') * (P(1) - newline)^0))
2021

21-
-- Cron operators.
22-
local operators = S('*,-/?')
23-
lex:add_rule('operator', token(lexer.OPERATOR, operators))
22+
-- Numbers (field-specific)
23+
lex:add_rule('minute', lexer.token('minute', digit^1))
24+
lex:add_rule('hour', lexer.token('hour', digit^1))
25+
lex:add_rule('dom', lexer.token('dom', digit^1)) -- day of month
26+
lex:add_rule('month_num', lexer.token('month_num', digit^1))
27+
lex:add_rule('dow_num', lexer.token('dow_num', digit^1)) -- day of week
28+
lex:add_rule('number', lexer.token(lexer.NUMBER, digit^1))
2429

25-
-- Special time strings.
26-
local special_times = word_match{
30+
-- Special time strings
31+
local special_times = lexer.word_match{
2732
'@yearly', '@annually', '@monthly', '@weekly', '@daily', '@midnight', '@hourly', '@reboot'
2833
}
29-
lex:add_rule('special_time', token('special_time', special_times))
34+
lex:add_rule('special_time', lexer.token(lexer.KEYWORD, special_times))
3035

31-
-- Month names (can be used in month field).
32-
local month_names = word_match{
36+
-- Month names
37+
local month_names = lexer.word_match{
3338
'jan', 'feb', 'mar', 'apr', 'may', 'jun',
34-
'jul', 'aug', 'sep', 'oct', 'nov', 'dec',
35-
'january', 'february', 'march', 'april', 'may', 'june',
36-
'july', 'august', 'september', 'october', 'november', 'december'
39+
'jul', 'aug', 'sep', 'oct', 'nov', 'dec'
3740
}
41+
lex:add_rule('month_name', lexer.token(lexer.KEYWORD, month_names))
3842

39-
-- Day names (can be used in day-of-week field).
40-
local day_names = word_match{
41-
'sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat',
42-
'sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'
43+
-- Day names
44+
local day_names = lexer.word_match{
45+
'sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'
4346
}
47+
lex:add_rule('day_name', lexer.token(lexer.KEYWORD, day_names))
4448

45-
-- Month and day names.
46-
lex:add_rule('month_name', token('month_name', month_names))
47-
lex:add_rule('day_name', token('day_name', day_names))
48-
49-
-- Environment variables (VAR=value).
50-
local env_var = lexer.alpha * (lexer.alnum + '_')^0 * '=' * lexer.nonnewline^0
51-
lex:add_rule('environment', token('environment', env_var))
52-
53-
-- Strings (quoted).
54-
local sq_str = lexer.range("'", true)
55-
local dq_str = lexer.range('"', true)
56-
lex:add_rule('string', token(lexer.STRING, sq_str + dq_str))
57-
58-
-- Commands (everything after the 5 time fields).
59-
-- This is a complex rule that matches after whitespace-separated fields.
60-
local field = (number + operators + month_names + day_names + lexer.alpha^1)
61-
local time_spec = special_times + (field * (lexer.space^1 * field)^4)
62-
local command_start = time_spec * lexer.space^1
63-
local command = lexer.nonnewline^1
64-
65-
-- Add a rule for the entire cron line structure.
66-
local cron_line = P{
67-
'line',
68-
line = V('special_line') + V('env_line') + V('cron_entry_line') + V('comment_line'),
69-
70-
special_line = token('special_time', special_times) *
71-
lexer.space^1 *
72-
token('command', lexer.nonnewline^0),
73-
74-
env_line = token('environment', lexer.alpha * (lexer.alnum + '_')^0) *
75-
token(lexer.OPERATOR, '=') *
76-
token('env_value', lexer.nonnewline^0),
77-
78-
comment_line = token(lexer.COMMENT, '#' * lexer.nonnewline^0),
79-
80-
cron_entry_line = V('minute_field') * lexer.space^1 *
81-
V('hour_field') * lexer.space^1 *
82-
V('dom_field') * lexer.space^1 *
83-
V('month_field') * lexer.space^1 *
84-
V('dow_field') * lexer.space^1 *
85-
token('command', lexer.nonnewline^0),
86-
87-
minute_field = token('minute', V('field_content')),
88-
hour_field = token('hour', V('field_content')),
89-
dom_field = token('dom', V('field_content')),
90-
month_field = token('month', V('field_content') + month_names),
91-
dow_field = token('dow', V('field_content') + day_names),
92-
93-
field_content = (number + operators + '?')^1
94-
}
49+
-- Cron operators
50+
lex:add_rule('operator', lexer.token(lexer.OPERATOR, S('*,-/?')))
9551

96-
-- Simplified approach - use individual rules and rely on order.
97-
-- Identifiers (for unrecognized words that might be part of commands).
98-
lex:add_rule('identifier', token(lexer.IDENTIFIER, lexer.alpha * (lexer.alnum + '_')^0))
99-
100-
-- Everything else (commands, paths, etc.).
101-
lex:add_rule('default', token(lexer.DEFAULT, lexer.any))
102-
103-
-- Define token styles.
104-
local styles = {
105-
special_time = lexer.STYLE_KEYWORD,
106-
environment = lexer.STYLE_VARIABLE,
107-
env_value = lexer.STYLE_STRING,
108-
month_name = lexer.STYLE_KEYWORD .. ',bold',
109-
day_name = lexer.STYLE_KEYWORD .. ',bold',
110-
command = lexer.STYLE_FUNCTION,
111-
minute = lexer.STYLE_NUMBER .. ',fore:#FF6B35', -- Orange for minute
112-
hour = lexer.STYLE_NUMBER .. ',fore:#4ECDC4', -- Teal for hour
113-
dom = lexer.STYLE_NUMBER .. ',fore:#45B7D1', -- Blue for day of month
114-
month = lexer.STYLE_NUMBER .. ',fore:#96CEB4', -- Green for month
115-
dow = lexer.STYLE_NUMBER .. ',fore:#FFEAA7' -- Yellow for day of week
116-
}
52+
-- Environment variables
53+
lex:add_rule('environment', lexer.token('environment', alpha * (alnum + P('_'))^0 * P('=') * (P(1) - newline)^0))
54+
55+
-- Commands (everything after time fields on a line)
56+
lex:add_rule('command', lexer.token('command', alpha * (P(1) - space - newline)^0))
57+
58+
-- Strings
59+
local sq_str = P("'") * (P(1) - P("'"))^0 * P("'")
60+
local dq_str = P('"') * (P(1) - P('"'))^0 * P('"')
61+
lex:add_rule('string', lexer.token(lexer.STRING, sq_str + dq_str))
62+
63+
-- Identifiers
64+
lex:add_rule('identifier', lexer.token(lexer.IDENTIFIER, alpha * (alnum + S('_.-'))^0))
65+
66+
-- Default
67+
lex:add_rule('default', lexer.token(lexer.DEFAULT, P(1)))
68+
69+
-- Add custom styles for cron fields
70+
lex:add_style('minute', lexer.styles.number)
71+
lex:add_style('hour', lexer.styles.number)
72+
lex:add_style('dom', lexer.styles.number)
73+
lex:add_style('month_num', lexer.styles.number)
74+
lex:add_style('dow_num', lexer.styles.number)
75+
lex:add_style('environment', lexer.styles.variable)
76+
lex:add_style('command', lexer.styles['function'])
11777

118-
-- Apply styles.
119-
for token_name, style in pairs(styles) do
120-
lex:add_style(token_name, style)
121-
end
122-
123-
-- Folding (minimal - just for readability).
124-
-- Fold on blank lines to separate cron entries.
125-
lex:add_fold_point(lexer.WHITESPACE, lexer.newline^2, '')
126-
127-
-- Word lists for autocompletion (commented out as they cause errors in some Scintillua versions).
128-
-- If your Scintillua version supports word lists, uncomment these:
129-
-- lex:set_word_list(lexer.KEYWORD, {
130-
-- '@yearly', '@annually', '@monthly', '@weekly', '@daily', '@midnight', '@hourly', '@reboot'
131-
-- })
132-
-- lex:set_word_list('month', {
133-
-- 'jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'
134-
-- })
135-
-- lex:set_word_list('day', {
136-
-- 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'
137-
-- })
78+
-- Add folding points (fold comment blocks)
79+
lex:add_fold_point(lexer.COMMENT, '#', newline)
13880

13981
return lex

0 commit comments

Comments
 (0)