Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pygments/lexers/configs.py: 88%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

283 statements  

1""" 

2 pygments.lexers.configs 

3 ~~~~~~~~~~~~~~~~~~~~~~~ 

4 

5 Lexers for configuration file formats. 

6 

7 :copyright: Copyright 2006-2025 by the Pygments team, see AUTHORS. 

8 :license: BSD, see LICENSE for details. 

9""" 

10 

11import re 

12 

13from pygments.lexer import ExtendedRegexLexer, RegexLexer, default, words, \ 

14 bygroups, include, using, line_re 

15from pygments.token import Text, Comment, Operator, Keyword, Name, String, \ 

16 Number, Punctuation, Whitespace, Literal, Error, Generic 

17from pygments.lexers.shell import BashLexer 

18from pygments.lexers.data import JsonLexer 

19 

20__all__ = ['IniLexer', 'SystemdLexer', 'DesktopLexer', 'RegeditLexer', 'PropertiesLexer', 

21 'KconfigLexer', 'Cfengine3Lexer', 'ApacheConfLexer', 'SquidConfLexer', 

22 'NginxConfLexer', 'LighttpdConfLexer', 'DockerLexer', 

23 'TerraformLexer', 'TermcapLexer', 'TerminfoLexer', 

24 'PkgConfigLexer', 'PacmanConfLexer', 'AugeasLexer', 'TOMLLexer', 

25 'NestedTextLexer', 'SingularityLexer', 'UnixConfigLexer'] 

26 

27 

28class IniLexer(RegexLexer): 

29 """ 

30 Lexer for configuration files in INI style. 

31 """ 

32 

33 name = 'INI' 

34 aliases = ['ini', 'cfg', 'dosini'] 

35 filenames = [ 

36 '*.ini', '*.cfg', '*.inf', '.editorconfig', 

37 ] 

38 mimetypes = ['text/x-ini', 'text/inf'] 

39 url = 'https://en.wikipedia.org/wiki/INI_file' 

40 version_added = '' 

41 

42 tokens = { 

43 'root': [ 

44 (r'\s+', Whitespace), 

45 (r'[;#].*', Comment.Single), 

46 (r'(\[.*?\])([ \t]*)$', bygroups(Keyword, Whitespace)), 

47 (r'''(.*?)([ \t]*)([=:])([ \t]*)(["'])''', 

48 bygroups(Name.Attribute, Whitespace, Operator, Whitespace, String), 

49 "quoted_value"), 

50 (r'(.*?)([ \t]*)([=:])([ \t]*)([^;#\n]*)(\\)(\s+)', 

51 bygroups(Name.Attribute, Whitespace, Operator, Whitespace, String, 

52 Text, Whitespace), 

53 "value"), 

54 (r'(.*?)([ \t]*)([=:])([ \t]*)([^ ;#\n]*(?: +[^ ;#\n]+)*)', 

55 bygroups(Name.Attribute, Whitespace, Operator, Whitespace, String)), 

56 # standalone option, supported by some INI parsers 

57 (r'(.+?)$', Name.Attribute), 

58 ], 

59 'quoted_value': [ 

60 (r'''([^"'\n]*)(["'])(\s*)''', 

61 bygroups(String, String, Whitespace), "#pop"), 

62 (r'[;#].*', Comment.Single), 

63 (r'$', String, "#pop"), 

64 ], 

65 'value': [ # line continuation 

66 (r'\s+', Whitespace), 

67 (r'(\s*)(.*)(\\)([ \t]*)', 

68 bygroups(Whitespace, String, Text, Whitespace)), 

69 (r'.*$', String, "#pop"), 

70 ], 

71 } 

72 

73 def analyse_text(text): 

74 npos = text.find('\n') 

75 if npos < 3: 

76 return False 

77 if text[0] == '[' and text[npos-1] == ']': 

78 return 0.8 

79 return False 

80 

81 

82class DesktopLexer(RegexLexer): 

83 """ 

84 Lexer for .desktop files. 

85 """ 

86 

87 name = 'Desktop file' 

88 url = "https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html" 

89 aliases = ['desktop'] 

90 filenames = ['*.desktop'] 

91 mimetypes = ['application/x-desktop'] 

92 version_added = '2.16' 

93 

94 tokens = { 

95 'root': [ 

96 (r'^[ \t]*\n', Whitespace), 

97 (r'^(#.*)(\n)', bygroups(Comment.Single, Whitespace)), 

98 (r'(\[[^\]\n]+\])(\n)', bygroups(Keyword, Whitespace)), 

99 (r'([-A-Za-z0-9]+)(\[[^\] \t=]+\])?([ \t]*)(=)([ \t]*)([^\n]*)([ \t\n]*\n)', 

100 bygroups(Name.Attribute, Name.Namespace, Whitespace, Operator, Whitespace, String, Whitespace)), 

101 ], 

102 } 

103 

104 def analyse_text(text): 

105 if text.startswith("[Desktop Entry]"): 

106 return 1.0 

107 if re.search(r"^\[Desktop Entry\][ \t]*$", text[:500], re.MULTILINE) is not None: 

108 return 0.9 

109 return 0.0 

110 

111 

112class SystemdLexer(RegexLexer): 

113 """ 

114 Lexer for systemd unit files. 

115 """ 

116 

117 name = 'Systemd' 

118 url = "https://www.freedesktop.org/software/systemd/man/systemd.syntax.html" 

119 aliases = ['systemd'] 

120 filenames = [ 

121 '*.service', '*.socket', '*.device', '*.mount', '*.automount', 

122 '*.swap', '*.target', '*.path', '*.timer', '*.slice', '*.scope', 

123 ] 

124 version_added = '2.16' 

125 

126 tokens = { 

127 'root': [ 

128 (r'^[ \t]*\n', Whitespace), 

129 (r'^([;#].*)(\n)', bygroups(Comment.Single, Whitespace)), 

130 (r'(\[[^\]\n]+\])(\n)', bygroups(Keyword, Whitespace)), 

131 (r'([^=]+)([ \t]*)(=)([ \t]*)([^\n]*)(\\)(\n)', 

132 bygroups(Name.Attribute, Whitespace, Operator, Whitespace, String, 

133 Text, Whitespace), 

134 "value"), 

135 (r'([^=]+)([ \t]*)(=)([ \t]*)([^\n]*)(\n)', 

136 bygroups(Name.Attribute, Whitespace, Operator, Whitespace, String, Whitespace)), 

137 ], 

138 'value': [ 

139 # line continuation 

140 (r'^([;#].*)(\n)', bygroups(Comment.Single, Whitespace)), 

141 (r'([ \t]*)([^\n]*)(\\)(\n)', 

142 bygroups(Whitespace, String, Text, Whitespace)), 

143 (r'([ \t]*)([^\n]*)(\n)', 

144 bygroups(Whitespace, String, Whitespace), "#pop"), 

145 ], 

146 } 

147 

148 def analyse_text(text): 

149 if text.startswith("[Unit]"): 

150 return 1.0 

151 if re.search(r"^\[Unit\][ \t]*$", text[:500], re.MULTILINE) is not None: 

152 return 0.9 

153 return 0.0 

154 

155 

156class RegeditLexer(RegexLexer): 

157 """ 

158 Lexer for Windows Registry files produced by regedit. 

159 """ 

160 

161 name = 'reg' 

162 url = 'http://en.wikipedia.org/wiki/Windows_Registry#.REG_files' 

163 aliases = ['registry'] 

164 filenames = ['*.reg'] 

165 mimetypes = ['text/x-windows-registry'] 

166 version_added = '1.6' 

167 

