/*
   +----------------------------------------------------------------------+
   | Copyright (c) The PHP Group                                          |
   +----------------------------------------------------------------------+
   | This source file is subject to version 3.01 of the PHP license,      |
   | that is bundled with this package in the file LICENSE, and is        |
   | available through the world-wide-web at the following url:           |
   | https://www.php.net/license/3_01.txt                                 |
   | If you did not receive a copy of the PHP license and are unable to   |
   | obtain it through the world-wide-web, please send a note to          |
   | license@php.net so we can mail you a copy immediately.               |
   +----------------------------------------------------------------------+
   | Author: Dmitry Stogov <dmitry@zend.com>                              |
   +----------------------------------------------------------------------+
*/

/*
To generate ffi_parser.c use llk <https://github.com/dstogov/llk>:
php llk.php ffi.g
*/

%start          declarations
%sub-start      type_name
%case-sensetive true
%global-vars    false
%output         "ffi_parser.c"
%language       "c"
%indent         "\t"

%{
/*
   +----------------------------------------------------------------------+
   | Copyright (c) The PHP Group                                          |
   +----------------------------------------------------------------------+
   | This source file is subject to version 3.01 of the PHP license,      |
   | that is bundled with this package in the file LICENSE, and is        |
   | available through the world-wide-web at the following url:           |
   | https://www.php.net/license/3_01.txt                                 |
   | If you did not receive a copy of the PHP license and are unable to   |
   | obtain it through the world-wide-web, please send a note to          |
   | license@php.net so we can mail you a copy immediately.               |
   +----------------------------------------------------------------------+
   | Author: Dmitry Stogov <dmitry@zend.com>                              |
   +----------------------------------------------------------------------+
*/

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include "php.h"
#include "php_ffi.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define yy_buf  FFI_G(buf)
#define yy_end  FFI_G(end)
#define yy_pos  FFI_G(pos)
#define yy_text FFI_G(text)
#define yy_line FFI_G(line)

/* forward declarations */
static void yy_error(const char *msg);
static void yy_error_sym(const char *msg, int sym);

%}

declarations:
	(
		{zend_ffi_dcl common_dcl = ZEND_FFI_ATTR_INIT;}
		"__extension__"?
		declaration_specifiers(&common_dcl)
		(
			{const char *name;}
			{size_t name_len;}
			{zend_ffi_dcl dcl;}
			{dcl = common_dcl;}
			declarator(&dcl, &name, &name_len)
			(
				{zend_ffi_val asm_str;}
				"__asm__"
				"("
				STRING(&asm_str)+
				/*TODO*/
				")"
			)?
			attributes(&dcl)?
			initializer?
			{zend_ffi_declare(name, name_len, &dcl);}
			(	","
				{dcl = common_dcl;}
				declarator(&dcl, &name, &name_len)
				attributes(&dcl)?
				initializer?
				{zend_ffi_declare(name, name_len, &dcl);}
			)*
		)?
		";"
	)*
;

declaration_specifiers(zend_ffi_dcl *dcl):
	(	?{sym != YY_ID || !(dcl->flags & ZEND_FFI_DCL_TYPE_SPECIFIERS)}
		(	{if (dcl->flags & ZEND_FFI_DCL_STORAGE_CLASS) yy_error_sym("unexpected", sym);}
			"typedef"
			{dcl->flags |= ZEND_FFI_DCL_TYPEDEF;}
		|	{if (dcl->flags & ZEND_FFI_DCL_STORAGE_CLASS) yy_error_sym("unexpected", sym);}
			"extern"
			{dcl->flags |= ZEND_FFI_DCL_EXTERN;}
		|	{if (dcl->flags & ZEND_FFI_DCL_STORAGE_CLASS) yy_error_sym("unexpected", sym);}
			"static"
			{dcl->flags |= ZEND_FFI_DCL_STATIC;}
		|	{if (dcl->flags & ZEND_FFI_DCL_STORAGE_CLASS) yy_error_sym("unexpected", sym);}
			"auto"
			{dcl->flags |= ZEND_FFI_DCL_AUTO;}
		|	{if (dcl->flags & ZEND_FFI_DCL_STORAGE_CLASS) yy_error_sym("unexpected", sym);}
			"register"
			{dcl->flags |= ZEND_FFI_DCL_REGISTER;}
//		|	"_Thread_local" // TODO: not-implemented ???
		|	("inline"|"__inline"|"__inline__")
			{dcl->flags |= ZEND_FFI_DCL_INLINE;}
		|	"_Noreturn"
			{dcl->flags |= ZEND_FFI_DCL_NO_RETURN;}
		|	"_Alignas"
			"("
			(	&type_name_start
				{zend_ffi_dcl align_dcl = ZEND_FFI_ATTR_INIT;}
				type_name(&align_dcl)
				{zend_ffi_align_as_type(dcl, &align_dcl);}
			|	{zend_ffi_val align_val;}
				constant_expression(&align_val)
				{zend_ffi_align_as_val(dcl, &align_val);}
			)
			")"
		|	attributes(dcl)
		|	type_qualifier(dcl)
		|	type_specifier(dcl)
		)
	)+
