/* -*- mode: c++; indent-tabs-mode: nil -*- */
/*
    QC_Program.qpp

    Qore Programming Language

    Copyright (C) 2003 - 2023 Qore Technologies, s.r.o.

    Permission is hereby granted, free of charge, to any person obtaining a
    copy of this software and associated documentation files (the "Software"),
    to deal in the Software without restriction, including without limitation
    the rights to use, copy, modify, merge, publish, distribute, sublicense,
    and/or sell copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in
    all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    DEALINGS IN THE SOFTWARE.

    Note that the Qore library is released under a choice of three open-source
    licenses: MIT (as above), LGPL 2+, or GPL 2+; see README-LICENSE for more
    information.
*/

#include <qore/Qore.h>
#include "qore/intern/QC_Program.h"
#include "qore/intern/QC_ProgramControl.h"
#include "qore/intern/QC_TimeZone.h"
#include "qore/intern/QC_Breakpoint.h"
#include "qore/intern/QC_Expression.h"
#include "qore/intern/qore_program_private.h"
#include "qore/intern/QoreObjectIntern.h"
#include "qore/intern/ModuleInfo.h"
#include "qore/ParseOptionMap.h"

#include "qore/intern/ModuleInfo.h"
extern QoreModuleManager QMM;

#ifndef WARN_MODULES
// needed so that the Qore default argument value in sinatures below will match a C++ value
#define WARN_MODULES QP_WARN_MODULES
#endif

//! source location information
/**
    @since %Qore 0.9
*/
hashdecl SourceLocationInfo {
    //! if @ref True then the code is builtin and there is no user source location
    bool builtin;

    //! the file name or parse label for the code
    *string file;

    //! the starting source line number
    int line;

    //! the ending source line number
    int endline;

    //! the source object or file name tag (used when multiple source objects are defined from the same source)
    *string source;

    //! the offset line in the source (for when multiple source objects are defined from the same source)
    int offset;
}

/** @defgroup import_visibility_constants Import Visibility Constants
    Constants providing values for import visibility

    @since %Qore 0.9.3
*/
///@{
//! Imports the object with the same module visibility
const CSP_UNCHANGED = CSP_UNCHANGED;

//! Imports the object with public visibility
const CSP_SETPUB = CSP_SETPUB;

//! Imports the object with private visibility
const CSP_SETPRIV = CSP_SETPRIV;
///@}

/** @defgroup functional_domain_constants Functional Domain Constants
    Constants providing values for functional domains; each constant is a unique element of a bitfield

    @since %Qore 0.9
*/
///@{
//! provides process control functionality (can affect or stop the current process)
/** @see
    - @ref Qore::PO_NO_PROCESS_CONTROL "PO_NO_PROCESS_CONTROL"
    - @ref no-process-control
*/
const DOM_PROCESS_CONTROL = QDOM_PROCESS;

//! provides network functionality
/** @see
    - @ref Qore::PO_NO_NETWORK "PO_NO_NETWORK"
    - @ref no-network
*/
const DOM_NETWORK = QDOM_NETWORK;

//! provides external process control functionality (can affect, start, or stop external processes)
/** @see
    - @ref Qore::PO_NO_EXTERNAL_PROCESS "PO_NO_EXTERNAL_PROCESS"
    - @ref no-external-process
*/
const DOM_EXTERNAL_PROCESS = QDOM_EXTERNAL_PROCESS;

//! provides access to the filesystem
/** @see
    - @ref Qore::PO_NO_FILESYSTEM "PO_NO_FILESYSTEM"
    - @ref no-filesystem
*/
const DOM_FILESYSTEM = QDOM_FILESYSTEM;

//! provides thread control functionality
/** @see
    - @ref Qore::PO_NO_THREAD_CLASSES "PO_NO_THREAD_CLASSES"
    - @ref no-thread-classes
*/
const DOM_THREAD_CLASS = QDOM_THREAD_CLASS;

//! provides the ability to check or manipulate threads (including starting new threads)
/** @see
    - @ref Qore::PO_NO_THREAD_CONTROL "PO_NO_THREAD_CONTROL"
    - @ref no-thread-control
*/
const DOM_THREAD_CONTROL = QDOM_THREAD_CONTROL;

//! provides access to databases
/** @see
    - @ref Qore::PO_NO_DATABASE "PO_NO_DATABASE"
    - @ref no-database
*/
const DOM_DATABASE = QDOM_DATABASE;

//! provides GUI functionality
/** @see
    - @ref Qore::PO_NO_GUI "PO_NO_GUI"
    - @ref no-gui
*/
const DOM_GUI = QDOM_GUI;

//! provides terminal I/O functionality
/** @see
    - @ref Qore::PO_NO_TERMINAL_IO "PO_NO_TERMINAL_IO"
    - @ref no-terminal-io
*/
const DOM_TERMINAL_IO = QDOM_TERMINAL_IO;

//! provides access to external information (ex: hostname, pid, process uid, etc)
/** @see
    - @ref Qore::PO_NO_EXTERNAL_INFO "PO_NO_EXTERNAL_INFO"
    - @ref no-external-info
*/
const DOM_EXTERNAL_INFO = QDOM_EXTERNAL_INFO;

//! provides access to information regarding threading (tid, active threads, etc)
/** @see
    - @ref Qore::PO_NO_THREAD_INFO "PO_NO_THREAD_INFO"
    - @ref no-thread-info
*/
const DOM_THREAD_INFO = QDOM_THREAD_INFO;

//! provides access to functionality that changes locale information
/** @see
    - @ref Qore::PO_NO_LOCALE_CONTROL "PO_NO_LOCALE_CONTROL"
    - @ref no-locale-control
*/
const DOM_LOCALE_CONTROL = QDOM_LOCALE_CONTROL;

//! provides access to external modules
/** @see
    - @ref Qore::PO_NO_MODULES "PO_NO_MODULES"
    - @ref no-modules
*/
const DOM_MODULE = QDOM_MODULES;

//! provides dynamic parsing functionality
/** @see @ref Qore::PO_NO_EMBEDDED_LOGIC "PO_NO_EMBEDDED_LOGIC"
*/
const DOM_EMBEDDED_LOGIC = QDOM_EMBEDDED_LOGIC;

//! provides functionality related to code / dependency injection
/** @see @ref Qore::PO_ALLOW_INJECTION "PO_ALLOW_INJECTION"
*/
const DOM_INJECTION = QDOM_INJECTION;

//! provides unchecked access to system functionality that could bypass Qore's sandboxing controls
/** @see
    - @ref Qore::PO_NO_UNCONTROLLED_APIS "PO_NO_UNCONTROLLED_APIS"
    - @ref no-uncontrolled-apis
*/
const DOM_UNCONTROLLED_API = QDOM_UNCONTROLLED_API;

//! provides debugging functionality
/** @see @ref Qore::PO_ALLOW_DEBUGGER "PO_ALLOW_DEBUGGER"
*/
const DOM_DEBUGGER = QDOM_DEBUGGER;

//! provides reflection functionality
/** @see @ref Qore::PO_NO_REFLECTION "PO_NO_REFLECTION"
*/
const DOM_REFLECTION = QDOM_REFLECTION;
///@}

/** @defgroup parse_options Parse Option Constants
    These are the possible values that can be or'ed together to set @ref Qore::Program "Program" parse options
*/
///@{
//! Maps from parse option code values (as string keys) to parse option names
/** @see ParseOptionStringMap for the reverse mapping

    @since %Qore 0.8.12
 */
const ParseOptionCodeMap = qore(pomaps.getCodeToStringMap());

//! Maps from parse option strings to parse option bitfield integer codes
/** @see ParseOptionCodeMap for the reverse mapping

    @since %Qore 0.8.12
 */
const ParseOptionStringMap = qore(pomaps.getStringToCodeMap());

//! Maps from functional domain bitfield code values (as string keys) to functional domain names
/** @see DomainStringMap for the reverse mapping

    @since %Qore 0.9
 */
const DomainCodeMap = qore(pomaps.getDomainToStringMap());

//! Maps from functional domain strings to functional domain bitfield integer codes
/** @see DomainCodeMap for the reverse mapping

    @since %Qore 0.9
 */
const DomainStringMap = qore(pomaps.getStringToDomainMap());

//! Maps from parse option code values (as string keys) to parse option names. Names are in form used in script or on command line, e.g. new-style
/** @see ParseOptionCmdStringMap for the reverse mapping

    @since %Qore 0.8.13
 */
const ParseOptionCmdCodeMap = qore(ParseOptionMap::getCodeToStringMap());

//! Maps from parse option strings to parse option integer codes. Names are in form used in script or on command line, e.g. new-style.
/** @see ParseOptionCmdCodeMap for the reverse mapping

    @since %Qore 0.8.13
 */
const ParseOptionCmdStringMap = qore(ParseOptionMap::getStringToCodeMap());

//! Prohibits the use of the '$' character in variable names, method calls, and object member references
/** This option should probably be named \c "PO_REQUIRE_BARE_REFS" as it is an error to use the \c "$" character when this option is set

    @see @ref allow-bare-refs "%allow-bare-refs"
*/
const PO_ALLOW_BARE_REFS = PO_ALLOW_BARE_REFS;

//! Assume local variable scope when variables are first referenced if no \c \b my or \c \b our is present
/** This option is set implicitly with @ref PO_NEW_STYLE

    @see @ref assume-local "%assume-local"
*/
const PO_ASSUME_LOCAL = PO_ASSUME_LOCAL;

//! This option is the empty option, meaning no options are set
const PO_DEFAULT = PO_DEFAULT;

//! mask of options that have no effect on code access or code safety but just affect programming style
/** made up of @ref PO_ALLOW_BARE_REFS | @ref PO_ASSUME_LOCAL
 */
const PO_FREE_OPTIONS = PO_FREE_OPTIONS;

//! Only set by the system when in a @ref user_modules "user module" Program
/** @note This option cannot be set manually or an exception will be raised
 */
const PO_IN_MODULE = PO_IN_MODULE;

//! Precludes global variables from being inherited into the new Program object
const PO_NO_INHERIT_GLOBAL_VARS = PO_NO_INHERIT_GLOBAL_VARS;

//! Precludes public user function variants from being inherited into the new Program object
const PO_NO_INHERIT_USER_FUNC_VARIANTS = PO_NO_INHERIT_USER_FUNC_VARIANTS;

//! Disallows changes to the warning mask
/**
    @see @ref lock-warnings "%lock-warnings"
*/
const PO_LOCK_WARNINGS = PO_LOCK_WARNINGS;

//! Sets very restrictive access; this restriction is designed to allow code to only execute logic, no I/O, no threading, no external access
/** made up of @ref PO_NO_EXTERNAL_ACCESS | @ref PO_NO_THREADS | @ref PO_NO_IO

    @see @ref lockdown "%lockdown"
 */
const PO_LOCKDOWN = PO_LOCKDOWN;

//! Set a more C++ or Java type programming style; prohibits usage of the \c "$" character and also assumes local variable scope without \c \b my
/** made up of @ref PO_ALLOW_BARE_REFS | @ref PO_ASSUME_LOCAL

    @see @ref new-style "%new-style"
 */
const PO_NEW_STYLE = PO_NEW_STYLE;

//! Disallows loading @ref qore_modules "modules" with the @ref requires "%requires" directive or at runtime with load_module()
/**
    @see @ref no-modules "%no-modules"
*/
const PO_NO_MODULES = PO_NO_MODULES;

//! Allows child program objects to have fewer parse restrictions (i.e. more capabilies) than the parent object
/**
    @see @ref no-child-restrictions "%no-child-restrictions"
*/
const PO_NO_CHILD_PO_RESTRICTIONS = PO_NO_CHILD_PO_RESTRICTIONS;

//! Disallows class definitions
/**
    @see @ref no-class-defs "%no-class-defs"
*/
const PO_NO_CLASS_DEFS = PO_NO_CLASS_DEFS;

//! Disallows constant definitions
/**
    @see @ref no-constant-defs "%no-constant-defs"
*/
const PO_NO_CONSTANT_DEFS = PO_NO_CONSTANT_DEFS;

//! Disallows access to database functionality
/**
    @see @ref no-database "%no-database"
*/
const PO_NO_DATABASE = PO_NO_DATABASE;

//! Prohibits embedded logic from being used
const PO_NO_EMBEDDED_LOGIC = PO_NO_EMBEDDED_LOGIC;

//! Prohibits any external access
/** made up of @ref PO_NO_PROCESS_CONTROL | @ref PO_NO_NETWORK | @ref PO_NO_FILESYSTEM | @ref PO_NO_DATABASE | @ref PO_NO_EXTERNAL_INFO | @ref PO_NO_EXTERNAL_PROCESS | @ref PO_NO_UNCONTROLLED_APIS

    @see @ref no-external-access "%no-external-access"
 */
const PO_NO_EXTERNAL_ACCESS = PO_NO_EXTERNAL_ACCESS;

