Commit a08e5c05 authored by twanvl's avatar twanvl

Added 'or else' construct to script language.

parent 224f6238
...@@ -317,6 +317,9 @@ void instrBinary (BinaryInstructionType i, ScriptValueP& a, const ScriptValueP& ...@@ -317,6 +317,9 @@ void instrBinary (BinaryInstructionType i, ScriptValueP& a, const ScriptValueP&
case I_GT: OPERATOR_DI(>); case I_GT: OPERATOR_DI(>);
case I_LE: OPERATOR_DI(<=); case I_LE: OPERATOR_DI(<=);
case I_GE: OPERATOR_DI(>=); case I_GE: OPERATOR_DI(>=);
case I_OR_ELSE:
if (at == SCRIPT_ERROR) a = b;
break;
} }
} }
......
...@@ -12,18 +12,31 @@ ...@@ -12,18 +12,31 @@
#include <util/dynamic_arg.hpp> #include <util/dynamic_arg.hpp>
#include <util/io/package.hpp> #include <util/io/package.hpp>
#include <gfx/generated_image.hpp> #include <gfx/generated_image.hpp>
#include <data/field/image.hpp>
// ----------------------------------------------------------------------------- : Utility // ----------------------------------------------------------------------------- : Utility
// convert any script value to a GeneratedImageP // convert any script value to a GeneratedImageP
GeneratedImageP image_from_script(const ScriptValueP& value) { GeneratedImageP image_from_script(const ScriptValueP& value) {
if (value->type() == SCRIPT_STRING) { ScriptType t = value->type();
return new_intrusive1<PackagedImage>(value->toString()); if (t == SCRIPT_IMAGE) {
} else {
GeneratedImageP img = dynamic_pointer_cast<GeneratedImage>(value); GeneratedImageP img = dynamic_pointer_cast<GeneratedImage>(value);
if (!img) throw ScriptError(_ERROR_2_("can't convert", value->typeName(), _TYPE_("image"))); if (img) return img;
return img; } else if (t == SCRIPT_STRING) {
return new_intrusive1<PackagedImage>(value->toString());
} else if (t == SCRIPT_NIL) {
return new_intrusive<BlankImage>();
} else if (t == SCRIPT_OBJECT) {
// maybe it's an image value?
intrusive_ptr<ScriptObject<ValueP> > v = dynamic_pointer_cast<ScriptObject<ValueP> >(value);
if (v) {
ImageValueP iv = dynamic_pointer_cast<ImageValue>(v->getValue());
if (iv) {
return new_intrusive2<ImageValueToImage>(iv->filename, iv->last_update);
}
}
} }
throw ScriptError(_ERROR_2_("can't convert", value->typeName(), _TYPE_("image")));
} }
// ----------------------------------------------------------------------------- : ScriptableImage // ----------------------------------------------------------------------------- : ScriptableImage
...@@ -100,6 +113,15 @@ bool ScriptableImage::update(Context& ctx) { ...@@ -100,6 +113,15 @@ bool ScriptableImage::update(Context& ctx) {
} }
} }
ScriptP ScriptableImage::getScriptP() {
if (script) return script.getScriptP();
// return value or a blank image
ScriptP s(new Script);
s->addInstruction(I_PUSH_CONST, value ? static_pointer_cast<ScriptValue>(value) : script_nil);
s->addInstruction(I_RET);
return s;
}
// ----------------------------------------------------------------------------- : Reflection // ----------------------------------------------------------------------------- : Reflection
// we need some custom io, because the behaviour is different for each of Reader/Writer/GetMember // we need some custom io, because the behaviour is different for each of Reader/Writer/GetMember
......
...@@ -29,10 +29,12 @@ class ScriptableImage { ...@@ -29,10 +29,12 @@ class ScriptableImage {
inline ScriptableImage(const String& script) : script(script) {} inline ScriptableImage(const String& script) : script(script) {}
inline ScriptableImage(const GeneratedImageP& gen) : value(gen) {} inline ScriptableImage(const GeneratedImageP& gen) : value(gen) {}
/// Is there an image set? /// Is there a scripted image set?
inline bool isScripted() const { return script; } inline bool isScripted() const { return script; }
/// Is there an image generator available? /// Is there an image generator available?
inline bool isReady() const { return value; } inline bool isReady() const { return value; }
/// Is there an image set?
inline bool isSet() const { return script || value; }
/// Generate an image. /// Generate an image.
Image generate(const GeneratedImage::Options& options, bool cache = false) const; Image generate(const GeneratedImage::Options& options, bool cache = false) const;
...@@ -49,6 +51,11 @@ class ScriptableImage { ...@@ -49,6 +51,11 @@ class ScriptableImage {
/// Can this be safely generated from another thread? /// Can this be safely generated from another thread?
inline bool threadSafe() const { return !value || value->threadSafe(); } inline bool threadSafe() const { return !value || value->threadSafe(); }
/// Get access to the script, be careful
inline Script& getScript() { return script.getScript(); }
/// Get access to the script, always returns a valid script
ScriptP getScriptP();
private: private:
OptionalScript script; ///< The script, not really optional OptionalScript script; ///< The script, not really optional
GeneratedImageP value; ///< The image generator GeneratedImageP value; ///< The image generator
......
...@@ -399,7 +399,7 @@ void parseExpr(TokenIterator& input, Script& script, Precedence minPrec) { ...@@ -399,7 +399,7 @@ void parseExpr(TokenIterator& input, Script& script, Precedence minPrec) {
// implicit numbered element // implicit numbered element
script.addInstruction(I_PUSH_CONST, script_nil); script.addInstruction(I_PUSH_CONST, script_nil);
} }
parseOper(input, script, PREC_SEQ); parseOper(input, script, PREC_AND);
++count; ++count;
t = input.peek(); t = input.peek();
if (t == _(",")) { if (t == _(",")) {
...@@ -419,10 +419,12 @@ void parseExpr(TokenIterator& input, Script& script, Precedence minPrec) { ...@@ -419,10 +419,12 @@ void parseExpr(TokenIterator& input, Script& script, Precedence minPrec) {
script.addInstruction(I_PUSH_CONST, script_true); // boolean constant : true script.addInstruction(I_PUSH_CONST, script_true); // boolean constant : true
} else if (token == _("false")) { } else if (token == _("false")) {
script.addInstruction(I_PUSH_CONST, script_false); // boolean constant : false script.addInstruction(I_PUSH_CONST, script_false); // boolean constant : false
} else if (token == _("nil")) {
script.addInstruction(I_PUSH_CONST, script_nil); // universal constant : nil
} else if (token == _("if")) { } else if (token == _("if")) {
// if AAA then BBB else CCC // if AAA then BBB else CCC
unsigned int jmpElse, jmpEnd; unsigned int jmpElse, jmpEnd;
parseOper(input, script, PREC_SET); // AAA parseOper(input, script, PREC_AND); // AAA
jmpElse = script.getLabel(); // jmp_else: jmpElse = script.getLabel(); // jmp_else:
script.addInstruction(I_JUMP_IF_NOT, 0xFFFF); // jnz lbl_else script.addInstruction(I_JUMP_IF_NOT, 0xFFFF); // jnz lbl_else
expectToken(input, _("then")); // then expectToken(input, _("then")); // then
...@@ -449,7 +451,7 @@ void parseExpr(TokenIterator& input, Script& script, Precedence minPrec) { ...@@ -449,7 +451,7 @@ void parseExpr(TokenIterator& input, Script& script, Precedence minPrec) {
input.expected(_("name")); input.expected(_("name"));
} }
expectToken(input, _("in")); // in expectToken(input, _("in")); // in
parseOper(input, script, PREC_SET); // BBB parseOper(input, script, PREC_AND); // BBB
script.addInstruction(I_UNARY, I_ITERATOR_C); // iterator_collection script.addInstruction(I_UNARY, I_ITERATOR_C); // iterator_collection
script.addInstruction(I_PUSH_CONST, script_nil); // push nil script.addInstruction(I_PUSH_CONST, script_nil); // push nil
lblStart = script.getLabel(); // lbl_start: lblStart = script.getLabel(); // lbl_start:
...@@ -465,9 +467,9 @@ void parseExpr(TokenIterator& input, Script& script, Precedence minPrec) { ...@@ -465,9 +467,9 @@ void parseExpr(TokenIterator& input, Script& script, Precedence minPrec) {
// for AAA from BBB to CCC do DDD // for AAA from BBB to CCC do DDD
Token name = input.read(); // AAA Token name = input.read(); // AAA
expectToken(input, _("from")); // from expectToken(input, _("from")); // from
parseOper(input, script, PREC_SET); // BBB parseOper(input, script, PREC_AND); // BBB
expectToken(input, _("to")); // to expectToken(input, _("to")); // to
parseOper(input, script, PREC_SET); // CCC parseOper(input, script, PREC_AND); // CCC
script.addInstruction(I_BINARY, I_ITERATOR_R); // iterator_range script.addInstruction(I_BINARY, I_ITERATOR_R); // iterator_range
script.addInstruction(I_PUSH_CONST, script_nil); // push nil script.addInstruction(I_PUSH_CONST, script_nil); // push nil
lblStart = script.getLabel(); // lbl_start: lblStart = script.getLabel(); // lbl_start:
...@@ -546,8 +548,21 @@ void parseOper(TokenIterator& input, Script& script, Precedence minPrec, Instruc ...@@ -546,8 +548,21 @@ void parseOper(TokenIterator& input, Script& script, Precedence minPrec, Instruc
parseOper(input, script, PREC_SET, I_SET_VAR, instr.data); parseOper(input, script, PREC_SET, I_SET_VAR, instr.data);
} }
else if (minPrec <= PREC_AND && token==_("and")) parseOper(input, script, PREC_CMP, I_BINARY, I_AND); else if (minPrec <= PREC_AND && token==_("and")) parseOper(input, script, PREC_CMP, I_BINARY, I_AND);
else if (minPrec <= PREC_AND && token==_("or" )) parseOper(input, script, PREC_CMP, I_BINARY, I_OR); else if (minPrec <= PREC_AND && token==_("or" )) {
else if (minPrec <= PREC_CMP && token==_("=")) parseOper(input, script, PREC_ADD, I_BINARY, I_EQ); Token t = input.peek();
if (t == _("else")) {// or else
input.read(); // skip else
parseOper(input, script, PREC_CMP, I_BINARY, I_OR_ELSE);
} else {
parseOper(input, script, PREC_CMP, I_BINARY, I_OR);
}
}
else if (minPrec <= PREC_CMP && token==_("=")) {
if (minPrec <= PREC_SET) {
input.add_error(_("Use of '=', did you mean ':=' or '=='?"));
}
parseOper(input, script, PREC_ADD, I_BINARY, I_EQ);
}
else if (minPrec <= PREC_CMP && token==_("==")) parseOper(input, script, PREC_ADD, I_BINARY, I_EQ); else if (minPrec <= PREC_CMP && token==_("==")) parseOper(input, script, PREC_ADD, I_BINARY, I_EQ);
else if (minPrec <= PREC_CMP && token==_("!=")) parseOper(input, script, PREC_ADD, I_BINARY, I_NEQ); else if (minPrec <= PREC_CMP && token==_("!=")) parseOper(input, script, PREC_ADD, I_BINARY, I_NEQ);
else if (minPrec <= PREC_CMP && token==_("<")) parseOper(input, script, PREC_ADD, I_BINARY, I_LT); else if (minPrec <= PREC_CMP && token==_("<")) parseOper(input, script, PREC_ADD, I_BINARY, I_LT);
......
...@@ -70,6 +70,8 @@ enum BinaryInstructionType ...@@ -70,6 +70,8 @@ enum BinaryInstructionType
, I_GT ///< operator > , I_GT ///< operator >
, I_LE ///< operator <= , I_LE ///< operator <=
, I_GE ///< operator >= , I_GE ///< operator >=
// Error handling
, I_OR_ELSE ///< if a != error then a else b
}; };
/// Types of ternary instructions (taking three arguments from the stack) /// Types of ternary instructions (taking three arguments from the stack)
......
...@@ -31,6 +31,37 @@ ScriptValueP make_iterator(const T& v) { ...@@ -31,6 +31,37 @@ ScriptValueP make_iterator(const T& v) {
template <typename T> template <typename T>
void mark_dependency_member(const T& value, const String& name, const Dependency& dep) {} void mark_dependency_member(const T& value, const String& name, const Dependency& dep) {}
// ----------------------------------------------------------------------------- : Errors
/// A delayed error message.
/** Only when trying to use the object will the error be thrown.
* This can be 'caught' by the "or else" construct
*/
class ScriptDelayedError : public ScriptValue {
public:
inline ScriptDelayedError(const ScriptError& error) : error(error) {}
virtual ScriptType type() const;// { return SCRIPT_ERROR; }
// all of these throw
virtual String typeName() const;
virtual operator String() const;
virtual operator double() const;
virtual operator int() const;
virtual operator Color() const;
virtual int itemCount() const;
virtual const void* comparePointer() const;
// these can propagate the error
virtual ScriptValueP getMember(const String& name) const;
virtual ScriptValueP dependencyMember(const String& name, const Dependency&) const;
virtual ScriptValueP eval(Context&) const;
virtual ScriptValueP dependencies(Context&, const Dependency&) const;
virtual ScriptValueP makeIterator(const ScriptValueP& thisP) const;
private:
ScriptError error; // the error message
};
// ----------------------------------------------------------------------------- : Iterators // ----------------------------------------------------------------------------- : Iterators
// Iterator over a collection // Iterator over a collection
......
...@@ -14,14 +14,18 @@ ...@@ -14,14 +14,18 @@
// ----------------------------------------------------------------------------- : ScriptValue // ----------------------------------------------------------------------------- : ScriptValue
// Base cases // Base cases
inline ScriptValueP delayError(const String& m) {
return new_intrusive1<ScriptDelayedError>(ScriptError(m));
}
ScriptValue::operator String() const { return _("[[") + typeName() + _("]]"); } ScriptValue::operator String() const { return _("[[") + typeName() + _("]]"); }
ScriptValue::operator int() const { throw ScriptError(_ERROR_2_("can't convert", typeName(), _TYPE_("integer" ))); } ScriptValue::operator int() const { throw ScriptError(_ERROR_2_("can't convert", typeName(), _TYPE_("integer" ))); }
ScriptValue::operator double() const { throw ScriptError(_ERROR_2_("can't convert", typeName(), _TYPE_("real" ))); } ScriptValue::operator double() const { throw ScriptError(_ERROR_2_("can't convert", typeName(), _TYPE_("real" ))); }
ScriptValue::operator Color() const { throw ScriptError(_ERROR_2_("can't convert", typeName(), _TYPE_("color" ))); } ScriptValue::operator Color() const { throw ScriptError(_ERROR_2_("can't convert", typeName(), _TYPE_("color" ))); }
ScriptValueP ScriptValue::eval(Context&) const { throw ScriptError(_ERROR_2_("can't convert", typeName(), _TYPE_("function"))); } ScriptValueP ScriptValue::eval(Context&) const { return delayError(_ERROR_2_("can't convert", typeName(), _TYPE_("function"))); }
ScriptValueP ScriptValue::getMember(const String& name) const { throw ScriptError(_ERROR_2_("has no member", typeName(), name)); } ScriptValueP ScriptValue::getMember(const String& name) const { return delayError(_ERROR_2_("has no member", typeName(), name)); }
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 { throw ScriptError(_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; } const void* ScriptValue::comparePointer() const { return nullptr; }
...@@ -29,6 +33,24 @@ ScriptValueP ScriptValue::dependencyMember(const String& name, const Dependency& ...@@ -29,6 +33,24 @@ ScriptValueP ScriptValue::dependencyMember(const String& name, const Dependency&
ScriptValueP ScriptValue::dependencies(Context&, const Dependency&) const { return dependency_dummy; } ScriptValueP ScriptValue::dependencies(Context&, const Dependency&) const { return dependency_dummy; }
// ----------------------------------------------------------------------------- : Errors
ScriptType ScriptDelayedError::type() const { return SCRIPT_ERROR; }
String ScriptDelayedError::typeName() const { throw error; }
ScriptDelayedError::operator String() const { throw error; }
ScriptDelayedError::operator double() const { throw error; }
ScriptDelayedError::operator int() const { throw error; }
ScriptDelayedError::operator Color() const { throw error; }
int ScriptDelayedError::itemCount() const { throw error; }
const void* ScriptDelayedError::comparePointer() 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); }
ScriptValueP ScriptDelayedError::dependencies(Context&, const Dependency&) const { return new_intrusive1<ScriptDelayedError>(error); }
ScriptValueP ScriptDelayedError::makeIterator(const ScriptValueP& thisP) const { return thisP; }
// ----------------------------------------------------------------------------- : Iterators // ----------------------------------------------------------------------------- : Iterators
ScriptType ScriptIterator::type() const { return SCRIPT_ITERATOR; } ScriptType ScriptIterator::type() const { return SCRIPT_ITERATOR; }
......
...@@ -29,6 +29,7 @@ enum ScriptType ...@@ -29,6 +29,7 @@ enum ScriptType
, SCRIPT_COLLECTION , SCRIPT_COLLECTION
, SCRIPT_ITERATOR , SCRIPT_ITERATOR
, SCRIPT_DUMMY , SCRIPT_DUMMY
, SCRIPT_ERROR
}; };
/// A value that can be handled by the scripting engine. /// A value that can be handled by the scripting engine.
......
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