From 40e55e8151726f19a531d144849de9a5c7bbb20a Mon Sep 17 00:00:00 2001 From: twanvl Date: Thu, 14 Jun 2007 14:25:53 +0000 Subject: [PATCH] scripting language now has support for list and map literals: " [a,b,c] " git-svn-id: svn://svn.code.sf.net/p/magicseteditor/code/trunk@422 0fc631ac-6414-0410-93d0-97cfa31319b6 --- src/script/context.cpp | 22 ++++++++++++++++++++++ src/script/context.hpp | 2 ++ src/script/dependency.cpp | 5 +++++ src/script/parser.cpp | 27 ++++++++++++++++++++++++++- src/script/script.hpp | 11 ++++++----- src/script/to_value.hpp | 4 +++- src/script/value.cpp | 13 +++++++++---- 7 files changed, 73 insertions(+), 11 deletions(-) diff --git a/src/script/context.cpp b/src/script/context.cpp index 10e94112..538bf42b 100644 --- a/src/script/context.cpp +++ b/src/script/context.cpp @@ -101,6 +101,11 @@ ScriptValueP Context::eval(const Script& script, bool useScope) { } break; } + // Make an object + case I_MAKE_OBJECT: { + makeObject(i.data); + break; + } // Function call case I_CALL: { @@ -324,3 +329,20 @@ void instrTernary(TernaryInstructionType i, ScriptValueP& a, const ScriptValueP& break; } } + +// ----------------------------------------------------------------------------- : Simple instructions : object + +void Context::makeObject(size_t n) { + intrusive_ptr ret(new ScriptCustomCollection()); + size_t begin = stack.size() - 2 * n; + for (size_t i = 0 ; i < n ; ++i) { + const ScriptValueP& key = stack[begin + 2 * i]; + const ScriptValueP& val = stack[begin + 2 * i + 1]; + ret->value.push_back(val); + if (key != script_nil) { // valid key + ret->key_value[key->toString()] = val; + } + } + stack.resize(begin); + stack.push_back(ret); +} diff --git a/src/script/context.hpp b/src/script/context.hpp index 6f66321b..19f30949 100644 --- a/src/script/context.hpp +++ b/src/script/context.hpp @@ -94,6 +94,8 @@ class Context { void getBindings(size_t scope, vector&); /// Remove all bindings made in the current scope 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); }; // ----------------------------------------------------------------------------- : EOF diff --git a/src/script/dependency.cpp b/src/script/dependency.cpp index c1cfb393..5cbbd29d 100644 --- a/src/script/dependency.cpp +++ b/src/script/dependency.cpp @@ -231,6 +231,11 @@ ScriptValueP Context::dependencies(const Dependency& dep, const Script& script) } break; } + // Make an object + case I_MAKE_OBJECT: { + makeObject(i.data); + break; + } // Function call (as normal) case I_CALL: { diff --git a/src/script/parser.cpp b/src/script/parser.cpp index 6898878f..db15b3a6 100644 --- a/src/script/parser.cpp +++ b/src/script/parser.cpp @@ -385,6 +385,31 @@ void parseExpr(TokenIterator& input, Script& script, Precedence minPrec) { parseOper(input, *subScript, PREC_ALL, I_RET); expectToken(input, _("}")); script.addInstruction(I_PUSH_CONST, subScript); + } else if (token == _("[")) { + // [] = list or map literal + unsigned int count = 0; + Token t = input.peek(); + while (t != _("]") && t != TOK_EOF) { + if (input.peek(2) == _(":") && (t.type == TOK_NAME || t.type == TOK_INT || t.type == TOK_STRING)) { + // name: ... + script.addInstruction(I_PUSH_CONST, to_script(t.value)); + input.read(); // skip the name + input.read(); // and the : + } else { + // implicit numbered element + script.addInstruction(I_PUSH_CONST, script_nil); + } + parseOper(input, script, PREC_SEQ); + ++count; + t = input.peek(); + if (t == _(",")) { + // Comma separating the elements + input.read(); + t = input.peek(); + } + } + expectToken(input, _("]")); + script.addInstruction(I_MAKE_OBJECT, count); } else if (minPrec <= PREC_UNARY && token == _("-")) { parseOper(input, script, PREC_UNARY, I_UNARY, I_NEGATE); // unary negation } else if (token == TOK_NAME) { @@ -549,7 +574,7 @@ void parseOper(TokenIterator& input, Script& script, Precedence minPrec, Instruc vector arguments; Token t = input.peek(); while (t != _(")") && t != TOK_EOF) { - if (input.peek(2) == _(":")) { + if (input.peek(2) == _(":") && t.type == TOK_NAME) { // name: ... arguments.push_back(string_to_variable(t.value)); input.read(); // skip the name diff --git a/src/script/script.hpp b/src/script/script.hpp index 3ada5aeb..6479c771 100644 --- a/src/script/script.hpp +++ b/src/script/script.hpp @@ -33,13 +33,14 @@ enum InstructionType , I_MEMBER_C = 7 ///< arg = name : finds a member of the top of the stack replaces the top of the stack with the member , I_LOOP = -8 ///< arg = int : loop over the elements of an iterator, which is the *second* element of the stack (this allows for combing the results of multiple iterations) ///< at the end performs a jump and pops the iterator. note: The second element of the stack must be an iterator! +, I_MAKE_OBJECT = -7 ///< arg = int : make a list/map with n elements, pops 2n values of the stack, n key/value pairs // Functions -, I_CALL = -7 ///< arg = int, int+ : 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_RET = -6 ///< return from the current function +, I_CALL = -6 ///< arg = int, int+ : 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_RET = -5 ///< return from the current function // Simple instructions -, I_UNARY = -5 ///< pop 1 value, apply a function, push the result -, I_BINARY = -4 ///< pop 2 values, apply a function, push the result -, I_TERNARY = -3 ///< pop 3 values, apply a function, push the result +, I_UNARY = -4 ///< pop 1 value, apply a function, push the result +, I_BINARY = -3 ///< pop 2 values, apply a function, push the result +, I_TERNARY = -2 ///< pop 3 values, apply a function, push the result }; /// Types of unary instructions (taking one argument from the stack) diff --git a/src/script/to_value.hpp b/src/script/to_value.hpp index 576e225d..824f0ef8 100644 --- a/src/script/to_value.hpp +++ b/src/script/to_value.hpp @@ -147,8 +147,10 @@ class ScriptCustomCollection : public ScriptValue { /// Collections can be compared by comparing pointers virtual const void* comparePointer() const { return &value; } - /// The collection + /// The collection as a list (contains all values) vector value; + /// The collection as a map (contains only the values that have a key) + map key_value; }; // ----------------------------------------------------------------------------- : Objects diff --git a/src/script/value.cpp b/src/script/value.cpp index 9916f07e..50104c81 100644 --- a/src/script/value.cpp +++ b/src/script/value.cpp @@ -257,11 +257,16 @@ class ScriptCustomCollectionIterator : public ScriptIterator { }; ScriptValueP ScriptCustomCollection::getMember(const String& name) const { - long index; - if (name.ToLong(&index) && index >= 0 && (size_t)index < value.size()) { - return value.at(index); + map::const_iterator it = key_value.find(name); + if (it != key_value.end()) { + return it->second; } else { - return ScriptValue::getMember(name); + long index; + if (name.ToLong(&index) && index >= 0 && (size_t)index < value.size()) { + return value.at(index); + } else { + return ScriptValue::getMember(name); + } } } ScriptValueP ScriptCustomCollection::makeIterator(const ScriptValueP& thisP) const {