;

specifier_qualifier_list(zend_ffi_dcl *dcl):
	"__extension__"?
	(	?{sym != YY_ID || zend_ffi_is_typedef_name((const char*)yy_text, yy_pos - yy_text) || (dcl->flags & ZEND_FFI_DCL_TYPE_SPECIFIERS) == 0}
		(	type_specifier(dcl)
		|	type_qualifier(dcl)
		|	attributes(dcl)
		)
	)+
;

type_qualifier_list(zend_ffi_dcl *dcl):
	(	type_qualifier(dcl)
	|	attributes(dcl)
	)+
;

type_qualifier(zend_ffi_dcl *dcl):
		("const"|"__const"|"__const__")
		{dcl->flags |= ZEND_FFI_DCL_CONST;}
		{dcl->attr |= ZEND_FFI_ATTR_CONST;}
	|	("restrict"|"__restrict"|"__restrict__")
		{dcl->flags |= ZEND_FFI_DCL_RESTRICT;}
	|	("volatile"|"__volatile"|"__volatile__")
		{dcl->flags |= ZEND_FFI_DCL_VOLATILE;}
	|	"_Atomic"
		{dcl->flags |= ZEND_FFI_DCL_ATOMIC;}
;

type_specifier(zend_ffi_dcl *dcl):
    {const char *name;}
    {size_t name_len;}
	(	{if (dcl->flags & ZEND_FFI_DCL_TYPE_SPECIFIERS) yy_error_sym("unexpected", sym);}
		"void"
		{dcl->flags |= ZEND_FFI_DCL_VOID;}
	|	{if (dcl->flags & (ZEND_FFI_DCL_TYPE_SPECIFIERS-(ZEND_FFI_DCL_SIGNED|ZEND_FFI_DCL_UNSIGNED))) yy_error_sym("unexpected", sym);}
		"char"
		{dcl->flags |= ZEND_FFI_DCL_CHAR;}
	|	{if (dcl->flags & (ZEND_FFI_DCL_TYPE_SPECIFIERS-(ZEND_FFI_DCL_SIGNED|ZEND_FFI_DCL_UNSIGNED|ZEND_FFI_DCL_INT))) yy_error_sym("unexpected", sym);}
		"short"
		{dcl->flags |= ZEND_FFI_DCL_SHORT;}
	|   {if (dcl->flags & (ZEND_FFI_DCL_TYPE_SPECIFIERS-(ZEND_FFI_DCL_SIGNED|ZEND_FFI_DCL_UNSIGNED|ZEND_FFI_DCL_SHORT|ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_LONG_LONG))) yy_error_sym("unexpected", sym);}
		"int"
		{dcl->flags |= ZEND_FFI_DCL_INT;}
	|	{
			if (dcl->flags & ZEND_FFI_DCL_LONG) {
				if (dcl->flags & (ZEND_FFI_DCL_TYPE_SPECIFIERS-(ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_SIGNED|ZEND_FFI_DCL_UNSIGNED|ZEND_FFI_DCL_INT))) yy_error_sym("unexpected", sym);
				dcl->flags |= ZEND_FFI_DCL_LONG_LONG;
			} else {
				if (dcl->flags & (ZEND_FFI_DCL_TYPE_SPECIFIERS-(ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_SIGNED|ZEND_FFI_DCL_UNSIGNED|ZEND_FFI_DCL_INT|ZEND_FFI_DCL_DOUBLE|ZEND_FFI_DCL_COMPLEX))) yy_error_sym("unexpected", sym);
				dcl->flags |= ZEND_FFI_DCL_LONG;
			}
		}
		"long"
	|	{if (dcl->flags & (ZEND_FFI_DCL_TYPE_SPECIFIERS-(ZEND_FFI_DCL_COMPLEX))) yy_error_sym("unexpected", sym);}
		"float"
		{dcl->flags |= ZEND_FFI_DCL_FLOAT;}
	|	{if (dcl->flags & (ZEND_FFI_DCL_TYPE_SPECIFIERS-(ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_COMPLEX))) yy_error_sym("unexpected", sym);}
		"double"
		{dcl->flags |= ZEND_FFI_DCL_DOUBLE;}
	|	{if (dcl->flags & (ZEND_FFI_DCL_TYPE_SPECIFIERS-(ZEND_FFI_DCL_CHAR|ZEND_FFI_DCL_SHORT|ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_LONG_LONG|ZEND_FFI_DCL_INT))) yy_error_sym("unexpected", sym);}
		"signed"
		{dcl->flags |= ZEND_FFI_DCL_SIGNED;}
	|	{if (dcl->flags & (ZEND_FFI_DCL_TYPE_SPECIFIERS-(ZEND_FFI_DCL_CHAR|ZEND_FFI_DCL_SHORT|ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_LONG_LONG|ZEND_FFI_DCL_INT))) yy_error_sym("unexpected", sym);}
		"unsigned"
		{dcl->flags |= ZEND_FFI_DCL_UNSIGNED;}
	|	{if (dcl->flags & ZEND_FFI_DCL_TYPE_SPECIFIERS) yy_error_sym("unexpected", sym);}
		"_Bool"
		{dcl->flags |= ZEND_FFI_DCL_BOOL;}
	|	{if (dcl->flags & (ZEND_FFI_DCL_TYPE_SPECIFIERS-(ZEND_FFI_DCL_FLOAT|ZEND_FFI_DCL_DOUBLE|ZEND_FFI_DCL_LONG))) yy_error_sym("unexpected", sym);}
		("_Complex"|"complex"|"__complex"|"__complex__")
		{dcl->flags |= ZEND_FFI_DCL_COMPLEX;}
//	|	"_Atomic" "(" type_name ")" // TODO: not-implemented ???
	|	{if (dcl->flags & ZEND_FFI_DCL_TYPE_SPECIFIERS) yy_error_sym("unexpected", sym);}
		struct_or_union_specifier(dcl)
	|	{if (dcl->flags & ZEND_FFI_DCL_TYPE_SPECIFIERS) yy_error_sym("unexpected", sym);}
		enum_specifier(dcl)
	|   {if (dcl->flags & ZEND_FFI_DCL_TYPE_SPECIFIERS) yy_error_sym("unexpected", sym);}
		{/*redeclaration of '%.*s' ??? */}
		ID(&name, &name_len)
		{dcl->flags |= ZEND_FFI_DCL_TYPEDEF_NAME;}
		{zend_ffi_resolve_typedef(name, name_len, dcl);}
	)