168 tokens = { 

169 'root': [ 

170 (r'Windows Registry Editor.*', Text), 

171 (r'\s+', Whitespace), 

172 (r'[;#].*', Comment.Single), 

173 (r'(\[)(-?)(HKEY_[A-Z_]+)(.*?\])$', 

174 bygroups(Keyword, Operator, Name.Builtin, Keyword)), 

175 # String keys, which obey somewhat normal escaping 

176 (r'("(?:\\"|\\\\|[^"])+")([ \t]*)(=)([ \t]*)', 

177 bygroups(Name.Attribute, Whitespace, Operator, Whitespace), 

178 'value'), 

179 # Bare keys (includes @) 

180 (r'(.*?)([ \t]*)(=)([ \t]*)', 

181 bygroups(Name.Attribute, Whitespace, Operator, Whitespace), 

182 'value'), 

183 ], 

184 'value': [ 

185 (r'-', Operator, '#pop'), # delete value 

186 (r'(dword|hex(?:\([0-9a-fA-F]\))?)(:)([0-9a-fA-F,]+)', 

187 bygroups(Name.Variable, Punctuation, Number), '#pop'), 

188 # As far as I know, .reg files do not support line continuation. 

189 (r'.+', String, '#pop'), 

190 default('#pop'), 

191 ] 

192 } 

193 

194 def analyse_text(text): 

195 return text.startswith('Windows Registry Editor') 

196 

197 

198class PropertiesLexer(RegexLexer): 

199 """ 

200 Lexer for configuration files in Java's properties format. 

201 

202 Note: trailing whitespace counts as part of the value as per spec 

203 """ 

204 

205 name = 'Properties' 

206 aliases = ['properties', 'jproperties'] 

207 filenames = ['*.properties'] 

208 mimetypes = ['text/x-java-properties'] 

209 url = 'https://en.wikipedia.org/wiki/.properties' 

210 version_added = '1.4' 

211 

212 tokens = { 

213 'root': [ 

214 # comments 

215 (r'[!#].*|/{2}.*', Comment.Single), 

216 # ending a comment or whitespace-only line 

217 (r'\n', Whitespace), 

218 # eat whitespace at the beginning of a line 

219 (r'^[^\S\n]+', Whitespace), 

220 # start lexing a key 

221 default('key'), 

222 ], 

223 'key': [ 

224 # non-escaped key characters 

225 (r'[^\\:=\s]+', Name.Attribute), 

226 # escapes 

227 include('escapes'), 

228 # separator is the first non-escaped whitespace or colon or '=' on the line; 

229 # if it's whitespace, = and : are gobbled after it 

230 (r'([^\S\n]*)([:=])([^\S\n]*)', 

231 bygroups(Whitespace, Operator, Whitespace), 

232 ('#pop', 'value')), 

233 (r'[^\S\n]+', Whitespace, ('#pop', 'value')), 

234 # maybe we got no value after all 

235 (r'\n', Whitespace, '#pop'), 

236 ], 

237 'value': [ 

238 # non-escaped value characters 

239 (r'[^\\\n]+', String), 

240 # escapes 

241 include('escapes'), 

242 # end the value on an unescaped newline 

243 (r'\n', Whitespace, '#pop'), 

244 ], 

245 'escapes': [ 

246 # line continuations; these gobble whitespace at the beginning of the next line 

247 (r'(\\\n)([^\S\n]*)', bygroups(String.Escape, Whitespace)), 

248 # other escapes 

249 (r'\\(.|\n)', String.Escape), 

250 ], 

251 } 

252 

253 

254def _rx_indent(level): 

255 # Kconfig *always* interprets a tab as 8 spaces, so this is the default. 

256 # Edit this if you are in an environment where KconfigLexer gets expanded 

257 # input (tabs expanded to spaces) and the expansion tab width is != 8, 

258 # e.g. in connection with Trac (trac.ini, [mimeviewer], tab_width). 

259 # Value range here is 2 <= {tab_width} <= 8. 

260 tab_width = 8 

261 # Regex matching a given indentation {level}, assuming that indentation is 

262 # a multiple of {tab_width}. In other cases there might be problems. 

263 if tab_width == 2: 

264 space_repeat = '+' 

265 else: 

266 space_repeat = '{1,%d}' % (tab_width - 1) 

267 if level == 1: 

268 level_repeat = '' 

269 else: 

270 level_repeat = f'{{{level}}}' 

271 return rf'(?:\t| {space_repeat}\t| {{{tab_width}}}){level_repeat}.*\n' 

272 

273 

274class KconfigLexer(RegexLexer): 

275 """ 

276 For Linux-style Kconfig files. 

277 """ 

278 

279 name = 'Kconfig' 

280 aliases = ['kconfig', 'menuconfig', 'linux-config', 'kernel-config'] 

281 version_added = '1.6' 

282 # Adjust this if new kconfig file names appear in your environment 

283 filenames = ['Kconfig*', '*Config.in*', 'external.in*', 

284 'standard-modules.in'] 

285 mimetypes = ['text/x-kconfig'] 

286 url = 'https://www.kernel.org/doc/html/latest/kbuild/kconfig-language.html' 

287 

288 # No re.MULTILINE, indentation-aware help text needs line-by-line handling 

289 flags = 0 

290 

291 def call_indent(level): 

292 # If indentation >= {level} is detected, enter state 'indent{level}' 

293 return (_rx_indent(level), String.Doc, f'indent{level}') 

294 

295 def do_indent(level): 

296 # Print paragraphs of indentation level >= {level} as String.Doc, 

297 # ignoring blank lines. Then return to 'root' state. 

298 return [ 

299 (_rx_indent(level), String.Doc), 

300 (r'\s*\n', Text), 

301 default('#pop:2') 

302 ] 

303 

304 tokens = { 

305 'root': [ 

306 (r'\s+', Whitespace), 

307 (r'#.*?\n', Comment.Single), 

308 (words(( 

309 'mainmenu', 'config', 'menuconfig', 'choice', 'endchoice', 

310 'comment', 'menu', 'endmenu', 'visible if', 'if', 'endif', 

311 'source', 'prompt', 'select', 'depends on', 'default', 

312 'range', 'option'), suffix=r'\b'), 

313 Keyword), 

314 (r'(---help---|help)[\t ]*\n', Keyword, 'help'), 

315 (r'(bool|tristate|string|hex|int|defconfig_list|modules|env)\b', 

316 Name.Builtin), 

317 (r'[!=&|]', Operator), 

318 (r'[()]', Punctuation), 

319 (r'[0-9]+', Number.Integer), 

320 (r"'(''|[^'])*'", String.Single), 

321 (r'"(""|[^"])*"', String.Double), 

322 (r'\S+', Text), 

323 ], 

324 # Help text is indented, multi-line and ends when a lower indentation 

325 # level is detected. 

326 'help': [ 

327 # Skip blank lines after help token, if any 

328 (r'\s*\n', Text), 

329 # Determine the first help line's indentation level heuristically(!). 

330 # Attention: this is not perfect, but works for 99% of "normal" 

331 # indentation schemes up to a max. indentation level of 7. 

332 call_indent(7), 

333 call_indent(6), 

334 call_indent(5), 

335 call_indent(4), 

336 call_indent(3), 

337 call_indent(2), 

338 call_indent(1), 

339 default('#pop'), # for incomplete help sections without text 

340 ], 

341 # Handle text for indentation levels 7 to 1 

342 'indent7': do_indent(7), 

343 'indent6': do_indent(6), 

344 'indent5': do_indent(5), 

345 'indent4': do_indent(4), 

346 'indent3': do_indent(3), 

347 'indent2': do_indent(2), 

348 'indent1': do_indent(1), 

349 } 

350 

351 

352class Cfengine3Lexer(RegexLexer): 

353 """ 

354 Lexer for CFEngine3 policy files. 

355 """ 

356 

357 name = 'CFEngine3' 

358 url = 'http://cfengine.org' 

359 aliases = ['cfengine3', 'cf3'] 

360 filenames = ['*.cf'] 

361 mimetypes = [] 

362 version_added = '1.5' 

363 

