Exemplo n.º 1
0
/// <summary>
/// Replace all instances of a pattern in a string with a replacement string (scanning from left to right).
/// </summary>
/// <param name="str">The string to search through (if NULL, an empty string is returned).</param>
/// <param name="pattern">The pattern to search for (if NULL, 'str' is returned).</param>
/// <param name="replacement">Replacement text for each instance of the pattern (if NULL, will be treated as empty).</param>
/// <returns>A new string where all instances of the pattern have been replaced by the given replacement string.</returns>
String String_Replace(const String str, const String pattern, const String replacement)
{
	const struct StringInt *s, *p, *r;
	StringBuilder stringBuilder;
	struct StringBuilderInt sb;
	Byte *text, *patText, *repText;
	Int lastEnd, index;

	if (String_IsNullOrEmpty(str)) return String_Empty;
	if (String_IsNullOrEmpty(pattern)) return str;

	s = (const struct StringInt *)str;
	text = s->text;
	p = (const struct StringInt *)pattern;
	patText = p->text;
	r = replacement != NULL ? (const struct StringInt *)replacement : (const struct StringInt *)String_Empty;
	repText = r->text;

	stringBuilder = (StringBuilder)&sb;
	StringBuilder_InitWithSize(stringBuilder, s->length);

	lastEnd = 0;
	index = 0;
	while ((index = String_IndexOf(str, pattern, index)) >= 0)
	{
		if (index > lastEnd)
		{
			StringBuilder_Append(stringBuilder, text, lastEnd, index - lastEnd);
		}
		StringBuilder_Append(stringBuilder, repText, 0, r->length);
		lastEnd = (index += p->length);
	}

	if (lastEnd < s->length)
	{
		StringBuilder_Append(stringBuilder, text, lastEnd, s->length - lastEnd);
	}

	return StringBuilder_ToString(stringBuilder);
}
StringBuilder StringBuilder_AppendInt(StringBuilder dynString, int value)
{
    DynamicString string = (DynamicString) dynString;
    if (string)
    {
        // 32-bits gives a 10-digit number + 1 for the null-terminator
        char str[11];
        memset(str, 0, sizeof(str));
        sprintf((char*) str, (const char*) "%d", value);
        string = StringBuilder_Append(string, str);
    }
    return (StringBuilder) string;
}
static bool CommandBoardDetails(int argc, char** argv)
{
    bool result = false;
    if (ConfigStore_Config_Read() && ConfigStore_Config_IsValid() && ConfigStore_Config_IsMagicValid())
    {
        StringBuilder response = StringBuilder_New(256);

        // DeviceType
        response = StringBuilder_Append(response, ConfigStore_GetDeviceType());
        response = StringBuilder_Append(response, LINE_TERM);

        // MAC address
        response = StringBuilder_Append(response, ConfigStore_GetMacAddress());
        response = StringBuilder_Append(response, LINE_TERM);

        // Serial number
        char snString[17];
        memset((void*) snString, 0, 17);
        if (DeviceSerial_GetCpuSerialNumberHexString(snString, 17))
            response = StringBuilder_Append(response, snString);
        else
            response = StringBuilder_Append(response, " ");
        response = StringBuilder_Append(response, LINE_TERM);
        // WiFi SoftAP SSID
        response = StringBuilder_Append(response, ConfigStore_GetSoftAPSSID());
        response = StringBuilder_Append(response, LINE_TERM);

        // WiFi SoftAP password
        response = StringBuilder_Append(response, ConfigStore_GetSoftAPPassword());
        response = StringBuilder_Append(response, LINE_TERM);
        char CB[2] =
        { 0, 0 };
        int32_t byteCount = StringBuilder_GetLength(response);
        int32_t byteIndex = 0;
        for (byteIndex = 0; byteIndex < byteCount; byteIndex++)
        {
            CB[0] += (StringBuilder_GetCString(response))[byteIndex];
        }
        // Compute checkbyte - 2's compliment of MOD-256 sum
        CB[0] ^= 0xFF;
        CB[0]++;
        response = StringBuilder_Append(response, CB);
        response = StringBuilder_Append(response, LINE_TERM);
        CreatorConsole_Puts(response);

        // Output entire response  // TODO - delete?
//		byteCount = StringBuilder_GetLength(response);
//		uint8_t* _response = (uint8_t*) StringBuilder_GetCString(response);
//		for (byteIndex = 0; byteIndex < byteCount; byteIndex++)
//		{
//			if (((char *) _response)[byteIndex] == '\n')
//				CreatorConsole_Puts(LINE_TERM);
//			else
//				CreatorConsole_Putc(((char *) _response)[byteIndex]);
//		}
        //CreatorConsole_Puts(LINE_TERM);

        StringBuilder_Free(&response);
        result = true;
    }
    return result;
}
Exemplo n.º 4
0
String Real_ToFixedString(Byte *buffer, Int32 len, Int32 exp, Int32 kind, Int32 minIntDigits, Int32 minFracDigits, Bool forceSign)
{
	DECLARE_INLINE_STRINGBUILDER(numBuilder, 256);

	INIT_INLINE_STRINGBUILDER(numBuilder);

	switch (kind) {
	case REAL_KIND_POS_INF:
		return forceSign ? Real_String_PosInf : Real_String_Inf;
	case REAL_KIND_NEG_INF:
		return Real_String_NegInf;
	case REAL_KIND_POS_QNAN:
		return forceSign ? Real_String_PosNaN : Real_String_NaN;
	case REAL_KIND_NEG_QNAN:
		return Real_String_NegNaN;
	case REAL_KIND_POS_SNAN:
		return forceSign ? Real_String_PosSNaN : Real_String_SNaN;
	case REAL_KIND_NEG_SNAN:
		return Real_String_NegSNaN;
	case REAL_KIND_POS_ZERO:
	case REAL_KIND_POS_NUM:
		if (forceSign) {
			StringBuilder_AppendByte(numBuilder, '+');
		}
		break;
	case REAL_KIND_NEG_ZERO:
	case REAL_KIND_NEG_NUM:
		StringBuilder_AppendByte(numBuilder, '-');
		break;
	}

	if (exp >= 0) {

		// All digits are integer; none are fractional.
		if (len + exp < minIntDigits) {
			// Need to prepend initial zeros.
			StringBuilder_AppendRepeat(numBuilder, '0', minIntDigits - (len + exp));
		}

		// Copy all the integer digits.
		StringBuilder_Append(numBuilder, buffer, 0, len);

		if (exp > 0) {
			// Emit trailing zeros.
			StringBuilder_AppendRepeat(numBuilder, '0', exp);
		}

		if (minFracDigits > 0) {
			// Need to append trailing fractional zeros.
			StringBuilder_AppendByte(numBuilder, '.');
			StringBuilder_AppendRepeat(numBuilder, '0', minFracDigits);
		}
	}
	else if (-exp >= len) {

		// All digits are fractional; none are integer.
		if (minIntDigits > 0) {
			// Need to prepend initial zeros.
			StringBuilder_AppendRepeat(numBuilder, '0', minIntDigits);
		}

		// Emit the decimal point.
		StringBuilder_AppendByte(numBuilder, '.');

		if (-exp - len > 0) {
			// Emit leading zeros.
			StringBuilder_AppendRepeat(numBuilder, '0', -exp - len);
		}

		// Copy the fractional digits.
		StringBuilder_Append(numBuilder, buffer, 0, len);

		if (minFracDigits > -exp) {
			// Emit trailing zeros.
			StringBuilder_AppendRepeat(numBuilder, '0', minFracDigits - -exp);
		}
	}
	else {
		// Negative exponent, and split across the decimal point: Some integer, some fractional digits.
		Int32 numIntDigits = len - -exp, numFracDigits = -exp;

		if (numIntDigits < minIntDigits) {
			// Need to prepend initial zeros.
			StringBuilder_AppendRepeat(numBuilder, '0', minIntDigits - numIntDigits);
		}

		// Copy all the integer digits.
		StringBuilder_Append(numBuilder, buffer, 0, numIntDigits);

		// Emit the decimal point.
		StringBuilder_AppendByte(numBuilder, '.');

		// Copy the fractional digits.
		StringBuilder_Append(numBuilder, buffer, numIntDigits, numFracDigits);

		if (numFracDigits < minFracDigits) {
			// Need to append trailing zeros.
			StringBuilder_AppendRepeat(numBuilder, '0', minFracDigits - numFracDigits);
		}
	}

	return StringBuilder_ToString(numBuilder);
}
Exemplo n.º 5
0
String Real_ToExpString(Byte *buffer, Int32 len, Int32 exp, Int32 kind, Int32 minFracDigits, Bool forceSign)
{
	DECLARE_INLINE_STRINGBUILDER(numBuilder, 256);
	Int32 numFracDigits;

	INIT_INLINE_STRINGBUILDER(numBuilder);

	switch (kind) {
	case REAL_KIND_POS_INF:
		return forceSign ? Real_String_PosInf : Real_String_Inf;
	case REAL_KIND_NEG_INF:
		return Real_String_NegInf;
	case REAL_KIND_POS_QNAN:
		return forceSign ? Real_String_PosNaN : Real_String_NaN;
	case REAL_KIND_NEG_QNAN:
		return Real_String_NegNaN;
	case REAL_KIND_POS_SNAN:
		return forceSign ? Real_String_PosSNaN : Real_String_SNaN;
	case REAL_KIND_NEG_SNAN:
		return Real_String_NegSNaN;
	case REAL_KIND_POS_ZERO:
	case REAL_KIND_POS_NUM:
		if (forceSign) {
			StringBuilder_AppendByte(numBuilder, '+');
		}
		break;
	case REAL_KIND_NEG_ZERO:
	case REAL_KIND_NEG_NUM:
		StringBuilder_AppendByte(numBuilder, '-');
		break;
	}

	// Output the digits.
	StringBuilder_AppendByte(numBuilder, buffer[0]);
	numFracDigits = len - 1;
	if (numFracDigits > 0 || numFracDigits < minFracDigits) {
		StringBuilder_AppendByte(numBuilder, '.');
	}
	if (numFracDigits > 0) {
		StringBuilder_Append(numBuilder, buffer, 1, numFracDigits);
	}
	exp += numFracDigits;

	// Pad any missing trailing zeros.  This is unrolled for speed.
	while (numFracDigits + 16 <= minFracDigits) {
		StringBuilder_Append(numBuilder, "0000000000000000", 0, 16);
		numFracDigits += 16;
	}
	if (numFracDigits <= minFracDigits) {
		StringBuilder_Append(numBuilder, "0000000000000000", 0, minFracDigits - numFracDigits);
		numFracDigits += (minFracDigits - numFracDigits);
	}

	// Output the exponent, always including 'e+' or 'e-' before it.  The only exception
	// to this is if we're outputting a zero.
	if (kind != REAL_KIND_POS_ZERO && kind != REAL_KIND_NEG_ZERO) {
		StringBuilder_Append(numBuilder, exp < 0 ? "e-" : "e+", 0, 2);
		StringBuilder_AppendFormat(numBuilder, "%d", exp < 0 ? -exp : exp);
	}

	// And we're done!
	return StringBuilder_ToString(numBuilder);
}
Exemplo n.º 6
0
SMILE_API_FUNC Bool Real128_TryParse(String str, Real128 *result)
{
	DECLARE_INLINE_STRINGBUILDER(cleanString, 256);
	const Byte *src, *end, *start;
	Byte ch;

	src = String_GetBytes(str);
	end = src + String_Length(str);

	INIT_INLINE_STRINGBUILDER(cleanString);

	// We need to clean the Smile-isms out of the string so that it's just raw digits,
	// decimal points, and possibly 'E' and signs.  Then we can pass it to the native
	// parsing function.

	// Skip initial whitespace.
	while (src < end && (ch = *src) >= '\x00' && ch <= '\x20') src++;

	// If there's no content, this is a fail.
	if (src >= end) {
		*result = Real128_Zero;
		return False;
	}

	// Trim off trailing whitespace.
	while (end > src && (ch = end[-1]) >= '\x00' && ch <= '\x20') end--;

	// Check for named numeric values like "inf" and "nan".  We only allow quiet NaNs, since
	// nothing in Smile's numerics supports signaling NaNs.  We have to check for these up front,
	// since the underlying parser can't indicate the difference beween a failed parse and the
	// user actually requesting "NaN".  It also allows "infinity", fully-spelled-out, which we
	// do not.
	//
	// Note: These tests are carefully ordered so that the compiler's optimizer can easily
	// perform CSE on them; these read cleanly, but they optimize down to the most-efficient way
	// of testing for this.  Don't reorder these without a good reason.
	if (src + 3 == end
		&& (((ch = src[0]) == 'i' || ch == 'I')
		 && ((ch = src[1]) == 'n' || ch == 'N')
		 && ((ch = src[2]) == 'f' || ch == 'F'))) {
		*result = Real128_Inf;
		return True;
	}
	else if (src + 3 == end
		&& (((ch = src[0]) == 'n' || ch == 'N')
		&& ((ch = src[1]) == 'a' || ch == 'A')
		&& ((ch = src[2]) == 'n' || ch == 'N'))) {
		*result = Real128_NaN;
		return True;
	}
	else if (src + 4 == end
		&& (src[0] == '+'
		 && ((ch = src[1]) == 'i' || ch == 'I')
		 && ((ch = src[2]) == 'n' || ch == 'N')
		 && ((ch = src[3]) == 'f' || ch == 'F'))) {
		*result = Real128_Inf;
		return True;
	}
	else if (src + 4 == end
		&& (src[0] == '+'
		 && ((ch = src[1]) == 'n' || ch == 'N')
		 && ((ch = src[2]) == 'a' || ch == 'A')
		 && ((ch = src[3]) == 'n' || ch == 'N'))) {
		*result = Real128_NaN;
		return True;
	}
	else if (src + 4 == end
		&& (src[0] == '-'
		&& ((ch = src[1]) == 'i' || ch == 'I')
		&& ((ch = src[2]) == 'n' || ch == 'N')
		&& ((ch = src[3]) == 'f' || ch == 'F'))) {
		*result = Real128_NegInf;
		return True;
	}
	else if (src + 4 == end
		&& (src[0] == '-'
		&& ((ch = src[1]) == 'n' || ch == 'N')
		&& ((ch = src[2]) == 'a' || ch == 'A')
		&& ((ch = src[3]) == 'n' || ch == 'N'))) {
		*result = Real128_NegNaN;
		return True;
	}

	start = src;

	// Copy an optional initial '+' or '-' as a sign.
	if ((ch = *src) == '+' || ch == '-') {
		src++;
	}

	// Make sure this doesn't start with a ' or " or _ character, since those separators are illegal starting chars.
	if ((ch = *src) == '\'' || ch == '\"' || ch == '_') {
		*result = Real128_Zero;
		return False;
	}

	// Copy digit chunks and radix and exponent characters, discarding embedded ' and " and _ characters.
	// We don't need to validate this part, because the underlying parser will do so.
	while (src < end) {
		switch (ch = *src) {

			case '\'':
			case '\"':
			case '_':
				// Separator character.
				if (src > start) {
					StringBuilder_Append(cleanString, start, 0, src - start);
				}
				else {
					// Two separator chars in a row is illegal.
					*result = Real128_Zero;
					return False;
				}
				start = ++src;
				break;

			case '0': case '1': case '2': case '3': case '4':
			case '5': case '6': case '7': case '8': case '9':
			case 'e':
			case 'E':
			case '+':
			case '-':
			case '.':
				// Legal numeric character of some kind.
				src++;
				break;

			default:
				// Unknown character is an error.
				*result = Real128_Zero;
				return False;
				break;
		}
	}

	if (src > start) {
		StringBuilder_Append(cleanString, start, 0, src - start);
	}
	else {
		// Ending with a separator character is illegal.
		*result = Real128_Zero;
		return False;
	}

	// Make sure this results in a C-style string.
	StringBuilder_AppendByte(cleanString, '\0');

	// The StringBuilder now contains the desired string, at it's at least *somewhat*
	// legitimately structured.  The rest of the parsing (and validation) can be done
	// by the underlying raw parser, which will return a NaN if the string isn't valid.
	// We read the content right out of the StringBuilder:  If the content is short
	// enough, all of the data will be on the stack, so we can avoid ever allocating
	// anything at all on the heap, which is great for performance.
	*result = Real128_FromRawCString((const char *)StringBuilder_GetBytes(cleanString));
	return !Real128_IsNaN(*result);
}
/*********************************************************************
 * Function:        void SYS_ArduinoMonitorTask(void)
 *
 * PreCondition:    None
 *
 * Input:           None
 *
 * Output:          Receive and monitor communications with the Arduino-shield serial comms channel
 *
 * Side Effects:    None
 *
 * Overview:        This function implements the task for monitoring comms from the Arduino shield.
 *
 * Note:            None
 ********************************************************************/
