-- Copyright 2023 Sam Nystrom -- Execline LPeg lexer. local l = require('lexer') local token, word_match = l.token, l.word_match local P, R, S = lpeg.P, lpeg.R, lpeg.S local M = {_NAME = 'execline'} -- Whitespace. local ws = token(l.WHITESPACE, l.space^1) -- Comments. local comment = token(l.COMMENT, l.starts_line(S('#')) * l.nonnewline^0) -- Strings. local sq_str = l.range("'") local dq_str = l.range('"') local string = token(l.STRING, sq_str + dq_str) -- Numbers. local dec = l.digit^1 * ('_' * l.digit^1)^0 local oct_num = '0' * S('01234567_')^1 local integer = S('+-')^-1 * (l.hex_num + oct_num + dec) local number = token(l.NUMBER, (l.float + integer)) -- Keywords. local keyword = token(l.KEYWORD, word_match{ 'execlineb', 'execline-cd', 'posix-cd', 'execline-umask', 'posix-umask', 'emptyenv', 'envfile', 'export', 'unexport', 'fdclose', 'fdblock', 'fdmove', 'fdswap', 'fdreserve', 'redirfd', 'piperw', 'heredoc', 'wait', 'getcwd', 'getpid', 'exec', 'tryexec', 'exit', 'trap', 'withstdinas', 'foreground', 'background', 'case', 'if', 'ifelse', 'ifte', 'ifthenelse', 'backtick', 'pipeline', 'runblock', 'define', 'importas', 'elglob', 'elgetpositionals', 'multidefine', 'multisubstitute', 'forx', 'forstdin', 'forbacktickx', 'loopwhilex', 'elgetopt', 'shift', 'dollarat', 'eltest', 'homeof', 'execline' }) -- Identifiers. local identifier = token(l.IDENTIFIER, l.word) local variable = token(l.VARIABLE, '$' * (l.word + l.range('{}', true, true, true))) -- Operators. local operator = token(l.OPERATOR, S('{}')) M._rules = { {'whitespace', ws}, {'keyword', keyword}, {'identifier', identifier}, {'string', string}, {'comment', comment}, {'number', number}, {'variable', variable}, {'operator', operator}, } M._LEXBYLINE = true return M