parser_parse_expression (parser_context_t *context_p, /**< context */
                         int options) /**< option flags */
{
  size_t grouping_level = (options & PARSE_EXPR_LEFT_HAND_SIDE);

  parser_stack_push_uint8 (context_p, LEXER_EXPRESSION_START);

  if (options & PARSE_EXPR_HAS_LITERAL)
  {
    JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_LITERAL);
    goto process_unary_expression;
  }

  while (true)
  {
    if (parser_parse_unary_expression (context_p, &grouping_level))
    {
      parser_process_binary_opcodes (context_p, 0);
      break;
    }

    while (true)
    {
process_unary_expression:
      parser_process_unary_expression (context_p, grouping_level);

      if (JERRY_LIKELY (grouping_level != PARSE_EXPR_LEFT_HAND_SIDE))
      {
        uint8_t min_prec_treshold = 0;

        if (LEXER_IS_BINARY_OP_TOKEN (context_p->token.type))
        {
#if JERRY_ESNEXT
          if (JERRY_UNLIKELY (context_p->token.type == LEXER_NULLISH_COALESCING))
          {
            parser_check_invalid_logical_op (context_p, LEXER_LOGICAL_OR, LEXER_LOGICAL_AND);
          }
#endif /* JERRY_ESNEXT */

          min_prec_treshold = parser_binary_precedence_table[context_p->token.type - LEXER_FIRST_BINARY_OP];

#if JERRY_ESNEXT
          /* Check for BINARY_LVALUE tokens + LEXER_LOGICAL_OR + LEXER_LOGICAL_AND + LEXER_EXPONENTIATION */
          if ((min_prec_treshold == PARSER_RIGHT_TO_LEFT_ORDER_EXPONENTIATION)
              || (min_prec_treshold <= PARSER_RIGHT_TO_LEFT_ORDER_MAX_PRECEDENCE
                  && min_prec_treshold != PARSER_RIGHT_TO_LEFT_ORDER_TERNARY_PRECEDENCE))
          {
            /* Right-to-left evaluation order. */
            min_prec_treshold++;
          }
#else /* !JERRY_ESNEXT */
          /* Check for BINARY_LVALUE tokens + LEXER_LOGICAL_OR + LEXER_LOGICAL_AND */
          if (min_prec_treshold <= PARSER_RIGHT_TO_LEFT_ORDER_MAX_PRECEDENCE
              && min_prec_treshold != PARSER_RIGHT_TO_LEFT_ORDER_TERNARY_PRECEDENCE)
          {
            /* Right-to-left evaluation order. */
            min_prec_treshold++;
          }
#endif /* JERRY_ESNEXT */
        }

        parser_process_binary_opcodes (context_p, min_prec_treshold);
      }
      if (context_p->token.type == LEXER_RIGHT_PAREN
          && (context_p->stack_top_uint8 == LEXER_LEFT_PAREN
              || context_p->stack_top_uint8 == LEXER_COMMA_SEP_LIST))
      {
        parser_process_group_expression (context_p, &grouping_level);
        continue;
      }

      break;
    }

    if (grouping_level == PARSE_EXPR_LEFT_HAND_SIDE)
    {
      break;
    }

    if (JERRY_UNLIKELY (context_p->token.type == LEXER_QUESTION_MARK))
    {
      parser_process_ternary_expression (context_p);

      if (context_p->token.type == LEXER_RIGHT_PAREN)
      {
        goto process_unary_expression;
      }
    }
    else if (LEXER_IS_BINARY_OP_TOKEN (context_p->token.type))
    {
      parser_append_binary_token (context_p);
      lexer_next_token (context_p);
      continue;
    }

    if (JERRY_UNLIKELY (context_p->token.type == LEXER_COMMA)
        && (!(options & PARSE_EXPR_NO_COMMA) || grouping_level >= PARSER_GROUPING_LEVEL_INCREASE))
    {
      parser_process_expression_sequence (context_p);
      continue;
    }

    break;
  }

  if (grouping_level >= PARSER_GROUPING_LEVEL_INCREASE)
  {
    parser_raise_error (context_p, PARSER_ERR_RIGHT_PAREN_EXPECTED);
  }

  JERRY_ASSERT (context_p->stack_top_uint8 == LEXER_EXPRESSION_START);
  parser_stack_pop_uint8 (context_p);

  if (!(options & PARSE_EXPR_NO_PUSH_RESULT))
  {
    parser_push_result (context_p);
  }
} /* parser_parse_expression */