Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
76.92% covered (warning)
76.92%
10 / 13
CRAP
70.77% covered (warning)
70.77%
46 / 65
Aviat\Kilo\ctrl_key
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
3 / 3
Aviat\Kilo\is_ascii
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
3 / 3
Aviat\Kilo\is_ctrl
100.00% covered (success)
100.00%
1 / 1
3
100.00% covered (success)
100.00%
2 / 2
Aviat\Kilo\is_digit
100.00% covered (success)
100.00%
1 / 1
3
100.00% covered (success)
100.00%
2 / 2
Aviat\Kilo\is_space
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
7 / 7
Aviat\Kilo\is_separator
100.00% covered (success)
100.00%
1 / 1
4
100.00% covered (success)
100.00%
4 / 4
Aviat\Kilo\array_replace_range
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
5 / 5
Aviat\Kilo\str_has
100.00% covered (success)
100.00%
1 / 1
3
100.00% covered (success)
100.00%
5 / 5
Aviat\Kilo\syntax_to_color
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
14 / 14
Aviat\Kilo\tabs_to_spaces
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
Aviat\Kilo\error_code_name
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 15
Aviat\Kilo\saturating_add
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 1
Aviat\Kilo\saturating_sub
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 3
1<?php declare(strict_types=1);
2
3namespace Aviat\Kilo;
4
5use Aviat\Kilo\Enum\{Color, Highlight, RawKeyCode};
6
7// ----------------------------------------------------------------------------
8// ! C function/macro equivalents
9// ----------------------------------------------------------------------------
10
11/**
12 * Do bit twiddling to convert a letter into
13 * its Ctrl-letter equivalent ordinal ascii value
14 *
15 * @param string $char
16 * @return int
17 */
18function ctrl_key(string $char): int
19{
20    if ( ! is_ascii($char))
21    {
22        return -1;
23    }
24
25    // b1,100,001 (a) & b0,011,111 (0x1f) = b0,000,001 (SOH)
26    // b1,100,010 (b) & b0,011,111 (0x1f) = b0,000,010 (STX)
27    // ...and so on
28    return ord($char) & 0x1f;
29}
30
31/**
32 * Does the one-character string contain an ascii ordinal value?
33 *
34 * @param string $single_char
35 * @return bool
36 */
37function is_ascii(string $single_char): bool
38{
39    if (strlen($single_char) > 1)
40    {
41        return FALSE;
42    }
43
44    return ord($single_char) < 0x80;
45}
46
47/**
48 * Does the one-character string contain an ascii control character?
49 *
50 * @param string $char
51 * @return bool
52 */
53function is_ctrl(string $char): bool
54{
55    $c = ord($char);
56    return is_ascii($char) && ( $c === 0x7f || $c < 0x20 );
57}
58
59/**
60 * Does the one-character string contain an ascii number?
61 *
62 * @param string $char
63 * @return bool
64 */
65function is_digit(string $char): bool
66{
67    $c = ord($char);
68    return is_ascii($char) && ( $c > 0x2f && $c < 0x3a );
69}
70
71/**
72 * Does the one-character string contain ascii whitespace?
73 *
74 * @param string $char
75 * @return bool
76 */
77function is_space(string $char): bool
78{
79    return match($char) {
80        RawKeyCode::CARRIAGE_RETURN,
81        RawKeyCode::FORM_FEED,
82        RawKeyCode::NEWLINE,
83        RawKeyCode::SPACE,
84        RawKeyCode::TAB,
85        RawKeyCode::VERTICAL_TAB => true,
86
87        default => false,
88    };
89}
90
91// ----------------------------------------------------------------------------
92// ! Helper functions
93// ----------------------------------------------------------------------------
94
95/**
96 * Does the one-character string contain a character that separates tokens?
97 *
98 * @param string $char
99 * @return bool
100 */
101function is_separator(string $char): bool
102{
103    if ( ! is_ascii($char))
104    {
105        return FALSE;
106    }
107
108    $isSep = str_contains(',.()+-/*=~%<>[];', $char);
109
110    return is_space($char) || $char === RawKeyCode::NULL || $isSep;
111}
112
113/**
114 * Replaces a slice of an array with the same value
115 *
116 * @param array $array The array to update
117 * @param int $offset The index of the first location to update
118 * @param int $length The number of indices to update
119 * @param mixed $value The value to replace in the range
120 */
121function array_replace_range(array &$array, int $offset, int $length, mixed $value):void
122{
123    if ($length === 1)
124    {
125        $array[$offset] = $value;
126        return;
127    }
128
129    $replacement = array_fill(0, $length, $value);
130    array_splice($array, $offset, $length, $replacement);
131}
132
133/**
134 * Does the string $haystack contain $str, optionally searching from $offset?
135 *
136 * @param string $haystack
137 * @param string $str
138 * @param int|null $offset
139 * @return bool
140 */
141function str_has(string $haystack, string $str, ?int $offset = NULL): bool
142{
143    if (empty($str))
144    {
145        return FALSE;
146    }
147
148    return ($offset !== NULL)
149        ? strpos($haystack, $str, $offset) !== FALSE
150        : \str_contains($haystack, $str);
151}
152
153/**
154 * Get the ASCII color escape number for the specified syntax type
155 *
156 * @param int $hl
157 * @return int
158 */
159function syntax_to_color(int $hl): int
160{
161    return match ($hl)
162    {
163        Highlight::COMMENT => Color::FG_CYAN,
164        Highlight::ML_COMMENT => Color::FG_BRIGHT_BLACK,
165        Highlight::KEYWORD1 => Color::FG_YELLOW,
166        Highlight::KEYWORD2 => Color::FG_GREEN,
167        Highlight::STRING => Color::FG_MAGENTA,
168        Highlight::CHARACTER => Color::FG_BRIGHT_MAGENTA,
169        Highlight::NUMBER => Color::FG_BRIGHT_RED,
170        Highlight::OPERATOR => Color::FG_BRIGHT_GREEN,
171        Highlight::VARIABLE => Color::FG_BRIGHT_CYAN,
172        Highlight::DELIMITER => Color::FG_BLUE,
173        Highlight::INVALID => Color::BG_BRIGHT_RED,
174        Highlight::MATCH => Color::INVERT,
175        Highlight::IDENTIFIER => Color::FG_BRIGHT_WHITE,
176        default => Color::FG_WHITE,
177    };
178}
179
180/**
181 * Replace tabs with the specified number of spaces.
182 *
183 * @param string $str
184 * @param int $number
185 * @return string
186 */
187function tabs_to_spaces(string $str, int $number = KILO_TAB_STOP): string
188{
189    return str_replace(RawKeyCode::TAB, str_repeat(RawKeyCode::SPACE, $number), $str);
190}
191
192function error_code_name(int $code): string
193{
194    return     match ($code) {
195        E_ERROR => 'Error',
196        E_WARNING => 'Warning',
197        E_PARSE => 'Parse Error',
198        E_NOTICE => 'Notice',
199        E_CORE_ERROR => 'Core Error',
200        E_CORE_WARNING => 'Core Warning',
201        E_COMPILE_ERROR => 'Compile Error',
202        E_COMPILE_WARNING => 'Compile Warning',
203        E_USER_ERROR => 'User Error',
204        E_USER_WARNING => 'User Warning',
205        E_USER_NOTICE => 'User Notice',
206        E_RECOVERABLE_ERROR => 'Recoverable Error',
207        E_DEPRECATED => 'Deprecated',
208        E_USER_DEPRECATED => 'User Deprecated',
209        default => 'Unknown',
210    };
211}
212
213function saturating_add(int $a, int $b, int $max): int
214{
215    return ($a + $b > $max) ? $max : $a + $b;
216}
217
218function saturating_sub(int $a, int $b): int
219{
220    if ($b > $a)
221    {
222        return 0;
223    }
224
225    return $a - $b;
226}