364 tokens = { 

365 'root': [ 

366 (r'#.*?\n', Comment), 

367 (r'(body)(\s+)(\S+)(\s+)(control)', 

368 bygroups(Keyword, Whitespace, Keyword, Whitespace, Keyword)), 

369 (r'(body|bundle)(\s+)(\S+)(\s+)(\w+)(\()', 

370 bygroups(Keyword, Whitespace, Keyword, Whitespace, Name.Function, Punctuation), 

371 'arglist'), 

372 (r'(body|bundle)(\s+)(\S+)(\s+)(\w+)', 

373 bygroups(Keyword, Whitespace, Keyword, Whitespace, Name.Function)), 

374 (r'(")([^"]+)(")(\s+)(string|slist|int|real)(\s*)(=>)(\s*)', 

375 bygroups(Punctuation, Name.Variable, Punctuation, 

376 Whitespace, Keyword.Type, Whitespace, Operator, Whitespace)), 

377 (r'(\S+)(\s*)(=>)(\s*)', 

378 bygroups(Keyword.Reserved, Whitespace, Operator, Text)), 

379 (r'"', String, 'string'), 

380 (r'(\w+)(\()', bygroups(Name.Function, Punctuation)), 

381 (r'([\w.!&|()]+)(::)', bygroups(Name.Class, Punctuation)), 

382 (r'(\w+)(:)', bygroups(Keyword.Declaration, Punctuation)), 

383 (r'@[{(][^)}]+[})]', Name.Variable), 

384 (r'[(){},;]', Punctuation), 

385 (r'=>', Operator), 

386 (r'->', Operator), 

387 (r'\d+\.\d+', Number.Float), 

388 (r'\d+', Number.Integer), 

389 (r'\w+', Name.Function), 

390 (r'\s+', Whitespace), 

391 ], 

392 'string': [ 

393 (r'\$[{(]', String.Interpol, 'interpol'), 

394 (r'\\.', String.Escape), 

395 (r'"', String, '#pop'), 

396 (r'\n', String), 

397 (r'.', String), 

398 ], 

399 'interpol': [ 

400 (r'\$[{(]', String.Interpol, '#push'), 

401 (r'[})]', String.Interpol, '#pop'), 

402 (r'[^${()}]+', String.Interpol), 

403 ], 

404 'arglist': [ 

405 (r'\)', Punctuation, '#pop'), 

406 (r',', Punctuation), 

407 (r'\w+', Name.Variable), 

408 (r'\s+', Whitespace), 

409 ], 

410 } 

411 

412 

413class ApacheConfLexer(RegexLexer): 

414 """ 

415 Lexer for configuration files following the Apache config file 

416 format. 

417 """ 

418 

419 name = 'ApacheConf' 

420 aliases = ['apacheconf', 'aconf', 'apache'] 

421 filenames = ['.htaccess', 'apache.conf', 'apache2.conf'] 

422 mimetypes = ['text/x-apacheconf'] 

423 url = 'https://httpd.apache.org/docs/current/configuring.html' 

424 version_added = '0.6' 

425 flags = re.MULTILINE | re.IGNORECASE 

426 

427 tokens = { 

428 'root': [ 

429 (r'\s+', Whitespace), 

430 (r'#(.*\\\n)+.*$|(#.*?)$', Comment), 

431 (r'(<[^\s>/][^\s>]*)(?:(\s+)(.*))?(>)', 

432 bygroups(Name.Tag, Whitespace, String, Name.Tag)), 

433 (r'(</[^\s>]+)(>)', 

434 bygroups(Name.Tag, Name.Tag)), 

435 (r'[a-z]\w*', Name.Builtin, 'value'), 

436 (r'\.+', Text), 

437 ], 

438 'value': [ 

439 (r'\\\n', Text), 

440 (r'\n+', Whitespace, '#pop'), 

441 (r'\\', Text), 

442 (r'[^\S\n]+', Whitespace), 

443 (r'\d+\.\d+\.\d+\.\d+(?:/\d+)?', Number), 

444 (r'\d+', Number), 

445 (r'/([*a-z0-9][*\w./-]+)', String.Other), 

446 (r'(on|off|none|any|all|double|email|dns|min|minimal|' 

447 r'os|productonly|full|emerg|alert|crit|error|warn|' 

448 r'notice|info|debug|registry|script|inetd|standalone|' 

449 r'user|group)\b', Keyword), 

450 (r'"([^"\\]*(?:\\(.|\n)[^"\\]*)*)"', String.Double), 

451 (r'[^\s"\\]+', Text) 

452 ], 

453 } 

454 

455 

456class SquidConfLexer(RegexLexer): 

457 """ 

458 Lexer for squid configuration files. 

459 """ 

460 

461 name = 'SquidConf' 

462 url = 'http://www.squid-cache.org/' 

463 aliases = ['squidconf', 'squid.conf', 'squid'] 

464 filenames = ['squid.conf'] 

465 mimetypes = ['text/x-squidconf'] 

466 version_added = '0.9' 

467 flags = re.IGNORECASE 

468 

469 keywords = ( 

470 "access_log", "acl", "always_direct", "announce_host", 

471 "announce_period", "announce_port", "announce_to", "anonymize_headers", 

472 "append_domain", "as_whois_server", "auth_param_basic", 

473 "authenticate_children", "authenticate_program", "authenticate_ttl", 

474 "broken_posts", "buffered_logs", "cache_access_log", "cache_announce", 

475 "cache_dir", "cache_dns_program", "cache_effective_group", 

476 "cache_effective_user", "cache_host", "cache_host_acl", 

477 "cache_host_domain", "cache_log", "cache_mem", "cache_mem_high", 

478 "cache_mem_low", "cache_mgr", "cachemgr_passwd", "cache_peer", 

479 "cache_peer_access", "cache_replacement_policy", "cache_stoplist", 

480 "cache_stoplist_pattern", "cache_store_log", "cache_swap", 

481 "cache_swap_high", "cache_swap_log", "cache_swap_low", "client_db", 

482 "client_lifetime", "client_netmask", "connect_timeout", "coredump_dir", 

483 "dead_peer_timeout", "debug_options", "delay_access", "delay_class", 

484 "delay_initial_bucket_level", "delay_parameters", "delay_pools", 

485 "deny_info", "dns_children", "dns_defnames", "dns_nameservers", 

486 "dns_testnames", "emulate_httpd_log", "err_html_text", 

487 "fake_user_agent", "firewall_ip", "forwarded_for", "forward_snmpd_port", 

488 "fqdncache_size", "ftpget_options", "ftpget_program", "ftp_list_width", 

489 "ftp_passive", "ftp_user", "half_closed_clients", "header_access", 

490 "header_replace", "hierarchy_stoplist", "high_response_time_warning", 

491 "high_page_fault_warning", "hosts_file", "htcp_port", "http_access", 

492 "http_anonymizer", "httpd_accel", "httpd_accel_host", 

493 "httpd_accel_port", "httpd_accel_uses_host_header", 

494 "httpd_accel_with_proxy", "http_port", "http_reply_access", 

495 "icp_access", "icp_hit_stale", "icp_port", "icp_query_timeout", 

496 "ident_lookup", "ident_lookup_access", "ident_timeout", 

497 "incoming_http_average", "incoming_icp_average", "inside_firewall", 

498 "ipcache_high", "ipcache_low", "ipcache_size", "local_domain", 

499 "local_ip", "logfile_rotate", "log_fqdn", "log_icp_queries", 

500 "log_mime_hdrs", "maximum_object_size", "maximum_single_addr_tries", 

501 "mcast_groups", "mcast_icp_query_timeout", "mcast_miss_addr", 

502 "mcast_miss_encode_key", "mcast_miss_port", "memory_pools", 

503 "memory_pools_limit", "memory_replacement_policy", "mime_table", 

504 "min_http_poll_cnt", "min_icp_poll_cnt", "minimum_direct_hops", 

505 "minimum_object_size", "minimum_retry_timeout", "miss_access", 

506 "negative_dns_ttl", "negative_ttl", "neighbor_timeout", 

507 "neighbor_type_domain", "netdb_high", "netdb_low", "netdb_ping_period", 

508 "netdb_ping_rate", "never_direct", "no_cache", "passthrough_proxy", 

509 "pconn_timeout", "pid_filename", "pinger_program", "positive_dns_ttl", 

510 "prefer_direct", "proxy_auth", "proxy_auth_realm", "query_icmp", 

511 "quick_abort", "quick_abort_max", "quick_abort_min", 

512 "quick_abort_pct", "range_offset_limit", "read_timeout", 

513 "redirect_children", "redirect_program", 

514 "redirect_rewrites_host_header", "reference_age", 

515 "refresh_pattern", "reload_into_ims", "request_body_max_size", 

516 "request_size", "request_timeout", "shutdown_lifetime", 

517 "single_parent_bypass", "siteselect_timeout", "snmp_access", 

518 "snmp_incoming_address", "snmp_port", "source_ping", "ssl_proxy", 

519 "store_avg_object_size", "store_objects_per_bucket", 

520 "strip_query_terms", "swap_level1_dirs", "swap_level2_dirs", 

521 "tcp_incoming_address", "tcp_outgoing_address", "tcp_recv_bufsize", 

522 "test_reachability", "udp_hit_obj", "udp_hit_obj_size", 

523 "udp_incoming_address", "udp_outgoing_address", "unique_hostname", 

524 "unlinkd_program", "uri_whitespace", "useragent_log", 

525 "visible_hostname", "wais_relay", "wais_relay_host", "wais_relay_port", 

526 ) 

