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:
@"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:
......
......@@ -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 */
......
......@@ -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
......
......@@ -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:
......
......@@ -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));
}
......@@ -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];
......
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