// This is the yacc input for creating the parser for HCL JSON.

%{
package json

import (
	"fmt"
	"strconv"

	"github.com/hashicorp/hcl/ast"
)

%}

%union {
	array    ast.ListNode
	assign   ast.AssignmentNode
	item     ast.Node
	klist    []ast.KeyedNode
	list     []ast.Node
	num      int
	str      string
	obj      ast.ObjectNode
}

%type	<array> array
%type	<assign> pair
%type	<item> value number
%type	<klist> members
%type	<list> elements
%type	<num> int
%type	<obj> object
%type	<str> frac

%token  <num> NUMBER
%token  <str> COLON COMMA IDENTIFIER EQUAL NEWLINE STRING
%token  <str> LEFTBRACE RIGHTBRACE LEFTBRACKET RIGHTBRACKET
%token  <str> TRUE FALSE NULL MINUS PERIOD

%%

top:
	object
	{
		obj := $1
		jsonResult = &obj
	}

object:
	LEFTBRACE members RIGHTBRACE
	{
		$$ = ast.ObjectNode{Elem: $2}
	}
|	LEFTBRACE RIGHTBRACE
	{
		$$ = ast.ObjectNode{}
	}

members:
	pair
	{
		$$ = []ast.KeyedNode{$1}
	}
|	pair COMMA members
	{
		$$ = append($3, $1)
	}

pair:
	STRING COLON value
	{
		$$ = ast.AssignmentNode{
			K:     $1,
			Value: $3,
		}
	}

value:
	STRING
	{
		$$ = ast.LiteralNode{
			Type:  ast.ValueTypeString,
			Value: $1,
		}
	}
|	number
	{
		$$ = $1
	}
|	object
	{
		$$ = $1
	}
|	array
	{
		$$ = $1
	}
|	TRUE
	{
		$$ = ast.LiteralNode{
			Type:  ast.ValueTypeBool,
			Value: true,
		}
	}
|	FALSE
	{
		$$ = ast.LiteralNode{
			Type:  ast.ValueTypeBool,
			Value: false,
		}
	}
|	NULL
	{
		$$ = ast.LiteralNode{
			Type:  ast.ValueTypeNil,
			Value: nil,
		}
	}

array:
	LEFTBRACKET RIGHTBRACKET
	{
		$$ = ast.ListNode{}
	}
|	LEFTBRACKET elements RIGHTBRACKET
	{
		$$ = ast.ListNode{Elem: $2}
	}

elements:
	value
	{
		$$ = []ast.Node{$1}
	}
|	value COMMA elements
	{
		$$ = append($3, $1)
	}

number:
	int
	{
		$$ = ast.LiteralNode{
			Type:  ast.ValueTypeInt,
			Value: $1,
		}
	}
|	int frac
	{
		fs := fmt.Sprintf("%d.%s", $1, $2)
		f, err := strconv.ParseFloat(fs, 64)
		if err != nil {
			panic(err)
		}

		$$ = ast.LiteralNode{
			Type:  ast.ValueTypeFloat,
			Value: f,
		}
	}

int:
	MINUS int
	{
		$$ = $2 * -1
	}
|	NUMBER
	{
		$$ = $1
	}

frac:
	PERIOD NUMBER
	{
		$$ = strconv.FormatInt(int64($2), 10)
	}

%%