yi-misc-modes-0.13: src/Yi/Lexer/GNUMake.x
-- -*- haskell -*-
-- Lexer for Makefiles with consideration of GNU extensions
-- This is based off the syntax as described in the GNU Make manual:
-- http://www.gnu.org/software/make/manual/make.html
-- Maintainer: Corey O'Connor
{
{-# OPTIONS -w #-}
module Yi.Lexer.GNUMake ( lexer ) where
import Yi.Lexer.Alex hiding (tokenToStyle)
import Yi.Style
( Style ( .. )
, StyleName
)
import qualified Yi.Style as Style
}
@varAssignOp =
"="
| "?="
| "+="
| ":="
-- The documentation implies that {,},(, and ) can be used as single character variable names.
-- "A variable name may be any sequence of characters not containing `:', `#', `=', or leading or
-- trailing whitespace."
-- http://www.gnu.org/software/make/manual/make.html#Using-Variables
-- However when I try to feed GNU makefile containing such weird variable names GNU make fails.
-- Though the specification does leave limiting the scope of valid variable names that as an open
-- option for "the future"
$varChar = $printable # [\: \# \= \ \{ \} \( \)]
@directives =
include
| if
| export
| unexport
| define
@specialVars =
MAKEFILE_LIST
| ".DEFAULT_GOAL"
| MAKE_RESTARTS
| ".VARIABLES"
| ".FEATURES"
| ".INCLUDE_DIRS"
| MAKE
$space = [\ ]
make :-
<0>
{
-- All lines that start with a \t are passed to the shell post variable expansion and '\'
-- handling.
-- TODO: I'm almost convinced I'd like to see the tab character visually distinct from a space.
-- One possibility would be to treat the tab character as an operator.
^\t
{ m (const RuleCommand) Style.defaultStyle }
-- There can be any number of spaces (but not tabs!) preceeded a directive.
^$space+
{ c Style.defaultStyle }
-- The "include" directive can occur in two forms:
-- One preceeded by a "-"
-- Another not preceeded by a "-"
\-?"include"
{ m (const IncludeDirective) Style.importStyle }
-- A variable expansion outside of a prerequisite can occur in three different forms.
-- Inside a prerequisite they can occur in four different forms.
-- TODO: Highlight the automatic variables differently.
-- 1. Single character variable names unless the character is a $.
"$$"
{ c Style.defaultStyle }
\$$varChar
{ c Style.variableStyle }
-- 2 & 3: Parentheses or brackets could indicate a variable expansion or function call.
"${"
{ m (const $ ComplexExpansion '}' TopLevel) Style.operatorStyle }
"$("
{ m (const $ ComplexExpansion ')' TopLevel) Style.operatorStyle }
\#
{ m (const $ InComment) Style.commentStyle }
\n
{ c Style.defaultStyle }
.
{ c Style.defaultStyle }
}
-- The include directive is a space separated list. Optionally followed by a comment.
<includeDirective>
{
$space+
{ c Style.defaultStyle }
\#
{ m (const $ InComment) Style.commentStyle }
\n
{ m (const $ TopLevel) Style.defaultStyle }
-- For now anything else is considered a string.
-- This is incorrect. The items of the space separated list can be:
-- 0. File globs
-- 1. Variable expansions
-- 2. String literals
.
{ c Style.stringStyle }
}
-- A variable expansion that starts with a parentheses or bracket could be a function call. For now
-- everything up to the close character is considered part of the variable name.
<complexExpansion>
{
$white+ { c Style.defaultStyle }
-- Variable expansion is supported in a variable expansion. Unlike in a rule commmand the
-- sequence $$ means the variable named $$.
"$$"
{ c Style.variableStyle }
\$$varChar
{ c Style.variableStyle }
"${"
{ m (\this -> ComplexExpansion '}' this) Style.operatorStyle }
"$("
{ m (\this -> ComplexExpansion ')' this) Style.operatorStyle }
./
{
\state preInput _ _ ->
case state of
ComplexExpansion endChar _ ->
let currentChar = head $ alexCollectChar preInput
in if (currentChar == endChar) then True else False
_ -> False
}
{
m (\(ComplexExpansion _ prevState) -> prevState) Style.operatorStyle
}
.
{ c Style.variableStyle }
}
-- After all the lines joined by a '\' character are appended together the text only undergoes
-- variable expansion before being passed to the shell.
-- This means that a '#' character only indicates a comment *only* if the shell interpretting the
-- expanded text would consider it a comment. Wack huh?
-- See 3.1
<ruleCommand>
{
-- If the \n is preceeded by a \ then the next line is part of this command even if there is no
-- \t at the start.
\\[.\n]
{ c Style.makeFileAction }
\n
{ m (const $ TopLevel) Style.defaultStyle }
-- Variable expansion is supported in a rule command.
"$$"
{ c Style.makeFileAction }
\$$varChar
{ c Style.variableStyle }
"${"
{ m (const $ ComplexExpansion '}' RuleCommand) Style.operatorStyle }
"$("
{ m (const $ ComplexExpansion ')' RuleCommand) Style.operatorStyle }
.
{ c Style.makeFileAction }
}
<comment>
{
-- Comments can be continued to the next line with a trailing slash.
-- See 3.1
\\[.\n]
{ c Style.commentStyle }
\n
{ m (const TopLevel) Style.defaultStyle }
.
{ c Style.commentStyle }
}
{
data HlState =
TopLevel
| InComment
| IncludeDirective
| ComplexExpansion Char HlState
| RuleCommand
deriving Show
stateToInit TopLevel = 0
stateToInit InComment = comment
stateToInit IncludeDirective = includeDirective
stateToInit (ComplexExpansion _ _) = complexExpansion
stateToInit RuleCommand = ruleCommand
initState :: HlState
initState = TopLevel
type Token = StyleName
lexer :: StyleLexerASI HlState Token
lexer = StyleLexer
{ _tokenToStyle = id
, _styleLexer = commonLexer alexScanToken initState
}
#include "common.hsinc"
}