/**
 * 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.
 *
 * Some portions of this file were modeled after the example Java 1.5
 * parser included with JavaCC. The following license applies to those
 * portions:
 *
 * Copyright (c) 2006, Sun Microsystems, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright notice,
 *       this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the Sun Microsystems, Inc. nor the names of its
 *       contributors may be used to endorse or promote products derived from
 *       this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */

options {
  JAVA_UNICODE_ESCAPE = true;
  UNICODE_INPUT = true;
  ERROR_REPORTING = true;
  STATIC = false;
  FORCE_LA_CHECK = true;
  JDK_VERSION = "1.6";
}

PARSER_BEGIN(Idl)

package org.apache.avro.compiler.idl;

import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.net.URL;

import org.apache.avro.Schema;
import org.apache.avro.Schema.*;
import org.apache.avro.Protocol;
import org.apache.avro.Protocol.*;

import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.node.*;

import org.apache.commons.lang.StringEscapeUtils;

/**
 * Grammar to parse a higher-level language into an Avro Schema.
 *
 * Note: each instance is not thread-safe, but multiple separate
 * instances are safely independent.
 */
public class Idl implements Closeable {
  static JsonNodeFactory FACTORY = JsonNodeFactory.instance;

  File inputDir = new File(".");
  ClassLoader resourceLoader = null;
  String namespace;
  Map<String,Schema> names = new LinkedHashMap<String,Schema>();

  private static final ThreadLocal<String> DOC = new ThreadLocal<String>();
  static void setDoc(String doc) { DOC.set(doc.trim()); }
  static String getDoc() {
    String doc = DOC.get();
    DOC.set(null);
    return doc;
  }

  public Idl(File inputFile) throws IOException {
    this(new FileInputStream(inputFile), "UTF-8");
    this.inputDir = inputFile.getParentFile();
  }

  public Idl(File inputFile, ClassLoader resourceLoader) throws IOException {
    this(inputFile);
    this.resourceLoader = resourceLoader;
  }
  
  private Idl(URL input, Idl parent) throws IOException {
    this(input.openStream(), "UTF-8");
    this.inputDir = "file".equals(input.getProtocol())
      ? new File(input.getPath()).getParentFile()
      : parent.inputDir;
    this.resourceLoader = parent.resourceLoader;
  }
  
  public void close() throws IOException {
    jj_input_stream.inputStream.close();
  }

  private ParseException error(String message, Token token) {
    return new ParseException
      (message+", at line "+token.beginLine+", column "+token.beginColumn);
  }

  private String getTextProp(String key, Map<String,JsonNode> props,
                             Token token) throws ParseException {
    JsonNode value = props.get(key);
    if (value.isTextual())
      return value.getTextValue();
    throw error(key+" property must be textual: "+value, token);
  }

  private List<String> getTextProps(String key, Map<String,JsonNode> props,
                             Token token) throws ParseException {
    JsonNode value = props.get(key);
    if (!value.isArray())
      throw error(key+" property must be array: "+value, token);
    List<String> values = new ArrayList<String>();
    for (JsonNode n : value)
      if (n.isTextual())
        values.add(n.getTextValue());
      else
        throw error(key+" values must be textual: "+n, token);
    return values;
  }
  
  private URL findFile(String importFile) throws IOException {
    File file = new File(this.inputDir, importFile);
    URL result = null;
    if (file.exists())
      result = file.toURL();
    else if (this.resourceLoader != null)
      result = this.resourceLoader.getResource(importFile);
    if (result == null)
      throw new FileNotFoundException(importFile);
    return result;
  }

}

PARSER_END(Idl)

/* WHITE SPACE */

SKIP :
{
  " "
| "\t"
| "\n"
| "\r"
| "\f"
}

/* COMMENTS */

SKIP :
{
  <SINGLE_LINE_COMMENT: "//" (~["\n", "\r"])* ("\n" | "\r" | "\r\n")?>
}

SKIP :
{
  <"/**" ~["/"]> { input_stream.backup(1); } : DOC_COMMENT
|
  "/*" : MULTI_LINE_COMMENT
}

<DOC_COMMENT,MULTI_LINE_COMMENT>
MORE :
{
  < ~[] >
}

<DOC_COMMENT>
SPECIAL_TOKEN :
{
  <"*/" > {Idl.setDoc(image.substring(0, image.length()-2));} : DEFAULT
}

<MULTI_LINE_COMMENT>
SKIP :
{
  <"*/" > : DEFAULT
}

/* RESERVED WORDS AND LITERALS */

TOKEN :
{
  < ARRAY: "array" >
| < BOOLEAN: "boolean" >
| < DOUBLE: "double" >
| < ENUM: "enum" >
| < ERROR: "error" >
| < FALSE: "false" >
| < FIXED: "fixed" >
| < FLOAT: "float" >
| < IDL: "idl" >
| < IMPORT: "import" >
| < INT: "int" >
| < LONG: "long" >
| < MAP: "map" >
| < ONEWAY: "oneway" >
| < BYTES: "bytes" >
| < SCHEMA: "schema" >
| < STRING: "string" >
| < NULL: "null" >
| < PROTOCOL: "protocol" >
| < RECORD: "record" >
| < THROWS: "throws" >
| < TRUE: "true" >
| < UNION: "union" >
| < VOID: "void" >
}

/* LITERALS */