//! Disallows access to functionality that provides information about the computing environment
/**
    @see @ref no-external-info "%no-external-info"
*/
const PO_NO_EXTERNAL_INFO = PO_NO_EXTERNAL_INFO;

//! Disallows any access to external processes (with system(), backquote(), exec(), etc)
/**
    @see @ref no-external-process "%no-external-process"
*/
const PO_NO_EXTERNAL_PROCESS = PO_NO_EXTERNAL_PROCESS;

//! Disallows access to the filesystem
/**
    @see @ref no-filesystem "%no-filesystem"
*/
const PO_NO_FILESYSTEM = PO_NO_FILESYSTEM;

//! Disallows the use of global variables
/**
    @see @ref no-global-vars "%no-global-vars"
*/
const PO_NO_GLOBAL_VARS = PO_NO_GLOBAL_VARS;

//! Disallows access to functionality that draws graphics to the display
/** @see PO_NO_TERMINAL_IO
    @see @ref no-gui "%no-gui"
 */
const PO_NO_GUI = PO_NO_GUI;

//! Prohibits all terminal and file I/O and GUI operations
/** made up of @ref PO_NO_GUI | @ref PO_NO_TERMINAL_IO | @ref PO_NO_FILESYSTEM | @ref PO_NO_NETWORK | @ref PO_NO_DATABASE | @ref PO_NO_UNCONTROLLED_APIS

    @see @ref no-io "%no-io"
 */
const PO_NO_IO = PO_NO_IO;

//! Disallows access to functionality that can change locale parameters
/**
    @see @ref no-locale-control "%no-locale-control"
*/
const PO_NO_LOCALE_CONTROL = PO_NO_LOCALE_CONTROL;

//! Disallows new namespace definitions
/**
    @see @ref no-namespace-defs "%no-namespace-defs"
*/
const PO_NO_NAMESPACE_DEFS = PO_NO_NAMESPACE_DEFS;

//! Disallows access to network functionality
/**
    @see @ref no-network "%no-network"
*/
const PO_NO_NETWORK = PO_NO_NETWORK;

//! Disallows use of the @ref new "new operator"
/** Note that objects can still be instantiated with syntax like:
    @code{.py}
Mutex m();
    @endcode

    @see @ref no-new "%no-new"
 */
const PO_NO_NEW = PO_NO_NEW;

//! Disallows access to functions that would affect the current process (exit(), exec(), fork(), etc)
/**
    @see @ref no-process-control "%no-process-control"
*/
const PO_NO_PROCESS_CONTROL = PO_NO_PROCESS_CONTROL;

//! Disallows subroutine (function) definitions
/**
    @see @ref no-subroutine-defs "%no-subroutine-defs"
*/
const PO_NO_SUBROUTINE_DEFS = PO_NO_SUBROUTINE_DEFS;

//! Prohibits system classes from being imported into the new Program object
/** @note @ref Qore::PO_NO_SYSTEM_CLASSES "PO_NO_SYSTEM_CLASSES" is a synonym of this constant

    @since %Qore 0.8.12 to harmonize permission names
 */
const PO_NO_INHERIT_SYSTEM_CLASSES = PO_NO_INHERIT_SYSTEM_CLASSES;

//! Prohibits system classes from being imported into the new Program object
/**
    @note @ref Qore::PO_NO_INHERIT_SYSTEM_CLASSES "PO_NO_INHERIT_SYSTEM_CLASSES" was added in %Qore 0.8.12 with the same value of this constant to provide a consistent naming convention for inheritance permissions
 */
const PO_NO_SYSTEM_CLASSES = PO_NO_INHERIT_SYSTEM_CLASSES;

//! Prohibits builtin/system function variants from being imported into the new Program object
/** @note @ref Qore::PO_NO_SYSTEM_FUNC_VARIANTS "PO_NO_SYSTEM_FUNC_VARIANTS" is a synonym of this constant

    @since %Qore 0.8.12 to harmonize permission names
 */
const PO_NO_INHERIT_SYSTEM_FUNC_VARIANTS = PO_NO_INHERIT_SYSTEM_FUNC_VARIANTS;

//! Prohibits builtin/system function variants from being imported into the new Program object
/**
    @note @ref Qore::PO_NO_INHERIT_SYSTEM_FUNC_VARIANTS "PO_NO_INHERIT_SYSTEM_FUNC_VARIANTS" was added in %Qore 0.8.12 with the same value of this constant to provide a consistent naming convention for inheritance permissions
*/
const PO_NO_SYSTEM_FUNC_VARIANTS = PO_NO_INHERIT_SYSTEM_FUNC_VARIANTS;

//! Disallows access to reading from and/or writing to the terminal
/**
    @see @ref no-terminal-io "%no-terminal-io"
*/
const PO_NO_TERMINAL_IO = PO_NO_TERMINAL_IO;

//! Disallows access to any thread-control functions and thread-relevant statements and operators (for example the @ref background "background operator" and the @ref thread_exit "thread_exit statement")
/**
    @see @ref no-thread-control "%no-thread-control"
*/
const PO_NO_THREAD_CONTROL = PO_NO_THREAD_CONTROL;

//! Disallows access to any thread classes
/**
    @see @ref no-thread-classes "%no-thread-classes"
*/
const PO_NO_THREAD_CLASSES = PO_NO_THREAD_CLASSES;

//! Disallows access to functionality that provides information about threading
/**
    @see @ref no-thread-info "%no-thread-info"
*/
const PO_NO_THREAD_INFO = PO_NO_THREAD_INFO;

//! Disallows top level code
/**
    @see @ref no-top-level "%no-top-level"
*/
const PO_NO_TOP_LEVEL_STATEMENTS = PO_NO_TOP_LEVEL_STATEMENTS;

//! Prohibits user classes from being imported into the new Program object
/** @note @ref Qore::PO_NO_USER_CLASSES "PO_NO_USER_CLASSES" is a synonym of this constant

    @since %Qore 0.8.12 to harmonize permission names
 */
const PO_NO_INHERIT_USER_CLASSES = PO_NO_INHERIT_USER_CLASSES;

//! Prohibits user classes from being imported into the new Program object
/**
    @note @ref Qore::PO_NO_INHERIT_USER_CLASSES "PO_NO_INHERIT_USER_CLASSES" was added in %Qore 0.8.12 with the same value of this constant to provide a consistent naming convention for inheritance permissions
 */
const PO_NO_USER_CLASSES = PO_NO_INHERIT_USER_CLASSES;

//! Prohibits access to all threading information
/** made up of @ref PO_NO_THREAD_CONTROL | @ref PO_NO_THREAD_CLASSES | @ref PO_NO_THREAD_INFO

    @see @ref no-threads "%no-threads"
 */
const PO_NO_THREADS = PO_NO_THREADS;

//! mask of all parse options allowing for more freedom (instead of less)
/** made up of a binary-or'ed combination of:
    - @ref PO_NO_CHILD_PO_RESTRICTIONS
    - @ref PO_ALLOW_INJECTION
 */
const PO_POSITIVE_OPTIONS = PO_POSITIVE_OPTIONS;

//! Requires global variables to be declared with our before use
/**
    @see @ref require-our "%require-our"
*/
const PO_REQUIRE_OUR = PO_REQUIRE_OUR;

//! Requires all function and method parameters and return types to have type declarations
/** However, variable declarations and object members are not required to have type declarations

    @see PO_REQUIRE_TYPES

    @see @ref require-prototypes "%require-prototypes"
 */
const PO_REQUIRE_PROTOTYPES = PO_REQUIRE_PROTOTYPES;

//! Requires all function and method parameters, return types, variables, and object members to have type declarations
/** @note This option also implies @ref PO_STRICT_ARGS
    @see @ref PO_REQUIRE_PROTOTYPES

    @see @ref require-types "%require-types"
 */
const PO_REQUIRE_TYPES = PO_REQUIRE_TYPES;

//! Prohibits access to builtin functions and methods flagged with RT_NOOP and also causes errors to be raised if excess arguments are given to functions that do not access excess arguments
/** This option is set implicitly with @ref PO_REQUIRE_TYPES

    @see @ref strict-args "%strict-args"
 */
const PO_STRICT_ARGS = PO_STRICT_ARGS;

//! Sets strict mathematical boolean evaluation runtime mode (the qore default prior to v0.8.6)
/** @see @ref strict-bool-eval "%strict-bool-eval"

    @since %Qore 0.8.6
 */
const PO_STRICT_BOOLEAN_EVAL = PO_STRICT_BOOLEAN_EVAL;

//! Allows code/dependency injections in the contained Program object
/** @see
    - @ref Qore::Program::importClass()
    - @ref Qore::Program::importFunction()

    @since %Qore 0.8.12
 */
const PO_ALLOW_INJECTION = PO_ALLOW_INJECTION;

//! Prohibits user constants from being imported into the new Program object
/** @note this does not apply to class constants

    @since %Qore 0.8.12
 */
const PO_NO_INHERIT_USER_CONSTANTS = PO_NO_INHERIT_USER_CONSTANTS;

//! Prohibits system constants from being imported into the new Program object
/** @note this does not apply to class constants

    @since %Qore 0.8.12
 */
const PO_NO_INHERIT_SYSTEM_CONSTANTS = PO_NO_INHERIT_SYSTEM_CONSTANTS;

//! Prohibits any user code from being inherited into the Program object
/** made up of:
    - @ref PO_NO_INHERIT_GLOBAL_VARS
    - @ref PO_NO_INHERIT_USER_CLASSES
    - @ref PO_NO_INHERIT_USER_CONSTANTS
    - @ref PO_NO_INHERIT_USER_FUNC_VARIANTS

    @since %Qore 0.8.12
 */
const PO_NO_USER_API = PO_NO_USER_API;

//! Prohibits any code from being inherited into the Program object, including the system API
/** made up of:
    - @ref PO_NO_INHERIT_SYSTEM_CLASSES
    - @ref PO_NO_INHERIT_SYSTEM_FUNC_VARIANTS
    - @ref PO_NO_INHERIT_SYSTEM_CONSTANTS
    - @ref PO_NO_INHERIT_SYSTEM_HASHDECLS

    @since %Qore 0.8.12
 */
const PO_NO_SYSTEM_API = PO_NO_SYSTEM_API;

//! Prohibits any code from being inherited into the Program object, including the system API
/** made up of:
    - @ref PO_NO_INHERIT_SYSTEM_CLASSES
    - @ref PO_NO_INHERIT_SYSTEM_CONSTANTS
    - @ref PO_NO_INHERIT_SYSTEM_FUNC_VARIANTS
    - @ref PO_NO_INHERIT_SYSTEM_HASHDECLS
    - @ref PO_NO_INHERIT_GLOBAL_VARS
    - @ref PO_NO_INHERIT_USER_CLASSES
    - @ref PO_NO_INHERIT_USER_CONSTANTS
    - @ref PO_NO_INHERIT_USER_FUNC_VARIANTS
    - @ref PO_NO_INHERIT_USER_HASHDECLS

    @since %Qore 0.8.12
 */
const PO_NO_API = PO_NO_API;

//! allows for old pre-%Qore 0.8.12 broken list rewriting in the parser
/** @see @ref broken-list-parsing "%broken-list-parsing"

    @since %Qore 0.8.12
*/
const PO_BROKEN_LIST_PARSING = PO_BROKEN_LIST_PARSING;

//! allows for old pre-%Qore 0.8.12 precedence of logical and bitwise operators
/** @see @ref broken-logic-precedence "%broken-logic-precedence"

    @since %Qore 0.8.12
*/
const PO_BROKEN_LOGIC_PRECEDENCE = PO_BROKEN_LOGIC_PRECEDENCE;

//! allows for old pre-%Qore 0.8.12 broken runtime @ref int_type "int" assignments where runtime type errors were ignored
/** @see @ref broken-int-assignments "%broken-int-assignments"

    @since %Qore 0.8.12
*/
const PO_BROKEN_INT_ASSIGNMENTS = PO_BROKEN_INT_ASSIGNMENTS;

//! allows for old pre-%Qore 0.8.12 broken runtime parsing of multi-character operators with spaces
/** @see @ref broken-operators "%broken-operators"

    @since %Qore 0.8.12
*/
const PO_BROKEN_OPERATORS = PO_BROKEN_OPERATORS;

//! allows for old pre-%Qore 0.8.13 handling of break and continue
/** @see @ref broken-loop-statement "%broken-loop-statement"

    @since %Qore 0.8.13
*/
const PO_BROKEN_LOOP_STATEMENT = PO_BROKEN_LOOP_STATEMENT;

//! allows for old pre-%Qore 0.9.5 handling of @ref Qore::range() "range()" and @ref Qore::xrange() "xrange()"
/** @see @ref broken-range "%broken-range"

    @since %Qore 0.9.5
*/
const PO_BROKEN_RANGE = PO_BROKEN_RANGE;