527 

528 opts = ( 

529 "proxy-only", "weight", "ttl", "no-query", "default", "round-robin", 

530 "multicast-responder", "on", "off", "all", "deny", "allow", "via", 

531 "parent", "no-digest", "heap", "lru", "realm", "children", "q1", "q2", 

532 "credentialsttl", "none", "disable", "offline_toggle", "diskd", 

533 ) 

534 

535 actions = ( 

536 "shutdown", "info", "parameter", "server_list", "client_list", 

537 r'squid.conf', 

538 ) 

539 

540 actions_stats = ( 

541 "objects", "vm_objects", "utilization", "ipcache", "fqdncache", "dns", 

542 "redirector", "io", "reply_headers", "filedescriptors", "netdb", 

543 ) 

544 

545 actions_log = ("status", "enable", "disable", "clear") 

546 

547 acls = ( 

548 "url_regex", "urlpath_regex", "referer_regex", "port", "proto", 

549 "req_mime_type", "rep_mime_type", "method", "browser", "user", "src", 

550 "dst", "time", "dstdomain", "ident", "snmp_community", 

551 ) 

552 

553 ipv4_group = r'(\d+|0x[0-9a-f]+)' 

554 ipv4 = rf'({ipv4_group}(\.{ipv4_group}){{3}})' 

555 ipv6_group = r'([0-9a-f]{0,4})' 

556 ipv6 = rf'({ipv6_group}(:{ipv6_group}){{1,7}})' 

557 bare_ip = rf'({ipv4}|{ipv6})' 

558 # XXX: /integer is a subnet mark, but what is /IP ? 

559 # There is no test where it is used. 

560 ip = rf'{bare_ip}(/({bare_ip}|\d+))?' 

561 

562 tokens = { 

563 'root': [ 

564 (r'\s+', Whitespace), 

565 (r'#', Comment, 'comment'), 

566 (words(keywords, prefix=r'\b', suffix=r'\b'), Keyword), 

567 (words(opts, prefix=r'\b', suffix=r'\b'), Name.Constant), 

568 # Actions 

569 (words(actions, prefix=r'\b', suffix=r'\b'), String), 

570 (words(actions_stats, prefix=r'stats/', suffix=r'\b'), String), 

571 (words(actions_log, prefix=r'log/', suffix=r'='), String), 

572 (words(acls, prefix=r'\b', suffix=r'\b'), Keyword), 

573 (ip, Number.Float), 

574 (r'(?:\b\d+\b(?:-\b\d+|%)?)', Number), 

575 (r'\S+', Text), 

576 ], 

577 'comment': [ 

578 (r'\s*TAG:.*', String.Escape, '#pop'), 

579 (r'.+', Comment, '#pop'), 

580 default('#pop'), 

581 ], 

582 } 

583 

584 

585class NginxConfLexer(RegexLexer): 

586 """ 

587 Lexer for Nginx configuration files. 

588 """ 

589 name = 'Nginx configuration file' 

590 url = 'http://nginx.net/' 

591 aliases = ['nginx'] 

592 filenames = ['nginx.conf'] 

593 mimetypes = ['text/x-nginx-conf'] 

594 version_added = '0.11' 

595 

596 tokens = { 

597 'root': [ 

598 (r'(include)(\s+)([^\s;]+)', bygroups(Keyword, Whitespace, Name)), 

599 (r'[^\s;#]+', Keyword, 'stmt'), 

600 include('base'), 

601 ], 

602 'block': [ 

603 (r'\}', Punctuation, '#pop:2'), 

604 (r'[^\s;#]+', Keyword.Namespace, 'stmt'), 

605 include('base'), 

606 ], 

607 'stmt': [ 

608 (r'\{', Punctuation, 'block'), 

609 (r';', Punctuation, '#pop'), 

610 include('base'), 

611 ], 

612 'base': [ 

613 (r'#.*\n', Comment.Single), 

614 (r'on|off', Name.Constant), 

615 (r'\$[^\s;#()]+', Name.Variable), 

616 (r'([a-z0-9.-]+)(:)([0-9]+)', 

617 bygroups(Name, Punctuation, Number.Integer)), 

618 (r'[a-z-]+/[a-z-+]+', String), # mimetype 

619 # (r'[a-zA-Z._-]+', Keyword), 

620 (r'[0-9]+[km]?\b', Number.Integer), 

621 (r'(~)(\s*)([^\s{]+)', bygroups(Punctuation, Whitespace, String.Regex)), 

622 (r'[:=~]', Punctuation), 

623 (r'[^\s;#{}$]+', String), # catch all 

624 (r'/[^\s;#]*', Name), # pathname 

625 (r'\s+', Whitespace), 

626 (r'[$;]', Text), # leftover characters 

627 ], 

628 } 

629 

630 

631class LighttpdConfLexer(RegexLexer): 

632 """ 

633 Lexer for Lighttpd configuration files. 

634 """ 

635 name = 'Lighttpd configuration file' 

636 url = 'http://lighttpd.net/' 

637 aliases = ['lighttpd', 'lighty'] 

638 filenames = ['lighttpd.conf'] 

639 mimetypes = ['text/x-lighttpd-conf'] 

640 version_added = '0.11' 

641 

642 tokens = { 

643 'root': [ 

644 (r'#.*\n', Comment.Single), 

645 (r'/\S*', Name), # pathname 

646 (r'[a-zA-Z._-]+', Keyword), 

647 (r'\d+\.\d+\.\d+\.\d+(?:/\d+)?', Number), 

648 (r'[0-9]+', Number), 

649 (r'=>|=~|\+=|==|=|\+', Operator), 

650 (r'\$[A-Z]+', Name.Builtin), 

651 (r'[(){}\[\],]', Punctuation), 

652 (r'"([^"\\]*(?:\\.[^"\\]*)*)"', String.Double), 

653 (r'\s+', Whitespace), 

654 ], 

655 

656 } 

657 

658 

659class DockerLexer(RegexLexer): 

660 """ 

661 Lexer for Docker configuration files. 

662 """ 

663 name = 'Docker' 

664 url = 'http://docker.io' 

665 aliases = ['docker', 'dockerfile'] 

666 filenames = ['Dockerfile', '*.docker'] 

667 mimetypes = ['text/x-dockerfile-config'] 

668 version_added = '2.0' 

669 

670 _keywords = (r'(?:MAINTAINER|EXPOSE|WORKDIR|USER|STOPSIGNAL)') 

671 _bash_keywords = (r'(?:RUN|CMD|ENTRYPOINT|ENV|ARG|LABEL|ADD|COPY)') 

672 _lb = r'(?:\s*\\?\s*)' # dockerfile line break regex 

673 flags = re.IGNORECASE | re.MULTILINE 

674 