TOKEN :
{
  < INTEGER_LITERAL:
  ("-")?
    (   <DECIMAL_LITERAL> (["l","L"])?
      | <HEX_LITERAL> (["l","L"])?
      | <OCTAL_LITERAL> (["l","L"])?
        )  
  >
|
  < #DECIMAL_LITERAL: ["1"-"9"] (["0"-"9"])* >
|
  < #HEX_LITERAL: "0" ["x","X"] (["0"-"9","a"-"f","A"-"F"])+ >
|
  < #OCTAL_LITERAL: "0" (["0"-"7"])* >
|
  < FLOATING_POINT_LITERAL:
  ("-")?
    ( "NaN" | "Infinity" |
      <DECIMAL_FLOATING_POINT_LITERAL> | <HEXADECIMAL_FLOATING_POINT_LITERAL> )
  >
|
  < #DECIMAL_FLOATING_POINT_LITERAL:
        (["0"-"9"])+ "." (["0"-"9"])* (<DECIMAL_EXPONENT>)? (["f","F","d","D"])?
      | "." (["0"-"9"])+ (<DECIMAL_EXPONENT>)? (["f","F","d","D"])?
      | (["0"-"9"])+ <DECIMAL_EXPONENT> (["f","F","d","D"])?
      | (["0"-"9"])+ (<DECIMAL_EXPONENT>)? ["f","F","d","D"]
  >
|
  < #DECIMAL_EXPONENT: ["e","E"] (["+","-"])? (["0"-"9"])+ >
|
  < #HEXADECIMAL_FLOATING_POINT_LITERAL:
        "0" ["x", "X"] (["0"-"9","a"-"f","A"-"F"])+ (".")? <HEXADECIMAL_EXPONENT> (["f","F","d","D"])?
      | "0" ["x", "X"] (["0"-"9","a"-"f","A"-"F"])* "." (["0"-"9","a"-"f","A"-"F"])+ <HEXADECIMAL_EXPONENT> (["f","F","d","D"])?
  >
|
  < #HEXADECIMAL_EXPONENT: ["p","P"] (["+","-"])? (["0"-"9"])+ >
|
  < CHARACTER_LITERAL:
      "'"
      (   (~["'","\\","\n","\r"])
        | ("\\"
            ( ["n","t","b","r","f","\\","'","\""]
            | ["0"-"7"] ( ["0"-"7"] )?
            | ["0"-"3"] ["0"-"7"] ["0"-"7"]
            )
          )
      )
      "'"
  >
|
  < STRING_LITERAL:
      "\""
      (   (~["\"","\\","\n","\r"])
        | ("\\"
            ( ["n","t","b","r","f","\\","'","\""]
            | ["0"-"7"] ( ["0"-"7"] )?
            | ["0"-"3"] ["0"-"7"] ["0"-"7"]
            )
          )
      )*
      "\""
  >
}

/* IDENTIFIERS */

