--- linuxsampler/trunk/src/scriptvm/common.h 2017/05/26 18:30:42 3221 +++ linuxsampler/trunk/src/scriptvm/common.h 2019/08/23 11:44:00 3561 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2017 Christian Schoenebeck + * Copyright (c) 2014-2019 Christian Schoenebeck * * http://www.linuxsampler.org * @@ -9,7 +9,8 @@ // This header defines data types shared between the VM core implementation // (inside the current source directory) and other parts of the sampler -// (located at other source directories). +// (located at other source directories). It also acts as public API of the +// Real-Time script engine for other applications. #ifndef LS_INSTR_SCRIPT_PARSER_COMMON_H #define LS_INSTR_SCRIPT_PARSER_COMMON_H @@ -22,6 +23,26 @@ namespace LinuxSampler { /** + * Native data type used by the script engine both internally, as well as + * for all integer data types used by scripts (i.e. for all $foo variables + * in NKSP scripts). Note that this is different from the original KSP which + * is limited to 32 bit for integer variables in KSP scripts. + */ + typedef int64_t vmint; + + /** + * Native data type used internally by the script engine for all unsigned + * integer types. This type is currently not exposed to scripts. + */ + typedef uint64_t vmuint; + + /** + * Native data type used internally by the script engine for floating point + * data types. This type is currently not exposed to scripts. + */ + typedef float vmfloat; + + /** * Identifies the type of a noteworthy issue identified by the script * parser. That's either a parser error or parser warning. */ @@ -80,19 +101,127 @@ * * Identifies one of the possible event handler callback types defined by * the NKSP script language. + * + * IMPORTANT: this type is forced to be emitted as int32_t type ATM, because + * that's the native size expected by the built-in instrument script + * variable bindings (see occurrences of VMInt32RelPtr and DECLARE_VMINT + * respectively. A native type mismatch between the two could lead to + * undefined behavior! Background: By definition the C/C++ compiler is free + * to choose a bit size for individual enums which it might find + * appropriate, which is usually decided by the compiler according to the + * biggest enum constant value defined (in practice it is usually 32 bit). */ - enum VMEventHandlerType_t { + enum VMEventHandlerType_t : int32_t { VM_EVENT_HANDLER_INIT, ///< Initilization event handler, that is script's "on init ... end on" code block. VM_EVENT_HANDLER_NOTE, ///< Note event handler, that is script's "on note ... end on" code block. VM_EVENT_HANDLER_RELEASE, ///< Release event handler, that is script's "on release ... end on" code block. VM_EVENT_HANDLER_CONTROLLER, ///< Controller event handler, that is script's "on controller ... end on" code block. }; + /** + * All metric unit prefixes (actually just scale factors) supported by this + * script engine. + */ + enum MetricPrefix_t { + VM_NO_PREFIX = 0, ///< = 1 + VM_KILO, ///< = 10^3, short 'k' + VM_HECTO, ///< = 10^2, short 'h' + VM_DECA, ///< = 10, short 'da' + VM_DECI, ///< = 10^-1, short 'd' + VM_CENTI, ///< = 10^-2, short 'c' (this is also used for tuning "cents") + VM_MILLI, ///< = 10^-3, short 'm' + VM_MICRO, ///< = 10^-6, short 'u' + }; + + /** + * All measurement unit types supported by this script engine. + * + * @e Note: there is no standard unit "cents" here (for pitch/tuning), use + * @c VM_CENTI for the latter instad. That's because the commonly cited + * "cents" unit is actually no measurement unit type but rather a metric + * unit prefix. + * + * @see MetricPrefix_t + */ + enum StdUnit_t { + VM_NO_UNIT = 0, ///< No unit used, the number is just an abstract number. + VM_SECOND, ///< Measuring time. + VM_HERTZ, ///< Measuring frequency. + VM_BEL, ///< Measuring relation between two energy levels (in logarithmic scale). Since we are using it for accoustics, we are always referring to A-weighted Bels (i.e. dBA). + }; + // just symbol prototyping class VMIntExpr; class VMStringExpr; class VMIntArrayExpr; class VMStringArrayExpr; + class VMParserContext; + + /** @brief Virtual machine measuring unit. + * + * Abstract base class representing standard measurement units throughout + * the script engine. These might be i.e. "dB" (deci Bel) for loudness, + * "Hz" (Hertz) for frequencies or "s" for "seconds". + * + * Originally the script engine only supported abstract integer values for + * controlling any synthesis parameter or built-in function argument or + * variable. Under certain situations it makes sense though for an + * instrument script author to provide values in real, standard measurement + * units, for example setting the frequency of some LFO directly to "20Hz". + * Hence support for standard units in scripts was added as an extension to + * the NKSP script engine. + */ + class VMUnit { + public: + /** + * Returns the metric prefix of this unit. A metric prefix essentially + * is just a mathematical scale factor that should be applied to the + * number associated with the measurement unit. Usually a unit either + * has exactly none or one prefix, but note that there might also be + * units with more than one prefix, for example mdB (mili deci bel) + * is used sometimes which has two prefixes. This is an exception though + * and more than two prefixes is currently not supported by the script + * engine. + * + * Start iterating over the prefixes of this unit by passing @c 0 as + * argument to this method. The prefixes are terminated with return + * value VM_NO_PREFIX being always the last element. + * + * @param i - index of prefix + * @returns prefix of requested index or VM_NO_PREFIX otherwise + * @see unitFactor() + */ + virtual MetricPrefix_t unitPrefix(vmuint i) const = 0; + + /** + * Conveniently returns the final mathematical factor that should be + * multiplied against the number associated with this unit. This factor + * results from the sequence of metric prefixes of this unit. + * + * @see unitPrefix() + */ + vmfloat unitFactor() const; + + /** + * This is the actual fundamental measuring unit base type of this unit, + * which might be either Hertz, second or Bel. + * + * @returns standard unit type identifier or VM_NO_UNIT if no unit used + */ + virtual StdUnit_t unitType() const = 0; + + /** + * Returns the actual mathematical factor represented by the passed + * @a prefix argument. + */ + static vmfloat unitFactor(MetricPrefix_t prefix); + + /** + * Returns the actual mathematical factor represented by the passed + * two @a prefix1 and @a prefix2 arguments. + */ + static vmfloat unitFactor(MetricPrefix_t prefix1, MetricPrefix_t prefix2); + }; /** @brief Virtual machine expression * @@ -207,18 +336,64 @@ * abstract method evalInt() to return the actual integer result value of * the expression. */ - class VMIntExpr : virtual public VMExpr { + class VMIntExpr : virtual public VMExpr, virtual public VMUnit { public: /** * Returns the result of this expression as integer (scalar) value. * This abstract method must be implemented by deriving classes. */ - virtual int evalInt() = 0; + virtual vmint evalInt() = 0; + + /** + * Returns the result of this expression as integer (scalar) value and + * thus behaves similar to the previous method, however this overridden + * method automatically takes unit prefixes into account and returns a + * value corresponding to the expected given unit @a prefix. + * + * @param prefix - default measurement unit prefix expected by caller + */ + vmint evalInt(MetricPrefix_t prefix); + + /** + * This method behaves like the previous method, just that it takes + * a default measurement prefix with two elements (i.e. "milli cents" + * for tuning). + */ + vmint evalInt(MetricPrefix_t prefix1, MetricPrefix_t prefix2); /** * Returns always INT_EXPR for instances of this class. */ ExprType_t exprType() const OVERRIDE { return INT_EXPR; } + + /** + * Returns @c true if the value of this expression should be applied + * as final value to the respective destination synthesis chain + * parameter. + * + * This property is somewhat special and dedicated for the purpose of + * this expression's integer value to be applied as parameter to the + * synthesis chain of the sampler (i.e. for altering a filter cutoff + * frequency). Now historically and by default all values of scripts are + * applied relatively to the sampler's synthesis chain, that is the + * synthesis parameter value of a script is multiplied against other + * sources for the same synthesis parameter (i.e. an LFO or a dedicated + * MIDI controller either hard wired in the engine or defined by the + * instrument patch). So by default the resulting actual final synthesis + * parameter is a combination of all these sources. This has the + * advantage that it creates a very living and dynamic overall sound. + * + * However sometimes there are requirements by script authors where this + * is not what you want. Therefore the NKSP script engine added a + * language extension by prefixing a value in scripts with a @c ! + * character the value will be defined as being the "final" value of the + * destination synthesis parameter, so that causes this value to be + * applied exclusively, and the values of all other sources are thus + * entirely ignored by the sampler's synthesis core as long as this + * value is assigned by the script engine as "final" value for the + * requested synthesis parameter. + */ + virtual bool isFinal() const = 0; }; /** @brief Virtual machine string expression @@ -255,7 +430,7 @@ * Returns amount of elements in this array. This abstract method must * be implemented by deriving classes. */ - virtual int arraySize() const = 0; + virtual vmint arraySize() const = 0; }; /** @brief Virtual Machine Integer Array Expression @@ -273,7 +448,7 @@ * * @param i - array element index (must be between 0 .. arraySize() - 1) */ - virtual int evalIntElement(uint i) = 0; + virtual vmint evalIntElement(vmuint i) = 0; /** * Changes the current value of an element (given by array element @@ -282,7 +457,7 @@ * @param i - array element index (must be between 0 .. arraySize() - 1) * @param value - new integer scalar value to be assigned to that array element */ - virtual void assignIntElement(uint i, int value) = 0; + virtual void assignIntElement(vmuint i, vmint value) = 0; /** * Returns always INT_ARR_EXPR for instances of this class. @@ -304,7 +479,7 @@ * Returns the amount of arguments going to be passed to the script * function. */ - virtual int argsCount() const = 0; + virtual vmint argsCount() const = 0; /** * Returns the respective argument (requested by argument index @a i) of @@ -314,7 +489,7 @@ * * @param i - function argument index (indexed from left to right) */ - virtual VMExpr* arg(int i) = 0; + virtual VMExpr* arg(vmint i) = 0; }; /** @brief Result value returned from a call to a built-in script function. @@ -378,14 +553,14 @@ * script is calling this function with less arguments, the script * parser will throw a parser error. */ - virtual int minRequiredArgs() const = 0; + virtual vmint minRequiredArgs() const = 0; /** * Maximum amount of function arguments this functions accepts. If a * script is calling this function with more arguments, the script * parser will throw a parser error. */ - virtual int maxAllowedArgs() const = 0; + virtual vmint maxAllowedArgs() const = 0; /** * Script data type of the function's @c iArg 'th function argument. @@ -401,7 +576,7 @@ * @param iArg - index of the function argument in question * (must be between 0 .. maxAllowedArgs() - 1) */ - virtual ExprType_t argType(int iArg) const = 0; + virtual ExprType_t argType(vmint iArg) const = 0; /** * This method is called by the parser to check whether arguments @@ -419,7 +594,69 @@ * @return true if the given data type would be accepted for the * respective function argument by the function */ - virtual bool acceptsArgType(int iArg, ExprType_t type) const = 0; + virtual bool acceptsArgType(vmint iArg, ExprType_t type) const = 0; + + /** + * This method is called by the parser to check whether arguments + * passed in scripts to this function are accepted by this function. If + * a script calls this function with an argument's measuremnt unit type + * not accepted by this function, the parser will throw a parser error. + * + * This default implementation of this method does not accept any + * measurement unit. Deriving subclasses would override this method + * implementation in case they do accept any measurement unit for its + * function arguments. + * + * @param iArg - index of the function argument in question + * (must be between 0 .. maxAllowedArgs() - 1) + * @param type - standard measurement unit data type used for this + * function argument by currently parsed script + * @return true if the given standard measurement unit type would be + * accepted for the respective function argument by the function + */ + virtual bool acceptsArgUnitType(vmint iArg, StdUnit_t type) const; + + /** + * This method is called by the parser to check whether arguments + * passed in scripts to this function are accepted by this function. If + * a script calls this function with a metric unit prefix and metric + * prefixes are not accepted for that argument by this function, then + * the parser will throw a parser error. + * + * This default implementation of this method does not accept any + * metric prefix. Deriving subclasses would override this method + * implementation in case they do accept any metric prefix for its + * function arguments. + * + * @param iArg - index of the function argument in question + * (must be between 0 .. maxAllowedArgs() - 1) + * + * @return true if a metric prefix would be accepted for the respective + * function argument by this function + * + * @see MetricPrefix_t + */ + virtual bool acceptsArgUnitPrefix(vmint iArg) const; + + /** + * This method is called by the parser to check whether arguments + * passed in scripts to this function are accepted by this function. If + * a script calls this function with an argument that is declared to be + * a "final" value and this is not accepted by this function, the parser + * will throw a parser error. + * + * This default implementation of this method does not accept a "final" + * value. Deriving subclasses would override this method implementation + * in case they do accept a "final" value for its function arguments. + * + * @param iArg - index of the function argument in question + * (must be between 0 .. maxAllowedArgs() - 1) + * @return true if a "final" value would be accepted for the respective + * function argument by the function + * + * @see VMIntExpr::isFinal() + */ + virtual bool acceptsArgFinal(vmint iArg) const; /** * This method is called by the parser to check whether some arguments @@ -434,7 +671,7 @@ * @param iArg - index of the function argument in question * (must be between 0 .. maxAllowedArgs() - 1) */ - virtual bool modifiesArg(int iArg) const = 0; + virtual bool modifiesArg(vmint iArg) const = 0; /** * Implements the actual function execution. This exec() method is @@ -468,25 +705,85 @@ /** @brief Virtual machine relative pointer. * - * POD base of VMIntRelPtr and VMInt8RelPtr structures. Not intended to be - * used directly. Use VMIntRelPtr or VMInt8RelPtr instead. + * POD base of VMInt64RelPtr, VMInt32RelPtr and VMInt8RelPtr structures. Not + * intended to be used directly. Use VMInt64RelPtr, VMInt32RelPtr, + * VMInt8RelPtr instead. * - * @see VMIntRelPtr, VMInt8RelPtr + * @see VMInt64RelPtr, VMInt32RelPtr, VMInt8RelPtr */ struct VMRelPtr { void** base; ///< Base pointer. - int offset; ///< Offset (in bytes) relative to base pointer. + vmint offset; ///< Offset (in bytes) relative to base pointer. bool readonly; ///< Whether the pointed data may be modified or just be read. }; - /** @brief Pointer to built-in VM integer variable (of C/C++ type int). + /** @brief Pointer to built-in VM integer variable (interface class). + * + * This class acts as an abstract interface to all built-in integer script + * variables, independent of their actual native size (i.e. some built-in + * script variables are internally using a native int size of 64 bit or 32 + * bit or 8 bit). The virtual machine is using this interface class instead + * of its implementing descendants (VMInt64RelPtr, VMInt32RelPtr, + * VMInt8RelPtr) in order for the virtual machine for not being required to + * handle each of them differently. + */ + struct VMIntPtr { + virtual vmint evalInt() = 0; + virtual void assign(vmint i) = 0; + virtual bool isAssignable() const = 0; + }; + + /** @brief Pointer to built-in VM integer variable (of C/C++ type int64_t). + * + * Used for defining built-in 64 bit integer script variables. + * + * @b CAUTION: You may only use this class for pointing to C/C++ variables + * of type "int64_t" (thus being exactly 64 bit in size). If the C/C++ int + * variable you want to reference is only 32 bit in size then you @b must + * use VMInt32RelPtr instead! Respectively for a referenced native variable + * with only 8 bit in size you @b must use VMInt8RelPtr instead! + * + * For efficiency reasons the actual native C/C++ int variable is referenced + * by two components here. The actual native int C/C++ variable in memory + * is dereferenced at VM run-time by taking the @c base pointer dereference + * and adding @c offset bytes. This has the advantage that for a large + * number of built-in int variables, only one (or few) base pointer need + * to be re-assigned before running a script, instead of updating each + * built-in variable each time before a script is executed. + * + * Refer to DECLARE_VMINT() for example code. + * + * @see VMInt32RelPtr, VMInt8RelPtr, DECLARE_VMINT() + */ + struct VMInt64RelPtr : VMRelPtr, VMIntPtr { + VMInt64RelPtr() { + base = NULL; + offset = 0; + readonly = false; + } + VMInt64RelPtr(const VMRelPtr& data) { + base = data.base; + offset = data.offset; + readonly = false; + } + vmint evalInt() OVERRIDE { + return (vmint)*(int64_t*)&(*(uint8_t**)base)[offset]; + } + void assign(vmint i) OVERRIDE { + *(int64_t*)&(*(uint8_t**)base)[offset] = (int64_t)i; + } + bool isAssignable() const OVERRIDE { return !readonly; } + }; + + /** @brief Pointer to built-in VM integer variable (of C/C++ type int32_t). * * Used for defining built-in 32 bit integer script variables. * * @b CAUTION: You may only use this class for pointing to C/C++ variables - * of type "int" (which on most systems is 32 bit in size). If the C/C++ int - * variable you want to reference is only 8 bit in size, then you @b must - * use VMInt8RelPtr instead! + * of type "int32_t" (thus being exactly 32 bit in size). If the C/C++ int + * variable you want to reference is 64 bit in size then you @b must use + * VMInt64RelPtr instead! Respectively for a referenced native variable with + * only 8 bit in size you @b must use VMInt8RelPtr instead! * * For efficiency reasons the actual native C/C++ int variable is referenced * by two components here. The actual native int C/C++ variable in memory @@ -498,21 +795,26 @@ * * Refer to DECLARE_VMINT() for example code. * - * @see VMInt8RelPtr, DECLARE_VMINT() + * @see VMInt64RelPtr, VMInt8RelPtr, DECLARE_VMINT() */ - struct VMIntRelPtr : VMRelPtr { - VMIntRelPtr() { + struct VMInt32RelPtr : VMRelPtr, VMIntPtr { + VMInt32RelPtr() { base = NULL; offset = 0; readonly = false; } - VMIntRelPtr(const VMRelPtr& data) { + VMInt32RelPtr(const VMRelPtr& data) { base = data.base; offset = data.offset; readonly = false; } - virtual int evalInt() { return *(int*)&(*(uint8_t**)base)[offset]; } - virtual void assign(int i) { *(int*)&(*(uint8_t**)base)[offset] = i; } + vmint evalInt() OVERRIDE { + return (vmint)*(int32_t*)&(*(uint8_t**)base)[offset]; + } + void assign(vmint i) OVERRIDE { + *(int32_t*)&(*(uint8_t**)base)[offset] = (int32_t)i; + } + bool isAssignable() const OVERRIDE { return !readonly; } }; /** @brief Pointer to built-in VM integer variable (of C/C++ type int8_t). @@ -521,8 +823,9 @@ * * @b CAUTION: You may only use this class for pointing to C/C++ variables * of type "int8_t" (8 bit integer). If the C/C++ int variable you want to - * reference is an "int" type (which is 32 bit on most systems), then you - * @b must use VMIntRelPtr instead! + * reference is not exactly 8 bit in size then you @b must respectively use + * either VMInt32RelPtr for native 32 bit variables or VMInt64RelPtrl for + * native 64 bit variables instead! * * For efficiency reasons the actual native C/C++ int variable is referenced * by two components here. The actual native int C/C++ variable in memory @@ -534,19 +837,37 @@ * * Refer to DECLARE_VMINT() for example code. * - * @see VMIntRelPtr, DECLARE_VMINT() + * @see VMIntRel32Ptr, VMIntRel64Ptr, DECLARE_VMINT() */ - struct VMInt8RelPtr : VMIntRelPtr { - VMInt8RelPtr() : VMIntRelPtr() {} - VMInt8RelPtr(const VMRelPtr& data) : VMIntRelPtr(data) {} - virtual int evalInt() OVERRIDE { - return *(uint8_t*)&(*(uint8_t**)base)[offset]; + struct VMInt8RelPtr : VMRelPtr, VMIntPtr { + VMInt8RelPtr() { + base = NULL; + offset = 0; + readonly = false; + } + VMInt8RelPtr(const VMRelPtr& data) { + base = data.base; + offset = data.offset; + readonly = false; } - virtual void assign(int i) OVERRIDE { - *(uint8_t*)&(*(uint8_t**)base)[offset] = i; + vmint evalInt() OVERRIDE { + return (vmint)*(uint8_t*)&(*(uint8_t**)base)[offset]; } + void assign(vmint i) OVERRIDE { + *(uint8_t*)&(*(uint8_t**)base)[offset] = (uint8_t)i; + } + bool isAssignable() const OVERRIDE { return !readonly; } }; + /** @brief Pointer to built-in VM integer variable (of C/C++ type vmint). + * + * Use this typedef if the native variable to be pointed to is using the + * typedef vmint. If the native C/C++ variable to be pointed to is using + * another C/C++ type then better use one of VMInt64RelPtr or VMInt32RelPtr + * instead. + */ + typedef VMInt64RelPtr VMIntRelPtr; + #if HAVE_CXX_EMBEDDED_PRAGMA_DIAGNOSTICS # define COMPILER_DISABLE_OFFSETOF_WARNING \ _Pragma("GCC diagnostic push") \ @@ -559,13 +880,13 @@ #endif /** - * Convenience macro for initializing VMIntRelPtr and VMInt8RelPtr - * structures. Usage example: + * Convenience macro for initializing VMInt64RelPtr, VMInt32RelPtr and + * VMInt8RelPtr structures. Usage example: * @code * struct Foo { * uint8_t a; // native representation of a built-in integer script variable - * int b; // native representation of another built-in integer script variable - * int c; // native representation of another built-in integer script variable + * int64_t b; // native representation of another built-in integer script variable + * int64_t c; // native representation of another built-in integer script variable * uint8_t d; // native representation of another built-in integer script variable * }; * @@ -576,8 +897,8 @@ * Foo* pFoo; * * VMInt8RelPtr varA = DECLARE_VMINT(pFoo, class Foo, a); - * VMIntRelPtr varB = DECLARE_VMINT(pFoo, class Foo, b); - * VMIntRelPtr varC = DECLARE_VMINT(pFoo, class Foo, c); + * VMInt64RelPtr varB = DECLARE_VMINT(pFoo, class Foo, b); + * VMInt64RelPtr varC = DECLARE_VMINT(pFoo, class Foo, c); * VMInt8RelPtr varD = DECLARE_VMINT(pFoo, class Foo, d); * * pFoo = &foo1; @@ -612,10 +933,10 @@ ) \ /** - * Same as DECLARE_VMINT(), but this one defines the VMIntRelPtr and - * VMInt8RelPtr structures to be of read-only type. That means the script - * parser will abort any script at parser time if the script is trying to - * modify such a read-only built-in variable. + * Same as DECLARE_VMINT(), but this one defines the VMInt64RelPtr, + * VMInt32RelPtr and VMInt8RelPtr structures to be of read-only type. + * That means the script parser will abort any script at parser time if the + * script is trying to modify such a read-only built-in variable. * * @b NOTE: this is only intended for built-in read-only variables that * may change during runtime! If your built-in variable's data is rather @@ -643,9 +964,10 @@ */ struct VMInt8Array { int8_t* data; - int size; + vmint size; + bool readonly; ///< Whether the array data may be modified or just be read. - VMInt8Array() : data(NULL), size(0) {} + VMInt8Array() : data(NULL), size(0), readonly(false) {} }; /** @brief Virtual machine script variable. @@ -745,6 +1067,9 @@ */ class VMDynIntVar : virtual public VMDynVar, virtual public VMIntExpr { public: + MetricPrefix_t unitPrefix(vmuint i) const OVERRIDE { return VM_NO_PREFIX; } + StdUnit_t unitType() const OVERRIDE { return VM_NO_UNIT; } + bool isFinal() const OVERRIDE { return false; } }; /** @brief Dynamically executed variable (of string data type). @@ -785,10 +1110,28 @@ virtual VMFunction* functionByName(const String& name) = 0; /** + * Returns @c true if the passed built-in function is disabled and + * should be ignored by the parser. This method is called by the + * parser on preprocessor level for each built-in function call within + * a script. Accordingly if this method returns @c true, then the + * respective function call is completely filtered out on preprocessor + * level, so that built-in function won't make into the result virtual + * machine representation, nor would expressions of arguments passed to + * that built-in function call be evaluated, nor would any check + * regarding correct usage of the built-in function be performed. + * In other words: a disabled function call ends up as a comment block. + * + * @param fn - built-in function to be checked + * @param ctx - parser context at the position where the built-in + * function call is located within the script + */ + virtual bool isFunctionDisabled(VMFunction* fn, VMParserContext* ctx) = 0; + + /** * Returns a variable name indexed map of all built-in script variables * which point to native "int" scalar (usually 32 bit) variables. */ - virtual std::map builtInIntVariables() = 0; + virtual std::map builtInIntVariables() = 0; /** * Returns a variable name indexed map of all built-in script integer @@ -800,7 +1143,7 @@ * Returns a variable name indexed map of all built-in constant script * variables, which never change their value at runtime. */ - virtual std::map builtInConstIntVariables() = 0; + virtual std::map builtInConstIntVariables() = 0; /** * Returns a variable name indexed map of all built-in dynamic variables, @@ -849,7 +1192,7 @@ * * @see ScriptVM::exec() */ - virtual int suspensionTimeMicroseconds() const = 0; + virtual vmint suspensionTimeMicroseconds() const = 0; /** * Causes all polyphonic variables to be reset to zero values. A @@ -870,6 +1213,45 @@ * ScriptVM::exec() call. */ virtual size_t instructionsPerformed() const = 0; + + /** + * Sends a signal to this script execution instance to abort its script + * execution as soon as possible. This method is called i.e. when one + * script execution instance intends to stop another script execution + * instance. + */ + virtual void signalAbort() = 0; + + /** + * Copies the current entire execution state from this object to the + * given object. So this can be used to "fork" a new script thread which + * then may run independently with its own polyphonic data for instance. + */ + virtual void forkTo(VMExecContext* ectx) const = 0; + + /** + * In case the script called the built-in exit() function and passed a + * value as argument to the exit() function, then this method returns + * the value that had been passed as argument to the exit() function. + * Otherwise if the exit() function has not been called by the script + * or no argument had been passed to the exit() function, then this + * method returns NULL instead. + * + * Currently this is only used for automated test cases against the + * script engine, which return some kind of value in the individual + * test case scripts to check their behaviour in automated way. There + * is no purpose for this mechanism in production use. Accordingly this + * exit result value is @b always completely ignored by the sampler + * engines. + * + * Officially the built-in exit() function does not expect any arguments + * to be passed to its function call, and by default this feature is + * hence disabled and will yield in a parser error unless + * ScriptVM::setExitResultEnabled() was explicitly set. + * + * @see ScriptVM::setExitResultEnabled() + */ + virtual VMExpr* exitResult() = 0; }; /** @brief Script callback for a certain event. @@ -901,6 +1283,24 @@ }; /** + * Reflects the precise position and span of a specific code block within + * a script. This is currently only used for the locations of commented + * code blocks due to preprocessor statements, and for parser errors and + * parser warnings. + * + * @see ParserIssue for code locations of parser errors and parser warnings + * + * @see VMParserContext::preprocessorComments() for locations of code which + * have been filtered out by preprocessor statements + */ + struct CodeBlock { + int firstLine; ///< The first line number of this code block within the script (indexed with 1 being the very first line). + int lastLine; ///< The last line number of this code block within the script. + int firstColumn; ///< The first column of this code block within the script (indexed with 1 being the very first column). + int lastColumn; ///< The last column of this code block within the script. + }; + + /** * Encapsulates a noteworty parser issue. This encompasses the type of the * issue (either a parser error or parser warning), a human readable * explanation text of the error or warning and the location of the @@ -908,12 +1308,8 @@ * * @see VMSourceToken for processing syntax highlighting instead. */ - struct ParserIssue { + struct ParserIssue : CodeBlock { String txt; ///< Human readable explanation text of the parser issue. - int firstLine; ///< The first line number within the script where this issue was encountered (indexed with 1 being the very first line). - int lastLine; ///< The last line number within the script where this issue was encountered. - int firstColumn; ///< The first column within the script where this issue was encountered (indexed with 1 being the very first column). - int lastColumn; ///< The last column within the script where this issue was encountered. ParserIssueType_t type; ///< Whether this issue is either a parser error or just a parser warning. /** @@ -959,6 +1355,20 @@ return "invalid"; } + /** + * Convenience function used for converting an StdUnit_t constant to a + * string, i.e. for generating error message by the parser. + */ + inline String unitTypeStr(const StdUnit_t& type) { + switch (type) { + case VM_NO_UNIT: return "none"; + case VM_SECOND: return "seconds"; + case VM_HERTZ: return "Hz"; + case VM_BEL: return "Bel"; + } + return "invalid"; + } + /** @brief Virtual machine representation of a script. * * An instance of this abstract base class represents a parsed script, @@ -993,6 +1403,12 @@ virtual std::vector warnings() const = 0; /** + * Returns all code blocks of the script which were filtered out by the + * preprocessor. + */ + virtual std::vector preprocessorComments() const = 0; + + /** * Returns the translated virtual machine representation of an event * handler block (i.e. "on note ... end on" code block) within the * parsed script. This translated representation of the event handler