From 6d7ba58f880be618ade07f8ea080fe8c4bf8a896 Mon Sep 17 00:00:00 2001 From: cyfraeviolae Date: Wed, 3 Apr 2024 03:10:44 -0400 Subject: venv --- .../site-packages/pygments/lexers/configs.py | 1435 ++++++++++++++++++++ 1 file changed, 1435 insertions(+) create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/configs.py (limited to 'venv/lib/python3.11/site-packages/pygments/lexers/configs.py') diff --git a/venv/lib/python3.11/site-packages/pygments/lexers/configs.py b/venv/lib/python3.11/site-packages/pygments/lexers/configs.py new file mode 100644 index 0000000..6c5e542 --- /dev/null +++ b/venv/lib/python3.11/site-packages/pygments/lexers/configs.py @@ -0,0 +1,1435 @@ +""" + pygments.lexers.configs + ~~~~~~~~~~~~~~~~~~~~~~~ + + Lexers for configuration file formats. + + :copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import re + +from pygments.lexer import ExtendedRegexLexer, RegexLexer, default, words, \ + bygroups, include, using, line_re +from pygments.token import Text, Comment, Operator, Keyword, Name, String, \ + Number, Punctuation, Whitespace, Literal, Error, Generic +from pygments.lexers.shell import BashLexer +from pygments.lexers.data import JsonLexer + +__all__ = ['IniLexer', 'SystemdLexer', 'DesktopLexer', 'RegeditLexer', 'PropertiesLexer', + 'KconfigLexer', 'Cfengine3Lexer', 'ApacheConfLexer', 'SquidConfLexer', + 'NginxConfLexer', 'LighttpdConfLexer', 'DockerLexer', + 'TerraformLexer', 'TermcapLexer', 'TerminfoLexer', + 'PkgConfigLexer', 'PacmanConfLexer', 'AugeasLexer', 'TOMLLexer', + 'NestedTextLexer', 'SingularityLexer', 'UnixConfigLexer'] + + +class IniLexer(RegexLexer): + """ + Lexer for configuration files in INI style. + """ + + name = 'INI' + aliases = ['ini', 'cfg', 'dosini'] + filenames = [ + '*.ini', '*.cfg', '*.inf', '.editorconfig', + ] + mimetypes = ['text/x-ini', 'text/inf'] + + tokens = { + 'root': [ + (r'\s+', Whitespace), + (r'[;#].*', Comment.Single), + (r'(\[.*?\])([ \t]*)$', bygroups(Keyword, Whitespace)), + (r'(.*?)([  \t]*)([=:])([ \t]*)([^;#\n]*)(\\)(\s+)', + bygroups(Name.Attribute, Whitespace, Operator, Whitespace, String, + Text, Whitespace), + "value"), + (r'(.*?)([ \t]*)([=:])([  \t]*)([^ ;#\n]*(?: +[^ ;#\n]+)*)', + bygroups(Name.Attribute, Whitespace, Operator, Whitespace, String)), + # standalone option, supported by some INI parsers + (r'(.+?)$', Name.Attribute), + ], + 'value': [ # line continuation + (r'\s+', Whitespace), + (r'(\s*)(.*)(\\)([ \t]*)', + bygroups(Whitespace, String, Text, Whitespace)), + (r'.*$', String, "#pop"), + ], + } + + def analyse_text(text): + npos = text.find('\n') + if npos < 3: + return False + if text[0] == '[' and text[npos-1] == ']': + return 0.8 + return False + + +class DesktopLexer(RegexLexer): + """ + Lexer for .desktop files. + + .. versionadded:: 2.16 + """ + + name = 'Desktop file' + url = "https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html" + aliases = ['desktop'] + filenames = ['*.desktop'] + + tokens = { + 'root': [ + (r'^[ \t]*\n', Whitespace), + (r'^(#.*)(\n)', bygroups(Comment.Single, Whitespace)), + (r'(\[[^\]\n]+\])(\n)', bygroups(Keyword, Whitespace)), + (r'([-A-Za-z0-9]+)(\[[^\] \t=]+\])?([ \t]*)(=)([ \t]*)([^\n]*)([ \t\n]*\n)', + bygroups(Name.Attribute, Name.Namespace, Whitespace, Operator, Whitespace, String, Whitespace)), + ], + } + + def analyse_text(text): + if text.startswith("[Desktop Entry]"): + return 1.0 + if re.search(r"^\[Desktop Entry\][ \t]*$", text[:500], re.MULTILINE) is not None: + return 0.9 + return 0.0 + + +class SystemdLexer(RegexLexer): + """ + Lexer for systemd unit files. + + .. versionadded:: 2.16 + """ + + name = 'Systemd' + url = "https://www.freedesktop.org/software/systemd/man/systemd.syntax.html" + aliases = ['systemd'] + filenames = [ + '*.service', '*.socket', '*.device', '*.mount', '*.automount', + '*.swap', '*.target', '*.path', '*.timer', '*.slice', '*.scope', + ] + + tokens = { + 'root': [ + (r'^[ \t]*\n', Whitespace), + (r'^([;#].*)(\n)', bygroups(Comment.Single, Whitespace)), + (r'(\[[^\]\n]+\])(\n)', bygroups(Keyword, Whitespace)), + (r'([^=]+)([ \t]*)(=)([ \t]*)([^\n]*)(\\)(\n)', + bygroups(Name.Attribute, Whitespace, Operator, Whitespace, String, + Text, Whitespace), + "value"), + (r'([^=]+)([ \t]*)(=)([ \t]*)([^\n]*)(\n)', + bygroups(Name.Attribute, Whitespace, Operator, Whitespace, String, Whitespace)), + ], + 'value': [ + # line continuation + (r'^([;#].*)(\n)', bygroups(Comment.Single, Whitespace)), + (r'([ \t]*)([^\n]*)(\\)(\n)', + bygroups(Whitespace, String, Text, Whitespace)), + (r'([ \t]*)([^\n]*)(\n)', + bygroups(Whitespace, String, Whitespace), "#pop"), + ], + } + + def analyse_text(text): + if text.startswith("[Unit]"): + return 1.0 + if re.search(r"^\[Unit\][ \t]*$", text[:500], re.MULTILINE) is not None: + return 0.9 + return 0.0 + + +class RegeditLexer(RegexLexer): + """ + Lexer for Windows Registry files produced by regedit. + + .. versionadded:: 1.6 + """ + + name = 'reg' + url = 'http://en.wikipedia.org/wiki/Windows_Registry#.REG_files' + aliases = ['registry'] + filenames = ['*.reg'] + mimetypes = ['text/x-windows-registry'] + + tokens = { + 'root': [ + (r'Windows Registry Editor.*', Text), + (r'\s+', Whitespace), + (r'[;#].*', Comment.Single), + (r'(\[)(-?)(HKEY_[A-Z_]+)(.*?\])$', + bygroups(Keyword, Operator, Name.Builtin, Keyword)), + # String keys, which obey somewhat normal escaping + (r'("(?:\\"|\\\\|[^"])+")([ \t]*)(=)([ \t]*)', + bygroups(Name.Attribute, Whitespace, Operator, Whitespace), + 'value'), + # Bare keys (includes @) + (r'(.*?)([ \t]*)(=)([ \t]*)', + bygroups(Name.Attribute, Whitespace, Operator, Whitespace), + 'value'), + ], + 'value': [ + (r'-', Operator, '#pop'), # delete value + (r'(dword|hex(?:\([0-9a-fA-F]\))?)(:)([0-9a-fA-F,]+)', + bygroups(Name.Variable, Punctuation, Number), '#pop'), + # As far as I know, .reg files do not support line continuation. + (r'.+', String, '#pop'), + default('#pop'), + ] + } + + def analyse_text(text): + return text.startswith('Windows Registry Editor') + + +class PropertiesLexer(RegexLexer): + """ + Lexer for configuration files in Java's properties format. + + Note: trailing whitespace counts as part of the value as per spec + + .. versionadded:: 1.4 + """ + + name = 'Properties' + aliases = ['properties', 'jproperties'] + filenames = ['*.properties'] + mimetypes = ['text/x-java-properties'] + + tokens = { + 'root': [ + # comments + (r'[!#].*|/{2}.*', Comment.Single), + # ending a comment or whitespace-only line + (r'\n', Whitespace), + # eat whitespace at the beginning of a line + (r'^[^\S\n]+', Whitespace), + # start lexing a key + default('key'), + ], + 'key': [ + # non-escaped key characters + (r'[^\\:=\s]+', Name.Attribute), + # escapes + include('escapes'), + # separator is the first non-escaped whitespace or colon or '=' on the line; + # if it's whitespace, = and : are gobbled after it + (r'([^\S\n]*)([:=])([^\S\n]*)', + bygroups(Whitespace, Operator, Whitespace), + ('#pop', 'value')), + (r'[^\S\n]+', Whitespace, ('#pop', 'value')), + # maybe we got no value after all + (r'\n', Whitespace, '#pop'), + ], + 'value': [ + # non-escaped value characters + (r'[^\\\n]+', String), + # escapes + include('escapes'), + # end the value on an unescaped newline + (r'\n', Whitespace, '#pop'), + ], + 'escapes': [ + # line continuations; these gobble whitespace at the beginning of the next line + (r'(\\\n)([^\S\n]*)', bygroups(String.Escape, Whitespace)), + # other escapes + (r'\\(.|\n)', String.Escape), + ], + } + + +def _rx_indent(level): + # Kconfig *always* interprets a tab as 8 spaces, so this is the default. + # Edit this if you are in an environment where KconfigLexer gets expanded + # input (tabs expanded to spaces) and the expansion tab width is != 8, + # e.g. in connection with Trac (trac.ini, [mimeviewer], tab_width). + # Value range here is 2 <= {tab_width} <= 8. + tab_width = 8 + # Regex matching a given indentation {level}, assuming that indentation is + # a multiple of {tab_width}. In other cases there might be problems. + if tab_width == 2: + space_repeat = '+' + else: + space_repeat = '{1,%d}' % (tab_width - 1) + if level == 1: + level_repeat = '' + else: + level_repeat = '{%s}' % level + return r'(?:\t| %s\t| {%s})%s.*\n' % (space_repeat, tab_width, level_repeat) + + +class KconfigLexer(RegexLexer): + """ + For Linux-style Kconfig files. + + .. versionadded:: 1.6 + """ + + name = 'Kconfig' + aliases = ['kconfig', 'menuconfig', 'linux-config', 'kernel-config'] + # Adjust this if new kconfig file names appear in your environment + filenames = ['Kconfig*', '*Config.in*', 'external.in*', + 'standard-modules.in'] + mimetypes = ['text/x-kconfig'] + # No re.MULTILINE, indentation-aware help text needs line-by-line handling + flags = 0 + + def call_indent(level): + # If indentation >= {level} is detected, enter state 'indent{level}' + return (_rx_indent(level), String.Doc, 'indent%s' % level) + + def do_indent(level): + # Print paragraphs of indentation level >= {level} as String.Doc, + # ignoring blank lines. Then return to 'root' state. + return [ + (_rx_indent(level), String.Doc), + (r'\s*\n', Text), + default('#pop:2') + ] + + tokens = { + 'root': [ + (r'\s+', Whitespace), + (r'#.*?\n', Comment.Single), + (words(( + 'mainmenu', 'config', 'menuconfig', 'choice', 'endchoice', + 'comment', 'menu', 'endmenu', 'visible if', 'if', 'endif', + 'source', 'prompt', 'select', 'depends on', 'default', + 'range', 'option'), suffix=r'\b'), + Keyword), + (r'(---help---|help)[\t ]*\n', Keyword, 'help'), + (r'(bool|tristate|string|hex|int|defconfig_list|modules|env)\b', + Name.Builtin), + (r'[!=&|]', Operator), + (r'[()]', Punctuation), + (r'[0-9]+', Number.Integer), + (r"'(''|[^'])*'", String.Single), + (r'"(""|[^"])*"', String.Double), + (r'\S+', Text), + ], + # Help text is indented, multi-line and ends when a lower indentation + # level is detected. + 'help': [ + # Skip blank lines after help token, if any + (r'\s*\n', Text), + # Determine the first help line's indentation level heuristically(!). + # Attention: this is not perfect, but works for 99% of "normal" + # indentation schemes up to a max. indentation level of 7. + call_indent(7), + call_indent(6), + call_indent(5), + call_indent(4), + call_indent(3), + call_indent(2), + call_indent(1), + default('#pop'), # for incomplete help sections without text + ], + # Handle text for indentation levels 7 to 1 + 'indent7': do_indent(7), + 'indent6': do_indent(6), + 'indent5': do_indent(5), + 'indent4': do_indent(4), + 'indent3': do_indent(3), + 'indent2': do_indent(2), + 'indent1': do_indent(1), + } + + +class Cfengine3Lexer(RegexLexer): + """ + Lexer for CFEngine3 policy files. + + .. versionadded:: 1.5 + """ + + name = 'CFEngine3' + url = 'http://cfengine.org' + aliases = ['cfengine3', 'cf3'] + filenames = ['*.cf'] + mimetypes = [] + + tokens = { + 'root': [ + (r'#.*?\n', Comment), + (r'(body)(\s+)(\S+)(\s+)(control)', + bygroups(Keyword, Whitespace, Keyword, Whitespace, Keyword)), + (r'(body|bundle)(\s+)(\S+)(\s+)(\w+)(\()', + bygroups(Keyword, Whitespace, Keyword, Whitespace, Name.Function, Punctuation), + 'arglist'), + (r'(body|bundle)(\s+)(\S+)(\s+)(\w+)', + bygroups(Keyword, Whitespace, Keyword, Whitespace, Name.Function)), + (r'(")([^"]+)(")(\s+)(string|slist|int|real)(\s*)(=>)(\s*)', + bygroups(Punctuation, Name.Variable, Punctuation, + Whitespace, Keyword.Type, Whitespace, Operator, Whitespace)), + (r'(\S+)(\s*)(=>)(\s*)', + bygroups(Keyword.Reserved, Whitespace, Operator, Text)), + (r'"', String, 'string'), + (r'(\w+)(\()', bygroups(Name.Function, Punctuation)), + (r'([\w.!&|()]+)(::)', bygroups(Name.Class, Punctuation)), + (r'(\w+)(:)', bygroups(Keyword.Declaration, Punctuation)), + (r'@[{(][^)}]+[})]', Name.Variable), + (r'[(){},;]', Punctuation), + (r'=>', Operator), + (r'->', Operator), + (r'\d+\.\d+', Number.Float), + (r'\d+', Number.Integer), + (r'\w+', Name.Function), + (r'\s+', Whitespace), + ], + 'string': [ + (r'\$[{(]', String.Interpol, 'interpol'), + (r'\\.', String.Escape), + (r'"', String, '#pop'), + (r'\n', String), + (r'.', String), + ], + 'interpol': [ + (r'\$[{(]', String.Interpol, '#push'), + (r'[})]', String.Interpol, '#pop'), + (r'[^${()}]+', String.Interpol), + ], + 'arglist': [ + (r'\)', Punctuation, '#pop'), + (r',', Punctuation), + (r'\w+', Name.Variable), + (r'\s+', Whitespace), + ], + } + + +class ApacheConfLexer(RegexLexer): + """ + Lexer for configuration files following the Apache config file + format. + + .. versionadded:: 0.6 + """ + + name = 'ApacheConf' + aliases = ['apacheconf', 'aconf', 'apache'] + filenames = ['.htaccess', 'apache.conf', 'apache2.conf'] + mimetypes = ['text/x-apacheconf'] + flags = re.MULTILINE | re.IGNORECASE + + tokens = { + 'root': [ + (r'\s+', Whitespace), + (r'#(.*\\\n)+.*$|(#.*?)$', Comment), + (r'(<[^\s>/][^\s>]*)(?:(\s+)(.*))?(>)', + bygroups(Name.Tag, Whitespace, String, Name.Tag)), + (r'(]+)(>)', + bygroups(Name.Tag, Name.Tag)), + (r'[a-z]\w*', Name.Builtin, 'value'), + (r'\.+', Text), + ], + 'value': [ + (r'\\\n', Text), + (r'\n+', Whitespace, '#pop'), + (r'\\', Text), + (r'[^\S\n]+', Whitespace), + (r'\d+\.\d+\.\d+\.\d+(?:/\d+)?', Number), + (r'\d+', Number), + (r'/([*a-z0-9][*\w./-]+)', String.Other), + (r'(on|off|none|any|all|double|email|dns|min|minimal|' + r'os|productonly|full|emerg|alert|crit|error|warn|' + r'notice|info|debug|registry|script|inetd|standalone|' + r'user|group)\b', Keyword), + (r'"([^"\\]*(?:\\(.|\n)[^"\\]*)*)"', String.Double), + (r'[^\s"\\]+', Text) + ], + } + + +class SquidConfLexer(RegexLexer): + """ + Lexer for squid configuration files. + + .. versionadded:: 0.9 + """ + + name = 'SquidConf' + url = 'http://www.squid-cache.org/' + aliases = ['squidconf', 'squid.conf', 'squid'] + filenames = ['squid.conf'] + mimetypes = ['text/x-squidconf'] + flags = re.IGNORECASE + + keywords = ( + "access_log", "acl", "always_direct", "announce_host", + "announce_period", "announce_port", "announce_to", "anonymize_headers", + "append_domain", "as_whois_server", "auth_param_basic", + "authenticate_children", "authenticate_program", "authenticate_ttl", + "broken_posts", "buffered_logs", "cache_access_log", "cache_announce", + "cache_dir", "cache_dns_program", "cache_effective_group", + "cache_effective_user", "cache_host", "cache_host_acl", + "cache_host_domain", "cache_log", "cache_mem", "cache_mem_high", + "cache_mem_low", "cache_mgr", "cachemgr_passwd", "cache_peer", + "cache_peer_access", "cache_replacement_policy", "cache_stoplist", + "cache_stoplist_pattern", "cache_store_log", "cache_swap", + "cache_swap_high", "cache_swap_log", "cache_swap_low", "client_db", + "client_lifetime", "client_netmask", "connect_timeout", "coredump_dir", + "dead_peer_timeout", "debug_options", "delay_access", "delay_class", + "delay_initial_bucket_level", "delay_parameters", "delay_pools", + "deny_info", "dns_children", "dns_defnames", "dns_nameservers", + "dns_testnames", "emulate_httpd_log", "err_html_text", + "fake_user_agent", "firewall_ip", "forwarded_for", "forward_snmpd_port", + "fqdncache_size", "ftpget_options", "ftpget_program", "ftp_list_width", + "ftp_passive", "ftp_user", "half_closed_clients", "header_access", + "header_replace", "hierarchy_stoplist", "high_response_time_warning", + "high_page_fault_warning", "hosts_file", "htcp_port", "http_access", + "http_anonymizer", "httpd_accel", "httpd_accel_host", + "httpd_accel_port", "httpd_accel_uses_host_header", + "httpd_accel_with_proxy", "http_port", "http_reply_access", + "icp_access", "icp_hit_stale", "icp_port", "icp_query_timeout", + "ident_lookup", "ident_lookup_access", "ident_timeout", + "incoming_http_average", "incoming_icp_average", "inside_firewall", + "ipcache_high", "ipcache_low", "ipcache_size", "local_domain", + "local_ip", "logfile_rotate", "log_fqdn", "log_icp_queries", + "log_mime_hdrs", "maximum_object_size", "maximum_single_addr_tries", + "mcast_groups", "mcast_icp_query_timeout", "mcast_miss_addr", + "mcast_miss_encode_key", "mcast_miss_port", "memory_pools", + "memory_pools_limit", "memory_replacement_policy", "mime_table", + "min_http_poll_cnt", "min_icp_poll_cnt", "minimum_direct_hops", + "minimum_object_size", "minimum_retry_timeout", "miss_access", + "negative_dns_ttl", "negative_ttl", "neighbor_timeout", + "neighbor_type_domain", "netdb_high", "netdb_low", "netdb_ping_period", + "netdb_ping_rate", "never_direct", "no_cache", "passthrough_proxy", + "pconn_timeout", "pid_filename", "pinger_program", "positive_dns_ttl", + "prefer_direct", "proxy_auth", "proxy_auth_realm", "query_icmp", + "quick_abort", "quick_abort_max", "quick_abort_min", + "quick_abort_pct", "range_offset_limit", "read_timeout", + "redirect_children", "redirect_program", + "redirect_rewrites_host_header", "reference_age", + "refresh_pattern", "reload_into_ims", "request_body_max_size", + "request_size", "request_timeout", "shutdown_lifetime", + "single_parent_bypass", "siteselect_timeout", "snmp_access", + "snmp_incoming_address", "snmp_port", "source_ping", "ssl_proxy", + "store_avg_object_size", "store_objects_per_bucket", + "strip_query_terms", "swap_level1_dirs", "swap_level2_dirs", + "tcp_incoming_address", "tcp_outgoing_address", "tcp_recv_bufsize", + "test_reachability", "udp_hit_obj", "udp_hit_obj_size", + "udp_incoming_address", "udp_outgoing_address", "unique_hostname", + "unlinkd_program", "uri_whitespace", "useragent_log", + "visible_hostname", "wais_relay", "wais_relay_host", "wais_relay_port", + ) + + opts = ( + "proxy-only", "weight", "ttl", "no-query", "default", "round-robin", + "multicast-responder", "on", "off", "all", "deny", "allow", "via", + "parent", "no-digest", "heap", "lru", "realm", "children", "q1", "q2", + "credentialsttl", "none", "disable", "offline_toggle", "diskd", + ) + + actions = ( + "shutdown", "info", "parameter", "server_list", "client_list", + r'squid.conf', + ) + + actions_stats = ( + "objects", "vm_objects", "utilization", "ipcache", "fqdncache", "dns", + "redirector", "io", "reply_headers", "filedescriptors", "netdb", + ) + + actions_log = ("status", "enable", "disable", "clear") + + acls = ( + "url_regex", "urlpath_regex", "referer_regex", "port", "proto", + "req_mime_type", "rep_mime_type", "method", "browser", "user", "src", + "dst", "time", "dstdomain", "ident", "snmp_community", + ) + + ipv4_group = r'(\d+|0x[0-9a-f]+)' + ipv4 = rf'({ipv4_group}(\.{ipv4_group}){{3}})' + ipv6_group = r'([0-9a-f]{0,4})' + ipv6 = rf'({ipv6_group}(:{ipv6_group}){{1,7}})' + bare_ip = rf'({ipv4}|{ipv6})' + # XXX: /integer is a subnet mark, but what is /IP ? + # There is no test where it is used. + ip = rf'{bare_ip}(/({bare_ip}|\d+))?' + + tokens = { + 'root': [ + (r'\s+', Whitespace), + (r'#', Comment, 'comment'), + (words(keywords, prefix=r'\b', suffix=r'\b'), Keyword), + (words(opts, prefix=r'\b', suffix=r'\b'), Name.Constant), + # Actions + (words(actions, prefix=r'\b', suffix=r'\b'), String), + (words(actions_stats, prefix=r'stats/', suffix=r'\b'), String), + (words(actions_log, prefix=r'log/', suffix=r'='), String), + (words(acls, prefix=r'\b', suffix=r'\b'), Keyword), + (ip, Number.Float), + (r'(?:\b\d+\b(?:-\b\d+|%)?)', Number), + (r'\S+', Text), + ], + 'comment': [ + (r'\s*TAG:.*', String.Escape, '#pop'), + (r'.+', Comment, '#pop'), + default('#pop'), + ], + } + + +class NginxConfLexer(RegexLexer): + """ + Lexer for Nginx configuration files. + + .. versionadded:: 0.11 + """ + name = 'Nginx configuration file' + url = 'http://nginx.net/' + aliases = ['nginx'] + filenames = ['nginx.conf'] + mimetypes = ['text/x-nginx-conf'] + + tokens = { + 'root': [ + (r'(include)(\s+)([^\s;]+)', bygroups(Keyword, Whitespace, Name)), + (r'[^\s;#]+', Keyword, 'stmt'), + include('base'), + ], + 'block': [ + (r'\}', Punctuation, '#pop:2'), + (r'[^\s;#]+', Keyword.Namespace, 'stmt'), + include('base'), + ], + 'stmt': [ + (r'\{', Punctuation, 'block'), + (r';', Punctuation, '#pop'), + include('base'), + ], + 'base': [ + (r'#.*\n', Comment.Single), + (r'on|off', Name.Constant), + (r'\$[^\s;#()]+', Name.Variable), + (r'([a-z0-9.-]+)(:)([0-9]+)', + bygroups(Name, Punctuation, Number.Integer)), + (r'[a-z-]+/[a-z-+]+', String), # mimetype + # (r'[a-zA-Z._-]+', Keyword), + (r'[0-9]+[km]?\b', Number.Integer), + (r'(~)(\s*)([^\s{]+)', bygroups(Punctuation, Whitespace, String.Regex)), + (r'[:=~]', Punctuation), + (r'[^\s;#{}$]+', String), # catch all + (r'/[^\s;#]*', Name), # pathname + (r'\s+', Whitespace), + (r'[$;]', Text), # leftover characters + ], + } + + +class LighttpdConfLexer(RegexLexer): + """ + Lexer for Lighttpd configuration files. + + .. versionadded:: 0.11 + """ + name = 'Lighttpd configuration file' + url = 'http://lighttpd.net/' + aliases = ['lighttpd', 'lighty'] + filenames = ['lighttpd.conf'] + mimetypes = ['text/x-lighttpd-conf'] + + tokens = { + 'root': [ + (r'#.*\n', Comment.Single), + (r'/\S*', Name), # pathname + (r'[a-zA-Z._-]+', Keyword), + (r'\d+\.\d+\.\d+\.\d+(?:/\d+)?', Number), + (r'[0-9]+', Number), + (r'=>|=~|\+=|==|=|\+', Operator), + (r'\$[A-Z]+', Name.Builtin), + (r'[(){}\[\],]', Punctuation), + (r'"([^"\\]*(?:\\.[^"\\]*)*)"', String.Double), + (r'\s+', Whitespace), + ], + + } + + +class DockerLexer(RegexLexer): + """ + Lexer for Docker configuration files. + + .. versionadded:: 2.0 + """ + name = 'Docker' + url = 'http://docker.io' + aliases = ['docker', 'dockerfile'] + filenames = ['Dockerfile', '*.docker'] + mimetypes = ['text/x-dockerfile-config'] + + _keywords = (r'(?:MAINTAINER|EXPOSE|WORKDIR|USER|STOPSIGNAL)') + _bash_keywords = (r'(?:RUN|CMD|ENTRYPOINT|ENV|ARG|LABEL|ADD|COPY)') + _lb = r'(?:\s*\\?\s*)' # dockerfile line break regex + flags = re.IGNORECASE | re.MULTILINE + + tokens = { + 'root': [ + (r'#.*', Comment), + (r'(FROM)([ \t]*)(\S*)([ \t]*)(?:(AS)([ \t]*)(\S*))?', + bygroups(Keyword, Whitespace, String, Whitespace, Keyword, Whitespace, String)), + (r'(ONBUILD)(\s+)(%s)' % (_lb,), bygroups(Keyword, Whitespace, using(BashLexer))), + (r'(HEALTHCHECK)(\s+)((%s--\w+=\w+%s)*)' % (_lb, _lb), + bygroups(Keyword, Whitespace, using(BashLexer))), + (r'(VOLUME|ENTRYPOINT|CMD|SHELL)(\s+)(%s)(\[.*?\])' % (_lb,), + bygroups(Keyword, Whitespace, using(BashLexer), using(JsonLexer))), + (r'(LABEL|ENV|ARG)(\s+)((%s\w+=\w+%s)*)' % (_lb, _lb), + bygroups(Keyword, Whitespace, using(BashLexer))), + (r'(%s|VOLUME)\b(\s+)(.*)' % (_keywords), bygroups(Keyword, Whitespace, String)), + (r'(%s)(\s+)' % (_bash_keywords,), bygroups(Keyword, Whitespace)), + (r'(.*\\\n)*.+', using(BashLexer)), + ] + } + + +class TerraformLexer(ExtendedRegexLexer): + """ + Lexer for terraformi ``.tf`` files. + + .. versionadded:: 2.1 + """ + + name = 'Terraform' + url = 'https://www.terraform.io/' + aliases = ['terraform', 'tf', 'hcl'] + filenames = ['*.tf', '*.hcl'] + mimetypes = ['application/x-tf', 'application/x-terraform'] + + classes = ('backend', 'data', 'module', 'output', 'provider', + 'provisioner', 'resource', 'variable') + classes_re = "({})".format(('|').join(classes)) + + types = ('string', 'number', 'bool', 'list', 'tuple', 'map', 'set', 'object', 'null') + + numeric_functions = ('abs', 'ceil', 'floor', 'log', 'max', + 'mix', 'parseint', 'pow', 'signum') + + string_functions = ('chomp', 'format', 'formatlist', 'indent', + 'join', 'lower', 'regex', 'regexall', 'replace', + 'split', 'strrev', 'substr', 'title', 'trim', + 'trimprefix', 'trimsuffix', 'trimspace', 'upper' + ) + + collection_functions = ('alltrue', 'anytrue', 'chunklist', 'coalesce', + 'coalescelist', 'compact', 'concat', 'contains', + 'distinct', 'element', 'flatten', 'index', 'keys', + 'length', 'list', 'lookup', 'map', 'matchkeys', + 'merge', 'range', 'reverse', 'setintersection', + 'setproduct', 'setsubtract', 'setunion', 'slice', + 'sort', 'sum', 'transpose', 'values', 'zipmap' + ) + + encoding_functions = ('base64decode', 'base64encode', 'base64gzip', + 'csvdecode', 'jsondecode', 'jsonencode', 'textdecodebase64', + 'textencodebase64', 'urlencode', 'yamldecode', 'yamlencode') + + filesystem_functions = ('abspath', 'dirname', 'pathexpand', 'basename', + 'file', 'fileexists', 'fileset', 'filebase64', 'templatefile') + + date_time_functions = ('formatdate', 'timeadd', 'timestamp') + + hash_crypto_functions = ('base64sha256', 'base64sha512', 'bcrypt', 'filebase64sha256', + 'filebase64sha512', 'filemd5', 'filesha1', 'filesha256', 'filesha512', + 'md5', 'rsadecrypt', 'sha1', 'sha256', 'sha512', 'uuid', 'uuidv5') + + ip_network_functions = ('cidrhost', 'cidrnetmask', 'cidrsubnet', 'cidrsubnets') + + type_conversion_functions = ('can', 'defaults', 'tobool', 'tolist', 'tomap', + 'tonumber', 'toset', 'tostring', 'try') + + builtins = numeric_functions + string_functions + collection_functions + encoding_functions +\ + filesystem_functions + date_time_functions + hash_crypto_functions + ip_network_functions +\ + type_conversion_functions + builtins_re = "({})".format(('|').join(builtins)) + + def heredoc_callback(self, match, ctx): + # Parse a terraform heredoc + # match: 1 = <<[-]?, 2 = name 3 = rest of line + + start = match.start(1) + yield start, Operator, match.group(1) # <<[-]? + yield match.start(2), String.Delimiter, match.group(2) # heredoc name + + ctx.pos = match.start(3) + ctx.end = match.end(3) + yield ctx.pos, String.Heredoc, match.group(3) + ctx.pos = match.end() + + hdname = match.group(2) + tolerant = True # leading whitespace is always accepted + + lines = [] + + for match in line_re.finditer(ctx.text, ctx.pos): + if tolerant: + check = match.group().strip() + else: + check = match.group().rstrip() + if check == hdname: + for amatch in lines: + yield amatch.start(), String.Heredoc, amatch.group() + yield match.start(), String.Delimiter, match.group() + ctx.pos = match.end() + break + else: + lines.append(match) + else: + # end of heredoc not found -- error! + for amatch in lines: + yield amatch.start(), Error, amatch.group() + ctx.end = len(ctx.text) + + tokens = { + 'root': [ + include('basic'), + include('whitespace'), + + # Strings + (r'(".*")', bygroups(String.Double)), + + # Constants + (words(('true', 'false'), prefix=r'\b', suffix=r'\b'), Name.Constant), + + # Types + (words(types, prefix=r'\b', suffix=r'\b'), Keyword.Type), + + include('identifier'), + include('punctuation'), + (r'[0-9]+', Number), + ], + 'basic': [ + (r'\s*/\*', Comment.Multiline, 'comment'), + (r'\s*(#|//).*\n', Comment.Single), + include('whitespace'), + + # e.g. terraform { + # e.g. egress { + (r'(\s*)([0-9a-zA-Z-_]+)(\s*)(=?)(\s*)(\{)', + bygroups(Whitespace, Name.Builtin, Whitespace, Operator, Whitespace, Punctuation)), + + # Assignment with attributes, e.g. something = ... + (r'(\s*)([0-9a-zA-Z-_]+)(\s*)(=)(\s*)', + bygroups(Whitespace, Name.Attribute, Whitespace, Operator, Whitespace)), + + # Assignment with environment variables and similar, e.g. "something" = ... + # or key value assignment, e.g. "SlotName" : ... + (r'(\s*)("\S+")(\s*)([=:])(\s*)', + bygroups(Whitespace, Literal.String.Double, Whitespace, Operator, Whitespace)), + + # Functions, e.g. jsonencode(element("value")) + (builtins_re + r'(\()', bygroups(Name.Function, Punctuation)), + + # List of attributes, e.g. ignore_changes = [last_modified, filename] + (r'(\[)([a-z_,\s]+)(\])', bygroups(Punctuation, Name.Builtin, Punctuation)), + + # e.g. resource "aws_security_group" "allow_tls" { + # e.g. backend "consul" { + (classes_re + r'(\s+)("[0-9a-zA-Z-_]+")?(\s*)("[0-9a-zA-Z-_]+")(\s+)(\{)', + bygroups(Keyword.Reserved, Whitespace, Name.Class, Whitespace, Name.Variable, Whitespace, Punctuation)), + + # here-doc style delimited strings + (r'(<<-?)\s*([a-zA-Z_]\w*)(.*?\n)', heredoc_callback), + ], + 'identifier': [ + (r'\b(var\.[0-9a-zA-Z-_\.\[\]]+)\b', bygroups(Name.Variable)), + (r'\b([0-9a-zA-Z-_\[\]]+\.[0-9a-zA-Z-_\.\[\]]+)\b', + bygroups(Name.Variable)), + ], + 'punctuation': [ + (r'[\[\]()\{\},.?:!=]', Punctuation), + ], + 'comment': [ + (r'[^*/]', Comment.Multiline), + (r'/\*', Comment.Multiline, '#push'), + (r'\*/', Comment.Multiline, '#pop'), + (r'[*/]', Comment.Multiline) + ], + 'whitespace': [ + (r'\n', Whitespace), + (r'\s+', Whitespace), + (r'(\\)(\n)', bygroups(Text, Whitespace)), + ], + } + + +class TermcapLexer(RegexLexer): + """ + Lexer for termcap database source. + + This is very simple and minimal. + + .. versionadded:: 2.1 + """ + name = 'Termcap' + aliases = ['termcap'] + filenames = ['termcap', 'termcap.src'] + mimetypes = [] + + # NOTE: + # * multiline with trailing backslash + # * separator is ':' + # * to embed colon as data, we must use \072 + # * space after separator is not allowed (mayve) + tokens = { + 'root': [ + (r'^#.*', Comment), + (r'^[^\s#:|]+', Name.Tag, 'names'), + (r'\s+', Whitespace), + ], + 'names': [ + (r'\n', Whitespace, '#pop'), + (r':', Punctuation, 'defs'), + (r'\|', Punctuation), + (r'[^:|]+', Name.Attribute), + ], + 'defs': [ + (r'(\\)(\n[ \t]*)', bygroups(Text, Whitespace)), + (r'\n[ \t]*', Whitespace, '#pop:2'), + (r'(#)([0-9]+)', bygroups(Operator, Number)), + (r'=', Operator, 'data'), + (r':', Punctuation), + (r'[^\s:=#]+', Name.Class), + ], + 'data': [ + (r'\\072', Literal), + (r':', Punctuation, '#pop'), + (r'[^:\\]+', Literal), # for performance + (r'.', Literal), + ], + } + + +class TerminfoLexer(RegexLexer): + """ + Lexer for terminfo database source. + + This is very simple and minimal. + + .. versionadded:: 2.1 + """ + name = 'Terminfo' + aliases = ['terminfo'] + filenames = ['terminfo', 'terminfo.src'] + mimetypes = [] + + # NOTE: + # * multiline with leading whitespace + # * separator is ',' + # * to embed comma as data, we can use \, + # * space after separator is allowed + tokens = { + 'root': [ + (r'^#.*$', Comment), + (r'^[^\s#,|]+', Name.Tag, 'names'), + (r'\s+', Whitespace), + ], + 'names': [ + (r'\n', Whitespace, '#pop'), + (r'(,)([ \t]*)', bygroups(Punctuation, Whitespace), 'defs'), + (r'\|', Punctuation), + (r'[^,|]+', Name.Attribute), + ], + 'defs': [ + (r'\n[ \t]+', Whitespace), + (r'\n', Whitespace, '#pop:2'), + (r'(#)([0-9]+)', bygroups(Operator, Number)), + (r'=', Operator, 'data'), + (r'(,)([ \t]*)', bygroups(Punctuation, Whitespace)), + (r'[^\s,=#]+', Name.Class), + ], + 'data': [ + (r'\\[,\\]', Literal), + (r'(,)([ \t]*)', bygroups(Punctuation, Whitespace), '#pop'), + (r'[^\\,]+', Literal), # for performance + (r'.', Literal), + ], + } + + +class PkgConfigLexer(RegexLexer): + """ + Lexer for pkg-config + (see also `manual page `_). + + .. versionadded:: 2.1 + """ + + name = 'PkgConfig' + url = 'http://www.freedesktop.org/wiki/Software/pkg-config/' + aliases = ['pkgconfig'] + filenames = ['*.pc'] + mimetypes = [] + + tokens = { + 'root': [ + (r'#.*$', Comment.Single), + + # variable definitions + (r'^(\w+)(=)', bygroups(Name.Attribute, Operator)), + + # keyword lines + (r'^([\w.]+)(:)', + bygroups(Name.Tag, Punctuation), 'spvalue'), + + # variable references + include('interp'), + + # fallback + (r'\s+', Whitespace), + (r'[^${}#=:\n.]+', Text), + (r'.', Text), + ], + 'interp': [ + # you can escape literal "$" as "$$" + (r'\$\$', Text), + + # variable references + (r'\$\{', String.Interpol, 'curly'), + ], + 'curly': [ + (r'\}', String.Interpol, '#pop'), + (r'\w+', Name.Attribute), + ], + 'spvalue': [ + include('interp'), + + (r'#.*$', Comment.Single, '#pop'), + (r'\n', Whitespace, '#pop'), + + # fallback + (r'\s+', Whitespace), + (r'[^${}#\n\s]+', Text), + (r'.', Text), + ], + } + + +class PacmanConfLexer(RegexLexer): + """ + Lexer for pacman.conf. + + Actually, IniLexer works almost fine for this format, + but it yield error token. It is because pacman.conf has + a form without assignment like: + + UseSyslog + Color + TotalDownload + CheckSpace + VerbosePkgLists + + These are flags to switch on. + + .. versionadded:: 2.1 + """ + + name = 'PacmanConf' + url = 'https://www.archlinux.org/pacman/pacman.conf.5.html' + aliases = ['pacmanconf'] + filenames = ['pacman.conf'] + mimetypes = [] + + tokens = { + 'root': [ + # comment + (r'#.*$', Comment.Single), + + # section header + (r'^(\s*)(\[.*?\])(\s*)$', bygroups(Whitespace, Keyword, Whitespace)), + + # variable definitions + # (Leading space is allowed...) + (r'(\w+)(\s*)(=)', + bygroups(Name.Attribute, Whitespace, Operator)), + + # flags to on + (r'^(\s*)(\w+)(\s*)$', + bygroups(Whitespace, Name.Attribute, Whitespace)), + + # built-in special values + (words(( + '$repo', # repository + '$arch', # architecture + '%o', # outfile + '%u', # url + ), suffix=r'\b'), + Name.Variable), + + # fallback + (r'\s+', Whitespace), + (r'.', Text), + ], + } + + +class AugeasLexer(RegexLexer): + """ + Lexer for Augeas. + + .. versionadded:: 2.4 + """ + name = 'Augeas' + url = 'http://augeas.net' + aliases = ['augeas'] + filenames = ['*.aug'] + + tokens = { + 'root': [ + (r'(module)(\s*)([^\s=]+)', bygroups(Keyword.Namespace, Whitespace, Name.Namespace)), + (r'(let)(\s*)([^\s=]+)', bygroups(Keyword.Declaration, Whitespace, Name.Variable)), + (r'(del|store|value|counter|seq|key|label|autoload|incl|excl|transform|test|get|put)(\s+)', bygroups(Name.Builtin, Whitespace)), + (r'(\()([^:]+)(\:)(unit|string|regexp|lens|tree|filter)(\))', bygroups(Punctuation, Name.Variable, Punctuation, Keyword.Type, Punctuation)), + (r'\(\*', Comment.Multiline, 'comment'), + (r'[*+\-.;=?|]', Operator), + (r'[()\[\]{}]', Operator), + (r'"', String.Double, 'string'), + (r'\/', String.Regex, 'regex'), + (r'([A-Z]\w*)(\.)(\w+)', bygroups(Name.Namespace, Punctuation, Name.Variable)), + (r'.', Name.Variable), + (r'\s+', Whitespace), + ], + 'string': [ + (r'\\.', String.Escape), + (r'[^"]', String.Double), + (r'"', String.Double, '#pop'), + ], + 'regex': [ + (r'\\.', String.Escape), + (r'[^/]', String.Regex), + (r'\/', String.Regex, '#pop'), + ], + 'comment': [ + (r'[^*)]', Comment.Multiline), + (r'\(\*', Comment.Multiline, '#push'), + (r'\*\)', Comment.Multiline, '#pop'), + (r'[)*]', Comment.Multiline) + ], + } + + +class TOMLLexer(RegexLexer): + """ + Lexer for TOML, a simple language for config files. + + .. versionadded:: 2.4 + """ + + name = 'TOML' + aliases = ['toml'] + filenames = ['*.toml', 'Pipfile', 'poetry.lock'] + mimetypes = ['application/toml'] + url = 'https://toml.io' + + # Based on the TOML spec: https://toml.io/en/v1.0.0 + + # The following is adapted from CPython's tomllib: + _time = r"\d\d:\d\d:\d\d(\.\d+)?" + _datetime = rf"""(?x) + \d\d\d\d-\d\d-\d\d # date, e.g., 1988-10-27 + ( + [Tt ] {_time} # optional time + ( + [Zz]|[+-]\d\d:\d\d # optional time offset + )? + )? + """ + + tokens = { + 'root': [ + # Note that we make an effort in order to distinguish + # moments at which we're parsing a key and moments at + # which we're parsing a value. In the TOML code + # + # 1234 = 1234 + # + # the first "1234" should be Name, the second Integer. + + # Whitespace + (r'\s+', Whitespace), + + # Comment + (r'#.*', Comment.Single), + + # Assignment keys + include('key'), + + # After "=", find a value + (r'(=)(\s*)', bygroups(Operator, Whitespace), 'value'), + + # Table header + (r'\[\[?', Keyword, 'table-key'), + ], + 'key': [ + # Start of bare key (only ASCII is allowed here). + (r'[A-Za-z0-9_-]+', Name), + # Quoted key + (r'"', String.Double, 'basic-string'), + (r"'", String.Single, 'literal-string'), + # Dots act as separators in keys + (r'\.', Punctuation), + ], + 'table-key': [ + # This is like 'key', but highlights the name components + # and separating dots as Keyword because it looks better + # when the whole table header is Keyword. We do highlight + # strings as strings though. + # Start of bare key (only ASCII is allowed here). + (r'[A-Za-z0-9_-]+', Keyword), + (r'"', String.Double, 'basic-string'), + (r"'", String.Single, 'literal-string'), + (r'\.', Keyword), + (r'\]\]?', Keyword, '#pop'), + + # Inline whitespace allowed + (r'[ \t]+', Whitespace), + ], + 'value': [ + # Datetime, baretime + (_datetime, Literal.Date, '#pop'), + (_time, Literal.Date, '#pop'), + + # Recognize as float if there is a fractional part + # and/or an exponent. + (r'[+-]?\d[0-9_]*[eE][+-]?\d[0-9_]*', Number.Float, '#pop'), + (r'[+-]?\d[0-9_]*\.\d[0-9_]*([eE][+-]?\d[0-9_]*)?', + Number.Float, '#pop'), + + # Infinities and NaN + (r'[+-]?(inf|nan)', Number.Float, '#pop'), + + # Integers + (r'-?0b[01_]+', Number.Bin, '#pop'), + (r'-?0o[0-7_]+', Number.Oct, '#pop'), + (r'-?0x[0-9a-fA-F_]+', Number.Hex, '#pop'), + (r'[+-]?[0-9_]+', Number.Integer, '#pop'), + + # Strings + (r'"""', String.Double, ('#pop', 'multiline-basic-string')), + (r'"', String.Double, ('#pop', 'basic-string')), + (r"'''", String.Single, ('#pop', 'multiline-literal-string')), + (r"'", String.Single, ('#pop', 'literal-string')), + + # Booleans + (r'true|false', Keyword.Constant, '#pop'), + + # Start of array + (r'\[', Punctuation, ('#pop', 'array')), + + # Start of inline table + (r'\{', Punctuation, ('#pop', 'inline-table')), + ], + 'array': [ + # Whitespace, including newlines, is ignored inside arrays, + # and comments are allowed. + (r'\s+', Whitespace), + (r'#.*', Comment.Single), + + # Delimiters + (r',', Punctuation), + + # End of array + (r'\]', Punctuation, '#pop'), + + # Parse a value and come back + default('value'), + ], + 'inline-table': [ + # Note that unlike inline arrays, inline tables do not + # allow newlines or comments. + (r'[ \t]+', Whitespace), + + # Keys + include('key'), + + # Values + (r'(=)(\s*)', bygroups(Punctuation, Whitespace), 'value'), + + # Delimiters + (r',', Punctuation), + + # End of inline table + (r'\}', Punctuation, '#pop'), + ], + 'basic-string': [ + (r'"', String.Double, '#pop'), + include('escapes'), + (r'[^"\\]+', String.Double), + ], + 'literal-string': [ + (r".*?'", String.Single, '#pop'), + ], + 'multiline-basic-string': [ + (r'"""', String.Double, '#pop'), + (r'(\\)(\n)', bygroups(String.Escape, Whitespace)), + include('escapes'), + (r'[^"\\]+', String.Double), + (r'"', String.Double), + ], + 'multiline-literal-string': [ + (r"'''", String.Single, '#pop'), + (r"[^']+", String.Single), + (r"'", String.Single), + ], + 'escapes': [ + (r'\\u[0-9a-fA-F]{4}|\\U[0-9a-fA-F]{8}', String.Escape), + (r'\\.', String.Escape), + ], + } + +class NestedTextLexer(RegexLexer): + """ + Lexer for *NextedText*, a human-friendly data format. + + .. versionadded:: 2.9 + + .. versionchanged:: 2.16 + Added support for *NextedText* v3.0. + """ + + name = 'NestedText' + url = 'https://nestedtext.org' + aliases = ['nestedtext', 'nt'] + filenames = ['*.nt'] + + tokens = { + 'root': [ + # Comment: # ... + (r'^([ ]*)(#.*)$', bygroups(Whitespace, Comment)), + + # Inline dictionary: {...} + (r'^([ ]*)(\{)', bygroups(Whitespace, Punctuation), 'inline_dict'), + + # Inline list: [...] + (r'^([ ]*)(\[)', bygroups(Whitespace, Punctuation), 'inline_list'), + + # empty multiline string item: > + (r'^([ ]*)(>)$', bygroups(Whitespace, Punctuation)), + + # multiline string item: > ... + (r'^([ ]*)(>)( )(.*?)([ \t]*)$', bygroups(Whitespace, Punctuation, Whitespace, Text, Whitespace)), + + # empty list item: - + (r'^([ ]*)(-)$', bygroups(Whitespace, Punctuation)), + + # list item: - ... + (r'^([ ]*)(-)( )(.*?)([ \t]*)$', bygroups(Whitespace, Punctuation, Whitespace, Text, Whitespace)), + + # empty multiline key item: : + (r'^([ ]*)(:)$', bygroups(Whitespace, Punctuation)), + + # multiline key item: : ... + (r'^([ ]*)(:)( )([^\n]*?)([ \t]*)$', bygroups(Whitespace, Punctuation, Whitespace, Name.Tag, Whitespace)), + + # empty dict key item: ...: + (r'^([ ]*)([^\{\[\s].*?)(:)$', bygroups(Whitespace, Name.Tag, Punctuation)), + + # dict key item: ...: ... + (r'^([ ]*)([^\{\[\s].*?)(:)( )(.*?)([ \t]*)$', bygroups(Whitespace, Name.Tag, Punctuation, Whitespace, Text, Whitespace)), + ], + 'inline_list': [ + include('whitespace'), + (r'[^\{\}\[\],\s]+', Text), + include('inline_value'), + (r',', Punctuation), + (r'\]', Punctuation, '#pop'), + (r'\n', Error, '#pop'), + ], + 'inline_dict': [ + include('whitespace'), + (r'[^\{\}\[\],:\s]+', Name.Tag), + (r':', Punctuation, 'inline_dict_value'), + (r'\}', Punctuation, '#pop'), + (r'\n', Error, '#pop'), + ], + 'inline_dict_value': [ + include('whitespace'), + (r'[^\{\}\[\],:\s]+', Text), + include('inline_value'), + (r',', Punctuation, '#pop'), + (r'\}', Punctuation, '#pop:2'), + ], + 'inline_value': [ + include('whitespace'), + (r'\{', Punctuation, 'inline_dict'), + (r'\[', Punctuation, 'inline_list'), + ], + 'whitespace': [ + (r'[ \t]+', Whitespace), + ], + } + + +class SingularityLexer(RegexLexer): + """ + Lexer for Singularity definition files. + + .. versionadded:: 2.6 + """ + + name = 'Singularity' + url = 'https://www.sylabs.io/guides/3.0/user-guide/definition_files.html' + aliases = ['singularity'] + filenames = ['*.def', 'Singularity'] + flags = re.IGNORECASE | re.MULTILINE | re.DOTALL + + _headers = r'^(\s*)(bootstrap|from|osversion|mirrorurl|include|registry|namespace|includecmd)(:)' + _section = r'^(%(?:pre|post|setup|environment|help|labels|test|runscript|files|startscript))(\s*)' + _appsect = r'^(%app(?:install|help|run|labels|env|test|files))(\s*)' + + tokens = { + 'root': [ + (_section, bygroups(Generic.Heading, Whitespace), 'script'), + (_appsect, bygroups(Generic.Heading, Whitespace), 'script'), + (_headers, bygroups(Whitespace, Keyword, Text)), + (r'\s*#.*?\n', Comment), + (r'\b(([0-9]+\.?[0-9]*)|(\.[0-9]+))\b', Number), + (r'[ \t]+', Whitespace), + (r'(?!^\s*%).', Text), + ], + 'script': [ + (r'(.+?(?=^\s*%))|(.*)', using(BashLexer), '#pop'), + ], + } + + def analyse_text(text): + """This is a quite simple script file, but there are a few keywords + which seem unique to this language.""" + result = 0 + if re.search(r'\b(?:osversion|includecmd|mirrorurl)\b', text, re.IGNORECASE): + result += 0.5 + + if re.search(SingularityLexer._section[1:], text): + result += 0.49 + + return result + + +class UnixConfigLexer(RegexLexer): + """ + Lexer for Unix/Linux config files using colon-separated values, e.g. + + * ``/etc/group`` + * ``/etc/passwd`` + * ``/etc/shadow`` + + .. versionadded:: 2.12 + """ + + name = 'Unix/Linux config files' + aliases = ['unixconfig', 'linuxconfig'] + filenames = [] + + tokens = { + 'root': [ + (r'^#.*', Comment), + (r'\n', Whitespace), + (r':', Punctuation), + (r'[0-9]+', Number), + (r'((?!\n)[a-zA-Z0-9\_\-\s\(\),]){2,}', Text), + (r'[^:\n]+', String), + ], + } -- cgit v1.2.3