;

struct_or_union_specifier(zend_ffi_dcl *dcl):
	(	"struct"
		{dcl->flags |= ZEND_FFI_DCL_STRUCT;}
	| 	"union"
		{dcl->flags |= ZEND_FFI_DCL_UNION;}
	)
	attributes(dcl)?
	(   {const char *name;}
		{size_t name_len;}
		ID(&name, &name_len)
		{zend_ffi_declare_tag(name, name_len, dcl, 1);}
		(	struct_contents(dcl)
			{zend_ffi_declare_tag(name, name_len, dcl, 0);}
		)?
	|	{zend_ffi_make_struct_type(dcl);}
		struct_contents(dcl)
	)
;

struct_contents(zend_ffi_dcl *dcl):
	"{"
	(	struct_declaration(dcl)
		(	";"
			struct_declaration(dcl)
		)*
		(";")?
	)?
    "}"
	attributes(dcl)?+
	{zend_ffi_adjust_struct_size(dcl);}
;


struct_declaration(zend_ffi_dcl *struct_dcl):
	{zend_ffi_dcl common_field_dcl = ZEND_FFI_ATTR_INIT;}
	specifier_qualifier_list(&common_field_dcl)
	(	/* empty */
		{zend_ffi_add_anonymous_field(struct_dcl, &common_field_dcl);}
	|	struct_declarator(struct_dcl, &common_field_dcl)
		(	","
			{zend_ffi_dcl field_dcl = common_field_dcl;}
			attributes(&field_dcl)?
			struct_declarator(struct_dcl, &field_dcl)
		)*
	)
