StringUtils
Table of Contents
1. Description
License: BSD-3-Clause
// SPDX-FileCopyrightText: © 2024 Alexander Kromm <mmaulwurff@gmail.com> // SPDX-License-Identifier: BSD-3-Clause // StringUtils module version: 1.4.0 // StringUtils is a part of DoomToolbox: https://github.com/mmaulwurff/doom-toolbox/
2. Classes
2.1. StringUtils
This class acts as a namespace for free functions.
class NAMESPACE_su { static clearscope string join(Array<string> strings, string delimiter = ", ") { uint nStrings = strings.size(); if (nStrings == 0) return ""; string result = strings[0]; for (uint i = 1; i < nStrings; ++i) result.appendFormat("%s%s", delimiter, strings[i]); return result; } static clearscope string repeat(string aString, int times) { // Make the specified number of spaces using padding format. string result = string.format("%*d", times + 1, 0); result.deleteLastCharacter(); result.replace(" ", aString); return result; } static clearscope string boolToString(bool value) { return value ? "true" : "false"; } static clearscope bool isWordDelimiter(int character) { if (character == NAMESPACE_Ascii.HYPHEN_MINUS) return false; if (character < NAMESPACE_Ascii.DIGIT_ZERO) return true; if (NAMESPACE_Ascii.COLON <= character && character <= NAMESPACE_Ascii.COMMERCIAL_AT) return true; if (NAMESPACE_Ascii.LEFT_SQUARE_BRACKET <= character && character <= NAMESPACE_Ascii.GRAVE_ACCENT) return true; if (NAMESPACE_Ascii.LEFT_CURLY_BRACKET <= character && character <= NAMESPACE_Ascii.DELETE) return true; // Various unicode spaces. if (0x2000 <= character && character <= 0x200B) return true; static const int unicodeDelimiters[] = { 0x0085, // next line 0x00A0, // non-breaking space 0x2028, // line separator 0x2029, // paragraph separator 0x202F, // narrow non-breaking space 0x205F, // medium mathematical space 0x3000 // ideographic space }; foreach (unicodeDelimiter : unicodeDelimiters) if (character == unicodeDelimiter) return true; return false; } static clearscope void splitByWords(string text, out Array<string> words) { int length = text.length(); string currentWord; for (int i = 0; i < length;) { int character; [character, i] = text.getNextCodePoint(i); if (!isWordDelimiter(character)) { currentWord.appendCharacter(character); continue; } if (currentWord.length() != 0) { words.push(currentWord); currentWord = ""; } } if (currentWord.length() != 0) words.push(currentWord); } }
2.1.1. join
Joins strings to a single string separated by delimiter.
static clearscope string join(Array<string> strings, string delimiter = ", ") { uint nStrings = strings.size(); if (nStrings == 0) return ""; string result = strings[0]; for (uint i = 1; i < nStrings; ++i) result.appendFormat("%s%s", delimiter, strings[i]); return result; }
{
Array<string> strings;
it("join: empty", Assert(su.join(strings, ".") == ""));
strings.push("hello");
it("join: one", Assert(su.join(strings) == "hello"));
strings.push("world");
it("join: default delimiter", Assert(su.join(strings) == "hello, world"));
it("join: empty delimiter", Assert(su.join(strings, "") == "helloworld"));
}
2.1.2. repeat
Repeats the specified string the specified number of times.
static clearscope string repeat(string aString, int times) { // Make the specified number of spaces using padding format. string result = string.format("%*d", times + 1, 0); result.deleteLastCharacter(); result.replace(" ", aString); return result; }
{
it("repeat: zero", Assert(su.repeat("a", 0) == ""));
it("repeat: one", Assert(su.repeat("hello", 1) == "hello"));
it("repeat: 3", Assert(su.repeat("!?", 3) == "!?!?!?"));
it("repeat: empty", Assert(su.repeat("", 7) == ""));
}
2.1.3. boolToString
Writes out a boolean value.
static clearscope string boolToString(bool value) { return value ? "true" : "false"; }
{
it("boolToString: true", Assert(su.boolToString(true) == "true"));
it("boolToString: false", Assert(su.boolToString(false) == "false"));
}
2.1.4. isWordDelimiter
static clearscope bool isWordDelimiter(int character) { if (character == NAMESPACE_Ascii.HYPHEN_MINUS) return false; if (character < NAMESPACE_Ascii.DIGIT_ZERO) return true; if (NAMESPACE_Ascii.COLON <= character && character <= NAMESPACE_Ascii.COMMERCIAL_AT) return true; if (NAMESPACE_Ascii.LEFT_SQUARE_BRACKET <= character && character <= NAMESPACE_Ascii.GRAVE_ACCENT) return true; if (NAMESPACE_Ascii.LEFT_CURLY_BRACKET <= character && character <= NAMESPACE_Ascii.DELETE) return true; // Various unicode spaces. if (0x2000 <= character && character <= 0x200B) return true; static const int unicodeDelimiters[] = { 0x0085, // next line 0x00A0, // non-breaking space 0x2028, // line separator 0x2029, // paragraph separator 0x202F, // narrow non-breaking space 0x205F, // medium mathematical space 0x3000 // ideographic space }; foreach (unicodeDelimiter : unicodeDelimiters) if (character == unicodeDelimiter) return true; return false; }
{
it("isWordDelimiter: space", Assert(su.isWordDelimiter(Ascii.SPACE)));
it("isWordDelimiter: comma", Assert(su.isWordDelimiter(Ascii.COMMA)));
it("isWordDelimiter: non-breaking space", Assert(su.isWordDelimiter(0x00A0)));
it("isWordDelimiter: a", Assert(!su.isWordDelimiter(Ascii.LATIN_SMALL_LETTER_A)));
it("isWordDelimiter: 0", Assert(!su.isWordDelimiter(Ascii.DIGIT_ZERO)));
it("isWordDelimiter: hyphen", Assert(!su.isWordDelimiter(Ascii.HYPHEN_MINUS)));
}
2.1.5. splitByWords
Splits a UTF-8 text by words. Characters considered word delimiters: ASCII control characters, space, comma (','), period ('.'). Delimiters are not included in the output.
static clearscope void splitByWords(string text, out Array<string> words) { int length = text.length(); string currentWord; for (int i = 0; i < length;) { int character; [character, i] = text.getNextCodePoint(i); if (!isWordDelimiter(character)) { currentWord.appendCharacter(character); continue; } if (currentWord.length() != 0) { words.push(currentWord); currentWord = ""; } } if (currentWord.length() != 0) words.push(currentWord); }
{
Array<string> words;
su.splitByWords("", words);
it("split empty", Assert(words.size() == 0));
} {
Array<string> words;
su.splitByWords("hello", words);
it("one word", Assert(words.size() == 1 && words[0] == "hello"));
} {
Array<string> words;
su.splitByWords("hello-world", words);
it("one word with -", Assert(words.size() == 1 && words[0] == "hello-world"));
} {
Array<string> words;
su.splitByWords(" hello world \n ", words);
it("two words, many spaces",
Assert(words.size() == 2 && words[0] == "hello" && words[1] == "world"));
}
2.2. Description
class NAMESPACE_Description { string compose() { return NAMESPACE_su.join(mFields); } NAMESPACE_Description add(string name, string value) { mFields.push(name .. ": " .. value); return self; } NAMESPACE_Description addObject(string name, Object anObject) { if (anObject == NULL) return add(name, "NULL"); string className = anObject.getClassName(); return add(name, className); } NAMESPACE_Description addClass(string name, Class aClass) { if (aClass == NULL) return add(name, "NULL"); return add(name, aClass.getClassName()); } NAMESPACE_Description addBool(string name, bool value) { return add(name, NAMESPACE_su.boolToString(value)); } NAMESPACE_Description addInt(string name, int value) { return add(name, string.format("%d", value)); } NAMESPACE_Description addFloat(string name, double value) { return add(name, string.format("%.2f", value)); } NAMESPACE_Description addDamageFlags(string name, EDmgFlags flags) { Array<string> results; if (flags & DMG_NO_ARMOR) results.push("DMG_NO_ARMOR"); if (flags & DMG_INFLICTOR_IS_PUFF) results.push("DMG_INFLICTOR_IS_PUFF"); if (flags & DMG_THRUSTLESS) results.push("DMG_THRUSTLESS"); if (flags & DMG_FORCED) results.push("DMG_FORCED"); if (flags & DMG_NO_FACTOR) results.push("DMG_NO_FACTOR"); if (flags & DMG_PLAYERATTACK) results.push("DMG_PLAYERATTACK"); if (flags & DMG_FOILINVUL) results.push("DMG_FOILINVUL"); if (flags & DMG_FOILBUDDHA) results.push("DMG_FOILBUDDHA"); if (flags & DMG_NO_PROTECT) results.push("DMG_NO_PROTECT"); if (flags & DMG_USEANGLE) results.push("DMG_USEANGLE"); if (flags & DMG_NO_PAIN) results.push("DMG_NO_PAIN"); if (flags & DMG_EXPLOSION) results.push("DMG_EXPLOSION"); if (flags & DMG_NO_ENHANCE) results.push("DMG_NO_ENHANCE"); return add(name, NAMESPACE_su.join(results)); } NAMESPACE_Description addCvar(string name) { let aCvar = Cvar.getCvar(name, players[consolePlayer]); if (aCvar == NULL) return add(name, "NULL"); switch (aCvar.getRealType()) { case Cvar.CVAR_Bool: return addBool(name, NAMESPACE_su.boolToString(aCvar.getInt())); case Cvar.CVAR_Int: return addInt(name, aCvar.getInt()); case Cvar.CVAR_Float: return addFloat(name, aCvar.getFloat()); case Cvar.CVAR_String: return add(name, aCvar.getString()); // TODO: implement color: case Cvar.CVAR_Color: return addInt(name, aCvar.getInt()); } return add(name, string.format("unknown type (%d)", aCvar.getRealType())); } /// SPAC - special activation types. NAMESPACE_Description addSpac(string name, int flags) { Array<string> results; if (flags & SPAC_Cross) results.push("SPAC_Cross"); if (flags & SPAC_Use) results.push("SPAC_Use"); if (flags & SPAC_MCross) results.push("SPAC_MCross"); if (flags & SPAC_Impact) results.push("SPAC_Impact"); if (flags & SPAC_Push) results.push("SPAC_Push"); if (flags & SPAC_PCross) results.push("SPAC_PCross"); if (flags & SPAC_UseThrough) results.push("SPAC_UseThrough"); if (flags & SPAC_AnyCross) results.push("SPAC_AnyCross"); if (flags & SPAC_MUse) results.push("SPAC_MUse"); if (flags & SPAC_MPush) results.push("SPAC_MPush"); if (flags & SPAC_UseBack) results.push("SPAC_UseBack"); if (flags & SPAC_Damage) results.push("SPAC_Damage"); if (flags & SPAC_Death) results.push("SPAC_Death"); return add(name, NAMESPACE_su.join(results)); } NAMESPACE_Description addLine(string name, Line aLine) { return addInt(name, aLine.index()); } NAMESPACE_Description addSectorPart(string name, SectorPart part) { switch (part) { case SECPART_None: return add(name, "SECPART_None"); case SECPART_Floor: return add(name, "SECPART_Floor"); case SECPART_Ceiling: return add(name, "SECPART_Ceiling"); case SECPART_3D: return add(name, "SECPART_3D"); } return add(name, string.format("unknown SECPART (%d)", part)); } NAMESPACE_Description addSector(string name, Sector aSector) { return addInt(name, aSector.index()); } NAMESPACE_Description addVector3(string name, vector3 vector) { return add(name, string.format("%.2f, %.2f, %.2f", vector.x, vector.y, vector.z)); } NAMESPACE_Description addState(string name, State aState) { return add(name, new("NAMESPACE_Description"). addInt("sprite", aState.sprite). addInt("frame", aState.Frame).compose()); } private Array<string> mFields; }
Optimization opportunity: add… functions append to a private string, not an array. Compose simply returns it.
2.2.1. compose
string compose() { return NAMESPACE_su.join(mFields); }
{
let d = new("Description");
it("description: empty", Assert(d.compose() == ""));
}
2.2.2. add
NAMESPACE_Description add(string name, string value) { mFields.push(name .. ": " .. value); return self; }
{
let d = new("Description");
d.add("k1", "v1").add("k2", "v2");
it("description: two", Assert(d.compose() == "k1: v1, k2: v2"));
}
2.2.3. addObject
NAMESPACE_Description addObject(string name, Object anObject) { if (anObject == NULL) return add(name, "NULL"); string className = anObject.getClassName(); return add(name, className); }
{
let d = new("Description");
Object o;
d.addObject("n", o).addObject("self", self);
it("description: object", Assert(d.compose() == "n: NULL, self: su_Test"));
}
2.2.4. addClass
NAMESPACE_Description addClass(string name, Class aClass) { if (aClass == NULL) return add(name, "NULL"); return add(name, aClass.getClassName()); }
{
string result = new("Description").addClass("c", self.getClass()).compose();
it("description: class", Assert(result == "c: su_Test"));
}
2.2.5. addBool
NAMESPACE_Description addBool(string name, bool value) { return add(name, NAMESPACE_su.boolToString(value)); }
{
let d = new("Description");
d.addBool("b", true);
it("description: bool", Assert(d.compose() == "b: true"));
}
2.2.6. addInt
NAMESPACE_Description addInt(string name, int value) { return add(name, string.format("%d", value)); }
{
let d = new("Description");
d.addInt("value", -19);
it("description: int", Assert(d.compose() == "value: -19"));
}
2.2.7. addFloat
NAMESPACE_Description addFloat(string name, double value) { return add(name, string.format("%.2f", value)); }
{
let d = new("Description");
d.addFloat("value", -19.4);
it("description: float", Assert(d.compose() == "value: -19.40"));
}
2.2.8. addDamageFlags
NAMESPACE_Description addDamageFlags(string name, EDmgFlags flags) { Array<string> results; if (flags & DMG_NO_ARMOR) results.push("DMG_NO_ARMOR"); if (flags & DMG_INFLICTOR_IS_PUFF) results.push("DMG_INFLICTOR_IS_PUFF"); if (flags & DMG_THRUSTLESS) results.push("DMG_THRUSTLESS"); if (flags & DMG_FORCED) results.push("DMG_FORCED"); if (flags & DMG_NO_FACTOR) results.push("DMG_NO_FACTOR"); if (flags & DMG_PLAYERATTACK) results.push("DMG_PLAYERATTACK"); if (flags & DMG_FOILINVUL) results.push("DMG_FOILINVUL"); if (flags & DMG_FOILBUDDHA) results.push("DMG_FOILBUDDHA"); if (flags & DMG_NO_PROTECT) results.push("DMG_NO_PROTECT"); if (flags & DMG_USEANGLE) results.push("DMG_USEANGLE"); if (flags & DMG_NO_PAIN) results.push("DMG_NO_PAIN"); if (flags & DMG_EXPLOSION) results.push("DMG_EXPLOSION"); if (flags & DMG_NO_ENHANCE) results.push("DMG_NO_ENHANCE"); return add(name, NAMESPACE_su.join(results)); }
{
let d = new("Description");
d.addDamageFlags("d", DMG_NO_ARMOR | DMG_NO_ENHANCE);
it("description: damage", Assert(d.compose() == "d: DMG_NO_ARMOR, DMG_NO_ENHANCE"));
}
2.2.9. addCvar
NAMESPACE_Description addCvar(string name) { let aCvar = Cvar.getCvar(name, players[consolePlayer]); if (aCvar == NULL) return add(name, "NULL"); switch (aCvar.getRealType()) { case Cvar.CVAR_Bool: return addBool(name, NAMESPACE_su.boolToString(aCvar.getInt())); case Cvar.CVAR_Int: return addInt(name, aCvar.getInt()); case Cvar.CVAR_Float: return addFloat(name, aCvar.getFloat()); case Cvar.CVAR_String: return add(name, aCvar.getString()); // TODO: implement color: case Cvar.CVAR_Color: return addInt(name, aCvar.getInt()); } return add(name, string.format("unknown type (%d)", aCvar.getRealType())); }
{
let d = new("Description");
d.addCvar("autoaim").addCvar("dmflags2");
it("description: cvar", Assert(d.compose() == "autoaim: 35.00, dmflags2: 0"));
}
2.2.10. addSpac
/// SPAC - special activation types. NAMESPACE_Description addSpac(string name, int flags) { Array<string> results; if (flags & SPAC_Cross) results.push("SPAC_Cross"); if (flags & SPAC_Use) results.push("SPAC_Use"); if (flags & SPAC_MCross) results.push("SPAC_MCross"); if (flags & SPAC_Impact) results.push("SPAC_Impact"); if (flags & SPAC_Push) results.push("SPAC_Push"); if (flags & SPAC_PCross) results.push("SPAC_PCross"); if (flags & SPAC_UseThrough) results.push("SPAC_UseThrough"); if (flags & SPAC_AnyCross) results.push("SPAC_AnyCross"); if (flags & SPAC_MUse) results.push("SPAC_MUse"); if (flags & SPAC_MPush) results.push("SPAC_MPush"); if (flags & SPAC_UseBack) results.push("SPAC_UseBack"); if (flags & SPAC_Damage) results.push("SPAC_Damage"); if (flags & SPAC_Death) results.push("SPAC_Death"); return add(name, NAMESPACE_su.join(results)); }
{
let d = new("Description");
d.addSpac("s", SPAC_Cross | SPAC_Death);
it("description: SPAC", Assert(d.compose() == "s: SPAC_Cross, SPAC_Death"));
}
2.2.11. addLine
NAMESPACE_Description addLine(string name, Line aLine) { return addInt(name, aLine.index()); }
{
let d = new("Description");
d.addLine("l", level.lines[1]);
it("description: line", Assert(d.compose() == "l: 1"));
}
2.2.12. addSectorPart
NAMESPACE_Description addSectorPart(string name, SectorPart part) { switch (part) { case SECPART_None: return add(name, "SECPART_None"); case SECPART_Floor: return add(name, "SECPART_Floor"); case SECPART_Ceiling: return add(name, "SECPART_Ceiling"); case SECPART_3D: return add(name, "SECPART_3D"); } return add(name, string.format("unknown SECPART (%d)", part)); }
{
let d = new("Description");
d.addSectorPart("s", SECPART_3D);
it("description: SECPART", Assert(d.compose() == "s: SECPART_3D"));
}
2.2.13. addSector
NAMESPACE_Description addSector(string name, Sector aSector) { return addInt(name, aSector.index()); }
{
let d = new("Description");
d.addSector("s", level.sectors[1]);
it("description: sector", Assert(d.compose() == "s: 1"));
}
2.2.14. addVector3
NAMESPACE_Description addVector3(string name, vector3 vector) { return add(name, string.format("%.2f, %.2f, %.2f", vector.x, vector.y, vector.z)); }
{
let d = new("Description");
vector3 v = (1.1, 2.2, 3.3);
d.addVector3("v", v);
it("description: vector", Assert(d.compose() == "v: 1.10, 2.20, 3.30"));
}
2.2.15. addState
NAMESPACE_Description addState(string name, State aState) { return add(name, new("NAMESPACE_Description"). addInt("sprite", aState.sprite). addInt("frame", aState.Frame).compose()); }
{
let d = new("Description");
let state = players[consolePlayer].ReadyWeapon.FindState("Fire");
d.addState("s", state);
string expected = string.format("s: sprite: %d, frame: %d",
state.sprite,
state.Frame);
it("description: state", Assert(d.compose() == expected));
}
2.3. Ascii
class NAMESPACE_Ascii { enum _ { CHARACTER_NULL = 0, START_OF_HEADING = 1, START_OF_TEXT = 2, END_OF_TEXT = 3, END_OF_TRANSMISSION = 4, ENQUIRY = 5, ACKNOWLEDGE = 6, BELL = 7, BACKSPACE = 8, CHARACTER_TABULATION = 9, LINE_FEED_LF = 10, LINE_TABULATION = 11, FORM_FEED_FF = 12, CARRIAGE_RETURN_CR = 13, SHIFT_OUT = 14, SHIFT_IN = 15, DATA_LINK_ESCAPE = 16, DEVICE_CONTROL_ONE = 17, DEVICE_CONTROL_TWO = 18, DEVICE_CONTROL_THREE = 19, DEVICE_CONTROL_FOUR = 20, NEGATIVE_ACKNOWLEDGE = 21, SYNCHRONOUS_IDLE = 22, END_OF_TRANSMISSION_BLOCK = 23, CANCEL = 24, END_OF_MEDIUM = 25, SUBSTITUTE = 26, ESCAPE = 27, INFORMATION_SEPARATOR_FOUR = 28, INFORMATION_SEPARATOR_THREE = 29, INFORMATION_SEPARATOR_TWO = 30, INFORMATION_SEPARATOR_ONE = 31, SPACE = 32, EXCLAMATION_MARK = 33, QUOTATION_MARK = 34, NUMBER_SIGN = 35, DOLLAR_SIGN = 36, PERCENT_SIGN = 37, AMPERSAND = 38, APOSTROPHE = 39, LEFT_PARENTHESIS = 40, RIGHT_PARENTHESIS = 41, ASTERISK = 42, PLUS_SIGN = 43, COMMA = 44, HYPHEN_MINUS = 45, FULL_STOP = 46, SOLIDUS = 47, DIGIT_ZERO = 48, DIGIT_ONE = 49, DIGIT_TWO = 50, DIGIT_THREE = 51, DIGIT_FOUR = 52, DIGIT_FIVE = 53, DIGIT_SIX = 54, DIGIT_SEVEN = 55, DIGIT_EIGHT = 56, DIGIT_NINE = 57, COLON = 58, SEMICOLON = 59, LESS_THAN_SIGN = 60, EQUALS_SIGN = 61, GREATER_THAN_SIGN = 62, QUESTION_MARK = 63, COMMERCIAL_AT = 64, LATIN_CAPITAL_LETTER_A = 65, LATIN_CAPITAL_LETTER_B = 66, LATIN_CAPITAL_LETTER_C = 67, LATIN_CAPITAL_LETTER_D = 68, LATIN_CAPITAL_LETTER_E = 69, LATIN_CAPITAL_LETTER_F = 70, LATIN_CAPITAL_LETTER_G = 71, LATIN_CAPITAL_LETTER_H = 72, LATIN_CAPITAL_LETTER_I = 73, LATIN_CAPITAL_LETTER_J = 74, LATIN_CAPITAL_LETTER_K = 75, LATIN_CAPITAL_LETTER_L = 76, LATIN_CAPITAL_LETTER_M = 77, LATIN_CAPITAL_LETTER_N = 78, LATIN_CAPITAL_LETTER_O = 79, LATIN_CAPITAL_LETTER_P = 80, LATIN_CAPITAL_LETTER_Q = 81, LATIN_CAPITAL_LETTER_R = 82, LATIN_CAPITAL_LETTER_S = 83, LATIN_CAPITAL_LETTER_T = 84, LATIN_CAPITAL_LETTER_U = 85, LATIN_CAPITAL_LETTER_V = 86, LATIN_CAPITAL_LETTER_W = 87, LATIN_CAPITAL_LETTER_X = 88, LATIN_CAPITAL_LETTER_Y = 89, LATIN_CAPITAL_LETTER_Z = 90, LEFT_SQUARE_BRACKET = 91, REVERSE_SOLIDUS = 92, RIGHT_SQUARE_BRACKET = 93, CIRCUMFLEX_ACCENT = 94, LOW_LINE = 95, GRAVE_ACCENT = 96, LATIN_SMALL_LETTER_A = 97, LATIN_SMALL_LETTER_B = 98, LATIN_SMALL_LETTER_C = 99, LATIN_SMALL_LETTER_D = 100, LATIN_SMALL_LETTER_E = 101, LATIN_SMALL_LETTER_F = 102, LATIN_SMALL_LETTER_G = 103, LATIN_SMALL_LETTER_H = 104, LATIN_SMALL_LETTER_I = 105, LATIN_SMALL_LETTER_J = 106, LATIN_SMALL_LETTER_K = 107, LATIN_SMALL_LETTER_L = 108, LATIN_SMALL_LETTER_M = 109, LATIN_SMALL_LETTER_N = 110, LATIN_SMALL_LETTER_O = 111, LATIN_SMALL_LETTER_P = 112, LATIN_SMALL_LETTER_Q = 113, LATIN_SMALL_LETTER_R = 114, LATIN_SMALL_LETTER_S = 115, LATIN_SMALL_LETTER_T = 116, LATIN_SMALL_LETTER_U = 117, LATIN_SMALL_LETTER_V = 118, LATIN_SMALL_LETTER_W = 119, LATIN_SMALL_LETTER_X = 120, LATIN_SMALL_LETTER_Y = 121, LATIN_SMALL_LETTER_Z = 122, LEFT_CURLY_BRACKET = 123, VERTICAL_LINE = 124, RIGHT_CURLY_BRACKET = 125, TILDE = 126, DELETE = 127, END } const FIRST_PRINTABLE = SPACE; const CASE_DIFFERENCE = LATIN_SMALL_LETTER_A - LATIN_CAPITAL_LETTER_A; static bool isControlCharacter(int code) { return code < NAMESPACE_Ascii.SPACE || code == NAMESPACE_Ascii.DELETE; } }
(defun number-to-ascii-enum (number) (format "%s = %d" (string-replace "(" "" (string-replace ")" "" (string-replace "-" "_" (string-replace " " "_" (char-to-name number))))) number)) (mapconcat 'number-to-ascii-enum (number-sequence 1 127) ",\n")
{
it("ASCII tab", Assert("\t" == string.format("%c", Ascii.CHARACTER_TABULATION)));
it("ASCII \\n", Assert("\n" == string.format("%c", Ascii.LINE_FEED_LF)));
it("ASCII ',' is not a control character",
Assert(!Ascii.isControlCharacter(Ascii.COMMA)));
it("ASCII '\\n' is a control character",
Assert(Ascii.isControlCharacter(Ascii.LINE_FEED_LF)));
}