mirror of
https://github.com/amyinspace/MagicSetEditor2.git
synced 2026-06-10 04:57:00 -04:00
"div" operator for integer division,
Added parser support for closure operator fun@(args) Use equal() function for all script comparisons, better support for deciding when to use strings and when to use pointers equality. git-svn-id: svn://svn.code.sf.net/p/magicseteditor/code/trunk@964 0fc631ac-6414-0410-93d0-97cfa31319b6
This commit is contained in:
@@ -9,8 +9,8 @@ MSE script supports most basic mathamatical operators:
|
||||
@"3" + "2" == "32"@ concatenate two strings or compose two functions (see below)
|
||||
| @a - b@ @3 - 2 == 1@ Substract two numbers
|
||||
| @a * b@ @3 * 2 == 6@ Multiply two numbers
|
||||
| @a / b@ @3 / 2 == 1@<br/> Divide two numbers.<br/> Rounds towards zero for [[type:int]]s.<br/>
|
||||
@3 / 2.0 == 1.5@ Does not round for [[type:double]]s.
|
||||
| @a / b@ @3 / 2 == 1.5@ Divide two numbers. Does not round, always produces a [[type:double]].
|
||||
| @a div b@ @3 div 2 == 1@ Divide two numbers. Rounds towards zero, producing an [[type:int]].<br/>
|
||||
| @a mod b@ @3 mod 2 == 1@ Take the remainder after integer division (modulo)
|
||||
| @-a@ @-(3 + 2) == -5@ Negate a number (make it negative if positive and vice versa)
|
||||
|
||||
@@ -36,6 +36,8 @@ It is also possible to compare values. All comparisons evaluate to either @true@
|
||||
@"x" <= "y"@ Is a less than b or are they equal?
|
||||
| @a >= b@ @2 >= 1@<br/>
|
||||
@"x" >= "x"@ Is a greater than b or are they equal?
|
||||
| @min(a,b)@ @min(1,2) == 1@ Returns the smallest of two values.
|
||||
| @max(a,b)@ @max(1,2) == 2@ Returns the largest of two values.
|
||||
|
||||
--Booleans--
|
||||
[[type:Boolean]]s (for example from comparisons) can be combined using:
|
||||
@@ -60,13 +62,13 @@ Operators can be grouped differently using parentheses.
|
||||
The exact order of precedence is given in the following table,
|
||||
higher in the table means that this operator binds tighter to its arguments, @*@ binds tighter then @+@.
|
||||
| @a(...)@, @a.b@, @a[b]@ Function calls, property access, see below
|
||||
| @-a@, @not@ Unary operators
|
||||
| @*@, @/@, @mod@ Multiplication and division
|
||||
| @+@, @-@ Addition and substraction
|
||||
| @-a@, @not@ Unary operators
|
||||
| @*@, @/@, @div@, @mod@ Multiplication and division
|
||||
| @+@, @-@ Addition and substraction
|
||||
| @==@, @!=@, @<@, @>@, @<=@, @>=@ Comparisons
|
||||
| @and@, @or@, @xor@ Boolean operators
|
||||
| @:=@ Assignement, see below
|
||||
| @;@ Sequence, see below
|
||||
| @and@, @or@, @xor@ Boolean operators
|
||||
| @:=@ Assignement, see below
|
||||
| @;@ Sequence, see below
|
||||
|
||||
--Properties--
|
||||
Properties of types, as described in the [[type:index|data type section]] of the documentation, can be accessed using the @.@ operator:
|
||||
|
||||
+28
-16
@@ -138,6 +138,12 @@ ScriptValueP Context::eval(const Script& script, bool useScope) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Closure object
|
||||
case I_CLOSURE: {
|
||||
makeClosure(i.data);
|
||||
break;
|
||||
}
|
||||
|
||||
// Simple instruction: unary
|
||||
case I_UNARY: {
|
||||
instrUnary(i.instr1, stack.back());
|
||||
@@ -278,18 +284,6 @@ void instrUnary (UnaryInstructionType i, ScriptValueP& a) {
|
||||
} \
|
||||
break
|
||||
|
||||
// operator on strings or doubles or ints, when in doubt, uses strings
|
||||
#define OPERATOR_SDI(OP) \
|
||||
if (at == SCRIPT_INT && bt == SCRIPT_INT) { \
|
||||
a = to_script((int)*a OP (int)*b); \
|
||||
} else if ((at == SCRIPT_INT || at == SCRIPT_DOUBLE) && \
|
||||
(bt == SCRIPT_INT || bt == SCRIPT_DOUBLE)) { \
|
||||
a = to_script((double)*a OP (double)*b); \
|
||||
} else { \
|
||||
a = to_script(a->toString() OP b->toString()); \
|
||||
} \
|
||||
break
|
||||
|
||||
/// Composition of two functions
|
||||
class ScriptCompose : public ScriptValue {
|
||||
public:
|
||||
@@ -330,6 +324,8 @@ void instrBinary (BinaryInstructionType i, ScriptValueP& a, const ScriptValueP&
|
||||
// a = a;
|
||||
} else if (at == SCRIPT_FUNCTION && bt == SCRIPT_FUNCTION) {
|
||||
a = new_intrusive2<ScriptCompose>(a, b);
|
||||
} else if (at == SCRIPT_COLLECTION && bt == SCRIPT_COLLECTION) {
|
||||
a = new_intrusive2<ScriptConcatCollection>(a, b);
|
||||
} else if (at == SCRIPT_INT && bt == SCRIPT_INT) {
|
||||
a = to_script((int)*a + (int)*b);
|
||||
} else if ((at == SCRIPT_INT || at == SCRIPT_DOUBLE) &&
|
||||
@@ -341,7 +337,16 @@ void instrBinary (BinaryInstructionType i, ScriptValueP& a, const ScriptValueP&
|
||||
break;
|
||||
case I_SUB: OPERATOR_DI(-);
|
||||
case I_MUL: OPERATOR_DI(*);
|
||||
case I_DIV: OPERATOR_DI(/);
|
||||
case I_FDIV:
|
||||
a = to_script((double)*a / (double)*b);
|
||||
break;
|
||||
case I_DIV:
|
||||
if (at == SCRIPT_DOUBLE || bt == SCRIPT_DOUBLE) {
|
||||
a = to_script((int)((double)*a / (double)*b));
|
||||
} else {
|
||||
a = to_script((int)*a / (int)*b);
|
||||
}
|
||||
break;
|
||||
case I_MOD:
|
||||
if (at == SCRIPT_DOUBLE || bt == SCRIPT_DOUBLE) {
|
||||
a = to_script(fmod((double)*a, (double)*b));
|
||||
@@ -352,8 +357,8 @@ void instrBinary (BinaryInstructionType i, ScriptValueP& a, const ScriptValueP&
|
||||
case I_AND: OPERATOR_I(&&);
|
||||
case I_OR: OPERATOR_I(||);
|
||||
case I_XOR: a = to_script((bool)*a != (bool)*b); break;
|
||||
case I_EQ: OPERATOR_SDI(==);
|
||||
case I_NEQ: OPERATOR_SDI(!=);
|
||||
case I_EQ: a = to_script( equal(*a,*b)); break;
|
||||
case I_NEQ: a = to_script(!equal(*a,*b)); break;
|
||||
case I_LT: OPERATOR_DI(<);
|
||||
case I_GT: OPERATOR_DI(>);
|
||||
case I_LE: OPERATOR_DI(<=);
|
||||
@@ -386,7 +391,7 @@ void instrQuaternary(QuaternaryInstructionType i, ScriptValueP& a, const ScriptV
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Simple instructions : object
|
||||
// ----------------------------------------------------------------------------- : Simple instructions : objects and closures
|
||||
|
||||
void Context::makeObject(size_t n) {
|
||||
intrusive_ptr<ScriptCustomCollection> ret(new ScriptCustomCollection());
|
||||
@@ -402,3 +407,10 @@ void Context::makeObject(size_t n) {
|
||||
stack.resize(begin);
|
||||
stack.push_back(ret);
|
||||
}
|
||||
|
||||
void Context::makeClosure(size_t n) {
|
||||
//intrusive_ptr<ScriptClosure> ret(new ScriptClosure());
|
||||
// TODO
|
||||
//stack.push_back(ret);
|
||||
throw InternalError(_("TODO: makeClosure"));
|
||||
}
|
||||
|
||||
@@ -101,6 +101,8 @@ class Context {
|
||||
void resetBindings(size_t scope);
|
||||
/// Make an object with n elements, popping 2n values from the stack, and push it onto the stack
|
||||
void makeObject(size_t n);
|
||||
/// Make a closure with n arguments
|
||||
void makeClosure(size_t n);
|
||||
};
|
||||
|
||||
/// A class that creates a local scope
|
||||
|
||||
@@ -250,6 +250,12 @@ ScriptValueP Context::dependencies(const Dependency& dep, const Script& script)
|
||||
break;
|
||||
}
|
||||
|
||||
// Closure object
|
||||
case I_CLOSURE: {
|
||||
makeClosure(i.data);
|
||||
break;
|
||||
}
|
||||
|
||||
// Get a variable (almost as normal)
|
||||
case I_GET_VAR: {
|
||||
ScriptValueP value = variables[i.data].value;
|
||||
|
||||
@@ -173,24 +173,6 @@ SCRIPT_FUNCTION(remove_tags) {
|
||||
|
||||
// ----------------------------------------------------------------------------- : Collection stuff
|
||||
|
||||
/// compare script values for equallity
|
||||
bool equal(const ScriptValue& a, const ScriptValue& b) {
|
||||
if (&a == &b) return true;
|
||||
ScriptType at = a.type(), bt = b.type();
|
||||
if (at != bt) {
|
||||
return false;
|
||||
} else if (at == SCRIPT_INT) {
|
||||
return (int)a == (int)b;
|
||||
} else if (at == SCRIPT_DOUBLE) {
|
||||
return (double)a == (double)b;
|
||||
} else if (at == SCRIPT_STRING) {
|
||||
return a.toString() == b.toString();
|
||||
} else {
|
||||
// compare pointers, must be equal and not null
|
||||
const void *ap = a.comparePointer(), *bp = b.comparePointer();
|
||||
return (ap && ap == bp);
|
||||
}
|
||||
}
|
||||
|
||||
/// position of some element in a vector
|
||||
/** 0 based index, -1 if not found */
|
||||
|
||||
+13
-2
@@ -112,7 +112,7 @@ class TokenIterator {
|
||||
|
||||
bool isAlpha_(Char c) { return isAlpha(c) || c==_('_'); }
|
||||
bool isAlnum_(Char c) { return isAlnum(c) || c==_('_'); }
|
||||
bool isOper (Char c) { return c==_('+') || c==_('-') || c==_('*') || c==_('/') || c==_('!') || c==_('.') ||
|
||||
bool isOper (Char c) { return c==_('+') || c==_('-') || c==_('*') || c==_('/') || c==_('!') || c==_('.') || c==_('@') ||
|
||||
c==_(':') || c==_('=') || c==_('<') || c==_('>') || c==_(';') || c==_(','); }
|
||||
bool isLparen(Char c) { return c==_('(') || c==_('[') || c==_('{'); }
|
||||
bool isRparen(Char c) { return c==_(')') || c==_(']') || c==_('}'); }
|
||||
@@ -623,7 +623,8 @@ void parseOper(TokenIterator& input, Script& script, Precedence minPrec, Instruc
|
||||
else if (minPrec <= PREC_ADD && token==_("+")) parseOper(input, script, PREC_MUL, I_BINARY, I_ADD);
|
||||
else if (minPrec <= PREC_ADD && token==_("-")) parseOper(input, script, PREC_MUL, I_BINARY, I_SUB);
|
||||
else if (minPrec <= PREC_MUL && token==_("*")) parseOper(input, script, PREC_UNARY, I_BINARY, I_MUL);
|
||||
else if (minPrec <= PREC_MUL && token==_("/")) parseOper(input, script, PREC_UNARY, I_BINARY, I_DIV);
|
||||
else if (minPrec <= PREC_MUL && token==_("/")) parseOper(input, script, PREC_UNARY, I_BINARY, I_FDIV);
|
||||
else if (minPrec <= PREC_MUL && token==_("div")) parseOper(input, script, PREC_UNARY, I_BINARY, I_DIV);
|
||||
else if (minPrec <= PREC_MUL && token==_("mod")) parseOper(input, script, PREC_UNARY, I_BINARY, I_MOD);
|
||||
else if (minPrec <= PREC_FUN && token==_(".")) { // get member by name
|
||||
const Token& token = input.read();
|
||||
@@ -655,6 +656,16 @@ void parseOper(TokenIterator& input, Script& script, Precedence minPrec, Instruc
|
||||
FOR_EACH(arg,arguments) {
|
||||
script.addInstruction(I_NOP, arg);
|
||||
}
|
||||
} else if (minPrec <= PREC_FUN && token==_("@")) {
|
||||
// closure call, read arguments
|
||||
expectToken(input, _("("));
|
||||
vector<Variable> arguments;
|
||||
parseCallArguments(input, script, arguments);
|
||||
// generate instruction
|
||||
script.addInstruction(I_CLOSURE, (unsigned int)arguments.size());
|
||||
FOR_EACH(arg,arguments) {
|
||||
script.addInstruction(I_NOP, arg);
|
||||
}
|
||||
} else if (minPrec <= PREC_STRING && token==_("\"{")) {
|
||||
// for smart strings: "x" {{ e }} "y"
|
||||
// optimize: "" + e -> e
|
||||
|
||||
@@ -154,6 +154,7 @@ String Script::dumpInstr(unsigned int pos, Instruction i) const {
|
||||
case I_LOOP: ret += _("loop"); break;
|
||||
case I_MAKE_OBJECT: ret += _("make object");break;
|
||||
case I_CALL: ret += _("call"); break;
|
||||
case I_CLOSURE: ret += _("closure"); break;
|
||||
case I_UNARY: ret += _("unary\t");
|
||||
switch (i.instr1) {
|
||||
case I_ITERATOR_C: ret += _("iterator_c"); break;
|
||||
@@ -200,7 +201,7 @@ String Script::dumpInstr(unsigned int pos, Instruction i) const {
|
||||
ret += _("\t") + constants[i.data]->toString();
|
||||
ret += _("\t(") + constants[i.data]->typeName() + _(")");
|
||||
break;
|
||||
case I_JUMP: case I_JUMP_IF_NOT: case I_LOOP: case I_CALL: // int
|
||||
case I_JUMP: case I_JUMP_IF_NOT: case I_LOOP: case I_MAKE_OBJECT: case I_CALL: case I_CLOSURE: // int
|
||||
ret += String::Format(_("\t%d"), i.data);
|
||||
break;
|
||||
case I_GET_VAR: case I_SET_VAR: case I_NOP: // variable
|
||||
@@ -235,7 +236,7 @@ const Instruction* Script::backtraceSkip(const Instruction* instr, int to_skip)
|
||||
to_skip += 2; break; // nett stack effect 1-3 == -2
|
||||
case I_QUATERNARY:
|
||||
to_skip += 3; break; // nett stack effect 1-4 == -3
|
||||
case I_CALL:
|
||||
case I_CALL: case I_CLOSURE:
|
||||
to_skip += instr->data; // arguments of call
|
||||
break;
|
||||
case I_MAKE_OBJECT:
|
||||
@@ -299,13 +300,15 @@ String Script::instructionName(const Instruction* instr) const {
|
||||
+ _(".")
|
||||
+ constants[instr->data]->toString();
|
||||
} else if (instr->instr == I_BINARY && instr->instr2 == I_MEMBER) {
|
||||
throw _("??\?[...]");
|
||||
return _("??\?[...]");
|
||||
} else if (instr->instr == I_BINARY && instr->instr2 == I_ADD) {
|
||||
return _("??? + ???");
|
||||
} else if (instr->instr == I_NOP) {
|
||||
return _("??\?(...)");
|
||||
} else if (instr->instr == I_CALL) {
|
||||
return instructionName(backtraceSkip(instr - 1, instr->data)) + _("(...)");
|
||||
} else if (instr->instr == I_CLOSURE) {
|
||||
return instructionName(backtraceSkip(instr - 1, instr->data)) + _("@(...)");
|
||||
} else {
|
||||
return _("??\?");
|
||||
}
|
||||
|
||||
@@ -35,11 +35,12 @@ enum InstructionType
|
||||
, I_MAKE_OBJECT = 8 ///< arg = int : make a list/map with n elements, pops 2n values of the stack, n key/value pairs
|
||||
// Functions
|
||||
, I_CALL = 9 ///< 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 = 10 ///< arg = int, n*var : construct a call closure object with the given arguments
|
||||
// Simple instructions
|
||||
, I_UNARY = 10 ///< arg = 1ary instr : pop 1 value, apply a function, push the result
|
||||
, I_BINARY = 11 ///< arg = 2ary instr : pop 2 values, apply a function, push the result
|
||||
, I_TERNARY = 12 ///< arg = 3ary instr : pop 3 values, apply a function, push the result
|
||||
, I_QUATERNARY = 13 ///< arg = 4ary instr : pop 4 values, apply a function, push the result
|
||||
, I_UNARY = 11 ///< arg = 1ary instr : pop 1 value, apply a function, push the result
|
||||
, I_BINARY = 12 ///< arg = 2ary instr : pop 2 values, apply a function, push the result
|
||||
, I_TERNARY = 13 ///< arg = 3ary instr : pop 3 values, apply a function, push the result
|
||||
, I_QUATERNARY = 14 ///< arg = 4ary instr : pop 4 values, apply a function, push the result
|
||||
};
|
||||
|
||||
/// Types of unary instructions (taking one argument from the stack)
|
||||
@@ -55,13 +56,14 @@ enum BinaryInstructionType
|
||||
, I_ITERATOR_R ///< Make an iterator for a range (two integers)
|
||||
, I_MEMBER ///< Member of an object
|
||||
// Arithmatic
|
||||
, I_ADD ///< add
|
||||
, I_ADD ///< add
|
||||
, I_SUB ///< subtract
|
||||
, I_MUL ///< multiply
|
||||
, I_DIV ///< divide
|
||||
, I_FDIV ///< floating point division
|
||||
, I_DIV ///< integer division
|
||||
, I_MOD ///< modulus
|
||||
// Logical
|
||||
, I_AND ///< logical and
|
||||
, I_AND ///< logical and
|
||||
, I_OR ///< logical or
|
||||
, I_XOR ///< logical xor
|
||||
// Comparison
|
||||
|
||||
+46
-6
@@ -61,7 +61,7 @@ class ScriptDelayedError : public ScriptValue {
|
||||
virtual operator int() const;
|
||||
virtual operator AColor() const;
|
||||
virtual int itemCount() const;
|
||||
virtual const void* comparePointer() const;
|
||||
virtual CompareWhat compareAs(String&, void const*&) const;
|
||||
// these can propagate the error
|
||||
virtual ScriptValueP getMember(const String& name) const;
|
||||
virtual ScriptValueP dependencyMember(const String& name, const Dependency&) const;
|
||||
@@ -82,6 +82,7 @@ inline ScriptValueP delayError(const String& m) {
|
||||
struct ScriptIterator : public ScriptValue {
|
||||
virtual ScriptType type() const;// { return SCRIPT_ITERATOR; }
|
||||
virtual String typeName() const;// { return "iterator"; }
|
||||
virtual CompareWhat compareAs(String&, void const*&) const; // { return COMPARE_NO; }
|
||||
|
||||
/// Return the next item for this iterator, or ScriptValueP() if there is no such item
|
||||
virtual ScriptValueP next() = 0;
|
||||
@@ -129,7 +130,10 @@ class ScriptCollection : public ScriptValue {
|
||||
}
|
||||
virtual int itemCount() const { return (int)value->size(); }
|
||||
/// Collections can be compared by comparing pointers
|
||||
virtual const void* comparePointer() const { return value; }
|
||||
virtual CompareWhat compareAs(String&, void const*& compare_ptr) const {
|
||||
compare_ptr = value;
|
||||
return COMPARE_AS_POINTER;
|
||||
}
|
||||
private:
|
||||
/// Store a pointer to a collection, collections are only ever used for structures owned outside the script
|
||||
const Collection* value;
|
||||
@@ -168,12 +172,15 @@ class ScriptMap : public ScriptValue {
|
||||
return get_member(*value, name);
|
||||
}
|
||||
virtual int itemCount() const { return (int)value->size(); }
|
||||
/// Collections can be compared by comparing pointers
|
||||
virtual const void* comparePointer() const { return value; }
|
||||
virtual ScriptValueP dependencyMember(const String& name, const Dependency& dep) const {
|
||||
mark_dependency_member(*value, name, dep);
|
||||
return getMember(name);
|
||||
}
|
||||
/// Collections can be compared by comparing pointers
|
||||
virtual CompareWhat compareAs(String&, void const*& compare_ptr) const {
|
||||
compare_ptr = value;
|
||||
return COMPARE_AS_POINTER;
|
||||
}
|
||||
private:
|
||||
/// Store a pointer to a collection, collections are only ever used for structures owned outside the script
|
||||
const Collection* value;
|
||||
@@ -190,7 +197,10 @@ class ScriptCustomCollection : public ScriptValue {
|
||||
virtual ScriptValueP makeIterator(const ScriptValueP& thisP) const;
|
||||
virtual int itemCount() const { return (int)value.size(); }
|
||||
/// Collections can be compared by comparing pointers
|
||||
virtual const void* comparePointer() const { return &value; }
|
||||
virtual CompareWhat compareAs(String&, void const*& compare_ptr) const {
|
||||
compare_ptr = this;
|
||||
return COMPARE_AS_POINTER;
|
||||
}
|
||||
|
||||
/// The collection as a list (contains all values)
|
||||
vector<ScriptValueP> value;
|
||||
@@ -198,6 +208,28 @@ class ScriptCustomCollection : public ScriptValue {
|
||||
map<String,ScriptValueP> key_value;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : Collections : concatenation
|
||||
|
||||
/// Script value containing the concatenation of two collections
|
||||
class ScriptConcatCollection : public ScriptValue {
|
||||
public:
|
||||
inline ScriptConcatCollection(ScriptValueP a, ScriptValueP b) : a(a), b(b) {}
|
||||
virtual ScriptType type() const { return SCRIPT_COLLECTION; }
|
||||
virtual String typeName() const { return _TYPE_("collection"); }
|
||||
virtual ScriptValueP getMember(const String& name) const;
|
||||
virtual ScriptValueP makeIterator(const ScriptValueP& thisP) const;
|
||||
virtual int itemCount() const { return a->itemCount() + b->itemCount(); }
|
||||
/// Collections can be compared by comparing pointers
|
||||
virtual CompareWhat compareAs(String&, void const*& compare_ptr) const {
|
||||
compare_ptr = this;
|
||||
return COMPARE_AS_POINTER;
|
||||
}
|
||||
|
||||
private:
|
||||
ScriptValueP a,b;
|
||||
friend class ScriptConcatCollectionIterator;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : Objects
|
||||
|
||||
/// Script value containing an object (pointer)
|
||||
@@ -238,7 +270,15 @@ class ScriptObject : public ScriptValue {
|
||||
return i >= 0 ? i : ScriptValue::itemCount();
|
||||
}
|
||||
/// Objects can be compared by comparing pointers
|
||||
virtual const void* comparePointer() const { return &*value; }
|
||||
virtual CompareWhat compareAs(String& compare_str, void const*& compare_ptr) const {
|
||||
ScriptValueP d = getDefault();
|
||||
if (d) {
|
||||
return d->compareAs(compare_str, compare_ptr);
|
||||
} else {
|
||||
compare_ptr = &*value;
|
||||
return COMPARE_AS_POINTER;
|
||||
}
|
||||
}
|
||||
/// Get access to the value
|
||||
inline T getValue() const { return value; }
|
||||
private:
|
||||
|
||||
+64
-2
@@ -24,11 +24,38 @@ ScriptValueP ScriptValue::getMember(const String& name) const { return delay
|
||||
ScriptValueP ScriptValue::next() { throw InternalError(_("Can't convert from ")+typeName()+_(" to iterator")); }
|
||||
ScriptValueP ScriptValue::makeIterator(const ScriptValueP&) const { return delayError(_ERROR_2_("can't convert", typeName(), _TYPE_("collection"))); }
|
||||
int ScriptValue::itemCount() const { throw ScriptError(_ERROR_2_("can't convert", typeName(), _TYPE_("collection"))); }
|
||||
const void* ScriptValue::comparePointer() const { return nullptr; }
|
||||
CompareWhat ScriptValue::compareAs(String& compare_str, void const*& compare_ptr) const {
|
||||
compare_str = (String)(*this);
|
||||
return COMPARE_AS_STRING;
|
||||
}
|
||||
|
||||
ScriptValueP ScriptValue::dependencyMember(const String& name, const Dependency&) const { return dependency_dummy; }
|
||||
ScriptValueP ScriptValue::dependencies(Context&, const Dependency&) const { return dependency_dummy; }
|
||||
|
||||
/// compare script values for equallity
|
||||
bool equal(const ScriptValue& a, const ScriptValue& b) {
|
||||
if (&a == &b) return true;
|
||||
ScriptType at = a.type(), bt = b.type();
|
||||
if (at == bt && at == SCRIPT_INT) {
|
||||
return (int)a == (int)b;
|
||||
} else if ((at == SCRIPT_INT || at == SCRIPT_DOUBLE) &&
|
||||
(bt == SCRIPT_INT || bt == SCRIPT_DOUBLE)) {
|
||||
return (double)a == (double)b;
|
||||
} else {
|
||||
String as, bs;
|
||||
const void* ap, *bp;
|
||||
CompareWhat aw = a.compareAs(as, ap);
|
||||
CompareWhat bw = b.compareAs(bs, bp);
|
||||
// compare pointers or strings
|
||||
if (aw == COMPARE_AS_STRING || bw == COMPARE_AS_STRING) {
|
||||
return as == bs;
|
||||
} else if (aw == COMPARE_AS_POINTER || bw == COMPARE_AS_POINTER) {
|
||||
return ap == bp;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Errors
|
||||
|
||||
@@ -40,7 +67,7 @@ ScriptDelayedError::operator double() const { throw error; }
|
||||
ScriptDelayedError::operator int() const { throw error; }
|
||||
ScriptDelayedError::operator AColor() const { throw error; }
|
||||
int ScriptDelayedError::itemCount() const { throw error; }
|
||||
const void* ScriptDelayedError::comparePointer() const { throw error; }
|
||||
CompareWhat ScriptDelayedError::compareAs(String&, void const*&) const { throw error; }
|
||||
ScriptValueP ScriptDelayedError::getMember(const String&) const { return new_intrusive1<ScriptDelayedError>(error); }
|
||||
ScriptValueP ScriptDelayedError::dependencyMember(const String&, const Dependency&) const { return new_intrusive1<ScriptDelayedError>(error); }
|
||||
ScriptValueP ScriptDelayedError::eval(Context&) const { return new_intrusive1<ScriptDelayedError>(error); }
|
||||
@@ -52,6 +79,7 @@ ScriptValueP ScriptDelayedError::makeIterator(const ScriptValueP& thisP) const
|
||||
|
||||
ScriptType ScriptIterator::type() const { return SCRIPT_ITERATOR; }
|
||||
String ScriptIterator::typeName() const { return _("iterator"); }
|
||||
CompareWhat ScriptIterator::compareAs(String&, void const*&) const { return COMPARE_NO; }
|
||||
|
||||
// Iterator over a range of integers
|
||||
class ScriptRangeIterator : public ScriptIterator {
|
||||
@@ -295,3 +323,37 @@ ScriptValueP ScriptCustomCollection::makeIterator(const ScriptValueP& thisP) con
|
||||
return new_intrusive2<ScriptCustomCollectionIterator>(&value, thisP);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Concat collection
|
||||
|
||||
// Iterator over a concatenated collection
|
||||
class ScriptConcatCollectionIterator : public ScriptIterator {
|
||||
public:
|
||||
ScriptConcatCollectionIterator(const ScriptValueP& itA, const ScriptValueP& itB)
|
||||
: itA(itA), itB(itB) {}
|
||||
virtual ScriptValueP next() {
|
||||
if (itA) {
|
||||
ScriptValueP v = itA->next();
|
||||
if (v) return v;
|
||||
else itA = ScriptValueP();
|
||||
}
|
||||
return itB->next();
|
||||
}
|
||||
private:
|
||||
ScriptValueP itA, itB;
|
||||
};
|
||||
|
||||
ScriptValueP ScriptConcatCollection::getMember(const String& name) const {
|
||||
ScriptValueP member = a->getMember(name);
|
||||
if (member->type() != SCRIPT_ERROR) return member;
|
||||
long index;
|
||||
int itemsInA = a->itemCount();
|
||||
if (name.ToLong(&index) && index - itemsInA >= 0 && index - itemsInA < b->itemCount()) {
|
||||
// adjust integer index
|
||||
return b->getMember(String() << (index - itemsInA));
|
||||
} else {
|
||||
return b->getMember(name);
|
||||
}
|
||||
}
|
||||
ScriptValueP ScriptConcatCollection::makeIterator(const ScriptValueP& thisP) const {
|
||||
return new_intrusive2<ScriptConcatCollectionIterator>(a->makeIterator(a), b->makeIterator(b));
|
||||
}
|
||||
|
||||
+12
-3
@@ -33,6 +33,12 @@ enum ScriptType
|
||||
, SCRIPT_ERROR
|
||||
};
|
||||
|
||||
enum CompareWhat
|
||||
{ COMPARE_NO
|
||||
, COMPARE_AS_STRING
|
||||
, COMPARE_AS_POINTER
|
||||
};
|
||||
|
||||
/// A value that can be handled by the scripting engine.
|
||||
/// Actual values are derived types
|
||||
class ScriptValue : public IntrusivePtrBaseWithDelete {
|
||||
@@ -43,9 +49,9 @@ class ScriptValue : public IntrusivePtrBaseWithDelete {
|
||||
virtual ScriptType type() const = 0;
|
||||
/// Name of the type of value
|
||||
virtual String typeName() const = 0;
|
||||
/// A pointer that uniquely identifies the value, used for comparing.
|
||||
/** If implementation is not possible, should return nullptr. */
|
||||
virtual const void* comparePointer() const;
|
||||
/// How to compare this object?
|
||||
/** Returns 1 if the pointer should be used, 0 otherwise */
|
||||
virtual CompareWhat compareAs(String& compare_str, void const*& compare_ptr) const;
|
||||
|
||||
/// Convert this value to a string
|
||||
virtual operator String() const;
|
||||
@@ -89,5 +95,8 @@ extern ScriptValueP script_true; ///< The preallocated true value
|
||||
extern ScriptValueP script_false; ///< The preallocated false value
|
||||
extern ScriptValueP dependency_dummy; ///< Dummy value used during dependency analysis
|
||||
|
||||
/// compare script values for equallity
|
||||
bool equal(const ScriptValue& a, const ScriptValue& b);
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
#endif
|
||||
|
||||
@@ -131,7 +131,7 @@ function highlight_script($code) {
|
||||
while(strlen($code)) {
|
||||
if (preg_match("@^<[^>]+>@",$code, $matches)) {
|
||||
$ret .= $matches[0]; // plain tag
|
||||
} else if (preg_match("@^(if|then|else|for( each)?|in(?= )|do|mod|and|or|xor|not|rgb|rgba|from|to)\b(?!:)@",$code, $matches)) {
|
||||
} else if (preg_match("@^(if|then|else|for( each)?|in(?= )|do|div|mod|and|or|xor|not|rgb|rgba|from|to)\b(?!:)@",$code, $matches)) {
|
||||
$ret .= "<span class='hl-kw'>" . $matches[0] . "</span>";
|
||||
} else if (preg_match("@^(include file:)(.*)@",$code, $matches)) {
|
||||
$ret .= "<span class='hl-key'>" . $matches[1] . "</span>" . $matches[2];
|
||||
|
||||
Reference in New Issue
Block a user