;

struct_declarator(zend_ffi_dcl *struct_dcl, zend_ffi_dcl *field_dcl):
	{const char *name = NULL;}
	{size_t name_len = 0;}
	{zend_ffi_val bits;}
	(	declarator(field_dcl, &name, &name_len)
		(	":"
			constant_expression(&bits)
			attributes(field_dcl)?
			{zend_ffi_add_bit_field(struct_dcl, name, name_len, field_dcl, &bits);}
		|	/*empty */
			attributes(field_dcl)?
			{zend_ffi_add_field(struct_dcl, name, name_len, field_dcl);}
		)
	|	":"
		constant_expression(&bits)
		{zend_ffi_add_bit_field(struct_dcl, NULL, 0, field_dcl, &bits);}
	)
;

enum_specifier(zend_ffi_dcl *dcl):
	"enum"
	{dcl->flags |= ZEND_FFI_DCL_ENUM;}
	attributes(dcl)?
	(   {const char *name;}
		{size_t name_len;}
		ID(&name, &name_len)
		(	{zend_ffi_declare_tag(name, name_len, dcl, 0);}
			"{"
			enumerator_list(dcl)
			"}"
			attributes(dcl)?+
		|	{zend_ffi_declare_tag(name, name_len, dcl, 1);}
		)
	|	"{"
		{zend_ffi_make_enum_type(dcl);}
		enumerator_list(dcl)
		"}"
		attributes(dcl)?+
	)
;

enumerator_list(zend_ffi_dcl *enum_dcl):
	{int64_t min = 0, max = 0, last = -1;}
	enumerator(enum_dcl, &min, &max, &last)
	(	","
		enumerator(enum_dcl, &min, &max, &last)
	)*
	","?
;

enumerator(zend_ffi_dcl *enum_dcl, int64_t *min, int64_t *max, int64_t *last):
	{const char *name;}
	{size_t name_len;}
	{zend_ffi_val val = {.kind = ZEND_FFI_VAL_EMPTY};}
	ID(&name, &name_len)
	(	"="
		constant_expression(&val)
	)?
	{zend_ffi_add_enum_val(enum_dcl, name, name_len, &val, min, max, last);}
;

declarator(zend_ffi_dcl *dcl, const char **name, size_t *name_len):
	{zend_ffi_dcl nested_dcl = {ZEND_FFI_DCL_CHAR, 0, 0, 0, NULL};}
	{bool nested = 0;}
	pointer(dcl)?
	(	ID(name, name_len)
	|	"("
		attributes(&nested_dcl)?
		declarator(&nested_dcl, name, name_len)
		")"
		{nested = 1;}
	)
	array_or_function_declarators(dcl, &nested_dcl)?
	{if (nested) zend_ffi_nested_declaration(dcl, &nested_dcl);}
;

abstract_declarator(zend_ffi_dcl *dcl):
	{zend_ffi_dcl nested_dcl = {ZEND_FFI_DCL_CHAR, 0, 0, 0, NULL};}
	{bool nested = 0;}
	pointer(dcl)?
	(	&nested_declarator_start
		"("
		attributes(&nested_dcl)?
		abstract_declarator(&nested_dcl)
		")"
		{nested = 1;}
	)?
	array_or_function_declarators(dcl, &nested_dcl)?
	{if (nested) zend_ffi_nested_declaration(dcl, &nested_dcl);}
;

parameter_declarator(zend_ffi_dcl *dcl, const char **name, size_t *name_len):
	{zend_ffi_dcl nested_dcl = {ZEND_FFI_DCL_CHAR, 0, 0, 0, NULL};}
	{bool nested = 0;}
	pointer(dcl)?
	(	&nested_declarator_start
		"("
		attributes(&nested_dcl)?
		parameter_declarator(&nested_dcl, name, name_len)
		")"
		{nested = 1;}
	|	ID(name, name_len)
	|	/* empty */
	)
	array_or_function_declarators(dcl, &nested_dcl)?
	{if (nested) zend_ffi_nested_declaration(dcl, &nested_dcl);}
