parser_process_unary_expression (parser_context_t *context_p, /**< context */
                                 size_t grouping_level) /**< grouping level */
{
  /* Parse postfix part of a primary expression. */
  while (true)
  {
    /* Since break would only break the switch, we use
     * continue to continue this loop. Without continue,
     * the code abandons the loop. */
    switch (context_p->token.type)
    {
      case LEXER_DOT:
      {
        parser_push_result (context_p);

        lexer_expect_identifier (context_p, LEXER_STRING_LITERAL);
        JERRY_ASSERT (context_p->token.type == LEXER_LITERAL
                      && context_p->lit_object.literal_p->type == LEXER_STRING_LITERAL);
        context_p->token.lit_location.type = LEXER_STRING_LITERAL;

        if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL)
        {
          JERRY_ASSERT (CBC_ARGS_EQ (CBC_PUSH_PROP_LITERAL_LITERAL,
                                     CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2));
          context_p->last_cbc_opcode = CBC_PUSH_PROP_LITERAL_LITERAL;
          context_p->last_cbc.value = context_p->lit_object.index;
        }
        else if (context_p->last_cbc_opcode == CBC_PUSH_THIS)
        {
          context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE;
          parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_PROP_THIS_LITERAL);
        }
#if JERRY_ESNEXT
        else if (context_p->last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_SUPER))
        {
          context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_SUPER_PROP_LITERAL);
          context_p->last_cbc.literal_index = context_p->lit_object.index;
        }
#endif /* JERRY_ESNEXT */
        else
        {
          parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_PROP_LITERAL);
        }
        lexer_next_token (context_p);
        continue;
      }

      case LEXER_LEFT_SQUARE:
      {
        parser_push_result (context_p);

#if JERRY_ESNEXT
        uint16_t last_cbc_opcode = context_p->last_cbc_opcode;

        if (last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_SUPER))
        {
          context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE;
        }
#endif /* JERRY_ESNEXT */

        lexer_next_token (context_p);
        parser_parse_expression (context_p, PARSE_EXPR);
        if (context_p->token.type != LEXER_RIGHT_SQUARE)
        {
          parser_raise_error (context_p, PARSER_ERR_RIGHT_SQUARE_EXPECTED);
        }
        lexer_next_token (context_p);

#if JERRY_ESNEXT
        if (last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_SUPER))
        {
          parser_emit_cbc_ext (context_p, CBC_EXT_PUSH_SUPER_PROP);
          continue;
        }
#endif /* JERRY_ESNEXT */

        if (PARSER_IS_MUTABLE_PUSH_LITERAL (context_p->last_cbc_opcode))
        {
          context_p->last_cbc_opcode = PARSER_PUSH_LITERAL_TO_PUSH_PROP_LITERAL (context_p->last_cbc_opcode);
        }
        else
        {
          parser_emit_cbc (context_p, CBC_PUSH_PROP);
        }
        continue;
      }

#if JERRY_ESNEXT
      case LEXER_TEMPLATE_LITERAL:
#endif /* JERRY_ESNEXT */
      case LEXER_LEFT_PAREN:
      {
        size_t call_arguments = 0;
        uint16_t opcode = CBC_CALL;
        bool is_eval = false;

        parser_push_result (context_p);

        if (context_p->stack_top_uint8 == LEXER_KEYW_NEW)
        {
#if JERRY_ESNEXT
          if (context_p->token.type == LEXER_LEFT_PAREN)
#endif /* JERRY_ESNEXT */
          {
            parser_stack_pop_uint8 (context_p);
            opcode = CBC_NEW;
          }
        }
        else
        {
          if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL
              && context_p->last_cbc.literal_keyword_type == LEXER_KEYW_EVAL
              && context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL)
          {
            is_eval = true;
          }

          if (PARSER_IS_PUSH_PROP (context_p->last_cbc_opcode))
          {
            opcode = CBC_CALL_PROP;
            context_p->last_cbc_opcode = PARSER_PUSH_PROP_TO_PUSH_PROP_REFERENCE (context_p->last_cbc_opcode);
          }
#if JERRY_ESNEXT
          else if (context_p->last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_SUPER_CONSTRUCTOR))
          {
            opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_SUPER_CALL);
          }
          else if (context_p->last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_SUPER_PROP_LITERAL))
          {
            context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_SUPER_PROP_LITERAL_REFERENCE);
            opcode = CBC_CALL_PROP;
          }
          else if (context_p->last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_SUPER_PROP))
          {
            context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_SUPER_PROP_REFERENCE);
            opcode = CBC_CALL_PROP;
          }
