/* parser.php.lemon
 * This file is part of the Phalcon Framework.
 *
 * (c) Phalcon Team <team@phalcon.io>
 *
 * For the full copyright and license information, please view the
 * LICENSE.txt file that was distributed with this source code.
 */

%token_prefix PHANNOT_
%token_type {phannot_parser_token*}
%default_type {zval}
%extra_argument {phannot_parser_status *status}
%name phannot_

%left COMMA .

%include {
#include "parser.php.inc.h"
}

%syntax_error {
	if (status->scanner_state->start_length) {
		char *token_name = NULL;
		const phannot_token_names *tokens = phannot_tokens;
		int active_token = status->scanner_state->active_token;
		unsigned int near_length = status->scanner_state->start_length;

		if (active_token) {
			do {
				if (tokens->code == active_token) {
					token_name = tokens->name;
					break;
				}
				++tokens;
			} while (tokens[0].code != 0);
		}

		if (!token_name) {
			token_name  = "UNKNOWN";
		}

		if (near_length > 0) {
			if (status->token->value) {
				spprintf(&status->syntax_error, 0, "Syntax error, unexpected token %s(%s), near to '%s' in %s on line %d", token_name, status->token->value, status->scanner_state->start, status->scanner_state->active_file, status->scanner_state->active_line);
			} else {
				spprintf(&status->syntax_error, 0, "Syntax error, unexpected token %s, near to '%s' in %s on line %d", token_name, status->scanner_state->start, status->scanner_state->active_file, status->scanner_state->active_line);
			}
		} else {
			if (active_token != PHANNOT_T_IGNORE) {
				if (status->token->value) {
					spprintf(&status->syntax_error, 0, "Syntax error, unexpected token %s(%s), at the end of docblock in %s on line %d", token_name, status->token->value, status->scanner_state->active_file, status->scanner_state->active_line);
				} else {
					spprintf(&status->syntax_error, 0, "Syntax error, unexpected token %s, at the end of docblock in %s on line %d", token_name, status->scanner_state->active_file, status->scanner_state->active_line);
				}
			} else {
				spprintf(&status->syntax_error, 0, "Syntax error, unexpected EOF, at the end of docblock in %s on line %d", status->scanner_state->active_file, status->scanner_state->active_line);
			}
		}
	} else {
		spprintf(&status->syntax_error, 0, "Syntax error, unexpected EOF in %s", status->scanner_state->active_file);
	}

	status->status = PHANNOT_PARSING_FAILED;
}

%stack_overflow {
	status->status = PHANNOT_PARSING_FAILED;
	spprintf(&status->syntax_error, 0, "Parsing failed, the annotation is nested too deeply in %s on line %d", status->scanner_state->active_file, status->scanner_state->active_line);
}

%token_destructor {
	if ($$) {
		if ($$->free_flag) {
			efree($$->token);
		}
		efree($$);
	}
}

program ::= annotation_language(Q) . {
	ZVAL_ZVAL(&status->ret, &Q, 1, 1);
}

%destructor annotation_language {
    zval_ptr_dtor(&$$);
}

annotation_language(R) ::= annotation_list(L) . {
	R = L;
}

%destructor annotation_list {
    zval_ptr_dtor(&$$);
}

annotation_list(R) ::= annotation_list(L) annotation(S) . {
	phannot_ret_zval_list(&R, &L, &S);
}

annotation_list(R) ::= annotation(S) . {
	phannot_ret_zval_list(&R, NULL, &S);
}

%destructor annotation {
    zval_ptr_dtor(&$$);
}

annotation(R) ::= AT IDENTIFIER(I) PARENTHESES_OPEN argument_list(L) PARENTHESES_CLOSE . {
	phannot_ret_annotation(&R, I, &L, status->scanner_state);
}

annotation(R) ::= AT IDENTIFIER(I) PARENTHESES_OPEN PARENTHESES_CLOSE . {
	phannot_ret_annotation(&R, I, NULL, status->scanner_state);
}

annotation(R) ::= AT IDENTIFIER(I) . {
	phannot_ret_annotation(&R, I, NULL, status->scanner_state);
}

%destructor argument_list {
    zval_ptr_dtor(&$$);
}

argument_list(R) ::= argument_list(L) COMMA argument_item(I) . {
	phannot_ret_zval_list(&R, &L, &I);
}

argument_list(R) ::= argument_item(I) . {
	phannot_ret_zval_list(&R, NULL, &I);
}

%destructor argument_item {
    zval_ptr_dtor(&$$);
}

argument_item(R) ::= expr(E) . {
	phannot_ret_named_item(&R, NULL, &E);
}

argument_item(R) ::= STRING(S) EQUALS expr(E) . {
	phannot_ret_named_item(&R, S, &E);
}

argument_item(R) ::= STRING(S) COLON expr(E) . {
	phannot_ret_named_item(&R, S, &E);
}

argument_item(R) ::= IDENTIFIER(I) EQUALS expr(E) . {
	phannot_ret_named_item(&R, I, &E);
}

argument_item(R) ::= IDENTIFIER(I) COLON expr(E) . {
	phannot_ret_named_item(&R, I, &E);
}

%destructor expr {
    zval_ptr_dtor(&$$);
}

expr(R) ::= annotation(S) . {
	R = S;
}

expr(R) ::= array(A) . {
	R = A;
}

expr(R) ::= IDENTIFIER(I) . {
	phannot_ret_literal_zval(&R, PHANNOT_T_IDENTIFIER, I);
}

expr(R) ::= INTEGER(I) . {
	phannot_ret_literal_zval(&R, PHANNOT_T_INTEGER, I);
}

expr(R) ::= STRING(S) . {
	phannot_ret_literal_zval(&R, PHANNOT_T_STRING, S);
}

expr(R) ::= DOUBLE(D) . {
	phannot_ret_literal_zval(&R, PHANNOT_T_DOUBLE, D);
}

expr(R) ::= NULL . {
	phannot_ret_literal_zval(&R, PHANNOT_T_NULL, NULL);
}

expr(R) ::= FALSE . {
	phannot_ret_literal_zval(&R, PHANNOT_T_FALSE, NULL);
}

expr(R) ::= TRUE . {
	phannot_ret_literal_zval(&R, PHANNOT_T_TRUE, NULL);
}

array(R) ::= BRACKET_OPEN argument_list(A) BRACKET_CLOSE . {
	phannot_ret_array(&R, &A);
}

array(R) ::= SBRACKET_OPEN argument_list(A) SBRACKET_CLOSE . {
	phannot_ret_array(&R, &A);
}
