| 1238 | SubscriptExpr(const Location & loc, std::shared_ptr<Expression> && b, std::shared_ptr<Expression> && i) |
| 1239 | : Expression(loc), base(std::move(b)), index(std::move(i)) {} |
| 1240 | Value do_evaluate(const std::shared_ptr<Context> & context) const override { |
| 1241 | if (!base) throw std::runtime_error("SubscriptExpr.base is null"); |
| 1242 | if (!index) throw std::runtime_error("SubscriptExpr.index is null"); |
| 1243 | auto target_value = base->evaluate(context); |
| 1244 | if (auto slice = dynamic_cast<SliceExpr*>(index.get())) { |
| 1245 | auto len = target_value.size(); |
| 1246 | auto wrap = [len](int64_t i) -> int64_t { |
| 1247 | if (i < 0) { |
| 1248 | return i + len; |
| 1249 | } |
| 1250 | return i; |
| 1251 | }; |
| 1252 | int64_t step = slice->step ? slice->step->evaluate(context).get<int64_t>() : 1; |
| 1253 | if (!step) { |
| 1254 | throw std::runtime_error("slice step cannot be zero"); |
| 1255 | } |
| 1256 | int64_t start = slice->start ? wrap(slice->start->evaluate(context).get<int64_t>()) : (step < 0 ? len - 1 : 0); |
| 1257 | int64_t end = slice->end ? wrap(slice->end->evaluate(context).get<int64_t>()) : (step < 0 ? -1 : len); |
| 1258 | if (target_value.is_string()) { |
| 1259 | std::string s = target_value.get<std::string>(); |
| 1260 | |
| 1261 | std::string result; |
| 1262 | if (start < end && step == 1) { |
| 1263 | result = s.substr(start, end - start); |
| 1264 | } else { |
| 1265 | for (int64_t i = start; step > 0 ? i < end : i > end; i += step) { |
| 1266 | result += s[i]; |
| 1267 | } |
| 1268 | } |
| 1269 | return result; |
| 1270 | |
| 1271 | } else if (target_value.is_array()) { |
| 1272 | auto result = Value::array(); |
| 1273 | for (int64_t i = start; step > 0 ? i < end : i > end; i += step) { |
| 1274 | result.push_back(target_value.at(i)); |
| 1275 | } |
| 1276 | return result; |
| 1277 | } else { |
| 1278 | throw std::runtime_error(target_value.is_null() ? "Cannot subscript null" : "Subscripting only supported on arrays and strings"); |
| 1279 | } |
| 1280 | } else { |
| 1281 | auto index_value = index->evaluate(context); |
| 1282 | if (target_value.is_null()) { |
| 1283 | if (auto t = dynamic_cast<VariableExpr*>(base.get())) { |
| 1284 | throw std::runtime_error("'" + t->get_name() + "' is " + (context->contains(t->get_name()) ? "null" : "not defined")); |
| 1285 | } |
| 1286 | throw std::runtime_error("Trying to access property '" + index_value.dump() + "' on null!"); |
| 1287 | } |
| 1288 | return target_value.get(index_value); |
| 1289 | } |
| 1290 | } |
| 1291 | }; |
| 1292 | |
| 1293 | class UnaryOpExpr : public Expression { |