;

pointer(zend_ffi_dcl *dcl):
	(	"*"
		{zend_ffi_make_pointer_type(dcl);}
		type_qualifier_list(dcl)?
	)+
;

array_or_function_declarators(zend_ffi_dcl *dcl, zend_ffi_dcl *nested_dcl):
	{zend_ffi_dcl dummy = ZEND_FFI_ATTR_INIT;}
	{zend_ffi_val len = {.kind = ZEND_FFI_VAL_EMPTY};}
	{HashTable *args = NULL;}
	{uint32_t attr = 0;}
	(	"["
	    (	"static"
			type_qualifier_list(&dummy)?
			assignment_expression(&len)
		|	type_qualifier_list(&dummy)
			(	"static" assignment_expression(&len)
			|	/* empty */
				{attr |= ZEND_FFI_ATTR_INCOMPLETE_ARRAY;}
			|	"*"
				{attr |= ZEND_FFI_ATTR_VLA;}
			|	assignment_expression(&len)
			)
		|	(	/* empty */
				{attr |= ZEND_FFI_ATTR_INCOMPLETE_ARRAY;}
			|	"*"
				{attr |= ZEND_FFI_ATTR_VLA;}
			|	assignment_expression(&len)
			)
		)
		"]"
		array_or_function_declarators(dcl, nested_dcl)?
		{dcl->attr |= attr;}
		{zend_ffi_make_array_type(dcl, &len);}
	|	"("
		(
			parameter_declaration(&args)
			(	","
				parameter_declaration(&args)
			)*
			(
				","
				"..."
				{attr |= ZEND_FFI_ATTR_VARIADIC;}
			)?
		|	"..."
			{attr |= ZEND_FFI_ATTR_VARIADIC;}
		)?
		")"
		array_or_function_declarators(dcl, nested_dcl)?
		{dcl->attr |= attr;}
		{zend_ffi_make_func_type(dcl, args, nested_dcl);}
//	|	"(" (ID ("," ID)*)? ")" // TODO: ANSI function not-implemented ???
	)
;

parameter_declaration(HashTable **args):
	{const char *name = NULL;}
	{size_t name_len = 0;}
	{bool old_allow_vla = FFI_G(allow_vla);}
	{FFI_G(allow_vla) = 1;}
	{zend_ffi_dcl param_dcl = ZEND_FFI_ATTR_INIT;}
	specifier_qualifier_list(&param_dcl)
	parameter_declarator(&param_dcl, &name, &name_len)
	/*attributes(&param_dcl)? conflict ???*/
	{zend_ffi_add_arg(args, name, name_len, &param_dcl);}
	{FFI_G(allow_vla) = old_allow_vla;}
;

type_name(zend_ffi_dcl *dcl):
	specifier_qualifier_list(dcl)
	abstract_declarator(dcl)
;

attributes(zend_ffi_dcl *dcl):
	{const char *name;}
	{size_t name_len;}
	{zend_ffi_val val;}
	(
		("__attribute"|"__attribute__")
		"("
		"("
		attrib(dcl)
		(	","
			attrib(dcl)
		)*
		")"
		")"
	|	"__declspec"
		"("
			(	ID(&name, &name_len)
				(
					"("
						assignment_expression(&val)
						{zend_ffi_add_msvc_attribute_value(dcl, name, name_len, &val);}
					")"
				)?
			)+
		")"
	|	"__cdecl"
		{zend_ffi_set_abi(dcl, ZEND_FFI_ABI_CDECL);}
	|	"__stdcall"
		{zend_ffi_set_abi(dcl, ZEND_FFI_ABI_STDCALL);}
	|	"__fastcall"
		{zend_ffi_set_abi(dcl, ZEND_FFI_ABI_FASTCALL);}
	|	"__thiscall"
		{zend_ffi_set_abi(dcl, ZEND_FFI_ABI_THISCALL);}
	|	"__vectorcall"
		{zend_ffi_set_abi(dcl, ZEND_FFI_ABI_VECTORCALL);}
	)++
;