//! allows for old pre-%Qore 0.8.13 handling of @ref reference_type "reference" and @ref reference_or_nothing_type "*reference"
/** @see @ref broken-references "%broken-references"

    @since %Qore 0.8.13
*/
const PO_BROKEN_REFERENCES = PO_BROKEN_REFERENCES;

//! disallows out-of-line class and namespace declarations
/** @see @ref strong-encapsulation "%strong-encapsulation"

    @since %Qore 0.8.13
*/
const PO_STRONG_ENCAPSULATION = PO_STRONG_ENCAPSULATION;

//! disallow access to "uncontrolled APIs" like external language bindings or direct generic system call APIs that could bypass %Qore's sandboxing controls
/** @see @ref no-uncontrolled-apis "%no-uncontrolled-apis"

    @since %Qore 0.8.13
*/
const PO_NO_UNCONTROLLED_APIS = PO_NO_UNCONTROLLED_APIS;

//! Forbids debugging of the current @ref Qore::Program "Program" object; when this option is set, debuggers cannot attach to the @ref Qore::Program "Program" object
/** @see @ref no-debugging "%no-debugging"

    @since %Qore 0.8.13
*/
const PO_NO_DEBUGGING = PO_NO_DEBUGGING;

//! Prohibits user hashdecls from being imported into the new Program object
/** @since %Qore 0.8.13
 */
const PO_NO_INHERIT_USER_HASHDECLS = PO_NO_INHERIT_USER_HASHDECLS;

//! Prohibits system hashdecls from being imported into the new Program object
/** @since %Qore 0.8.13
 */
const PO_NO_INHERIT_SYSTEM_HASHDECLS = PO_NO_INHERIT_SYSTEM_HASHDECLS;

//! Allows the use of the @ref weak_assignment_operator "weak assignment operator (:=)"
/** @see @ref weak_assignment_operator

    @since %Qore 0.8.13
 */
const PO_ALLOW_WEAK_REFERENCES = PO_ALLOW_WEAK_REFERENCES;

//! Allows the use of functionality required to implement a debugger, mainly via @ref Qore::DebugProgram "DebugProgram"
/**
    @since %Qore 0.8.13
 */
const PO_ALLOW_DEBUGGER = PO_ALLOW_DEBUGGER;

//! Allows for old pre-%Qore 0.9 top-level statements having no effect
/**
    @since %Qore 0.9
 */
const PO_ALLOW_STATEMENT_NO_EFFECT = PO_ALLOW_STATEMENT_NO_EFFECT;

//! Disallows access to reflection
/**
    @since %Qore 0.9
*/
const PO_NO_REFLECTION = PO_NO_REFLECTION;

//! Disallows the @ref transient "transient"
/**
    @since %Qore 0.9
*/
const PO_NO_TRANSIENT = PO_NO_TRANSIENT;

//! Enables broken @ref Qore::sprintf() "sprintf()" handling where no argument is treated diffferently than @ref nothing
/** Also applies to all other functions and methods using format strings for formatted output

    @since %Qore 0.9
*/
const PO_BROKEN_SPRINTF = PO_BROKEN_SPRINTF;

//! Allows for old pre-%Qore 0.9.4 broken @ref cast "cast<>" operator behavior where @ref nothing was silently accepted
/** @see @ref broken-cast "%broken-cast"

    @since %Qore 0.9.4
*/
const PO_BROKEN_CAST = PO_BROKEN_CAST;

//! Allows the use of the deprecated \c returns keyword
/** @see @ref allow-returns "%allow-returns"

    @since %Qore 0.9.4
*/
const PO_ALLOW_RETURNS = PO_ALLOW_RETURNS;

//! Sets strict type checking and automatically sets default values for lvalues with type restrictions for types with default values
/** @see @ref strict-types "%strict-types"

    @since %Qore 0.9.4
*/
const PO_STRICT_TYPES = PO_STRICT_TYPES;

//! Do not inherit module-specific Program data from the parent
/** @since %Qore 1.0
*/
const PO_NO_INHERIT_PROGRAM_DATA = PO_NO_INHERIT_PROGRAM_DATA;

//! allows for old pre-%Qore 1.17 handling of varargs
/** @see @ref broken-varargs "%broken-varargs"

    @since %Qore 1.17
*/
const PO_BROKEN_VARARGS = PO_BROKEN_VARARGS;
///@}

/** @defgroup warning_constants Warning Constants
    These are the possible values that can be or'ed together to set the @ref Qore::Program "Program" warning mask
*/
///@{
//! Enables all warnings
const WARN_ALL = QP_WARN_ALL;

//! Enables warnings when the parser determines that the argument types of a function or method call are such that the operation is guaranteed to produce a constant value
/** @see @ref call-with-type-errors
 */
const WARN_CALL_WITH_TYPE_ERRORS = QP_WARN_CALL_WITH_TYPE_ERRORS;

//! The default warning mask
/** This warning is made up of the following values combined with binary or:
    - @ref WARN_UNKNOWN_WARNING
    - @ref WARN_UNREACHABLE_CODE
    - @ref WARN_NONEXISTENT_METHOD_CALL
    - @ref WARN_INVALID_OPERATION
    - @ref WARN_CALL_WITH_TYPE_ERRORS
    - @ref WARN_RETURN_VALUE_IGNORED
    - @ref WARN_DEPRECATED
    - @ref WARN_DUPLICATE_HASH_KEY
    - @ref WARN_DUPLICATE_BLOCK_VARS
    - @ref WARN_BROKEN_LOGIC_PRECEDENCE
    - @ref WARN_INVALID_CATCH
*/
const WARN_DEFAULT = QP_WARN_DEFAULT;

//! Enables a warning when deprecated code is used
/** @see @ref deprecated
 */
const WARN_DEPRECATED = QP_WARN_DEPRECATED;

//! Enables a warning when a program declares a local variable more than once in the same block; note that this is not a warning but rather an error when %assume-local or %new-style parse options are set
/** @see @ref duplicate-block-vars
 */
const WARN_DUPLICATE_BLOCK_VARS = QP_WARN_DUPLICATE_BLOCK_VARS;

//! Indicates that the embedded code has declared the same global variable more than once
/** @see @ref duplicate-global-vars
 */
const WARN_DUPLICATE_GLOBAL_VARS = QP_WARN_DUPLICATE_GLOBAL_VARS;

//! Enables a warning when an immediate hash is declared and at least one of the keys is repeated
/** @see @ref duplicate-hash-key
 */
const WARN_DUPLICATE_HASH_KEY = QP_WARN_DUPLICATE_HASH_KEY;

//! Enables a warning when a local variable with the same name is declared in a subblock (ie another local variable with the same name is reachable in the same lexical scope); note that this warning can raise false positives if the programmer is used to redeclaring the same variable names in subblocks
/** @see @ref duplicate-local-vars and @ref WARN_DUPLICATE_BLOCK_VARS
 */
const WARN_DUPLICATE_LOCAL_VARS = QP_WARN_DUPLICATE_LOCAL_VARS;

//! Enables a warning when a function or method call is made with more arguments than are used by the function or method
/** @see @ref excess-args
 */
const WARN_EXCESS_ARGS = QP_WARN_EXCESS_ARGS;

//! Indicates that the embedded code performs some operation that is guaranteed to produce no result (for example, using the @ref list_element_operator "[] operator" on an integer value)
/** @see @ref invalid-operation
 */
const WARN_INVALID_OPERATION = QP_WARN_INVALID_OPERATION;

//! The default warning mask for @ref user_modules "user modules"
/** This warning is made up of the following values combined with binary or:
    - @ref WARN_UNREACHABLE_CODE
    - @ref WARN_NONEXISTENT_METHOD_CALL
    - @ref WARN_INVALID_OPERATION
    - @ref WARN_CALL_WITH_TYPE_ERRORS
    - @ref WARN_RETURN_VALUE_IGNORED
    - @ref WARN_DUPLICATE_HASH_KEY
    - @ref WARN_DUPLICATE_BLOCK_VARS
    - @ref WARN_BROKEN_LOGIC_PRECEDENCE
    - @ref WARN_INVALID_CATCH
*/
const WARN_MODULES = QP_WARN_MODULES;

//! Represents no warning
const WARN_NONE = QP_WARN_NONE;

//! Indicates that the embedded code is calling an unknown method in a class
/** This warning may generate false positives; it may be vaild operation if the calling method is only called from a derived class that actually implements the method.  In this case, the @ref cast "cast<> operator" can be used to eliminate the warning.

    @see @ref non-existent-method-call
 */
const WARN_NONEXISTENT_METHOD_CALL = QP_WARN_NONEXISTENT_METHOD_CALL;

//! Enables a warning when a function or method call is made with no side effects and the return value is ignored
/** @see @ref return-value-ignored
 */
const WARN_RETURN_VALUE_IGNORED = QP_WARN_RETURN_VALUE_IGNORED;

//! Indicates that the embedded code referenced an undeclared variable that will be assumed to be a global variable
/** @see @ref undeclared-var
 */
const WARN_UNDECLARED_VAR = QP_WARN_UNDECLARED_VAR;

//! Indicates that the embedded code tried to enable or disable an unknown warning
/** @see @ref unknown-warning
 */
const WARN_UNKNOWN_WARNING = QP_WARN_UNKNOWN_WARNING;

//! Indicates that code cannot be reached (for example; code in the same local block after an unconditional @ref return "return" or @ref thread_exit "thread_exit" statement)
/** @see @ref unreachable-code
 */
const WARN_UNREACHABLE_CODE = QP_WARN_UNREACHABLE_CODE;

//! This warning is raised when a variable is declared in a block but never referenced
/** @see @ref unreferenced-variable
 */
const WARN_UNREFERENCED_VARIABLE = QP_WARN_UNREFERENCED_VARIABLE;

//! This warning means that the embedded code tried to change the warning mask, but it was locked, so the warning mask was actually unchanged
/** @see @ref warning-mask-unchanged
 */
const WARN_WARNING_MASK_UNCHANGED = QP_WARN_WARNING_MASK_UNCHANGED;

//! This warns before expressions affected by @ref broken-logic-precedence "%broken-logic-precedence"
/** @see @ref warning-broken-logic-precedence

    @since %Qore 0.8.12.11
*/
const WARN_BROKEN_LOGIC_PRECEDENCE = QP_WARN_BROKEN_LOGIC_PRECEDENCE;

//! This warns when the catch variable has no type definition and types are required
/** @see @ref invalid-catch

    @note This is an error when @ref strict-types "%strict-types" is in effect

    @since %Qore 1.0
*/
const WARN_INVALID_CATCH = QP_WARN_INVALID_CATCH;
///@}

//! Program objects allow %Qore programs to support subprograms with the option to restrict capabilities, for example, to support user-defined logic for application actions
/** Parsing in %Qore happens in two steps; first all code is parsed to pending data structures, and then in the second stage,
    all references are resolved, and, if there are no errors, then all changes are committed to the Program object. Note that
    all parse actions (Program::parse(), Program::parsePending(), Program::parseCommit(), and Program::parseRollback()) are atomic;
    there is a thread lock on each Program object to ensure atomicity, and if any parse errors occur in any stage of parsing, any
    pending changes to the Program object are automatically rolled back. However parse actions that affect only one stage of the two
    stages of parsing (Program::parsePending(), Program::parseCommit() and Program::parseRollback()) are atomic within themselves,
    but not between calls, so one thread may inadvertently commit changes to a Program object if two or more threads are trying to
    perform transaction-safe two-stage parsing on a Program object without explicit user locking.

    A complete parse action (Program::parse() or Program::parseCommit()) can only be performed once; subsequent attempts to parse
    code into the same %Program object will fail with an exception.  If parsing fails due to a parse exception, the Program
    object is generally no longer usable and must be recreated to be used.

    @ref parse_options "Parse option constants" can be used to limit the capabilities of a Program object. These options should be
    binary-OR'ed together and passed to the Program object's constructor. Also see @ref command_line_processing for equivalent
    command-line options, and @ref parse_directives for equivalent parse directives.

    Note that a program can provide controlled access to functionality otherwise restricted by parse options by exporting a custom API
    into the child program object using either the Program::importFunction() or Program::importGlobalVariable() method. This is possible
    because code (functions or object methods) imported into and called from a subprogram will run in the parent's space and therefore
    with the parent's capabilities.

    @ref qore_classes "Classes", @ref constants "constants", @ref qore_namespaces "namespaces", @ref qore_functions "functions", and
    @ref global_variables "global variables" are only inherited into child Program objects if they are declared @ref mod_public "public"
    and no @ref parse_options "parse options" prohibit it.

    Symbols can also be imported into Program objects singly using methods such as Program::importClass() and
    Program::importGlobalVariable(), etc.
 */
qclass Program [arg=QoreProgram* p];

