Color::FG_CYAN, Highlight::MultiLineComment => Color::FG_BRIGHT_BLACK, Highlight::Keyword1 => Color::FG_YELLOW, Highlight::Keyword2 => Color::FG_GREEN, Highlight::String => Color::FG_MAGENTA, Highlight::Character => Color::FG_BRIGHT_MAGENTA, Highlight::Number => Color::FG_BRIGHT_RED, Highlight::Operator => Color::FG_BRIGHT_GREEN, Highlight::Variable => Color::FG_BRIGHT_CYAN, Highlight::Delimiter => Color::FG_BLUE, Highlight::Invalid => Color::BG_BRIGHT_RED, Highlight::SearchMatch => Color::INVERT, Highlight::Identifier => Color::FG_BRIGHT_WHITE, default => Color::FG_WHITE, }; } // ---------------------------------------------------------------------------- // ! C function/macro equivalents // ---------------------------------------------------------------------------- /** * Do bit twiddling to convert a letter into * its Ctrl-letter equivalent ordinal ascii value */ function ctrl_key(string $char): int { if ( ! is_ascii($char)) { return -1; } // b1,100,001 (a) & b0,011,111 (0x1f) = b0,000,001 (SOH) // b1,100,010 (b) & b0,011,111 (0x1f) = b0,000,010 (STX) // ...and so on return ord($char) & 0x1f; } /** * Does the one-character string contain an ascii ordinal value? */ function is_ascii(string $single_char): bool { if (strlen($single_char) > 1) { return FALSE; } return ord($single_char) < 0x80; } /** * Does the one-character string contain an ascii control character? */ function is_ctrl(string $char): bool { $c = ord($char); return is_ascii($char) && ( $c === 0x7f || $c < 0x20 ); } /** * Does the one-character string contain an ascii number? */ function is_digit(string $char): bool { $c = ord($char); return is_ascii($char) && ( $c > 0x2f && $c < 0x3a ); } /** * Does the one-character string contain ascii whitespace? */ function is_space(string $char): bool { return match($char) { RawKeyCode::CARRIAGE_RETURN, RawKeyCode::FORM_FEED, RawKeyCode::NEWLINE, RawKeyCode::SPACE, RawKeyCode::TAB, RawKeyCode::VERTICAL_TAB => true, default => false, }; } // ---------------------------------------------------------------------------- // ! Helper functions // ---------------------------------------------------------------------------- /** * Does the one-character string contain a character that separates tokens? */ function is_separator(string $char): bool { if ( ! is_ascii($char)) { return FALSE; } $isSep = str_contains(',.()+-/*=~%<>[];', $char); return is_space($char) || $char === RawKeyCode::NULL || $isSep; } /** * Replaces a slice of an array with the same value * * @param array $array The array to update * @param int $offset The index of the first location to update * @param int $length The number of indices to update * @param mixed $value The value to replace in the range */ function array_replace_range(array &$array, int $offset, int $length, mixed $value):void { if ($length === 1) { $array[$offset] = $value; return; } $replacement = array_fill(0, $length, $value); array_splice($array, $offset, $length, $replacement); } /** * Does the string $haystack contain $str, optionally searching from $offset */ function str_has(string $haystack, string $str, ?int $offset = NULL): bool { if (empty($str)) { return FALSE; } return ($offset !== NULL) ? strpos($haystack, $str, $offset) !== FALSE : \str_contains($haystack, $str); } /** * Replace tabs with the specified number of spaces. */ function tabs_to_spaces(string $str, int $number = KILO_TAB_STOP): string { return str_replace(RawKeyCode::TAB, str_repeat(RawKeyCode::SPACE, $number), $str); } function error_code_name(int $code): string { return match ($code) { E_ERROR => 'Error', E_WARNING => 'Warning', E_PARSE => 'Parse Error', E_NOTICE => 'Notice', E_CORE_ERROR => 'Core Error', E_CORE_WARNING => 'Core Warning', E_COMPILE_ERROR => 'Compile Error', E_COMPILE_WARNING => 'Compile Warning', E_USER_ERROR => 'User Error', E_USER_WARNING => 'User Warning', E_USER_NOTICE => 'User Notice', E_RECOVERABLE_ERROR => 'Recoverable Error', E_DEPRECATED => 'Deprecated', E_USER_DEPRECATED => 'User Deprecated', default => 'Unknown', }; } /** * Adds two numbers to at most the $max value */ function saturating_add(int $a, int $b, int $max): int { return ($a + $b > $max) ? $max : $a + $b; } /** * Delete one number from another, down to zero at the least */ function saturating_sub(int $a, int $b): int { if ($b > $a) { return 0; } return $a - $b; }