--- linuxsampler/trunk/src/scriptvm/common.h 2016/04/24 18:16:10 2888 +++ linuxsampler/trunk/src/scriptvm/common.h 2016/07/15 15:29:04 2948 @@ -160,6 +160,18 @@ * @see exprType() */ VMIntArrayExpr* asIntArray() 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 + * expression's constant value may be evaluated already at script + * parse time, which may result in performance benefits during script + * runtime. + */ + virtual bool isConstExpr() const = 0; + + bool isModifyable() const; }; /** @brief Virtual machine integer expression @@ -366,7 +378,7 @@ virtual ExprType_t argType(int iArg) const = 0; /** - * This function is called by the parser to check whether arguments + * 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 data type not * accepted by this function, the parser will throw a parser error. On @@ -384,6 +396,21 @@ virtual bool acceptsArgType(int iArg, ExprType_t type) const = 0; /** + * This method is called by the parser to check whether some arguments + * (and if yes which ones) passed to this script function will be + * modified by this script function. Most script functions simply use + * their arguments as inputs, that is they only read the argument's + * values. However some script function may also use passed + * argument(s) as output variables. In this case the function + * implementation must return @c true for the respective argument + * index here. + * + * @param iArg - index of the function argument in question + * (must be between 0 .. maxAllowedArgs() - 1) + */ + virtual bool modifiesArg(int iArg) const = 0; + + /** * Implements the actual function execution. This exec() method is * called by the VM whenever this function implementation shall be * executed at script runtime. This method blocks until the function @@ -423,6 +450,7 @@ struct VMRelPtr { void** base; ///< Base pointer. int 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). @@ -450,10 +478,12 @@ VMIntRelPtr() { base = NULL; offset = 0; + readonly = false; } VMIntRelPtr(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; } @@ -536,10 +566,32 @@ #define DECLARE_VMINT(basePtr, T_struct, T_member) ( \ (VMRelPtr) { \ (void**) &basePtr, \ - offsetof(T_struct, T_member) \ + offsetof(T_struct, T_member), \ + false \ } \ ) \ + /** + * 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. + * + * @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 + * already available at parser time and won't change during runtime, then + * you should rather register a built-in constant in your VM class instead! + * + * @see ScriptVM::builtInConstIntVariables() + */ + #define DECLARE_VMINT_READONLY(basePtr, T_struct, T_member) ( \ + (VMRelPtr) { \ + (void**) &basePtr, \ + offsetof(T_struct, T_member), \ + true \ + } \ + ) \ + /** @brief Built-in VM 8 bit integer array variable. * * Used for defining built-in integer array script variables (8 bit per @@ -553,6 +605,113 @@ VMInt8Array() : data(NULL), size(0) {} }; + /** @brief Virtual machine script variable. + * + * Common interface for all variables accessed in scripts. + */ + class VMVariable : virtual public VMExpr { + public: + /** + * Whether a script may modify the content of this variable by + * assigning a new value to it. + * + * @see isConstExpr(), assign() + */ + virtual bool isAssignable() const = 0; + + /** + * In case this variable is assignable, this method will be called to + * perform the value assignment to this variable with @a expr + * reflecting the new value to be assigned. + * + * @param expr - new value to be assigned to this variable + */ + virtual void assignExpr(VMExpr* expr) = 0; + }; + + /** @brief Dynamically executed variable (abstract base class). + * + * Interface for the implementation of a dynamically generated content of + * a built-in script variable. Most built-in variables are simply pointers + * to some native location in memory. So when a script reads them, the + * memory location is simply read to get the value of the variable. A + * dynamic variable however is not simply a memory location. For each access + * to a dynamic variable some native code is executed to actually generate + * and provide the content (value) of this type of variable. + */ + class VMDynVar : public VMVariable { + public: + /** + * Returns true in case this dynamic variable 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 expression's + * constant value may be evaluated already at script parse time, which + * may result in performance benefits during script runtime. + * + * However due to the "dynamic" behavior of dynamic variables, almost + * all dynamic variables are probably not constant expressions. That's + * why this method returns @c false by default. If you are really sure + * that your dynamic variable implementation can be considered a + * constant expression then you may override this method and return + * @c true instead. Note that when you return @c true here, your + * dynamic variable will really just be executed once; and exectly + * already when the script is loaded! + * + * As an example you may implement a "constant" built-in dynamic + * variable that checks for a certain operating system feature and + * returns the result of that OS feature check as content (value) of + * this dynamic variable. Since the respective OS feature might become + * available/unavailable after OS updates, software migration, etc. the + * OS feature check should at least be performed once each time the + * application is launched. And since the OS feature check might take a + * certain amount of execution time, it might make sense to only + * perform the check if the respective variable name is actually + * referenced at all in the script to be loaded. Note that the dynamic + * variable will still be evaluated again though if the script is + * loaded again. So it is up to you to probably cache the result in the + * implementation of your dynamic variable. + * + * On doubt, please rather consider to use a constant built-in script + * variable instead of implementing a "constant" dynamic variable, due + * to the runtime overhead a dynamic variable may cause. + * + * @see isAssignable() + */ + bool isConstExpr() const OVERRIDE { return false; } + + /** + * In case this dynamic variable is assignable, the new value (content) + * to be assigned to this dynamic variable. + * + * By default this method does nothing. Override and implement this + * method in your subclass in case your dynamic variable allows to + * assign a new value by script. + * + * @param expr - new value to be assigned to this variable + */ + void assignExpr(VMExpr* expr) OVERRIDE {} + }; + + /** @brief Dynamically executed variable (of integer data type). + * + * This is the base class for all built-in integer script variables whose + * variable content needs to be provided dynamically by executable native + * code on each script variable access. + */ + class VMDynIntVar : virtual public VMDynVar, virtual public VMIntExpr { + public: + }; + + /** @brief Dynamically executed variable (of string data type). + * + * This is the base class for all built-in string script variables whose + * variable content needs to be provided dynamically by executable native + * code on each script variable access. + */ + class VMDynStringVar : virtual public VMDynVar, virtual public VMStringExpr { + public: + }; + /** @brief Provider for built-in script functions and variables. * * Abstract base class defining the high-level interface for all classes @@ -587,6 +746,13 @@ * variables, which never change their value at runtime. */ virtual std::map builtInConstIntVariables() = 0; + + /** + * Returns a variable name indexed map of all built-in dynamic variables, + * which are not simply data stores, rather each one of them executes + * natively to provide or alter the respective script variable data. + */ + virtual std::map builtInDynamicVariables() = 0; }; /** @brief Execution state of a virtual machine. @@ -667,8 +833,10 @@ */ struct ParserIssue { String txt; ///< Human readable explanation text of the parser issue. - int line; ///< Line number within the script where this issue was encountered. - int column; ///< Column within the script where this issue was encountered. + 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. /** @@ -677,10 +845,10 @@ inline void dump() { switch (type) { case PARSER_ERROR: - printf("[ERROR] line %d, column %d: %s\n", line, column, txt.c_str()); + printf("[ERROR] line %d, column %d: %s\n", firstLine, firstColumn, txt.c_str()); break; case PARSER_WARNING: - printf("[Warning] line %d, column %d: %s\n", line, column, txt.c_str()); + printf("[Warning] line %d, column %d: %s\n", firstLine, firstColumn, txt.c_str()); break; } } @@ -792,8 +960,8 @@ String text() const; // position of token in script - int firstLine() const; - int firstColumn() const; + int firstLine() const; ///< First line this source token is located at in script source code (indexed with 0 being the very first line). + int firstColumn() const; ///< Last line this source token is located at in script source code. // base types bool isEOF() const;