#include "fud_format.hpp" #include "fud_string_convert.hpp" #include "fud_utf8.hpp" namespace fud { namespace impl { Result preScanSpec(StringView formatView, FormatSpec& spec, bool& hasPosition, bool& hasColon); FudStatus getPosition(StringView& formatView, FormatSpec& spec, bool& hasPosition); FudStatus getFill(StringView& formatView, FormatSpec& spec); FudStatus getSpecialOpts(StringView& formatView, FormatSpec& spec); FudStatus getWidth(StringView& formatView, FormatSpec& spec); FudStatus getPrecision(StringView& formatView, FormatSpec& spec); FudStatus getFormatSign(StringView& formatView, FormatSpec& spec); } // namespace impl Result FormatSpec::make(StringView formatView, size_t& specLength) { static_cast(formatView); static_cast(specLength); if (formatView[0] != FormatSpec::openBracket) { return FudStatus::ArgumentInvalid; } auto hasEnded = [](StringView& view) { fudAssert(view.length() > 0); return view[0] == FormatSpec::closeBracket; }; FormatSpec spec{}; size_t length = 1; formatView.advanceUnsafe(); bool hasPosition{false}; bool hasColon{false}; auto lengthResult = impl::preScanSpec(formatView, spec, hasPosition, hasColon); if (lengthResult.isError()) { return lengthResult.takeError(); } length += lengthResult.takeOkay(); if (length < 1) { return FudStatus::FormatInvalid; } specLength = length; auto status = impl::getPosition(formatView, spec, hasPosition); if (status != FudStatus::Success) { return status; } // check early ending if (!hasColon) { if (hasEnded(formatView)) { return spec; } return FudStatus::FormatInvalid; } // check validity for being non-default spec and advance if (formatView[0] != ':') { return FudStatus::FormatInvalid; } formatView.advanceUnsafe(); if (hasEnded(formatView)) { return spec; } // Spec status = impl::getFill(formatView, spec); if (status != FudStatus::Success) { return status; } if (hasEnded(formatView)) { return spec; } // sign, alternate, leading zero status = impl::getSpecialOpts(formatView, spec); if (status != FudStatus::Success) { return status; } if (hasEnded(formatView)) { return spec; } // Width status = impl::getWidth(formatView, spec); if (status != FudStatus::Success) { return status; } if (hasEnded(formatView)) { return spec; } // Precision status = impl::getPrecision(formatView, spec); if (status != FudStatus::Success) { return status; } if (hasEnded(formatView)) { return spec; } // Locale if (formatView[0] == FormatSpec::localeChar) { spec.hasLocale = true; formatView.advanceUnsafe(); } if (hasEnded(formatView)) { return spec; } // Format Sign status = impl::getFormatSign(formatView, spec); if (status != FudStatus::Success) { return status; } if (!hasEnded(formatView)) { return FudStatus::FormatInvalid; } return spec; } namespace impl { Result preScanSpec(StringView formatView, FormatSpec& spec, bool& hasPosition, bool& hasColon) { int nesting = 0; uint32_t captureGroups = 0; size_t index = 0; hasColon = false; while (index < formatView.length() && (formatView[index] != FormatSpec::closeBracket || nesting > 0)) { auto letter = formatView[index]; bool isDigit = classify::isDigit(static_cast(letter)); bool priorDot = index > 0 && formatView[index - 1] == '.'; bool takesWidth = not spec.takesWidth and not priorDot; bool takesPrecision = not spec.takesPrecision and priorDot; switch (letter) { case FormatSpec::openBracket: if (not hasColon) { return FudStatus::FormatInvalid; } if (takesWidth) { spec.takesWidth = true; } else if (takesPrecision) { spec.takesPrecision = true; } else { return FudStatus::FormatInvalid; } nesting++; captureGroups++; break; case FormatSpec::closeBracket: nesting--; break; case ':': hasColon = true; break; case '.': break; case 'L': spec.hasLocale = true; break; default: if (not hasColon and isDigit) { hasPosition = true; } break; } if (nesting > 1) { return FudStatus::FormatInvalid; } index++; } if (nesting != 0 || captureGroups > 2 || index >= formatView.length() || formatView[index] != FormatSpec::closeBracket) { return FudStatus::FormatInvalid; } return index + 1U; } FudStatus getPosition(StringView& formatView, FormatSpec& spec, bool& hasPosition) { if (hasPosition) { auto positionResult{fromString(formatView, Radix::Decimal)}; if (positionResult.isError()) { return positionResult.takeError(); } auto positionValue{positionResult.takeOkay()}; spec.position = positionValue.value; auto advanceStatus = formatView.advance(positionValue.nextIndex); if (!advanceStatus) { return FudStatus::Failure; } } return FudStatus::Success; } FudStatus getFill(StringView& formatView, FormatSpec& spec) { Option fillOpt = NullOpt; if (formatView[0] != FormatSpec::openBracket) { size_t fillLength = 0; auto fillResult = FormatFill::make(formatView, fillLength); if (fillResult.isError()) { return fillResult.takeError(); } fillOpt = fillResult.takeOkay(); formatView.advanceUnsafe(fillLength); } spec.fill = fillOpt.valueOr(FormatFill::make()); return FudStatus::Success; } FudStatus getSpecialOpts(StringView& formatView, FormatSpec& spec) { if (formatView.length() < 1) { return FudStatus::ArgumentInvalid; } bool done = false; switch (formatView[0]) { case static_cast(FormatSign::Plus): spec.formatSign = FormatSign::Plus; break; case static_cast(FormatSign::Minus): spec.formatSign = FormatSign::Minus; break; case static_cast(FormatSign::Space): spec.formatSign = FormatSign::Space; break; case '#': spec.alternateForm = true; break; case '0': spec.leadingZero = true; done = true; break; default: return FudStatus::Success; } formatView.advanceUnsafe(); if (done || formatView.length() < 1) { return FudStatus::Success; } switch (formatView[0]) { case '#': if (spec.alternateForm) { return FudStatus::FormatInvalid; } spec.alternateForm = true; break; case '0': spec.leadingZero = true; done = true; break; default: return FudStatus::Success; } formatView.advanceUnsafe(); if (done || formatView.length() < 1) { return FudStatus::Success; } if (formatView[0] == '0') { spec.leadingZero = true; formatView.advanceUnsafe(); } return FudStatus::Success; } FudStatus getWidthTakesWidth(StringView& formatView, FormatSpec& spec); FudStatus getWidth(StringView& formatView, FormatSpec& spec) { if (spec.takesWidth) { return getWidthTakesWidth(formatView, spec); } if (formatView.length() < 1 || not classify::isDigit(formatView[0])) { return FudStatus::Success; } auto widthResult{fromString(formatView, Radix::Decimal)}; if (widthResult.isError()) { return widthResult.takeError(); } auto widthValue{widthResult.takeOkay()}; if (widthValue.value < 1) { return FudStatus::FormatInvalid; } spec.width = widthValue.value; if (formatView.length() < widthValue.nextIndex) { return FudStatus::Failure; } formatView.advanceUnsafe(widthValue.nextIndex); return FudStatus::Success; } FudStatus getWidthTakesWidth(StringView& formatView, FormatSpec& spec) { if (!spec.takesWidth) { return FudStatus::OperationInvalid; } if (formatView.length() < 2 || formatView[0] != FormatSpec::openBracket) { return FudStatus::ArgumentInvalid; } formatView.advanceUnsafe(); if (formatView.length() > 0 && formatView[0] == FormatSpec::closeBracket && spec.position == FormatSpec::positionUnspecified) { formatView.advanceUnsafe(); return FudStatus::Success; } if (spec.position == FormatSpec::positionUnspecified) { return FudStatus::FormatInvalid; } auto positionResult{fromString(formatView, Radix::Decimal)}; if (positionResult.isError()) { return positionResult.takeError(); } auto positionValue{positionResult.takeOkay()}; spec.width = positionValue.value; if (formatView.length() + 1 < positionValue.nextIndex) { return FudStatus::Failure; } if (formatView[positionValue.nextIndex + 1] != FormatSpec::closeBracket) { return FudStatus::FormatInvalid; } formatView.advanceUnsafe(positionValue.nextIndex + 1); return FudStatus::Success; } FudStatus getPrecisionTakesPrecision(StringView& formatView, FormatSpec& spec); FudStatus getPrecision(StringView& formatView, FormatSpec& spec) { if (formatView.length() < 1 || formatView[0] != '.') { return FudStatus::Success; } formatView.advanceUnsafe(); if (spec.takesPrecision) { return getPrecisionTakesPrecision(formatView, spec); } if (formatView.length() < 1 || not classify::isDigit(formatView[0])) { return FudStatus::Success; } auto precisionResult{fromString(formatView, Radix::Decimal)}; if (precisionResult.isError()) { return precisionResult.takeError(); } auto precisionValue{precisionResult.takeOkay()}; if (precisionValue.value < 1) { return FudStatus::FormatInvalid; } spec.precision = precisionValue.value; if (formatView.length() < precisionValue.nextIndex) { return FudStatus::Failure; } formatView.advanceUnsafe(precisionValue.nextIndex); return FudStatus::Success; } FudStatus getPrecisionTakesPrecision(StringView& formatView, FormatSpec& spec) { if (!spec.takesPrecision) { return FudStatus::OperationInvalid; } if (formatView.length() < 2 || formatView[0] != FormatSpec::openBracket) { return FudStatus::ArgumentInvalid; } formatView.advanceUnsafe(); if (formatView.length() > 0 && formatView[0] == FormatSpec::closeBracket && spec.position == FormatSpec::positionUnspecified) { formatView.advanceUnsafe(); return FudStatus::Success; } if (spec.position == FormatSpec::positionUnspecified) { return FudStatus::FormatInvalid; } auto positionResult{fromString(formatView, Radix::Decimal)}; if (positionResult.isError()) { return positionResult.takeError(); } auto positionValue{positionResult.takeOkay()}; spec.precision = positionValue.value; if (formatView.length() + 1 < positionValue.nextIndex) { return FudStatus::Failure; } if (formatView[positionValue.nextIndex + 1] != FormatSpec::closeBracket) { return FudStatus::FormatInvalid; } formatView.advanceUnsafe(positionValue.nextIndex + 1); return FudStatus::Success; } FudStatus getFormatSign(StringView& formatView, FormatSpec& spec) { if (formatView.length() < 1) { spec.formatType = FormatType::Unspecified; return FudStatus::Success; } auto letter = formatView[0]; switch (letter) { case static_cast(FormatType::String): spec.formatType = FormatType::String; break; case static_cast(FormatType::Escaped): spec.formatType = FormatType::Escaped; break; case static_cast(FormatType::BinaryLower): spec.formatType = FormatType::BinaryLower; break; case static_cast(FormatType::BinaryUpper): spec.formatType = FormatType::BinaryUpper; break; case static_cast(FormatType::Character): spec.formatType = FormatType::Character; break; case static_cast(FormatType::Decimal): spec.formatType = FormatType::Decimal; break; case static_cast(FormatType::Octal): spec.formatType = FormatType::Octal; break; case static_cast(FormatType::HexLower): spec.formatType = FormatType::HexLower; break; case static_cast(FormatType::HexUpper): spec.formatType = FormatType::HexUpper; break; case static_cast(FormatType::FloatHexLower): spec.formatType = FormatType::FloatHexLower; break; case static_cast(FormatType::FloatHexUpper): spec.formatType = FormatType::FloatHexUpper; break; case static_cast(FormatType::ScientificLower): spec.formatType = FormatType::ScientificLower; break; case static_cast(FormatType::ScientificUpper): spec.formatType = FormatType::ScientificUpper; break; case static_cast(FormatType::FixedLower): spec.formatType = FormatType::FixedLower; break; case static_cast(FormatType::FixedUpper): spec.formatType = FormatType::FixedUpper; break; case static_cast(FormatType::GeneralLower): spec.formatType = FormatType::GeneralLower; break; case static_cast(FormatType::GeneralUpper): spec.formatType = FormatType::GeneralUpper; break; case static_cast(FormatType::Unspecified): default: return FudStatus::FormatInvalid; } formatView.advanceUnsafe(); return FudStatus::Success; } } // namespace impl } // namespace fud