Commit 5a96fa68 authored by twanvl's avatar twanvl

Added scripting support; not yet integrated with the rest of the app.

parent 07d69616
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
// ----------------------------------------------------------------------------- : Includes
#include <script/context.hpp>
#include <util/error.hpp>
#include <iostream>
// ----------------------------------------------------------------------------- : Context
Context::Context()
: level(0)
{}
// ----------------------------------------------------------------------------- : Evaluate
// Perform a unary simple instruction, store the result in a (not in *a)
void instrUnary (UnaryInstructionType i, ScriptValueP& a);
// Perform a binary simple instruction, store the result in a (not in *a)
void instrBinary (BinaryInstructionType i, ScriptValueP& a, const ScriptValueP& b);
// Perform a ternary simple instruction, store the result in a (not in *a)
void instrTernary(TernaryInstructionType i, ScriptValueP& a, const ScriptValueP& b, const ScriptValueP& c);
ScriptValueP Context::eval(const Script& script, bool useScope) {
size_t stack_size = stack.size();
size_t scope = useScope ? openScope() : 0;
try {
// Instruction pointer
const Instruction* instr = &script.instructions[0];
// Loop until we are done
while (true) {
assert(instr < &*script.instructions.end());
// debug
// cout << script.dumpInstr(instr - &script.instructions[0], *instr) << endl;
// Evaluate the current instruction
Instruction i = *instr++;
switch (i.instr) {
case I_NOP: break;
// Push a constant
case I_PUSH_CONST: {
stack.push_back(script.constants[i.data]);
break;
}
// Pop top value
case I_POP: {
stack.pop_back();
break;
}
// Jump
case I_JUMP: {
instr = &script.instructions[i.data];
break;
}
// Conditional jump
case I_JUMP_IF_NOT: {
int condition = *stack.back();
stack.pop_back();
if (!condition) {
instr = &script.instructions[i.data];
}
break;
}
// Get a variable
case I_GET_VAR: {
ScriptValueP value = variables[i.data].value;
if (!value) throw ScriptError(_("Variable not set: ") + variableToString(i.data));
stack.push_back(value);
break;
}
// Set a variable
case I_SET_VAR: {
setVariable(i.data, stack.back());
break;
}
// Get an object member
case I_MEMBER_C: {
stack.back() = stack.back()->getMember(*script.constants[i.data]);
break;
}
// Loop over a container, push next value or jump
case I_LOOP: {
ScriptValueP& it = stack[stack.size() - 2]; // second element of stack
assert(dynamic_pointer_cast<ScriptIterator>(it)); // top of stack must be an iterator
ScriptValueP val = static_pointer_cast<ScriptIterator>(it)->next();
if (val) {
stack.push_back(val);
} else {
stack.erase(stack.end() - 2); // remove iterator
instr = &script.instructions[i.data];
}
break;
}
// Function call
case I_CALL: {
// new scope
size_t scope = openScope();
// prepare arguments
for (unsigned int j = 0 ; j < i.data ; ++j) {
setVariable(instr[i.data - j - 1].data, stack.back());
stack.pop_back();
}
instr += i.data; // skip arguments
// get function and call
stack.back() = stack.back()->eval(*this);
// restore scope
closeScope(scope);
break;
}
// Function return
case I_RET: {
// restore shadowed variables
if (useScope) closeScope(scope);
// return top of stack
ScriptValueP result = stack.back();
stack.pop_back();
assert(stack.size() == stack_size); // we end up with the same stack
return result;
}
// Simple instruction: unary
case I_UNARY: {
instrUnary(i.instr1, stack.back());
// cout << "\t\t-> " << (String)*stack.back() << endl;
break;
}
// Simple instruction: binary
case I_BINARY: {
ScriptValueP b = stack.back(); stack.pop_back();
ScriptValueP& a = stack.back();
instrBinary(i.instr2, a, b);
// cout << "\t\t-> " << (String)*stack.back() << endl;
break;
}
// Simple instruction: ternary
case I_TERNARY: {
ScriptValueP c = stack.back(); stack.pop_back();
ScriptValueP b = stack.back(); stack.pop_back();
ScriptValueP& a = stack.back();
instrTernary(i.instr3, a, b, c);
// cout << "\t\t-> " << (String)*stack.back() << endl;
break;
}
}
}
} catch (...) {
// cleanup after an exception
if (scope) closeScope(scope); // restore scope
stack.resize(stack_size); // restore stack
throw; // rethrow
}
}
void Context::setVariable(const String& name, const ScriptValueP& value) {
setVariable(stringToVariable(name), value);
}
void Context::setVariable(int name, const ScriptValueP& value) {
Variable& var = variables[name];
if (var.value && var.level < level) {
// keep shadow copy
Binding bind = {name, var};
shadowed.push_back(bind);
}
var.level = level;
var.value = value;
}
ScriptValueP Context::getVariable(const String& name) {
ScriptValueP value = variables[stringToVariable(name)].value;
if (!value) throw ScriptError(_("Variable not set: ") + name);
return value;
}
ScriptValueP Context::getVariableOrNil(const String& name) {
return variables[stringToVariable(name)].value;
}
size_t Context::openScope() {
level += 1;
return shadowed.size();
}
void Context::closeScope(size_t scope) {
assert(level > 0);
assert(scope <= shadowed.size());
level -= 1;
// restore shadowed variables
while (shadowed.size() > scope) {
variables[shadowed.back().variable] = shadowed.back().value;
shadowed.pop_back();
}
}
// ----------------------------------------------------------------------------- : Simple instructions : unary
void instrUnary (UnaryInstructionType i, ScriptValueP& a) {
switch (i) {
case I_ITERATOR_C:
a = a->makeIterator();
break;
case I_NEGATE:
a = toScript(-(int)*a);
break;
case I_NOT:
a = toScript(!(int)*a);
break;
}
}
// ----------------------------------------------------------------------------- : Simple instructions : binary
// operator on ints
#define OPERATOR_I(OP) \
a = toScript((int)*a OP (int)*b); \
break
// operator on doubles or ints
#define OPERATOR_DI(OP) \
if (at == SCRIPT_DOUBLE || bt == SCRIPT_DOUBLE) { \
a = toScript((double)*a OP (double)*b); \
} else { \
a = toScript((int)*a OP (int)*b); \
} \
break
// operator on strings or doubles or ints
#define OPERATOR_SDI(OP) \
if (at == SCRIPT_STRING || bt == SCRIPT_STRING) { \
a = toScript((String)*a OP (String)*b); \
} else if (at == SCRIPT_DOUBLE || bt == SCRIPT_DOUBLE) { \
a = toScript((double)*a OP (double)*b); \
} else { \
a = toScript((int)*a OP (int)*b); \
} \
break
void instrBinary (BinaryInstructionType i, ScriptValueP& a, const ScriptValueP& b) {
ScriptType at = a->type(), bt = b->type();
switch (i) {
case I_MEMBER:
a = a->getMember(*b);
break;
case I_ITERATOR_R:
a = rangeIterator(*a, *b);
break;
case I_ADD: // add is quite overloaded
if (at == SCRIPT_NIL) {
a = b;
} else if (bt == SCRIPT_NIL) {
// a = a;
//} else if (a->likesFunction() && b->likesFunction()) {
// a = compose(a, b);
} else if (at == SCRIPT_STRING || bt == SCRIPT_STRING) {
a = toScript((String)*a + (String)*b);
} else if (at == SCRIPT_DOUBLE || bt == SCRIPT_DOUBLE) {
a = toScript((double)*a + (double)*b);
} else {
a = toScript((int)*a + (int)*b);
}
break;
case I_SUB: OPERATOR_DI(-);
case I_MUL: OPERATOR_DI(*);
case I_DIV: OPERATOR_DI(/);
case I_MOD: // fmod
case I_AND: OPERATOR_I(&&);
case I_OR: OPERATOR_I(||);
case I_EQ: OPERATOR_SDI(==);
case I_NEQ: OPERATOR_SDI(!=);
case I_LT: OPERATOR_DI(<);
case I_GT: OPERATOR_DI(>);
case I_LE: OPERATOR_DI(<=);
case I_GE: OPERATOR_DI(>=);
}
}
// ----------------------------------------------------------------------------- : Simple instructions : ternary
void instrTernary(TernaryInstructionType i, ScriptValueP& a, const ScriptValueP& b, const ScriptValueP& c) {
}
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
#ifndef HEADER_SCRIPT_CONTEXT
#define HEADER_SCRIPT_CONTEXT
// ----------------------------------------------------------------------------- : Includes
#include <script/script.hpp>
class Dependency;
// ----------------------------------------------------------------------------- : VectorIntMap
/// A map like data structure that stores the elements in a vector.
/** K should be an integer type, the keys should be dense. */
template <typename K, typename V>
class VectorIntMap {
public:
inline V& operator [] (K key) {
if (values.size() <= key) {
values.resize(key + 1);
}
return values[key];
}
private:
vector<V> values;
};
// ----------------------------------------------------------------------------- : Context
/// Context for script evaluation
class Context {
public:
Context();
/// Evaluate a script inside this context.
/** This function is safely reentrant.
* @param openScope if false, variables set in this eval call will leak out.
*/
ScriptValueP eval(const Script& script, bool openScope = true);
/// Analyze the dependencies of a script
/** All things the script depends on are marked with signalDependent(dep).
* The return value of this function should be ignored
*/
ScriptValueP dependencies(const Dependency& dep, const Script& script);
/// Set a variable to a new value (in the current scope)
void setVariable(const String& name, const ScriptValueP& value);
/// Get the value of a variable, throws if it not set
ScriptValueP getVariable(const String& name);
/// Get the value of a variable, returns nil if it is not set
ScriptValueP getVariableOrNil(const String& name);
public:// public for FOR_EACH
/// Record of a variable
struct Variable {
unsigned int level; ///< Scope level on which this variable was set
ScriptValueP value; ///< Value of this variable
};
/// Record of a variable binding that is being shadowed (overwritten) by another binding
struct Binding {
int variable; ///< Name of the overwritten variable.
Variable value; ///< Old value of that variable.
};
private:
/// Variables, indexed by integer naem (using stringToVariable)
VectorIntMap<unsigned int, Variable> variables;
/// Shadowed variable bindings
vector<Binding> shadowed;
/// Number of scopes opened
unsigned int level;
/// Stack of values
vector<ScriptValueP> stack;
// utility types for dependency analysis
struct Jump;
struct JumpOrder;
/// Set a variable to a new value (in the current scope)
void setVariable(int name, const ScriptValueP& value);
/// Open a new scope
/** returns the number of shadowed binding before that scope */
size_t openScope();
/// Close a scope, must be passed a value from openScope
void closeScope(size_t scope);
/// Return the bindings in the current scope
void getBindings(size_t scope, vector<Binding>&);
/// Remove all bindings made in the current scope
void resetBindings(size_t scope);
};
// ----------------------------------------------------------------------------- : EOF
#endif
This diff is collapsed.
This diff is collapsed.
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
#ifndef HEADER_SCRIPT_PARSER
#define HEADER_SCRIPT_PARSER
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
#include <script/script.hpp>
// ----------------------------------------------------------------------------- : Parser
// Parse a String to a Script
ScriptP parse(const String& s);
// ----------------------------------------------------------------------------- : EOF
#endif
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
// ----------------------------------------------------------------------------- : Includes
#include <script/script.hpp>
#include <script/context.hpp>
#include <boost/lexical_cast.hpp>
// ----------------------------------------------------------------------------- : Variables
typedef map<string, unsigned int> Variables;
Variables variables;
DECLARE_TYPEOF(Variables);
/// Return a unique name for a variable to allow for faster loopups
unsigned int stringToVariable(const String& s) {
map<string, unsigned int>::iterator it = variables.find(s);
if (it == variables.end()) {
unsigned int v = (unsigned int)variables.size();
variables.insert(make_pair(s,v));
return v;
} else {
return it->second;
}
}
/// Get the name of a vaiable
/** Warning: this function is slow, it should only be used for error messages and such.
*/
String variableToString(unsigned int v) {
FOR_EACH(vi, variables) {
if (vi.second == v) return vi.first;
}
throw "Variable not found: " + lexical_cast<String>(v);
}
// ----------------------------------------------------------------------------- : Script
ScriptType Script::type() const {
return SCRIPT_SCRIPT_FUN;
}
String Script::typeName() const {
return "function";
}
ScriptValueP Script::eval(Context& ctx) const {
return ctx.eval(*this);
}
ScriptValueP Script::dependencies(Context& ctx, const Dependency& dep) const {
return ctx.dependencies(dep, *this);
}
void Script::addInstruction(InstructionType t) {
//if (t == I_MEMBER_V && !instructions.empty() && instructions.back().instr == I_PUSH_CONST) {
// // optimize: push x ; member_v --> member x
// instructions.back().instr = I_MEMBER;
//} else {
Instruction i = {t, 0};
instructions.push_back(i);
//}
}
void Script::addInstruction(InstructionType t, unsigned int d) {
Instruction i = {t, d};
instructions.push_back(i);
}
void Script::addInstruction(InstructionType t, const ScriptValueP& c) {
constants.push_back(c);
Instruction i = {t, (unsigned int)constants.size() - 1};
instructions.push_back(i);
}
void Script::addInstruction(InstructionType t, const String& s) {
constants.push_back(toScript(s));
Instruction i = {t, (unsigned int)constants.size() - 1};
instructions.push_back(i);
}
void Script::comeFrom(unsigned int pos) {
assert( instructions.at(pos).instr == I_JUMP
|| instructions.at(pos).instr == I_JUMP_IF_NOT
|| instructions.at(pos).instr == I_LOOP);
instructions.at(pos).data = (unsigned int)instructions.size();
}
unsigned int Script::getLabel() const {
return (unsigned int)instructions.size();
}
DECLARE_TYPEOF_COLLECTION(Instruction);
String Script::dumpScript() const {
String ret;
int pos = 0;
FOR_EACH_CONST(i, instructions) {
ret += dumpInstr(pos++, i) + "\n";
}
return ret;
}
String Script::dumpInstr(unsigned int pos, Instruction i) const {
String ret = lexical_cast<String>(pos) + ":\t";
// instruction
switch (i.instr) {
case I_NOP: ret += "nop"; break;
case I_PUSH_CONST: ret += "push"; break;
case I_POP: ret += "pop"; break;
case I_JUMP: ret += "jump"; break;
case I_JUMP_IF_NOT: ret += "jnz"; break;
case I_GET_VAR: ret += "get"; break;
case I_SET_VAR: ret += "set"; break;
case I_MEMBER_C: ret += "member_c"; break;
case I_LOOP: ret += "loop"; break;
case I_CALL: ret += "call"; break;
case I_RET: ret += "ret"; break;
case I_UNARY: ret += "unary\t";
switch (i.instr1) {
case I_ITERATOR_C: ret += "iterator_c";break;
case I_NEGATE: ret += "negate"; break;
case I_NOT: ret += "not"; break;
}
break;
case I_BINARY: ret += "binary\t";
switch (i.instr2) {
case I_ITERATOR_R: ret += "iterator_r";break;
case I_MEMBER: ret += "member"; break;
case I_ADD: ret += "+"; break;
case I_SUB: ret += "-"; break;
case I_MUL: ret += "*"; break;
case I_DIV: ret += "/"; break;
case I_MOD: ret += "*"; break;
case I_AND: ret += "and"; break;
case I_OR: ret += "or"; break;
case I_EQ: ret += "=="; break;
case I_NEQ: ret += "!="; break;
case I_LT: ret += "<"; break;
case I_GT: ret += ">"; break;
case I_LE: ret += "<="; break;
case I_GE: ret += ">="; break;
}
break;
case I_TERNARY: ret += "ternary\t";
switch (i.instr3) {
case I_RGB: ret += "rgb"; break;
}
break;
}
// arg
switch (i.instr) {
case I_PUSH_CONST: case I_MEMBER_C: // const
ret += "\t" + (String)*constants[i.data];
ret += "\t(" + constants[i.data]->typeName();
ret += ", #" + lexical_cast<String>(i.data) + ")";
break;
case I_JUMP: case I_JUMP_IF_NOT: case I_LOOP: case I_CALL: // int
ret += "\t" + lexical_cast<String>(i.data);
break;
case I_GET_VAR: case I_SET_VAR: case I_NOP: // variable
ret += "\t" + variableToString(i.data) + "\t$" + lexical_cast<String>(i.data);
break;
}
return ret;
}
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| 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 reason for the negative values is that the compiler (at least MSVC) considers the bitfield signed
*/
enum InstructionType
// Basic
{ I_NOP = 0 ///< no operation, used as placeholder for extra data values
, I_PUSH_CONST = 1 ///< arg = value : push a constant onto the stack
, I_POP = 2 ///< pop the top value from the stack (used for ; operator)
, I_JUMP = 3 ///< arg = int : move the instruction pointer to the given position
, I_JUMP_IF_NOT = 4 ///< arg = int : move the instruction pointer if the top of the stack is false
// Variables
, I_GET_VAR = 5 ///< arg = int : find a variable, push its value onto the stack, it is an error if the variable is not found
, I_SET_VAR = 6 ///< arg = int : assign the top value from the stack to a variable (doesn't pop)
// Objects
, I_MEMBER_C = 7 ///< arg = name : finds a member of the top of the stack replaces the top of the stack with the member
, I_LOOP = -8 ///< arg = int : 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!
// Functions
, I_CALL = -7 ///< arg = int, int+ : 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_RET = -6 ///< return from the current function
// Simple instructions
, I_UNARY = -5 ///< pop 1 value, apply a function, push the result
, I_BINARY = -4 ///< pop 2 values, apply a function, push the result
, I_TERNARY = -3 ///< pop 3 values, apply a function, push the result
};
/// 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_DIV ///< divide
, I_MOD ///< modulus
// Logical
, I_AND ///< logical and
, I_OR ///< logical or
// Comparison
, I_EQ ///< operator ==
, I_NEQ ///< operator !=
, I_LT ///< operator <
, I_GT ///< operator >
, I_LE ///< operator <=
, I_GE ///< operator >=
};
/// Types of ternary instructions (taking three arguments from the stack)
enum TernaryInstructionType
{ I_RGB ///< pop r,g,b, push a color value
};
/// An instruction in a script, consists of the opcode and data
/** If the opcode is one of I_UNARY,I_BINARY,I_TERNARY,
* Then the instr? member gives the actual instruction to perform
*/
struct Instruction {
InstructionType instr : 4;
union {
unsigned int data : 28;
UnaryInstructionType instr1 : 28;
BinaryInstructionType instr2 : 28;
TernaryInstructionType instr3 : 28;
};
};
// ----------------------------------------------------------------------------- : Variables
/// Return a unique name for a variable to allow for faster loopups
unsigned int stringToVariable(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 variableToString(unsigned int v);
// ----------------------------------------------------------------------------- : 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:
/// Set the script to be executed
//void setScript(const String& script);
virtual ScriptType type() const;
virtual String typeName() const;
virtual ScriptValueP eval(Context& ctx) const;
virtual ScriptValueP dependencies(Context& ctx, const Dependency&) const;
/// Add an instruction with no data
void 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).
* The position must be a label just before the instruction to be updated.
*/
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; }
/// 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;
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;
friend class Context;
};
// ----------------------------------------------------------------------------- : EOF
#endif
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
// ----------------------------------------------------------------------------- : Includes
#include <script/value.hpp>
#include <util/error.hpp>
#include <boost/lexical_cast.hpp> //%%
#include <boost/pool/singleton_pool.hpp>
// ----------------------------------------------------------------------------- : ScriptValue
// Base cases
ScriptValue::operator String() const { return "[[" + typeName() + "]]"; }
ScriptValue::operator int() const { throw ScriptError("Can't convert from "+typeName()+" to integer number"); }
ScriptValue::operator double() const { throw ScriptError("Can't convert from "+typeName()+" to real number" ); }
ScriptValue::operator Color() const { throw ScriptError("Can't convert from "+typeName()+" to color" ); }
ScriptValueP ScriptValue::eval(Context&) const
{ throw ScriptError("Can't convert from "+typeName()+" to function" ); }
ScriptValueP ScriptValue::getMember(const String& name) const
{ throw (typeName() + " has no member '" + name + "'"); }
ScriptValueP ScriptValue::next() { throw InternalError("Can't convert from "+typeName()+" to iterator"); }
ScriptValueP ScriptValue::makeIterator() const { throw ScriptError("Can't convert from "+typeName()+" to collection"); }
void ScriptValue::signalDependent(Context&, const Dependency&, const String& name) {}
ScriptValueP ScriptValue::dependencies( Context&, const Dependency&) const { return scriptNil; }
// ----------------------------------------------------------------------------- : Iterators
ScriptType ScriptIterator::type() const { return SCRIPT_OBJECT; }
String ScriptIterator::typeName() const { return "iterator"; }
// Iterator over a range of integers
class ScriptRangeIterator : public ScriptIterator {
public:
// Construct a range iterator with the given bounds (inclusive)
ScriptRangeIterator(int start, int end)
: pos(start), end(end) {}
virtual ScriptValueP next() {
if (pos <= end) {
return toScript(pos++);
} else {
return ScriptValueP();
}
}
private:
int pos, end;
};
ScriptValueP rangeIterator(int start, int end) {
return new_intrusive2<ScriptRangeIterator>(start, end);
}
// ----------------------------------------------------------------------------- : Integers
// Integer values
class ScriptInt : public ScriptValue {
public:
ScriptInt(int v) : value(v) {}
virtual ScriptType type() const { return SCRIPT_INT; }
virtual String typeName() const { return "integer number"; }
virtual operator String() const { return lexical_cast<String>(value); }
virtual operator double() const { return value; }
virtual operator int() const { return value; }
protected:
virtual void destroy() {
boost::singleton_pool<ScriptValue, sizeof(ScriptInt)>::free(this);
}
private:
int value;
};
ScriptValueP toScript(int v) {
// return new_intrusive1<ScriptInt>(v);
return ScriptValueP(
new(boost::singleton_pool<ScriptValue, sizeof(ScriptInt)>::malloc())
ScriptInt(v));
}
// use integers to represent true/false
ScriptValueP scriptTrue = toScript((int)true);
ScriptValueP scriptFalse = toScript((int)false);
// ----------------------------------------------------------------------------- : Doubles
// Double values
class ScriptDouble : public ScriptValue {
public:
ScriptDouble(double v) : value(v) {}
virtual ScriptType type() const { return SCRIPT_DOUBLE; }
virtual String typeName() const { return "real number"; }
virtual operator String() const { return lexical_cast<String>(value); }
virtual operator double() const { return value; }
virtual operator int() const { return (int)value; }
private:
double value;
};
ScriptValueP toScript(double v) {
return new_intrusive1<ScriptDouble>(v);
}
// ----------------------------------------------------------------------------- : String type
// String values
class ScriptString : public ScriptValue {
public:
ScriptString(const String& v) : value(v) {}
virtual ScriptType type() const { return SCRIPT_STRING; }
virtual String typeName() const { return "string"; }
virtual operator String() const { return value; }
virtual operator double() const { return lexical_cast<double>(value); }
virtual operator int() const { return lexical_cast<int>(value); }
private:
String value;
};
ScriptValueP toScript(const String& v) {
return new_intrusive1<ScriptString>(v);
}
// ----------------------------------------------------------------------------- : Color
// Color values
class ScriptColor : public ScriptValue {
public:
ScriptColor(const Color& v) : value(v) {}
virtual ScriptType type() const { return SCRIPT_COLOR; }
virtual String typeName() const { return "color"; }
private:
Color value;
};
ScriptValueP toScript(const Color& v) {
return new_intrusive1<ScriptColor>(v);
}
// ----------------------------------------------------------------------------- : Nil type
// the nil object
class ScriptNil : public ScriptValue {
public:
virtual ScriptType type() const { return SCRIPT_NIL; }
virtual String typeName() const { return "nil"; }
virtual operator String() const { return ""; }
virtual operator double() const { return 0.0; }
virtual operator int() const { return 0; }
};
/// The preallocated nil value
ScriptValueP scriptNil(new ScriptNil);
// ----------------------------------------------------------------------------- : EOF
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
#ifndef HEADER_SCRIPT_VALUE
#define HEADER_SCRIPT_VALUE
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
#include <boost/intrusive_ptr.hpp>
#include <boost/lexical_cast.hpp> //%%
//#ifndef WINVER
//#define WINVER 0x0501
//#endif
//#include <winresrc.h>
//#include <windef.h>
//#include <winbase.h> // Interlocked*crement
#include <windows.h>
#define TokenType TokenType_
extern "C" {
LONG __cdecl _InterlockedIncrement(LONG volatile *Addend);
LONG __cdecl _InterlockedDecrement(LONG volatile *Addend);
}
#pragma intrinsic (_InterlockedIncrement)
#define InterlockedIncrement _InterlockedIncrement
#pragma intrinsic (_InterlockedDecrement)
#define InterlockedDecrement _InterlockedDecrement
//long __declspec(dllimport) __stdcall InterlockedDecrement(long volatile* Addend);
//long __declspec(dllimport) __stdcall InterlockedIncrement(long volatile* Addend);
//#pragma intrinsic(_InterlockedIncrement)
//#pragma intrinsic( _InterlockedDecrement )
class Context;
class Dependency;
// ----------------------------------------------------------------------------- : ScriptValue
//DECLARE_POINTER_TYPE(ScriptValue);
class ScriptValue;
typedef boost::intrusive_ptr<ScriptValue> ScriptValueP;
enum ScriptType
{ SCRIPT_NIL
, SCRIPT_INT
, SCRIPT_DOUBLE
, SCRIPT_STRING
, SCRIPT_COLOR
, SCRIPT_BUILDIN_FUN
, SCRIPT_SCRIPT_FUN
, SCRIPT_OBJECT
, SCRIPT_DUMMY
};
/// A value that can be handled by the scripting engine.
/// Actual values are derived types
class ScriptValue {
public:
inline ScriptValue() : refCount(0) {}
virtual ~ScriptValue() {}
/// Information on the type of this value
virtual ScriptType type() const = 0;
/// Name of the type of value
virtual String typeName() const = 0;
/// Convert this value to a string
virtual operator String() const;
/// Convert this value to a double
virtual operator double() const;
/// Convert this value to an integer
virtual operator int() const;
/// Convert this value to a color
virtual operator Color() const;
/// Get a member variable from this value
virtual ScriptValueP getMember(const String& name) const;
/// Evaluate this value (if it is a function)
virtual ScriptValueP eval(Context&) const;
/// Return an iterator for the current collection, an iterator is a value that has next()
virtual ScriptValueP makeIterator() const;
/// Return the next item for this iterator, or ScriptValueP() if there is no such item
virtual ScriptValueP next();
/// Signal that a script depends on a member of this value
virtual void signalDependent(Context&, const Dependency&, const String& name);
/// Mark the scripts that this function depends on
/** Return value is an abstract version of the return value of eval */
virtual ScriptValueP dependencies(Context&, const Dependency&) const;
protected:
/// Delete this object
virtual void destroy() {
delete this;
}
private:
volatile LONG refCount;
friend void intrusive_ptr_add_ref(ScriptValue*);
friend void intrusive_ptr_release(ScriptValue*);
};
inline void intrusive_ptr_add_ref(ScriptValue* p) {
//p->refCount += 1;
InterlockedIncrement(&p->refCount);
}
inline void intrusive_ptr_release(ScriptValue* p) {
if (InterlockedDecrement(&p->refCount) == 0) {
//if (--p->refCount == 0) {
p->destroy();
}
}
extern ScriptValueP scriptNil; ///< The preallocated nil value
extern ScriptValueP scriptTrue; ///< The preallocated true value
extern ScriptValueP scriptFalse; ///< The preallocated false value
// ----------------------------------------------------------------------------- : Iterators
// Iterator over a collection
struct ScriptIterator : public ScriptValue {
virtual ScriptType type() const;// { return SCRIPT_OBJECT; }
virtual String typeName() const;// { return "iterator"; }
/// Return the next item for this iterator, or ScriptValueP() if there is no such item
virtual ScriptValueP next() = 0;
};
// make an iterator over a range
ScriptValueP rangeIterator(int start, int end);
// ----------------------------------------------------------------------------- : Collections
// Iterator over a collection
template <typename Collection>
class ScriptCollectionIterator : public ScriptIterator {
public:
ScriptCollectionIterator(const Collection* col) : pos(0), col(col) {}
virtual ScriptValueP next() {
if (pos < col->size()) {
return toScript(col->at(pos++));
} else {
return ScriptValueP();
}
}
private:
size_t pos;
const Collection* col;
};
/// Script value containing a collection
template <typename Collection>
class ScriptCollection : public ScriptValue {
public:
inline ScriptCollection(const Collection* v) : value(v) {}
virtual ScriptType type() const { return SCRIPT_OBJECT; }
virtual String typeName() const { return "collection"; }
virtual ScriptValueP getMember(const String& name) const {
int index = lexical_cast<int>(name);
return toScript(value->at(index));
}
virtual ScriptValueP makeIterator() const {
return new_intrusive1<ScriptCollectionIterator<Collection> >(value);
}
private:
/// Store a pointer to a collection, collections are only ever used for structures owned outside the script
const Collection* value;
};
// ----------------------------------------------------------------------------- : Objects
/// Script value containing an object (pointer)
template <typename T>
class ScriptObject : public ScriptValue {
public:
inline ScriptObject(const shared_ptr<T>& v) : value(v) {}
virtual ScriptType type() const { return SCRIPT_OBJECT; }
virtual String typeName() const { return "object"; }
virtual ScriptValueP getMember(const String& name) const {
GetMember gm(name);
gm.handle(*value);
if (gm.result()) return gm.result();
else throw ScriptError(_("Object has no member '") + name + _("'"));
}
private:
shared_ptr<T> value; ///< The object
};
// ----------------------------------------------------------------------------- : Creating
/// Convert a value to a script value
ScriptValueP toScript(int v);
ScriptValueP toScript(double v);
ScriptValueP toScript(const String& v);
ScriptValueP toScript(const Color& v);
inline ScriptValueP toScript(bool v) { return v ? scriptTrue : scriptFalse; }
template <typename T>
inline ScriptValueP toScript(const vector<T>* v) { return new_intrusive1<ScriptCollection<vector<T> > >(v); }
template <typename T>
inline ScriptValueP toScript(const shared_ptr<T>& v) { return new_intrusive1<ScriptObject<T> >(v); }
// ----------------------------------------------------------------------------- : Buildin functions
/// Macro to declare a new script function
/** Usage:
* @code
* SCRIPT_FUNCTION(my_function) {
* // function code goes here
* }
* @endcode
* This declares a value 'script_my_function' which can be added as a variable to a context
* using:
* @code
* extern ScriptValueP script_my_function;
* context.setVariable("my_function", script_my_function);
* @endcode
*/
#define SCRIPT_FUNCTION(name) SCRIPT_FUNCTION_AUX(name,virtual)
/// Macro to declare a new script function with custom dependency handling
#define SCRIPT_FUNCTION_DEP(name) SCRIPT_FUNCTION_AUX(name,virtual) //TODO
// helper for SCRIPT_FUNCTION and SCRIPT_FUNCTION_DEP
#define SCRIPT_FUNCTION_AUX(name,dep) \
class ScriptBuildin_##name : public ScriptValue { \
dep \
/* virtual */ ScriptType type() const \
{ return SCRIPT_BUILDIN_FUN; } \
virtual String typeName() const \
{ return _("build in function '") _(#name) _("'"); } \
virtual ScriptValueP eval(Context&) const; \
}; \
ScriptValueP script_##name(new ScriptBuildin_##name); \
ScriptValueP ScriptBuildin_##name::eval(Context& ctx) const
/// Retrieve a parameter to a SCRIPT_FUNCTION with the given name and type
/** Usage:
* @code
* SCRIPT_FUNCTION(my_function) {
* SCRIPT_PARAM(String, my_string_param);
* }
* @endcode
* Throws an error if the parameter is not found.
*/
#define SCRIPT_PARAM(Type, name) \
Type name = *ctx.getVariable(_(#name))
/// Return a value from a SCRIPT_FUNCTION
#define SCRIPT_RETURN(value) return toScript(value)
// ----------------------------------------------------------------------------- : EOF
#endif
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
// ----------------------------------------------------------------------------- : Includes
#include <util/io/get_member.hpp>
#include <script/value.hpp>
// ----------------------------------------------------------------------------- : GetMember
GetMember::GetMember(const String& name)
: targetName(name)
{}
void GetMember::store(const String& v) { value = toScript(v); }
void GetMember::store(const int v) { value = toScript(v); }
void GetMember::store(const unsigned int v) { value = toScript((int)v); }
void GetMember::store(const double v) { value = toScript(v); }
void GetMember::store(const bool v) { value = toScript(v); }
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
#ifndef HEADER_UTIL_IO_GET_MEMBER
#define HEADER_UTIL_IO_GET_MEMBER
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
class ScriptValue;
typedef boost::intrusive_ptr<ScriptValue> ScriptValueP;
inline void intrusive_ptr_add_ref(ScriptValue* p);
inline void intrusive_ptr_release(ScriptValue* p);
// ----------------------------------------------------------------------------- : GetMember
/// Find a member with a specific name using reflection
/** The member is wrapped in a ScriptValue */
class GetMember {
public:
/// Construct a member getter that looks for the given name
GetMember(const String& name);
/// Tell the reflection code we are not reading
inline bool reading() const { return false; }
/// The result, or scriptNil if the member was not found
inline ScriptValueP result() { return value; }
// --------------------------------------------------- : Handling objects
/// Handle an object: we are done if the name matches
template <typename T>
void handle(const Char* name, const T& object) {
if (!value && name == targetName) store(object);
}
template <typename T>
void handle(const T&);
/// Store something in the return value
void store(const String& v);
void store(const int v);
void store(const unsigned int v);
void store(const double v);
void store(const bool v);
/// Store a vector in the return value
template <typename T> void store(const vector<T>& vector) {
value = toScript(&vector);
}
/// Store a shared_ptr in the return value
template <typename T> void store(const shared_ptr<T>& pointer) {
value = toScript(pointer);
}
private:
const String& targetName; ///< The name we are looking for
ScriptValueP value; ///< The value we found (if any)
};
// ----------------------------------------------------------------------------- : Reflection
/// Implement reflection as used by GetMember
#define REFLECT_OBJECT_GET_MEMBER(Cls) \
template<> void GetMember::handle<Cls>(const Cls& object) { \
const_cast<Cls&>(object).reflect(*this); \
}
// ----------------------------------------------------------------------------- : Reflection for enumerations
/// Implement enum reflection as used by Writer
#define REFLECT_ENUM_WRITER(Enum) \
template<> void Writer::handle<Enum>(const Enum& enum_) { \
EnumGetMember gm(*this); \
reflect_ ## Enum(const_cast<Enum&>(enum_), gm); \
}
/// 'Tag' to be used when reflecting enumerations for GetMember
class EnumGetMember {
public:
inline EnumGetMember(GetMember& getMember)
: getMember(getMember) {}
/// Handle a possible value for the enum, if the name matches the name in the input
template <typename Enum>
inline void handle(const Char* name, Enum value, Enum enum_) {
if (enum_ == value) {
writer.store(name);
}
}
private:
GetMember& getMember; ///< The writer to write output to
};
// ----------------------------------------------------------------------------- : EOF
#endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment