diff --git a/lib/config/expression.cpp b/lib/config/expression.cpp index 0c8f08037..d0c273bb6 100644 --- a/lib/config/expression.cpp +++ b/lib/config/expression.cpp @@ -242,7 +242,7 @@ Value FunctionCallExpression::DoEvaluate(VMFrame& frame, DebugHint *dhint) const return Empty; Value index = m_IName[i]->Evaluate(frame); - result = VMOps::GetField(result, index); + result = VMOps::GetField(result, index, GetDebugInfo()); if (i == m_IName.size() - 2) { if (!result.IsObject()) @@ -349,7 +349,7 @@ Value SetExpression::DoEvaluate(VMFrame& frame, DebugHint *dhint) const break; } - object = VMOps::GetField(parent, tempindex); + object = VMOps::GetField(parent, tempindex, GetDebugInfo()); if (i != m_Indexer.size() - 1 && object.IsEmpty()) { object = new Dictionary(); @@ -424,7 +424,7 @@ Value IndexerExpression::DoEvaluate(VMFrame& frame, DebugHint *dhint) const Value ImportExpression::DoEvaluate(VMFrame& frame, DebugHint *dhint) const { - String type = VMOps::GetField(frame.Self, "type"); + String type = VMOps::GetField(frame.Self, "type", GetDebugInfo()); Value name = m_Name->Evaluate(frame); ConfigItem::Ptr item = ConfigItem::GetObject(type, name); diff --git a/lib/config/vmops.hpp b/lib/config/vmops.hpp index 0ebd20f05..bfbb1f10d 100644 --- a/lib/config/vmops.hpp +++ b/lib/config/vmops.hpp @@ -80,7 +80,7 @@ public: return func->Invoke(arguments); } - static inline Value Indexer(VMFrame& frame, const std::vector& indexer) + static inline Value Indexer(VMFrame& frame, const std::vector& indexer, const DebugInfo& debugInfo = DebugInfo()) { Value result = indexer[0]->Evaluate(frame); @@ -89,7 +89,7 @@ public: return Empty; Value index = indexer[i]->Evaluate(frame); - result = GetField(result, index); + result = GetField(result, index, debugInfo); } return result; @@ -188,6 +188,16 @@ public: frame.Locals->Set(fkvar, value); expression->Evaluate(frame); } + } else if (value.IsString()) { + if (!fvvar.IsEmpty()) + BOOST_THROW_EXCEPTION(ScriptError("Cannot use dictionary iterator for string.", debugInfo)); + + String str = value; + + BOOST_FOREACH(char ch, str) { + frame.Locals->Set(fkvar, String(1, ch)); + expression->Evaluate(frame); + } } else if (value.IsObjectType()) { if (fvvar.IsEmpty()) BOOST_THROW_EXCEPTION(ScriptError("Cannot use array iterator for dictionary.", debugInfo)); @@ -200,8 +210,7 @@ public: frame.Locals->Set(fvvar, kv.second); expression->Evaluate(frame); } - } - else + } else BOOST_THROW_EXCEPTION(ScriptError("Invalid type in for expression: " + value.GetTypeName(), debugInfo)); return Empty; @@ -223,21 +232,34 @@ public: } } - static inline Value GetField(const Object::Ptr& context, const String& field) + static inline Value GetField(const Value& context, const String& field, const DebugInfo& debugInfo = DebugInfo()) { - Dictionary::Ptr dict = dynamic_pointer_cast(context); + if (context.IsString()) { + String str = context; + int index = Convert::ToLong(field); + if (index < 0 || index >= str.GetLength()) + BOOST_THROW_EXCEPTION(ScriptError("Index is out of bounds", debugInfo)); + return String(1, str[index]); + } + + if (!context.IsObject()) + BOOST_THROW_EXCEPTION(ScriptError("Tried to access invalid field '" + field + "' on object of type '" + context.GetTypeName() + "'", debugInfo)); + + Object::Ptr object = context; + + Dictionary::Ptr dict = dynamic_pointer_cast(object); if (dict) return dict->Get(field); - Array::Ptr arr = dynamic_pointer_cast(context); + Array::Ptr arr = dynamic_pointer_cast(object); if (arr) { int index = Convert::ToLong(field); return arr->Get(index); } - Type::Ptr type = context->GetReflectionType(); + Type::Ptr type = object->GetReflectionType(); if (!type) return Empty; @@ -247,7 +269,7 @@ public: if (fid == -1) return Empty; - return context->GetField(fid); + return object->GetField(fid); } static inline void SetField(const Object::Ptr& context, const String& field, const Value& value, const DebugInfo& debugInfo = DebugInfo()) diff --git a/test/config-ops.cpp b/test/config-ops.cpp index 458ae1b15..0c85bde01 100644 --- a/test/config-ops.cpp +++ b/test/config-ops.cpp @@ -294,6 +294,10 @@ BOOST_AUTO_TEST_CASE(advanced) expr = ConfigCompiler::CompileText("", "7 & 15 > 6"); BOOST_CHECK(expr->Evaluate(frame)); delete expr; + + expr = ConfigCompiler::CompileText("", "s = \"test\"; s[2]"); + BOOST_CHECK(expr->Evaluate(frame) == "s"); + delete expr; } BOOST_AUTO_TEST_SUITE_END()