TOKEN :
{
  < IDENTIFIER: <LETTER> (<PART_LETTER>)* >
|
  < #LETTER:
      [  // all chars for which Character.isIdentifierStart is true
         "$",
         "A"-"Z",
         "_",
         "a"-"z",
         "\u00a2"-"\u00a5",
         "\u00aa",
         "\u00b5",
         "\u00ba",
         "\u00c0"-"\u00d6",
         "\u00d8"-"\u00f6",
         "\u00f8"-"\u021f",
         "\u0222"-"\u0233",
         "\u0250"-"\u02ad",
         "\u02b0"-"\u02b8",
         "\u02bb"-"\u02c1",
         "\u02d0"-"\u02d1",
         "\u02e0"-"\u02e4",
         "\u02ee",
         "\u037a",
         "\u0386",
         "\u0388"-"\u038a",
         "\u038c",
         "\u038e"-"\u03a1",
         "\u03a3"-"\u03ce",
         "\u03d0"-"\u03d7",
         "\u03da"-"\u03f3",
         "\u0400"-"\u0481",
         "\u048c"-"\u04c4",
         "\u04c7"-"\u04c8",
         "\u04cb"-"\u04cc",
         "\u04d0"-"\u04f5",
         "\u04f8"-"\u04f9",
         "\u0531"-"\u0556",
         "\u0559",
         "\u0561"-"\u0587",
         "\u05d0"-"\u05ea",
         "\u05f0"-"\u05f2",
         "\u0621"-"\u063a",
         "\u0640"-"\u064a",
         "\u0671"-"\u06d3",
         "\u06d5",
         "\u06e5"-"\u06e6",
         "\u06fa"-"\u06fc",
         "\u0710",
         "\u0712"-"\u072c",
         "\u0780"-"\u07a5",
         "\u0905"-"\u0939",
         "\u093d",
         "\u0950",
         "\u0958"-"\u0961",
         "\u0985"-"\u098c",
         "\u098f"-"\u0990",
         "\u0993"-"\u09a8",
         "\u09aa"-"\u09b0",
         "\u09b2",
         "\u09b6"-"\u09b9",
         "\u09dc"-"\u09dd",
         "\u09df"-"\u09e1",
         "\u09f0"-"\u09f3",
         "\u0a05"-"\u0a0a",
         "\u0a0f"-"\u0a10",
         "\u0a13"-"\u0a28",
         "\u0a2a"-"\u0a30",
         "\u0a32"-"\u0a33",
         "\u0a35"-"\u0a36",
         "\u0a38"-"\u0a39",
         "\u0a59"-"\u0a5c",
         "\u0a5e",
         "\u0a72"-"\u0a74",
         "\u0a85"-"\u0a8b",
         "\u0a8d",
         "\u0a8f"-"\u0a91",
         "\u0a93"-"\u0aa8",
         "\u0aaa"-"\u0ab0",
         "\u0ab2"-"\u0ab3",
         "\u0ab5"-"\u0ab9",
         "\u0abd",
         "\u0ad0",
         "\u0ae0",
         "\u0b05"-"\u0b0c",
         "\u0b0f"-"\u0b10",
         "\u0b13"-"\u0b28",
         "\u0b2a"-"\u0b30",
         "\u0b32"-"\u0b33",
         "\u0b36"-"\u0b39",
         "\u0b3d",
         "\u0b5c"-"\u0b5d",
         "\u0b5f"-"\u0b61",
         "\u0b85"-"\u0b8a",
         "\u0b8e"-"\u0b90",
         "\u0b92"-"\u0b95",
         "\u0b99"-"\u0b9a",
         "\u0b9c",
         "\u0b9e"-"\u0b9f",
         "\u0ba3"-"\u0ba4",
         "\u0ba8"-"\u0baa",
         "\u0bae"-"\u0bb5",
         "\u0bb7"-"\u0bb9",
         "\u0c05"-"\u0c0c",
         "\u0c0e"-"\u0c10",
         "\u0c12"-"\u0c28",
         "\u0c2a"-"\u0c33",
         "\u0c35"-"\u0c39",
         "\u0c60"-"\u0c61",
         "\u0c85"-"\u0c8c",
         "\u0c8e"-"\u0c90",
         "\u0c92"-"\u0ca8",
         "\u0caa"-"\u0cb3",
         "\u0cb5"-"\u0cb9",
         "\u0cde",
         "\u0ce0"-"\u0ce1",
         "\u0d05"-"\u0d0c",
         "\u0d0e"-"\u0d10",
         "\u0d12"-"\u0d28",
         "\u0d2a"-"\u0d39",
         "\u0d60"-"\u0d61",
         "\u0d85"-"\u0d96",
         "\u0d9a"-"\u0db1",
         "\u0db3"-"\u0dbb",
         "\u0dbd",
         "\u0dc0"-"\u0dc6",
         "\u0e01"-"\u0e30",
         "\u0e32"-"\u0e33",
         "\u0e3f"-"\u0e46",
         "\u0e81"-"\u0e82",
         "\u0e84",
         "\u0e87"-"\u0e88",
         "\u0e8a",
         "\u0e8d",
         "\u0e94"-"\u0e97",
         "\u0e99"-"\u0e9f",
         "\u0ea1"-"\u0ea3",
         "\u0ea5",
         "\u0ea7",
         "\u0eaa"-"\u0eab",
         "\u0ead"-"\u0eb0",
         "\u0eb2"-"\u0eb3",
         "\u0ebd",
         "\u0ec0"-"\u0ec4",
         "\u0ec6",
         "\u0edc"-"\u0edd",
         "\u0f00",
         "\u0f40"-"\u0f47",
         "\u0f49"-"\u0f6a",
         "\u0f88"-"\u0f8b",
         "\u1000"-"\u1021",
         "\u1023"-"\u1027",
         "\u1029"-"\u102a",
         "\u1050"-"\u1055",
         "\u10a0"-"\u10c5",
         "\u10d0"-"\u10f6",
         "\u1100"-"\u1159",
         "\u115f"-"\u11a2",
         "\u11a8"-"\u11f9",
         "\u1200"-"\u1206",
         "\u1208"-"\u1246",
         "\u1248",
         "\u124a"-"\u124d",
         "\u1250"-"\u1256",
         "\u1258",
         "\u125a"-"\u125d",
         "\u1260"-"\u1286",
         "\u1288",
         "\u128a"-"\u128d",
         "\u1290"-"\u12ae",
         "\u12b0",
         "\u12b2"-"\u12b5",
         "\u12b8"-"\u12be",
         "\u12c0",
         "\u12c2"-"\u12c5",
         "\u12c8"-"\u12ce",
         "\u12d0"-"\u12d6",
         "\u12d8"-"\u12ee",
         "\u12f0"-"\u130e",
         "\u1310",
         "\u1312"-"\u1315",
         "\u1318"-"\u131e",
         "\u1320"-"\u1346",
         "\u1348"-"\u135a",
         "\u13a0"-"\u13f4",
         "\u1401"-"\u166c",
         "\u166f"-"\u1676",
         "\u1681"-"\u169a",
         "\u16a0"-"\u16ea",
         "\u1780"-"\u17b3",
         "\u17db",
         "\u1820"-"\u1877",
         "\u1880"-"\u18a8",
         "\u1e00"-"\u1e9b",
         "\u1ea0"-"\u1ef9",
         "\u1f00"-"\u1f15",
         "\u1f18"-"\u1f1d",
         "\u1f20"-"\u1f45",
         "\u1f48"-"\u1f4d",
         "\u1f50"-"\u1f57",
         "\u1f59",
         "\u1f5b",
         "\u1f5d",
         "\u1f5f"-"\u1f7d",
         "\u1f80"-"\u1fb4",
         "\u1fb6"-"\u1fbc",
         "\u1fbe",
         "\u1fc2"-"\u1fc4",
         "\u1fc6"-"\u1fcc",
         "\u1fd0"-"\u1fd3",
         "\u1fd6"-"\u1fdb",
         "\u1fe0"-"\u1fec",
         "\u1ff2"-"\u1ff4",
         "\u1ff6"-"\u1ffc",
         "\u203f"-"\u2040",
         "\u207f",
         "\u20a0"-"\u20af",
         "\u2102",
         "\u2107",
         "\u210a"-"\u2113",
         "\u2115",
         "\u2119"-"\u211d",
         "\u2124",
         "\u2126",
         "\u2128",
         "\u212a"-"\u212d",
         "\u212f"-"\u2131",
         "\u2133"-"\u2139",
         "\u2160"-"\u2183",
         "\u3005"-"\u3007",
         "\u3021"-"\u3029",
         "\u3031"-"\u3035",
         "\u3038"-"\u303a",
         "\u3041"-"\u3094",
         "\u309d"-"\u309e",
         "\u30a1"-"\u30fe",
         "\u3105"-"\u312c",
         "\u3131"-"\u318e",
         "\u31a0"-"\u31b7",
         "\u3400"-"\u4db5",
         "\u4e00"-"\u9fa5",
         "\ua000"-"\ua48c",
         "\uac00"-"\ud7a3",
         "\uf900"-"\ufa2d",
         "\ufb00"-"\ufb06",
         "\ufb13"-"\ufb17",
         "\ufb1d",
         "\ufb1f"-"\ufb28",
         "\ufb2a"-"\ufb36",
         "\ufb38"-"\ufb3c",
         "\ufb3e",
         "\ufb40"-"\ufb41",
         "\ufb43"-"\ufb44",
         "\ufb46"-"\ufbb1",
         "\ufbd3"-"\ufd3d",
         "\ufd50"-"\ufd8f",
         "\ufd92"-"\ufdc7",
         "\ufdf0"-"\ufdfb",
         "\ufe33"-"\ufe34",
         "\ufe4d"-"\ufe4f",
         "\ufe69",
         "\ufe70"-"\ufe72",
         "\ufe74",
         "\ufe76"-"\ufefc",
         "\uff04",
         "\uff21"-"\uff3a",
         "\uff3f",
         "\uff41"-"\uff5a",
         "\uff65"-"\uffbe",
         "\uffc2"-"\uffc7",
         "\uffca"-"\uffcf",
         "\uffd2"-"\uffd7",
         "\uffda"-"\uffdc",
         "\uffe0"-"\uffe1",
         "\uffe5"-"\uffe6"
      ]
  >
|
  < #PART_LETTER:
      [  // all chars for which Character.isIdentifierPart is true
         "\u0000"-"\u0008",
         "\u000e"-"\u001b",
         "$",
         "0"-"9",
         "A"-"Z",
         "_",
         "a"-"z",
         "\u007f"-"\u009f",
         "\u00a2"-"\u00a5",
         "\u00aa",
         "\u00b5",
         "\u00ba",
         "\u00c0"-"\u00d6",
         "\u00d8"-"\u00f6",
         "\u00f8"-"\u021f",
         "\u0222"-"\u0233",
         "\u0250"-"\u02ad",
         "\u02b0"-"\u02b8",
         "\u02bb"-"\u02c1",
         "\u02d0"-"\u02d1",
         "\u02e0"-"\u02e4",
         "\u02ee",
         "\u0300"-"\u034e",
         "\u0360"-"\u0362",
         "\u037a",
         "\u0386",
         "\u0388"-"\u038a",
         "\u038c",
         "\u038e"-"\u03a1",
         "\u03a3"-"\u03ce",
         "\u03d0"-"\u03d7",
         "\u03da"-"\u03f3",
         "\u0400"-"\u0481",
         "\u0483"-"\u0486",
         "\u048c"-"\u04c4",
         "\u04c7"-"\u04c8",
         "\u04cb"-"\u04cc",
         "\u04d0"-"\u04f5",
         "\u04f8"-"\u04f9",
         "\u0531"-"\u0556",
         "\u0559",
         "\u0561"-"\u0587",
         "\u0591"-"\u05a1",
         "\u05a3"-"\u05b9",
         "\u05bb"-"\u05bd",
         "\u05bf",
         "\u05c1"-"\u05c2",
         "\u05c4",
         "\u05d0"-"\u05ea",
         "\u05f0"-"\u05f2",
         "\u0621"-"\u063a",
         "\u0640"-"\u0655",
         "\u0660"-"\u0669",
         "\u0670"-"\u06d3",
         "\u06d5"-"\u06dc",
         "\u06df"-"\u06e8",
         "\u06ea"-"\u06ed",
         "\u06f0"-"\u06fc",
         "\u070f"-"\u072c",
         "\u0730"-"\u074a",
         "\u0780"-"\u07b0",
         "\u0901"-"\u0903",
         "\u0905"-"\u0939",
         "\u093c"-"\u094d",
         "\u0950"-"\u0954",
         "\u0958"-"\u0963",
         "\u0966"-"\u096f",
         "\u0981"-"\u0983",
         "\u0985"-"\u098c",
         "\u098f"-"\u0990",
         "\u0993"-"\u09a8",
         "\u09aa"-"\u09b0",
         "\u09b2",
         "\u09b6"-"\u09b9",
         "\u09bc",
         "\u09be"-"\u09c4",
         "\u09c7"-"\u09c8",
         "\u09cb"-"\u09cd",
         "\u09d7",
         "\u09dc"-"\u09dd",
         "\u09df"-"\u09e3",
         "\u09e6"-"\u09f3",
         "\u0a02",
         "\u0a05"-"\u0a0a",
         "\u0a0f"-"\u0a10",
         "\u0a13"-"\u0a28",
         "\u0a2a"-"\u0a30",
         "\u0a32"-"\u0a33",
         "\u0a35"-"\u0a36",
         "\u0a38"-"\u0a39",
         "\u0a3c",
         "\u0a3e"-"\u0a42",
         "\u0a47"-"\u0a48",
         "\u0a4b"-"\u0a4d",
         "\u0a59"-"\u0a5c",
         "\u0a5e",
         "\u0a66"-"\u0a74",
         "\u0a81"-"\u0a83",
         "\u0a85"-"\u0a8b",
         "\u0a8d",
         "\u0a8f"-"\u0a91",
         "\u0a93"-"\u0aa8",
         "\u0aaa"-"\u0ab0",
         "\u0ab2"-"\u0ab3",
         "\u0ab5"-"\u0ab9",
         "\u0abc"-"\u0ac5",
         "\u0ac7"-"\u0ac9",
         "\u0acb"-"\u0acd",
         "\u0ad0",
         "\u0ae0",
         "\u0ae6"-"\u0aef",
         "\u0b01"-"\u0b03",
         "\u0b05"-"\u0b0c",
         "\u0b0f"-"\u0b10",
         "\u0b13"-"\u0b28",
         "\u0b2a"-"\u0b30",
         "\u0b32"-"\u0b33",
         "\u0b36"-"\u0b39",
         "\u0b3c"-"\u0b43",
         "\u0b47"-"\u0b48",
         "\u0b4b"-"\u0b4d",
         "\u0b56"-"\u0b57",
         "\u0b5c"-"\u0b5d",
         "\u0b5f"-"\u0b61",
         "\u0b66"-"\u0b6f",
         "\u0b82"-"\u0b83",
         "\u0b85"-"\u0b8a",
         "\u0b8e"-"\u0b90",
         "\u0b92"-"\u0b95",
         "\u0b99"-"\u0b9a",
         "\u0b9c",
         "\u0b9e"-"\u0b9f",
         "\u0ba3"-"\u0ba4",
         "\u0ba8"-"\u0baa",
         "\u0bae"-"\u0bb5",
         "\u0bb7"-"\u0bb9",
         "\u0bbe"-"\u0bc2",
         "\u0bc6"-"\u0bc8",
         "\u0bca"-"\u0bcd",
         "\u0bd7",
         "\u0be7"-"\u0bef",
         "\u0c01"-"\u0c03",
         "\u0c05"-"\u0c0c",
         "\u0c0e"-"\u0c10",
         "\u0c12"-"\u0c28",
         "\u0c2a"-"\u0c33",
         "\u0c35"-"\u0c39",
         "\u0c3e"-"\u0c44",
         "\u0c46"-"\u0c48",
         "\u0c4a"-"\u0c4d",
         "\u0c55"-"\u0c56",
         "\u0c60"-"\u0c61",
         "\u0c66"-"\u0c6f",
         "\u0c82"-"\u0c83",
         "\u0c85"-"\u0c8c",
         "\u0c8e"-"\u0c90",
         "\u0c92"-"\u0ca8",
         "\u0caa"-"\u0cb3",
         "\u0cb5"-"\u0cb9",
         "\u0cbe"-"\u0cc4",
         "\u0cc6"-"\u0cc8",
         "\u0cca"-"\u0ccd",
         "\u0cd5"-"\u0cd6",
         "\u0cde",
         "\u0ce0"-"\u0ce1",
         "\u0ce6"-"\u0cef",
         "\u0d02"-"\u0d03",
         "\u0d05"-"\u0d0c",
         "\u0d0e"-"\u0d10",
         "\u0d12"-"\u0d28",
         "\u0d2a"-"\u0d39",
         "\u0d3e"-"\u0d43",
         "\u0d46"-"\u0d48",
         "\u0d4a"-"\u0d4d",
         "\u0d57",
         "\u0d60"-"\u0d61",
         "\u0d66"-"\u0d6f",
         "\u0d82"-"\u0d83",
         "\u0d85"-"\u0d96",
         "\u0d9a"-"\u0db1",
         "\u0db3"-"\u0dbb",
         "\u0dbd",
         "\u0dc0"-"\u0dc6",
         "\u0dca",
         "\u0dcf"-"\u0dd4",
         "\u0dd6",
         "\u0dd8"-"\u0ddf",
         "\u0df2"-"\u0df3",
         "\u0e01"-"\u0e3a",
         "\u0e3f"-"\u0e4e",
         "\u0e50"-"\u0e59",
         "\u0e81"-"\u0e82",
         "\u0e84",
         "\u0e87"-"\u0e88",
         "\u0e8a",
         "\u0e8d",
         "\u0e94"-"\u0e97",
         "\u0e99"-"\u0e9f",
         "\u0ea1"-"\u0ea3",
         "\u0ea5",
         "\u0ea7",
         "\u0eaa"-"\u0eab",
         "\u0ead"-"\u0eb9",
         "\u0ebb"-"\u0ebd",
         "\u0ec0"-"\u0ec4",
         "\u0ec6",
         "\u0ec8"-"\u0ecd",
         "\u0ed0"-"\u0ed9",
         "\u0edc"-"\u0edd",
         "\u0f00",
         "\u0f18"-"\u0f19",
         "\u0f20"-"\u0f29",
         "\u0f35",
         "\u0f37",
         "\u0f39",
         "\u0f3e"-"\u0f47",
         "\u0f49"-"\u0f6a",
         "\u0f71"-"\u0f84",
         "\u0f86"-"\u0f8b",
         "\u0f90"-"\u0f97",
         "\u0f99"-"\u0fbc",
         "\u0fc6",
         "\u1000"-"\u1021",
         "\u1023"-"\u1027",
         "\u1029"-"\u102a",
         "\u102c"-"\u1032",
         "\u1036"-"\u1039",
         "\u1040"-"\u1049",
         "\u1050"-"\u1059",
         "\u10a0"-"\u10c5",
         "\u10d0"-"\u10f6",
         "\u1100"-"\u1159",
         "\u115f"-"\u11a2",
         "\u11a8"-"\u11f9",
         "\u1200"-"\u1206",
         "\u1208"-"\u1246",
         "\u1248",
         "\u124a"-"\u124d",
         "\u1250"-"\u1256",
         "\u1258",
         "\u125a"-"\u125d",
         "\u1260"-"\u1286",
         "\u1288",
         "\u128a"-"\u128d",
         "\u1290"-"\u12ae",
         "\u12b0",
         "\u12b2"-"\u12b5",
         "\u12b8"-"\u12be",
         "\u12c0",
         "\u12c2"-"\u12c5",
         "\u12c8"-"\u12ce",
         "\u12d0"-"\u12d6",
         "\u12d8"-"\u12ee",
         "\u12f0"-"\u130e",
         "\u1310",
         "\u1312"-"\u1315",
         "\u1318"-"\u131e",
         "\u1320"-"\u1346",
         "\u1348"-"\u135a",
         "\u1369"-"\u1371",
         "\u13a0"-"\u13f4",
         "\u1401"-"\u166c",
         "\u166f"-"\u1676",
         "\u1681"-"\u169a",
         "\u16a0"-"\u16ea",
         "\u1780"-"\u17d3",
         "\u17db",
         "\u17e0"-"\u17e9",
         "\u180b"-"\u180e",
         "\u1810"-"\u1819",
         "\u1820"-"\u1877",
         "\u1880"-"\u18a9",
         "\u1e00"-"\u1e9b",
         "\u1ea0"-"\u1ef9",
         "\u1f00"-"\u1f15",
         "\u1f18"-"\u1f1d",
         "\u1f20"-"\u1f45",
         "\u1f48"-"\u1f4d",
         "\u1f50"-"\u1f57",
         "\u1f59",
         "\u1f5b",
         "\u1f5d",
         "\u1f5f"-"\u1f7d",
         "\u1f80"-"\u1fb4",
         "\u1fb6"-"\u1fbc",
         "\u1fbe",
         "\u1fc2"-"\u1fc4",
         "\u1fc6"-"\u1fcc",
         "\u1fd0"-"\u1fd3",
         "\u1fd6"-"\u1fdb",
         "\u1fe0"-"\u1fec",
         "\u1ff2"-"\u1ff4",
         "\u1ff6"-"\u1ffc",
         "\u200c"-"\u200f",
         "\u202a"-"\u202e",
         "\u203f"-"\u2040",
         "\u206a"-"\u206f",
         "\u207f",
         "\u20a0"-"\u20af",
         "\u20d0"-"\u20dc",
         "\u20e1",
         "\u2102",
         "\u2107",
         "\u210a"-"\u2113",
         "\u2115",
         "\u2119"-"\u211d",
         "\u2124",
         "\u2126",
         "\u2128",
         "\u212a"-"\u212d",
         "\u212f"-"\u2131",
         "\u2133"-"\u2139",
         "\u2160"-"\u2183",
         "\u3005"-"\u3007",
         "\u3021"-"\u302f",
         "\u3031"-"\u3035",
         "\u3038"-"\u303a",
         "\u3041"-"\u3094",
         "\u3099"-"\u309a",
         "\u309d"-"\u309e",
         "\u30a1"-"\u30fe",
         "\u3105"-"\u312c",
         "\u3131"-"\u318e",
         "\u31a0"-"\u31b7",
         "\u3400"-"\u4db5",
         "\u4e00"-"\u9fa5",
         "\ua000"-"\ua48c",
         "\uac00"-"\ud7a3",
         "\uf900"-"\ufa2d",
         "\ufb00"-"\ufb06",
         "\ufb13"-"\ufb17",
         "\ufb1d"-"\ufb28",
         "\ufb2a"-"\ufb36",
         "\ufb38"-"\ufb3c",
         "\ufb3e",
         "\ufb40"-"\ufb41",
         "\ufb43"-"\ufb44",
         "\ufb46"-"\ufbb1",
         "\ufbd3"-"\ufd3d",
         "\ufd50"-"\ufd8f",
         "\ufd92"-"\ufdc7",
         "\ufdf0"-"\ufdfb",
         "\ufe20"-"\ufe23",
         "\ufe33"-"\ufe34",
         "\ufe4d"-"\ufe4f",
         "\ufe69",
         "\ufe70"-"\ufe72",
         "\ufe74",
         "\ufe76"-"\ufefc",
         "\ufeff",
         "\uff04",
         "\uff10"-"\uff19",
         "\uff21"-"\uff3a",
         "\uff3f",
         "\uff41"-"\uff5a",
         "\uff65"-"\uffbe",
         "\uffc2"-"\uffc7",
         "\uffca"-"\uffcf",
         "\uffd2"-"\uffd7",
         "\uffda"-"\uffdc",
         "\uffe0"-"\uffe1",
         "\uffe5"-"\uffe6",
         "\ufff9"-"\ufffb"
      ]
  >
}

