diff --git a/Cesium.CodeGen/Ir/Expressions/ConstantLiteralExpression.cs b/Cesium.CodeGen/Ir/Expressions/ConstantLiteralExpression.cs index cf9bd3d4..de2ba723 100644 --- a/Cesium.CodeGen/Ir/Expressions/ConstantLiteralExpression.cs +++ b/Cesium.CodeGen/Ir/Expressions/ConstantLiteralExpression.cs @@ -47,7 +47,7 @@ private static IConstant GetConstant(Ast.ConstantLiteralExpression expression) private static IConstant ParseFloatingPoint(string value) { - if (value.EndsWith('f')) + if (value.EndsWith('f') || value.EndsWith('F')) { if (!float.TryParse(value.AsSpan().Slice(0, value.Length - 1), NumberStyles.Float, CultureInfo.InvariantCulture.NumberFormat, out var floatValue)) { diff --git a/Cesium.Compiler/stdlib/inttypes.h b/Cesium.Compiler/stdlib/inttypes.h new file mode 100644 index 00000000..b77d5602 --- /dev/null +++ b/Cesium.Compiler/stdlib/inttypes.h @@ -0,0 +1,168 @@ +#pragma once + +//#include +#define PRId8 "hhd" +#define PRId16 "hd" +#define PRId32 "d" +#define PRId64 "lld" +#define PRIdLEAST8 PRId8 +#define PRIdLEAST16 PRId16 +#define PRIdLEAST32 PRId32 +#define PRIdLEAST64 PRId64 +#define PRIdFAST8 PRId8 +#define PRIdFAST16 PRId32 +#define PRIdFAST32 PRId32 +#define PRIdFAST64 PRId64 +#define PRIdMAX PRId64 +#define PRIdPTR PRId64 + +#define PRIi8 "hhi" +#define PRIi16 "hi" +#define PRIi32 "i" +#define PRIi64 "lli" +#define PRIiLEAST8 PRIi8 +#define PRIiLEAST16 PRIi16 +#define PRIiLEAST32 PRIi32 +#define PRIiLEAST64 PRIi64 +#define PRIiFAST8 PRIi8 +#define PRIiFAST16 PRIi32 +#define PRIiFAST32 PRIi32 +#define PRIiFAST64 PRIi64 +#define PRIiMAX PRIi64 +#define PRIiPTR PRIi64 + +#define PRIo8 "hho" +#define PRIo16 "ho" +#define PRIo32 "o" +#define PRIo64 "llo" +#define PRIoLEAST8 PRIo8 +#define PRIoLEAST16 PRIo16 +#define PRIoLEAST32 PRIo32 +#define PRIoLEAST64 PRIo64 +#define PRIoFAST8 PRIo8 +#define PRIoFAST16 PRIo32 +#define PRIoFAST32 PRIo32 +#define PRIoFAST64 PRIo64 +#define PRIoMAX PRIo64 +#define PRIoPTR PRIo64 + +#define PRIu8 "hhu" +#define PRIu16 "hu" +#define PRIu32 "u" +#define PRIu64 "llu" +#define PRIuLEAST8 PRIu8 +#define PRIuLEAST16 PRIu16 +#define PRIuLEAST32 PRIu32 +#define PRIuLEAST64 PRIu64 +#define PRIuFAST8 PRIu8 +#define PRIuFAST16 PRIu32 +#define PRIuFAST32 PRIu32 +#define PRIuFAST64 PRIu64 +#define PRIuMAX PRIu64 +#define PRIuPTR PRIu64 + +#define PRIx8 "hhx" +#define PRIx16 "hx" +#define PRIx32 "x" +#define PRIx64 "llx" +#define PRIxLEAST8 PRIx8 +#define PRIxLEAST16 PRIx16 +#define PRIxLEAST32 PRIx32 +#define PRIxLEAST64 PRIx64 +#define PRIxFAST8 PRIx8 +#define PRIxFAST16 PRIx32 +#define PRIxFAST32 PRIx32 +#define PRIxFAST64 PRIx64 +#define PRIxMAX PRIx64 +#define PRIxPTR PRIx64 + +#define PRIX8 "hhX" +#define PRIX16 "hX" +#define PRIX32 "X" +#define PRIX64 "llX" +#define PRIXLEAST8 PRIX8 +#define PRIXLEAST16 PRIX16 +#define PRIXLEAST32 PRIX32 +#define PRIXLEAST64 PRIX64 +#define PRIXFAST8 PRIX8 +#define PRIXFAST16 PRIX32 +#define PRIXFAST32 PRIX32 +#define PRIXFAST64 PRIX64 +#define PRIXMAX PRIX64 +#define PRIXPTR PRIX64 + + +#define SCNd8 "hhd" +#define SCNd16 "hd" +#define SCNd32 "d" +#define SCNd64 "lld" +#define SCNdLEAST8 SCNd8 +#define SCNdLEAST16 SCNd16 +#define SCNdLEAST32 SCNd32 +#define SCNdLEAST64 SCNd64 +#define SCNdFAST8 SCNd8 +#define SCNdFAST16 SCNd32 +#define SCNdFAST32 SCNd32 +#define SCNdFAST64 SCNd64 +#define SCNdMAX SCNd64 +#define SCNdPTR SCNd64 + +#define SCNi8 "hhi" +#define SCNi16 "hi" +#define SCNi32 "i" +#define SCNi64 "lli" +#define SCNiLEAST8 SCNi8 +#define SCNiLEAST16 SCNi16 +#define SCNiLEAST32 SCNi32 +#define SCNiLEAST64 SCNi64 +#define SCNiFAST8 SCNi8 +#define SCNiFAST16 SCNi32 +#define SCNiFAST32 SCNi32 +#define SCNiFAST64 SCNi64 +#define SCNiMAX SCNi64 +#define SCNiPTR SCNi64 + +#define SCNo8 "hho" +#define SCNo16 "ho" +#define SCNo32 "o" +#define SCNo64 "llo" +#define SCNoLEAST8 SCNo8 +#define SCNoLEAST16 SCNo16 +#define SCNoLEAST32 SCNo32 +#define SCNoLEAST64 SCNo64 +#define SCNoFAST8 SCNo8 +#define SCNoFAST16 SCNo32 +#define SCNoFAST32 SCNo32 +#define SCNoFAST64 SCNo64 +#define SCNoMAX SCNo64 +#define SCNoPTR SCNo64 + +#define SCNu8 "hhu" +#define SCNu16 "hu" +#define SCNu32 "u" +#define SCNu64 "llu" +#define SCNuLEAST8 SCNu8 +#define SCNuLEAST16 SCNu16 +#define SCNuLEAST32 SCNu32 +#define SCNuLEAST64 SCNu64 +#define SCNuFAST8 SCNu8 +#define SCNuFAST16 SCNu32 +#define SCNuFAST32 SCNu32 +#define SCNuFAST64 SCNu64 +#define SCNuMAX SCNu64 +#define SCNuPTR SCNu64 + +#define SCNx8 "hhx" +#define SCNx16 "hx" +#define SCNx32 "x" +#define SCNx64 "llx" +#define SCNxLEAST8 SCNx8 +#define SCNxLEAST16 SCNx16 +#define SCNxLEAST32 SCNx32 +#define SCNxLEAST64 SCNx64 +#define SCNxFAST8 SCNx8 +#define SCNxFAST16 SCNx32 +#define SCNxFAST32 SCNx32 +#define SCNxFAST64 SCNx64 +#define SCNxMAX SCNx64 +#define SCNxPTR SCNx64 diff --git a/Cesium.Compiler/stdlib/stdint.h b/Cesium.Compiler/stdlib/stdint.h new file mode 100644 index 00000000..2691dff4 --- /dev/null +++ b/Cesium.Compiler/stdlib/stdint.h @@ -0,0 +1,40 @@ +#pragma once + +#define INT8_MIN (-127i8 - 1) +#define INT16_MIN (-32767i16 - 1) +#define INT32_MIN (-2147483647i32 - 1) +#define INT64_MIN (-9223372036854775807i64 - 1) +#define INT8_MAX 127i8 +#define INT16_MAX 32767i16 +#define INT32_MAX 2147483647i32 +#define INT64_MAX 9223372036854775807i64 +#define UINT8_MAX 0xffui8 +#define UINT16_MAX 0xffffui16 +#define UINT32_MAX 0xffffffffui32 +#define UINT64_MAX 0xffffffffffffffffui64 + +#define INT_LEAST8_MIN INT8_MIN +#define INT_LEAST16_MIN INT16_MIN +#define INT_LEAST32_MIN INT32_MIN +#define INT_LEAST64_MIN INT64_MIN +#define INT_LEAST8_MAX INT8_MAX +#define INT_LEAST16_MAX INT16_MAX +#define INT_LEAST32_MAX INT32_MAX +#define INT_LEAST64_MAX INT64_MAX +#define UINT_LEAST8_MAX UINT8_MAX +#define UINT_LEAST16_MAX UINT16_MAX +#define UINT_LEAST32_MAX UINT32_MAX +#define UINT_LEAST64_MAX UINT64_MAX + +#define INT_FAST8_MIN INT8_MIN +#define INT_FAST16_MIN INT32_MIN +#define INT_FAST32_MIN INT32_MIN +#define INT_FAST64_MIN INT64_MIN +#define INT_FAST8_MAX INT8_MAX +#define INT_FAST16_MAX INT32_MAX +#define INT_FAST32_MAX INT32_MAX +#define INT_FAST64_MAX INT64_MAX +#define UINT_FAST8_MAX UINT8_MAX +#define UINT_FAST16_MAX UINT32_MAX +#define UINT_FAST32_MAX UINT32_MAX +#define UINT_FAST64_MAX UINT64_MAX diff --git a/Cesium.IntegrationTests/stdlib/io/fprintf.c b/Cesium.IntegrationTests/stdlib/io/fprintf.c new file mode 100644 index 00000000..bbe7fb8f --- /dev/null +++ b/Cesium.IntegrationTests/stdlib/io/fprintf.c @@ -0,0 +1,39 @@ +#include +#include +#include + +int main(void) +{ + const char* s = "Hello"; + printf("Strings:\n"); // same as puts("Strings"); + printf(" padding:\n"); + printf("\t[%10s]\n", s); + printf("\t[%-10s]\n", s); + printf("\t[%*s]\n", 10, s); + printf(" truncating:\n"); + printf("\t%.4s\n", s); + printf("\t%.*s\n", 3, s); + + printf("Characters:\t%c %%\n", 'A'); + + printf("Integers:\n"); + printf("\tDecimal:\t%i %d %.6i %i %.0i %+i %i\n", + 1, 2, 3, 0, 0, 4, -4); + printf("\tHexadecimal:\t%x %x %X %#x\n", 5, 10, 10, 6); + printf("\tOctal:\t\t%o %#o %#o\n", 10, 10, 4); + + printf("Floating point:\n"); + //printf("\tRounding:\t%f %.0f %.32f\n", 1.5, 1.5, 1.3); + printf("\tRounding:\t%f %.0f\n", 1.5, 1.5); + printf("\tPadding:\t%05.2f %.2f %5.2f\n", 1.5, 1.5, 1.5); + printf("\tScientific:\t%E %e\n", 1.5, 1.5); + // Not in the mood for this. + //printf("\tHexadecimal:\t%a %A\n", 1.5, 1.5); + //float divzero = 0.0F; + //printf("\tSpecial values:\t0/0=%g 1/0=%g\n", 0.0 / divzero, 1.0 / divzero); + + //printf("Fixed-width types:\n"); + //printf("\tLargest 32-bit value is %" PRIu32 " or %#" PRIx32 "\n", + // UINT32_MAX, UINT32_MAX); + return 42; +} diff --git a/Cesium.Runtime/StdIoFunctions.cs b/Cesium.Runtime/StdIoFunctions.cs index 30403154..800bea61 100644 --- a/Cesium.Runtime/StdIoFunctions.cs +++ b/Cesium.Runtime/StdIoFunctions.cs @@ -155,6 +155,34 @@ public static int FPrintF(void* stream, byte* str, void* varargs) consumedBytes += lengthTillPercent; int addition = 1; int width = 0; + bool alwaysSign = false; + if (formatString[formatStartPosition + addition] == '+') + { + alwaysSign = true; + addition++; + } + + bool leftAdjust = false; + if (formatString[formatStartPosition + addition] == '-') + { + leftAdjust = true; + addition++; + } + + bool zeroPrepend = false; + if (formatString[formatStartPosition + addition] == '0') + { + zeroPrepend = true; + addition++; + } + + bool alternativeImplementation = false; + if (formatString[formatStartPosition + addition] == '#') + { + alternativeImplementation = true; + addition++; + } + if (formatString[formatStartPosition + addition] == '0') { addition++; @@ -166,17 +194,34 @@ public static int FPrintF(void* stream, byte* str, void* varargs) addition++; } - int precision = 0; // 0 - not set, -1 - star + int paddingRequested = 0; // 0 - not set, -1 - star + if (formatString[formatStartPosition + addition] == '*') + { + paddingRequested = -1; + addition++; + } + + int precision = -1; // -1 - not set, -2 - star if (formatString[formatStartPosition + addition] == '.') { addition++; if (formatString[formatStartPosition + addition] == '*') { - precision = -1; + precision = -2; addition++; while (formatString[formatStartPosition + addition] >= '0' && formatString[formatStartPosition + addition] <= '9') { + if (precision == -2) precision = 0; + precision = precision * 10 + (formatString[formatStartPosition + addition] - '0'); + addition++; + } + } + else + { + while (formatString[formatStartPosition + addition] >= '0' && formatString[formatStartPosition + addition] <= '9') + { + if (precision == -1) precision = 0; precision = precision * 10 + (formatString[formatStartPosition + addition] - '0'); addition++; } @@ -190,28 +235,59 @@ public static int FPrintF(void* stream, byte* str, void* varargs) formatSpecifier += formatString[formatStartPosition + addition].ToString(); } + int padding = -1; + if (paddingRequested == -1) + { + padding = (int)((long*)varargs)[consumedArgs]; + consumedArgs++; + } + else if (width != 0) + { + padding = width; + } + + int trim = -1; + if (precision == -2) + { + trim = (int)((long*)varargs)[consumedArgs]; + consumedArgs++; + } + else if (precision >= 0) + { + trim = precision; + } + switch (formatSpecifier) { case "s": - int trim = -1; - if (precision == -1) { - trim = (int)((long*)varargs)[consumedArgs]; - consumedArgs++; - } + string? stringValue = RuntimeHelpers.Unmarshal((byte*)((long*)varargs)[consumedArgs]); + if (trim != -1) + { + stringValue = stringValue?.Substring(0, Math.Max(0, Math.Min(stringValue.Length - 1, trim))); + } + + if (padding != -1) + { + if (leftAdjust) + { + var actualLength = stringValue?.Length ?? 0; + if (actualLength < padding) + { + stringValue += new string(' ', padding - actualLength); + } + } + else + { + stringValue = string.Format("{0," + padding + "}", stringValue); + } + } - string? stringValue = RuntimeHelpers.Unmarshal((byte*)((long*)varargs)[consumedArgs]); - if (precision == -1) - { - streamWriter.Write(stringValue?.Substring(0, Math.Max(0, Math.Min(stringValue.Length - 1, trim)))); - } - else - { streamWriter.Write(stringValue); + consumedBytes += stringValue?.Length ?? 0; + consumedArgs++; + break; } - consumedBytes += stringValue?.Length ?? 0; - consumedArgs++; - break; case "c": streamWriter.Write((char)(byte)((long*)varargs)[consumedArgs]); consumedBytes++; @@ -223,25 +299,98 @@ public static int FPrintF(void* stream, byte* str, void* varargs) case "li": int intValue = (int)((long*)varargs)[consumedArgs]; var intValueString = intValue.ToString(); - streamWriter.Write(intValueString); + if (alwaysSign && intValue > 0) + { + streamWriter.Write('+'); + } + + if (intValueString.Length < precision) + { + streamWriter.Write(new string('0', precision - intValueString.Length)); + } + + if (precision != 0 || intValue != 0) + { + streamWriter.Write(intValueString); + } + consumedBytes += intValueString.Length; consumedArgs++; break; case "u": case "lu": - uint uintValue = (uint)((long*)varargs)[consumedArgs]; - var uintValueString = uintValue.ToString(); - streamWriter.Write(uintValueString); - consumedBytes += uintValueString.Length; - consumedArgs++; - break; + { + uint uintValue = (uint)((long*)varargs)[consumedArgs]; + var uintValueString = uintValue.ToString(); + streamWriter.Write(uintValueString); + consumedBytes += uintValueString.Length; + consumedArgs++; + break; + } case "f": - var floatNumber = ((double*)varargs)[consumedArgs]; - string floatNumberString = floatNumber.ToString("F6"); - streamWriter.Write(floatNumberString); - consumedBytes += floatNumberString.Length; - consumedArgs++; - break; + { + var floatNumber = ((double*)varargs)[consumedArgs]; + string floatNumberString = floatNumber.ToString("F" + (trim == -1 ? 6 : trim)); + if (alwaysSign && floatNumber > 0) + { + streamWriter.Write('+'); + } + + if (floatNumberString.Length < width) + { + streamWriter.Write(new string(zeroPrepend ? '0' : ' ', width - floatNumberString.Length)); + } + + streamWriter.Write(floatNumberString); + consumedBytes += floatNumberString.Length; + consumedArgs++; + break; + } + case "e": + case "E": + { + var floatNumber = ((double*)varargs)[consumedArgs]; + //streamWriter.Write($"!padding {padding} trim {trim} precision {precision} "); + string floatNumberString = floatNumber.ToString("0." + new string('0', trim == -1 ? 6 : trim) + formatSpecifier + "+00"); + if (alwaysSign && floatNumber > 0) + { + streamWriter.Write('+'); + } + + streamWriter.Write(floatNumberString); + //streamWriter.Write($"!"); + consumedBytes += floatNumberString.Length; + consumedArgs++; + break; + } + case "o": + { + uint uintValue = (uint)((long*)varargs)[consumedArgs]; + StringBuilder stringBuilder = new(); + while (uintValue >= 8) + { + stringBuilder.Insert(0, (uintValue % 8)); + uintValue /= 8; + } + + stringBuilder.Insert(0, uintValue); + + var stringValue = stringBuilder.ToString(); + if (paddingRequested == -1) + { + stringValue = string.Format("{0," + padding + "}", stringValue); + } + + if (alternativeImplementation && stringValue[0] != '0') + { + streamWriter.Write('0'); + } + + streamWriter.Write(stringValue); + consumedBytes += stringValue.Length; + consumedArgs++; + break; + } case "p": nint pointerValue = ((nint*)varargs)[consumedArgs]; string pointerValueString = pointerValue.ToString("X"); @@ -254,6 +403,12 @@ public static int FPrintF(void* stream, byte* str, void* varargs) nuint hexadecimalValue = ((nuint*)varargs)[consumedArgs]; if (hexadecimalValue != 0) { + if (alternativeImplementation) + { + streamWriter.Write('0'); + streamWriter.Write(formatSpecifier); + } + var targetFormat = "{0:" + formatSpecifier + (width == 0 ? "" : width) + "}"; // NOTE: without converting nuint to long, this was broken on .NET Framework var hexadecimalValueString = string.Format(targetFormat, (long)hexadecimalValue);