Commit d1afd16c authored by twanvl's avatar twanvl

"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.
parent 66e7a4d3
...@@ -9,8 +9,8 @@ MSE script supports most basic mathamatical operators: ...@@ -9,8 +9,8 @@ MSE script supports most basic mathamatical operators:
@"3" + "2" == "32"@ concatenate two strings or compose two functions (see below) @"3" + "2" == "32"@ concatenate two strings or compose two functions (see below)
| @a - b@ @3 - 2 == 1@ Substract two numbers | @a - b@ @3 - 2 == 1@ Substract two numbers
| @a * b@ @3 * 2 == 6@ Multiply 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/> | @a / b@ @3 / 2 == 1.5@ Divide two numbers. Does not round, always produces a [[type:double]].
@3 / 2.0 == 1.5@ Does not round for [[type:double]]s. | @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 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) | @-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@ ...@@ -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? @"x" <= "y"@ Is a less than b or are they equal?
| @a >= b@ @2 >= 1@<br/> | @a >= b@ @2 >= 1@<br/>
@"x" >= "x"@ Is a greater than b or are they equal? @"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-- --Booleans--
[[type:Boolean]]s (for example from comparisons) can be combined using: [[type:Boolean]]s (for example from comparisons) can be combined using:
...@@ -61,7 +63,7 @@ The exact order of precedence is given in the following table, ...@@ -61,7 +63,7 @@ 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 @+@. 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(...)@, @a.b@, @a[b]@ Function calls, property access, see below
| @-a@, @not@ Unary operators | @-a@, @not@ Unary operators
| @*@, @/@, @mod@ Multiplication and division | @*@, @/@, @div@, @mod@ Multiplication and division
| @+@, @-@ Addition and substraction | @+@, @-@ Addition and substraction
| @==@, @!=@, @<@, @>@, @<=@, @>=@ Comparisons | @==@, @!=@, @<@, @>@, @<=@, @>=@ Comparisons
| @and@, @or@, @xor@ Boolean operators | @and@, @or@, @xor@ Boolean operators
......
...@@ -138,6 +138,12 @@ ScriptValueP Context::eval(const Script& script, bool useScope) { ...@@ -138,6 +138,12 @@ ScriptValueP Context::eval(const Script& script, bool useScope) {
break; break;
} }
// Closure object
case I_CLOSURE: {
makeClosure(i.data);
break;
}
// Simple instruction: unary // Simple instruction: unary
case I_UNARY: { case I_UNARY: {
instrUnary(i.instr1, stack.back()); instrUnary(i.instr1, stack.back());
...@@ -278,18 +284,6 @@ void instrUnary (UnaryInstructionType i, ScriptValueP& a) { ...@@ -278,18 +284,6 @@ void instrUnary (UnaryInstructionType i, ScriptValueP& a) {
} \ } \
break 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 /// Composition of two functions
class ScriptCompose : public ScriptValue { class ScriptCompose : public ScriptValue {
public: public:
...@@ -330,6 +324,8 @@ void instrBinary (BinaryInstructionType i, ScriptValueP& a, const ScriptValueP& ...@@ -330,6 +324,8 @@ void instrBinary (BinaryInstructionType i, ScriptValueP& a, const ScriptValueP&
// a = a; // a = a;
} else if (at == SCRIPT_FUNCTION && bt == SCRIPT_FUNCTION) { } else if (at == SCRIPT_FUNCTION && bt == SCRIPT_FUNCTION) {
a = new_intrusive2<ScriptCompose>(a, b); 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) { } else if (at == SCRIPT_INT && bt == SCRIPT_INT) {
a = to_script((int)*a + (int)*b); a = to_script((int)*a + (int)*b);
} else if ((at == SCRIPT_INT || at == SCRIPT_DOUBLE) && } else if ((at == SCRIPT_INT || at == SCRIPT_DOUBLE) &&
...@@ -341,7 +337,16 @@ void instrBinary (BinaryInstructionType i, ScriptValueP& a, const ScriptValueP& ...@@ -341,7 +337,16 @@ void instrBinary (BinaryInstructionType i, ScriptValueP& a, const ScriptValueP&
break; break;
case I_SUB: OPERATOR_DI(-); case I_SUB: OPERATOR_DI(-);
case I_MUL: 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: case I_MOD:
if (at == SCRIPT_DOUBLE || bt == SCRIPT_DOUBLE) { if (at == SCRIPT_DOUBLE || bt == SCRIPT_DOUBLE) {
a = to_script(fmod((double)*a, (double)*b)); a = to_script(fmod((double)*a, (double)*b));
...@@ -352,8 +357,8 @@ void instrBinary (BinaryInstructionType i, ScriptValueP& a, const ScriptValueP& ...@@ -352,8 +357,8 @@ void instrBinary (BinaryInstructionType i, ScriptValueP& a, const ScriptValueP&
case I_AND: OPERATOR_I(&&); case I_AND: OPERATOR_I(&&);
case I_OR: OPERATOR_I(||); case I_OR: OPERATOR_I(||);
case I_XOR: a = to_script((bool)*a != (bool)*b); break; case I_XOR: a = to_script((bool)*a != (bool)*b); break;
case I_EQ: OPERATOR_SDI(==); case I_EQ: a = to_script( equal(*a,*b)); break;
case I_NEQ: OPERATOR_SDI(!=); case I_NEQ: a = to_script(!equal(*a,*b)); break;
case I_LT: OPERATOR_DI(<); case I_LT: OPERATOR_DI(<);
case I_GT: OPERATOR_DI(>); case I_GT: OPERATOR_DI(>);
case I_LE: OPERATOR_DI(<=); case I_LE: OPERATOR_DI(<=);
...@@ -386,7 +391,7 @@ void instrQuaternary(QuaternaryInstructionType i, ScriptValueP& a, const ScriptV ...@@ -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) { void Context::makeObject(size_t n) {
intrusive_ptr<ScriptCustomCollection> ret(new ScriptCustomCollection()); intrusive_ptr<ScriptCustomCollection> ret(new ScriptCustomCollection());
...@@ -402,3 +407,10 @@ void Context::makeObject(size_t n) { ...@@ -402,3 +407,10 @@ void Context::makeObject(size_t n) {
stack.resize(begin); stack.resize(begin);
stack.push_back(ret); 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 { ...@@ -101,6 +101,8 @@ class Context {
void resetBindings(size_t scope); void resetBindings(size_t scope);
/// Make an object with n elements, popping 2n values from the stack, and push it onto the stack /// Make an object with n elements, popping 2n values from the stack, and push it onto the stack
void makeObject(size_t n); void makeObject(size_t n);
/// Make a closure with n arguments
void makeClosure(size_t n);
}; };
/// A class that creates a local scope /// A class that creates a local scope
......
...@@ -250,6 +250,12 @@ ScriptValueP Context::dependencies(const Dependency& dep, const Script& script) ...@@ -250,6 +250,12 @@ ScriptValueP Context::dependencies(const Dependency& dep, const Script& script)
break; break;
} }
// Closure object
case I_CLOSURE: {
makeClosure(i.data);
break;
}
// Get a variable (almost as normal) // Get a variable (almost as normal)
case I_GET_VAR: { case I_GET_VAR: {
ScriptValueP value = variables[i.data].value; ScriptValueP value = variables[i.data].value;
......
...@@ -173,24 +173,6 @@ SCRIPT_FUNCTION(remove_tags) { ...@@ -173,24 +173,6 @@ SCRIPT_FUNCTION(remove_tags) {
// ----------------------------------------------------------------------------- : Collection stuff // ----------------------------------------------------------------------------- : 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 /// position of some element in a vector
/** 0 based index, -1 if not found */ /** 0 based index, -1 if not found */
......
...@@ -112,7 +112,7 @@ class TokenIterator { ...@@ -112,7 +112,7 @@ class TokenIterator {
bool isAlpha_(Char c) { return isAlpha(c) || c==_('_'); } bool isAlpha_(Char c) { return isAlpha(c) || c==_('_'); }
bool isAlnum_(Char c) { return isAlnum(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==_(','); } c==_(':') || c==_('=') || c==_('<') || c==_('>') || c==_(';') || c==_(','); }
bool isLparen(Char c) { return c==_('(') || c==_('[') || c==_('{'); } bool isLparen(Char c) { return c==_('(') || c==_('[') || c==_('{'); }
bool isRparen(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 ...@@ -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_ADD);
else if (minPrec <= PREC_ADD && token==_("-")) parseOper(input, script, PREC_MUL, I_BINARY, I_SUB); 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_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_MUL && token==_("mod")) parseOper(input, script, PREC_UNARY, I_BINARY, I_MOD);
else if (minPrec <= PREC_FUN && token==_(".")) { // get member by name else if (minPrec <= PREC_FUN && token==_(".")) { // get member by name
const Token& token = input.read(); const Token& token = input.read();
...@@ -655,6 +656,16 @@ void parseOper(TokenIterator& input, Script& script, Precedence minPrec, Instruc ...@@ -655,6 +656,16 @@ void parseOper(TokenIterator& input, Script& script, Precedence minPrec, Instruc
FOR_EACH(arg,arguments) { FOR_EACH(arg,arguments) {
script.addInstruction(I_NOP, arg); 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==_("\"{")) { } else if (minPrec <= PREC_STRING && token==_("\"{")) {
// for smart strings: "x" {{ e }} "y" // for smart strings: "x" {{ e }} "y"
// optimize: "" + e -> e // optimize: "" + e -> e
......
...@@ -154,6 +154,7 @@ String Script::dumpInstr(unsigned int pos, Instruction i) const { ...@@ -154,6 +154,7 @@ String Script::dumpInstr(unsigned int pos, Instruction i) const {
case I_LOOP: ret += _("loop"); break; case I_LOOP: ret += _("loop"); break;
case I_MAKE_OBJECT: ret += _("make object");break; case I_MAKE_OBJECT: ret += _("make object");break;
case I_CALL: ret += _("call"); break; case I_CALL: ret += _("call"); break;
case I_CLOSURE: ret += _("closure"); break;
case I_UNARY: ret += _("unary\t"); case I_UNARY: ret += _("unary\t");
switch (i.instr1) { switch (i.instr1) {
case I_ITERATOR_C: ret += _("iterator_c"); break; case I_ITERATOR_C: ret += _("iterator_c"); break;
...@@ -200,7 +201,7 @@ String Script::dumpInstr(unsigned int pos, Instruction i) const { ...@@ -200,7 +201,7 @@ String Script::dumpInstr(unsigned int pos, Instruction i) const {
ret += _("\t") + constants[i.data]->toString(); ret += _("\t") + constants[i.data]->toString();
ret += _("\t(") + constants[i.data]->typeName() + _(")"); ret += _("\t(") + constants[i.data]->typeName() + _(")");
break; 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); ret += String::Format(_("\t%d"), i.data);
break; break;
case I_GET_VAR: case I_SET_VAR: case I_NOP: // variable 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) ...@@ -235,7 +236,7 @@ const Instruction* Script::backtraceSkip(const Instruction* instr, int to_skip)
to_skip += 2; break; // nett stack effect 1-3 == -2 to_skip += 2; break; // nett stack effect 1-3 == -2
case I_QUATERNARY: case I_QUATERNARY:
to_skip += 3; break; // nett stack effect 1-4 == -3 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 to_skip += instr->data; // arguments of call
break; break;
case I_MAKE_OBJECT: case I_MAKE_OBJECT:
...@@ -299,13 +300,15 @@ String Script::instructionName(const Instruction* instr) const { ...@@ -299,13 +300,15 @@ String Script::instructionName(const Instruction* instr) const {
+ _(".") + _(".")
+ constants[instr->data]->toString(); + constants[instr->data]->toString();
} else if (instr->instr == I_BINARY && instr->instr2 == I_MEMBER) { } else if (instr->instr == I_BINARY && instr->instr2 == I_MEMBER) {
throw _("??\?[...]"); return _("??\?[...]");
} else if (instr->instr == I_BINARY && instr->instr2 == I_ADD) { } else if (instr->instr == I_BINARY && instr->instr2 == I_ADD) {
return _("??? + ???"); return _("??? + ???");
} else if (instr->instr == I_NOP) { } else if (instr->instr == I_NOP) {
return _("??\?(...)"); return _("??\?(...)");
} else if (instr->instr == I_CALL) { } else if (instr->instr == I_CALL) {
return instructionName(backtraceSkip(instr - 1, instr->data)) + _("(...)"); return instructionName(backtraceSkip(instr - 1, instr->data)) + _("(...)");
} else if (instr->instr == I_CLOSURE) {
return instructionName(backtraceSkip(instr - 1, instr->data)) + _("@(...)");
} else { } else {
return _("??\?"); return _("??\?");
} }
......
...@@ -35,11 +35,12 @@ enum InstructionType ...@@ -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 , I_MAKE_OBJECT = 8 ///< arg = int : make a list/map with n elements, pops 2n values of the stack, n key/value pairs
// Functions // 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_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 // Simple instructions
, I_UNARY = 10 ///< arg = 1ary instr : pop 1 value, apply a function, push the result , I_UNARY = 11 ///< 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_BINARY = 12 ///< 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_TERNARY = 13 ///< 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_QUATERNARY = 14 ///< arg = 4ary instr : pop 4 values, apply a function, push the result
}; };
/// Types of unary instructions (taking one argument from the stack) /// Types of unary instructions (taking one argument from the stack)
...@@ -58,7 +59,8 @@ enum BinaryInstructionType ...@@ -58,7 +59,8 @@ enum BinaryInstructionType
, I_ADD ///< add , I_ADD ///< add
, I_SUB ///< subtract , I_SUB ///< subtract
, I_MUL ///< multiply , I_MUL ///< multiply
, I_DIV ///< divide , I_FDIV ///< floating point division
, I_DIV ///< integer division
, I_MOD ///< modulus , I_MOD ///< modulus
// Logical // Logical
, I_AND ///< logical and , I_AND ///< logical and
......
...@@ -61,7 +61,7 @@ class ScriptDelayedError : public ScriptValue { ...@@ -61,7 +61,7 @@ class ScriptDelayedError : public ScriptValue {
virtual operator int() const; virtual operator int() const;
virtual operator AColor() const; virtual operator AColor() const;
virtual int itemCount() const; virtual int itemCount() const;
virtual const void* comparePointer() const; virtual CompareWhat compareAs(String&, void const*&) const;
// these can propagate the error // these can propagate the error
virtual ScriptValueP getMember(const String& name) const; virtual ScriptValueP getMember(const String& name) const;
virtual ScriptValueP dependencyMember(const String& name, const Dependency&) const; virtual ScriptValueP dependencyMember(const String& name, const Dependency&) const;
...@@ -82,6 +82,7 @@ inline ScriptValueP delayError(const String& m) { ...@@ -82,6 +82,7 @@ inline ScriptValueP delayError(const String& m) {
struct ScriptIterator : public ScriptValue { struct ScriptIterator : public ScriptValue {
virtual ScriptType type() const;// { return SCRIPT_ITERATOR; } virtual ScriptType type() const;// { return SCRIPT_ITERATOR; }
virtual String typeName() const;// { return "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 /// Return the next item for this iterator, or ScriptValueP() if there is no such item
virtual ScriptValueP next() = 0; virtual ScriptValueP next() = 0;
...@@ -129,7 +130,10 @@ class ScriptCollection : public ScriptValue { ...@@ -129,7 +130,10 @@ class ScriptCollection : public ScriptValue {
} }
virtual int itemCount() const { return (int)value->size(); } virtual int itemCount() const { return (int)value->size(); }
/// Collections can be compared by comparing pointers /// 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: private:
/// Store a pointer to a collection, collections are only ever used for structures owned outside the script /// Store a pointer to a collection, collections are only ever used for structures owned outside the script
const Collection* value; const Collection* value;
...@@ -168,12 +172,15 @@ class ScriptMap : public ScriptValue { ...@@ -168,12 +172,15 @@ class ScriptMap : public ScriptValue {
return get_member(*value, name); return get_member(*value, name);
} }
virtual int itemCount() const { return (int)value->size(); } 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 { virtual ScriptValueP dependencyMember(const String& name, const Dependency& dep) const {
mark_dependency_member(*value, name, dep); mark_dependency_member(*value, name, dep);
return getMember(name); 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: private:
/// Store a pointer to a collection, collections are only ever used for structures owned outside the script /// Store a pointer to a collection, collections are only ever used for structures owned outside the script
const Collection* value; const Collection* value;
...@@ -190,7 +197,10 @@ class ScriptCustomCollection : public ScriptValue { ...@@ -190,7 +197,10 @@ class ScriptCustomCollection : public ScriptValue {
virtual ScriptValueP makeIterator(const ScriptValueP& thisP) const; virtual ScriptValueP makeIterator(const ScriptValueP& thisP) const;
virtual int itemCount() const { return (int)value.size(); } virtual int itemCount() const { return (int)value.size(); }
/// Collections can be compared by comparing pointers /// 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) /// The collection as a list (contains all values)
vector<ScriptValueP> value; vector<ScriptValueP> value;
...@@ -198,6 +208,28 @@ class ScriptCustomCollection : public ScriptValue { ...@@ -198,6 +208,28 @@ class ScriptCustomCollection : public ScriptValue {
map<String,ScriptValueP> key_value; 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 // ----------------------------------------------------------------------------- : Objects
/// Script value containing an object (pointer) /// Script value containing an object (pointer)
...@@ -238,7 +270,15 @@ class ScriptObject : public ScriptValue { ...@@ -238,7 +270,15 @@ class ScriptObject : public ScriptValue {
return i >= 0 ? i : ScriptValue::itemCount(); return i >= 0 ? i : ScriptValue::itemCount();
} }
/// Objects can be compared by comparing pointers /// 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 /// Get access to the value
inline T getValue() const { return value; } inline T getValue() const { return value; }
private: private:
......
...@@ -24,11 +24,38 @@ ScriptValueP ScriptValue::getMember(const String& name) const { return delay ...@@ -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::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"))); } 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"))); } 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::dependencyMember(const String& name, const Dependency&) const { return dependency_dummy; }
ScriptValueP ScriptValue::dependencies(Context&, 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 // ----------------------------------------------------------------------------- : Errors
...@@ -40,7 +67,7 @@ ScriptDelayedError::operator double() const { throw error; } ...@@ -40,7 +67,7 @@ ScriptDelayedError::operator double() const { throw error; }
ScriptDelayedError::operator int() const { throw error; } ScriptDelayedError::operator int() const { throw error; }
ScriptDelayedError::operator AColor() const { throw error; } ScriptDelayedError::operator AColor() const { throw error; }
int ScriptDelayedError::itemCount() 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::getMember(const String&) const { return new_intrusive1<ScriptDelayedError>(error); }
ScriptValueP ScriptDelayedError::dependencyMember(const String&, const Dependency&) 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); } ScriptValueP ScriptDelayedError::eval(Context&) const { return new_intrusive1<ScriptDelayedError>(error); }
...@@ -52,6 +79,7 @@ ScriptValueP ScriptDelayedError::makeIterator(const ScriptValueP& thisP) const ...@@ -52,6 +79,7 @@ ScriptValueP ScriptDelayedError::makeIterator(const ScriptValueP& thisP) const
ScriptType ScriptIterator::type() const { return SCRIPT_ITERATOR; } ScriptType ScriptIterator::type() const { return SCRIPT_ITERATOR; }
String ScriptIterator::typeName() const { return _("iterator"); } String ScriptIterator::typeName() const { return _("iterator"); }
CompareWhat ScriptIterator::compareAs(String&, void const*&) const { return COMPARE_NO; }
// Iterator over a range of integers // Iterator over a range of integers
class ScriptRangeIterator : public ScriptIterator { class ScriptRangeIterator : public ScriptIterator {
...@@ -295,3 +323,37 @@ ScriptValueP ScriptCustomCollection::makeIterator(const ScriptValueP& thisP) con ...@@ -295,3 +323,37 @@ ScriptValueP ScriptCustomCollection::makeIterator(const ScriptValueP& thisP) con
return new_intrusive2<ScriptCustomCollectionIterator>(&value, thisP); 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));
}
...@@ -33,6 +33,12 @@ enum ScriptType ...@@ -33,6 +33,12 @@ enum ScriptType
, SCRIPT_ERROR , SCRIPT_ERROR
}; };
enum CompareWhat
{ COMPARE_NO
, COMPARE_AS_STRING
, COMPARE_AS_POINTER
};
/// A value that can be handled by the scripting engine. /// A value that can be handled by the scripting engine.
/// Actual values are derived types /// Actual values are derived types
class ScriptValue : public IntrusivePtrBaseWithDelete { class ScriptValue : public IntrusivePtrBaseWithDelete {
...@@ -43,9 +49,9 @@ class ScriptValue : public IntrusivePtrBaseWithDelete { ...@@ -43,9 +49,9 @@ class ScriptValue : public IntrusivePtrBaseWithDelete {
virtual ScriptType type() const = 0; virtual ScriptType type() const = 0;
/// Name of the type of value /// Name of the type of value
virtual String typeName() const = 0; virtual String typeName() const = 0;
/// A pointer that uniquely identifies the value, used for comparing. /// How to compare this object?
/** If implementation is not possible, should return nullptr. */ /** Returns 1 if the pointer should be used, 0 otherwise */
virtual const void* comparePointer() const; virtual CompareWhat compareAs(String& compare_str, void const*& compare_ptr) const;
/// Convert this value to a string /// Convert this value to a string
virtual operator String() const; virtual operator String() const;
...@@ -89,5 +95,8 @@ extern ScriptValueP script_true; ///< The preallocated true value ...@@ -89,5 +95,8 @@ extern ScriptValueP script_true; ///< The preallocated true value
extern ScriptValueP script_false; ///< The preallocated false value extern ScriptValueP script_false; ///< The preallocated false value
extern ScriptValueP dependency_dummy; ///< Dummy value used during dependency analysis extern ScriptValueP dependency_dummy; ///< Dummy value used during dependency analysis
/// compare script values for equallity
bool equal(const ScriptValue& a, const ScriptValue& b);
// ----------------------------------------------------------------------------- : EOF // ----------------------------------------------------------------------------- : EOF
#endif #endif
...@@ -131,7 +131,7 @@ function highlight_script($code) { ...@@ -131,7 +131,7 @@ function highlight_script($code) {
while(strlen($code)) { while(strlen($code)) {
if (preg_match("@^<[^>]+>@",$code, $matches)) { if (preg_match("@^<[^>]+>@",$code, $matches)) {
$ret .= $matches[0]; // plain tag $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>"; $ret .= "<span class='hl-kw'>" . $matches[0] . "</span>";
} else if (preg_match("@^(include file:)(.*)@",$code, $matches)) { } else if (preg_match("@^(include file:)(.*)@",$code, $matches)) {
$ret .= "<span class='hl-key'>" . $matches[1] . "</span>" . $matches[2]; $ret .= "<span class='hl-key'>" . $matches[1] . "</span>" . $matches[2];
......
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