/* SEPARATORS */

TOKEN :
{
  < LPAREN: "(" >
| < RPAREN: ")" >
| < LBRACE: "{" >
| < RBRACE: "}" >
| < LBRACK: "[" >
| < RBRACK: "]" >
| < COLON:  ":" >
| < SEMICOLON: ";" >
| < COMMA: "," >
| < AT: "@" >
| < EQUALS: "=" >
| < DOT: "." >
| < DASH: "-" >
}

TOKEN :
{
  < LT: "<" >
| < GT: ">" >
| < TICK: "`" >
}


/********************************************
 * THE AVRO IDL LANGUAGE GRAMMAR STARTS HERE *
 ********************************************/

/**
 * The input to Idl is a CompilationUnit, which is currently
 * just a single Protocol.
 */
Protocol CompilationUnit():
{
  Protocol p;
}
{
  p = ProtocolDeclaration()
  ( < "\u001a" > )?
  ( <STUFF_TO_IGNORE: ~[]> )?
  <EOF>
  { return p; }
}

/*
 * Declaration syntax follows.
 */
Schema NamedSchemaDeclaration(Map<String, JsonNode> props):
{
  Schema s;
  String savedSpace = this.namespace;
}
{
  {
    if (props.containsKey("namespace"))
      this.namespace = getTextProp("namespace", props, token);
  }
 (
     s = FixedDeclaration()
   | s = EnumDeclaration()
   | s = RecordDeclaration()
 )
 {
   this.namespace = savedSpace;

   for (String key : props.keySet())
     if ("namespace".equals(key)) {               // already handled: ignore
     } else if ("aliases".equals(key)) {          // aliases
       for (String alias : getTextProps("aliases", props, token))
         s.addAlias(alias);
     } else {                                     // add all other props
       s.addProp(key, props.get(key));
     }

   return s;
 }
}