//! Creates the program object and optionally sets program capabilities (@ref parse_options "parse options")
/** Note that if @ref PO_NO_CHILD_PO_RESTRICTIONS is not set in the parent Program when the new Program object is
    created, then the created Program object will have the parent's parse options added to its parse options as given
    by the argument to the constructor.

    In other words, if @ref PO_NO_CHILD_PO_RESTRICTIONS is not set, it's not possible to create a child Program
    object with fewer restrictions than the parent Program object (any attempt to do so will be silently ignored).

    However, if @ref PO_NO_CHILD_PO_RESTRICTIONS is set in the parent Program object, then the parse option argument
    to the constructor will be applied literally to the child object.  Additionally in this case module-specific-
    program data is not imported into the child Program object.

    @ref Qore::ProgramControl "ProgramControl" provides internal Program data for debugging purposes.

    Use the @ref Qore::ProgramControl::getProgram() "ProgramControl::getProgram()" method to attach to the currently
    running program

    @param po A binary OR'ed product of @ref parse_options "parse options"

    @par Example:
    @code{.py}
Program pgm();
    @endcode

    @throw PROGRAM-OPTION-ERROR invalid parse options used
 */
Program::constructor(softint po = PO_DEFAULT) [dom=EMBEDDED_LOGIC] {
    if (po & PO_SYSTEM_OPS) {
        xsink->raiseException("PROGRAM-OPTION-ERROR", "parse options (0x" QLLX ") contain restricted options that can only be set by the system", po);
        return;
    }

    QoreProgram* pgm = new QoreProgram(getProgram(), po);
    //pgm->registerQoreObject(self, xsink);
    assert(!xsink->isEvent());

    self->setPrivate(CID_PROGRAM, pgm);
    printd(5, "Program::constructor() pgm: %p, pgmid: %d, self: %p\n", pgm, pgm->getProgramId(), self);
}

//! Throws an exception to prevent objects of this class from being copied
/**
    @throw PROGRAM-COPY-ERROR copying Program objects is currently unsupported
 */
Program::copy() {
    xsink->raiseException("PROGRAM-COPY-ERROR", "copying Program objects is currently unsupported");
}

//! Waits for all threads to finish executing, then deletes all global variables, dereferences the internal Program object and deletes the %Qore object
/**
 */
Program::destructor() {
    printd(5, "Program::destructor() pgm: %p pgmid: %d self: %p\n", p, p->getProgramId(), self);
    // inherited system object destructor is not executed
    //p->unregisterQoreObject(self, xsink);

    p->waitForTerminationAndDeref(xsink);
}

//! Parses the string argument and adds the code to the Program object
/** This method causes both stages of parsing to be executed; if this method is successful, then the code parsed is
    committed to the Program object.
    This method is equivalent to calling Program::parsePending() and Program::parseCommit() in one atomic call.

    If an exception occurs in this method, all pending code is backed out, not just code parsed by this method (for
    example, in case uncommitted code added by Program::parsePending() also exists in the Program object before
    calling this method).

    @param code The code to parse into the Program object
    @param label The label for the code; this label will be given if any parse or run-time errors are raised for the
    code given
    @param warning_mask An optional warning mask; see @ref warning_constants for values to combine by binary-or; if
    this argument is 0 or not given then no warnings will be checked or issued and the return value will always be
    @ref nothing
    @param source An optional source file name for the code being parsed; this is useful if sections of a file are
    parsed
    @param offset An optional line offset for use with the \a source parameter; this gives the line offset in the file
    to the code being parsed
    @param format_label obsolete / ignored since %Qore 0.9

    @return if warnings included in the warning mask are raised during parsing, this method will return an
    @ref Qore::ExceptionInfo "ExceptionInfo" hash with warning information, otherwise @ref nothing is returned

    @par Example:
    @code{.py}
*hash<ExceptionInfo> wh = pgm.parse(code, "label", WARN_DEFAULT);
while (wh) {
    printf("warning: %s:%d: %s: %s\n", wh.file, wh.line, wh.err, wh.desc);
    wh = wh.next;
}
    @endcode

    @note This method could throw many parse exceptions which are not enumerated here; any parse errors will result in
    an appropriate exception.

    @see
    - Qore::Program::parsePending()
    - Qore::Program::parseCommit()
    - Qore::parse()

    @throw PROGRAM-PARSE-ERROR this exception is thrown if any parse actions are started while the Program object has
    running threads

    @note A complete parse action (Program::parse() or Program::parseCommit()) can only be performed once; subsequent
    attempts to parse code into the same %Program object will fail with an exception.  If parsing fails due to a parse
    exception, the Program object is generally no longer usable and must be recreated to be used.

    @since
    - %Qore 0.8.7 the \a source, \a offset, and \a format_label arguments were added
    - %Qore 0.9 parsing can only be executed once per %Program object
    - %Qore 0.9 the \a format_label is obsolete / ignored
 */
*hash<ExceptionInfo> Program::parse(string code, string label, *softint warning_mask, *string source, *softint offset, softbool[doc] format_label = True) [dom=EMBEDDED_LOGIC] {
    if (warning_mask) {
        ExceptionSink wsink;
        p->parse(code, label, xsink, &wsink, warning_mask, source, offset);
        if (!wsink.isException())
            return QoreValue();

        QoreException *e = wsink.catchException();
        return e->makeExceptionObjectAndDelete(xsink);
    }

    p->parse(code, label, xsink, 0, 0, source, offset);
}

//! Parses the text passed to pending lists in the Program object; does not resolve all references or commit the code to the Program object
/** References are resolved in the Program::parseCommit() method.

    Program::parseCommit() must be called to resolve all references and commit the code to the Program object; until Program::parseCommit() is called, none of the code parsed by this method will be available for execution in the Program object.

    If an exception occurs in this method, all pending code is backed out, not just code parsed by this method.

    @param code The code to parse into the Program object
    @param label The label for the code; this label will be given if any parse or run-time errors are raised for the code given
    @param warning_mask An optional warning mask; see @ref warning_constants for values to combine by binary-or; if this argument is 0 or not given then no warnings will be checked or issued and the return value will always be @ref nothing
    @param source An optional source file name for the code being parsed; this is useful if sections of a file are parsed
    @param offset An optional line offset for use with the \a source parameter; this gives the line offset in the file to the code being parsed
    @param format_label obsolete / ignored since %Qore 0.9

    @return if warnings included in the warning mask are raised during parsing, this method will return an @ref Qore::ExceptionInfo "ExceptionInfo" hash with warning information, otherwise @ref nothing is returned

    @par Example:
    @code{.py}
*hash<ExceptionInfo> wh = pgm.parsePending(code, "label", WARN_DEFAULT);
while (wh) {
    printf("warning: %s:%d: %s: %s\n", wh.file, wh.line, wh.err, wh.desc);
    wh = wh.next;
}
pgm.parseCommit();
    @endcode

    @note This method could throw many parse exceptions which are not enumerated here; any parse errors will result in an appropriate exception.

    @see
    - Qore::Program::parse()
    - Qore::Program::parseCommit()
    - Qore::Program::parseRollback()
    - Qore::parse()

    @throw PROGRAM-PARSE-ERROR this exception is thrown if any parse actions are started while the Program object has running threads

    @note A complete parse action (Program::parse() or Program::parseCommit()) can only be performed once; subsequent attempts to parse
    code into the same %Program object will fail with an exception.  If parsing fails due to a parse exception, the Program
    object is generally no longer usable and must be recreated to be used.

    @since
    - %Qore 0.8.7 the \a source, \a offset, and \a format_label arguments were added
    - %Qore 0.9 parsing can only be executed once per %Program object
    - %Qore 0.9 the \a format_label is obsolete / ignored
 */
*hash<ExceptionInfo> Program::parsePending(string code, string label, *softint warning_mask, *string source, *softint offset, softbool[doc] format_label = True) [dom=EMBEDDED_LOGIC] {
    if (warning_mask) {
        ExceptionSink wsink;
        p->parsePending(code, label, xsink, &wsink, warning_mask, source, offset);
        if (!wsink.isException())
            return QoreValue();

        QoreException *e = wsink.catchException();
        return e->makeExceptionObjectAndDelete(xsink);
    }

    p->parsePending(code, label, xsink, 0, 0, source, offset);
}

//! Commits and pending code processed with Program::parsePending() to the Program object after resolving all outstanding references in the pending code
/** An exception in this method causes all pending code to be rolled back immediately.

    @par Example:
    @code{.py}
pgm.parseCommit();
    @endcode

    @note This method could throw many parse exceptions related to resolving references which are not enumerated here;
    any parse errors will result in an appropriate exception.

    @see
    - Qore::Program::parse()
    - Qore::Program::parsePending()
    - Qore::Program::parseRollback()
    - Qore::parse()

    @throw PROGRAM-PARSE-ERROR this exception is thrown if any parse actions are started while the Program object has
    running threads

    @note A complete parse action (Program::parse() or Program::parseCommit()) can only be performed once; subsequent
    attempts to parse code into the same %Program object will fail with an exception.  If parsing fails due to a parse
    exception, the Program object is generally no longer usable and must be recreated to be used.

    @since %Qore 0.9 parsing can only be executed once per %Program object
 */
nothing Program::parseCommit() {
    p->parseCommit(xsink);
}

//! Commits and pending code processed with Program::parsePending() to the Program object after resolving all outstanding references in the pending code
/** An exception in this method causes all pending code to be rolled back immediately.

    @par Example:
    @code{.py}
*hash<ExceptionInfo> wh = pgm.parseCommit(WARN_DEFAULT);
while (exists wh) {
    printf("warning: %s:%d: %s: %s\n", wh.file, wh.line, wh.err, wh.desc);
    wh = wh.next;
}
    @endcode

    @note This method could throw many parse exceptions related to resolving references which are not enumerated here; any parse errors will result in an appropriate exception.

    @see
    - Qore::Program::parse()
    - Qore::Program::parsePending()
    - Qore::Program::parseRollback()
    - Qore::parse()

    @throw PROGRAM-PARSE-ERROR this exception is thrown if any parse actions are started while the Program object has running threads

    @note A complete parse action (Program::parse() or Program::parseCommit()) can only be performed once; subsequent attempts to parse
    code into the same %Program object will fail with an exception.  If parsing fails due to a parse exception, the Program
    object is generally no longer usable and must be recreated to be used.

    @since %Qore 0.9 parsing can only be executed once per %Program object
 */
*hash<ExceptionInfo> Program::parseCommit(int warning_mask) {
    ExceptionSink wsink;
    p->parseCommit(xsink, &wsink, warning_mask);
    if (!wsink.isException())
        return QoreValue();

    QoreException *e = wsink.catchException();
    return e->makeExceptionObjectAndDelete(xsink);
}

//! Removes all partially or fully-parsed code from the object along with other builtin objects; a %Program object is not usable after calling this method; do not use this method but instead destroy the %Program object
/**
    @see
    - Qore::Program::parse()
    - Qore::Program::parseCommit()
    - Qore::Program::parsePending()
    - Qore::parse()

    @throw PROGRAM-PARSE-ERROR this exception is thrown if any parse actions are started while the Program object has running threads

    @deprecated if parsing fails or is rolled back, the %Program object is no longer usable
 */
nothing Program::parseRollback() [flags=DEPRECATED] {
    p->parseRollback(xsink);
}

//! Calls a function in the program object and returns the return value
/** The function runs with the permissions of the Program object containing the function.

    @param name The name of the function to call
    @param ... The remaining arguments passed to the method are passed to the function to be called

    @return Depends on the function being called

    @par Example:
    @code{.py}
auto result = pgm.callFunction("func_name", arg1, arg2);
    @endcode

    @throw INVALID-FUNCTION-ACCESS @ref parse_options "Parse options" do not allow this builtin function to be called
    @throw NO-FUNCTION The function does not exist
    @throw ENCODING-CONVERSION-ERROR the function name could not be converted to the @ref default_encoding "default character encoding"

    @note The function called could also cause other exceptions to be thrown
 */
auto Program::callFunction(string name, ...) {
    TempEncodingHelper tmp(name, QCS_DEFAULT, xsink);
    if (!tmp)
        return QoreValue();

    ReferenceHolder<QoreListNode> vargs(xsink);
    if (args->size() > 1)
        vargs = args->copyListFrom(1);

    return p->callFunction(tmp->getBuffer(), *vargs, xsink);
}

//! Calls a function in the program object giving the arguments to the function as a list and returns the return value
/** The function runs with the permissions of the Program object containing the function.

    @param name The name of the function to call
    @param vargs The arguments to the function to be called

    @return Depends on the function being called

    @par Example:
    @code{.py}
auto result = pgm.callFunctionArgs("func_name", (arg1, arg2));
    @endcode

    @throw INVALID-FUNCTION-ACCESS @ref parse_options "Parse options" do not allow this builtin function to be called
    @throw NO-FUNCTION The function does not exist
    @throw ENCODING-CONVERSION-ERROR the function name could not be converted to the @ref default_encoding "default character encoding"

    @note The function called could also cause other exceptions to be thrown
 */
