Jianw
2025-05-13 b21510ffde25ef027d63862212495ac43b35b10f
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
-- Copyright 2006-2018 Mitchell mitchell.att.foicica.com. See License.txt.
-- Makefile LPeg lexer.
 
local lexer = require('lexer')
local token, word_match = lexer.token, lexer.word_match
local P, R, S = lpeg.P, lpeg.R, lpeg.S
 
local lex = lexer.new('makefile', {lex_by_line = true})
 
-- Whitespace.
local ws = token(lexer.WHITESPACE, lexer.space^1)
lex:add_rule('whitespace', ws)
 
-- Keywords.
lex:add_rule('keyword', token(lexer.KEYWORD, P('!')^-1 * word_match([[
  -- GNU Make conditionals.
  ifeq ifneq ifdef ifndef else endif
  -- Other conditionals.
  if elseif elseifdef elseifndef
  -- Directives and other keywords.
  define endef export include override private undefine unexport vpath
]], true)))
 
-- Targets.
local special_target = token(lexer.CONSTANT, word_match[[
  .PHONY .SUFFIXES .DEFAULT .PRECIOUS .INTERMEDIATE .SECONDARY .SECONDEXPANSION
  .DELETE_ON_ERROR .IGNORE .LOW_RESOLUTION_TIME .SILENT .EXPORT_ALL_VARIABLES
  .NOTPARALLEL .ONESHELL .POSIX
]])
local normal_target = token('target', (lexer.any - lexer.space - S(':#='))^1)
lex:add_rule('target', lexer.starts_line((special_target + normal_target) *
                                         ws^0 * #(':' * -P('='))))
lex:add_style('target', lexer.STYLE_LABEL)
 
-- Variables.
local word_char = lexer.any - lexer.space - S(':#=(){}')
local assign = S(':+?')^-1 * '='
local expanded_var = '$' * ('(' * word_char^1 * ')' + '{' * word_char^1 * '}')
local auto_var = '$' * S('@%<?^+|*')
local special_var = word_match[[
  MAKEFILE_LIST .DEFAULT_GOAL MAKE_RESTARTS .RECIPEPREFIX .VARIABLES .FEATURES
  .INCLUDE_DIRS GPATH MAKECMDGOALS MAKESHELL SHELL VPATH
]] * #(ws^0 * assign)
local implicit_var = word_match[[
  -- Some common variables.
  AR AS CC CXX CPP FC M2C PC CO GET LEX YACC LINT MAKEINFO TEX TEXI2DVI WEAVE
  CWEAVE TANGLE CTANGLE RM
  -- Some common flag variables.
  ARFLAGS ASFLAGS CFLAGS CXXFLAGS COFLAGS CPPFLAGS FFLAGS GFLAGS LDFLAGS LFLAGS
  YFLAGS PFLAGS RFLAGS LINTFLAGS
  -- Other.
  DESTDIR MAKE MAKEFLAGS MAKEOVERRIDES MFLAGS
]] * #(ws^0 * assign)
local computed_var = token(lexer.OPERATOR, '$' * S('({')) *
                     token(lexer.FUNCTION, word_match[[
  -- Functions for String Substitution and Analysis.
  subst patsubst strip findstring filter filter-out sort word wordlist words
  firstword lastword
  -- Functions for File Names.
  dir notdir suffix basename addsuffix addprefix join wildcard realpath abspath
  -- Functions for Conditionals.
  if or and
  -- Miscellaneous Functions.
  foreach call value eval origin flavor shell
  -- Functions That Control Make.
  error warning info
]])
local variable = token(lexer.VARIABLE, expanded_var + auto_var + special_var +
                                       implicit_var) + computed_var
lex:add_rule('variable', variable)
 
-- Operators.
lex:add_rule('operator', token(lexer.OPERATOR, assign + S(':$(){}')))
 
-- Identifiers.
lex:add_rule('identifier', token(lexer.IDENTIFIER, word_char^1))
 
-- Comments.
lex:add_rule('comment', token(lexer.COMMENT, '#' * lexer.nonnewline^0))
 
-- Embedded Bash.
local bash = lexer.load('bash')
bash:modify_rule('variable', token(lexer.VARIABLE, '$$' * word_char^1) +
                             bash:get_rule('variable') + variable)
local bash_start_rule = token(lexer.WHITESPACE, P('\t')) +
                        token(lexer.OPERATOR, P(';'))
local bash_end_rule = token(lexer.WHITESPACE, P('\n'))
lex:embed(bash, bash_start_rule, bash_end_rule)
 
return lex