Schema UnionDefinition():
{
  Schema s;
  List<Schema> schemata = new ArrayList<Schema>();
}
{
 // TODO should probably disallow other unions here in the parser?

  "union"
  "{"
  s = Type()
  { schemata.add(s); }

  (
    ","
    s = Type()
    { schemata.add(s); }
  )*
  "}"
  {
    return Schema.createUnion(schemata);
  }
}


Protocol ProtocolDeclaration():
{
  String name;
  Protocol p;
  Map<String, JsonNode> props = new LinkedHashMap<String, JsonNode>();
}
{
  ( SchemaProperty(props) )*
  {
    if (props.containsKey("namespace"))
      namespace = getTextProp("namespace", props, token);
  }
 "protocol"
   name = Identifier()
 {
   p = new Protocol(name, getDoc(), namespace);
   for (String key : props.keySet())
     if ("namespace".equals(key)) {               // already handled: ignore
     } else {                                     // add all other props
       p.addProp(key, props.get(key));
     }
 }
 ProtocolBody(p)
 {
   return p;
 }
}


Schema EnumDeclaration():
{
  String name;
  List<String> symbols;
}
{
  "enum"
  name = Identifier()
  symbols = EnumBody()
  {
    Schema s = Schema.createEnum(name, getDoc(), this.namespace, symbols);
    names.put(s.getFullName(), s);
    return s;
  }
}