#endif /* JERRY_ESNEXT */
          else if (JERRY_UNLIKELY (context_p->status_flags & PARSER_INSIDE_WITH)
                   && PARSER_IS_PUSH_LITERALS_WITH_THIS (context_p->last_cbc_opcode)
                   && context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL)
          {
            opcode = CBC_CALL_PROP;
            parser_emit_ident_reference (context_p, CBC_PUSH_IDENT_REFERENCE);
            parser_emit_cbc_ext (context_p, CBC_EXT_RESOLVE_BASE);
          }
        }

#if JERRY_ESNEXT
        bool has_spread_element = false;

        if (context_p->token.type == LEXER_TEMPLATE_LITERAL)
        {
          call_arguments = parser_parse_tagged_template_literal (context_p);
        }
        else
        {
          lexer_next_token (context_p);

          while (context_p->token.type != LEXER_RIGHT_PAREN)
          {
            if (++call_arguments > CBC_MAXIMUM_BYTE_VALUE)
            {
              parser_raise_error (context_p, PARSER_ERR_ARGUMENT_LIMIT_REACHED);
            }

            if (context_p->token.type == LEXER_THREE_DOTS)
            {
              has_spread_element = true;
              call_arguments++;
              parser_emit_cbc_ext (context_p, CBC_EXT_PUSH_SPREAD_ELEMENT);
              lexer_next_token (context_p);
            }

            parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA);

            if (context_p->token.type == LEXER_COMMA)
            {
              lexer_next_token (context_p);
              continue;
            }

            if (context_p->token.type != LEXER_RIGHT_PAREN)
            {
              parser_raise_error (context_p, PARSER_ERR_RIGHT_PAREN_EXPECTED);
            }

            break;
          }
        }
#else /* !JERRY_ESNEXT */
        lexer_next_token (context_p);

        if (context_p->token.type != LEXER_RIGHT_PAREN)
        {
          while (true)
          {
            if (++call_arguments > CBC_MAXIMUM_BYTE_VALUE)
            {
              parser_raise_error (context_p, PARSER_ERR_ARGUMENT_LIMIT_REACHED);
            }

            parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA);

            if (context_p->token.type != LEXER_COMMA)
            {
              break;
            }
            lexer_next_token (context_p);
          }

          if (context_p->token.type != LEXER_RIGHT_PAREN)
          {
            parser_raise_error (context_p, PARSER_ERR_RIGHT_PAREN_EXPECTED);
          }
        }
#endif /* JERRY_ESNEXT */

        lexer_next_token (context_p);

        if (is_eval)
        {
          context_p->status_flags |= PARSER_LEXICAL_ENV_NEEDED;

#if JERRY_ESNEXT
          uint16_t eval_flags = PARSER_SAVE_STATUS_FLAGS (context_p->status_flags);
          const uint32_t required_flags = PARSER_IS_FUNCTION | PARSER_LEXICAL_BLOCK_NEEDED;

          if (context_p->status_flags & PARSER_FUNCTION_IS_PARSING_ARGS)
          {
            context_p->status_flags |= PARSER_LEXICAL_BLOCK_NEEDED;
          }
          else if (((context_p->status_flags & (required_flags | PARSER_IS_STRICT)) == required_flags)
                   || ((context_p->global_status_flags & ECMA_PARSE_FUNCTION_CONTEXT)
                       && !(context_p->status_flags & PARSER_IS_FUNCTION)))
          {
            eval_flags |= PARSER_GET_EVAL_FLAG (ECMA_PARSE_FUNCTION_CONTEXT);
          }

          if (eval_flags != 0)
          {
            parser_emit_cbc_ext_call (context_p, CBC_EXT_LOCAL_EVAL, eval_flags);
          }
          else
          {
#endif /* JERRY_ESNEXT */
            parser_emit_cbc (context_p, CBC_EVAL);
#if JERRY_ESNEXT
          }
#endif /* JERRY_ESNEXT */
        }

#if JERRY_ESNEXT
        if (has_spread_element)
        {
          uint16_t spread_opcode;

          if (opcode == CBC_CALL)
          {
            spread_opcode = CBC_EXT_SPREAD_CALL;
          }
          else if (opcode == CBC_CALL_PROP)
          {
            spread_opcode = CBC_EXT_SPREAD_CALL_PROP;
          }
          else if (opcode == CBC_NEW)
          {
            spread_opcode = CBC_EXT_SPREAD_NEW;
          }
          else
          {
            /* opcode is unchanged */
            JERRY_ASSERT (opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_SUPER_CALL));
            spread_opcode = CBC_EXT_SPREAD_SUPER_CALL;
          }

          parser_emit_cbc_ext_call (context_p, spread_opcode, call_arguments);
          continue;
        }
