diff options
-rw-r--r-- | AUTHORS | 2 | ||||
-rw-r--r-- | pygments/lexers/_mapping.py | 1 | ||||
-rw-r--r-- | pygments/lexers/sieve.py | 68 | ||||
-rw-r--r-- | tests/examplefiles/example.sieve | 56 |
4 files changed, 126 insertions, 1 deletions
@@ -32,7 +32,7 @@ Other contributors, listed alphabetically, are: * Sébastien Bigaret -- QVT Operational lexer * Jarrett Billingsley -- MiniD lexer * Adam Blinkinsop -- Haskell, Redcode lexers -* Stéphane Blondon -- SGF lexer +* Stéphane Blondon -- SGF and Sieve lexers * Frits van Bommel -- assembler lexers * Pierre Bourdon -- bugfixes * Martijn Braam -- Kernel log lexer diff --git a/pygments/lexers/_mapping.py b/pygments/lexers/_mapping.py index 1afd5df7..2ae0c375 100644 --- a/pygments/lexers/_mapping.py +++ b/pygments/lexers/_mapping.py @@ -398,6 +398,7 @@ LEXERS = { 'ScssLexer': ('pygments.lexers.css', 'SCSS', ('scss',), ('*.scss',), ('text/x-scss',)), 'ShExCLexer': ('pygments.lexers.rdf', 'ShExC', ('shexc', 'shex'), ('*.shex',), ('text/shex',)), 'ShenLexer': ('pygments.lexers.lisp', 'Shen', ('shen',), ('*.shen',), ('text/x-shen', 'application/x-shen')), + 'SieveLexer': ('pygments.lexers.sieve', 'Sieve', ('sieve',), ('*.siv', '*.sieve'), ()), 'SilverLexer': ('pygments.lexers.verification', 'Silver', ('silver',), ('*.sil', '*.vpr'), ()), 'SlashLexer': ('pygments.lexers.slash', 'Slash', ('slash',), ('*.sl',), ()), 'SlimLexer': ('pygments.lexers.webmisc', 'Slim', ('slim',), ('*.slim',), ('text/x-slim',)), diff --git a/pygments/lexers/sieve.py b/pygments/lexers/sieve.py new file mode 100644 index 00000000..9d7dba35 --- /dev/null +++ b/pygments/lexers/sieve.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- +""" + pygments.lexers.sieve + ~~~~~~~~~~~~~~~~~~~~~ + + Lexer for Sieve file format. + + https://tools.ietf.org/html/rfc5228 + https://tools.ietf.org/html/rfc5173 + https://tools.ietf.org/html/rfc5229 + https://tools.ietf.org/html/rfc5230 + https://tools.ietf.org/html/rfc5232 + https://tools.ietf.org/html/rfc5235 + https://tools.ietf.org/html/rfc5429 + https://tools.ietf.org/html/rfc8580 + + :copyright: Copyright 2006-2019 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +from pygments.lexer import RegexLexer, bygroups +from pygments.token import Comment, Name, Literal, String, Text, Punctuation, Keyword + +__all__ = ["SieveLexer"] + + +class SieveLexer(RegexLexer): + """ + Lexer for sieve format. + """ + name = 'Sieve' + filenames = ['*.siv', '*.sieve'] + + tokens = { + 'root': [ + (r'\s+', Text), + (r'[();,{}\[\]]', Punctuation), + # import: + (r'(?i)require', + Keyword.Namespace), + # tags: + (r'(?i)(:)(addresses|all|contains|content|create|copy|comparator|count|days|detail|domain|fcc|flags|from|handle|importance|is|localpart|length|lowerfirst|lower|matches|message|mime|options|over|percent|quotewildcard|raw|regex|specialuse|subject|text|under|upperfirst|upper|value)', + bygroups(Name.Tag, Name.Tag)), + # tokens: + (r'(?i)(address|addflag|allof|anyof|body|discard|elsif|else|envelope|ereject|exists|false|fileinto|if|hasflag|header|keep|notify_method_capability|notify|not|redirect|reject|removeflag|setflag|size|spamtest|stop|string|true|vacation|virustest)', + Name.Builtin), + (r'(?i)set', + Keyword.Declaration), + # number: + (r'([0-9.]+)([kmgKMG])?', + bygroups(Literal.Number, Literal.Number)), + # comment: + (r'#.*$', + Comment.Single), + (r'/\*.*\*/', + Comment.Multiline), + # string: + (r'"[^"]*?"', + String), + # text block: + (r'text:', + Name.Tag, 'text'), + ], + 'text': [ + (r'[^.].*?\n', String), + (r'^\.', Punctuation, "#pop"), + ] + } diff --git a/tests/examplefiles/example.sieve b/tests/examplefiles/example.sieve new file mode 100644 index 00000000..8abed421 --- /dev/null +++ b/tests/examplefiles/example.sieve @@ -0,0 +1,56 @@ +# Based on examples included the RFC files. +# It doesn't show cases for every token parsed by the lexer. + +require ["fileinto", "envelope"]; + +if address :is "to" "demo@example.org" { + fileinto "demo-list"; +} elsif envelope :is "from" "owner@example.org" { + fileinto "lists.cipe"; +} elsif (exists "X-Cron-Env", + header :regex ["subject"] [".* A", + ".* B"]) { + addflag "Cron"; + stop; +} else { + keep; +} + +if body :text :contains "project schedule" { + fileinto "project/schedule"; +} + +require "vacation"; +if header :contains "subject" "lunch" { + vacation :handle "ran-away" "I'm out and can't meet for lunch"; +} else { + vacation :handle "ran-away" "I'm out"; +} + + +if virustest :value "eq" :comparator "i;ascii-numeric" "0" { + fileinto "Unscanned"; + /* Infected with high probability (value range in 1-5) */ +} + +if envelope :detail "to" "spam"{ + fileinto "Spam"; +} + +if size :over 100k { + discard; +} +elsif address :DOMAIN :is ["From", "To"] "example.com" +{ + keep; +} + +require ["reject"]; + +if size :over 1M { + reject text: +Your message is too big. If you want to send me a big attachment, +put it on a public web site and send me a URL. +. +; +} |