/**
 * Adapted from the Google GSON Parser http://google-gson.googlecode.com/ and http://code.google.com/p/jsonparser/
 *
 * Author: Siegfried Puchbauer
 */

options {
  STATIC = false;
  UNICODE_INPUT = true;
}

PARSER_BEGIN(JsonParser)

package org.codehaus.groovy.grails.web.json.parser;

import org.codehaus.groovy.grails.web.json.JSONElement;
import org.codehaus.groovy.grails.web.json.JSONObject;
import org.codehaus.groovy.grails.web.json.JSONArray;


@SuppressWarnings("all")
public final class JSONParser {

}

PARSER_END(JSONParser)

SKIP : { " " | "\t" | "\n" | "\r" }
/*
 * Technically Json does not allow leading zeros in numbers, but we
 * will allow that.
 */
TOKEN : {
  <E : ["e","E"](["+","-"])?>
  | <DIGITS : (["0"-"9"])+>
  | <#HEX_CHAR : ["a"-"f","A"-"F","0"-"9"]>
  | <UNICODE_CHAR : "\\u" <HEX_CHAR><HEX_CHAR><HEX_CHAR><HEX_CHAR> >
  | <#ESCAPE_CHAR: "\\" ["n","t","b","r","f","\\","'","\"", "/"] >
  | <SINGLE_QUOTE_LITERAL: "\'" ( (~["\'","\\","\n","\r"]) | <ESCAPE_CHAR> | <UNICODE_CHAR>)* "\'" >
  | <DOUBLE_QUOTE_LITERAL: "\"" ( (~["\"","\\","\n","\r"]) | <ESCAPE_CHAR> | <UNICODE_CHAR>)* "\"" >
  | <QUOTE : "\""> : STRING_STATE
}
<STRING_STATE> MORE  : { "\\" : ESC_STATE }
<STRING_STATE> TOKEN : {
  <ENDQUOTE : <QUOTE> > : DEFAULT
| <CHAR : ~["\"","\\"]>
}
<ESC_STATE> TOKEN : {
  <CNTRL_ESC : ["\"","\\","/","b","f","n","r","t"]> : STRING_STATE
}
<ESC_STATE> MORE  : { "u" : HEX_STATE }
<HEX_STATE>  TOKEN : {
  <#HEX : ["a"-"f","A"-"F","0"-"9"]>
| <HEX_ESC : <HEX><HEX><HEX><HEX> > : STRING_STATE
}

public Object parse() :
{
  Object json = null;
}
{
  ( json=JsonObject() |
    json=JsonArray() |
    json=JsonPrimitive() |
    json=JsonNull())
  { return json; }
}

public JSONElement parseJSON() :
{
    JSONElement json = null;
}
{
    ( json=JsonObject()
    | json=JsonArray())
    { return json; }
}

private JSONObject JsonObject() :
{
  JSONObject o = new JSONObject();
}
{
  "{" [ Members(o) ] "}"
  { return o; }
}

private Object JsonNull() :
{
   Object o = null;
}
{
  "null"
  { return o; }
}

private void Members(JSONObject o) :
{ }
{
  Pair(o) [ "," Members(o) ]
}

private void Pair(JSONObject o) :
{
  String property;
  Object value;
}
{
  property=JsonString() ":" value=JsonValue()
  {
    o.put(property, value);
  }
}

private JSONArray JsonArray() :
{ JSONArray array = new JSONArray(); }
{
  "[" [ Elements(array) ] "]"
  {
    //array.reverse();
    return array;
  }
}

private void Elements(JSONArray array) :
{
  Object element;
}
{
  element=JsonValue() [ "," Elements(array) ]
  { array.add(element); }
}

private Object JsonValue() :
{ Object o = null; }
{
( o=JsonString() |
  o=JsonNumber() |
  o=JsonObject() |
  o=JsonArray() |
  ( "true" { o = Boolean.TRUE; } ) |
  ( "false" { o = Boolean.FALSE; } ) |
  "null" )
  { return o; }
}

private Object JsonPrimitive() :
{
  Object value;
}
{
  ( value=JsonString()) { return value; } |
  ( value=JsonNumber()) { return value; } |
  ( "true" { return Boolean.TRUE; }) |
  ( "false" { return Boolean.FALSE; } )
}

private Object JsonNumber() :
{
  String intpart = null,
         fracpart = null,
         exppart = null;
}
{
  intpart=JsonInt() [ fracpart=JsonFrac() ] [ exppart=JsonExp() ]
  {
    Number n;
    if (exppart != null) {
      n = new java.math.BigDecimal(intpart + fracpart + exppart);
    } else if (fracpart != null) {
      n = new Double(intpart + fracpart);
    } else {
      // See if the number fits in an integer, or long
      // Use BigInteger only if it is big enough.
      if (intpart.length() < 10) {
        n = new Integer(intpart);
      } else if (intpart.length() < 19) {
        n = new Long(intpart);
      } else {
        n = new java.math.BigInteger(intpart);
      }
    }
    return n;
  }
}

private String JsonInt() :
{
  String digits;
  boolean negative = false;
}
{
  ["-" { negative = true; } ] digits=Digits()
  {
    if(negative)
      return "-" + digits;
    return digits;
  }
}

private String JsonFrac() :
{ String digits; }
{
  "." digits=Digits()
  { return "." + digits; }
}

private String JsonExp() :
{
  Token t;
  String digits;
}
{
  t=<E> digits=Digits()
  { return t.image + digits; }
}

private String Digits() :
{ Token t; }
{
  t=<DIGITS>
  { return t.image; }
}

private String JsonString() :
{ Token t; }
{
  (t=<SINGLE_QUOTE_LITERAL> | t=<DOUBLE_QUOTE_LITERAL>) { 
    return StringUnmarshaller.unmarshall(t.image);    
  }
}