attrib(zend_ffi_dcl *dcl):
	{const char *name;}
	{size_t name_len;}
	{int n;}
	{zend_ffi_val val;}
	{bool orig_attribute_parsing;}
	(   ID(&name, &name_len)
		(	/* empty */
			{zend_ffi_add_attribute(dcl, name, name_len);}
		|	"("
			{orig_attribute_parsing = FFI_G(attribute_parsing);}
			{FFI_G(attribute_parsing) = 1;}
			assignment_expression(&val)
			{zend_ffi_add_attribute_value(dcl, name, name_len, 0, &val);}
			{n = 0;}
			(	","
				assignment_expression(&val)
				{zend_ffi_add_attribute_value(dcl, name, name_len, ++n, &val);}
			)*
			{FFI_G(attribute_parsing) = orig_attribute_parsing;}
			")"
		)
	|	"const"
	|	"__const"
	|	"__const__"
	)?
;

initializer:
	{zend_ffi_val dummy;}
	"="
	(	assignment_expression(&dummy)
	|	"{" designation? initializer ( "," designation? initializer)* ","? "}"
	)
;

designation:
	{const char *name;}
	{size_t name_len;}
	{zend_ffi_val dummy;}
	(	"[" constant_expression(&dummy) "]"
	|	"." ID(&name, &name_len)
	)+
	"="
;

expr_list:
	{zend_ffi_val dummy;}
	assignment_expression(&dummy)
	(	","
		assignment_expression(&dummy)
	)*
;

expression(zend_ffi_val *val):
	assignment_expression(val)
	(	","
		assignment_expression(val)
	)*
;

assignment_expression(zend_ffi_val *val):
//	(	unary_expression
//		("="|"*="|"/="|"%="|"+="|"-="|"<<="|">>="|"&="|"^="|"|=")
//	)* // TODO: not-implemented ???
	conditional_expression(val)
;

constant_expression(zend_ffi_val *val):
	conditional_expression(val)
;

conditional_expression(zend_ffi_val *val):
	{zend_ffi_val op2, op3;}
	logical_or_expression(val)
	(	"?"
		expression(&op2)
		":"
		conditional_expression(&op3)
		{zend_ffi_expr_conditional(val, &op2, &op3);}
	)?
;

logical_or_expression(zend_ffi_val *val):
	{zend_ffi_val op2;}
	logical_and_expression(val)
	(   "||"
		logical_and_expression(&op2)
		{zend_ffi_expr_bool_or(val, &op2);}
	)*
;

logical_and_expression(zend_ffi_val *val):
	{zend_ffi_val op2;}
	inclusive_or_expression(val)
	(   "&&"
		inclusive_or_expression(&op2)
		{zend_ffi_expr_bool_and(val, &op2);}
	)*
;

inclusive_or_expression(zend_ffi_val *val):
	{zend_ffi_val op2;}
	exclusive_or_expression(val)
	(	"|"
		exclusive_or_expression(&op2)
		{zend_ffi_expr_bw_or(val, &op2);}
	)*
;

exclusive_or_expression(zend_ffi_val *val):
	{zend_ffi_val op2;}
	and_expression(val)
	(	"^"
		and_expression(&op2)
		{zend_ffi_expr_bw_xor(val, &op2);}
	)*
;

and_expression(zend_ffi_val *val):
	{zend_ffi_val op2;}
	equality_expression(val)
	(	"&"
		equality_expression(&op2)
		{zend_ffi_expr_bw_and(val, &op2);}
	)*
;

equality_expression(zend_ffi_val *val):
	{zend_ffi_val op2;}
	relational_expression(val)
	(	"=="
		relational_expression(&op2)
		{zend_ffi_expr_is_equal(val, &op2);}
	|	"!="
		relational_expression(&op2)
		{zend_ffi_expr_is_not_equal(val, &op2);}
	)*
;

relational_expression(zend_ffi_val *val):
	{zend_ffi_val op2;}
	shift_expression(val)
	(	"<"
		shift_expression(&op2)
		{zend_ffi_expr_is_less(val, &op2);}
	|	">"
		shift_expression(&op2)
		{zend_ffi_expr_is_greater(val, &op2);}
	|	"<="
		shift_expression(&op2)
		{zend_ffi_expr_is_less_or_equal(val, &op2);}
	|	">="
		shift_expression(&op2)
		{zend_ffi_expr_is_greater_or_equal(val, &op2);}
	)*
;

