%name LucyParseJson

/* 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.
 */

%token_type { cfish_Obj* }
%token_destructor { CFISH_DECREF($$); }
%token_prefix LUCY_JSON_TOKENTYPE_

%include {
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "Lucy/Object/Hash.h"
#include "Lucy/Object/VArray.h"
#include "Lucy/Object/CharBuf.h"
#include "Lucy/Object/Err.h"
#include "Lucy/Util/Json.h"
}

%extra_argument { lucy_JsonParserState *state }

%syntax_error {
    state->errors = true;
}

result ::= top_level_value(A).
{
     state->result = A;
}

/* Allow any "value" as a top-level construct.  This "loose", tolerant grammar
 * makes testing somewhat easier.  A strict JSON parser would only allow JSON
 * Objects and Arrays at the top level.
 */
top_level_value(A) ::= value(B).  { A = B; }

/* Values */
%type STRING { cfish_CharBuf* }

value(A) ::= FALSE(B).   { A = B; }
value(A) ::= NULL(B).    { A = B; }
value(A) ::= TRUE(B).    { A = B; }
value(A) ::= object(B).  { A = (cfish_Obj*)B; }
value(A) ::= array(B).   { A = (cfish_Obj*)B; }
value(A) ::= NUMBER(B).  { A = (cfish_Obj*)B; }
value(A) ::= STRING(B).  { A = B; }

/* Javascript Objects, implemented as Clownfish Hashes. */
%type object                    { cfish_Hash* }
%type empty_object              { cfish_Hash* }
%type single_pair_object        { cfish_Hash* }
%type multi_pair_object         { cfish_Hash* }
%type key_value_pair_list       { cfish_Hash* }
%destructor object              { CFISH_DECREF($$); }
%destructor empty_object        { CFISH_DECREF($$); }
%destructor single_pair_object  { CFISH_DECREF($$); }
%destructor multi_pair_object   { CFISH_DECREF($$); }
%destructor key_value_pair_list { CFISH_DECREF($$); }

object(A) ::= empty_object(B).         { A = B; }
object(A) ::= single_pair_object(B).   { A = B; }
object(A) ::= multi_pair_object(B).    { A = B; }

empty_object(A) ::= LEFT_CURLY_BRACKET RIGHT_CURLY_BRACKET.
{ 
    A = cfish_Hash_new(0);
}

single_pair_object(A) ::= LEFT_CURLY_BRACKET STRING(B) COLON value(C) RIGHT_CURLY_BRACKET.
{
    A = cfish_Hash_new(1);
    Cfish_Hash_Store(A, (cfish_Obj*)B, C);
    CFISH_DECREF(B);
}

multi_pair_object(A) ::= LEFT_CURLY_BRACKET key_value_pair_list(B) STRING(C) COLON value(D) RIGHT_CURLY_BRACKET.
{
    A = B;
    Cfish_Hash_Store(A, (cfish_Obj*)C, D);
    CFISH_DECREF(C);
}

key_value_pair_list(A) ::= key_value_pair_list(B) STRING(C) COLON value(D) COMMA.
{ 
    A = B; 
    Cfish_Hash_Store(A, (cfish_Obj*)C, D);
    CFISH_DECREF(C);
}

key_value_pair_list(A) ::= STRING(B) COLON value(C) COMMA.
{
    A = cfish_Hash_new(0);
    Cfish_Hash_Store(A, (cfish_Obj*)B, C);
    CFISH_DECREF(B);
}

/* Arrays. */
%type array                     { cfish_VArray* }
%type empty_array               { cfish_VArray* }
%type single_elem_array         { cfish_VArray* }
%type multi_elem_array          { cfish_VArray* }
%type array_elem_list           { cfish_VArray* }
%destructor array               { CFISH_DECREF($$); }
%destructor single_elem_array   { CFISH_DECREF($$); }
%destructor multi_elem_array    { CFISH_DECREF($$); }
%destructor array_elem_list     { CFISH_DECREF($$); }

array(A) ::= empty_array(B).       { A = B; }
array(A) ::= single_elem_array(B). { A = B; }
array(A) ::= multi_elem_array(B).  { A = B; }

empty_array(A) ::= LEFT_SQUARE_BRACKET RIGHT_SQUARE_BRACKET.
{
    A = cfish_VA_new(0);
}

single_elem_array(A) ::= LEFT_SQUARE_BRACKET value(B) RIGHT_SQUARE_BRACKET.
{
    A = cfish_VA_new(1);
    Cfish_VA_Push(A, B);
}

multi_elem_array(A) ::= LEFT_SQUARE_BRACKET array_elem_list(B) value(C) RIGHT_SQUARE_BRACKET.
{
    A = B;
    Cfish_VA_Push(A, C);
}

array_elem_list(A) ::= array_elem_list(B) value(C) COMMA. 
{ 
    A = B; 
    Cfish_VA_Push(A, C);
}

array_elem_list(A) ::= value(B) COMMA.
{
    A = cfish_VA_new(1);
    Cfish_VA_Push(A, B);
}