675 tokens = { 

676 'root': [ 

677 (r'#.*', Comment), 

678 (r'(FROM)([ \t]*)(\S*)([ \t]*)(?:(AS)([ \t]*)(\S*))?', 

679 bygroups(Keyword, Whitespace, String, Whitespace, Keyword, Whitespace, String)), 

680 (rf'(ONBUILD)(\s+)({_lb})', bygroups(Keyword, Whitespace, using(BashLexer))), 

681 (rf'(HEALTHCHECK)(\s+)(({_lb}--\w+=\w+{_lb})*)', 

682 bygroups(Keyword, Whitespace, using(BashLexer))), 

683 (rf'(VOLUME|ENTRYPOINT|CMD|SHELL)(\s+)({_lb})(\[.*?\])', 

684 bygroups(Keyword, Whitespace, using(BashLexer), using(JsonLexer))), 

685 (rf'(LABEL|ENV|ARG)(\s+)(({_lb}\w+=\w+{_lb})*)', 

686 bygroups(Keyword, Whitespace, using(BashLexer))), 

687 (rf'({_keywords}|VOLUME)\b(\s+)(.*)', bygroups(Keyword, Whitespace, String)), 

688 (rf'({_bash_keywords})(\s+)', bygroups(Keyword, Whitespace)), 

689 (r'(.*\\\n)*.+', using(BashLexer)), 

690 ] 

691 } 

692 

693 

694class TerraformLexer(ExtendedRegexLexer): 

695 """ 

696 Lexer for terraformi ``.tf`` files. 

697 """ 

698 

699 name = 'Terraform' 

700 url = 'https://www.terraform.io/' 

701 aliases = ['terraform', 'tf', 'hcl'] 

702 filenames = ['*.tf', '*.hcl'] 

703 mimetypes = ['application/x-tf', 'application/x-terraform'] 

704 version_added = '2.1' 

705 

706 classes = ('backend', 'data', 'module', 'output', 'provider', 

707 'provisioner', 'resource', 'variable') 

708 classes_re = "({})".format(('|').join(classes)) 

709 

710 types = ('string', 'number', 'bool', 'list', 'tuple', 'map', 'set', 'object', 'null') 

711 

712 numeric_functions = ('abs', 'ceil', 'floor', 'log', 'max', 

713 'mix', 'parseint', 'pow', 'signum') 

714 

715 string_functions = ('chomp', 'format', 'formatlist', 'indent', 

716 'join', 'lower', 'regex', 'regexall', 'replace', 

717 'split', 'strrev', 'substr', 'title', 'trim', 

718 'trimprefix', 'trimsuffix', 'trimspace', 'upper' 

719 ) 

720 

721 collection_functions = ('alltrue', 'anytrue', 'chunklist', 'coalesce', 

722 'coalescelist', 'compact', 'concat', 'contains', 

723 'distinct', 'element', 'flatten', 'index', 'keys', 

724 'length', 'list', 'lookup', 'map', 'matchkeys', 

725 'merge', 'range', 'reverse', 'setintersection', 

726 'setproduct', 'setsubtract', 'setunion', 'slice', 

727 'sort', 'sum', 'transpose', 'values', 'zipmap' 

728 ) 

729 

730 encoding_functions = ('base64decode', 'base64encode', 'base64gzip', 

731 'csvdecode', 'jsondecode', 'jsonencode', 'textdecodebase64', 

732 'textencodebase64', 'urlencode', 'yamldecode', 'yamlencode') 

733 

734 filesystem_functions = ('abspath', 'dirname', 'pathexpand', 'basename', 

735 'file', 'fileexists', 'fileset', 'filebase64', 'templatefile') 

736 

737 date_time_functions = ('formatdate', 'timeadd', 'timestamp') 

738 

739 hash_crypto_functions = ('base64sha256', 'base64sha512', 'bcrypt', 'filebase64sha256', 

740 'filebase64sha512', 'filemd5', 'filesha1', 'filesha256', 'filesha512', 

741 'md5', 'rsadecrypt', 'sha1', 'sha256', 'sha512', 'uuid', 'uuidv5') 

742 

743 ip_network_functions = ('cidrhost', 'cidrnetmask', 'cidrsubnet', 'cidrsubnets') 

744 

745 type_conversion_functions = ('can', 'defaults', 'tobool', 'tolist', 'tomap', 

746 'tonumber', 'toset', 'tostring', 'try') 

747 

748 builtins = numeric_functions + string_functions + collection_functions + encoding_functions +\ 

749 filesystem_functions + date_time_functions + hash_crypto_functions + ip_network_functions +\ 

750 type_conversion_functions 

751 builtins_re = "({})".format(('|').join(builtins)) 

752 

753 def heredoc_callback(self, match, ctx): 

754 # Parse a terraform heredoc 

755 # match: 1 = <<[-]?, 2 = name 3 = rest of line 

756 

757 start = match.start(1) 

758 yield start, Operator, match.group(1) # <<[-]? 

759 yield match.start(2), String.Delimiter, match.group(2) # heredoc name 

760 

761 ctx.pos = match.start(3) 

762 ctx.end = match.end(3) 

763 yield ctx.pos, String.Heredoc, match.group(3) 

764 ctx.pos = match.end() 

765 

766 hdname = match.group(2) 

767 tolerant = True # leading whitespace is always accepted 

768 

769 lines = [] 

770 

771 for match in line_re.finditer(ctx.text, ctx.pos): 

772 if tolerant: 

773 check = match.group().strip() 

774 else: 

775 check = match.group().rstrip() 

776 if check == hdname: 

777 for amatch in lines: 

778 yield amatch.start(), String.Heredoc, amatch.group() 

779 yield match.start(), String.Delimiter, match.group() 

780 ctx.pos = match.end() 

781 break 

782 else: 

783 lines.append(match) 

784 else: 

785 # end of heredoc not found -- error! 

786 for amatch in lines: 

787 yield amatch.start(), Error, amatch.group() 

788 ctx.end = len(ctx.text) 

789 

790 tokens = { 

791 'root': [ 

792 include('basic'), 

793 include('whitespace'), 

794 

795 # Strings 

796 (r'(".*")', bygroups(String.Double)), 

797 

798 # Constants 

799 (words(('true', 'false'), prefix=r'\b', suffix=r'\b'), Name.Constant), 

800 

801 # Types 

802 (words(types, prefix=r'\b', suffix=r'\b'), Keyword.Type), 

803 

804 include('identifier'), 

805 include('punctuation'), 

806 (r'[0-9]+', Number), 

807 ], 

808 'basic': [ 

809 (r'\s*/\*', Comment.Multiline, 'comment'), 

810 (r'\s*(#|//).*\n', Comment.Single), 

811 include('whitespace'), 

812 

813 # e.g. terraform { 

814 # e.g. egress { 

815 (r'(\s*)([0-9a-zA-Z-_]+)(\s*)(=?)(\s*)(\{)', 

816 bygroups(Whitespace, Name.Builtin, Whitespace, Operator, Whitespace, Punctuation)), 

817 

818 # Assignment with attributes, e.g. something = ... 

819 (r'(\s*)([0-9a-zA-Z-_]+)(\s*)(=)(\s*)', 

820 bygroups(Whitespace, Name.Attribute, Whitespace, Operator, Whitespace)), 

821 

822 # Assignment with environment variables and similar, e.g. "something" = ... 

823 # or key value assignment, e.g. "SlotName" : ... 

824 (r'(\s*)("\S+")(\s*)([=:])(\s*)', 

825 bygroups(Whitespace, Literal.String.Double, Whitespace, Operator, Whitespace)), 

826 

827 # Functions, e.g. jsonencode(element("value")) 

828 (builtins_re + r'(\()', bygroups(Name.Function, Punctuation)), 

829 

830 # List of attributes, e.g. ignore_changes = [last_modified, filename] 

831 (r'(\[)([a-z_,\s]+)(\])', bygroups(Punctuation, Name.Builtin, Punctuation)), 

832 

833 # e.g. resource "aws_security_group" "allow_tls" { 

834 # e.g. backend "consul" { 

835 (classes_re + r'(\s+)("[0-9a-zA-Z-_]+")?(\s*)("[0-9a-zA-Z-_]+")(\s+)(\{)', 

836 bygroups(Keyword.Reserved, Whitespace, Name.Class, Whitespace, Name.Variable, Whitespace, Punctuation)), 

837 

838 # here-doc style delimited strings 

839 (r'(<<-?)\s*([a-zA-Z_]\w*)(.*?\n)', heredoc_callback), 

840 ], 

841 'identifier': [ 

842 (r'\b(var\.[0-9a-zA-Z-_\.\[\]]+)\b', bygroups(Name.Variable)), 

843 (r'\b([0-9a-zA-Z-_\[\]]+\.[0-9a-zA-Z-_\.\[\]]+)\b', 

844 bygroups(Name.Variable)), 

845 ], 

846 'punctuation': [ 

847 (r'[\[\]()\{\},.?:!=]', Punctuation), 

848 ], 

849 'comment': [ 

850 (r'[^*/]', Comment.Multiline), 

851 (r'/\*', Comment.Multiline, '#push'), 

852 (r'\*/', Comment.Multiline, '#pop'), 

853 (r'[*/]', Comment.Multiline) 

854 ], 

855 'whitespace': [ 

856 (r'\n', Whitespace), 

857 (r'\s+', Whitespace), 

858 (r'(\\)(\n)', bygroups(Text, Whitespace)), 

859 ], 

860 } 