List<String> EnumBody():
{
  List<String> symbols = new ArrayList<String>();
}
{
  "{"
  [ EnumConstant(symbols) ( LOOKAHEAD(2) "," EnumConstant(symbols) )* ]
  "}"
  {
    return symbols;
  }
}

void EnumConstant(List<String> symbols):
{
  String sym;
}
{
  sym = Identifier() { symbols.add(sym); }
}

void ProtocolBody(Protocol p):
{
  Schema schema;
  Message message;
  Protocol importProtocol;
  Map<String, JsonNode> props = new LinkedHashMap<String, JsonNode>();
}
{
  "{"
  (
   <IMPORT>
   ((( importProtocol = ImportIdl() | importProtocol = ImportProtocol()) {
       for (Schema s : importProtocol.getTypes())
         names.put(s.getFullName(), s);
       p.getMessages().putAll(importProtocol.getMessages());
     })
     | schema = ImportSchema()
     )
   |
   ( SchemaProperty(props) )*
   (
     schema = NamedSchemaDeclaration(props)
     |
     message = MessageDeclaration(p, props) {
       p.getMessages().put(message.getName(), message);
     }
    )  { props.clear(); }
  ) *
  "}"

  {
    p.setTypes(names.values());
  }
}


Protocol ImportIdl() : {
  String importFile;
}
{
  <IDL> importFile = JsonString() ";"
    {
      try {
        Idl idl = new Idl(findFile(importFile), this); 
        try {
          return idl.CompilationUnit();
        } finally {
          idl.close();
        }
      } catch (IOException e) {
        throw error("Error importing "+importFile+": "+e, token);
      }        
    }
}

