From 5cc7cbc3704ec255eb5d0ac53b2cc0fcb1221d63 Mon Sep 17 00:00:00 2001 From: Dominick Allen Date: Wed, 23 Oct 2024 13:21:10 -0500 Subject: String conversion and parsing format spec. --- source/fud_format.cpp | 515 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 508 insertions(+), 7 deletions(-) (limited to 'source/fud_format.cpp') diff --git a/source/fud_format.cpp b/source/fud_format.cpp index ab1bb4f..04e611e 100644 --- a/source/fud_format.cpp +++ b/source/fud_format.cpp @@ -1,14 +1,515 @@ -// #include "fud_format.hpp" +#include "fud_format.hpp" + +#include "fud_string_convert.hpp" +#include "fud_utf8.hpp" namespace fud { -/* -Result FormatSpec::make(StringView view, size_t& length) +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) { - static_cast(view); - static_cast(length); - return FudStatus::NotImplemented; + 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 -- cgit v1.2.3