shift_expression(zend_ffi_val *val):
	{zend_ffi_val op2;}
	additive_expression(val)
	(	"<<"
		additive_expression(&op2)
		{zend_ffi_expr_shift_left(val, &op2);}
	|	">>"
		additive_expression(&op2)
		{zend_ffi_expr_shift_right(val, &op2);}
	)*
;

additive_expression(zend_ffi_val *val):
	{zend_ffi_val op2;}
	multiplicative_expression(val)
	(	"+"
		multiplicative_expression(&op2)
		{zend_ffi_expr_add(val, &op2);}
	|	"-"
		multiplicative_expression(&op2)
		{zend_ffi_expr_sub(val, &op2);}
	)*
;

multiplicative_expression(zend_ffi_val *val):
	{zend_ffi_val op2;}
	cast_expression(val)
	(	"*"
		cast_expression(&op2)
		{zend_ffi_expr_mul(val, &op2);}
	|	"/"
		cast_expression(&op2)
		{zend_ffi_expr_div(val, &op2);}
	|	"%"
		cast_expression(&op2)
		{zend_ffi_expr_mod(val, &op2);}
	)*
;

cast_expression(zend_ffi_val *val):
	{int do_cast = 0;}
	{zend_ffi_dcl dcl = ZEND_FFI_ATTR_INIT;}
	(	&( "(" type_name_start )
		"("
		type_name(&dcl)
		")"
		{do_cast = 1;}
	)?
	unary_expression(val)
	{if (do_cast) zend_ffi_expr_cast(val, &dcl);}
;

unary_expression(zend_ffi_val *val):
	{const char *name;}
	{size_t name_len;}
	{zend_ffi_dcl dcl = ZEND_FFI_ATTR_INIT;}
	(	ID(&name, &name_len)
		{zend_ffi_resolve_const(name, name_len, val);}
		(
			(	"["
				expr_list
				"]"
			|	"("
				expr_list?
				")"
			|	"."
				ID(&name, &name_len)
			|	"->"
				ID(&name, &name_len)
		    |	"++"
			|	"--"
			)
			{zend_ffi_val_error(val);}
		)*
	|	OCTNUMBER(val)
	|	DECNUMBER(val)
	|	HEXNUMBER(val)
	|	FLOATNUMBER(val)
	|	STRING(val)
	|	CHARACTER(val)
	|	"("
		expression(val)
		")"
	|	"++"
		unary_expression(val)
		{zend_ffi_val_error(val);}
	|	"--"
		unary_expression(val)
		{zend_ffi_val_error(val);}
	|	"&"
		cast_expression(val)
		{zend_ffi_val_error(val);}
	|	"*"
		cast_expression(val)
		{zend_ffi_val_error(val);}
	|	"+"
		cast_expression(val)
		{zend_ffi_expr_plus(val);}
	|	"-"
		cast_expression(val)
		{zend_ffi_expr_neg(val);}
	|	"~"
		cast_expression(val)
		{zend_ffi_expr_bw_not(val);}
	|	"!"
		cast_expression(val)
		{zend_ffi_expr_bool_not(val);}
	|	"sizeof"
		(	&( "(" type_name_start )
			"("
			type_name(&dcl)
			")"
			{zend_ffi_expr_sizeof_type(val, &dcl);}
		|	unary_expression(val)
			{zend_ffi_expr_sizeof_val(val);}
		)
	|	"_Alignof"
		"("
		type_name(&dcl)
		")"
		{zend_ffi_expr_alignof_type(val, &dcl);}
	|	("__alignof"|"__alignof__")
		(	&( "(" type_name_start )
			"("
			type_name(&dcl)
			")"
			{zend_ffi_expr_alignof_type(val, &dcl);}
		|	unary_expression(val)
			{zend_ffi_expr_alignof_val(val);}
		)
	)
;

/* lookahead rules */
nested_declarator_start:
    "("
	(   ?{!zend_ffi_is_typedef_name((const char*)yy_text, yy_pos - yy_text)}
		ID
	|	"__attribute"
	|	"__attribute__"
	|	"__declspec"
	|	"*"
	|	"("
	|	"["
	)
;

