From e287348843f771167ae91191471cf9726a3cb603 Mon Sep 17 00:00:00 2001 From: twanvl Date: Thu, 14 Jun 2007 18:31:47 +0000 Subject: [PATCH] Added 'or else' construct to script language. git-svn-id: svn://svn.code.sf.net/p/magicseteditor/code/trunk@423 0fc631ac-6414-0410-93d0-97cfa31319b6 --- src/script/context.cpp | 3 +++ src/script/image.cpp | 32 +++++++++++++++++++++++++++----- src/script/image.hpp | 9 ++++++++- src/script/parser.cpp | 29 ++++++++++++++++++++++------- src/script/script.hpp | 2 ++ src/script/to_value.hpp | 31 +++++++++++++++++++++++++++++++ src/script/value.cpp | 28 +++++++++++++++++++++++++--- src/script/value.hpp | 1 + 8 files changed, 119 insertions(+), 16 deletions(-) diff --git a/src/script/context.cpp b/src/script/context.cpp index 538bf42b..910b060c 100644 --- a/src/script/context.cpp +++ b/src/script/context.cpp @@ -317,6 +317,9 @@ void instrBinary (BinaryInstructionType i, ScriptValueP& a, const ScriptValueP& case I_GT: OPERATOR_DI(>); case I_LE: OPERATOR_DI(<=); case I_GE: OPERATOR_DI(>=); + case I_OR_ELSE: + if (at == SCRIPT_ERROR) a = b; + break; } } diff --git a/src/script/image.cpp b/src/script/image.cpp index ad492f84..326a9984 100644 --- a/src/script/image.cpp +++ b/src/script/image.cpp @@ -12,18 +12,31 @@ #include #include #include +#include // ----------------------------------------------------------------------------- : Utility // convert any script value to a GeneratedImageP GeneratedImageP image_from_script(const ScriptValueP& value) { - if (value->type() == SCRIPT_STRING) { - return new_intrusive1(value->toString()); - } else { + ScriptType t = value->type(); + if (t == SCRIPT_IMAGE) { GeneratedImageP img = dynamic_pointer_cast(value); - if (!img) throw ScriptError(_ERROR_2_("can't convert", value->typeName(), _TYPE_("image"))); - return img; + if (img) return img; + } else if (t == SCRIPT_STRING) { + return new_intrusive1(value->toString()); + } else if (t == SCRIPT_NIL) { + return new_intrusive(); + } else if (t == SCRIPT_OBJECT) { + // maybe it's an image value? + intrusive_ptr > v = dynamic_pointer_cast >(value); + if (v) { + ImageValueP iv = dynamic_pointer_cast(v->getValue()); + if (iv) { + return new_intrusive2(iv->filename, iv->last_update); + } + } } + throw ScriptError(_ERROR_2_("can't convert", value->typeName(), _TYPE_("image"))); } // ----------------------------------------------------------------------------- : ScriptableImage @@ -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(value) : script_nil); + s->addInstruction(I_RET); + return s; +} + // ----------------------------------------------------------------------------- : Reflection // we need some custom io, because the behaviour is different for each of Reader/Writer/GetMember diff --git a/src/script/image.hpp b/src/script/image.hpp index 0493073c..7917adcd 100644 --- a/src/script/image.hpp +++ b/src/script/image.hpp @@ -29,10 +29,12 @@ class ScriptableImage { inline ScriptableImage(const String& script) : script(script) {} inline ScriptableImage(const GeneratedImageP& gen) : value(gen) {} - /// Is there an image set? + /// Is there a scripted image set? inline bool isScripted() const { return script; } /// Is there an image generator available? inline bool isReady() const { return value; } + /// Is there an image set? + inline bool isSet() const { return script || value; } /// Generate an image. Image generate(const GeneratedImage::Options& options, bool cache = false) const; @@ -49,6 +51,11 @@ class ScriptableImage { /// Can this be safely generated from another thread? 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: OptionalScript script; ///< The script, not really optional GeneratedImageP value; ///< The image generator diff --git a/src/script/parser.cpp b/src/script/parser.cpp index db15b3a6..6feb63f3 100644 --- a/src/script/parser.cpp +++ b/src/script/parser.cpp @@ -399,7 +399,7 @@ void parseExpr(TokenIterator& input, Script& script, Precedence minPrec) { // implicit numbered element script.addInstruction(I_PUSH_CONST, script_nil); } - parseOper(input, script, PREC_SEQ); + parseOper(input, script, PREC_AND); ++count; t = input.peek(); if (t == _(",")) { @@ -419,10 +419,12 @@ void parseExpr(TokenIterator& input, Script& script, Precedence minPrec) { script.addInstruction(I_PUSH_CONST, script_true); // boolean constant : true } else if (token == _("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")) { // if AAA then BBB else CCC unsigned int jmpElse, jmpEnd; - parseOper(input, script, PREC_SET); // AAA + parseOper(input, script, PREC_AND); // AAA jmpElse = script.getLabel(); // jmp_else: script.addInstruction(I_JUMP_IF_NOT, 0xFFFF); // jnz lbl_else expectToken(input, _("then")); // then @@ -449,7 +451,7 @@ void parseExpr(TokenIterator& input, Script& script, Precedence minPrec) { input.expected(_("name")); } 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_PUSH_CONST, script_nil); // push nil lblStart = script.getLabel(); // lbl_start: @@ -465,9 +467,9 @@ void parseExpr(TokenIterator& input, Script& script, Precedence minPrec) { // for AAA from BBB to CCC do DDD Token name = input.read(); // AAA expectToken(input, _("from")); // from - parseOper(input, script, PREC_SET); // BBB + parseOper(input, script, PREC_AND); // BBB 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_PUSH_CONST, script_nil); // push nil lblStart = script.getLabel(); // lbl_start: @@ -546,8 +548,21 @@ void parseOper(TokenIterator& input, Script& script, Precedence minPrec, Instruc 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==_("or" )) parseOper(input, script, PREC_CMP, I_BINARY, I_OR); - else if (minPrec <= PREC_CMP && token==_("=")) parseOper(input, script, PREC_ADD, I_BINARY, I_EQ); + else if (minPrec <= PREC_AND && token==_("or" )) { + 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_NEQ); else if (minPrec <= PREC_CMP && token==_("<")) parseOper(input, script, PREC_ADD, I_BINARY, I_LT); diff --git a/src/script/script.hpp b/src/script/script.hpp index 6479c771..738ba4f4 100644 --- a/src/script/script.hpp +++ b/src/script/script.hpp @@ -70,6 +70,8 @@ enum BinaryInstructionType , I_GT ///< operator > , I_LE ///< 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) diff --git a/src/script/to_value.hpp b/src/script/to_value.hpp index 824f0ef8..c68fc94d 100644 --- a/src/script/to_value.hpp +++ b/src/script/to_value.hpp @@ -31,6 +31,37 @@ ScriptValueP make_iterator(const T& v) { template 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 // Iterator over a collection diff --git a/src/script/value.cpp b/src/script/value.cpp index 50104c81..7929e18c 100644 --- a/src/script/value.cpp +++ b/src/script/value.cpp @@ -14,14 +14,18 @@ // ----------------------------------------------------------------------------- : ScriptValue // Base cases +inline ScriptValueP delayError(const String& m) { + return new_intrusive1(ScriptError(m)); +} + ScriptValue::operator String() const { return _("[[") + typeName() + _("]]"); } 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 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::getMember(const String& name) const { throw ScriptError(_ERROR_2_("has no member", typeName(), name)); } +ScriptValueP ScriptValue::eval(Context&) const { return delayError(_ERROR_2_("can't convert", typeName(), _TYPE_("function"))); } +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::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"))); } const void* ScriptValue::comparePointer() const { return nullptr; } @@ -29,6 +33,24 @@ ScriptValueP ScriptValue::dependencyMember(const String& name, const Dependency& 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(error); } +ScriptValueP ScriptDelayedError::dependencyMember(const String&, const Dependency&) const { return new_intrusive1(error); } +ScriptValueP ScriptDelayedError::eval(Context&) const { return new_intrusive1(error); } +ScriptValueP ScriptDelayedError::dependencies(Context&, const Dependency&) const { return new_intrusive1(error); } +ScriptValueP ScriptDelayedError::makeIterator(const ScriptValueP& thisP) const { return thisP; } + + // ----------------------------------------------------------------------------- : Iterators ScriptType ScriptIterator::type() const { return SCRIPT_ITERATOR; } diff --git a/src/script/value.hpp b/src/script/value.hpp index 6e4e087e..117052fa 100644 --- a/src/script/value.hpp +++ b/src/script/value.hpp @@ -29,6 +29,7 @@ enum ScriptType , SCRIPT_COLLECTION , SCRIPT_ITERATOR , SCRIPT_DUMMY +, SCRIPT_ERROR }; /// A value that can be handled by the scripting engine.