void SYS_ArduinoMonitorTask(FlowThread thread, void *taskParameters)
{
	char incomingChar = 0;

	#define ARDUINO_SERIAL_PORT 6
	#define ARDUINO_SERIAL_BAUD 115200

	// power the PMODRS232
	#if defined(MICROCHIP_PIC32)
    /*
     * UART 6 - Shield RS232 port
     * - u6Txd on pin RPB15
     * - u6RXd on pin RPC2
     */

    // TXd Pin setup.
	PLIB_PORTS_PinModePerPortSelect (PORTS_ID_0, PORT_CHANNEL_B, PORTS_BIT_POS_15, PORTS_PIN_MODE_DIGITAL);
    PLIB_PORTS_PinDirectionOutputSet( PORTS_ID_0, PORT_CHANNEL_B, PORTS_BIT_POS_15 );
    RPB15R = 0x04;

    // RXd Pin setup.
	PLIB_PORTS_PinModePerPortSelect (PORTS_ID_0, PORT_CHANNEL_C, PORTS_BIT_POS_2, PORTS_PIN_MODE_DIGITAL);
	PLIB_PORTS_PinDirectionInputSet( PORTS_ID_0, PORT_CHANNEL_C, PORTS_BIT_POS_2 );
    U6RXR = 0x0C;

	// RPG8 3V3 supply Digilent PMODRS232
    PLIB_PORTS_PinModePerPortSelect (PORTS_ID_0, PORT_CHANNEL_G, PORTS_BIT_POS_8, PORTS_PIN_MODE_DIGITAL);
	PLIB_PORTS_PinDirectionOutputSet( PORTS_ID_0, PORT_CHANNEL_G, PORTS_BIT_POS_8 );
	PLIB_PORTS_PinSet( PORTS_ID_0, PORT_CHANNEL_G, PORTS_BIT_POS_8 );

	// RPG7 0V supply to Digilent PMODRS232
    PLIB_PORTS_PinModePerPortSelect (PORTS_ID_0, PORT_CHANNEL_G, PORTS_BIT_POS_7, PORTS_PIN_MODE_DIGITAL);
	PLIB_PORTS_PinDirectionOutputSet( PORTS_ID_0, PORT_CHANNEL_G, PORTS_BIT_POS_7 );
	PLIB_PORTS_PinClear( PORTS_ID_0, PORT_CHANNEL_G, PORTS_BIT_POS_7 );
	#endif

	arduinoSerial = FlowSerial_Init(ARDUINO_SERIAL_PORT, ARDUINO_SERIAL_BAUD);

	#define ARDUINO_CMD_BUFFER_LENGTH 255
	char commandBuffer[ARDUINO_CMD_BUFFER_LENGTH+1];
	char* cursor = commandBuffer;
	unsigned int freeBufferSize = ARDUINO_CMD_BUFFER_LENGTH;
	bool processCommand = false;

	memset(commandBuffer, '\0', ARDUINO_CMD_BUFFER_LENGTH+1);

	while(1)
	{
		while (FlowSerial_Ready(arduinoSerial))
		{
			incomingChar = FlowSerial_Getc(arduinoSerial);

			if (freeBufferSize > 0 && incomingChar != '\n' && incomingChar != '\r')
			{
				*cursor = incomingChar;
				freeBufferSize--;
				cursor++;
			}
			else
			{
				*cursor = '\0';
				processCommand = true;
			}

			if (processCommand)
			{
				cmdRequestID++;
				
				// Create a request to send to device owner
				StringBuilder request = StringBuilder_New(256);

				// Response Header
				request = StringBuilder_Append(request, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
				request = StringBuilder_Append(request, "<command>");

				char* datetime = CommandHandlers_GetTimeString();
				request = StringBuilder_Append(request, "<sent type=\"datetime\">");
				request = StringBuilder_Append(request, datetime);
				request = StringBuilder_Append(request, "</sent>");
				Flow_MemFree((void **) &datetime);

				request = StringBuilder_Append(request, "<to>");
				request = StringBuilder_Append(request, g_OwnerAoR);
				request = StringBuilder_Append(request, "</to>");

				request = StringBuilder_Append(request, "<from>");
				request = StringBuilder_Append(request, g_DeviceAoR);
				request = StringBuilder_Append(request, "</from>");

				request = StringBuilder_Append(request, "<clientid type=\"integer\">");
				request = StringBuilder_Append(request, g_ClientID);
				request = StringBuilder_Append(request, "</clientid>");

				request = StringBuilder_Append(request, "<requestid type=\"integer\">");
				request = StringBuilder_AppendInt(request, cmdRequestID);
				request = StringBuilder_Append(request, "</requestid>");

				request = StringBuilder_Append(request, "<details>MIF DISPLAY ");
				request = StringBuilder_Append(request, commandBuffer);
				request = StringBuilder_Append(request, "</details>");
				
				request = StringBuilder_Append(request, "</command>");

				if (FlowMessaging_SendMessageToUser((FlowID) g_OwnerID, "text/plain", (char *)StringBuilder_GetCString(request), StringBuilder_GetLength(request), 60))
				{
					FlowConsole_Printf("\n\rSuccessfully forwarded command received from Arduino-shield comms interface ('%s').\n\r", commandBuffer);
				}
				else
				{
					FlowConsole_Puts("\n\rWarning: Could not forward command received from Arduino-shield comms interface.");
				}				

				// Clean up
				StringBuilder_Free(&request);
				processCommand = false;
				cursor = commandBuffer;
				freeBufferSize = ARDUINO_CMD_BUFFER_LENGTH;
			}
		}
        vTaskDelay( 100 / portTICK_RATE_MS );
	}
}
Exemplo n.º 8
0
Int Lexer_ParseReal(Lexer lexer, Bool isFirstContentOnLine)
{
	DECLARE_INLINE_STRINGBUILDER(digitBuilder, 256);	// 256 is plenty for most numbers, but it can grow if necessary.
	const Byte *src = lexer->src;
	const Byte *end = lexer->end;
	const Byte *start;
	Byte ch;
	Token token = lexer->token;
	Int integerDigitCount = 0;
	Int fractionalDigitCount = 0;
	const Byte *digits;
	String digitString, suffix;
	const Byte *suffixText;
	Float64 float64;

	UNUSED(isFirstContentOnLine);

	INIT_INLINE_STRINGBUILDER(digitBuilder);

	START_TOKEN(src);

	// Collect integer digits.
	start = src;
	while (src < end && (ch = *src) >= '0' && ch <= '9') {
		src++;
		if (src + 1 < end
			&& ((ch = *src) == '\'' || ch == '\"' || ch == '_')
			&& src[1] >= '0' && src[1] <= '9') {
			if (src > start) {
				StringBuilder_Append(digitBuilder, start, 0, src - start);
			}
			src++;
			start = src;
		}
	}

	// Copy into the digitBuilder whatever integers are left.
	if (src > start) {
		StringBuilder_Append(digitBuilder, start, 0, src - start);
	}

	integerDigitCount = StringBuilder_GetLength(digitBuilder);

	// Collect the decimal point.
	if (src < end && *src == '.') {
		src++;

		// Collect fractional digits.
		while (src < end && (ch = *src) >= '0' && ch <= '9') {
			src++;
			if (src + 1 < end
				&& ((ch = *src) == '\'' || ch == '\"' || ch == '_')
				&& src[1] >= '0' && src[1] <= '9') {
				if (src > start) {
					StringBuilder_Append(digitBuilder, start, 0, src - start);
				}
				src++;
				start = src;
			}
		}

		fractionalDigitCount = StringBuilder_GetLength(digitBuilder) - 1 - integerDigitCount;
	}

	// Finally copy into the digitBuilder whatever's left.
	if (src > start) {
		StringBuilder_Append(digitBuilder, start, 0, src - start);
	}
	lexer->src = src;

	// Make the result C-friendly.
	StringBuilder_AppendByte(digitBuilder, '\0');

	// Extract out the raw text of the number.
	digitString = StringBuilder_ToString(digitBuilder);
	digits = String_GetBytes(digitString);

	// Get any trailing type identifiers.
	suffix = CollectAlphanumericSuffix(lexer);

	// And make sure the result is clean.
	if (!EnsureEndOfNumber(lexer)) {
		token->text = IllegalRealValueMessage;
		return END_TOKEN(TOKEN_ERROR);
	}

	suffixText = String_GetBytes(suffix);
	if (suffixText[0] == '\0') {
		// Real64.
		if (!Real64_TryParse(digitString, &token->data.real64)) {
			token->text = IllegalRealValueMessage;
			return END_TOKEN(TOKEN_ERROR);
		}
		token->text = digitString;
		return END_TOKEN(TOKEN_REAL64);
	}
	else if (suffixText[0] == 'F' || suffixText[0] == 'f') {
		if (suffixText[1] == '\0') {
			// Float64.
			float64 = strtod(digits, NULL);
			token->data.float64 = float64;
			token->text = digitString;
			return END_TOKEN(TOKEN_FLOAT64);
		}
		else goto badSuffix;
	}
	else if (suffixText[0] == 'L' || suffixText[0] == 'l') {
		// 128-bit something-or-other.
		if (suffixText[1] == '\0') {
			// Real128.
			if (!Real128_TryParse(digitString, &token->data.real128)) {
				token->text = IllegalRealValueMessage;
				return END_TOKEN(TOKEN_ERROR);
			}
			token->text = String_Concat(digitString, suffix);
			return END_TOKEN(TOKEN_REAL128);
		}
		else if ((suffixText[1] == 'F' || suffixText[1] == 'f') && suffixText[2] == '\0') {
			// Float128 (not yet supported).
			goto badSuffix;
		}
		else goto badSuffix;
	}
	else if (suffixText[0] == 'H' || suffixText[0] == 'h') {
		// 32-bit something-or-other.
		if (suffixText[1] == '\0') {
			// Real32.
			if (!Real32_TryParse(digitString, &token->data.real32)) {
				token->text = IllegalRealValueMessage;
				return END_TOKEN(TOKEN_ERROR);
			}
			token->text = String_Concat(digitString, suffix);
			return END_TOKEN(TOKEN_REAL32);
		}
		else if ((suffixText[1] == 'F' || suffixText[1] == 'f') && suffixText[2] == '\0') {
			// Float32.
			float64 = strtod(digits, NULL);
			token->data.float32 = (Float32)float64;
			token->text = digitString;
			return END_TOKEN(TOKEN_FLOAT32);
		}
		else goto badSuffix;
	}
	else goto badSuffix;

badSuffix:
	token->text = String_FormatString(IllegalNumericSuffixMessage, suffix);
	return END_TOKEN(TOKEN_ERROR);
}