package io.gitlab.jfronny.muscript.ast.dynamic; import io.gitlab.jfronny.muscript.ast.DynamicExpr; import io.gitlab.jfronny.muscript.ast.Expr; import io.gitlab.jfronny.muscript.annotations.CanThrow; import io.gitlab.jfronny.muscript.annotations.UncheckedDynamic; import io.gitlab.jfronny.muscript.ast.literal.StringLiteral; import io.gitlab.jfronny.muscript.compiler.*; import io.gitlab.jfronny.muscript.data.Scope; import io.gitlab.jfronny.muscript.data.dynamic.*; import io.gitlab.jfronny.muscript.error.LocationalException; import io.gitlab.jfronny.muscript.error.TypeMismatchException; import java.io.IOException; import java.util.stream.Stream; @CanThrow @UncheckedDynamic public class Get extends DynamicExpr { private final DynamicExpr left; private final Expr name; public Get(CodeLocation location, DynamicExpr left, Expr name) { super(Order.Call, location); this.left = left; this.name = name; if (name.getResultType() != Type.String && name.getResultType() != Type.Number && name.getResultType() != Type.Dynamic) { throw new TypeMismatchException(location, Type.String, name.getResultType(), "Name must be either a string or a number"); } } @Override public Dynamic get(Scope dataRoot) { Dynamic left = this.left.get(dataRoot); if (Dynamic.isNull(left)) throw new LocationalException(location, "Could not get \"" + name.asStringExpr().get(dataRoot) + "\" because left is null"); if (left.isObject()) { DObject o = left.asObject(); var n = name.asStringExpr().get(dataRoot); if (!o.has(n)) throw new LocationalException(location, "Object does not contain \"" + n + "\""); return o.get(n); } else if (left.isList()) { DList l = left.asList(); int idx = name.asNumberExpr().get(dataRoot).intValue(); if (idx < 0 || idx >= l.size()) throw new LocationalException(location, "Index " + idx + " is out of range for list with size " + l.size()); return l.get(idx); } throw new DynamicTypeConversionException("object or list", left).locational(location); } @Override public DynamicExpr optimize() { DynamicExpr left = this.left.optimize(); Expr name = this.name.optimize(); return new Get(location, left, name); } @Override public Stream> extractSideEffects() { return Stream.concat(left.extractSideEffects(), name.extractSideEffects()); } @Override public void decompile(ExprWriter writer) throws IOException { parenthesize(left, writer, false); if (name instanceof StringLiteral lit && Lexer.isValidId(lit.value)) { writer.append('.').append(lit.value); } else { writer.append('['); name.decompile(writer); writer.append(']'); } } @Override public boolean equals(Object obj) { return obj instanceof Get get && left.equals(get.left) && name.equals(get.name); } }