Protocol ImportProtocol() : {
  String importFile;
}
{
  <PROTOCOL> importFile = JsonString() ";"
    {

      try {
        InputStream stream = findFile(importFile).openStream();
        try {
          return Protocol.parse(stream);
        } finally {
          stream.close();
        }
      } catch (IOException e) {
        throw error("Error importing "+importFile+": "+e, token);
      }        
    }
}

Schema ImportSchema() : {
  String importFile;
}
{
  <SCHEMA> importFile = JsonString() ";"
    {
      try {
        Parser parser = new Schema.Parser();
        parser.addTypes(names);                   // inherit names
        InputStream stream = findFile(importFile).openStream();
        try {
          Schema value = parser.parse(stream);
          names = parser.getTypes();                // update names
          return value;
        } finally {
          stream.close();
        }
      } catch (IOException e) {
        throw error("Error importing "+importFile+": "+e, token);
      }        
    }
}

Schema FixedDeclaration():
{
  String name;
  Token sizeTok;
}
{
  "fixed" name = Identifier() "(" sizeTok = <INTEGER_LITERAL> ")"
  ";"
  {
    Schema s = Schema.createFixed(name, getDoc(), this.namespace,
                                  Integer.parseInt(sizeTok.image));
    names.put(s.getFullName(), s);
    return s;
  }
}

Schema RecordDeclaration():
{
  String name;
  List<Field> fields = new ArrayList<Field>();
  boolean isError;
}
{
  (
    "record" { isError = false; }
    | "error" { isError = true; }
  )
  name = Identifier()
  {
    Schema result = Schema.createRecord(
      name, getDoc(), this.namespace, isError);
    names.put(result.getFullName(), result);
  }
  "{"
    ( FieldDeclaration(fields) )*
  "}"
  {
    result.setFields(fields);
    return result;
  }
}

void SchemaProperty(Map<String, JsonNode> properties):
{
  String key;
  JsonNode val;
}
{
  "@" key = PropertyName() "(" val = Json() ")"
  {
    if (properties.containsKey(key))
      throw error("Property '" + key + "' already specified", token);
    properties.put(key, val);
  }
}


void FieldDeclaration(List<Field> fields):
{
  Schema type;
  Map<String, JsonNode> props = new LinkedHashMap<String, JsonNode>();
}
{
  // TODO should we be able to specify properties on any Type?
  // or just on field declarations as done here

  ( SchemaProperty(props) )*
  type = Type()
  VariableDeclarator(type, fields) ( "," VariableDeclarator(type, fields) )*
  ";"
  {
    for (String key : props.keySet())
      type.addProp(key, props.get(key));
  }
}

void VariableDeclarator(Schema type, List<Field> fields):
{
  String name;
  JsonNode defaultValue = null;
  Map<String, JsonNode> props = new LinkedHashMap<String, JsonNode>();
}
{
    ( SchemaProperty(props) )*
    
  name = Identifier()

    [ <EQUALS> defaultValue=Json() ]
    
  {
    Field.Order order = Field.Order.ASCENDING;
    for (String key : props.keySet())
      if ("order".equals(key))
        order = Field.Order.valueOf(getTextProp(key,props,token).toUpperCase());
    Field field = new Field(name, type, getDoc(), defaultValue, order);
    for (String key : props.keySet())
      if ("order".equals(key)) {                  // already handled: ignore
      } else if ("aliases".equals(key)) {         // aliases
        for (String alias : getTextProps("aliases", props, token))
          field.addAlias(alias);
      } else {                                    // add all other props
        field.addProp(key, props.get(key));
      }
    fields.add(field);
  }
}


String MessageDocumentation():
{}
{
   // Don't parse anything, just return the doc string
   {
       return getDoc();
   }
}

Message MessageDeclaration(Protocol p, Map<String, JsonNode> props):
{
  String msgDoc;
  String name;
  Schema request;
  Schema response;
  boolean oneWay = false;
  List<Schema> errorSchemata = new ArrayList<Schema>();
  errorSchemata.add(Protocol.SYSTEM_ERROR);
}
{
  msgDoc = MessageDocumentation()
  response = ResultType()
  name = Identifier()
  request = FormalParameters()
  [ "oneway" {oneWay = true; } | "throws" ErrorList(errorSchemata) ]
  ";"
  {
    Schema errors = Schema.createUnion(errorSchemata);
    if (oneWay && response.getType() != Type.NULL)
      throw error("One-way message'"+name+"' must return void", token);
    return oneWay
    ? p.createMessage(name, msgDoc, props, request)
    : p.createMessage(name, msgDoc, props, request, response, errors);
    
  }
}