861 

862 

863class TermcapLexer(RegexLexer): 

864 """ 

865 Lexer for termcap database source. 

866 

867 This is very simple and minimal. 

868 """ 

869 name = 'Termcap' 

870 aliases = ['termcap'] 

871 filenames = ['termcap', 'termcap.src'] 

872 mimetypes = [] 

873 url = 'https://en.wikipedia.org/wiki/Termcap' 

874 version_added = '2.1' 

875 

876 # NOTE: 

877 # * multiline with trailing backslash 

878 # * separator is ':' 

879 # * to embed colon as data, we must use \072 

880 # * space after separator is not allowed (mayve) 

881 tokens = { 

882 'root': [ 

883 (r'^#.*', Comment), 

884 (r'^[^\s#:|]+', Name.Tag, 'names'), 

885 (r'\s+', Whitespace), 

886 ], 

887 'names': [ 

888 (r'\n', Whitespace, '#pop'), 

889 (r':', Punctuation, 'defs'), 

890 (r'\|', Punctuation), 

891 (r'[^:|]+', Name.Attribute), 

892 ], 

893 'defs': [ 

894 (r'(\\)(\n[ \t]*)', bygroups(Text, Whitespace)), 

895 (r'\n[ \t]*', Whitespace, '#pop:2'), 

896 (r'(#)([0-9]+)', bygroups(Operator, Number)), 

897 (r'=', Operator, 'data'), 

898 (r':', Punctuation), 

899 (r'[^\s:=#]+', Name.Class), 

900 ], 

901 'data': [ 

902 (r'\\072', Literal), 

903 (r':', Punctuation, '#pop'), 

904 (r'[^:\\]+', Literal), # for performance 

905 (r'.', Literal), 

906 ], 

907 } 

908 

909 

910class TerminfoLexer(RegexLexer): 

911 """ 

912 Lexer for terminfo database source. 

913 

914 This is very simple and minimal. 

915 """ 

916 name = 'Terminfo' 

917 aliases = ['terminfo'] 

918 filenames = ['terminfo', 'terminfo.src'] 

919 mimetypes = [] 

920 url = 'https://en.wikipedia.org/wiki/Terminfo' 

921 version_added = '2.1' 

922 

923 # NOTE: 

924 # * multiline with leading whitespace 

925 # * separator is ',' 

926 # * to embed comma as data, we can use \, 

927 # * space after separator is allowed 

928 tokens = { 

929 'root': [ 

930 (r'^#.*$', Comment), 

931 (r'^[^\s#,|]+', Name.Tag, 'names'), 

932 (r'\s+', Whitespace), 

933 ], 

934 'names': [ 

935 (r'\n', Whitespace, '#pop'), 

936 (r'(,)([ \t]*)', bygroups(Punctuation, Whitespace), 'defs'), 

937 (r'\|', Punctuation), 

938 (r'[^,|]+', Name.Attribute), 

939 ], 

940 'defs': [ 

941 (r'\n[ \t]+', Whitespace), 

942 (r'\n', Whitespace, '#pop:2'), 

943 (r'(#)([0-9]+)', bygroups(Operator, Number)), 

944 (r'=', Operator, 'data'), 

945 (r'(,)([ \t]*)', bygroups(Punctuation, Whitespace)), 

946 (r'[^\s,=#]+', Name.Class), 

947 ], 

948 'data': [ 

949 (r'\\[,\\]', Literal), 

950 (r'(,)([ \t]*)', bygroups(Punctuation, Whitespace), '#pop'), 

951 (r'[^\\,]+', Literal), # for performance 

952 (r'.', Literal), 

953 ], 

954 } 

955 

956 

957class PkgConfigLexer(RegexLexer): 

958 """ 

959 Lexer for pkg-config 

960 (see also `manual page <http://linux.die.net/man/1/pkg-config>`_). 

961 """ 

962 

963 name = 'PkgConfig' 

964 url = 'http://www.freedesktop.org/wiki/Software/pkg-config/' 

965 aliases = ['pkgconfig'] 

966 filenames = ['*.pc'] 

967 mimetypes = [] 

968 version_added = '2.1' 

969 

970 tokens = { 

971 'root': [ 

972 (r'#.*$', Comment.Single), 

973 

974 # variable definitions 

975 (r'^(\w+)(=)', bygroups(Name.Attribute, Operator)), 

976 

977 # keyword lines 

978 (r'^([\w.]+)(:)', 

979 bygroups(Name.Tag, Punctuation), 'spvalue'), 

980 

981 # variable references 

982 include('interp'), 

983 

984 # fallback 

985 (r'\s+', Whitespace), 

986 (r'[^${}#=:\n.]+', Text), 

987 (r'.', Text), 

988 ], 

989 'interp': [ 

990 # you can escape literal "$" as "$$" 

991 (r'\$\$', Text), 

992 

993 # variable references 

994 (r'\$\{', String.Interpol, 'curly'), 

995 ], 

996 'curly': [ 

997 (r'\}', String.Interpol, '#pop'), 

998 (r'\w+', Name.Attribute), 

999 ], 

1000 'spvalue': [ 

1001 include('interp'), 

1002 

1003 (r'#.*$', Comment.Single, '#pop'), 

1004 (r'\n', Whitespace, '#pop'), 

1005 

1006 # fallback 

1007 (r'\s+', Whitespace), 

1008 (r'[^${}#\n\s]+', Text), 

1009 (r'.', Text), 

1010 ], 

1011 } 

1012 

1013 

1014class PacmanConfLexer(RegexLexer): 

1015 """ 

1016 Lexer for pacman.conf. 

1017 

1018 Actually, IniLexer works almost fine for this format, 

1019 but it yield error token. It is because pacman.conf has 

1020 a form without assignment like: 

1021 

1022 UseSyslog 

1023 Color 

1024 TotalDownload 

1025 CheckSpace 

1026 VerbosePkgLists 

1027 

1028 These are flags to switch on. 

1029 """ 

1030 

1031 name = 'PacmanConf' 

1032 url = 'https://www.archlinux.org/pacman/pacman.conf.5.html' 

1033 aliases = ['pacmanconf'] 

1034 filenames = ['pacman.conf'] 

1035 mimetypes = [] 

1036 version_added = '2.1' 

1037 

1038 tokens = { 

1039 'root': [ 

1040 # comment 

1041 (r'#.*$', Comment.Single), 

1042 

1043 # section header 

1044 (r'^(\s*)(\[.*?\])(\s*)$', bygroups(Whitespace, Keyword, Whitespace)), 

1045 

1046 # variable definitions 

1047 # (Leading space is allowed...) 

1048 (r'(\w+)(\s*)(=)', 

1049 bygroups(Name.Attribute, Whitespace, Operator)), 

1050 

1051 # flags to on 

1052 (r'^(\s*)(\w+)(\s*)$', 

1053 bygroups(Whitespace, Name.Attribute, Whitespace)), 

1054 

1055 # built-in special values 

1056 (words(( 

1057 '$repo', # repository 

1058 '$arch', # architecture 

1059 '%o', # outfile 

1060 '%u', # url 

1061 ), suffix=r'\b'), 

1062 Name.Variable), 

1063 

1064 # fallback 

1065 (r'\s+', Whitespace), 

1066 (r'.', Text), 

1067 ], 

1068 } 