auto Program::callFunctionArgs(string name, *softlist<auto> vargs) {
    TempEncodingHelper tmp(name, QCS_DEFAULT, xsink);
    if (!tmp)
        return QoreValue();
    return p->callFunction(tmp->getBuffer(), vargs, xsink);
}

//! Calls a static method of an object, passing the arguments to the function as arguments to the method
/** @par Example:
    @code{.py}
auto result = p.callStaticMethod("Namespace::Class::method", arg1, arg2);
    @endcode

    @param class_name the with an optional namespace prefix
    @param method the static method to call
    @param ... any additional arguments to the method

    @return the value returned by the method call

    @throw METHOD-DOES-NOT-EXIST The named method does not exist in this class
    @throw ILLEGAL-EXPLICIT-METHOD-CALL The named method may not be called explicitly
    @throw METHOD-IS-PRIVATE The named method is private and therefore can only be called within the class
    @throw BASE-CLASS-IS-PRIVATE The named method is a member of a privately inherited base class

    @note the method called could cause additional exceptions to be thrown

    @see
    - call_static_method()
    - @ref Qore::Program::callStaticMethodArgs() "Program::callStaticMethodArgs()"

    @since %Qore 0.9.5
*/
auto Program::callStaticMethod(string class_name, string method, ...) {
    ReferenceHolder<QoreListNode> call_args(xsink);
    // if there are arguments to pass
    if (num_args(args) > 2) {
        // create argument list by copying current list
        call_args = args->copyListFrom(2);
    }
    return q_call_static_method_args(p, class_name, method, *call_args, xsink);
}

//! Calls a static method of an object, passing the arguments to the function as arguments to the method
/** @par Example:
    @code{.py}
auto result = p.callStaticMethodArgs("Namespace::Class::method", (arg1, arg2));
    @endcode

    @param class_name the with an optional namespace prefix
    @param method the static method to call
    @param call_args any additional arguments to the method

    @return the value returned by the method call

    @throw METHOD-DOES-NOT-EXIST The named method does not exist in this class
    @throw ILLEGAL-EXPLICIT-METHOD-CALL The named method may not be called explicitly
    @throw METHOD-IS-PRIVATE The named method is private and therefore can only be called within the class
    @throw BASE-CLASS-IS-PRIVATE The named method is a member of a privately inherited base class

    @note the method called could cause additional exceptions to be thrown

    @see
    - call_static_method_args()
    - @ref Qore::Program::callStaticMethod() "Program::callStaticMethod()"

    @since %Qore 0.9.5
*/
auto Program::callStaticMethodArgs(string class_name, string method, *softlist<auto> call_args) {
    return q_call_static_method_args(p, class_name, method, call_args, xsink);
}

//! Checks if a user function exists in the program object
/** @param name The name of the function to check

    @return @ref True if the function exists, @ref False if not

    @par Example:
    @code{.py}
if (pgm.existsFunction("my_func"))
    printf("my_func() exists in the Program\n");
    @endcode

    @throw ENCODING-CONVERSION-ERROR the function name could not be converted to the @ref default_encoding "default character encoding"
 */
bool Program::existsFunction(string name) [flags=RET_VALUE_ONLY] {
    TempEncodingHelper tmp(name, QCS_DEFAULT, xsink);
    if (!tmp)
        return false;
    return p->existsFunction(tmp->getBuffer());
}

//! Runs the program and optionally returns a value if the top-level code exits with a @ref return "return statement
/** @return the value given to the @ref return "return statement" at the top-level, if any, otherwise @ref nothing

    @par Example:
    @code{.py}
pgm.run();
    @endcode
 */
auto Program::run() {
    return p->run(xsink);
}

//! Imports a function into the program object's space; any calls to the imported function will run with the parent Program object's permissions
/** This allows a user-defined API with greater capabilities than the embedded Program object to be imported into the embedded code.

    @param func the name of the function to import into the Program object

    @par Example:
    @code{.py}
pgm.importFunction("log");
    @endcode

    @throw FUNCTION-IMPORT-ERROR Cannot import a function into the same Program object; function with this name already exists
    @throw PROGRAM-IMPORTFUNCTION-NO-FUNCTION The function does not exist
    @throw ENCODING-CONVERSION-ERROR the function name could not be converted to the @ref default_encoding "default character encoding"

    @since %Qore 0.8.4 the function name can include a namespace path (ex \c "Namespace::func")
 */
nothing Program::importFunction(string func) {
    TempEncodingHelper tmp(func, QCS_DEFAULT, xsink);
    if (!tmp)
        return QoreValue();
    qore_program_private::exportFunction(getProgram(), xsink, p, tmp->getBuffer());
}

//! Imports a function into the program object's space and gives it a new name; any calls to the imported function will run with the parent Program object's permissions
/** This allows a user-defined API with greater capabilities than the embedded Program object to be imported into the
    embedded code.

    @param func the name of the function to import into the Program object
    @param new_name the name of the function as it will be known in the Program object once imported; can be a fully-
    qualified namespace path, in which case the namespace path will be created in the target Program object if
    necessary as well (ex: \c "Ns1::Ns2::my_func")
    @param inject if @ref True "True" then the imported function will be marked as a dependency injection and
    therefore will not be overritten by subsequent calls to Program::importSystemFunctions(),
    Program::importSystemApi(), Program::loadApplyToPrivateUserModule(), or Program::loadApplyToUserModule(); note
    that for this call to succeed, the Program must have @ref Qore::PO_ALLOW_INJECTION set

    @par Example:
    @code{.py}
pgm.importFunction("service_log", "log");
    @endcode

    @throw FUNCTION-IMPORT-ERROR Cannot import a function into the same Program object; function with this name
    already exists; target namespace does not exist; injection indicated but @ref Qore::PO_ALLOW_INJECTION not set
    @throw PROGRAM-IMPORTFUNCTION-NO-FUNCTION The function does not exist
    @throw ENCODING-CONVERSION-ERROR the function name could not be converted to the
    @ref default_encoding "default character encoding"

    @since
    - %Qore 0.8.4 the function name can include a namespace path (ex \c "Namespace::func")
    - %Qore 0.8.12 added the \a inject parameter
 */
nothing Program::importFunction(string func, string new_name, *softbool inject) {
    TempEncodingHelper tfunc(func, QCS_DEFAULT, xsink);
    if (!tfunc)
        return QoreValue();
    TempEncodingHelper tnew_name(new_name, QCS_DEFAULT, xsink);
    if (!tnew_name)
        return QoreValue();
    if (tnew_name->empty())
        xsink->raiseException("FUNCTION-IMPORT-ERROR", "the new function name while importing function '%s' may not be empty", func);

    qore_program_private::exportFunction(getProgram(), xsink, p, tfunc->getBuffer(), tnew_name->getBuffer(), inject);
}

//! Imports a global variable into the program object's space
/** If the variable is an object, then any methods called from the subprogram will run in the parent's space

    @param varname The name of the global variable without the \c "$"
    @param readonly If this argument is @ref True, then the variable will be imported read-only, and cannot be changed by the subprogram (note that if the imported

    @par Example:
    @code{.py}
pgm.importGlobalVariable("var");
    @endcode

    @throw PROGRAM-IMPORTGLOBALVARIABLE-EXCEPTION The global variable does not exist in the source program, or the target variable already exists
    @throw ENCODING-CONVERSION-ERROR the global variablt name could not be converted to the @ref default_encoding "default character encoding"
 */
nothing Program::importGlobalVariable(string varname, bool readonly = False) {
    TempEncodingHelper tmp(varname, QCS_DEFAULT, xsink);
    if (!tmp)
        return QoreValue();
    qore_program_private::exportGlobalVariable(getProgram(), tmp->getBuffer(), readonly, p, xsink);
}

//! Imports a class into the program object's space; any calls to the imported class's code will run with the parent Program object's permissions
/** This allows a user-defined API with greater capabilities than the embedded Program object to be imported into the
    embedded code.

    @param cls the name of the class to import into the Program object
    @param new_name the name of the class in the imported Program object; can be a fully-qualified namespace path, in
    which case the namespace path will be created in the target Program object if necessary as well (ex:
    \c "Ns1::Ns2::MyClass")
    @param inject if @ref True "True" then the imported class will be marked as a dependency injection and
    therefore will not be overritten by subsequent calls to Program::importSystemClasses(),
    Program::importSystemHashDecls(), Program::importSystemConstants(), Program::importSystemApi(),
    Program::loadApplyToPrivateUserModule(), or Program::loadApplyToUserModule(); note that for this call to succeed,
    the Program must have @ref Qore::PO_ALLOW_INJECTION set
    @param module_visibility see @ref import_visibility_constants for valid values

    @par Example:
    @code{.py}
pgm.importClass("MyNamespace::MyClass");
    @endcode

    @throw CLASS-IMPORT-ERROR Cannot import a class into the same Program object; class or namespace with this name already exists or the source class does not exist; injection indicated but @ref Qore::PO_ALLOW_INJECTION not set
    @throw ENCODING-CONVERSION-ERROR the class name could not be converted to the @ref default_encoding "default character encoding"

    @since
    - This method was first implemented in %Qore 0.8.7
    - %Qore 0.8.12 added the \a new_name and \a inject parameters
    - %Qore 0.9,3 added the \a module_visibility parameter
 */
nothing Program::importClass(string cls, *string new_name, *softbool inject, int module_visibility = CSP_UNCHANGED) {
    TempEncodingHelper tmp(cls, QCS_DEFAULT, xsink);
    if (!tmp) {
        assert(*xsink);
        return QoreValue();
    }

    TempEncodingHelper nn;
    if (new_name) {
        nn.set(new_name, QCS_DEFAULT, xsink);
        if (!nn) {
            assert(*xsink);
            return QoreValue();
        }

        if (nn->empty())
            return xsink->raiseException("CLASS-IMPORT-ERROR", "the new class name while importing class '%s' may not be empty", tmp->c_str());
    }
    qore_program_private::get(*p)->importClass(xsink, *qore_program_private::get(*getProgram()), tmp->c_str(), new_name ? nn->c_str() : nullptr, inject, static_cast<q_setpub_t>(module_visibility));
}

//! Imports a @ref hashdecl "typed hash declaration" into the program object's space
/** This allows a user-defined API with greater capabilities than the embedded Program object to be imported into the embedded code.

    @param name the name of the @ref hashdecl "typed hash declaration" to import into the Program object
    @param new_name the name of the @ref hashdecl "typed hash declaration" in the imported Program object; can be a fully-qualified namespace path, in which case the namespace path will be created in the target Program object if necessary as well (ex: \c "Ns1::Ns2::MyClass")

    @par Example:
    @code{.py}
pgm.importHashDecl("MyNamespace::MyHash");
    @endcode

    @throw HASHDECL-IMPORT-ERROR Cannot import a hashdecl into the same Program object; conflict with an existing definition
    @throw ENCODING-CONVERSION-ERROR the hashdecl name could not be converted to the @ref default_encoding "default character encoding"

    @since %Qore 0.8.13
 */
nothing Program::importHashDecl(string name, *string new_name) {
    TempEncodingHelper tmp(name, QCS_DEFAULT, xsink);
    if (!tmp) {
        assert(*xsink);
        return QoreValue();
    }

    TempEncodingHelper nn;
    if (new_name) {
        nn.set(new_name, QCS_DEFAULT, xsink);
        if (!nn) {
            assert(*xsink);
            return QoreValue();
        }

        if (nn->empty())
            return xsink->raiseException("HASHDECL-IMPORT-ERROR", "the new hashdecl name while importing hashdecl '%s' may not be empty", tmp->c_str());
    }
    qore_program_private::get(*p)->importHashDecl(xsink, *qore_program_private::get(*getProgram()), tmp->c_str(), new_name ? nn->c_str() : nullptr);
}

//! Returns a list of strings of all user functions defined in the program object
/** @return a list of strings of all user functions defined in the program object

    @par Example:
    @code{.py}
list<string> l = pgm.getUserFunctionList();
    @endcode

    @note the <a href="../../modules/reflection/html/index.html">reflection</a> module supercedes this functionality
 */
softlist<string> Program::getUserFunctionList() [flags=CONSTANT] {
   return p->getUserFunctionList();
}

//! Returns the current binary-or'ed parse option mask for the Program object
/** @return the current binary-or'ed parse option mask for the Program object

    @par Example:
    @code{.py}
int mask = pgm.getParseOptions();
    @endcode
 */
int Program::getParseOptions() [flags=CONSTANT] {
   return p->getParseOptions64();
}

//! returns a list of parse option strings for the program object
/** @par Example:
    @code{.py}
list<string> l = pgm.getParseOptionStringList();
    @endcode

    @return a list of parse option strings for the program object

    @since %Qore 0.9
*/
list<string> Program::getParseOptionStringList() [flags=CONSTANT] {
    return parse_option_bitfield_to_string_list(p->getParseOptions64(), xsink);
}

