1# -*- coding: utf-8 -*-
2"""
3    pygments.lexers.shell
4    ~~~~~~~~~~~~~~~~~~~~~
5
6    Lexers for various shells.
7
8    :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS.
9    :license: BSD, see LICENSE for details.
10"""
11
12import re
13
14from pygments.lexer import Lexer, RegexLexer, do_insertions, bygroups, \
15    include, default, this, using, words
16from pygments.token import Punctuation, \
17    Text, Comment, Operator, Keyword, Name, String, Number, Generic
18from pygments.util import shebang_matches
19
20
21__all__ = ['BashLexer', 'BashSessionLexer', 'TcshLexer', 'BatchLexer',
22           'SlurmBashLexer', 'MSDOSSessionLexer', 'PowerShellLexer',
23           'PowerShellSessionLexer', 'TcshSessionLexer', 'FishShellLexer',
24           'ExeclineLexer']
25
26line_re = re.compile('.*?\n')
27
28
29class BashLexer(RegexLexer):
30    """
31    Lexer for (ba|k|z|)sh shell scripts.
32
33    .. versionadded:: 0.6
34    """
35
36    name = 'Bash'
37    aliases = ['bash', 'sh', 'ksh', 'zsh', 'shell']
38    filenames = ['*.sh', '*.ksh', '*.bash', '*.ebuild', '*.eclass',
39                 '*.exheres-0', '*.exlib', '*.zsh',
40                 '.bashrc', 'bashrc', '.bash_*', 'bash_*', 'zshrc', '.zshrc',
41                 'PKGBUILD']
42    mimetypes = ['application/x-sh', 'application/x-shellscript', 'text/x-shellscript']
43
44    tokens = {
45        'root': [
46            include('basic'),
47            (r'`', String.Backtick, 'backticks'),
48            include('data'),
49            include('interp'),
50        ],
51        'interp': [
52            (r'\$\(\(', Keyword, 'math'),
53            (r'\$\(', Keyword, 'paren'),
54            (r'\$\{#?', String.Interpol, 'curly'),
55            (r'\$[a-zA-Z_]\w*', Name.Variable),  # user variable
56            (r'\$(?:\d+|[#$?!_*@-])', Name.Variable),      # builtin
57            (r'\$', Text),
58        ],
59        'basic': [
60            (r'\b(if|fi|else|while|do|done|for|then|return|function|case|'
61             r'select|continue|until|esac|elif)(\s*)\b',
62             bygroups(Keyword, Text)),
63            (r'\b(alias|bg|bind|break|builtin|caller|cd|command|compgen|'
64             r'complete|declare|dirs|disown|echo|enable|eval|exec|exit|'
65             r'export|false|fc|fg|getopts|hash|help|history|jobs|kill|let|'
66             r'local|logout|popd|printf|pushd|pwd|read|readonly|set|shift|'
67             r'shopt|source|suspend|test|time|times|trap|true|type|typeset|'
68             r'ulimit|umask|unalias|unset|wait)(?=[\s)`])',
69             Name.Builtin),
70            (r'\A#!.+\n', Comment.Hashbang),
71            (r'#.*\n', Comment.Single),
72            (r'\\[\w\W]', String.Escape),
73            (r'(\b\w+)(\s*)(\+?=)', bygroups(Name.Variable, Text, Operator)),
74            (r'[\[\]{}()=]', Operator),
75            (r'<<<', Operator),  # here-string
76            (r'<<-?\s*(\'?)\\?(\w+)[\w\W]+?\2', String),
77            (r'&&|\|\|', Operator),
78        ],
79        'data': [
80            (r'(?s)\$?"(\\.|[^"\\$])*"', String.Double),
81            (r'"', String.Double, 'string'),
82            (r"(?s)\$'(\\\\|\\[0-7]+|\\.|[^'\\])*'", String.Single),
83            (r"(?s)'.*?'", String.Single),
84            (r';', Punctuation),
85            (r'&', Punctuation),
86            (r'\|', Punctuation),
87            (r'\s+', Text),
88            (r'\d+\b', Number),
89            (r'[^=\s\[\]{}()$"\'`\\<&|;]+', Text),
90            (r'<', Text),
91        ],
92        'string': [
93            (r'"', String.Double, '#pop'),
94            (r'(?s)(\\\\|\\[0-7]+|\\.|[^"\\$])+', String.Double),
95            include('interp'),
96        ],
97        'curly': [
98            (r'\}', String.Interpol, '#pop'),
99            (r':-', Keyword),
100            (r'\w+', Name.Variable),
101            (r'[^}:"\'`$\\]+', Punctuation),
102            (r':', Punctuation),
103            include('root'),
104        ],
105        'paren': [
106            (r'\)', Keyword, '#pop'),
107            include('root'),
108        ],
109        'math': [
110            (r'\)\)', Keyword, '#pop'),
111            (r'[-+*/%^|&]|\*\*|\|\|', Operator),
112            (r'\d+#\d+', Number),
113            (r'\d+#(?! )', Number),
114            (r'\d+', Number),
115            include('root'),
116        ],
117        'backticks': [
118            (r'`', String.Backtick, '#pop'),
119            include('root'),
120        ],
121    }
122
123    def analyse_text(text):
124        if shebang_matches(text, r'(ba|z|)sh'):
125            return 1
126        if text.startswith('$ '):
127            return 0.2
128
129
130class SlurmBashLexer(BashLexer):
131    """
132    Lexer for (ba|k|z|)sh Slurm scripts.
133
134    .. versionadded:: 2.4
135    """
136
137    name = 'Slurm'
138    aliases = ['slurm', 'sbatch']
139    filenames = ['*.sl']
140    mimetypes = []
141    EXTRA_KEYWORDS = {'srun'}
142
143    def get_tokens_unprocessed(self, text):
144        for index, token, value in BashLexer.get_tokens_unprocessed(self, text):
145            if token is Text and value in self.EXTRA_KEYWORDS:
146                yield index, Name.Builtin, value
147            elif token is Comment.Single and 'SBATCH' in value:
148                yield index, Keyword.Pseudo, value
149            else:
150                yield index, token, value
151
152class ShellSessionBaseLexer(Lexer):
153    """
154    Base lexer for simplistic shell sessions.
155
156    .. versionadded:: 2.1
157    """
158
159    _venv = re.compile(r'^(\([^)]*\))(\s*)')
160
161    def get_tokens_unprocessed(self, text):
162        innerlexer = self._innerLexerCls(**self.options)
163
164        pos = 0
165        curcode = ''
166        insertions = []
167        backslash_continuation = False
168
169        for match in line_re.finditer(text):
170            line = match.group()
171            if backslash_continuation:
172                curcode += line
173                backslash_continuation = curcode.endswith('\\\n')
174                continue
175
176            venv_match = self._venv.match(line)
177            if venv_match:
178                venv = venv_match.group(1)
179                venv_whitespace = venv_match.group(2)
180                insertions.append((len(curcode),
181                    [(0, Generic.Prompt.VirtualEnv, venv)]))
182                if venv_whitespace:
183                    insertions.append((len(curcode),
184                        [(0, Text, venv_whitespace)]))
185                line = line[venv_match.end():]
186
187            m = self._ps1rgx.match(line)
188            if m:
189                # To support output lexers (say diff output), the output
190                # needs to be broken by prompts whenever the output lexer
191                # changes.
192                if not insertions:
193                    pos = match.start()
194
195                insertions.append((len(curcode),
196                                   [(0, Generic.Prompt, m.group(1))]))
197                curcode += m.group(2)
198                backslash_continuation = curcode.endswith('\\\n')
199            elif line.startswith(self._ps2):
200                insertions.append((len(curcode),
201                                   [(0, Generic.Prompt, line[:len(self._ps2)])]))
202                curcode += line[len(self._ps2):]
203                backslash_continuation = curcode.endswith('\\\n')
204            else:
205                if insertions:
206                    toks = innerlexer.get_tokens_unprocessed(curcode)
207                    for i, t, v in do_insertions(insertions, toks):
208                        yield pos+i, t, v
209                yield match.start(), Generic.Output, line
210                insertions = []
211                curcode = ''
212        if insertions:
213            for i, t, v in do_insertions(insertions,
214                                         innerlexer.get_tokens_unprocessed(curcode)):
215                yield pos+i, t, v
216
217
218class BashSessionLexer(ShellSessionBaseLexer):
219    """
220    Lexer for simplistic shell sessions.
221
222    .. versionadded:: 1.1
223    """
224
225    name = 'Bash Session'
226    aliases = ['console', 'shell-session']
227    filenames = ['*.sh-session', '*.shell-session']
228    mimetypes = ['application/x-shell-session', 'application/x-sh-session']
229
230    _innerLexerCls = BashLexer
231    _ps1rgx = re.compile(
232        r'^((?:(?:\[.*?\])|(?:\(\S+\))?(?:| |sh\S*?|\w+\S+[@:]\S+(?:\s+\S+)' \
233        r'?|\[\S+[@:][^\n]+\].+))\s*[$#%])(.*\n?)')
234    _ps2 = '>'
235
236
237class BatchLexer(RegexLexer):
238    """
239    Lexer for the DOS/Windows Batch file format.
240
241    .. versionadded:: 0.7
242    """
243    name = 'Batchfile'
244    aliases = ['bat', 'batch', 'dosbatch', 'winbatch']
245    filenames = ['*.bat', '*.cmd']
246    mimetypes = ['application/x-dos-batch']
247
248    flags = re.MULTILINE | re.IGNORECASE
249
250    _nl = r'\n\x1a'
251    _punct = r'&<>|'
252    _ws = r'\t\v\f\r ,;=\xa0'
253    _nlws = r'\s\x1a\xa0,;='
254    _space = r'(?:(?:(?:\^[%s])?[%s])+)' % (_nl, _ws)
255    _keyword_terminator = (r'(?=(?:\^[%s]?)?[%s+./:[\\\]]|[%s%s(])' %
256                           (_nl, _ws, _nl, _punct))
257    _token_terminator = r'(?=\^?[%s]|[%s%s])' % (_ws, _punct, _nl)
258    _start_label = r'((?:(?<=^[^:])|^[^:]?)[%s]*)(:)' % _ws
259    _label = r'(?:(?:[^%s%s+:^]|\^[%s]?[\w\W])*)' % (_nlws, _punct, _nl)
260    _label_compound = r'(?:(?:[^%s%s+:^)]|\^[%s]?[^)])*)' % (_nlws, _punct, _nl)
261    _number = r'(?:-?(?:0[0-7]+|0x[\da-f]+|\d+)%s)' % _token_terminator
262    _opword = r'(?:equ|geq|gtr|leq|lss|neq)'
263    _string = r'(?:"[^%s"]*(?:"|(?=[%s])))' % (_nl, _nl)
264    _variable = (r'(?:(?:%%(?:\*|(?:~[a-z]*(?:\$[^:]+:)?)?\d|'
265                 r'[^%%:%s]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:[^%%%s^]|'
266                 r'\^[^%%%s])[^=%s]*=(?:[^%%%s^]|\^[^%%%s])*)?)?%%))|'
267                 r'(?:\^?![^!:%s]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:'
268                 r'[^!%s^]|\^[^!%s])[^=%s]*=(?:[^!%s^]|\^[^!%s])*)?)?\^?!))' %
269                 (_nl, _nl, _nl, _nl, _nl, _nl, _nl, _nl, _nl, _nl, _nl, _nl))
270    _core_token = r'(?:(?:(?:\^[%s]?)?[^"%s%s])+)' % (_nl, _nlws, _punct)
271    _core_token_compound = r'(?:(?:(?:\^[%s]?)?[^"%s%s)])+)' % (_nl, _nlws, _punct)
272    _token = r'(?:[%s]+|%s)' % (_punct, _core_token)
273    _token_compound = r'(?:[%s]+|%s)' % (_punct, _core_token_compound)
274    _stoken = (r'(?:[%s]+|(?:%s|%s|%s)+)' %
275               (_punct, _string, _variable, _core_token))
276
277    def _make_begin_state(compound, _core_token=_core_token,
278                          _core_token_compound=_core_token_compound,
279                          _keyword_terminator=_keyword_terminator,
280                          _nl=_nl, _punct=_punct, _string=_string,
281                          _space=_space, _start_label=_start_label,
282                          _stoken=_stoken, _token_terminator=_token_terminator,
283                          _variable=_variable, _ws=_ws):
284        rest = '(?:%s|%s|[^"%%%s%s%s])*' % (_string, _variable, _nl, _punct,
285                                            ')' if compound else '')
286        rest_of_line = r'(?:(?:[^%s^]|\^[%s]?[\w\W])*)' % (_nl, _nl)
287        rest_of_line_compound = r'(?:(?:[^%s^)]|\^[%s]?[^)])*)' % (_nl, _nl)
288        set_space = r'((?:(?:\^[%s]?)?[^\S\n])*)' % _nl
289        suffix = ''
290        if compound:
291            _keyword_terminator = r'(?:(?=\))|%s)' % _keyword_terminator
292            _token_terminator = r'(?:(?=\))|%s)' % _token_terminator
293            suffix = '/compound'
294        return [
295            ((r'\)', Punctuation, '#pop') if compound else
296             (r'\)((?=\()|%s)%s' % (_token_terminator, rest_of_line),
297              Comment.Single)),
298            (r'(?=%s)' % _start_label, Text, 'follow%s' % suffix),
299            (_space, using(this, state='text')),
300            include('redirect%s' % suffix),
301            (r'[%s]+' % _nl, Text),
302            (r'\(', Punctuation, 'root/compound'),
303            (r'@+', Punctuation),
304            (r'((?:for|if|rem)(?:(?=(?:\^[%s]?)?/)|(?:(?!\^)|'
305             r'(?<=m))(?:(?=\()|%s)))(%s?%s?(?:\^[%s]?)?/(?:\^[%s]?)?\?)' %
306             (_nl, _token_terminator, _space,
307              _core_token_compound if compound else _core_token, _nl, _nl),
308             bygroups(Keyword, using(this, state='text')),
309             'follow%s' % suffix),
310            (r'(goto%s)(%s(?:\^[%s]?)?/(?:\^[%s]?)?\?%s)' %
311             (_keyword_terminator, rest, _nl, _nl, rest),
312             bygroups(Keyword, using(this, state='text')),
313             'follow%s' % suffix),
314            (words(('assoc', 'break', 'cd', 'chdir', 'cls', 'color', 'copy',
315                    'date', 'del', 'dir', 'dpath', 'echo', 'endlocal', 'erase',
316                    'exit', 'ftype', 'keys', 'md', 'mkdir', 'mklink', 'move',
317                    'path', 'pause', 'popd', 'prompt', 'pushd', 'rd', 'ren',
318                    'rename', 'rmdir', 'setlocal', 'shift', 'start', 'time',
319                    'title', 'type', 'ver', 'verify', 'vol'),
320                   suffix=_keyword_terminator), Keyword, 'follow%s' % suffix),
321            (r'(call)(%s?)(:)' % _space,
322             bygroups(Keyword, using(this, state='text'), Punctuation),
323             'call%s' % suffix),
324            (r'call%s' % _keyword_terminator, Keyword),
325            (r'(for%s(?!\^))(%s)(/f%s)' %
326             (_token_terminator, _space, _token_terminator),
327             bygroups(Keyword, using(this, state='text'), Keyword),
328             ('for/f', 'for')),
329            (r'(for%s(?!\^))(%s)(/l%s)' %
330             (_token_terminator, _space, _token_terminator),
331             bygroups(Keyword, using(this, state='text'), Keyword),
332             ('for/l', 'for')),
333            (r'for%s(?!\^)' % _token_terminator, Keyword, ('for2', 'for')),
334            (r'(goto%s)(%s?)(:?)' % (_keyword_terminator, _space),
335             bygroups(Keyword, using(this, state='text'), Punctuation),
336             'label%s' % suffix),
337            (r'(if(?:(?=\()|%s)(?!\^))(%s?)((?:/i%s)?)(%s?)((?:not%s)?)(%s?)' %
338             (_token_terminator, _space, _token_terminator, _space,
339              _token_terminator, _space),
340             bygroups(Keyword, using(this, state='text'), Keyword,
341                      using(this, state='text'), Keyword,
342                      using(this, state='text')), ('(?', 'if')),
343            (r'rem(((?=\()|%s)%s?%s?.*|%s%s)' %
344             (_token_terminator, _space, _stoken, _keyword_terminator,
345              rest_of_line_compound if compound else rest_of_line),
346             Comment.Single, 'follow%s' % suffix),
347            (r'(set%s)%s(/a)' % (_keyword_terminator, set_space),
348             bygroups(Keyword, using(this, state='text'), Keyword),
349             'arithmetic%s' % suffix),
350            (r'(set%s)%s((?:/p)?)%s((?:(?:(?:\^[%s]?)?[^"%s%s^=%s]|'
351             r'\^[%s]?[^"=])+)?)((?:(?:\^[%s]?)?=)?)' %
352             (_keyword_terminator, set_space, set_space, _nl, _nl, _punct,
353              ')' if compound else '', _nl, _nl),
354             bygroups(Keyword, using(this, state='text'), Keyword,
355                      using(this, state='text'), using(this, state='variable'),
356                      Punctuation),
357             'follow%s' % suffix),
358            default('follow%s' % suffix)
359        ]
360
361    def _make_follow_state(compound, _label=_label,
362                           _label_compound=_label_compound, _nl=_nl,
363                           _space=_space, _start_label=_start_label,
364                           _token=_token, _token_compound=_token_compound,
365                           _ws=_ws):
366        suffix = '/compound' if compound else ''
367        state = []
368        if compound:
369            state.append((r'(?=\))', Text, '#pop'))
370        state += [
371            (r'%s([%s]*)(%s)(.*)' %
372             (_start_label, _ws, _label_compound if compound else _label),
373             bygroups(Text, Punctuation, Text, Name.Label, Comment.Single)),
374            include('redirect%s' % suffix),
375            (r'(?=[%s])' % _nl, Text, '#pop'),
376            (r'\|\|?|&&?', Punctuation, '#pop'),
377            include('text')
378        ]
379        return state
380
381    def _make_arithmetic_state(compound, _nl=_nl, _punct=_punct,
382                               _string=_string, _variable=_variable,
383                               _ws=_ws, _nlws=_nlws):
384        op = r'=+\-*/!~'
385        state = []
386        if compound:
387            state.append((r'(?=\))', Text, '#pop'))
388        state += [
389            (r'0[0-7]+', Number.Oct),
390            (r'0x[\da-f]+', Number.Hex),
391            (r'\d+', Number.Integer),
392            (r'[(),]+', Punctuation),
393            (r'([%s]|%%|\^\^)+' % op, Operator),
394            (r'(%s|%s|(\^[%s]?)?[^()%s%%\^"%s%s]|\^[%s]?%s)+' %
395             (_string, _variable, _nl, op, _nlws, _punct, _nlws,
396              r'[^)]' if compound else r'[\w\W]'),
397             using(this, state='variable')),
398            (r'(?=[\x00|&])', Text, '#pop'),
399            include('follow')
400        ]
401        return state
402
403    def _make_call_state(compound, _label=_label,
404                         _label_compound=_label_compound):
405        state = []
406        if compound:
407            state.append((r'(?=\))', Text, '#pop'))
408        state.append((r'(:?)(%s)' % (_label_compound if compound else _label),
409                      bygroups(Punctuation, Name.Label), '#pop'))
410        return state
411
412    def _make_label_state(compound, _label=_label,
413                          _label_compound=_label_compound, _nl=_nl,
414                          _punct=_punct, _string=_string, _variable=_variable):
415        state = []
416        if compound:
417            state.append((r'(?=\))', Text, '#pop'))
418        state.append((r'(%s?)((?:%s|%s|\^[%s]?%s|[^"%%^%s%s%s])*)' %
419                      (_label_compound if compound else _label, _string,
420                       _variable, _nl, r'[^)]' if compound else r'[\w\W]', _nl,
421                       _punct, r')' if compound else ''),
422                      bygroups(Name.Label, Comment.Single), '#pop'))
423        return state
424
425    def _make_redirect_state(compound,
426                             _core_token_compound=_core_token_compound,
427                             _nl=_nl, _punct=_punct, _stoken=_stoken,
428                             _string=_string, _space=_space,
429                             _variable=_variable, _nlws=_nlws):
430        stoken_compound = (r'(?:[%s]+|(?:%s|%s|%s)+)' %
431                           (_punct, _string, _variable, _core_token_compound))
432        return [
433            (r'((?:(?<=[%s])\d)?)(>>?&|<&)([%s]*)(\d)' %
434             (_nlws, _nlws),
435             bygroups(Number.Integer, Punctuation, Text, Number.Integer)),
436            (r'((?:(?<=[%s])(?<!\^[%s])\d)?)(>>?|<)(%s?%s)' %
437             (_nlws, _nl, _space, stoken_compound if compound else _stoken),
438             bygroups(Number.Integer, Punctuation, using(this, state='text')))
439        ]
440
441    tokens = {
442        'root': _make_begin_state(False),
443        'follow': _make_follow_state(False),
444        'arithmetic': _make_arithmetic_state(False),
445        'call': _make_call_state(False),
446        'label': _make_label_state(False),
447        'redirect': _make_redirect_state(False),
448        'root/compound': _make_begin_state(True),
449        'follow/compound': _make_follow_state(True),
450        'arithmetic/compound': _make_arithmetic_state(True),
451        'call/compound': _make_call_state(True),
452        'label/compound': _make_label_state(True),
453        'redirect/compound': _make_redirect_state(True),
454        'variable-or-escape': [
455            (_variable, Name.Variable),
456            (r'%%%%|\^[%s]?(\^!|[\w\W])' % _nl, String.Escape)
457        ],
458        'string': [
459            (r'"', String.Double, '#pop'),
460            (_variable, Name.Variable),
461            (r'\^!|%%', String.Escape),
462            (r'[^"%%^%s]+|[%%^]' % _nl, String.Double),
463            default('#pop')
464        ],
465        'sqstring': [
466            include('variable-or-escape'),
467            (r'[^%]+|%', String.Single)
468        ],
469        'bqstring': [
470            include('variable-or-escape'),
471            (r'[^%]+|%', String.Backtick)
472        ],
473        'text': [
474            (r'"', String.Double, 'string'),
475            include('variable-or-escape'),
476            (r'[^"%%^%s%s\d)]+|.' % (_nlws, _punct), Text)
477        ],
478        'variable': [
479            (r'"', String.Double, 'string'),
480            include('variable-or-escape'),
481            (r'[^"%%^%s]+|.' % _nl, Name.Variable)
482        ],
483        'for': [
484            (r'(%s)(in)(%s)(\()' % (_space, _space),
485             bygroups(using(this, state='text'), Keyword,
486                      using(this, state='text'), Punctuation), '#pop'),
487            include('follow')
488        ],
489        'for2': [
490            (r'\)', Punctuation),
491            (r'(%s)(do%s)' % (_space, _token_terminator),
492             bygroups(using(this, state='text'), Keyword), '#pop'),
493            (r'[%s]+' % _nl, Text),
494            include('follow')
495        ],
496        'for/f': [
497            (r'(")((?:%s|[^"])*?")([%s]*)(\))' % (_variable, _nlws),
498             bygroups(String.Double, using(this, state='string'), Text,
499                      Punctuation)),
500            (r'"', String.Double, ('#pop', 'for2', 'string')),
501            (r"('(?:%%%%|%s|[\w\W])*?')([%s]*)(\))" % (_variable, _nlws),
502             bygroups(using(this, state='sqstring'), Text, Punctuation)),
503            (r'(`(?:%%%%|%s|[\w\W])*?`)([%s]*)(\))' % (_variable, _nlws),
504             bygroups(using(this, state='bqstring'), Text, Punctuation)),
505            include('for2')
506        ],
507        'for/l': [
508            (r'-?\d+', Number.Integer),
509            include('for2')
510        ],
511        'if': [
512            (r'((?:cmdextversion|errorlevel)%s)(%s)(\d+)' %
513             (_token_terminator, _space),
514             bygroups(Keyword, using(this, state='text'),
515                      Number.Integer), '#pop'),
516            (r'(defined%s)(%s)(%s)' % (_token_terminator, _space, _stoken),
517             bygroups(Keyword, using(this, state='text'),
518                      using(this, state='variable')), '#pop'),
519            (r'(exist%s)(%s%s)' % (_token_terminator, _space, _stoken),
520             bygroups(Keyword, using(this, state='text')), '#pop'),
521            (r'(%s%s)(%s)(%s%s)' % (_number, _space, _opword, _space, _number),
522             bygroups(using(this, state='arithmetic'), Operator.Word,
523                      using(this, state='arithmetic')), '#pop'),
524            (_stoken, using(this, state='text'), ('#pop', 'if2')),
525        ],
526        'if2': [
527            (r'(%s?)(==)(%s?%s)' % (_space, _space, _stoken),
528             bygroups(using(this, state='text'), Operator,
529                      using(this, state='text')), '#pop'),
530            (r'(%s)(%s)(%s%s)' % (_space, _opword, _space, _stoken),
531             bygroups(using(this, state='text'), Operator.Word,
532                      using(this, state='text')), '#pop')
533        ],
534        '(?': [
535            (_space, using(this, state='text')),
536            (r'\(', Punctuation, ('#pop', 'else?', 'root/compound')),
537            default('#pop')
538        ],
539        'else?': [
540            (_space, using(this, state='text')),
541            (r'else%s' % _token_terminator, Keyword, '#pop'),
542            default('#pop')
543        ]
544    }
545
546
547class MSDOSSessionLexer(ShellSessionBaseLexer):
548    """
549    Lexer for simplistic MSDOS sessions.
550
551    .. versionadded:: 2.1
552    """
553
554    name = 'MSDOS Session'
555    aliases = ['doscon']
556    filenames = []
557    mimetypes = []
558
559    _innerLexerCls = BatchLexer
560    _ps1rgx = re.compile(r'^([^>]*>)(.*\n?)')
561    _ps2 = 'More? '
562
563
564class TcshLexer(RegexLexer):
565    """
566    Lexer for tcsh scripts.
567
568    .. versionadded:: 0.10
569    """
570
571    name = 'Tcsh'
572    aliases = ['tcsh', 'csh']
573    filenames = ['*.tcsh', '*.csh']
574    mimetypes = ['application/x-csh']
575
576    tokens = {
577        'root': [
578            include('basic'),
579            (r'\$\(', Keyword, 'paren'),
580            (r'\$\{#?', Keyword, 'curly'),
581            (r'`', String.Backtick, 'backticks'),
582            include('data'),
583        ],
584        'basic': [
585            (r'\b(if|endif|else|while|then|foreach|case|default|'
586             r'continue|goto|breaksw|end|switch|endsw)\s*\b',
587             Keyword),
588            (r'\b(alias|alloc|bg|bindkey|break|builtins|bye|caller|cd|chdir|'
589             r'complete|dirs|echo|echotc|eval|exec|exit|fg|filetest|getxvers|'
590             r'glob|getspath|hashstat|history|hup|inlib|jobs|kill|'
591             r'limit|log|login|logout|ls-F|migrate|newgrp|nice|nohup|notify|'
592             r'onintr|popd|printenv|pushd|rehash|repeat|rootnode|popd|pushd|'
593             r'set|shift|sched|setenv|setpath|settc|setty|setxvers|shift|'
594             r'source|stop|suspend|source|suspend|telltc|time|'
595             r'umask|unalias|uncomplete|unhash|universe|unlimit|unset|unsetenv|'
596             r'ver|wait|warp|watchlog|where|which)\s*\b',
597             Name.Builtin),
598            (r'#.*', Comment),
599            (r'\\[\w\W]', String.Escape),
600            (r'(\b\w+)(\s*)(=)', bygroups(Name.Variable, Text, Operator)),
601            (r'[\[\]{}()=]+', Operator),
602            (r'<<\s*(\'?)\\?(\w+)[\w\W]+?\2', String),
603            (r';', Punctuation),
604        ],
605        'data': [
606            (r'(?s)"(\\\\|\\[0-7]+|\\.|[^"\\])*"', String.Double),
607            (r"(?s)'(\\\\|\\[0-7]+|\\.|[^'\\])*'", String.Single),
608            (r'\s+', Text),
609            (r'[^=\s\[\]{}()$"\'`\\;#]+', Text),
610            (r'\d+(?= |\Z)', Number),
611            (r'\$#?(\w+|.)', Name.Variable),
612        ],
613        'curly': [
614            (r'\}', Keyword, '#pop'),
615            (r':-', Keyword),
616            (r'\w+', Name.Variable),
617            (r'[^}:"\'`$]+', Punctuation),
618            (r':', Punctuation),
619            include('root'),
620        ],
621        'paren': [
622            (r'\)', Keyword, '#pop'),
623            include('root'),
624        ],
625        'backticks': [
626            (r'`', String.Backtick, '#pop'),
627            include('root'),
628        ],
629    }
630
631
632class TcshSessionLexer(ShellSessionBaseLexer):
633    """
634    Lexer for Tcsh sessions.
635
636    .. versionadded:: 2.1
637    """
638
639    name = 'Tcsh Session'
640    aliases = ['tcshcon']
641    filenames = []
642    mimetypes = []
643
644    _innerLexerCls = TcshLexer
645    _ps1rgx = re.compile(r'^([^>]+>)(.*\n?)')
646    _ps2 = '? '
647
648
649class PowerShellLexer(RegexLexer):
650    """
651    For Windows PowerShell code.
652
653    .. versionadded:: 1.5
654    """
655    name = 'PowerShell'
656    aliases = ['powershell', 'posh', 'ps1', 'psm1']
657    filenames = ['*.ps1', '*.psm1']
658    mimetypes = ['text/x-powershell']
659
660    flags = re.DOTALL | re.IGNORECASE | re.MULTILINE
661
662    keywords = (
663        'while validateset validaterange validatepattern validatelength '
664        'validatecount until trap switch return ref process param parameter in '
665        'if global: function foreach for finally filter end elseif else '
666        'dynamicparam do default continue cmdletbinding break begin alias \\? '
667        '% #script #private #local #global mandatory parametersetname position '
668        'valuefrompipeline valuefrompipelinebypropertyname '
669        'valuefromremainingarguments helpmessage try catch throw').split()
670
671    operators = (
672        'and as band bnot bor bxor casesensitive ccontains ceq cge cgt cle '
673        'clike clt cmatch cne cnotcontains cnotlike cnotmatch contains '
674        'creplace eq exact f file ge gt icontains ieq ige igt ile ilike ilt '
675        'imatch ine inotcontains inotlike inotmatch ireplace is isnot le like '
676        'lt match ne not notcontains notlike notmatch or regex replace '
677        'wildcard').split()
678
679    verbs = (
680        'write where watch wait use update unregister unpublish unprotect '
681        'unlock uninstall undo unblock trace test tee take sync switch '
682        'suspend submit stop step start split sort skip show set send select '
683        'search scroll save revoke resume restore restart resolve resize '
684        'reset request repair rename remove register redo receive read push '
685        'publish protect pop ping out optimize open new move mount merge '
686        'measure lock limit join invoke install initialize import hide group '
687        'grant get format foreach find export expand exit enter enable edit '
688        'dismount disconnect disable deny debug cxnew copy convertto '
689        'convertfrom convert connect confirm compress complete compare close '
690        'clear checkpoint block backup assert approve aggregate add').split()
691
692    aliases_ = (
693        'ac asnp cat cd cfs chdir clc clear clhy cli clp cls clv cnsn '
694        'compare copy cp cpi cpp curl cvpa dbp del diff dir dnsn ebp echo epal '
695        'epcsv epsn erase etsn exsn fc fhx fl foreach ft fw gal gbp gc gci gcm '
696        'gcs gdr ghy gi gjb gl gm gmo gp gps gpv group gsn gsnp gsv gu gv gwmi '
697        'h history icm iex ihy ii ipal ipcsv ipmo ipsn irm ise iwmi iwr kill lp '
698        'ls man md measure mi mount move mp mv nal ndr ni nmo npssc nsn nv ogv '
699        'oh popd ps pushd pwd r rbp rcjb rcsn rd rdr ren ri rjb rm rmdir rmo '
700        'rni rnp rp rsn rsnp rujb rv rvpa rwmi sajb sal saps sasv sbp sc select '
701        'set shcm si sl sleep sls sort sp spjb spps spsv start sujb sv swmi tee '
702        'trcm type wget where wjb write').split()
703
704    commenthelp = (
705        'component description example externalhelp forwardhelpcategory '
706        'forwardhelptargetname functionality inputs link '
707        'notes outputs parameter remotehelprunspace role synopsis').split()
708
709    tokens = {
710        'root': [
711            # we need to count pairs of parentheses for correct highlight
712            # of '$(...)' blocks in strings
713            (r'\(', Punctuation, 'child'),
714            (r'\s+', Text),
715            (r'^(\s*#[#\s]*)(\.(?:%s))([^\n]*$)' % '|'.join(commenthelp),
716             bygroups(Comment, String.Doc, Comment)),
717            (r'#[^\n]*?$', Comment),
718            (r'(&lt;|<)#', Comment.Multiline, 'multline'),
719            (r'@"\n', String.Heredoc, 'heredoc-double'),
720            (r"@'\n.*?\n'@", String.Heredoc),
721            # escaped syntax
722            (r'`[\'"$@-]', Punctuation),
723            (r'"', String.Double, 'string'),
724            (r"'([^']|'')*'", String.Single),
725            (r'(\$|@@|@)((global|script|private|env):)?\w+',
726             Name.Variable),
727            (r'(%s)\b' % '|'.join(keywords), Keyword),
728            (r'-(%s)\b' % '|'.join(operators), Operator),
729            (r'(%s)-[a-z_]\w*\b' % '|'.join(verbs), Name.Builtin),
730            (r'(%s)\s' % '|'.join(aliases_), Name.Builtin),
731            (r'\[[a-z_\[][\w. `,\[\]]*\]', Name.Constant),  # .net [type]s
732            (r'-[a-z_]\w*', Name),
733            (r'\w+', Name),
734            (r'[.,;@{}\[\]$()=+*/\\&%!~?^`|<>-]|::', Punctuation),
735        ],
736        'child': [
737            (r'\)', Punctuation, '#pop'),
738            include('root'),
739        ],
740        'multline': [
741            (r'[^#&.]+', Comment.Multiline),
742            (r'#(>|&gt;)', Comment.Multiline, '#pop'),
743            (r'\.(%s)' % '|'.join(commenthelp), String.Doc),
744            (r'[#&.]', Comment.Multiline),
745        ],
746        'string': [
747            (r"`[0abfnrtv'\"$`]", String.Escape),
748            (r'[^$`"]+', String.Double),
749            (r'\$\(', Punctuation, 'child'),
750            (r'""', String.Double),
751            (r'[`$]', String.Double),
752            (r'"', String.Double, '#pop'),
753        ],
754        'heredoc-double': [
755            (r'\n"@', String.Heredoc, '#pop'),
756            (r'\$\(', Punctuation, 'child'),
757            (r'[^@\n]+"]', String.Heredoc),
758            (r".", String.Heredoc),
759        ]
760    }
761
762
763class PowerShellSessionLexer(ShellSessionBaseLexer):
764    """
765    Lexer for simplistic Windows PowerShell sessions.
766
767    .. versionadded:: 2.1
768    """
769
770    name = 'PowerShell Session'
771    aliases = ['ps1con']
772    filenames = []
773    mimetypes = []
774
775    _innerLexerCls = PowerShellLexer
776    _ps1rgx = re.compile(r'^((?:\[[^]]+\]: )?PS[^>]*> ?)(.*\n?)')
777    _ps2 = '>> '
778
779
780class FishShellLexer(RegexLexer):
781    """
782    Lexer for Fish shell scripts.
783
784    .. versionadded:: 2.1
785    """
786
787    name = 'Fish'
788    aliases = ['fish', 'fishshell']
789    filenames = ['*.fish', '*.load']
790    mimetypes = ['application/x-fish']
791
792    tokens = {
793        'root': [
794            include('basic'),
795            include('data'),
796            include('interp'),
797        ],
798        'interp': [
799            (r'\$\(\(', Keyword, 'math'),
800            (r'\(', Keyword, 'paren'),
801            (r'\$#?(\w+|.)', Name.Variable),
802        ],
803        'basic': [
804            (r'\b(begin|end|if|else|while|break|for|in|return|function|block|'
805             r'case|continue|switch|not|and|or|set|echo|exit|pwd|true|false|'
806             r'cd|count|test)(\s*)\b',
807             bygroups(Keyword, Text)),
808            (r'\b(alias|bg|bind|breakpoint|builtin|command|commandline|'
809             r'complete|contains|dirh|dirs|emit|eval|exec|fg|fish|fish_config|'
810             r'fish_indent|fish_pager|fish_prompt|fish_right_prompt|'
811             r'fish_update_completions|fishd|funced|funcsave|functions|help|'
812             r'history|isatty|jobs|math|mimedb|nextd|open|popd|prevd|psub|'
813             r'pushd|random|read|set_color|source|status|trap|type|ulimit|'
814             r'umask|vared|fc|getopts|hash|kill|printf|time|wait)\s*\b(?!\.)',
815             Name.Builtin),
816            (r'#.*\n', Comment),
817            (r'\\[\w\W]', String.Escape),
818            (r'(\b\w+)(\s*)(=)', bygroups(Name.Variable, Text, Operator)),
819            (r'[\[\]()=]', Operator),
820            (r'<<-?\s*(\'?)\\?(\w+)[\w\W]+?\2', String),
821        ],
822        'data': [
823            (r'(?s)\$?"(\\\\|\\[0-7]+|\\.|[^"\\$])*"', String.Double),
824            (r'"', String.Double, 'string'),
825            (r"(?s)\$'(\\\\|\\[0-7]+|\\.|[^'\\])*'", String.Single),
826            (r"(?s)'.*?'", String.Single),
827            (r';', Punctuation),
828            (r'&|\||\^|<|>', Operator),
829            (r'\s+', Text),
830            (r'\d+(?= |\Z)', Number),
831            (r'[^=\s\[\]{}()$"\'`\\<&|;]+', Text),
832        ],
833        'string': [
834            (r'"', String.Double, '#pop'),
835            (r'(?s)(\\\\|\\[0-7]+|\\.|[^"\\$])+', String.Double),
836            include('interp'),
837        ],
838        'paren': [
839            (r'\)', Keyword, '#pop'),
840            include('root'),
841        ],
842        'math': [
843            (r'\)\)', Keyword, '#pop'),
844            (r'[-+*/%^|&]|\*\*|\|\|', Operator),
845            (r'\d+#\d+', Number),
846            (r'\d+#(?! )', Number),
847            (r'\d+', Number),
848            include('root'),
849        ],
850    }
851
852class ExeclineLexer(RegexLexer):
853    """
854    Lexer for Laurent Bercot's execline language
855    (https://skarnet.org/software/execline).
856
857    .. versionadded:: 2.7
858    """
859
860    name = 'execline'
861    aliases = ['execline']
862    filenames = ['*.exec']
863
864    tokens = {
865        'root': [
866            include('basic'),
867            include('data'),
868            include('interp')
869        ],
870        'interp': [
871            (r'\$\{', String.Interpol, 'curly'),
872            (r'\$[\w@#]+', Name.Variable),  # user variable
873            (r'\$', Text),
874        ],
875        'basic': [
876            (r'\b(background|backtick|cd|define|dollarat|elgetopt|'
877             r'elgetpositionals|elglob|emptyenv|envfile|exec|execlineb|'
878             r'exit|export|fdblock|fdclose|fdmove|fdreserve|fdswap|'
879             r'forbacktickx|foreground|forstdin|forx|getcwd|getpid|heredoc|'
880             r'homeof|if|ifelse|ifte|ifthenelse|importas|loopwhilex|'
881             r'multidefine|multisubstitute|pipeline|piperw|posix-cd|'
882             r'redirfd|runblock|shift|trap|tryexec|umask|unexport|wait|'
883             r'withstdinas)\b', Name.Builtin),
884            (r'\A#!.+\n', Comment.Hashbang),
885            (r'#.*\n', Comment.Single),
886            (r'[{}]', Operator)
887        ],
888        'data': [
889            (r'(?s)"(\\.|[^"\\$])*"', String.Double),
890            (r'"', String.Double, 'string'),
891            (r'\s+', Text),
892            (r'[^\s{}$"\\]+', Text)
893        ],
894        'string': [
895            (r'"', String.Double, '#pop'),
896            (r'(?s)(\\\\|\\.|[^"\\$])+', String.Double),
897            include('interp'),
898        ],
899        'curly': [
900            (r'\}', String.Interpol, '#pop'),
901            (r'[\w#@]+', Name.Variable),
902            include('root')
903        ]
904
905    }
906
907    def analyse_text(text):
908        if shebang_matches(text, r'execlineb'):
909            return 1
910