--- linuxsampler/trunk/src/scriptvm/common.h 2016/12/16 12:57:59 3056 +++ linuxsampler/trunk/src/scriptvm/common.h 2019/08/27 21:36:53 3573 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2016 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,27 @@ 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 by the script engine both internally for floating + * point data, as well as for all @c real data types used by scripts (i.e. + * for all ~foo variables in NKSP 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. */ @@ -43,6 +65,8 @@ INT_ARR_EXPR, ///< integer array expression STRING_EXPR, ///< string expression STRING_ARR_EXPR, ///< string array expression + REAL_EXPR, ///< floating point (scalar) expression + REAL_ARR_EXPR, ///< floating point array expression }; /** @brief Result flags of a script statement or script function call. @@ -80,19 +104,130 @@ * * 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 VMRealExpr; class VMStringExpr; + class VMScalarNumberExpr; class VMIntArrayExpr; + class VMRealArrayExpr; 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 * @@ -125,13 +260,50 @@ * if this expression is i.e. actually a string expression like "12", * calling asInt() will @b not cast that numerical string expression to * an integer expression 12 for you, instead this method will simply - * return NULL! + * return NULL! Same applies if this expression is actually a real + * number expression: asInt() would return NULL in that case as well. * - * @see exprType() + * @see exprType(), asReal(), asScalarNumberExpr() */ VMIntExpr* asInt() const; /** + * In case this expression is a real number (floating point) expression, + * then this method returns a casted pointer to that VMRealExpr object. + * It returns NULL if this expression is not a real number expression. + * + * @b Note: type casting performed by this method is strict! That means + * if this expression is i.e. actually a string expression like "12", + * calling asReal() will @b not cast that numerical string expression to + * a real number expression 12.0 for you, instead this method will + * simply return NULL! Same applies if this expression is actually an + * integer expression: asReal() would return NULL in that case as well. + * + * @see exprType(), asInt(), asScalarNumberExpr() + */ + VMRealExpr* asReal() const; + + /** + * In case this expression is a scalar number expression, that is either + * an integer (scalar) expression or a real number (floating point + * scalar) expression, then this method returns a casted pointer to that + * VMScalarNumberExpr base class object. It returns NULL if this + * expression is neither an integer (scalar), nor a real number (scalar) + * expression. + * + * Since the methods asInt() and asReal() are very strict, this method + * is provided as convenience access in case only very general + * information (e.g. which standard measurement unit is being used or + * whether final operator being effective to this expression) is + * intended to be retrieved of this scalar number expression independent + * from whether this expression is actually an integer or a real number + * expression. + * + * @see exprType(), asInt(), asReal() + */ + VMScalarNumberExpr* asScalarNumberExpr() const; + + /** * In case this expression is a string expression, then this method * returns a casted pointer to that VMStringExpr object. It returns NULL * if this expression is not a string expression. @@ -152,10 +324,10 @@ * returns NULL if this expression is not an integer array expression. * * @b Note: type casting performed by this method is strict! That means - * if this expression is i.e. an integer expression or a string - * expression, calling asIntArray() will @b not cast those scalar - * expressions to an array expression for you, instead this method will - * simply return NULL! + * if this expression is i.e. an integer scalar expression, a real + * number expression or a string expression, calling asIntArray() will + * @b not cast those expressions to an integer array expression for you, + * instead this method will simply return NULL! * * @b Note: this method is currently, and in contrast to its other * counter parts, declared as virtual method. Some deriving classes are @@ -170,6 +342,30 @@ virtual VMIntArrayExpr* asIntArray() const; /** + * In case this expression is a real number (floating point) array + * expression, then this method returns a casted pointer to that + * VMRealArrayExpr object. It returns NULL if this expression is not a + * real number array expression. + * + * @b Note: type casting performed by this method is strict! That means + * if this expression is i.e. a real number scalar expression, an + * integer expression or a string expression, calling asRealArray() will + * @b not cast those scalar expressions to a real number array + * expression for you, instead this method will simply return NULL! + * + * @b Note: this method is currently, and in contrast to its other + * counter parts, declared as virtual method. Some deriving classes are + * currently using this to override this default implementation in order + * to implement an "evaluate now as real number array" behavior. This + * has efficiency reasons, however this also currently makes this part + * of the API less clean and should thus be addressed in future with + * appropriate changes to the API. + * + * @see exprType() + */ + virtual VMRealArrayExpr* asRealArray() const; + + /** * Returns true in case this expression can be considered to be a * constant expression. A constant expression will retain the same * value throughout the entire life time of a script and the @@ -200,6 +396,44 @@ bool isModifyable() const; }; + /** @brief Virtual machine scalar number expression + * + * This is the abstract base class for integer (scalar) expressions and + * real number (floating point scalar) expressions of scripts. + */ + class VMScalarNumberExpr : virtual public VMExpr, virtual public VMUnit { + public: + /** + * 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 or real number) 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 integer expression * * This is the abstract base class for all expressions inside scripts which @@ -207,13 +441,30 @@ * abstract method evalInt() to return the actual integer result value of * the expression. */ - class VMIntExpr : virtual public VMExpr { + class VMIntExpr : virtual public VMScalarNumberExpr { 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. @@ -221,6 +472,46 @@ ExprType_t exprType() const OVERRIDE { return INT_EXPR; } }; + /** @brief Virtual machine real number (floating point scalar) expression + * + * This is the abstract base class for all expressions inside scripts which + * evaluate to a real number (floating point scalar) value. Deriving classes + * implement the abstract method evalReal() to return the actual floating + * point result value of the expression. + */ + class VMRealExpr : virtual public VMScalarNumberExpr { + public: + /** + * Returns the result of this expression as real number (floating point + * scalar) value. This abstract method must be implemented by deriving + * classes. + */ + virtual vmfloat evalReal() = 0; + + /** + * Returns the result of this expression as real number (floating point + * 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 + */ + vmfloat evalReal(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). + */ + vmfloat evalReal(MetricPrefix_t prefix1, MetricPrefix_t prefix2); + + /** + * Returns always REAL_EXPR for instances of this class. + */ + ExprType_t exprType() const OVERRIDE { return REAL_EXPR; } + }; + /** @brief Virtual machine string expression * * This is the abstract base class for all expressions inside scripts which @@ -255,7 +546,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 +564,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 +573,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. @@ -290,6 +581,38 @@ ExprType_t exprType() const OVERRIDE { return INT_ARR_EXPR; } }; + /** @brief Virtual Machine Real Number Array Expression + * + * This is the abstract base class for all expressions inside scripts which + * evaluate to an array of real numbers (floating point values). Deriving + * classes implement the abstract methods arraySize(), evalRealElement() and + * assignRealElement() to access the array's individual real numbers. + */ + class VMRealArrayExpr : virtual public VMArrayExpr { + public: + /** + * Returns the (scalar) real mumber (floating point value) of the array + * element given by element index @a i. + * + * @param i - array element index (must be between 0 .. arraySize() - 1) + */ + virtual vmfloat evalRealElement(vmuint i) = 0; + + /** + * Changes the current value of an element (given by array element + * index @a i) of this real number array. + * + * @param i - array element index (must be between 0 .. arraySize() - 1) + * @param value - new real number value to be assigned to that array element + */ + virtual void assignRealElement(vmuint i, vmfloat value) = 0; + + /** + * Returns always REAL_ARR_EXPR for instances of this class. + */ + ExprType_t exprType() const OVERRIDE { return REAL_ARR_EXPR; } + }; + /** @brief Arguments (parameters) for being passed to a built-in script function. * * An argument or a set of arguments passed to a script function are @@ -304,7 +627,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 +637,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 +701,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 +724,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 +742,71 @@ * @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) + * @param type - standard measurement unit data type used for that + * function argument by currently parsed script + * + * @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, 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 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 +821,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 +855,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 +945,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 +973,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 +987,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; + } + vmint evalInt() OVERRIDE { + return (vmint)*(uint8_t*)&(*(uint8_t**)base)[offset]; } - virtual void assign(int i) OVERRIDE { - *(uint8_t*)&(*(uint8_t**)base)[offset] = i; + 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 +1030,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 +1047,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 +1083,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 @@ -638,19 +1109,22 @@ /** @brief Built-in VM 8 bit integer array variable. * * Used for defining built-in integer array script variables (8 bit per - * array element). Currently there is no support for any other kind of array - * type. So all integer arrays of scripts use 8 bit data types. + * array element). Currently there is no support for any other kind of + * built-in array type. So all built-in integer arrays accessed by scripts + * use 8 bit data types. */ 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. * - * Common interface for all variables accessed in scripts. + * Common interface for all variables accessed in scripts, independent of + * their precise data type. */ class VMVariable : virtual public VMExpr { public: @@ -671,7 +1145,7 @@ */ virtual void assignExpr(VMExpr* expr) = 0; }; - + /** @brief Dynamically executed variable (abstract base class). * * Interface for the implementation of a dynamically generated content of @@ -745,6 +1219,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). @@ -757,6 +1234,16 @@ public: }; + /** @brief Dynamically executed variable (of integer array data type). + * + * This is the base class for all built-in integer array script variables + * whose variable content needs to be provided dynamically by executable + * native code on each script variable access. + */ + class VMDynIntArrayVar : virtual public VMDynVar, virtual public VMIntArrayExpr { + public: + }; + /** @brief Provider for built-in script functions and variables. * * Abstract base class defining the high-level interface for all classes @@ -775,10 +1262,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 @@ -790,7 +1295,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, @@ -839,7 +1344,66 @@ * * @see ScriptVM::exec() */ - virtual int suspensionTimeMicroseconds() const = 0; + virtual vmint suspensionTimeMicroseconds() const = 0; + + /** + * Causes all polyphonic variables to be reset to zero values. A + * polyphonic variable is expected to be zero when entering a new event + * handler instance. As an exception the values of polyphonic variables + * shall only be preserved from an note event handler instance to its + * correspending specific release handler instance. So in the latter + * case the script author may pass custom data from the note handler to + * the release handler, but only for the same specific note! + */ + virtual void resetPolyphonicData() = 0; + + /** + * Returns amount of virtual machine instructions which have been + * performed the last time when this execution context was executing a + * script. So in case you need the overall amount of instructions + * instead, then you need to add them by yourself after each + * 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. @@ -871,6 +1435,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 @@ -878,12 +1460,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. /** @@ -923,12 +1501,65 @@ case EMPTY_EXPR: return "empty"; case INT_EXPR: return "integer"; case INT_ARR_EXPR: return "integer array"; + case REAL_EXPR: return "real number"; + case REAL_ARR_EXPR: return "real number array"; case STRING_EXPR: return "string"; case STRING_ARR_EXPR: return "string array"; } return "invalid"; } + /** + * Convenience function used for retrieving the data type of a script + * variable name being passed to this function. + * + * @param name - some script variable name (e.g. "$foo") + * @return variable's data type (e.g. INT_EXPR for example above) + */ + inline ExprType_t exprTypeOfVarName(const String& name) { + if (name.empty()) return (ExprType_t) -1; + const char prefix = name[0]; + switch (prefix) { + case '$': return INT_EXPR; + case '%': return INT_ARR_EXPR; + case '~': return REAL_EXPR; + case '?': return REAL_ARR_EXPR; + case '@': return STRING_EXPR; + case '!': return STRING_ARR_EXPR; + } + return (ExprType_t) -1; + } + + /** + * Returns @c true in case the passed data type is some array data type. + */ + inline bool isArray(const ExprType_t& type) { + return type == INT_ARR_EXPR || type == REAL_ARR_EXPR || + type == STRING_ARR_EXPR; + } + + /** + * Returns @c true in case the passed data type is some scalar number type + * (i.e. not an array and not a string). + */ + inline bool isScalarNumber(const ExprType_t& type) { + return type == INT_EXPR || type == REAL_EXPR; + } + + /** + * 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, @@ -963,6 +1594,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 @@ -1022,12 +1659,17 @@ bool isStringLiteral() const; ///< Returns true in case this source token represents a string literal (i.e. "Some text"). bool isComment() const; ///< Returns true in case this source token represents a source code comment. bool isPreprocessor() const; ///< Returns true in case this source token represents a preprocessor statement. + bool isMetricPrefix() const; + bool isStdUnit() const; bool isOther() const; ///< Returns true in case this source token represents anything else not covered by the token types mentioned above. // extended types bool isIntegerVariable() const; ///< Returns true in case this source token represents an integer variable name (i.e. "$someIntVariable"). + bool isRealVariable() const; ///< Returns true in case this source token represents a floating point variable name (i.e. "~someRealVariable"). bool isStringVariable() const; ///< Returns true in case this source token represents an string variable name (i.e. "\@someStringVariable"). - bool isArrayVariable() const; ///< Returns true in case this source token represents an array variable name (i.e. "%someArryVariable"). + bool isIntArrayVariable() const; ///< Returns true in case this source token represents an integer array variable name (i.e. "%someArrayVariable"). + bool isRealArrayVariable() const; ///< Returns true in case this source token represents a real number array variable name (i.e. "?someArrayVariable"). + bool isArrayVariable() const DEPRECATED_API; ///< Returns true in case this source token represents an @b integer array variable name (i.e. "%someArrayVariable"). @deprecated This method will be removed, use isIntArrayVariable() instead. bool isEventHandlerName() const; ///< Returns true in case this source token represents an event handler name (i.e. "note", "release", "controller"). VMSourceToken& operator=(const VMSourceToken& other);