#endif /* JERRY_ESNEXT */

        if (call_arguments <= 1)
        {
          if (opcode == CBC_CALL)
          {
            parser_emit_cbc (context_p, (uint16_t) (CBC_CALL0 + (call_arguments * 6)));
            continue;
          }
          if (opcode == CBC_CALL_PROP)
          {
            parser_emit_cbc (context_p, (uint16_t) (CBC_CALL0_PROP + (call_arguments * 6)));
            continue;
          }
          if (opcode == CBC_NEW)
          {
            parser_emit_cbc (context_p, (uint16_t) (CBC_NEW0 + call_arguments));
            continue;
          }
        }
        else if (call_arguments == 2)
        {
          if (opcode == CBC_CALL)
          {
            parser_emit_cbc (context_p, CBC_CALL2);
            continue;
          }
          if (opcode == CBC_CALL_PROP)
          {
            parser_flush_cbc (context_p);
            /* Manually adjusting stack usage. */
            JERRY_ASSERT (context_p->stack_depth > 0);
            context_p->stack_depth--;
            parser_emit_cbc (context_p, CBC_CALL2_PROP);
            continue;
          }
        }

        parser_emit_cbc_call (context_p, opcode, call_arguments);
        continue;
      }

      default:
      {
        if (context_p->stack_top_uint8 == LEXER_KEYW_NEW)
        {
          parser_push_result (context_p);
          parser_emit_cbc (context_p, CBC_NEW0);
          parser_stack_pop_uint8 (context_p);
          continue;
        }

        if (!(context_p->token.flags & LEXER_WAS_NEWLINE)
            && (context_p->token.type == LEXER_INCREASE || context_p->token.type == LEXER_DECREASE)
            && grouping_level != PARSE_EXPR_LEFT_HAND_SIDE)
        {
          cbc_opcode_t opcode = (context_p->token.type == LEXER_INCREASE) ? CBC_POST_INCR : CBC_POST_DECR;
          parser_push_result (context_p);
          parser_emit_unary_lvalue_opcode (context_p, opcode);
          lexer_next_token (context_p);
        }
        break;
      }
    }
    break;
  }

#if JERRY_ESNEXT
  uint8_t last_unary_token = LEXER_INCREASE;
#endif /* JERRY_ESNEXT */

  /* Generate byte code for the unary operators. */
  while (true)
  {
    uint8_t token = context_p->stack_top_uint8;
    if (!LEXER_IS_UNARY_OP_TOKEN (token))
    {
#if JERRY_ESNEXT
      if (context_p->token.type == LEXER_EXPONENTIATION
          && last_unary_token != LEXER_INCREASE
          && last_unary_token != LEXER_DECREASE)
      {
        parser_raise_error (context_p, PARSER_ERR_INVALID_EXPONENTIATION);
      }
#endif /* JERRY_ESNEXT */
      break;
    }

#if JERRY_ESNEXT
    last_unary_token = token;
#endif /* JERRY_ESNEXT */

    parser_push_result (context_p);
    parser_stack_pop_uint8 (context_p);

    if (LEXER_IS_UNARY_LVALUE_OP_TOKEN (token))
    {
      if (token == LEXER_KEYW_DELETE)
      {
        token = CBC_DELETE_PUSH_RESULT;
      }
      else
      {
        token = (uint8_t) (LEXER_UNARY_LVALUE_OP_TOKEN_TO_OPCODE (token));
      }
      parser_emit_unary_lvalue_opcode (context_p, (cbc_opcode_t) token);
    }
#if JERRY_ESNEXT
    else if (JERRY_UNLIKELY (token == LEXER_KEYW_AWAIT))
    {
      cbc_ext_opcode_t opcode = ((context_p->status_flags & PARSER_IS_GENERATOR_FUNCTION) ? CBC_EXT_GENERATOR_AWAIT
                                                                                          : CBC_EXT_AWAIT);
      parser_emit_cbc_ext (context_p, opcode);
    }
#endif /* JERRY_ESNEXT */
    else
    {
      token = (uint8_t) (LEXER_UNARY_OP_TOKEN_TO_OPCODE (token));

      if (token == CBC_TYPEOF)
      {
        if (PARSER_IS_PUSH_LITERALS_WITH_THIS (context_p->last_cbc_opcode)
            && context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL)
        {
          parser_emit_ident_reference (context_p, CBC_TYPEOF_IDENT);
        }
        else
        {
          parser_emit_cbc (context_p, token);
        }
      }
      else
      {
        if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL)
        {
          /* It is not worth to combine with push multiple literals
           * since the byte code size will not decrease. */
          JERRY_ASSERT (CBC_SAME_ARGS (context_p->last_cbc_opcode, token + 1));
          context_p->last_cbc_opcode = (uint16_t) (token + 1);
        }
        else
        {
          parser_emit_cbc (context_p, token);
        }
      }
    }
  }
} /* parser_process_unary_expression */