1# -*- coding: utf-8 -*-
2"""
3    pygments.lexers.varnish
4    ~~~~~~~~~~~~~~~~~~~~~~~
5
6    Lexers for Varnish configuration
7
8    :copyright: Copyright 2006-2017 by the Pygments team, see AUTHORS.
9    :license: BSD, see LICENSE for details.
10"""
11
12from pygments.lexer import RegexLexer, include, bygroups, using, this, \
13    inherit, words
14from pygments.token import Text, Comment, Operator, Keyword, Name, String, \
15    Number, Punctuation, Literal
16
17__all__ = ['VCLLexer', 'VCLSnippetLexer']
18
19
20class VCLLexer(RegexLexer):
21    """
22    For Varnish Configuration Language (VCL).
23
24    .. versionadded:: 2.2
25    """
26    name = 'VCL'
27    aliases = ['vcl']
28    filenames = ['*.vcl']
29    mimetypes = ['text/x-vclsrc']
30
31    def analyse_text(text):
32        # If the very first line is 'vcl 4.0;' it's pretty much guaranteed
33        # that this is VCL
34        if text.startswith('vcl 4.0;'):
35            return 1.0
36        # Skip over comments and blank lines
37        # This is accurate enough that returning 0.9 is reasonable.
38        # Almost no VCL files start without some comments.
39        elif '\nvcl 4\.0;' in text[:1000]:
40            return 0.9
41
42    tokens = {
43        'probe': [
44            include('whitespace'),
45            include('comments'),
46            (r'(\.\w+)(\s*=\s*)([^;]*)(;)',
47             bygroups(Name.Attribute, Operator, using(this), Punctuation)),
48            (r'\}', Punctuation, '#pop'),
49        ],
50        'acl': [
51            include('whitespace'),
52            include('comments'),
53            (r'[!/]+', Operator),
54            (r';', Punctuation),
55            (r'\d+', Number),
56            (r'\}', Punctuation, '#pop'),
57        ],
58        'backend': [
59            include('whitespace'),
60            (r'(\.probe)(\s*=\s*)(\w+)(;)',
61             bygroups(Name.Attribute, Operator, Name.Variable.Global, Punctuation)),
62            (r'(\.probe)(\s*=\s*)(\{)',
63             bygroups(Name.Attribute, Operator, Punctuation), 'probe'),
64            (r'(\.\w+\b)(\s*=\s*)([^;]*)(\s*;)',
65             bygroups(Name.Attribute, Operator, using(this), Punctuation)),
66            (r'\{', Punctuation, '#push'),
67            (r'\}', Punctuation, '#pop'),
68        ],
69        'statements': [
70            (r'(\d\.)?\d+[sdwhmy]', Literal.Date),
71            (r'(\d\.)?\d+ms', Literal.Date),
72            (r'(vcl_pass|vcl_hash|vcl_hit|vcl_init|vcl_backend_fetch|vcl_pipe|'
73             r'vcl_backend_response|vcl_synth|vcl_deliver|vcl_backend_error|'
74             r'vcl_fini|vcl_recv|vcl_purge|vcl_miss)\b', Name.Function),
75            (r'(pipe|retry|hash|synth|deliver|purge|abandon|lookup|pass|fail|ok|'
76             r'miss|fetch|restart)\b', Name.Constant),
77            (r'(beresp|obj|resp|req|req_top|bereq)\.http\.[a-zA-Z_-]+\b', Name.Variable),
78            (words((
79                'obj.status', 'req.hash_always_miss', 'beresp.backend', 'req.esi_level',
80                'req.can_gzip', 'beresp.ttl', 'obj.uncacheable', 'req.ttl', 'obj.hits',
81                'client.identity', 'req.hash_ignore_busy', 'obj.reason', 'req.xid',
82                'req_top.proto', 'beresp.age', 'obj.proto', 'obj.age', 'local.ip',
83                'beresp.uncacheable', 'req.method', 'beresp.backend.ip', 'now',
84                'obj.grace', 'req.restarts', 'beresp.keep', 'req.proto', 'resp.proto',
85                'bereq.xid', 'bereq.between_bytes_timeout', 'req.esi',
86                'bereq.first_byte_timeout', 'bereq.method', 'bereq.connect_timeout',
87                'beresp.do_gzip',  'resp.status', 'beresp.do_gunzip',
88                'beresp.storage_hint', 'resp.is_streaming', 'beresp.do_stream',
89                'req_top.method', 'bereq.backend', 'beresp.backend.name', 'beresp.status',
90                'req.url', 'obj.keep', 'obj.ttl', 'beresp.reason', 'bereq.retries',
91                'resp.reason', 'bereq.url', 'beresp.do_esi', 'beresp.proto', 'client.ip',
92                'bereq.proto', 'server.hostname', 'remote.ip', 'req.backend_hint',
93                'server.identity', 'req_top.url', 'beresp.grace', 'beresp.was_304',
94                'server.ip', 'bereq.uncacheable'), suffix=r'\b'),
95             Name.Variable),
96            (r'[!%&+*\-,/<.}{>=|~]+', Operator),
97            (r'[();]', Punctuation),
98
99            (r'[,]+', Punctuation),
100            (words(('hash_data', 'regsub', 'regsuball', 'if', 'else',
101                    'elsif', 'elif', 'synth', 'synthetic', 'ban',
102                    'return', 'set', 'unset', 'import', 'include', 'new',
103                    'rollback', 'call'), suffix=r'\b'),
104             Keyword),
105            (r'storage\.\w+\.\w+\b', Name.Variable),
106            (words(('true', 'false')), Name.Builtin),
107            (r'\d+\b', Number),
108            (r'(backend)(\s+\w+)(\s*\{)',
109             bygroups(Keyword, Name.Variable.Global, Punctuation), 'backend'),
110            (r'(probe\s)(\s*\w+\s)(\{)',
111             bygroups(Keyword, Name.Variable.Global, Punctuation), 'probe'),
112            (r'(acl\s)(\s*\w+\s)(\{)',
113             bygroups(Keyword, Name.Variable.Global, Punctuation), 'acl'),
114            (r'(vcl )(4.0)(;)$',
115             bygroups(Keyword.Reserved, Name.Constant, Punctuation)),
116            (r'(sub\s+)([a-zA-Z]\w*)(\s*\{)',
117                bygroups(Keyword, Name.Function, Punctuation)),
118            (r'([a-zA-Z_]\w*)'
119             r'(\.)'
120             r'([a-zA-Z_]\w*)'
121             r'(\s*\(.*\))',
122             bygroups(Name.Function, Punctuation, Name.Function, using(this))),
123            ('[a-zA-Z_]\w*', Name),
124        ],
125        'comment': [
126            (r'[^*/]+', Comment.Multiline),
127            (r'/\*', Comment.Multiline, '#push'),
128            (r'\*/', Comment.Multiline, '#pop'),
129            (r'[*/]', Comment.Multiline),
130        ],
131        'comments': [
132            (r'#.*$', Comment),
133            (r'/\*', Comment.Multiline, 'comment'),
134            (r'//.*$', Comment),
135        ],
136        'string': [
137            (r'"', String, '#pop'),
138            (r'[^"\n]+', String),  # all other characters
139        ],
140        'multistring': [
141            (r'[^"}]', String),
142            (r'"\}', String, '#pop'),
143            (r'["}]', String),
144        ],
145        'whitespace': [
146            (r'L?"', String, 'string'),
147            (r'\{"', String, 'multistring'),
148            (r'\n', Text),
149            (r'\s+', Text),
150            (r'\\\n', Text),  # line continuation
151        ],
152        'root': [
153            include('whitespace'),
154            include('comments'),
155            include('statements'),
156            (r'\s+', Text),
157        ],
158    }
159
160
161class VCLSnippetLexer(VCLLexer):
162    """
163    For Varnish Configuration Language snippets.
164
165    .. versionadded:: 2.2
166    """
167    name = 'VCLSnippets'
168    aliases = ['vclsnippets', 'vclsnippet']
169    mimetypes = ['text/x-vclsnippet']
170    filenames = []
171
172    def analyse_text(text):
173        # override method inherited from VCLLexer
174        return 0
175
176    tokens = {
177        'snippetspre': [
178            (r'\.\.\.+', Comment),
179            (r'(bereq|req|req_top|resp|beresp|obj|client|server|local|remote|'
180             r'storage)($|\.\*)', Name.Variable),
181        ],
182        'snippetspost': [
183            (r'(backend)\b', Keyword.Reserved),
184        ],
185        'root': [
186            include('snippetspre'),
187            inherit,
188            include('snippetspost'),
189        ],
190    }
191