| /**************************************************************** |
| * Licensed to the Apache Software Foundation (ASF) under one * |
| * or more contributor license agreements. See the NOTICE file * |
| * distributed with this work for additional information * |
| * regarding copyright ownership. The ASF licenses this file * |
| * to you under the Apache License, Version 2.0 (the * |
| * "License"); you may not use this file except in compliance * |
| * with the License. You may obtain a copy of the License at * |
| * * |
| * http://www.apache.org/licenses/LICENSE-2.0 * |
| * * |
| * Unless required by applicable law or agreed to in writing, * |
| * software distributed under the License is distributed on an * |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * |
| * KIND, either express or implied. See the License for the * |
| * specific language governing permissions and limitations * |
| * under the License. * |
| ****************************************************************/ |
| |
| options { |
| MULTI=true; |
| VISITOR=true; |
| VISITOR_EXCEPTION="org.apache.jsieve.exception.SieveException"; |
| NODE_PACKAGE="org.apache.jsieve.parser.generated"; |
| NODE_DEFAULT_VOID=false; |
| STATIC = false; |
| JAVA_UNICODE_ESCAPE = true; |
| DEBUG_PARSER = false; |
| OUTPUT_DIRECTORY="./org/apache/jsieve/parser/generated"; |
| NODE_EXTENDS="org.apache.jsieve.parser.SieveNode"; |
| NODE_SCOPE_HOOK=true; |
| JDK_VERSION="1.5"; |
| } |
| |
| PARSER_BEGIN(SieveParser) |
| |
| package org.apache.jsieve.parser.generated; |
| |
| import org.apache.jsieve.*; |
| import org.apache.jsieve.parser.*; |
| |
| public class SieveParser { |
| |
| public void jjtreeOpenNodeScope(Node n) { |
| ((SieveNode) n).setFirstToken(getToken(1)); |
| } |
| |
| public void jjtreeCloseNodeScope(Node n) { |
| ((SieveNode) n).setLastToken(getToken(0)); |
| } |
| } |
| |
| PARSER_END(SieveParser) |
| |
| /***************************************** |
| * THE SIEVE LANGUAGE TOKENS STARTS HERE * |
| *****************************************/ |
| |
| SKIP : /* WHITE SPACE */ |
| { |
| " " |
| | "\t" |
| | "\n" |
| | "\r" |
| | "\f" |
| } |
| |
| SPECIAL_TOKEN : /* COMMENTS */ |
| { |
| <HASH_COMMENT: "#" (~["\n","\r"])* ("\n"|"\r"|"\r\n")> |
| | |
| <BRACKET_COMMENT: "/*" (~["*"])* "*" ("*" | (~["*","/"] (~["*"])* "*"))* "/"> |
| } |
| |
| TOKEN : /* Can't make this one special */ |
| { |
| <EOF_HASH_COMMENT: "#" (~["\n","\r"])*> |
| } |
| |
| |
| // identifier = (ALPHA / "_") *(ALPHA DIGIT "_") |
| TOKEN : /* IDENTIFIER */ |
| { |
| <IDENTIFIER: (<ALPHA>|"_") (<ALPHA>|<DIGIT>|"_")*> |
| | |
| < #ALPHA: |
| [ |
| "\u0041"-"\u005a", |
| "\u0061"-"\u007a" |
| ] |
| > |
| | |
| < #DIGIT: |
| [ |
| "\u0030"-"\u0039" |
| ] |
| > |
| } |
| |
| // tag = ":" identifier |
| TOKEN : /* TAG */ |
| { |
| <TAG: ":" <IDENTIFIER> > |
| } |
| |
| TOKEN : /* LITERALS */ |
| { |
| // number = 1*DIGIT [QUANTIFIER] |
| // QUANTIFIER = "K" / "M" / "G" |
| < NUMBER: |
| <DECIMAL_LITERAL> (<QUANTIFIER>)? |
| > |
| | |
| < #DECIMAL_LITERAL: (["0"-"9"])+ > |
| | |
| < #QUANTIFIER: ["K","M","G"] > |
| | |
| // quoted-string = DQUOTE *CHAR DQUOTE |
| // in general, \ CHAR inside a string maps to CHAR |
| // so \" maps to " and \\ maps to \ |
| // note that newlines and other characters are all allowed strings |
| < QUOTED_STRING: |
| "\"" |
| (~["\""]|"\\\"")* |
| "\"" |
| > |
| | |
| // multi-line = "text:" *(SP / HTAB) (hash-comment / CRLF) |
| // *(multi-line-literal / multi-line-dotstuff) |
| // "." CRLF |
| // Hmm. What we need to do is treat (CRLF / LF / CR) as <NEWLINE> throughout |
| < MULTI_LINE: |
| <MULTI_LINE_START> |
| (<MULTI_LINE_LITERAL> |<MULTI_LINE_DOTSTUFF>)* |
| <MULTI_LINE_END> |
| > |
| | |
| <#MULTI_LINE_START: |
| ("text:") |
| ([" ", "\t"])* |
| (<HASH_COMMENT>|<NEWLINE>) |
| > |
| | |
| <#MULTI_LINE_END: |
| ("." <NEWLINE>) |
| > |
| | |
| <#NEWLINE: |
| ("\n"|"\r"|"\r\n") |
| > |
| | |
| // multi-line-literal = [CHAR-NOT-DOT *CHAR_NOT_NEWLINE] NEWLINE |
| < #MULTI_LINE_LITERAL: |
| (<CHAR_NOT_DOT> (<CHAR_NOT_NEWLINE>)*)? |
| <NEWLINE> |
| > |
| | |
| < #CHAR_NOT_DOT: |
| (~["."]) |
| > |
| | |
| < #CHAR_NOT_NEWLINE: |
| (~["\n"] | ("\r" ~["\n"])) |
| > |
| | |
| // multi-line-dotstuff = "." 1*CHAR-NOT-CRLF CRLF |
| // A line containing only "." ends the multi-line. |
| // Remove a leading '.' if followed by another '.'. |
| < #MULTI_LINE_DOTSTUFF: |
| "." |
| (<CHAR_NOT_NEWLINE>)+ |
| <NEWLINE> |
| > |
| } |
| |
| /****************************************** |
| * THE SIEVE LANGUAGE GRAMMAR STARTS HERE * |
| ******************************************/ |
| |
| // start = commands |
| SimpleNode start() : |
| { } |
| { |
| commands() |
| (<EOF_HASH_COMMENT>)? // Allow a Hash comment immediately prior to EOF |
| <EOF> |
| { return jjtThis; } |
| } |
| |
| // commands = *command |
| void commands() : |
| { } |
| { |
| (command())* |
| } |
| |
| // command = identifier arguments ( ";" / block ) |
| void command() : |
| { Token identifier = null; } |
| { |
| (identifier = <IDENTIFIER>) arguments() (";" | block()) |
| { |
| jjtThis.setName(identifier.image); |
| } |
| } |
| |
| // block = "{" commands "}" |
| void block() : |
| { } |
| { |
| "{" commands() "}" |
| } |
| |
| // arguments = *argument [test / test-list] |
| void arguments() : |
| { } |
| { |
| (argument())* (test() | test_list())? |
| } |
| |
| // argument = string-list / number / tag |
| void argument() : |
| { Token string_list = null, number = null, tag = null; } |
| { |
| (string_list() | number = <NUMBER> | tag = <TAG>) |
| { |
| Argument value = null; |
| if (null != number) |
| value = new NumberArgument(number); |
| else if (null != tag) |
| value = new TagArgument(tag); |
| jjtThis.setValue(value); |
| } |
| } |
| |
| // test = identifier arguments |
| void test() : |
| { Token identifier = null; } |
| { |
| (identifier = <IDENTIFIER> arguments()) |
| { |
| jjtThis.setName(identifier.image); |
| } |
| } |
| |
| // test-list = "(" test *("," test) ")" |
| void test_list() : |
| { } |
| { |
| "(" test() ("," test())* ")" |
| } |
| |
| // string = quoted-string / multi-line |
| void string() : |
| { Token quoted_string = null, multi_line = null;} |
| { |
| (quoted_string = <QUOTED_STRING> | multi_line = <MULTI_LINE>) |
| { |
| final StringBuilder builder; |
| if (null != quoted_string) { |
| builder=new StringBuilder(quoted_string.image); |
| // unquote |
| builder.deleteCharAt(builder.length() - 1); |
| } else if (null != multi_line) { |
| builder=new StringBuilder(multi_line.image); |
| // remove prefixing 'text' and whitespace |
| while (builder.length()>0 && builder.charAt(0) != '\n') { |
| builder.deleteCharAt(0); |
| } |
| // remove suffixing newline-dot-newline |
| builder.deleteCharAt(builder.length() - 1); |
| builder.deleteCharAt(builder.length() - 1); |
| builder.deleteCharAt(builder.length() - 1); |
| |
| int nextStuffedDot = builder.indexOf("\n.."); |
| while (nextStuffedDot >= 0) { |
| builder.deleteCharAt(nextStuffedDot+1); |
| nextStuffedDot = builder.indexOf("\n..", nextStuffedDot + 2); |
| } |
| } else { |
| builder=null; |
| } |
| if (builder != null) { |
| // Unescape |
| builder.deleteCharAt(0); |
| int i = 0; |
| while (i < builder.length()) { |
| if ('\\' == builder.charAt(i)) { |
| builder.deleteCharAt(i); |
| } |
| i++; |
| } |
| jjtThis.setValue(builder.toString()); |
| } |
| } |
| } |
| |
| // string-list = "[" string *("," string) "]" / string ;; if |
| // there is only a single string, the brackets are optional |
| void string_list() : |
| { } |
| { |
| ("[" string() ("," string())* "]") | string() |
| } |
| |
| |
| |
| |