void ErrorList(List<Schema> errors):
{
  Schema s;
}
{
  s = ReferenceType() { errors.add(s); }
  ( "," s = ReferenceType() { errors.add(s); } )*
}

Schema FormalParameters():
{
  List<Field> fields = new ArrayList<Field>();
}
{
  (
    "(" [ FormalParameter(fields) ( "," FormalParameter(fields) )* ] ")"
  )
  {
    return Schema.createRecord(fields);
  }
}

void FormalParameter(List<Field> fields):
{
  Schema type;
}
{
  type = Type()
  VariableDeclarator(type, fields)
}

Schema Type():
{
  Schema s;
  Map<String, JsonNode> props = new LinkedHashMap<String, JsonNode>();
}
{

    ( SchemaProperty(props) )*
  (
      LOOKAHEAD(2) s = ReferenceType()
    | s = PrimitiveType()
    | s = UnionDefinition()
    | s = ArrayType()
    | s = MapType()
  )
  {
    for (String key : props.keySet())
      s.addProp(key, props.get(key));
    return s;
  }
}

Schema ArrayType():
{
  Schema elemSchema;
}
{
  "array" "<" elemSchema = Type() ">"
  {
    return Schema.createArray(elemSchema);
  }
}

Schema MapType():
{
  Schema elemSchema;
}
{
  "map" "<" elemSchema = Type() ">"
  {
    return Schema.createMap(elemSchema);
  }
}

/**
 * A reference to some other existing type
 */
Schema ReferenceType():
{
  String part;
  Token tok;
  StringBuilder sb = new StringBuilder();
}
{
  (
    part = Identifier() { sb.append(part); }
    ("." tok = AnyIdentifier() { sb.append(".").append(tok.image); })*
    )
  {
    String name = sb.toString();
    if ((name.indexOf('.') == -1) && namespace != null)
      name = namespace + "." + name;
    Schema type = names.get(name);
    if (type == null)
      throw error("Undefined name '" + name + "'", token);
    return type;
  }
}

Schema PrimitiveType():
{}
{
  "boolean" { return Schema.create(Type.BOOLEAN); }
| "bytes" { return Schema.create(Type.BYTES); }
| "int" { return Schema.create(Type.INT); }
| "string" { return Schema.create(Type.STRING); }
| "float" { return Schema.create(Type.FLOAT); }
| "double" { return Schema.create(Type.DOUBLE); }
| "long" { return Schema.create(Type.LONG); }
| "null" { return Schema.create(Type.NULL); }
}

/**
 * Result types are like other types, except we provide "void" as
 * an alias of "null"
 */
Schema ResultType():
{
  Schema schema;
}
{
  LOOKAHEAD(2)
    "void"          { return Schema.create(Type.NULL); }
  | schema = Type() { return schema; }
}

String PropertyName():
{
  Token t;
  StringBuffer name = new StringBuffer();
}
{
  t = <IDENTIFIER>   { name.append(t.image); }
  ( t = <DASH>       { name.append(t.image); }
    t = <IDENTIFIER> { name.append(t.image); }
  ) *
  { return name.toString(); }
}

String Identifier():
{
  Token t;
}
{
  ( t = <IDENTIFIER> { return t.image; } )
| ( "`" t = AnyIdentifier() "`" {
    return t.image;
  })
}

Token AnyIdentifier():
{
  Token t;
}
{
  (t = <ARRAY> |
   t = <BOOLEAN> |
   t = <DOUBLE> |
   t = <ENUM> |
   t = <ERROR> |
   t = <FALSE> |
   t = <FIXED> |
   t = <FLOAT> |
   t = <INT> |
   t = <LONG> |
   t = <MAP> |
   t = <BYTES> |
   t = <STRING> |
   t = <PROTOCOL> |
   t = <RECORD> |
   t = <THROWS> |
   t = <TRUE> |
   t = <UNION> |
   t = <VOID> |
   t = <IDENTIFIER>)
  {
    return t;
  }
}

JsonNode Json() :
{ String s; Token t; JsonNode n; }
{ 
( s = JsonString() { n = new TextNode(s); }
| (t=<INTEGER_LITERAL> { n = new LongNode(Long.parseLong(t.image)); })
| (t=<FLOATING_POINT_LITERAL> {n=new DoubleNode(Double.parseDouble(t.image));})
| n=JsonObject()
| n=JsonArray()
| ( "true" { n = BooleanNode.TRUE; } )
| ( "false" { n = BooleanNode.FALSE; } )
| ( "null" { n = NullNode.instance; } )
 )
  { return n; }
}

String JsonString() :
{ Token t; }
{
  t = <STRING_LITERAL>
  {
    String betweenQuotes = t.image.substring(1, t.image.length() - 1);
    return StringEscapeUtils.unescapeJavaScript(betweenQuotes);
  }
}

JsonNode JsonObject() :
{ 
  ObjectNode o = FACTORY.objectNode();
}
{
  "{" [ JsonFields(o) ] "}"
  { return o; }
}

void JsonFields(ObjectNode o) :
{}
{
  JsonPair(o) [ "," JsonFields(o) ]
}

void JsonPair(ObjectNode o) :
{
  String name;
  JsonNode value;
}
{
  name=JsonString() <COLON> value=Json()
    { o.put(name, value); } 
}

JsonNode JsonArray() :
{ ArrayNode a = FACTORY.arrayNode(); }
{
  <LBRACK> [ JsonElements(a) ] <RBRACK>
    { return a; }
}

void JsonElements(ArrayNode a) :
{ JsonNode element; }
{
  element=Json() { a.add(element); } [ "," JsonElements(a) ]
}
