/**************************************************************** * 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 */ { | } TOKEN : /* Can't make this one special */ { } // identifier = (ALPHA / "_") *(ALPHA DIGIT "_") TOKEN : /* IDENTIFIER */ { |"_") (||"_")*> | < #ALPHA: [ "\u0041"-"\u005a", "\u0061"-"\u007a" ] > | < #DIGIT: [ "\u0030"-"\u0039" ] > } // tag = ":" identifier TOKEN : /* TAG */ { > } TOKEN : /* LITERALS */ { // number = 1*DIGIT [QUANTIFIER] // QUANTIFIER = "K" / "M" / "G" < NUMBER: ()? > | < #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: "\"" (~["\"","\\"]|)* "\"" > | <#ESCAPED_CHAR: ("\\" ("\\"|"\"")) > | // 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 throughout < MULTI_LINE: ( |)* > | <#MULTI_LINE_START: ("text:") ([" ", "\t"])* (|) > | <#MULTI_LINE_END: ("." ) > | <#CRLF: ("\r\n") > | <#NEWLINE: ("\n"|"\r"|"\r\n") > | // multi-line-literal = [CHAR-NOT-DOT *CHAR_NOT_NEWLINE] NEWLINE < #MULTI_LINE_LITERAL: ( ()*)? > | < #CHAR_NOT_DOT: (~[".","\n","\r"]) > | < #CHAR_NOT_NEWLINE: (~["\n","\r"]) > | // 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: "." ()+ > } /****************************************** * THE SIEVE LANGUAGE GRAMMAR STARTS HERE * ******************************************/ // start = commands SimpleNode start() : { } { commands() ()? // Allow a Hash comment immediately prior to EOF { return jjtThis; } } // commands = *command void commands() : { } { (command())* } // command = identifier arguments ( ";" / block ) void command() : { Token identifier = null; } { (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 = | 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 = 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 = | 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 CRLF-dot-CRLF builder.deleteCharAt(builder.length() - 1); builder.deleteCharAt(builder.length() - 1); 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() }