summaryrefslogtreecommitdiff
path: root/source/fud_format.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/fud_format.cpp')
-rw-r--r--source/fud_format.cpp515
1 files changed, 508 insertions, 7 deletions
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, FudStatus> FormatSpec::make(StringView view, size_t& length)
+namespace impl {
+
+Result<size_t, FudStatus> 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, FudStatus> FormatSpec::make(StringView formatView, size_t& specLength)
+{
+ static_cast<void>(formatView);
+ static_cast<void>(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<size_t, FudStatus> 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<char>(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<void>(view);
- static_cast<void>(length);
- return FudStatus::NotImplemented;
+ if (hasPosition) {
+ auto positionResult{fromString<decltype(spec.position)>(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<FormatFill> 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<utf8>(FormatSign::Plus):
+ spec.formatSign = FormatSign::Plus;
+ break;
+ case static_cast<utf8>(FormatSign::Minus):
+ spec.formatSign = FormatSign::Minus;
+ break;
+ case static_cast<utf8>(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<decltype(spec.width)>(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<decltype(spec.position)>(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<decltype(spec.width)>(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<decltype(spec.position)>(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<utf8>(FormatType::String):
+ spec.formatType = FormatType::String;
+ break;
+ case static_cast<utf8>(FormatType::Escaped):
+ spec.formatType = FormatType::Escaped;
+ break;
+ case static_cast<utf8>(FormatType::BinaryLower):
+ spec.formatType = FormatType::BinaryLower;
+ break;
+ case static_cast<utf8>(FormatType::BinaryUpper):
+ spec.formatType = FormatType::BinaryUpper;
+ break;
+ case static_cast<utf8>(FormatType::Character):
+ spec.formatType = FormatType::Character;
+ break;
+ case static_cast<utf8>(FormatType::Decimal):
+ spec.formatType = FormatType::Decimal;
+ break;
+ case static_cast<utf8>(FormatType::Octal):
+ spec.formatType = FormatType::Octal;
+ break;
+ case static_cast<utf8>(FormatType::HexLower):
+ spec.formatType = FormatType::HexLower;
+ break;
+ case static_cast<utf8>(FormatType::HexUpper):
+ spec.formatType = FormatType::HexUpper;
+ break;
+ case static_cast<utf8>(FormatType::FloatHexLower):
+ spec.formatType = FormatType::FloatHexLower;
+ break;
+ case static_cast<utf8>(FormatType::FloatHexUpper):
+ spec.formatType = FormatType::FloatHexUpper;
+ break;
+ case static_cast<utf8>(FormatType::ScientificLower):
+ spec.formatType = FormatType::ScientificLower;
+ break;
+ case static_cast<utf8>(FormatType::ScientificUpper):
+ spec.formatType = FormatType::ScientificUpper;
+ break;
+ case static_cast<utf8>(FormatType::FixedLower):
+ spec.formatType = FormatType::FixedLower;
+ break;
+ case static_cast<utf8>(FormatType::FixedUpper):
+ spec.formatType = FormatType::FixedUpper;
+ break;
+ case static_cast<utf8>(FormatType::GeneralLower):
+ spec.formatType = FormatType::GeneralLower;
+ break;
+ case static_cast<utf8>(FormatType::GeneralUpper):
+ spec.formatType = FormatType::GeneralUpper;
+ break;
+ case static_cast<utf8>(FormatType::Unspecified):
+ default:
+ return FudStatus::FormatInvalid;
+ }
+
+ formatView.advanceUnsafe();
+ return FudStatus::Success;
+}
+
+} // namespace impl
} // namespace fud