|
1 # -*- coding: utf-8 -*- |
|
2 """ |
|
3 pygments.lexers.shell |
|
4 ~~~~~~~~~~~~~~~~~~~~~ |
|
5 |
|
6 Lexers for various shells. |
|
7 |
|
8 :copyright: Copyright 2006-2012 by the Pygments team, see AUTHORS. |
|
9 :license: BSD, see LICENSE for details. |
|
10 """ |
|
11 |
|
12 import re |
|
13 |
|
14 from pygments.lexer import Lexer, RegexLexer, do_insertions, bygroups, include |
|
15 from pygments.token import Punctuation, \ |
|
16 Text, Comment, Operator, Keyword, Name, String, Number, Generic |
|
17 from pygments.util import shebang_matches |
|
18 |
|
19 |
|
20 __all__ = ['BashLexer', 'BashSessionLexer', 'TcshLexer', 'BatchLexer', |
|
21 'PowerShellLexer'] |
|
22 |
|
23 line_re = re.compile('.*?\n') |
|
24 |
|
25 |
|
26 class BashLexer(RegexLexer): |
|
27 """ |
|
28 Lexer for (ba|k|)sh shell scripts. |
|
29 |
|
30 *New in Pygments 0.6.* |
|
31 """ |
|
32 |
|
33 name = 'Bash' |
|
34 aliases = ['bash', 'sh', 'ksh'] |
|
35 filenames = ['*.sh', '*.ksh', '*.bash', '*.ebuild', '*.eclass', |
|
36 '.bashrc', 'bashrc', '.bash_*', 'bash_*'] |
|
37 mimetypes = ['application/x-sh', 'application/x-shellscript'] |
|
38 |
|
39 tokens = { |
|
40 'root': [ |
|
41 include('basic'), |
|
42 (r'\$\(\(', Keyword, 'math'), |
|
43 (r'\$\(', Keyword, 'paren'), |
|
44 (r'\${#?', Keyword, 'curly'), |
|
45 (r'`', String.Backtick, 'backticks'), |
|
46 include('data'), |
|
47 ], |
|
48 'basic': [ |
|
49 (r'\b(if|fi|else|while|do|done|for|then|return|function|case|' |
|
50 r'select|continue|until|esac|elif)\s*\b', |
|
51 Keyword), |
|
52 (r'\b(alias|bg|bind|break|builtin|caller|cd|command|compgen|' |
|
53 r'complete|declare|dirs|disown|echo|enable|eval|exec|exit|' |
|
54 r'export|false|fc|fg|getopts|hash|help|history|jobs|kill|let|' |
|
55 r'local|logout|popd|printf|pushd|pwd|read|readonly|set|shift|' |
|
56 r'shopt|source|suspend|test|time|times|trap|true|type|typeset|' |
|
57 r'ulimit|umask|unalias|unset|wait)\s*\b(?!\.)', |
|
58 Name.Builtin), |
|
59 (r'#.*\n', Comment), |
|
60 (r'\\[\w\W]', String.Escape), |
|
61 (r'(\b\w+)(\s*)(=)', bygroups(Name.Variable, Text, Operator)), |
|
62 (r'[\[\]{}()=]', Operator), |
|
63 (r'<<-?\s*(\'?)\\?(\w+)[\w\W]+?\2', String), |
|
64 (r'&&|\|\|', Operator), |
|
65 ], |
|
66 'data': [ |
|
67 (r'(?s)\$?"(\\\\|\\[0-7]+|\\.|[^"\\])*"', String.Double), |
|
68 (r"(?s)\$?'(\\\\|\\[0-7]+|\\.|[^'\\])*'", String.Single), |
|
69 (r';', Text), |
|
70 (r'\s+', Text), |
|
71 (r'[^=\s\[\]{}()$"\'`\\<]+', Text), |
|
72 (r'\d+(?= |\Z)', Number), |
|
73 (r'\$#?(\w+|.)', Name.Variable), |
|
74 (r'<', Text), |
|
75 ], |
|
76 'curly': [ |
|
77 (r'}', Keyword, '#pop'), |
|
78 (r':-', Keyword), |
|
79 (r'[a-zA-Z0-9_]+', Name.Variable), |
|
80 (r'[^}:"\'`$]+', Punctuation), |
|
81 (r':', Punctuation), |
|
82 include('root'), |
|
83 ], |
|
84 'paren': [ |
|
85 (r'\)', Keyword, '#pop'), |
|
86 include('root'), |
|
87 ], |
|
88 'math': [ |
|
89 (r'\)\)', Keyword, '#pop'), |
|
90 (r'[-+*/%^|&]|\*\*|\|\|', Operator), |
|
91 (r'\d+', Number), |
|
92 include('root'), |
|
93 ], |
|
94 'backticks': [ |
|
95 (r'`', String.Backtick, '#pop'), |
|
96 include('root'), |
|
97 ], |
|
98 } |
|
99 |
|
100 def analyse_text(text): |
|
101 return shebang_matches(text, r'(ba|z|)sh') |
|
102 |
|
103 |
|
104 class BashSessionLexer(Lexer): |
|
105 """ |
|
106 Lexer for simplistic shell sessions. |
|
107 |
|
108 *New in Pygments 1.1.* |
|
109 """ |
|
110 |
|
111 name = 'Bash Session' |
|
112 aliases = ['console'] |
|
113 filenames = ['*.sh-session'] |
|
114 mimetypes = ['application/x-shell-session'] |
|
115 |
|
116 def get_tokens_unprocessed(self, text): |
|
117 bashlexer = BashLexer(**self.options) |
|
118 |
|
119 pos = 0 |
|
120 curcode = '' |
|
121 insertions = [] |
|
122 |
|
123 for match in line_re.finditer(text): |
|
124 line = match.group() |
|
125 m = re.match(r'^((?:\(\S+\))?(?:|sh\S*?|\w+\S+[@:]\S+(?:\s+\S+)' |
|
126 r'?|\[\S+[@:][^\n]+\].+)[$#%])(.*\n?)' , line) |
|
127 if m: |
|
128 # To support output lexers (say diff output), the output |
|
129 # needs to be broken by prompts whenever the output lexer |
|
130 # changes. |
|
131 if not insertions: |
|
132 pos = match.start() |
|
133 |
|
134 insertions.append((len(curcode), |
|
135 [(0, Generic.Prompt, m.group(1))])) |
|
136 curcode += m.group(2) |
|
137 elif line.startswith('>'): |
|
138 insertions.append((len(curcode), |
|
139 [(0, Generic.Prompt, line[:1])])) |
|
140 curcode += line[1:] |
|
141 else: |
|
142 if insertions: |
|
143 toks = bashlexer.get_tokens_unprocessed(curcode) |
|
144 for i, t, v in do_insertions(insertions, toks): |
|
145 yield pos+i, t, v |
|
146 yield match.start(), Generic.Output, line |
|
147 insertions = [] |
|
148 curcode = '' |
|
149 if insertions: |
|
150 for i, t, v in do_insertions(insertions, |
|
151 bashlexer.get_tokens_unprocessed(curcode)): |
|
152 yield pos+i, t, v |
|
153 |
|
154 |
|
155 class BatchLexer(RegexLexer): |
|
156 """ |
|
157 Lexer for the DOS/Windows Batch file format. |
|
158 |
|
159 *New in Pygments 0.7.* |
|
160 """ |
|
161 name = 'Batchfile' |
|
162 aliases = ['bat'] |
|
163 filenames = ['*.bat', '*.cmd'] |
|
164 mimetypes = ['application/x-dos-batch'] |
|
165 |
|
166 flags = re.MULTILINE | re.IGNORECASE |
|
167 |
|
168 tokens = { |
|
169 'root': [ |
|
170 # Lines can start with @ to prevent echo |
|
171 (r'^\s*@', Punctuation), |
|
172 (r'^(\s*)(rem\s.*)$', bygroups(Text, Comment)), |
|
173 (r'".*?"', String.Double), |
|
174 (r"'.*?'", String.Single), |
|
175 # If made more specific, make sure you still allow expansions |
|
176 # like %~$VAR:zlt |
|
177 (r'%%?[~$:\w]+%?', Name.Variable), |
|
178 (r'::.*', Comment), # Technically :: only works at BOL |
|
179 (r'(set)(\s+)(\w+)', bygroups(Keyword, Text, Name.Variable)), |
|
180 (r'(call)(\s+)(:\w+)', bygroups(Keyword, Text, Name.Label)), |
|
181 (r'(goto)(\s+)(\w+)', bygroups(Keyword, Text, Name.Label)), |
|
182 (r'\b(set|call|echo|on|off|endlocal|for|do|goto|if|pause|' |
|
183 r'setlocal|shift|errorlevel|exist|defined|cmdextversion|' |
|
184 r'errorlevel|else|cd|md|del|deltree|cls|choice)\b', Keyword), |
|
185 (r'\b(equ|neq|lss|leq|gtr|geq)\b', Operator), |
|
186 include('basic'), |
|
187 (r'.', Text), |
|
188 ], |
|
189 'echo': [ |
|
190 # Escapes only valid within echo args? |
|
191 (r'\^\^|\^<|\^>|\^\|', String.Escape), |
|
192 (r'\n', Text, '#pop'), |
|
193 include('basic'), |
|
194 (r'[^\'"^]+', Text), |
|
195 ], |
|
196 'basic': [ |
|
197 (r'".*?"', String.Double), |
|
198 (r"'.*?'", String.Single), |
|
199 (r'`.*?`', String.Backtick), |
|
200 (r'-?\d+', Number), |
|
201 (r',', Punctuation), |
|
202 (r'=', Operator), |
|
203 (r'/\S+', Name), |
|
204 (r':\w+', Name.Label), |
|
205 (r'\w:\w+', Text), |
|
206 (r'([<>|])(\s*)(\w+)', bygroups(Punctuation, Text, Name)), |
|
207 ], |
|
208 } |
|
209 |
|
210 |
|
211 class TcshLexer(RegexLexer): |
|
212 """ |
|
213 Lexer for tcsh scripts. |
|
214 |
|
215 *New in Pygments 0.10.* |
|
216 """ |
|
217 |
|
218 name = 'Tcsh' |
|
219 aliases = ['tcsh', 'csh'] |
|
220 filenames = ['*.tcsh', '*.csh'] |
|
221 mimetypes = ['application/x-csh'] |
|
222 |
|
223 tokens = { |
|
224 'root': [ |
|
225 include('basic'), |
|
226 (r'\$\(', Keyword, 'paren'), |
|
227 (r'\${#?', Keyword, 'curly'), |
|
228 (r'`', String.Backtick, 'backticks'), |
|
229 include('data'), |
|
230 ], |
|
231 'basic': [ |
|
232 (r'\b(if|endif|else|while|then|foreach|case|default|' |
|
233 r'continue|goto|breaksw|end|switch|endsw)\s*\b', |
|
234 Keyword), |
|
235 (r'\b(alias|alloc|bg|bindkey|break|builtins|bye|caller|cd|chdir|' |
|
236 r'complete|dirs|echo|echotc|eval|exec|exit|fg|filetest|getxvers|' |
|
237 r'glob|getspath|hashstat|history|hup|inlib|jobs|kill|' |
|
238 r'limit|log|login|logout|ls-F|migrate|newgrp|nice|nohup|notify|' |
|
239 r'onintr|popd|printenv|pushd|rehash|repeat|rootnode|popd|pushd|' |
|
240 r'set|shift|sched|setenv|setpath|settc|setty|setxvers|shift|' |
|
241 r'source|stop|suspend|source|suspend|telltc|time|' |
|
242 r'umask|unalias|uncomplete|unhash|universe|unlimit|unset|unsetenv|' |
|
243 r'ver|wait|warp|watchlog|where|which)\s*\b', |
|
244 Name.Builtin), |
|
245 (r'#.*\n', Comment), |
|
246 (r'\\[\w\W]', String.Escape), |
|
247 (r'(\b\w+)(\s*)(=)', bygroups(Name.Variable, Text, Operator)), |
|
248 (r'[\[\]{}()=]+', Operator), |
|
249 (r'<<\s*(\'?)\\?(\w+)[\w\W]+?\2', String), |
|
250 ], |
|
251 'data': [ |
|
252 (r'(?s)"(\\\\|\\[0-7]+|\\.|[^"\\])*"', String.Double), |
|
253 (r"(?s)'(\\\\|\\[0-7]+|\\.|[^'\\])*'", String.Single), |
|
254 (r'\s+', Text), |
|
255 (r'[^=\s\[\]{}()$"\'`\\]+', Text), |
|
256 (r'\d+(?= |\Z)', Number), |
|
257 (r'\$#?(\w+|.)', Name.Variable), |
|
258 ], |
|
259 'curly': [ |
|
260 (r'}', Keyword, '#pop'), |
|
261 (r':-', Keyword), |
|
262 (r'[a-zA-Z0-9_]+', Name.Variable), |
|
263 (r'[^}:"\'`$]+', Punctuation), |
|
264 (r':', Punctuation), |
|
265 include('root'), |
|
266 ], |
|
267 'paren': [ |
|
268 (r'\)', Keyword, '#pop'), |
|
269 include('root'), |
|
270 ], |
|
271 'backticks': [ |
|
272 (r'`', String.Backtick, '#pop'), |
|
273 include('root'), |
|
274 ], |
|
275 } |
|
276 |
|
277 |
|
278 class PowerShellLexer(RegexLexer): |
|
279 """ |
|
280 For Windows PowerShell code. |
|
281 |
|
282 *New in Pygments 1.5.* |
|
283 """ |
|
284 name = 'PowerShell' |
|
285 aliases = ['powershell', 'posh', 'ps1'] |
|
286 filenames = ['*.ps1'] |
|
287 mimetypes = ['text/x-powershell'] |
|
288 |
|
289 flags = re.DOTALL | re.IGNORECASE | re.MULTILINE |
|
290 |
|
291 keywords = ( |
|
292 'while validateset validaterange validatepattern validatelength ' |
|
293 'validatecount until trap switch return ref process param parameter in ' |
|
294 'if global: function foreach for finally filter end elseif else ' |
|
295 'dynamicparam do default continue cmdletbinding break begin alias \\? ' |
|
296 '% #script #private #local #global mandatory parametersetname position ' |
|
297 'valuefrompipeline valuefrompipelinebypropertyname ' |
|
298 'valuefromremainingarguments helpmessage try catch').split() |
|
299 |
|
300 operators = ( |
|
301 'and as band bnot bor bxor casesensitive ccontains ceq cge cgt cle ' |
|
302 'clike clt cmatch cne cnotcontains cnotlike cnotmatch contains ' |
|
303 'creplace eq exact f file ge gt icontains ieq ige igt ile ilike ilt ' |
|
304 'imatch ine inotcontains inotlike inotmatch ireplace is isnot le like ' |
|
305 'lt match ne not notcontains notlike notmatch or regex replace ' |
|
306 'wildcard').split() |
|
307 |
|
308 verbs = ( |
|
309 'write where wait use update unregister undo trace test tee take ' |
|
310 'suspend stop start split sort skip show set send select scroll resume ' |
|
311 'restore restart resolve resize reset rename remove register receive ' |
|
312 'read push pop ping out new move measure limit join invoke import ' |
|
313 'group get format foreach export expand exit enter enable disconnect ' |
|
314 'disable debug cxnew copy convertto convertfrom convert connect ' |
|
315 'complete compare clear checkpoint aggregate add').split() |
|
316 |
|
317 commenthelp = ( |
|
318 'component description example externalhelp forwardhelpcategory ' |
|
319 'forwardhelptargetname functionality inputs link ' |
|
320 'notes outputs parameter remotehelprunspace role synopsis').split() |
|
321 |
|
322 tokens = { |
|
323 'root': [ |
|
324 (r'\s+', Text), |
|
325 (r'^(\s*#[#\s]*)(\.(?:%s))([^\n]*$)' % '|'.join(commenthelp), |
|
326 bygroups(Comment, String.Doc, Comment)), |
|
327 (r'#[^\n]*?$', Comment), |
|
328 (r'(<|<)#', Comment.Multiline, 'multline'), |
|
329 (r'@"\n.*?\n"@', String.Heredoc), |
|
330 (r"@'\n.*?\n'@", String.Heredoc), |
|
331 (r'"', String.Double, 'string'), |
|
332 (r"'([^']|'')*'", String.Single), |
|
333 (r'(\$|@@|@)((global|script|private|env):)?[a-z0-9_]+', |
|
334 Name.Variable), |
|
335 (r'(%s)\b' % '|'.join(keywords), Keyword), |
|
336 (r'-(%s)\b' % '|'.join(operators), Operator), |
|
337 (r'(%s)-[a-z_][a-z0-9_]*\b' % '|'.join(verbs), Name.Builtin), |
|
338 (r'\[[a-z_\[][a-z0-9_. `,\[\]]*\]', Name.Constant), # .net [type]s |
|
339 (r'-[a-z_][a-z0-9_]*', Name), |
|
340 (r'\w+', Name), |
|
341 (r'[.,{}\[\]$()=+*/\\&%!~?^`|<>-]', Punctuation), |
|
342 ], |
|
343 'multline': [ |
|
344 (r'[^#&.]+', Comment.Multiline), |
|
345 (r'#(>|>)', Comment.Multiline, '#pop'), |
|
346 (r'\.(%s)' % '|'.join(commenthelp), String.Doc), |
|
347 (r'[#&.]', Comment.Multiline), |
|
348 ], |
|
349 'string': [ |
|
350 (r'[^$`"]+', String.Double), |
|
351 (r'\$\(', String.Interpol, 'interpol'), |
|
352 (r'`"|""', String.Double), |
|
353 (r'[`$]', String.Double), |
|
354 (r'"', String.Double, '#pop'), |
|
355 ], |
|
356 'interpol': [ |
|
357 (r'[^$)]+', String.Interpol), |
|
358 (r'\$\(', String.Interpol, '#push'), |
|
359 (r'\)', String.Interpol, '#pop'), |
|
360 ] |
|
361 } |