1069 

1070 

1071class AugeasLexer(RegexLexer): 

1072 """ 

1073 Lexer for Augeas. 

1074 """ 

1075 name = 'Augeas' 

1076 url = 'http://augeas.net' 

1077 aliases = ['augeas'] 

1078 filenames = ['*.aug'] 

1079 version_added = '2.4' 

1080 

1081 tokens = { 

1082 'root': [ 

1083 (r'(module)(\s*)([^\s=]+)', bygroups(Keyword.Namespace, Whitespace, Name.Namespace)), 

1084 (r'(let)(\s*)([^\s=]+)', bygroups(Keyword.Declaration, Whitespace, Name.Variable)), 

1085 (r'(del|store|value|counter|seq|key|label|autoload|incl|excl|transform|test|get|put)(\s+)', bygroups(Name.Builtin, Whitespace)), 

1086 (r'(\()([^:]+)(\:)(unit|string|regexp|lens|tree|filter)(\))', bygroups(Punctuation, Name.Variable, Punctuation, Keyword.Type, Punctuation)), 

1087 (r'\(\*', Comment.Multiline, 'comment'), 

1088 (r'[*+\-.;=?|]', Operator), 

1089 (r'[()\[\]{}]', Operator), 

1090 (r'"', String.Double, 'string'), 

1091 (r'\/', String.Regex, 'regex'), 

1092 (r'([A-Z]\w*)(\.)(\w+)', bygroups(Name.Namespace, Punctuation, Name.Variable)), 

1093 (r'.', Name.Variable), 

1094 (r'\s+', Whitespace), 

1095 ], 

1096 'string': [ 

1097 (r'\\.', String.Escape), 

1098 (r'[^"]', String.Double), 

1099 (r'"', String.Double, '#pop'), 

1100 ], 

1101 'regex': [ 

1102 (r'\\.', String.Escape), 

1103 (r'[^/]', String.Regex), 

1104 (r'\/', String.Regex, '#pop'), 

1105 ], 

1106 'comment': [ 

1107 (r'[^*)]', Comment.Multiline), 

1108 (r'\(\*', Comment.Multiline, '#push'), 

1109 (r'\*\)', Comment.Multiline, '#pop'), 

1110 (r'[)*]', Comment.Multiline) 

1111 ], 

1112 } 

1113 

1114 

1115class TOMLLexer(RegexLexer): 

1116 """ 

1117 Lexer for TOML, a simple language for config files. 

1118 """ 

1119 

1120 name = 'TOML' 

1121 aliases = ['toml'] 

1122 filenames = ['*.toml', 'Pipfile', 'poetry.lock'] 

1123 mimetypes = ['application/toml'] 

1124 url = 'https://toml.io' 

1125 version_added = '2.4' 

1126 

1127 # Based on the TOML spec: https://toml.io/en/v1.0.0 

1128 

1129 # The following is adapted from CPython's tomllib: 

1130 _time = r"\d\d:\d\d:\d\d(\.\d+)?" 

1131 _datetime = rf"""(?x) 

1132 \d\d\d\d-\d\d-\d\d # date, e.g., 1988-10-27 

1133 ( 

1134 [Tt ] {_time} # optional time 

1135 ( 

1136 [Zz]|[+-]\d\d:\d\d # optional time offset 

1137 )? 

1138 )? 

1139 """ 

1140 

1141 tokens = { 

1142 'root': [ 

1143 # Note that we make an effort in order to distinguish 

1144 # moments at which we're parsing a key and moments at 

1145 # which we're parsing a value. In the TOML code 

1146 # 

1147 # 1234 = 1234 

1148 # 

1149 # the first "1234" should be Name, the second Integer. 

1150 

1151 # Whitespace 

1152 (r'\s+', Whitespace), 

1153 

1154 # Comment 

1155 (r'#.*', Comment.Single), 

1156 

1157 # Assignment keys 

1158 include('key'), 

1159 

1160 # After "=", find a value 

1161 (r'(=)(\s*)', bygroups(Operator, Whitespace), 'value'), 

1162 

1163 # Table header 

1164 (r'\[\[?', Keyword, 'table-key'), 

1165 ], 

1166 'key': [ 

1167 # Start of bare key (only ASCII is allowed here). 

1168 (r'[A-Za-z0-9_-]+', Name), 

1169 # Quoted key 

1170 (r'"', String.Double, 'basic-string'), 

1171 (r"'", String.Single, 'literal-string'), 

1172 # Dots act as separators in keys 

1173 (r'\.', Punctuation), 

1174 ], 

1175 'table-key': [ 

1176 # This is like 'key', but highlights the name components 

1177 # and separating dots as Keyword because it looks better 

1178 # when the whole table header is Keyword. We do highlight 

1179 # strings as strings though. 

1180 # Start of bare key (only ASCII is allowed here). 

1181 (r'[A-Za-z0-9_-]+', Keyword), 

1182 (r'"', String.Double, 'basic-string'), 

1183 (r"'", String.Single, 'literal-string'), 

1184 (r'\.', Keyword), 

1185 (r'\]\]?', Keyword, '#pop'), 

1186 

1187 # Inline whitespace allowed 

1188 (r'[ \t]+', Whitespace), 

1189 ], 

1190 'value': [ 

1191 # Datetime, baretime 

1192 (_datetime, Literal.Date, '#pop'), 

1193 (_time, Literal.Date, '#pop'), 

1194 

1195 # Recognize as float if there is a fractional part 

1196 # and/or an exponent. 

1197 (r'[+-]?\d[0-9_]*[eE][+-]?\d[0-9_]*', Number.Float, '#pop'), 

1198 (r'[+-]?\d[0-9_]*\.\d[0-9_]*([eE][+-]?\d[0-9_]*)?', 

1199 Number.Float, '#pop'), 

1200 

1201 # Infinities and NaN 

1202 (r'[+-]?(inf|nan)', Number.Float, '#pop'), 

1203 

1204 # Integers 

1205 (r'-?0b[01_]+', Number.Bin, '#pop'), 

1206 (r'-?0o[0-7_]+', Number.Oct, '#pop'), 

1207 (r'-?0x[0-9a-fA-F_]+', Number.Hex, '#pop'), 

1208 (r'[+-]?[0-9_]+', Number.Integer, '#pop'), 

1209 

1210 # Strings 

1211 (r'"""', String.Double, ('#pop', 'multiline-basic-string')), 

1212 (r'"', String.Double, ('#pop', 'basic-string')), 

1213 (r"'''", String.Single, ('#pop', 'multiline-literal-string')), 

1214 (r"'", String.Single, ('#pop', 'literal-string')), 

1215 

1216 # Booleans 

1217 (r'true|false', Keyword.Constant, '#pop'), 

1218 

1219 # Start of array 

1220 (r'\[', Punctuation, ('#pop', 'array')), 

1221 

1222 # Start of inline table 

1223 (r'\{', Punctuation, ('#pop', 'inline-table')), 

1224 ], 

1225 'array': [ 

1226 # Whitespace, including newlines, is ignored inside arrays, 

1227 # and comments are allowed. 

1228 (r'\s+', Whitespace), 

1229 (r'#.*', Comment.Single), 

1230 

1231 # Delimiters 

1232 (r',', Punctuation), 

1233 

1234 # End of array 

1235 (r'\]', Punctuation, '#pop'), 

1236 

1237 # Parse a value and come back 

1238 default('value'), 

1239 ], 

1240 'inline-table': [ 

1241 # Note that unlike inline arrays, inline tables do not 

1242 # allow newlines or comments. 

1243 (r'[ \t]+', Whitespace), 

1244 

1245 # Keys 

1246 include('key'), 

1247 

1248 # Values 

1249 (r'(=)(\s*)', bygroups(Punctuation, Whitespace), 'value'), 

1250 

1251 # Delimiters 

1252 (r',', Punctuation), 

1253 

1254 # End of inline table 

1255 (r'\}', Punctuation, '#pop'), 

1256 ], 

1257 'basic-string': [ 

1258 (r'"', String.Double, '#pop'), 

1259 include('escapes'), 

1260 (r'[^"\\]+', String.Double), 

1261 ], 

1262 'literal-string': [ 

1263 (r".*?'", String.Single, '#pop'), 

1264 ], 

1265 'multiline-basic-string': [ 

1266 (r'"""', String.Double, '#pop'), 

1267 (r'(\\)(\n)', bygroups(String.Escape, Whitespace)), 

1268 include('escapes'), 

1269 (r'[^"\\]+', String.Double), 

1270 (r'"', String.Double), 

1271 ], 

1272 'multiline-literal-string': [ 

1273 (r"'''", String.Single, '#pop'), 

1274 (r"[^']+", String.Single), 

1275 (r"'", String.Single), 

1276 ], 

1277 'escapes': [ 

1278 (r'\\u[0-9a-fA-F]{4}|\\U[0-9a-fA-F]{8}', String.Escape), 

1279 (r'\\.', String.Escape), 

1280 ], 

1281 } 

