Files
MagicSetEditor2/src/script/script.hpp
T
2017-01-18 08:43:21 -06:00

223 lines
8.9 KiB
C++

//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2010 Twan van Laarhoven and Sean Hunt |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
#ifndef HEADER_SCRIPT_SCRIPT
#define HEADER_SCRIPT_SCRIPT
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
#include <script/value.hpp>
DECLARE_POINTER_TYPE(Script);
// ----------------------------------------------------------------------------- : Instructions
/// A type of instruction to be performed in a script
/** The instruction type should fit in 6 bits.
* Some stupid compilers use signed integers for bitfields, so values should be < 32
*/
enum InstructionType
// Basic
{ I_NOP = 0 ///< arg = * : no operation, used as placeholder for extra data values
, I_PUSH_CONST = 1 ///< arg = const val : push a constant onto the stack
, I_JUMP = 2 ///< arg = address : move the instruction pointer to the given position
, I_JUMP_IF_NOT = 3 ///< arg = address : move the instruction pointer if the top of the stack is false
, I_JUMP_SC_AND = 19 ///< arg = address : (short-circuiting and) jump and don't pop if the top of the stack is false
, I_JUMP_SC_OR = 20 ///< arg = address : (short-circuiting or) jump and don't pop if the top of the stack is true
// Variables
, I_GET_VAR = 4 ///< arg = var : find a variable, push its value onto the stack, it is an error if the variable is not found
, I_SET_VAR = 5 ///< arg = var : assign the top value from the stack to a variable (doesn't pop)
// Objects
, I_MEMBER_C = 6 ///< arg = const name : finds a member of the top of the stack replaces the top of the stack with the member
, I_LOOP = 7 ///< arg = address : loop over the elements of an iterator, which is the *second* element of the stack (this allows for combing the results of multiple iterations)
///< at the end performs a jump and pops the iterator. note: The second element of the stack must be an iterator!
, I_LOOP_WITH_KEY = 8 ///< arg = address : loop, but also pushing the key
, I_MAKE_OBJECT = 9 ///< arg = int : make a list/map with n elements, pops 2n values of the stack, n key/value pairs
// Functions
, I_CALL = 10 ///< arg = int, n*var : call the top item of the stack, with the given number of arguments (set with SET_VAR, but in the activation record of the call)
, I_CLOSURE = 11 ///< arg = int, n*var : construct a call closure object with the given arguments
, I_TAILCALL = 12 ///< arg = int, n*var : perform a tail call - like I_CALL, except faster
// Simple instructions
, I_UNARY = 13 ///< arg = 1ary instr : pop 1 value, apply a function, push the result
, I_BINARY = 14 ///< arg = 2ary instr : pop 2 values, apply a function, push the result
, I_TERNARY = 15 ///< arg = 3ary instr : pop 3 values, apply a function, push the result
, I_QUATERNARY = 16 ///< arg = 4ary instr : pop 4 values, apply a function, push the result
, I_DUP = 17 ///< arg = int : duplicate the k-from-top element of the stack
, I_POP = 18 ///< arg = * : pop the top value off the stack.
};
/// Types of unary instructions (taking one argument from the stack)
enum UnaryInstructionType
{ I_ITERATOR_C ///< Make an iterator for a collection
, I_NEGATE
, I_NOT
};
/// Types of binary instructions (taking two arguments from the stack)
enum BinaryInstructionType
{ I_ITERATOR_R ///< Make an iterator for a range (two integers)
, I_MEMBER ///< Member of an object
// Arithmatic
, I_ADD ///< add
, I_SUB ///< subtract
, I_MUL ///< multiply
, I_FDIV ///< floating point division
, I_DIV ///< integer division
, I_MOD ///< modulus
, I_POW ///< power
// Logical
, I_AND ///< logical and
, I_OR ///< logical or
, I_XOR ///< logical xor
// Comparison
, I_EQ ///< operator ==
, I_NEQ ///< operator !=
, I_LT ///< operator <
, I_GT ///< operator >
, I_LE ///< operator <=
, I_GE ///< operator >=
, I_MIN ///< minimum
, I_MAX ///< maximum
// Error handling
, I_OR_ELSE ///< if a != error then a else b
};
/// Types of ternary instructions (taking three arguments from the stack)
enum TernaryInstructionType
{ I_RGB ///< pop r,g,b, push a color value
};
/// Types of quaternary instructions (taking four arguments from the stack)
enum QuaternaryInstructionType
{ I_RGBA ///< pop r,g,b,a, push an acolor value
};
/// An instruction in a script, consists of the opcode and data
/** If the opcode is one of I_UNARY,I_BINARY,I_TERNARY,I_QUATERNARY,
* Then the instr? member gives the actual instruction to perform
*/
struct Instruction {
InstructionType instr : 6;
union {
unsigned int data : 26;
UnaryInstructionType instr1 : 26;
BinaryInstructionType instr2 : 26;
TernaryInstructionType instr3 : 26;
QuaternaryInstructionType instr4 : 26;
};
};
// ----------------------------------------------------------------------------- : Variables
// for faster lookup from code
enum Variable
{ SCRIPT_VAR_input
, SCRIPT_VAR__1
, SCRIPT_VAR__2
, SCRIPT_VAR_in
, SCRIPT_VAR_match
, SCRIPT_VAR_replace
, SCRIPT_VAR_in_context
, SCRIPT_VAR_recursive
, SCRIPT_VAR_order
, SCRIPT_VAR_begin
, SCRIPT_VAR_end
, SCRIPT_VAR_filter
, SCRIPT_VAR_choice
, SCRIPT_VAR_choices
, SCRIPT_VAR_format
, SCRIPT_VAR_tag
, SCRIPT_VAR_contents
, SCRIPT_VAR_set
, SCRIPT_VAR_game
, SCRIPT_VAR_stylesheet
, SCRIPT_VAR_card_style
, SCRIPT_VAR_card
, SCRIPT_VAR_styling
, SCRIPT_VAR_value
, SCRIPT_VAR_condition
, SCRIPT_VAR_language
, SCRIPT_VAR_CUSTOM_FIRST // other variables start from here
, SCRIPT_VAR_CUSTOM_LOTS = 0xFFFFFF // ensure that sizeof(Variable) is large enough
};
/// Return a unique name for a variable to allow for faster loopups
Variable string_to_variable(const String& s);
/// Get the name of a vaiable
/** Warning: this function is slow, it should only be used for error messages and such.
*/
String variable_to_string(Variable v);
/// initialze the script variables
void init_script_variables();
// ----------------------------------------------------------------------------- : Script
/// A script that can be executed
/** Represent a function that returns a single value.
* The script is itself a ScriptValue
*/
class Script : public ScriptValue {
public:
virtual ScriptType type() const;
virtual String typeName() const;
virtual ScriptValueP dependencies(Context& ctx, const Dependency&) const;
/// Add a jump instruction, later comeFrom should be called on the returned value
unsigned int addInstruction(InstructionType t);
/// Add an instruction with integer data
void addInstruction(InstructionType t, unsigned int d);
/// Add an instruction with constant data
void addInstruction(InstructionType t, const ScriptValueP& c);
/// Add an instruction with string data
void addInstruction(InstructionType t, const String& s);
/// Update an instruction to point to the current position
/** The instruction at pos must be a jumping instruction, it is changed so the current position
* 'comes from' pos (in addition to other control flow).
*/
void comeFrom(unsigned int pos);
/// Get the current instruction position
unsigned int getLabel() const;
/// Get access to the vector of instructions
inline vector<Instruction>& getInstructions() { return instructions; }
/// Get access to the vector of constants
inline vector<ScriptValueP>& getConstants() { return constants; }
/// Output the instructions in a human readable format
String dumpScript() const;
/// Output an instruction in a human readable format
String dumpInstr(unsigned int pos, Instruction i) const;
protected:
virtual ScriptValueP do_eval(Context& ctx, bool openScope) const;
private:
/// Data of the instructions that make up this script
vector<Instruction> instructions;
/// Constant values that can be referred to from the script
vector<ScriptValueP> constants;
/// Do a backtrace for error messages.
/** Starting from instr, move backwards until the nett stack effect
* of the skipped instructions is equal to_skip.
* If the backtrace fails, returns nullptr
*/
const Instruction* backtraceSkip(const Instruction* instr, int to_skip) const;
/// Find the name of an instruction
String instructionName(const Instruction* instr) const;
friend class Context;
};
// ----------------------------------------------------------------------------- : EOF
#endif