//! Sets parse options in the parse option mask for the Program object
/** An \c OPTIONS-LOCKED exception is thrown if parse options have been locked (for example with Program::lockOptions())

    @param opt A single @ref parse_options "parse option" or binary-or'ed combination of @ref parse_options "parse options" to set in the parse option mask for the object; the given argument will be combined with binary or with the existing parse option mask

    @par Example:
    @code{.py}
# disable threading and GUI operations
pgm.setParseOptions(PO_NO_THREADS | PO_NO_GUI);
    @endcode

    @throw OPTIONS-LOCKED Parse options have been locked and cannot be changed
    @throw PROGRAM-OPTION-ERROR invalid parse options used

    @see Program::disableParseOptions() for a reciprocal method that disables parse options; also see Program::replaceParseOptions()
 */
nothing Program::setParseOptions(softint opt = PO_DEFAULT) {
   if (opt & PO_SYSTEM_OPS)
      return xsink->raiseException("PROGRAM-OPTION-ERROR", "parse options (0x" QLLX ") contain restricted options that can only be set by the system", opt);

   p->setParseOptions(opt, xsink);
}

//! Removes the given parse options to the current parse option mask
/** An \c OPTIONS-LOCKED exception is thrown if parse options have been locked (for example with Program::lockOptions())

    @param opt A single @ref parse_options "parse option" or binary-or'ed combination of @ref parse_options "parse options" to unset in the parse option mask for the object; any bit set in this argument will be unset (ie zeroed out or combined with inverse binary and) in the Program's parse option mask

    @par Example:
    @code{.py}
# allow threading and GUI operations
pgm.disableParseOptions(PO_NO_THREADS | PO_NO_GUI);
    @endcode

    @throw OPTIONS-LOCKED Parse options have been locked and cannot be changed
    @throw PROGRAM-OPTION-ERROR invalid parse options used

    @see Program::setParseOptions() for a reciprocal method that enables parse options; also see Program::replaceParseOptions()
 */
nothing Program::disableParseOptions(softint opt) {
   if (opt & PO_SYSTEM_OPS)
      return xsink->raiseException("PROGRAM-OPTION-ERROR", "parse options (0x" QLLX ") contain restricted options that can only be set by the system", opt);

   p->disableParseOptions(opt, xsink);
}

//! Replaces the parse options for the Program object
/** An \c OPTION-ERROR exception is thrown if the calling Program object does not have @ref PO_NO_CHILD_PO_RESTRICTIONS set.

    @param opt A single @ref parse_options "parse option" or binary-or'ed combination of @ref parse_options "parse options" to unset in the parse option mask for the object

    @par Example:
    @code{.py}
# disallow threading and GUI operations
pgm.replaceParseOptions(PO_NO_THREADS | PO_NO_GUI);
    @endcode

    @throw OPTION-ERROR The calling Program does not have the @ref PO_NO_CHILD_PO_RESTRICTIONS option set, and therefore cannot call Program::replaceParseOptions()

    @see Program::setParseOptions() and Program::disableParseOptions().
 */
nothing Program::replaceParseOptions(softint opt) {
   p->replaceParseOptions(opt, xsink);
}

//! Sets (or clears) the script path (directory and filename) for the object
/** @param path The path (directory and filename) for the current script; if the directory component is missing, then the current directory is assumed

    @par Example:
    @code{.py}
pgm.setScriptPath("/users/test/test.q");
    @endcode
 */
nothing Program::setScriptPath(*string path) {
   p->setScriptPath(path ? path->c_str() : nullptr);
}

//! Returns the current script directory as a string or @ref nothing if not set
/** Gets the script directory set with Program::setScriptPath(). This is the same value that will be returned in the %Qore program code with the get_script_dir() function if called from within the Program.

    @return Returns the current script directory as a string or @ref nothing if not set; if a string is returned, it should normally include the trailing directory separator character (\c "/" on UNIX, \c "\\" on Windows)

    @par Example:
    @code{.py}
*string dir = pgm.getScriptDir();
    @endcode
 */
*string Program::getScriptDir() [flags=CONSTANT] {
   return p->getScriptDir();
}

//! Returns the current script name as a string or @ref nothing if not set
/** Gets the script filename set with Program::setScriptPath(). This is the same value that will be returned in the %Qore program code with the get_script_name() function if called from within the Program.

    @return the current script name as a string or @ref nothing if not set

    @par Example:
    @code{.py}
*string name = pgm.getScriptName();
    @endcode
 */
*string Program::getScriptName() [flags=CONSTANT] {
   return p->getScriptName();
}

//! Returns the current script directory and filename if known, otherwise returns @ref nothing
/** Gets the script directory and filename set with Program::setScriptPath(). This is the same value that will be returned in the %Qore program code with the get_script_path() function if called from within the Program.

    @return the current script directory and filename if known, otherwise returns @ref nothing

    @par Example:
    @code{.py}
*string path = pgm.getScriptPath();
    @endcode
 */
*string Program::getScriptPath() [flags=CONSTANT] {
   return p->getScriptPath();
}

//! Locks parse options so that they cannot be changed
/**
    @par Example:
    @code{.py}
 pgm.lockOptions();
   @endcode
 */
nothing Program::lockOptions() {
   p->lockOptions();
}

//! Returns a the value of the global variable identified by the first string argument
/**
    @param varname The string name of the global variable to find, not including the leading \c "$" character
    @param rexists An lvalue reference to a @ref bool_type to determine if the global variable exists (because this method could return @ref nothing when the variable exists as well as when it does not)

    @return the value of the global variable identified by the first string argument giving the name of the variable (without any leading \c "$" symbol)

    @par Example:
    @code{.py}
bool existsv;
auto val = pgm.getGlobalVariable("error_count", \existsv);
    @endcode
 */
auto Program::getGlobalVariable(string varname, *reference<bool> rexists) {
    TempEncodingHelper t(varname, QCS_DEFAULT, xsink);
    if (!t)
        return QoreValue();

    bool found;
    ValueHolder rv(p->getGlobalVariableValue(t->c_str(), found), xsink);

    if (rexists) {
        QoreTypeSafeReferenceHelper r(rexists, xsink);
        if (!r)
            return QoreValue();

        if (r.assign(found))
            return QoreValue();
    }

    return rv.release();
}

//! Sets the default local time zone for the object from a path to a zoneinfo time zone region file
/** If there are errors opening, reading, or parsing the file (or the Windows registry entry, depending on the platform), an exception is thrown

    @param region The path to the zoneinfo file for the time zone region to set as the local time zone for the Program object

    @par Example:
    @code{.py}
pgm.setTimeZoneRegion("Europe/Prague");
    @endcode

    @throw TZINFO-ERROR Unable to read zoneinfo file; invalid file magic; error parsing zoneinfo file, etc
 */
nothing Program::setTimeZoneRegion(string region) {
   const AbstractQoreZoneInfo *zone = QTZM.findLoadRegion(region->getBuffer(), xsink);
   if (!*xsink)
      p->setTZ(zone);
}

//! Sets the default time zone for the Program object based on the number of seconds east of UTC; for zones west of UTC, use negative numbers
/** Time zones set with this method cannot have any daylight savings time information; to set a zone with daylight savings time information, use Program::setTimeZoneRegion() instead

    @param seconds_east The number of seconds east of UTC; for zones west of UTC, use negative numbers

    @par Example:
The following examples are all equivalent, setting the time zone to +02 UTC:
    @code{.py}
pgm.setTimeZoneUTCOffset(7200);
    @endcode
    @code{.py}
pgm.setTimeZoneUTCOffset(2h);
    @endcode
    @code{.py}
pgm.setTimeZoneUTCOffset(PT2H);
    @endcode

    @see TimeZone::setUTCOffset()
 */
nothing Program::setTimeZoneUTCOffset(softint seconds_east) {
   const AbstractQoreZoneInfo *zone = QTZM.findCreateOffsetZone(seconds_east);
   p->setTZ(zone);
}

//! Sets the default local time zone for the object
/**
    @par Example:
    @code{.py}
TimeZone tz("Europe/Prague");
pgm.setTimeZone(tz);
    @endcode

    @see TimeZone::set()
 */
nothing Program::setTimeZone(TimeZone[TimeZoneData] zone) {
   p->setTZ(zone->get());
}

//! Returns the default local time zone for the object
/** @return the default local time zone for the object

    @par Example:
    @code{.py}
TimeZone tz = pgm.getTimeZone();
    @endcode
 */
TimeZone Program::getTimeZone() [flags=CONSTANT] {
   return new QoreObject(QC_TIMEZONE, 0, new TimeZoneData(p->currentTZ()));
}

//! Sets a @ref conditional_parsing "parse define" for the current Program
/** @param def The parse define to assign
    @param val The value to assign to the define

    @par Example:
    @code{.py}
pgm.define("PRODUCTION", True);
    @endcode
 */
nothing Program::define(string def, auto val) {
   TempEncodingHelper t(def, QCS_DEFAULT, xsink);
   if (t)
      qore_program_private::runTimeDefine(p, t->getBuffer(), val.refSelf(), xsink);
}

//! Unsets a @ref conditional_parsing "parse define" for the current Program
/** @param def The name of the define to undefine; if the given define is not defined anyway, the operation is ignored

    @par Example:
    @code{.py}
pgm.undefine("PRODUCTION");
    @endcode
 */
nothing Program::undefine(string def) {
   TempEncodingHelper t(def, QCS_DEFAULT, xsink);
   if (t)
      qore_program_private::runTimeUnDefine(p, t->getBuffer(), xsink);
}

//! Returns @ref True if the given @ref conditional_parsing "parse define" is defined in the current Program (does not have to have a value defined to return @ref True), @ref False if not
/** @param def The name of the define to check

    @return @ref True if the given @ref conditional_parsing "parse define" is defined in the current Program (does not have to have a value defined to return @ref True), @ref False if not

    @par Example:
    @code{.py}
bool b = pgm.isDefined("PRODUCTION");
    @endcode

    @throw ENCODING-CONVERSION-ERROR this error is thrown if the string cannot be converted to the @ref default_encoding "default character encoding"
 */
bool Program::isDefined(string def) [flags=RET_VALUE_ONLY] {
   TempEncodingHelper t(def, QCS_DEFAULT, xsink);
   return t ? qore_program_private::runTimeIsDefined(p, t->getBuffer()) : false;
}

//! Retrieves the value of the given @ref conditional_parsing "parse define" in the current Program
/** @return the value of the given @ref conditional_parsing "parse define" in the current Program

    @par Example:
    @code{.py}
auto val = pgm.getDefine("PRODUCTION");
    @endcode

    @throw ENCODING-CONVERSION-ERROR this error is thrown if the string cannot be converted to the @ref default_encoding "default character encoding"

    @note A parse define may be defined with no value; use Program::isDefined() to check if a parse define is actually defined or not
 */
auto Program::getDefine(string def) [flags=RET_VALUE_ONLY] {
   TempEncodingHelper t(def, QCS_DEFAULT, xsink);
   return t ? qore_program_private::runTimeGetDefine(p, t->c_str()) : QoreValue();
}

//! Retrieves all @ref conditional_parsing "parse defines" in the current Program
/** @return the hash of values of all @ref conditional_parsing "parse defines" in the current Program

    @par Example:
    @code{.py}
*hash<auto> val = pgm.getAllDefines();
    @endcode

    @note A parse define may be defined with no value then values may have nothing values

    @since %Qore 0.8.13
 */
*hash<auto> Program::getAllDefines() [flags=CONSTANT] {
   return qore_program_private::runTimeGetAllDefines(p);
}

//! Get program id
/**
   @see
      @ref Qore::Program::getProgramId "Program::getProgramId"

   @since %Qore 0.8.13
 */
int Program::getProgramId() [flags=CONSTANT] {
    return p->getProgramId();
}

//! Loads a %Qore module into the Program object at run-time
/** If a feature with the same name already exists, then this feature's code is imported into the current Program object if necessary and no further action is taken.

    Note that modules providing objects resolved at parse time (classes, constants, functions, etc) must be loaded prior to parsing.

    @param name either a feature name (a module will be searched with this feature name) or a path to a module to load
    @param warning_mask the warning mask to use when loading the module; note that warnings are treated as errors

    @par Example:
    @code{.py}
pgm.loadModule("mysql");
    @endcode

    @throw LOAD-MODULE-ERROR module cannot be loaded: API incompatibility, module defines symbols already defined in the target object, etc

    @see
    - Program::loadApplyToPrivateUserModule()
    - Program::loadApplyToPrivateUserModuleWarn()
    - Program::loadApplyToUserModule()
    - Program::loadApplyToUserModuleWarn()
    - Program::loadModuleWarn()
    - Program::loadUserModuleWithProgram()
    - Program::loadUserModuleWithProgramWarn()
    - get_module_hash()
    - get_feature_list()

    @since
    - %Qore 0.8.7 introduced this method
    - %Qore 0.9 added the \c warning_mask parameter
*/
Program::loadModule(string name, int warning_mask = WARN_MODULES) [dom=MODULES] {
    QMM.runTimeLoadModule(*xsink, *xsink, name->c_str(), p, nullptr, QMLO_NONE, warning_mask);
}

