parser_parse_source (void *source_p, /**< source code */
                     uint32_t parse_opts, /**< ecma_parse_opts_t option bits */
                     const jerry_parse_options_t *options_p) /**< additional configuration options */
{
  parser_context_t context;
  ecma_compiled_code_t *compiled_code_p;

  context.error = PARSER_ERR_NO_ERROR;
  context.status_flags = parse_opts & PARSER_STRICT_MODE_MASK;
  context.global_status_flags = parse_opts;

#if JERRY_ESNEXT
  context.status_flags |= PARSER_RESTORE_STATUS_FLAGS (parse_opts);
  context.tagged_template_literal_cp = JMEM_CP_NULL;
#endif /* JERRY_ESNEXT */

  context.stack_depth = 0;
  context.stack_limit = 0;
  context.options_p = options_p;
  context.arguments_start_p = NULL;
  context.arguments_size = 0;
#if JERRY_MODULE_SYSTEM
  if (context.global_status_flags & ECMA_PARSE_MODULE)
  {
    context.status_flags |= PARSER_IS_STRICT;
  }

  context.module_names_p = NULL;
#endif /* JERRY_MODULE_SYSTEM */

  ecma_value_t argument_list = ECMA_VALUE_EMPTY;

  if (context.options_p != NULL
      && (context.options_p->options & JERRY_PARSE_HAS_ARGUMENT_LIST))
  {
    argument_list = context.options_p->argument_list;
  }
  else if (context.global_status_flags & ECMA_PARSE_HAS_ARGUMENT_LIST_VALUE)
  {
    JERRY_ASSERT (context.global_status_flags & ECMA_PARSE_HAS_SOURCE_VALUE);
    argument_list = ((ecma_value_t *) source_p)[1];
  }

  if (argument_list != ECMA_VALUE_EMPTY)
  {
    JERRY_ASSERT (ecma_is_value_string (argument_list));

    context.status_flags |= PARSER_IS_FUNCTION;
#if JERRY_ESNEXT
    if (parse_opts & ECMA_PARSE_GENERATOR_FUNCTION)
    {
      context.status_flags |= PARSER_IS_GENERATOR_FUNCTION;
    }
    if (parse_opts & ECMA_PARSE_ASYNC_FUNCTION)
    {
      context.status_flags |= PARSER_IS_ASYNC_FUNCTION;
    }
#endif /* JERRY_ESNEXT */

    ecma_string_t *string_p = ecma_get_string_from_value (argument_list);
    uint8_t flags = ECMA_STRING_FLAG_EMPTY;

    context.arguments_start_p = ecma_string_get_chars (string_p, &context.arguments_size, NULL, NULL, &flags);

    if (flags & ECMA_STRING_FLAG_MUST_BE_FREED)
    {
      context.global_status_flags |= ECMA_PARSE_INTERNAL_FREE_ARG_LIST;
    }
  }

  if (!(context.global_status_flags & ECMA_PARSE_HAS_SOURCE_VALUE))
  {
    context.source_start_p = ((parser_source_char_t *) source_p)->source_p;
    context.source_size = (lit_utf8_size_t) ((parser_source_char_t *) source_p)->source_size;
  }
  else
  {
    ecma_value_t source = ((ecma_value_t *) source_p)[0];

    JERRY_ASSERT (ecma_is_value_string (source));

    ecma_string_t *string_p = ecma_get_string_from_value (source);
    uint8_t flags = ECMA_STRING_FLAG_EMPTY;

    context.source_start_p = ecma_string_get_chars (string_p, &context.source_size, NULL, NULL, &flags);

    if (flags & ECMA_STRING_FLAG_MUST_BE_FREED)
    {
      context.global_status_flags |= ECMA_PARSE_INTERNAL_FREE_SOURCE;
    }
  }

#if JERRY_DEBUGGER
  if (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED)
  {
    jerry_debugger_send_string (JERRY_DEBUGGER_SOURCE_CODE,
                                JERRY_DEBUGGER_NO_SUBTYPE,
                                context.source_start_p,
                                context.source_size);
  }
#endif /* JERRY_DEBUGGER */

  context.user_value = ECMA_VALUE_EMPTY;

  if ((context.global_status_flags & ECMA_PARSE_EVAL)
      && JERRY_CONTEXT (vm_top_context_p) != NULL)
  {
    const ecma_compiled_code_t *bytecode_header_p = JERRY_CONTEXT (vm_top_context_p)->shared_p->bytecode_header_p;

#if JERRY_SNAPSHOT_EXEC
    if (JERRY_LIKELY (!(bytecode_header_p->status_flags & CBC_CODE_FLAGS_STATIC_FUNCTION)))
    {
#endif /* JERRY_SNAPSHOT_EXEC */
      ecma_value_t parent_script_value = ((cbc_uint8_arguments_t *) bytecode_header_p)->script_value;;
      cbc_script_t *parent_script_p = ECMA_GET_INTERNAL_VALUE_POINTER (cbc_script_t, parent_script_value);

      if (CBC_SCRIPT_GET_TYPE (parent_script_p) != CBC_SCRIPT_GENERIC)
      {
        context.user_value = ((cbc_script_user_t *) parent_script_p)->user_value;
      }
#if JERRY_SNAPSHOT_EXEC
    }
#endif /* JERRY_SNAPSHOT_EXEC */
  }
  else if (context.options_p != NULL
           && (context.options_p->options & JERRY_PARSE_HAS_USER_VALUE))
  {
    context.user_value = context.options_p->user_value;
  }

  uint32_t script_size = (context.user_value != ECMA_VALUE_EMPTY ? sizeof (cbc_script_user_t)
                                                                 : sizeof (cbc_script_t));
  context.script_p = jmem_heap_alloc_block_null_on_error (script_size);

  if (JERRY_UNLIKELY (context.script_p == NULL))
  {
    /* It is unlikely that memory can be allocated in an out-of-memory
     * situation. However, a simple value can still be thrown. */
    jcontext_raise_exception (ECMA_VALUE_NULL);
    return NULL;
  }

  CBC_SCRIPT_SET_TYPE (context.script_p, context.user_value, CBC_SCRIPT_REF_ONE);

#if JERRY_BUILTIN_REALMS
  context.script_p->realm_p = (ecma_object_t *) JERRY_CONTEXT (global_object_p);
#endif /* JERRY_BUILTIN_REALMS */

#if JERRY_RESOURCE_NAME
  ecma_value_t resource_name = ecma_make_magic_string_value (LIT_MAGIC_STRING_RESOURCE_ANON);

  if (context.options_p != NULL
      && (context.options_p->options & JERRY_PARSE_HAS_RESOURCE))
  {
    JERRY_ASSERT (ecma_is_value_string (context.options_p->resource_name));

    ecma_ref_ecma_string (ecma_get_string_from_value (context.options_p->resource_name));
    resource_name = context.options_p->resource_name;
  }
  else if (context.global_status_flags & ECMA_PARSE_EVAL)
  {
    resource_name = ecma_make_magic_string_value (LIT_MAGIC_STRING_RESOURCE_EVAL);
  }

  context.script_p->resource_name = resource_name;
#endif /* JERRY_RESOURCE_NAME */

  ECMA_SET_INTERNAL_VALUE_POINTER (context.script_value, context.script_p);

  context.last_context_p = NULL;
  context.last_statement.current_p = NULL;
  context.token.flags = 0;
  lexer_init_line_info (&context);

  scanner_info_t scanner_info_end;
  scanner_info_end.next_p = NULL;
  scanner_info_end.source_p = NULL;
  scanner_info_end.type = SCANNER_TYPE_END;
  context.next_scanner_info_p = &scanner_info_end;
  context.active_scanner_info_p = NULL;
  context.skipped_scanner_info_p = NULL;
  context.skipped_scanner_info_end_p = NULL;

  context.last_cbc_opcode = PARSER_CBC_UNAVAILABLE;

  context.argument_count = 0;
#if JERRY_ESNEXT
  context.argument_length = UINT16_MAX;
#endif /* JERRY_ESNEXT */
  context.register_count = 0;
  context.literal_count = 0;

  parser_cbc_stream_init (&context.byte_code);
  context.byte_code_size = 0;
  parser_list_init (&context.literal_pool,
                    sizeof (lexer_literal_t),
                    (uint32_t) ((128 - sizeof (void *)) / sizeof (lexer_literal_t)));
  context.scope_stack_p = NULL;
  context.scope_stack_size = 0;
  context.scope_stack_top = 0;
  context.scope_stack_reg_top = 0;
#if JERRY_ESNEXT
  context.scope_stack_global_end = 0;
  context.tagged_template_literal_cp = JMEM_CP_NULL;
#endif /* JERRY_ESNEXT */

#ifndef JERRY_NDEBUG
  context.context_stack_depth = 0;
#endif /* !JERRY_NDEBUG */

#if JERRY_LINE_INFO
  context.line_info_p = NULL;
#endif /* JERRY_LINE_INFO */

#if JERRY_PARSER_DUMP_BYTE_CODE
  context.is_show_opcodes = (JERRY_CONTEXT (jerry_init_flags) & JERRY_INIT_SHOW_OPCODES);
  context.total_byte_code_size = 0;

  if (context.is_show_opcodes)
  {
    JERRY_DEBUG_MSG ("\n--- %s parsing start ---\n\n",
                     (context.arguments_start_p == NULL) ? "Script"
                                                         : "Function");
  }
#endif /* JERRY_PARSER_DUMP_BYTE_CODE */

  scanner_scan_all (&context);

  if (JERRY_UNLIKELY (context.error != PARSER_ERR_NO_ERROR))
  {
    JERRY_ASSERT (context.error == PARSER_ERR_OUT_OF_MEMORY);

    /* It is unlikely that memory can be allocated in an out-of-memory
     * situation. However, a simple value can still be thrown. */
    jcontext_raise_exception (ECMA_VALUE_NULL);
    return NULL;
  }

  if (context.arguments_start_p == NULL)
  {
    context.source_p = context.source_start_p;
    context.source_end_p = context.source_start_p + context.source_size;
  }
  else
  {
    context.source_p = context.arguments_start_p;
    context.source_end_p = context.arguments_start_p + context.arguments_size;
  }

  context.u.allocated_buffer_p = NULL;
  context.token.flags = 0;
  lexer_init_line_info (&context);

  parser_stack_init (&context);

#if JERRY_DEBUGGER
  context.breakpoint_info_count = 0;
#endif /* JERRY_DEBUGGER */

  JERRY_ASSERT (context.next_scanner_info_p->source_p == context.source_p);
  JERRY_ASSERT (context.next_scanner_info_p->type == SCANNER_TYPE_FUNCTION);

  if (context.next_scanner_info_p->u8_arg & SCANNER_FUNCTION_IS_STRICT)
  {
    context.status_flags |= PARSER_IS_STRICT;
  }

  PARSER_TRY (context.try_buffer)
  {
    /* Pushing a dummy value ensures the stack is never empty.
     * This simplifies the stack management routines. */
    parser_stack_push_uint8 (&context, CBC_MAXIMUM_BYTE_VALUE);
    /* The next token must always be present to make decisions
     * in the parser. Therefore when a token is consumed, the
     * lexer_next_token() must be immediately called. */
    lexer_next_token (&context);

    if (context.arguments_start_p != NULL)
    {
      parser_parse_function_arguments (&context, LEXER_EOS);

      JERRY_ASSERT (context.next_scanner_info_p->type == SCANNER_TYPE_END_ARGUMENTS);
      scanner_release_next (&context, sizeof (scanner_info_t));

      context.source_p = context.source_start_p;
      context.source_end_p = context.source_start_p + context.source_size;
      lexer_init_line_info (&context);

      lexer_next_token (&context);
    }
#if JERRY_MODULE_SYSTEM
    else if (parse_opts & ECMA_PARSE_MODULE)
    {
      parser_branch_t branch;
      parser_emit_cbc_forward_branch (&context, CBC_JUMP_FORWARD, &branch);

      scanner_create_variables (&context, SCANNER_CREATE_VARS_NO_OPTS);
      parser_emit_cbc (&context, CBC_RETURN_WITH_BLOCK);

      parser_set_branch_to_current_position (&context, &branch);
    }
#endif /* JERRY_MODULE_SYSTEM */
    else
    {
      JERRY_ASSERT (context.next_scanner_info_p->source_p == context.source_start_p
                    && context.next_scanner_info_p->type == SCANNER_TYPE_FUNCTION);

#if JERRY_ESNEXT
      if (scanner_is_context_needed (&context, PARSER_CHECK_GLOBAL_CONTEXT))
      {
        context.status_flags |= PARSER_LEXICAL_BLOCK_NEEDED;
      }

      if (!(parse_opts & ECMA_PARSE_EVAL))
      {
        scanner_check_variables (&context);
      }
#endif /* JERRY_ESNEXT */

      scanner_create_variables (&context, SCANNER_CREATE_VARS_IS_SCRIPT);
    }

    parser_parse_statements (&context);

    JERRY_ASSERT (context.last_statement.current_p == NULL);

    JERRY_ASSERT (context.last_cbc_opcode == PARSER_CBC_UNAVAILABLE);
    JERRY_ASSERT (context.u.allocated_buffer_p == NULL);

#ifndef JERRY_NDEBUG
    JERRY_ASSERT (context.status_flags & PARSER_SCANNING_SUCCESSFUL);
    JERRY_ASSERT (!(context.global_status_flags & ECMA_PARSE_INTERNAL_FOR_IN_OFF_CONTEXT_ERROR));
    context.status_flags &= (uint32_t) ~PARSER_SCANNING_SUCCESSFUL;
#endif /* !JERRY_NDEBUG */

    JERRY_ASSERT (!(context.status_flags & PARSER_HAS_LATE_LIT_INIT));

    compiled_code_p = parser_post_processing (&context);
    parser_list_free (&context.literal_pool);

    /* When parsing is successful, only the dummy value can be remained on the stack. */
    JERRY_ASSERT (context.stack_top_uint8 == CBC_MAXIMUM_BYTE_VALUE
                  && context.stack.last_position == 1
                  && context.stack.first_p != NULL
                  && context.stack.first_p->next_p == NULL
                  && context.stack.last_p == NULL);

    JERRY_ASSERT (context.arguments_start_p != NULL || !(context.status_flags & PARSER_ARGUMENTS_NEEDED));

    context.script_p->refs_and_type -= CBC_SCRIPT_REF_ONE;

    if (context.user_value != ECMA_VALUE_EMPTY)
    {
      ((cbc_script_user_t *) context.script_p)->user_value = ecma_copy_value_if_not_object (context.user_value);
    }

#if JERRY_PARSER_DUMP_BYTE_CODE
    if (context.is_show_opcodes)
    {
      JERRY_DEBUG_MSG ("\n%s parsing successfully completed. Total byte code size: %d bytes\n",
                       (context.arguments_start_p == NULL) ? "Script"
                                                           : "Function",
                       (int) context.total_byte_code_size);
    }
#endif /* JERRY_PARSER_DUMP_BYTE_CODE */
  }
  PARSER_CATCH
  {
    if (context.last_statement.current_p != NULL)
    {
      parser_free_jumps (context.last_statement);
    }

    parser_free_allocated_buffer (&context);

    scanner_cleanup (&context);

#if JERRY_MODULE_SYSTEM
    if (context.module_names_p != NULL)
    {
      ecma_module_release_module_names (context.module_names_p);
    }
#endif

    compiled_code_p = NULL;
    parser_free_literals (&context.literal_pool);
    parser_cbc_stream_free (&context.byte_code);

#if JERRY_RESOURCE_NAME
    ecma_deref_ecma_string (ecma_get_string_from_value (context.script_p->resource_name));
#endif /* JERRY_RESOURCE_NAME */

    JERRY_ASSERT (context.script_p->refs_and_type >= CBC_SCRIPT_REF_ONE);
    jmem_heap_free_block (context.script_p, script_size);
  }
  PARSER_TRY_END

  if (context.scope_stack_p != NULL)
  {
    parser_free (context.scope_stack_p, context.scope_stack_size * sizeof (parser_scope_stack_t));
  }

#if JERRY_LINE_INFO
  parser_line_info_free (context.line_info_p);
#endif /* JERRY_LINE_INFO */

#if JERRY_PARSER_DUMP_BYTE_CODE
  if (context.is_show_opcodes)
  {
    JERRY_DEBUG_MSG ("\n--- %s parsing end ---\n\n",
                     (context.arguments_start_p == NULL) ? "Script"
                                                         : "Function");
  }
#endif /* JERRY_PARSER_DUMP_BYTE_CODE */

  parser_stack_free (&context);

  if (context.global_status_flags & ECMA_PARSE_INTERNAL_FREE_SOURCE)
  {
    jmem_heap_free_block ((void *) context.source_start_p, context.source_size);
  }

  if (context.global_status_flags & ECMA_PARSE_INTERNAL_FREE_ARG_LIST)
  {
    jmem_heap_free_block ((void *) context.arguments_start_p, context.arguments_size);
  }

  if (compiled_code_p != NULL)
  {
    return compiled_code_p;
  }

#if JERRY_DEBUGGER
  if (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED)
  {
    jerry_debugger_send_type (JERRY_DEBUGGER_PARSE_ERROR);
  }
#endif /* JERRY_DEBUGGER */

  if (context.error == PARSER_ERR_OUT_OF_MEMORY)
  {
    /* It is unlikely that memory can be allocated in an out-of-memory
     * situation. However, a simple value can still be thrown. */
    jcontext_raise_exception (ECMA_VALUE_NULL);
    return NULL;
  }

#if JERRY_ERROR_MESSAGES
  ecma_string_t *err_str_p;

  if (context.error == PARSER_ERR_INVALID_REGEXP)
  {
    ecma_value_t error = jcontext_take_exception ();
    ecma_property_t *prop_p = ecma_find_named_property (ecma_get_object_from_value (error),
                                                        ecma_get_magic_string (LIT_MAGIC_STRING_MESSAGE));
    ecma_free_value (error);
    JERRY_ASSERT (prop_p);
    err_str_p = ecma_get_string_from_value (ECMA_PROPERTY_VALUE_PTR (prop_p)->value);
    ecma_ref_ecma_string (err_str_p);
  }
  else
  {
    const lit_utf8_byte_t *err_bytes_p = (const lit_utf8_byte_t *) parser_error_to_string (context.error);
    lit_utf8_size_t err_bytes_size = lit_zt_utf8_string_size (err_bytes_p);
    err_str_p = ecma_new_ecma_string_from_utf8 (err_bytes_p, err_bytes_size);
  }
  ecma_value_t err_str_val = ecma_make_string_value (err_str_p);
  ecma_value_t line_str_val = ecma_make_uint32_value (context.token.line);
  ecma_value_t col_str_val = ecma_make_uint32_value (context.token.column);

  ecma_raise_standard_error_with_format (JERRY_ERROR_SYNTAX,
                                         "% [%:%:%]",
                                         err_str_val,
                                         resource_name,
                                         line_str_val,
                                         col_str_val);

  ecma_free_value (col_str_val);
  ecma_free_value (line_str_val);
  ecma_deref_ecma_string (err_str_p);
#else /* !JERRY_ERROR_MESSAGES */
  if (context.error == PARSER_ERR_INVALID_REGEXP)
  {
    jcontext_release_exception ();
  }

  ecma_raise_syntax_error ("");
#endif /* JERRY_ERROR_MESSAGES */

  return NULL;
} /* parser_parse_source */