vis
a vi-like editor based on Plan 9's structural regular expressions
git clone https://9o.is/git/vis.git
makefile.lua
(5080B)
1 -- Copyright 2006-2025 Mitchell. See LICENSE.
2 -- Makefile LPeg lexer.
3
4 local lexer = lexer
5 local P, S, B = lpeg.P, lpeg.S, lpeg.B
6
7 local lex = lexer.new(..., {lex_by_line = true})
8
9 -- Function definition.
10 local word = (lexer.any - lexer.space - S('$:,#=(){}'))^1
11 local func_name = lex:tag(lexer.FUNCTION, word)
12 local ws = lex:get_rule('whitespace')
13 local eq = lex:tag(lexer.OPERATOR, '=')
14 lex:add_rule('function_def', lex:tag(lexer.KEYWORD, lexer.word_match('define')) * ws * func_name *
15 ws^-1 * (eq + -1))
16
17 -- Keywords.
18 lex:add_rule('keyword', lex:tag(lexer.KEYWORD, P('!')^-1 * lex:word_match(lexer.KEYWORD, true)))
19
20 -- Targets.
21 local special_target = lex:tag(lexer.CONSTANT_BUILTIN, '.' * lex:word_match('special_targets'))
22 -- local normal_target = lex:tag('target', (lexer.any - lexer.space - S(':+?!=#'))^1)
23 local target = special_target -- + normal_target * (ws * normal_target)^0
24 lex:add_rule('target', lexer.starts_line(target * ws^-1 * #(':' * lexer.space)))
25
26 -- Variable and function assignments.
27 local func_assign = func_name * ws^-1 * eq *
28 #P(function(input, index) return input:find('%$%(%d%)', index) end)
29 local builtin_var = lex:tag(lexer.VARIABLE_BUILTIN, lex:word_match(lexer.VARIABLE_BUILTIN))
30 local var_name = lex:tag(lexer.VARIABLE, word)
31 local var_assign = (builtin_var + var_name) * ws^-1 *
32 lex:tag(lexer.OPERATOR, S(':+?!')^-1 * '=' + '::=')
33 lex:add_rule('assign', lexer.starts_line(func_assign + var_assign, true) + B(': ') * var_assign)
34
35 -- Operators.
36 lex:add_rule('operator', lex:tag(lexer.OPERATOR, S(':(){}|')))
37
38 -- Strings.
39 lex:add_rule('string', lexer.range("'", true) + lexer.range('"', true))
40
41 -- Identifiers.
42 lex:add_rule('identifier', lex:tag(lexer.IDENTIFIER, word))
43
44 -- Functions.
45 local builtin_func = lex:tag(lexer.FUNCTION_BUILTIN, lex:word_match(lexer.FUNCTION_BUILTIN))
46 local call_func = lex:tag(lexer.FUNCTION_BUILTIN, 'call') * ws * func_name
47 local func = lex:tag(lexer.OPERATOR, '$' * S('({')) * (call_func + builtin_func)
48 lex:add_rule('function', func)
49
50 -- Variables.
51 local auto_var = lex:tag(lexer.OPERATOR, '$') * lex:tag(lexer.VARIABLE_BUILTIN, S('@%<?^+|*')) +
52 lex:tag(lexer.OPERATOR, '$(') * lex:tag(lexer.VARIABLE_BUILTIN, S('@%<?^+*') * S('DF'))
53 local var_ref = lex:tag(lexer.OPERATOR, P('$(') + '${') * (builtin_var + var_name)
54 local var = auto_var + var_ref
55 lex:add_rule('variable', var)
56
57 -- Comments.
58 lex:add_rule('comment', lex:tag(lexer.COMMENT, lexer.to_eol('#')))
59
60 -- Embedded Bash in target rules.
61 local bash = lexer.load('bash')
62 bash:modify_rule('variable',
63 lex:tag(lexer.VARIABLE, '$$' * word) + func + var + bash:get_rule('variable'))
64 local bash_start_rule = lex:tag(lexer.WHITESPACE, '\t') + lex:tag(lexer.OPERATOR, ';')
65 local bash_end_rule = lex:tag(lexer.WHITESPACE, '\n')
66 lex:embed(bash, bash_start_rule, bash_end_rule)
67 -- Embedded Bash in $(shell ...) calls.
68 local shell = lexer.load('bash', 'bash.shell')
69 bash_start_rule = #P('$(shell') * func
70 bash_end_rule = -B('\\') * lex:tag(lexer.OPERATOR, ')')
71 lex:embed(shell, bash_start_rule, bash_end_rule)
72
73 -- Word lists.
74 lex:set_word_list(lexer.KEYWORD, {
75 'define', 'endef', -- multi-line
76 'else', 'endif', 'ifdef', 'ifeq', 'ifndef', 'ifneq', -- conditionals
77 'export', 'include', 'load', 'override', 'undefine', 'unexport', 'vpath', -- directives
78 'private', --
79 'if', 'elseif', 'elseifdef', 'elseifndef' -- non-Make conditionals
80 })
81
82 lex:set_word_list('special_targets', {
83 'DEFAULT', 'DELETE_ON_ERROR', 'EXPORT_ALL_VARIABLES', 'IGNORE', 'INTERMEDIATE',
84 'LOW_RESOLUTION_TIME', 'NOTPARALLEL', 'ONESHELL', 'PHONY', 'POSIX', 'PRECIOUS', 'SECONDARY',
85 'SECONDEXPANSION', 'SILENT', 'SUFFIXES'
86 })
87
88 lex:set_word_list(lexer.VARIABLE_BUILTIN, {
89 -- Special.
90 '.DEFAULT_GOAL', '.FEATURES', '.INCLUDE_DIRS', '.LIBPATTERNS', '.LOADED', '.RECIPEPREFIX',
91 '.SHELLFLAGS', '.SHELLSTATUS', '.VARIABLES', --
92 'COMSPEC', 'MAKESHELL', 'SHELL', -- choosing the shell
93 'GPATH', 'VPATH', -- search
94 -- Make.
95 'MAKE', 'MAKECMDGOALS', 'MAKEFILES', 'MAKEFILE_LIST', 'MAKEFLAGS', 'MAKELEVEL', 'MAKEOVERRIDES',
96 'MAKE_RESTARTS', 'MAKE_TERMERR', 'MAKE_TERMOUT', 'MFLAGS',
97 -- Other.
98 'CURDIR', 'OUTPUT_OPTION', 'SUFFIXES',
99 -- Implicit.
100 'AR', 'ARFLAGS', 'AS', 'ASFLAGS', 'CC', 'CFLAGS', 'CO', 'COFLAGS', 'CPP', 'CPPFLAGS', 'CTANGLE',
101 'CWEAVE', 'CXX', 'CXXFLAGS', 'FC', 'FFLAGS', 'GET', 'GFLAGS', 'LDFLAGS', 'LDLIBS', 'LEX',
102 'LFLAGS', 'LINT', 'LINTFLAGS', 'M2C', 'MAKEINFO', 'PC', 'PFLAGS', 'RFLAGS', 'RM', 'TANGLE', 'TEX',
103 'TEXI2DVI', 'WEAVE', 'YACC', 'YFLAGS', --
104 'bindir', 'DESTDIR', 'exec_prefix', 'libexecdir', 'prefix', 'sbindir' -- directory
105 })
106
107 lex:set_word_list(lexer.FUNCTION_BUILTIN, {
108 -- Filename.
109 'abspath', 'addprefix', 'addsuffix', 'basename', 'dir', 'join', 'notdir', 'realpath', 'suffix',
110 'wildcard', --
111 'and', 'if', 'or', -- conditional
112 'error', 'info', 'warning', -- control
113 'filter', 'filter-out', 'findstring', 'firstword', 'lastword', 'patsubst', 'sort', 'strip',
114 -- Text.
115 'subst', 'word', 'wordlist', 'words', --
116 'call', 'eval', 'file', 'flavor', 'foreach', 'origin', 'shell', 'value' -- other
117 })
118
119 lexer.property['scintillua.comment'] = '#'
120
121 return lex