void ExpressionConverter::postorder()

in flexsai/p4/backend/json_stage/expression.cpp [231:399]


void ExpressionConverter::postorder(const IR::Member* expression)  {
    // TODO: deal with references that return bool
    auto result = new Util::JsonObject();

    auto parentType = backend->getTypeMap()->getType(expression->expr, true);
    cstring fieldName = expression->member.name;
    auto type = backend->getTypeMap()->getType(expression, true);

    if (parentType->is<IR::Type_StructLike>()) {
        auto st = parentType->to<IR::Type_StructLike>();
        auto field = st->getField(expression->member);
        if (field != nullptr)
            // field could be a method call, i.e., isValid.
            fieldName = field->controlPlaneName();
    }

    {
        if (type->is<IR::Type_Error>() && expression->expr->is<IR::TypeNameExpression>()) {
            // this deals with constants that have type 'error'
            result->emplace("type", "hexstr");
            auto decl = type->to<IR::Type_Error>()->getDeclByName(expression->member.name);
            ErrorCodesMap errCodes = backend->getErrorCodesMap();
            auto errorValue = errCodes.at(decl);
            result->emplace("value", Util::toString(errorValue));
            map.emplace(expression, result);
            return;
        }
    }

    auto param = enclosingParamReference(expression->expr);
    if (param != nullptr) {
        if (backend->isStandardMetadataParameter(param)) {
            result->emplace("type", "field");
            auto e = mkArrayField(result, "value");
            e->append(SAI::SaiModelProperties::jsonMetadataParameterName);
            e->append(fieldName);
        } else {
            if (type->is<IR::Type_Stack>()) {
                result->emplace("type", "header_stack");
                result->emplace("value", fieldName);
            } else if (type->is<IR::Type_HeaderUnion>()) {
                result->emplace("type", "header_union");
                result->emplace("value", fieldName);
            } else if (parentType->is<IR::Type_HeaderUnion>()) {
                auto l = get(expression->expr);
                cstring nestedField = fieldName;
                if (l->is<Util::JsonObject>()) {
                    auto lv = l->to<Util::JsonObject>()->get("value");
                    if (lv->is<Util::JsonValue>()) {
                        // header in union reference ["u", "f"] => "u.f"
                        cstring prefix = lv->to<Util::JsonValue>()->getString();
                        nestedField = prefix + "." + nestedField;
                    }
                }
                result->emplace("type", "header");
                result->emplace("value", nestedField);
            } else if (parentType->is<IR::Type_StructLike>() &&
                       (type->is<IR::Type_Bits>() || type->is<IR::Type_Error>() ||
                        type->is<IR::Type_Boolean>())) {
                auto field = parentType->to<IR::Type_StructLike>()->getField(
                    expression->member);
                LOG3("looking up field " << field);
                CHECK_NULL(field);
                auto name = ::get(backend->scalarMetadataFields, field);
                CHECK_NULL(name);
                if (type->is<IR::Type_Bits>() || type->is<IR::Type_Error>() ||
                    leftValue || simpleExpressionsOnly) {
                    result->emplace("type", "field");
                    auto e = mkArrayField(result, "value");
                    e->append(scalarsName);
                    e->append(name);
                } else if (type->is<IR::Type_Boolean>()) {
                    // Boolean variables are stored as ints, so we
                    // have to insert a conversion when reading such a
                    // variable
                    result->emplace("type", "expression");
                    auto e = new Util::JsonObject();
                    result->emplace("value", e);
                    e->emplace("op", "d2b");  // data to Boolean cast
                    e->emplace("left", Util::JsonValue::null);
                    auto r = new Util::JsonObject();
                    e->emplace("right", r);

                    r->emplace("type", "field");
                    auto a = mkArrayField(r, "value");
                    a->append(scalarsName);
                    a->append(name);
                }
            } else {
                // This may be wrong, but the caller will handle it properly
                // (e.g., this can be a method, such as packet.lookahead)
                result->emplace("type", "header");
                result->emplace("value", fieldName);
            }
        }
    } else {
        bool done = false;
        if (expression->expr->is<IR::Member>()) {
            // array.next.field => type: "stack_field", value: [ array, field ]
            auto mem = expression->expr->to<IR::Member>();
            auto memtype = backend->getTypeMap()->getType(mem->expr, true);
            if (memtype->is<IR::Type_Stack>() && mem->member == IR::Type_Stack::last) {
                auto l = get(mem->expr);
                CHECK_NULL(l);
                result->emplace("type", "stack_field");
                auto e = mkArrayField(result, "value");
                if (l->is<Util::JsonObject>())
                    e->append(l->to<Util::JsonObject>()->get("value"));
                else
                    e->append(l);
                e->append(fieldName);
                done = true;
            }
        }

        if (!done) {
            auto l = get(expression->expr);
            CHECK_NULL(l);
            if (parentType->is<IR::Type_HeaderUnion>()) {
                BUG_CHECK(l->is<Util::JsonObject>(), "Not a JsonObject");
                auto lv = l->to<Util::JsonObject>()->get("value");
                BUG_CHECK(lv->is<Util::JsonValue>(), "Not a JsonValue");
                fieldName = lv->to<Util::JsonValue>()->getString() + "." + fieldName;
                // Each header in a union is allocated a separate header instance.
                // Refer to that instance directly.
                result->emplace("type", "header");
                result->emplace("value", fieldName);
            } else {
                result->emplace("type", "field");
                auto e = mkArrayField(result, "value");
                if (l->is<Util::JsonObject>()) {
                    auto lv = l->to<Util::JsonObject>()->get("value");
                    if (lv->is<Util::JsonArray>()) {
                        // TODO: is this case still necessary after eliminating nested structs?
                        // nested struct reference [ ["m", "f"], "x" ] => [ "m", "f.x" ]
                        auto array = lv->to<Util::JsonArray>();
                        BUG_CHECK(array->size() == 2, "expected 2 elements");
                        auto first = array->at(0);
                        auto second = array->at(1);
                        BUG_CHECK(second->is<Util::JsonValue>(), "expected a value");
                        e->append(first);
                        cstring nestedField = second->to<Util::JsonValue>()->getString();
                        nestedField += "." + fieldName;
                        e->append(nestedField);
                    } else if (lv->is<Util::JsonValue>()) {
                        e->append(lv);
                        e->append(fieldName);
                    } else {
                        BUG("%1%: Unexpected json", lv);
                    }
                } else {
                    e->append(l);
                    e->append(fieldName);
                }
                if (!simpleExpressionsOnly && !leftValue && type->is<IR::Type_Boolean>()) {
                    auto cast = new Util::JsonObject();
                    auto value = new Util::JsonObject();
                    cast->emplace("type", "expression");
                    cast->emplace("value", value);
                    value->emplace("op", "d2b");  // data to Boolean cast
                    value->emplace("left", Util::JsonValue::null);
                    value->emplace("right", result);
                    result = cast;
                }
            }
        }
    }
    map.emplace(expression, result);
}