//! Loads a %Qore module into the Program object at run-time
/** If a feature with the same name already exists, then this feature's code is imported into the current Program object if necessary and no further action is taken.

    Note that modules providing objects resolved at parse time (classes, constants, functions, etc) must be loaded prior to parsing.

    @param name either a feature name (a module will be searched with this feature name) or a path to a module to load
    @param warning_mask the warning mask to use when loading the module; any warnings will be returned in the return value

    @return any warnings found while parsing the module (if the module is a user module)

    @par Example:
    @code{.py}
pgm.loadModule("mysql");
    @endcode

    @throw LOAD-MODULE-ERROR module cannot be loaded: API incompatibility, module defines symbols already defined in the target object, etc

    @see
    - Program::loadApplyToPrivateUserModule()
    - Program::loadApplyToPrivateUserModuleWarn()
    - Program::loadApplyToUserModule()
    - Program::loadApplyToUserModuleWarn()
    - Program::loadModule()
    - Program::loadUserModuleWithProgram()
    - Program::loadUserModuleWithProgramWarn()
    - get_module_hash()
    - get_feature_list()

    @since %Qore 0.9 introduced this method
*/
*hash<ExceptionInfo> Program::loadModuleWarn(string name, int warning_mask = WARN_MODULES) [dom=MODULES] {
    ExceptionSink wsink;
    QMM.runTimeLoadModule(*xsink, wsink, name->c_str(), p, nullptr, QMLO_NONE, warning_mask);
    if (!*xsink && wsink) {
        return wsink.catchException()->makeExceptionObjectAndDelete(xsink);
    }
    return QoreValue();
}

//! Loads a %Qore user module into the Program object at run-time using the given @ref Qore::Program "Program" object as the container for the user module code
/** This method allows a user module to be loaded with a custom API already present in the user module's @ref Qore::Program "Program" container.
    If a feature with the same name already exists, then this feature's code is imported into the current Program object if necessary and no further action is taken.

    Note that modules providing objects resolved at parse time (classes, constants, functions, etc) must be loaded prior to parsing.

    @par Example:
    @code{.py}
pgm.loadUserModuleWithProgram("MyModule", p);
    @endcode

    @param name the name or path of the user module to load
    @param pgm the @ref Qore::Program "Program" object to use as a container for the new user module, presumably this has a custom API that the user module can use; note that after this call the @ref Qore::Program "Program" object will be owned by the user module, therefore the object itself will no longer be valid and any accesses to the object after this call will result in an exception
    @param warning_mask the warning mask to use when loading the module; note that warnings are treated as errors

    @throw LOAD-MODULE-ERROR module cannot be loaded: binary modules cannot be loaded in @ref Qore::Program "Program" containers, module defines symbols already defined in the target object, etc

    @see
    - Program::loadApplyToPrivateUserModule()
    - Program::loadApplyToPrivateUserModuleWarn()
    - Program::loadApplyToUserModule()
    - Program::loadApplyToUserModuleWarn()
    - Program::loadModule()
    - Program::loadModuleWarn()
    - Program::loadUserModuleWithProgramWarn()
    - get_module_hash()
    - get_feature_list()

    @since
    - %Qore 0.8.12 introduced this method
    - %Qore 0.9 added the \c warning_mask parameter
*/
Program::loadUserModuleWithProgram(string name, !Qore::Program[QoreProgram] pgm, int warning_mask = WARN_MODULES) [dom=MODULES,INJECTION] {
    QMM.runTimeLoadModule(*xsink, *xsink, name->c_str(), p, pgm, QMLO_INJECT, warning_mask);
}

//! Loads a %Qore user module into the Program object at run-time using the given @ref Qore::Program "Program" object as the container for the user module code
/** This method allows a user module to be loaded with a custom API already present in the user module's @ref Qore::Program "Program" container.
    If a feature with the same name already exists, then this feature's code is imported into the current Program object if necessary and no further action is taken.

    Note that modules providing objects resolved at parse time (classes, constants, functions, etc) must be loaded prior to parsing.

    @par Example:
    @code{.py}
pgm.loadUserModuleWithProgram("MyModule", p);
    @endcode

    @param name the name or path of the user module to load
    @param pgm the @ref Qore::Program "Program" object to use as a container for the new user module, presumably this has a custom API that the user module can use; note that after this call the @ref Qore::Program "Program" object will be owned by the user module, therefore the object itself will no longer be valid and any accesses to the object after this call will result in an exception
    @param warning_mask the warning mask to use when loading the module; any warnings will be returned in the return value

    @return any warnings found while parsing the module (if the module is a user module)

    @throw LOAD-MODULE-ERROR module cannot be loaded: binary modules cannot be loaded in @ref Qore::Program "Program" containers, module defines symbols already defined in the target object, etc

    @see
    - Program::loadApplyToPrivateUserModule()
    - Program::loadApplyToPrivateUserModuleWarn()
    - Program::loadApplyToUserModule()
    - Program::loadApplyToUserModuleWarn()
    - Program::loadModule()
    - Program::loadModuleWarn()
    - Program::loadUserModuleWithProgram()
    - get_module_hash()
    - get_feature_list()

    @since %Qore 0.9 introduced this method
*/
*hash<ExceptionInfo> Program::loadUserModuleWithProgramWarn(string name, !Qore::Program[QoreProgram] pgm, int warning_mask = WARN_MODULES) [dom=MODULES,INJECTION] {
    ExceptionSink wsink;
    QMM.runTimeLoadModule(*xsink, wsink, name->c_str(), p, pgm, QMLO_INJECT, warning_mask);
    if (!*xsink && wsink) {
        return wsink.catchException()->makeExceptionObjectAndDelete(xsink);
    }
    return QoreValue();
}

//! Loads a %Qore user module into the given Program container at run-time; after this call the Program object is owned by the user module and is no longer accessible in the object used for the call
/** This method allows a user module to be loaded with a custom API already present in the user module's @ref Qore::Program "Program" container.

    @param name the name or path of the user module to load
    @param reinject if @ref True "True" then the module will be reloaded even if already loaded previously; if the module was not already loaded then it is loaded with this call
    @param warning_mask the warning mask to use when loading the module; note that warnings are treated as errors
    @param reexport reexport symbols from the imported module (only valid when used in a user module)

    @throw LOAD-MODULE-ERROR module cannot be loaded: binary modules cannot be loaded in @ref Qore::Program "Program" containers, etc

    @note
    - it is not an error to set the \a reinject argument to @ref True "True" when the given module has not already been loaded
    - after this call the Program object will be owned by the user module and will no longer be valid, so any accesses to the object after this call will result in an exception

    @see
    - Program::loadApplyToPrivateUserModule()
    - Program::loadApplyToPrivateUserModuleWarn()
    - Program::loadApplyToUserModuleWarn()
    - Program::loadModule()
    - Program::loadModuleWarn()
    - Program::loadUserModuleWithProgram()
    - Program::loadUserModuleWithProgramWarn()
    - get_module_hash()
    - get_feature_list()

    @since
    - %Qore 0.9 added the \c warning_mask parameter
    - %Qore 0.9.4 added the \c reexport parameter
 */
Program::loadApplyToUserModule(string name, *softbool reinject, int warning_mask = WARN_MODULES, bool reexport = False) [dom=MODULES,INJECTION] {
    // remove the QoreProgram private data from the object
    printd(5, "Program::loadApplyToUserModule pgm: %p, pgmid: %d, self: %p\n", p, p->getProgramId(), self);
#ifdef DEBUG
    QoreProgram* pgm = (QoreProgram*)
#endif
    qore_object_private::get(*self)->getAndRemovePrivateData(CID_PROGRAM, xsink);
#ifdef DEBUG
    assert(pgm == p);
#endif
    if (!*xsink) {
        QMM.runTimeLoadModule(*xsink, *xsink, name->c_str(), 0, p, QMLO_INJECT | (reinject ? QMLO_REINJECT : 0),
            warning_mask, reexport);
        // getAndRemovePrivateData removed private data, so destructor is not executed. We must remove reference to this object
        //p->unregisterQoreObject(self, xsink);
        self->doDelete(xsink);
    }
}

//! Loads a %Qore user module into the given Program container at run-time; after this call the Program object is owned by the user module and is no longer accessible in the object used for the call
/** This method allows a user module to be loaded with a custom API already present in the user module's @ref Qore::Program "Program" container.

    @param name the name or path of the user module to load
    @param reinject if @ref True "True" then the module will be reloaded even if already loaded previously; if the module was not already loaded then it is loaded with this call
    @param warning_mask the warning mask to use when loading the module; any warnings will be returned in the return value
    @param reexport reexport symbols from the imported module (only valid when used in a user module)

    @return any warnings found while parsing the module (if the module is a user module)

    @throw LOAD-MODULE-ERROR module cannot be loaded: binary modules cannot be loaded in @ref Qore::Program "Program" containers, etc

    @note
    - it is not an error to set the \a reinject argument to @ref True "True" when the given module has not already been loaded
    - after this call the Program object will be owned by the user module and will no longer be valid, so any accesses to the object after this call will result in an exception

    @see
    - Program::loadApplyToPrivateUserModule()
    - Program::loadApplyToPrivateUserModuleWarn()
    - Program::loadApplyToUserModule()
    - Program::loadModule()
    - Program::loadModuleWarn()
    - Program::loadUserModuleWithProgram()
    - Program::loadUserModuleWithProgramWarn()
    - get_module_hash()
    - get_feature_list()

    @since
    - %Qore 0.9 added the \c warning_mask parameter
    - %Qore 0.9.4 added the \c reexport parameter
 */
*hash<ExceptionInfo> Program::loadApplyToUserModuleWarn(string name, *softbool reinject, int warning_mask = WARN_MODULES, bool reexport = False) [dom=MODULES,INJECTION] {
    // remove the QoreProgram private data from the object
    printd(5, "Program::loadApplyToUserModule pgm: %p, pgmid: %d, self: %p\n", p, p->getProgramId(), self);
#ifdef DEBUG
    QoreProgram* pgm = (QoreProgram*)
#endif
    qore_object_private::get(*self)->getAndRemovePrivateData(CID_PROGRAM, xsink);
#ifdef DEBUG
    assert(pgm == p);
#endif
    if (!*xsink) {
        ExceptionSink wsink;
        QMM.runTimeLoadModule(*xsink, wsink, name->c_str(), 0, p, QMLO_INJECT | (reinject ? QMLO_REINJECT : 0),
            warning_mask, reexport);
        // getAndRemovePrivateData removed private data, so destructor is not executed. We must remove reference to this object
        //p->unregisterQoreObject(self, xsink);
        self->doDelete(xsink);
        if (!*xsink && wsink) {
            return wsink.catchException()->makeExceptionObjectAndDelete(xsink);
        }
    }
    return QoreValue();
}

//! Loads a %Qore user module privately into the given Program container at run-time; after this call the Program object is owned by the user module and is no longer accessible in the object used for the call
/** This method allows a user module to be privately loaded with a custom API already present in the user module's @ref Qore::Program "Program" container.

    @param name the name or path of the user module to load
    @param warning_mask the warning mask to use when loading the module; note that warnings are treated as errors

    @throw LOAD-MODULE-ERROR module cannot be loaded: binary modules cannot be loaded in @ref Qore::Program "Program" containers, etc

    @note
    - after this call the Program object will be owned by the user module and will no longer be valid, so any accesses to the object after this call will result in an exception
    - this module will not be available for use in other Program objects; this method is useful for loading modules that register themselves with an injected API in code that does not have @ref Qore::PO_ALLOW_INJECTION set or where the module's functionality should be protected from other %Qore code

    @see
    - Program::loadApplyToPrivateUserModuleWarn()
    - Program::loadApplyToUserModule()
    - Program::loadApplyToUserModuleWarn()
    - Program::loadModule()
    - Program::loadModuleWarn()
    - Program::loadUserModuleWithProgram()
    - Program::loadUserModuleWithProgramWarn()
    - get_module_hash()
    - get_feature_list()

    @since %Qore 0.9 added the \c warning_mask parameter
 */
Program::loadApplyToPrivateUserModule(string name, int warning_mask = WARN_MODULES) [dom=MODULES] {
    // remove the QoreProgram private data from the object
    printd(5, "Program::loadApplyToPrivateUserModule pgm: %p, pgmid: %d, self: %p\n", p, p->getProgramId(), self);
#ifdef DEBUG
    QoreProgram* pgm = (QoreProgram*)
#endif
    qore_object_private::get(*self)->getAndRemovePrivateData(CID_PROGRAM, xsink);
#ifdef DEBUG
    assert(pgm == p);
#endif
    if (!*xsink) {
        QMM.runTimeLoadModule(*xsink, *xsink, name->c_str(), 0, p, QMLO_INJECT | QMLO_PRIVATE, warning_mask);
        // getAndRemovePrivateData removed private data, so destructor is not executed. We must remove reference to this object
        //p->unregisterQoreObject(self, xsink);
        self->doDelete(xsink);
    }
}