type_name_start:
	(	?{zend_ffi_is_typedef_name((const char*)yy_text, yy_pos - yy_text)}
		ID
	|	"void"
	|	"char"
	|	"short"
	|   "int"
	|	"long"
	|	"float"
	|	"double"
	|	"signed"
	|	"unsigned"
	|	"_Bool"
	|	"_Complex"
	|	"complex"
	|	"__complex"
	|	"__complex__"
	|	"struct"
	|	"union"
	|	"enum"
	|	"const"
	|	"__const"
	|	"__const__"
	|	"restrict"
	|	"__restict"
	|	"__restrict__"
	|	"volatile"
	|	"__volatile"
	|	"__volatile__"
	|	"_Atomic"
	|	"__attribute"
	|	"__attribute__"
	|	"__declspec"
	)
;

/* scanner rules */
ID(const char **name, size_t *name_len):
	/[A-Za-z_][A-Za-z_0-9]*/
	{*name = (const char*)yy_text; *name_len = yy_pos - yy_text;}
;

OCTNUMBER(zend_ffi_val *val):
	/0[0-7]*([Uu](L|l|LL|l)?|[Ll][Uu]?|(LL|ll)[Uu])?/
	{zend_ffi_val_number(val, 8, (const char*)yy_text, yy_pos - yy_text);}
;

DECNUMBER(zend_ffi_val *val):
	/[1-9][0-9]*([Uu](L|l|LL|l)?|[Ll][Uu]?|(LL|ll)[Uu])?/
	{zend_ffi_val_number(val, 10, (const char*)yy_text, yy_pos - yy_text);}
;

HEXNUMBER(zend_ffi_val *val):
	/0[xX][0-9A-Fa-f][0-9A-Fa-f]*([Uu](L|l|LL|l)?|[Ll][Uu]?|(LL|ll)[Uu])?/
	{zend_ffi_val_number(val, 16, (const char*)yy_text + 2, yy_pos - yy_text - 2);}
;

FLOATNUMBER(zend_ffi_val *val):
	/([0-9]*\.[0-9]+([Ee][\+\-]?[0-9]+)?|[0-9]+\.([Ee][\+\-]?[0-9]+)?|[0-9]+[Ee][\+\-]?[0-9]+)[flFL]?/
	{zend_ffi_val_float_number(val, (const char*)yy_text, yy_pos - yy_text);}
;

STRING(zend_ffi_val *val):
	/(u8|u|U|L)?"([^"\\]|\\.)*"/
	{zend_ffi_val_string(val, (const char*)yy_text, yy_pos - yy_text);}
;

CHARACTER(zend_ffi_val *val):
	/[LuU]?'([^'\\]|\\.)*'/
	{zend_ffi_val_character(val, (const char*)yy_text, yy_pos - yy_text);}
;

EOL: /\r\n|\r|\n/;
WS: /[ \t\f\v]+/;
ONE_LINE_COMMENT: /(\/\/|#)[^\r\n]*(\r\n|\r|\n)/;
COMMENT: /\/\*([^\*]|\*+[^\*\/])*\*+\//;

SKIP: ( EOL | WS | ONE_LINE_COMMENT | COMMENT )*;

%%
zend_result zend_ffi_parse_decl(const char *str, size_t len) {
	if (SETJMP(FFI_G(bailout))==0) {
		FFI_G(allow_vla) = 0;
		FFI_G(attribute_parsing) = 0;
		yy_buf = (unsigned char*)str;
		yy_end = yy_buf + len;
		parse();
		return SUCCESS;
	} else {
		return FAILURE;
	}
}

zend_result zend_ffi_parse_type(const char *str, size_t len, zend_ffi_dcl *dcl) {
	int sym;

	if (SETJMP(FFI_G(bailout))==0) {
		FFI_G(allow_vla) = 0;
		FFI_G(attribute_parsing) = 0;
		yy_pos = yy_text = yy_buf = (unsigned char*)str;
		yy_end = yy_buf + len;
		yy_line = 1;
		sym = parse_type_name(get_sym(), dcl);
		if (sym != YY_EOF) {
			yy_error_sym("<EOF> expected, got", sym);
		}
		zend_ffi_validate_type_name(dcl);
		return SUCCESS;
	} else {
		return FAILURE;
	};
}

static void yy_error(const char *msg) {
	zend_ffi_parser_error("%s at line %d", msg, yy_line);
}

static void yy_error_sym(const char *msg, int sym) {
	zend_ffi_parser_error("%s '%s' at line %d", msg, sym_name[sym], yy_line);
}