1282 

1283class NestedTextLexer(RegexLexer): 

1284 """ 

1285 Lexer for *NextedText*, a human-friendly data format. 

1286 

1287 .. versionchanged:: 2.16 

1288 Added support for *NextedText* v3.0. 

1289 """ 

1290 

1291 name = 'NestedText' 

1292 url = 'https://nestedtext.org' 

1293 aliases = ['nestedtext', 'nt'] 

1294 filenames = ['*.nt'] 

1295 version_added = '2.9' 

1296 

1297 tokens = { 

1298 'root': [ 

1299 # Comment: # ... 

1300 (r'^([ ]*)(#.*)$', bygroups(Whitespace, Comment)), 

1301 

1302 # Inline dictionary: {...} 

1303 (r'^([ ]*)(\{)', bygroups(Whitespace, Punctuation), 'inline_dict'), 

1304 

1305 # Inline list: [...] 

1306 (r'^([ ]*)(\[)', bygroups(Whitespace, Punctuation), 'inline_list'), 

1307 

1308 # empty multiline string item: > 

1309 (r'^([ ]*)(>)$', bygroups(Whitespace, Punctuation)), 

1310 

1311 # multiline string item: > ... 

1312 (r'^([ ]*)(>)( )(.*?)([ \t]*)$', bygroups(Whitespace, Punctuation, Whitespace, Text, Whitespace)), 

1313 

1314 # empty list item: - 

1315 (r'^([ ]*)(-)$', bygroups(Whitespace, Punctuation)), 

1316 

1317 # list item: - ... 

1318 (r'^([ ]*)(-)( )(.*?)([ \t]*)$', bygroups(Whitespace, Punctuation, Whitespace, Text, Whitespace)), 

1319 

1320 # empty multiline key item: : 

1321 (r'^([ ]*)(:)$', bygroups(Whitespace, Punctuation)), 

1322 

1323 # multiline key item: : ... 

1324 (r'^([ ]*)(:)( )([^\n]*?)([ \t]*)$', bygroups(Whitespace, Punctuation, Whitespace, Name.Tag, Whitespace)), 

1325 

1326 # empty dict key item: ...: 

1327 (r'^([ ]*)([^\{\[\s].*?)(:)$', bygroups(Whitespace, Name.Tag, Punctuation)), 

1328 

1329 # dict key item: ...: ... 

1330 (r'^([ ]*)([^\{\[\s].*?)(:)( )(.*?)([ \t]*)$', bygroups(Whitespace, Name.Tag, Punctuation, Whitespace, Text, Whitespace)), 

1331 ], 

1332 'inline_list': [ 

1333 include('whitespace'), 

1334 (r'[^\{\}\[\],\s]+', Text), 

1335 include('inline_value'), 

1336 (r',', Punctuation), 

1337 (r'\]', Punctuation, '#pop'), 

1338 (r'\n', Error, '#pop'), 

1339 ], 

1340 'inline_dict': [ 

1341 include('whitespace'), 

1342 (r'[^\{\}\[\],:\s]+', Name.Tag), 

1343 (r':', Punctuation, 'inline_dict_value'), 

1344 (r'\}', Punctuation, '#pop'), 

1345 (r'\n', Error, '#pop'), 

1346 ], 

1347 'inline_dict_value': [ 

1348 include('whitespace'), 

1349 (r'[^\{\}\[\],:\s]+', Text), 

1350 include('inline_value'), 

1351 (r',', Punctuation, '#pop'), 

1352 (r'\}', Punctuation, '#pop:2'), 

1353 ], 

1354 'inline_value': [ 

1355 include('whitespace'), 

1356 (r'\{', Punctuation, 'inline_dict'), 

1357 (r'\[', Punctuation, 'inline_list'), 

1358 ], 

1359 'whitespace': [ 

1360 (r'[ \t]+', Whitespace), 

1361 ], 

1362 } 

1363 

1364 

1365class SingularityLexer(RegexLexer): 

1366 """ 

1367 Lexer for Singularity definition files. 

1368 """ 

1369 

1370 name = 'Singularity' 

1371 url = 'https://www.sylabs.io/guides/3.0/user-guide/definition_files.html' 

1372 aliases = ['singularity'] 

1373 filenames = ['*.def', 'Singularity'] 

1374 version_added = '2.6' 

1375 flags = re.IGNORECASE | re.MULTILINE | re.DOTALL 

1376 

1377 _headers = r'^(\s*)(bootstrap|from|osversion|mirrorurl|include|registry|namespace|includecmd)(:)' 

1378 _section = r'^(%(?:pre|post|setup|environment|help|labels|test|runscript|files|startscript))(\s*)' 

1379 _appsect = r'^(%app(?:install|help|run|labels|env|test|files))(\s*)' 

1380 

1381 tokens = { 

1382 'root': [ 

1383 (_section, bygroups(Generic.Heading, Whitespace), 'script'), 

1384 (_appsect, bygroups(Generic.Heading, Whitespace), 'script'), 

1385 (_headers, bygroups(Whitespace, Keyword, Text)), 

1386 (r'\s*#.*?\n', Comment), 

1387 (r'\b(([0-9]+\.?[0-9]*)|(\.[0-9]+))\b', Number), 

1388 (r'[ \t]+', Whitespace), 

1389 (r'(?!^\s*%).', Text), 

1390 ], 

1391 'script': [ 

1392 (r'(.+?(?=^\s*%))|(.*)', using(BashLexer), '#pop'), 

1393 ], 

1394 } 

1395 

1396 def analyse_text(text): 

1397 """This is a quite simple script file, but there are a few keywords 

1398 which seem unique to this language.""" 

1399 result = 0 

1400 if re.search(r'\b(?:osversion|includecmd|mirrorurl)\b', text, re.IGNORECASE): 

1401 result += 0.5 

1402 

1403 if re.search(SingularityLexer._section[1:], text): 

1404 result += 0.49 

1405 

1406 return result 

1407 

1408 

1409class UnixConfigLexer(RegexLexer): 

1410 """ 

1411 Lexer for Unix/Linux config files using colon-separated values, e.g. 

1412 

1413 * ``/etc/group`` 

1414 * ``/etc/passwd`` 

1415 * ``/etc/shadow`` 

1416 """ 

1417 

1418 name = 'Unix/Linux config files' 

1419 aliases = ['unixconfig', 'linuxconfig'] 

1420 filenames = [] 

1421 url = 'https://en.wikipedia.org/wiki/Configuration_file#Unix_and_Unix-like_operating_systems' 

1422 version_added = '2.12' 

1423 

1424 tokens = { 

1425 'root': [ 

1426 (r'^#.*', Comment), 

1427 (r'\n', Whitespace), 

1428 (r':', Punctuation), 

1429 (r'[0-9]+', Number), 

1430 (r'((?!\n)[a-zA-Z0-9\_\-\s\(\),]){2,}', Text), 

1431 (r'[^:\n]+', String), 

1432 ], 

1433 }