//! Loads a %Qore user module privately into the given Program container at run-time; after this call the Program object is owned by the user module and is no longer accessible in the object used for the call
/** This method allows a user module to be privately loaded with a custom API already present in the user module's @ref Qore::Program "Program" container.

    @param name the name or path of the user module to load
    @param warning_mask the warning mask to use when loading the module; any warnings will be returned in the return value

    @return any warnings found while parsing the module (if the module is a user module)

    @throw LOAD-MODULE-ERROR module cannot be loaded: binary modules cannot be loaded in @ref Qore::Program "Program" containers, etc

    @note
    - after this call the Program object will be owned by the user module and will no longer be valid, so any accesses to the object after this call will result in an exception
    - this module will not be available for use in other Program objects; this method is useful for loading modules that register themselves with an injected API in code that does not have @ref Qore::PO_ALLOW_INJECTION set or where the module's functionality should be protected from other %Qore code

    @see
    - Program::loadApplyToPrivateUserModuleWarn()
    - Program::loadApplyToUserModule()
    - Program::loadApplyToUserModuleWarn()
    - Program::loadModule()
    - Program::loadModuleWarn()
    - Program::loadUserModuleWithProgram()
    - Program::loadUserModuleWithProgramWarn()
    - get_module_hash()
    - get_feature_list()

    @since %Qore 0.9 added the \c warning_mask parameter
 */
*hash<ExceptionInfo> Program::loadApplyToPrivateUserModuleWarn(string name, int warning_mask = WARN_MODULES) [dom=MODULES] {
    // remove the QoreProgram private data from the object
    printd(5, "Program::loadApplyToPrivateUserModule pgm: %p, pgmid: %d, self: %p\n", p, p->getProgramId(), self);
#ifdef DEBUG
    QoreProgram* pgm = (QoreProgram*)
#endif
    qore_object_private::get(*self)->getAndRemovePrivateData(CID_PROGRAM, xsink);
#ifdef DEBUG
    assert(pgm == p);
#endif
    if (!*xsink) {
        ExceptionSink wsink;
        QMM.runTimeLoadModule(*xsink, wsink, name->c_str(), 0, p, QMLO_INJECT | QMLO_PRIVATE, warning_mask);
        // getAndRemovePrivateData removed private data, so destructor is not executed. We must remove reference to this object
        //p->unregisterQoreObject(self, xsink);
        self->doDelete(xsink);
        if (!*xsink && wsink) {
            return wsink.catchException()->makeExceptionObjectAndDelete(xsink);
        }
    }
    return QoreValue();
}

//! imports system classes into a Program container at runtime; will throw an exception if the Program was not created with @ref Qore::PO_NO_INHERIT_SYSTEM_CLASSES
/** @par Example:
    @code{.py}
pgm.importSystemClasses();
    @endcode

    @throw IMPORT-SYSTEM-API-ERROR objects already imported; parse options are locked on the Program object; conflicting object found; system API incorrectly imported; in case this exception is raised, the Program object will not contain the full system API and is most likely unusable

    @note removes @ref Qore::PO_NO_INHERIT_SYSTEM_CLASSES from the parse option mask after a successful completion

    @since %Qore 0.8.12
*/
Program::importSystemClasses() {
   qore_program_private::runtimeImportSystemClasses(*p, xsink);
}

//! imports system hashdecls into a Program container at runtime; will throw an exception if the Program was not created with @ref Qore::PO_NO_INHERIT_SYSTEM_HASHDECLS
/** @par Example:
    @code{.py}
pgm.importSystemHashDecls();
    @endcode

    @throw IMPORT-SYSTEM-API-ERROR objects already imported; parse options are locked on the Program object; conflicting object found; system API incorrectly imported; in case this exception is raised, the Program object will not contain the full system API and is most likely unusable

    @note removes @ref Qore::PO_NO_INHERIT_SYSTEM_HASHDECLS from the parse option mask after a successful completion

    @since %Qore 0.8.13
*/
Program::importSystemHashDecls() {
   qore_program_private::runtimeImportSystemHashDecls(*p, xsink);
}

//! imports system constants into a Program container at runtime; will throw an exception if the Program was not created with @ref Qore::PO_NO_INHERIT_SYSTEM_CONSTANTS
/** @par Example:
    @code{.py}
pgm.importSystemConstants();
    @endcode

    @throw IMPORT-SYSTEM-API-ERROR objects already imported; parse options are locked on the Program object; conflicting object found; system API incorrectly imported; in case this exception is raised, the Program object will not contain the full system API and is most likely unusable

    @note removes @ref Qore::PO_NO_INHERIT_SYSTEM_CONSTANTS from the parse option mask after a successful completion

    @since %Qore 0.8.12
*/
Program::importSystemConstants() {
   qore_program_private::runtimeImportSystemConstants(*p, xsink);
}

//! imports system functions into a Program container at runtime; will throw an exception if the Program was not created with @ref Qore::PO_NO_INHERIT_SYSTEM_FUNC_VARIANTS
/** @par Example:
    @code{.py}
pgm.importSystemFunctions();
    @endcode

    @throw IMPORT-SYSTEM-API-ERROR objects already imported; parse options are locked on the Program object; conflicting object found; system API incorrectly imported; in case this exception is raised, the Program object will not contain the full system API and is most likely unusable

    @note removes @ref Qore::PO_NO_INHERIT_SYSTEM_FUNC_VARIANTS from the parse option mask after a successful completion

    @since %Qore 0.8.12
*/
Program::importSystemFunctions() {
   qore_program_private::runtimeImportSystemFunctions(*p, xsink);
}

//! imports system classes and functions into a Program container at runtime; will throw an exception if the Program was not created with @ref Qore::PO_NO_INHERIT_SYSTEM_FUNC_VARIANTS, @ref Qore::PO_NO_INHERIT_SYSTEM_HASHDECLS, @ref Qore::PO_NO_INHERIT_SYSTEM_CONSTANTS, and @ref Qore::PO_NO_INHERIT_SYSTEM_CLASSES
/** @par Example:
    @code{.py}
pgm.importSystemApi();
    @endcode

    @throw IMPORT-SYSTEM-API-ERROR objects already imported; parse options are locked on the Program object; conflicting object found; system API incorrectly imported; in case this exception is raised, the Program object will not contain the full system API and is most likely unusable

    @note removes @ref Qore::PO_NO_INHERIT_SYSTEM_CLASSES, @ref Qore::PO_NO_INHERIT_SYSTEM_HASHDECLS, @ref Qore::PO_NO_INHERIT_SYSTEM_CONSTANTS, and @ref Qore::PO_NO_INHERIT_SYSTEM_FUNC_VARIANTS from the parse option mask after a successful completion

    @since %Qore 0.8.12
*/
Program::importSystemApi() {
   qore_program_private::runtimeImportSystemApi(*p, xsink);
}

//! Sets a @ref call_reference "call reference" or @ref closure "closure" to run every time a new thread is started
/** This code can be used to initialize @ref threading_and_variables "global thread-local variables", for example.

    @param init a @ref call_reference "call reference" or @ref closure "closure" to run every time a new thread is started or @ref nothing to clear any thread initialization code

    @return @ref True if there was already user initialization code set, @ref False if not

    @par Example:
    @code{.py}
pgm.setThreadInit(sub () { var = 123; });
    @endcode

    @note the code will be run for all new threads, but is not run by this method for the current thread

    @see @ref Qore::set_thread_init()

    @since %Qore 0.8.13
*/
bool Program::setThreadInit(*code init) [dom=THREAD_CONTROL] {
   return qore_program_private::setThreadInit(*p, init, xsink);
}

//! returns a hash of global variables
/** @par Example:
    @code{.py}
hash<auto> h = pgm.getGlobalVars();
    @endcode

    @return a hash of global variables and their values

    @see @ref Qore::get_global_vars()

    @since %Qore 0.8.13
 */
hash<auto> Program::getGlobalVars() [flags=CONSTANT] {
   return p->getGlobalVars();
}

//! set the value of a global variable
/** @par Example:
    @code
pgm.setGlobalVarValue("a", 1);
    @endcode

    @param name the name of the variable
    @param value the value to assign

    @throw UNKNOWN-VARIABLE the variable is not a global variable

    @note other exceptions could be thrown if the value cannot be assigned to the given variable

    @see @ref Qore::set_global_var_value()

    @since %Qore 0.8.13
 */
nothing Program::setGlobalVarValue(string name, auto value) {
   p->setGlobalVarValue(name->c_str(), value.refSelf(), xsink);
}

//! finds all variants of a function or class method and returns a list of the results
/** @param function the function or class method name; may also be namespace-justified

    @return a list of hashes for each resolved name; when matched, each hash element has the following keys:
    - \c desc: a string description of the call which includes the name and the full text call signature
    - \c params: a QoreListNode object that gives the params in a format that can be used by @ref Qore::ProgramControl::findFunctionStatementId "ProgramControl::findFunctionStatementId"

    @throw PROGRAM-ERROR thrown if the function cannot be found

    @since %Qore 0.8.13

    @note the <a href="../../modules/reflection/html/index.html">reflection</a> module supercedes this functionality
*/
list<hash<auto>> Program::findFunctionVariants(string function) {
    ReferenceHolder<QoreListNode> vl(p->runtimeFindCallVariants(function->c_str(), xsink), xsink);
    if (!vl)
        return xsink->raiseException("PROGRAM-ERROR", "cannot find any function \"%s\"", function->c_str());
    return vl.release();
}

//! returns a list of thread IDs active in this Program
/** @par Example:
    @code
list<int> l = pgm.getThreadList();
    @endcode

    @return a list of thread IDs active in this Program

    @since %Qore 0.8.13
*/
list<int> Program::getThreadList() [flags=CONSTANT] {
    return p->getThreadList();
}

//! Get @ref Qore::ProgramControl "ProgramControl"
/**
 */
ProgramControl Program::getProgram() [flags=RET_VALUE_ONLY; dom=DEBUGGER] {
   return QoreProgram::getQoreObject(p);
}

//! issues a module command for the given module; the module is loaded into the current %Program object if it is not already present
/** @par Example:
    @code{.py}
pgm.issueModuleCmd("jni", "define-class " + java_class_path_name + " " + byte_code.toBase64());
    @endcode

    @param module the module name
    @param cmd the command string to execute to be parsed by the module

    @throw PARSE-COMMAND-ERROR the module does not support commands

    @note command errors can result in other module- and command-specific exceptions being thrown; see
    documentation for the module and command in question for more details

    @since %Qore 0.9
*/
nothing Program::issueModuleCmd(string module, string cmd) {
    TempEncodingHelper tmp_module(module, QCS_DEFAULT, xsink);
    if (!tmp_module) {
        return QoreValue();
    }

    TempEncodingHelper tmp_cmd(cmd, QCS_DEFAULT, xsink);
    if (!tmp_cmd) {
        return QoreValue();
    }

    QMM.issueRuntimeCmd(tmp_module->c_str(), p, **tmp_cmd, xsink);
}

//! resolve the string as a call reference in the given Program
/** @param identifier the string to resolve; function or static class method, can include namespace path

    @return the call reference to the given function or static method; the string is resolved as follows:
    - if in a class method, a non-static method lookup is made
    - if not found, then a static method lookup is made
    - if not found, then a function lookup is made
    - if not found, an \c CALL-REFERENCE-ERROR exception is thrown

    @throw CALL-REFERENCE-ERROR cannot resolve call reference; method not accessible in the calling context

    @note
    - use <object>::getCallReference() to get call references to non-public class methods; this builtin function
      will always run without any class context, however pseudo-methods run with the class context of the caller, which
      enables it to access any accessible private method
    - call references to non-static methods contain a weak reference to the object; the lifetime of the object is not
      extended by the call reference

    @see
    - @ref Qore::get_call_reference()
    - <object>::getCallReference()

    @since %Qore 0.9.4
 */
code Program::getCallReference(string identifier) [flags=RET_VALUE_ONLY] {
    QoreProgramContextHelper pch(p);
    return get_call_reference_intern(runtime_get_stack_object(), identifier, xsink);
}

//! returns an expression object for the given source code
/** @param source The source to the expression
    @param label the label for the expression

    @since %Qore 0.9.5
*/
Expression Program::getExpression(string source, string label) {
    ReferenceHolder<QoreExpression> exp(new QoreExpression(*p, *source, *label, xsink), xsink);
    if (!*xsink) {
        return new QoreObject(QC_EXPRESSION, p, exp.release());
    }
    return QoreValue();
}

//! Returns a list of strings of the builtin and module-supplied features of Qore
/** @return a list of strings of the builtin and module-supplied features of Qore

    @since %Qore 1.0
*/
list<string> Program::getFeatureList() [flags=RET_VALUE_ONLY] {
    return p->getFeatureList();
}
